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        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        const 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 = user_key_payload(key);
 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        .open           = fscache_objlist_open,
 408        .read           = seq_read,
 409        .llseek         = seq_lseek,
 410        .release        = fscache_objlist_release,
 411};
 412