Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 168831
b: refs/heads/master
c: 201a154
h: refs/heads/master
i:
  168829: f26220c
  168827: a91fcc4
  168823: 214ca9b
  168815: 12b510a
  168799: 0f522de
  168767: db98f08
  168703: 98fbea8
v: v3
  • Loading branch information
David Howells committed Nov 19, 2009
1 parent 44601d8 commit ccaaac7
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 36 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: e3d4d28b1c8cc7c26536a50b43d86ccd39878550
refs/heads/master: 201a15428bd54f83eccec8b7c64a04b8f9431204
4 changes: 4 additions & 0 deletions trunk/Documentation/filesystems/caching/fscache.txt
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ proc files.
pgs=N Number of pages given store req processing time
rxd=N Number of store reqs deleted from tracking tree
olm=N Number of store reqs over store limit
VmScan nos=N Number of release reqs against pages with no pending store
gon=N Number of release reqs against pages stored by time lock granted
bsy=N Number of release reqs ignored due to in-progress store
can=N Number of page stores cancelled due to release req
Ops pend=N Number of times async ops added to pending queues
run=N Number of times async ops given CPU time
enq=N Number of times async ops queued for processing
Expand Down
21 changes: 20 additions & 1 deletion trunk/Documentation/filesystems/caching/netfs-api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ data file must be retired (see the relinquish cookie function below).

Furthermore, note that this does not cancel the asynchronous read or write
operation started by the read/alloc and write functions, so the page
invalidation and release functions must use:
invalidation functions must use:

bool fscache_check_page_write(struct fscache_cookie *cookie,
struct page *page);
Expand All @@ -654,6 +654,25 @@ to see if a page is being written to the cache, and:
to wait for it to finish if it is.


When releasepage() is being implemented, a special FS-Cache function exists to
manage the heuristics of coping with vmscan trying to eject pages, which may
conflict with the cache trying to write pages to the cache (which may itself
need to allocate memory):

bool fscache_maybe_release_page(struct fscache_cookie *cookie,
struct page *page,
gfp_t gfp);

This takes the netfs cookie, and the page and gfp arguments as supplied to
releasepage(). It will return false if the page cannot be released yet for
some reason and if it returns true, the page has been uncached and can now be
released.

To make a page available for release, this function may wait for an outstanding
storage request to complete, or it may attempt to cancel the storage request -
in which case the page will not be stored in the cache this time.


==========================
INDEX AND DATA FILE UPDATE
==========================
Expand Down
14 changes: 1 addition & 13 deletions trunk/fs/9p/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,18 +343,7 @@ int __v9fs_fscache_release_page(struct page *page, gfp_t gfp)

BUG_ON(!vcookie->fscache);

if (PageFsCache(page)) {
if (fscache_check_page_write(vcookie->fscache, page)) {
if (!(gfp & __GFP_WAIT))
return 0;
fscache_wait_on_page_write(vcookie->fscache, page);
}

fscache_uncache_page(vcookie->fscache, page);
ClearPageFsCache(page);
}

return 1;
return fscache_maybe_release_page(vnode->cache, page, gfp);
}

void __v9fs_fscache_invalidate_page(struct page *page)
Expand All @@ -368,7 +357,6 @@ void __v9fs_fscache_invalidate_page(struct page *page)
fscache_wait_on_page_write(vcookie->fscache, page);
BUG_ON(!PageLocked(page));
fscache_uncache_page(vcookie->fscache, page);
ClearPageFsCache(page);
}
}

Expand Down
15 changes: 3 additions & 12 deletions trunk/fs/afs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,6 @@ static void afs_invalidatepage(struct page *page, unsigned long offset)
struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
fscache_wait_on_page_write(vnode->cache, page);
fscache_uncache_page(vnode->cache, page);
ClearPageFsCache(page);
}
#endif

Expand Down Expand Up @@ -349,17 +348,9 @@ static int afs_releasepage(struct page *page, gfp_t gfp_flags)
/* deny if page is being written to the cache and the caller hasn't
* elected to wait */
#ifdef CONFIG_AFS_FSCACHE
if (PageFsCache(page)) {
if (fscache_check_page_write(vnode->cache, page)) {
if (!(gfp_flags & __GFP_WAIT)) {
_leave(" = F [cache busy]");
return 0;
}
fscache_wait_on_page_write(vnode->cache, page);
}

fscache_uncache_page(vnode->cache, page);
ClearPageFsCache(page);
if (!fscache_maybe_release_page(vnode->cache, page, gfp_flags)) {
_leave(" = F [cache busy]");
return 0;
}
#endif

Expand Down
5 changes: 5 additions & 0 deletions trunk/fs/fscache/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ extern atomic_t fscache_n_store_pages;
extern atomic_t fscache_n_store_radix_deletes;
extern atomic_t fscache_n_store_pages_over_limit;

extern atomic_t fscache_n_store_vmscan_not_storing;
extern atomic_t fscache_n_store_vmscan_gone;
extern atomic_t fscache_n_store_vmscan_busy;
extern atomic_t fscache_n_store_vmscan_cancelled;

extern atomic_t fscache_n_marks;
extern atomic_t fscache_n_uncaches;

Expand Down
79 changes: 77 additions & 2 deletions trunk/fs/fscache/page.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,75 @@ void __fscache_wait_on_page_write(struct fscache_cookie *cookie, struct page *pa
}
EXPORT_SYMBOL(__fscache_wait_on_page_write);

/*
* decide whether a page can be released, possibly by cancelling a store to it
* - we're allowed to sleep if __GFP_WAIT is flagged
*/
bool __fscache_maybe_release_page(struct fscache_cookie *cookie,
struct page *page,
gfp_t gfp)
{
struct page *xpage;
void *val;

_enter("%p,%p,%x", cookie, page, gfp);

rcu_read_lock();
val = radix_tree_lookup(&cookie->stores, page->index);
if (!val) {
rcu_read_unlock();
fscache_stat(&fscache_n_store_vmscan_not_storing);
__fscache_uncache_page(cookie, page);
return true;
}

/* see if the page is actually undergoing storage - if so we can't get
* rid of it till the cache has finished with it */
if (radix_tree_tag_get(&cookie->stores, page->index,
FSCACHE_COOKIE_STORING_TAG)) {
rcu_read_unlock();
goto page_busy;
}

/* the page is pending storage, so we attempt to cancel the store and
* discard the store request so that the page can be reclaimed */
spin_lock(&cookie->stores_lock);
rcu_read_unlock();

if (radix_tree_tag_get(&cookie->stores, page->index,
FSCACHE_COOKIE_STORING_TAG)) {
/* the page started to undergo storage whilst we were looking,
* so now we can only wait or return */
spin_unlock(&cookie->stores_lock);
goto page_busy;
}

xpage = radix_tree_delete(&cookie->stores, page->index);
spin_unlock(&cookie->stores_lock);

if (xpage) {
fscache_stat(&fscache_n_store_vmscan_cancelled);
fscache_stat(&fscache_n_store_radix_deletes);
ASSERTCMP(xpage, ==, page);
} else {
fscache_stat(&fscache_n_store_vmscan_gone);
}

wake_up_bit(&cookie->flags, 0);
if (xpage)
page_cache_release(xpage);
__fscache_uncache_page(cookie, page);
return true;

page_busy:
/* we might want to wait here, but that could deadlock the allocator as
* the slow-work threads writing to the cache may all end up sleeping
* on memory allocation */
fscache_stat(&fscache_n_store_vmscan_busy);
return false;
}
EXPORT_SYMBOL(__fscache_maybe_release_page);

/*
* note that a page has finished being written to the cache
*/
Expand All @@ -57,6 +126,8 @@ static void fscache_end_page_write(struct fscache_object *object,
/* delete the page from the tree if it is now no longer
* pending */
spin_lock(&cookie->stores_lock);
radix_tree_tag_clear(&cookie->stores, page->index,
FSCACHE_COOKIE_STORING_TAG);
if (!radix_tree_tag_get(&cookie->stores, page->index,
FSCACHE_COOKIE_PENDING_TAG)) {
fscache_stat(&fscache_n_store_radix_deletes);
Expand Down Expand Up @@ -640,8 +711,12 @@ static void fscache_write_op(struct fscache_operation *_op)
goto superseded;
}

radix_tree_tag_clear(&cookie->stores, page->index,
FSCACHE_COOKIE_PENDING_TAG);
if (page) {
radix_tree_tag_set(&cookie->stores, page->index,
FSCACHE_COOKIE_STORING_TAG);
radix_tree_tag_clear(&cookie->stores, page->index,
FSCACHE_COOKIE_PENDING_TAG);
}

spin_unlock(&cookie->stores_lock);
spin_unlock(&object->lock);
Expand Down
11 changes: 11 additions & 0 deletions trunk/fs/fscache/stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ atomic_t fscache_n_store_pages;
atomic_t fscache_n_store_radix_deletes;
atomic_t fscache_n_store_pages_over_limit;

atomic_t fscache_n_store_vmscan_not_storing;
atomic_t fscache_n_store_vmscan_gone;
atomic_t fscache_n_store_vmscan_busy;
atomic_t fscache_n_store_vmscan_cancelled;

atomic_t fscache_n_marks;
atomic_t fscache_n_uncaches;

Expand Down Expand Up @@ -211,6 +216,12 @@ static int fscache_stats_show(struct seq_file *m, void *v)
atomic_read(&fscache_n_store_radix_deletes),
atomic_read(&fscache_n_store_pages_over_limit));

seq_printf(m, "VmScan : nos=%u gon=%u bsy=%u can=%u\n",
atomic_read(&fscache_n_store_vmscan_not_storing),
atomic_read(&fscache_n_store_vmscan_gone),
atomic_read(&fscache_n_store_vmscan_busy),
atomic_read(&fscache_n_store_vmscan_cancelled));

seq_printf(m, "Ops : pend=%u run=%u enq=%u can=%u rej=%u\n",
atomic_read(&fscache_n_op_pend),
atomic_read(&fscache_n_op_run),
Expand Down
10 changes: 3 additions & 7 deletions trunk/fs/nfs/fscache.c
Original file line number Diff line number Diff line change
Expand Up @@ -359,17 +359,13 @@ int nfs_fscache_release_page(struct page *page, gfp_t gfp)

BUG_ON(!cookie);

if (fscache_check_page_write(cookie, page)) {
if (!(gfp & __GFP_WAIT))
return 0;
fscache_wait_on_page_write(cookie, page);
}

if (PageFsCache(page)) {
dfprintk(FSCACHE, "NFS: fscache releasepage (0x%p/0x%p/0x%p)\n",
cookie, page, nfsi);

fscache_uncache_page(cookie, page);
if (!fscache_maybe_release_page(cookie, page, gfp))
return 0;

nfs_add_fscache_stats(page->mapping->host,
NFSIOS_FSCACHE_PAGES_UNCACHED, 1);
}
Expand Down
1 change: 1 addition & 0 deletions trunk/include/linux/fscache-cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ struct fscache_cookie {
void *netfs_data; /* back pointer to netfs */
struct radix_tree_root stores; /* pages to be stored on this cookie */
#define FSCACHE_COOKIE_PENDING_TAG 0 /* pages tag: pending write to cache */
#define FSCACHE_COOKIE_STORING_TAG 1 /* pages tag: writing to cache */

unsigned long flags;
#define FSCACHE_COOKIE_LOOKING_UP 0 /* T if non-index cookie being looked up still */
Expand Down
27 changes: 27 additions & 0 deletions trunk/include/linux/fscache.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ extern int __fscache_write_page(struct fscache_cookie *, struct page *, gfp_t);
extern void __fscache_uncache_page(struct fscache_cookie *, struct page *);
extern bool __fscache_check_page_write(struct fscache_cookie *, struct page *);
extern void __fscache_wait_on_page_write(struct fscache_cookie *, struct page *);
extern bool __fscache_maybe_release_page(struct fscache_cookie *, struct page *,
gfp_t);

/**
* fscache_register_netfs - Register a filesystem as desiring caching services
Expand Down Expand Up @@ -615,4 +617,29 @@ void fscache_wait_on_page_write(struct fscache_cookie *cookie,
__fscache_wait_on_page_write(cookie, page);
}

/**
* fscache_maybe_release_page - Consider releasing a page, cancelling a store
* @cookie: The cookie representing the cache object
* @page: The netfs page that is being cached.
* @gfp: The gfp flags passed to releasepage()
*
* Consider releasing a page for the vmscan algorithm, on behalf of the netfs's
* releasepage() call. A storage request on the page may cancelled if it is
* not currently being processed.
*
* The function returns true if the page no longer has a storage request on it,
* and false if a storage request is left in place. If true is returned, the
* page will have been passed to fscache_uncache_page(). If false is returned
* the page cannot be freed yet.
*/
static inline
bool fscache_maybe_release_page(struct fscache_cookie *cookie,
struct page *page,
gfp_t gfp)
{
if (fscache_cookie_valid(cookie) && PageFsCache(page))
return __fscache_maybe_release_page(cookie, page, gfp);
return false;
}

#endif /* _LINUX_FSCACHE_H */

0 comments on commit ccaaac7

Please sign in to comment.