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
 132int
 133rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
 134{
 135        int len, err;
 136        char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */
 137
 138        /* Already registered? */
 139        if (clnt->cl_debugfs)
 140                return 0;
 141
 142        len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
 143        if (len >= sizeof(name))
 144                return -EINVAL;
 145
 146        /* make the per-client dir */
 147        clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
 148        if (!clnt->cl_debugfs)
 149                return -ENOMEM;
 150
 151        /* make tasks file */
 152        err = -ENOMEM;
 153        if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs,
 154                                 clnt, &tasks_fops))
 155                goto out_err;
 156
 157        err = -EINVAL;
 158        rcu_read_lock();
 159        len = snprintf(name, sizeof(name), "../../rpc_xprt/%s",
 160                        rcu_dereference(clnt->cl_xprt)->debugfs->d_name.name);
 161        rcu_read_unlock();
 162        if (len >= sizeof(name))
 163                goto out_err;
 164
 165        err = -ENOMEM;
 166        if (!debugfs_create_symlink("xprt", clnt->cl_debugfs, name))
 167                goto out_err;
 168
 169        return 0;
 170out_err:
 171        debugfs_remove_recursive(clnt->cl_debugfs);
 172        clnt->cl_debugfs = NULL;
 173        return err;
 174}
 175
 176void
 177rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
 178{
 179        debugfs_remove_recursive(clnt->cl_debugfs);
 180        clnt->cl_debugfs = NULL;
 181}
 182
 183static int
 184xprt_info_show(struct seq_file *f, void *v)
 185{
 186        struct rpc_xprt *xprt = f->private;
 187
 188        seq_printf(f, "netid: %s\n", xprt->address_strings[RPC_DISPLAY_NETID]);
 189        seq_printf(f, "addr:  %s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);
 190        seq_printf(f, "port:  %s\n", xprt->address_strings[RPC_DISPLAY_PORT]);
 191        seq_printf(f, "state: 0x%lx\n", xprt->state);
 192        return 0;
 193}
 194
 195static int
 196xprt_info_open(struct inode *inode, struct file *filp)
 197{
 198        int ret;
 199        struct rpc_xprt *xprt = inode->i_private;
 200
 201        ret = single_open(filp, xprt_info_show, xprt);
 202
 203        if (!ret) {
 204                if (!xprt_get(xprt)) {
 205                        single_release(inode, filp);
 206                        ret = -EINVAL;
 207                }
 208        }
 209        return ret;
 210}
 211
 212static int
 213xprt_info_release(struct inode *inode, struct file *filp)
 214{
 215        struct rpc_xprt *xprt = inode->i_private;
 216
 217        xprt_put(xprt);
 218        return single_release(inode, filp);
 219}
 220
 221static const struct file_operations xprt_info_fops = {
 222        .owner          = THIS_MODULE,
 223        .open           = xprt_info_open,
 224        .read           = seq_read,
 225        .llseek         = seq_lseek,
 226        .release        = xprt_info_release,
 227};
 228
 229int
 230rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
 231{
 232        int len, id;
 233        static atomic_t cur_id;
 234        char            name[9]; /* 8 hex digits + NULL term */
 235
 236        id = (unsigned int)atomic_inc_return(&cur_id);
 237
 238        len = snprintf(name, sizeof(name), "%x", id);
 239        if (len >= sizeof(name))
 240                return -EINVAL;
 241
 242        /* make the per-client dir */
 243        xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir);
 244        if (!xprt->debugfs)
 245                return -ENOMEM;
 246
 247        /* make tasks file */
 248        if (!debugfs_create_file("info", S_IFREG | S_IRUSR, xprt->debugfs,
 249                                 xprt, &xprt_info_fops)) {
 250                debugfs_remove_recursive(xprt->debugfs);
 251                xprt->debugfs = NULL;
 252                return -ENOMEM;
 253        }
 254
 255        return 0;
 256}
 257
 258void
 259rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
 260{
 261        debugfs_remove_recursive(xprt->debugfs);
 262        xprt->debugfs = NULL;
 263}
 264
 265void __exit
 266sunrpc_debugfs_exit(void)
 267{
 268        debugfs_remove_recursive(topdir);
 269}
 270
 271int __init
 272sunrpc_debugfs_init(void)
 273{
 274        topdir = debugfs_create_dir("sunrpc", NULL);
 275        if (!topdir)
 276                goto out;
 277
 278        rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
 279        if (!rpc_clnt_dir)
 280                goto out_remove;
 281
 282        rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir);
 283        if (!rpc_xprt_dir)
 284                goto out_remove;
 285
 286        return 0;
 287out_remove:
 288        debugfs_remove_recursive(topdir);
 289        topdir = NULL;
 290out:
 291        return -ENOMEM;
 292}
 293