From f3aa3b93df710fa008546deb19cf0b983cfd3692 Mon Sep 17 00:00:00 2001 From: Brian Behlendorf Date: Wed, 4 Aug 2010 14:39:12 -0700 Subject: [PATCH] Fix zvol partition creation during module load Partitions for a zvol device were not appearing in /dev/zvol// at module load time for a couple of reasons. 1) The Linux block layer expects a block device to have a non-zero capacity during add_disk(). If the capacity is zero it does not attempt to open the device which means we never trigger a partition scan. The device capacity was just being set during the first open. 2) Because we expect to be adding a block device to the zvol_state_list during zvol_create_minor() the zvol_state_lock() is held. This can result in a deadlock in add_disk() when it attempts to open the block device via zvol_open() which also takes this same lock. To avoid this issue special handling has been added to zvol_open() and zvol_release() to allow the mutex owner to enter these functions without retaking the lock. 3) In __zvol_create_minor() the call to dmu_objset_disown() must occur before the call to add_disk(). As mentioned above add_disk() results in a call to zvol_open() which will attempt to call dmu_objset_own() again on the objset. If the objset is already open it will fail resulting in a failed open. This in turn means the kernel will be unable to read the partition information from the device. --- module/zfs/zvol.c | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 16c65d348c..15716aba3c 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -815,9 +815,19 @@ static int zvol_open(struct block_device *bdev, fmode_t flag) { zvol_state_t *zv = bdev->bd_disk->private_data; - int error = 0; + int error = 0, drop_mutex = 0; + + /* + * If the caller is already holding the mutex do not take it + * again, this will happen as part of zvol_create_minor(). + * Once add_disk() is called the device is live and the kernel + * will attempt to open it to read the partition information. + */ + if (!mutex_owned(&zvol_state_lock)) { + mutex_enter(&zvol_state_lock); + drop_mutex = 1; + } - mutex_enter(&zvol_state_lock); ASSERT3P(zv, !=, NULL); if (zv->zv_open_count == 0) { @@ -839,7 +849,8 @@ out_open_count: zvol_last_close(zv); out_mutex: - mutex_exit(&zvol_state_lock); + if (drop_mutex) + mutex_exit(&zvol_state_lock); check_disk_change(bdev); @@ -850,15 +861,21 @@ static int zvol_release(struct gendisk *disk, fmode_t mode) { zvol_state_t *zv = disk->private_data; + int drop_mutex = 0; + + if (!mutex_owned(&zvol_state_lock)) { + mutex_enter(&zvol_state_lock); + drop_mutex = 1; + } - mutex_enter(&zvol_state_lock); ASSERT3P(zv, !=, NULL); ASSERT3U(zv->zv_open_count, >, 0); zv->zv_open_count--; if (zv->zv_open_count == 0) zvol_last_close(zv); - mutex_exit(&zvol_state_lock); + if (drop_mutex) + mutex_exit(&zvol_state_lock); return (0); } @@ -1083,6 +1100,7 @@ __zvol_create_minor(const char *name) zvol_state_t *zv; objset_t *os; dmu_object_info_t *doi; + uint64_t volsize; unsigned minor = 0; int error = 0; @@ -1104,6 +1122,10 @@ __zvol_create_minor(const char *name) if (error) goto out_dmu_objset_disown; + error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &volsize); + if (error) + goto out_dmu_objset_disown; + error = zvol_find_minor(&minor); if (error) goto out_dmu_objset_disown; @@ -1118,23 +1140,28 @@ __zvol_create_minor(const char *name) zv->zv_flags |= ZVOL_RDONLY; zv->zv_volblocksize = doi->doi_data_block_size; + zv->zv_volsize = volsize; zv->zv_objset = os; + set_capacity(zv->zv_disk, zv->zv_volsize >> 9); + if (zil_replay_disable) zil_destroy(dmu_objset_zil(os), B_FALSE); else zil_replay(os, zv, zvol_replay_vector); - zvol_insert(zv); - add_disk(zv->zv_disk); - error = 0; - out_dmu_objset_disown: dmu_objset_disown(os, zvol_tag); zv->zv_objset = NULL; out_doi: kmem_free(doi, sizeof(dmu_object_info_t)); out: + + if (error == 0) { + zvol_insert(zv); + add_disk(zv->zv_disk); + } + return (error); }