Skip to content

Commit

Permalink
zpool import -d to specify device path
Browse files Browse the repository at this point in the history
When we know which devices have the pool we are looking for, sometime
it's better if we can directly pass those device paths to zpool import
instead of letting it to search through all unrelated stuff, which might
take a lot of time if you have hundreds of disks.

This patch allows option -d <dev_path> to zpool import. You can have
multiple pairs of -d <dev_path>, and zpool import will only search
through those devices. For example:

    zpool import -d /dev/sda -d /dev/sdb

Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Chunwei Chen <david.chen@nutanix.com>
Closes #7077
  • Loading branch information
tuxoko authored and behlendorf committed Jan 26, 2018
1 parent f55a875 commit 522db29
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 48 deletions.
1 change: 1 addition & 0 deletions lib/libspl/include/libgen.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#define _LIBSPL_LIBGEN_H

#include <sys/types.h>
#include_next <libgen.h>

extern int mkdirp(const char *, mode_t);

Expand Down
166 changes: 130 additions & 36 deletions lib/libzfs/libzfs_import.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <dirent.h>
#include <errno.h>
#include <libintl.h>
#include <libgen.h>
#ifdef HAVE_LIBUDEV
#include <libudev.h>
#include <sched.h>
Expand Down Expand Up @@ -1676,6 +1677,120 @@ zpool_clear_label(int fd)
return (0);
}

static void
zpool_find_import_scan_add_slice(libzfs_handle_t *hdl, pthread_mutex_t *lock,
avl_tree_t *cache, char *path, const char *name, int order)
{
avl_index_t where;
rdsk_node_t *slice;

slice = zfs_alloc(hdl, sizeof (rdsk_node_t));
if (asprintf(&slice->rn_name, "%s/%s", path, name) == -1) {
free(slice);
return;
}
slice->rn_vdev_guid = 0;
slice->rn_lock = lock;
slice->rn_avl = cache;
slice->rn_hdl = hdl;
slice->rn_order = order + IMPORT_ORDER_SCAN_OFFSET;
slice->rn_labelpaths = B_FALSE;

pthread_mutex_lock(lock);
if (avl_find(cache, slice, &where)) {
free(slice->rn_name);
free(slice);
} else {
avl_insert(cache, slice, where);
}
pthread_mutex_unlock(lock);
}

static int
zpool_find_import_scan_dir(libzfs_handle_t *hdl, pthread_mutex_t *lock,
avl_tree_t *cache, char *dir, int order)
{
int error;
char path[MAXPATHLEN];
struct dirent64 *dp;
DIR *dirp;

if (realpath(dir, path) == NULL) {
error = errno;
if (error == ENOENT)
return (0);

zfs_error_aux(hdl, strerror(error));
(void) zfs_error_fmt(hdl, EZFS_BADPATH, dgettext(
TEXT_DOMAIN, "cannot resolve path '%s'"), dir);
return (error);
}

dirp = opendir(path);
if (dirp == NULL) {
error = errno;
zfs_error_aux(hdl, strerror(error));
(void) zfs_error_fmt(hdl, EZFS_BADPATH,
dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
return (error);
}

while ((dp = readdir64(dirp)) != NULL) {
const char *name = dp->d_name;
if (name[0] == '.' &&
(name[1] == 0 || (name[1] == '.' && name[2] == 0)))
continue;

zpool_find_import_scan_add_slice(hdl, lock, cache, path, name,
order);
}

(void) closedir(dirp);
return (0);
}

static int
zpool_find_import_scan_path(libzfs_handle_t *hdl, pthread_mutex_t *lock,
avl_tree_t *cache, char *dir, int order)
{
int error = 0;
char path[MAXPATHLEN];
char *d, *b;
char *dpath, *name;

/*
* Seperate the directory part and last part of the
* path. We do this so that we can get the realpath of
* the directory. We don't get the realpath on the
* whole path because if it's a symlink, we want the
* path of the symlink not where it points to.
*/
d = zfs_strdup(hdl, dir);
b = zfs_strdup(hdl, dir);
dpath = dirname(d);
name = basename(b);

if (realpath(dpath, path) == NULL) {
error = errno;
if (error == ENOENT) {
error = 0;
goto out;
}

zfs_error_aux(hdl, strerror(error));
(void) zfs_error_fmt(hdl, EZFS_BADPATH, dgettext(
TEXT_DOMAIN, "cannot resolve path '%s'"), dir);
goto out;
}

zpool_find_import_scan_add_slice(hdl, lock, cache, path, name, order);

out:
free(b);
free(d);
return (error);
}

/*
* Scan a list of directories for zfs devices.
*/
Expand All @@ -1694,11 +1809,9 @@ zpool_find_import_scan(libzfs_handle_t *hdl, pthread_mutex_t *lock,
offsetof(rdsk_node_t, rn_node));

for (i = 0; i < dirs; i++) {
char path[MAXPATHLEN];
struct dirent64 *dp;
DIR *dirp;
struct stat sbuf;

if (realpath(dir[i], path) == NULL) {
if (stat(dir[i], &sbuf) != 0) {
error = errno;
if (error == ENOENT)
continue;
Expand All @@ -1709,39 +1822,20 @@ zpool_find_import_scan(libzfs_handle_t *hdl, pthread_mutex_t *lock,
goto error;
}

dirp = opendir(path);
if (dirp == NULL) {
error = errno;
zfs_error_aux(hdl, strerror(error));
(void) zfs_error_fmt(hdl, EZFS_BADPATH,
dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
goto error;
}

while ((dp = readdir64(dirp)) != NULL) {
const char *name = dp->d_name;
if (name[0] == '.' &&
(name[1] == 0 || (name[1] == '.' && name[2] == 0)))
continue;

slice = zfs_alloc(hdl, sizeof (rdsk_node_t));
error = asprintf(&slice->rn_name, "%s/%s", path, name);
if (error == -1) {
free(slice);
continue;
}
slice->rn_vdev_guid = 0;
slice->rn_lock = lock;
slice->rn_avl = cache;
slice->rn_hdl = hdl;
slice->rn_order = i + IMPORT_ORDER_SCAN_OFFSET;
slice->rn_labelpaths = B_FALSE;
pthread_mutex_lock(lock);
avl_add(cache, slice);
pthread_mutex_unlock(lock);
/*
* If dir[i] is a directory, we walk through it and add all
* the entry to the cache. If it's not a directory, we just
* add it to the cache.
*/
if (S_ISDIR(sbuf.st_mode)) {
if ((error = zpool_find_import_scan_dir(hdl, lock,
cache, dir[i], i)) != 0)
goto error;
} else {
if ((error = zpool_find_import_scan_path(hdl, lock,
cache, dir[i], i)) != 0)
goto error;
}

(void) closedir(dirp);
}

*slice_cache = cache;
Expand Down
30 changes: 18 additions & 12 deletions man/man8/zpool.8
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,21 @@
.Nm
.Cm import
.Op Fl D
.Op Fl d Ar dir
.Op Fl d Ar dir Ns | Ns device
.Nm
.Cm import
.Fl a
.Op Fl DflmN
.Op Fl F Oo Fl n Oc Oo Fl T Oc Oo Fl X Oc
.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir
.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir Ns | Ns device
.Op Fl o Ar mntopts
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Op Fl R Ar root
.Nm
.Cm import
.Op Fl Dflm
.Op Fl F Oo Fl n Oc Oo Fl T Oc Oo Fl X Oc
.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir
.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir Ns | Ns device
.Op Fl o Ar mntopts
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Op Fl R Ar root
Expand Down Expand Up @@ -1137,7 +1137,7 @@ performed.
.Nm
.Cm import
.Op Fl D
.Op Fl d Ar dir
.Op Fl d Ar dir Ns | Ns device
.Xc
Lists pools available to import.
If the
Expand Down Expand Up @@ -1168,8 +1168,10 @@ pool property.
This
.Ar cachefile
is used instead of searching for devices.
.It Fl d Ar dir
Searches for devices or files in
.It Fl d Ar dir Ns | Ns Ar device
Uses
.Ar device
or searches for devices or files in
.Ar dir .
The
.Fl d
Expand All @@ -1183,7 +1185,7 @@ Lists destroyed pools only.
.Fl a
.Op Fl DflmN
.Op Fl F Oo Fl n Oc Oo Fl T Oc Oo Fl X Oc
.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir
.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir Ns | Ns device
.Op Fl o Ar mntopts
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Op Fl R Ar root
Expand All @@ -1209,8 +1211,10 @@ pool property.
This
.Ar cachefile
is used instead of searching for devices.
.It Fl d Ar dir
Searches for devices or files in
.It Fl d Ar dir Ns | Ns Ar device
Uses
.Ar device
or searches for devices or files in
.Ar dir .
The
.Fl d
Expand Down Expand Up @@ -1304,7 +1308,7 @@ health of your pool and should only be used as a last resort.
.Cm import
.Op Fl Dflm
.Op Fl F Oo Fl n Oc Oo Fl t Oc Oo Fl T Oc Oo Fl X Oc
.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir
.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir Ns | Ns device
.Op Fl o Ar mntopts
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Op Fl R Ar root
Expand Down Expand Up @@ -1338,8 +1342,10 @@ pool property.
This
.Ar cachefile
is used instead of searching for devices.
.It Fl d Ar dir
Searches for devices or files in
.It Fl d Ar dir Ns | Ns Ar device
Uses
.Ar device
or searches for devices or files in
.Ar dir .
The
.Fl d
Expand Down
1 change: 1 addition & 0 deletions tests/runfiles/linux.run
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ tests = ['zpool_import_001_pos', 'zpool_import_002_pos',
'zpool_import_006_pos', 'zpool_import_007_pos', 'zpool_import_008_pos',
'zpool_import_009_neg', 'zpool_import_010_pos', 'zpool_import_011_neg',
'zpool_import_012_pos', 'zpool_import_013_neg', 'zpool_import_014_pos',
'zpool_import_015_pos',
'zpool_import_features_001_pos', 'zpool_import_features_002_neg',
'zpool_import_features_003_pos','zpool_import_missing_001_pos',
'zpool_import_missing_002_pos',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ dist_pkgdata_SCRIPTS = \
zpool_import_012_pos.ksh \
zpool_import_013_neg.ksh \
zpool_import_014_pos.ksh \
zpool_import_015_pos.ksh \
zpool_import_all_001_pos.ksh \
zpool_import_features_001_pos.ksh \
zpool_import_features_002_neg.ksh \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/bin/ksh -p
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#

#
# Copyright (c) 2018 by Nutanix. All rights reserved.
#

. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.cfg

#
# DESCRIPTION:
# Make sure zpool import -d <device> works.
#
# STRATEGY:
# 1. Create test pool A.
# 2. Export pool A.
# 3. Verify 'import -d <device>' works
#

verify_runnable "global"

function cleanup
{
destroy_pool $TESTPOOL1

log_must rm $VDEV0 $VDEV1
log_must truncate -s $FILE_SIZE $VDEV0 $VDEV1
}

log_assert "Pool can be imported with '-d <device>'"
log_onexit cleanup

log_must zpool create $TESTPOOL1 $VDEV0 $VDEV1
log_must zpool export $TESTPOOL1

log_must zpool import -d $VDEV0 -d $VDEV1 $TESTPOOL1
log_must zpool export $TESTPOOL1

# mix -d <dir> and -d <device>
log_must mkdir $DEVICE_DIR/test_dir
log_must ln -s $VDEV0 $DEVICE_DIR/test_dir/disk
log_must zpool import -d $DEVICE_DIR/test_dir -d $VDEV1 $TESTPOOL1

log_pass "Pool can be imported with '-d <device>'"

0 comments on commit 522db29

Please sign in to comment.