Print this page
8115 parallel zfs mount

Split Close
Expand all
Collapse all
          --- old/usr/src/lib/libzfs/common/libzfs_mount.c
          +++ new/usr/src/lib/libzfs/common/libzfs_mount.c
↓ open down ↓ 14 lines elided ↑ open up ↑
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  24   24   * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  25      - * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
       25 + * Copyright (c) 2014, 2017 by Delphix. All rights reserved.
  26   26   * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
  27   27   * Copyright 2017 Joyent, Inc.
  28   28   * Copyright 2017 RackTop Systems.
  29   29   */
  30   30  
  31   31  /*
  32   32   * Routines to manage ZFS mounts.  We separate all the nasty routines that have
  33   33   * to deal with the OS.  The following functions are the main entry points --
  34   34   * they are used by mount and unmount and when changing a filesystem's
  35   35   * mountpoint.
  36   36   *
  37      - *      zfs_is_mounted()
  38      - *      zfs_mount()
  39      - *      zfs_unmount()
  40      - *      zfs_unmountall()
       37 + *      zfs_is_mounted()
       38 + *      zfs_mount()
       39 + *      zfs_unmount()
       40 + *      zfs_unmountall()
  41   41   *
  42   42   * This file also contains the functions used to manage sharing filesystems via
  43   43   * NFS and iSCSI:
  44   44   *
  45      - *      zfs_is_shared()
  46      - *      zfs_share()
  47      - *      zfs_unshare()
       45 + *      zfs_is_shared()
       46 + *      zfs_share()
       47 + *      zfs_unshare()
  48   48   *
  49      - *      zfs_is_shared_nfs()
  50      - *      zfs_is_shared_smb()
  51      - *      zfs_share_proto()
  52      - *      zfs_shareall();
  53      - *      zfs_unshare_nfs()
  54      - *      zfs_unshare_smb()
  55      - *      zfs_unshareall_nfs()
       49 + *      zfs_is_shared_nfs()
       50 + *      zfs_is_shared_smb()
       51 + *      zfs_share_proto()
       52 + *      zfs_shareall();
       53 + *      zfs_unshare_nfs()
       54 + *      zfs_unshare_smb()
       55 + *      zfs_unshareall_nfs()
  56   56   *      zfs_unshareall_smb()
  57   57   *      zfs_unshareall()
  58   58   *      zfs_unshareall_bypath()
  59   59   *
  60   60   * The following functions are available for pool consumers, and will
  61   61   * mount/unmount and share/unshare all datasets within pool:
  62   62   *
  63      - *      zpool_enable_datasets()
  64      - *      zpool_disable_datasets()
       63 + *      zpool_enable_datasets()
       64 + *      zpool_disable_datasets()
  65   65   */
  66   66  
  67   67  #include <dirent.h>
  68   68  #include <dlfcn.h>
  69   69  #include <errno.h>
  70   70  #include <fcntl.h>
  71   71  #include <libgen.h>
  72   72  #include <libintl.h>
  73   73  #include <stdio.h>
  74   74  #include <stdlib.h>
  75   75  #include <strings.h>
  76   76  #include <unistd.h>
  77   77  #include <zone.h>
  78   78  #include <sys/mntent.h>
  79   79  #include <sys/mount.h>
  80   80  #include <sys/stat.h>
  81   81  #include <sys/statvfs.h>
       82 +#include <sys/taskq.h>
  82   83  
  83   84  #include <libzfs.h>
  84   85  
  85   86  #include "libzfs_impl.h"
  86   87  
  87   88  #include <libshare.h>
  88   89  #include <sys/systeminfo.h>
  89   90  #define MAXISALEN       257     /* based on sysinfo(2) man page */
  90   91  
       92 +static int mount_tq_nthr = 512; /* taskq threads for multi-threaded mounting */
       93 +
       94 +static void zfs_mount_task(void *);
  91   95  static int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *);
  92   96  zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
  93   97      zfs_share_proto_t);
  94   98  
  95   99  /*
  96  100   * The share protocols table must be in the same order as the zfs_share_proto_t
  97  101   * enum in libzfs_impl.h
  98  102   */
  99  103  typedef struct {
 100  104          zfs_prop_t p_prop;
↓ open down ↓ 969 lines elided ↑ open up ↑
1070 1074                  /*
1071 1075                   * Try to remove the directory, silently ignoring any errors.
1072 1076                   * The filesystem may have since been removed or moved around,
1073 1077                   * and this error isn't really useful to the administrator in
1074 1078                   * any way.
1075 1079                   */
1076 1080                  (void) rmdir(mountpoint);
1077 1081          }
1078 1082  }
1079 1083  
     1084 +/*
     1085 + * Add the given zfs handle to the cb_handles array, dynamically reallocating
     1086 + * the array if it is out of space.
     1087 + */
1080 1088  void
1081 1089  libzfs_add_handle(get_all_cb_t *cbp, zfs_handle_t *zhp)
1082 1090  {
1083 1091          if (cbp->cb_alloc == cbp->cb_used) {
1084 1092                  size_t newsz;
1085      -                void *ptr;
     1093 +                zfs_handle_t **newhandles;
1086 1094  
1087      -                newsz = cbp->cb_alloc ? cbp->cb_alloc * 2 : 64;
1088      -                ptr = zfs_realloc(zhp->zfs_hdl,
1089      -                    cbp->cb_handles, cbp->cb_alloc * sizeof (void *),
1090      -                    newsz * sizeof (void *));
1091      -                cbp->cb_handles = ptr;
     1095 +                newsz = cbp->cb_alloc != 0 ? cbp->cb_alloc * 2 : 64;
     1096 +                newhandles = zfs_realloc(zhp->zfs_hdl,
     1097 +                    cbp->cb_handles, cbp->cb_alloc * sizeof (zfs_handle_t *),
     1098 +                    newsz * sizeof (zfs_handle_t *));
     1099 +                cbp->cb_handles = newhandles;
1092 1100                  cbp->cb_alloc = newsz;
1093 1101          }
1094 1102          cbp->cb_handles[cbp->cb_used++] = zhp;
1095 1103  }
1096 1104  
     1105 +/*
     1106 + * Recursive helper function used during file system enumeration
     1107 + */
1097 1108  static int
1098      -mount_cb(zfs_handle_t *zhp, void *data)
     1109 +zfs_iter_cb(zfs_handle_t *zhp, void *data)
1099 1110  {
1100 1111          get_all_cb_t *cbp = data;
1101 1112  
1102 1113          if (!(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM)) {
1103 1114                  zfs_close(zhp);
1104 1115                  return (0);
1105 1116          }
1106 1117  
1107 1118          if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_NOAUTO) {
1108 1119                  zfs_close(zhp);
↓ open down ↓ 5 lines elided ↑ open up ↑
1114 1125           * token, we can not mount it.
1115 1126           */
1116 1127          if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) &&
1117 1128              zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
1118 1129              NULL, 0, NULL, NULL, 0, B_TRUE) == 0) {
1119 1130                  zfs_close(zhp);
1120 1131                  return (0);
1121 1132          }
1122 1133  
1123 1134          libzfs_add_handle(cbp, zhp);
1124      -        if (zfs_iter_filesystems(zhp, mount_cb, cbp) != 0) {
     1135 +        if (zfs_iter_filesystems(zhp, zfs_iter_cb, cbp) != 0) {
1125 1136                  zfs_close(zhp);
1126 1137                  return (-1);
1127 1138          }
1128 1139          return (0);
1129 1140  }
1130 1141  
     1142 +/*
     1143 + * Sort comparator that compares two mountpoint paths. We sort these paths so
     1144 + * that subdirectories immediately follow their parents. This means that we
     1145 + * effectively treat the '/' character as the lowest value non-nul char. An
     1146 + * example sorted list using this comparator would look like:
     1147 + *
     1148 + * /foo
     1149 + * /foo/bar
     1150 + * /foo/bar/baz
     1151 + * /foo/baz
     1152 + * /foo.bar
     1153 + *
     1154 + * The mounting code depends on this ordering to deterministically iterate
     1155 + * over filesystems in order to spawn parallel mount tasks.
     1156 + */
1131 1157  int
1132      -libzfs_dataset_cmp(const void *a, const void *b)
     1158 +mountpoint_cmp(const void *arga, const void *argb)
1133 1159  {
1134      -        zfs_handle_t **za = (zfs_handle_t **)a;
1135      -        zfs_handle_t **zb = (zfs_handle_t **)b;
     1160 +        zfs_handle_t *const *zap = arga;
     1161 +        zfs_handle_t *za = *zap;
     1162 +        zfs_handle_t *const *zbp = argb;
     1163 +        zfs_handle_t *zb = *zbp;
1136 1164          char mounta[MAXPATHLEN];
1137 1165          char mountb[MAXPATHLEN];
     1166 +        const char *a = mounta;
     1167 +        const char *b = mountb;
1138 1168          boolean_t gota, gotb;
1139 1169  
1140      -        if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0)
1141      -                verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
     1170 +        gota = (zfs_get_type(za) == ZFS_TYPE_FILESYSTEM);
     1171 +        if (gota) {
     1172 +                verify(zfs_prop_get(za, ZFS_PROP_MOUNTPOINT, mounta,
1142 1173                      sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
1143      -        if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0)
1144      -                verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
     1174 +        }
     1175 +        gotb = (zfs_get_type(zb) == ZFS_TYPE_FILESYSTEM);
     1176 +        if (gotb) {
     1177 +                verify(zfs_prop_get(zb, ZFS_PROP_MOUNTPOINT, mountb,
1145 1178                      sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
     1179 +        }
1146 1180  
1147      -        if (gota && gotb)
1148      -                return (strcmp(mounta, mountb));
     1181 +        if (gota && gotb) {
     1182 +                while (*a != '\0' && (*a == *b)) {
     1183 +                        a++;
     1184 +                        b++;
     1185 +                }
     1186 +                if (*a == *b)
     1187 +                        return (0);
     1188 +                if (*a == '\0')
     1189 +                        return (-1);
     1190 +                if (*b == '\0')
     1191 +                        return (1);
     1192 +                if (*a == '/')
     1193 +                        return (-1);
     1194 +                if (*b == '/')
     1195 +                        return (1);
     1196 +                return (*a < *b ? -1 : *a > *b);
     1197 +        }
1149 1198  
1150 1199          if (gota)
1151 1200                  return (-1);
1152 1201          if (gotb)
1153 1202                  return (1);
1154 1203  
1155      -        return (strcmp(zfs_get_name(a), zfs_get_name(b)));
     1204 +        /*
     1205 +         * If neither filesystem has a mountpoint, revert to sorting by
     1206 +         * dataset name.
     1207 +         */
     1208 +        return (strcmp(zfs_get_name(za), zfs_get_name(zb)));
1156 1209  }
1157 1210  
1158 1211  /*
     1212 + * Return true if path2 is a child of path1.
     1213 + */
     1214 +static boolean_t
     1215 +libzfs_path_contains(const char *path1, const char *path2)
     1216 +{
     1217 +        return (strstr(path2, path1) == path2 && path2[strlen(path1)] == '/');
     1218 +}
     1219 +
     1220 +/*
     1221 + * Given a mountpoint specified by idx in the handles array, find the first
     1222 + * non-descendent of that mountpoint and return its index. Descendant paths
     1223 + * start with the parent's path. This function relies on the ordering
     1224 + * enforced by mountpoint_cmp().
     1225 + */
     1226 +static int
     1227 +non_descendant_idx(zfs_handle_t **handles, size_t num_handles, int idx)
     1228 +{
     1229 +        char parent[ZFS_MAXPROPLEN];
     1230 +        char child[ZFS_MAXPROPLEN];
     1231 +        int i;
     1232 +
     1233 +        verify(zfs_prop_get(handles[idx], ZFS_PROP_MOUNTPOINT, parent,
     1234 +            sizeof (parent), NULL, NULL, 0, B_FALSE) == 0);
     1235 +
     1236 +        for (i = idx + 1; i < num_handles; i++) {
     1237 +                verify(zfs_prop_get(handles[i], ZFS_PROP_MOUNTPOINT, child,
     1238 +                    sizeof (child), NULL, NULL, 0, B_FALSE) == 0);
     1239 +                if (!libzfs_path_contains(parent, child))
     1240 +                        break;
     1241 +        }
     1242 +        return (i);
     1243 +}
     1244 +
     1245 +typedef struct mnt_param {
     1246 +        libzfs_handle_t *mnt_hdl;
     1247 +        taskq_t         *mnt_tq;
     1248 +        zfs_handle_t    **mnt_zhps; /* filesystems to mount */
     1249 +        size_t          mnt_num_handles;
     1250 +        int             mnt_idx;        /* Index of selected entry to mount */
     1251 +        zfs_iter_f      mnt_func;
     1252 +        void            *mnt_data;
     1253 +} mnt_param_t;
     1254 +
     1255 +/*
     1256 + * Allocate and populate the parameter struct for mount function, and
     1257 + * schedule mounting of the entry selected by idx.
     1258 + */
     1259 +static void
     1260 +zfs_dispatch_mount(libzfs_handle_t *hdl, zfs_handle_t **handles,
     1261 +    size_t num_handles, int idx, zfs_iter_f func, void *data, taskq_t *tq)
     1262 +{
     1263 +        mnt_param_t *mnt_param = zfs_alloc(hdl, sizeof (mnt_param_t));
     1264 +
     1265 +        mnt_param->mnt_hdl = hdl;
     1266 +        mnt_param->mnt_tq = tq;
     1267 +        mnt_param->mnt_zhps = handles;
     1268 +        mnt_param->mnt_num_handles = num_handles;
     1269 +        mnt_param->mnt_idx = idx;
     1270 +        mnt_param->mnt_func = func;
     1271 +        mnt_param->mnt_data = data;
     1272 +
     1273 +        (void) taskq_dispatch(tq, zfs_mount_task, (void*)mnt_param, TQ_SLEEP);
     1274 +}
     1275 +
     1276 +/*
     1277 + * This is the structure used to keep state of mounting or sharing operations
     1278 + * during a call to zpool_enable_datasets().
     1279 + */
     1280 +typedef struct mount_state {
     1281 +        /*
     1282 +         * ms_mntstatus is set to -1 if any mount fails. While multiple threads
     1283 +         * could update this variable concurrently, no synchronization is
     1284 +         * needed as it's only ever set to -1.
     1285 +         */
     1286 +        int             ms_mntstatus;
     1287 +        int             ms_mntflags;
     1288 +        const char      *ms_mntopts;
     1289 +} mount_state_t;
     1290 +
     1291 +static int
     1292 +zfs_mount_one(zfs_handle_t *zhp, void *arg)
     1293 +{
     1294 +        mount_state_t *ms = arg;
     1295 +        int ret = 0;
     1296 +
     1297 +        if (zfs_mount(zhp, ms->ms_mntopts, ms->ms_mntflags) != 0)
     1298 +                ret = ms->ms_mntstatus = -1;
     1299 +        return (ret);
     1300 +}
     1301 +
     1302 +static int
     1303 +zfs_share_one(zfs_handle_t *zhp, void *arg)
     1304 +{
     1305 +        mount_state_t *ms = arg;
     1306 +        int ret = 0;
     1307 +
     1308 +        if (zfs_share(zhp) != 0)
     1309 +                ret = ms->ms_mntstatus = -1;
     1310 +        return (ret);
     1311 +}
     1312 +
     1313 +/*
     1314 + * Task queue function to mount one file system. On completion, it finds and
     1315 + * schedules its children to be mounted. This depends on the sorting done in
     1316 + * zfs_foreach_mountpoint(). Note that the degenerate case (chain of entries
     1317 + * each descending from the previous) will have no parallelism since we always
     1318 + * have to wait for the parent to finish mounting before we can schedule
     1319 + * its children.
     1320 + */
     1321 +static void
     1322 +zfs_mount_task(void *arg)
     1323 +{
     1324 +        mnt_param_t *mp = arg;
     1325 +        int idx = mp->mnt_idx;
     1326 +        zfs_handle_t **handles = mp->mnt_zhps;
     1327 +        size_t num_handles = mp->mnt_num_handles;
     1328 +        char mountpoint[ZFS_MAXPROPLEN];
     1329 +
     1330 +        verify(zfs_prop_get(handles[idx], ZFS_PROP_MOUNTPOINT, mountpoint,
     1331 +            sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
     1332 +
     1333 +        if (mp->mnt_func(handles[idx], mp->mnt_data) != 0)
     1334 +                return;
     1335 +
     1336 +        /*
     1337 +         * We dispatch tasks to mount filesystems with mountpoints underneath
     1338 +         * this one. We do this by dispatching the next filesystem with a
     1339 +         * descendant mountpoint of the one we just mounted, then skip all of
     1340 +         * its descendants, dispatch the next descendant mountpoint, and so on.
     1341 +         * The non_descendant_idx() function skips over filesystems that are
     1342 +         * descendants of the filesystem we just dispatched.
     1343 +         */
     1344 +        for (int i = idx + 1; i < num_handles;
     1345 +            i = non_descendant_idx(handles, num_handles, i)) {
     1346 +                char child[ZFS_MAXPROPLEN];
     1347 +                verify(zfs_prop_get(handles[i], ZFS_PROP_MOUNTPOINT,
     1348 +                    child, sizeof (child), NULL, NULL, 0, B_FALSE) == 0);
     1349 +
     1350 +                if (!libzfs_path_contains(mountpoint, child))
     1351 +                        break; /* not a descendant, return */
     1352 +                zfs_dispatch_mount(mp->mnt_hdl, handles, num_handles, i,
     1353 +                    mp->mnt_func, mp->mnt_data, mp->mnt_tq);
     1354 +        }
     1355 +        free(mp);
     1356 +}
     1357 +
     1358 +/*
     1359 + * Issue the func callback for each ZFS handle contained in the handles
     1360 + * array. This function is used to mount all datasets, and so this function
     1361 + * guarantees that filesystems for parent mountpoints are called before their
     1362 + * children. As such, before issuing any callbacks, we first sort the array
     1363 + * of handles by mountpoint.
     1364 + *
     1365 + * Callbacks are issued in one of two ways:
     1366 + *
     1367 + * 1. Sequentially: If the parallel argument is B_FALSE or the ZFS_SERIAL_MOUNT
     1368 + *    environment variable is set, then we issue callbacks sequentially.
     1369 + *
     1370 + * 2. In parallel: If the parallel argument is B_TRUE and the ZFS_SERIAL_MOUNT
     1371 + *    environment variable is not set, then we use a taskq to dispatch threads
     1372 + *    to mount filesystems is parallel. This function dispatches tasks to mount
     1373 + *    the filesystems at the top-level mountpoints, and these tasks in turn
     1374 + *    are responsible for recursively mounting filesystems in their children
     1375 + *    mountpoints.
     1376 + */
     1377 +void
     1378 +zfs_foreach_mountpoint(libzfs_handle_t *hdl, zfs_handle_t **handles,
     1379 +    size_t num_handles, zfs_iter_f func, void *data, boolean_t parallel)
     1380 +{
     1381 +        /*
     1382 +         * The ZFS_SERIAL_MOUNT environment variable is an undocumented
     1383 +         * variable that can be used as a convenience to do a/b comparison
     1384 +         * of serial vs. parallel mounting.
     1385 +         */
     1386 +        boolean_t serial_mount = !parallel ||
     1387 +            (getenv("ZFS_SERIAL_MOUNT") != NULL);
     1388 +
     1389 +        /*
     1390 +         * Sort the datasets by mountpoint. See mountpoint_cmp for details
     1391 +         * of how these are sorted.
     1392 +         */
     1393 +        qsort(handles, num_handles, sizeof (zfs_handle_t *), mountpoint_cmp);
     1394 +
     1395 +        if (serial_mount) {
     1396 +                for (int i = 0; i < num_handles; i++) {
     1397 +                        func(handles[i], data);
     1398 +                }
     1399 +                return;
     1400 +        }
     1401 +
     1402 +        /*
     1403 +         * Issue the callback function for each dataset using a parallel
     1404 +         * algorithm that uses a taskq to manage threads.
     1405 +         */
     1406 +        taskq_t *tq = taskq_create("mount_taskq", mount_tq_nthr, 0,
     1407 +            mount_tq_nthr, mount_tq_nthr, TASKQ_DYNAMIC | TASKQ_PREPOPULATE);
     1408 +
     1409 +        /*
     1410 +         * There may be multiple "top level" mountpoints outside of the pool's
     1411 +         * root mountpoint, e.g.: /foo /bar. Dispatch a mount task for each of
     1412 +         * these.
     1413 +         */
     1414 +        for (int i = 0; i < num_handles;
     1415 +            i = non_descendant_idx(handles, num_handles, i)) {
     1416 +                zfs_dispatch_mount(hdl, handles, num_handles, i, func, data,
     1417 +                    tq);
     1418 +        }
     1419 +
     1420 +        taskq_wait(tq); /* wait for all scheduled mounts to complete */
     1421 +        taskq_destroy(tq);
     1422 +}
     1423 +
     1424 +/*
1159 1425   * Mount and share all datasets within the given pool.  This assumes that no
1160      - * datasets within the pool are currently mounted.  Because users can create
1161      - * complicated nested hierarchies of mountpoints, we first gather all the
1162      - * datasets and mountpoints within the pool, and sort them by mountpoint.  Once
1163      - * we have the list of all filesystems, we iterate over them in order and mount
1164      - * and/or share each one.
     1426 + * datasets within the pool are currently mounted.
1165 1427   */
1166 1428  #pragma weak zpool_mount_datasets = zpool_enable_datasets
1167 1429  int
1168 1430  zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
1169 1431  {
1170 1432          get_all_cb_t cb = { 0 };
1171      -        libzfs_handle_t *hdl = zhp->zpool_hdl;
     1433 +        mount_state_t ms = { 0 };
1172 1434          zfs_handle_t *zfsp;
1173      -        int i, ret = -1;
1174      -        int *good;
     1435 +        sa_init_selective_arg_t sharearg;
     1436 +        int ret = 0;
1175 1437  
1176      -        /*
1177      -         * Gather all non-snap datasets within the pool.
1178      -         */
1179      -        if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_DATASET)) == NULL)
     1438 +        if ((zfsp = zfs_open(zhp->zpool_hdl, zhp->zpool_name,
     1439 +            ZFS_TYPE_DATASET)) == NULL)
1180 1440                  goto out;
1181 1441  
1182      -        libzfs_add_handle(&cb, zfsp);
1183      -        if (zfs_iter_filesystems(zfsp, mount_cb, &cb) != 0)
1184      -                goto out;
1185      -        /*
1186      -         * Sort the datasets by mountpoint.
1187      -         */
1188      -        qsort(cb.cb_handles, cb.cb_used, sizeof (void *),
1189      -            libzfs_dataset_cmp);
1190 1442  
1191 1443          /*
1192      -         * And mount all the datasets, keeping track of which ones
1193      -         * succeeded or failed.
     1444 +         * Gather all non-snapshot datasets within the pool. Start by adding
     1445 +         * the root filesystem for this pool to the list, and then iterate
     1446 +         * over all child filesystems.
1194 1447           */
1195      -        if ((good = zfs_alloc(zhp->zpool_hdl,
1196      -            cb.cb_used * sizeof (int))) == NULL)
     1448 +        libzfs_add_handle(&cb, zfsp);
     1449 +        if (zfs_iter_filesystems(zfsp, zfs_iter_cb, &cb) != 0)
1197 1450                  goto out;
1198 1451  
1199      -        ret = 0;
1200      -        for (i = 0; i < cb.cb_used; i++) {
1201      -                if (zfs_mount(cb.cb_handles[i], mntopts, flags) != 0)
1202      -                        ret = -1;
1203      -                else
1204      -                        good[i] = 1;
1205      -        }
     1452 +        ms.ms_mntopts = mntopts;
     1453 +        ms.ms_mntflags = flags;
     1454 +        zfs_foreach_mountpoint(zhp->zpool_hdl, cb.cb_handles, cb.cb_used,
     1455 +            zfs_mount_one, &ms, B_TRUE);
     1456 +        if (ms.ms_mntstatus != 0)
     1457 +                ret = ms.ms_mntstatus;
1206 1458  
1207 1459          /*
1208      -         * Then share all the ones that need to be shared. This needs
1209      -         * to be a separate pass in order to avoid excessive reloading
1210      -         * of the configuration. Good should never be NULL since
1211      -         * zfs_alloc is supposed to exit if memory isn't available.
     1460 +         * Share all filesystems that need to be shared. This needs to be
     1461 +         * a separate pass because libshare is not mt-safe, and so we need
     1462 +         * to share serially.
1212 1463           */
1213      -        for (i = 0; i < cb.cb_used; i++) {
1214      -                if (good[i] && zfs_share(cb.cb_handles[i]) != 0)
1215      -                        ret = -1;
1216      -        }
     1464 +        sharearg.zhandle_arr = cb.cb_handles;
     1465 +        sharearg.zhandle_len = cb.cb_used;
     1466 +        if ((ret = zfs_init_libshare_arg(zhp->zpool_hdl,
     1467 +            SA_INIT_SHARE_API_SELECTIVE, &sharearg)) != 0)
     1468 +                goto out;
1217 1469  
1218      -        free(good);
     1470 +        ms.ms_mntstatus = 0;
     1471 +        zfs_foreach_mountpoint(zhp->zpool_hdl, cb.cb_handles, cb.cb_used,
     1472 +            zfs_share_one, &ms, B_FALSE);
     1473 +        if (ms.ms_mntstatus != 0)
     1474 +                ret = ms.ms_mntstatus;
1219 1475  
1220 1476  out:
1221      -        for (i = 0; i < cb.cb_used; i++)
     1477 +        for (int i = 0; i < cb.cb_used; i++)
1222 1478                  zfs_close(cb.cb_handles[i]);
1223 1479          free(cb.cb_handles);
1224 1480  
1225 1481          return (ret);
1226 1482  }
1227 1483  
1228 1484  static int
1229 1485  mountpoint_compare(const void *a, const void *b)
1230 1486  {
1231 1487          const char *mounta = *((char **)a);
↓ open down ↓ 149 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX