Unionfs: avoid using drop_pagecache_sb in remount
authorErez Zadok <ezk@cs.sunysb.edu>
Thu, 13 Dec 2007 15:51:15 +0000 (10:51 -0500)
committerRachita Kothiyal <rachita@dewey.fsl.cs.sunysb.edu>
Thu, 1 May 2008 23:03:26 +0000 (19:03 -0400)
Exporting drop_pagecache_sb to modules is somewhat risky because one cannot
sleep inside invalidate_mapping_pages.  This could cause a lot of latency in
the pre-emption code.  So don't export this symbol to minimize the risk that
others will use it.

Instead, unionfs will try to directly invalidate as many pages it can from
the unionfs_remount code.  Invalidating those inode pages is not strictly
required, but helpful in encouraging a revalidation of inodes sooner than
waiting for individual f/s ops to access the union.  Since a remount is
already an expensive but rare operation, this inode pages invalidation
shouldn't add too much overhead.

CC: Nick Piggin <nickpiggin@yahoo.com.au>
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
fs/drop_caches.c
fs/unionfs/dentry.c
fs/unionfs/super.c
fs/unionfs/union.h
include/linux/mm.h

index 90410acd1790288aec5a7b07b7573d847a30e7f3..59375efcf39d6769d94a6d77ef33a06054e4f4b9 100644 (file)
@@ -3,7 +3,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/writeback.h>
@@ -13,7 +12,7 @@
 /* A global variable is a bit ugly, but it keeps the code simple */
 int sysctl_drop_caches;
 
-void drop_pagecache_sb(struct super_block *sb)
+static void drop_pagecache_sb(struct super_block *sb)
 {
        struct inode *inode;
 
@@ -25,7 +24,6 @@ void drop_pagecache_sb(struct super_block *sb)
        }
        spin_unlock(&inode_lock);
 }
-EXPORT_SYMBOL(drop_pagecache_sb);
 
 void drop_pagecache(void)
 {
index fb836552d26fd43d87aef1899ed59872eed014d9..7dccd8bddffd38f7b16c44a43873b26f6c80410e 100644 (file)
@@ -297,6 +297,17 @@ static inline void purge_inode_data(struct inode *inode)
                truncate_inode_pages(&inode->i_data, 0);
 }
 
+void purge_sb_data(struct super_block *sb)
+{
+       struct inode *inode;
+
+       list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
+               if (inode->i_state & (I_FREEING|I_WILL_FREE))
+                       continue;
+               purge_inode_data(inode);
+       }
+}
+
 /*
  * Revalidate a parent chain of dentries, then the actual node.
  * Assumes that dentry is locked, but will lock all parents if/when needed.
index 5903ce51deaf0f67e982c97900d352b0c9b357ca..5959d17e0df906ffe4e7afd182ce1c8abf49cd77 100644 (file)
@@ -736,29 +736,19 @@ out_no_change:
         */
 
        /*
-        * Now we call drop_pagecache_sb() to invalidate all pages in this
-        * super.  This function calls invalidate_inode_pages(mapping),
-        * which calls invalidate_mapping_pages(): the latter, however, will
-        * not invalidate pages which are dirty, locked, under writeback, or
-        * mapped into page tables.  We shouldn't have to worry about dirty
-        * or under-writeback pages, because do_remount_sb() called
-        * fsync_super() which would not have returned until all dirty pages
-        * were flushed.
-        *
-        * But do we have to worry about locked pages?  Is there any chance
-        * that in here we'll get locked pages?
-        *
-        * XXX: what about pages mapped into pagetables?  Are these pages
-        * which user processes may have mmap(2)'ed?  If so, then we need to
-        * invalidate those too, no?  Maybe we'll have to write our own
-        * version of invalidate_mapping_pages() which also handled mapped
-        * pages.
-        *
-        * XXX: Alternatively, maybe we should call truncate_inode_pages(),
-        * which use two passes over the pages list, and will truncate all
-        * pages.
+        * Once we finish the remounting successfully, our superblock
+        * generation number will have increased.  This will be detected by
+        * our dentry-revalidation code upon subsequent f/s operations
+        * through unionfs.  The revalidation code will rebuild the union of
+        * lower inodes for a given unionfs inode and invalidate any pages
+        * of such "stale" inodes (by calling our purge_inode_data
+        * function).  This revalidation will happen lazily and
+        * incrementally, as users perform operations on cached inodes.  We
+        * would like to encourage this revalidation to happen sooner if
+        * possible, so we try to invalidate as many other pages in our
+        * superblock as we can.
         */
-       drop_pagecache_sb(sb);
+       purge_sb_data(sb);
 
        /* copy new vectors into their correct place */
        tmp_data = UNIONFS_SB(sb)->data;
index 8bc1b51803087c356b7df1849f4ded176a358c11..e862a29acb4204c2f68404c71cbec093a92af4ca 100644 (file)
@@ -370,6 +370,7 @@ extern int unionfs_rmdir(struct inode *dir, struct dentry *dentry);
 extern bool __unionfs_d_revalidate_chain(struct dentry *dentry,
                                         struct nameidata *nd, bool willwrite);
 extern bool is_newer_lower(const struct dentry *dentry);
+extern void purge_sb_data(struct super_block *sb);
 int unionfs_force_rm(struct dentry *dentry, struct dentry **lower_dentry,
                     int bindex);
 int unionfs_silly_rename(struct dentry *dentry, struct dentry *lower_dentry);
index fc61bd3ccf04f319c5e2869c7b8377d56dc0ba3b..1b7b95c67acaf5df2fd9cb860599931119ddc029 100644 (file)
@@ -19,7 +19,6 @@ struct anon_vma;
 struct file_ra_state;
 struct user_struct;
 struct writeback_control;
-struct super_block;
 
 #ifndef CONFIG_DISCONTIGMEM          /* Don't use mapnrs, do it properly */
 extern unsigned long max_mapnr;
@@ -1136,7 +1135,6 @@ int drop_caches_sysctl_handler(struct ctl_table *, int, struct file *,
                                        void __user *, size_t *, loff_t *);
 unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask,
                        unsigned long lru_pages);
-extern void drop_pagecache_sb(struct super_block *);
 void drop_pagecache(void);
 void drop_slab(void);