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_fault_dir;
  14static struct dentry *rpc_clnt_dir;
  15static struct dentry *rpc_xprt_dir;
  16
  17unsigned int rpc_inject_disconnect;
  18
  19static int
  20tasks_show(struct seq_file *f, void *v)
  21{
  22        u32 xid = 0;
  23        struct rpc_task *task = v;
  24        struct rpc_clnt *clnt = task->tk_client;
  25        const char *rpc_waitq = "none";
  26
  27        if (RPC_IS_QUEUED(task))
  28                rpc_waitq = rpc_qname(task->tk_waitqueue);
  29
  30        if (task->tk_rqstp)
  31                xid = be32_to_cpu(task->tk_rqstp->rq_xid);
  32
  33        seq_printf(f, "%5u %04x %6d 0x%x 0x%x %8ld %ps %sv%u %s a:%ps q:%s\n",
  34                task->tk_pid, task->tk_flags, task->tk_status,
  35                clnt->cl_clid, xid, task->tk_timeout, task->tk_ops,
  36                clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task),
  37                task->tk_action, rpc_waitq);
  38        return 0;
  39}
  40
  41static void *
  42tasks_start(struct seq_file *f, loff_t *ppos)
  43        __acquires(&clnt->cl_lock)
  44{
  45        struct rpc_clnt *clnt = f->private;
  46        loff_t pos = *ppos;
  47        struct rpc_task *task;
  48
  49        spin_lock(&clnt->cl_lock);
  50        list_for_each_entry(task, &clnt->cl_tasks, tk_task)
  51                if (pos-- == 0)
  52                        return task;
  53        return NULL;
  54}
  55
  56static void *
  57tasks_next(struct seq_file *f, void *v, loff_t *pos)
  58{
  59        struct rpc_clnt *clnt = f->private;
  60        struct rpc_task *task = v;
  61        struct list_head *next = task->tk_task.next;
  62
  63        ++*pos;
  64
  65        /* If there's another task on list, return it */
  66        if (next == &clnt->cl_tasks)
  67                return NULL;
  68        return list_entry(next, struct rpc_task, tk_task);
  69}
  70
  71static void
  72tasks_stop(struct seq_file *f, void *v)
  73        __releases(&clnt->cl_lock)
  74{
  75        struct rpc_clnt *clnt = f->private;
  76        spin_unlock(&clnt->cl_lock);
  77}
  78
  79static const struct seq_operations tasks_seq_operations = {
  80        .start  = tasks_start,
  81        .next   = tasks_next,
  82        .stop   = tasks_stop,
  83        .show   = tasks_show,
  84};
  85
  86static int tasks_open(struct inode *inode, struct file *filp)
  87{
  88        int ret = seq_open(filp, &tasks_seq_operations);
  89        if (!ret) {
  90                struct seq_file *seq = filp->private_data;
  91                struct rpc_clnt *clnt = seq->private = inode->i_private;
  92
  93                if (!atomic_inc_not_zero(&clnt->cl_count)) {
  94                        seq_release(inode, filp);
  95                        ret = -EINVAL;
  96                }
  97        }
  98
  99        return ret;
 100}
 101
 102static int
 103tasks_release(struct inode *inode, struct file *filp)
 104{
 105        struct seq_file *seq = filp->private_data;
 106        struct rpc_clnt *clnt = seq->private;
 107
 108        rpc_release_client(clnt);
 109        return seq_release(inode, filp);
 110}
 111
 112static const struct file_operations tasks_fops = {
 113        .owner          = THIS_MODULE,
 114        .open           = tasks_open,
 115        .read           = seq_read,
 116        .llseek         = seq_lseek,
 117        .release        = tasks_release,
 118};
 119
 120void
 121rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
 122{
 123        int len;
 124        char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */
 125        struct rpc_xprt *xprt;
 126
 127        /* Already registered? */
 128        if (clnt->cl_debugfs || !rpc_clnt_dir)
 129                return;
 130
 131        len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
 132        if (len >= sizeof(name))
 133                return;
 134
 135        /* make the per-client dir */
 136        clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
 137        if (!clnt->cl_debugfs)
 138                return;
 139
 140        /* make tasks file */
 141        if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs,
 142                                 clnt, &tasks_fops))
 143                goto out_err;
 144
 145        rcu_read_lock();
 146        xprt = rcu_dereference(clnt->cl_xprt);
 147        /* no "debugfs" dentry? Don't bother with the symlink. */
 148        if (!xprt->debugfs) {
 149                rcu_read_unlock();
 150                return;
 151        }
 152        len = snprintf(name, sizeof(name), "../../rpc_xprt/%s",
 153                        xprt->debugfs->d_name.name);
 154        rcu_read_unlock();
 155
 156        if (len >= sizeof(name))
 157                goto out_err;
 158
 159        if (!debugfs_create_symlink("xprt", clnt->cl_debugfs, name))
 160                goto out_err;
 161
 162        return;
 163out_err:
 164        debugfs_remove_recursive(clnt->cl_debugfs);
 165        clnt->cl_debugfs = NULL;
 166}
 167
 168void
 169rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
 170{
 171        debugfs_remove_recursive(clnt->cl_debugfs);
 172        clnt->cl_debugfs = NULL;
 173}
 174
 175static int
 176xprt_info_show(struct seq_file *f, void *v)
 177{
 178        struct rpc_xprt *xprt = f->private;
 179
 180        seq_printf(f, "netid: %s\n", xprt->address_strings[RPC_DISPLAY_NETID]);
 181        seq_printf(f, "addr:  %s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);
 182        seq_printf(f, "port:  %s\n", xprt->address_strings[RPC_DISPLAY_PORT]);
 183        seq_printf(f, "state: 0x%lx\n", xprt->state);
 184        return 0;
 185}
 186
 187static int
 188xprt_info_open(struct inode *inode, struct file *filp)
 189{
 190        int ret;
 191        struct rpc_xprt *xprt = inode->i_private;
 192
 193        ret = single_open(filp, xprt_info_show, xprt);
 194
 195        if (!ret) {
 196                if (!xprt_get(xprt)) {
 197                        single_release(inode, filp);
 198                        ret = -EINVAL;
 199                }
 200        }
 201        return ret;
 202}
 203
 204static int
 205xprt_info_release(struct inode *inode, struct file *filp)
 206{
 207        struct rpc_xprt *xprt = inode->i_private;
 208
 209        xprt_put(xprt);
 210        return single_release(inode, filp);
 211}
 212
 213static const struct file_operations xprt_info_fops = {
 214        .owner          = THIS_MODULE,
 215        .open           = xprt_info_open,
 216        .read           = seq_read,
 217        .llseek         = seq_lseek,
 218        .release        = xprt_info_release,
 219};
 220
 221void
 222rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
 223{
 224        int len, id;
 225        static atomic_t cur_id;
 226        char            name[9]; /* 8 hex digits + NULL term */
 227
 228        if (!rpc_xprt_dir)
 229                return;
 230
 231        id = (unsigned int)atomic_inc_return(&cur_id);
 232
 233        len = snprintf(name, sizeof(name), "%x", id);
 234        if (len >= sizeof(name))
 235                return;
 236
 237        /* make the per-client dir */
 238        xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir);
 239        if (!xprt->debugfs)
 240                return;
 241
 242        /* make tasks file */
 243        if (!debugfs_create_file("info", S_IFREG | S_IRUSR, xprt->debugfs,
 244                                 xprt, &xprt_info_fops)) {
 245                debugfs_remove_recursive(xprt->debugfs);
 246                xprt->debugfs = NULL;
 247        }
 248
 249        atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect);
 250}
 251
 252void
 253rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
 254{
 255        debugfs_remove_recursive(xprt->debugfs);
 256        xprt->debugfs = NULL;
 257}
 258
 259static int
 260fault_open(struct inode *inode, struct file *filp)
 261{
 262        filp->private_data = kmalloc(128, GFP_KERNEL);
 263        if (!filp->private_data)
 264                return -ENOMEM;
 265        return 0;
 266}
 267
 268static int
 269fault_release(struct inode *inode, struct file *filp)
 270{
 271        kfree(filp->private_data);
 272        return 0;
 273}
 274
 275static ssize_t
 276fault_disconnect_read(struct file *filp, char __user *user_buf,
 277                      size_t len, loff_t *offset)
 278{
 279        char *buffer = (char *)filp->private_data;
 280        size_t size;
 281
 282        size = sprintf(buffer, "%u\n", rpc_inject_disconnect);
 283        return simple_read_from_buffer(user_buf, len, offset, buffer, size);
 284}
 285
 286static ssize_t
 287fault_disconnect_write(struct file *filp, const char __user *user_buf,
 288                       size_t len, loff_t *offset)
 289{
 290        char buffer[16];
 291
 292        if (len >= sizeof(buffer))
 293                len = sizeof(buffer) - 1;
 294        if (copy_from_user(buffer, user_buf, len))
 295                return -EFAULT;
 296        buffer[len] = '\0';
 297        if (kstrtouint(buffer, 10, &rpc_inject_disconnect))
 298                return -EINVAL;
 299        return len;
 300}
 301
 302static const struct file_operations fault_disconnect_fops = {
 303        .owner          = THIS_MODULE,
 304        .open           = fault_open,
 305        .read           = fault_disconnect_read,
 306        .write          = fault_disconnect_write,
 307        .release        = fault_release,
 308};
 309
 310static struct dentry *
 311inject_fault_dir(struct dentry *topdir)
 312{
 313        struct dentry *faultdir;
 314
 315        faultdir = debugfs_create_dir("inject_fault", topdir);
 316        if (!faultdir)
 317                return NULL;
 318
 319        if (!debugfs_create_file("disconnect", S_IFREG | S_IRUSR, faultdir,
 320                                 NULL, &fault_disconnect_fops))
 321                return NULL;
 322
 323        return faultdir;
 324}
 325
 326void __exit
 327sunrpc_debugfs_exit(void)
 328{
 329        debugfs_remove_recursive(topdir);
 330        topdir = NULL;
 331        rpc_fault_dir = NULL;
 332        rpc_clnt_dir = NULL;
 333        rpc_xprt_dir = NULL;
 334}
 335
 336void __init
 337sunrpc_debugfs_init(void)
 338{
 339        topdir = debugfs_create_dir("sunrpc", NULL);
 340        if (!topdir)
 341                return;
 342
 343        rpc_fault_dir = inject_fault_dir(topdir);
 344        if (!rpc_fault_dir)
 345                goto out_remove;
 346
 347        rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
 348        if (!rpc_clnt_dir)
 349                goto out_remove;
 350
 351        rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir);
 352        if (!rpc_xprt_dir)
 353                goto out_remove;
 354
 355        return;
 356out_remove:
 357        debugfs_remove_recursive(topdir);
 358        topdir = NULL;
 359        rpc_fault_dir = NULL;
 360        rpc_clnt_dir = NULL;
 361}
 362