linux/net/sunrpc/debugfs.c
<<
>>
Prefs
   1/**
   2 * debugfs interface for sunrpc
   3 *
   4 * (c) 2014 Jeff Layton <jlayton@primarydata.com>
   5 */
   6
   7#include <linux/debugfs.h>
   8#include <linux/sunrpc/sched.h>
   9#include <linux/sunrpc/clnt.h>
  10#include "netns.h"
  11
  12static struct dentry *topdir;
  13static struct dentry *rpc_clnt_dir;
  14static struct dentry *rpc_xprt_dir;
  15
  16struct rpc_clnt_iter {
  17        struct rpc_clnt *clnt;
  18        loff_t          pos;
  19};
  20
  21static int
  22tasks_show(struct seq_file *f, void *v)
  23{
  24        u32 xid = 0;
  25        struct rpc_task *task = v;
  26        struct rpc_clnt *clnt = task->tk_client;
  27        const char *rpc_waitq = "none";
  28
  29        if (RPC_IS_QUEUED(task))
  30                rpc_waitq = rpc_qname(task->tk_waitqueue);
  31
  32        if (task->tk_rqstp)
  33                xid = be32_to_cpu(task->tk_rqstp->rq_xid);
  34
  35        seq_printf(f, "%5u %04x %6d 0x%x 0x%x %8ld %ps %sv%u %s a:%ps q:%s\n",
  36                task->tk_pid, task->tk_flags, task->tk_status,
  37                clnt->cl_clid, xid, task->tk_timeout, task->tk_ops,
  38                clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task),
  39                task->tk_action, rpc_waitq);
  40        return 0;
  41}
  42
  43static void *
  44tasks_start(struct seq_file *f, loff_t *ppos)
  45        __acquires(&clnt->cl_lock)
  46{
  47        struct rpc_clnt_iter *iter = f->private;
  48        loff_t pos = *ppos;
  49        struct rpc_clnt *clnt = iter->clnt;
  50        struct rpc_task *task;
  51
  52        iter->pos = pos + 1;
  53        spin_lock(&clnt->cl_lock);
  54        list_for_each_entry(task, &clnt->cl_tasks, tk_task)
  55                if (pos-- == 0)
  56                        return task;
  57        return NULL;
  58}
  59
  60static void *
  61tasks_next(struct seq_file *f, void *v, loff_t *pos)
  62{
  63        struct rpc_clnt_iter *iter = f->private;
  64        struct rpc_clnt *clnt = iter->clnt;
  65        struct rpc_task *task = v;
  66        struct list_head *next = task->tk_task.next;
  67
  68        ++iter->pos;
  69        ++*pos;
  70
  71        /* If there's another task on list, return it */
  72        if (next == &clnt->cl_tasks)
  73                return NULL;
  74        return list_entry(next, struct rpc_task, tk_task);
  75}
  76
  77static void
  78tasks_stop(struct seq_file *f, void *v)
  79        __releases(&clnt->cl_lock)
  80{
  81        struct rpc_clnt_iter *iter = f->private;
  82        struct rpc_clnt *clnt = iter->clnt;
  83
  84        spin_unlock(&clnt->cl_lock);
  85}
  86
  87static const struct seq_operations tasks_seq_operations = {
  88        .start  = tasks_start,
  89        .next   = tasks_next,
  90        .stop   = tasks_stop,
  91        .show   = tasks_show,
  92};
  93
  94static int tasks_open(struct inode *inode, struct file *filp)
  95{
  96        int ret = seq_open_private(filp, &tasks_seq_operations,
  97                                        sizeof(struct rpc_clnt_iter));
  98
  99        if (!ret) {
 100                struct seq_file *seq = filp->private_data;
 101                struct rpc_clnt_iter *iter = seq->private;
 102
 103                iter->clnt = inode->i_private;
 104
 105                if (!atomic_inc_not_zero(&iter->clnt->cl_count)) {
 106                        seq_release_private(inode, filp);
 107                        ret = -EINVAL;
 108                }
 109        }
 110
 111        return ret;
 112}
 113
 114static int
 115tasks_release(struct inode *inode, struct file *filp)
 116{
 117        struct seq_file *seq = filp->private_data;
 118        struct rpc_clnt_iter *iter = seq->private;
 119
 120        rpc_release_client(iter->clnt);
 121        return seq_release_private(inode, filp);
 122}
 123
 124static const struct file_operations tasks_fops = {
 125        .owner          = THIS_MODULE,
 126        .open           = tasks_open,
 127        .read           = seq_read,
 128        .llseek         = seq_lseek,
 129        .release        = tasks_release,
 130};
 131
 132void
 133rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
 134{
 135        int len;
 136        char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */
 137        struct rpc_xprt *xprt;
 138
 139        /* Already registered? */
 140        if (clnt->cl_debugfs || !rpc_clnt_dir)
 141                return;
 142
 143        len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
 144        if (len >= sizeof(name))
 145                return;
 146
 147        /* make the per-client dir */
 148        clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
 149        if (!clnt->cl_debugfs)
 150                return;
 151
 152        /* make tasks file */
 153        if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs,
 154                                 clnt, &tasks_fops))
 155                goto out_err;
 156
 157        rcu_read_lock();
 158        xprt = rcu_dereference(clnt->cl_xprt);
 159        /* no "debugfs" dentry? Don't bother with the symlink. */
 160        if (!xprt->debugfs) {
 161                rcu_read_unlock();
 162                return;
 163        }
 164        len = snprintf(name, sizeof(name), "../../rpc_xprt/%s",
 165                        xprt->debugfs->d_name.name);
 166        rcu_read_unlock();
 167
 168        if (len >= sizeof(name))
 169                goto out_err;
 170
 171        if (!debugfs_create_symlink("xprt", clnt->cl_debugfs, name))
 172                goto out_err;
 173
 174        return;
 175out_err:
 176        debugfs_remove_recursive(clnt->cl_debugfs);
 177        clnt->cl_debugfs = NULL;
 178}
 179
 180void
 181rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
 182{
 183        debugfs_remove_recursive(clnt->cl_debugfs);
 184        clnt->cl_debugfs = NULL;
 185}
 186
 187static int
 188xprt_info_show(struct seq_file *f, void *v)
 189{
 190        struct rpc_xprt *xprt = f->private;
 191
 192        seq_printf(f, "netid: %s\n", xprt->address_strings[RPC_DISPLAY_NETID]);
 193        seq_printf(f, "addr:  %s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);
 194        seq_printf(f, "port:  %s\n", xprt->address_strings[RPC_DISPLAY_PORT]);
 195        seq_printf(f, "state: 0x%lx\n", xprt->state);
 196        return 0;
 197}
 198
 199static int
 200xprt_info_open(struct inode *inode, struct file *filp)
 201{
 202        int ret;
 203        struct rpc_xprt *xprt = inode->i_private;
 204
 205        ret = single_open(filp, xprt_info_show, xprt);
 206
 207        if (!ret) {
 208                if (!xprt_get(xprt)) {
 209                        single_release(inode, filp);
 210                        ret = -EINVAL;
 211                }
 212        }
 213        return ret;
 214}
 215
 216static int
 217xprt_info_release(struct inode *inode, struct file *filp)
 218{
 219        struct rpc_xprt *xprt = inode->i_private;
 220
 221        xprt_put(xprt);
 222        return single_release(inode, filp);
 223}
 224
 225static const struct file_operations xprt_info_fops = {
 226        .owner          = THIS_MODULE,
 227        .open           = xprt_info_open,
 228        .read           = seq_read,
 229        .llseek         = seq_lseek,
 230        .release        = xprt_info_release,
 231};
 232
 233void
 234rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
 235{
 236        int len, id;
 237        static atomic_t cur_id;
 238        char            name[9]; /* 8 hex digits + NULL term */
 239
 240        if (!rpc_xprt_dir)
 241                return;
 242
 243        id = (unsigned int)atomic_inc_return(&cur_id);
 244
 245        len = snprintf(name, sizeof(name), "%x", id);
 246        if (len >= sizeof(name))
 247                return;
 248
 249        /* make the per-client dir */
 250        xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir);
 251        if (!xprt->debugfs)
 252                return;
 253
 254        /* make tasks file */
 255        if (!debugfs_create_file("info", S_IFREG | S_IRUSR, xprt->debugfs,
 256                                 xprt, &xprt_info_fops)) {
 257                debugfs_remove_recursive(xprt->debugfs);
 258                xprt->debugfs = NULL;
 259        }
 260}
 261
 262void
 263rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
 264{
 265        debugfs_remove_recursive(xprt->debugfs);
 266        xprt->debugfs = NULL;
 267}
 268
 269void __exit
 270sunrpc_debugfs_exit(void)
 271{
 272        debugfs_remove_recursive(topdir);
 273        topdir = NULL;
 274        rpc_clnt_dir = NULL;
 275        rpc_xprt_dir = NULL;
 276}
 277
 278void __init
 279sunrpc_debugfs_init(void)
 280{
 281        topdir = debugfs_create_dir("sunrpc", NULL);
 282        if (!topdir)
 283                return;
 284
 285        rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
 286        if (!rpc_clnt_dir)
 287                goto out_remove;
 288
 289        rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir);
 290        if (!rpc_xprt_dir)
 291                goto out_remove;
 292
 293        return;
 294out_remove:
 295        debugfs_remove_recursive(topdir);
 296        topdir = NULL;
 297        rpc_clnt_dir = NULL;
 298}
 299