Skip to content

Commit

Permalink
Diff reduction against FreeBSD. This also solves the hang issue when
Browse files Browse the repository at this point in the history
detaching device from mirror.

Ticket: #4037
  • Loading branch information
delphij committed Feb 5, 2014
1 parent bb1f8cb commit 9dfc052
Showing 1 changed file with 53 additions and 134 deletions.
187 changes: 53 additions & 134 deletions sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,61 +101,8 @@ vdev_geom_orphan(struct g_consumer *cp)
spa_async_request(vd->vdev_spa, SPA_ASYNC_REMOVE);
}

static void
vdev_geom_attrchanged(struct g_consumer *cp, const char *attr)
{
vdev_t *vd;
spa_t *spa;
char *physpath;
int error, physpath_len;

g_topology_assert();

if (strcmp(attr, "GEOM::physpath") != 0)
return;

if (g_access(cp, 1, 0, 0) != 0)
return;

/*
* Record/Update physical path information for this device.
*/
vd = cp->private;
spa = vd->vdev_spa;
physpath_len = MAXPATHLEN;
physpath = g_malloc(physpath_len, M_WAITOK|M_ZERO);
error = g_io_getattr("GEOM::physpath", cp, &physpath_len, physpath);
g_access(cp, -1, 0, 0);
if (error == 0) {
char *old_physpath;

old_physpath = vd->vdev_physpath;
vd->vdev_physpath = spa_strdup(physpath);
spa_async_request(spa, SPA_ASYNC_CONFIG_UPDATE);

if (old_physpath != NULL) {
int held_lock;

held_lock = spa_config_held(spa, SCL_STATE, RW_WRITER);
if (held_lock == 0) {
g_topology_unlock();
spa_config_enter(spa, SCL_STATE, FTAG,
RW_WRITER);
}

spa_strfree(old_physpath);

if (held_lock == 0) {
spa_config_exit(spa, SCL_STATE, FTAG);
g_topology_lock();
}
}
}
g_free(physpath);
}

static struct g_consumer *
vdev_geom_attach(struct g_provider *pp, vdev_t *vd)
vdev_geom_attach(struct g_provider *pp)
{
struct g_geom *gp;
struct g_consumer *cp;
Expand All @@ -174,7 +121,6 @@ vdev_geom_attach(struct g_provider *pp, vdev_t *vd)
if (gp == NULL) {
gp = g_new_geomf(&zfs_vdev_class, "zfs::vdev");
gp->orphan = vdev_geom_orphan;
gp->attrchanged = vdev_geom_attrchanged;
cp = g_new_consumer(gp);
if (g_attach(cp, pp) != 0) {
g_wither_geom(gp, ENXIO);
Expand Down Expand Up @@ -211,12 +157,6 @@ vdev_geom_attach(struct g_provider *pp, vdev_t *vd)
ZFS_LOG(1, "Used existing consumer for %s.", pp->name);
}
}

cp->private = vd;

/* Fetch initial physical path information for this device. */
vdev_geom_attrchanged(cp, "GEOM::physpath");

return (cp);
}

Expand All @@ -225,18 +165,12 @@ vdev_geom_detach(void *arg, int flag __unused)
{
struct g_geom *gp;
struct g_consumer *cp;
vdev_t *vd;

g_topology_assert();
cp = arg;
gp = cp->geom;

ZFS_LOG(1, "Closing access to %s.", cp->provider->name);
vd = cp->private;
if (vd != NULL) {
vd->vdev_tsd = NULL;
cp->private = NULL;
}
g_access(cp, -1, 0, -1);
/* Destroy consumer on last close. */
if (cp->acr == 0 && cp->ace == 0) {
Expand All @@ -254,16 +188,12 @@ vdev_geom_detach(void *arg, int flag __unused)
}

static uint64_t
nvlist_get_guid(nvlist_t *list, uint64_t *pguid)
nvlist_get_guid(nvlist_t *list)
{
uint64_t value;

value = 0;
nvlist_lookup_uint64(list, ZPOOL_CONFIG_GUID, &value);
if (pguid) {
*pguid = 0;
nvlist_lookup_uint64(list, ZPOOL_CONFIG_POOL_GUID, pguid);
}
return (value);
}

Expand Down Expand Up @@ -450,12 +380,18 @@ vdev_geom_attach_taster(struct g_consumer *cp, struct g_provider *pp)

if (pp->flags & G_PF_WITHER)
return (EINVAL);
if (pp->sectorsize > VDEV_PAD_SIZE || !ISP2(pp->sectorsize))
return (EINVAL);
if (strcmp(pp->geom->class->name, ZFSVOL_CLASS_NAME) == 0)
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);
Expand Down Expand Up @@ -525,7 +461,7 @@ vdev_geom_read_pool_label(const char *name,
}

static uint64_t
vdev_geom_read_guid(struct g_consumer *cp, uint64_t *pguid)
vdev_geom_read_guid(struct g_consumer *cp)
{
nvlist_t *config;
uint64_t guid;
Expand All @@ -534,22 +470,20 @@ vdev_geom_read_guid(struct g_consumer *cp, uint64_t *pguid)

guid = 0;
if (vdev_geom_read_config(cp, &config) == 0) {
guid = nvlist_get_guid(config, pguid);
guid = nvlist_get_guid(config);
nvlist_free(config);
} else if (pguid)
*pguid = 0;
}
return (guid);
}

static struct g_consumer *
vdev_geom_attach_by_guids(vdev_t *vd)
vdev_geom_attach_by_guid(uint64_t guid)
{
struct g_class *mp;
struct g_geom *gp, *zgp;
struct g_provider *pp;
struct g_consumer *cp, *zcp;
uint64_t pguid;
uint64_t vguid;

g_topology_assert();

Expand All @@ -569,13 +503,12 @@ vdev_geom_attach_by_guids(vdev_t *vd)
if (vdev_geom_attach_taster(zcp, pp) != 0)
continue;
g_topology_unlock();
vguid = vdev_geom_read_guid(zcp, &pguid);
pguid = vdev_geom_read_guid(zcp);
g_topology_lock();
vdev_geom_detach_taster(zcp);
if (pguid != spa_guid(vd->vdev_spa) ||
vguid != vd->vdev_guid)
if (pguid != guid)
continue;
cp = vdev_geom_attach(pp, vd);
cp = vdev_geom_attach(pp);
if (cp == NULL) {
printf("ZFS WARNING: Unable to attach to %s.\n",
pp->name);
Expand All @@ -596,17 +529,16 @@ vdev_geom_attach_by_guids(vdev_t *vd)
}

static struct g_consumer *
vdev_geom_open_by_guids(vdev_t *vd)
vdev_geom_open_by_guid(vdev_t *vd)
{
struct g_consumer *cp;
char *buf;
size_t len;

g_topology_assert();

ZFS_LOG(1, "Searching by guids [%ju:%ju].",
(uintmax_t)spa_guid(vd->vdev_spa), (uintmax_t)vd->vdev_guid);
cp = vdev_geom_attach_by_guids(vd);
ZFS_LOG(1, "Searching by guid [%ju].", (uintmax_t)vd->vdev_guid);
cp = vdev_geom_attach_by_guid(vd->vdev_guid);
if (cp != NULL) {
len = strlen(cp->provider->name) + strlen("/dev/") + 1;
buf = kmem_alloc(len, KM_SLEEP);
Expand All @@ -615,12 +547,10 @@ vdev_geom_open_by_guids(vdev_t *vd)
spa_strfree(vd->vdev_path);
vd->vdev_path = buf;

ZFS_LOG(1, "Attach by guids [%ju:%ju] succeeded, provider %s.",
(uintmax_t)spa_guid(vd->vdev_spa),
ZFS_LOG(1, "Attach by guid [%ju] succeeded, provider %s.",
(uintmax_t)vd->vdev_guid, vd->vdev_path);
} else {
ZFS_LOG(1, "Search by guids [%ju:%ju] failed.",
(uintmax_t)spa_guid(vd->vdev_spa),
ZFS_LOG(1, "Search by guid [%ju] failed.",
(uintmax_t)vd->vdev_guid);
}

Expand All @@ -632,32 +562,28 @@ vdev_geom_open_by_path(vdev_t *vd, int check_guid)
{
struct g_provider *pp;
struct g_consumer *cp;
uint64_t pguid;
uint64_t vguid;
uint64_t guid;

g_topology_assert();

cp = NULL;
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);
cp = vdev_geom_attach(pp);
if (cp != NULL && check_guid && ISP2(pp->sectorsize) &&
pp->sectorsize <= VDEV_PAD_SIZE) {
g_topology_unlock();
vguid = vdev_geom_read_guid(cp, &pguid);
guid = vdev_geom_read_guid(cp);
g_topology_lock();
if (pguid != spa_guid(vd->vdev_spa) ||
vguid != vd->vdev_guid) {
if (guid != vd->vdev_guid) {
vdev_geom_detach(cp, 0);
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);
"%ju != %ju.", vd->vdev_path,
(uintmax_t)vd->vdev_guid, (uintmax_t)guid);
} else {
ZFS_LOG(1, "guids match for provider %s.",
ZFS_LOG(1, "guid match for provider %s.",
vd->vdev_path);
}
}
Expand Down Expand Up @@ -690,37 +616,23 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
error = 0;

/*
* Try using the recorded path for this device, but only
* accept it if its label data contains the expected GUIDs.
* If we're creating or splitting a pool, just find the GEOM provider
* by its name and ignore GUID mismatches.
*/
cp = vdev_geom_open_by_path(vd, 1);
if (cp == NULL) {
/*
* The device at vd->vdev_path doesn't have the
* expected GUIDs. The disks might have merely
* moved around so try all other GEOM providers
* to find one with the right GUIDs.
*/
cp = vdev_geom_open_by_guids(vd);
}

if (cp == NULL &&
((vd->vdev_prevstate == VDEV_STATE_UNKNOWN &&
vd->vdev_spa->spa_load_state == SPA_LOAD_NONE) ||
vd->vdev_spa->spa_splitting_newspa == B_TRUE)) {
/*
* We are dealing with a vdev that hasn't been previosly
* opened (since boot), and we are not loading an
* existing pool configuration (e.g. this operations is
* an add of a vdev to new or * existing pool) or we are
* in the process of splitting a pool. Find the GEOM
* provider by its name, ignoring GUID mismatches.
*
* XXPOLICY: It would be safer to only allow a device
* that is unlabeled or labeled but missing
* GUID information to be opened in this fashion.
*/
if (vd->vdev_spa->spa_load_state == SPA_LOAD_NONE ||
vd->vdev_spa->spa_splitting_newspa == B_TRUE)
cp = vdev_geom_open_by_path(vd, 0);
else {
cp = vdev_geom_open_by_path(vd, 1);
if (cp == NULL) {
/*
* The device at vd->vdev_path doesn't have the
* expected guid. The disks might have merely
* moved around so try all other GEOM providers
* to find one with the right guid.
*/
cp = vdev_geom_open_by_guid(vd);
}
}

if (cp == NULL) {
Expand Down Expand Up @@ -751,15 +663,16 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
cp = NULL;
}
}

g_topology_unlock();
PICKUP_GIANT();
if (cp == NULL) {
vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
return (error);
}
pp = cp->provider;

cp->private = vd;
vd->vdev_tsd = cp;
pp = cp->provider;

/*
* Determine the actual size of the device.
Expand All @@ -783,6 +696,12 @@ vdev_geom_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
*/
vd->vdev_nowritecache = B_FALSE;

if (vd->vdev_physpath != NULL)
spa_strfree(vd->vdev_physpath);
bufsize = sizeof("/dev/") + strlen(pp->name);
vd->vdev_physpath = kmem_alloc(bufsize, KM_SLEEP);
snprintf(vd->vdev_physpath, bufsize, "/dev/%s", pp->name);

return (0);
}

Expand Down

0 comments on commit 9dfc052

Please sign in to comment.