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/slab.h> 16 #include <linux/key.h> 17 #include <keys/user-type.h> 18 #include "internal.h" 19 20 static struct rb_root fscache_object_list; 21 static DEFINE_RWLOCK(fscache_object_list_lock); 22 23 struct fscache_objlist_data { 24 unsigned long config; /* display configuration */ 25 #define FSCACHE_OBJLIST_CONFIG_KEY 0x00000001 /* show object keys */ 26 #define FSCACHE_OBJLIST_CONFIG_AUX 0x00000002 /* show object auxdata */ 27 #define FSCACHE_OBJLIST_CONFIG_COOKIE 0x00000004 /* show objects with cookies */ 28 #define FSCACHE_OBJLIST_CONFIG_NOCOOKIE 0x00000008 /* show objects without cookies */ 29 #define FSCACHE_OBJLIST_CONFIG_BUSY 0x00000010 /* show busy objects */ 30 #define FSCACHE_OBJLIST_CONFIG_IDLE 0x00000020 /* show idle objects */ 31 #define FSCACHE_OBJLIST_CONFIG_PENDWR 0x00000040 /* show objects with pending writes */ 32 #define FSCACHE_OBJLIST_CONFIG_NOPENDWR 0x00000080 /* show objects without pending writes */ 33 #define FSCACHE_OBJLIST_CONFIG_READS 0x00000100 /* show objects with active reads */ 34 #define FSCACHE_OBJLIST_CONFIG_NOREADS 0x00000200 /* show objects without active reads */ 35 #define FSCACHE_OBJLIST_CONFIG_EVENTS 0x00000400 /* show objects with events */ 36 #define FSCACHE_OBJLIST_CONFIG_NOEVENTS 0x00000800 /* show objects without no events */ 37 #define FSCACHE_OBJLIST_CONFIG_WORK 0x00001000 /* show objects with work */ 38 #define FSCACHE_OBJLIST_CONFIG_NOWORK 0x00002000 /* show objects without work */ 39 40 u8 buf[512]; /* key and aux data buffer */ 41 }; 42 43 /* 44 * Add an object to the object list 45 * - we use the address of the fscache_object structure as the key into the 46 * tree 47 */ 48 void fscache_objlist_add(struct fscache_object *obj) 49 { 50 struct fscache_object *xobj; 51 struct rb_node **p = &fscache_object_list.rb_node, *parent = NULL; 52 53 ASSERT(RB_EMPTY_NODE(&obj->objlist_link)); 54 55 write_lock(&fscache_object_list_lock); 56 57 while (*p) { 58 parent = *p; 59 xobj = rb_entry(parent, struct fscache_object, objlist_link); 60 61 if (obj < xobj) 62 p = &(*p)->rb_left; 63 else if (obj > xobj) 64 p = &(*p)->rb_right; 65 else 66 BUG(); 67 } 68 69 rb_link_node(&obj->objlist_link, parent, p); 70 rb_insert_color(&obj->objlist_link, &fscache_object_list); 71 72 write_unlock(&fscache_object_list_lock); 73 } 74 75 /* 76 * Remove an object from the object list. 77 */ 78 void fscache_objlist_remove(struct fscache_object *obj) 79 { 80 if (RB_EMPTY_NODE(&obj->objlist_link)) 81 return; 82 83 write_lock(&fscache_object_list_lock); 84 85 BUG_ON(RB_EMPTY_ROOT(&fscache_object_list)); 86 rb_erase(&obj->objlist_link, &fscache_object_list); 87 88 write_unlock(&fscache_object_list_lock); 89 } 90 91 /* 92 * find the object in the tree on or after the specified index 93 */ 94 static struct fscache_object *fscache_objlist_lookup(loff_t *_pos) 95 { 96 struct fscache_object *pobj, *obj = NULL, *minobj = NULL; 97 struct rb_node *p; 98 unsigned long pos; 99 100 if (*_pos >= (unsigned long) ERR_PTR(-ENOENT)) 101 return NULL; 102 pos = *_pos; 103 104 /* banners (can't represent line 0 by pos 0 as that would involve 105 * returning a NULL pointer) */ 106 if (pos == 0) 107 return (struct fscache_object *)(long)++(*_pos); 108 if (pos < 3) 109 return (struct fscache_object *)pos; 110 111 pobj = (struct fscache_object *)pos; 112 p = fscache_object_list.rb_node; 113 while (p) { 114 obj = rb_entry(p, struct fscache_object, objlist_link); 115 if (pobj < obj) { 116 if (!minobj || minobj > obj) 117 minobj = obj; 118 p = p->rb_left; 119 } else if (pobj > obj) { 120 p = p->rb_right; 121 } else { 122 minobj = obj; 123 break; 124 } 125 obj = NULL; 126 } 127 128 if (!minobj) 129 *_pos = (unsigned long) ERR_PTR(-ENOENT); 130 else if (minobj != obj) 131 *_pos = (unsigned long) minobj; 132 return minobj; 133 } 134 135 /* 136 * set up the iterator to start reading from the first line 137 */ 138 static void *fscache_objlist_start(struct seq_file *m, loff_t *_pos) 139 __acquires(&fscache_object_list_lock) 140 { 141 read_lock(&fscache_object_list_lock); 142 return fscache_objlist_lookup(_pos); 143 } 144 145 /* 146 * move to the next line 147 */ 148 static void *fscache_objlist_next(struct seq_file *m, void *v, loff_t *_pos) 149 { 150 (*_pos)++; 151 return fscache_objlist_lookup(_pos); 152 } 153 154 /* 155 * clean up after reading 156 */ 157 static void fscache_objlist_stop(struct seq_file *m, void *v) 158 __releases(&fscache_object_list_lock) 159 { 160 read_unlock(&fscache_object_list_lock); 161 } 162 163 /* 164 * display an object 165 */ 166 static int fscache_objlist_show(struct seq_file *m, void *v) 167 { 168 struct fscache_objlist_data *data = m->private; 169 struct fscache_object *obj = v; 170 struct fscache_cookie *cookie; 171 unsigned long config = data->config; 172 char _type[3], *type; 173 u8 *buf = data->buf, *p; 174 175 if ((unsigned long) v == 1) { 176 seq_puts(m, "OBJECT PARENT STAT CHLDN OPS OOP IPR EX READS" 177 " EM EV FL S" 178 " | NETFS_COOKIE_DEF TY FL NETFS_DATA"); 179 if (config & (FSCACHE_OBJLIST_CONFIG_KEY | 180 FSCACHE_OBJLIST_CONFIG_AUX)) 181 seq_puts(m, " "); 182 if (config & FSCACHE_OBJLIST_CONFIG_KEY) 183 seq_puts(m, "OBJECT_KEY"); 184 if ((config & (FSCACHE_OBJLIST_CONFIG_KEY | 185 FSCACHE_OBJLIST_CONFIG_AUX)) == 186 (FSCACHE_OBJLIST_CONFIG_KEY | FSCACHE_OBJLIST_CONFIG_AUX)) 187 seq_puts(m, ", "); 188 if (config & FSCACHE_OBJLIST_CONFIG_AUX) 189 seq_puts(m, "AUX_DATA"); 190 seq_puts(m, "\n"); 191 return 0; 192 } 193 194 if ((unsigned long) v == 2) { 195 seq_puts(m, "======== ======== ==== ===== === === === == =====" 196 " == == == =" 197 " | ================ == == ================"); 198 if (config & (FSCACHE_OBJLIST_CONFIG_KEY | 199 FSCACHE_OBJLIST_CONFIG_AUX)) 200 seq_puts(m, " ================"); 201 seq_puts(m, "\n"); 202 return 0; 203 } 204 205 /* filter out any unwanted objects */ 206 #define FILTER(criterion, _yes, _no) \ 207 do { \ 208 unsigned long yes = FSCACHE_OBJLIST_CONFIG_##_yes; \ 209 unsigned long no = FSCACHE_OBJLIST_CONFIG_##_no; \ 210 if (criterion) { \ 211 if (!(config & yes)) \ 212 return 0; \ 213 } else { \ 214 if (!(config & no)) \ 215 return 0; \ 216 } \ 217 } while(0) 218 219 cookie = obj->cookie; 220 if (~config) { 221 FILTER(cookie->def, 222 COOKIE, NOCOOKIE); 223 FILTER(fscache_object_is_active(obj) || 224 obj->n_ops != 0 || 225 obj->n_obj_ops != 0 || 226 obj->flags || 227 !list_empty(&obj->dependents), 228 BUSY, IDLE); 229 FILTER(test_bit(FSCACHE_OBJECT_PENDING_WRITE, &obj->flags), 230 PENDWR, NOPENDWR); 231 FILTER(atomic_read(&obj->n_reads), 232 READS, NOREADS); 233 FILTER(obj->events & obj->event_mask, 234 EVENTS, NOEVENTS); 235 FILTER(work_busy(&obj->work), WORK, NOWORK); 236 } 237 238 seq_printf(m, 239 "%8x %8x %s %5u %3u %3u %3u %2u %5u %2lx %2lx %2lx %1x | ", 240 obj->debug_id, 241 obj->parent ? obj->parent->debug_id : -1, 242 obj->state->short_name, 243 obj->n_children, 244 obj->n_ops, 245 obj->n_obj_ops, 246 obj->n_in_progress, 247 obj->n_exclusive, 248 atomic_read(&obj->n_reads), 249 obj->event_mask, 250 obj->events, 251 obj->flags, 252 work_busy(&obj->work)); 253 254 if (fscache_use_cookie(obj)) { 255 uint16_t keylen = 0, auxlen = 0; 256 257 switch (cookie->def->type) { 258 case 0: 259 type = "IX"; 260 break; 261 case 1: 262 type = "DT"; 263 break; 264 default: 265 snprintf(_type, sizeof(_type), "%02u", 266 cookie->def->type); 267 type = _type; 268 break; 269 } 270 271 seq_printf(m, "%-16s %s %2lx %16p", 272 cookie->def->name, 273 type, 274 cookie->flags, 275 cookie->netfs_data); 276 277 if (cookie->def->get_key && 278 config & FSCACHE_OBJLIST_CONFIG_KEY) 279 keylen = cookie->def->get_key(cookie->netfs_data, 280 buf, 400); 281 282 if (cookie->def->get_aux && 283 config & FSCACHE_OBJLIST_CONFIG_AUX) 284 auxlen = cookie->def->get_aux(cookie->netfs_data, 285 buf + keylen, 512 - keylen); 286 fscache_unuse_cookie(obj); 287 288 if (keylen > 0 || auxlen > 0) { 289 seq_puts(m, " "); 290 for (p = buf; keylen > 0; keylen--) 291 seq_printf(m, "%02x", *p++); 292 if (auxlen > 0) { 293 if (config & FSCACHE_OBJLIST_CONFIG_KEY) 294 seq_puts(m, ", "); 295 for (; auxlen > 0; auxlen--) 296 seq_printf(m, "%02x", *p++); 297 } 298 } 299 300 seq_puts(m, "\n"); 301 } else { 302 seq_puts(m, "<no_netfs>\n"); 303 } 304 return 0; 305 } 306 307 static const struct seq_operations fscache_objlist_ops = { 308 .start = fscache_objlist_start, 309 .stop = fscache_objlist_stop, 310 .next = fscache_objlist_next, 311 .show = fscache_objlist_show, 312 }; 313 314 /* 315 * get the configuration for filtering the list 316 */ 317 static void fscache_objlist_config(struct fscache_objlist_data *data) 318 { 319 #ifdef CONFIG_KEYS 320 const struct user_key_payload *confkey; 321 unsigned long config; 322 struct key *key; 323 const char *buf; 324 int len; 325 326 key = request_key(&key_type_user, "fscache:objlist", NULL); 327 if (IS_ERR(key)) 328 goto no_config; 329 330 config = 0; 331 rcu_read_lock(); 332 333 confkey = user_key_payload_rcu(key); 334 if (!confkey) { 335 /* key was revoked */ 336 rcu_read_unlock(); 337 key_put(key); 338 goto no_config; 339 } 340 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 392 data = __seq_open_private(file, &fscache_objlist_ops, sizeof(*data)); 393 if (!data) 394 return -ENOMEM; 395 396 /* get the configuration key */ 397 fscache_objlist_config(data); 398 399 return 0; 400 } 401 402 /* 403 * clean up on close 404 */ 405 static int fscache_objlist_release(struct inode *inode, struct file *file) 406 { 407 struct seq_file *m = file->private_data; 408 409 kfree(m->private); 410 m->private = NULL; 411 return seq_release(inode, file); 412 } 413 414 const struct file_operations fscache_objlist_fops = { 415 .open = fscache_objlist_open, 416 .read = seq_read, 417 .llseek = seq_lseek, 418 .release = fscache_objlist_release, 419 }; 420
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.