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 */ |
