Refactor vdev_geom_attach and friends to reduce code duplication
sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c Move checks for provider's sectorsize and mediasize into a single location in vdev_geom_attach. Remove the zfs::vdev::taste class; it's ok to use the regular vdev class for tasting. Consolidate guid checks into a single location in vdev_attach_ok. Consolidate some error handling code from vdev_geom_attach into vdev_geom_detach, closing a resource leak of geom consumers in the process. Reviewed by: avg MFC after: 4 weeks Sponsored by: Spectra Logic Corp Differential Revision: https://reviews.freebsd.org/D5974
This commit is contained in:
parent
7f5ddefe05
commit
663f649ff6
@ -61,6 +61,9 @@ static int vdev_geom_bio_delete_disable;
|
||||
SYSCTL_INT(_vfs_zfs_vdev, OID_AUTO, bio_delete_disable, CTLFLAG_RWTUN,
|
||||
&vdev_geom_bio_delete_disable, 0, "Disable BIO_DELETE");
|
||||
|
||||
/* Declare local functions */
|
||||
static void vdev_geom_detach(struct g_consumer *cp, boolean_t open_for_read);
|
||||
|
||||
/*
|
||||
* Thread local storage used to indicate when a thread is probing geoms
|
||||
* for their guids. If NULL, this thread is not tasting geoms. If non NULL,
|
||||
@ -168,6 +171,17 @@ vdev_geom_attach(struct g_provider *pp, vdev_t *vd)
|
||||
g_topology_assert();
|
||||
|
||||
ZFS_LOG(1, "Attaching to %s.", pp->name);
|
||||
|
||||
if (pp->sectorsize > VDEV_PAD_SIZE || !ISP2(pp->sectorsize)) {
|
||||
ZFS_LOG(1, "Failing attach of %s. Incompatible sectorsize %d\n",
|
||||
pp->name, pp->sectorsize);
|
||||
return (NULL);
|
||||
} else if (pp->mediasize < SPA_MINDEVSIZE) {
|
||||
ZFS_LOG(1, "Failing attach of %s. Incompatible mediasize %ju\n",
|
||||
pp->name, pp->mediasize);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Do we have geom already? No? Create one. */
|
||||
LIST_FOREACH(gp, &zfs_vdev_class.geom, geom) {
|
||||
if (gp->flags & G_GEOM_WITHER)
|
||||
@ -185,14 +199,14 @@ vdev_geom_attach(struct g_provider *pp, vdev_t *vd)
|
||||
if (error != 0) {
|
||||
ZFS_LOG(1, "%s(%d): g_attach failed: %d\n", __func__,
|
||||
__LINE__, error);
|
||||
g_wither_geom(gp, ENXIO);
|
||||
vdev_geom_detach(cp, B_FALSE);
|
||||
return (NULL);
|
||||
}
|
||||
error = g_access(cp, 1, 0, 1);
|
||||
if (error != 0) {
|
||||
ZFS_LOG(1, "%s(%d): g_access failed: %d\n", __func__,
|
||||
__LINE__, error);
|
||||
g_wither_geom(gp, ENXIO);
|
||||
vdev_geom_detach(cp, B_FALSE);
|
||||
return (NULL);
|
||||
}
|
||||
ZFS_LOG(1, "Created geom and consumer for %s.", pp->name);
|
||||
@ -210,15 +224,14 @@ vdev_geom_attach(struct g_provider *pp, vdev_t *vd)
|
||||
if (error != 0) {
|
||||
ZFS_LOG(1, "%s(%d): g_attach failed: %d\n",
|
||||
__func__, __LINE__, error);
|
||||
g_destroy_consumer(cp);
|
||||
vdev_geom_detach(cp, B_FALSE);
|
||||
return (NULL);
|
||||
}
|
||||
error = g_access(cp, 1, 0, 1);
|
||||
if (error != 0) {
|
||||
ZFS_LOG(1, "%s(%d): g_access failed: %d\n",
|
||||
__func__, __LINE__, error);
|
||||
g_detach(cp);
|
||||
g_destroy_consumer(cp);
|
||||
vdev_geom_detach(cp, B_FALSE);
|
||||
return (NULL);
|
||||
}
|
||||
ZFS_LOG(1, "Created consumer for %s.", pp->name);
|
||||
@ -244,39 +257,41 @@ vdev_geom_attach(struct g_provider *pp, vdev_t *vd)
|
||||
* 2) Set it to a linked list of vdevs, not just a single vdev
|
||||
*/
|
||||
cp->private = vd;
|
||||
vd->vdev_tsd = cp;
|
||||
if (vd != NULL)
|
||||
vd->vdev_tsd = cp;
|
||||
|
||||
cp->flags |= G_CF_DIRECT_SEND | G_CF_DIRECT_RECEIVE;
|
||||
return (cp);
|
||||
}
|
||||
|
||||
static void
|
||||
vdev_geom_close_locked(vdev_t *vd)
|
||||
vdev_geom_detach(struct g_consumer *cp, boolean_t open_for_read)
|
||||
{
|
||||
struct g_geom *gp;
|
||||
struct g_consumer *cp;
|
||||
vdev_t *vd;
|
||||
|
||||
g_topology_assert();
|
||||
|
||||
cp = vd->vdev_tsd;
|
||||
if (cp == NULL)
|
||||
return;
|
||||
ZFS_LOG(1, "Detaching consumer. Provider %s.",
|
||||
cp->provider && cp->provider->name ? cp->provider->name : "NULL");
|
||||
|
||||
ZFS_LOG(1, "Closing access to %s.", cp->provider->name);
|
||||
KASSERT(vd->vdev_tsd == cp, ("%s: vdev_tsd is not cp", __func__));
|
||||
vd->vdev_tsd = NULL;
|
||||
vd->vdev_delayed_close = B_FALSE;
|
||||
vd = cp->private;
|
||||
if (vd != NULL) {
|
||||
vd->vdev_tsd = NULL;
|
||||
vd->vdev_delayed_close = B_FALSE;
|
||||
}
|
||||
cp->private = NULL;
|
||||
|
||||
gp = cp->geom;
|
||||
g_access(cp, -1, 0, -1);
|
||||
if (open_for_read)
|
||||
g_access(cp, -1, 0, -1);
|
||||
/* Destroy consumer on last close. */
|
||||
if (cp->acr == 0 && cp->ace == 0) {
|
||||
if (cp->acw > 0)
|
||||
g_access(cp, 0, -cp->acw, 0);
|
||||
if (cp->provider != NULL) {
|
||||
ZFS_LOG(1, "Destroyed consumer to %s.",
|
||||
cp->provider->name);
|
||||
ZFS_LOG(1, "Destroying consumer to %s.",
|
||||
cp->provider->name ? cp->provider->name : "NULL");
|
||||
g_detach(cp);
|
||||
}
|
||||
g_destroy_consumer(cp);
|
||||
@ -288,6 +303,22 @@ vdev_geom_close_locked(vdev_t *vd)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vdev_geom_close_locked(vdev_t *vd)
|
||||
{
|
||||
struct g_consumer *cp;
|
||||
|
||||
g_topology_assert();
|
||||
|
||||
cp = vd->vdev_tsd;
|
||||
if (cp == NULL)
|
||||
return;
|
||||
|
||||
ZFS_LOG(1, "Closing access to %s.", cp->provider->name);
|
||||
|
||||
vdev_geom_detach(cp, B_TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
nvlist_get_guids(nvlist_t *list, uint64_t *pguid, uint64_t *vguid)
|
||||
{
|
||||
@ -331,13 +362,6 @@ vdev_geom_io(struct g_consumer *cp, int cmd, void *data, off_t offset, off_t siz
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
vdev_geom_taste_orphan(struct g_consumer *cp)
|
||||
{
|
||||
ZFS_LOG(0, "WARNING: Orphan %s while tasting its VDev GUID.",
|
||||
cp->provider->name);
|
||||
}
|
||||
|
||||
static int
|
||||
vdev_geom_read_config(struct g_consumer *cp, nvlist_t **config)
|
||||
{
|
||||
@ -470,41 +494,12 @@ ignore:
|
||||
nvlist_free(cfg);
|
||||
}
|
||||
|
||||
static int
|
||||
vdev_geom_attach_taster(struct g_consumer *cp, struct g_provider *pp)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (pp->flags & G_PF_WITHER)
|
||||
return (EINVAL);
|
||||
g_attach(cp, pp);
|
||||
error = g_access(cp, 1, 0, 0);
|
||||
if (error == 0) {
|
||||
if (pp->sectorsize > VDEV_PAD_SIZE || !ISP2(pp->sectorsize))
|
||||
error = EINVAL;
|
||||
else if (pp->mediasize < SPA_MINDEVSIZE)
|
||||
error = EINVAL;
|
||||
if (error != 0)
|
||||
g_access(cp, -1, 0, 0);
|
||||
}
|
||||
if (error != 0)
|
||||
g_detach(cp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
vdev_geom_detach_taster(struct g_consumer *cp)
|
||||
{
|
||||
g_access(cp, -1, 0, 0);
|
||||
g_detach(cp);
|
||||
}
|
||||
|
||||
int
|
||||
vdev_geom_read_pool_label(const char *name,
|
||||
nvlist_t ***configs, uint64_t *count)
|
||||
{
|
||||
struct g_class *mp;
|
||||
struct g_geom *gp, *zgp;
|
||||
struct g_geom *gp;
|
||||
struct g_provider *pp;
|
||||
struct g_consumer *zcp;
|
||||
nvlist_t *vdev_cfg;
|
||||
@ -514,11 +509,6 @@ vdev_geom_read_pool_label(const char *name,
|
||||
DROP_GIANT();
|
||||
g_topology_lock();
|
||||
|
||||
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);
|
||||
|
||||
*configs = NULL;
|
||||
*count = 0;
|
||||
pool_guid = 0;
|
||||
@ -531,12 +521,13 @@ vdev_geom_read_pool_label(const char *name,
|
||||
LIST_FOREACH(pp, &gp->provider, provider) {
|
||||
if (pp->flags & G_PF_WITHER)
|
||||
continue;
|
||||
if (vdev_geom_attach_taster(zcp, pp) != 0)
|
||||
zcp = vdev_geom_attach(pp, NULL);
|
||||
if (zcp == NULL)
|
||||
continue;
|
||||
g_topology_unlock();
|
||||
error = vdev_geom_read_config(zcp, &vdev_cfg);
|
||||
g_topology_lock();
|
||||
vdev_geom_detach_taster(zcp);
|
||||
vdev_geom_detach(zcp, B_TRUE);
|
||||
if (error)
|
||||
continue;
|
||||
ZFS_LOG(1, "successfully read vdev config");
|
||||
@ -546,9 +537,6 @@ vdev_geom_read_pool_label(const char *name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_destroy_consumer(zcp);
|
||||
g_destroy_geom(zgp);
|
||||
g_topology_unlock();
|
||||
PICKUP_GIANT();
|
||||
|
||||
@ -570,21 +558,55 @@ vdev_geom_read_guids(struct g_consumer *cp, uint64_t *pguid, uint64_t *vguid)
|
||||
}
|
||||
}
|
||||
|
||||
static boolean_t
|
||||
vdev_attach_ok(vdev_t *vd, struct g_provider *pp)
|
||||
{
|
||||
uint64_t pool_guid;
|
||||
uint64_t vdev_guid;
|
||||
struct g_consumer *zcp;
|
||||
boolean_t pool_ok;
|
||||
boolean_t vdev_ok;
|
||||
|
||||
zcp = vdev_geom_attach(pp, NULL);
|
||||
if (zcp == NULL) {
|
||||
ZFS_LOG(1, "Unable to attach tasting instance to %s.",
|
||||
pp->name);
|
||||
return (B_FALSE);
|
||||
}
|
||||
g_topology_unlock();
|
||||
vdev_geom_read_guids(zcp, &pool_guid, &vdev_guid);
|
||||
g_topology_lock();
|
||||
vdev_geom_detach(zcp, B_TRUE);
|
||||
|
||||
/*
|
||||
* Check that the label's vdev guid matches the desired guid. If the
|
||||
* label has a pool guid, check that it matches too. (Inactive spares
|
||||
* and L2ARCs do not have any pool guid in the label.)
|
||||
*/
|
||||
if ((pool_guid == 0 || pool_guid == spa_guid(vd->vdev_spa)) &&
|
||||
vdev_guid == vd->vdev_guid) {
|
||||
ZFS_LOG(1, "guids match for provider %s.", vd->vdev_path);
|
||||
return (B_TRUE);
|
||||
} else {
|
||||
ZFS_LOG(1, "guid mismatch for provider %s: "
|
||||
"%ju:%ju != %ju:%ju.", vd->vdev_path,
|
||||
(uintmax_t)spa_guid(vd->vdev_spa),
|
||||
(uintmax_t)vd->vdev_guid,
|
||||
(uintmax_t)pool_guid, (uintmax_t)vdev_guid);
|
||||
return (B_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static struct g_consumer *
|
||||
vdev_geom_attach_by_guids(vdev_t *vd)
|
||||
{
|
||||
struct g_class *mp;
|
||||
struct g_geom *gp, *zgp;
|
||||
struct g_geom *gp;
|
||||
struct g_provider *pp;
|
||||
struct g_consumer *cp, *zcp;
|
||||
uint64_t pguid, vguid;
|
||||
struct g_consumer *cp;
|
||||
|
||||
g_topology_assert();
|
||||
|
||||
zgp = g_new_geomf(&zfs_vdev_class, "zfs::vdev::taste");
|
||||
zgp->orphan = vdev_geom_taste_orphan;
|
||||
zcp = g_new_consumer(zgp);
|
||||
|
||||
cp = NULL;
|
||||
LIST_FOREACH(mp, &g_classes, class) {
|
||||
if (mp == &zfs_vdev_class)
|
||||
@ -593,22 +615,7 @@ vdev_geom_attach_by_guids(vdev_t *vd)
|
||||
if (gp->flags & G_GEOM_WITHER)
|
||||
continue;
|
||||
LIST_FOREACH(pp, &gp->provider, provider) {
|
||||
if (vdev_geom_attach_taster(zcp, pp) != 0)
|
||||
continue;
|
||||
g_topology_unlock();
|
||||
vdev_geom_read_guids(zcp, &pguid, &vguid);
|
||||
g_topology_lock();
|
||||
vdev_geom_detach_taster(zcp);
|
||||
/*
|
||||
* Check that the label's vdev guid matches the
|
||||
* desired guid. If the label has a pool guid,
|
||||
* check that it matches too. (Inactive spares
|
||||
* and L2ARCs do not have any pool guid in the
|
||||
* label.)
|
||||
*/
|
||||
if ((pguid != 0 &&
|
||||
pguid != spa_guid(vd->vdev_spa)) ||
|
||||
vguid != vd->vdev_guid)
|
||||
if (!vdev_attach_ok(vd, pp))
|
||||
continue;
|
||||
cp = vdev_geom_attach(pp, vd);
|
||||
if (cp == NULL) {
|
||||
@ -625,8 +632,6 @@ vdev_geom_attach_by_guids(vdev_t *vd)
|
||||
break;
|
||||
}
|
||||
end:
|
||||
g_destroy_consumer(zcp);
|
||||
g_destroy_geom(zgp);
|
||||
return (cp);
|
||||
}
|
||||
|
||||
@ -667,7 +672,6 @@ vdev_geom_open_by_path(vdev_t *vd, int check_guid)
|
||||
{
|
||||
struct g_provider *pp;
|
||||
struct g_consumer *cp;
|
||||
uint64_t pguid, vguid;
|
||||
|
||||
g_topology_assert();
|
||||
|
||||
@ -675,34 +679,8 @@ vdev_geom_open_by_path(vdev_t *vd, int check_guid)
|
||||
pp = g_provider_by_name(vd->vdev_path + sizeof("/dev/") - 1);
|
||||
if (pp != NULL) {
|
||||
ZFS_LOG(1, "Found provider by name %s.", vd->vdev_path);
|
||||
cp = vdev_geom_attach(pp, vd);
|
||||
if (cp != NULL && check_guid && ISP2(pp->sectorsize) &&
|
||||
pp->sectorsize <= VDEV_PAD_SIZE) {
|
||||
g_topology_unlock();
|
||||
vdev_geom_read_guids(cp, &pguid, &vguid);
|
||||
g_topology_lock();
|
||||
/*
|
||||
* Check that the label's vdev guid matches the
|
||||
* desired guid. If the label has a pool guid,
|
||||
* check that it matches too. (Inactive spares
|
||||
* and L2ARCs do not have any pool guid in the
|
||||
* label.)
|
||||
*/
|
||||
if ((pguid != 0 &&
|
||||
pguid != spa_guid(vd->vdev_spa)) ||
|
||||
vguid != vd->vdev_guid) {
|
||||
vdev_geom_close_locked(vd);
|
||||
cp = NULL;
|
||||
ZFS_LOG(1, "guid mismatch for provider %s: "
|
||||
"%ju:%ju != %ju:%ju.", vd->vdev_path,
|
||||
(uintmax_t)spa_guid(vd->vdev_spa),
|
||||
(uintmax_t)vd->vdev_guid,
|
||||
(uintmax_t)pguid, (uintmax_t)vguid);
|
||||
} else {
|
||||
ZFS_LOG(1, "guid match for provider %s.",
|
||||
vd->vdev_path);
|
||||
}
|
||||
}
|
||||
if (!check_guid || vdev_attach_ok(vd, pp))
|
||||
cp = vdev_geom_attach(pp, vd);
|
||||
}
|
||||
|
||||
return (cp);
|
||||
|
Loading…
x
Reference in New Issue
Block a user