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 write_lock(&fscache_object_list_lock); 54 55 while (*p) { 56 parent = *p; 57 xobj = rb_entry(parent, struct fscache_object, objlist_link); 58 59 if (obj < xobj) 60 p = &(*p)->rb_left; 61 else if (obj > xobj) 62 p = &(*p)->rb_right; 63 else 64 BUG(); 65 } 66 67 rb_link_node(&obj->objlist_link, parent, p); 68 rb_insert_color(&obj->objlist_link, &fscache_object_list); 69 70 write_unlock(&fscache_object_list_lock); 71 } 72 73 /** 74 * fscache_object_destroy - Note that a cache object is about to be destroyed 75 * @object: The object to be destroyed 76 * 77 * Note the imminent destruction and deallocation of a cache object record. 78 */ 79 void fscache_object_destroy(struct fscache_object *obj) 80 { 81 write_lock(&fscache_object_list_lock); 82 83 BUG_ON(RB_EMPTY_ROOT(&fscache_object_list)); 84 rb_erase(&obj->objlist_link, &fscache_object_list); 85 86 write_unlock(&fscache_object_list_lock); 87 } 88 EXPORT_SYMBOL(fscache_object_destroy); 89 90 /* 91 * find the object in the tree on or after the specified index 92 */ 93 static struct fscache_object *fscache_objlist_lookup(loff_t *_pos) 94 { 95 struct fscache_object *pobj, *obj = NULL, *minobj = NULL; 96 struct rb_node *p; 97 unsigned long pos; 98 99 if (*_pos >= (unsigned long) ERR_PTR(-ENOENT)) 100 return NULL; 101 pos = *_pos; 102 103 /* banners (can't represent line 0 by pos 0 as that would involve 104 * returning a NULL pointer) */ 105 if (pos == 0) 106 return (struct fscache_object *)(long)++(*_pos); 107 if (pos < 3) 108 return (struct fscache_object *)pos; 109 110 pobj = (struct fscache_object *)pos; 111 p = fscache_object_list.rb_node; 112 while (p) { 113 obj = rb_entry(p, struct fscache_object, objlist_link); 114 if (pobj < obj) { 115 if (!minobj || minobj > obj) 116 minobj = obj; 117 p = p->rb_left; 118 } else if (pobj > obj) { 119 p = p->rb_right; 120 } else { 121 minobj = obj; 122 break; 123 } 124 obj = NULL; 125 } 126 127 if (!minobj) 128 *_pos = (unsigned long) ERR_PTR(-ENOENT); 129 else if (minobj != obj) 130 *_pos = (unsigned long) minobj; 131 return minobj; 132 } 133 134 /* 135 * set up the iterator to start reading from the first line 136 */ 137 static void *fscache_objlist_start(struct seq_file *m, loff_t *_pos) 138 __acquires(&fscache_object_list_lock) 139 { 140 read_lock(&fscache_object_list_lock); 141 return fscache_objlist_lookup(_pos); 142 } 143 144 /* 145 * move to the next line 146 */ 147 static void *fscache_objlist_next(struct seq_file *m, void *v, loff_t *_pos) 148 { 149 (*_pos)++; 150 return fscache_objlist_lookup(_pos); 151 } 152 153 /* 154 * clean up after reading 155 */ 156 static void fscache_objlist_stop(struct seq_file *m, void *v) 157 __releases(&fscache_object_list_lock) 158 { 159 read_unlock(&fscache_object_list_lock); 160 } 161 162 /* 163 * display an object 164 */ 165 static int fscache_objlist_show(struct seq_file *m, void *v) 166 { 167 struct fscache_objlist_data *data = m->private; 168 struct fscache_object *obj = v; 169 unsigned long config = data->config; 170 uint16_t keylen, auxlen; 171 char _type[3], *type; 172 bool no_cookie; 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 F 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 if (~config) { 220 FILTER(obj->cookie, 221 COOKIE, NOCOOKIE); 222 FILTER(obj->state != FSCACHE_OBJECT_ACTIVE || 223 obj->n_ops != 0 || 224 obj->n_obj_ops != 0 || 225 obj->flags || 226 !list_empty(&obj->dependents), 227 BUSY, IDLE); 228 FILTER(test_bit(FSCACHE_OBJECT_PENDING_WRITE, &obj->flags), 229 PENDWR, NOPENDWR); 230 FILTER(atomic_read(&obj->n_reads), 231 READS, NOREADS); 232 FILTER(obj->events & obj->event_mask, 233 EVENTS, NOEVENTS); 234 FILTER(work_busy(&obj->work), WORK, NOWORK); 235 } 236 237 seq_printf(m, 238 "%8x %8x %s %5u %3u %3u %3u %2u %5u %2lx %2lx %1lx %1x | ", 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, 249 obj->events, 250 obj->flags, 251 work_busy(&obj->work)); 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 if (!confkey) { 342 /* key was revoked */ 343 rcu_read_unlock(); 344 key_put(key); 345 goto no_config; 346 } 347 348 buf = confkey->data; 349 350 for (len = confkey->datalen - 1; len >= 0; len--) { 351 switch (buf[len]) { 352 case 'K': config |= FSCACHE_OBJLIST_CONFIG_KEY; break; 353 case 'A': config |= FSCACHE_OBJLIST_CONFIG_AUX; break; 354 case 'C': config |= FSCACHE_OBJLIST_CONFIG_COOKIE; break; 355 case 'c': config |= FSCACHE_OBJLIST_CONFIG_NOCOOKIE; break; 356 case 'B': config |= FSCACHE_OBJLIST_CONFIG_BUSY; break; 357 case 'b': config |= FSCACHE_OBJLIST_CONFIG_IDLE; break; 358 case 'W': config |= FSCACHE_OBJLIST_CONFIG_PENDWR; break; 359 case 'w': config |= FSCACHE_OBJLIST_CONFIG_NOPENDWR; break; 360 case 'R': config |= FSCACHE_OBJLIST_CONFIG_READS; break; 361 case 'r': config |= FSCACHE_OBJLIST_CONFIG_NOREADS; break; 362 case 'S': config |= FSCACHE_OBJLIST_CONFIG_WORK; break; 363 case 's': config |= FSCACHE_OBJLIST_CONFIG_NOWORK; break; 364 } 365 } 366 367 rcu_read_unlock(); 368 key_put(key); 369 370 if (!(config & (FSCACHE_OBJLIST_CONFIG_COOKIE | FSCACHE_OBJLIST_CONFIG_NOCOOKIE))) 371 config |= FSCACHE_OBJLIST_CONFIG_COOKIE | FSCACHE_OBJLIST_CONFIG_NOCOOKIE; 372 if (!(config & (FSCACHE_OBJLIST_CONFIG_BUSY | FSCACHE_OBJLIST_CONFIG_IDLE))) 373 config |= FSCACHE_OBJLIST_CONFIG_BUSY | FSCACHE_OBJLIST_CONFIG_IDLE; 374 if (!(config & (FSCACHE_OBJLIST_CONFIG_PENDWR | FSCACHE_OBJLIST_CONFIG_NOPENDWR))) 375 config |= FSCACHE_OBJLIST_CONFIG_PENDWR | FSCACHE_OBJLIST_CONFIG_NOPENDWR; 376 if (!(config & (FSCACHE_OBJLIST_CONFIG_READS | FSCACHE_OBJLIST_CONFIG_NOREADS))) 377 config |= FSCACHE_OBJLIST_CONFIG_READS | FSCACHE_OBJLIST_CONFIG_NOREADS; 378 if (!(config & (FSCACHE_OBJLIST_CONFIG_EVENTS | FSCACHE_OBJLIST_CONFIG_NOEVENTS))) 379 config |= FSCACHE_OBJLIST_CONFIG_EVENTS | FSCACHE_OBJLIST_CONFIG_NOEVENTS; 380 if (!(config & (FSCACHE_OBJLIST_CONFIG_WORK | FSCACHE_OBJLIST_CONFIG_NOWORK))) 381 config |= FSCACHE_OBJLIST_CONFIG_WORK | FSCACHE_OBJLIST_CONFIG_NOWORK; 382 383 data->config = config; 384 return; 385 386 no_config: 387 #endif 388 data->config = ULONG_MAX; 389 } 390 391 /* 392 * open "/proc/fs/fscache/objects" to provide a list of active objects 393 * - can be configured by a user-defined key added to the caller's keyrings 394 */ 395 static int fscache_objlist_open(struct inode *inode, struct file *file) 396 { 397 struct fscache_objlist_data *data; 398 struct seq_file *m; 399 int ret; 400 401 ret = seq_open(file, &fscache_objlist_ops); 402 if (ret < 0) 403 return ret; 404 405 m = file->private_data; 406 407 /* buffer for key extraction */ 408 data = kmalloc(sizeof(struct fscache_objlist_data), GFP_KERNEL); 409 if (!data) { 410 seq_release(inode, file); 411 return -ENOMEM; 412 } 413 414 /* get the configuration key */ 415 fscache_objlist_config(data); 416 417 m->private = data; 418 return 0; 419 } 420 421 /* 422 * clean up on close 423 */ 424 static int fscache_objlist_release(struct inode *inode, struct file *file) 425 { 426 struct seq_file *m = file->private_data; 427 428 kfree(m->private); 429 m->private = NULL; 430 return seq_release(inode, file); 431 } 432 433 const struct file_operations fscache_objlist_fops = { 434 .owner = THIS_MODULE, 435 .open = fscache_objlist_open, 436 .read = seq_read, 437 .llseek = seq_lseek, 438 .release = fscache_objlist_release, 439 }; 440
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.