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