+static void osd_recordsize_changed_cb(void *arg, uint64_t newval)
+{
+ struct osd_device *osd = arg;
+
+ LASSERT(newval <= osd_spa_maxblocksize(dmu_objset_spa(osd->od_os)));
+ LASSERT(newval >= SPA_MINBLOCKSIZE);
+ LASSERT(ISP2(newval));
+
+ osd->od_max_blksz = newval;
+}
+
+static void osd_readonly_changed_cb(void *arg, uint64_t newval)
+{
+ struct osd_device *osd = arg;
+
+ osd->od_prop_rdonly = !!newval;
+}
+
+/*
+ * This function unregisters all registered callbacks. It's harmless to
+ * unregister callbacks that were never registered so it is used to safely
+ * unwind a partially completed call to osd_objset_register_callbacks().
+ */
+static void osd_objset_unregister_callbacks(struct osd_device *o)
+{
+ struct dsl_dataset *ds = dmu_objset_ds(o->od_os);
+
+ (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_XATTR),
+ osd_xattr_changed_cb, o);
+ (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_RECORDSIZE),
+ osd_recordsize_changed_cb, o);
+ (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_READONLY),
+ osd_readonly_changed_cb, o);
+
+ if (o->arc_prune_cb != NULL) {
+ arc_remove_prune_callback(o->arc_prune_cb);
+ o->arc_prune_cb = NULL;
+ }
+}
+
+/*
+ * Register the required callbacks to be notified when zfs properties
+ * are modified using the 'zfs(8)' command line utility.
+ */
+static int osd_objset_register_callbacks(struct osd_device *o)
+{
+ struct dsl_dataset *ds = dmu_objset_ds(o->od_os);
+ dsl_pool_t *dp = dmu_objset_pool(o->od_os);
+ int rc;
+
+ LASSERT(ds);
+ LASSERT(dp);
+
+ dsl_pool_config_enter(dp, FTAG);
+ rc = -dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_XATTR),
+ osd_xattr_changed_cb, o);
+ if (rc)
+ GOTO(err, rc);
+
+ rc = -dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_RECORDSIZE),
+ osd_recordsize_changed_cb, o);
+ if (rc)
+ GOTO(err, rc);
+
+ rc = -dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_READONLY),
+ osd_readonly_changed_cb, o);
+ if (rc)
+ GOTO(err, rc);
+
+ o->arc_prune_cb = arc_add_prune_callback(arc_prune_func, o);
+err:
+ dsl_pool_config_exit(dp, FTAG);
+ if (rc)
+ osd_objset_unregister_callbacks(o);
+
+ RETURN(rc);
+}
+
+static int osd_objset_open(struct osd_device *o)
+{
+ uint64_t version = ZPL_VERSION;
+ uint64_t sa_obj, unlink_obj;
+ int rc;
+ ENTRY;
+
+ rc = -dmu_objset_own(o->od_mntdev, DMU_OST_ZFS,
+ o->od_dt_dev.dd_rdonly ? B_TRUE : B_FALSE,
+ o, &o->od_os);
+ if (rc) {
+ CERROR("%s: can't open %s\n", o->od_svname, o->od_mntdev);
+ o->od_os = NULL;
+
+ GOTO(out, rc);
+ }
+
+ /* Check ZFS version */
+ rc = -zap_lookup(o->od_os, MASTER_NODE_OBJ,
+ ZPL_VERSION_STR, 8, 1, &version);
+ if (rc) {
+ CERROR("%s: Error looking up ZPL VERSION\n", o->od_mntdev);
+ /*
+ * We can't return ENOENT because that would mean the objset
+ * didn't exist.
+ */
+ GOTO(out, rc = -EIO);
+ }
+
+ rc = -zap_lookup(o->od_os, MASTER_NODE_OBJ,
+ ZFS_SA_ATTRS, 8, 1, &sa_obj);
+ if (rc)
+ GOTO(out, rc);
+
+ rc = -sa_setup(o->od_os, sa_obj, zfs_attr_table,
+ ZPL_END, &o->z_attr_table);
+ if (rc)
+ GOTO(out, rc);
+
+ rc = -zap_lookup(o->od_os, MASTER_NODE_OBJ, ZFS_ROOT_OBJ,
+ 8, 1, &o->od_rootid);
+ if (rc) {
+ CERROR("%s: lookup for root failed: rc = %d\n",
+ o->od_svname, rc);
+ GOTO(out, rc);
+ }
+
+ rc = -zap_lookup(o->od_os, MASTER_NODE_OBJ, ZFS_UNLINKED_SET,
+ 8, 1, &unlink_obj);
+ if (rc) {
+ CERROR("%s: lookup for %s failed: rc = %d\n",
+ o->od_svname, ZFS_UNLINKED_SET, rc);
+ GOTO(out, rc);
+ }
+
+ /* Check that user/group usage tracking is supported */
+ if (!dmu_objset_userused_enabled(o->od_os) ||
+ DMU_USERUSED_DNODE(o->od_os)->dn_type != DMU_OT_USERGROUP_USED ||
+ DMU_GROUPUSED_DNODE(o->od_os)->dn_type != DMU_OT_USERGROUP_USED) {
+ CERROR("%s: Space accounting not supported by this target, "
+ "aborting\n", o->od_svname);
+ GOTO(out, rc = -ENOTSUPP);
+ }
+
+ rc = __osd_obj2dnode(o->od_os, unlink_obj, &o->od_unlinked);
+ if (rc) {
+ CERROR("%s: can't get dnode for unlinked: rc = %d\n",
+ o->od_svname, rc);
+ GOTO(out, rc);
+ }
+
+out:
+ if (rc != 0 && o->od_os != NULL) {
+ dmu_objset_disown(o->od_os, o);
+ o->od_os = NULL;
+ }
+
+ RETURN(rc);
+}
+
+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);
+}
+