Print this page
8115 parallel zfs mount

@@ -58,10 +58,11 @@
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/fs/zfs.h>
 #include <sys/types.h>
 #include <time.h>
+#include <synch.h>
 
 #include <libzfs.h>
 #include <libzfs_core.h>
 #include <zfs_prop.h>
 #include <zfs_deleg.h>

@@ -5747,23 +5748,28 @@
         return (0 != errors);
 }
 
 #define CHECK_SPINNER 30
 #define SPINNER_TIME 3          /* seconds */
-#define MOUNT_TIME 5            /* seconds */
+#define MOUNT_TIME 1            /* seconds */
 
+typedef struct get_all_state {
+        boolean_t       ga_verbose;
+        get_all_cb_t    *ga_cbp;
+} get_all_state_t;
+
 static int
 get_one_dataset(zfs_handle_t *zhp, void *data)
 {
         static char *spin[] = { "-", "\\", "|", "/" };
         static int spinval = 0;
         static int spincheck = 0;
         static time_t last_spin_time = (time_t)0;
-        get_all_cb_t *cbp = data;
+        get_all_state_t *state = data;
         zfs_type_t type = zfs_get_type(zhp);
 
-        if (cbp->cb_verbose) {
+        if (state->ga_verbose) {
                 if (--spincheck < 0) {
                         time_t now = time(NULL);
                         if (last_spin_time + SPINNER_TIME < now) {
                                 update_progress(spin[spinval++ % 4]);
                                 last_spin_time = now;

@@ -5785,42 +5791,51 @@
          */
         if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
                 zfs_close(zhp);
                 return (0);
         }
-        libzfs_add_handle(cbp, zhp);
-        assert(cbp->cb_used <= cbp->cb_alloc);
+        libzfs_add_handle(state->ga_cbp, zhp);
+        assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc);
 
         return (0);
 }
 
 static void
-get_all_datasets(zfs_handle_t ***dslist, size_t *count, boolean_t verbose)
+get_all_datasets(get_all_cb_t *cbp, boolean_t verbose)
 {
-        get_all_cb_t cb = { 0 };
-        cb.cb_verbose = verbose;
-        cb.cb_getone = get_one_dataset;
+        get_all_state_t state = {
+            .ga_verbose = verbose,
+            .ga_cbp = cbp
+        };
 
         if (verbose)
                 set_progress_header(gettext("Reading ZFS config"));
-        (void) zfs_iter_root(g_zfs, get_one_dataset, &cb);
+        (void) zfs_iter_root(g_zfs, get_one_dataset, &state);
 
-        *dslist = cb.cb_handles;
-        *count = cb.cb_used;
-
         if (verbose)
                 finish_progress(gettext("done."));
 }
 
 /*
  * Generic callback for sharing or mounting filesystems.  Because the code is so
  * similar, we have a common function with an extra parameter to determine which
  * mode we are using.
  */
-#define OP_SHARE        0x1
-#define OP_MOUNT        0x2
+typedef enum { OP_SHARE, OP_MOUNT } share_mount_op_t;
 
+typedef struct share_mount_state {
+        share_mount_op_t sm_op;
+        boolean_t sm_verbose;
+        int     sm_flags;
+        char    *sm_options;
+        char    *sm_proto; /* only valid for OP_SHARE */
+        mutex_t sm_lock; /* protects the remaining fields */
+        uint_t  sm_total; /* number of filesystems to process */
+        uint_t  sm_done; /* number of filesystems processed */
+        int     sm_status; /* -1 if any of the share/mount operations failed */
+} share_mount_state_t;
+
 /*
  * Share or mount a dataset.
  */
 static int
 share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,

@@ -6057,10 +6072,33 @@
                 finish_progress(info);
         else
                 update_progress(info);
 }
 
+/*
+ * zfs_foreach_mountpoint() callback that mounts or shares one filesystem and
+ * updates the progress meter.
+ */
+static int
+share_mount_one_cb(zfs_handle_t *zhp, void *arg)
+{
+        share_mount_state_t *sms = arg;
+        int ret;
+
+        ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto,
+            B_FALSE, sms->sm_options);
+
+        (void) mutex_lock(&sms->sm_lock);
+        if (ret != 0)
+                sms->sm_status = ret;
+        sms->sm_done++;
+        if (sms->sm_verbose)
+                report_mount_progress(sms->sm_done, sms->sm_total);
+        (void) mutex_unlock(&sms->sm_lock);
+        return (ret);
+}
+
 static void
 append_options(char *mntopts, char *newopts)
 {
         int len = strlen(mntopts);
 

@@ -6129,12 +6167,10 @@
         argc -= optind;
         argv += optind;
 
         /* check number of arguments */
         if (do_all) {
-                zfs_handle_t **dslist = NULL;
-                size_t i, count = 0;
                 char *protocol = NULL;
 
                 if (op == OP_SHARE && argc > 0) {
                         if (strcmp(argv[0], "nfs") != 0 &&
                             strcmp(argv[0], "smb") != 0) {

@@ -6151,37 +6187,48 @@
                         (void) fprintf(stderr, gettext("too many arguments\n"));
                         usage(B_FALSE);
                 }
 
                 start_progress_timer();
-                get_all_datasets(&dslist, &count, verbose);
+                get_all_cb_t cb = { 0 };
+                get_all_datasets(&cb, verbose);
 
-                if (count == 0)
+                if (cb.cb_used == 0)
                         return (0);
 
-                qsort(dslist, count, sizeof (void *), libzfs_dataset_cmp);
+                if (op == OP_SHARE) {
                 sa_init_selective_arg_t sharearg;
-                sharearg.zhandle_arr = dslist;
-                sharearg.zhandle_len = count;
-                if ((ret = zfs_init_libshare_arg(zfs_get_handle(dslist[0]),
+                        sharearg.zhandle_arr = cb.cb_handles;
+                        sharearg.zhandle_len = cb.cb_used;
+                        if ((ret = zfs_init_libshare_arg(g_zfs,
                     SA_INIT_SHARE_API_SELECTIVE, &sharearg)) != SA_OK) {
-                        (void) fprintf(stderr,
-                            gettext("Could not initialize libshare, %d"), ret);
+                                (void) fprintf(stderr, gettext(
+                                    "Could not initialize libshare, %d"), ret);
                         return (ret);
                 }
-
-                for (i = 0; i < count; i++) {
-                        if (verbose)
-                                report_mount_progress(i, count);
-
-                        if (share_mount_one(dslist[i], op, flags, protocol,
-                            B_FALSE, options) != 0)
-                                ret = 1;
-                        zfs_close(dslist[i]);
                 }
 
-                free(dslist);
+                share_mount_state_t share_mount_state = { 0 };
+                share_mount_state.sm_op = op;
+                share_mount_state.sm_verbose = verbose;
+                share_mount_state.sm_flags = flags;
+                share_mount_state.sm_options = options;
+                share_mount_state.sm_proto = protocol;
+                share_mount_state.sm_total = cb.cb_used;
+                (void) mutex_init(&share_mount_state.sm_lock, USYNC_THREAD,
+                    NULL);
+                /*
+                 * libshare isn't mt-safe, so only do the operation in parallel
+                 * if we're mounting.
+                 */
+                zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used,
+                    share_mount_one_cb, &share_mount_state, op == OP_MOUNT);
+                ret = share_mount_state.sm_status;
+
+                for (int i = 0; i < cb.cb_used; i++)
+                        zfs_close(cb.cb_handles[i]);
+                free(cb.cb_handles);
         } else if (argc == 0) {
                 struct mnttab entry;
 
                 if ((op == OP_SHARE) || (options != NULL)) {
                         (void) fprintf(stderr, gettext("missing filesystem "