From 2a3871d4bcc65dff7be4c9b55cb863421ddc8c3a Mon Sep 17 00:00:00 2001 From: Richard Yao Date: Tue, 2 Jul 2013 11:59:10 -0700 Subject: [PATCH] Cleanup zvol initialization code The following error will occur on some (possibly all) kernels because blk_init_queue() will try to take the spinlock before we initialize it. BUG: spinlock bad magic on CPU#0, zpool/4054 lock: 0xffff88021a73de60, .magic: 00000000, .owner: /-1, .owner_cpu: 0 Pid: 4054, comm: zpool Not tainted 3.9.3 #11 Call Trace: [] spin_dump+0x8c/0x91 [] spin_bug+0x21/0x26 [] do_raw_spin_lock+0x127/0x130 [] _raw_spin_lock_irq+0x21/0x30 [] cfq_init_queue+0x1fe/0x350 [] elevator_init+0x78/0x140 [] blk_init_allocated_queue+0x87/0xb0 [] blk_init_queue_node+0x35/0x70 [] blk_init_queue+0xe/0x10 [] __zvol_create_minor+0x24b/0x620 [] zvol_create_minors_cb+0x24/0x30 [] dmu_objset_find_spa+0xea/0x510 [] dmu_objset_find_spa+0x191/0x510 [] zvol_create_minors+0x92/0x180 [] spa_open_common+0x250/0x380 [] spa_open+0xe/0x10 [] pool_status_check.part.22+0x1e/0x80 [] zfsdev_ioctl+0x155/0x190 [] do_vfs_ioctl+0x325/0x5a0 [] sys_ioctl+0x40/0x80 [] ? do_page_fault+0x9/0x10 [] system_call_fastpath+0x16/0x1b zd0: unknown partition table We fix this by calling spin_lock_init before blk_init_queue. The manner in which zvol_init() initializes structures is suspectible to a race between initialization and a probe on a zvol. We reorganize zvol_init() to prevent that. Signed-off-by: Richard Yao Signed-off-by: Brian Behlendorf --- module/zfs/zvol.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 43a7bb69f0..97b65c815e 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -1215,6 +1215,9 @@ zvol_alloc(dev_t dev, const char *name) zv = kmem_zalloc(sizeof (zvol_state_t), KM_SLEEP); + spin_lock_init(&zv->zv_lock); + list_link_init(&zv->zv_next); + zv->zv_queue = blk_init_queue(zvol_request, &zv->zv_lock); if (zv->zv_queue == NULL) goto out_kmem; @@ -1248,9 +1251,6 @@ zvol_alloc(dev_t dev, const char *name) sizeof (rl_t), offsetof(rl_t, r_node)); zv->zv_znode.z_is_zvol = TRUE; - spin_lock_init(&zv->zv_lock); - list_link_init(&zv->zv_next); - zv->zv_disk->major = zvol_major; zv->zv_disk->first_minor = (dev & MINORMASK); zv->zv_disk->fops = &zvol_ops; @@ -1561,30 +1561,38 @@ zvol_init(void) { int error; + list_create(&zvol_state_list, sizeof (zvol_state_t), + offsetof(zvol_state_t, zv_next)); + mutex_init(&zvol_state_lock, NULL, MUTEX_DEFAULT, NULL); + zvol_taskq = taskq_create(ZVOL_DRIVER, zvol_threads, maxclsyspri, zvol_threads, INT_MAX, TASKQ_PREPOPULATE); if (zvol_taskq == NULL) { printk(KERN_INFO "ZFS: taskq_create() failed\n"); - return (-ENOMEM); + error = -ENOMEM; + goto out1; } error = register_blkdev(zvol_major, ZVOL_DRIVER); if (error) { printk(KERN_INFO "ZFS: register_blkdev() failed %d\n", error); - taskq_destroy(zvol_taskq); - return (error); + goto out2; } blk_register_region(MKDEV(zvol_major, 0), 1UL << MINORBITS, THIS_MODULE, zvol_probe, NULL, NULL); - mutex_init(&zvol_state_lock, NULL, MUTEX_DEFAULT, NULL); - list_create(&zvol_state_list, sizeof (zvol_state_t), - offsetof(zvol_state_t, zv_next)); - (void) zvol_create_minors(NULL); return (0); + +out2: + taskq_destroy(zvol_taskq); +out1: + mutex_destroy(&zvol_state_lock); + list_destroy(&zvol_state_list); + + return (error); } void