+int osd_unlinked_object_free(const struct lu_env *env, struct osd_device *osd,
+ uint64_t oid)
+{
+ char *key = osd_oti_get(env)->oti_str;
+ int rc;
+ dmu_tx_t *tx;
+
+ if (osd->od_dt_dev.dd_rdonly) {
+ CERROR("%s: someone try to free objects under "
+ "readonly mode, should be disabled.\n", osd_name(osd));
+ dump_stack();
+
+ return -EROFS;
+ }
+
+ rc = -dmu_free_long_range(osd->od_os, oid, 0, DMU_OBJECT_END);
+ if (rc != 0) {
+ CWARN("%s: Cannot truncate %llu: rc = %d\n",
+ osd->od_svname, oid, rc);
+ return rc;
+ }
+
+ tx = dmu_tx_create(osd->od_os);
+ dmu_tx_hold_free(tx, oid, 0, DMU_OBJECT_END);
+ osd_tx_hold_zap(tx, osd->od_unlinked->dn_object, osd->od_unlinked,
+ FALSE, NULL);
+ rc = -dmu_tx_assign(tx, TXG_WAIT);
+ if (rc != 0) {
+ CWARN("%s: Cannot assign tx for %llu: rc = %d\n",
+ osd->od_svname, oid, rc);
+ goto failed;
+ }
+
+ snprintf(key, sizeof(osd_oti_get(env)->oti_str), "%llx", oid);
+ rc = osd_zap_remove(osd, osd->od_unlinked->dn_object,
+ osd->od_unlinked, key, tx);
+ if (rc != 0) {
+ CWARN("%s: Cannot remove %llu from unlinked set: rc = %d\n",
+ osd->od_svname, oid, rc);
+ goto failed;
+ }
+
+ rc = -dmu_object_free(osd->od_os, oid, tx);
+ if (rc != 0) {
+ CWARN("%s: Cannot free %llu: rc = %d\n",
+ osd->od_svname, oid, rc);
+ goto failed;
+ }
+ dmu_tx_commit(tx);
+
+ return 0;
+
+failed:
+ LASSERT(rc != 0);
+ dmu_tx_abort(tx);
+
+ return rc;
+}
+
+static void
+osd_unlinked_drain(const struct lu_env *env, struct osd_device *osd)
+{
+ zap_cursor_t zc;
+ zap_attribute_t *za = &osd_oti_get(env)->oti_za;
+
+ zap_cursor_init(&zc, osd->od_os, osd->od_unlinked->dn_object);
+
+ while (zap_cursor_retrieve(&zc, za) == 0) {
+ /* If cannot free the object, leave it in the unlinked set,
+ * until the OSD is mounted again when obd_unlinked_drain()
+ * will be called. */
+ if (osd_unlinked_object_free(env, osd, za->za_first_integer))
+ break;
+ zap_cursor_advance(&zc);
+ }
+
+ zap_cursor_fini(&zc);
+}
+