You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-2.6-fscache
* git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-2.6-fscache: (31 commits) FS-Cache: Provide nop fscache_stat_d() if CONFIG_FSCACHE_STATS=n SLOW_WORK: Fix GFS2 to #include <linux/module.h> before using THIS_MODULE SLOW_WORK: Fix CIFS to pass THIS_MODULE to slow_work_register_user() CacheFiles: Don't log lookup/create failing with ENOBUFS CacheFiles: Catch an overly long wait for an old active object CacheFiles: Better showing of debugging information in active object problems CacheFiles: Mark parent directory locks as I_MUTEX_PARENT to keep lockdep happy CacheFiles: Handle truncate unlocking the page we're reading CacheFiles: Don't write a full page if there's only a partial page to cache FS-Cache: Actually requeue an object when requested FS-Cache: Start processing an object's operations on that object's death FS-Cache: Make sure FSCACHE_COOKIE_LOOKING_UP cleared on lookup failure FS-Cache: Add a retirement stat counter FS-Cache: Handle pages pending storage that get evicted under OOM conditions FS-Cache: Handle read request vs lookup, creation or other cache failure FS-Cache: Don't delete pending pages from the page-store tracking tree FS-Cache: Fix lock misorder in fscache_write_op() FS-Cache: The object-available state can't rely on the cookie to be available FS-Cache: Permit cache retrieval ops to be interrupted in the initial wait phase FS-Cache: Use radix tree preload correctly in tracking of pages to be stored ...
This commit is contained in:
@@ -235,6 +235,7 @@ proc files.
|
||||
neg=N Number of negative lookups made
|
||||
pos=N Number of positive lookups made
|
||||
crt=N Number of objects created by lookup
|
||||
tmo=N Number of lookups timed out and requeued
|
||||
Updates n=N Number of update cookie requests seen
|
||||
nul=N Number of upd reqs given a NULL parent
|
||||
run=N Number of upd reqs granted CPU time
|
||||
@@ -250,8 +251,10 @@ proc files.
|
||||
ok=N Number of successful alloc reqs
|
||||
wt=N Number of alloc reqs that waited on lookup completion
|
||||
nbf=N Number of alloc reqs rejected -ENOBUFS
|
||||
int=N Number of alloc reqs aborted -ERESTARTSYS
|
||||
ops=N Number of alloc reqs submitted
|
||||
owt=N Number of alloc reqs waited for CPU time
|
||||
abt=N Number of alloc reqs aborted due to object death
|
||||
Retrvls n=N Number of retrieval (read) requests seen
|
||||
ok=N Number of successful retr reqs
|
||||
wt=N Number of retr reqs that waited on lookup completion
|
||||
@@ -261,6 +264,7 @@ proc files.
|
||||
oom=N Number of retr reqs failed -ENOMEM
|
||||
ops=N Number of retr reqs submitted
|
||||
owt=N Number of retr reqs waited for CPU time
|
||||
abt=N Number of retr reqs aborted due to object death
|
||||
Stores n=N Number of storage (write) requests seen
|
||||
ok=N Number of successful store reqs
|
||||
agn=N Number of store reqs on a page already pending storage
|
||||
@@ -268,12 +272,37 @@ proc files.
|
||||
oom=N Number of store reqs failed -ENOMEM
|
||||
ops=N Number of store reqs submitted
|
||||
run=N Number of store reqs granted CPU time
|
||||
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
|
||||
can=N Number of async ops cancelled
|
||||
rej=N Number of async ops rejected due to object lookup/create failure
|
||||
dfr=N Number of async ops queued for deferred release
|
||||
rel=N Number of async ops released
|
||||
gc=N Number of deferred-release async ops garbage collected
|
||||
CacheOp alo=N Number of in-progress alloc_object() cache ops
|
||||
luo=N Number of in-progress lookup_object() cache ops
|
||||
luc=N Number of in-progress lookup_complete() cache ops
|
||||
gro=N Number of in-progress grab_object() cache ops
|
||||
upo=N Number of in-progress update_object() cache ops
|
||||
dro=N Number of in-progress drop_object() cache ops
|
||||
pto=N Number of in-progress put_object() cache ops
|
||||
syn=N Number of in-progress sync_cache() cache ops
|
||||
atc=N Number of in-progress attr_changed() cache ops
|
||||
rap=N Number of in-progress read_or_alloc_page() cache ops
|
||||
ras=N Number of in-progress read_or_alloc_pages() cache ops
|
||||
alp=N Number of in-progress allocate_page() cache ops
|
||||
als=N Number of in-progress allocate_pages() cache ops
|
||||
wrp=N Number of in-progress write_page() cache ops
|
||||
ucp=N Number of in-progress uncache_page() cache ops
|
||||
dsp=N Number of in-progress dissociate_pages() cache ops
|
||||
|
||||
|
||||
(*) /proc/fs/fscache/histogram
|
||||
@@ -299,6 +328,87 @@ proc files.
|
||||
jiffy range covered, and the SECS field the equivalent number of seconds.
|
||||
|
||||
|
||||
===========
|
||||
OBJECT LIST
|
||||
===========
|
||||
|
||||
If CONFIG_FSCACHE_OBJECT_LIST is enabled, the FS-Cache facility will maintain a
|
||||
list of all the objects currently allocated and allow them to be viewed
|
||||
through:
|
||||
|
||||
/proc/fs/fscache/objects
|
||||
|
||||
This will look something like:
|
||||
|
||||
[root@andromeda ~]# head /proc/fs/fscache/objects
|
||||
OBJECT PARENT STAT CHLDN OPS OOP IPR EX READS EM EV F S | NETFS_COOKIE_DEF TY FL NETFS_DATA OBJECT_KEY, AUX_DATA
|
||||
======== ======== ==== ===== === === === == ===== == == = = | ================ == == ================ ================
|
||||
17e4b 2 ACTV 0 0 0 0 0 0 7b 4 0 8 | NFS.fh DT 0 ffff88001dd82820 010006017edcf8bbc93b43298fdfbe71e50b57b13a172c0117f38472, e567634700000000000000000000000063f2404a000000000000000000000000c9030000000000000000000063f2404a
|
||||
1693a 2 ACTV 0 0 0 0 0 0 7b 4 0 8 | NFS.fh DT 0 ffff88002db23380 010006017edcf8bbc93b43298fdfbe71e50b57b1e0162c01a2df0ea6, 420ebc4a000000000000000000000000420ebc4a0000000000000000000000000e1801000000000000000000420ebc4a
|
||||
|
||||
where the first set of columns before the '|' describe the object:
|
||||
|
||||
COLUMN DESCRIPTION
|
||||
======= ===============================================================
|
||||
OBJECT Object debugging ID (appears as OBJ%x in some debug messages)
|
||||
PARENT Debugging ID of parent object
|
||||
STAT Object state
|
||||
CHLDN Number of child objects of this object
|
||||
OPS Number of outstanding operations on this object
|
||||
OOP Number of outstanding child object management operations
|
||||
IPR
|
||||
EX Number of outstanding exclusive operations
|
||||
READS Number of outstanding read operations
|
||||
EM Object's event mask
|
||||
EV Events raised on this object
|
||||
F Object flags
|
||||
S Object slow-work work item flags
|
||||
|
||||
and the second set of columns describe the object's cookie, if present:
|
||||
|
||||
COLUMN DESCRIPTION
|
||||
=============== =======================================================
|
||||
NETFS_COOKIE_DEF Name of netfs cookie definition
|
||||
TY Cookie type (IX - index, DT - data, hex - special)
|
||||
FL Cookie flags
|
||||
NETFS_DATA Netfs private data stored in the cookie
|
||||
OBJECT_KEY Object key } 1 column, with separating comma
|
||||
AUX_DATA Object aux data } presence may be configured
|
||||
|
||||
The data shown may be filtered by attaching the a key to an appropriate keyring
|
||||
before viewing the file. Something like:
|
||||
|
||||
keyctl add user fscache:objlist <restrictions> @s
|
||||
|
||||
where <restrictions> are a selection of the following letters:
|
||||
|
||||
K Show hexdump of object key (don't show if not given)
|
||||
A Show hexdump of object aux data (don't show if not given)
|
||||
|
||||
and the following paired letters:
|
||||
|
||||
C Show objects that have a cookie
|
||||
c Show objects that don't have a cookie
|
||||
B Show objects that are busy
|
||||
b Show objects that aren't busy
|
||||
W Show objects that have pending writes
|
||||
w Show objects that don't have pending writes
|
||||
R Show objects that have outstanding reads
|
||||
r Show objects that don't have outstanding reads
|
||||
S Show objects that have slow work queued
|
||||
s Show objects that don't have slow work queued
|
||||
|
||||
If neither side of a letter pair is given, then both are implied. For example:
|
||||
|
||||
keyctl add user fscache:objlist KB @s
|
||||
|
||||
shows objects that are busy, and lists their object keys, but does not dump
|
||||
their auxiliary data. It also implies "CcWwRrSs", but as 'B' is given, 'b' is
|
||||
not implied.
|
||||
|
||||
By default all objects and all fields will be shown.
|
||||
|
||||
|
||||
=========
|
||||
DEBUGGING
|
||||
=========
|
||||
|
||||
@@ -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);
|
||||
@@ -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
|
||||
==========================
|
||||
|
||||
+154
-6
@@ -41,6 +41,13 @@ expand files, provided the time taken to do so isn't too long.
|
||||
Operations of both types may sleep during execution, thus tying up the thread
|
||||
loaned to it.
|
||||
|
||||
A further class of work item is available, based on the slow work item class:
|
||||
|
||||
(*) Delayed slow work items.
|
||||
|
||||
These are slow work items that have a timer to defer queueing of the item for
|
||||
a while.
|
||||
|
||||
|
||||
THREAD-TO-CLASS ALLOCATION
|
||||
--------------------------
|
||||
@@ -64,9 +71,11 @@ USING SLOW WORK ITEMS
|
||||
Firstly, a module or subsystem wanting to make use of slow work items must
|
||||
register its interest:
|
||||
|
||||
int ret = slow_work_register_user();
|
||||
int ret = slow_work_register_user(struct module *module);
|
||||
|
||||
This will return 0 if successful, or a -ve error upon failure.
|
||||
This will return 0 if successful, or a -ve error upon failure. The module
|
||||
pointer should be the module interested in using this facility (almost
|
||||
certainly THIS_MODULE).
|
||||
|
||||
|
||||
Slow work items may then be set up by:
|
||||
@@ -91,6 +100,10 @@ Slow work items may then be set up by:
|
||||
|
||||
slow_work_init(&myitem, &myitem_ops);
|
||||
|
||||
or:
|
||||
|
||||
delayed_slow_work_init(&myitem, &myitem_ops);
|
||||
|
||||
or:
|
||||
|
||||
vslow_work_init(&myitem, &myitem_ops);
|
||||
@@ -102,15 +115,92 @@ A suitably set up work item can then be enqueued for processing:
|
||||
int ret = slow_work_enqueue(&myitem);
|
||||
|
||||
This will return a -ve error if the thread pool is unable to gain a reference
|
||||
on the item, 0 otherwise.
|
||||
on the item, 0 otherwise, or (for delayed work):
|
||||
|
||||
int ret = delayed_slow_work_enqueue(&myitem, my_jiffy_delay);
|
||||
|
||||
|
||||
The items are reference counted, so there ought to be no need for a flush
|
||||
operation. When all a module's slow work items have been processed, and the
|
||||
operation. But as the reference counting is optional, means to cancel
|
||||
existing work items are also included:
|
||||
|
||||
cancel_slow_work(&myitem);
|
||||
cancel_delayed_slow_work(&myitem);
|
||||
|
||||
can be used to cancel pending work. The above cancel function waits for
|
||||
existing work to have been executed (or prevent execution of them, depending
|
||||
on timing).
|
||||
|
||||
|
||||
When all a module's slow work items have been processed, and the
|
||||
module has no further interest in the facility, it should unregister its
|
||||
interest:
|
||||
|
||||
slow_work_unregister_user();
|
||||
slow_work_unregister_user(struct module *module);
|
||||
|
||||
The module pointer is used to wait for all outstanding work items for that
|
||||
module before completing the unregistration. This prevents the put_ref() code
|
||||
from being taken away before it completes. module should almost certainly be
|
||||
THIS_MODULE.
|
||||
|
||||
|
||||
================
|
||||
HELPER FUNCTIONS
|
||||
================
|
||||
|
||||
The slow-work facility provides a function by which it can be determined
|
||||
whether or not an item is queued for later execution:
|
||||
|
||||
bool queued = slow_work_is_queued(struct slow_work *work);
|
||||
|
||||
If it returns false, then the item is not on the queue (it may be executing
|
||||
with a requeue pending). This can be used to work out whether an item on which
|
||||
another depends is on the queue, thus allowing a dependent item to be queued
|
||||
after it.
|
||||
|
||||
If the above shows an item on which another depends not to be queued, then the
|
||||
owner of the dependent item might need to wait. However, to avoid locking up
|
||||
the threads unnecessarily be sleeping in them, it can make sense under some
|
||||
circumstances to return the work item to the queue, thus deferring it until
|
||||
some other items have had a chance to make use of the yielded thread.
|
||||
|
||||
To yield a thread and defer an item, the work function should simply enqueue
|
||||
the work item again and return. However, this doesn't work if there's nothing
|
||||
actually on the queue, as the thread just vacated will jump straight back into
|
||||
the item's work function, thus busy waiting on a CPU.
|
||||
|
||||
Instead, the item should use the thread to wait for the dependency to go away,
|
||||
but rather than using schedule() or schedule_timeout() to sleep, it should use
|
||||
the following function:
|
||||
|
||||
bool requeue = slow_work_sleep_till_thread_needed(
|
||||
struct slow_work *work,
|
||||
signed long *_timeout);
|
||||
|
||||
This will add a second wait and then sleep, such that it will be woken up if
|
||||
either something appears on the queue that could usefully make use of the
|
||||
thread - and behind which this item can be queued, or if the event the caller
|
||||
set up to wait for happens. True will be returned if something else appeared
|
||||
on the queue and this work function should perhaps return, of false if
|
||||
something else woke it up. The timeout is as for schedule_timeout().
|
||||
|
||||
For example:
|
||||
|
||||
wq = bit_waitqueue(&my_flags, MY_BIT);
|
||||
init_wait(&wait);
|
||||
requeue = false;
|
||||
do {
|
||||
prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE);
|
||||
if (!test_bit(MY_BIT, &my_flags))
|
||||
break;
|
||||
requeue = slow_work_sleep_till_thread_needed(&my_work,
|
||||
&timeout);
|
||||
} while (timeout > 0 && !requeue);
|
||||
finish_wait(wq, &wait);
|
||||
if (!test_bit(MY_BIT, &my_flags)
|
||||
goto do_my_thing;
|
||||
if (requeue)
|
||||
return; // to slow_work
|
||||
|
||||
|
||||
===============
|
||||
@@ -118,7 +208,8 @@ ITEM OPERATIONS
|
||||
===============
|
||||
|
||||
Each work item requires a table of operations of type struct slow_work_ops.
|
||||
All members are required:
|
||||
Only ->execute() is required; the getting and putting of a reference and the
|
||||
describing of an item are all optional.
|
||||
|
||||
(*) Get a reference on an item:
|
||||
|
||||
@@ -148,6 +239,16 @@ All members are required:
|
||||
This should perform the work required of the item. It may sleep, it may
|
||||
perform disk I/O and it may wait for locks.
|
||||
|
||||
(*) View an item through /proc:
|
||||
|
||||
void (*desc)(struct slow_work *work, struct seq_file *m);
|
||||
|
||||
If supplied, this should print to 'm' a small string describing the work
|
||||
the item is to do. This should be no more than about 40 characters, and
|
||||
shouldn't include a newline character.
|
||||
|
||||
See the 'Viewing executing and queued items' section below.
|
||||
|
||||
|
||||
==================
|
||||
POOL CONFIGURATION
|
||||
@@ -172,3 +273,50 @@ The slow-work thread pool has a number of configurables:
|
||||
is bounded to between 1 and one fewer than the number of active threads.
|
||||
This ensures there is always at least one thread that can process very
|
||||
slow work items, and always at least one thread that won't.
|
||||
|
||||
|
||||
==================================
|
||||
VIEWING EXECUTING AND QUEUED ITEMS
|
||||
==================================
|
||||
|
||||
If CONFIG_SLOW_WORK_PROC is enabled, a proc file is made available:
|
||||
|
||||
/proc/slow_work_rq
|
||||
|
||||
through which the list of work items being executed and the queues of items to
|
||||
be executed may be viewed. The owner of a work item is given the chance to
|
||||
add some information of its own.
|
||||
|
||||
The contents look something like the following:
|
||||
|
||||
THR PID ITEM ADDR FL MARK DESC
|
||||
=== ===== ================ == ===== ==========
|
||||
0 3005 ffff880023f52348 a 952ms FSC: OBJ17d3: LOOK
|
||||
1 3006 ffff880024e33668 2 160ms FSC: OBJ17e5 OP60d3b: Write1/Store fl=2
|
||||
2 3165 ffff8800296dd180 a 424ms FSC: OBJ17e4: LOOK
|
||||
3 4089 ffff8800262c8d78 a 212ms FSC: OBJ17ea: CRTN
|
||||
4 4090 ffff88002792bed8 2 388ms FSC: OBJ17e8 OP60d36: Write1/Store fl=2
|
||||
5 4092 ffff88002a0ef308 2 388ms FSC: OBJ17e7 OP60d2e: Write1/Store fl=2
|
||||
6 4094 ffff88002abaf4b8 2 132ms FSC: OBJ17e2 OP60d4e: Write1/Store fl=2
|
||||
7 4095 ffff88002bb188e0 a 388ms FSC: OBJ17e9: CRTN
|
||||
vsq - ffff880023d99668 1 308ms FSC: OBJ17e0 OP60f91: Write1/EnQ fl=2
|
||||
vsq - ffff8800295d1740 1 212ms FSC: OBJ16be OP4d4b6: Write1/EnQ fl=2
|
||||
vsq - ffff880025ba3308 1 160ms FSC: OBJ179a OP58dec: Write1/EnQ fl=2
|
||||
vsq - ffff880024ec83e0 1 160ms FSC: OBJ17ae OP599f2: Write1/EnQ fl=2
|
||||
vsq - ffff880026618e00 1 160ms FSC: OBJ17e6 OP60d33: Write1/EnQ fl=2
|
||||
vsq - ffff880025a2a4b8 1 132ms FSC: OBJ16a2 OP4d583: Write1/EnQ fl=2
|
||||
vsq - ffff880023cbe6d8 9 212ms FSC: OBJ17eb: LOOK
|
||||
vsq - ffff880024d37590 9 212ms FSC: OBJ17ec: LOOK
|
||||
vsq - ffff880027746cb0 9 212ms FSC: OBJ17ed: LOOK
|
||||
vsq - ffff880024d37ae8 9 212ms FSC: OBJ17ee: LOOK
|
||||
vsq - ffff880024d37cb0 9 212ms FSC: OBJ17ef: LOOK
|
||||
vsq - ffff880025036550 9 212ms FSC: OBJ17f0: LOOK
|
||||
vsq - ffff8800250368e0 9 212ms FSC: OBJ17f1: LOOK
|
||||
vsq - ffff880025036aa8 9 212ms FSC: OBJ17f2: LOOK
|
||||
|
||||
In the 'THR' column, executing items show the thread they're occupying and
|
||||
queued threads indicate which queue they're on. 'PID' shows the process ID of
|
||||
a slow-work thread that's executing something. 'FL' shows the work item flags.
|
||||
'MARK' indicates how long since an item was queued or began executing. Lastly,
|
||||
the 'DESC' column permits the owner of an item to give some information.
|
||||
|
||||
|
||||
+1
-13
@@ -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)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-12
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -114,8 +114,9 @@ nomem_lookup_data:
|
||||
|
||||
/*
|
||||
* attempt to look up the nominated node in this cache
|
||||
* - return -ETIMEDOUT to be scheduled again
|
||||
*/
|
||||
static void cachefiles_lookup_object(struct fscache_object *_object)
|
||||
static int cachefiles_lookup_object(struct fscache_object *_object)
|
||||
{
|
||||
struct cachefiles_lookup_data *lookup_data;
|
||||
struct cachefiles_object *parent, *object;
|
||||
@@ -145,13 +146,15 @@ static void cachefiles_lookup_object(struct fscache_object *_object)
|
||||
object->fscache.cookie->def->type != FSCACHE_COOKIE_TYPE_INDEX)
|
||||
cachefiles_attr_changed(&object->fscache);
|
||||
|
||||
if (ret < 0) {
|
||||
printk(KERN_WARNING "CacheFiles: Lookup failed error %d\n",
|
||||
ret);
|
||||
if (ret < 0 && ret != -ETIMEDOUT) {
|
||||
if (ret != -ENOBUFS)
|
||||
printk(KERN_WARNING
|
||||
"CacheFiles: Lookup failed error %d\n", ret);
|
||||
fscache_object_lookup_error(&object->fscache);
|
||||
}
|
||||
|
||||
_leave(" [%d]", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -331,6 +334,7 @@ static void cachefiles_put_object(struct fscache_object *_object)
|
||||
}
|
||||
|
||||
cache = object->fscache.cache;
|
||||
fscache_object_destroy(&object->fscache);
|
||||
kmem_cache_free(cachefiles_object_jar, object);
|
||||
fscache_object_destroyed(cache);
|
||||
}
|
||||
@@ -403,12 +407,26 @@ static int cachefiles_attr_changed(struct fscache_object *_object)
|
||||
if (oi_size == ni_size)
|
||||
return 0;
|
||||
|
||||
newattrs.ia_size = ni_size;
|
||||
newattrs.ia_valid = ATTR_SIZE;
|
||||
|
||||
cachefiles_begin_secure(cache, &saved_cred);
|
||||
mutex_lock(&object->backer->d_inode->i_mutex);
|
||||
|
||||
/* if there's an extension to a partial page at the end of the backing
|
||||
* file, we need to discard the partial page so that we pick up new
|
||||
* data after it */
|
||||
if (oi_size & ~PAGE_MASK && ni_size > oi_size) {
|
||||
_debug("discard tail %llx", oi_size);
|
||||
newattrs.ia_valid = ATTR_SIZE;
|
||||
newattrs.ia_size = oi_size & PAGE_MASK;
|
||||
ret = notify_change(object->backer, &newattrs);
|
||||
if (ret < 0)
|
||||
goto truncate_failed;
|
||||
}
|
||||
|
||||
newattrs.ia_valid = ATTR_SIZE;
|
||||
newattrs.ia_size = ni_size;
|
||||
ret = notify_change(object->backer, &newattrs);
|
||||
|
||||
truncate_failed:
|
||||
mutex_unlock(&object->backer->d_inode->i_mutex);
|
||||
cachefiles_end_secure(cache, saved_cred);
|
||||
|
||||
|
||||
+142
-45
@@ -21,17 +21,81 @@
|
||||
#include <linux/security.h>
|
||||
#include "internal.h"
|
||||
|
||||
static int cachefiles_wait_bit(void *flags)
|
||||
#define CACHEFILES_KEYBUF_SIZE 512
|
||||
|
||||
/*
|
||||
* dump debugging info about an object
|
||||
*/
|
||||
static noinline
|
||||
void __cachefiles_printk_object(struct cachefiles_object *object,
|
||||
const char *prefix,
|
||||
u8 *keybuf)
|
||||
{
|
||||
schedule();
|
||||
return 0;
|
||||
struct fscache_cookie *cookie;
|
||||
unsigned keylen, loop;
|
||||
|
||||
printk(KERN_ERR "%sobject: OBJ%x\n",
|
||||
prefix, object->fscache.debug_id);
|
||||
printk(KERN_ERR "%sobjstate=%s fl=%lx swfl=%lx ev=%lx[%lx]\n",
|
||||
prefix, fscache_object_states[object->fscache.state],
|
||||
object->fscache.flags, object->fscache.work.flags,
|
||||
object->fscache.events,
|
||||
object->fscache.event_mask & FSCACHE_OBJECT_EVENTS_MASK);
|
||||
printk(KERN_ERR "%sops=%u inp=%u exc=%u\n",
|
||||
prefix, object->fscache.n_ops, object->fscache.n_in_progress,
|
||||
object->fscache.n_exclusive);
|
||||
printk(KERN_ERR "%sparent=%p\n",
|
||||
prefix, object->fscache.parent);
|
||||
|
||||
spin_lock(&object->fscache.lock);
|
||||
cookie = object->fscache.cookie;
|
||||
if (cookie) {
|
||||
printk(KERN_ERR "%scookie=%p [pr=%p nd=%p fl=%lx]\n",
|
||||
prefix,
|
||||
object->fscache.cookie,
|
||||
object->fscache.cookie->parent,
|
||||
object->fscache.cookie->netfs_data,
|
||||
object->fscache.cookie->flags);
|
||||
if (keybuf)
|
||||
keylen = cookie->def->get_key(cookie->netfs_data, keybuf,
|
||||
CACHEFILES_KEYBUF_SIZE);
|
||||
else
|
||||
keylen = 0;
|
||||
} else {
|
||||
printk(KERN_ERR "%scookie=NULL\n", prefix);
|
||||
keylen = 0;
|
||||
}
|
||||
spin_unlock(&object->fscache.lock);
|
||||
|
||||
if (keylen) {
|
||||
printk(KERN_ERR "%skey=[%u] '", prefix, keylen);
|
||||
for (loop = 0; loop < keylen; loop++)
|
||||
printk("%02x", keybuf[loop]);
|
||||
printk("'\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* dump debugging info about a pair of objects
|
||||
*/
|
||||
static noinline void cachefiles_printk_object(struct cachefiles_object *object,
|
||||
struct cachefiles_object *xobject)
|
||||
{
|
||||
u8 *keybuf;
|
||||
|
||||
keybuf = kmalloc(CACHEFILES_KEYBUF_SIZE, GFP_NOIO);
|
||||
if (object)
|
||||
__cachefiles_printk_object(object, "", keybuf);
|
||||
if (xobject)
|
||||
__cachefiles_printk_object(xobject, "x", keybuf);
|
||||
kfree(keybuf);
|
||||
}
|
||||
|
||||
/*
|
||||
* record the fact that an object is now active
|
||||
*/
|
||||
static void cachefiles_mark_object_active(struct cachefiles_cache *cache,
|
||||
struct cachefiles_object *object)
|
||||
static int cachefiles_mark_object_active(struct cachefiles_cache *cache,
|
||||
struct cachefiles_object *object)
|
||||
{
|
||||
struct cachefiles_object *xobject;
|
||||
struct rb_node **_p, *_parent = NULL;
|
||||
@@ -42,8 +106,11 @@ static void cachefiles_mark_object_active(struct cachefiles_cache *cache,
|
||||
try_again:
|
||||
write_lock(&cache->active_lock);
|
||||
|
||||
if (test_and_set_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags))
|
||||
if (test_and_set_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags)) {
|
||||
printk(KERN_ERR "CacheFiles: Error: Object already active\n");
|
||||
cachefiles_printk_object(object, NULL);
|
||||
BUG();
|
||||
}
|
||||
|
||||
dentry = object->dentry;
|
||||
_p = &cache->active_nodes.rb_node;
|
||||
@@ -66,8 +133,8 @@ try_again:
|
||||
rb_insert_color(&object->active_node, &cache->active_nodes);
|
||||
|
||||
write_unlock(&cache->active_lock);
|
||||
_leave("");
|
||||
return;
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
/* an old object from a previous incarnation is hogging the slot - we
|
||||
* need to wait for it to be destroyed */
|
||||
@@ -76,44 +143,70 @@ wait_for_old_object:
|
||||
printk(KERN_ERR "\n");
|
||||
printk(KERN_ERR "CacheFiles: Error:"
|
||||
" Unexpected object collision\n");
|
||||
printk(KERN_ERR "xobject: OBJ%x\n",
|
||||
xobject->fscache.debug_id);
|
||||
printk(KERN_ERR "xobjstate=%s\n",
|
||||
fscache_object_states[xobject->fscache.state]);
|
||||
printk(KERN_ERR "xobjflags=%lx\n", xobject->fscache.flags);
|
||||
printk(KERN_ERR "xobjevent=%lx [%lx]\n",
|
||||
xobject->fscache.events, xobject->fscache.event_mask);
|
||||
printk(KERN_ERR "xops=%u inp=%u exc=%u\n",
|
||||
xobject->fscache.n_ops, xobject->fscache.n_in_progress,
|
||||
xobject->fscache.n_exclusive);
|
||||
printk(KERN_ERR "xcookie=%p [pr=%p nd=%p fl=%lx]\n",
|
||||
xobject->fscache.cookie,
|
||||
xobject->fscache.cookie->parent,
|
||||
xobject->fscache.cookie->netfs_data,
|
||||
xobject->fscache.cookie->flags);
|
||||
printk(KERN_ERR "xparent=%p\n",
|
||||
xobject->fscache.parent);
|
||||
printk(KERN_ERR "object: OBJ%x\n",
|
||||
object->fscache.debug_id);
|
||||
printk(KERN_ERR "cookie=%p [pr=%p nd=%p fl=%lx]\n",
|
||||
object->fscache.cookie,
|
||||
object->fscache.cookie->parent,
|
||||
object->fscache.cookie->netfs_data,
|
||||
object->fscache.cookie->flags);
|
||||
printk(KERN_ERR "parent=%p\n",
|
||||
object->fscache.parent);
|
||||
cachefiles_printk_object(object, xobject);
|
||||
BUG();
|
||||
}
|
||||
atomic_inc(&xobject->usage);
|
||||
write_unlock(&cache->active_lock);
|
||||
|
||||
_debug(">>> wait");
|
||||
wait_on_bit(&xobject->flags, CACHEFILES_OBJECT_ACTIVE,
|
||||
cachefiles_wait_bit, TASK_UNINTERRUPTIBLE);
|
||||
_debug("<<< waited");
|
||||
if (test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags)) {
|
||||
wait_queue_head_t *wq;
|
||||
|
||||
signed long timeout = 60 * HZ;
|
||||
wait_queue_t wait;
|
||||
bool requeue;
|
||||
|
||||
/* if the object we're waiting for is queued for processing,
|
||||
* then just put ourselves on the queue behind it */
|
||||
if (slow_work_is_queued(&xobject->fscache.work)) {
|
||||
_debug("queue OBJ%x behind OBJ%x immediately",
|
||||
object->fscache.debug_id,
|
||||
xobject->fscache.debug_id);
|
||||
goto requeue;
|
||||
}
|
||||
|
||||
/* otherwise we sleep until either the object we're waiting for
|
||||
* is done, or the slow-work facility wants the thread back to
|
||||
* do other work */
|
||||
wq = bit_waitqueue(&xobject->flags, CACHEFILES_OBJECT_ACTIVE);
|
||||
init_wait(&wait);
|
||||
requeue = false;
|
||||
do {
|
||||
prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE);
|
||||
if (!test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags))
|
||||
break;
|
||||
requeue = slow_work_sleep_till_thread_needed(
|
||||
&object->fscache.work, &timeout);
|
||||
} while (timeout > 0 && !requeue);
|
||||
finish_wait(wq, &wait);
|
||||
|
||||
if (requeue &&
|
||||
test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags)) {
|
||||
_debug("queue OBJ%x behind OBJ%x after wait",
|
||||
object->fscache.debug_id,
|
||||
xobject->fscache.debug_id);
|
||||
goto requeue;
|
||||
}
|
||||
|
||||
if (timeout <= 0) {
|
||||
printk(KERN_ERR "\n");
|
||||
printk(KERN_ERR "CacheFiles: Error: Overlong"
|
||||
" wait for old active object to go away\n");
|
||||
cachefiles_printk_object(object, xobject);
|
||||
goto requeue;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(!test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags));
|
||||
|
||||
cache->cache.ops->put_object(&xobject->fscache);
|
||||
goto try_again;
|
||||
|
||||
requeue:
|
||||
clear_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags);
|
||||
cache->cache.ops->put_object(&xobject->fscache);
|
||||
_leave(" = -ETIMEDOUT");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -254,7 +347,7 @@ int cachefiles_delete_object(struct cachefiles_cache *cache,
|
||||
|
||||
dir = dget_parent(object->dentry);
|
||||
|
||||
mutex_lock(&dir->d_inode->i_mutex);
|
||||
mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
ret = cachefiles_bury_object(cache, dir, object->dentry);
|
||||
|
||||
dput(dir);
|
||||
@@ -307,7 +400,7 @@ lookup_again:
|
||||
/* search the current directory for the element name */
|
||||
_debug("lookup '%s'", name);
|
||||
|
||||
mutex_lock(&dir->d_inode->i_mutex);
|
||||
mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
|
||||
start = jiffies;
|
||||
next = lookup_one_len(name, dir, nlen);
|
||||
@@ -418,12 +511,15 @@ lookup_again:
|
||||
}
|
||||
|
||||
/* note that we're now using this object */
|
||||
cachefiles_mark_object_active(cache, object);
|
||||
ret = cachefiles_mark_object_active(cache, object);
|
||||
|
||||
mutex_unlock(&dir->d_inode->i_mutex);
|
||||
dput(dir);
|
||||
dir = NULL;
|
||||
|
||||
if (ret == -ETIMEDOUT)
|
||||
goto mark_active_timed_out;
|
||||
|
||||
_debug("=== OBTAINED_OBJECT ===");
|
||||
|
||||
if (object->new) {
|
||||
@@ -467,6 +563,10 @@ create_error:
|
||||
cachefiles_io_error(cache, "Create/mkdir failed");
|
||||
goto error;
|
||||
|
||||
mark_active_timed_out:
|
||||
_debug("mark active timed out");
|
||||
goto release_dentry;
|
||||
|
||||
check_error:
|
||||
_debug("check error %d", ret);
|
||||
write_lock(&cache->active_lock);
|
||||
@@ -474,7 +574,7 @@ check_error:
|
||||
clear_bit(CACHEFILES_OBJECT_ACTIVE, &object->flags);
|
||||
wake_up_bit(&object->flags, CACHEFILES_OBJECT_ACTIVE);
|
||||
write_unlock(&cache->active_lock);
|
||||
|
||||
release_dentry:
|
||||
dput(object->dentry);
|
||||
object->dentry = NULL;
|
||||
goto error_out;
|
||||
@@ -495,9 +595,6 @@ error:
|
||||
error_out2:
|
||||
dput(dir);
|
||||
error_out:
|
||||
if (ret == -ENOSPC)
|
||||
ret = -ENOBUFS;
|
||||
|
||||
_leave(" = error %d", -ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
+116
-12
@@ -40,8 +40,10 @@ static int cachefiles_read_waiter(wait_queue_t *wait, unsigned mode,
|
||||
|
||||
_debug("--- monitor %p %lx ---", page, page->flags);
|
||||
|
||||
if (!PageUptodate(page) && !PageError(page))
|
||||
dump_stack();
|
||||
if (!PageUptodate(page) && !PageError(page)) {
|
||||
/* unlocked, not uptodate and not erronous? */
|
||||
_debug("page probably truncated");
|
||||
}
|
||||
|
||||
/* remove from the waitqueue */
|
||||
list_del(&wait->task_list);
|
||||
@@ -60,6 +62,84 @@ static int cachefiles_read_waiter(wait_queue_t *wait, unsigned mode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle a probably truncated page
|
||||
* - check to see if the page is still relevant and reissue the read if
|
||||
* possible
|
||||
* - return -EIO on error, -ENODATA if the page is gone, -EINPROGRESS if we
|
||||
* must wait again and 0 if successful
|
||||
*/
|
||||
static int cachefiles_read_reissue(struct cachefiles_object *object,
|
||||
struct cachefiles_one_read *monitor)
|
||||
{
|
||||
struct address_space *bmapping = object->backer->d_inode->i_mapping;
|
||||
struct page *backpage = monitor->back_page, *backpage2;
|
||||
int ret;
|
||||
|
||||
kenter("{ino=%lx},{%lx,%lx}",
|
||||
object->backer->d_inode->i_ino,
|
||||
backpage->index, backpage->flags);
|
||||
|
||||
/* skip if the page was truncated away completely */
|
||||
if (backpage->mapping != bmapping) {
|
||||
kleave(" = -ENODATA [mapping]");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
backpage2 = find_get_page(bmapping, backpage->index);
|
||||
if (!backpage2) {
|
||||
kleave(" = -ENODATA [gone]");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
if (backpage != backpage2) {
|
||||
put_page(backpage2);
|
||||
kleave(" = -ENODATA [different]");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
/* the page is still there and we already have a ref on it, so we don't
|
||||
* need a second */
|
||||
put_page(backpage2);
|
||||
|
||||
INIT_LIST_HEAD(&monitor->op_link);
|
||||
add_page_wait_queue(backpage, &monitor->monitor);
|
||||
|
||||
if (trylock_page(backpage)) {
|
||||
ret = -EIO;
|
||||
if (PageError(backpage))
|
||||
goto unlock_discard;
|
||||
ret = 0;
|
||||
if (PageUptodate(backpage))
|
||||
goto unlock_discard;
|
||||
|
||||
kdebug("reissue read");
|
||||
ret = bmapping->a_ops->readpage(NULL, backpage);
|
||||
if (ret < 0)
|
||||
goto unlock_discard;
|
||||
}
|
||||
|
||||
/* but the page may have been read before the monitor was installed, so
|
||||
* the monitor may miss the event - so we have to ensure that we do get
|
||||
* one in such a case */
|
||||
if (trylock_page(backpage)) {
|
||||
_debug("jumpstart %p {%lx}", backpage, backpage->flags);
|
||||
unlock_page(backpage);
|
||||
}
|
||||
|
||||
/* it'll reappear on the todo list */
|
||||
kleave(" = -EINPROGRESS");
|
||||
return -EINPROGRESS;
|
||||
|
||||
unlock_discard:
|
||||
unlock_page(backpage);
|
||||
spin_lock_irq(&object->work_lock);
|
||||
list_del(&monitor->op_link);
|
||||
spin_unlock_irq(&object->work_lock);
|
||||
kleave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* copy data from backing pages to netfs pages to complete a read operation
|
||||
* - driven by FS-Cache's thread pool
|
||||
@@ -92,20 +172,26 @@ static void cachefiles_read_copier(struct fscache_operation *_op)
|
||||
|
||||
_debug("- copy {%lu}", monitor->back_page->index);
|
||||
|
||||
error = -EIO;
|
||||
recheck:
|
||||
if (PageUptodate(monitor->back_page)) {
|
||||
copy_highpage(monitor->netfs_page, monitor->back_page);
|
||||
|
||||
pagevec_add(&pagevec, monitor->netfs_page);
|
||||
fscache_mark_pages_cached(monitor->op, &pagevec);
|
||||
error = 0;
|
||||
}
|
||||
|
||||
if (error)
|
||||
} else if (!PageError(monitor->back_page)) {
|
||||
/* the page has probably been truncated */
|
||||
error = cachefiles_read_reissue(object, monitor);
|
||||
if (error == -EINPROGRESS)
|
||||
goto next;
|
||||
goto recheck;
|
||||
} else {
|
||||
cachefiles_io_error_obj(
|
||||
object,
|
||||
"Readpage failed on backing file %lx",
|
||||
(unsigned long) monitor->back_page->flags);
|
||||
error = -EIO;
|
||||
}
|
||||
|
||||
page_cache_release(monitor->back_page);
|
||||
|
||||
@@ -114,6 +200,7 @@ static void cachefiles_read_copier(struct fscache_operation *_op)
|
||||
fscache_put_retrieval(op);
|
||||
kfree(monitor);
|
||||
|
||||
next:
|
||||
/* let the thread pool have some air occasionally */
|
||||
max--;
|
||||
if (max < 0 || need_resched()) {
|
||||
@@ -333,7 +420,8 @@ int cachefiles_read_or_alloc_page(struct fscache_retrieval *op,
|
||||
|
||||
shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits;
|
||||
|
||||
op->op.flags = FSCACHE_OP_FAST;
|
||||
op->op.flags &= FSCACHE_OP_KEEP_FLAGS;
|
||||
op->op.flags |= FSCACHE_OP_FAST;
|
||||
op->op.processor = cachefiles_read_copier;
|
||||
|
||||
pagevec_init(&pagevec, 0);
|
||||
@@ -639,7 +727,8 @@ int cachefiles_read_or_alloc_pages(struct fscache_retrieval *op,
|
||||
|
||||
pagevec_init(&pagevec, 0);
|
||||
|
||||
op->op.flags = FSCACHE_OP_FAST;
|
||||
op->op.flags &= FSCACHE_OP_KEEP_FLAGS;
|
||||
op->op.flags |= FSCACHE_OP_FAST;
|
||||
op->op.processor = cachefiles_read_copier;
|
||||
|
||||
INIT_LIST_HEAD(&backpages);
|
||||
@@ -801,7 +890,8 @@ int cachefiles_write_page(struct fscache_storage *op, struct page *page)
|
||||
struct cachefiles_cache *cache;
|
||||
mm_segment_t old_fs;
|
||||
struct file *file;
|
||||
loff_t pos;
|
||||
loff_t pos, eof;
|
||||
size_t len;
|
||||
void *data;
|
||||
int ret;
|
||||
|
||||
@@ -835,15 +925,29 @@ int cachefiles_write_page(struct fscache_storage *op, struct page *page)
|
||||
ret = -EIO;
|
||||
if (file->f_op->write) {
|
||||
pos = (loff_t) page->index << PAGE_SHIFT;
|
||||
|
||||
/* we mustn't write more data than we have, so we have
|
||||
* to beware of a partial page at EOF */
|
||||
eof = object->fscache.store_limit_l;
|
||||
len = PAGE_SIZE;
|
||||
if (eof & ~PAGE_MASK) {
|
||||
ASSERTCMP(pos, <, eof);
|
||||
if (eof - pos < PAGE_SIZE) {
|
||||
_debug("cut short %llx to %llx",
|
||||
pos, eof);
|
||||
len = eof - pos;
|
||||
ASSERTCMP(pos + len, ==, eof);
|
||||
}
|
||||
}
|
||||
|
||||
data = kmap(page);
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
ret = file->f_op->write(
|
||||
file, (const void __user *) data, PAGE_SIZE,
|
||||
&pos);
|
||||
file, (const void __user *) data, len, &pos);
|
||||
set_fs(old_fs);
|
||||
kunmap(page);
|
||||
if (ret != PAGE_SIZE)
|
||||
if (ret != len)
|
||||
ret = -EIO;
|
||||
}
|
||||
fput(file);
|
||||
|
||||
+1
-1
@@ -1037,7 +1037,7 @@ init_cifs(void)
|
||||
if (rc)
|
||||
goto out_unregister_key_type;
|
||||
#endif
|
||||
rc = slow_work_register_user();
|
||||
rc = slow_work_register_user(THIS_MODULE);
|
||||
if (rc)
|
||||
goto out_unregister_resolver_key;
|
||||
|
||||
|
||||
@@ -54,3 +54,10 @@ config FSCACHE_DEBUG
|
||||
enabled by setting bits in /sys/modules/fscache/parameter/debug.
|
||||
|
||||
See Documentation/filesystems/caching/fscache.txt for more information.
|
||||
|
||||
config FSCACHE_OBJECT_LIST
|
||||
bool "Maintain global object list for debugging purposes"
|
||||
depends on FSCACHE && PROC_FS
|
||||
help
|
||||
Maintain a global list of active fscache objects that can be
|
||||
retrieved through /proc/fs/fscache/objects for debugging purposes
|
||||
|
||||
@@ -15,5 +15,6 @@ fscache-y := \
|
||||
fscache-$(CONFIG_PROC_FS) += proc.o
|
||||
fscache-$(CONFIG_FSCACHE_STATS) += stats.o
|
||||
fscache-$(CONFIG_FSCACHE_HISTOGRAM) += histogram.o
|
||||
fscache-$(CONFIG_FSCACHE_OBJECT_LIST) += object-list.o
|
||||
|
||||
obj-$(CONFIG_FSCACHE) := fscache.o
|
||||
|
||||
@@ -263,6 +263,7 @@ int fscache_add_cache(struct fscache_cache *cache,
|
||||
spin_lock(&cache->object_list_lock);
|
||||
list_add_tail(&ifsdef->cache_link, &cache->object_list);
|
||||
spin_unlock(&cache->object_list_lock);
|
||||
fscache_objlist_add(ifsdef);
|
||||
|
||||
/* add the cache's netfs definition index object to the top level index
|
||||
* cookie as a known backing object */
|
||||
@@ -380,11 +381,15 @@ void fscache_withdraw_cache(struct fscache_cache *cache)
|
||||
|
||||
/* make sure all pages pinned by operations on behalf of the netfs are
|
||||
* written to disk */
|
||||
fscache_stat(&fscache_n_cop_sync_cache);
|
||||
cache->ops->sync_cache(cache);
|
||||
fscache_stat_d(&fscache_n_cop_sync_cache);
|
||||
|
||||
/* dissociate all the netfs pages backed by this cache from the block
|
||||
* mappings in the cache */
|
||||
fscache_stat(&fscache_n_cop_dissociate_pages);
|
||||
cache->ops->dissociate_pages(cache);
|
||||
fscache_stat_d(&fscache_n_cop_dissociate_pages);
|
||||
|
||||
/* we now have to destroy all the active objects pertaining to this
|
||||
* cache - which we do by passing them off to thread pool to be
|
||||
|
||||
+20
-6
@@ -36,6 +36,7 @@ void fscache_cookie_init_once(void *_cookie)
|
||||
|
||||
memset(cookie, 0, sizeof(*cookie));
|
||||
spin_lock_init(&cookie->lock);
|
||||
spin_lock_init(&cookie->stores_lock);
|
||||
INIT_HLIST_HEAD(&cookie->backing_objects);
|
||||
}
|
||||
|
||||
@@ -102,7 +103,9 @@ struct fscache_cookie *__fscache_acquire_cookie(
|
||||
cookie->netfs_data = netfs_data;
|
||||
cookie->flags = 0;
|
||||
|
||||
INIT_RADIX_TREE(&cookie->stores, GFP_NOFS);
|
||||
/* radix tree insertion won't use the preallocation pool unless it's
|
||||
* told it may not wait */
|
||||
INIT_RADIX_TREE(&cookie->stores, GFP_NOFS & ~__GFP_WAIT);
|
||||
|
||||
switch (cookie->def->type) {
|
||||
case FSCACHE_COOKIE_TYPE_INDEX:
|
||||
@@ -249,7 +252,9 @@ static int fscache_alloc_object(struct fscache_cache *cache,
|
||||
|
||||
/* ask the cache to allocate an object (we may end up with duplicate
|
||||
* objects at this stage, but we sort that out later) */
|
||||
fscache_stat(&fscache_n_cop_alloc_object);
|
||||
object = cache->ops->alloc_object(cache, cookie);
|
||||
fscache_stat_d(&fscache_n_cop_alloc_object);
|
||||
if (IS_ERR(object)) {
|
||||
fscache_stat(&fscache_n_object_no_alloc);
|
||||
ret = PTR_ERR(object);
|
||||
@@ -270,8 +275,11 @@ static int fscache_alloc_object(struct fscache_cache *cache,
|
||||
/* only attach if we managed to allocate all we needed, otherwise
|
||||
* discard the object we just allocated and instead use the one
|
||||
* attached to the cookie */
|
||||
if (fscache_attach_object(cookie, object) < 0)
|
||||
if (fscache_attach_object(cookie, object) < 0) {
|
||||
fscache_stat(&fscache_n_cop_put_object);
|
||||
cache->ops->put_object(object);
|
||||
fscache_stat_d(&fscache_n_cop_put_object);
|
||||
}
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
@@ -287,7 +295,9 @@ object_already_extant:
|
||||
return 0;
|
||||
|
||||
error_put:
|
||||
fscache_stat(&fscache_n_cop_put_object);
|
||||
cache->ops->put_object(object);
|
||||
fscache_stat_d(&fscache_n_cop_put_object);
|
||||
error:
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
@@ -349,6 +359,8 @@ static int fscache_attach_object(struct fscache_cookie *cookie,
|
||||
object->cookie = cookie;
|
||||
atomic_inc(&cookie->usage);
|
||||
hlist_add_head(&object->cookie_link, &cookie->backing_objects);
|
||||
|
||||
fscache_objlist_add(object);
|
||||
ret = 0;
|
||||
|
||||
cant_attach_object:
|
||||
@@ -403,6 +415,8 @@ void __fscache_relinquish_cookie(struct fscache_cookie *cookie, int retire)
|
||||
unsigned long event;
|
||||
|
||||
fscache_stat(&fscache_n_relinquishes);
|
||||
if (retire)
|
||||
fscache_stat(&fscache_n_relinquishes_retire);
|
||||
|
||||
if (!cookie) {
|
||||
fscache_stat(&fscache_n_relinquishes_null);
|
||||
@@ -428,12 +442,8 @@ void __fscache_relinquish_cookie(struct fscache_cookie *cookie, int retire)
|
||||
|
||||
event = retire ? FSCACHE_OBJECT_EV_RETIRE : FSCACHE_OBJECT_EV_RELEASE;
|
||||
|
||||
/* detach pointers back to the netfs */
|
||||
spin_lock(&cookie->lock);
|
||||
|
||||
cookie->netfs_data = NULL;
|
||||
cookie->def = NULL;
|
||||
|
||||
/* break links with all the active objects */
|
||||
while (!hlist_empty(&cookie->backing_objects)) {
|
||||
object = hlist_entry(cookie->backing_objects.first,
|
||||
@@ -456,6 +466,10 @@ void __fscache_relinquish_cookie(struct fscache_cookie *cookie, int retire)
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* detach pointers back to the netfs */
|
||||
cookie->netfs_data = NULL;
|
||||
cookie->def = NULL;
|
||||
|
||||
spin_unlock(&cookie->lock);
|
||||
|
||||
if (cookie->parent) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* - cache->object_list_lock
|
||||
* - object->lock
|
||||
* - object->parent->lock
|
||||
* - cookie->stores_lock
|
||||
* - fscache_thread_lock
|
||||
*
|
||||
*/
|
||||
@@ -88,10 +89,23 @@ extern int fscache_wait_bit_interruptible(void *);
|
||||
/*
|
||||
* object.c
|
||||
*/
|
||||
extern const char fscache_object_states_short[FSCACHE_OBJECT__NSTATES][5];
|
||||
|
||||
extern void fscache_withdrawing_object(struct fscache_cache *,
|
||||
struct fscache_object *);
|
||||
extern void fscache_enqueue_object(struct fscache_object *);
|
||||
|
||||
/*
|
||||
* object-list.c
|
||||
*/
|
||||
#ifdef CONFIG_FSCACHE_OBJECT_LIST
|
||||
extern const struct file_operations fscache_objlist_fops;
|
||||
|
||||
extern void fscache_objlist_add(struct fscache_object *);
|
||||
#else
|
||||
#define fscache_objlist_add(object) do {} while(0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* operation.c
|
||||
*/
|
||||
@@ -99,6 +113,7 @@ extern int fscache_submit_exclusive_op(struct fscache_object *,
|
||||
struct fscache_operation *);
|
||||
extern int fscache_submit_op(struct fscache_object *,
|
||||
struct fscache_operation *);
|
||||
extern int fscache_cancel_op(struct fscache_operation *);
|
||||
extern void fscache_abort_object(struct fscache_object *);
|
||||
extern void fscache_start_operations(struct fscache_object *);
|
||||
extern void fscache_operation_gc(struct work_struct *);
|
||||
@@ -127,6 +142,8 @@ extern atomic_t fscache_n_op_enqueue;
|
||||
extern atomic_t fscache_n_op_deferred_release;
|
||||
extern atomic_t fscache_n_op_release;
|
||||
extern atomic_t fscache_n_op_gc;
|
||||
extern atomic_t fscache_n_op_cancelled;
|
||||
extern atomic_t fscache_n_op_rejected;
|
||||
|
||||
extern atomic_t fscache_n_attr_changed;
|
||||
extern atomic_t fscache_n_attr_changed_ok;
|
||||
@@ -138,6 +155,8 @@ extern atomic_t fscache_n_allocs;
|
||||
extern atomic_t fscache_n_allocs_ok;
|
||||
extern atomic_t fscache_n_allocs_wait;
|
||||
extern atomic_t fscache_n_allocs_nobufs;
|
||||
extern atomic_t fscache_n_allocs_intr;
|
||||
extern atomic_t fscache_n_allocs_object_dead;
|
||||
extern atomic_t fscache_n_alloc_ops;
|
||||
extern atomic_t fscache_n_alloc_op_waits;
|
||||
|
||||
@@ -148,6 +167,7 @@ extern atomic_t fscache_n_retrievals_nodata;
|
||||
extern atomic_t fscache_n_retrievals_nobufs;
|
||||
extern atomic_t fscache_n_retrievals_intr;
|
||||
extern atomic_t fscache_n_retrievals_nomem;
|
||||
extern atomic_t fscache_n_retrievals_object_dead;
|
||||
extern atomic_t fscache_n_retrieval_ops;
|
||||
extern atomic_t fscache_n_retrieval_op_waits;
|
||||
|
||||
@@ -158,6 +178,14 @@ extern atomic_t fscache_n_stores_nobufs;
|
||||
extern atomic_t fscache_n_stores_oom;
|
||||
extern atomic_t fscache_n_store_ops;
|
||||
extern atomic_t fscache_n_store_calls;
|
||||
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;
|
||||
@@ -176,6 +204,7 @@ extern atomic_t fscache_n_updates_run;
|
||||
extern atomic_t fscache_n_relinquishes;
|
||||
extern atomic_t fscache_n_relinquishes_null;
|
||||
extern atomic_t fscache_n_relinquishes_waitcrt;
|
||||
extern atomic_t fscache_n_relinquishes_retire;
|
||||
|
||||
extern atomic_t fscache_n_cookie_index;
|
||||
extern atomic_t fscache_n_cookie_data;
|
||||
@@ -186,6 +215,7 @@ extern atomic_t fscache_n_object_no_alloc;
|
||||
extern atomic_t fscache_n_object_lookups;
|
||||
extern atomic_t fscache_n_object_lookups_negative;
|
||||
extern atomic_t fscache_n_object_lookups_positive;
|
||||
extern atomic_t fscache_n_object_lookups_timed_out;
|
||||
extern atomic_t fscache_n_object_created;
|
||||
extern atomic_t fscache_n_object_avail;
|
||||
extern atomic_t fscache_n_object_dead;
|
||||
@@ -195,15 +225,41 @@ extern atomic_t fscache_n_checkaux_okay;
|
||||
extern atomic_t fscache_n_checkaux_update;
|
||||
extern atomic_t fscache_n_checkaux_obsolete;
|
||||
|
||||
extern atomic_t fscache_n_cop_alloc_object;
|
||||
extern atomic_t fscache_n_cop_lookup_object;
|
||||
extern atomic_t fscache_n_cop_lookup_complete;
|
||||
extern atomic_t fscache_n_cop_grab_object;
|
||||
extern atomic_t fscache_n_cop_update_object;
|
||||
extern atomic_t fscache_n_cop_drop_object;
|
||||
extern atomic_t fscache_n_cop_put_object;
|
||||
extern atomic_t fscache_n_cop_sync_cache;
|
||||
extern atomic_t fscache_n_cop_attr_changed;
|
||||
extern atomic_t fscache_n_cop_read_or_alloc_page;
|
||||
extern atomic_t fscache_n_cop_read_or_alloc_pages;
|
||||
extern atomic_t fscache_n_cop_allocate_page;
|
||||
extern atomic_t fscache_n_cop_allocate_pages;
|
||||
extern atomic_t fscache_n_cop_write_page;
|
||||
extern atomic_t fscache_n_cop_uncache_page;
|
||||
extern atomic_t fscache_n_cop_dissociate_pages;
|
||||
|
||||
static inline void fscache_stat(atomic_t *stat)
|
||||
{
|
||||
atomic_inc(stat);
|
||||
}
|
||||
|
||||
static inline void fscache_stat_d(atomic_t *stat)
|
||||
{
|
||||
atomic_dec(stat);
|
||||
}
|
||||
|
||||
#define __fscache_stat(stat) (stat)
|
||||
|
||||
extern const struct file_operations fscache_stats_fops;
|
||||
#else
|
||||
|
||||
#define __fscache_stat(stat) (NULL)
|
||||
#define fscache_stat(stat) do {} while (0)
|
||||
#define fscache_stat_d(stat) do {} while (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||
+3
-3
@@ -48,7 +48,7 @@ static int __init fscache_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = slow_work_register_user();
|
||||
ret = slow_work_register_user(THIS_MODULE);
|
||||
if (ret < 0)
|
||||
goto error_slow_work;
|
||||
|
||||
@@ -80,7 +80,7 @@ error_kobj:
|
||||
error_cookie_jar:
|
||||
fscache_proc_cleanup();
|
||||
error_proc:
|
||||
slow_work_unregister_user();
|
||||
slow_work_unregister_user(THIS_MODULE);
|
||||
error_slow_work:
|
||||
return ret;
|
||||
}
|
||||
@@ -97,7 +97,7 @@ static void __exit fscache_exit(void)
|
||||
kobject_put(fscache_root);
|
||||
kmem_cache_destroy(fscache_cookie_jar);
|
||||
fscache_proc_cleanup();
|
||||
slow_work_unregister_user();
|
||||
slow_work_unregister_user(THIS_MODULE);
|
||||
printk(KERN_NOTICE "FS-Cache: Unloaded\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,432 @@
|
||||
/* Global fscache object list maintainer and viewer
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#define FSCACHE_DEBUG_LEVEL COOKIE
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/key.h>
|
||||
#include <keys/user-type.h>
|
||||
#include "internal.h"
|
||||
|
||||
static struct rb_root fscache_object_list;
|
||||
static DEFINE_RWLOCK(fscache_object_list_lock);
|
||||
|
||||
struct fscache_objlist_data {
|
||||
unsigned long config; /* display configuration */
|
||||
#define FSCACHE_OBJLIST_CONFIG_KEY 0x00000001 /* show object keys */
|
||||
#define FSCACHE_OBJLIST_CONFIG_AUX 0x00000002 /* show object auxdata */
|
||||
#define FSCACHE_OBJLIST_CONFIG_COOKIE 0x00000004 /* show objects with cookies */
|
||||
#define FSCACHE_OBJLIST_CONFIG_NOCOOKIE 0x00000008 /* show objects without cookies */
|
||||
#define FSCACHE_OBJLIST_CONFIG_BUSY 0x00000010 /* show busy objects */
|
||||
#define FSCACHE_OBJLIST_CONFIG_IDLE 0x00000020 /* show idle objects */
|
||||
#define FSCACHE_OBJLIST_CONFIG_PENDWR 0x00000040 /* show objects with pending writes */
|
||||
#define FSCACHE_OBJLIST_CONFIG_NOPENDWR 0x00000080 /* show objects without pending writes */
|
||||
#define FSCACHE_OBJLIST_CONFIG_READS 0x00000100 /* show objects with active reads */
|
||||
#define FSCACHE_OBJLIST_CONFIG_NOREADS 0x00000200 /* show objects without active reads */
|
||||
#define FSCACHE_OBJLIST_CONFIG_EVENTS 0x00000400 /* show objects with events */
|
||||
#define FSCACHE_OBJLIST_CONFIG_NOEVENTS 0x00000800 /* show objects without no events */
|
||||
#define FSCACHE_OBJLIST_CONFIG_WORK 0x00001000 /* show objects with slow work */
|
||||
#define FSCACHE_OBJLIST_CONFIG_NOWORK 0x00002000 /* show objects without slow work */
|
||||
|
||||
u8 buf[512]; /* key and aux data buffer */
|
||||
};
|
||||
|
||||
/*
|
||||
* Add an object to the object list
|
||||
* - we use the address of the fscache_object structure as the key into the
|
||||
* tree
|
||||
*/
|
||||
void fscache_objlist_add(struct fscache_object *obj)
|
||||
{
|
||||
struct fscache_object *xobj;
|
||||
struct rb_node **p = &fscache_object_list.rb_node, *parent = NULL;
|
||||
|
||||
write_lock(&fscache_object_list_lock);
|
||||
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
xobj = rb_entry(parent, struct fscache_object, objlist_link);
|
||||
|
||||
if (obj < xobj)
|
||||
p = &(*p)->rb_left;
|
||||
else if (obj > xobj)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
BUG();
|
||||
}
|
||||
|
||||
rb_link_node(&obj->objlist_link, parent, p);
|
||||
rb_insert_color(&obj->objlist_link, &fscache_object_list);
|
||||
|
||||
write_unlock(&fscache_object_list_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* fscache_object_destroy - Note that a cache object is about to be destroyed
|
||||
* @object: The object to be destroyed
|
||||
*
|
||||
* Note the imminent destruction and deallocation of a cache object record.
|
||||
*/
|
||||
void fscache_object_destroy(struct fscache_object *obj)
|
||||
{
|
||||
write_lock(&fscache_object_list_lock);
|
||||
|
||||
BUG_ON(RB_EMPTY_ROOT(&fscache_object_list));
|
||||
rb_erase(&obj->objlist_link, &fscache_object_list);
|
||||
|
||||
write_unlock(&fscache_object_list_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(fscache_object_destroy);
|
||||
|
||||
/*
|
||||
* find the object in the tree on or after the specified index
|
||||
*/
|
||||
static struct fscache_object *fscache_objlist_lookup(loff_t *_pos)
|
||||
{
|
||||
struct fscache_object *pobj, *obj, *minobj = NULL;
|
||||
struct rb_node *p;
|
||||
unsigned long pos;
|
||||
|
||||
if (*_pos >= (unsigned long) ERR_PTR(-ENOENT))
|
||||
return NULL;
|
||||
pos = *_pos;
|
||||
|
||||
/* banners (can't represent line 0 by pos 0 as that would involve
|
||||
* returning a NULL pointer) */
|
||||
if (pos == 0)
|
||||
return (struct fscache_object *) ++(*_pos);
|
||||
if (pos < 3)
|
||||
return (struct fscache_object *)pos;
|
||||
|
||||
pobj = (struct fscache_object *)pos;
|
||||
p = fscache_object_list.rb_node;
|
||||
while (p) {
|
||||
obj = rb_entry(p, struct fscache_object, objlist_link);
|
||||
if (pobj < obj) {
|
||||
if (!minobj || minobj > obj)
|
||||
minobj = obj;
|
||||
p = p->rb_left;
|
||||
} else if (pobj > obj) {
|
||||
p = p->rb_right;
|
||||
} else {
|
||||
minobj = obj;
|
||||
break;
|
||||
}
|
||||
obj = NULL;
|
||||
}
|
||||
|
||||
if (!minobj)
|
||||
*_pos = (unsigned long) ERR_PTR(-ENOENT);
|
||||
else if (minobj != obj)
|
||||
*_pos = (unsigned long) minobj;
|
||||
return minobj;
|
||||
}
|
||||
|
||||
/*
|
||||
* set up the iterator to start reading from the first line
|
||||
*/
|
||||
static void *fscache_objlist_start(struct seq_file *m, loff_t *_pos)
|
||||
__acquires(&fscache_object_list_lock)
|
||||
{
|
||||
read_lock(&fscache_object_list_lock);
|
||||
return fscache_objlist_lookup(_pos);
|
||||
}
|
||||
|
||||
/*
|
||||
* move to the next line
|
||||
*/
|
||||
static void *fscache_objlist_next(struct seq_file *m, void *v, loff_t *_pos)
|
||||
{
|
||||
(*_pos)++;
|
||||
return fscache_objlist_lookup(_pos);
|
||||
}
|
||||
|
||||
/*
|
||||
* clean up after reading
|
||||
*/
|
||||
static void fscache_objlist_stop(struct seq_file *m, void *v)
|
||||
__releases(&fscache_object_list_lock)
|
||||
{
|
||||
read_unlock(&fscache_object_list_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* display an object
|
||||
*/
|
||||
static int fscache_objlist_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct fscache_objlist_data *data = m->private;
|
||||
struct fscache_object *obj = v;
|
||||
unsigned long config = data->config;
|
||||
uint16_t keylen, auxlen;
|
||||
char _type[3], *type;
|
||||
bool no_cookie;
|
||||
u8 *buf = data->buf, *p;
|
||||
|
||||
if ((unsigned long) v == 1) {
|
||||
seq_puts(m, "OBJECT PARENT STAT CHLDN OPS OOP IPR EX READS"
|
||||
" EM EV F S"
|
||||
" | NETFS_COOKIE_DEF TY FL NETFS_DATA");
|
||||
if (config & (FSCACHE_OBJLIST_CONFIG_KEY |
|
||||
FSCACHE_OBJLIST_CONFIG_AUX))
|
||||
seq_puts(m, " ");
|
||||
if (config & FSCACHE_OBJLIST_CONFIG_KEY)
|
||||
seq_puts(m, "OBJECT_KEY");
|
||||
if ((config & (FSCACHE_OBJLIST_CONFIG_KEY |
|
||||
FSCACHE_OBJLIST_CONFIG_AUX)) ==
|
||||
(FSCACHE_OBJLIST_CONFIG_KEY | FSCACHE_OBJLIST_CONFIG_AUX))
|
||||
seq_puts(m, ", ");
|
||||
if (config & FSCACHE_OBJLIST_CONFIG_AUX)
|
||||
seq_puts(m, "AUX_DATA");
|
||||
seq_puts(m, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((unsigned long) v == 2) {
|
||||
seq_puts(m, "======== ======== ==== ===== === === === == ====="
|
||||
" == == = ="
|
||||
" | ================ == == ================");
|
||||
if (config & (FSCACHE_OBJLIST_CONFIG_KEY |
|
||||
FSCACHE_OBJLIST_CONFIG_AUX))
|
||||
seq_puts(m, " ================");
|
||||
seq_puts(m, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* filter out any unwanted objects */
|
||||
#define FILTER(criterion, _yes, _no) \
|
||||
do { \
|
||||
unsigned long yes = FSCACHE_OBJLIST_CONFIG_##_yes; \
|
||||
unsigned long no = FSCACHE_OBJLIST_CONFIG_##_no; \
|
||||
if (criterion) { \
|
||||
if (!(config & yes)) \
|
||||
return 0; \
|
||||
} else { \
|
||||
if (!(config & no)) \
|
||||
return 0; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
if (~config) {
|
||||
FILTER(obj->cookie,
|
||||
COOKIE, NOCOOKIE);
|
||||
FILTER(obj->state != FSCACHE_OBJECT_ACTIVE ||
|
||||
obj->n_ops != 0 ||
|
||||
obj->n_obj_ops != 0 ||
|
||||
obj->flags ||
|
||||
!list_empty(&obj->dependents),
|
||||
BUSY, IDLE);
|
||||
FILTER(test_bit(FSCACHE_OBJECT_PENDING_WRITE, &obj->flags),
|
||||
PENDWR, NOPENDWR);
|
||||
FILTER(atomic_read(&obj->n_reads),
|
||||
READS, NOREADS);
|
||||
FILTER(obj->events & obj->event_mask,
|
||||
EVENTS, NOEVENTS);
|
||||
FILTER(obj->work.flags & ~(1UL << SLOW_WORK_VERY_SLOW),
|
||||
WORK, NOWORK);
|
||||
}
|
||||
|
||||
seq_printf(m,
|
||||
"%8x %8x %s %5u %3u %3u %3u %2u %5u %2lx %2lx %1lx %1lx | ",
|
||||
obj->debug_id,
|
||||
obj->parent ? obj->parent->debug_id : -1,
|
||||
fscache_object_states_short[obj->state],
|
||||
obj->n_children,
|
||||
obj->n_ops,
|
||||
obj->n_obj_ops,
|
||||
obj->n_in_progress,
|
||||
obj->n_exclusive,
|
||||
atomic_read(&obj->n_reads),
|
||||
obj->event_mask & FSCACHE_OBJECT_EVENTS_MASK,
|
||||
obj->events,
|
||||
obj->flags,
|
||||
obj->work.flags);
|
||||
|
||||
no_cookie = true;
|
||||
keylen = auxlen = 0;
|
||||
if (obj->cookie) {
|
||||
spin_lock(&obj->lock);
|
||||
if (obj->cookie) {
|
||||
switch (obj->cookie->def->type) {
|
||||
case 0:
|
||||
type = "IX";
|
||||
break;
|
||||
case 1:
|
||||
type = "DT";
|
||||
break;
|
||||
default:
|
||||
sprintf(_type, "%02u",
|
||||
obj->cookie->def->type);
|
||||
type = _type;
|
||||
break;
|
||||
}
|
||||
|
||||
seq_printf(m, "%-16s %s %2lx %16p",
|
||||
obj->cookie->def->name,
|
||||
type,
|
||||
obj->cookie->flags,
|
||||
obj->cookie->netfs_data);
|
||||
|
||||
if (obj->cookie->def->get_key &&
|
||||
config & FSCACHE_OBJLIST_CONFIG_KEY)
|
||||
keylen = obj->cookie->def->get_key(
|
||||
obj->cookie->netfs_data,
|
||||
buf, 400);
|
||||
|
||||
if (obj->cookie->def->get_aux &&
|
||||
config & FSCACHE_OBJLIST_CONFIG_AUX)
|
||||
auxlen = obj->cookie->def->get_aux(
|
||||
obj->cookie->netfs_data,
|
||||
buf + keylen, 512 - keylen);
|
||||
|
||||
no_cookie = false;
|
||||
}
|
||||
spin_unlock(&obj->lock);
|
||||
|
||||
if (!no_cookie && (keylen > 0 || auxlen > 0)) {
|
||||
seq_printf(m, " ");
|
||||
for (p = buf; keylen > 0; keylen--)
|
||||
seq_printf(m, "%02x", *p++);
|
||||
if (auxlen > 0) {
|
||||
if (config & FSCACHE_OBJLIST_CONFIG_KEY)
|
||||
seq_printf(m, ", ");
|
||||
for (; auxlen > 0; auxlen--)
|
||||
seq_printf(m, "%02x", *p++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (no_cookie)
|
||||
seq_printf(m, "<no_cookie>\n");
|
||||
else
|
||||
seq_printf(m, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations fscache_objlist_ops = {
|
||||
.start = fscache_objlist_start,
|
||||
.stop = fscache_objlist_stop,
|
||||
.next = fscache_objlist_next,
|
||||
.show = fscache_objlist_show,
|
||||
};
|
||||
|
||||
/*
|
||||
* get the configuration for filtering the list
|
||||
*/
|
||||
static void fscache_objlist_config(struct fscache_objlist_data *data)
|
||||
{
|
||||
#ifdef CONFIG_KEYS
|
||||
struct user_key_payload *confkey;
|
||||
unsigned long config;
|
||||
struct key *key;
|
||||
const char *buf;
|
||||
int len;
|
||||
|
||||
key = request_key(&key_type_user, "fscache:objlist", NULL);
|
||||
if (IS_ERR(key))
|
||||
goto no_config;
|
||||
|
||||
config = 0;
|
||||
rcu_read_lock();
|
||||
|
||||
confkey = key->payload.data;
|
||||
buf = confkey->data;
|
||||
|
||||
for (len = confkey->datalen - 1; len >= 0; len--) {
|
||||
switch (buf[len]) {
|
||||
case 'K': config |= FSCACHE_OBJLIST_CONFIG_KEY; break;
|
||||
case 'A': config |= FSCACHE_OBJLIST_CONFIG_AUX; break;
|
||||
case 'C': config |= FSCACHE_OBJLIST_CONFIG_COOKIE; break;
|
||||
case 'c': config |= FSCACHE_OBJLIST_CONFIG_NOCOOKIE; break;
|
||||
case 'B': config |= FSCACHE_OBJLIST_CONFIG_BUSY; break;
|
||||
case 'b': config |= FSCACHE_OBJLIST_CONFIG_IDLE; break;
|
||||
case 'W': config |= FSCACHE_OBJLIST_CONFIG_PENDWR; break;
|
||||
case 'w': config |= FSCACHE_OBJLIST_CONFIG_NOPENDWR; break;
|
||||
case 'R': config |= FSCACHE_OBJLIST_CONFIG_READS; break;
|
||||
case 'r': config |= FSCACHE_OBJLIST_CONFIG_NOREADS; break;
|
||||
case 'S': config |= FSCACHE_OBJLIST_CONFIG_WORK; break;
|
||||
case 's': config |= FSCACHE_OBJLIST_CONFIG_NOWORK; break;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
key_put(key);
|
||||
|
||||
if (!(config & (FSCACHE_OBJLIST_CONFIG_COOKIE | FSCACHE_OBJLIST_CONFIG_NOCOOKIE)))
|
||||
config |= FSCACHE_OBJLIST_CONFIG_COOKIE | FSCACHE_OBJLIST_CONFIG_NOCOOKIE;
|
||||
if (!(config & (FSCACHE_OBJLIST_CONFIG_BUSY | FSCACHE_OBJLIST_CONFIG_IDLE)))
|
||||
config |= FSCACHE_OBJLIST_CONFIG_BUSY | FSCACHE_OBJLIST_CONFIG_IDLE;
|
||||
if (!(config & (FSCACHE_OBJLIST_CONFIG_PENDWR | FSCACHE_OBJLIST_CONFIG_NOPENDWR)))
|
||||
config |= FSCACHE_OBJLIST_CONFIG_PENDWR | FSCACHE_OBJLIST_CONFIG_NOPENDWR;
|
||||
if (!(config & (FSCACHE_OBJLIST_CONFIG_READS | FSCACHE_OBJLIST_CONFIG_NOREADS)))
|
||||
config |= FSCACHE_OBJLIST_CONFIG_READS | FSCACHE_OBJLIST_CONFIG_NOREADS;
|
||||
if (!(config & (FSCACHE_OBJLIST_CONFIG_EVENTS | FSCACHE_OBJLIST_CONFIG_NOEVENTS)))
|
||||
config |= FSCACHE_OBJLIST_CONFIG_EVENTS | FSCACHE_OBJLIST_CONFIG_NOEVENTS;
|
||||
if (!(config & (FSCACHE_OBJLIST_CONFIG_WORK | FSCACHE_OBJLIST_CONFIG_NOWORK)))
|
||||
config |= FSCACHE_OBJLIST_CONFIG_WORK | FSCACHE_OBJLIST_CONFIG_NOWORK;
|
||||
|
||||
data->config = config;
|
||||
return;
|
||||
|
||||
no_config:
|
||||
#endif
|
||||
data->config = ULONG_MAX;
|
||||
}
|
||||
|
||||
/*
|
||||
* open "/proc/fs/fscache/objects" to provide a list of active objects
|
||||
* - can be configured by a user-defined key added to the caller's keyrings
|
||||
*/
|
||||
static int fscache_objlist_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct fscache_objlist_data *data;
|
||||
struct seq_file *m;
|
||||
int ret;
|
||||
|
||||
ret = seq_open(file, &fscache_objlist_ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
m = file->private_data;
|
||||
|
||||
/* buffer for key extraction */
|
||||
data = kmalloc(sizeof(struct fscache_objlist_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
seq_release(inode, file);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* get the configuration key */
|
||||
fscache_objlist_config(data);
|
||||
|
||||
m->private = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* clean up on close
|
||||
*/
|
||||
static int fscache_objlist_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *m = file->private_data;
|
||||
|
||||
kfree(m->private);
|
||||
m->private = NULL;
|
||||
return seq_release(inode, file);
|
||||
}
|
||||
|
||||
const struct file_operations fscache_objlist_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = fscache_objlist_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = fscache_objlist_release,
|
||||
};
|
||||
+93
-11
@@ -14,9 +14,10 @@
|
||||
|
||||
#define FSCACHE_DEBUG_LEVEL COOKIE
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include "internal.h"
|
||||
|
||||
const char *fscache_object_states[] = {
|
||||
const char *fscache_object_states[FSCACHE_OBJECT__NSTATES] = {
|
||||
[FSCACHE_OBJECT_INIT] = "OBJECT_INIT",
|
||||
[FSCACHE_OBJECT_LOOKING_UP] = "OBJECT_LOOKING_UP",
|
||||
[FSCACHE_OBJECT_CREATING] = "OBJECT_CREATING",
|
||||
@@ -33,9 +34,28 @@ const char *fscache_object_states[] = {
|
||||
};
|
||||
EXPORT_SYMBOL(fscache_object_states);
|
||||
|
||||
const char fscache_object_states_short[FSCACHE_OBJECT__NSTATES][5] = {
|
||||
[FSCACHE_OBJECT_INIT] = "INIT",
|
||||
[FSCACHE_OBJECT_LOOKING_UP] = "LOOK",
|
||||
[FSCACHE_OBJECT_CREATING] = "CRTN",
|
||||
[FSCACHE_OBJECT_AVAILABLE] = "AVBL",
|
||||
[FSCACHE_OBJECT_ACTIVE] = "ACTV",
|
||||
[FSCACHE_OBJECT_UPDATING] = "UPDT",
|
||||
[FSCACHE_OBJECT_DYING] = "DYNG",
|
||||
[FSCACHE_OBJECT_LC_DYING] = "LCDY",
|
||||
[FSCACHE_OBJECT_ABORT_INIT] = "ABTI",
|
||||
[FSCACHE_OBJECT_RELEASING] = "RELS",
|
||||
[FSCACHE_OBJECT_RECYCLING] = "RCYC",
|
||||
[FSCACHE_OBJECT_WITHDRAWING] = "WTHD",
|
||||
[FSCACHE_OBJECT_DEAD] = "DEAD",
|
||||
};
|
||||
|
||||
static void fscache_object_slow_work_put_ref(struct slow_work *);
|
||||
static int fscache_object_slow_work_get_ref(struct slow_work *);
|
||||
static void fscache_object_slow_work_execute(struct slow_work *);
|
||||
#ifdef CONFIG_SLOW_WORK_PROC
|
||||
static void fscache_object_slow_work_desc(struct slow_work *, struct seq_file *);
|
||||
#endif
|
||||
static void fscache_initialise_object(struct fscache_object *);
|
||||
static void fscache_lookup_object(struct fscache_object *);
|
||||
static void fscache_object_available(struct fscache_object *);
|
||||
@@ -45,9 +65,13 @@ static void fscache_enqueue_dependents(struct fscache_object *);
|
||||
static void fscache_dequeue_object(struct fscache_object *);
|
||||
|
||||
const struct slow_work_ops fscache_object_slow_work_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.get_ref = fscache_object_slow_work_get_ref,
|
||||
.put_ref = fscache_object_slow_work_put_ref,
|
||||
.execute = fscache_object_slow_work_execute,
|
||||
#ifdef CONFIG_SLOW_WORK_PROC
|
||||
.desc = fscache_object_slow_work_desc,
|
||||
#endif
|
||||
};
|
||||
EXPORT_SYMBOL(fscache_object_slow_work_ops);
|
||||
|
||||
@@ -81,6 +105,7 @@ static inline void fscache_done_parent_op(struct fscache_object *object)
|
||||
static void fscache_object_state_machine(struct fscache_object *object)
|
||||
{
|
||||
enum fscache_object_state new_state;
|
||||
struct fscache_cookie *cookie;
|
||||
|
||||
ASSERT(object != NULL);
|
||||
|
||||
@@ -120,20 +145,31 @@ static void fscache_object_state_machine(struct fscache_object *object)
|
||||
case FSCACHE_OBJECT_UPDATING:
|
||||
clear_bit(FSCACHE_OBJECT_EV_UPDATE, &object->events);
|
||||
fscache_stat(&fscache_n_updates_run);
|
||||
fscache_stat(&fscache_n_cop_update_object);
|
||||
object->cache->ops->update_object(object);
|
||||
fscache_stat_d(&fscache_n_cop_update_object);
|
||||
goto active_transit;
|
||||
|
||||
/* handle an object dying during lookup or creation */
|
||||
case FSCACHE_OBJECT_LC_DYING:
|
||||
object->event_mask &= ~(1 << FSCACHE_OBJECT_EV_UPDATE);
|
||||
fscache_stat(&fscache_n_cop_lookup_complete);
|
||||
object->cache->ops->lookup_complete(object);
|
||||
fscache_stat_d(&fscache_n_cop_lookup_complete);
|
||||
|
||||
spin_lock(&object->lock);
|
||||
object->state = FSCACHE_OBJECT_DYING;
|
||||
if (test_and_clear_bit(FSCACHE_COOKIE_CREATING,
|
||||
&object->cookie->flags))
|
||||
wake_up_bit(&object->cookie->flags,
|
||||
FSCACHE_COOKIE_CREATING);
|
||||
cookie = object->cookie;
|
||||
if (cookie) {
|
||||
if (test_and_clear_bit(FSCACHE_COOKIE_LOOKING_UP,
|
||||
&cookie->flags))
|
||||
wake_up_bit(&cookie->flags,
|
||||
FSCACHE_COOKIE_LOOKING_UP);
|
||||
if (test_and_clear_bit(FSCACHE_COOKIE_CREATING,
|
||||
&cookie->flags))
|
||||
wake_up_bit(&cookie->flags,
|
||||
FSCACHE_COOKIE_CREATING);
|
||||
}
|
||||
spin_unlock(&object->lock);
|
||||
|
||||
fscache_done_parent_op(object);
|
||||
@@ -165,6 +201,7 @@ static void fscache_object_state_machine(struct fscache_object *object)
|
||||
}
|
||||
spin_unlock(&object->lock);
|
||||
fscache_enqueue_dependents(object);
|
||||
fscache_start_operations(object);
|
||||
goto terminal_transit;
|
||||
|
||||
/* handle an abort during initialisation */
|
||||
@@ -316,15 +353,30 @@ static void fscache_object_slow_work_execute(struct slow_work *work)
|
||||
|
||||
_enter("{OBJ%x}", object->debug_id);
|
||||
|
||||
clear_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events);
|
||||
|
||||
start = jiffies;
|
||||
fscache_object_state_machine(object);
|
||||
fscache_hist(fscache_objs_histogram, start);
|
||||
if (object->events & object->event_mask)
|
||||
fscache_enqueue_object(object);
|
||||
clear_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events);
|
||||
}
|
||||
|
||||
/*
|
||||
* describe an object for slow-work debugging
|
||||
*/
|
||||
#ifdef CONFIG_SLOW_WORK_PROC
|
||||
static void fscache_object_slow_work_desc(struct slow_work *work,
|
||||
struct seq_file *m)
|
||||
{
|
||||
struct fscache_object *object =
|
||||
container_of(work, struct fscache_object, work);
|
||||
|
||||
seq_printf(m, "FSC: OBJ%x: %s",
|
||||
object->debug_id,
|
||||
fscache_object_states_short[object->state]);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* initialise an object
|
||||
* - check the specified object's parent to see if we can make use of it
|
||||
@@ -376,7 +428,9 @@ static void fscache_initialise_object(struct fscache_object *object)
|
||||
* binding on to us, so we need to make sure we don't
|
||||
* add ourself to the list multiple times */
|
||||
if (list_empty(&object->dep_link)) {
|
||||
fscache_stat(&fscache_n_cop_grab_object);
|
||||
object->cache->ops->grab_object(object);
|
||||
fscache_stat_d(&fscache_n_cop_grab_object);
|
||||
list_add(&object->dep_link,
|
||||
&parent->dependents);
|
||||
|
||||
@@ -414,6 +468,7 @@ static void fscache_lookup_object(struct fscache_object *object)
|
||||
{
|
||||
struct fscache_cookie *cookie = object->cookie;
|
||||
struct fscache_object *parent;
|
||||
int ret;
|
||||
|
||||
_enter("");
|
||||
|
||||
@@ -438,11 +493,20 @@ static void fscache_lookup_object(struct fscache_object *object)
|
||||
object->cache->tag->name);
|
||||
|
||||
fscache_stat(&fscache_n_object_lookups);
|
||||
object->cache->ops->lookup_object(object);
|
||||
fscache_stat(&fscache_n_cop_lookup_object);
|
||||
ret = object->cache->ops->lookup_object(object);
|
||||
fscache_stat_d(&fscache_n_cop_lookup_object);
|
||||
|
||||
if (test_bit(FSCACHE_OBJECT_EV_ERROR, &object->events))
|
||||
set_bit(FSCACHE_COOKIE_UNAVAILABLE, &cookie->flags);
|
||||
|
||||
if (ret == -ETIMEDOUT) {
|
||||
/* probably stuck behind another object, so move this one to
|
||||
* the back of the queue */
|
||||
fscache_stat(&fscache_n_object_lookups_timed_out);
|
||||
set_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events);
|
||||
}
|
||||
|
||||
_leave("");
|
||||
}
|
||||
|
||||
@@ -546,7 +610,8 @@ static void fscache_object_available(struct fscache_object *object)
|
||||
|
||||
spin_lock(&object->lock);
|
||||
|
||||
if (test_and_clear_bit(FSCACHE_COOKIE_CREATING, &object->cookie->flags))
|
||||
if (object->cookie &&
|
||||
test_and_clear_bit(FSCACHE_COOKIE_CREATING, &object->cookie->flags))
|
||||
wake_up_bit(&object->cookie->flags, FSCACHE_COOKIE_CREATING);
|
||||
|
||||
fscache_done_parent_op(object);
|
||||
@@ -562,7 +627,9 @@ static void fscache_object_available(struct fscache_object *object)
|
||||
}
|
||||
spin_unlock(&object->lock);
|
||||
|
||||
fscache_stat(&fscache_n_cop_lookup_complete);
|
||||
object->cache->ops->lookup_complete(object);
|
||||
fscache_stat_d(&fscache_n_cop_lookup_complete);
|
||||
fscache_enqueue_dependents(object);
|
||||
|
||||
fscache_hist(fscache_obj_instantiate_histogram, object->lookup_jif);
|
||||
@@ -581,11 +648,16 @@ static void fscache_drop_object(struct fscache_object *object)
|
||||
|
||||
_enter("{OBJ%x,%d}", object->debug_id, object->n_children);
|
||||
|
||||
ASSERTCMP(object->cookie, ==, NULL);
|
||||
ASSERT(hlist_unhashed(&object->cookie_link));
|
||||
|
||||
spin_lock(&cache->object_list_lock);
|
||||
list_del_init(&object->cache_link);
|
||||
spin_unlock(&cache->object_list_lock);
|
||||
|
||||
fscache_stat(&fscache_n_cop_drop_object);
|
||||
cache->ops->drop_object(object);
|
||||
fscache_stat_d(&fscache_n_cop_drop_object);
|
||||
|
||||
if (parent) {
|
||||
_debug("release parent OBJ%x {%d}",
|
||||
@@ -600,7 +672,9 @@ static void fscache_drop_object(struct fscache_object *object)
|
||||
}
|
||||
|
||||
/* this just shifts the object release to the slow work processor */
|
||||
fscache_stat(&fscache_n_cop_put_object);
|
||||
object->cache->ops->put_object(object);
|
||||
fscache_stat_d(&fscache_n_cop_put_object);
|
||||
|
||||
_leave("");
|
||||
}
|
||||
@@ -690,8 +764,12 @@ static int fscache_object_slow_work_get_ref(struct slow_work *work)
|
||||
{
|
||||
struct fscache_object *object =
|
||||
container_of(work, struct fscache_object, work);
|
||||
int ret;
|
||||
|
||||
return object->cache->ops->grab_object(object) ? 0 : -EAGAIN;
|
||||
fscache_stat(&fscache_n_cop_grab_object);
|
||||
ret = object->cache->ops->grab_object(object) ? 0 : -EAGAIN;
|
||||
fscache_stat_d(&fscache_n_cop_grab_object);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -702,7 +780,9 @@ static void fscache_object_slow_work_put_ref(struct slow_work *work)
|
||||
struct fscache_object *object =
|
||||
container_of(work, struct fscache_object, work);
|
||||
|
||||
return object->cache->ops->put_object(object);
|
||||
fscache_stat(&fscache_n_cop_put_object);
|
||||
object->cache->ops->put_object(object);
|
||||
fscache_stat_d(&fscache_n_cop_put_object);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -739,7 +819,9 @@ static void fscache_enqueue_dependents(struct fscache_object *object)
|
||||
|
||||
/* sort onto appropriate lists */
|
||||
fscache_enqueue_object(dep);
|
||||
fscache_stat(&fscache_n_cop_put_object);
|
||||
dep->cache->ops->put_object(dep);
|
||||
fscache_stat_d(&fscache_n_cop_put_object);
|
||||
|
||||
if (!list_empty(&object->dependents))
|
||||
cond_resched_lock(&object->lock);
|
||||
|
||||
+92
-28
@@ -13,6 +13,7 @@
|
||||
|
||||
#define FSCACHE_DEBUG_LEVEL OPERATION
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include "internal.h"
|
||||
|
||||
atomic_t fscache_op_debug_id;
|
||||
@@ -31,32 +32,33 @@ void fscache_enqueue_operation(struct fscache_operation *op)
|
||||
_enter("{OBJ%x OP%x,%u}",
|
||||
op->object->debug_id, op->debug_id, atomic_read(&op->usage));
|
||||
|
||||
fscache_set_op_state(op, "EnQ");
|
||||
|
||||
ASSERT(list_empty(&op->pend_link));
|
||||
ASSERT(op->processor != NULL);
|
||||
ASSERTCMP(op->object->state, >=, FSCACHE_OBJECT_AVAILABLE);
|
||||
ASSERTCMP(atomic_read(&op->usage), >, 0);
|
||||
|
||||
if (list_empty(&op->pend_link)) {
|
||||
switch (op->flags & FSCACHE_OP_TYPE) {
|
||||
case FSCACHE_OP_FAST:
|
||||
_debug("queue fast");
|
||||
atomic_inc(&op->usage);
|
||||
if (!schedule_work(&op->fast_work))
|
||||
fscache_put_operation(op);
|
||||
break;
|
||||
case FSCACHE_OP_SLOW:
|
||||
_debug("queue slow");
|
||||
slow_work_enqueue(&op->slow_work);
|
||||
break;
|
||||
case FSCACHE_OP_MYTHREAD:
|
||||
_debug("queue for caller's attention");
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "FS-Cache: Unexpected op type %lx",
|
||||
op->flags);
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
fscache_stat(&fscache_n_op_enqueue);
|
||||
fscache_stat(&fscache_n_op_enqueue);
|
||||
switch (op->flags & FSCACHE_OP_TYPE) {
|
||||
case FSCACHE_OP_FAST:
|
||||
_debug("queue fast");
|
||||
atomic_inc(&op->usage);
|
||||
if (!schedule_work(&op->fast_work))
|
||||
fscache_put_operation(op);
|
||||
break;
|
||||
case FSCACHE_OP_SLOW:
|
||||
_debug("queue slow");
|
||||
slow_work_enqueue(&op->slow_work);
|
||||
break;
|
||||
case FSCACHE_OP_MYTHREAD:
|
||||
_debug("queue for caller's attention");
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "FS-Cache: Unexpected op type %lx",
|
||||
op->flags);
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(fscache_enqueue_operation);
|
||||
@@ -67,6 +69,8 @@ EXPORT_SYMBOL(fscache_enqueue_operation);
|
||||
static void fscache_run_op(struct fscache_object *object,
|
||||
struct fscache_operation *op)
|
||||
{
|
||||
fscache_set_op_state(op, "Run");
|
||||
|
||||
object->n_in_progress++;
|
||||
if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags))
|
||||
wake_up_bit(&op->flags, FSCACHE_OP_WAITING);
|
||||
@@ -87,9 +91,12 @@ int fscache_submit_exclusive_op(struct fscache_object *object,
|
||||
|
||||
_enter("{OBJ%x OP%x},", object->debug_id, op->debug_id);
|
||||
|
||||
fscache_set_op_state(op, "SubmitX");
|
||||
|
||||
spin_lock(&object->lock);
|
||||
ASSERTCMP(object->n_ops, >=, object->n_in_progress);
|
||||
ASSERTCMP(object->n_ops, >=, object->n_exclusive);
|
||||
ASSERT(list_empty(&op->pend_link));
|
||||
|
||||
ret = -ENOBUFS;
|
||||
if (fscache_object_is_active(object)) {
|
||||
@@ -190,9 +197,12 @@ int fscache_submit_op(struct fscache_object *object,
|
||||
|
||||
ASSERTCMP(atomic_read(&op->usage), >, 0);
|
||||
|
||||
fscache_set_op_state(op, "Submit");
|
||||
|
||||
spin_lock(&object->lock);
|
||||
ASSERTCMP(object->n_ops, >=, object->n_in_progress);
|
||||
ASSERTCMP(object->n_ops, >=, object->n_exclusive);
|
||||
ASSERT(list_empty(&op->pend_link));
|
||||
|
||||
ostate = object->state;
|
||||
smp_rmb();
|
||||
@@ -222,6 +232,11 @@ int fscache_submit_op(struct fscache_object *object,
|
||||
list_add_tail(&op->pend_link, &object->pending_ops);
|
||||
fscache_stat(&fscache_n_op_pend);
|
||||
ret = 0;
|
||||
} else if (object->state == FSCACHE_OBJECT_DYING ||
|
||||
object->state == FSCACHE_OBJECT_LC_DYING ||
|
||||
object->state == FSCACHE_OBJECT_WITHDRAWING) {
|
||||
fscache_stat(&fscache_n_op_rejected);
|
||||
ret = -ENOBUFS;
|
||||
} else if (!test_bit(FSCACHE_IOERROR, &object->cache->flags)) {
|
||||
fscache_report_unexpected_submission(object, op, ostate);
|
||||
ASSERT(!fscache_object_is_active(object));
|
||||
@@ -264,12 +279,7 @@ void fscache_start_operations(struct fscache_object *object)
|
||||
stop = true;
|
||||
}
|
||||
list_del_init(&op->pend_link);
|
||||
object->n_in_progress++;
|
||||
|
||||
if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags))
|
||||
wake_up_bit(&op->flags, FSCACHE_OP_WAITING);
|
||||
if (op->processor)
|
||||
fscache_enqueue_operation(op);
|
||||
fscache_run_op(object, op);
|
||||
|
||||
/* the pending queue was holding a ref on the object */
|
||||
fscache_put_operation(op);
|
||||
@@ -281,6 +291,36 @@ void fscache_start_operations(struct fscache_object *object)
|
||||
object->n_in_progress, object->debug_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* cancel an operation that's pending on an object
|
||||
*/
|
||||
int fscache_cancel_op(struct fscache_operation *op)
|
||||
{
|
||||
struct fscache_object *object = op->object;
|
||||
int ret;
|
||||
|
||||
_enter("OBJ%x OP%x}", op->object->debug_id, op->debug_id);
|
||||
|
||||
spin_lock(&object->lock);
|
||||
|
||||
ret = -EBUSY;
|
||||
if (!list_empty(&op->pend_link)) {
|
||||
fscache_stat(&fscache_n_op_cancelled);
|
||||
list_del_init(&op->pend_link);
|
||||
object->n_ops--;
|
||||
if (test_bit(FSCACHE_OP_EXCLUSIVE, &op->flags))
|
||||
object->n_exclusive--;
|
||||
if (test_and_clear_bit(FSCACHE_OP_WAITING, &op->flags))
|
||||
wake_up_bit(&op->flags, FSCACHE_OP_WAITING);
|
||||
fscache_put_operation(op);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
spin_unlock(&object->lock);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* release an operation
|
||||
* - queues pending ops if this is the last in-progress op
|
||||
@@ -298,6 +338,8 @@ void fscache_put_operation(struct fscache_operation *op)
|
||||
if (!atomic_dec_and_test(&op->usage))
|
||||
return;
|
||||
|
||||
fscache_set_op_state(op, "Put");
|
||||
|
||||
_debug("PUT OP");
|
||||
if (test_and_set_bit(FSCACHE_OP_DEAD, &op->flags))
|
||||
BUG();
|
||||
@@ -311,6 +353,9 @@ void fscache_put_operation(struct fscache_operation *op)
|
||||
|
||||
object = op->object;
|
||||
|
||||
if (test_bit(FSCACHE_OP_DEC_READ_CNT, &op->flags))
|
||||
atomic_dec(&object->n_reads);
|
||||
|
||||
/* now... we may get called with the object spinlock held, so we
|
||||
* complete the cleanup here only if we can immediately acquire the
|
||||
* lock, and defer it otherwise */
|
||||
@@ -452,8 +497,27 @@ static void fscache_op_execute(struct slow_work *work)
|
||||
_leave("");
|
||||
}
|
||||
|
||||
/*
|
||||
* describe an operation for slow-work debugging
|
||||
*/
|
||||
#ifdef CONFIG_SLOW_WORK_PROC
|
||||
static void fscache_op_desc(struct slow_work *work, struct seq_file *m)
|
||||
{
|
||||
struct fscache_operation *op =
|
||||
container_of(work, struct fscache_operation, slow_work);
|
||||
|
||||
seq_printf(m, "FSC: OBJ%x OP%x: %s/%s fl=%lx",
|
||||
op->object->debug_id, op->debug_id,
|
||||
op->name, op->state, op->flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct slow_work_ops fscache_op_slow_work_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.get_ref = fscache_op_get_ref,
|
||||
.put_ref = fscache_op_put_ref,
|
||||
.execute = fscache_op_execute,
|
||||
#ifdef CONFIG_SLOW_WORK_PROC
|
||||
.desc = fscache_op_desc,
|
||||
#endif
|
||||
};
|
||||
|
||||
+228
-53
@@ -43,18 +43,102 @@ void __fscache_wait_on_page_write(struct fscache_cookie *cookie, struct page *pa
|
||||
EXPORT_SYMBOL(__fscache_wait_on_page_write);
|
||||
|
||||
/*
|
||||
* note that a page has finished being written to the cache
|
||||
* decide whether a page can be released, possibly by cancelling a store to it
|
||||
* - we're allowed to sleep if __GFP_WAIT is flagged
|
||||
*/
|
||||
static void fscache_end_page_write(struct fscache_cookie *cookie, struct page *page)
|
||||
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;
|
||||
}
|
||||
|
||||
spin_lock(&cookie->lock);
|
||||
xpage = radix_tree_delete(&cookie->stores, page->index);
|
||||
spin_unlock(&cookie->lock);
|
||||
ASSERT(xpage != NULL);
|
||||
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
|
||||
*/
|
||||
static void fscache_end_page_write(struct fscache_object *object,
|
||||
struct page *page)
|
||||
{
|
||||
struct fscache_cookie *cookie;
|
||||
struct page *xpage = NULL;
|
||||
|
||||
spin_lock(&object->lock);
|
||||
cookie = object->cookie;
|
||||
if (cookie) {
|
||||
/* 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);
|
||||
xpage = radix_tree_delete(&cookie->stores, page->index);
|
||||
}
|
||||
spin_unlock(&cookie->stores_lock);
|
||||
wake_up_bit(&cookie->flags, 0);
|
||||
}
|
||||
spin_unlock(&object->lock);
|
||||
if (xpage)
|
||||
page_cache_release(xpage);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -63,14 +147,21 @@ static void fscache_end_page_write(struct fscache_cookie *cookie, struct page *p
|
||||
static void fscache_attr_changed_op(struct fscache_operation *op)
|
||||
{
|
||||
struct fscache_object *object = op->object;
|
||||
int ret;
|
||||
|
||||
_enter("{OBJ%x OP%x}", object->debug_id, op->debug_id);
|
||||
|
||||
fscache_stat(&fscache_n_attr_changed_calls);
|
||||
|
||||
if (fscache_object_is_active(object) &&
|
||||
object->cache->ops->attr_changed(object) < 0)
|
||||
fscache_abort_object(object);
|
||||
if (fscache_object_is_active(object)) {
|
||||
fscache_set_op_state(op, "CallFS");
|
||||
fscache_stat(&fscache_n_cop_attr_changed);
|
||||
ret = object->cache->ops->attr_changed(object);
|
||||
fscache_stat_d(&fscache_n_cop_attr_changed);
|
||||
fscache_set_op_state(op, "Done");
|
||||
if (ret < 0)
|
||||
fscache_abort_object(object);
|
||||
}
|
||||
|
||||
_leave("");
|
||||
}
|
||||
@@ -99,6 +190,7 @@ int __fscache_attr_changed(struct fscache_cookie *cookie)
|
||||
fscache_operation_init(op, NULL);
|
||||
fscache_operation_init_slow(op, fscache_attr_changed_op);
|
||||
op->flags = FSCACHE_OP_SLOW | (1 << FSCACHE_OP_EXCLUSIVE);
|
||||
fscache_set_op_name(op, "Attr");
|
||||
|
||||
spin_lock(&cookie->lock);
|
||||
|
||||
@@ -184,6 +276,7 @@ static struct fscache_retrieval *fscache_alloc_retrieval(
|
||||
op->start_time = jiffies;
|
||||
INIT_WORK(&op->op.fast_work, fscache_retrieval_work);
|
||||
INIT_LIST_HEAD(&op->to_do);
|
||||
fscache_set_op_name(&op->op, "Retr");
|
||||
return op;
|
||||
}
|
||||
|
||||
@@ -220,6 +313,43 @@ static int fscache_wait_for_deferred_lookup(struct fscache_cookie *cookie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait for an object to become active (or dead)
|
||||
*/
|
||||
static int fscache_wait_for_retrieval_activation(struct fscache_object *object,
|
||||
struct fscache_retrieval *op,
|
||||
atomic_t *stat_op_waits,
|
||||
atomic_t *stat_object_dead)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!test_bit(FSCACHE_OP_WAITING, &op->op.flags))
|
||||
goto check_if_dead;
|
||||
|
||||
_debug(">>> WT");
|
||||
fscache_stat(stat_op_waits);
|
||||
if (wait_on_bit(&op->op.flags, FSCACHE_OP_WAITING,
|
||||
fscache_wait_bit_interruptible,
|
||||
TASK_INTERRUPTIBLE) < 0) {
|
||||
ret = fscache_cancel_op(&op->op);
|
||||
if (ret == 0)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
/* it's been removed from the pending queue by another party,
|
||||
* so we should get to run shortly */
|
||||
wait_on_bit(&op->op.flags, FSCACHE_OP_WAITING,
|
||||
fscache_wait_bit, TASK_UNINTERRUPTIBLE);
|
||||
}
|
||||
_debug("<<< GO");
|
||||
|
||||
check_if_dead:
|
||||
if (unlikely(fscache_object_is_dead(object))) {
|
||||
fscache_stat(stat_object_dead);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* read a page from the cache or allocate a block in which to store it
|
||||
* - we return:
|
||||
@@ -257,6 +387,7 @@ int __fscache_read_or_alloc_page(struct fscache_cookie *cookie,
|
||||
_leave(" = -ENOMEM");
|
||||
return -ENOMEM;
|
||||
}
|
||||
fscache_set_op_name(&op->op, "RetrRA1");
|
||||
|
||||
spin_lock(&cookie->lock);
|
||||
|
||||
@@ -267,6 +398,9 @@ int __fscache_read_or_alloc_page(struct fscache_cookie *cookie,
|
||||
|
||||
ASSERTCMP(object->state, >, FSCACHE_OBJECT_LOOKING_UP);
|
||||
|
||||
atomic_inc(&object->n_reads);
|
||||
set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags);
|
||||
|
||||
if (fscache_submit_op(object, &op->op) < 0)
|
||||
goto nobufs_unlock;
|
||||
spin_unlock(&cookie->lock);
|
||||
@@ -279,23 +413,27 @@ int __fscache_read_or_alloc_page(struct fscache_cookie *cookie,
|
||||
|
||||
/* we wait for the operation to become active, and then process it
|
||||
* *here*, in this thread, and not in the thread pool */
|
||||
if (test_bit(FSCACHE_OP_WAITING, &op->op.flags)) {
|
||||
_debug(">>> WT");
|
||||
fscache_stat(&fscache_n_retrieval_op_waits);
|
||||
wait_on_bit(&op->op.flags, FSCACHE_OP_WAITING,
|
||||
fscache_wait_bit, TASK_UNINTERRUPTIBLE);
|
||||
_debug("<<< GO");
|
||||
}
|
||||
ret = fscache_wait_for_retrieval_activation(
|
||||
object, op,
|
||||
__fscache_stat(&fscache_n_retrieval_op_waits),
|
||||
__fscache_stat(&fscache_n_retrievals_object_dead));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* ask the cache to honour the operation */
|
||||
if (test_bit(FSCACHE_COOKIE_NO_DATA_YET, &object->cookie->flags)) {
|
||||
fscache_stat(&fscache_n_cop_allocate_page);
|
||||
ret = object->cache->ops->allocate_page(op, page, gfp);
|
||||
fscache_stat_d(&fscache_n_cop_allocate_page);
|
||||
if (ret == 0)
|
||||
ret = -ENODATA;
|
||||
} else {
|
||||
fscache_stat(&fscache_n_cop_read_or_alloc_page);
|
||||
ret = object->cache->ops->read_or_alloc_page(op, page, gfp);
|
||||
fscache_stat_d(&fscache_n_cop_read_or_alloc_page);
|
||||
}
|
||||
|
||||
error:
|
||||
if (ret == -ENOMEM)
|
||||
fscache_stat(&fscache_n_retrievals_nomem);
|
||||
else if (ret == -ERESTARTSYS)
|
||||
@@ -347,7 +485,6 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,
|
||||
void *context,
|
||||
gfp_t gfp)
|
||||
{
|
||||
fscache_pages_retrieval_func_t func;
|
||||
struct fscache_retrieval *op;
|
||||
struct fscache_object *object;
|
||||
int ret;
|
||||
@@ -369,6 +506,7 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,
|
||||
op = fscache_alloc_retrieval(mapping, end_io_func, context);
|
||||
if (!op)
|
||||
return -ENOMEM;
|
||||
fscache_set_op_name(&op->op, "RetrRAN");
|
||||
|
||||
spin_lock(&cookie->lock);
|
||||
|
||||
@@ -377,6 +515,9 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,
|
||||
object = hlist_entry(cookie->backing_objects.first,
|
||||
struct fscache_object, cookie_link);
|
||||
|
||||
atomic_inc(&object->n_reads);
|
||||
set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags);
|
||||
|
||||
if (fscache_submit_op(object, &op->op) < 0)
|
||||
goto nobufs_unlock;
|
||||
spin_unlock(&cookie->lock);
|
||||
@@ -389,21 +530,27 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie,
|
||||
|
||||
/* we wait for the operation to become active, and then process it
|
||||
* *here*, in this thread, and not in the thread pool */
|
||||
if (test_bit(FSCACHE_OP_WAITING, &op->op.flags)) {
|
||||
_debug(">>> WT");
|
||||
fscache_stat(&fscache_n_retrieval_op_waits);
|
||||
wait_on_bit(&op->op.flags, FSCACHE_OP_WAITING,
|
||||
fscache_wait_bit, TASK_UNINTERRUPTIBLE);
|
||||
_debug("<<< GO");
|
||||
}
|
||||
ret = fscache_wait_for_retrieval_activation(
|
||||
object, op,
|
||||
__fscache_stat(&fscache_n_retrieval_op_waits),
|
||||
__fscache_stat(&fscache_n_retrievals_object_dead));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* ask the cache to honour the operation */
|
||||
if (test_bit(FSCACHE_COOKIE_NO_DATA_YET, &object->cookie->flags))
|
||||
func = object->cache->ops->allocate_pages;
|
||||
else
|
||||
func = object->cache->ops->read_or_alloc_pages;
|
||||
ret = func(op, pages, nr_pages, gfp);
|
||||
if (test_bit(FSCACHE_COOKIE_NO_DATA_YET, &object->cookie->flags)) {
|
||||
fscache_stat(&fscache_n_cop_allocate_pages);
|
||||
ret = object->cache->ops->allocate_pages(
|
||||
op, pages, nr_pages, gfp);
|
||||
fscache_stat_d(&fscache_n_cop_allocate_pages);
|
||||
} else {
|
||||
fscache_stat(&fscache_n_cop_read_or_alloc_pages);
|
||||
ret = object->cache->ops->read_or_alloc_pages(
|
||||
op, pages, nr_pages, gfp);
|
||||
fscache_stat_d(&fscache_n_cop_read_or_alloc_pages);
|
||||
}
|
||||
|
||||
error:
|
||||
if (ret == -ENOMEM)
|
||||
fscache_stat(&fscache_n_retrievals_nomem);
|
||||
else if (ret == -ERESTARTSYS)
|
||||
@@ -461,6 +608,7 @@ int __fscache_alloc_page(struct fscache_cookie *cookie,
|
||||
op = fscache_alloc_retrieval(page->mapping, NULL, NULL);
|
||||
if (!op)
|
||||
return -ENOMEM;
|
||||
fscache_set_op_name(&op->op, "RetrAL1");
|
||||
|
||||
spin_lock(&cookie->lock);
|
||||
|
||||
@@ -475,18 +623,22 @@ int __fscache_alloc_page(struct fscache_cookie *cookie,
|
||||
|
||||
fscache_stat(&fscache_n_alloc_ops);
|
||||
|
||||
if (test_bit(FSCACHE_OP_WAITING, &op->op.flags)) {
|
||||
_debug(">>> WT");
|
||||
fscache_stat(&fscache_n_alloc_op_waits);
|
||||
wait_on_bit(&op->op.flags, FSCACHE_OP_WAITING,
|
||||
fscache_wait_bit, TASK_UNINTERRUPTIBLE);
|
||||
_debug("<<< GO");
|
||||
}
|
||||
ret = fscache_wait_for_retrieval_activation(
|
||||
object, op,
|
||||
__fscache_stat(&fscache_n_alloc_op_waits),
|
||||
__fscache_stat(&fscache_n_allocs_object_dead));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* ask the cache to honour the operation */
|
||||
fscache_stat(&fscache_n_cop_allocate_page);
|
||||
ret = object->cache->ops->allocate_page(op, page, gfp);
|
||||
fscache_stat_d(&fscache_n_cop_allocate_page);
|
||||
|
||||
if (ret < 0)
|
||||
error:
|
||||
if (ret == -ERESTARTSYS)
|
||||
fscache_stat(&fscache_n_allocs_intr);
|
||||
else if (ret < 0)
|
||||
fscache_stat(&fscache_n_allocs_nobufs);
|
||||
else
|
||||
fscache_stat(&fscache_n_allocs_ok);
|
||||
@@ -521,7 +673,7 @@ static void fscache_write_op(struct fscache_operation *_op)
|
||||
struct fscache_storage *op =
|
||||
container_of(_op, struct fscache_storage, op);
|
||||
struct fscache_object *object = op->op.object;
|
||||
struct fscache_cookie *cookie = object->cookie;
|
||||
struct fscache_cookie *cookie;
|
||||
struct page *page;
|
||||
unsigned n;
|
||||
void *results[1];
|
||||
@@ -529,16 +681,19 @@ static void fscache_write_op(struct fscache_operation *_op)
|
||||
|
||||
_enter("{OP%x,%d}", op->op.debug_id, atomic_read(&op->op.usage));
|
||||
|
||||
spin_lock(&cookie->lock);
|
||||
spin_lock(&object->lock);
|
||||
fscache_set_op_state(&op->op, "GetPage");
|
||||
|
||||
if (!fscache_object_is_active(object)) {
|
||||
spin_lock(&object->lock);
|
||||
cookie = object->cookie;
|
||||
|
||||
if (!fscache_object_is_active(object) || !cookie) {
|
||||
spin_unlock(&object->lock);
|
||||
spin_unlock(&cookie->lock);
|
||||
_leave("");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock(&cookie->stores_lock);
|
||||
|
||||
fscache_stat(&fscache_n_store_calls);
|
||||
|
||||
/* find a page to store */
|
||||
@@ -549,23 +704,35 @@ static void fscache_write_op(struct fscache_operation *_op)
|
||||
goto superseded;
|
||||
page = results[0];
|
||||
_debug("gang %d [%lx]", n, page->index);
|
||||
if (page->index > op->store_limit)
|
||||
if (page->index > op->store_limit) {
|
||||
fscache_stat(&fscache_n_store_pages_over_limit);
|
||||
goto superseded;
|
||||
|
||||
radix_tree_tag_clear(&cookie->stores, page->index,
|
||||
FSCACHE_COOKIE_PENDING_TAG);
|
||||
|
||||
spin_unlock(&object->lock);
|
||||
spin_unlock(&cookie->lock);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (page) {
|
||||
fscache_set_op_state(&op->op, "Store");
|
||||
fscache_stat(&fscache_n_store_pages);
|
||||
fscache_stat(&fscache_n_cop_write_page);
|
||||
ret = object->cache->ops->write_page(op, page);
|
||||
fscache_end_page_write(cookie, page);
|
||||
page_cache_release(page);
|
||||
if (ret < 0)
|
||||
fscache_stat_d(&fscache_n_cop_write_page);
|
||||
fscache_set_op_state(&op->op, "EndWrite");
|
||||
fscache_end_page_write(object, page);
|
||||
if (ret < 0) {
|
||||
fscache_set_op_state(&op->op, "Abort");
|
||||
fscache_abort_object(object);
|
||||
else
|
||||
} else {
|
||||
fscache_enqueue_operation(&op->op);
|
||||
}
|
||||
}
|
||||
|
||||
_leave("");
|
||||
@@ -575,9 +742,9 @@ superseded:
|
||||
/* this writer is going away and there aren't any more things to
|
||||
* write */
|
||||
_debug("cease");
|
||||
spin_unlock(&cookie->stores_lock);
|
||||
clear_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags);
|
||||
spin_unlock(&object->lock);
|
||||
spin_unlock(&cookie->lock);
|
||||
_leave("");
|
||||
}
|
||||
|
||||
@@ -634,6 +801,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,
|
||||
fscache_operation_init(&op->op, fscache_release_write_op);
|
||||
fscache_operation_init_slow(&op->op, fscache_write_op);
|
||||
op->op.flags = FSCACHE_OP_SLOW | (1 << FSCACHE_OP_WAITING);
|
||||
fscache_set_op_name(&op->op, "Write1");
|
||||
|
||||
ret = radix_tree_preload(gfp & ~__GFP_HIGHMEM);
|
||||
if (ret < 0)
|
||||
@@ -652,6 +820,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,
|
||||
/* add the page to the pending-storage radix tree on the backing
|
||||
* object */
|
||||
spin_lock(&object->lock);
|
||||
spin_lock(&cookie->stores_lock);
|
||||
|
||||
_debug("store limit %llx", (unsigned long long) object->store_limit);
|
||||
|
||||
@@ -672,6 +841,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,
|
||||
if (test_and_set_bit(FSCACHE_OBJECT_PENDING_WRITE, &object->flags))
|
||||
goto already_pending;
|
||||
|
||||
spin_unlock(&cookie->stores_lock);
|
||||
spin_unlock(&object->lock);
|
||||
|
||||
op->op.debug_id = atomic_inc_return(&fscache_op_debug_id);
|
||||
@@ -693,6 +863,7 @@ int __fscache_write_page(struct fscache_cookie *cookie,
|
||||
already_queued:
|
||||
fscache_stat(&fscache_n_stores_again);
|
||||
already_pending:
|
||||
spin_unlock(&cookie->stores_lock);
|
||||
spin_unlock(&object->lock);
|
||||
spin_unlock(&cookie->lock);
|
||||
radix_tree_preload_end();
|
||||
@@ -702,7 +873,9 @@ already_pending:
|
||||
return 0;
|
||||
|
||||
submit_failed:
|
||||
spin_lock(&cookie->stores_lock);
|
||||
radix_tree_delete(&cookie->stores, page->index);
|
||||
spin_unlock(&cookie->stores_lock);
|
||||
page_cache_release(page);
|
||||
ret = -ENOBUFS;
|
||||
goto nobufs;
|
||||
@@ -763,7 +936,9 @@ void __fscache_uncache_page(struct fscache_cookie *cookie, struct page *page)
|
||||
if (TestClearPageFsCache(page) &&
|
||||
object->cache->ops->uncache_page) {
|
||||
/* the cache backend releases the cookie lock */
|
||||
fscache_stat(&fscache_n_cop_uncache_page);
|
||||
object->cache->ops->uncache_page(object, page);
|
||||
fscache_stat_d(&fscache_n_cop_uncache_page);
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,20 @@ int __init fscache_proc_init(void)
|
||||
goto error_histogram;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FSCACHE_OBJECT_LIST
|
||||
if (!proc_create("fs/fscache/objects", S_IFREG | 0444, NULL,
|
||||
&fscache_objlist_fops))
|
||||
goto error_objects;
|
||||
#endif
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_FSCACHE_OBJECT_LIST
|
||||
error_objects:
|
||||
#endif
|
||||
#ifdef CONFIG_FSCACHE_HISTOGRAM
|
||||
remove_proc_entry("fs/fscache/histogram", NULL);
|
||||
error_histogram:
|
||||
#endif
|
||||
#ifdef CONFIG_FSCACHE_STATS
|
||||
@@ -58,6 +68,9 @@ error_dir:
|
||||
*/
|
||||
void fscache_proc_cleanup(void)
|
||||
{
|
||||
#ifdef CONFIG_FSCACHE_OBJECT_LIST
|
||||
remove_proc_entry("fs/fscache/objects", NULL);
|
||||
#endif
|
||||
#ifdef CONFIG_FSCACHE_HISTOGRAM
|
||||
remove_proc_entry("fs/fscache/histogram", NULL);
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user