diff options
| author | Andrew Morton <akpm@osdl.org> | 2004-03-11 16:16:17 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-03-11 16:16:17 -0800 |
| commit | 238a43a0f005e50e466690241b6310ae8d5c2cce (patch) | |
| tree | 11e38dbd12fb08213489d4c45d6dec8f0208ee9e /include/linux | |
| parent | 275da6a305c6b1ee5283011ed5757d4cc45c5994 (diff) | |
[PATCH] loop setup race fix
From: Chris Mason <mason@suse.com>
There's a race in loopback setup, it's easiest to trigger with one or more
procs doing loopback mounts at the same time. The problem is that
fs/block_dev.c:do_open() only calls bdev_set_size on the first open.
Picture two procs:
proc1: mount -o loop file1 mnt1
proc2: mount -o loop file2 mnt2
proc1 proc2
open /dev/loop0 # bd_openers now 1
do_open
bd_set_size(bdev, 0) # loop unbound, so bdev size is 0
open /dev/loop0 # bd_openers now 2
loop_set_fd # disk capacity now correct, but
# bdev not updated
mount /dev/loop0 /mnt
do_open
Because bd_openers != 0 for the last do_open, bd_set_size is not called
again and a size of 0 is used. This eventually leads to an oops when the
loop device is unmounted, because fsync_bdev calls block_write_full_page
who decides every page on the block device is outside i_size and unmaps
them.
When ext2 or reiserfs try to sync a metadata buffer, we get an oops on
because the buffers are no longer mapped.
The patch below changes loop_set_fd and loop_clr_fd to also manipulate the
size of the block device, which fixes things for me.
Diffstat (limited to 'include/linux')
| -rw-r--r-- | include/linux/fs.h | 1 |
1 files changed, 1 insertions, 0 deletions
diff --git a/include/linux/fs.h b/include/linux/fs.h index b424acbe1016..661e12c67875 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1136,6 +1136,7 @@ extern void vfs_caches_init(unsigned long); extern int register_blkdev(unsigned int, const char *); extern int unregister_blkdev(unsigned int, const char *); extern struct block_device *bdget(dev_t); +extern void bd_set_size(struct block_device *, loff_t size); extern void bd_forget(struct inode *inode); extern void bdput(struct block_device *); extern int blkdev_open(struct inode *, struct file *); |
