aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorChris Williamson <[email protected]>2018-02-08 09:16:23 -0700
committerBrian Behlendorf <[email protected]>2018-02-08 15:28:18 -0800
commitd99a015343425a1c856c900aa8223016400ac2dc (patch)
treef6ab517b27b650c32127953b74567baa99951d08 /lib
parent8824a7f133e4402f7176115cf8efd535c8cbdab2 (diff)
OpenZFS 7431 - ZFS Channel Programs
Authored by: Chris Williamson <[email protected]> Reviewed by: Matthew Ahrens <[email protected]> Reviewed by: George Wilson <[email protected]> Reviewed by: John Kennedy <[email protected]> Reviewed by: Dan Kimmel <[email protected]> Approved by: Garrett D'Amore <[email protected]> Ported-by: Don Brady <[email protected]> Ported-by: John Kennedy <[email protected]> OpenZFS-issue: https://www.illumos.org/issues/7431 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/dfc11533 Porting Notes: * The CLI long option arguments for '-t' and '-m' don't parse on linux * Switched from kmem_alloc to vmem_alloc in zcp_lua_alloc * Lua implementation is built as its own module (zlua.ko) * Lua headers consumed directly by zfs code moved to 'include/sys/lua/' * There is no native setjmp/longjump available in stock Linux kernel. Brought over implementations from illumos and FreeBSD * The get_temporary_prop() was adapted due to VFS platform differences * Use of inline functions in lua parser to reduce stack usage per C call * Skip some ZFS Test Suite ZCP tests on sparc64 to avoid stack overflow
Diffstat (limited to 'lib')
-rw-r--r--lib/libzfs/libzfs_dataset.c99
-rw-r--r--lib/libzfs/libzfs_util.c4
-rw-r--r--lib/libzfs_core/Makefile.am3
-rw-r--r--lib/libzfs_core/libzfs_core.c64
-rw-r--r--lib/libzpool/Makefile.am38
5 files changed, 197 insertions, 11 deletions
diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c
index 1a4bad18c..b9ba719a0 100644
--- a/lib/libzfs/libzfs_dataset.c
+++ b/lib/libzfs/libzfs_dataset.c
@@ -2480,6 +2480,73 @@ zfs_get_clones_nvl(zfs_handle_t *zhp)
}
/*
+ * Accepts a property and value and checks that the value
+ * matches the one found by the channel program. If they are
+ * not equal, print both of them.
+ */
+static void
+zcp_check(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t intval,
+ const char *strval)
+{
+ if (!zhp->zfs_hdl->libzfs_prop_debug)
+ return;
+ int error;
+ char *poolname = zhp->zpool_hdl->zpool_name;
+ const char *prop_name = zfs_prop_to_name(prop);
+ const char *program =
+ "args = ...\n"
+ "ds = args['dataset']\n"
+ "prop = args['property']\n"
+ "value, setpoint = zfs.get_prop(ds, prop)\n"
+ "return {value=value, setpoint=setpoint}\n";
+ nvlist_t *outnvl;
+ nvlist_t *retnvl;
+ nvlist_t *argnvl = fnvlist_alloc();
+
+ fnvlist_add_string(argnvl, "dataset", zhp->zfs_name);
+ fnvlist_add_string(argnvl, "property", zfs_prop_to_name(prop));
+
+ error = lzc_channel_program(poolname, program,
+ 10 * 1000 * 1000, 10 * 1024 * 1024, argnvl, &outnvl);
+
+ if (error == 0) {
+ retnvl = fnvlist_lookup_nvlist(outnvl, "return");
+ if (zfs_prop_get_type(prop) == PROP_TYPE_NUMBER) {
+ int64_t ans;
+ error = nvlist_lookup_int64(retnvl, "value", &ans);
+ if (error != 0) {
+ (void) fprintf(stderr, "%s: zcp check error: "
+ "%u\n", prop_name, error);
+ return;
+ }
+ if (ans != intval) {
+ (void) fprintf(stderr, "%s: zfs found %llu, "
+ "but zcp found %llu\n", prop_name,
+ (u_longlong_t)intval, (u_longlong_t)ans);
+ }
+ } else {
+ char *str_ans;
+ error = nvlist_lookup_string(retnvl, "value", &str_ans);
+ if (error != 0) {
+ (void) fprintf(stderr, "%s: zcp check error: "
+ "%u\n", prop_name, error);
+ return;
+ }
+ if (strcmp(strval, str_ans) != 0) {
+ (void) fprintf(stderr,
+ "%s: zfs found '%s', but zcp found '%s'\n",
+ prop_name, strval, str_ans);
+ }
+ }
+ } else {
+ (void) fprintf(stderr, "%s: zcp check failed, channel program "
+ "error: %u\n", prop_name, error);
+ }
+ nvlist_free(argnvl);
+ nvlist_free(outnvl);
+}
+
+/*
* Retrieve a property from the given object. If 'literal' is specified, then
* numbers are left as exact values. Otherwise, numbers are converted to a
* human-readable form.
@@ -2526,6 +2593,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
(void) snprintf(propbuf, proplen, "%llu",
(u_longlong_t)val);
}
+ zcp_check(zhp, prop, val, NULL);
break;
case ZFS_PROP_MOUNTPOINT:
@@ -2594,7 +2662,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
/* 'legacy' or 'none' */
(void) strlcpy(propbuf, str, proplen);
}
-
+ zcp_check(zhp, prop, 0, propbuf);
break;
case ZFS_PROP_ORIGIN:
@@ -2602,6 +2670,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
if (str == NULL)
return (-1);
(void) strlcpy(propbuf, str, proplen);
+ zcp_check(zhp, prop, 0, str);
break;
case ZFS_PROP_CLONES:
@@ -2616,7 +2685,6 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
return (-1);
-
/*
* If quota or reservation is 0, we translate this into 'none'
* (unless literal is set), and indicate that it's the default
@@ -2635,6 +2703,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
else
zfs_nicebytes(val, propbuf, proplen);
}
+ zcp_check(zhp, prop, val, NULL);
break;
case ZFS_PROP_FILESYSTEM_LIMIT:
@@ -2659,6 +2728,8 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
} else {
zfs_nicenum(val, propbuf, proplen);
}
+
+ zcp_check(zhp, prop, val, NULL);
break;
case ZFS_PROP_REFRATIO:
@@ -2673,6 +2744,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
(void) snprintf(propbuf, proplen, "%llu.%02llux",
(u_longlong_t)(val / 100),
(u_longlong_t)(val % 100));
+ zcp_check(zhp, prop, val, NULL);
break;
case ZFS_PROP_TYPE:
@@ -2693,6 +2765,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
abort();
}
(void) snprintf(propbuf, proplen, "%s", str);
+ zcp_check(zhp, prop, 0, propbuf);
break;
case ZFS_PROP_MOUNTED:
@@ -2718,6 +2791,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
* consumers.
*/
(void) strlcpy(propbuf, zhp->zfs_name, proplen);
+ zcp_check(zhp, prop, 0, propbuf);
break;
case ZFS_PROP_MLSLABEL:
@@ -2773,6 +2847,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
return (-1);
(void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);
+ zcp_check(zhp, prop, val, NULL);
break;
case ZFS_PROP_REFERENCED:
@@ -2784,31 +2859,39 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
case ZFS_PROP_USEDCHILD:
if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
return (-1);
- if (literal)
+ if (literal) {
(void) snprintf(propbuf, proplen, "%llu",
(u_longlong_t)val);
- else
+ } else {
zfs_nicebytes(val, propbuf, proplen);
+ }
+ zcp_check(zhp, prop, val, NULL);
break;
default:
switch (zfs_prop_get_type(prop)) {
case PROP_TYPE_NUMBER:
if (get_numeric_property(zhp, prop, src,
- &source, &val) != 0)
+ &source, &val) != 0) {
return (-1);
- if (literal)
+ }
+
+ if (literal) {
(void) snprintf(propbuf, proplen, "%llu",
(u_longlong_t)val);
- else
+ } else {
zfs_nicenum(val, propbuf, proplen);
+ }
+ zcp_check(zhp, prop, val, NULL);
break;
case PROP_TYPE_STRING:
str = getprop_string(zhp, prop, &source);
if (str == NULL)
return (-1);
+
(void) strlcpy(propbuf, str, proplen);
+ zcp_check(zhp, prop, 0, str);
break;
case PROP_TYPE_INDEX:
@@ -2817,7 +2900,9 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
return (-1);
if (zfs_prop_index_to_string(prop, val, &strval) != 0)
return (-1);
+
(void) strlcpy(propbuf, strval, proplen);
+ zcp_check(zhp, prop, 0, strval);
break;
default:
diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c
index e13fee16d..ee8d09b11 100644
--- a/lib/libzfs/libzfs_util.c
+++ b/lib/libzfs/libzfs_util.c
@@ -1053,6 +1053,10 @@ libzfs_init(void)
libzfs_mnttab_init(hdl);
fletcher_4_init();
+ if (getenv("ZFS_PROP_DEBUG") != NULL) {
+ hdl->libzfs_prop_debug = B_TRUE;
+ }
+
return (hdl);
}
diff --git a/lib/libzfs_core/Makefile.am b/lib/libzfs_core/Makefile.am
index 8abaebe73..421b8b4bf 100644
--- a/lib/libzfs_core/Makefile.am
+++ b/lib/libzfs_core/Makefile.am
@@ -12,7 +12,8 @@ USER_C = \
nodist_libzfs_core_la_SOURCES = $(USER_C)
libzfs_core_la_LIBADD = \
- $(top_builddir)/lib/libnvpair/libnvpair.la
+ $(top_builddir)/lib/libnvpair/libnvpair.la \
+ $(top_builddir)/lib/libuutil/libuutil.la
libzfs_core_la_LDFLAGS = -version-info 1:0:0
diff --git a/lib/libzfs_core/libzfs_core.c b/lib/libzfs_core/libzfs_core.c
index 675f4a3de..2fa035e67 100644
--- a/lib/libzfs_core/libzfs_core.c
+++ b/lib/libzfs_core/libzfs_core.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright (c) 2012, 2014 by Delphix. All rights reserved.
+ * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
* Copyright (c) 2017 Datto Inc.
* Copyright 2017 RackTop Systems.
@@ -156,7 +156,15 @@ lzc_ioctl(zfs_ioc_t ioc, const char *name,
}
while (ioctl(g_fd, ioc, &zc) != 0) {
- if (errno == ENOMEM && resultp != NULL) {
+ /*
+ * If ioctl exited with ENOMEM, we retry the ioctl after
+ * increasing the size of the destination nvlist.
+ *
+ * Channel programs that exit with ENOMEM probably ran over the
+ * lua memory sandbox; they should not be retried.
+ */
+ if (errno == ENOMEM && resultp != NULL &&
+ ioc != ZFS_IOC_CHANNEL_PROGRAM) {
free((void *)(uintptr_t)zc.zc_nvlist_dst);
zc.zc_nvlist_dst_size *= 2;
zc.zc_nvlist_dst = (uint64_t)(uintptr_t)
@@ -1051,6 +1059,57 @@ lzc_destroy_bookmarks(nvlist_t *bmarks, nvlist_t **errlist)
}
/*
+ * Executes a channel program.
+ *
+ * If this function returns 0 the channel program was successfully loaded and
+ * ran without failing. Note that individual commands the channel program ran
+ * may have failed and the channel program is responsible for reporting such
+ * errors through outnvl if they are important.
+ *
+ * This method may also return:
+ *
+ * EINVAL The program contains syntax errors, or an invalid memory or time
+ * limit was given. No part of the channel program was executed.
+ * If caused by syntax errors, 'outnvl' contains information about the
+ * errors.
+ *
+ * ECHRNG The program was executed, but encountered a runtime error, such as
+ * calling a function with incorrect arguments, invoking the error()
+ * function directly, failing an assert() command, etc. Some portion
+ * of the channel program may have executed and committed changes.
+ * Information about the failure can be found in 'outnvl'.
+ *
+ * ENOMEM The program fully executed, but the output buffer was not large
+ * enough to store the returned value. No output is returned through
+ * 'outnvl'.
+ *
+ * ENOSPC The program was terminated because it exceeded its memory usage
+ * limit. Some portion of the channel program may have executed and
+ * committed changes to disk. No output is returned through 'outnvl'.
+ *
+ * ETIME The program was terminated because it exceeded its Lua instruction
+ * limit. Some portion of the channel program may have executed and
+ * committed changes to disk. No output is returned through 'outnvl'.
+ */
+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 (error);
+}
+
+/*
* Performs key management functions
*
* crypto_cmd should be a value from zfs_ioc_crypto_cmd_t. If the command
@@ -1110,6 +1169,7 @@ lzc_change_key(const char *fsname, uint64_t crypt_cmd, nvlist_t *props,
error = lzc_ioctl(ZFS_IOC_CHANGE_KEY, fsname, ioc_args, NULL);
nvlist_free(hidden_args);
nvlist_free(ioc_args);
+
return (error);
}
diff --git a/lib/libzpool/Makefile.am b/lib/libzpool/Makefile.am
index 06219168b..4ea7961f9 100644
--- a/lib/libzpool/Makefile.am
+++ b/lib/libzpool/Makefile.am
@@ -3,6 +3,7 @@ include $(top_srcdir)/config/Rules.am
VPATH = \
$(top_srcdir)/module/zfs \
$(top_srcdir)/module/zcommon \
+ $(top_srcdir)/module/lua \
$(top_srcdir)/lib/libzpool
# Suppress unused but set variable warnings often due to ASSERTs
@@ -121,6 +122,11 @@ KERNEL_C = \
zap.c \
zap_leaf.c \
zap_micro.c \
+ zcp.c \
+ zcp_get.c \
+ zcp_global.c \
+ zcp_iter.c \
+ zcp_synctask.c \
zfeature.c \
zfs_byteswap.c \
zfs_debug.c \
@@ -139,9 +145,39 @@ KERNEL_C = \
zle.c \
zrlock.c
+LUA_C = \
+ lapi.c \
+ lauxlib.c \
+ lbaselib.c \
+ lbitlib.c \
+ lcode.c \
+ lcompat.c \
+ lcorolib.c \
+ lctype.c \
+ ldebug.c \
+ ldo.c \
+ ldump.c \
+ lfunc.c \
+ lgc.c \
+ llex.c \
+ lmem.c \
+ lobject.c \
+ lopcodes.c \
+ lparser.c \
+ lstate.c \
+ lstring.c \
+ lstrlib.c \
+ ltable.c \
+ ltablib.c \
+ ltm.c \
+ lundump.c \
+ lvm.c \
+ lzio.c
+
nodist_libzpool_la_SOURCES = \
$(USER_C) \
- $(KERNEL_C)
+ $(KERNEL_C) \
+ $(LUA_C)
libzpool_la_LIBADD = \
$(top_builddir)/lib/libavl/libavl.la \