diff options
-rw-r--r-- | Documentation/filesystems/caching/fscache.txt | 4 | ||||
-rw-r--r-- | Documentation/filesystems/caching/netfs-api.txt | 21 | ||||
-rw-r--r-- | fs/9p/cache.c | 14 | ||||
-rw-r--r-- | fs/afs/file.c | 15 | ||||
-rw-r--r-- | fs/fscache/internal.h | 5 | ||||
-rw-r--r-- | fs/fscache/page.c | 79 | ||||
-rw-r--r-- | fs/fscache/stats.c | 11 | ||||
-rw-r--r-- | fs/nfs/fscache.c | 10 | ||||
-rw-r--r-- | include/linux/fscache-cache.h | 1 | ||||
-rw-r--r-- | include/linux/fscache.h | 27 |
10 files changed, 152 insertions, 35 deletions
diff --git a/Documentation/filesystems/caching/fscache.txt b/Documentation/filesystems/caching/fscache.txt index 057a3c71d524..7097fd29fb3d 100644 --- a/Documentation/filesystems/caching/fscache.txt +++ b/Documentation/filesystems/caching/fscache.txt | |||
@@ -272,6 +272,10 @@ proc files. | |||
272 | pgs=N Number of pages given store req processing time | 272 | pgs=N Number of pages given store req processing time |
273 | rxd=N Number of store reqs deleted from tracking tree | 273 | rxd=N Number of store reqs deleted from tracking tree |
274 | olm=N Number of store reqs over store limit | 274 | olm=N Number of store reqs over store limit |
275 | VmScan nos=N Number of release reqs against pages with no pending store | ||
276 | gon=N Number of release reqs against pages stored by time lock granted | ||
277 | bsy=N Number of release reqs ignored due to in-progress store | ||
278 | can=N Number of page stores cancelled due to release req | ||
275 | Ops pend=N Number of times async ops added to pending queues | 279 | Ops pend=N Number of times async ops added to pending queues |
276 | run=N Number of times async ops given CPU time | 280 | run=N Number of times async ops given CPU time |
277 | enq=N Number of times async ops queued for processing | 281 | enq=N Number of times async ops queued for processing |
diff --git a/Documentation/filesystems/caching/netfs-api.txt b/Documentation/filesystems/caching/netfs-api.txt index 2666b1ed5e9e..1902c57b72ef 100644 --- a/Documentation/filesystems/caching/netfs-api.txt +++ b/Documentation/filesystems/caching/netfs-api.txt | |||
@@ -641,7 +641,7 @@ data file must be retired (see the relinquish cookie function below). | |||
641 | 641 | ||
642 | Furthermore, note that this does not cancel the asynchronous read or write | 642 | Furthermore, note that this does not cancel the asynchronous read or write |
643 | operation started by the read/alloc and write functions, so the page | 643 | operation started by the read/alloc and write functions, so the page |
644 | invalidation and release functions must use: | 644 | invalidation functions must use: |
645 | 645 | ||
646 | bool fscache_check_page_write(struct fscache_cookie *cookie, | 646 | bool fscache_check_page_write(struct fscache_cookie *cookie, |
647 | struct page *page); | 647 | struct page *page); |
@@ -654,6 +654,25 @@ to see if a page is being written to the cache, and: | |||
654 | to wait for it to finish if it is. | 654 | to wait for it to finish if it is. |
655 | 655 | ||
656 | 656 | ||
657 | When releasepage() is being implemented, a special FS-Cache function exists to | ||
658 | manage the heuristics of coping with vmscan trying to eject pages, which may | ||
659 | conflict with the cache trying to write pages to the cache (which may itself | ||
660 | need to allocate memory): | ||
661 | |||
662 | bool fscache_maybe_release_page(struct fscache_cookie *cookie, | ||
663 | struct page *page, | ||
664 | gfp_t gfp); | ||
665 | |||
666 | This takes the netfs cookie, and the page and gfp arguments as supplied to | ||
667 | releasepage(). It will return false if the page cannot be released yet for | ||
668 | some reason and if it returns true, the page has been uncached and can now be | ||
669 | released. | ||
670 | |||
671 | To make a page available for release, this function may wait for an outstanding | ||
672 | storage request to complete, or it may attempt to cancel the storage request - | ||
673 | in which case the page will not be stored in the cache this time. | ||
674 | |||
675 | |||
657 | ========================== | 676 | ========================== |
658 | INDEX AND DATA FILE UPDATE | 677 | INDEX AND DATA FILE UPDATE |
659 | ========================== | 678 | ========================== |
diff --git a/fs/9p/cache.c b/fs/9p/cache.c index 51c94e26a346..bcc5357a9069 100644 --- a/fs/9p/cache.c +++ b/fs/9p/cache.c | |||
@@ -343,18 +343,7 @@ int __v9fs_fscache_release_page(struct page *page, gfp_t gfp) | |||
343 | 343 | ||
344 | BUG_ON(!vcookie->fscache); | 344 | BUG_ON(!vcookie->fscache); |
345 | 345 | ||
346 | if (PageFsCache(page)) { | 346 | return fscache_maybe_release_page(vnode->cache, page, gfp); |
347 | if (fscache_check_page_write(vcookie->fscache, page)) { | ||
348 | if (!(gfp & __GFP_WAIT)) | ||
349 | return 0; | ||
350 | fscache_wait_on_page_write(vcookie->fscache, page); | ||
351 | } | ||
352 | |||
353 | fscache_uncache_page(vcookie->fscache, page); | ||
354 | ClearPageFsCache(page); | ||
355 | } | ||
356 | |||
357 | return 1; | ||
358 | } | 347 | } |
359 | 348 | ||
360 | void __v9fs_fscache_invalidate_page(struct page *page) | 349 | void __v9fs_fscache_invalidate_page(struct page *page) |
@@ -368,7 +357,6 @@ void __v9fs_fscache_invalidate_page(struct page *page) | |||
368 | fscache_wait_on_page_write(vcookie->fscache, page); | 357 | fscache_wait_on_page_write(vcookie->fscache, page); |
369 | BUG_ON(!PageLocked(page)); | 358 | BUG_ON(!PageLocked(page)); |
370 | fscache_uncache_page(vcookie->fscache, page); | 359 | fscache_uncache_page(vcookie->fscache, page); |
371 | ClearPageFsCache(page); | ||
372 | } | 360 | } |
373 | } | 361 | } |
374 | 362 | ||
diff --git a/fs/afs/file.c b/fs/afs/file.c index 681c2a7b013f..39b301662f22 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c | |||
@@ -315,7 +315,6 @@ static void afs_invalidatepage(struct page *page, unsigned long offset) | |||
315 | struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); | 315 | struct afs_vnode *vnode = AFS_FS_I(page->mapping->host); |
316 | fscache_wait_on_page_write(vnode->cache, page); | 316 | fscache_wait_on_page_write(vnode->cache, page); |
317 | fscache_uncache_page(vnode->cache, page); | 317 | fscache_uncache_page(vnode->cache, page); |
318 | ClearPageFsCache(page); | ||
319 | } | 318 | } |
320 | #endif | 319 | #endif |
321 | 320 | ||
@@ -349,17 +348,9 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags) | |||
349 | /* deny if page is being written to the cache and the caller hasn't | 348 | /* deny if page is being written to the cache and the caller hasn't |
350 | * elected to wait */ | 349 | * elected to wait */ |
351 | #ifdef CONFIG_AFS_FSCACHE | 350 | #ifdef CONFIG_AFS_FSCACHE |
352 | if (PageFsCache(page)) { | 351 | if (!fscache_maybe_release_page(vnode->cache, page, gfp_flags)) { |
353 | if (fscache_check_page_write(vnode->cache, page)) { | 352 | _leave(" = F [cache busy]"); |
354 | if (!(gfp_flags & __GFP_WAIT)) { | 353 | return 0; |
355 | _leave(" = F [cache busy]"); | ||
356 | return 0; | ||
357 | } | ||
358 | fscache_wait_on_page_write(vnode->cache, page); | ||
359 | } | ||
360 | |||
361 | fscache_uncache_page(vnode->cache, page); | ||
362 | ClearPageFsCache(page); | ||
363 | } | 354 | } |
364 | #endif | 355 | #endif |
365 | 356 | ||
diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index a0769872b19c..e5046519b153 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h | |||
@@ -180,6 +180,11 @@ extern atomic_t fscache_n_store_pages; | |||
180 | extern atomic_t fscache_n_store_radix_deletes; | 180 | extern atomic_t fscache_n_store_radix_deletes; |
181 | extern atomic_t fscache_n_store_pages_over_limit; | 181 | extern atomic_t fscache_n_store_pages_over_limit; |
182 | 182 | ||
183 | extern atomic_t fscache_n_store_vmscan_not_storing; | ||
184 | extern atomic_t fscache_n_store_vmscan_gone; | ||
185 | extern atomic_t fscache_n_store_vmscan_busy; | ||
186 | extern atomic_t fscache_n_store_vmscan_cancelled; | ||
187 | |||
183 | extern atomic_t fscache_n_marks; | 188 | extern atomic_t fscache_n_marks; |
184 | extern atomic_t fscache_n_uncaches; | 189 | extern atomic_t fscache_n_uncaches; |
185 | 190 | ||
diff --git a/fs/fscache/page.c b/fs/fscache/page.c index 022a5da8e130..fc76798bd968 100644 --- a/fs/fscache/page.c +++ b/fs/fscache/page.c | |||
@@ -43,6 +43,75 @@ void __fscache_wait_on_page_write(struct fscache_cookie *cookie, struct page *pa | |||
43 | EXPORT_SYMBOL(__fscache_wait_on_page_write); | 43 | EXPORT_SYMBOL(__fscache_wait_on_page_write); |
44 | 44 | ||
45 | /* | 45 | /* |
46 | * decide whether a page can be released, possibly by cancelling a store to it | ||
47 | * - we're allowed to sleep if __GFP_WAIT is flagged | ||
48 | */ | ||
49 | bool __fscache_maybe_release_page(struct fscache_cookie *cookie, | ||
50 | struct page *page, | ||
51 | gfp_t gfp) | ||
52 | { | ||
53 | struct page *xpage; | ||
54 | void *val; | ||
55 | |||
56 | _enter("%p,%p,%x", cookie, page, gfp); | ||
57 | |||
58 | rcu_read_lock(); | ||
59 | val = radix_tree_lookup(&cookie->stores, page->index); | ||
60 | if (!val) { | ||
61 | rcu_read_unlock(); | ||
62 | fscache_stat(&fscache_n_store_vmscan_not_storing); | ||
63 | __fscache_uncache_page(cookie, page); | ||
64 | return true; | ||
65 | } | ||
66 | |||
67 | /* see if the page is actually undergoing storage - if so we can't get | ||
68 | * rid of it till the cache has finished with it */ | ||
69 | if (radix_tree_tag_get(&cookie->stores, page->index, | ||
70 | FSCACHE_COOKIE_STORING_TAG)) { | ||
71 | rcu_read_unlock(); | ||
72 | goto page_busy; | ||
73 | } | ||
74 | |||
75 | /* the page is pending storage, so we attempt to cancel the store and | ||
76 | * discard the store request so that the page can be reclaimed */ | ||
77 | spin_lock(&cookie->stores_lock); | ||
78 | rcu_read_unlock(); | ||
79 | |||
80 | if (radix_tree_tag_get(&cookie->stores, page->index, | ||
81 | FSCACHE_COOKIE_STORING_TAG)) { | ||
82 | /* the page started to undergo storage whilst we were looking, | ||
83 | * so now we can only wait or return */ | ||
84 | spin_unlock(&cookie->stores_lock); | ||
85 | goto page_busy; | ||
86 | } | ||
87 | |||
88 | xpage = radix_tree_delete(&cookie->stores, page->index); | ||
89 | spin_unlock(&cookie->stores_lock); | ||
90 | |||
91 | if (xpage) { | ||
92 | fscache_stat(&fscache_n_store_vmscan_cancelled); | ||
93 | fscache_stat(&fscache_n_store_radix_deletes); | ||
94 | ASSERTCMP(xpage, ==, page); | ||
95 | } else { | ||
96 | fscache_stat(&fscache_n_store_vmscan_gone); | ||
97 | } | ||
98 | |||
99 | wake_up_bit(&cookie->flags, 0); | ||
100 | if (xpage) | ||
101 | page_cache_release(xpage); | ||
102 | __fscache_uncache_page(cookie, page); | ||
103 | return true; | ||
104 | |||
105 | page_busy: | ||
106 | /* we might want to wait here, but that could deadlock the allocator as | ||
107 | * the slow-work threads writing to the cache may all end up sleeping | ||
108 | * on memory allocation */ | ||
109 | fscache_stat(&fscache_n_store_vmscan_busy); | ||
110 | return false; | ||
111 | } | ||
112 | EXPORT_SYMBOL(__fscache_maybe_release_page); | ||
113 | |||
114 | /* | ||
46 | * note that a page has finished being written to the cache | 115 | * note that a page has finished being written to the cache |
47 | */ | 116 | */ |
48 | static void fscache_end_page_write(struct fscache_object *object, | 117 | static void fscache_end_page_write(struct fscache_object *object, |
@@ -57,6 +126,8 @@ static void fscache_end_page_write(struct fscache_object *object, | |||
57 | /* delete the page from the tree if it is now no longer | 126 | /* delete the page from the tree if it is now no longer |
58 | * pending */ | 127 | * pending */ |
59 | spin_lock(&cookie->stores_lock); | 128 | spin_lock(&cookie->stores_lock); |
129 | radix_tree_tag_clear(&cookie->stores, page->index, | ||
130 | FSCACHE_COOKIE_STORING_TAG); | ||
60 | if (!radix_tree_tag_get(&cookie->stores, page->index, | 131 | if (!radix_tree_tag_get(&cookie->stores, page->index, |
61 | FSCACHE_COOKIE_PENDING_TAG)) { | 132 | FSCACHE_COOKIE_PENDING_TAG)) { |
62 | fscache_stat(&fscache_n_store_radix_deletes); | 133 | fscache_stat(&fscache_n_store_radix_deletes); |
@@ -640,8 +711,12 @@ static void fscache_write_op(struct fscache_operation *_op) | |||
640 | goto superseded; | 711 | goto superseded; |
641 | } | 712 | } |
642 | 713 | ||
643 | radix_tree_tag_clear(&cookie->stores, page->index, | 714 | if (page) { |
644 | FSCACHE_COOKIE_PENDING_TAG); | 715 | radix_tree_tag_set(&cookie->stores, page->index, |
716 | FSCACHE_COOKIE_STORING_TAG); | ||
717 | radix_tree_tag_clear(&cookie->stores, page->index, | ||
718 | FSCACHE_COOKIE_PENDING_TAG); | ||
719 | } | ||
645 | 720 | ||
646 | spin_unlock(&cookie->stores_lock); | 721 | spin_unlock(&cookie->stores_lock); |
647 | spin_unlock(&object->lock); | 722 | spin_unlock(&object->lock); |
diff --git a/fs/fscache/stats.c b/fs/fscache/stats.c index 045ba396dbf2..cda69994e06d 100644 --- a/fs/fscache/stats.c +++ b/fs/fscache/stats.c | |||
@@ -63,6 +63,11 @@ atomic_t fscache_n_store_pages; | |||
63 | atomic_t fscache_n_store_radix_deletes; | 63 | atomic_t fscache_n_store_radix_deletes; |
64 | atomic_t fscache_n_store_pages_over_limit; | 64 | atomic_t fscache_n_store_pages_over_limit; |
65 | 65 | ||
66 | atomic_t fscache_n_store_vmscan_not_storing; | ||
67 | atomic_t fscache_n_store_vmscan_gone; | ||
68 | atomic_t fscache_n_store_vmscan_busy; | ||
69 | atomic_t fscache_n_store_vmscan_cancelled; | ||
70 | |||
66 | atomic_t fscache_n_marks; | 71 | atomic_t fscache_n_marks; |
67 | atomic_t fscache_n_uncaches; | 72 | atomic_t fscache_n_uncaches; |
68 | 73 | ||
@@ -211,6 +216,12 @@ static int fscache_stats_show(struct seq_file *m, void *v) | |||
211 | atomic_read(&fscache_n_store_radix_deletes), | 216 | atomic_read(&fscache_n_store_radix_deletes), |
212 | atomic_read(&fscache_n_store_pages_over_limit)); | 217 | atomic_read(&fscache_n_store_pages_over_limit)); |
213 | 218 | ||
219 | seq_printf(m, "VmScan : nos=%u gon=%u bsy=%u can=%u\n", | ||
220 | atomic_read(&fscache_n_store_vmscan_not_storing), | ||
221 | atomic_read(&fscache_n_store_vmscan_gone), | ||
222 | atomic_read(&fscache_n_store_vmscan_busy), | ||
223 | atomic_read(&fscache_n_store_vmscan_cancelled)); | ||
224 | |||
214 | seq_printf(m, "Ops : pend=%u run=%u enq=%u can=%u rej=%u\n", | 225 | seq_printf(m, "Ops : pend=%u run=%u enq=%u can=%u rej=%u\n", |
215 | atomic_read(&fscache_n_op_pend), | 226 | atomic_read(&fscache_n_op_pend), |
216 | atomic_read(&fscache_n_op_run), | 227 | atomic_read(&fscache_n_op_run), |
diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c index 70fad69eb959..fa588006588d 100644 --- a/fs/nfs/fscache.c +++ b/fs/nfs/fscache.c | |||
@@ -359,17 +359,13 @@ int nfs_fscache_release_page(struct page *page, gfp_t gfp) | |||
359 | 359 | ||
360 | BUG_ON(!cookie); | 360 | BUG_ON(!cookie); |
361 | 361 | ||
362 | if (fscache_check_page_write(cookie, page)) { | ||
363 | if (!(gfp & __GFP_WAIT)) | ||
364 | return 0; | ||
365 | fscache_wait_on_page_write(cookie, page); | ||
366 | } | ||
367 | |||
368 | if (PageFsCache(page)) { | 362 | if (PageFsCache(page)) { |
369 | dfprintk(FSCACHE, "NFS: fscache releasepage (0x%p/0x%p/0x%p)\n", | 363 | dfprintk(FSCACHE, "NFS: fscache releasepage (0x%p/0x%p/0x%p)\n", |
370 | cookie, page, nfsi); | 364 | cookie, page, nfsi); |
371 | 365 | ||
372 | fscache_uncache_page(cookie, page); | 366 | if (!fscache_maybe_release_page(cookie, page, gfp)) |
367 | return 0; | ||
368 | |||
373 | nfs_add_fscache_stats(page->mapping->host, | 369 | nfs_add_fscache_stats(page->mapping->host, |
374 | NFSIOS_FSCACHE_PAGES_UNCACHED, 1); | 370 | NFSIOS_FSCACHE_PAGES_UNCACHED, 1); |
375 | } | 371 | } |
diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index f3aa4bdafef6..4750d5fb419f 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h | |||
@@ -317,6 +317,7 @@ struct fscache_cookie { | |||
317 | void *netfs_data; /* back pointer to netfs */ | 317 | void *netfs_data; /* back pointer to netfs */ |
318 | struct radix_tree_root stores; /* pages to be stored on this cookie */ | 318 | struct radix_tree_root stores; /* pages to be stored on this cookie */ |
319 | #define FSCACHE_COOKIE_PENDING_TAG 0 /* pages tag: pending write to cache */ | 319 | #define FSCACHE_COOKIE_PENDING_TAG 0 /* pages tag: pending write to cache */ |
320 | #define FSCACHE_COOKIE_STORING_TAG 1 /* pages tag: writing to cache */ | ||
320 | 321 | ||
321 | unsigned long flags; | 322 | unsigned long flags; |
322 | #define FSCACHE_COOKIE_LOOKING_UP 0 /* T if non-index cookie being looked up still */ | 323 | #define FSCACHE_COOKIE_LOOKING_UP 0 /* T if non-index cookie being looked up still */ |
diff --git a/include/linux/fscache.h b/include/linux/fscache.h index 6d8ee466e0a0..595ce49288b7 100644 --- a/include/linux/fscache.h +++ b/include/linux/fscache.h | |||
@@ -202,6 +202,8 @@ extern int __fscache_write_page(struct fscache_cookie *, struct page *, gfp_t); | |||
202 | extern void __fscache_uncache_page(struct fscache_cookie *, struct page *); | 202 | extern void __fscache_uncache_page(struct fscache_cookie *, struct page *); |
203 | extern bool __fscache_check_page_write(struct fscache_cookie *, struct page *); | 203 | extern bool __fscache_check_page_write(struct fscache_cookie *, struct page *); |
204 | extern void __fscache_wait_on_page_write(struct fscache_cookie *, struct page *); | 204 | extern void __fscache_wait_on_page_write(struct fscache_cookie *, struct page *); |
205 | extern bool __fscache_maybe_release_page(struct fscache_cookie *, struct page *, | ||
206 | gfp_t); | ||
205 | 207 | ||
206 | /** | 208 | /** |
207 | * fscache_register_netfs - Register a filesystem as desiring caching services | 209 | * fscache_register_netfs - Register a filesystem as desiring caching services |
@@ -615,4 +617,29 @@ void fscache_wait_on_page_write(struct fscache_cookie *cookie, | |||
615 | __fscache_wait_on_page_write(cookie, page); | 617 | __fscache_wait_on_page_write(cookie, page); |
616 | } | 618 | } |
617 | 619 | ||
620 | /** | ||
621 | * fscache_maybe_release_page - Consider releasing a page, cancelling a store | ||
622 | * @cookie: The cookie representing the cache object | ||
623 | * @page: The netfs page that is being cached. | ||
624 | * @gfp: The gfp flags passed to releasepage() | ||
625 | * | ||
626 | * Consider releasing a page for the vmscan algorithm, on behalf of the netfs's | ||
627 | * releasepage() call. A storage request on the page may cancelled if it is | ||
628 | * not currently being processed. | ||
629 | * | ||
630 | * The function returns true if the page no longer has a storage request on it, | ||
631 | * and false if a storage request is left in place. If true is returned, the | ||
632 | * page will have been passed to fscache_uncache_page(). If false is returned | ||
633 | * the page cannot be freed yet. | ||
634 | */ | ||
635 | static inline | ||
636 | bool fscache_maybe_release_page(struct fscache_cookie *cookie, | ||
637 | struct page *page, | ||
638 | gfp_t gfp) | ||
639 | { | ||
640 | if (fscache_cookie_valid(cookie) && PageFsCache(page)) | ||
641 | return __fscache_maybe_release_page(cookie, page, gfp); | ||
642 | return false; | ||
643 | } | ||
644 | |||
618 | #endif /* _LINUX_FSCACHE_H */ | 645 | #endif /* _LINUX_FSCACHE_H */ |