lkml.org 
[lkml]   [2011]   [Mar]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subjectblock: Fix oops caused by __blkdev_get() calling disk_unblock_events() with invalid @disk
    Commit 57c966b8b2 (block: Don't check events while open is in
    progress) made __blkdev_get() block events around open calls; however,
    it used invalid @disk pointer in the following cases.

    * When ->open() returns -ERESTARTSYS, disk_unblock_events() is called
    after @disk is put. @disk may be invalid by the time unblock is
    called.

    This is fixed by moving references after disk_unblock_events().

    * When there are multiple openers, @disk is cleared to %NULL and later
    disk_unblock_disk() is called with %NULL @disk causing oops.

    This is fixed by moving reference putting after open success is
    determined and not clearing @disk to %NULL. On success, @disk is
    valid because there is another opener holding reference to it. On
    failure, the references are put after disk_unblock_events() is
    called.

    Signed-off-by: Tejun Heo <tj@kernel.org>
    Reported-by: Jens Axboe <axboe@kernel.dk>
    ---
    Ah, sorry about that. My test setup didn't include a device with
    multiple partitions on it so the more than one opener case never
    triggered.

    Thanks.

    fs/block_dev.c | 13 ++++++-------
    1 file changed, 6 insertions(+), 7 deletions(-)

    Index: work/fs/block_dev.c
    ===================================================================
    --- work.orig/fs/block_dev.c
    +++ work/fs/block_dev.c
    @@ -1109,11 +1109,11 @@ static int __blkdev_get(struct block_dev
    */
    disk_put_part(bdev->bd_part);
    bdev->bd_part = NULL;
    - module_put(disk->fops->owner);
    - put_disk(disk);
    bdev->bd_disk = NULL;
    mutex_unlock(&bdev->bd_mutex);
    disk_unblock_events(disk);
    + module_put(disk->fops->owner);
    + put_disk(disk);
    goto restart;
    }
    if (ret)
    @@ -1150,9 +1150,6 @@ static int __blkdev_get(struct block_dev
    bd_set_size(bdev, (loff_t)bdev->bd_part->nr_sects << 9);
    }
    } else {
    - module_put(disk->fops->owner);
    - put_disk(disk);
    - disk = NULL;
    if (bdev->bd_contains == bdev) {
    if (bdev->bd_disk->fops->open) {
    ret = bdev->bd_disk->fops->open(bdev, mode);
    @@ -1162,6 +1159,9 @@ static int __blkdev_get(struct block_dev
    if (bdev->bd_invalidated)
    rescan_partitions(bdev->bd_disk, bdev);
    }
    + /* only one opener holds refs to the module and disk */
    + module_put(disk->fops->owner);
    + put_disk(disk);
    }
    bdev->bd_openers++;
    if (for_part)
    @@ -1182,8 +1182,7 @@ static int __blkdev_get(struct block_dev
    mutex_unlock(&bdev->bd_mutex);
    disk_unblock_events(disk);
    out:
    - if (disk)
    - module_put(disk->fops->owner);
    + module_put(disk->fops->owner);
    put_disk(disk);
    bdput(bdev);


    \
     
     \ /
      Last update: 2011-03-09 16:43    [W:0.025 / U:0.516 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site