changeloop brokenness test case
Bill Nottingham
notting at redhat.com
Sat Jan 31 02:00:56 UTC 2004
Greg KH (greg at kroah.com) said:
> On Fri, Jan 30, 2004 at 12:32:50PM -0500, Jeremy Katz wrote:
> > (Background: Since Red Hat Linux 7.0, we've added a kernel patch to
> > allow switching the backing store for a loopback device which gets used
> > so that CD installs can switch CDs. This has been ported to 2.6, but
> > currently isn't working and is blocking test1. In the interest of maybe
> > more eyes seeing something, here's the test case I sent to Al)
>
> Any chance you could also post the kernel patch?
Al Viro found the fix. Here's the attached fixed version in
all its 'glory', for your amusement (or something).
Bill
-------------- next part --------------
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 <linux/suspend.h>
#include <linux/writeback.h>
#include <linux/buffer_head.h> /* for invalidate_bdev() */
+#include <linux/completion.h>
#include <asm/uaccess.h>
@@ -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
More information about the fedora-devel-list
mailing list