summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSerapheim Dimitropoulos <serapheim@delphix.com>2017-04-12 09:55:25 -0700
committerPrakash Surya <prakash.surya@delphix.com>2017-11-17 10:36:13 -0800
commita3b2868063897ff0083dea538f55f9873eec981f (patch)
tree4bd886c06c61867f0654bce2eab8a8ddac8eee21
parent9dca21df58d57c2dadfa3bcb1d33c7bd0260cfd8 (diff)
downloadillumos-joyent-a3b2868063897ff0083dea538f55f9873eec981f.tar.gz
8677 Open-Context Channel Programs
Reviewed by: Matt Ahrens <mahrens@delphix.com> Reviewed by: Chris Williamson <chris.williamson@delphix.com> Reviewed by: Pavel Zakharov <pavel.zakharov@delphix.com> Approved by: Robert Mustacchi <rm@joyent.com>
-rw-r--r--usr/src/cmd/zfs/zfs_main.c18
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c2
-rw-r--r--usr/src/lib/libzfs_core/common/libzfs_core.c51
-rw-r--r--usr/src/lib/libzfs_core/common/libzfs_core.h6
-rw-r--r--usr/src/lib/libzfs_core/common/mapfile-vers1
-rw-r--r--usr/src/man/man1m/zfs-program.1m9
-rw-r--r--usr/src/man/man1m/zfs.1m11
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/channel_common.kshlib181
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh2
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh4
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.ksh4
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh4
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh4
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh4
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_mult.ksh2
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_one.ksh2
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh2
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh3
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh2
-rw-r--r--usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh2
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_destroy.c1
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zcp.h28
-rw-r--r--usr/src/uts/common/fs/zfs/zcp.c142
-rw-r--r--usr/src/uts/common/fs/zfs/zcp_synctask.c43
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_ioctl.c6
-rw-r--r--usr/src/uts/common/sys/fs/zfs.h1
26 files changed, 411 insertions, 124 deletions
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c
index 4f52949fba..48ffda5563 100644
--- a/usr/src/cmd/zfs/zfs_main.c
+++ b/usr/src/cmd/zfs/zfs_main.c
@@ -329,7 +329,7 @@ get_usage(zfs_help_t idx)
case HELP_BOOKMARK:
return (gettext("\tbookmark <snapshot> <bookmark>\n"));
case HELP_CHANNEL_PROGRAM:
- return (gettext("\tprogram [-t <instruction limit>] "
+ return (gettext("\tprogram [-n] [-t <instruction limit>] "
"[-m <memory limit (b)>] <pool> <program file> "
"[lua args...]\n"));
}
@@ -7009,11 +7009,12 @@ zfs_do_channel_program(int argc, char **argv)
nvlist_t *outnvl;
uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT;
uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT;
+ boolean_t sync_flag = B_TRUE;
zpool_handle_t *zhp;
/* check options */
while (-1 !=
- (c = getopt(argc, argv, "t:(instr-limit)m:(memory-limit)"))) {
+ (c = getopt(argc, argv, "nt:(instr-limit)m:(memory-limit)"))) {
switch (c) {
case 't':
case 'm': {
@@ -7051,6 +7052,10 @@ zfs_do_channel_program(int argc, char **argv)
}
break;
}
+ case 'n': {
+ sync_flag = B_FALSE;
+ break;
+ }
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -7122,8 +7127,13 @@ zfs_do_channel_program(int argc, char **argv)
nvlist_t *argnvl = fnvlist_alloc();
fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV, argv + 2, argc - 2);
- ret = lzc_channel_program(poolname, progbuf, instrlimit, memlimit,
- argnvl, &outnvl);
+ if (sync_flag) {
+ ret = lzc_channel_program(poolname, progbuf,
+ instrlimit, memlimit, argnvl, &outnvl);
+ } else {
+ ret = lzc_channel_program_nosync(poolname, progbuf,
+ instrlimit, memlimit, argnvl, &outnvl);
+ }
if (ret != 0) {
/*
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index 9f55741bc9..68f9a53d23 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -2348,7 +2348,7 @@ zcp_check(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t intval,
fnvlist_add_string(argnvl, "dataset", zhp->zfs_name);
fnvlist_add_string(argnvl, "property", zfs_prop_to_name(prop));
- error = lzc_channel_program(poolname, program,
+ error = lzc_channel_program_nosync(poolname, program,
10 * 1000 * 1000, 10 * 1024 * 1024, argnvl, &outnvl);
if (error == 0) {
diff --git a/usr/src/lib/libzfs_core/common/libzfs_core.c b/usr/src/lib/libzfs_core/common/libzfs_core.c
index d3e92151f7..c910b677af 100644
--- a/usr/src/lib/libzfs_core/common/libzfs_core.c
+++ b/usr/src/lib/libzfs_core/common/libzfs_core.c
@@ -881,6 +881,25 @@ lzc_destroy_bookmarks(nvlist_t *bmarks, nvlist_t **errlist)
return (error);
}
+static int
+lzc_channel_program_impl(const char *pool, const char *program, boolean_t sync,
+ uint64_t instrlimit, uint64_t memlimit, nvlist_t *argnvl, nvlist_t **outnvl)
+{
+ int error;
+ nvlist_t *args;
+
+ args = fnvlist_alloc();
+ fnvlist_add_string(args, ZCP_ARG_PROGRAM, program);
+ fnvlist_add_nvlist(args, ZCP_ARG_ARGLIST, argnvl);
+ fnvlist_add_boolean_value(args, ZCP_ARG_SYNC, sync);
+ fnvlist_add_uint64(args, ZCP_ARG_INSTRLIMIT, instrlimit);
+ fnvlist_add_uint64(args, ZCP_ARG_MEMLIMIT, memlimit);
+ error = lzc_ioctl(ZFS_IOC_CHANNEL_PROGRAM, pool, args, outnvl);
+ fnvlist_free(args);
+
+ return (error);
+}
+
/*
* Executes a channel program.
*
@@ -918,16 +937,26 @@ int
lzc_channel_program(const char *pool, const char *program, uint64_t instrlimit,
uint64_t memlimit, nvlist_t *argnvl, nvlist_t **outnvl)
{
- int error;
- nvlist_t *args;
-
- args = fnvlist_alloc();
- fnvlist_add_string(args, ZCP_ARG_PROGRAM, program);
- fnvlist_add_nvlist(args, ZCP_ARG_ARGLIST, argnvl);
- fnvlist_add_uint64(args, ZCP_ARG_INSTRLIMIT, instrlimit);
- fnvlist_add_uint64(args, ZCP_ARG_MEMLIMIT, memlimit);
- error = lzc_ioctl(ZFS_IOC_CHANNEL_PROGRAM, pool, args, outnvl);
- fnvlist_free(args);
+ return (lzc_channel_program_impl(pool, program, B_TRUE, instrlimit,
+ memlimit, argnvl, outnvl));
+}
- return (error);
+/*
+ * Executes a read-only channel program.
+ *
+ * A read-only channel program works programmatically the same way as a
+ * normal channel program executed with lzc_channel_program(). The only
+ * difference is it runs exclusively in open-context and therefore can
+ * return faster. The downside to that, is that the program cannot change
+ * on-disk state by calling functions from the zfs.sync submodule.
+ *
+ * The return values of this function (and their meaning) are exactly the
+ * same as the ones described in lzc_channel_program().
+ */
+int
+lzc_channel_program_nosync(const char *pool, const char *program,
+ uint64_t timeout, uint64_t memlimit, nvlist_t *argnvl, nvlist_t **outnvl)
+{
+ return (lzc_channel_program_impl(pool, program, B_FALSE, timeout,
+ memlimit, argnvl, outnvl));
}
diff --git a/usr/src/lib/libzfs_core/common/libzfs_core.h b/usr/src/lib/libzfs_core/common/libzfs_core.h
index 2dcb1f639d..c21dbe109a 100644
--- a/usr/src/lib/libzfs_core/common/libzfs_core.h
+++ b/usr/src/lib/libzfs_core/common/libzfs_core.h
@@ -86,8 +86,10 @@ boolean_t lzc_exists(const char *);
int lzc_rollback(const char *, char *, int);
int lzc_rollback_to(const char *, const char *);
-int lzc_channel_program(const char *, const char *, uint64_t, uint64_t,
- nvlist_t *, nvlist_t **);
+int lzc_channel_program(const char *, const char *, uint64_t,
+ uint64_t, nvlist_t *, nvlist_t **);
+int lzc_channel_program_nosync(const char *, const char *, uint64_t,
+ uint64_t, nvlist_t *, nvlist_t **);
#ifdef __cplusplus
}
diff --git a/usr/src/lib/libzfs_core/common/mapfile-vers b/usr/src/lib/libzfs_core/common/mapfile-vers
index 9361062ba8..7e91a5305b 100644
--- a/usr/src/lib/libzfs_core/common/mapfile-vers
+++ b/usr/src/lib/libzfs_core/common/mapfile-vers
@@ -44,6 +44,7 @@ SYMBOL_VERSION ILLUMOS_0.1 {
libzfs_core_init;
lzc_bookmark;
lzc_channel_program;
+ lzc_channel_program_nosync;
lzc_clone;
lzc_promote;
lzc_create;
diff --git a/usr/src/man/man1m/zfs-program.1m b/usr/src/man/man1m/zfs-program.1m
index be42dae84b..abd93a5a73 100644
--- a/usr/src/man/man1m/zfs-program.1m
+++ b/usr/src/man/man1m/zfs-program.1m
@@ -18,6 +18,7 @@
.Nd executes ZFS channel programs
.Sh SYNOPSIS
.Cm "zfs program"
+.Op Fl n
.Op Fl t Ar instruction-limit
.Op Fl m Ar memory-limit
.Ar pool
@@ -45,6 +46,14 @@ will be run on
and any attempts to access or modify other pools will cause an error.
.Sh OPTIONS
.Bl -tag -width "-t"
+.It Fl n
+Executes a read-only channel program, which runs faster.
+The program cannot change on-disk state by calling functions from the
+zfs.sync submodule.
+The program can be used to gather information such as properties and
+determining if changes would succeed (zfs.check.*).
+Without this flag, all pending changes must be synced to disk before a
+channel program can complete.
.It Fl t Ar instruction-limit
Execution time limit, in number of Lua instructions to execute.
If a channel program executes more than the specified number of instructions,
diff --git a/usr/src/man/man1m/zfs.1m b/usr/src/man/man1m/zfs.1m
index 8f3643995b..6f138940b2 100644
--- a/usr/src/man/man1m/zfs.1m
+++ b/usr/src/man/man1m/zfs.1m
@@ -267,6 +267,7 @@
.Ar snapshot Ar snapshot Ns | Ns Ar filesystem
.Nm
.Cm program
+.Op Fl n
.Op Fl t Ar timeout
.Op Fl m Ar memory_limit
.Ar pool script
@@ -3408,6 +3409,7 @@ Display the path's inode change time as the first column of output.
.It Xo
.Nm
.Cm program
+.Op Fl n
.Op Fl t Ar timeout
.Op Fl m Ar memory_limit
.Ar pool script
@@ -3427,8 +3429,15 @@ Channel programs may only be run with root privileges.
.sp
For full documentation of the ZFS channel program interface, see the manual
page for
-.Xr zfs-program 1M .
.Bl -tag -width ""
+.It Fl n
+Executes a read-only channel program, which runs faster.
+The program cannot change on-disk state by calling functions from
+the zfs.sync submodule.
+The program can be used to gather information such as properties and
+determining if changes would succeed (zfs.check.*).
+Without this flag, all pending changes must be synced to disk before
+a channel program can complete.
.It Fl t Ar timeout
Execution time limit, in milliseconds.
If a channel program executes for longer than the provided timeout, it will
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/channel_common.kshlib b/usr/src/test/zfs-tests/tests/functional/channel_program/channel_common.kshlib
index f3097940f9..8398c24d17 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/channel_common.kshlib
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/channel_common.kshlib
@@ -11,22 +11,31 @@
#
#
-# Copyright (c) 2016 by Delphix. All rights reserved.
+# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
#
. $STF_SUITE/include/libtest.shlib
ZCP_ROOT=$STF_SUITE/tests/functional/channel_program
-# <exitcode> <expected error string> <zfs program args>
-# e.g. log_program 0 $POOL foo.zcp arg1 arg2
+#
+# Note: In case of failure (log_fail) in this function
+# we delete the file passed as <input file> so the
+# test suite doesn't leak temp files on failures. So it
+# is expected that <input file> is a temp file and not
+# an installed file.
+#
+# <exitcode> <expected error string> <input file> <zfs program args>
+# e.g. log_program 0 "" tmp.7a12V $POOL foo.zcp arg1 arg2
function log_program
{
typeset expectexit=$1
shift
typeset expecterror=$1
shift
- typeset cmdargs=$@ tmpout=$(mktemp) tmperr=$(mktemp) tmpin=$(mktemp)
+ typeset tmpin=$1
+ shift
+ typeset cmdargs=$@ tmpout=$(mktemp) tmperr=$(mktemp)
# Expected output/error filename is the same as the .zcp name
typeset basename
@@ -36,65 +45,195 @@ function log_program
log_note "running: zfs program $cmdargs:"
- tee $tmpin | zfs program $cmdargs >$tmpout 2>$tmperr
+ zfs program $cmdargs >$tmpout 2>$tmperr
typeset ret=$?
log_note "input:\n$(cat $tmpin)"
log_note "output:\n$(cat $tmpout)"
log_note "error:\n$(cat $tmperr)"
- # verify correct return value
+
+ #
+ # Verify correct return value
+ #
if [[ $ret -ne $expectexit ]]; then
+ rm $tmpout $tmperr $tmpin
log_fail "return mismatch: expected $expectexit, got $ret"
fi
#
# Check the output or reported error for successful or error returns,
# respectively.
+ #
if [[ -f "$basename.out" ]] && [[ $expectexit -eq 0 ]]; then
outdiff=$(diff "$basename.out" "$tmpout")
- [[ $? -ne 0 ]] && log_fail "Output mismatch. Expected:\n" \
- "$(cat $basename.out)\nBut got:$(cat $tmpout)\n" \
- "Diff:\n$outdiff"
+ if [[ $? -ne 0 ]]; then
+ output=$(cat $tmpout)
+ rm $tmpout $tmperr $tmpin
+ log_fail "Output mismatch. Expected:\n" \
+ "$(cat $basename.out)\nBut got:\n$output\n" \
+ "Diff:\n$outdiff"
+ fi
elif [[ -f "$basename.err" ]] && [[ $expectexit -ne 0 ]]; then
outdiff=$(diff "$basename.err" "$tmperr")
- [[ $? -ne 0 ]] && log_fail "Error mismatch. Expected:\n" \
- "$(cat $basename.err)\nBut got:$(cat $tmpout)\n" \
- "Diff:\n$outdiff"
+ if [[ $? -ne 0 ]]; then
+ outputerror=$(cat $tmperr)
+ rm $tmpout $tmperr $tmpin
+ log_fail "Error mismatch. Expected:\n" \
+ "$(cat $basename.err)\nBut got:\n$outputerror\n" \
+ "Diff:\n$outdiff"
+ fi
elif [[ -n $expecterror ]] && [[ $expectexit -ne 0 ]]; then
- grep -q "$expecterror" $tmperr || \
- log_fail "Error mismatch. Expected to contain:\n" \
- "$expecterror\nBut got:$(cat $tmpout)\n"
+ grep -q "$expecterror" $tmperr
+ if [[ $? -ne 0 ]]; then
+ outputerror=$(cat $tmperr)
+ rm $tmpout $tmperr $tmpin
+ log_fail "Error mismatch. Expected to contain:\n" \
+ "$expecterror\nBut got:\n$outputerror\n"
+ fi
elif [[ $expectexit -ne 0 ]]; then
#
# If there's no expected output, error reporting is allowed to
# vary, but ensure that we didn't fail silently.
#
- [[ -z "$(cat $tmperr)" ]] && \
- log_fail "error with no stderr output"
+ if [[ -z "$(cat $tmperr)" ]]; then
+ rm $tmpout $tmperr $tmpin
+ log_fail "error with no stderr output"
+ fi
fi
+ #
+ # Clean up all temp files except $tmpin which is
+ # reused for the second invocation of log_program.
+ #
rm $tmpout $tmperr
}
+#
+# Even though the command's arguments are passed correctly
+# to the log_must_program family of wrappers the majority
+# of the time, zcp scripts passed as HERE documents can
+# make things trickier (see comment within the fucntion
+# below) in the ordering of the commands arguments and how
+# they are passed. Thus, with this function we reconstruct
+# them to ensure that they are passed properly.
+#
+function log_program_construct_args
+{
+ typeset tmpin=$1
+ shift
+
+ args=""
+ i=0
+ while getopts "nt:m:" opt; do
+ case $opt in
+ t) args=$args" -t $OPTARG"; i=$(($i + 2)) ;;
+ m) args=$args" -m $OPTARG"; i=$(($i + 2)) ;;
+ n) args=$args" -n"; i=$(($i + 1)) ;;
+ esac
+ done
+ shift $i
+
+ pool=$1
+ shift
+
+ #
+ # Catch HERE document if it exists and save it within our
+ # temp file. The reason we do this is that since the
+ # log_must_program wrapper calls zfs-program twice (once
+ # for open context and once for syncing) the HERE doc
+ # is consumed in the first invocation and the second one
+ # does not have a program to run.
+ #
+ cat > $tmpin
+
+ #
+ # If $tmpin has contents it means that we consumed a HERE
+ # doc and $1 currently holds "-" (a dash). If there is no
+ # HERE doc and $tmpin is empty, then we copy the contents
+ # of the original channel program to $tmpin.
+ #
+ [[ -s $tmpin ]] || cp $1 $tmpin
+ shift
+
+ lua_args=$@
+
+ echo "$args $pool $tmpin $lua_args"
+}
+
+#
+# Program should complete successfully
+# when run in either context.
+#
function log_must_program
{
- log_program 0 "" "$@"
+ typeset tmpin=$(mktemp)
+
+ program_args=$(log_program_construct_args $tmpin $@)
+
+ log_program 0 "" $tmpin "-n $program_args"
+ log_program 0 "" $tmpin "$program_args"
+
+ rm $tmpin
+}
+#
+# Program should error as expected in
+# the same way in both contexts.
+#
+function log_mustnot_checkerror_program
+{
+ typeset expecterror=$1
+ shift
+ typeset tmpin=$(mktemp)
+
+ program_args=$(log_program_construct_args $tmpin $@)
+
+ log_program 1 "$expecterror" $tmpin "-n $program_args"
+ log_program 1 "$expecterror" $tmpin "$program_args"
+
+ rm $tmpin
}
+#
+# Program should fail when run in either
+# context.
+#
function log_mustnot_program
{
- log_program 1 "" "$@"
+ log_mustnot_checkerror_program "" $@
}
-function log_mustnot_checkerror_program
+
+#
+# Program should error as expected in
+# open context but complete successfully
+# in syncing context.
+#
+function log_mustnot_checkerror_program_open
{
typeset expecterror=$1
shift
- log_program 1 "$expecterror" "$@"
+ typeset tmpin=$(mktemp)
+
+ program_args=$(log_program_construct_args $tmpin $@)
+
+ log_program 1 "$expecterror" $tmpin "-n $program_args"
+ log_program 0 "" $tmpin "$program_args"
+
+ rm $tmpin
+}
+
+#
+# Program should complete successfully
+# when run in syncing context but fail
+# when attempted to run in open context.
+#
+function log_must_program_sync
+{
+ log_mustnot_checkerror_program_open "requires passing sync=TRUE" $@
}
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh
index 369ad846a0..ba9c407394 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/lua_core/tst.return_large.ksh
@@ -47,7 +47,7 @@ output_lines=$(log_must zfs program $TESTPOOL \
#
# Make sure we fail if the return is over the memory limit
#
-log_mustnot_program $TESTPOOL -m 10000 \
+log_mustnot_program -m 10000 $TESTPOOL \
$ZCP_ROOT/lua_core/tst.return_large.zcp
log_pass "Large return values work properly"
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh
index c9a2e64b7b..b2e0e600ea 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_fs.ksh
@@ -11,7 +11,7 @@
#
#
-# Copyright (c) 2016 by Delphix. All rights reserved.
+# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
#
verify_runnable "global"
@@ -32,7 +32,7 @@ log_must zfs unmount $fs
log_must datasetexists $fs
-log_must_program $TESTPOOL - $fs <<-EOF
+log_must_program_sync $TESTPOOL - $fs <<-EOF
arg = ...
fs = arg["argv"][1]
err = zfs.sync.destroy(fs)
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.ksh
index d74b47a1dc..0205e6c11e 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.destroy_snap.ksh
@@ -11,7 +11,7 @@
#
#
-# Copyright (c) 2016 by Delphix. All rights reserved.
+# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
#
verify_runnable "global"
@@ -31,7 +31,7 @@ create_snapshot $TESTPOOL/$TESTFS $TESTSNAP
log_must snapexists $snap
-log_must_program $TESTPOOL - $snap <<-EOF
+log_must_program_sync $TESTPOOL - $snap <<-EOF
arg = ...
snap = arg["argv"][1]
err = zfs.sync.destroy(snap)
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh
index d5d377b771..0739940da8 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_conflict.ksh
@@ -11,7 +11,7 @@
#
#
-# Copyright (c) 2016 by Delphix. All rights reserved.
+# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
#
. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
@@ -49,7 +49,7 @@ log_must zfs snapshot $clone@$snap
# code and description, which should be EEXIST (17) and the name of the
# conflicting snapshot.
#
-log_must_program $TESTPOOL \
+log_must_program_sync $TESTPOOL \
$ZCP_ROOT/synctask_core/tst.promote_conflict.zcp $clone
log_pass "Promoting a clone with a conflicting snapshot fails."
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh
index 13ed119ece..a3c05fabd5 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_multiple.ksh
@@ -11,7 +11,7 @@
#
#
-# Copyright (c) 2016 by Delphix. All rights reserved.
+# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
#
. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
@@ -62,7 +62,7 @@ log_must zfs clone $snap2 $clone2
log_must zfs unmount -f $clone1
-log_must_program $TESTPOOL - <<-EOF
+log_must_program_sync $TESTPOOL - <<-EOF
assert(zfs.sync.promote("$clone2") == 0)
assert(zfs.sync.promote("$clone2") == 0)
assert(zfs.sync.destroy("$clone1") == 0)
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh
index 4d1ac5974a..83685c4dc7 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.promote_simple.ksh
@@ -11,7 +11,7 @@
#
#
-# Copyright (c) 2016 by Delphix. All rights reserved.
+# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
#
. $STF_SUITE/tests/functional/channel_program/channel_common.kshlib
@@ -40,7 +40,7 @@ log_must zfs create $fs
log_must zfs snapshot $snap
log_must zfs clone $snap $clone
-log_must_program $TESTPOOL - <<-EOF
+log_must_program_sync $TESTPOOL - <<-EOF
assert(zfs.sync.promote("$clone") == 0)
EOF
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_mult.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_mult.ksh
index 778abc09dd..e0b917a32b 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_mult.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_mult.ksh
@@ -39,7 +39,7 @@ log_must snapexists $snap1
log_must snapexists $snap2
log_must zfs unmount $fs
-log_must_program $TESTPOOL - $fs $snap2 <<-EOF
+log_must_program_sync $TESTPOOL - $fs $snap2 <<-EOF
arg = ...
fs = arg["argv"][1]
snap = arg["argv"][2]
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_one.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_one.ksh
index 2a8e83913b..d6396f488b 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_one.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.rollback_one.ksh
@@ -37,7 +37,7 @@ log_must rm $file
log_must snapexists $snap
log_must zfs unmount $fs
-log_must_program $TESTPOOL - $fs <<-EOF
+log_must_program_sync $TESTPOOL - $fs <<-EOF
arg = ...
fs = arg["argv"][1]
err = zfs.sync.rollback(fs)
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh
index 98317db6e6..3691492bfa 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_destroy.ksh
@@ -33,7 +33,7 @@ log_onexit cleanup
log_must zfs create $fs
-log_must_program $TESTPOOL \
+log_must_program_sync $TESTPOOL \
$ZCP_ROOT/synctask_core/tst.snapshot_destroy.zcp $fs
log_pass "Creating/destroying snapshots in one channel program works"
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh
index c3585c9386..c20ed8a61d 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_neg.ksh
@@ -38,7 +38,8 @@ log_must zfs create $fs1
log_must zfs create $fs2
log_must zfs snapshot $fs1@snap1
-log_must_program $TESTPOOL $ZCP_ROOT/synctask_core/tst.snapshot_neg.zcp $fs1 $fs2
+log_must_program_sync $TESTPOOL \
+ $ZCP_ROOT/synctask_core/tst.snapshot_neg.zcp $fs1 $fs2
log_pass "zfs.sync.snapshot returns correct errors on invalid input"
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh
index c0180d90c4..f112bf0d28 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_recursive.ksh
@@ -46,7 +46,7 @@ for fs in $filesystems; do
log_must zfs create $fs
done
-log_must_program $TESTPOOL \
+log_must_program_sync $TESTPOOL \
$ZCP_ROOT/synctask_core/tst.snapshot_recursive.zcp $rootfs $snapname
#
diff --git a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh
index e818ce9fcb..a26234278a 100644
--- a/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/channel_program/synctask_core/tst.snapshot_simple.ksh
@@ -34,7 +34,7 @@ log_onexit cleanup
log_must zfs create $fs
-log_must_program $TESTPOOL \
+log_must_program_sync $TESTPOOL \
$ZCP_ROOT/synctask_core/tst.snapshot_simple.zcp $fs $snapname
log_pass "Simple snapshotting works"
diff --git a/usr/src/uts/common/fs/zfs/dsl_destroy.c b/usr/src/uts/common/fs/zfs/dsl_destroy.c
index d11d8c5f72..c8cf60dd1b 100644
--- a/usr/src/uts/common/fs/zfs/dsl_destroy.c
+++ b/usr/src/uts/common/fs/zfs/dsl_destroy.c
@@ -542,6 +542,7 @@ dsl_destroy_snapshots_nvl(nvlist_t *snaps, boolean_t defer,
nvlist_t *result = fnvlist_alloc();
int error = zcp_eval(nvpair_name(nvlist_next_nvpair(snaps, NULL)),
program,
+ B_TRUE,
0,
zfs_lua_max_memlimit,
nvlist_next_nvpair(wrapper, NULL), result);
diff --git a/usr/src/uts/common/fs/zfs/sys/zcp.h b/usr/src/uts/common/fs/zfs/sys/zcp.h
index 2e4ad7aac5..5713a748f7 100644
--- a/usr/src/uts/common/fs/zfs/sys/zcp.h
+++ b/usr/src/uts/common/fs/zfs/sys/zcp.h
@@ -38,20 +38,25 @@ extern uint64_t zfs_lua_max_memlimit;
int zcp_argerror(lua_State *, int, const char *, ...);
-int zcp_eval(const char *, const char *, uint64_t, uint64_t, nvpair_t *,
- nvlist_t *);
+int zcp_eval(const char *, const char *, boolean_t, uint64_t, uint64_t,
+ nvpair_t *, nvlist_t *);
int zcp_load_list_lib(lua_State *);
int zcp_load_synctask_lib(lua_State *, boolean_t);
typedef void (zcp_cleanup_t)(void *);
+typedef struct zcp_cleanup_handler {
+ zcp_cleanup_t *zch_cleanup_func;
+ void *zch_cleanup_arg;
+ list_node_t zch_node;
+} zcp_cleanup_handler_t;
typedef struct zcp_run_info {
dsl_pool_t *zri_pool;
/*
- * An estimate of the total ammount of space consumed by all
+ * An estimate of the total amount of space consumed by all
* synctasks we have successfully performed so far in this
* channel program. Used to generate ENOSPC errors for syncfuncs.
*/
@@ -89,16 +94,21 @@ typedef struct zcp_run_info {
boolean_t zri_timed_out;
/*
- * The currently registered cleanup function, which will be called
- * with the stored argument if a fatal error occurs.
+ * Boolean indicating whether or not we are running in syncing
+ * context.
*/
- zcp_cleanup_t *zri_cleanup;
- void *zri_cleanup_arg;
+ boolean_t zri_sync;
+
+ /*
+ * List of currently registered cleanup handlers, which will be
+ * triggered in the event of a fatal error.
+ */
+ list_t zri_cleanup_handlers;
} zcp_run_info_t;
zcp_run_info_t *zcp_run_info(lua_State *);
-void zcp_register_cleanup(lua_State *, zcp_cleanup_t, void *);
-void zcp_clear_cleanup(lua_State *);
+zcp_cleanup_handler_t *zcp_register_cleanup(lua_State *, zcp_cleanup_t, void *);
+void zcp_deregister_cleanup(lua_State *, zcp_cleanup_handler_t *);
void zcp_cleanup(lua_State *);
/*
diff --git a/usr/src/uts/common/fs/zfs/zcp.c b/usr/src/uts/common/fs/zfs/zcp.c
index 3410fac1a4..5b970c42ef 100644
--- a/usr/src/uts/common/fs/zfs/zcp.c
+++ b/usr/src/uts/common/fs/zfs/zcp.c
@@ -130,13 +130,6 @@ typedef struct zcp_eval_arg {
uint64_t ea_instrlimit;
} zcp_eval_arg_t;
-/*ARGSUSED*/
-static int
-zcp_eval_check(void *arg, dmu_tx_t *tx)
-{
- return (0);
-}
-
/*
* The outer-most error callback handler for use with lua_pcall(). On
* error Lua will call this callback with a single argument that
@@ -180,41 +173,45 @@ zcp_argerror(lua_State *state, int narg, const char *msg, ...)
*
* If an error occurs, the cleanup function will be invoked exactly once and
* then unreigstered.
+ *
+ * Returns the registered cleanup handler so the caller can deregister it
+ * if no error occurs.
*/
-void
+zcp_cleanup_handler_t *
zcp_register_cleanup(lua_State *state, zcp_cleanup_t cleanfunc, void *cleanarg)
{
zcp_run_info_t *ri = zcp_run_info(state);
- /*
- * A cleanup function should always be explicitly removed before
- * installing a new one to avoid accidental clobbering.
- */
- ASSERT3P(ri->zri_cleanup, ==, NULL);
- ri->zri_cleanup = cleanfunc;
- ri->zri_cleanup_arg = cleanarg;
+ zcp_cleanup_handler_t *zch = kmem_alloc(sizeof (*zch), KM_SLEEP);
+ zch->zch_cleanup_func = cleanfunc;
+ zch->zch_cleanup_arg = cleanarg;
+ list_insert_head(&ri->zri_cleanup_handlers, zch);
+
+ return (zch);
}
void
-zcp_clear_cleanup(lua_State *state)
+zcp_deregister_cleanup(lua_State *state, zcp_cleanup_handler_t *zch)
{
zcp_run_info_t *ri = zcp_run_info(state);
-
- ri->zri_cleanup = NULL;
- ri->zri_cleanup_arg = NULL;
+ list_remove(&ri->zri_cleanup_handlers, zch);
+ kmem_free(zch, sizeof (*zch));
}
/*
- * If it exists, execute the currently set cleanup function then unregister it.
+ * Execute the currently registered cleanup handlers then free them and
+ * destroy the handler list.
*/
void
zcp_cleanup(lua_State *state)
{
zcp_run_info_t *ri = zcp_run_info(state);
- if (ri->zri_cleanup != NULL) {
- ri->zri_cleanup(ri->zri_cleanup_arg);
- zcp_clear_cleanup(state);
+ for (zcp_cleanup_handler_t *zch =
+ list_remove_head(&ri->zri_cleanup_handlers); zch != NULL;
+ zch = list_remove_head(&ri->zri_cleanup_handlers)) {
+ zch->zch_cleanup_func(zch->zch_cleanup_arg);
+ kmem_free(zch, sizeof (*zch));
}
}
@@ -815,19 +812,12 @@ zcp_panic_cb(lua_State *state)
}
static void
-zcp_eval_sync(void *arg, dmu_tx_t *tx)
+zcp_eval_impl(dmu_tx_t *tx, boolean_t sync, zcp_eval_arg_t *evalargs)
{
int err;
zcp_run_info_t ri;
- zcp_eval_arg_t *evalargs = arg;
lua_State *state = evalargs->ea_state;
- /*
- * Open context should have setup the stack to contain:
- * 1: Error handler callback
- * 2: Script to run (converted to a Lua function)
- * 3: nvlist input to function (converted to Lua table or nil)
- */
VERIFY3U(3, ==, lua_gettop(state));
/*
@@ -840,8 +830,9 @@ zcp_eval_sync(void *arg, dmu_tx_t *tx)
ri.zri_cred = evalargs->ea_cred;
ri.zri_tx = tx;
ri.zri_timed_out = B_FALSE;
- ri.zri_cleanup = NULL;
- ri.zri_cleanup_arg = NULL;
+ ri.zri_sync = sync;
+ list_create(&ri.zri_cleanup_handlers, sizeof (zcp_cleanup_handler_t),
+ offsetof(zcp_cleanup_handler_t, zch_node));
ri.zri_curinstrs = 0;
ri.zri_maxinstrs = evalargs->ea_instrlimit;
@@ -878,10 +869,10 @@ zcp_eval_sync(void *arg, dmu_tx_t *tx)
/*
* Remove the error handler callback from the stack. At this point,
- * if there is a cleanup function registered, then it was registered
- * but never run or removed, which should never occur.
+ * there shouldn't be any cleanup handler registered in the handler
+ * list (zri_cleanup_handlers), regardless of whether it ran or not.
*/
- ASSERT3P(ri.zri_cleanup, ==, NULL);
+ list_destroy(&ri.zri_cleanup_handlers);
lua_remove(state, 1);
switch (err) {
@@ -963,9 +954,73 @@ zcp_eval_sync(void *arg, dmu_tx_t *tx)
}
}
+static void
+zcp_pool_error(zcp_eval_arg_t *evalargs, const char *poolname)
+{
+ evalargs->ea_result = SET_ERROR(ECHRNG);
+ (void) lua_pushfstring(evalargs->ea_state, "Could not open pool: %s",
+ poolname);
+ zcp_convert_return_values(evalargs->ea_state, evalargs->ea_outnvl,
+ ZCP_RET_ERROR, evalargs);
+
+}
+
+static void
+zcp_eval_sync(void *arg, dmu_tx_t *tx)
+{
+ zcp_eval_arg_t *evalargs = arg;
+
+ /*
+ * Open context should have setup the stack to contain:
+ * 1: Error handler callback
+ * 2: Script to run (converted to a Lua function)
+ * 3: nvlist input to function (converted to Lua table or nil)
+ */
+ VERIFY3U(3, ==, lua_gettop(evalargs->ea_state));
+
+ zcp_eval_impl(tx, B_TRUE, evalargs);
+}
+
+static void
+zcp_eval_open(zcp_eval_arg_t *evalargs, const char *poolname)
+{
+
+ int error;
+ dsl_pool_t *dp;
+ dmu_tx_t *tx;
+
+ /*
+ * See comment from the same assertion in zcp_eval_sync().
+ */
+ VERIFY3U(3, ==, lua_gettop(evalargs->ea_state));
+
+ error = dsl_pool_hold(poolname, FTAG, &dp);
+ if (error != 0) {
+ zcp_pool_error(evalargs, poolname);
+ return;
+ }
+
+ /*
+ * As we are running in open-context, we have no transaction associated
+ * with the channel program. At the same time, functions from the
+ * zfs.check submodule need to be associated with a transaction as
+ * they are basically dry-runs of their counterparts in the zfs.sync
+ * submodule. These functions should be able to run in open-context.
+ * Therefore we create a new transaction that we later abort once
+ * the channel program has been evaluated.
+ */
+ tx = dmu_tx_create_dd(dp->dp_mos_dir);
+
+ zcp_eval_impl(tx, B_FALSE, evalargs);
+
+ dmu_tx_abort(tx);
+
+ dsl_pool_rele(dp, FTAG);
+}
+
int
-zcp_eval(const char *poolname, const char *program, uint64_t instrlimit,
- uint64_t memlimit, nvpair_t *nvarg, nvlist_t *outnvl)
+zcp_eval(const char *poolname, const char *program, boolean_t sync,
+ uint64_t instrlimit, uint64_t memlimit, nvpair_t *nvarg, nvlist_t *outnvl)
{
int err;
lua_State *state;
@@ -1076,9 +1131,14 @@ zcp_eval(const char *poolname, const char *program, uint64_t instrlimit,
evalargs.ea_outnvl = outnvl;
evalargs.ea_result = 0;
- VERIFY0(dsl_sync_task(poolname, zcp_eval_check,
- zcp_eval_sync, &evalargs, 0, ZFS_SPACE_CHECK_NONE));
-
+ if (sync) {
+ err = dsl_sync_task(poolname, NULL,
+ zcp_eval_sync, &evalargs, 0, ZFS_SPACE_CHECK_NONE);
+ if (err != 0)
+ zcp_pool_error(&evalargs, poolname);
+ } else {
+ zcp_eval_open(&evalargs, poolname);
+ }
lua_close(state);
return (evalargs.ea_result);
diff --git a/usr/src/uts/common/fs/zfs/zcp_synctask.c b/usr/src/uts/common/fs/zfs/zcp_synctask.c
index 6ec3f71f62..e215e06060 100644
--- a/usr/src/uts/common/fs/zfs/zcp_synctask.c
+++ b/usr/src/uts/common/fs/zfs/zcp_synctask.c
@@ -55,6 +55,10 @@ typedef struct zcp_synctask_info {
*
* If 'sync' is false, executes a dry run and returns the error code.
*
+ * If we are not running in syncing context and we are not doing a dry run
+ * (meaning we are running a zfs.sync function in open-context) then we
+ * return a Lua error.
+ *
* This function also handles common fatal error cases for channel program
* library functions. If a fatal error occurs, err_dsname will be the dataset
* name reported in error messages, if supplied.
@@ -70,6 +74,13 @@ zcp_sync_task(lua_State *state, dsl_checkfunc_t *checkfunc,
if (!sync)
return (err);
+ if (!ri->zri_sync) {
+ return (luaL_error(state, "running functions from the zfs.sync "
+ "submodule requires passing sync=TRUE to "
+ "lzc_channel_program() (i.e. do not specify the \"-n\" "
+ "command line argument)"));
+ }
+
if (err == 0) {
syncfunc(arg, ri->zri_tx);
} else if (err == EIO) {
@@ -234,6 +245,15 @@ zcp_synctask_snapshot(lua_State *state, boolean_t sync, nvlist_t *err_details)
zcp_run_info_t *ri = zcp_run_info(state);
/*
+ * On old pools, the ZIL must not be active when a snapshot is created,
+ * but we can't suspend the ZIL because we're already in syncing
+ * context.
+ */
+ if (spa_version(ri->zri_pool->dp_spa) < SPA_VERSION_FAST_SNAP) {
+ return (ENOTSUP);
+ }
+
+ /*
* We only allow for a single snapshot rather than a list, so the
* error list output is unnecessary.
*/
@@ -243,33 +263,23 @@ zcp_synctask_snapshot(lua_State *state, boolean_t sync, nvlist_t *err_details)
ddsa.ddsa_snaps = fnvlist_alloc();
fnvlist_add_boolean(ddsa.ddsa_snaps, dsname);
- /*
- * On old pools, the ZIL must not be active when a snapshot is created,
- * but we can't suspend the ZIL because we're already in syncing
- * context.
- */
- if (spa_version(ri->zri_pool->dp_spa) < SPA_VERSION_FAST_SNAP) {
- return (ENOTSUP);
- }
+ zcp_cleanup_handler_t *zch = zcp_register_cleanup(state,
+ (zcp_cleanup_t *)&fnvlist_free, ddsa.ddsa_snaps);
err = zcp_sync_task(state, dsl_dataset_snapshot_check,
dsl_dataset_snapshot_sync, &ddsa, sync, dsname);
+ zcp_deregister_cleanup(state, zch);
fnvlist_free(ddsa.ddsa_snaps);
return (err);
}
-void
-zcp_synctask_wrapper_cleanup(void *arg)
-{
- fnvlist_free(arg);
-}
-
static int
zcp_synctask_wrapper(lua_State *state)
{
int err;
+ zcp_cleanup_handler_t *zch;
int num_ret = 1;
nvlist_t *err_details = fnvlist_alloc();
@@ -277,7 +287,8 @@ zcp_synctask_wrapper(lua_State *state)
* Make sure err_details is properly freed, even if a fatal error is
* thrown during the synctask.
*/
- zcp_register_cleanup(state, &zcp_synctask_wrapper_cleanup, err_details);
+ zch = zcp_register_cleanup(state,
+ (zcp_cleanup_t *)&fnvlist_free, err_details);
zcp_synctask_info_t *info = lua_touserdata(state, lua_upvalueindex(1));
boolean_t sync = lua_toboolean(state, lua_upvalueindex(2));
@@ -317,7 +328,7 @@ zcp_synctask_wrapper(lua_State *state)
num_ret++;
}
- zcp_clear_cleanup(state);
+ zcp_deregister_cleanup(state, zch);
fnvlist_free(err_details);
return (num_ret);
diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
index a8543c6abc..4ad4f5952e 100644
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
@@ -3606,11 +3606,15 @@ zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl,
{
char *program;
uint64_t instrlimit, memlimit;
+ boolean_t sync_flag;
nvpair_t *nvarg = NULL;
if (0 != nvlist_lookup_string(innvl, ZCP_ARG_PROGRAM, &program)) {
return (EINVAL);
}
+ if (0 != nvlist_lookup_boolean_value(innvl, ZCP_ARG_SYNC, &sync_flag)) {
+ sync_flag = B_TRUE;
+ }
if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_INSTRLIMIT, &instrlimit)) {
instrlimit = ZCP_DEFAULT_INSTRLIMIT;
}
@@ -3626,7 +3630,7 @@ zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl,
if (memlimit == 0 || memlimit > zfs_lua_max_memlimit)
return (EINVAL);
- return (zcp_eval(poolname, program, instrlimit, memlimit,
+ return (zcp_eval(poolname, program, sync_flag, instrlimit, memlimit,
nvarg, outnvl));
}
diff --git a/usr/src/uts/common/sys/fs/zfs.h b/usr/src/uts/common/sys/fs/zfs.h
index c12cb65084..c94ff9e660 100644
--- a/usr/src/uts/common/sys/fs/zfs.h
+++ b/usr/src/uts/common/sys/fs/zfs.h
@@ -964,6 +964,7 @@ typedef enum {
*/
#define ZCP_ARG_PROGRAM "program"
#define ZCP_ARG_ARGLIST "arg"
+#define ZCP_ARG_SYNC "sync"
#define ZCP_ARG_INSTRLIMIT "instrlimit"
#define ZCP_ARG_MEMLIMIT "memlimit"