diff options
-rw-r--r-- | cddl/compat/opensolaris/include/devid.h | 27 | ||||
-rw-r--r-- | cddl/compat/opensolaris/misc/deviceid.c | 119 | ||||
-rw-r--r-- | cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c | 53 | ||||
-rw-r--r-- | cddl/lib/libzfs/Makefile | 3 | ||||
-rw-r--r-- | compat/opensolaris/include/devid.h | 27 | ||||
-rw-r--r-- | compat/opensolaris/misc/deviceid.c | 119 | ||||
-rw-r--r-- | contrib/opensolaris/cmd/zpool/zpool_vdev.c | 53 | ||||
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c | 171 | ||||
-rw-r--r-- | sys/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c | 171 |
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. |