]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
block: Make blk_get_backing_dev_info() safe without open bdev
authorJan Kara <jack@suse.cz>
Thu, 2 Feb 2017 14:56:52 +0000 (15:56 +0100)
committerThadeu Lima de Souza Cascardo <cascardo@canonical.com>
Thu, 27 Apr 2017 13:04:14 +0000 (10:04 -0300)
BugLink: http://bugs.launchpad.net/bugs/1659111
Currenly blk_get_backing_dev_info() is not safe to be called when the
block device is not open as bdev->bd_disk is NULL in that case. However
inode_to_bdi() uses this function and may be call called from flusher
worker or other writeback related functions without bdev being open
which leads to crashes such as:

[113031.075540] Unable to handle kernel paging request for data at address 0x00000000
[113031.075614] Faulting instruction address: 0xc0000000003692e0
0:mon> t
[c0000000fb65f900c00000000036cb6c writeback_sb_inodes+0x30c/0x590
[c0000000fb65fa10c00000000036ced4 __writeback_inodes_wb+0xe4/0x150
[c0000000fb65fa70c00000000036d33c wb_writeback+0x30c/0x450
[c0000000fb65fb40c00000000036e198 wb_workfn+0x268/0x580
[c0000000fb65fc50c0000000000f3470 process_one_work+0x1e0/0x590
[c0000000fb65fce0c0000000000f38c8 worker_thread+0xa8/0x660
[c0000000fb65fd80c0000000000fc4b0 kthread+0x110/0x130
[c0000000fb65fe30c0000000000098f0 ret_from_kernel_thread+0x5c/0x6c

Signed-off-by: Jens Axboe <axboe@fb.com>
(back ported from commit b1d2dc5659b41741f5a29b2ade76ffb4e5bb13d8)

Conflicts:
fs/block_dev.c

Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
Acked-by: Seth Forshee <seth.forshee@canonical.com>
Acked-by: Brad Figg <brad.figg@canonical.com>
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
block/blk-core.c
fs/block_dev.c
include/linux/fs.h

index d88ea555e6d0969185a45bb766ce2cdbe908437b..1f120337e1b64d5318c741837e0cae74365dc617 100644 (file)
@@ -108,14 +108,12 @@ void blk_queue_congestion_threshold(struct request_queue *q)
  * @bdev:      device
  *
  * Locates the passed device's request queue and returns the address of its
- * backing_dev_info.  This function can only be called if @bdev is opened
- * and the return value is never NULL.
+ * backing_dev_info. The return value is never NULL however we may return
+ * &noop_backing_dev_info if the bdev is not currently open.
  */
 struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev)
 {
-       struct request_queue *q = bdev_get_queue(bdev);
-
-       return q->backing_dev_info;
+       return bdev->bd_bdi;
 }
 EXPORT_SYMBOL(blk_get_backing_dev_info);
 
index 702480b40d6403d5e5845d7305e5fc5c20be1e8e..3783ea8d36810c420f3e1cc5f279ec2dceaf0b5f 100644 (file)
@@ -558,6 +558,8 @@ static void bdev_evict_inode(struct inode *inode)
        }
        list_del_init(&bdev->bd_list);
        spin_unlock(&bdev_lock);
+       if (bdev->bd_bdi != &noop_backing_dev_info)
+               bdi_put(bdev->bd_bdi);
 }
 
 static const struct super_operations bdev_sops = {
@@ -656,6 +658,7 @@ struct block_device *bdget(dev_t dev)
                bdev->bd_contains = NULL;
                bdev->bd_super = NULL;
                bdev->bd_inode = inode;
+               bdev->bd_bdi = &noop_backing_dev_info;
                bdev->bd_block_size = (1 << inode->i_blkbits);
                bdev->bd_part_count = 0;
                bdev->bd_invalidated = 0;
@@ -1219,6 +1222,9 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
                bdev->bd_queue = disk->queue;
                bdev->bd_contains = bdev;
                bdev->bd_inode->i_flags = disk->fops->direct_access ? S_DAX : 0;
+               if (bdev->bd_bdi == &noop_backing_dev_info)
+                       bdev->bd_bdi = bdi_get(disk->queue->backing_dev_info);
+
                if (!partno) {
                        ret = -ENXIO;
                        bdev->bd_part = disk_get_part(disk, partno);
@@ -1319,6 +1325,8 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
        bdev->bd_disk = NULL;
        bdev->bd_part = NULL;
        bdev->bd_queue = NULL;
+       bdi_put(bdev->bd_bdi);
+       bdev->bd_bdi = &noop_backing_dev_info;
        if (bdev != bdev->bd_contains)
                __blkdev_put(bdev->bd_contains, mode, 1);
        bdev->bd_contains = NULL;
index f0ecce4741109d56defb6ac76e1c006aa50d819b..df3ea97f91684c29ddadc2f916276041ec83854a 100644 (file)
@@ -470,6 +470,7 @@ struct block_device {
        int                     bd_invalidated;
        struct gendisk *        bd_disk;
        struct request_queue *  bd_queue;
+       struct backing_dev_info *bd_bdi;
        struct list_head        bd_list;
        /*
         * Private data.  You must have bd_claim'ed the block_device