linux/fs/fscache/object-list.c
<<
>>
Prefs
   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
  41/*
  42 * Add an object to the object list
  43 * - we use the address of the fscache_object structure as the key into the
  44 *   tree
  45 */
  46void fscache_objlist_add(struct fscache_object *obj)
  47{
  48        struct fscache_object *xobj;
  49        struct rb_node **p = &fscache_object_list.rb_node, *parent = NULL;
  50
  51        ASSERT(RB_EMPTY_NODE(&obj->objlist_link));
  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 * Remove an object from the object list.
  75 */
  76void fscache_objlist_remove(struct fscache_object *obj)
  77{
  78        if (RB_EMPTY_NODE(&obj->objlist_link))
  79                return;
  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
  89/*
  90 * find the object in the tree on or after the specified index
  91 */
  92static struct fscache_object *fscache_objlist_lookup(loff_t *_pos)
  93{
  94        struct fscache_object *pobj, *obj = NULL, *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 *)(long)++(*_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 */
 136static 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 */
 146static 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 */
 155static 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 */
 164static 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        struct fscache_cookie *cookie;
 169        unsigned long config = data->config;
 170        char _type[3], *type;
 171        u8 *p;
 172
 173        if ((unsigned long) v == 1) {
 174                seq_puts(m, "OBJECT   PARENT   STAT CHLDN OPS OOP IPR EX READS"
 175                         " EM EV FL S"
 176                         " | NETFS_COOKIE_DEF TY FL NETFS_DATA");
 177                if (config & (FSCACHE_OBJLIST_CONFIG_KEY |
 178                              FSCACHE_OBJLIST_CONFIG_AUX))
 179                        seq_puts(m, "       ");
 180                if (config & FSCACHE_OBJLIST_CONFIG_KEY)
 181                        seq_puts(m, "OBJECT_KEY");
 182                if ((config & (FSCACHE_OBJLIST_CONFIG_KEY |
 183                               FSCACHE_OBJLIST_CONFIG_AUX)) ==
 184                    (FSCACHE_OBJLIST_CONFIG_KEY | FSCACHE_OBJLIST_CONFIG_AUX))
 185                        seq_puts(m, ", ");
 186                if (config & FSCACHE_OBJLIST_CONFIG_AUX)
 187                        seq_puts(m, "AUX_DATA");
 188                seq_puts(m, "\n");
 189                return 0;
 190        }
 191
 192        if ((unsigned long) v == 2) {
 193                seq_puts(m, "======== ======== ==== ===== === === === == ====="
 194                         " == == == ="
 195                         " | ================ == == ================");
 196                if (config & (FSCACHE_OBJLIST_CONFIG_KEY |
 197                              FSCACHE_OBJLIST_CONFIG_AUX))
 198                        seq_puts(m, " ================");
 199                seq_puts(m, "\n");
 200                return 0;
 201        }
 202
 203        /* filter out any unwanted objects */
 204#define FILTER(criterion, _yes, _no)                                    \
 205        do {                                                            \
 206                unsigned long yes = FSCACHE_OBJLIST_CONFIG_##_yes;      \
 207                unsigned long no = FSCACHE_OBJLIST_CONFIG_##_no;        \
 208                if (criterion) {                                        \
 209                        if (!(config & yes))                            \
 210                                return 0;                               \
 211                } else {                                                \
 212                        if (!(config & no))                             \
 213                                return 0;                               \
 214                }                                                       \
 215        } while(0)
 216
 217        cookie = obj->cookie;
 218        if (~config) {
 219                FILTER(cookie->def,
 220                       COOKIE, NOCOOKIE);
 221                FILTER(fscache_object_is_active(obj) ||
 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(work_busy(&obj->work), WORK, NOWORK);
 234        }
 235
 236        seq_printf(m,
 237                   "%8x %8x %s %5u %3u %3u %3u %2u %5u %2lx %2lx %2lx %1x | ",
 238                   obj->debug_id,
 239                   obj->parent ? obj->parent->debug_id : -1,
 240                   obj->state->short_name,
 241                   obj->n_children,
 242                   obj->n_ops,
 243                   obj->n_obj_ops,
 244                   obj->n_in_progress,
 245                   obj->n_exclusive,
 246                   atomic_read(&obj->n_reads),
 247                   obj->event_mask,
 248                   obj->events,
 249                   obj->flags,
 250                   work_busy(&obj->work));
 251
 252        if (fscache_use_cookie(obj)) {
 253                uint16_t keylen = 0, auxlen = 0;
 254
 255                switch (cookie->type) {
 256                case 0:
 257                        type = "IX";
 258                        break;
 259                case 1:
 260                        type = "DT";
 261                        break;
 262                default:
 263                        snprintf(_type, sizeof(_type), "%02u",
 264                                 cookie->type);
 265                        type = _type;
 266                        break;
 267                }
 268
 269                seq_printf(m, "%-16s %s %2lx %16p",
 270                           cookie->def->name,
 271                           type,
 272                           cookie->flags,
 273                           cookie->netfs_data);
 274
 275                if (config & FSCACHE_OBJLIST_CONFIG_KEY)
 276                        keylen = cookie->key_len;
 277
 278                if (config & FSCACHE_OBJLIST_CONFIG_AUX)
 279                        auxlen = cookie->aux_len;
 280
 281                if (keylen > 0 || auxlen > 0) {
 282                        seq_puts(m, " ");
 283                        p = keylen <= sizeof(cookie->inline_key) ?
 284                                cookie->inline_key : cookie->key;
 285                        for (; keylen > 0; keylen--)
 286                                seq_printf(m, "%02x", *p++);
 287                        if (auxlen > 0) {
 288                                if (config & FSCACHE_OBJLIST_CONFIG_KEY)
 289                                        seq_puts(m, ", ");
 290                                p = auxlen <= sizeof(cookie->inline_aux) ?
 291                                        cookie->inline_aux : cookie->aux;
 292                                for (; auxlen > 0; auxlen--)
 293                                        seq_printf(m, "%02x", *p++);
 294                        }
 295                }
 296
 297                seq_puts(m, "\n");
 298                fscache_unuse_cookie(obj);
 299        } else {
 300                seq_puts(m, "<no_netfs>\n");
 301        }
 302        return 0;
 303}
 304
 305static const struct seq_operations fscache_objlist_ops = {
 306        .start          = fscache_objlist_start,
 307        .stop           = fscache_objlist_stop,
 308        .next           = fscache_objlist_next,
 309        .show           = fscache_objlist_show,
 310};
 311
 312/*
 313 * get the configuration for filtering the list
 314 */
 315static void fscache_objlist_config(struct fscache_objlist_data *data)
 316{
 317#ifdef CONFIG_KEYS
 318        const struct user_key_payload *confkey;
 319        unsigned long config;
 320        struct key *key;
 321        const char *buf;
 322        int len;
 323
 324        key = request_key(&key_type_user, "fscache:objlist", NULL);
 325        if (IS_ERR(key))
 326                goto no_config;
 327
 328        config = 0;
 329        rcu_read_lock();
 330
 331        confkey = user_key_payload_rcu(key);
 332        if (!confkey) {
 333                /* key was revoked */
 334                rcu_read_unlock();
 335                key_put(key);
 336                goto no_config;
 337        }
 338
 339        buf = confkey->data;
 340
 341        for (len = confkey->datalen - 1; len >= 0; len--) {
 342                switch (buf[len]) {
 343                case 'K': config |= FSCACHE_OBJLIST_CONFIG_KEY;         break;
 344                case 'A': config |= FSCACHE_OBJLIST_CONFIG_AUX;         break;
 345                case 'C': config |= FSCACHE_OBJLIST_CONFIG_COOKIE;      break;
 346                case 'c': config |= FSCACHE_OBJLIST_CONFIG_NOCOOKIE;    break;
 347                case 'B': config |= FSCACHE_OBJLIST_CONFIG_BUSY;        break;
 348                case 'b': config |= FSCACHE_OBJLIST_CONFIG_IDLE;        break;
 349                case 'W': config |= FSCACHE_OBJLIST_CONFIG_PENDWR;      break;
 350                case 'w': config |= FSCACHE_OBJLIST_CONFIG_NOPENDWR;    break;
 351                case 'R': config |= FSCACHE_OBJLIST_CONFIG_READS;       break;
 352                case 'r': config |= FSCACHE_OBJLIST_CONFIG_NOREADS;     break;
 353                case 'S': config |= FSCACHE_OBJLIST_CONFIG_WORK;        break;
 354                case 's': config |= FSCACHE_OBJLIST_CONFIG_NOWORK;      break;
 355                }
 356        }
 357
 358        rcu_read_unlock();
 359        key_put(key);
 360
 361        if (!(config & (FSCACHE_OBJLIST_CONFIG_COOKIE | FSCACHE_OBJLIST_CONFIG_NOCOOKIE)))
 362            config   |= FSCACHE_OBJLIST_CONFIG_COOKIE | FSCACHE_OBJLIST_CONFIG_NOCOOKIE;
 363        if (!(config & (FSCACHE_OBJLIST_CONFIG_BUSY | FSCACHE_OBJLIST_CONFIG_IDLE)))
 364            config   |= FSCACHE_OBJLIST_CONFIG_BUSY | FSCACHE_OBJLIST_CONFIG_IDLE;
 365        if (!(config & (FSCACHE_OBJLIST_CONFIG_PENDWR | FSCACHE_OBJLIST_CONFIG_NOPENDWR)))
 366            config   |= FSCACHE_OBJLIST_CONFIG_PENDWR | FSCACHE_OBJLIST_CONFIG_NOPENDWR;
 367        if (!(config & (FSCACHE_OBJLIST_CONFIG_READS | FSCACHE_OBJLIST_CONFIG_NOREADS)))
 368            config   |= FSCACHE_OBJLIST_CONFIG_READS | FSCACHE_OBJLIST_CONFIG_NOREADS;
 369        if (!(config & (FSCACHE_OBJLIST_CONFIG_EVENTS | FSCACHE_OBJLIST_CONFIG_NOEVENTS)))
 370            config   |= FSCACHE_OBJLIST_CONFIG_EVENTS | FSCACHE_OBJLIST_CONFIG_NOEVENTS;
 371        if (!(config & (FSCACHE_OBJLIST_CONFIG_WORK | FSCACHE_OBJLIST_CONFIG_NOWORK)))
 372            config   |= FSCACHE_OBJLIST_CONFIG_WORK | FSCACHE_OBJLIST_CONFIG_NOWORK;
 373
 374        data->config = config;
 375        return;
 376
 377no_config:
 378#endif
 379        data->config = ULONG_MAX;
 380}
 381
 382/*
 383 * open "/proc/fs/fscache/objects" to provide a list of active objects
 384 * - can be configured by a user-defined key added to the caller's keyrings
 385 */
 386static int fscache_objlist_open(struct inode *inode, struct file *file)
 387{
 388        struct fscache_objlist_data *data;
 389
 390        data = __seq_open_private(file, &fscache_objlist_ops, sizeof(*data));
 391        if (!data)
 392                return -ENOMEM;
 393
 394        /* get the configuration key */
 395        fscache_objlist_config(data);
 396
 397        return 0;
 398}
 399
 400/*
 401 * clean up on close
 402 */
 403static int fscache_objlist_release(struct inode *inode, struct file *file)
 404{
 405        struct seq_file *m = file->private_data;
 406
 407        kfree(m->private);
 408        m->private = NULL;
 409        return seq_release(inode, file);
 410}
 411
 412const struct file_operations fscache_objlist_fops = {
 413        .open           = fscache_objlist_open,
 414        .read           = seq_read,
 415        .llseek         = seq_lseek,
 416        .release        = fscache_objlist_release,
 417};
 418