diff options
-rw-r--r-- | Documentation/filesystems/caching/fscache.txt | 81 | ||||
-rw-r--r-- | fs/cachefiles/interface.c | 1 | ||||
-rw-r--r-- | fs/cachefiles/rdwr.c | 6 | ||||
-rw-r--r-- | fs/fscache/Kconfig | 7 | ||||
-rw-r--r-- | fs/fscache/Makefile | 1 | ||||
-rw-r--r-- | fs/fscache/cache.c | 1 | ||||
-rw-r--r-- | fs/fscache/cookie.c | 2 | ||||
-rw-r--r-- | fs/fscache/internal.h | 13 | ||||
-rw-r--r-- | fs/fscache/object-list.c | 432 | ||||
-rw-r--r-- | fs/fscache/object.c | 2 | ||||
-rw-r--r-- | fs/fscache/operation.c | 3 | ||||
-rw-r--r-- | fs/fscache/page.c | 6 | ||||
-rw-r--r-- | fs/fscache/proc.c | 13 | ||||
-rw-r--r-- | include/linux/fscache-cache.h | 13 |
14 files changed, 578 insertions, 3 deletions
diff --git a/Documentation/filesystems/caching/fscache.txt b/Documentation/filesystems/caching/fscache.txt index 9e94b9491d89..cac09e11ca30 100644 --- a/Documentation/filesystems/caching/fscache.txt +++ b/Documentation/filesystems/caching/fscache.txt | |||
@@ -299,6 +299,87 @@ proc files. | |||
299 | jiffy range covered, and the SECS field the equivalent number of seconds. | 299 | jiffy range covered, and the SECS field the equivalent number of seconds. |
300 | 300 | ||
301 | 301 | ||
302 | =========== | ||
303 | OBJECT LIST | ||
304 | =========== | ||
305 | |||
306 | If CONFIG_FSCACHE_OBJECT_LIST is enabled, the FS-Cache facility will maintain a | ||
307 | list of all the objects currently allocated and allow them to be viewed | ||
308 | through: | ||
309 | |||
310 | /proc/fs/fscache/objects | ||
311 | |||
312 | This will look something like: | ||
313 | |||
314 | [root@andromeda ~]# head /proc/fs/fscache/objects | ||
315 | OBJECT PARENT STAT CHLDN OPS OOP IPR EX READS EM EV F S | NETFS_COOKIE_DEF TY FL NETFS_DATA OBJECT_KEY, AUX_DATA | ||
316 | ======== ======== ==== ===== === === === == ===== == == = = | ================ == == ================ ================ | ||
317 | 17e4b 2 ACTV 0 0 0 0 0 0 7b 4 0 8 | NFS.fh DT 0 ffff88001dd82820 010006017edcf8bbc93b43298fdfbe71e50b57b13a172c0117f38472, e567634700000000000000000000000063f2404a000000000000000000000000c9030000000000000000000063f2404a | ||
318 | 1693a 2 ACTV 0 0 0 0 0 0 7b 4 0 8 | NFS.fh DT 0 ffff88002db23380 010006017edcf8bbc93b43298fdfbe71e50b57b1e0162c01a2df0ea6, 420ebc4a000000000000000000000000420ebc4a0000000000000000000000000e1801000000000000000000420ebc4a | ||
319 | |||
320 | where the first set of columns before the '|' describe the object: | ||
321 | |||
322 | COLUMN DESCRIPTION | ||
323 | ======= =============================================================== | ||
324 | OBJECT Object debugging ID (appears as OBJ%x in some debug messages) | ||
325 | PARENT Debugging ID of parent object | ||
326 | STAT Object state | ||
327 | CHLDN Number of child objects of this object | ||
328 | OPS Number of outstanding operations on this object | ||
329 | OOP Number of outstanding child object management operations | ||
330 | IPR | ||
331 | EX Number of outstanding exclusive operations | ||
332 | READS Number of outstanding read operations | ||
333 | EM Object's event mask | ||
334 | EV Events raised on this object | ||
335 | F Object flags | ||
336 | S Object slow-work work item flags | ||
337 | |||
338 | and the second set of columns describe the object's cookie, if present: | ||
339 | |||
340 | COLUMN DESCRIPTION | ||
341 | =============== ======================================================= | ||
342 | NETFS_COOKIE_DEF Name of netfs cookie definition | ||
343 | TY Cookie type (IX - index, DT - data, hex - special) | ||
344 | FL Cookie flags | ||
345 | NETFS_DATA Netfs private data stored in the cookie | ||
346 | OBJECT_KEY Object key } 1 column, with separating comma | ||
347 | AUX_DATA Object aux data } presence may be configured | ||
348 | |||
349 | The data shown may be filtered by attaching the a key to an appropriate keyring | ||
350 | before viewing the file. Something like: | ||
351 | |||
352 | keyctl add user fscache:objlist <restrictions> @s | ||
353 | |||
354 | where <restrictions> are a selection of the following letters: | ||
355 | |||
356 | K Show hexdump of object key (don't show if not given) | ||
357 | A Show hexdump of object aux data (don't show if not given) | ||
358 | |||
359 | and the following paired letters: | ||
360 | |||
361 | C Show objects that have a cookie | ||
362 | c Show objects that don't have a cookie | ||
363 | B Show objects that are busy | ||
364 | b Show objects that aren't busy | ||
365 | W Show objects that have pending writes | ||
366 | w Show objects that don't have pending writes | ||
367 | R Show objects that have outstanding reads | ||
368 | r Show objects that don't have outstanding reads | ||
369 | S Show objects that have slow work queued | ||
370 | s Show objects that don't have slow work queued | ||
371 | |||
372 | If neither side of a letter pair is given, then both are implied. For example: | ||
373 | |||
374 | keyctl add user fscache:objlist KB @s | ||
375 | |||
376 | shows objects that are busy, and lists their object keys, but does not dump | ||
377 | their auxiliary data. It also implies "CcWwRrSs", but as 'B' is given, 'b' is | ||
378 | not implied. | ||
379 | |||
380 | By default all objects and all fields will be shown. | ||
381 | |||
382 | |||
302 | ========= | 383 | ========= |
303 | DEBUGGING | 384 | DEBUGGING |
304 | ========= | 385 | ========= |
diff --git a/fs/cachefiles/interface.c b/fs/cachefiles/interface.c index 431accd475a7..dd7f852746cb 100644 --- a/fs/cachefiles/interface.c +++ b/fs/cachefiles/interface.c | |||
@@ -331,6 +331,7 @@ static void cachefiles_put_object(struct fscache_object *_object) | |||
331 | } | 331 | } |
332 | 332 | ||
333 | cache = object->fscache.cache; | 333 | cache = object->fscache.cache; |
334 | fscache_object_destroy(&object->fscache); | ||
334 | kmem_cache_free(cachefiles_object_jar, object); | 335 | kmem_cache_free(cachefiles_object_jar, object); |
335 | fscache_object_destroyed(cache); | 336 | fscache_object_destroyed(cache); |
336 | } | 337 | } |
diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c index a69787e7dd96..3304646dae84 100644 --- a/fs/cachefiles/rdwr.c +++ b/fs/cachefiles/rdwr.c | |||
@@ -333,7 +333,8 @@ int cachefiles_read_or_alloc_page(struct fscache_retrieval *op, | |||
333 | 333 | ||
334 | shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits; | 334 | shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits; |
335 | 335 | ||
336 | op->op.flags = FSCACHE_OP_FAST; | 336 | op->op.flags &= FSCACHE_OP_KEEP_FLAGS; |
337 | op->op.flags |= FSCACHE_OP_FAST; | ||
337 | op->op.processor = cachefiles_read_copier; | 338 | op->op.processor = cachefiles_read_copier; |
338 | 339 | ||
339 | pagevec_init(&pagevec, 0); | 340 | pagevec_init(&pagevec, 0); |
@@ -639,7 +640,8 @@ int cachefiles_read_or_alloc_pages(struct fscache_retrieval *op, | |||
639 | 640 | ||
640 | pagevec_init(&pagevec, 0); | 641 | pagevec_init(&pagevec, 0); |
641 | 642 | ||
642 | op->op.flags = FSCACHE_OP_FAST; | 643 | op->op.flags &= FSCACHE_OP_KEEP_FLAGS; |
644 | op->op.flags |= FSCACHE_OP_FAST; | ||
643 | op->op.processor = cachefiles_read_copier; | 645 | op->op.processor = cachefiles_read_copier; |
644 | 646 | ||
645 | INIT_LIST_HEAD(&backpages); | 647 | INIT_LIST_HEAD(&backpages); |
diff --git a/fs/fscache/Kconfig b/fs/fscache/Kconfig index 9bbb8ce7bea0..864dac20a242 100644 --- a/fs/fscache/Kconfig +++ b/fs/fscache/Kconfig | |||
@@ -54,3 +54,10 @@ config FSCACHE_DEBUG | |||
54 | enabled by setting bits in /sys/modules/fscache/parameter/debug. | 54 | enabled by setting bits in /sys/modules/fscache/parameter/debug. |
55 | 55 | ||
56 | See Documentation/filesystems/caching/fscache.txt for more information. | 56 | See Documentation/filesystems/caching/fscache.txt for more information. |
57 | |||
58 | config FSCACHE_OBJECT_LIST | ||
59 | bool "Maintain global object list for debugging purposes" | ||
60 | depends on FSCACHE && PROC_FS | ||
61 | help | ||
62 | Maintain a global list of active fscache objects that can be | ||
63 | retrieved through /proc/fs/fscache/objects for debugging purposes | ||
diff --git a/fs/fscache/Makefile b/fs/fscache/Makefile index 91571b95aacc..6d561531cb36 100644 --- a/fs/fscache/Makefile +++ b/fs/fscache/Makefile | |||
@@ -15,5 +15,6 @@ fscache-y := \ | |||
15 | fscache-$(CONFIG_PROC_FS) += proc.o | 15 | fscache-$(CONFIG_PROC_FS) += proc.o |
16 | fscache-$(CONFIG_FSCACHE_STATS) += stats.o | 16 | fscache-$(CONFIG_FSCACHE_STATS) += stats.o |
17 | fscache-$(CONFIG_FSCACHE_HISTOGRAM) += histogram.o | 17 | fscache-$(CONFIG_FSCACHE_HISTOGRAM) += histogram.o |
18 | fscache-$(CONFIG_FSCACHE_OBJECT_LIST) += object-list.o | ||
18 | 19 | ||
19 | obj-$(CONFIG_FSCACHE) := fscache.o | 20 | obj-$(CONFIG_FSCACHE) := fscache.o |
diff --git a/fs/fscache/cache.c b/fs/fscache/cache.c index e21985bbb1fb..724384ef96de 100644 --- a/fs/fscache/cache.c +++ b/fs/fscache/cache.c | |||
@@ -263,6 +263,7 @@ int fscache_add_cache(struct fscache_cache *cache, | |||
263 | spin_lock(&cache->object_list_lock); | 263 | spin_lock(&cache->object_list_lock); |
264 | list_add_tail(&ifsdef->cache_link, &cache->object_list); | 264 | list_add_tail(&ifsdef->cache_link, &cache->object_list); |
265 | spin_unlock(&cache->object_list_lock); | 265 | spin_unlock(&cache->object_list_lock); |
266 | fscache_objlist_add(ifsdef); | ||
266 | 267 | ||
267 | /* add the cache's netfs definition index object to the top level index | 268 | /* add the cache's netfs definition index object to the top level index |
268 | * cookie as a known backing object */ | 269 | * cookie as a known backing object */ |
diff --git a/fs/fscache/cookie.c b/fs/fscache/cookie.c index 72fd18f6c71f..9b5187328230 100644 --- a/fs/fscache/cookie.c +++ b/fs/fscache/cookie.c | |||
@@ -349,6 +349,8 @@ static int fscache_attach_object(struct fscache_cookie *cookie, | |||
349 | object->cookie = cookie; | 349 | object->cookie = cookie; |
350 | atomic_inc(&cookie->usage); | 350 | atomic_inc(&cookie->usage); |
351 | hlist_add_head(&object->cookie_link, &cookie->backing_objects); | 351 | hlist_add_head(&object->cookie_link, &cookie->backing_objects); |
352 | |||
353 | fscache_objlist_add(object); | ||
352 | ret = 0; | 354 | ret = 0; |
353 | 355 | ||
354 | cant_attach_object: | 356 | cant_attach_object: |
diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 1c341304621f..fe02973a9516 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h | |||
@@ -88,11 +88,24 @@ extern int fscache_wait_bit_interruptible(void *); | |||
88 | /* | 88 | /* |
89 | * object.c | 89 | * object.c |
90 | */ | 90 | */ |
91 | extern const char fscache_object_states_short[FSCACHE_OBJECT__NSTATES][5]; | ||
92 | |||
91 | extern void fscache_withdrawing_object(struct fscache_cache *, | 93 | extern void fscache_withdrawing_object(struct fscache_cache *, |
92 | struct fscache_object *); | 94 | struct fscache_object *); |
93 | extern void fscache_enqueue_object(struct fscache_object *); | 95 | extern void fscache_enqueue_object(struct fscache_object *); |
94 | 96 | ||
95 | /* | 97 | /* |
98 | * object-list.c | ||
99 | */ | ||
100 | #ifdef CONFIG_FSCACHE_OBJECT_LIST | ||
101 | extern const struct file_operations fscache_objlist_fops; | ||
102 | |||
103 | extern void fscache_objlist_add(struct fscache_object *); | ||
104 | #else | ||
105 | #define fscache_objlist_add(object) do {} while(0) | ||
106 | #endif | ||
107 | |||
108 | /* | ||
96 | * operation.c | 109 | * operation.c |
97 | */ | 110 | */ |
98 | extern int fscache_submit_exclusive_op(struct fscache_object *, | 111 | extern int fscache_submit_exclusive_op(struct fscache_object *, |
diff --git a/fs/fscache/object-list.c b/fs/fscache/object-list.c new file mode 100644 index 000000000000..e590242fa41a --- /dev/null +++ b/fs/fscache/object-list.c | |||
@@ -0,0 +1,432 @@ | |||
1 | /* Global fscache object list maintainer and viewer | ||
2 | * | ||
3 | * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #define FSCACHE_DEBUG_LEVEL COOKIE | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/seq_file.h> | ||
15 | #include <linux/key.h> | ||
16 | #include <keys/user-type.h> | ||
17 | #include "internal.h" | ||
18 | |||
19 | static struct rb_root fscache_object_list; | ||
20 | static DEFINE_RWLOCK(fscache_object_list_lock); | ||
21 | |||
22 | struct fscache_objlist_data { | ||
23 | unsigned long config; /* display configuration */ | ||
24 | #define FSCACHE_OBJLIST_CONFIG_KEY 0x00000001 /* show object keys */ | ||
25 | #define FSCACHE_OBJLIST_CONFIG_AUX 0x00000002 /* show object auxdata */ | ||
26 | #define FSCACHE_OBJLIST_CONFIG_COOKIE 0x00000004 /* show objects with cookies */ | ||
27 | #define FSCACHE_OBJLIST_CONFIG_NOCOOKIE 0x00000008 /* show objects without cookies */ | ||
28 | #define FSCACHE_OBJLIST_CONFIG_BUSY 0x00000010 /* show busy objects */ | ||
29 | #define FSCACHE_OBJLIST_CONFIG_IDLE 0x00000020 /* show idle objects */ | ||
30 | #define FSCACHE_OBJLIST_CONFIG_PENDWR 0x00000040 /* show objects with pending writes */ | ||
31 | #define FSCACHE_OBJLIST_CONFIG_NOPENDWR 0x00000080 /* show objects without pending writes */ | ||
32 | #define FSCACHE_OBJLIST_CONFIG_READS 0x00000100 /* show objects with active reads */ | ||
33 | #define FSCACHE_OBJLIST_CONFIG_NOREADS 0x00000200 /* show objects without active reads */ | ||
34 | #define FSCACHE_OBJLIST_CONFIG_EVENTS 0x00000400 /* show objects with events */ | ||
35 | #define FSCACHE_OBJLIST_CONFIG_NOEVENTS 0x00000800 /* show objects without no events */ | ||
36 | #define FSCACHE_OBJLIST_CONFIG_WORK 0x00001000 /* show objects with slow work */ | ||
37 | #define FSCACHE_OBJLIST_CONFIG_NOWORK 0x00002000 /* show objects without slow work */ | ||
38 | |||
39 | u8 buf[512]; /* key and aux data buffer */ | ||
40 | }; | ||
41 | |||
42 | /* | ||
43 | * Add an object to the object list | ||
44 | * - we use the address of the fscache_object structure as the key into the | ||
45 | * tree | ||
46 | */ | ||
47 | void fscache_objlist_add(struct fscache_object *obj) | ||
48 | { | ||
49 | struct fscache_object *xobj; | ||
50 | struct rb_node **p = &fscache_object_list.rb_node, *parent = NULL; | ||
51 | |||
52 | write_lock(&fscache_object_list_lock); | ||
53 | |||
54 | while (*p) { | ||
55 | parent = *p; | ||
56 | xobj = rb_entry(parent, struct fscache_object, objlist_link); | ||
57 | |||
58 | if (obj < xobj) | ||
59 | p = &(*p)->rb_left; | ||
60 | else if (obj > xobj) | ||
61 | p = &(*p)->rb_right; | ||
62 | else | ||
63 | BUG(); | ||
64 | } | ||
65 | |||
66 | rb_link_node(&obj->objlist_link, parent, p); | ||
67 | rb_insert_color(&obj->objlist_link, &fscache_object_list); | ||
68 | |||
69 | write_unlock(&fscache_object_list_lock); | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * fscache_object_destroy - Note that a cache object is about to be destroyed | ||
74 | * @object: The object to be destroyed | ||
75 | * | ||
76 | * Note the imminent destruction and deallocation of a cache object record. | ||
77 | */ | ||
78 | void fscache_object_destroy(struct fscache_object *obj) | ||
79 | { | ||
80 | write_lock(&fscache_object_list_lock); | ||
81 | |||
82 | BUG_ON(RB_EMPTY_ROOT(&fscache_object_list)); | ||
83 | rb_erase(&obj->objlist_link, &fscache_object_list); | ||
84 | |||
85 | write_unlock(&fscache_object_list_lock); | ||
86 | } | ||
87 | EXPORT_SYMBOL(fscache_object_destroy); | ||
88 | |||
89 | /* | ||
90 | * find the object in the tree on or after the specified index | ||
91 | */ | ||
92 | static struct fscache_object *fscache_objlist_lookup(loff_t *_pos) | ||
93 | { | ||
94 | struct fscache_object *pobj, *obj, *minobj = NULL; | ||
95 | struct rb_node *p; | ||
96 | unsigned long pos; | ||
97 | |||
98 | if (*_pos >= (unsigned long) ERR_PTR(-ENOENT)) | ||
99 | return NULL; | ||
100 | pos = *_pos; | ||
101 | |||
102 | /* banners (can't represent line 0 by pos 0 as that would involve | ||
103 | * returning a NULL pointer) */ | ||
104 | if (pos == 0) | ||
105 | return (struct fscache_object *) ++(*_pos); | ||
106 | if (pos < 3) | ||
107 | return (struct fscache_object *)pos; | ||
108 | |||
109 | pobj = (struct fscache_object *)pos; | ||
110 | p = fscache_object_list.rb_node; | ||
111 | while (p) { | ||
112 | obj = rb_entry(p, struct fscache_object, objlist_link); | ||
113 | if (pobj < obj) { | ||
114 | if (!minobj || minobj > obj) | ||
115 | minobj = obj; | ||
116 | p = p->rb_left; | ||
117 | } else if (pobj > obj) { | ||
118 | p = p->rb_right; | ||
119 | } else { | ||
120 | minobj = obj; | ||
121 | break; | ||
122 | } | ||
123 | obj = NULL; | ||
124 | } | ||
125 | |||
126 | if (!minobj) | ||
127 | *_pos = (unsigned long) ERR_PTR(-ENOENT); | ||
128 | else if (minobj != obj) | ||
129 | *_pos = (unsigned long) minobj; | ||
130 | return minobj; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * set up the iterator to start reading from the first line | ||
135 | */ | ||
136 | static void *fscache_objlist_start(struct seq_file *m, loff_t *_pos) | ||
137 | __acquires(&fscache_object_list_lock) | ||
138 | { | ||
139 | read_lock(&fscache_object_list_lock); | ||
140 | return fscache_objlist_lookup(_pos); | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * move to the next line | ||
145 | */ | ||
146 | static void *fscache_objlist_next(struct seq_file *m, void *v, loff_t *_pos) | ||
147 | { | ||
148 | (*_pos)++; | ||
149 | return fscache_objlist_lookup(_pos); | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * clean up after reading | ||
154 | */ | ||
155 | static void fscache_objlist_stop(struct seq_file *m, void *v) | ||
156 | __releases(&fscache_object_list_lock) | ||
157 | { | ||
158 | read_unlock(&fscache_object_list_lock); | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * display an object | ||
163 | */ | ||
164 | static int fscache_objlist_show(struct seq_file *m, void *v) | ||
165 | { | ||
166 | struct fscache_objlist_data *data = m->private; | ||
167 | struct fscache_object *obj = v; | ||
168 | unsigned long config = data->config; | ||
169 | uint16_t keylen, auxlen; | ||
170 | char _type[3], *type; | ||
171 | bool no_cookie; | ||
172 | u8 *buf = data->buf, *p; | ||
173 | |||
174 | if ((unsigned long) v == 1) { | ||
175 | seq_puts(m, "OBJECT PARENT STAT CHLDN OPS OOP IPR EX READS" | ||
176 | " EM EV F S" | ||
177 | " | NETFS_COOKIE_DEF TY FL NETFS_DATA"); | ||
178 | if (config & (FSCACHE_OBJLIST_CONFIG_KEY | | ||
179 | FSCACHE_OBJLIST_CONFIG_AUX)) | ||
180 | seq_puts(m, " "); | ||
181 | if (config & FSCACHE_OBJLIST_CONFIG_KEY) | ||
182 | seq_puts(m, "OBJECT_KEY"); | ||
183 | if ((config & (FSCACHE_OBJLIST_CONFIG_KEY | | ||
184 | FSCACHE_OBJLIST_CONFIG_AUX)) == | ||
185 | (FSCACHE_OBJLIST_CONFIG_KEY | FSCACHE_OBJLIST_CONFIG_AUX)) | ||
186 | seq_puts(m, ", "); | ||
187 | if (config & FSCACHE_OBJLIST_CONFIG_AUX) | ||
188 | seq_puts(m, "AUX_DATA"); | ||
189 | seq_puts(m, "\n"); | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | if ((unsigned long) v == 2) { | ||
194 | seq_puts(m, "======== ======== ==== ===== === === === == =====" | ||
195 | " == == = =" | ||
196 | " | ================ == == ================"); | ||
197 | if (config & (FSCACHE_OBJLIST_CONFIG_KEY | | ||
198 | FSCACHE_OBJLIST_CONFIG_AUX)) | ||
199 | seq_puts(m, " ================"); | ||
200 | seq_puts(m, "\n"); | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | /* filter out any unwanted objects */ | ||
205 | #define FILTER(criterion, _yes, _no) \ | ||
206 | do { \ | ||
207 | unsigned long yes = FSCACHE_OBJLIST_CONFIG_##_yes; \ | ||
208 | unsigned long no = FSCACHE_OBJLIST_CONFIG_##_no; \ | ||
209 | if (criterion) { \ | ||
210 | if (!(config & yes)) \ | ||
211 | return 0; \ | ||
212 | } else { \ | ||
213 | if (!(config & no)) \ | ||
214 | return 0; \ | ||
215 | } \ | ||
216 | } while(0) | ||
217 | |||
218 | if (~config) { | ||
219 | FILTER(obj->cookie, | ||
220 | COOKIE, NOCOOKIE); | ||
221 | FILTER(obj->state != FSCACHE_OBJECT_ACTIVE || | ||
222 | obj->n_ops != 0 || | ||
223 | obj->n_obj_ops != 0 || | ||
224 | obj->flags || | ||
225 | !list_empty(&obj->dependents), | ||
226 | BUSY, IDLE); | ||
227 | FILTER(test_bit(FSCACHE_OBJECT_PENDING_WRITE, &obj->flags), | ||
228 | PENDWR, NOPENDWR); | ||
229 | FILTER(atomic_read(&obj->n_reads), | ||
230 | READS, NOREADS); | ||
231 | FILTER(obj->events & obj->event_mask, | ||
232 | EVENTS, NOEVENTS); | ||
233 | FILTER(obj->work.flags & ~(1UL << SLOW_WORK_VERY_SLOW), | ||
234 | WORK, NOWORK); | ||
235 | } | ||
236 | |||
237 | seq_printf(m, | ||
238 | "%8x %8x %s %5u %3u %3u %3u %2u %5u %2lx %2lx %1lx %1lx | ", | ||
239 | obj->debug_id, | ||
240 | obj->parent ? obj->parent->debug_id : -1, | ||
241 | fscache_object_states_short[obj->state], | ||
242 | obj->n_children, | ||
243 | obj->n_ops, | ||
244 | obj->n_obj_ops, | ||
245 | obj->n_in_progress, | ||
246 | obj->n_exclusive, | ||
247 | atomic_read(&obj->n_reads), | ||
248 | obj->event_mask & FSCACHE_OBJECT_EVENTS_MASK, | ||
249 | obj->events, | ||
250 | obj->flags, | ||
251 | obj->work.flags); | ||
252 | |||
253 | no_cookie = true; | ||
254 | keylen = auxlen = 0; | ||
255 | if (obj->cookie) { | ||
256 | spin_lock(&obj->lock); | ||
257 | if (obj->cookie) { | ||
258 | switch (obj->cookie->def->type) { | ||
259 | case 0: | ||
260 | type = "IX"; | ||
261 | break; | ||
262 | case 1: | ||
263 | type = "DT"; | ||
264 | break; | ||
265 | default: | ||
266 | sprintf(_type, "%02u", | ||
267 | obj->cookie->def->type); | ||
268 | type = _type; | ||
269 | break; | ||
270 | } | ||
271 | |||
272 | seq_printf(m, "%-16s %s %2lx %16p", | ||
273 | obj->cookie->def->name, | ||
274 | type, | ||
275 | obj->cookie->flags, | ||
276 | obj->cookie->netfs_data); | ||
277 | |||
278 | if (obj->cookie->def->get_key && | ||
279 | config & FSCACHE_OBJLIST_CONFIG_KEY) | ||
280 | keylen = obj->cookie->def->get_key( | ||
281 | obj->cookie->netfs_data, | ||
282 | buf, 400); | ||
283 | |||
284 | if (obj->cookie->def->get_aux && | ||
285 | config & FSCACHE_OBJLIST_CONFIG_AUX) | ||
286 | auxlen = obj->cookie->def->get_aux( | ||
287 | obj->cookie->netfs_data, | ||
288 | buf + keylen, 512 - keylen); | ||
289 | |||
290 | no_cookie = false; | ||
291 | } | ||
292 | spin_unlock(&obj->lock); | ||
293 | |||
294 | if (!no_cookie && (keylen > 0 || auxlen > 0)) { | ||
295 | seq_printf(m, " "); | ||
296 | for (p = buf; keylen > 0; keylen--) | ||
297 | seq_printf(m, "%02x", *p++); | ||
298 | if (auxlen > 0) { | ||
299 | if (config & FSCACHE_OBJLIST_CONFIG_KEY) | ||
300 | seq_printf(m, ", "); | ||
301 | for (; auxlen > 0; auxlen--) | ||
302 | seq_printf(m, "%02x", *p++); | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | |||
307 | if (no_cookie) | ||
308 | seq_printf(m, "<no_cookie>\n"); | ||
309 | else | ||
310 | seq_printf(m, "\n"); | ||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | static const struct seq_operations fscache_objlist_ops = { | ||
315 | .start = fscache_objlist_start, | ||
316 | .stop = fscache_objlist_stop, | ||
317 | .next = fscache_objlist_next, | ||
318 | .show = fscache_objlist_show, | ||
319 | }; | ||
320 | |||
321 | /* | ||
322 | * get the configuration for filtering the list | ||
323 | */ | ||
324 | static void fscache_objlist_config(struct fscache_objlist_data *data) | ||
325 | { | ||
326 | #ifdef CONFIG_KEYS | ||
327 | struct user_key_payload *confkey; | ||
328 | unsigned long config; | ||
329 | struct key *key; | ||
330 | const char *buf; | ||
331 | int len; | ||
332 | |||
333 | key = request_key(&key_type_user, "fscache:objlist", NULL); | ||
334 | if (IS_ERR(key)) | ||
335 | goto no_config; | ||
336 | |||
337 | config = 0; | ||
338 | rcu_read_lock(); | ||
339 | |||
340 | confkey = key->payload.data; | ||
341 | buf = confkey->data; | ||
342 | |||
343 | for (len = confkey->datalen - 1; len >= 0; len--) { | ||
344 | switch (buf[len]) { | ||
345 | case 'K': config |= FSCACHE_OBJLIST_CONFIG_KEY; break; | ||
346 | case 'A': config |= FSCACHE_OBJLIST_CONFIG_AUX; break; | ||
347 | case 'C': config |= FSCACHE_OBJLIST_CONFIG_COOKIE; break; | ||
348 | case 'c': config |= FSCACHE_OBJLIST_CONFIG_NOCOOKIE; break; | ||
349 | case 'B': config |= FSCACHE_OBJLIST_CONFIG_BUSY; break; | ||
350 | case 'b': config |= FSCACHE_OBJLIST_CONFIG_IDLE; break; | ||
351 | case 'W': config |= FSCACHE_OBJLIST_CONFIG_PENDWR; break; | ||
352 | case 'w': config |= FSCACHE_OBJLIST_CONFIG_NOPENDWR; break; | ||
353 | case 'R': config |= FSCACHE_OBJLIST_CONFIG_READS; break; | ||
354 | case 'r': config |= FSCACHE_OBJLIST_CONFIG_NOREADS; break; | ||
355 | case 'S': config |= FSCACHE_OBJLIST_CONFIG_WORK; break; | ||
356 | case 's': config |= FSCACHE_OBJLIST_CONFIG_NOWORK; break; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | rcu_read_unlock(); | ||
361 | key_put(key); | ||
362 | |||
363 | if (!(config & (FSCACHE_OBJLIST_CONFIG_COOKIE | FSCACHE_OBJLIST_CONFIG_NOCOOKIE))) | ||
364 | config |= FSCACHE_OBJLIST_CONFIG_COOKIE | FSCACHE_OBJLIST_CONFIG_NOCOOKIE; | ||
365 | if (!(config & (FSCACHE_OBJLIST_CONFIG_BUSY | FSCACHE_OBJLIST_CONFIG_IDLE))) | ||
366 | config |= FSCACHE_OBJLIST_CONFIG_BUSY | FSCACHE_OBJLIST_CONFIG_IDLE; | ||
367 | if (!(config & (FSCACHE_OBJLIST_CONFIG_PENDWR | FSCACHE_OBJLIST_CONFIG_NOPENDWR))) | ||
368 | config |= FSCACHE_OBJLIST_CONFIG_PENDWR | FSCACHE_OBJLIST_CONFIG_NOPENDWR; | ||
369 | if (!(config & (FSCACHE_OBJLIST_CONFIG_READS | FSCACHE_OBJLIST_CONFIG_NOREADS))) | ||
370 | config |= FSCACHE_OBJLIST_CONFIG_READS | FSCACHE_OBJLIST_CONFIG_NOREADS; | ||
371 | if (!(config & (FSCACHE_OBJLIST_CONFIG_EVENTS | FSCACHE_OBJLIST_CONFIG_NOEVENTS))) | ||
372 | config |= FSCACHE_OBJLIST_CONFIG_EVENTS | FSCACHE_OBJLIST_CONFIG_NOEVENTS; | ||
373 | if (!(config & (FSCACHE_OBJLIST_CONFIG_WORK | FSCACHE_OBJLIST_CONFIG_NOWORK))) | ||
374 | config |= FSCACHE_OBJLIST_CONFIG_WORK | FSCACHE_OBJLIST_CONFIG_NOWORK; | ||
375 | |||
376 | data->config = config; | ||
377 | return; | ||
378 | |||
379 | no_config: | ||
380 | #endif | ||
381 | data->config = ULONG_MAX; | ||
382 | } | ||
383 | |||
384 | /* | ||
385 | * open "/proc/fs/fscache/objects" to provide a list of active objects | ||
386 | * - can be configured by a user-defined key added to the caller's keyrings | ||
387 | */ | ||
388 | static int fscache_objlist_open(struct inode *inode, struct file *file) | ||
389 | { | ||
390 | struct fscache_objlist_data *data; | ||
391 | struct seq_file *m; | ||
392 | int ret; | ||
393 | |||
394 | ret = seq_open(file, &fscache_objlist_ops); | ||
395 | if (ret < 0) | ||
396 | return ret; | ||
397 | |||
398 | m = file->private_data; | ||
399 | |||
400 | /* buffer for key extraction */ | ||
401 | data = kmalloc(sizeof(struct fscache_objlist_data), GFP_KERNEL); | ||
402 | if (!data) { | ||
403 | seq_release(inode, file); | ||
404 | return -ENOMEM; | ||
405 | } | ||
406 | |||
407 | /* get the configuration key */ | ||
408 | fscache_objlist_config(data); | ||
409 | |||
410 | m->private = data; | ||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | /* | ||
415 | * clean up on close | ||
416 | */ | ||
417 | static int fscache_objlist_release(struct inode *inode, struct file *file) | ||
418 | { | ||
419 | struct seq_file *m = file->private_data; | ||
420 | |||
421 | kfree(m->private); | ||
422 | m->private = NULL; | ||
423 | return seq_release(inode, file); | ||
424 | } | ||
425 | |||
426 | const struct file_operations fscache_objlist_fops = { | ||
427 | .owner = THIS_MODULE, | ||
428 | .open = fscache_objlist_open, | ||
429 | .read = seq_read, | ||
430 | .llseek = seq_lseek, | ||
431 | .release = fscache_objlist_release, | ||
432 | }; | ||
diff --git a/fs/fscache/object.c b/fs/fscache/object.c index 615b63dd9ecc..ad1644f073bd 100644 --- a/fs/fscache/object.c +++ b/fs/fscache/object.c | |||
@@ -34,7 +34,7 @@ const char *fscache_object_states[FSCACHE_OBJECT__NSTATES] = { | |||
34 | }; | 34 | }; |
35 | EXPORT_SYMBOL(fscache_object_states); | 35 | EXPORT_SYMBOL(fscache_object_states); |
36 | 36 | ||
37 | static const char fscache_object_states_short[FSCACHE_OBJECT__NSTATES][5] = { | 37 | const char fscache_object_states_short[FSCACHE_OBJECT__NSTATES][5] = { |
38 | [FSCACHE_OBJECT_INIT] = "INIT", | 38 | [FSCACHE_OBJECT_INIT] = "INIT", |
39 | [FSCACHE_OBJECT_LOOKING_UP] = "LOOK", | 39 | [FSCACHE_OBJECT_LOOKING_UP] = "LOOK", |
40 | [FSCACHE_OBJECT_CREATING] = "CRTN", | 40 | [FSCACHE_OBJECT_CREATING] = "CRTN", |
diff --git a/fs/fscache/operation.c b/fs/fscache/operation.c index 91bbe6f0377c..09e43b6e822f 100644 --- a/fs/fscache/operation.c +++ b/fs/fscache/operation.c | |||
@@ -322,6 +322,9 @@ void fscache_put_operation(struct fscache_operation *op) | |||
322 | 322 | ||
323 | object = op->object; | 323 | object = op->object; |
324 | 324 | ||
325 | if (test_bit(FSCACHE_OP_DEC_READ_CNT, &op->flags)) | ||
326 | atomic_dec(&object->n_reads); | ||
327 | |||
325 | /* now... we may get called with the object spinlock held, so we | 328 | /* now... we may get called with the object spinlock held, so we |
326 | * complete the cleanup here only if we can immediately acquire the | 329 | * complete the cleanup here only if we can immediately acquire the |
327 | * lock, and defer it otherwise */ | 330 | * lock, and defer it otherwise */ |
diff --git a/fs/fscache/page.c b/fs/fscache/page.c index e8bbc395cef6..c5973e38ce39 100644 --- a/fs/fscache/page.c +++ b/fs/fscache/page.c | |||
@@ -275,6 +275,9 @@ int __fscache_read_or_alloc_page(struct fscache_cookie *cookie, | |||
275 | 275 | ||
276 | ASSERTCMP(object->state, >, FSCACHE_OBJECT_LOOKING_UP); | 276 | ASSERTCMP(object->state, >, FSCACHE_OBJECT_LOOKING_UP); |
277 | 277 | ||
278 | atomic_inc(&object->n_reads); | ||
279 | set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags); | ||
280 | |||
278 | if (fscache_submit_op(object, &op->op) < 0) | 281 | if (fscache_submit_op(object, &op->op) < 0) |
279 | goto nobufs_unlock; | 282 | goto nobufs_unlock; |
280 | spin_unlock(&cookie->lock); | 283 | spin_unlock(&cookie->lock); |
@@ -386,6 +389,9 @@ int __fscache_read_or_alloc_pages(struct fscache_cookie *cookie, | |||
386 | object = hlist_entry(cookie->backing_objects.first, | 389 | object = hlist_entry(cookie->backing_objects.first, |
387 | struct fscache_object, cookie_link); | 390 | struct fscache_object, cookie_link); |
388 | 391 | ||
392 | atomic_inc(&object->n_reads); | ||
393 | set_bit(FSCACHE_OP_DEC_READ_CNT, &op->op.flags); | ||
394 | |||
389 | if (fscache_submit_op(object, &op->op) < 0) | 395 | if (fscache_submit_op(object, &op->op) < 0) |
390 | goto nobufs_unlock; | 396 | goto nobufs_unlock; |
391 | spin_unlock(&cookie->lock); | 397 | spin_unlock(&cookie->lock); |
diff --git a/fs/fscache/proc.c b/fs/fscache/proc.c index beeab44bc31a..1d9e4951a597 100644 --- a/fs/fscache/proc.c +++ b/fs/fscache/proc.c | |||
@@ -37,10 +37,20 @@ int __init fscache_proc_init(void) | |||
37 | goto error_histogram; | 37 | goto error_histogram; |
38 | #endif | 38 | #endif |
39 | 39 | ||
40 | #ifdef CONFIG_FSCACHE_OBJECT_LIST | ||
41 | if (!proc_create("fs/fscache/objects", S_IFREG | 0444, NULL, | ||
42 | &fscache_objlist_fops)) | ||
43 | goto error_objects; | ||
44 | #endif | ||
45 | |||
40 | _leave(" = 0"); | 46 | _leave(" = 0"); |
41 | return 0; | 47 | return 0; |
42 | 48 | ||
49 | #ifdef CONFIG_FSCACHE_OBJECT_LIST | ||
50 | error_objects: | ||
51 | #endif | ||
43 | #ifdef CONFIG_FSCACHE_HISTOGRAM | 52 | #ifdef CONFIG_FSCACHE_HISTOGRAM |
53 | remove_proc_entry("fs/fscache/histogram", NULL); | ||
44 | error_histogram: | 54 | error_histogram: |
45 | #endif | 55 | #endif |
46 | #ifdef CONFIG_FSCACHE_STATS | 56 | #ifdef CONFIG_FSCACHE_STATS |
@@ -58,6 +68,9 @@ error_dir: | |||
58 | */ | 68 | */ |
59 | void fscache_proc_cleanup(void) | 69 | void fscache_proc_cleanup(void) |
60 | { | 70 | { |
71 | #ifdef CONFIG_FSCACHE_OBJECT_LIST | ||
72 | remove_proc_entry("fs/fscache/objects", NULL); | ||
73 | #endif | ||
61 | #ifdef CONFIG_FSCACHE_HISTOGRAM | 74 | #ifdef CONFIG_FSCACHE_HISTOGRAM |
62 | remove_proc_entry("fs/fscache/histogram", NULL); | 75 | remove_proc_entry("fs/fscache/histogram", NULL); |
63 | #endif | 76 | #endif |
diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 7a9847ccd192..184cbdfbcc99 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h | |||
@@ -91,6 +91,8 @@ struct fscache_operation { | |||
91 | #define FSCACHE_OP_WAITING 4 /* cleared when op is woken */ | 91 | #define FSCACHE_OP_WAITING 4 /* cleared when op is woken */ |
92 | #define FSCACHE_OP_EXCLUSIVE 5 /* exclusive op, other ops must wait */ | 92 | #define FSCACHE_OP_EXCLUSIVE 5 /* exclusive op, other ops must wait */ |
93 | #define FSCACHE_OP_DEAD 6 /* op is now dead */ | 93 | #define FSCACHE_OP_DEAD 6 /* op is now dead */ |
94 | #define FSCACHE_OP_DEC_READ_CNT 7 /* decrement object->n_reads on destruction */ | ||
95 | #define FSCACHE_OP_KEEP_FLAGS 0xc0 /* flags to keep when repurposing an op */ | ||
94 | 96 | ||
95 | atomic_t usage; | 97 | atomic_t usage; |
96 | unsigned debug_id; /* debugging ID */ | 98 | unsigned debug_id; /* debugging ID */ |
@@ -357,6 +359,7 @@ struct fscache_object { | |||
357 | int n_obj_ops; /* number of object ops outstanding on object */ | 359 | int n_obj_ops; /* number of object ops outstanding on object */ |
358 | int n_in_progress; /* number of ops in progress */ | 360 | int n_in_progress; /* number of ops in progress */ |
359 | int n_exclusive; /* number of exclusive ops queued */ | 361 | int n_exclusive; /* number of exclusive ops queued */ |
362 | atomic_t n_reads; /* number of read ops in progress */ | ||
360 | spinlock_t lock; /* state and operations lock */ | 363 | spinlock_t lock; /* state and operations lock */ |
361 | 364 | ||
362 | unsigned long lookup_jif; /* time at which lookup started */ | 365 | unsigned long lookup_jif; /* time at which lookup started */ |
@@ -370,6 +373,7 @@ struct fscache_object { | |||
370 | #define FSCACHE_OBJECT_EV_RELEASE 4 /* T if netfs requested object release */ | 373 | #define FSCACHE_OBJECT_EV_RELEASE 4 /* T if netfs requested object release */ |
371 | #define FSCACHE_OBJECT_EV_RETIRE 5 /* T if netfs requested object retirement */ | 374 | #define FSCACHE_OBJECT_EV_RETIRE 5 /* T if netfs requested object retirement */ |
372 | #define FSCACHE_OBJECT_EV_WITHDRAW 6 /* T if cache requested object withdrawal */ | 375 | #define FSCACHE_OBJECT_EV_WITHDRAW 6 /* T if cache requested object withdrawal */ |
376 | #define FSCACHE_OBJECT_EVENTS_MASK 0x7f /* mask of all events*/ | ||
373 | 377 | ||
374 | unsigned long flags; | 378 | unsigned long flags; |
375 | #define FSCACHE_OBJECT_LOCK 0 /* T if object is busy being processed */ | 379 | #define FSCACHE_OBJECT_LOCK 0 /* T if object is busy being processed */ |
@@ -385,6 +389,9 @@ struct fscache_object { | |||
385 | struct list_head dependents; /* FIFO of dependent objects */ | 389 | struct list_head dependents; /* FIFO of dependent objects */ |
386 | struct list_head dep_link; /* link in parent's dependents list */ | 390 | struct list_head dep_link; /* link in parent's dependents list */ |
387 | struct list_head pending_ops; /* unstarted operations on this object */ | 391 | struct list_head pending_ops; /* unstarted operations on this object */ |
392 | #ifdef CONFIG_FSCACHE_OBJECT_LIST | ||
393 | struct rb_node objlist_link; /* link in global object list */ | ||
394 | #endif | ||
388 | pgoff_t store_limit; /* current storage limit */ | 395 | pgoff_t store_limit; /* current storage limit */ |
389 | }; | 396 | }; |
390 | 397 | ||
@@ -434,6 +441,12 @@ void fscache_object_init(struct fscache_object *object, | |||
434 | extern void fscache_object_lookup_negative(struct fscache_object *object); | 441 | extern void fscache_object_lookup_negative(struct fscache_object *object); |
435 | extern void fscache_obtained_object(struct fscache_object *object); | 442 | extern void fscache_obtained_object(struct fscache_object *object); |
436 | 443 | ||
444 | #ifdef CONFIG_FSCACHE_OBJECT_LIST | ||
445 | extern void fscache_object_destroy(struct fscache_object *object); | ||
446 | #else | ||
447 | #define fscache_object_destroy(object) do {} while(0) | ||
448 | #endif | ||
449 | |||
437 | /** | 450 | /** |
438 | * fscache_object_destroyed - Note destruction of an object in a cache | 451 | * fscache_object_destroyed - Note destruction of an object in a cache |
439 | * @cache: The cache from which the object came | 452 | * @cache: The cache from which the object came |