summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cddl/compat/opensolaris/include/devid.h27
-rw-r--r--cddl/compat/opensolaris/misc/deviceid.c119
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c53
-rw-r--r--cddl/lib/libzfs/Makefile3
-rw-r--r--compat/opensolaris/include/devid.h27
-rw-r--r--compat/opensolaris/misc/deviceid.c119
-rw-r--r--contrib/opensolaris/cmd/zpool/zpool_vdev.c53
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c171
-rw-r--r--sys/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c171
9 files changed, 680 insertions, 63 deletions
diff --git a/cddl/compat/opensolaris/include/devid.h b/cddl/compat/opensolaris/include/devid.h
index 06839dd..6718ce2 100644
--- a/cddl/compat/opensolaris/include/devid.h
+++ b/cddl/compat/opensolaris/include/devid.h
@@ -27,23 +27,28 @@
#ifndef _OPENSOLARIS_DEVID_H_
#define _OPENSOLARIS_DEVID_H_
-#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/disk.h>
#include <stdlib.h>
-typedef int ddi_devid_t;
+typedef struct ddi_devid {
+ char devid[DISK_IDENT_SIZE];
+} ddi_devid_t;
typedef struct devid_nmlist {
- char *devname;
+ char devname[MAXPATHLEN];
dev_t dev;
} devid_nmlist_t;
-static inline int devid_str_decode(char *devidstr, ddi_devid_t *retdevid, char **retminor_name) { abort(); }
-static inline int devid_deviceid_to_nmlist(char *search_path, ddi_devid_t devid, char *minor_name, devid_nmlist_t **retlist) { abort(); }
-static inline void devid_str_free(char *str) { abort(); }
-static inline void devid_free(ddi_devid_t devid) { abort(); }
-static inline void devid_free_nmlist(devid_nmlist_t *list) { abort(); }
-static inline int devid_get(int fd, ddi_devid_t *retdevid) { return -1; }
-static inline int devid_get_minor_name(int fd, char **retminor_name) { abort(); }
-static inline char *devid_str_encode(ddi_devid_t devid, char *minor_name) { abort(); }
+int devid_str_decode(char *devidstr, ddi_devid_t *retdevid,
+ char **retminor_name);
+int devid_deviceid_to_nmlist(char *search_path, ddi_devid_t devid,
+ char *minor_name, devid_nmlist_t **retlist);
+void devid_str_free(char *str);
+void devid_free(ddi_devid_t devid);
+void devid_free_nmlist(devid_nmlist_t *list);
+int devid_get(int fd, ddi_devid_t *retdevid);
+int devid_get_minor_name(int fd, char **retminor_name);
+char *devid_str_encode(ddi_devid_t devid, char *minor_name);
#endif /* !_OPENSOLARIS_DEVID_H_ */
diff --git a/cddl/compat/opensolaris/misc/deviceid.c b/cddl/compat/opensolaris/misc/deviceid.c
new file mode 100644
index 0000000..f37b1d6
--- /dev/null
+++ b/cddl/compat/opensolaris/misc/deviceid.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <libgeom.h>
+#include <devid.h>
+
+int
+devid_str_decode(char *devidstr, ddi_devid_t *retdevid, char **retminor_name)
+{
+
+ if (strlcpy(retdevid->devid, devidstr, sizeof(retdevid->devid)) >=
+ sizeof(retdevid->devid)) {
+ return (EINVAL);
+ }
+ *retminor_name = strdup("");
+ if (*retminor_name == NULL);
+ return (ENOMEM);
+ return (0);
+}
+
+int
+devid_deviceid_to_nmlist(char *search_path, ddi_devid_t devid, char *minor_name,
+ devid_nmlist_t **retlist)
+{
+ char path[MAXPATHLEN];
+ char *dst;
+
+ if (g_get_name(devid.devid, path, sizeof(path)) == -1)
+ return (errno);
+ *retlist = malloc(sizeof(**retlist));
+ if (*retlist == NULL)
+ return (ENOMEM);
+ if (strlcpy((*retlist)[0].devname, path,
+ sizeof((*retlist)[0].devname)) >= sizeof((*retlist)[0].devname)) {
+ free(*retlist);
+ return (ENAMETOOLONG);
+ }
+ return (0);
+}
+
+void
+devid_str_free(char *str)
+{
+
+ free(str);
+}
+
+void
+devid_free(ddi_devid_t devid)
+{
+ /* Do nothing. */
+}
+
+void
+devid_free_nmlist(devid_nmlist_t *list)
+{
+
+ free(list);
+}
+
+int
+devid_get(int fd, ddi_devid_t *retdevid)
+{
+
+ if (ioctl(fd, DIOCGIDENT, retdevid->devid) == -1)
+ return (errno);
+ if (retdevid->devid[0] == '\0')
+ return (ENOENT);
+ return (0);
+}
+
+int
+devid_get_minor_name(int fd, char **retminor_name)
+{
+
+ *retminor_name = strdup("");
+ if (*retminor_name == NULL)
+ return (ENOMEM);
+ return (0);
+}
+
+char *
+devid_str_encode(ddi_devid_t devid, char *minor_name)
+{
+
+ return (strdup(devid.devid));
+}
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c
index de07723..cfed1f0 100644
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c
+++ b/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c
@@ -159,18 +159,14 @@ out:
static boolean_t
is_provider(const char *name)
{
- off_t mediasize;
int fd;
- fd = open(name, O_RDONLY);
- if (fd == -1)
- return (B_FALSE);
- if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == -1) {
- close(fd);
- return (B_FALSE);
+ fd = g_open(name, 0);
+ if (fd >= 0) {
+ g_close(fd);
+ return (B_TRUE);
}
- close(fd);
- return (B_TRUE);
+ return (B_FALSE);
}
/*
@@ -183,9 +179,11 @@ is_provider(const char *name)
nvlist_t *
make_leaf_vdev(const char *arg)
{
- char path[MAXPATHLEN];
+ char ident[DISK_IDENT_SIZE], path[MAXPATHLEN];
+ struct stat64 statbuf;
nvlist_t *vdev = NULL;
char *type = NULL;
+ boolean_t wholedisk = B_FALSE;
if (strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
strlcpy(path, arg, sizeof (path));
@@ -212,6 +210,41 @@ make_leaf_vdev(const char *arg)
verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK,
(uint64_t)B_FALSE) == 0);
+ /*
+ * For a whole disk, defer getting its devid until after labeling it.
+ */
+ if (1 || (S_ISBLK(statbuf.st_mode) && !wholedisk)) {
+ /*
+ * Get the devid for the device.
+ */
+ int fd;
+ ddi_devid_t devid;
+ char *minor = NULL, *devid_str = NULL;
+
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ (void) fprintf(stderr, gettext("cannot open '%s': "
+ "%s\n"), path, strerror(errno));
+ nvlist_free(vdev);
+ return (NULL);
+ }
+
+ if (devid_get(fd, &devid) == 0) {
+ if (devid_get_minor_name(fd, &minor) == 0 &&
+ (devid_str = devid_str_encode(devid, minor)) !=
+ NULL) {
+ verify(nvlist_add_string(vdev,
+ ZPOOL_CONFIG_DEVID, devid_str) == 0);
+ }
+ if (devid_str != NULL)
+ devid_str_free(devid_str);
+ if (minor != NULL)
+ devid_str_free(minor);
+ devid_free(devid);
+ }
+
+ (void) close(fd);
+ }
+
return (vdev);
}
diff --git a/cddl/lib/libzfs/Makefile b/cddl/lib/libzfs/Makefile
index 9c2921b..a8f2c01 100644
--- a/cddl/lib/libzfs/Makefile
+++ b/cddl/lib/libzfs/Makefile
@@ -9,7 +9,8 @@ LIB= zfs
DPADD= ${LIBUTIL}
LDADD= -lutil
-SRCS= mnttab.c \
+SRCS= deviceid.c \
+ mnttab.c \
mkdirp.c \
zmount.c \
fsshare.c \
diff --git a/compat/opensolaris/include/devid.h b/compat/opensolaris/include/devid.h
index 06839dd..6718ce2 100644
--- a/compat/opensolaris/include/devid.h
+++ b/compat/opensolaris/include/devid.h
@@ -27,23 +27,28 @@
#ifndef _OPENSOLARIS_DEVID_H_
#define _OPENSOLARIS_DEVID_H_
-#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/disk.h>
#include <stdlib.h>
-typedef int ddi_devid_t;
+typedef struct ddi_devid {
+ char devid[DISK_IDENT_SIZE];
+} ddi_devid_t;
typedef struct devid_nmlist {
- char *devname;
+ char devname[MAXPATHLEN];
dev_t dev;
} devid_nmlist_t;
-static inline int devid_str_decode(char *devidstr, ddi_devid_t *retdevid, char **retminor_name) { abort(); }
-static inline int devid_deviceid_to_nmlist(char *search_path, ddi_devid_t devid, char *minor_name, devid_nmlist_t **retlist) { abort(); }
-static inline void devid_str_free(char *str) { abort(); }
-static inline void devid_free(ddi_devid_t devid) { abort(); }
-static inline void devid_free_nmlist(devid_nmlist_t *list) { abort(); }
-static inline int devid_get(int fd, ddi_devid_t *retdevid) { return -1; }
-static inline int devid_get_minor_name(int fd, char **retminor_name) { abort(); }
-static inline char *devid_str_encode(ddi_devid_t devid, char *minor_name) { abort(); }
+int devid_str_decode(char *devidstr, ddi_devid_t *retdevid,
+ char **retminor_name);
+int devid_deviceid_to_nmlist(char *search_path, ddi_devid_t devid,
+ char *minor_name, devid_nmlist_t **retlist);
+void devid_str_free(char *str);
+void devid_free(ddi_devid_t devid);
+void devid_free_nmlist(devid_nmlist_t *list);
+int devid_get(int fd, ddi_devid_t *retdevid);
+int devid_get_minor_name(int fd, char **retminor_name);
+char *devid_str_encode(ddi_devid_t devid, char *minor_name);
#endif /* !_OPENSOLARIS_DEVID_H_ */
diff --git a/compat/opensolaris/misc/deviceid.c b/compat/opensolaris/misc/deviceid.c
new file mode 100644
index 0000000..f37b1d6
--- /dev/null
+++ b/compat/opensolaris/misc/deviceid.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <libgeom.h>
+#include <devid.h>
+
+int
+devid_str_decode(char *devidstr, ddi_devid_t *retdevid, char **retminor_name)
+{
+
+ if (strlcpy(retdevid->devid, devidstr, sizeof(retdevid->devid)) >=
+ sizeof(retdevid->devid)) {
+ return (EINVAL);
+ }
+ *retminor_name = strdup("");
+ if (*retminor_name == NULL);
+ return (ENOMEM);
+ return (0);
+}
+
+int
+devid_deviceid_to_nmlist(char *search_path, ddi_devid_t devid, char *minor_name,
+ devid_nmlist_t **retlist)
+{
+ char path[MAXPATHLEN];
+ char *dst;
+
+ if (g_get_name(devid.devid, path, sizeof(path)) == -1)
+ return (errno);
+ *retlist = malloc(sizeof(**retlist));
+ if (*retlist == NULL)
+ return (ENOMEM);
+ if (strlcpy((*retlist)[0].devname, path,
+ sizeof((*retlist)[0].devname)) >= sizeof((*retlist)[0].devname)) {
+ free(*retlist);
+ return (ENAMETOOLONG);
+ }
+ return (0);
+}
+
+void
+devid_str_free(char *str)
+{
+
+ free(str);
+}
+
+void
+devid_free(ddi_devid_t devid)
+{
+ /* Do nothing. */
+}
+
+void
+devid_free_nmlist(devid_nmlist_t *list)
+{
+
+ free(list);
+}
+
+int
+devid_get(int fd, ddi_devid_t *retdevid)
+{
+
+ if (ioctl(fd, DIOCGIDENT, retdevid->devid) == -1)
+ return (errno);
+ if (retdevid->devid[0] == '\0')
+ return (ENOENT);
+ return (0);
+}
+
+int
+devid_get_minor_name(int fd, char **retminor_name)
+{
+
+ *retminor_name = strdup("");
+ if (*retminor_name == NULL)
+ return (ENOMEM);
+ return (0);
+}
+
+char *
+devid_str_encode(ddi_devid_t devid, char *minor_name)
+{
+
+ return (strdup(devid.devid));
+}
diff --git a/contrib/opensolaris/cmd/zpool/zpool_vdev.c b/contrib/opensolaris/cmd/zpool/zpool_vdev.c
index de07723..cfed1f0 100644
--- a/contrib/opensolaris/cmd/zpool/zpool_vdev.c
+++ b/contrib/opensolaris/cmd/zpool/zpool_vdev.c
@@ -159,18 +159,14 @@ out:
static boolean_t
is_provider(const char *name)
{
- off_t mediasize;
int fd;
- fd = open(name, O_RDONLY);
- if (fd == -1)
- return (B_FALSE);
- if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == -1) {
- close(fd);
- return (B_FALSE);
+ fd = g_open(name, 0);
+ if (fd >= 0) {
+ g_close(fd);
+ return (B_TRUE);
}
- close(fd);
- return (B_TRUE);
+ return (B_FALSE);
}
/*
@@ -183,9 +179,11 @@ is_provider(const char *name)
nvlist_t *
make_leaf_vdev(const char *arg)
{
- char path[MAXPATHLEN];
+ char ident[DISK_IDENT_SIZE], path[MAXPATHLEN];
+ struct stat64 statbuf;
nvlist_t *vdev = NULL;
char *type = NULL;
+ boolean_t wholedisk = B_FALSE;
if (strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
strlcpy(path, arg, sizeof (path));
@@ -212,6 +210,41 @@ make_leaf_vdev(const char *arg)
verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK,
(uint64_t)B_FALSE) == 0);
+ /*
+ * For a whole disk, defer getting its devid until after labeling it.
+ */
+ if (1 || (S_ISBLK(statbuf.st_mode) && !wholedisk)) {
+ /*
+ * Get the devid for the device.
+ */
+ int fd;
+ ddi_devid_t devid;
+ char *minor = NULL, *devid_str = NULL;
+
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ (void) fprintf(stderr, gettext("cannot open '%s': "
+ "%s\n"), path, strerror(errno));
+ nvlist_free(vdev);
+ return (NULL);
+ }
+
+ if (devid_get(fd, &devid) == 0) {
+ if (devid_get_minor_name(fd, &minor) == 0 &&
+ (devid_str = devid_str_encode(devid, minor)) !=
+ NULL) {
+ verify(nvlist_add_string(vdev,
+ ZPOOL_CONFIG_DEVID, devid_str) == 0);
+ }
+ if (devid_str != NULL)
+ devid_str_free(devid_str);
+ if (minor != NULL)
+ devid_str_free(minor);
+ devid_free(devid);
+ }
+
+ (void) close(fd);
+ }
+
return (vdev);
}
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c
index 39358e9..859895a 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c
@@ -27,11 +27,13 @@
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bio.h>
+#include <sys/disk.h>
#include <sys/spa.h>
#include <sys/vdev_impl.h>
#include <sys/fs/zfs.h>
#include <sys/zio.h>
#include <geom/geom.h>
+#include <geom/geom_int.h>
/*
* Virtual device vector for GEOM.
@@ -83,7 +85,8 @@ vdev_geom_orphan(struct g_consumer *cp)
error = cp->provider->error;
ZFS_LOG(1, "Closing access to %s.", cp->provider->name);
- g_access(cp, -cp->acr, -cp->acw, -cp->ace);
+ if (cp->acr + cp->acw + cp->ace > 0)
+ g_access(cp, -cp->acr, -cp->acw, -cp->ace);
ZFS_LOG(1, "Destroyed consumer to %s.", cp->provider->name);
g_detach(cp);
g_destroy_consumer(cp);
@@ -113,8 +116,11 @@ vdev_geom_attach(struct g_provider *pp, int write)
ZFS_LOG(1, "Attaching to %s.", pp->name);
/* Do we have geom already? No? Create one. */
LIST_FOREACH(gp, &zfs_vdev_class.geom, geom) {
- if (!(gp->flags & G_GEOM_WITHER))
- break;
+ if (gp->flags & G_GEOM_WITHER)
+ continue;
+ if (strcmp(gp->name, "zfs::vdev") != 0)
+ continue;
+ break;
}
if (gp == NULL) {
gp = g_new_geomf(&zfs_vdev_class, "zfs::vdev");
@@ -227,12 +233,125 @@ vdev_geom_worker(void *arg)
}
}
+static char *
+vdev_geom_get_id(struct g_consumer *cp)
+{
+ char *id;
+ int len;
+
+ g_topology_assert_not();
+ len = DISK_IDENT_SIZE;
+ id = kmem_zalloc(len, KM_SLEEP);
+ if (g_io_getattr("GEOM::ident", cp, &len, id) != 0) {
+ kmem_free(id, DISK_IDENT_SIZE);
+ return (NULL);
+ }
+ return (id);
+}
+
+static void
+vdev_geom_free_id(char *id)
+{
+
+ if (id != NULL)
+ kmem_free(id, DISK_IDENT_SIZE);
+}
+
+struct vdev_geom_find {
+ const char *id;
+ int write;
+ struct g_consumer *cp;
+};
+
+static void
+vdev_geom_taste_orphan(struct g_consumer *cp)
+{
+
+ KASSERT(1 == 0, ("%s called while tasting %s.", __func__,
+ cp->provider->name));
+}
+
+static void
+vdev_geom_attach_by_id_event(void *arg, int flags __unused)
+{
+ struct vdev_geom_find *ap;
+ struct g_class *mp;
+ struct g_geom *gp, *zgp;
+ struct g_provider *pp;
+ struct g_consumer *zcp;
+ char *id;
+
+ g_topology_assert();
+
+ ap = arg;
+
+ zgp = g_new_geomf(&zfs_vdev_class, "zfs::vdev::taste");
+ /* This orphan function should be never called. */
+ zgp->orphan = vdev_geom_taste_orphan;
+ zcp = g_new_consumer(zgp);
+
+ LIST_FOREACH(mp, &g_classes, class) {
+ if (mp == &zfs_vdev_class)
+ continue;
+ LIST_FOREACH(gp, &mp->geom, geom) {
+ if (gp->flags & G_GEOM_WITHER)
+ continue;
+ LIST_FOREACH(pp, &gp->provider, provider) {
+ if (pp->flags & G_PF_WITHER)
+ continue;
+ g_attach(zcp, pp);
+ if (g_access(zcp, 1, 0, 0) != 0) {
+ g_detach(zcp);
+ continue;
+ }
+ g_topology_unlock();
+ id = vdev_geom_get_id(zcp);
+ g_topology_lock();
+ g_access(zcp, -1, 0, 0);
+ g_detach(zcp);
+ if (id == NULL || strcmp(id, ap->id) != 0) {
+ vdev_geom_free_id(id);
+ continue;
+ }
+ vdev_geom_free_id(id);
+ ap->cp = vdev_geom_attach(pp, ap->write);
+ if (ap->cp == NULL) {
+ printf("ZFS WARNING: Cannot open %s "
+ "for writting.\n", pp->name);
+ continue;
+ }
+ goto end;
+ }
+ }
+ }
+ ap->cp = NULL;
+end:
+ g_destroy_consumer(zcp);
+ g_destroy_geom(zgp);
+}
+
+static struct g_consumer *
+vdev_geom_attach_by_id(const char *id, int write)
+{
+ struct vdev_geom_find *ap;
+ struct g_consumer *cp;
+
+ ap = kmem_zalloc(sizeof(*ap), KM_SLEEP);
+ ap->id = id;
+ ap->write = write;
+ g_waitfor_event(vdev_geom_attach_by_id_event, ap, M_WAITOK, NULL);
+ cp = ap->cp;
+ kmem_free(ap, sizeof(*ap));
+ return (cp);
+}
+
static int
vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
{
vdev_geom_ctx_t *ctx;
struct g_provider *pp;
struct g_consumer *cp;
+ char *id = NULL;
int owned;
/*
@@ -245,23 +364,55 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
if ((owned = mtx_owned(&Giant)))
mtx_unlock(&Giant);
+ cp = NULL;
g_topology_lock();
pp = g_provider_by_name(vd->vdev_path + sizeof("/dev/") - 1);
- if (pp == NULL) {
- g_topology_unlock();
- if (owned)
- mtx_lock(&Giant);
- vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL;
- return (EINVAL);
+ if (pp != NULL) {
+ ZFS_LOG(1, "Found provider by name %s.", vd->vdev_path);
+ cp = vdev_geom_attach(pp, !!(spa_mode & FWRITE));
+ if (cp != NULL && vd->vdev_devid != NULL) {
+ g_topology_unlock();
+ id = vdev_geom_get_id(cp);
+ g_topology_lock();
+ if (id == NULL || strcmp(id, vd->vdev_devid) != 0) {
+ vdev_geom_detach(cp, 0);
+ cp = NULL;
+ ZFS_LOG(1, "ID mismatch for provider %s: "
+ "[%s]!=[%s].", vd->vdev_path,
+ vd->vdev_devid, id);
+ goto next;
+ }
+ ZFS_LOG(1, "ID match for provider %s.", vd->vdev_path);
+ }
}
- cp = vdev_geom_attach(pp, !!(spa_mode & FWRITE));
+next:
g_topology_unlock();
+ vdev_geom_free_id(id);
+ if (cp == NULL && vd->vdev_devid != NULL) {
+ ZFS_LOG(0, "Searching by ID [%s].", vd->vdev_devid);
+ cp = vdev_geom_attach_by_id(vd->vdev_devid,
+ !!(spa_mode & FWRITE));
+ if (cp != NULL) {
+ size_t len = strlen(cp->provider->name) + 6; /* 6 == strlen("/dev/") + 1 */
+ char *buf = kmem_alloc(len, KM_SLEEP);
+
+ snprintf(buf, len, "/dev/%s", cp->provider->name);
+ spa_strfree(vd->vdev_path);
+ vd->vdev_path = buf;
+
+ ZFS_LOG(1, "Attach by ID [%s] succeeded, provider %s.",
+ vd->vdev_devid, vd->vdev_path);
+ }
+ }
if (owned)
mtx_lock(&Giant);
if (cp == NULL) {
+ ZFS_LOG(1, "Provider %s (id=[%s]) not found.", vd->vdev_path,
+ vd->vdev_devid);
vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
return (EACCES);
}
+ pp = cp->provider;
/*
* Determine the actual size of the device.
diff --git a/sys/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c b/sys/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c
index 39358e9..859895a 100644
--- a/sys/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c
+++ b/sys/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c
@@ -27,11 +27,13 @@
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bio.h>
+#include <sys/disk.h>
#include <sys/spa.h>
#include <sys/vdev_impl.h>
#include <sys/fs/zfs.h>
#include <sys/zio.h>
#include <geom/geom.h>
+#include <geom/geom_int.h>
/*
* Virtual device vector for GEOM.
@@ -83,7 +85,8 @@ vdev_geom_orphan(struct g_consumer *cp)
error = cp->provider->error;
ZFS_LOG(1, "Closing access to %s.", cp->provider->name);
- g_access(cp, -cp->acr, -cp->acw, -cp->ace);
+ if (cp->acr + cp->acw + cp->ace > 0)
+ g_access(cp, -cp->acr, -cp->acw, -cp->ace);
ZFS_LOG(1, "Destroyed consumer to %s.", cp->provider->name);
g_detach(cp);
g_destroy_consumer(cp);
@@ -113,8 +116,11 @@ vdev_geom_attach(struct g_provider *pp, int write)
ZFS_LOG(1, "Attaching to %s.", pp->name);
/* Do we have geom already? No? Create one. */
LIST_FOREACH(gp, &zfs_vdev_class.geom, geom) {
- if (!(gp->flags & G_GEOM_WITHER))
- break;
+ if (gp->flags & G_GEOM_WITHER)
+ continue;
+ if (strcmp(gp->name, "zfs::vdev") != 0)
+ continue;
+ break;
}
if (gp == NULL) {
gp = g_new_geomf(&zfs_vdev_class, "zfs::vdev");
@@ -227,12 +233,125 @@ vdev_geom_worker(void *arg)
}
}
+static char *
+vdev_geom_get_id(struct g_consumer *cp)
+{
+ char *id;
+ int len;
+
+ g_topology_assert_not();
+ len = DISK_IDENT_SIZE;
+ id = kmem_zalloc(len, KM_SLEEP);
+ if (g_io_getattr("GEOM::ident", cp, &len, id) != 0) {
+ kmem_free(id, DISK_IDENT_SIZE);
+ return (NULL);
+ }
+ return (id);
+}
+
+static void
+vdev_geom_free_id(char *id)
+{
+
+ if (id != NULL)
+ kmem_free(id, DISK_IDENT_SIZE);
+}
+
+struct vdev_geom_find {
+ const char *id;
+ int write;
+ struct g_consumer *cp;
+};
+
+static void
+vdev_geom_taste_orphan(struct g_consumer *cp)
+{
+
+ KASSERT(1 == 0, ("%s called while tasting %s.", __func__,
+ cp->provider->name));
+}
+
+static void
+vdev_geom_attach_by_id_event(void *arg, int flags __unused)
+{
+ struct vdev_geom_find *ap;
+ struct g_class *mp;
+ struct g_geom *gp, *zgp;
+ struct g_provider *pp;
+ struct g_consumer *zcp;
+ char *id;
+
+ g_topology_assert();
+
+ ap = arg;
+
+ zgp = g_new_geomf(&zfs_vdev_class, "zfs::vdev::taste");
+ /* This orphan function should be never called. */
+ zgp->orphan = vdev_geom_taste_orphan;
+ zcp = g_new_consumer(zgp);
+
+ LIST_FOREACH(mp, &g_classes, class) {
+ if (mp == &zfs_vdev_class)
+ continue;
+ LIST_FOREACH(gp, &mp->geom, geom) {
+ if (gp->flags & G_GEOM_WITHER)
+ continue;
+ LIST_FOREACH(pp, &gp->provider, provider) {
+ if (pp->flags & G_PF_WITHER)
+ continue;
+ g_attach(zcp, pp);
+ if (g_access(zcp, 1, 0, 0) != 0) {
+ g_detach(zcp);
+ continue;
+ }
+ g_topology_unlock();
+ id = vdev_geom_get_id(zcp);
+ g_topology_lock();
+ g_access(zcp, -1, 0, 0);
+ g_detach(zcp);
+ if (id == NULL || strcmp(id, ap->id) != 0) {
+ vdev_geom_free_id(id);
+ continue;
+ }
+ vdev_geom_free_id(id);
+ ap->cp = vdev_geom_attach(pp, ap->write);
+ if (ap->cp == NULL) {
+ printf("ZFS WARNING: Cannot open %s "
+ "for writting.\n", pp->name);
+ continue;
+ }
+ goto end;
+ }
+ }
+ }
+ ap->cp = NULL;
+end:
+ g_destroy_consumer(zcp);
+ g_destroy_geom(zgp);
+}
+
+static struct g_consumer *
+vdev_geom_attach_by_id(const char *id, int write)
+{
+ struct vdev_geom_find *ap;
+ struct g_consumer *cp;
+
+ ap = kmem_zalloc(sizeof(*ap), KM_SLEEP);
+ ap->id = id;
+ ap->write = write;
+ g_waitfor_event(vdev_geom_attach_by_id_event, ap, M_WAITOK, NULL);
+ cp = ap->cp;
+ kmem_free(ap, sizeof(*ap));
+ return (cp);
+}
+
static int
vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
{
vdev_geom_ctx_t *ctx;
struct g_provider *pp;
struct g_consumer *cp;
+ char *id = NULL;
int owned;
/*
@@ -245,23 +364,55 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
if ((owned = mtx_owned(&Giant)))
mtx_unlock(&Giant);
+ cp = NULL;
g_topology_lock();
pp = g_provider_by_name(vd->vdev_path + sizeof("/dev/") - 1);
- if (pp == NULL) {
- g_topology_unlock();
- if (owned)
- mtx_lock(&Giant);
- vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL;
- return (EINVAL);
+ if (pp != NULL) {
+ ZFS_LOG(1, "Found provider by name %s.", vd->vdev_path);
+ cp = vdev_geom_attach(pp, !!(spa_mode & FWRITE));
+ if (cp != NULL && vd->vdev_devid != NULL) {
+ g_topology_unlock();
+ id = vdev_geom_get_id(cp);
+ g_topology_lock();
+ if (id == NULL || strcmp(id, vd->vdev_devid) != 0) {
+ vdev_geom_detach(cp, 0);
+ cp = NULL;
+ ZFS_LOG(1, "ID mismatch for provider %s: "
+ "[%s]!=[%s].", vd->vdev_path,
+ vd->vdev_devid, id);
+ goto next;
+ }
+ ZFS_LOG(1, "ID match for provider %s.", vd->vdev_path);
+ }
}
- cp = vdev_geom_attach(pp, !!(spa_mode & FWRITE));
+next:
g_topology_unlock();
+ vdev_geom_free_id(id);
+ if (cp == NULL && vd->vdev_devid != NULL) {
+ ZFS_LOG(0, "Searching by ID [%s].", vd->vdev_devid);
+ cp = vdev_geom_attach_by_id(vd->vdev_devid,
+ !!(spa_mode & FWRITE));
+ if (cp != NULL) {
+ size_t len = strlen(cp->provider->name) + 6; /* 6 == strlen("/dev/") + 1 */
+ char *buf = kmem_alloc(len, KM_SLEEP);
+
+ snprintf(buf, len, "/dev/%s", cp->provider->name);
+ spa_strfree(vd->vdev_path);
+ vd->vdev_path = buf;
+
+ ZFS_LOG(1, "Attach by ID [%s] succeeded, provider %s.",
+ vd->vdev_devid, vd->vdev_path);
+ }
+ }
if (owned)
mtx_lock(&Giant);
if (cp == NULL) {
+ ZFS_LOG(1, "Provider %s (id=[%s]) not found.", vd->vdev_path,
+ vd->vdev_devid);
vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
return (EACCES);
}
+ pp = cp->provider;
/*
* Determine the actual size of the device.
OpenPOWER on IntegriCloud