Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

David Howells and committed by
Linus Torvalds
c902ce1b 075d9db1

+85 -5
+16
Documentation/filesystems/caching/netfs-api.txt
··· 673 673 in which case the page will not be stored in the cache this time. 674 674 675 675 676 + BULK INODE PAGE UNCACHE 677 + ----------------------- 678 + 679 + A convenience routine is provided to perform an uncache on all the pages 680 + attached to an inode. This assumes that the pages on the inode correspond on a 681 + 1: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 + 686 + This takes the netfs cookie that the pages were cached with and the inode that 687 + the pages are attached to. This function will wait for pages to finish being 688 + written to the cache and for the cache to finish with the page generally. No 689 + error is returned. 690 + 691 + 676 692 ========================== 677 693 INDEX AND DATA FILE UPDATE 678 694 ==========================
+1
fs/cifs/fscache.c
··· 92 92 93 93 if (cifsi->fscache) { 94 94 cFYI(1, "%s: (0x%p)", __func__, cifsi->fscache); 95 + fscache_uncache_all_inode_pages(cifsi->fscache, inode); 95 96 fscache_relinquish_cookie(cifsi->fscache, 1); 96 97 cifsi->fscache = NULL; 97 98 }
+44
fs/fscache/page.c
··· 954 954 pagevec_reinit(pagevec); 955 955 } 956 956 EXPORT_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 + */ 962 + void __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 + } 1000 + EXPORT_SYMBOL(__fscache_uncache_all_inode_pages);
+3 -5
fs/nfs/fscache.c
··· 259 259 dfprintk(FSCACHE, 260 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 263 - * turning off the cache. 262 + /* Need to uncache any pages attached to this inode that 263 + * fscache knows about before turning off the cache. 264 264 */ 265 - if (inode->i_mapping && inode->i_mapping->nrpages) 266 - invalidate_inode_pages2(inode->i_mapping); 267 - 265 + fscache_uncache_all_inode_pages(NFS_I(inode)->fscache, inode); 268 266 nfs_fscache_zap_inode_cookie(inode); 269 267 } 270 268 }
+21
include/linux/fscache.h
··· 204 204 extern void __fscache_wait_on_page_write(struct fscache_cookie *, struct page *); 205 205 extern bool __fscache_maybe_release_page(struct fscache_cookie *, struct page *, 206 206 gfp_t); 207 + extern void __fscache_uncache_all_inode_pages(struct fscache_cookie *, 208 + struct inode *); 207 209 208 210 /** 209 211 * fscache_register_netfs - Register a filesystem as desiring caching services ··· 643 641 if (fscache_cookie_valid(cookie) && PageFsCache(page)) 644 642 return __fscache_maybe_release_page(cookie, page, gfp); 645 643 return false; 644 + } 645 + 646 + /** 647 + * fscache_uncache_all_inode_pages - Uncache all an inode's pages 648 + * @cookie: The cookie representing the inode's cache object. 649 + * @inode: The inode to uncache pages from. 650 + * 651 + * Uncache all the pages in an inode that are marked PG_fscache, assuming them 652 + * to be associated with the given cookie. 653 + * 654 + * This function may sleep. It will wait for pages that are being written out 655 + * and will wait whilst the PG_fscache mark is removed by the cache. 656 + */ 657 + static inline 658 + void fscache_uncache_all_inode_pages(struct fscache_cookie *cookie, 659 + struct inode *inode) 660 + { 661 + if (fscache_cookie_valid(cookie)) 662 + __fscache_uncache_all_inode_pages(cookie, inode); 646 663 } 647 664 648 665 #endif /* _LINUX_FSCACHE_H */