

Sigh.  ramdisk almost works, except it loses data on umount.

This is because the files which are atop the ramdisk do not contribute to
dirty memory accounting, but they do need writeback.  So when sync() calls
sync_inodes_sb() to do the work, sync_inodes_sb() hopelessly underestimates
the number of pages which need writeback for a complete sync.

If you run `sync' enough times, everything eventually hits "disk" and all is
happy.

The root cause here is that the ramdisk and the files which it hosts shared
the same backing_dev_info.  This is inappropriate because the hosted files
*do* want to writeback and really should contribute to dirty memory
accounting. But the ramdisk inode itself wants neither.

So.  The patch sets up the infrastructure which permits a blockdev to provide
a separate backing_dev_info for the files which it hosts.  It's a bit of a
ramdisk-special.


---

 25-akpm/fs/block_dev.c     |    5 ++++-
 25-akpm/fs/inode.c         |   16 ++++++++++++++--
 25-akpm/include/linux/fs.h |    1 +
 3 files changed, 19 insertions(+), 3 deletions(-)

diff -puN include/linux/fs.h~blockdev-split-backing_dev_info include/linux/fs.h
--- 25/include/linux/fs.h~blockdev-split-backing_dev_info	2004-05-19 02:06:49.812053304 -0700
+++ 25-akpm/include/linux/fs.h	2004-05-19 02:11:42.675531288 -0700
@@ -358,6 +358,7 @@ struct block_device {
 	int			bd_invalidated;
 	struct gendisk *	bd_disk;
 	struct list_head	bd_list;
+	struct backing_dev_info *bd_inode_backing_dev_info;
 	/*
 	 * Private data.  You must have bd_claim'ed the block_device
 	 * to use this.  NOTE:  bd_claim allows an owner to claim
diff -puN fs/block_dev.c~blockdev-split-backing_dev_info fs/block_dev.c
--- 25/fs/block_dev.c~blockdev-split-backing_dev_info	2004-05-19 02:06:49.827051024 -0700
+++ 25-akpm/fs/block_dev.c	2004-05-19 02:13:00.955630904 -0700
@@ -238,7 +238,10 @@ static struct inode *bdev_alloc_inode(st
 
 static void bdev_destroy_inode(struct inode *inode)
 {
-	kmem_cache_free(bdev_cachep, BDEV_I(inode));
+	struct bdev_inode *bdi = BDEV_I(inode);
+
+	bdi->bdev.bd_inode_backing_dev_info = NULL;
+	kmem_cache_free(bdev_cachep, bdi);
 }
 
 static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
diff -puN fs/inode.c~blockdev-split-backing_dev_info fs/inode.c
--- 25/fs/inode.c~blockdev-split-backing_dev_info	2004-05-19 02:06:49.843048592 -0700
+++ 25-akpm/fs/inode.c	2004-05-19 02:14:54.328395624 -0700
@@ -149,8 +149,20 @@ static struct inode *alloc_inode(struct 
 		mapping_set_gfp_mask(mapping, GFP_HIGHUSER);
 		mapping->assoc_mapping = NULL;
 		mapping->backing_dev_info = &default_backing_dev_info;
-		if (sb->s_bdev)
-			mapping->backing_dev_info = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
+
+		/*
+		 * If the block_device provides a backing_dev_info for client
+		 * inodes then use that.  Otherwise the inode share the bdev's
+		 * backing_dev_info.
+		 */
+		if (sb->s_bdev) {
+			struct backing_dev_info *bdi;
+
+			bdi = sb->s_bdev->bd_inode_backing_dev_info;
+			if (!bdi)
+				bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
+			mapping->backing_dev_info = bdi;
+		}
 		memset(&inode->u, 0, sizeof(inode->u));
 		inode->i_mapping = mapping;
 	}

_
