Print this page
NEX-16623 Ability to set properties for multiple datasets/snapshots during single sync-round
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-7822 40Gb Intel XL710 NIC performance data
Reviewed by: Steve Peng <steve.peng@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-8827 AUTOSNAP: can't register recursive zone when there is a child under autosnappool/volumegrou
Reviewed by: Alex Deiter <alex.deiter@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-5987 need to change ssm wearout threshold to 90%
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-5925 KRRP: Incorrect collecting of snap-props causes errors during recv
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alexey Komarov <alexey.komarov@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-5795 Rename 'wrc' as 'wbc' in the source and in the tech docs
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
NEX-5341 Race condition causes wrc_004_pos to panic the system
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-5319 Panic trying to rename zvol used as COMSTAR lu
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
NEX-5060 WBC: Writecache and deduplication should not be used together
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-4934 Add capability to remove special vdev
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
NEX-4830 writecache=off leaks data on special vdev (the data will never migrate)
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
NEX-4608 WRC: Possible deadlock during the disabling of WRC for a dataset
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
6160 /usr/lib/fs/zfs/bootinstall should use bootadm
Reviewed by: Igor Kozhukhov <ikozhukhov@gmail.com>
Reviewed by: Adam Števko <adam.stevko@gmail.com>
Reviewed by: Josef Sipek <jeffpc@josefsipek.net>
Approved by: Richard Lowe <richlowe@richlowe.net>
4185 add new cryptographic checksums to ZFS: SHA-512, Skein, Edon-R (NULL is not an int)
6171 dsl_prop_unregister() slows down dataset eviction.
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Prakash Surya <prakash.surya@delphix.com>
Approved by: Dan McDonald <danmcd@omniti.com>
NEX-4476 WRC: Allow to use write back cache per tree of datasets
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Revert "NEX-4476 WRC: Allow to use write back cache per tree of datasets"
This reverts commit fe97b74444278a6f36fec93179133641296312da.
NEX-4476 WRC: Allow to use write back cache per tree of datasets
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Alex Aizman <alex.aizman@nexenta.com>
Remaining fixes for the illumos merge
re #12619 rb4429 More dp->dp_config_rwlock holds
re #13253 rb4328 ssh: openssl version checking needs updating
re #11441 rb4292 panic in apic_record_rdt_entry on VMware hardware version 9
re #12619, rb4287 Deadlocked zfs txg processing in dsl_sync_task_group_sync()

@@ -21,10 +21,11 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2012, 2015 by Delphix. All rights reserved.
  * Copyright (c) 2013 Martin Matuska. All rights reserved.
  * Copyright 2015, Joyent, Inc.
+ * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <sys/zfs_context.h>
 #include <sys/dmu.h>
 #include <sys/dmu_objset.h>

@@ -31,19 +32,19 @@
 #include <sys/dmu_tx.h>
 #include <sys/dsl_dataset.h>
 #include <sys/dsl_dir.h>
 #include <sys/dsl_prop.h>
 #include <sys/dsl_synctask.h>
+#include <sys/zfeature.h>
 #include <sys/spa.h>
 #include <sys/zap.h>
 #include <sys/fs/zfs.h>
+#include <sys/wbc.h>
 
 #include "zfs_prop.h"
+#include "zfs_errno.h"
 
-#define ZPROP_INHERIT_SUFFIX "$inherit"
-#define ZPROP_RECVD_SUFFIX "$recvd"
-
 static int
 dodefault(zfs_prop_t prop, int intsz, int numints, void *buf)
 {
         /*
          * The setonce properties are read-only, BUT they still

@@ -285,11 +286,20 @@
         dsl_prop_cb_record_t *cbr;
         int err;
 
         ASSERT(dsl_pool_config_held(dp));
 
+        if (zfs_name_to_prop(propname) == ZFS_PROP_WBC_MODE) {
+                wbc_mode_prop_val_t val;
+
+                err = dsl_prop_get_ds(ds, propname, 8,
+                    WBC_MODE_PROP_VAL_SZ, &val, NULL);
+                if (err == 0)
+                        value = (uintptr_t)((void *)&val);
+        } else
         err = dsl_prop_get_int_ds(ds, propname, &value);
+
         if (err != 0)
                 return (err);
 
         cbr = kmem_alloc(sizeof (dsl_prop_cb_record_t), KM_SLEEP);
         cbr->cbr_ds = ds;

@@ -475,10 +485,11 @@
         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;
+                        const char *propname;
 
                         /*
                          * Callback entries do not have holds on their
                          * datasets so that datasets with registered
                          * callbacks are still eligible for eviction.

@@ -502,14 +513,23 @@
                          */
                         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_pr->pr_propname, sizeof (value), 1,
-                            &value, NULL) == 0)
+                        propname = cbr->cbr_pr->pr_propname;
+                        if (zfs_name_to_prop(propname) == ZFS_PROP_WBC_MODE) {
+                                wbc_mode_prop_val_t val;
+
+                                if (dsl_prop_get_ds(cbr->cbr_ds, propname, 8,
+                                    WBC_MODE_PROP_VAL_SZ, &val, NULL) == 0) {
+                                        value = (uintptr_t)((void *)&val);
                                 cbr->cbr_func(cbr->cbr_arg, value);
+                                }
+                        } else if (dsl_prop_get_ds(cbr->cbr_ds, propname,
+                            sizeof (value), 1, &value, NULL) == 0) {
+                                cbr->cbr_func(cbr->cbr_arg, value);
+                        }
 
                         if (ds != cbr->cbr_ds)
                                 dsl_dataset_rele(cbr->cbr_ds, FTAG);
                 }
         }

@@ -618,11 +638,12 @@
         const char *valstr = NULL;
         char *inheritstr;
         char *recvdstr;
         char *tbuf = NULL;
         int err;
-        uint64_t version = spa_version(ds->ds_dir->dd_pool->dp_spa);
+        spa_t *spa = ds->ds_dir->dd_pool->dp_spa;
+        uint64_t version = spa_version(spa);
 
         isint = (dodefault(zfs_name_to_prop(propname), 8, 1, &intval) == 0);
 
         if (ds->ds_is_snapshot) {
                 ASSERT(version >= SPA_VERSION_SNAP_PROPS);

@@ -716,11 +737,51 @@
         }
 
         strfree(inheritstr);
         strfree(recvdstr);
 
-        if (isint) {
+        if (zfs_name_to_prop(propname) == ZFS_PROP_WBC_MODE) {
+                wbc_mode_prop_val_t val;
+
+                VERIFY0(dsl_prop_get_ds(ds, propname, 8,
+                    WBC_MODE_PROP_VAL_SZ, &val, NULL));
+
+                dsl_prop_changed_notify(ds->ds_dir->dd_pool,
+                    ds->ds_dir->dd_object, propname,
+                    (uintptr_t)((void *)&val), TRUE);
+
+                /*
+                 * Flow diagram of ZFS_PROP_WBC_MODE states
+                 *
+                 * off (root_ds_object == 0, txg_off == 0)
+                 *      (user sees wbc_mode=off, source=default)
+                 *
+                 *      user operation "set on" ==>>
+                 *
+                 * on (root_ds_object != 0, txg_off == 0)
+                 *      (user sees wbc_mode=on, source=local)
+                 *
+                 *      user operation "set off" ==>>
+                 *
+                 * off_delayed (root_ds_object != 0, txg_off != 0)
+                 *      (user sees wbc_mode=off, source=local)
+                 *
+                 *      internal operation "inherit" ==>>
+                 *
+                 * off (root_ds_object == 0, txg_off == 0)
+                 *      (user sees wbc_mode=off, source=default)
+                 */
+                if (val.root_ds_object == 0)
+                        spa_feature_decr(spa, SPA_FEATURE_WBC, tx);
+                else if (val.txg_off == 0)
+                        spa_feature_incr(spa, SPA_FEATURE_WBC, tx);
+
+                (void) snprintf(valbuf, sizeof (valbuf),
+                    "%s", (val.root_ds_object != 0 &&
+                    val.txg_off == 0) ? "on" : "off");
+                valstr = valbuf;
+        } else if (isint) {
                 VERIFY0(dsl_prop_get_int_ds(ds, propname, &intval));
 
                 if (ds->ds_is_snapshot) {
                         dsl_prop_cb_record_t *cbr;
                         /*

@@ -800,10 +861,79 @@
         error = dsl_props_set(dsname, source, nvl);
         fnvlist_free(nvl);
         return (error);
 }
 
+/* ARGSUSED */
+static int
+dsl_prop_wbc_mode_check_child_cb(dsl_pool_t *dp,
+    dsl_dataset_t *ds, void *arg)
+{
+        int err;
+        zfs_prop_t *prop = arg;
+        objset_t *os = NULL;
+
+        err = dmu_objset_from_ds(ds, &os);
+        if (err != 0)
+                return (err);
+
+        if (*prop == ZFS_PROP_DEDUP) {
+                /*
+                 * User tries to set ZFS_PROP_DEDUP.
+                 * In this case we just check that
+                 * the target DS and its children
+                 * do not use writecache
+                 */
+                if (os->os_wbc_mode != ZFS_WBC_MODE_OFF)
+                        return (SET_ERROR(EKZFS_WBCCONFLICT));
+        } else {
+                ASSERT3U(*prop, ==, ZFS_PROP_WBC_MODE);
+
+                /*
+                 * User tries to set ZFS_PROP_WBC_MODE.
+                 * In this case we need check that
+                 * the target DS and its children
+                 * do not use writecache and dedup
+                 */
+                if (os->os_wbc_mode != ZFS_WBC_MODE_OFF)
+                        return (SET_ERROR(EKZFS_WBCCHILD));
+
+                if (os->os_dedup_checksum != ZIO_CHECKSUM_OFF)
+                        return (SET_ERROR(EKZFS_WBCCONFLICT));
+        }
+
+        return (0);
+}
+
+static int
+dsl_prop_wbc_mode_check(dsl_dataset_t *ds, objset_t *os)
+{
+        int err = 0;
+        zfs_prop_t prop = ZFS_PROP_WBC_MODE;
+
+        if (os->os_wbc_mode != ZFS_WBC_MODE_OFF) {
+                /*
+                 * ZFS_PROP_WBC_MODE cannot be set for
+                 * a child DS if the prop was set for
+                 * the parent
+                 */
+                if (os->os_wbc_root_ds_obj != ds->ds_object)
+                        return (SET_ERROR(EKZFS_WBCPARENT));
+        } else {
+                /*
+                 * Is not allowed to change wbc_mode for parent
+                 * if its children already have the changed prop
+                 */
+                err = dmu_objset_find_dp(ds->ds_dir->dd_pool,
+                    ds->ds_dir->dd_object,
+                    dsl_prop_wbc_mode_check_child_cb, &prop,
+                    DS_FIND_CHILDREN);
+        }
+
+        return (err);
+}
+
 typedef struct dsl_props_set_arg {
         const char *dpsa_dsname;
         zprop_source_t dpsa_source;
         nvlist_t *dpsa_props;
 } dsl_props_set_arg_t;

@@ -812,33 +942,120 @@
 dsl_props_set_check(void *arg, dmu_tx_t *tx)
 {
         dsl_props_set_arg_t *dpsa = arg;
         dsl_pool_t *dp = dmu_tx_pool(tx);
         dsl_dataset_t *ds;
+        objset_t *os = NULL;
         uint64_t version;
         nvpair_t *elem = NULL;
         int err;
 
         err = dsl_dataset_hold(dp, dpsa->dpsa_dsname, FTAG, &ds);
         if (err != 0)
                 return (err);
 
+        err = dmu_objset_from_ds(ds, &os);
+        if (err != 0) {
+                dsl_dataset_rele(ds, FTAG);
+                return (err);
+        }
+
         version = spa_version(ds->ds_dir->dd_pool->dp_spa);
         while ((elem = nvlist_next_nvpair(dpsa->dpsa_props, elem)) != NULL) {
-                if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
+                const char *prop_name = nvpair_name(elem);
+                zfs_prop_t prop = zfs_name_to_prop(prop_name);
+
+                if (strlen(prop_name) >= ZAP_MAXNAMELEN) {
                         dsl_dataset_rele(ds, FTAG);
                         return (SET_ERROR(ENAMETOOLONG));
                 }
+
+                /*
+                 * Deduplication and WBC cannot be used together
+                 * This code returns error also for case when
+                 * WBC is ON, DEDUP is off and a user tries
+                 * to do DEDUP=off, because in this case the code
+                 * will be more complex, but benefit is too small
+                 */
+                if (prop == ZFS_PROP_DEDUP) {
+                        if (os->os_wbc_root_ds_obj != 0) {
+                                dsl_dataset_rele(ds, FTAG);
+                                return (SET_ERROR(EKZFS_WBCCONFLICT));
+                        }
+
+                        /*
+                         * Need to be sure that children DS
+                         * do not use writecache
+                         */
+                        err = dmu_objset_find_dp(ds->ds_dir->dd_pool,
+                            ds->ds_dir->dd_object,
+                            dsl_prop_wbc_mode_check_child_cb, &prop,
+                            DS_FIND_CHILDREN);
+                        if (err != 0) {
+                                dsl_dataset_rele(ds, FTAG);
+                                return (err);
+                        }
+                }
+
+                if (prop == ZFS_PROP_WBC_MODE) {
+                        uint64_t wbc_mode_new = 0;
+                        data_type_t elem_type = nvpair_type(elem);
+
+                        if (elem_type == DATA_TYPE_UINT64)
+                                wbc_mode_new = fnvpair_value_uint64(elem);
+
+                        if (os->os_wbc_root_ds_obj == ds->ds_object &&
+                            elem_type == DATA_TYPE_UINT64) {
+
+                                /*
+                                 * ZFS_WBC_MODE_OFF_DELAYED means that
+                                 * the coresponding wbc_instance is in
+                                 * transition state: from ON to OFF
+                                 * so ON/OFF is not permitted
+                                 */
+                                if (os->os_wbc_mode ==
+                                    ZFS_WBC_MODE_OFF_DELAYED) {
+                                        dsl_dataset_rele(ds, FTAG);
+                                        return (SET_ERROR(EINPROGRESS));
+                                }
+
+                                ASSERT3U(os->os_wbc_mode, ==, ZFS_WBC_MODE_ON);
+
+                                /* Check for double ON */
+                                if (wbc_mode_new == ZFS_WBC_MODE_ON) {
+                                        dsl_dataset_rele(ds, FTAG);
+                                        return (SET_ERROR(EALREADY));
+                                }
+                        }
+
+                        err = dsl_prop_wbc_mode_check(ds, os);
+                        if (err != 0) {
+                                dsl_dataset_rele(ds, FTAG);
+                                return (err);
+                        }
+
+                        /* Check for double OFF */
+                        if (os->os_wbc_root_ds_obj == 0 &&
+                            elem_type == DATA_TYPE_UINT64 &&
+                            wbc_mode_new == ZFS_WBC_MODE_OFF) {
+                                dsl_dataset_rele(ds, FTAG);
+                                return (SET_ERROR(EALREADY));
+                        }
+
+                        continue;
+                }
+
                 if (nvpair_type(elem) == DATA_TYPE_STRING) {
                         char *valstr = fnvpair_value_string(elem);
                         if (strlen(valstr) >= (version <
                             SPA_VERSION_STMF_PROP ?
                             ZAP_OLDMAXVALUELEN : ZAP_MAXVALUELEN)) {
                                 dsl_dataset_rele(ds, FTAG);
-                                return (E2BIG);
+                                return (SET_ERROR(E2BIG));
                         }
                 }
+
         }
 
         if (ds->ds_is_snapshot && version < SPA_VERSION_SNAP_PROPS) {
                 dsl_dataset_rele(ds, FTAG);
                 return (SET_ERROR(ENOTSUP));

@@ -853,34 +1070,56 @@
 {
         nvpair_t *elem = NULL;
 
         while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
                 nvpair_t *pair = elem;
-                const char *name = nvpair_name(pair);
 
                 if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
                         /*
-                         * This usually happens when we reuse the nvlist_t data
-                         * returned by the counterpart dsl_prop_get_all_impl().
-                         * For instance we do this to restore the original
-                         * received properties when an error occurs in the
-                         * zfs_ioc_recv() codepath.
+                         * dsl_prop_get_all_impl() returns properties in this
+                         * format.
                          */
                         nvlist_t *attrs = fnvpair_value_nvlist(pair);
                         pair = fnvlist_lookup_nvpair(attrs, ZPROP_VALUE);
                 }
 
                 if (nvpair_type(pair) == DATA_TYPE_STRING) {
                         const char *value = fnvpair_value_string(pair);
-                        dsl_prop_set_sync_impl(ds, name,
+                        dsl_prop_set_sync_impl(ds, nvpair_name(pair),
                             source, 1, strlen(value) + 1, value, tx);
                 } else if (nvpair_type(pair) == DATA_TYPE_UINT64) {
+                        const char *propname = nvpair_name(pair);
+                        zfs_prop_t prop = zfs_name_to_prop(propname);
                         uint64_t intval = fnvpair_value_uint64(pair);
-                        dsl_prop_set_sync_impl(ds, name,
+
+                        if (prop == ZFS_PROP_WBC_MODE) {
+                                wbc_mode_prop_val_t val;
+
+                                /*
+                                 * Disabling WBC involves the following:
+                                 * 1) all the subsequent data writes will
+                                 * stop using special vdev
+                                 * 2) already WriteBackCached data blocks
+                                 * (with TXG <= txg_off below) will gradually
+                                 * migrated from special vdev
+                                 *
+                                 * To handle this need to remember TXG.
+                                 * WBC will be completely disabled for this DS,
+                                 * after WBC-window cross this TXG
+                                 */
+                                val.txg_off = (intval == 0) ? tx->tx_txg : 0;
+                                val.root_ds_object = ds->ds_object;
+                                val.flags = 0;
+
+                                dsl_prop_set_sync_impl(ds, nvpair_name(pair),
+                                    source, 8, WBC_MODE_PROP_VAL_SZ, &val, tx);
+                        } else {
+                                dsl_prop_set_sync_impl(ds, nvpair_name(pair),
                             source, sizeof (intval), 1, &intval, tx);
+                        }
                 } else if (nvpair_type(pair) == DATA_TYPE_BOOLEAN) {
-                        dsl_prop_set_sync_impl(ds, name,
+                        dsl_prop_set_sync_impl(ds, nvpair_name(pair),
                             source, 0, 0, NULL, tx);
                 } else {
                         panic("invalid nvpair type");
                 }
         }

@@ -890,12 +1129,23 @@
 dsl_props_set_sync(void *arg, dmu_tx_t *tx)
 {
         dsl_props_set_arg_t *dpsa = arg;
         dsl_pool_t *dp = dmu_tx_pool(tx);
         dsl_dataset_t *ds;
+        objset_t *os = NULL;
 
         VERIFY0(dsl_dataset_hold(dp, dpsa->dpsa_dsname, FTAG, &ds));
+        /*
+         * Need to initialize os, to be sure that non-mounted datasets and
+         * non-exposed zvols will receive notification about modified
+         * properties.
+         * During the initialization a property can register its callback
+         * that will be called if the property is changed.
+         * dsl_props_set_sync_impl() calls dsl_prop_changed_notify()
+         * that calls the required callback if it exists.
+         */
+        VERIFY0(dmu_objset_from_ds(ds, &os));
         dsl_props_set_sync_impl(ds, dpsa->dpsa_source, dpsa->dpsa_props, tx);
         dsl_dataset_rele(ds, FTAG);
 }
 
 /*

@@ -920,10 +1170,97 @@
 
         return (dsl_sync_task(dsname, dsl_props_set_check, dsl_props_set_sync,
             &dpsa, nblks, ZFS_SPACE_CHECK_RESERVED));
 }
 
+static void
+dsl_props_mds_set_sync(void *arg, dmu_tx_t *tx)
+{
+        dsl_props_set_arg_t *top_dpsa = arg;
+        dsl_pool_t *dp = dmu_tx_pool(tx);
+        nvlist_t *dss_props = top_dpsa->dpsa_props;
+        nvpair_t *pair = NULL;
+
+        while ((pair = nvlist_next_nvpair(dss_props, pair)) != NULL) {
+                dsl_props_set_arg_t dpsa;
+                dsl_dataset_t *ds = NULL;
+                const char *ds_name;
+
+                ds_name = nvpair_name(pair);
+                VERIFY0(dsl_dataset_hold(dp, ds_name, FTAG, &ds));
+
+                dpsa.dpsa_dsname = ds_name;
+                dpsa.dpsa_source = ZPROP_SRC_LOCAL;
+                dpsa.dpsa_props = fnvpair_value_nvlist(pair);
+
+                dsl_props_set_sync(&dpsa, tx);
+                dsl_dataset_rele(ds, FTAG);
+        }
+}
+
+static int
+dsl_props_mds_set_check(void *arg, dmu_tx_t *tx)
+{
+        dsl_props_set_arg_t *top_dpsa = arg;
+        dsl_pool_t *dp = dmu_tx_pool(tx);
+        nvlist_t *dss_props = top_dpsa->dpsa_props;
+        nvpair_t *pair = NULL;
+
+        while ((pair = nvlist_next_nvpair(dss_props, pair)) != NULL) {
+                dsl_props_set_arg_t dpsa;
+                dsl_dataset_t *ds = NULL;
+                const char *ds_name;
+                int err;
+
+                ds_name = nvpair_name(pair);
+                err = dsl_dataset_hold(dp, ds_name, FTAG, &ds);
+                if (err != 0)
+                        return (err);
+
+                if (nvpair_type(pair) != DATA_TYPE_NVLIST) {
+                        dsl_dataset_rele(ds, FTAG);
+                        return (SET_ERROR(EINVAL));
+                }
+
+                dpsa.dpsa_dsname = ds_name;
+                dpsa.dpsa_source = ZPROP_SRC_LOCAL;
+                dpsa.dpsa_props = fnvpair_value_nvlist(pair);
+
+                err = dsl_props_set_check(&dpsa, tx);
+                dsl_dataset_rele(ds, FTAG);
+                if (err != 0)
+                        return (err);
+        }
+
+        return (0);
+}
+
+
+/*
+ * The given 'dss_props' nvlist represents the following struct:
+ *  ds1 -> prop1:value
+ *      -> prop3:value
+ *  ds2 -> prop1:value
+ *      -> prop2:value
+ *
+ * All-or-nothing: if any prop can't be set, nothing will be modified.
+ */
+int
+dsl_props_set_mds(const char *pool_name, nvlist_t *dss_props,
+    size_t num_props)
+{
+        dsl_props_set_arg_t dpsa;
+
+        dpsa.dpsa_dsname = pool_name;
+        dpsa.dpsa_source = ZPROP_SRC_LOCAL;
+        dpsa.dpsa_props = dss_props;
+
+        return (dsl_sync_task(pool_name, dsl_props_mds_set_check,
+            dsl_props_mds_set_sync, &dpsa, 2 * num_props,
+            ZFS_SPACE_CHECK_RESERVED));
+}
+
 typedef enum dsl_prop_getflags {
         DSL_PROP_GET_INHERITING = 0x1,  /* searching parent of target ds */
         DSL_PROP_GET_SNAPSHOT = 0x2,    /* snapshot dataset */
         DSL_PROP_GET_LOCAL = 0x4,       /* local properties */
         DSL_PROP_GET_RECEIVED = 0x8     /* received properties */

@@ -1027,18 +1364,38 @@
                             za.za_name, 1, za.za_num_integers, tmp);
                         if (err != 0) {
                                 kmem_free(tmp, za.za_num_integers);
                                 break;
                         }
+
                         VERIFY(nvlist_add_string(propval, ZPROP_VALUE,
                             tmp) == 0);
                         kmem_free(tmp, za.za_num_integers);
                 } else {
                         /*
                          * Integer property
                          */
                         ASSERT(za.za_integer_length == 8);
+
+                        if (prop == ZFS_PROP_WBC_MODE) {
+                                wbc_mode_prop_val_t val;
+
+                                ASSERT(za.za_num_integers ==
+                                    WBC_MODE_PROP_VAL_SZ);
+
+                                err = zap_lookup(mos, propobj,
+                                    za.za_name, 8, za.za_num_integers, &val);
+                                if (err != 0)
+                                        break;
+
+                                if (val.root_ds_object != 0 &&
+                                    val.txg_off == 0)
+                                        za.za_first_integer = ZFS_WBC_MODE_ON;
+                                else
+                                        za.za_first_integer = ZFS_WBC_MODE_OFF;
+                        }
+
                         (void) nvlist_add_uint64(propval, ZPROP_VALUE,
                             za.za_first_integer);
                 }
 
                 VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, source) == 0);