diff --git a/block/blk-core.c b/block/blk-core.c index 7c1a24fb09d8..6742c49b38e4 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -378,7 +378,8 @@ EXPORT_SYMBOL(blk_run_queue); void blk_put_queue(struct request_queue *q) { - kobject_put(&q->kobj); + if (q) + kobject_put(&q->kobj); } EXPORT_SYMBOL(blk_put_queue); diff --git a/fs/block_dev.c b/fs/block_dev.c index 05b553368bb4..7503110e37ff 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -589,12 +589,22 @@ static struct inode *bdev_alloc_inode(struct super_block *sb) return &ei->vfs_inode; } +static void bdev_release(struct work_struct *w) +{ + struct block_device *bdev = container_of(w, typeof(*bdev), bd_release); + struct bdev_inode *bdi = container_of(bdev, typeof(*bdi), bdev); + + blk_put_queue(bdev->bd_queue); + kmem_cache_free(bdev_cachep, bdi); +} + static void bdev_i_callback(struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); - struct bdev_inode *bdi = BDEV_I(inode); + struct block_device *bdev = &BDEV_I(inode)->bdev; - kmem_cache_free(bdev_cachep, bdi); + /* blk_put_queue needs process context */ + schedule_work(&bdev->bd_release); } static void bdev_destroy_inode(struct inode *inode) @@ -613,6 +623,7 @@ static void init_once(void *foo) #ifdef CONFIG_SYSFS INIT_LIST_HEAD(&bdev->bd_holder_disks); #endif + INIT_WORK(&bdev->bd_release, bdev_release); inode_init_once(&ei->vfs_inode); /* Initialize mutex for freeze. */ mutex_init(&bdev->bd_fsfreeze_mutex); @@ -1268,6 +1279,8 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) mutex_lock_nested(&bdev->bd_mutex, for_part); if (!bdev->bd_openers) { bdev->bd_disk = disk; + if (!blk_get_queue(disk->queue)) + goto out_clear; bdev->bd_queue = disk->queue; bdev->bd_contains = bdev; @@ -1288,7 +1301,6 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) disk_put_part(bdev->bd_part); bdev->bd_part = NULL; bdev->bd_disk = NULL; - bdev->bd_queue = NULL; mutex_unlock(&bdev->bd_mutex); disk_unblock_events(disk); put_disk(disk); @@ -1364,7 +1376,6 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) disk_put_part(bdev->bd_part); bdev->bd_disk = NULL; bdev->bd_part = NULL; - bdev->bd_queue = NULL; if (bdev != bdev->bd_contains) __blkdev_put(bdev->bd_contains, mode, 1); bdev->bd_contains = NULL; diff --git a/include/linux/fs.h b/include/linux/fs.h index dc0478c07b2a..f084e4a2198b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -488,6 +488,7 @@ struct block_device { int bd_fsfreeze_count; /* Mutex for freeze */ struct mutex bd_fsfreeze_mutex; + struct work_struct bd_release; }; /*