Commit 06f0e9e6 authored by Ming Lei's avatar Ming Lei Committed by Jens Axboe

block: loop: fix another reread part failure

loop_clr_fd() can be run piggyback with lo_release(), and
under this situation, reread partition may always fail because
bd_mutex has been held already.

This patch detects the situation by the reference count, and
call __blkdev_reread_part() to avoid acquiring the lock again.

In the meantime, this patch switches to new kernel APIs
of blkdev_reread_part() and __blkdev_reread_part().
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Tested-by: default avatarJarod Wilson <jarod@redhat.com>
Acked-by: default avatarJarod Wilson <jarod@redhat.com>
Signed-off-by: default avatarJarod Wilson <jarod@redhat.com>
Signed-off-by: default avatarMing Lei <ming.lei@canonical.com>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent f8933667
...@@ -474,6 +474,28 @@ static int loop_flush(struct loop_device *lo) ...@@ -474,6 +474,28 @@ static int loop_flush(struct loop_device *lo)
return loop_switch(lo, NULL); return loop_switch(lo, NULL);
} }
static void loop_reread_partitions(struct loop_device *lo,
struct block_device *bdev)
{
int rc;
/*
* bd_mutex has been held already in release path, so don't
* acquire it if this function is called in such case.
*
* If the reread partition isn't from release path, lo_refcnt
* must be at least one and it can only become zero when the
* current holder is released.
*/
if (!atomic_read(&lo->lo_refcnt))
rc = __blkdev_reread_part(bdev);
else
rc = blkdev_reread_part(bdev);
if (rc)
pr_warn("%s: partition scan of loop%d (%s) failed (rc=%d)\n",
__func__, lo->lo_number, lo->lo_file_name, rc);
}
/* /*
* loop_change_fd switched the backing store of a loopback device to * loop_change_fd switched the backing store of a loopback device to
* a new file. This is useful for operating system installers to free up * a new file. This is useful for operating system installers to free up
...@@ -522,7 +544,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, ...@@ -522,7 +544,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
fput(old_file); fput(old_file);
if (lo->lo_flags & LO_FLAGS_PARTSCAN) if (lo->lo_flags & LO_FLAGS_PARTSCAN)
ioctl_by_bdev(bdev, BLKRRPART, 0); loop_reread_partitions(lo, bdev);
return 0; return 0;
out_putf: out_putf:
...@@ -759,7 +781,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, ...@@ -759,7 +781,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
if (part_shift) if (part_shift)
lo->lo_flags |= LO_FLAGS_PARTSCAN; lo->lo_flags |= LO_FLAGS_PARTSCAN;
if (lo->lo_flags & LO_FLAGS_PARTSCAN) if (lo->lo_flags & LO_FLAGS_PARTSCAN)
ioctl_by_bdev(bdev, BLKRRPART, 0); loop_reread_partitions(lo, bdev);
/* Grab the block_device to prevent its destruction after we /* Grab the block_device to prevent its destruction after we
* put /dev/loopXX inode. Later in loop_clr_fd() we bdput(bdev). * put /dev/loopXX inode. Later in loop_clr_fd() we bdput(bdev).
...@@ -877,7 +899,7 @@ static int loop_clr_fd(struct loop_device *lo) ...@@ -877,7 +899,7 @@ static int loop_clr_fd(struct loop_device *lo)
blk_mq_unfreeze_queue(lo->lo_queue); blk_mq_unfreeze_queue(lo->lo_queue);
if (lo->lo_flags & LO_FLAGS_PARTSCAN && bdev) if (lo->lo_flags & LO_FLAGS_PARTSCAN && bdev)
ioctl_by_bdev(bdev, BLKRRPART, 0); loop_reread_partitions(lo, bdev);
lo->lo_flags = 0; lo->lo_flags = 0;
if (!part_shift) if (!part_shift)
lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN; lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN;
...@@ -954,7 +976,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) ...@@ -954,7 +976,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
!(lo->lo_flags & LO_FLAGS_PARTSCAN)) { !(lo->lo_flags & LO_FLAGS_PARTSCAN)) {
lo->lo_flags |= LO_FLAGS_PARTSCAN; lo->lo_flags |= LO_FLAGS_PARTSCAN;
lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN;
ioctl_by_bdev(lo->lo_device, BLKRRPART, 0); loop_reread_partitions(lo, lo->lo_device);
} }
lo->lo_encrypt_key_size = info->lo_encrypt_key_size; lo->lo_encrypt_key_size = info->lo_encrypt_key_size;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment