* [PATCH 1/7] block: use i_size_write() in bd_set_size()
2013-01-25 10:56 [PATCH 0/7] Loop device bug fix Guo Chao
@ 2013-01-25 10:56 ` Guo Chao
2013-01-25 10:56 ` [PATCH 2/7] block: remove redundant check to bd_openers Guo Chao
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Guo Chao @ 2013-01-25 10:56 UTC (permalink / raw)
To: axboe; +Cc: linux-kernel, Alexander Viro, linux-fsdevel
blkdev_ioctl(GETBLKSIZE) uses i_size_read() to read size
of block device. If we update block size directly, reader
may see intermediate result in some machines and configurations.
Use i_size_write() instead.
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Guo Chao <yan@linux.vnet.ibm.com>
---
fs/block_dev.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 172f849..82b7c9a 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1032,7 +1032,9 @@ void bd_set_size(struct block_device *bdev, loff_t size)
{
unsigned bsize = bdev_logical_block_size(bdev);
- bdev->bd_inode->i_size = size;
+ mutex_lock(&bdev->bd_inode->i_mutex);
+ i_size_write(bdev->bd_inode, size);
+ mutex_unlock(&bdev->bd_inode->i_mutex);
while (bsize < PAGE_CACHE_SIZE) {
if (size & bsize)
break;
--
1.7.9.5
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 2/7] block: remove redundant check to bd_openers
2013-01-25 10:56 [PATCH 0/7] Loop device bug fix Guo Chao
2013-01-25 10:56 ` [PATCH 1/7] block: use i_size_write() in bd_set_size() Guo Chao
@ 2013-01-25 10:56 ` Guo Chao
2013-01-25 10:56 ` [PATCH 3/7] loopdev: fix a dead lock Guo Chao
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Guo Chao @ 2013-01-25 10:56 UTC (permalink / raw)
To: axboe; +Cc: linux-kernel, Alexander Viro, linux-fsdevel
bd_openers is stable under bd_mutex, no need to check it twice.
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Guo Chao <yan@linux.vnet.ibm.com>
---
fs/block_dev.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 82b7c9a..2f70584 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1119,7 +1119,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
}
}
- if (!ret && !bdev->bd_openers) {
+ if (!ret) {
bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
bdi = blk_get_backing_dev_info(bdev);
if (bdi == NULL)
--
1.7.9.5
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 3/7] loopdev: fix a dead lock
2013-01-25 10:56 [PATCH 0/7] Loop device bug fix Guo Chao
2013-01-25 10:56 ` [PATCH 1/7] block: use i_size_write() in bd_set_size() Guo Chao
2013-01-25 10:56 ` [PATCH 2/7] block: remove redundant check to bd_openers Guo Chao
@ 2013-01-25 10:56 ` Guo Chao
2013-01-25 10:56 ` [PATCH 4/7] loopdev: update block device size in loop_set_status() Guo Chao
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Guo Chao @ 2013-01-25 10:56 UTC (permalink / raw)
To: axboe; +Cc: linux-kernel
bd_mutex and lo_ctl_mutex can be held in different order.
Path #1:
blkdev_open
blkdev_get
__blkdev_get (hold bd_mutex)
lo_open (hold lo_ctl_mutex)
Path #2:
blkdev_ioctl
lo_ioctl (hold lo_ctl_mutex)
lo_set_capacity (hold bd_mutex)
Lockdep does not report it, because path #2 actually holds
a subclass of lo_ctl_mutex. This subclass seems creep into
the code by mistake. The patch author actually just mentioned it
in the changelog, see commit f028f3b2, also see:
http://marc.info/?l=linux-kernel&m=123806169129727&w=2
Path #2 hold bd_mutex to call bd_set_size(), I've protected it
with i_mutex in a previous patch, so drop bd_mutex at this site.
Signed-off-by: Guo Chao <yan@linux.vnet.ibm.com>
---
drivers/block/loop.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index ae12512..7f80653 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1285,11 +1285,9 @@ static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev)
/* the width of sector_t may be narrow for bit-shift */
sz = sec;
sz <<= 9;
- mutex_lock(&bdev->bd_mutex);
bd_set_size(bdev, sz);
/* let user-space know about the new size */
kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
- mutex_unlock(&bdev->bd_mutex);
out:
return err;
--
1.7.9.5
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 4/7] loopdev: update block device size in loop_set_status()
2013-01-25 10:56 [PATCH 0/7] Loop device bug fix Guo Chao
` (2 preceding siblings ...)
2013-01-25 10:56 ` [PATCH 3/7] loopdev: fix a dead lock Guo Chao
@ 2013-01-25 10:56 ` Guo Chao
2013-01-25 10:56 ` [PATCH 5/7] loopdev: move common code into loop_figure_size() Guo Chao
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Guo Chao @ 2013-01-25 10:56 UTC (permalink / raw)
To: axboe; +Cc: linux-kernel
Loop device driver sometimes fails to impose the size limit on the
device. Keep issuing following two commands:
losetup --offset 7517244416 --sizelimit 3224971264 /dev/loop0 backed_file
blockdev --getsize64 /dev/loop0
blockdev reports file size instead of sizelimit several out of 100 times.
The problems are:
- losetup set up the device in two ioctl:
LOOP_SET_FD and LOOP_SET_STATUS64.
- LOOP_SET_STATUS64 only update size of gendisk.
Block device size will be updated lazily when device comes to use. If udev
rushes in between the two ioctl, it will bring in a block device whose
size is backing file size. If the device is not released after
LOOP_SET_STATUS64 ioctl, blockdev will not see the updated size.
Update block size in LOOP_SET_STATUS64 ioctl.
Reported-by: M. Hindess <hindessm@uk.ibm.com>
Signed-off-by: Guo Chao <yan@linux.vnet.ibm.com>
---
drivers/block/loop.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 7f80653..ed85036 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1092,8 +1092,13 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
if (lo->lo_offset != info->lo_offset ||
lo->lo_sizelimit != info->lo_sizelimit) {
+ struct block_device *bdev = lo->lo_device;
+
if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit))
return -EFBIG;
+
+ bd_set_size(bdev, (loff_t)get_capacity(bdev->bd_disk) << 9);
+ kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
}
loop_config_discard(lo);
--
1.7.9.5
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 5/7] loopdev: move common code into loop_figure_size()
2013-01-25 10:56 [PATCH 0/7] Loop device bug fix Guo Chao
` (3 preceding siblings ...)
2013-01-25 10:56 ` [PATCH 4/7] loopdev: update block device size in loop_set_status() Guo Chao
@ 2013-01-25 10:56 ` Guo Chao
2013-01-25 10:56 ` [PATCH 6/7] loopdev: remove an user triggable oops Guo Chao
2013-01-25 10:56 ` [PATCH 7/7] loopdev: ignore negative offset when calculate loop device size Guo Chao
6 siblings, 0 replies; 8+ messages in thread
From: Guo Chao @ 2013-01-25 10:56 UTC (permalink / raw)
To: axboe; +Cc: linux-kernel
Update block device size in accord with gendisk size and let userspace
know the change in loop_figure_size(). This is a clean up to remove
common code of loop_figure_size()'s two callers.
Signed-off-by: Guo Chao <yan@linux.vnet.ibm.com>
---
drivers/block/loop.c | 31 +++++++------------------------
1 file changed, 7 insertions(+), 24 deletions(-)
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index ed85036..6579f69 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -190,6 +190,7 @@ figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit)
{
loff_t size = get_size(offset, sizelimit, lo->lo_backing_file);
sector_t x = (sector_t)size;
+ struct block_device *bdev = lo->lo_device;
if (unlikely((loff_t)x != size))
return -EFBIG;
@@ -198,6 +199,9 @@ figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit)
if (lo->lo_sizelimit != sizelimit)
lo->lo_sizelimit = sizelimit;
set_capacity(lo->lo_disk, x);
+ bd_set_size(bdev, (loff_t)get_capacity(bdev->bd_disk) << 9);
+ /* let user-space know about the new size */
+ kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
return 0;
}
@@ -1091,15 +1095,10 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
return err;
if (lo->lo_offset != info->lo_offset ||
- lo->lo_sizelimit != info->lo_sizelimit) {
- struct block_device *bdev = lo->lo_device;
-
+ lo->lo_sizelimit != info->lo_sizelimit)
if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit))
return -EFBIG;
- bd_set_size(bdev, (loff_t)get_capacity(bdev->bd_disk) << 9);
- kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
- }
loop_config_discard(lo);
memcpy(lo->lo_file_name, info->lo_file_name, LO_NAME_SIZE);
@@ -1276,26 +1275,10 @@ loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) {
static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev)
{
- int err;
- sector_t sec;
- loff_t sz;
-
- err = -ENXIO;
if (unlikely(lo->lo_state != Lo_bound))
- goto out;
- err = figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit);
- if (unlikely(err))
- goto out;
- sec = get_capacity(lo->lo_disk);
- /* the width of sector_t may be narrow for bit-shift */
- sz = sec;
- sz <<= 9;
- bd_set_size(bdev, sz);
- /* let user-space know about the new size */
- kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE);
+ return -ENXIO;
- out:
- return err;
+ return figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit);
}
static int lo_ioctl(struct block_device *bdev, fmode_t mode,
--
1.7.9.5
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 6/7] loopdev: remove an user triggable oops
2013-01-25 10:56 [PATCH 0/7] Loop device bug fix Guo Chao
` (4 preceding siblings ...)
2013-01-25 10:56 ` [PATCH 5/7] loopdev: move common code into loop_figure_size() Guo Chao
@ 2013-01-25 10:56 ` Guo Chao
2013-01-25 10:56 ` [PATCH 7/7] loopdev: ignore negative offset when calculate loop device size Guo Chao
6 siblings, 0 replies; 8+ messages in thread
From: Guo Chao @ 2013-01-25 10:56 UTC (permalink / raw)
To: axboe; +Cc: linux-kernel
When loopdev is built as module and we pass an invalid parameter,
loop_init() will return directly without deregister misc device,
which will cause an oops when insert loop module next time because
we left some garbage in the misc device list.
Test case:
sudo modprobe loop max_part=1024
(failed due to invalid parameter)
sudo modprobe loop
(oops)
Clean up nicely to avoid such oops.
Signed-off-by: Guo Chao <yan@linux.vnet.ibm.com>
---
drivers/block/loop.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 6579f69..9190a82 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1844,11 +1844,15 @@ static int __init loop_init(void)
max_part = (1UL << part_shift) - 1;
}
- if ((1UL << part_shift) > DISK_MAX_PARTS)
- return -EINVAL;
+ if ((1UL << part_shift) > DISK_MAX_PARTS) {
+ err = -EINVAL;
+ goto misc_out;
+ }
- if (max_loop > 1UL << (MINORBITS - part_shift))
- return -EINVAL;
+ if (max_loop > 1UL << (MINORBITS - part_shift)) {
+ err = -EINVAL;
+ goto misc_out;
+ }
/*
* If max_loop is specified, create that many devices upfront.
@@ -1866,8 +1870,10 @@ static int __init loop_init(void)
range = 1UL << MINORBITS;
}
- if (register_blkdev(LOOP_MAJOR, "loop"))
- return -EIO;
+ if (register_blkdev(LOOP_MAJOR, "loop")) {
+ err = -EIO;
+ goto misc_out;
+ }
blk_register_region(MKDEV(LOOP_MAJOR, 0), range,
THIS_MODULE, loop_probe, NULL, NULL);
@@ -1880,6 +1886,10 @@ static int __init loop_init(void)
printk(KERN_INFO "loop: module loaded\n");
return 0;
+
+misc_out:
+ misc_deregister(&loop_misc);
+ return err;
}
static int loop_exit_cb(int id, void *ptr, void *data)
--
1.7.9.5
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 7/7] loopdev: ignore negative offset when calculate loop device size
2013-01-25 10:56 [PATCH 0/7] Loop device bug fix Guo Chao
` (5 preceding siblings ...)
2013-01-25 10:56 ` [PATCH 6/7] loopdev: remove an user triggable oops Guo Chao
@ 2013-01-25 10:56 ` Guo Chao
6 siblings, 0 replies; 8+ messages in thread
From: Guo Chao @ 2013-01-25 10:56 UTC (permalink / raw)
To: axboe; +Cc: linux-kernel
Negative offset may cause loop device size larger than backing file
size.
$ fallocate -l 1M a
$ losetup --offset 0xffffffffffff0000 /dev/loop0 a
$ blockdev --getsize64 /dev/loop0
1114112
$ ls -l a
-rw-r--r-- 1 root root 1048576 Jan 23 12:46 a
$ cat /dev/loop0
cat: /dev/loop0: Input/output error
It makes no sense to do that. Only apply offset when it's positive.
Fix a typo in the comment by the way.
Signed-off-by: Guo Chao <yan@linux.vnet.ibm.com>
---
drivers/block/loop.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 9190a82..7a235db 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -162,12 +162,13 @@ static struct loop_func_table *xfer_funcs[MAX_LO_CRYPT] = {
static loff_t get_size(loff_t offset, loff_t sizelimit, struct file *file)
{
- loff_t size, loopsize;
+ loff_t loopsize;
/* Compute loopsize in bytes */
- size = i_size_read(file->f_mapping->host);
- loopsize = size - offset;
- /* offset is beyond i_size, wierd but possible */
+ loopsize = i_size_read(file->f_mapping->host);
+ if (offset > 0)
+ loopsize -= offset;
+ /* offset is beyond i_size, weird but possible */
if (loopsize < 0)
return 0;
--
1.7.9.5
^ permalink raw reply [flat|nested] 8+ messages in thread