summaryrefslogtreecommitdiffstats
path: root/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c')
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c445
1 files changed, 352 insertions, 93 deletions
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 eebc911..f151f83 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
@@ -96,13 +96,9 @@ vdev_geom_orphan(struct g_consumer *cp)
g_wither_geom(gp, error);
}
vdev_geom_release(vd);
- /* Both methods below work, but in a bit different way. */
-#if 0
- vd->vdev_reopen_wanted = 1;
-#else
- vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
- vdev_set_state(vd, B_TRUE, VDEV_STATE_CANT_OPEN, vd->vdev_stat.vs_aux);
-#endif
+
+ vd->vdev_remove_wanted = B_TRUE;
+ spa_async_request(vd->vdev_spa, SPA_ASYNC_REMOVE);
}
static struct g_consumer *
@@ -229,7 +225,7 @@ vdev_geom_worker(void *arg)
vd->vdev_nowritecache = B_TRUE;
}
g_destroy_bio(bp);
- zio_next_stage_async(zio);
+ zio_interrupt(zio);
}
}
@@ -249,6 +245,194 @@ vdev_geom_get_id(struct g_consumer *cp)
return (id);
}
+static uint64_t
+nvlist_get_guid(nvlist_t *list)
+{
+ nvpair_t *elem = NULL;
+ uint64_t value;
+
+ while ((elem = nvlist_next_nvpair(list, elem)) != NULL) {
+ if (nvpair_type(elem) == DATA_TYPE_UINT64 &&
+ strcmp(nvpair_name(elem), "guid") == 0) {
+ VERIFY(nvpair_value_uint64(elem, &value) == 0);
+ return (value);
+ }
+ }
+ return (0);
+}
+
+static char *
+nvlist_get_devid(nvlist_t *list, uint64_t guid)
+{
+ nvpair_t *elem = NULL;
+ int progress;
+ char *id;
+
+ progress = 0;
+ id = NULL;
+
+ while ((elem = nvlist_next_nvpair(list, elem)) != NULL) {
+ switch (nvpair_type(elem)) {
+ case DATA_TYPE_STRING:
+ {
+ char *value;
+
+ VERIFY(nvpair_value_string(elem, &value) == 0);
+ if (strcmp(nvpair_name(elem), "type") == 0 &&
+ strcmp(value, "disk") == 0) {
+ progress |= 0x01;
+ } else if (strcmp(nvpair_name(elem), "devid") == 0) {
+ progress |= 0x02;
+ id = value;
+ }
+ break;
+ }
+ case DATA_TYPE_UINT64:
+ {
+ uint64_t value;
+
+ VERIFY(nvpair_value_uint64(elem, &value) == 0);
+ if (strcmp(nvpair_name(elem), "guid") == 0 &&
+ value == guid) {
+ progress |= 0x04;
+ }
+ break;
+ }
+ case DATA_TYPE_NVLIST:
+ {
+ nvlist_t *value;
+ char *lid;
+
+ VERIFY(nvpair_value_nvlist(elem, &value) == 0);
+ lid = nvlist_get_devid(value, guid);
+ if (lid != NULL)
+ return (lid);
+ break;
+ }
+ case DATA_TYPE_NVLIST_ARRAY:
+ {
+ nvlist_t **value;
+ u_int c, count;
+ char *lid;
+
+ VERIFY(nvpair_value_nvlist_array(elem, &value,
+ &count) == 0);
+
+ for (c = 0; c < count; c++) {
+ lid = nvlist_get_devid(value[c], guid);
+ if (lid != NULL)
+ return (lid);
+ }
+ break;
+ }
+ }
+ if (progress == 0x07)
+ break;
+ }
+ if (progress != 0x07)
+ id = NULL;
+ return (id);
+}
+
+static int
+vdev_geom_io(struct g_consumer *cp, int cmd, void *data, off_t offset, off_t size)
+{
+ struct bio *bp;
+ u_char *p;
+ off_t off;
+ int error;
+
+ ASSERT((offset % cp->provider->sectorsize) == 0);
+ ASSERT((size % cp->provider->sectorsize) == 0);
+
+ bp = g_alloc_bio();
+ off = offset;
+ offset += size;
+ p = data;
+ error = 0;
+
+ for (; off < offset; off += MAXPHYS, p += MAXPHYS, size -= MAXPHYS) {
+ bzero(bp, sizeof(*bp));
+ bp->bio_cmd = cmd;
+ bp->bio_done = NULL;
+ bp->bio_offset = off;
+ bp->bio_length = MIN(size, MAXPHYS);
+ bp->bio_data = p;
+ g_io_request(bp, cp);
+ error = biowait(bp, "vdev_geom_io");
+ if (error != 0)
+ break;
+ }
+
+ g_destroy_bio(bp);
+ return (error);
+}
+
+static char *
+vdev_geom_read_id(struct g_consumer *cp)
+{
+ struct g_provider *pp;
+ vdev_label_t *label;
+ char *p, *buf;
+ size_t buflen;
+ uint64_t psize;
+ off_t offset, size;
+ char *id;
+ int error, l, len;
+
+ g_topology_assert_not();
+
+ pp = cp->provider;
+
+ psize = pp->mediasize;
+ psize = P2ALIGN(psize, (uint64_t)sizeof(vdev_label_t));
+
+ size = sizeof(*label) + pp->sectorsize -
+ ((sizeof(*label) - 1) % pp->sectorsize) - 1;
+
+ id = NULL;
+ label = kmem_alloc(size, KM_SLEEP);
+ buflen = sizeof(label->vl_vdev_phys.vp_nvlist);
+
+ for (l = 0; l < VDEV_LABELS && id == NULL; l++) {
+ nvlist_t *config = NULL;
+ uint64_t guid;
+
+ offset = vdev_label_offset(psize, l, 0);
+ if ((offset % pp->sectorsize) != 0)
+ continue;
+
+ error = vdev_geom_io(cp, BIO_READ, label, offset, size);
+ if (error != 0)
+ continue;
+ buf = label->vl_vdev_phys.vp_nvlist;
+
+ if (nvlist_unpack(buf, buflen, &config, 0) != 0)
+ continue;
+
+ guid = nvlist_get_guid(config);
+ if (guid == 0) {
+ nvlist_free(config);
+ continue;
+ }
+ id = nvlist_get_devid(config, guid);
+ if (id != NULL) {
+ char *tmp;
+
+ tmp = kmem_zalloc(DISK_IDENT_SIZE, KM_SLEEP);
+ strlcpy(tmp, id, DISK_IDENT_SIZE);
+ id = tmp;
+ }
+
+ nvlist_free(config);
+ }
+
+ kmem_free(label, size);
+ if (id != NULL)
+ ZFS_LOG(1, "ID of %s: %s", pp->name, id);
+ return (id);
+}
+
static void
vdev_geom_free_id(char *id)
{
@@ -290,6 +474,7 @@ vdev_geom_attach_by_id_event(void *arg, int flags __unused)
zgp->orphan = vdev_geom_taste_orphan;
zcp = g_new_consumer(zgp);
+ /* First round tries to get provider's ID without reading metadata. */
LIST_FOREACH(mp, &g_classes, class) {
if (mp == &zfs_vdev_class)
continue;
@@ -324,6 +509,41 @@ vdev_geom_attach_by_id_event(void *arg, int flags __unused)
}
}
}
+ /* Second round looks for ID by reading ZFS metadata. */
+ 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_read_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);
@@ -345,25 +565,13 @@ vdev_geom_attach_by_id(const char *id, int write)
return (cp);
}
-static int
-vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
+static struct g_consumer *
+vdev_geom_open_by_path_and_devid(vdev_t *vd)
{
- vdev_geom_ctx_t *ctx;
struct g_provider *pp;
struct g_consumer *cp;
- char *id = NULL;
- int owned;
-
- /*
- * We must have a pathname, and it must be absolute.
- */
- if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') {
- vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL;
- return (EINVAL);
- }
+ char *id;
- if ((owned = mtx_owned(&Giant)))
- mtx_unlock(&Giant);
cp = NULL;
g_topology_lock();
pp = g_provider_by_name(vd->vdev_path + sizeof("/dev/") - 1);
@@ -380,40 +588,101 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *ashift)
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);
+ } else
+ ZFS_LOG(1, "ID match for provider %s.",
+ vd->vdev_path);
+ vdev_geom_free_id(id);
}
}
-next:
g_topology_unlock();
- vdev_geom_free_id(id);
- if (cp == NULL && vd->vdev_devid != NULL) {
- ZFS_LOG(1, "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);
+
+ return (cp);
+}
+
+static struct g_consumer *
+vdev_geom_open_by_devid(vdev_t *vd)
+{
+ struct g_consumer *cp;
+ char *buf;
+ size_t len;
+
+ /*
+ * We can't search by devid if it's missing.
+ */
+ if (vd->vdev_devid == NULL)
+ return (NULL);
+
+ ZFS_LOG(1, "Searching by ID [%s].", vd->vdev_devid);
+ cp = vdev_geom_attach_by_id(vd->vdev_devid, !!(spa_mode & FWRITE));
+ if (cp != NULL) {
+ len = strlen(cp->provider->name) + strlen("/dev/") + 1;
+ 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);
+ } else
+ ZFS_LOG(1, "Search by ID [%s] failed.", vd->vdev_devid);
+
+ 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;
+ int owned;
+
+ /*
+ * We must have a pathname, and it must be absolute.
+ */
+ if (vd->vdev_path == NULL || vd->vdev_path[0] != '/') {
+ vd->vdev_stat.vs_aux = VDEV_AUX_BAD_LABEL;
+ return (EINVAL);
+ }
+
+ vd->vdev_tsd = NULL;
+
+ if ((owned = mtx_owned(&Giant)))
+ mtx_unlock(&Giant);
+ cp = vdev_geom_open_by_path_and_devid(vd);
+ if (cp == NULL) {
+ /*
+ * The device at vd->vdev_path doesn't have the right devid.
+ * The disks might have merely moved around so try all other
+ * geom providers to find one with the right devid.
+ */
+ cp = vdev_geom_open_by_devid(vd);
+ if (cp == NULL) {
+ ZFS_LOG(1, "Provider %s not found.", vd->vdev_path);
+ vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
+ if (owned)
+ mtx_lock(&Giant);
+ return (EACCES);
}
}
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);
- }
+
+ cp->private = vd;
+
+ ctx = kmem_zalloc(sizeof(*ctx), KM_SLEEP);
+ bioq_init(&ctx->gc_queue);
+ mtx_init(&ctx->gc_queue_mtx, "zfs:vdev:geom:queue", NULL, MTX_DEF);
+ ctx->gc_consumer = cp;
+ ctx->gc_state = 0;
+
+ vd->vdev_tsd = ctx;
pp = cp->provider;
+ kproc_create(vdev_geom_worker, ctx, NULL, 0, 0, "vdev:worker %s",
+ pp->name);
+
/*
* Determine the actual size of the device.
*/
@@ -430,19 +699,6 @@ next:
*/
vd->vdev_nowritecache = B_FALSE;
- cp->private = vd;
-
- ctx = kmem_zalloc(sizeof(*ctx), KM_SLEEP);
- bioq_init(&ctx->gc_queue);
- mtx_init(&ctx->gc_queue_mtx, "zfs:vdev:geom:queue", NULL, MTX_DEF);
- ctx->gc_consumer = cp;
- ctx->gc_state = 0;
-
- vd->vdev_tsd = ctx;
-
- kproc_create(vdev_geom_worker, ctx, NULL, 0, 0, "vdev:worker %s",
- pp->name);
-
return (0);
}
@@ -469,13 +725,16 @@ vdev_geom_io_intr(struct bio *bp)
zio = bp->bio_caller1;
ctx = zio->io_vd->vdev_tsd;
+ if ((zio->io_error = bp->bio_error) == 0 && bp->bio_resid != 0)
+ zio->io_error = EIO;
+
mtx_lock(&ctx->gc_queue_mtx);
bioq_insert_tail(&ctx->gc_queue, bp);
wakeup_one(&ctx->gc_queue);
mtx_unlock(&ctx->gc_queue_mtx);
}
-static void
+static int
vdev_geom_io_start(zio_t *zio)
{
vdev_t *vd;
@@ -492,18 +751,19 @@ vdev_geom_io_start(zio_t *zio)
cp = ctx->gc_consumer;
if (zio->io_type == ZIO_TYPE_IOCTL) {
- zio_vdev_io_bypass(zio);
-
/* XXPOLICY */
- if (vdev_is_dead(vd)) {
+ if (!vdev_readable(vd)) {
zio->io_error = ENXIO;
- zio_next_stage_async(zio);
- return;
+ return (ZIO_PIPELINE_CONTINUE);
}
switch (zio->io_cmd) {
case DKIOCFLUSHWRITECACHE:
+
+ if (zfs_nocacheflush)
+ break;
+
if (vd->vdev_nowritecache) {
zio->io_error = ENOTSUP;
break;
@@ -514,27 +774,13 @@ vdev_geom_io_start(zio_t *zio)
zio->io_error = ENOTSUP;
}
- zio_next_stage_async(zio);
- return;
+ return (ZIO_PIPELINE_CONTINUE);
}
-
- if (zio->io_type == ZIO_TYPE_READ && vdev_cache_read(zio) == 0)
- return;
-
- if ((zio = vdev_queue_io(zio)) == NULL)
- return;
-
sendreq:
-
- error = vdev_is_dead(vd) ? ENXIO : vdev_error_inject(vd, zio);
- if (error == 0 && cp == NULL)
- error = ENXIO;
- if (error) {
- zio->io_error = error;
- zio_next_stage_async(zio);
- return;
+ if (cp == NULL) {
+ zio->io_error = ENXIO;
+ return (ZIO_PIPELINE_CONTINUE);
}
-
bp = g_alloc_bio();
bp->bio_caller1 = zio;
switch (zio->io_type) {
@@ -555,20 +801,33 @@ sendreq:
bp->bio_done = vdev_geom_io_intr;
g_io_request(bp, cp);
+
+ return (ZIO_PIPELINE_STOP);
}
static void
vdev_geom_io_done(zio_t *zio)
{
- vdev_queue_io_done(zio);
-
- if (zio->io_type == ZIO_TYPE_WRITE)
- vdev_cache_write(zio);
- if (zio_injection_enabled && zio->io_error == 0)
- zio->io_error = zio_handle_device_injection(zio->io_vd, EIO);
-
- zio_next_stage(zio);
+ /*
+ * If the device returned ENXIO, then attempt we should verify if GEOM
+ * provider has been removed. If this is the case, then we trigger an
+ * asynchronous removal of the device.
+ */
+ if (zio->io_error == ENXIO) {
+ vdev_t *vd = zio->io_vd;
+ vdev_geom_ctx_t *ctx;
+ struct g_provider *pp = NULL;
+
+ ctx = vd->vdev_tsd;
+ if (ctx != NULL && ctx->gc_consumer != NULL)
+ pp = ctx->gc_consumer->provider;
+
+ if (pp == NULL || (pp->flags & G_PF_ORPHAN)) {
+ vd->vdev_remove_wanted = B_TRUE;
+ spa_async_request(zio->io_spa, SPA_ASYNC_REMOVE);
+ }
+ }
}
vdev_ops_t vdev_geom_ops = {
OpenPOWER on IntegriCloud