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 20static struct rb_root fscache_object_list; 21static DEFINE_RWLOCK(fscache_object_list_lock); 22 23struct 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 */ 48void 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 */ 78void 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 */ 94static 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 */ 138static 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 */ 148static 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 */ 157static 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 */ 166static 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 sprintf(_type, "%02u", cookie->def->type); 266 type = _type; 267 break; 268 } 269 270 seq_printf(m, "%-16s %s %2lx %16p", 271 cookie->def->name, 272 type, 273 cookie->flags, 274 cookie->netfs_data); 275 276 if (cookie->def->get_key && 277 config & FSCACHE_OBJLIST_CONFIG_KEY) 278 keylen = cookie->def->get_key(cookie->netfs_data, 279 buf, 400); 280 281 if (cookie->def->get_aux && 282 config & FSCACHE_OBJLIST_CONFIG_AUX) 283 auxlen = cookie->def->get_aux(cookie->netfs_data, 284 buf + keylen, 512 - keylen); 285 fscache_unuse_cookie(obj); 286 287 if (keylen > 0 || auxlen > 0) { 288 seq_puts(m, " "); 289 for (p = buf; keylen > 0; keylen--) 290 seq_printf(m, "%02x", *p++); 291 if (auxlen > 0) { 292 if (config & FSCACHE_OBJLIST_CONFIG_KEY) 293 seq_puts(m, ", "); 294 for (; auxlen > 0; auxlen--) 295 seq_printf(m, "%02x", *p++); 296 } 297 } 298 299 seq_puts(m, "\n"); 300 } else { 301 seq_puts(m, "<no_netfs>\n"); 302 } 303 return 0; 304} 305 306static const struct seq_operations fscache_objlist_ops = { 307 .start = fscache_objlist_start, 308 .stop = fscache_objlist_stop, 309 .next = fscache_objlist_next, 310 .show = fscache_objlist_show, 311}; 312 313/* 314 * get the configuration for filtering the list 315 */ 316static void fscache_objlist_config(struct fscache_objlist_data *data) 317{ 318#ifdef CONFIG_KEYS 319 struct user_key_payload *confkey; 320 unsigned long config; 321 struct key *key; 322 const char *buf; 323 int len; 324 325 key = request_key(&key_type_user, "fscache:objlist", NULL); 326 if (IS_ERR(key)) 327 goto no_config; 328 329 config = 0; 330 rcu_read_lock(); 331 332 confkey = key->payload.data; 333 buf = confkey->data; 334 335 for (len = confkey->datalen - 1; len >= 0; len--) { 336 switch (buf[len]) { 337 case 'K': config |= FSCACHE_OBJLIST_CONFIG_KEY; break; 338 case 'A': config |= FSCACHE_OBJLIST_CONFIG_AUX; break; 339 case 'C': config |= FSCACHE_OBJLIST_CONFIG_COOKIE; break; 340 case 'c': config |= FSCACHE_OBJLIST_CONFIG_NOCOOKIE; break; 341 case 'B': config |= FSCACHE_OBJLIST_CONFIG_BUSY; break; 342 case 'b': config |= FSCACHE_OBJLIST_CONFIG_IDLE; break; 343 case 'W': config |= FSCACHE_OBJLIST_CONFIG_PENDWR; break; 344 case 'w': config |= FSCACHE_OBJLIST_CONFIG_NOPENDWR; break; 345 case 'R': config |= FSCACHE_OBJLIST_CONFIG_READS; break; 346 case 'r': config |= FSCACHE_OBJLIST_CONFIG_NOREADS; break; 347 case 'S': config |= FSCACHE_OBJLIST_CONFIG_WORK; break; 348 case 's': config |= FSCACHE_OBJLIST_CONFIG_NOWORK; break; 349 } 350 } 351 352 rcu_read_unlock(); 353 key_put(key); 354 355 if (!(config & (FSCACHE_OBJLIST_CONFIG_COOKIE | FSCACHE_OBJLIST_CONFIG_NOCOOKIE))) 356 config |= FSCACHE_OBJLIST_CONFIG_COOKIE | FSCACHE_OBJLIST_CONFIG_NOCOOKIE; 357 if (!(config & (FSCACHE_OBJLIST_CONFIG_BUSY | FSCACHE_OBJLIST_CONFIG_IDLE))) 358 config |= FSCACHE_OBJLIST_CONFIG_BUSY | FSCACHE_OBJLIST_CONFIG_IDLE; 359 if (!(config & (FSCACHE_OBJLIST_CONFIG_PENDWR | FSCACHE_OBJLIST_CONFIG_NOPENDWR))) 360 config |= FSCACHE_OBJLIST_CONFIG_PENDWR | FSCACHE_OBJLIST_CONFIG_NOPENDWR; 361 if (!(config & (FSCACHE_OBJLIST_CONFIG_READS | FSCACHE_OBJLIST_CONFIG_NOREADS))) 362 config |= FSCACHE_OBJLIST_CONFIG_READS | FSCACHE_OBJLIST_CONFIG_NOREADS; 363 if (!(config & (FSCACHE_OBJLIST_CONFIG_EVENTS | FSCACHE_OBJLIST_CONFIG_NOEVENTS))) 364 config |= FSCACHE_OBJLIST_CONFIG_EVENTS | FSCACHE_OBJLIST_CONFIG_NOEVENTS; 365 if (!(config & (FSCACHE_OBJLIST_CONFIG_WORK | FSCACHE_OBJLIST_CONFIG_NOWORK))) 366 config |= FSCACHE_OBJLIST_CONFIG_WORK | FSCACHE_OBJLIST_CONFIG_NOWORK; 367 368 data->config = config; 369 return; 370 371no_config: 372#endif 373 data->config = ULONG_MAX; 374} 375 376/* 377 * open "/proc/fs/fscache/objects" to provide a list of active objects 378 * - can be configured by a user-defined key added to the caller's keyrings 379 */ 380static int fscache_objlist_open(struct inode *inode, struct file *file) 381{ 382 struct fscache_objlist_data *data; 383 384 data = __seq_open_private(file, &fscache_objlist_ops, sizeof(*data)); 385 if (!data) 386 return -ENOMEM; 387 388 /* get the configuration key */ 389 fscache_objlist_config(data); 390 391 return 0; 392} 393 394/* 395 * clean up on close 396 */ 397static int fscache_objlist_release(struct inode *inode, struct file *file) 398{ 399 struct seq_file *m = file->private_data; 400 401 kfree(m->private); 402 m->private = NULL; 403 return seq_release(inode, file); 404} 405 406const struct file_operations fscache_objlist_fops = { 407 .owner = THIS_MODULE, 408 .open = fscache_objlist_open, 409 .read = seq_read, 410 .llseek = seq_lseek, 411 .release = fscache_objlist_release, 412}; 413