*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#include <sys/dmu_objset.h>
#include <sys/dsl_prop.h>
#include <sys/dsl_synctask.h>
#include <sys/dmu_traverse.h>
+#include <sys/dmu_impl.h>
#include <sys/dmu_tx.h>
#include <sys/arc.h>
#include <sys/zio.h>
ds = dmu_buf_get_user(dbuf);
if (ds == NULL) {
- dsl_dataset_t *winner;
+ dsl_dataset_t *winner = NULL;
- ds = kmem_zalloc(sizeof (dsl_dataset_t), KM_SLEEP);
+ ds = kmem_zalloc(sizeof (dsl_dataset_t), KM_PUSHPAGE);
ds->ds_dbuf = dbuf;
ds->ds_object = dsobj;
ds->ds_phys = dbuf->db_data;
+ list_link_init(&ds->ds_synced_link);
mutex_init(&ds->ds_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ds->ds_recvlock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ds->ds_opening_lock, NULL, MUTEX_DEFAULT, NULL);
- rw_init(&ds->ds_rwlock, 0, 0, 0);
+ mutex_init(&ds->ds_sendstream_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ rw_init(&ds->ds_rwlock, NULL, RW_DEFAULT, NULL);
cv_init(&ds->ds_exclusive_cv, NULL, CV_DEFAULT, NULL);
bplist_create(&ds->ds_pending_deadlist);
dsl_deadlist_open(&ds->ds_deadlist,
mos, ds->ds_phys->ds_deadlist_obj);
+ list_create(&ds->ds_sendstreams, sizeof (dmu_sendarg_t),
+ offsetof(dmu_sendarg_t, dsa_link));
+
if (err == 0) {
err = dsl_dir_open_obj(dp,
ds->ds_phys->ds_dir_obj, NULL, ds, &ds->ds_dir);
return (dsobj);
}
-struct destroyarg {
- dsl_sync_task_group_t *dstg;
- char *snapname;
- char *failed;
- boolean_t defer;
-};
-
-static int
-dsl_snapshot_destroy_one(const char *name, void *arg)
-{
- struct destroyarg *da = arg;
- dsl_dataset_t *ds;
- int err;
- char *dsname;
-
- dsname = kmem_asprintf("%s@%s", name, da->snapname);
- err = dsl_dataset_own(dsname, B_TRUE, da->dstg, &ds);
- strfree(dsname);
- if (err == 0) {
- struct dsl_ds_destroyarg *dsda;
-
- dsl_dataset_make_exclusive(ds, da->dstg);
- dsda = kmem_zalloc(sizeof (struct dsl_ds_destroyarg), KM_SLEEP);
- dsda->ds = ds;
- dsda->defer = da->defer;
- dsl_sync_task_create(da->dstg, dsl_dataset_destroy_check,
- dsl_dataset_destroy_sync, dsda, da->dstg, 0);
- } else if (err == ENOENT) {
- err = 0;
- } else {
- (void) strcpy(da->failed, name);
- }
- return (err);
-}
-
/*
- * Destroy 'snapname' in all descendants of 'fsname'.
+ * The snapshots must all be in the same pool.
*/
-#pragma weak dmu_snapshots_destroy = dsl_snapshots_destroy
int
-dsl_snapshots_destroy(char *fsname, char *snapname, boolean_t defer)
+dmu_snapshots_destroy_nvl(nvlist_t *snaps, boolean_t defer, char *failed)
{
int err;
- struct destroyarg da;
dsl_sync_task_t *dst;
spa_t *spa;
+ nvpair_t *pair;
+ dsl_sync_task_group_t *dstg;
- err = spa_open(fsname, &spa, FTAG);
+ pair = nvlist_next_nvpair(snaps, NULL);
+ if (pair == NULL)
+ return (0);
+
+ err = spa_open(nvpair_name(pair), &spa, FTAG);
if (err)
return (err);
- da.dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
- da.snapname = snapname;
- da.failed = fsname;
- da.defer = defer;
+ dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
+
+ for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
+ pair = nvlist_next_nvpair(snaps, pair)) {
+ dsl_dataset_t *ds;
+ int err;
- err = dmu_objset_find(fsname,
- dsl_snapshot_destroy_one, &da, DS_FIND_CHILDREN);
+ err = dsl_dataset_own(nvpair_name(pair), B_TRUE, dstg, &ds);
+ if (err == 0) {
+ struct dsl_ds_destroyarg *dsda;
+
+ dsl_dataset_make_exclusive(ds, dstg);
+ dsda = kmem_zalloc(sizeof (struct dsl_ds_destroyarg),
+ KM_SLEEP);
+ dsda->ds = ds;
+ dsda->defer = defer;
+ dsl_sync_task_create(dstg, dsl_dataset_destroy_check,
+ dsl_dataset_destroy_sync, dsda, dstg, 0);
+ } else if (err == ENOENT) {
+ err = 0;
+ } else {
+ (void) strcpy(failed, nvpair_name(pair));
+ break;
+ }
+ }
if (err == 0)
- err = dsl_sync_task_group_wait(da.dstg);
+ err = dsl_sync_task_group_wait(dstg);
- for (dst = list_head(&da.dstg->dstg_tasks); dst;
- dst = list_next(&da.dstg->dstg_tasks, dst)) {
+ for (dst = list_head(&dstg->dstg_tasks); dst;
+ dst = list_next(&dstg->dstg_tasks, dst)) {
struct dsl_ds_destroyarg *dsda = dst->dst_arg1;
dsl_dataset_t *ds = dsda->ds;
* Return the file system name that triggered the error
*/
if (dst->dst_err) {
- dsl_dataset_name(ds, fsname);
- *strchr(fsname, '@') = '\0';
+ dsl_dataset_name(ds, failed);
}
ASSERT3P(dsda->rm_origin, ==, NULL);
- dsl_dataset_disown(ds, da.dstg);
+ dsl_dataset_disown(ds, dstg);
kmem_free(dsda, sizeof (struct dsl_ds_destroyarg));
}
- dsl_sync_task_group_destroy(da.dstg);
+ dsl_sync_task_group_destroy(dstg);
spa_close(spa, FTAG);
return (err);
+
}
static boolean_t
dsl_dir_t *dd;
uint64_t obj;
struct dsl_ds_destroyarg dsda = { 0 };
- dsl_dataset_t dummy_ds = { 0 };
+ dsl_dataset_t *dummy_ds;
dsda.ds = ds;
}
dd = ds->ds_dir;
- dummy_ds.ds_dir = dd;
- dummy_ds.ds_object = ds->ds_object;
+ dummy_ds = kmem_zalloc(sizeof (dsl_dataset_t), KM_SLEEP);
+ dummy_ds->ds_dir = dd;
+ dummy_ds->ds_object = ds->ds_object;
/*
* Check for errors and mark this ds as inconsistent, in
err = dsl_sync_task_do(dd->dd_pool, dsl_dataset_destroy_begin_check,
dsl_dataset_destroy_begin_sync, ds, NULL, 0);
if (err)
- goto out;
+ goto out_free;
err = dmu_objset_from_ds(ds, &os);
if (err)
- goto out;
+ goto out_free;
/*
* remove the objects in open context, so that we won't
(void) dmu_free_object(os, obj);
}
if (err != ESRCH)
- goto out;
+ goto out_free;
/*
* Only the ZIL knows how to free log blocks.
rw_exit(&dd->dd_pool->dp_config_rwlock);
if (err)
- goto out;
+ goto out_free;
/*
* Blow away the dsl_dir + head dataset.
err = dsl_dataset_origin_rm_prep(&dsda, tag);
if (err) {
dsl_dir_close(dd, FTAG);
- goto out;
+ goto out_free;
}
}
dsl_sync_task_create(dstg, dsl_dataset_destroy_check,
dsl_dataset_destroy_sync, &dsda, tag, 0);
dsl_sync_task_create(dstg, dsl_dir_destroy_check,
- dsl_dir_destroy_sync, &dummy_ds, FTAG, 0);
+ dsl_dir_destroy_sync, dummy_ds, FTAG, 0);
err = dsl_sync_task_group_wait(dstg);
dsl_sync_task_group_destroy(dstg);
/* if it is successful, dsl_dir_destroy_sync will close the dd */
if (err)
dsl_dir_close(dd, FTAG);
+
+out_free:
+ kmem_free(dummy_ds, sizeof (dsl_dataset_t));
out:
dsl_dataset_disown(ds, tag);
return (err);
}
}
+boolean_t
+dsl_dataset_is_dirty(dsl_dataset_t *ds)
+{
+ int t;
+
+ for (t = 0; t < TXG_SIZE; t++) {
+ if (txg_list_member(&ds->ds_dir->dd_pool->dp_dirty_datasets,
+ ds, t))
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
/*
* The unique space in the head dataset can be calculated by subtracting
* the space used in the most recent snapshot, that is still being used
dmu_objset_sync(ds->ds_objset, zio, tx);
}
+static void
+get_clones_stat(dsl_dataset_t *ds, nvlist_t *nv)
+{
+ uint64_t count = 0;
+ objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
+ zap_cursor_t zc;
+ zap_attribute_t za;
+ nvlist_t *propval;
+ nvlist_t *val;
+
+ rw_enter(&ds->ds_dir->dd_pool->dp_config_rwlock, RW_READER);
+ VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+ VERIFY(nvlist_alloc(&val, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+
+ /*
+ * There may me missing entries in ds_next_clones_obj
+ * due to a bug in a previous version of the code.
+ * Only trust it if it has the right number of entries.
+ */
+ if (ds->ds_phys->ds_next_clones_obj != 0) {
+ ASSERT3U(0, ==, zap_count(mos, ds->ds_phys->ds_next_clones_obj,
+ &count));
+ }
+ if (count != ds->ds_phys->ds_num_children - 1) {
+ goto fail;
+ }
+ for (zap_cursor_init(&zc, mos, ds->ds_phys->ds_next_clones_obj);
+ zap_cursor_retrieve(&zc, &za) == 0;
+ zap_cursor_advance(&zc)) {
+ dsl_dataset_t *clone;
+ char buf[ZFS_MAXNAMELEN];
+ if (dsl_dataset_hold_obj(ds->ds_dir->dd_pool,
+ za.za_first_integer, FTAG, &clone) != 0) {
+ goto fail;
+ }
+ dsl_dir_name(clone->ds_dir, buf);
+ VERIFY(nvlist_add_boolean(val, buf) == 0);
+ dsl_dataset_rele(clone, FTAG);
+ }
+ zap_cursor_fini(&zc);
+ VERIFY(nvlist_add_nvlist(propval, ZPROP_VALUE, val) == 0);
+ VERIFY(nvlist_add_nvlist(nv, zfs_prop_to_name(ZFS_PROP_CLONES),
+ propval) == 0);
+fail:
+ nvlist_free(val);
+ nvlist_free(propval);
+ rw_exit(&ds->ds_dir->dd_pool->dp_config_rwlock);
+}
+
void
dsl_dataset_stats(dsl_dataset_t *ds, nvlist_t *nv)
{
- uint64_t refd, avail, uobjs, aobjs;
+ uint64_t refd, avail, uobjs, aobjs, ratio;
dsl_dir_stats(ds->ds_dir, nv);
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_DEFER_DESTROY,
DS_IS_DEFER_DESTROY(ds) ? 1 : 0);
+ if (ds->ds_phys->ds_prev_snap_obj != 0) {
+ uint64_t written, comp, uncomp;
+ dsl_pool_t *dp = ds->ds_dir->dd_pool;
+ dsl_dataset_t *prev;
+ int err;
+
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ err = dsl_dataset_hold_obj(dp,
+ ds->ds_phys->ds_prev_snap_obj, FTAG, &prev);
+ rw_exit(&dp->dp_config_rwlock);
+ if (err == 0) {
+ err = dsl_dataset_space_written(prev, ds, &written,
+ &comp, &uncomp);
+ dsl_dataset_rele(prev, FTAG);
+ if (err == 0) {
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_WRITTEN,
+ written);
+ }
+ }
+ }
+
+ ratio = ds->ds_phys->ds_compressed_bytes == 0 ? 100 :
+ (ds->ds_phys->ds_uncompressed_bytes * 100 /
+ ds->ds_phys->ds_compressed_bytes);
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_REFRATIO, ratio);
+
if (ds->ds_phys->ds_next_snap_obj) {
/*
* This is a snapshot; override the dd's space used with
*/
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_USED,
ds->ds_phys->ds_unique_bytes);
- dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO,
- ds->ds_phys->ds_compressed_bytes == 0 ? 100 :
- (ds->ds_phys->ds_uncompressed_bytes * 100 /
- ds->ds_phys->ds_compressed_bytes));
+ dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_COMPRESSRATIO, ratio);
+
+ get_clones_stat(ds, nv);
}
}
error = ERESTART;
else
error = EDQUOT;
+
+ DMU_TX_STAT_BUMP(dmu_tx_quota);
}
mutex_exit(&ds->ds_lock);
if (ds->ds_quota != effective_value) {
dmu_buf_will_dirty(ds->ds_dbuf, tx);
ds->ds_quota = effective_value;
-
- spa_history_log_internal(LOG_DS_REFQUOTA,
- ds->ds_dir->dd_pool->dp_spa, tx, "%lld dataset = %llu ",
- (longlong_t)ds->ds_quota, ds->ds_object);
}
}
dsl_dir_diduse_space(ds->ds_dir, DD_USED_REFRSRV, delta, 0, 0, tx);
mutex_exit(&ds->ds_dir->dd_lock);
-
- spa_history_log_internal(LOG_DS_REFRESERV,
- ds->ds_dir->dd_pool->dp_spa, tx, "%lld dataset = %llu",
- (longlong_t)effective_value, ds->ds_object);
}
int
VERIFY(error == 0 || error == ENOENT);
zapobj = ds->ds_phys->ds_userrefs_obj;
VERIFY(0 == zap_remove(mos, zapobj, ra->htag, tx));
+
+ spa_history_log_internal(LOG_DS_USER_RELEASE,
+ dp->dp_spa, tx, "<%s> %lld dataset = %llu",
+ ra->htag, (longlong_t)refs, dsobj);
+
if (ds->ds_userrefs == 0 && ds->ds_phys->ds_num_children == 1 &&
DS_IS_DEFER_DESTROY(ds)) {
struct dsl_ds_destroyarg dsda = {0};
/* We already did the destroy_check */
dsl_dataset_destroy_sync(&dsda, tag, tx);
}
-
- spa_history_log_internal(LOG_DS_USER_RELEASE,
- dp->dp_spa, tx, "<%s> %lld dataset = %llu",
- ra->htag, (longlong_t)refs, dsobj);
}
static int
}
/*
- * Note, this fuction is used as the callback for dmu_objset_find(). We
+ * Note, this function is used as the callback for dmu_objset_find(). We
* always return 0 so that we will continue to find and process
* inconsistent datasets, even if we encounter an error trying to
* process one of them.
}
return (0);
}
+
+
+/*
+ * Return (in *usedp) the amount of space written in new that is not
+ * present in oldsnap. New may be a snapshot or the head. Old must be
+ * a snapshot before new, in new's filesystem (or its origin). If not then
+ * fail and return EINVAL.
+ *
+ * The written space is calculated by considering two components: First, we
+ * ignore any freed space, and calculate the written as new's used space
+ * minus old's used space. Next, we add in the amount of space that was freed
+ * between the two snapshots, thus reducing new's used space relative to old's.
+ * Specifically, this is the space that was born before old->ds_creation_txg,
+ * and freed before new (ie. on new's deadlist or a previous deadlist).
+ *
+ * space freed [---------------------]
+ * snapshots ---O-------O--------O-------O------
+ * oldsnap new
+ */
+int
+dsl_dataset_space_written(dsl_dataset_t *oldsnap, dsl_dataset_t *new,
+ uint64_t *usedp, uint64_t *compp, uint64_t *uncompp)
+{
+ int err = 0;
+ uint64_t snapobj;
+ dsl_pool_t *dp = new->ds_dir->dd_pool;
+
+ *usedp = 0;
+ *usedp += new->ds_phys->ds_used_bytes;
+ *usedp -= oldsnap->ds_phys->ds_used_bytes;
+
+ *compp = 0;
+ *compp += new->ds_phys->ds_compressed_bytes;
+ *compp -= oldsnap->ds_phys->ds_compressed_bytes;
+
+ *uncompp = 0;
+ *uncompp += new->ds_phys->ds_uncompressed_bytes;
+ *uncompp -= oldsnap->ds_phys->ds_uncompressed_bytes;
+
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ snapobj = new->ds_object;
+ while (snapobj != oldsnap->ds_object) {
+ dsl_dataset_t *snap;
+ uint64_t used, comp, uncomp;
+
+ err = dsl_dataset_hold_obj(dp, snapobj, FTAG, &snap);
+ if (err != 0)
+ break;
+
+ if (snap->ds_phys->ds_prev_snap_txg ==
+ oldsnap->ds_phys->ds_creation_txg) {
+ /*
+ * The blocks in the deadlist can not be born after
+ * ds_prev_snap_txg, so get the whole deadlist space,
+ * which is more efficient (especially for old-format
+ * deadlists). Unfortunately the deadlist code
+ * doesn't have enough information to make this
+ * optimization itself.
+ */
+ dsl_deadlist_space(&snap->ds_deadlist,
+ &used, &comp, &uncomp);
+ } else {
+ dsl_deadlist_space_range(&snap->ds_deadlist,
+ 0, oldsnap->ds_phys->ds_creation_txg,
+ &used, &comp, &uncomp);
+ }
+ *usedp += used;
+ *compp += comp;
+ *uncompp += uncomp;
+
+ /*
+ * If we get to the beginning of the chain of snapshots
+ * (ds_prev_snap_obj == 0) before oldsnap, then oldsnap
+ * was not a snapshot of/before new.
+ */
+ snapobj = snap->ds_phys->ds_prev_snap_obj;
+ dsl_dataset_rele(snap, FTAG);
+ if (snapobj == 0) {
+ err = EINVAL;
+ break;
+ }
+
+ }
+ rw_exit(&dp->dp_config_rwlock);
+ return (err);
+}
+
+/*
+ * Return (in *usedp) the amount of space that will be reclaimed if firstsnap,
+ * lastsnap, and all snapshots in between are deleted.
+ *
+ * blocks that would be freed [---------------------------]
+ * snapshots ---O-------O--------O-------O--------O
+ * firstsnap lastsnap
+ *
+ * This is the set of blocks that were born after the snap before firstsnap,
+ * (birth > firstsnap->prev_snap_txg) and died before the snap after the
+ * last snap (ie, is on lastsnap->ds_next->ds_deadlist or an earlier deadlist).
+ * We calculate this by iterating over the relevant deadlists (from the snap
+ * after lastsnap, backward to the snap after firstsnap), summing up the
+ * space on the deadlist that was born after the snap before firstsnap.
+ */
+int
+dsl_dataset_space_wouldfree(dsl_dataset_t *firstsnap,
+ dsl_dataset_t *lastsnap,
+ uint64_t *usedp, uint64_t *compp, uint64_t *uncompp)
+{
+ int err = 0;
+ uint64_t snapobj;
+ dsl_pool_t *dp = firstsnap->ds_dir->dd_pool;
+
+ ASSERT(dsl_dataset_is_snapshot(firstsnap));
+ ASSERT(dsl_dataset_is_snapshot(lastsnap));
+
+ /*
+ * Check that the snapshots are in the same dsl_dir, and firstsnap
+ * is before lastsnap.
+ */
+ if (firstsnap->ds_dir != lastsnap->ds_dir ||
+ firstsnap->ds_phys->ds_creation_txg >
+ lastsnap->ds_phys->ds_creation_txg)
+ return (EINVAL);
+
+ *usedp = *compp = *uncompp = 0;
+
+ rw_enter(&dp->dp_config_rwlock, RW_READER);
+ snapobj = lastsnap->ds_phys->ds_next_snap_obj;
+ while (snapobj != firstsnap->ds_object) {
+ dsl_dataset_t *ds;
+ uint64_t used, comp, uncomp;
+
+ err = dsl_dataset_hold_obj(dp, snapobj, FTAG, &ds);
+ if (err != 0)
+ break;
+
+ dsl_deadlist_space_range(&ds->ds_deadlist,
+ firstsnap->ds_phys->ds_prev_snap_txg, UINT64_MAX,
+ &used, &comp, &uncomp);
+ *usedp += used;
+ *compp += comp;
+ *uncompp += uncomp;
+
+ snapobj = ds->ds_phys->ds_prev_snap_obj;
+ ASSERT3U(snapobj, !=, 0);
+ dsl_dataset_rele(ds, FTAG);
+ }
+ rw_exit(&dp->dp_config_rwlock);
+ return (err);
+}
+
+#if defined(_KERNEL) && defined(HAVE_SPL)
+EXPORT_SYMBOL(dmu_snapshots_destroy_nvl);
+EXPORT_SYMBOL(dsl_dataset_hold);
+EXPORT_SYMBOL(dsl_dataset_hold_obj);
+EXPORT_SYMBOL(dsl_dataset_own);
+EXPORT_SYMBOL(dsl_dataset_own_obj);
+EXPORT_SYMBOL(dsl_dataset_name);
+EXPORT_SYMBOL(dsl_dataset_rele);
+EXPORT_SYMBOL(dsl_dataset_disown);
+EXPORT_SYMBOL(dsl_dataset_drop_ref);
+EXPORT_SYMBOL(dsl_dataset_tryown);
+EXPORT_SYMBOL(dsl_dataset_make_exclusive);
+EXPORT_SYMBOL(dsl_dataset_create_sync);
+EXPORT_SYMBOL(dsl_dataset_create_sync_dd);
+EXPORT_SYMBOL(dsl_dataset_destroy);
+EXPORT_SYMBOL(dsl_dataset_destroy_check);
+EXPORT_SYMBOL(dsl_dataset_destroy_sync);
+EXPORT_SYMBOL(dsl_dataset_snapshot_check);
+EXPORT_SYMBOL(dsl_dataset_snapshot_sync);
+EXPORT_SYMBOL(dsl_dataset_rename);
+EXPORT_SYMBOL(dsl_dataset_promote);
+EXPORT_SYMBOL(dsl_dataset_clone_swap);
+EXPORT_SYMBOL(dsl_dataset_user_hold);
+EXPORT_SYMBOL(dsl_dataset_user_release);
+EXPORT_SYMBOL(dsl_dataset_user_release_tmp);
+EXPORT_SYMBOL(dsl_dataset_get_holds);
+EXPORT_SYMBOL(dsl_dataset_get_blkptr);
+EXPORT_SYMBOL(dsl_dataset_set_blkptr);
+EXPORT_SYMBOL(dsl_dataset_get_spa);
+EXPORT_SYMBOL(dsl_dataset_modified_since_lastsnap);
+EXPORT_SYMBOL(dsl_dataset_space_written);
+EXPORT_SYMBOL(dsl_dataset_space_wouldfree);
+EXPORT_SYMBOL(dsl_dataset_sync);
+EXPORT_SYMBOL(dsl_dataset_block_born);
+EXPORT_SYMBOL(dsl_dataset_block_kill);
+EXPORT_SYMBOL(dsl_dataset_block_freeable);
+EXPORT_SYMBOL(dsl_dataset_prev_snap_txg);
+EXPORT_SYMBOL(dsl_dataset_dirty);
+EXPORT_SYMBOL(dsl_dataset_stats);
+EXPORT_SYMBOL(dsl_dataset_fast_stat);
+EXPORT_SYMBOL(dsl_dataset_space);
+EXPORT_SYMBOL(dsl_dataset_fsid_guid);
+EXPORT_SYMBOL(dsl_dsobj_to_dsname);
+EXPORT_SYMBOL(dsl_dataset_check_quota);
+EXPORT_SYMBOL(dsl_dataset_set_quota);
+EXPORT_SYMBOL(dsl_dataset_set_quota_sync);
+EXPORT_SYMBOL(dsl_dataset_set_reservation);
+EXPORT_SYMBOL(dsl_destroy_inconsistent);
+#endif