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 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 */ 79void 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} 88EXPORT_SYMBOL(fscache_object_destroy); 89 90/* 91 * find the object in the tree on or after the specified index 92 */ 93static 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 */ 137static 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 */ 147static 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 */ 156static 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 */ 165static 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 & FSCACHE_OBJECT_EVENTS_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 314static 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 */ 324static 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 379no_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 */ 388static 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 */ 417static 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 426const 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}; 433