Skip to content

Commit

Permalink
OpenZFS 8677 - Open-Context Channel Programs
Browse files Browse the repository at this point in the history
Authored by: Serapheim Dimitropoulos <[email protected]>
Reviewed by: Matt Ahrens <[email protected]>
Reviewed by: Chris Williamson <[email protected]>
Reviewed by: Pavel Zakharov <[email protected]>
Approved by: Robert Mustacchi <[email protected]>
Ported-by: Don Brady <[email protected]>

We want to be able to run channel programs outside of synching
context. This would greatly improve performance for channel programs
that just gather information, as they won't have to wait for synching
context anymore.

=== What is implemented?

This feature introduces the following:
- A new command line flag in "zfs program" to specify our intention
  to run in open context. (The -n option)
- A new flag/option within the channel program ioctl which selects
  the context.
- Appropriate error handling whenever we try a channel program in
  open-context that contains zfs.sync* expressions.
- Documentation for the new feature in the manual pages.

=== How do we handle zfs.sync functions in open context?

When such a function is found by the interpreter and we are running
in open context we abort the script and we spit out a descriptive
runtime error. For example, given the script below ...

arg = ...
fs = arg["argv"][1]
err = zfs.sync.destroy(fs)
msg = "destroying " .. fs .. " err=" .. err
return msg

if we run it in open context, we will get back the following error:

Channel program execution failed:
[string "channel program"]:3: 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)
stack traceback:
            [C]: in function 'destroy'
            [string "channel program"]:3: in main chunk

=== What about testing?

We've introduced new wrappers for all channel program tests that
run each channel program as both (startard & open-context) and
expect the appropriate behavior depending on the program using
the zfs.sync module.

OpenZFS-issue: https://www.illumos.org/issues/8677
OpenZFS-commit: openzfs/openzfs@17a49e15
Closes openzfs#6558
  • Loading branch information
sdimitro authored and Nasf-Fan committed Feb 13, 2018
1 parent 2d553f1 commit 803e58a
Show file tree
Hide file tree
Showing 26 changed files with 422 additions and 133 deletions.
20 changes: 15 additions & 5 deletions cmd/zfs/zfs_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2016 by Delphix. All rights reserved.
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
* Copyright 2012 Milan Jurik. All rights reserved.
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
Expand Down Expand Up @@ -340,7 +340,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"));
case HELP_LOAD_KEY:
Expand Down Expand Up @@ -7098,10 +7098,11 @@ 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 ((c = getopt(argc, argv, "t:m:")) != -1) {
while ((c = getopt(argc, argv, "nt:m:")) != -1) {
switch (c) {
case 't':
case 'm': {
Expand Down Expand Up @@ -7139,6 +7140,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);
Expand Down Expand Up @@ -7210,8 +7215,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) {
/*
Expand Down
8 changes: 5 additions & 3 deletions include/libzfs_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/

/*
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
* Copyright (c) 2012, 2017 by Delphix. All rights reserved.
* Copyright (c) 2017 Datto Inc.
* Copyright 2017 RackTop Systems.
* Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
Expand Down Expand Up @@ -101,8 +101,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 **);

int lzc_sync(const char *, nvlist_t *, nvlist_t **);
int lzc_reopen(const char *, boolean_t);
Expand Down
3 changes: 2 additions & 1 deletion include/sys/fs/zfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2016 by Delphix. All rights reserved.
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2013, 2017 Joyent, Inc. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
Expand Down Expand Up @@ -1204,6 +1204,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"

Expand Down
28 changes: 19 additions & 9 deletions include/sys/zcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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 *);

/*
Expand Down
4 changes: 2 additions & 2 deletions lib/libzfs/libzfs_dataset.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
* Copyright (c) 2011, 2016 by Delphix. All rights reserved.
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
* Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.
* Copyright (c) 2012 Pawel Jakub Dawidek <[email protected]>.
* Copyright (c) 2013 Martin Matuska. All rights reserved.
Expand Down Expand Up @@ -2506,7 +2506,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) {
Expand Down
51 changes: 40 additions & 11 deletions lib/libzfs_core/libzfs_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,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.
*
Expand Down Expand Up @@ -1100,18 +1119,28 @@ 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));
}

/*
Expand Down
11 changes: 10 additions & 1 deletion man/man8/zfs-program.8
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
.Nm zfs program
.Nd executes ZFS channel programs
.Sh SYNOPSIS
.Cm zfs program
.Cm "zfs program"
.Op Fl n
.Op Fl t Ar instruction-limit
.Op Fl m Ar memory-limit
.Ar pool
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 11 additions & 1 deletion man/man8/zfs.8
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
.\"
.\" Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved.
.\" Copyright 2011 Joshua M. Clulow <[email protected]>
.\" Copyright (c) 2011, 2016 by Delphix. All rights reserved.
.\" Copyright (c) 2011, 2017 by Delphix. All rights reserved.
.\" Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
.\" Copyright (c) 2014, Joyent, Inc. All rights reserved.
.\" Copyright (c) 2014 by Adam Stevko. All rights reserved.
Expand Down Expand Up @@ -272,6 +272,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
Expand Down Expand Up @@ -4042,6 +4043,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
Expand All @@ -4063,6 +4065,14 @@ For full documentation of the ZFS channel program interface, see the manual
page for
.Xr zfs-program 8 .
.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
Expand Down
3 changes: 2 additions & 1 deletion module/zfs/dsl_destroy.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
* Copyright (c) 2012, 2017 by Delphix. All rights reserved.
* Copyright (c) 2013 Steven Hartland. All rights reserved.
* Copyright (c) 2013 by Joyent, Inc. All rights reserved.
* Copyright (c) 2016 Actifio, Inc. All rights reserved.
Expand Down Expand Up @@ -550,6 +550,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);
Expand Down
Loading

0 comments on commit 803e58a

Please sign in to comment.