diff --git a/include/sys/dsl_dataset.h b/include/sys/dsl_dataset.h index f033eace9..a596642e3 100644 --- a/include/sys/dsl_dataset.h +++ b/include/sys/dsl_dataset.h @@ -184,6 +184,9 @@ typedef struct dsl_dataset { kmutex_t ds_sendstream_lock; list_t ds_sendstreams; + /* Protected by our dsl_dir's dd_lock */ + list_t ds_prop_cbs; + /* * For ZFEATURE_FLAG_PER_DATASET features, set if this dataset * uses this feature. diff --git a/include/sys/dsl_dir.h b/include/sys/dsl_dir.h index 55f3a8e5b..a025c50a0 100644 --- a/include/sys/dsl_dir.h +++ b/include/sys/dsl_dir.h @@ -102,7 +102,7 @@ struct dsl_dir { /* Protected by dd_lock */ kmutex_t dd_lock; - list_t dd_prop_cbs; /* list of dsl_prop_cb_record_t's */ + list_t dd_props; /* list of dsl_prop_record_t's */ timestruc_t dd_snap_cmtime; /* last time snapshot namespace changed */ uint64_t dd_origin_txg; diff --git a/include/sys/dsl_prop.h b/include/sys/dsl_prop.h index 5fe18d6a7..62ef0ba67 100644 --- a/include/sys/dsl_prop.h +++ b/include/sys/dsl_prop.h @@ -41,10 +41,17 @@ struct dsl_dir; /* The callback func may not call into the DMU or DSL! */ typedef void (dsl_prop_changed_cb_t)(void *arg, uint64_t newval); +typedef struct dsl_prop_record { + list_node_t pr_node; /* link on dd_props */ + const char *pr_propname; + list_t pr_cbs; +} dsl_prop_record_t; + typedef struct dsl_prop_cb_record { - list_node_t cbr_node; /* link on dd_prop_cbs */ + list_node_t cbr_pr_node; /* link on pr_cbs */ + list_node_t cbr_ds_node; /* link on ds_prop_cbs */ + dsl_prop_record_t *cbr_pr; struct dsl_dataset *cbr_ds; - const char *cbr_propname; dsl_prop_changed_cb_t *cbr_func; void *cbr_arg; } dsl_prop_cb_record_t; @@ -54,10 +61,13 @@ typedef struct dsl_props_arg { zprop_source_t pa_source; } dsl_props_arg_t; +void dsl_prop_init(dsl_dir_t *dd); +void dsl_prop_fini(dsl_dir_t *dd); int dsl_prop_register(struct dsl_dataset *ds, const char *propname, dsl_prop_changed_cb_t *callback, void *cbarg); int dsl_prop_unregister(struct dsl_dataset *ds, const char *propname, dsl_prop_changed_cb_t *callback, void *cbarg); +void dsl_prop_unregister_all(struct dsl_dataset *ds, void *cbarg); void dsl_prop_notify_all(struct dsl_dir *dd); boolean_t dsl_prop_hascb(struct dsl_dataset *ds); diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index 779b3bb78..7e3a5fb20 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -688,40 +688,8 @@ dmu_objset_evict(objset_t *os) for (t = 0; t < TXG_SIZE; t++) ASSERT(!dmu_objset_is_dirty(os, t)); - if (ds) { - if (!ds->ds_is_snapshot) { - VERIFY0(dsl_prop_unregister(ds, - zfs_prop_to_name(ZFS_PROP_CHECKSUM), - checksum_changed_cb, os)); - VERIFY0(dsl_prop_unregister(ds, - zfs_prop_to_name(ZFS_PROP_COMPRESSION), - compression_changed_cb, os)); - VERIFY0(dsl_prop_unregister(ds, - zfs_prop_to_name(ZFS_PROP_COPIES), - copies_changed_cb, os)); - VERIFY0(dsl_prop_unregister(ds, - zfs_prop_to_name(ZFS_PROP_DEDUP), - dedup_changed_cb, os)); - VERIFY0(dsl_prop_unregister(ds, - zfs_prop_to_name(ZFS_PROP_LOGBIAS), - logbias_changed_cb, os)); - VERIFY0(dsl_prop_unregister(ds, - zfs_prop_to_name(ZFS_PROP_SYNC), - sync_changed_cb, os)); - VERIFY0(dsl_prop_unregister(ds, - zfs_prop_to_name(ZFS_PROP_REDUNDANT_METADATA), - redundant_metadata_changed_cb, os)); - VERIFY0(dsl_prop_unregister(ds, - zfs_prop_to_name(ZFS_PROP_RECORDSIZE), - recordsize_changed_cb, os)); - } - VERIFY0(dsl_prop_unregister(ds, - zfs_prop_to_name(ZFS_PROP_PRIMARYCACHE), - primary_cache_changed_cb, os)); - VERIFY0(dsl_prop_unregister(ds, - zfs_prop_to_name(ZFS_PROP_SECONDARYCACHE), - secondary_cache_changed_cb, os)); - } + if (ds) + dsl_prop_unregister_all(ds, os); if (os->os_sa) sa_tear_down(os); diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index 6d93e0ade..a5a9694fc 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -284,6 +284,7 @@ dsl_dataset_evict(void *dbu) ASSERT(!list_link_active(&ds->ds_synced_link)); + list_destroy(&ds->ds_prop_cbs); mutex_destroy(&ds->ds_lock); mutex_destroy(&ds->ds_opening_lock); mutex_destroy(&ds->ds_sendstream_lock); @@ -433,6 +434,9 @@ dsl_dataset_hold_obj(dsl_pool_t *dp, uint64_t dsobj, void *tag, list_create(&ds->ds_sendstreams, sizeof (dmu_sendarg_t), offsetof(dmu_sendarg_t, dsa_link)); + list_create(&ds->ds_prop_cbs, sizeof (dsl_prop_cb_record_t), + offsetof(dsl_prop_cb_record_t, cbr_ds_node)); + if (doi.doi_type == DMU_OTN_ZAP_METADATA) { spa_feature_t f; diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c index ba6c24486..2c521e285 100644 --- a/module/zfs/dsl_dir.c +++ b/module/zfs/dsl_dir.c @@ -147,11 +147,7 @@ dsl_dir_evict(void *dbu) spa_async_close(dd->dd_pool->dp_spa, dd); - /* - * The props callback list should have been cleaned up by - * objset_evict(). - */ - list_destroy(&dd->dd_prop_cbs); + dsl_prop_fini(dd); mutex_destroy(&dd->dd_lock); kmem_free(dd, sizeof (dsl_dir_t)); } @@ -186,9 +182,7 @@ dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj, dd->dd_dbuf = dbuf; dd->dd_pool = dp; mutex_init(&dd->dd_lock, NULL, MUTEX_DEFAULT, NULL); - - list_create(&dd->dd_prop_cbs, sizeof (dsl_prop_cb_record_t), - offsetof(dsl_prop_cb_record_t, cbr_node)); + dsl_prop_init(dd); dsl_dir_snap_cmtime_update(dd); @@ -246,6 +240,7 @@ dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj, if (winner != NULL) { if (dd->dd_parent) dsl_dir_rele(dd->dd_parent, dd); + dsl_prop_fini(dd); mutex_destroy(&dd->dd_lock); kmem_free(dd, sizeof (dsl_dir_t)); dd = winner; @@ -273,6 +268,7 @@ dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj, errout: if (dd->dd_parent) dsl_dir_rele(dd->dd_parent, dd); + dsl_prop_fini(dd); mutex_destroy(&dd->dd_lock); kmem_free(dd, sizeof (dsl_dir_t)); dmu_buf_rele(dbuf, tag); diff --git a/module/zfs/dsl_prop.c b/module/zfs/dsl_prop.c index 28b101eee..0c72dbca5 100644 --- a/module/zfs/dsl_prop.c +++ b/module/zfs/dsl_prop.c @@ -216,6 +216,58 @@ dsl_prop_get_ds(dsl_dataset_t *ds, const char *propname, intsz, numints, buf, setpoint, ds->ds_is_snapshot)); } +static dsl_prop_record_t * +dsl_prop_record_find(dsl_dir_t *dd, const char *propname) +{ + dsl_prop_record_t *pr = NULL; + + ASSERT(MUTEX_HELD(&dd->dd_lock)); + + for (pr = list_head(&dd->dd_props); + pr != NULL; pr = list_next(&dd->dd_props, pr)) { + if (strcmp(pr->pr_propname, propname) == 0) + break; + } + + return (pr); +} + +static dsl_prop_record_t * +dsl_prop_record_create(dsl_dir_t *dd, const char *propname) +{ + dsl_prop_record_t *pr; + + ASSERT(MUTEX_HELD(&dd->dd_lock)); + + pr = kmem_alloc(sizeof (dsl_prop_record_t), KM_SLEEP); + pr->pr_propname = spa_strdup(propname); + list_create(&pr->pr_cbs, sizeof (dsl_prop_cb_record_t), + offsetof(dsl_prop_cb_record_t, cbr_pr_node)); + list_insert_head(&dd->dd_props, pr); + + return (pr); +} + +void +dsl_prop_init(dsl_dir_t *dd) +{ + list_create(&dd->dd_props, sizeof (dsl_prop_record_t), + offsetof(dsl_prop_record_t, pr_node)); +} + +void +dsl_prop_fini(dsl_dir_t *dd) +{ + dsl_prop_record_t *pr; + + while ((pr = list_remove_head(&dd->dd_props)) != NULL) { + list_destroy(&pr->pr_cbs); + strfree((char *)pr->pr_propname); + kmem_free(pr, sizeof (dsl_prop_record_t)); + } + list_destroy(&dd->dd_props); +} + /* * Register interest in the named property. We'll call the callback * once to notify it of the current property value, and again each time @@ -229,6 +281,7 @@ dsl_prop_register(dsl_dataset_t *ds, const char *propname, { dsl_dir_t *dd = ds->ds_dir; uint64_t value; + dsl_prop_record_t *pr; dsl_prop_cb_record_t *cbr; int err; ASSERTV(dsl_pool_t *dp = dd->dd_pool); @@ -241,12 +294,16 @@ dsl_prop_register(dsl_dataset_t *ds, const char *propname, cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP); cbr->cbr_ds = ds; - cbr->cbr_propname = kmem_alloc(strlen(propname)+1, KM_SLEEP); - (void) strcpy((char *)cbr->cbr_propname, propname); cbr->cbr_func = callback; cbr->cbr_arg = cbarg; + mutex_enter(&dd->dd_lock); - list_insert_head(&dd->dd_prop_cbs, cbr); + pr = dsl_prop_record_find(dd, propname); + if (pr == NULL) + pr = dsl_prop_record_create(dd, propname); + cbr->cbr_pr = pr; + list_insert_head(&pr->pr_cbs, cbr); + list_insert_head(&ds->ds_prop_cbs, cbr); mutex_exit(&dd->dd_lock); cbr->cbr_func(cbr->cbr_arg, value); @@ -379,6 +436,9 @@ dsl_prop_predict(dsl_dir_t *dd, const char *propname, /* * Unregister this callback. Return 0 on success, ENOENT if ddname is * invalid, or ENOMSG if no matching callback registered. + * + * NOTE: This function is no longer used internally but has been preserved + * to prevent breaking external consumers (Lustre, etc). */ int dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, @@ -388,12 +448,12 @@ dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, dsl_prop_cb_record_t *cbr; mutex_enter(&dd->dd_lock); - for (cbr = list_head(&dd->dd_prop_cbs); - cbr; cbr = list_next(&dd->dd_prop_cbs, cbr)) { + for (cbr = list_head(&ds->ds_prop_cbs); + cbr; cbr = list_next(&ds->ds_prop_cbs, cbr)) { if (cbr->cbr_ds == ds && cbr->cbr_func == callback && cbr->cbr_arg == cbarg && - strcmp(cbr->cbr_propname, propname) == 0) + strcmp(cbr->cbr_pr->pr_propname, propname) == 0) break; } @@ -402,31 +462,43 @@ dsl_prop_unregister(dsl_dataset_t *ds, const char *propname, return (SET_ERROR(ENOMSG)); } - list_remove(&dd->dd_prop_cbs, cbr); + list_remove(&ds->ds_prop_cbs, cbr); + list_remove(&cbr->cbr_pr->pr_cbs, cbr); mutex_exit(&dd->dd_lock); - kmem_free((void*)cbr->cbr_propname, strlen(cbr->cbr_propname)+1); kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); return (0); } -boolean_t -dsl_prop_hascb(dsl_dataset_t *ds) +/* + * Unregister all callbacks that are registered with the + * given callback argument. + */ +void +dsl_prop_unregister_all(dsl_dataset_t *ds, void *cbarg) { + dsl_prop_cb_record_t *cbr, *next_cbr; + dsl_dir_t *dd = ds->ds_dir; - boolean_t rv = B_FALSE; - dsl_prop_cb_record_t *cbr; mutex_enter(&dd->dd_lock); - for (cbr = list_head(&dd->dd_prop_cbs); cbr; - cbr = list_next(&dd->dd_prop_cbs, cbr)) { - if (cbr->cbr_ds == ds) { - rv = B_TRUE; - break; + next_cbr = list_head(&ds->ds_prop_cbs); + while (next_cbr != NULL) { + cbr = next_cbr; + next_cbr = list_next(&ds->ds_prop_cbs, cbr); + if (cbr->cbr_arg == cbarg) { + list_remove(&ds->ds_prop_cbs, cbr); + list_remove(&cbr->cbr_pr->pr_cbs, cbr); + kmem_free(cbr, sizeof (dsl_prop_cb_record_t)); } } mutex_exit(&dd->dd_lock); - return (rv); +} + +boolean_t +dsl_prop_hascb(dsl_dataset_t *ds) +{ + return (!list_is_empty(&ds->ds_prop_cbs)); } /* ARGSUSED */ @@ -434,38 +506,50 @@ static int dsl_prop_notify_all_cb(dsl_pool_t *dp, dsl_dataset_t *ds, void *arg) { dsl_dir_t *dd = ds->ds_dir; + dsl_prop_record_t *pr; dsl_prop_cb_record_t *cbr; mutex_enter(&dd->dd_lock); - for (cbr = list_head(&dd->dd_prop_cbs); cbr; - cbr = list_next(&dd->dd_prop_cbs, cbr)) { - uint64_t value; + for (pr = list_head(&dd->dd_props); + pr; pr = list_next(&dd->dd_props, pr)) { + for (cbr = list_head(&pr->pr_cbs); cbr; + cbr = list_next(&pr->pr_cbs, cbr)) { + uint64_t value; - /* - * Callback entries do not have holds on their datasets - * so that datasets with registered callbacks are still - * eligible for eviction. Unlike operations on callbacks - * for a single dataset, we are performing a recursive - * descent of related datasets and the calling context - * for this iteration only has a dataset hold on the root. - * Without a hold, the callback's pointer to the dataset - * could be invalidated by eviction at any time. - * - * Use dsl_dataset_try_add_ref() to verify that the - * dataset has not begun eviction processing and to - * prevent eviction from occurring for the duration - * of the callback. If the hold attempt fails, this - * object is already being evicted and the callback can - * be safely ignored. - */ - if (!dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) - continue; + /* + * Callback entries do not have holds on their + * datasets so that datasets with registered + * callbacks are still eligible for eviction. + * Unlike operations to update properties on a + * single dataset, we are performing a recursive + * descent of related head datasets. The caller + * of this function only has a dataset hold on + * the passed in head dataset, not the snapshots + * associated with this dataset. Without a hold, + * the dataset pointer within callback records + * for snapshots can be invalidated by eviction + * at any time. + * + * Use dsl_dataset_try_add_ref() to verify + * that the dataset for a snapshot has not + * begun eviction processing and to prevent + * eviction from occurring for the duration of + * the callback. If the hold attempt fails, + * this object is already being evicted and the + * callback can be safely ignored. + */ + if (ds != cbr->cbr_ds && + !dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) + continue; - if (dsl_prop_get_ds(cbr->cbr_ds, cbr->cbr_propname, - sizeof (value), 1, &value, NULL) == 0) - cbr->cbr_func(cbr->cbr_arg, value); + if (dsl_prop_get_ds(cbr->cbr_ds, + cbr->cbr_pr->pr_propname, sizeof (value), 1, + &value, NULL) == 0) + cbr->cbr_func(cbr->cbr_arg, value); - dsl_dataset_rele(cbr->cbr_ds, FTAG); + if (ds != cbr->cbr_ds) + dsl_dataset_rele(cbr->cbr_ds, FTAG); + } } mutex_exit(&dd->dd_lock); @@ -490,6 +574,7 @@ dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, const char *propname, uint64_t value, int first) { dsl_dir_t *dd; + dsl_prop_record_t *pr; dsl_prop_cb_record_t *cbr; objset_t *mos = dp->dp_meta_objset; zap_cursor_t zc; @@ -516,30 +601,33 @@ dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, } mutex_enter(&dd->dd_lock); - for (cbr = list_head(&dd->dd_prop_cbs); cbr; - cbr = list_next(&dd->dd_prop_cbs, cbr)) { - uint64_t propobj; + pr = dsl_prop_record_find(dd, propname); + if (pr != NULL) { + for (cbr = list_head(&pr->pr_cbs); cbr; + cbr = list_next(&pr->pr_cbs, cbr)) { + uint64_t propobj; - /* - * cbr->cbf_ds may be invalidated due to eviction, - * requiring the use of dsl_dataset_try_add_ref(). - * See comment block in dsl_prop_notify_all_cb() - * for details. - */ - if (strcmp(cbr->cbr_propname, propname) != 0 || - !dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) - continue; + /* + * cbr->cbr_ds may be invalidated due to eviction, + * requiring the use of dsl_dataset_try_add_ref(). + * See comment block in dsl_prop_notify_all_cb() + * for details. + */ + if (!dsl_dataset_try_add_ref(dp, cbr->cbr_ds, FTAG)) + continue; - propobj = dsl_dataset_phys(cbr->cbr_ds)->ds_props_obj; + propobj = dsl_dataset_phys(cbr->cbr_ds)->ds_props_obj; - /* - * If the property is not set on this ds, then it is - * inherited here; call the callback. - */ - if (propobj == 0 || zap_contains(mos, propobj, propname) != 0) - cbr->cbr_func(cbr->cbr_arg, value); + /* + * If the property is not set on this ds, then it is + * inherited here; call the callback. + */ + if (propobj == 0 || + zap_contains(mos, propobj, propname) != 0) + cbr->cbr_func(cbr->cbr_arg, value); - dsl_dataset_rele(cbr->cbr_ds, FTAG); + dsl_dataset_rele(cbr->cbr_ds, FTAG); + } } mutex_exit(&dd->dd_lock); @@ -679,10 +767,10 @@ dsl_prop_set_sync_impl(dsl_dataset_t *ds, const char *propname, * ds here. */ mutex_enter(&ds->ds_dir->dd_lock); - for (cbr = list_head(&ds->ds_dir->dd_prop_cbs); cbr; - cbr = list_next(&ds->ds_dir->dd_prop_cbs, cbr)) { - if (cbr->cbr_ds == ds && - strcmp(cbr->cbr_propname, propname) == 0) + for (cbr = list_head(&ds->ds_prop_cbs); cbr; + cbr = list_next(&ds->ds_prop_cbs, cbr)) { + if (strcmp(cbr->cbr_pr->pr_propname, + propname) == 0) cbr->cbr_func(cbr->cbr_arg, intval); } mutex_exit(&ds->ds_dir->dd_lock); @@ -1158,6 +1246,7 @@ dsl_prop_nvlist_add_string(nvlist_t *nv, zfs_prop_t prop, const char *value) #if defined(_KERNEL) && defined(HAVE_SPL) EXPORT_SYMBOL(dsl_prop_register); EXPORT_SYMBOL(dsl_prop_unregister); +EXPORT_SYMBOL(dsl_prop_unregister_all); EXPORT_SYMBOL(dsl_prop_get); EXPORT_SYMBOL(dsl_prop_get_integer); EXPORT_SYMBOL(dsl_prop_get_all); diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c index bc4fa85fa..fc63ea569 100644 --- a/module/zfs/zfs_vfsops.c +++ b/module/zfs/zfs_vfsops.c @@ -341,38 +341,7 @@ zfs_register_callbacks(zfs_sb_t *zsb) return (0); unregister: - /* - * We may attempt to unregister some callbacks that are not - * registered, but this is OK; it will simply return ENOMSG, - * which we will ignore. - */ - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_ATIME), - atime_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_RELATIME), - relatime_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_XATTR), - xattr_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_RECORDSIZE), - blksz_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_READONLY), - readonly_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_DEVICES), - devices_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_SETUID), - setuid_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_EXEC), - exec_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_SNAPDIR), - snapdir_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_ACLTYPE), - acltype_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_ACLINHERIT), - acl_inherit_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_VSCAN), - vscan_changed_cb, zsb); - (void) dsl_prop_unregister(ds, zfs_prop_to_name(ZFS_PROP_NBMAND), - nbmand_changed_cb, zsb); - + dsl_prop_unregister_all(ds, zsb); return (error); } EXPORT_SYMBOL(zfs_register_callbacks); @@ -959,52 +928,9 @@ void zfs_unregister_callbacks(zfs_sb_t *zsb) { objset_t *os = zsb->z_os; - struct dsl_dataset *ds; - /* - * Unregister properties. - */ - if (!dmu_objset_is_snapshot(os)) { - ds = dmu_objset_ds(os); - VERIFY(dsl_prop_unregister(ds, "atime", atime_changed_cb, - zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "relatime", relatime_changed_cb, - zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "xattr", xattr_changed_cb, - zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "recordsize", blksz_changed_cb, - zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "readonly", readonly_changed_cb, - zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "devices", devices_changed_cb, - zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "setuid", setuid_changed_cb, - zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "exec", exec_changed_cb, - zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "snapdir", snapdir_changed_cb, - zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "acltype", acltype_changed_cb, - zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "aclinherit", - acl_inherit_changed_cb, zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "vscan", - vscan_changed_cb, zsb) == 0); - - VERIFY(dsl_prop_unregister(ds, "nbmand", - nbmand_changed_cb, zsb) == 0); - } + if (!dmu_objset_is_snapshot(os)) + dsl_prop_unregister_all(dmu_objset_ds(os), zsb); } EXPORT_SYMBOL(zfs_unregister_callbacks);