Print this page
NEX-19178 Changing the NFS export path makes the SMB share offline
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Revert "NEX-19178 Changing the ZFS mountpoint property of a dataset takes the SMB share offline"
This reverts commit 35bb44b3cdee0719ce685304ca801335d5cc234e.
NEX-19178 Changing the ZFS mountpoint property of a dataset takes the SMB share offline
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-15279 support NFS server in zone
NEX-15520 online NFS shares cause zoneadm halt to hang in nfs_export_zone_fini
Portions contributed by: Dan Kruchinin dan.kruchinin@nexenta.com
Portions contributed by: Stepan Zastupov stepan.zastupov@gmail.com
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-16219 pool import performance regression due to repeated libshare initialization
Reviewd by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15937 zpool import performance degradation in filesystem sharing
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-15937 zpool import performance degradation in filesystem sharing
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-6586 cleanup gcc warnings in libzfs_mount.c
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
2605 want to resume interrupted zfs send
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Reviewed by: Xin Li <delphij@freebsd.org>
Reviewed by: Arne Jansen <sensille@gmx.net>
Approved by: Dan McDonald <danmcd@omniti.com>
6280 libzfs: unshare_one() could fail with EZFS_SHARENFSFAILED
Reviewed by: Toomas Soome <tsoome@me.com>
Reviewed by: Dan McDonald <danmcd@omniti.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Approved by: Gordon Ross <gwr@nexenta.com>
NEX-1557 Parallel mount during HA Failover sometimes doesn't share the dataset, causes shares to go offline
SUP-647 Long failover times dominated by zpool import times trigger client-side errors
re #13594 rb4488 Lint complaints fix
re #10054 #13409 rb4387 added parallel unmount for zpool export
*** 18,29 ****
*
* CDDL HEADER END
*/
/*
! * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
* Copyright 2017 Joyent, Inc.
* Copyright 2017 RackTop Systems.
*/
--- 18,33 ----
*
* CDDL HEADER END
*/
/*
! * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+ /*
+ * Copyright 2019 Nexenta Systems, Inc.
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
* Copyright 2017 Joyent, Inc.
* Copyright 2017 RackTop Systems.
*/
*** 59,69 ****
--- 63,75 ----
*
* The following functions are available for pool consumers, and will
* mount/unmount and share/unshare all datasets within pool:
*
* zpool_enable_datasets()
+ * zpool_enable_datasets_ex()
* zpool_disable_datasets()
+ * zpool_disable_datasets_ex()
*/
#include <dirent.h>
#include <dlfcn.h>
#include <errno.h>
*** 76,85 ****
--- 82,92 ----
#include <unistd.h>
#include <zone.h>
#include <sys/mntent.h>
#include <sys/mount.h>
#include <sys/stat.h>
+ #include <thread_pool.h>
#include <sys/statvfs.h>
#include <libzfs.h>
#include "libzfs_impl.h"
*** 419,429 ****
* Unmount a single filesystem.
*/
static int
unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
{
! if (umount2(mountpoint, flags) != 0) {
zfs_error_aux(hdl, strerror(errno));
return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED,
dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
mountpoint));
}
--- 426,437 ----
* Unmount a single filesystem.
*/
static int
unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
{
! int ret = umount2(mountpoint, flags);
! if (ret != 0) {
zfs_error_aux(hdl, strerror(errno));
return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED,
dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
mountpoint));
}
*** 569,588 ****
* initialized in _zfs_init_libshare() are actually present.
*/
static sa_handle_t (*_sa_init)(int);
static sa_handle_t (*_sa_init_arg)(int, void *);
static void (*_sa_fini)(sa_handle_t);
static sa_share_t (*_sa_find_share)(sa_handle_t, char *);
static int (*_sa_enable_share)(sa_share_t, char *);
static int (*_sa_disable_share)(sa_share_t, char *);
static char *(*_sa_errorstr)(int);
static int (*_sa_parse_legacy_options)(sa_group_t, char *, char *);
static boolean_t (*_sa_needs_refresh)(sa_handle_t *);
static libzfs_handle_t *(*_sa_get_zfs_handle)(sa_handle_t);
! static int (*_sa_zfs_process_share)(sa_handle_t, sa_group_t, sa_share_t,
! char *, char *, zprop_source_t, char *, char *, char *);
static void (*_sa_update_sharetab_ts)(sa_handle_t);
/*
* _zfs_init_libshare()
*
--- 577,596 ----
* initialized in _zfs_init_libshare() are actually present.
*/
static sa_handle_t (*_sa_init)(int);
static sa_handle_t (*_sa_init_arg)(int, void *);
+ static int (*_sa_service)(sa_handle_t);
static void (*_sa_fini)(sa_handle_t);
static sa_share_t (*_sa_find_share)(sa_handle_t, char *);
static int (*_sa_enable_share)(sa_share_t, char *);
static int (*_sa_disable_share)(sa_share_t, char *);
static char *(*_sa_errorstr)(int);
static int (*_sa_parse_legacy_options)(sa_group_t, char *, char *);
static boolean_t (*_sa_needs_refresh)(sa_handle_t *);
static libzfs_handle_t *(*_sa_get_zfs_handle)(sa_handle_t);
! static int (* _sa_get_zfs_share)(sa_handle_t, char *, zfs_handle_t *);
static void (*_sa_update_sharetab_ts)(sa_handle_t);
/*
* _zfs_init_libshare()
*
*** 611,620 ****
--- 619,630 ----
if ((libshare = dlopen(path, RTLD_LAZY | RTLD_GLOBAL)) != NULL) {
_sa_init = (sa_handle_t (*)(int))dlsym(libshare, "sa_init");
_sa_init_arg = (sa_handle_t (*)(int, void *))dlsym(libshare,
"sa_init_arg");
_sa_fini = (void (*)(sa_handle_t))dlsym(libshare, "sa_fini");
+ _sa_service = (int (*)(sa_handle_t))dlsym(libshare,
+ "sa_service");
_sa_find_share = (sa_share_t (*)(sa_handle_t, char *))
dlsym(libshare, "sa_find_share");
_sa_enable_share = (int (*)(sa_share_t, char *))dlsym(libshare,
"sa_enable_share");
_sa_disable_share = (int (*)(sa_share_t, char *))dlsym(libshare,
*** 624,656 ****
dlsym(libshare, "sa_parse_legacy_options");
_sa_needs_refresh = (boolean_t (*)(sa_handle_t *))
dlsym(libshare, "sa_needs_refresh");
_sa_get_zfs_handle = (libzfs_handle_t *(*)(sa_handle_t))
dlsym(libshare, "sa_get_zfs_handle");
! _sa_zfs_process_share = (int (*)(sa_handle_t, sa_group_t,
! sa_share_t, char *, char *, zprop_source_t, char *,
! char *, char *))dlsym(libshare, "sa_zfs_process_share");
_sa_update_sharetab_ts = (void (*)(sa_handle_t))
dlsym(libshare, "sa_update_sharetab_ts");
if (_sa_init == NULL || _sa_init_arg == NULL ||
_sa_fini == NULL || _sa_find_share == NULL ||
_sa_enable_share == NULL || _sa_disable_share == NULL ||
_sa_errorstr == NULL || _sa_parse_legacy_options == NULL ||
_sa_needs_refresh == NULL || _sa_get_zfs_handle == NULL ||
! _sa_zfs_process_share == NULL ||
_sa_update_sharetab_ts == NULL) {
_sa_init = NULL;
_sa_init_arg = NULL;
_sa_fini = NULL;
_sa_disable_share = NULL;
_sa_enable_share = NULL;
_sa_errorstr = NULL;
_sa_parse_legacy_options = NULL;
(void) dlclose(libshare);
_sa_needs_refresh = NULL;
_sa_get_zfs_handle = NULL;
! _sa_zfs_process_share = NULL;
_sa_update_sharetab_ts = NULL;
}
}
}
--- 634,666 ----
dlsym(libshare, "sa_parse_legacy_options");
_sa_needs_refresh = (boolean_t (*)(sa_handle_t *))
dlsym(libshare, "sa_needs_refresh");
_sa_get_zfs_handle = (libzfs_handle_t *(*)(sa_handle_t))
dlsym(libshare, "sa_get_zfs_handle");
! _sa_get_zfs_share = (int (*)(sa_handle_t, char *,
! zfs_handle_t *)) dlsym(libshare, "sa_get_zfs_share");
_sa_update_sharetab_ts = (void (*)(sa_handle_t))
dlsym(libshare, "sa_update_sharetab_ts");
if (_sa_init == NULL || _sa_init_arg == NULL ||
_sa_fini == NULL || _sa_find_share == NULL ||
_sa_enable_share == NULL || _sa_disable_share == NULL ||
_sa_errorstr == NULL || _sa_parse_legacy_options == NULL ||
_sa_needs_refresh == NULL || _sa_get_zfs_handle == NULL ||
! _sa_get_zfs_share == NULL || _sa_service == NULL ||
_sa_update_sharetab_ts == NULL) {
_sa_init = NULL;
_sa_init_arg = NULL;
+ _sa_service = NULL;
_sa_fini = NULL;
_sa_disable_share = NULL;
_sa_enable_share = NULL;
_sa_errorstr = NULL;
_sa_parse_legacy_options = NULL;
(void) dlclose(libshare);
_sa_needs_refresh = NULL;
_sa_get_zfs_handle = NULL;
! _sa_get_zfs_share = NULL;
_sa_update_sharetab_ts = NULL;
}
}
}
*** 796,853 ****
char sourcestr[ZFS_MAXPROPLEN];
libzfs_handle_t *hdl = zhp->zfs_hdl;
sa_share_t share;
zfs_share_proto_t *curr_proto;
zprop_source_t sourcetype;
int ret;
if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
return (0);
for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) {
/*
* Return success if there are no share options.
*/
if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop,
shareopts, sizeof (shareopts), &sourcetype, sourcestr,
ZFS_MAXPROPLEN, B_FALSE) != 0 ||
strcmp(shareopts, "off") == 0)
continue;
! ret = zfs_init_libshare_arg(hdl, SA_INIT_ONE_SHARE_FROM_HANDLE,
! zhp);
if (ret != SA_OK) {
(void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
dgettext(TEXT_DOMAIN, "cannot share '%s': %s"),
zfs_get_name(zhp), _sa_errorstr != NULL ?
_sa_errorstr(ret) : "");
return (-1);
}
- /*
- * If the 'zoned' property is set, then zfs_is_mountable()
- * will have already bailed out if we are in the global zone.
- * But local zones cannot be NFS servers, so we ignore it for
- * local zones as well.
- */
- if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
- continue;
-
share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint);
if (share == NULL) {
/*
* This may be a new file system that was just
! * created so isn't in the internal cache
! * (second time through). Rather than
! * reloading the entire configuration, we can
! * assume ZFS has done the checking and it is
! * safe to add this to the internal
! * configuration.
*/
! if (_sa_zfs_process_share(hdl->libzfs_sharehdl,
! NULL, NULL, mountpoint,
! proto_table[*curr_proto].p_name, sourcetype,
! shareopts, sourcestr, zhp->zfs_name) != SA_OK) {
(void) zfs_error_fmt(hdl,
proto_table[*curr_proto].p_share_err,
dgettext(TEXT_DOMAIN, "cannot share '%s'"),
zfs_get_name(zhp));
return (-1);
--- 806,863 ----
char sourcestr[ZFS_MAXPROPLEN];
libzfs_handle_t *hdl = zhp->zfs_hdl;
sa_share_t share;
zfs_share_proto_t *curr_proto;
zprop_source_t sourcetype;
+ int service = SA_INIT_ONE_SHARE_FROM_HANDLE;
int ret;
if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
return (0);
+ /*
+ * Function may be called in a loop from higher up stack, with libshare
+ * initialized for multiple shares (SA_INIT_SHARE_API_SELECTIVE).
+ * zfs_init_libshare_arg will refresh the handle's cache if necessary.
+ * In this case we do not want to switch to per share initialization.
+ * Specify SA_INIT_SHARE_API to do full refresh, if refresh required.
+ */
+ if ((hdl->libzfs_sharehdl != NULL) && (_sa_service != NULL) &&
+ (_sa_service(hdl->libzfs_sharehdl) ==
+ SA_INIT_SHARE_API_SELECTIVE)) {
+ service = SA_INIT_SHARE_API;
+ }
+
for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) {
/*
* Return success if there are no share options.
*/
if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop,
shareopts, sizeof (shareopts), &sourcetype, sourcestr,
ZFS_MAXPROPLEN, B_FALSE) != 0 ||
strcmp(shareopts, "off") == 0)
continue;
! ret = zfs_init_libshare_arg(hdl, service, zhp);
if (ret != SA_OK) {
(void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
dgettext(TEXT_DOMAIN, "cannot share '%s': %s"),
zfs_get_name(zhp), _sa_errorstr != NULL ?
_sa_errorstr(ret) : "");
return (-1);
}
share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint);
if (share == NULL) {
/*
* This may be a new file system that was just
! * created so isn't in the internal cache.
! * Rather than reloading the entire configuration,
! * we can add just this one share to the cache.
*/
! if ((_sa_get_zfs_share == NULL) ||
! (_sa_get_zfs_share(hdl->libzfs_sharehdl, "zfs", zhp)
! != SA_OK)) {
(void) zfs_error_fmt(hdl,
proto_table[*curr_proto].p_share_err,
dgettext(TEXT_DOMAIN, "cannot share '%s'"),
zfs_get_name(zhp));
return (-1);
*** 905,930 ****
zfs_share_proto_t proto)
{
sa_share_t share;
int err;
char *mntpt;
/*
* Mountpoint could get trashed if libshare calls getmntany
* which it does during API initialization, so strdup the
* value.
*/
mntpt = zfs_strdup(hdl, mountpoint);
/*
! * make sure libshare initialized, initialize everything because we
! * don't know what other unsharing may happen later. Functions up the
! * stack are allowed to initialize instead a subset of shares at the
! * time the set is known.
*/
! if ((err = zfs_init_libshare_arg(hdl, SA_INIT_ONE_SHARE_FROM_NAME,
! (void *)name)) != SA_OK) {
free(mntpt); /* don't need the copy anymore */
return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
name, _sa_errorstr(err)));
}
--- 915,948 ----
zfs_share_proto_t proto)
{
sa_share_t share;
int err;
char *mntpt;
+ int service = SA_INIT_ONE_SHARE_FROM_NAME;
/*
* Mountpoint could get trashed if libshare calls getmntany
* which it does during API initialization, so strdup the
* value.
*/
mntpt = zfs_strdup(hdl, mountpoint);
/*
! * Function may be called in a loop from higher up stack, with libshare
! * initialized for multiple shares (SA_INIT_SHARE_API_SELECTIVE).
! * zfs_init_libshare_arg will refresh the handle's cache if necessary.
! * In this case we do not want to switch to per share initialization.
! * Specify SA_INIT_SHARE_API to do full refresh, if refresh required.
*/
! if ((hdl->libzfs_sharehdl != NULL) && (_sa_service != NULL) &&
! (_sa_service(hdl->libzfs_sharehdl) ==
! SA_INIT_SHARE_API_SELECTIVE)) {
! service = SA_INIT_SHARE_API;
! }
!
! err = zfs_init_libshare_arg(hdl, service, (void *)name);
! if (err != SA_OK) {
free(mntpt); /* don't need the copy anymore */
return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
name, _sa_errorstr(err)));
}
*** 1153,1179 ****
return (1);
return (strcmp(zfs_get_name(a), zfs_get_name(b)));
}
! /*
! * Mount and share all datasets within the given pool. This assumes that no
! * datasets within the pool are currently mounted. Because users can create
! * complicated nested hierarchies of mountpoints, we first gather all the
! * datasets and mountpoints within the pool, and sort them by mountpoint. Once
! * we have the list of all filesystems, we iterate over them in order and mount
! * and/or share each one.
*/
! #pragma weak zpool_mount_datasets = zpool_enable_datasets
int
! zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
{
get_all_cb_t cb = { 0 };
libzfs_handle_t *hdl = zhp->zpool_hdl;
zfs_handle_t *zfsp;
int i, ret = -1;
int *good;
/*
* Gather all non-snap datasets within the pool.
*/
if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_DATASET)) == NULL)
--- 1171,1661 ----
return (1);
return (strcmp(zfs_get_name(a), zfs_get_name(b)));
}
! static int
! mountpoint_compare(const void *a, const void *b)
! {
! const char *mounta = *((char **)a);
! const char *mountb = *((char **)b);
!
! return (strcmp(mountb, mounta));
! }
!
! typedef enum {
! TASK_TO_PROCESS,
! TASK_IN_PROCESSING,
! TASK_DONE,
! TASK_MAX
! } task_state_t;
!
! typedef struct mount_task {
! const char *mp;
! zfs_handle_t *zh;
! task_state_t state;
! int error;
! } mount_task_t;
!
! typedef struct mount_task_q {
! pthread_mutex_t q_lock;
! libzfs_handle_t *hdl;
! const char *mntopts;
! const char *error_mp;
! zfs_handle_t *error_zh;
! int error;
! int q_length;
! int n_tasks;
! int flags;
! mount_task_t task[1];
! } mount_task_q_t;
!
! static int
! mount_task_q_init(int argc, zfs_handle_t **handles, const char *mntopts,
! int flags, mount_task_q_t **task)
! {
! mount_task_q_t *task_q;
! int i, error;
! size_t task_q_size;
!
! *task = NULL;
! /* nothing to do ? should not be here */
! if (argc <= 0)
! return (EINVAL);
!
! /* allocate and init task_q */
! task_q_size = sizeof (mount_task_q_t) +
! (argc - 1) * sizeof (mount_task_t);
! task_q = calloc(task_q_size, 1);
! if (task_q == NULL)
! return (ENOMEM);
!
! if ((error = pthread_mutex_init(&task_q->q_lock, NULL)) != 0) {
! free(task_q);
! return (error);
! }
! task_q->q_length = argc;
! task_q->n_tasks = argc;
! task_q->flags = flags;
! task_q->mntopts = mntopts;
!
! /* we are not going to change the strings, so no need to strdup */
! for (i = 0; i < argc; ++i) {
! task_q->task[i].zh = handles[i];
! task_q->task[i].state = TASK_TO_PROCESS;
! task_q->error = 0;
! }
!
! *task = task_q;
! return (0);
! }
!
! static int
! umount_task_q_init(int argc, const char **argv, int flags,
! libzfs_handle_t *hdl, mount_task_q_t **task)
! {
! mount_task_q_t *task_q;
! int i, error;
! size_t task_q_size;
!
! *task = NULL;
! /* nothing to do ? should not be here */
! if (argc <= 0)
! return (EINVAL);
!
! /* allocate and init task_q */
! task_q_size = sizeof (mount_task_q_t) +
! (argc - 1) * sizeof (mount_task_t);
! task_q = calloc(task_q_size, 1);
! if (task_q == NULL)
! return (ENOMEM);
!
! if ((error = pthread_mutex_init(&task_q->q_lock, NULL)) != 0) {
! free(task_q);
! return (error);
! }
! task_q->hdl = hdl;
! task_q->q_length = argc;
! task_q->n_tasks = argc;
! task_q->flags = flags;
!
! /* we are not going to change the strings, so no need to strdup */
! for (i = 0; i < argc; ++i) {
! task_q->task[i].mp = argv[i];
! task_q->task[i].state = TASK_TO_PROCESS;
! task_q->error = 0;
! }
!
! *task = task_q;
! return (0);
! }
!
! static void
! mount_task_q_fini(mount_task_q_t *task_q)
! {
! assert(task_q != NULL);
! (void) pthread_mutex_destroy(&task_q->q_lock);
! free(task_q);
! }
!
! static int
! is_child_of(const char *s1, const char *s2)
! {
! for (; *s1 && *s2 && (*s1 == *s2); ++s1, ++s2)
! ;
! return (!*s2 && (*s1 == '/'));
! }
!
! static boolean_t
! task_completed(int ind, mount_task_q_t *task_q)
! {
! return (task_q->task[ind].state == TASK_DONE);
! }
!
! static boolean_t
! task_to_process(int ind, mount_task_q_t *task_q)
! {
! return (task_q->task[ind].state == TASK_TO_PROCESS);
! }
!
! static boolean_t
! task_in_processing(int ind, mount_task_q_t *task_q)
! {
! return (task_q->task[ind].state == TASK_IN_PROCESSING);
! }
!
! static void
! task_next_stage(int ind, mount_task_q_t *task_q)
! {
! /* our state machine is a pipeline */
! task_q->task[ind].state++;
! assert(task_q->task[ind].state < TASK_MAX);
! }
!
! static boolean_t
! task_state_valid(int ind, mount_task_q_t *task_q)
! {
! /* our state machine is a pipeline */
! return (task_q->task[ind].state < TASK_MAX);
! }
!
! static boolean_t
! child_umount_pending(int ind, mount_task_q_t *task_q)
! {
! int i;
! for (i = ind-1; i >= 0; --i) {
! assert(task_state_valid(i, task_q));
! if ((task_q->task[i].state != TASK_DONE) &&
! is_child_of(task_q->task[i].mp, task_q->task[ind].mp))
! return (B_TRUE);
! }
!
! return (B_FALSE);
! }
!
! static boolean_t
! parent_mount_pending(int ind, mount_task_q_t *task_q)
! {
! int i;
! for (i = ind-1; i >= 0; --i) {
! assert(task_state_valid(i, task_q));
! if ((task_q->task[i].state != TASK_DONE) &&
! is_child_of(task_q->task[ind].zh->zfs_name,
! task_q->task[i].zh->zfs_name))
! return (B_TRUE);
! }
!
! return (B_FALSE);
! }
!
! static void
! unmounter(void *arg)
! {
! mount_task_q_t *task_q = (mount_task_q_t *)arg;
! int error = 0, done = 0;
!
! assert(task_q != NULL);
! if (task_q == NULL)
! return;
!
! while (!error && !done) {
! mount_task_t *task;
! int i, t, umount_err, flags, q_error;
!
! if ((error = pthread_mutex_lock(&task_q->q_lock)) != 0)
! break; /* Out of while() loop */
!
! if (task_q->error || task_q->n_tasks == 0) {
! (void) pthread_mutex_unlock(&task_q->q_lock);
! break; /* Out of while() loop */
! }
!
! /* Find task ready for processing */
! for (i = 0, task = NULL, t = -1; i < task_q->q_length; ++i) {
! if (task_q->error) {
! /* Fatal error, stop processing */
! done = 1;
! break; /* Out of for() loop */
! }
!
! if (task_completed(i, task_q))
! continue; /* for() loop */
!
! if (task_to_process(i, task_q)) {
! /*
! * Cannot umount if some children are still
! * mounted; come back later
*/
! if ((child_umount_pending(i, task_q)))
! continue; /* for() loop */
! /* Should be OK to unmount now */
! task_next_stage(i, task_q);
! task = &task_q->task[i];
! t = i;
! break; /* Out of for() loop */
! }
!
! /* Otherwise, the task is already in processing */
! assert(task_in_processing(i, task_q));
! }
!
! flags = task_q->flags;
!
! error = pthread_mutex_unlock(&task_q->q_lock);
!
! if (done || (task == NULL) || error || task_q->error)
! break; /* Out of while() loop */
!
! umount_err = umount2(task->mp, flags);
! q_error = errno;
!
! if ((error = pthread_mutex_lock(&task_q->q_lock)) != 0)
! break; /* Out of while() loop */
!
! /* done processing */
! assert(t >= 0 && t < task_q->q_length);
! task_next_stage(t, task_q);
! assert(task_completed(t, task_q));
! task_q->n_tasks--;
!
! if (umount_err) {
! /*
! * umount2() failed, cannot be busy because of mounted
! * children - we have checked above, so it is fatal
! */
! assert(child_umount_pending(t, task_q) == B_FALSE);
! task->error = q_error;
! if (!task_q->error) {
! task_q->error = task->error;
! task_q->error_mp = task->mp;
! }
! done = 1;
! }
!
! if ((error = pthread_mutex_unlock(&task_q->q_lock)) != 0)
! break; /* Out of while() loop */
! }
! }
!
! static void
! mounter(void *arg)
! {
! mount_task_q_t *task_q = (mount_task_q_t *)arg;
! int error = 0, done = 0;
!
! assert(task_q != NULL);
! if (task_q == NULL)
! return;
!
! while (!error && !done) {
! mount_task_t *task;
! int i, t, mount_err, flags, q_error;
! const char *mntopts;
!
! if ((error = pthread_mutex_lock(&task_q->q_lock)) != 0)
! break; /* Out of while() loop */
!
! if (task_q->error || task_q->n_tasks == 0) {
! (void) pthread_mutex_unlock(&task_q->q_lock);
! break; /* Out of while() loop */
! }
!
! /* Find task ready for processing */
! for (i = 0, task = NULL, t = -1; i < task_q->q_length; ++i) {
! if (task_q->error) {
! /* Fatal error, stop processing */
! done = 1;
! break; /* Out of for() loop */
! }
!
! if (task_completed(i, task_q))
! continue; /* for() loop */
!
! if (task_to_process(i, task_q)) {
! /*
! * Cannot mount if some parents are not
! * mounted yet; come back later
! */
! if ((parent_mount_pending(i, task_q)))
! continue; /* for() loop */
! /* Should be OK to mount now */
! task_next_stage(i, task_q);
! task = &task_q->task[i];
! t = i;
! break; /* Out of for() loop */
! }
!
! /* Otherwise, the task is already in processing */
! assert(task_in_processing(i, task_q));
! }
!
! flags = task_q->flags;
! mntopts = task_q->mntopts;
!
! error = pthread_mutex_unlock(&task_q->q_lock);
!
! if (done || (task == NULL) || error || task_q->error)
! break; /* Out of while() loop */
!
! mount_err = zfs_mount(task->zh, mntopts, flags);
! q_error = errno;
!
! if ((error = pthread_mutex_lock(&task_q->q_lock)) != 0)
! break; /* Out of while() loop */
!
! /* done processing */
! assert(t >= 0 && t < task_q->q_length);
! task_next_stage(t, task_q);
! assert(task_completed(t, task_q));
! task_q->n_tasks--;
!
! if (mount_err) {
! task->error = q_error;
! if (!task_q->error) {
! task_q->error = task->error;
! task_q->error_zh = task->zh;
! }
! done = 1;
! }
!
! if ((error = pthread_mutex_unlock(&task_q->q_lock)) != 0)
! break; /* Out of while() loop */
! }
! }
!
! #define THREADS_HARD_LIMIT 128
! int parallel_unmount(libzfs_handle_t *hdl, int argc, const char **argv,
! int flags, int n_threads)
! {
! mount_task_q_t *task_queue = NULL;
! int i, error;
! tpool_t *t;
!
! if (argc == 0)
! return (0);
!
! if ((error = umount_task_q_init(argc, argv, flags, hdl, &task_queue))
! != 0) {
! assert(task_queue == NULL);
! return (error);
! }
!
! if (n_threads > argc)
! n_threads = argc;
!
! if (n_threads > THREADS_HARD_LIMIT)
! n_threads = THREADS_HARD_LIMIT;
!
! t = tpool_create(1, n_threads, 0, NULL);
!
! for (i = 0; i < n_threads; ++i)
! (void) tpool_dispatch(t, unmounter, task_queue);
!
! tpool_wait(t);
! tpool_destroy(t);
!
! if (task_queue->error) {
! /*
! * Tell ZFS!
! */
! zfs_error_aux(hdl,
! strerror(error ? error : task_queue->error));
! error = zfs_error_fmt(hdl, EZFS_UMOUNTFAILED,
! dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
! error ? "datasets" : task_queue->error_mp);
! }
! if (task_queue)
! mount_task_q_fini(task_queue);
!
! return (error);
! }
!
! int parallel_mount(get_all_cb_t *cb, int *good, const char *mntopts,
! int flags, int n_threads)
! {
! int i, error = 0;
! mount_task_q_t *task_queue = NULL;
! tpool_t *t;
!
! if (cb->cb_used == 0)
! return (0);
!
! if (n_threads > cb->cb_used)
! n_threads = cb->cb_used;
!
! if ((error = mount_task_q_init(cb->cb_used, cb->cb_handles,
! mntopts, flags, &task_queue)) != 0) {
! assert(task_queue == NULL);
! return (error);
! }
!
! t = tpool_create(1, n_threads, 0, NULL);
!
! for (i = 0; i < n_threads; ++i)
! (void) tpool_dispatch(t, mounter, task_queue);
!
! tpool_wait(t);
! for (i = 0; i < cb->cb_used; ++i) {
! good[i] = !task_queue->task[i].error;
! if (!good[i]) {
! zfs_handle_t *hdl = task_queue->error_zh;
! zfs_error_aux(hdl->zfs_hdl,
! strerror(task_queue->task[i].error));
! (void) zfs_error_fmt(hdl->zfs_hdl, EZFS_MOUNTFAILED,
! dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
! task_queue->task[i].zh->zfs_name);
! }
! }
! tpool_destroy(t);
!
! if (task_queue->error) {
! zfs_handle_t *hdl = task_queue->error_zh;
! /*
! * Tell ZFS!
! */
! zfs_error_aux(hdl->zfs_hdl,
! strerror(error ? error : task_queue->error));
! error = zfs_error_fmt(hdl->zfs_hdl, EZFS_MOUNTFAILED,
! dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
! error ? "datasets" : hdl->zfs_name);
! }
! if (task_queue)
! mount_task_q_fini(task_queue);
!
! return (error);
! }
!
int
! zpool_enable_datasets_ex(zpool_handle_t *zhp, const char *mntopts, int flags,
! int n_threads)
{
get_all_cb_t cb = { 0 };
libzfs_handle_t *hdl = zhp->zpool_hdl;
zfs_handle_t *zfsp;
int i, ret = -1;
int *good;
+ sa_init_selective_arg_t sharearg;
/*
* Gather all non-snap datasets within the pool.
*/
if ((zfsp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_DATASET)) == NULL)
*** 1195,1212 ****
--- 1677,1712 ----
if ((good = zfs_alloc(zhp->zpool_hdl,
cb.cb_used * sizeof (int))) == NULL)
goto out;
ret = 0;
+ if (n_threads < 2) {
for (i = 0; i < cb.cb_used; i++) {
if (zfs_mount(cb.cb_handles[i], mntopts, flags) != 0)
ret = -1;
else
good[i] = 1;
}
+ } else {
+ ret = parallel_mount(&cb, good, mntopts, flags, n_threads);
+ }
/*
+ * Initilialize libshare SA_INIT_SHARE_API_SELECTIVE here
+ * to avoid unneccesary load/unload of the libshare API
+ * per shared dataset downstream.
+ */
+ sharearg.zhandle_arr = cb.cb_handles;
+ sharearg.zhandle_len = cb.cb_used;
+ ret = zfs_init_libshare_arg(hdl, SA_INIT_SHARE_API_SELECTIVE,
+ &sharearg);
+ if (ret != 0) {
+ free(good);
+ goto out;
+ }
+
+ /*
* Then share all the ones that need to be shared. This needs
* to be a separate pass in order to avoid excessive reloading
* of the configuration. Good should never be NULL since
* zfs_alloc is supposed to exit if memory isn't available.
*/
*** 1223,1252 ****
free(cb.cb_handles);
return (ret);
}
- static int
- mountpoint_compare(const void *a, const void *b)
- {
- const char *mounta = *((char **)a);
- const char *mountb = *((char **)b);
-
- return (strcmp(mountb, mounta));
- }
-
- /* alias for 2002/240 */
- #pragma weak zpool_unmount_datasets = zpool_disable_datasets
- /*
- * Unshare and unmount all datasets within the given pool. We don't want to
- * rely on traversing the DSL to discover the filesystems within the pool,
- * because this may be expensive (if not all of them are mounted), and can fail
- * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and
- * gather all the filesystems that are currently mounted.
- */
int
! zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
{
int used, alloc;
struct mnttab entry;
size_t namelen;
char **mountpoints = NULL;
--- 1723,1734 ----
free(cb.cb_handles);
return (ret);
}
int
! zpool_disable_datasets_ex(zpool_handle_t *zhp, boolean_t force, int n_threads)
{
int used, alloc;
struct mnttab entry;
size_t namelen;
char **mountpoints = NULL;
*** 1344,1373 ****
for (i = 0; i < used; i++) {
zfs_share_proto_t *curr_proto;
for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
curr_proto++) {
if (is_shared(hdl, mountpoints[i], *curr_proto) &&
! unshare_one(hdl, mountpoints[i],
! mountpoints[i], *curr_proto) != 0)
goto out;
}
}
/*
* Now unmount everything, removing the underlying directories as
* appropriate.
*/
for (i = 0; i < used; i++) {
if (unmount_one(hdl, mountpoints[i], flags) != 0)
goto out;
}
!
for (i = 0; i < used; i++) {
if (datasets[i])
remove_mountpoint(datasets[i]);
}
-
ret = 0;
out:
for (i = 0; i < used; i++) {
if (datasets[i])
zfs_close(datasets[i]);
--- 1826,1859 ----
for (i = 0; i < used; i++) {
zfs_share_proto_t *curr_proto;
for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
curr_proto++) {
if (is_shared(hdl, mountpoints[i], *curr_proto) &&
! unshare_one(hdl, mountpoints[i], mountpoints[i],
! *curr_proto) != 0)
goto out;
}
}
/*
* Now unmount everything, removing the underlying directories as
* appropriate.
*/
+ if (n_threads < 2) {
for (i = 0; i < used; i++) {
if (unmount_one(hdl, mountpoints[i], flags) != 0)
goto out;
}
! } else {
! if (parallel_unmount(hdl, used, (const char **)mountpoints,
! flags, n_threads) != 0)
! goto out;
! }
for (i = 0; i < used; i++) {
if (datasets[i])
remove_mountpoint(datasets[i]);
}
ret = 0;
out:
for (i = 0; i < used; i++) {
if (datasets[i])
zfs_close(datasets[i]);
*** 1375,1380 ****
--- 1861,1896 ----
}
free(datasets);
free(mountpoints);
return (ret);
+ }
+
+ /*
+ * Mount and share all datasets within the given pool. This assumes that no
+ * datasets within the pool are currently mounted. Because users can create
+ * complicated nested hierarchies of mountpoints, we first gather all the
+ * datasets and mountpoints within the pool, and sort them by mountpoint. Once
+ * we have the list of all filesystems, we iterate over them in order and mount
+ * and/or share each one.
+ */
+ #pragma weak zpool_mount_datasets = zpool_enable_datasets
+ int
+ zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
+ {
+ return (zpool_enable_datasets_ex(zhp, mntopts, flags, 1));
+ }
+
+ /* alias for 2002/240 */
+ #pragma weak zpool_unmount_datasets = zpool_disable_datasets
+ /*
+ * Unshare and unmount all datasets within the given pool. We don't want to
+ * rely on traversing the DSL to discover the filesystems within the pool,
+ * because this may be expensive (if not all of them are mounted), and can fail
+ * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and
+ * gather all the filesystems that are currently mounted.
+ */
+ int
+ zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
+ {
+ return (zpool_disable_datasets_ex(zhp, force, 1));
}