mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/7] Loop device bug fix
@ 2013-01-25 10:56 Guo Chao
  2013-01-25 10:56 ` [PATCH 1/7] block: use i_size_write() in bd_set_size() Guo Chao
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Guo Chao @ 2013-01-25 10:56 UTC (permalink / raw)
  To: axboe; +Cc: linux-kernel

This patchset fix several loop device bugs.

Patch #1 fix non-atomic updating block device size. It also prepares for
a later dead lock patch.

Patch #2 is a trivial clean up about block device.

The rest of the patchset are loop device fix:

	#3 - fix a dead loop
	#4 - fix a race-like problem
	#5 - a little clean up based on #3 and #4
	#6 - fix a user triggable oops
	#7 - avoid loop device size being messed up by parameters from users


Guo Chao (7):
  block: use i_size_write() in bd_set_size()
  block: remove redundant check to bd_openers
  loopdev: fix a dead lock
  loopdev: update block device size in loop_set_status()
  loopdev: move common code into loop_figure_size()
  loopdev: remove an user triggable oops
  loopdev: ignore negative offset when calculate loop device size

 drivers/block/loop.c |   61 ++++++++++++++++++++++++--------------------------
 fs/block_dev.c       |    6 +++--
 2 files changed, 33 insertions(+), 34 deletions(-)

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [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

end of thread, other threads:[~2013-01-25 10:58 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH 3/7] loopdev: fix a dead lock Guo Chao
2013-01-25 10:56 ` [PATCH 4/7] loopdev: update block device size in loop_set_status() Guo Chao
2013-01-25 10:56 ` [PATCH 5/7] loopdev: move common code into loop_figure_size() 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome