aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2011-07-07 07:19:48 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-07 16:21:56 -0400
commitc902ce1bfb40d8b049bd2319b388b4b68b04bc27 (patch)
tree7dcb1e8378f8ec8e7ad7684cd26e9d5a1b5b22d1
parent075d9db13183c102770dc6cefabfee1b832f9614 (diff)
FS-Cache: Add a helper to bulk uncache pages on an inode
Add an FS-Cache helper to bulk uncache pages on an inode. This will only work for the circumstance where the pages in the cache correspond 1:1 with the pages attached to an inode's page cache. This is required for CIFS and NFS: When disabling inode cookie, we were returning the cookie and setting cifsi->fscache to NULL but failed to invalidate any previously mapped pages. This resulted in "Bad page state" errors and manifested in other kind of errors when running fsstress. Fix it by uncaching mapped pages when we disable the inode cookie. This patch should fix the following oops and "Bad page state" errors seen during fsstress testing. ------------[ cut here ]------------ kernel BUG at fs/cachefiles/namei.c:201! invalid opcode: 0000 [#1] SMP Pid: 5, comm: kworker/u:0 Not tainted 2.6.38.7-30.fc15.x86_64 #1 Bochs Bochs RIP: 0010: cachefiles_walk_to_object+0x436/0x745 [cachefiles] RSP: 0018:ffff88002ce6dd00 EFLAGS: 00010282 RAX: ffff88002ef165f0 RBX: ffff88001811f500 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000100 RDI: 0000000000000282 RBP: ffff88002ce6dda0 R08: 0000000000000100 R09: ffffffff81b3a300 R10: 0000ffff00066c0a R11: 0000000000000003 R12: ffff88002ae54840 R13: ffff88002ae54840 R14: ffff880029c29c00 R15: ffff88001811f4b0 FS: 00007f394dd32720(0000) GS:ffff88002ef00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 00007fffcb62ddf8 CR3: 000000001825f000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process kworker/u:0 (pid: 5, threadinfo ffff88002ce6c000, task ffff88002ce55cc0) Stack: 0000000000000246 ffff88002ce55cc0 ffff88002ce6dd58 ffff88001815dc00 ffff8800185246c0 ffff88001811f618 ffff880029c29d18 ffff88001811f380 ffff88002ce6dd50 ffffffff814757e4 ffff88002ce6dda0 ffffffff8106ac56 Call Trace: cachefiles_lookup_object+0x78/0xd4 [cachefiles] fscache_lookup_object+0x131/0x16d [fscache] fscache_object_work_func+0x1bc/0x669 [fscache] process_one_work+0x186/0x298 worker_thread+0xda/0x15d kthread+0x84/0x8c kernel_thread_helper+0x4/0x10 RIP cachefiles_walk_to_object+0x436/0x745 [cachefiles] ---[ end trace 1d481c9af1804caa ]--- I tested the uncaching by the following means: (1) Create a big file on my NFS server (104857600 bytes). (2) Read the file into the cache with md5sum on the NFS client. Look in /proc/fs/fscache/stats: Pages : mrk=25601 unc=0 (3) Open the file for read/write ("bash 5<>/warthog/bigfile"). Look in proc again: Pages : mrk=25601 unc=25601 Reported-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-and-Tested-by: Suresh Jayaraman <sjayaraman@suse.de> cc: stable@kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--Documentation/filesystems/caching/netfs-api.txt16
-rw-r--r--fs/cifs/fscache.c1
-rw-r--r--fs/fscache/page.c44
-rw-r--r--fs/nfs/fscache.c8
-rw-r--r--include/linux/fscache.h21
5 files changed, 85 insertions, 5 deletions
diff --git a/Documentation/filesystems/caching/netfs-api.txt b/Documentation/filesystems/caching/netfs-api.txt
index a167ab876c35..7cc6bf2871eb 100644
--- a/Documentation/filesystems/caching/netfs-api.txt
+++ b/Documentation/filesystems/caching/netfs-api.txt
@@ -673,6 +673,22 @@ storage request to complete, or it may attempt to cancel the storage request -
673in which case the page will not be stored in the cache this time. 673in which case the page will not be stored in the cache this time.
674 674
675 675
676BULK INODE PAGE UNCACHE
677-----------------------
678
679A convenience routine is provided to perform an uncache on all the pages
680attached to an inode. This assumes that the pages on the inode correspond on a
6811:1 basis with the pages in the cache.
682
683 void fscache_uncache_all_inode_pages(struct fscache_cookie *cookie,
684 struct inode *inode);
685
686This takes the netfs cookie that the pages were cached with and the inode that
687the pages are attached to. This function will wait for pages to finish being
688written to the cache and for the cache to finish with the page generally. No
689error is returned.
690
691
676========================== 692==========================
677INDEX AND DATA FILE UPDATE 693INDEX AND DATA FILE UPDATE
678========================== 694==========================
diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c
index 816696621ec9..42e5363b4102 100644
--- a/fs/cifs/fscache.c
+++ b/fs/cifs/fscache.c
@@ -92,6 +92,7 @@ static void cifs_fscache_disable_inode_cookie(struct inode *inode)
92 92
93 if (cifsi->fscache) { 93 if (cifsi->fscache) {
94 cFYI(1, "%s: (0x%p)", __func__, cifsi->fscache); 94 cFYI(1, "%s: (0x%p)", __func__, cifsi->fscache);
95 fscache_uncache_all_inode_pages(cifsi->fscache, inode);
95 fscache_relinquish_cookie(cifsi->fscache, 1); 96 fscache_relinquish_cookie(cifsi->fscache, 1);
96 cifsi->fscache = NULL; 97 cifsi->fscache = NULL;
97 } 98 }
diff --git a/fs/fscache/page.c b/fs/fscache/page.c
index a2a5d19ece6a..2f343b4d7a7d 100644
--- a/fs/fscache/page.c
+++ b/fs/fscache/page.c
@@ -954,3 +954,47 @@ void fscache_mark_pages_cached(struct fscache_retrieval *op,
954 pagevec_reinit(pagevec); 954 pagevec_reinit(pagevec);
955} 955}
956EXPORT_SYMBOL(fscache_mark_pages_cached); 956EXPORT_SYMBOL(fscache_mark_pages_cached);
957
958/*
959 * Uncache all the pages in an inode that are marked PG_fscache, assuming them
960 * to be associated with the given cookie.
961 */
962void __fscache_uncache_all_inode_pages(struct fscache_cookie *cookie,
963 struct inode *inode)
964{
965 struct address_space *mapping = inode->i_mapping;
966 struct pagevec pvec;
967 pgoff_t next;
968 int i;
969
970 _enter("%p,%p", cookie, inode);
971
972 if (!mapping || mapping->nrpages == 0) {
973 _leave(" [no pages]");
974 return;
975 }
976
977 pagevec_init(&pvec, 0);
978 next = 0;
979 while (next <= (loff_t)-1 &&
980 pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)
981 ) {
982 for (i = 0; i < pagevec_count(&pvec); i++) {
983 struct page *page = pvec.pages[i];
984 pgoff_t page_index = page->index;
985
986 ASSERTCMP(page_index, >=, next);
987 next = page_index + 1;
988
989 if (PageFsCache(page)) {
990 __fscache_wait_on_page_write(cookie, page);
991 __fscache_uncache_page(cookie, page);
992 }
993 }
994 pagevec_release(&pvec);
995 cond_resched();
996 }
997
998 _leave("");
999}
1000EXPORT_SYMBOL(__fscache_uncache_all_inode_pages);
diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c
index ce153a6b3aec..419119c371bf 100644
--- a/fs/nfs/fscache.c
+++ b/fs/nfs/fscache.c
@@ -259,12 +259,10 @@ static void nfs_fscache_disable_inode_cookie(struct inode *inode)
259 dfprintk(FSCACHE, 259 dfprintk(FSCACHE,
260 "NFS: nfsi 0x%p turning cache off\n", NFS_I(inode)); 260 "NFS: nfsi 0x%p turning cache off\n", NFS_I(inode));
261 261
262 /* Need to invalidate any mapped pages that were read in before 262 /* Need to uncache any pages attached to this inode that
263 * turning off the cache. 263 * fscache knows about before turning off the cache.
264 */ 264 */
265 if (inode->i_mapping && inode->i_mapping->nrpages) 265 fscache_uncache_all_inode_pages(NFS_I(inode)->fscache, inode);
266 invalidate_inode_pages2(inode->i_mapping);
267
268 nfs_fscache_zap_inode_cookie(inode); 266 nfs_fscache_zap_inode_cookie(inode);
269 } 267 }
270} 268}
diff --git a/include/linux/fscache.h b/include/linux/fscache.h
index 7c4d72f5581f..9ec20dec3353 100644
--- a/include/linux/fscache.h
+++ b/include/linux/fscache.h
@@ -204,6 +204,8 @@ extern bool __fscache_check_page_write(struct fscache_cookie *, struct page *);
204extern void __fscache_wait_on_page_write(struct fscache_cookie *, struct page *); 204extern void __fscache_wait_on_page_write(struct fscache_cookie *, struct page *);
205extern bool __fscache_maybe_release_page(struct fscache_cookie *, struct page *, 205extern bool __fscache_maybe_release_page(struct fscache_cookie *, struct page *,
206 gfp_t); 206 gfp_t);
207extern void __fscache_uncache_all_inode_pages(struct fscache_cookie *,
208 struct inode *);
207 209
208/** 210/**
209 * fscache_register_netfs - Register a filesystem as desiring caching services 211 * fscache_register_netfs - Register a filesystem as desiring caching services
@@ -643,4 +645,23 @@ bool fscache_maybe_release_page(struct fscache_cookie *cookie,
643 return false; 645 return false;
644} 646}
645 647
648/**
649 * fscache_uncache_all_inode_pages - Uncache all an inode's pages
650 * @cookie: The cookie representing the inode's cache object.
651 * @inode: The inode to uncache pages from.
652 *
653 * Uncache all the pages in an inode that are marked PG_fscache, assuming them
654 * to be associated with the given cookie.
655 *
656 * This function may sleep. It will wait for pages that are being written out
657 * and will wait whilst the PG_fscache mark is removed by the cache.
658 */
659static inline
660void fscache_uncache_all_inode_pages(struct fscache_cookie *cookie,
661 struct inode *inode)
662{
663 if (fscache_cookie_valid(cookie))
664 __fscache_uncache_all_inode_pages(cookie, inode);
665}
666
646#endif /* _LINUX_FSCACHE_H */ 667#endif /* _LINUX_FSCACHE_H */