zfs: allow a zvol to be used as a pool vdev, again

Do this by checking if spa_namespace_lock is already held and not taking
it again in that case.
Add a comment explaining why that is done and why it is safe.

Reviewed by:	pjd
MFC after:	24 days
This commit is contained in:
Andriy Gapon 2012-09-22 17:42:53 +00:00
parent a6512306ce
commit 81c4584e30

View File

@ -878,27 +878,36 @@ zvol_open(struct g_provider *pp, int flag, int count)
{ {
zvol_state_t *zv; zvol_state_t *zv;
int err = 0; int err = 0;
boolean_t locked = B_FALSE;
if (MUTEX_HELD(&spa_namespace_lock)) { /*
/* * Protect against recursively entering spa_namespace_lock
* If the spa_namespace_lock is being held, it means that ZFS * when spa_open() is used for a pool on a (local) ZVOL(s).
* is trying to open ZVOL as its VDEV. This is not supported. * This is needed since we replaced upstream zfsdev_state_lock
*/ * with spa_namespace_lock in the ZVOL code.
return (EOPNOTSUPP); * We are using the same trick as spa_open().
* Note that calls in zvol_first_open which need to resolve
* pool name to a spa object will enter spa_open()
* recursively, but that function already has all the
* necessary protection.
*/
if (!MUTEX_HELD(&spa_namespace_lock)) {
mutex_enter(&spa_namespace_lock);
locked = B_TRUE;
} }
mutex_enter(&spa_namespace_lock);
zv = pp->private; zv = pp->private;
if (zv == NULL) { if (zv == NULL) {
mutex_exit(&spa_namespace_lock); if (locked)
mutex_exit(&spa_namespace_lock);
return (ENXIO); return (ENXIO);
} }
if (zv->zv_total_opens == 0) if (zv->zv_total_opens == 0)
err = zvol_first_open(zv); err = zvol_first_open(zv);
if (err) { if (err) {
mutex_exit(&spa_namespace_lock); if (locked)
mutex_exit(&spa_namespace_lock);
return (err); return (err);
} }
if ((flag & FWRITE) && (zv->zv_flags & ZVOL_RDONLY)) { if ((flag & FWRITE) && (zv->zv_flags & ZVOL_RDONLY)) {
@ -920,13 +929,15 @@ zvol_open(struct g_provider *pp, int flag, int count)
#endif #endif
zv->zv_total_opens += count; zv->zv_total_opens += count;
mutex_exit(&spa_namespace_lock); if (locked)
mutex_exit(&spa_namespace_lock);
return (err); return (err);
out: out:
if (zv->zv_total_opens == 0) if (zv->zv_total_opens == 0)
zvol_last_close(zv); zvol_last_close(zv);
mutex_exit(&spa_namespace_lock); if (locked)
mutex_exit(&spa_namespace_lock);
return (err); return (err);
} }
@ -936,12 +947,18 @@ zvol_close(struct g_provider *pp, int flag, int count)
{ {
zvol_state_t *zv; zvol_state_t *zv;
int error = 0; int error = 0;
boolean_t locked = B_FALSE;
mutex_enter(&spa_namespace_lock); /* See comment in zvol_open(). */
if (!MUTEX_HELD(&spa_namespace_lock)) {
mutex_enter(&spa_namespace_lock);
locked = B_TRUE;
}
zv = pp->private; zv = pp->private;
if (zv == NULL) { if (zv == NULL) {
mutex_exit(&spa_namespace_lock); if (locked)
mutex_exit(&spa_namespace_lock);
return (ENXIO); return (ENXIO);
} }
@ -964,7 +981,8 @@ zvol_close(struct g_provider *pp, int flag, int count)
if (zv->zv_total_opens == 0) if (zv->zv_total_opens == 0)
zvol_last_close(zv); zvol_last_close(zv);
mutex_exit(&spa_namespace_lock); if (locked)
mutex_exit(&spa_namespace_lock);
return (error); return (error);
} }