diff -urN mm-loop-race-fix/drivers/block/loop.c mm-loop/drivers/block/loop.c --- mm-loop-race-fix/drivers/block/loop.c Wed Jan 14 07:20:40 2004 +++ mm-loop/drivers/block/loop.c Wed Jan 14 07:12:58 2004 @@ -65,6 +65,7 @@ #include #include #include /* for invalidate_bdev() */ +#include #include @@ -579,6 +580,13 @@ goto out; } +struct switch_request { + struct file *file; + struct completion wait; +}; + +static void do_loop_switch(struct loop_device *, struct switch_request *); + static inline void loop_handle_bio(struct loop_device *lo, struct bio *bio) { int ret; @@ -587,8 +595,13 @@ * For block backed loop, we know this is a READ */ if (lo->lo_flags & LO_FLAGS_DO_BMAP) { - ret = do_bio_filebacked(lo, bio); - bio_endio(bio, bio->bi_size, ret); + if (unlikely(!bio->bi_bdev)) { + do_loop_switch(lo, bio->bi_private); + bio_put(bio); + } else { + ret = do_bio_filebacked(lo, bio); + bio_endio(bio, bio->bi_size, ret); + } } else { struct bio *rbh = bio->bi_private; @@ -657,6 +670,82 @@ return 0; } +static int loop_switch(struct loop_device *lo, struct file *file) +{ + struct switch_request w; + struct bio *bio = bio_alloc(GFP_KERNEL, 1); + if (!bio) + return -ENOMEM; + init_completion(&w.wait); + w.file = file; + bio->bi_private = &w; + bio->bi_bdev = NULL; + loop_make_request(lo->lo_queue, bio); + wait_for_completion(&w.wait); + return 0; +} + +static void do_loop_switch(struct loop_device *lo, struct switch_request *p) +{ + struct file *file = p->file; + struct file *old_file = lo->lo_backing_file; + struct address_space *mapping = file->f_mapping; + + mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask); + lo->lo_backing_file = file; + lo->lo_blocksize = mapping->host->i_blksize; + lo->old_gfp_mask = mapping_gfp_mask(mapping); + mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS)); + complete(&p->wait); +} + +static int loop_change_fd(struct loop_device *lo, struct file *lo_file, + struct block_device *bdev, unsigned int arg) +{ + struct file *file, *old_file; + struct inode *inode; + int error; + + error = -ENXIO; + if (lo->lo_state != Lo_bound) + goto out; + + error = -EINVAL; + if (lo->lo_flags != (LO_FLAGS_READ_ONLY | LO_FLAGS_DO_BMAP)) + goto out; + + error = -EBADF; + file = fget(arg); + if (!file) + goto out; + + inode = file->f_mapping->host; + old_file = lo->lo_backing_file; + + error = -EINVAL; + + if (!S_ISREG(inode->i_mode)) + goto out_putf; + + if (!inode->i_fop->sendfile) + goto out_putf; + + if (get_loop_size(lo, file) != get_loop_size(lo, old_file)) + goto out_putf; + + error = loop_switch(lo, file); + if (error) + goto out_putf; + + fput(old_file); + return 0; + + out_putf: + fput(file); + out: + return error; +} + static int loop_set_fd(struct loop_device *lo, struct file *lo_file, struct block_device *bdev, unsigned int arg) { @@ -1077,6 +1165,9 @@ case LOOP_SET_FD: err = loop_set_fd(lo, file, inode->i_bdev, arg); break; + case LOOP_CHANGE_FD: + err = loop_change_fd(lo, file, inode->i_bdev, arg); + break; case LOOP_CLR_FD: err = loop_clr_fd(lo, inode->i_bdev); break; diff -urN mm-loop-race-fix/include/linux/loop.h mm-loop/include/linux/loop.h --- mm-loop-race-fix/include/linux/loop.h Sat Aug 9 02:21:03 2003 +++ mm-loop/include/linux/loop.h Wed Jan 14 07:08:24 2004 @@ -151,5 +151,6 @@ #define LOOP_GET_STATUS 0x4C03 #define LOOP_SET_STATUS64 0x4C04 #define LOOP_GET_STATUS64 0x4C05 +#define LOOP_CHANGE_FD 0x4C06 #endif