6362672 import gets confused about overlapping slices
6364582 need to fixup paths if they've changed
--- a/usr/src/cmd/truss/codes.c Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/cmd/truss/codes.c Mon Jan 30 21:34:28 2006 -0800
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -891,6 +891,8 @@
"zfs_cmd_t" },
{ (uint_t)ZFS_IOC_VDEV_DETACH, "ZFS_IOC_VDEV_DETACH",
"zfs_cmd_t" },
+ { (uint_t)ZFS_IOC_VDEV_SETPATH, "ZFS_IOC_VDEV_SETPATH",
+ "zfs_cmd_t" },
{ (uint_t)ZFS_IOC_OBJSET_STATS, "ZFS_IOC_OBJSET_STATS",
"zfs_cmd_t" },
{ (uint_t)ZFS_IOC_DATASET_LIST_NEXT, "ZFS_IOC_DATASET_LIST_NEXT",
--- a/usr/src/cmd/zpool/zpool_main.c Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/cmd/zpool/zpool_main.c Mon Jan 30 21:34:28 2006 -0800
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -261,7 +261,7 @@
}
void
-print_vdev_tree(const char *name, nvlist_t *nv, int indent)
+print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent)
{
nvlist_t **child;
uint_t c, children;
@@ -275,8 +275,8 @@
return;
for (c = 0; c < children; c++) {
- vname = vdev_get_name(child[c]);
- print_vdev_tree(vname, child[c], indent + 2);
+ vname = zpool_vdev_name(zhp, child[c]);
+ print_vdev_tree(zhp, vname, child[c], indent + 2);
free(vname);
}
}
@@ -364,8 +364,8 @@
(void) printf(gettext("would update '%s' to the following "
"configuration:\n"), zpool_get_name(zhp));
- print_vdev_tree(poolname, poolnvroot, 0);
- print_vdev_tree(NULL, nvroot, 0);
+ print_vdev_tree(zhp, poolname, poolnvroot, 0);
+ print_vdev_tree(zhp, NULL, nvroot, 0);
ret = 0;
} else {
@@ -527,7 +527,7 @@
(void) printf(gettext("would create '%s' with the "
"following layout:\n\n"), poolname);
- print_vdev_tree(poolname, nvroot, 0);
+ print_vdev_tree(NULL, poolname, nvroot, 0);
ret = 0;
} else {
@@ -691,9 +691,9 @@
* name column.
*/
static int
-max_width(nvlist_t *nv, int depth, int max)
+max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max)
{
- char *name = vdev_get_name(nv);
+ char *name = zpool_vdev_name(zhp, nv);
nvlist_t **child;
uint_t c, children;
int ret;
@@ -708,7 +708,7 @@
return (max);
for (c = 0; c < children; c++)
- if ((ret = max_width(child[c], depth + 2, max)) > max)
+ if ((ret = max_width(zhp, child[c], depth + 2, max)) > max)
max = ret;
return (max);
@@ -766,7 +766,7 @@
return;
for (c = 0; c < children; c++) {
- vname = vdev_get_name(child[c]);
+ vname = zpool_vdev_name(NULL, child[c]);
print_import_config(vname, child[c],
namewidth, depth + 2);
free(vname);
@@ -875,7 +875,7 @@
(void) printf(gettext("config:\n\n"));
- namewidth = max_width(nvroot, 0, 0);
+ namewidth = max_width(NULL, nvroot, 0, 0);
if (namewidth < 10)
namewidth = 10;
print_import_config(name, nvroot, namewidth, 0);
@@ -1192,8 +1192,8 @@
* is a verbose output, and we don't want to display the toplevel pool stats.
*/
void
-print_vdev_stats(const char *name, nvlist_t *oldnv, nvlist_t *newnv,
- iostat_cbdata_t *cb, int depth)
+print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv,
+ nvlist_t *newnv, iostat_cbdata_t *cb, int depth)
{
nvlist_t **oldchild, **newchild;
uint_t c, children;
@@ -1260,8 +1260,8 @@
return;
for (c = 0; c < children; c++) {
- vname = vdev_get_name(newchild[c]);
- print_vdev_stats(vname, oldnv ? oldchild[c] : NULL,
+ vname = zpool_vdev_name(zhp, newchild[c]);
+ print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL,
newchild[c], cb, depth + 2);
free(vname);
}
@@ -1308,7 +1308,7 @@
/*
* Print out the statistics for the pool.
*/
- print_vdev_stats(zpool_get_name(zhp), oldnvroot, newnvroot, cb, 0);
+ print_vdev_stats(zhp, zpool_get_name(zhp), oldnvroot, newnvroot, cb, 0);
if (cb->cb_verbose)
print_iostat_separator(cb);
@@ -1328,7 +1328,7 @@
if (!cb->cb_verbose)
cb->cb_namewidth = strlen(zpool_get_name(zhp));
else
- cb->cb_namewidth = max_width(nvroot, 0, 0);
+ cb->cb_namewidth = max_width(zhp, nvroot, 0, 0);
}
/*
@@ -2158,7 +2158,8 @@
* Print out configuration state as requested by status_callback.
*/
void
-print_status_config(const char *name, nvlist_t *nv, int namewidth, int depth)
+print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
+ int namewidth, int depth)
{
nvlist_t **child;
uint_t c, children;
@@ -2214,8 +2215,8 @@
(void) printf("\n");
for (c = 0; c < children; c++) {
- vname = vdev_get_name(child[c]);
- print_status_config(vname, child[c],
+ vname = zpool_vdev_name(zhp, child[c]);
+ print_status_config(zhp, vname, child[c],
namewidth, depth + 2);
free(vname);
}
@@ -2352,14 +2353,15 @@
(void) printf(gettext(" scrub: "));
print_scrub_status(nvroot);
- namewidth = max_width(nvroot, 0, 0);
+ namewidth = max_width(zhp, nvroot, 0, 0);
if (namewidth < 10)
namewidth = 10;
(void) printf(gettext("config:\n\n"));
(void) printf(gettext("\t%-*s %-8s %5s %5s %5s\n"), namewidth,
"NAME", "STATE", "READ", "WRITE", "CKSUM");
- print_status_config(zpool_get_name(zhp), nvroot, namewidth, 0);
+ print_status_config(zhp, zpool_get_name(zhp), nvroot,
+ namewidth, 0);
} else {
(void) printf(gettext("config: The configuration cannot be "
"determined.\n"));
--- a/usr/src/cmd/zpool/zpool_util.c Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/cmd/zpool/zpool_util.c Mon Jan 30 21:34:28 2006 -0800
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -78,33 +78,3 @@
gettext("internal error: out of memory\n"));
exit(1);
}
-
-/*
- * Given a vdev, return the name to display in iostat. If the vdev has a path,
- * we use that, stripping off any leading "/dev/dsk/"; if not, we use the type.
- * We also check if this is a whole disk, in which case we strip off the
- * trailing 's0' slice name.
- */
-char *
-vdev_get_name(nvlist_t *nv)
-{
- char *path;
- uint64_t wholedisk;
-
- if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) {
-
- if (strncmp(path, "/dev/dsk/", 9) == 0)
- path += 9;
-
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
- &wholedisk) == 0 && wholedisk) {
- char *tmp = safe_strdup(path);
- tmp[strlen(path) - 2] = '\0';
- return (tmp);
- }
- } else {
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &path) == 0);
- }
-
- return (safe_strdup(path));
-}
--- a/usr/src/cmd/zpool/zpool_util.h Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/cmd/zpool/zpool_util.h Mon Jan 30 21:34:28 2006 -0800
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -43,8 +43,6 @@
char *safe_strdup(const char *);
void no_memory(void);
-char *vdev_get_name(nvlist_t *nv);
-
/*
* Virtual device functions
*/
--- a/usr/src/lib/libzfs/common/libzfs.h Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/lib/libzfs/common/libzfs.h Mon Jan 30 21:34:28 2006 -0800
@@ -144,6 +144,11 @@
extern nvlist_t *zpool_find_import(int argc, char **argv);
/*
+ * Miscellaneous pool functions
+ */
+extern char *zpool_vdev_name(zpool_handle_t *, nvlist_t *);
+
+/*
* Basic handle manipulations. These functions do not create or destroy the
* underlying datasets, only the references to them.
*/
--- a/usr/src/lib/libzfs/common/libzfs_import.c Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/lib/libzfs/common/libzfs_import.c Mon Jan 30 21:34:28 2006 -0800
@@ -108,6 +108,7 @@
devid_str_free(minor);
devid_free(devid);
}
+ (void) close(fd);
return (ret);
}
@@ -123,8 +124,9 @@
nvlist_t **child;
uint_t c, children;
uint64_t guid;
- name_entry_t *ne;
- char *devid;
+ name_entry_t *ne, *best;
+ char *path, *devid;
+ int matched;
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
&child, &children) == 0) {
@@ -137,19 +139,55 @@
* This is a leaf (file or disk) vdev. In either case, go through
* the name list and see if we find a matching guid. If so, replace
* the path and see if we can calculate a new devid.
+ *
+ * There may be multiple names associated with a particular guid, in
+ * which case we have overlapping slices or multiple paths to the same
+ * disk. If this is the case, then we want to pick the path that is
+ * the most similar to the original, where "most similar" is the number
+ * of matching characters starting from the end of the path. This will
+ * preserve slice numbers even if the disks have been reorganized, and
+ * will also catch preferred disk names if multiple paths exist.
*/
verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0);
+ if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
+ path = NULL;
- for (ne = names; ne != NULL; ne = ne->ne_next)
- if (ne->ne_guid == guid)
- break;
+ matched = 0;
+ best = NULL;
+ for (ne = names; ne != NULL; ne = ne->ne_next) {
+ if (ne->ne_guid == guid) {
+ const char *src, *dst;
+ int count;
+
+ if (path == NULL) {
+ best = ne;
+ break;
+ }
- if (ne == NULL)
+ src = ne->ne_name + strlen(ne->ne_name) - 1;
+ dst = path + strlen(path) - 1;
+ for (count = 0; src >= ne->ne_name && dst >= path;
+ src--, dst--, count++)
+ if (*src != *dst)
+ break;
+
+ /*
+ * At this point, 'count' is the number of characters
+ * matched from the end.
+ */
+ if (count > matched || best == NULL) {
+ best = ne;
+ matched = count;
+ }
+ }
+ }
+
+ if (best == NULL)
return;
- verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, ne->ne_name) == 0);
+ verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, best->ne_name) == 0);
- if ((devid = get_devid(ne->ne_name)) == NULL) {
+ if ((devid = get_devid(best->ne_name)) == NULL) {
(void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID);
} else {
verify(nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) == 0);
--- a/usr/src/lib/libzfs/common/libzfs_pool.c Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c Mon Jan 30 21:34:28 2006 -0800
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -1150,3 +1150,145 @@
zfs_close(zfp);
return (ret);
}
+
+/*
+ * Convert from a devid string to a path.
+ */
+static char *
+devid_to_path(char *devid_str)
+{
+ ddi_devid_t devid;
+ char *minor;
+ char *path;
+ devid_nmlist_t *list = NULL;
+ int ret;
+
+ if (devid_str_decode(devid_str, &devid, &minor) != 0)
+ return (NULL);
+
+ ret = devid_deviceid_to_nmlist("/dev", devid, minor, &list);
+
+ devid_str_free(minor);
+ devid_free(devid);
+
+ if (ret != 0)
+ return (NULL);
+
+ path = zfs_strdup(list[0].devname);
+ devid_free_nmlist(list);
+
+ return (path);
+}
+
+/*
+ * Convert from a path to a devid string.
+ */
+static char *
+path_to_devid(const char *path)
+{
+ int fd;
+ ddi_devid_t devid;
+ char *minor, *ret;
+
+ if ((fd = open(path, O_RDONLY)) < 0)
+ return (NULL);
+
+ minor = NULL;
+ ret = NULL;
+ if (devid_get(fd, &devid) == 0) {
+ if (devid_get_minor_name(fd, &minor) == 0)
+ ret = devid_str_encode(devid, minor);
+ if (minor != NULL)
+ devid_str_free(minor);
+ devid_free(devid);
+ }
+ (void) close(fd);
+
+ return (ret);
+}
+
+/*
+ * Issue the necessary ioctl() to update the stored path value for the vdev. We
+ * ignore any failure here, since a common case is for an unprivileged user to
+ * type 'zpool status', and we'll display the correct information anyway.
+ */
+static void
+set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path)
+{
+ zfs_cmd_t zc = { 0 };
+
+ (void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ (void) strncpy(zc.zc_prop_value, path, sizeof (zc.zc_prop_value));
+ verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
+ &zc.zc_pool_guid) == 0);
+
+ (void) ioctl(zfs_fd, ZFS_IOC_VDEV_SETPATH, &zc);
+}
+
+/*
+ * Given a vdev, return the name to display in iostat. If the vdev has a path,
+ * we use that, stripping off any leading "/dev/dsk/"; if not, we use the type.
+ * We also check if this is a whole disk, in which case we strip off the
+ * trailing 's0' slice name.
+ *
+ * This routine is also responsible for identifying when disks have been
+ * reconfigured in a new location. The kernel will have opened the device by
+ * devid, but the path will still refer to the old location. To catch this, we
+ * first do a path -> devid translation (which is fast for the common case). If
+ * the devid matches, we're done. If not, we do a reverse devid -> path
+ * translation and issue the appropriate ioctl() to update the path of the vdev.
+ * If 'zhp' is NULL, then this is an exported pool, and we don't need to do any
+ * of these checks.
+ */
+char *
+zpool_vdev_name(zpool_handle_t *zhp, nvlist_t *nv)
+{
+ char *path, *devid;
+ uint64_t wholedisk;
+
+ if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) {
+
+ if (zhp != NULL &&
+ nvlist_lookup_string(nv, ZPOOL_CONFIG_DEVID, &devid) == 0) {
+ /*
+ * Determine if the current path is correct.
+ */
+ char *newdevid = path_to_devid(path);
+
+ if (newdevid == NULL ||
+ strcmp(devid, newdevid) != 0) {
+ char *newpath;
+
+ if ((newpath = devid_to_path(devid)) != NULL) {
+ /*
+ * Update the path appropriately.
+ */
+ set_path(zhp, nv, newpath);
+ verify(nvlist_add_string(nv,
+ ZPOOL_CONFIG_PATH, newpath) == 0);
+ free(newpath);
+ verify(nvlist_lookup_string(nv,
+ ZPOOL_CONFIG_PATH, &path) == 0);
+ }
+
+ if (newdevid)
+ devid_str_free(newdevid);
+ }
+
+ }
+
+ if (strncmp(path, "/dev/dsk/", 9) == 0)
+ path += 9;
+
+ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
+ &wholedisk) == 0 && wholedisk) {
+ char *tmp = zfs_strdup(path);
+ tmp[strlen(path) - 2] = '\0';
+ return (tmp);
+ }
+ } else {
+ verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &path) == 0);
+ }
+
+ return (zfs_strdup(path));
+}
--- a/usr/src/lib/libzfs/spec/libzfs.spec Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/lib/libzfs/spec/libzfs.spec Mon Jan 30 21:34:28 2006 -0800
@@ -1,5 +1,5 @@
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# CDDL HEADER START
@@ -335,3 +335,7 @@
function zpool_vdev_detach
version SUNWprivate_1.1
end
+
+function zpool_vdev_name
+version SUNWprivate_1.1
+end
--- a/usr/src/uts/common/fs/zfs/spa.c Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/uts/common/fs/zfs/spa.c Mon Jan 30 21:34:28 2006 -0800
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -1202,6 +1202,33 @@
}
/*
+ * Update the stored path for this vdev. Dirty the vdev configuration, relying
+ * on spa_vdev_enter/exit() to synchronize the labels and cache.
+ */
+int
+spa_vdev_setpath(spa_t *spa, uint64_t guid, const char *newpath)
+{
+ vdev_t *rvd, *vd;
+ uint64_t txg;
+
+ rvd = spa->spa_root_vdev;
+
+ txg = spa_vdev_enter(spa);
+
+ if ((vd = vdev_lookup_by_guid(rvd, guid)) == NULL)
+ return (spa_vdev_exit(spa, NULL, txg, ENOENT));
+
+ spa_strfree(vd->vdev_path);
+ vd->vdev_path = spa_strdup(newpath);
+
+ spa_config_set(spa, spa_config_generate(spa, rvd, txg, 0));
+
+ vdev_config_dirty(vd->vdev_top);
+
+ return (spa_vdev_exit(spa, NULL, txg, 0));
+}
+
+/*
* ==========================================================================
* SPA Scrubbing
* ==========================================================================
--- a/usr/src/uts/common/fs/zfs/sys/spa.h Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/uts/common/fs/zfs/sys/spa.h Mon Jan 30 21:34:28 2006 -0800
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -307,6 +307,7 @@
extern int spa_vdev_detach(spa_t *spa, const char *path, uint64_t guid,
int replace_done);
extern void spa_vdev_replace_done(spa_t *spa);
+extern int spa_vdev_setpath(spa_t *spa, uint64_t guid, const char *newpath);
/* scrubbing */
extern int spa_scrub(spa_t *spa, pool_scrub_type_t type, boolean_t force);
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c Mon Jan 30 21:34:28 2006 -0800
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -621,6 +621,25 @@
}
static int
+zfs_ioc_vdev_setpath(zfs_cmd_t *zc)
+{
+ spa_t *spa;
+ char *path = zc->zc_prop_value;
+ uint64_t guid = zc->zc_pool_guid;
+ int error;
+
+ error = spa_open(zc->zc_name, &spa, FTAG);
+ if (error != 0)
+ return (error);
+
+ error = spa_vdev_setpath(spa, guid, path);
+
+ spa_close(spa, FTAG);
+ return (error);
+}
+
+
+static int
zfs_get_stats(zfs_cmd_t *zc)
{
char *name = zc->zc_name;
@@ -1075,6 +1094,7 @@
{ zfs_ioc_vdev_offline, zfs_secpolicy_config, pool_name },
{ zfs_ioc_vdev_attach, zfs_secpolicy_config, pool_name },
{ zfs_ioc_vdev_detach, zfs_secpolicy_config, pool_name },
+ { zfs_ioc_vdev_setpath, zfs_secpolicy_config, pool_name },
{ zfs_ioc_objset_stats, zfs_secpolicy_read, dataset_name },
{ zfs_ioc_dataset_list_next, zfs_secpolicy_read, dataset_name },
{ zfs_ioc_snapshot_list_next, zfs_secpolicy_read, dataset_name },
--- a/usr/src/uts/common/sys/fs/zfs.h Mon Jan 30 18:57:13 2006 -0800
+++ b/usr/src/uts/common/sys/fs/zfs.h Mon Jan 30 21:34:28 2006 -0800
@@ -285,6 +285,7 @@
ZFS_IOC_VDEV_OFFLINE,
ZFS_IOC_VDEV_ATTACH,
ZFS_IOC_VDEV_DETACH,
+ ZFS_IOC_VDEV_SETPATH,
ZFS_IOC_OBJSET_STATS,
ZFS_IOC_DATASET_LIST_NEXT,
ZFS_IOC_SNAPSHOT_LIST_NEXT,