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.