1b4b9d2ccSJeff Layton /** 2b4b9d2ccSJeff Layton * debugfs interface for sunrpc 3b4b9d2ccSJeff Layton * 4b4b9d2ccSJeff Layton * (c) 2014 Jeff Layton <jlayton@primarydata.com> 5b4b9d2ccSJeff Layton */ 6b4b9d2ccSJeff Layton 7b4b9d2ccSJeff Layton #include <linux/debugfs.h> 8b4b9d2ccSJeff Layton #include <linux/sunrpc/sched.h> 9b4b9d2ccSJeff Layton #include <linux/sunrpc/clnt.h> 10b4b9d2ccSJeff Layton #include "netns.h" 11b4b9d2ccSJeff Layton 12b4b9d2ccSJeff Layton static struct dentry *topdir; 13b4b9d2ccSJeff Layton static struct dentry *rpc_clnt_dir; 14b4b9d2ccSJeff Layton 15b4b9d2ccSJeff Layton struct rpc_clnt_iter { 16b4b9d2ccSJeff Layton struct rpc_clnt *clnt; 17b4b9d2ccSJeff Layton loff_t pos; 18b4b9d2ccSJeff Layton }; 19b4b9d2ccSJeff Layton 20b4b9d2ccSJeff Layton static int 21b4b9d2ccSJeff Layton tasks_show(struct seq_file *f, void *v) 22b4b9d2ccSJeff Layton { 23b4b9d2ccSJeff Layton u32 xid = 0; 24b4b9d2ccSJeff Layton struct rpc_task *task = v; 25b4b9d2ccSJeff Layton struct rpc_clnt *clnt = task->tk_client; 26b4b9d2ccSJeff Layton const char *rpc_waitq = "none"; 27b4b9d2ccSJeff Layton 28b4b9d2ccSJeff Layton if (RPC_IS_QUEUED(task)) 29b4b9d2ccSJeff Layton rpc_waitq = rpc_qname(task->tk_waitqueue); 30b4b9d2ccSJeff Layton 31b4b9d2ccSJeff Layton if (task->tk_rqstp) 32b4b9d2ccSJeff Layton xid = be32_to_cpu(task->tk_rqstp->rq_xid); 33b4b9d2ccSJeff Layton 34b4b9d2ccSJeff Layton seq_printf(f, "%5u %04x %6d 0x%x 0x%x %8ld %ps %sv%u %s a:%ps q:%s\n", 35b4b9d2ccSJeff Layton task->tk_pid, task->tk_flags, task->tk_status, 36b4b9d2ccSJeff Layton clnt->cl_clid, xid, task->tk_timeout, task->tk_ops, 37b4b9d2ccSJeff Layton clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task), 38b4b9d2ccSJeff Layton task->tk_action, rpc_waitq); 39b4b9d2ccSJeff Layton return 0; 40b4b9d2ccSJeff Layton } 41b4b9d2ccSJeff Layton 42b4b9d2ccSJeff Layton static void * 43b4b9d2ccSJeff Layton tasks_start(struct seq_file *f, loff_t *ppos) 44b4b9d2ccSJeff Layton __acquires(&clnt->cl_lock) 45b4b9d2ccSJeff Layton { 46b4b9d2ccSJeff Layton struct rpc_clnt_iter *iter = f->private; 47b4b9d2ccSJeff Layton loff_t pos = *ppos; 48b4b9d2ccSJeff Layton struct rpc_clnt *clnt = iter->clnt; 49b4b9d2ccSJeff Layton struct rpc_task *task; 50b4b9d2ccSJeff Layton 51b4b9d2ccSJeff Layton iter->pos = pos + 1; 52b4b9d2ccSJeff Layton spin_lock(&clnt->cl_lock); 53b4b9d2ccSJeff Layton list_for_each_entry(task, &clnt->cl_tasks, tk_task) 54b4b9d2ccSJeff Layton if (pos-- == 0) 55b4b9d2ccSJeff Layton return task; 56b4b9d2ccSJeff Layton return NULL; 57b4b9d2ccSJeff Layton } 58b4b9d2ccSJeff Layton 59b4b9d2ccSJeff Layton static void * 60b4b9d2ccSJeff Layton tasks_next(struct seq_file *f, void *v, loff_t *pos) 61b4b9d2ccSJeff Layton { 62b4b9d2ccSJeff Layton struct rpc_clnt_iter *iter = f->private; 63b4b9d2ccSJeff Layton struct rpc_clnt *clnt = iter->clnt; 64b4b9d2ccSJeff Layton struct rpc_task *task = v; 65b4b9d2ccSJeff Layton struct list_head *next = task->tk_task.next; 66b4b9d2ccSJeff Layton 67b4b9d2ccSJeff Layton ++iter->pos; 68b4b9d2ccSJeff Layton ++*pos; 69b4b9d2ccSJeff Layton 70b4b9d2ccSJeff Layton /* If there's another task on list, return it */ 71b4b9d2ccSJeff Layton if (next == &clnt->cl_tasks) 72b4b9d2ccSJeff Layton return NULL; 73b4b9d2ccSJeff Layton return list_entry(next, struct rpc_task, tk_task); 74b4b9d2ccSJeff Layton } 75b4b9d2ccSJeff Layton 76b4b9d2ccSJeff Layton static void 77b4b9d2ccSJeff Layton tasks_stop(struct seq_file *f, void *v) 78b4b9d2ccSJeff Layton __releases(&clnt->cl_lock) 79b4b9d2ccSJeff Layton { 80b4b9d2ccSJeff Layton struct rpc_clnt_iter *iter = f->private; 81b4b9d2ccSJeff Layton struct rpc_clnt *clnt = iter->clnt; 82b4b9d2ccSJeff Layton 83b4b9d2ccSJeff Layton spin_unlock(&clnt->cl_lock); 84b4b9d2ccSJeff Layton } 85b4b9d2ccSJeff Layton 86b4b9d2ccSJeff Layton static const struct seq_operations tasks_seq_operations = { 87b4b9d2ccSJeff Layton .start = tasks_start, 88b4b9d2ccSJeff Layton .next = tasks_next, 89b4b9d2ccSJeff Layton .stop = tasks_stop, 90b4b9d2ccSJeff Layton .show = tasks_show, 91b4b9d2ccSJeff Layton }; 92b4b9d2ccSJeff Layton 93b4b9d2ccSJeff Layton static int tasks_open(struct inode *inode, struct file *filp) 94b4b9d2ccSJeff Layton { 95b4b9d2ccSJeff Layton int ret = seq_open_private(filp, &tasks_seq_operations, 96b4b9d2ccSJeff Layton sizeof(struct rpc_clnt_iter)); 97b4b9d2ccSJeff Layton 98b4b9d2ccSJeff Layton if (!ret) { 99b4b9d2ccSJeff Layton struct seq_file *seq = filp->private_data; 100b4b9d2ccSJeff Layton struct rpc_clnt_iter *iter = seq->private; 101b4b9d2ccSJeff Layton 102b4b9d2ccSJeff Layton iter->clnt = inode->i_private; 103b4b9d2ccSJeff Layton 104b4b9d2ccSJeff Layton if (!atomic_inc_not_zero(&iter->clnt->cl_count)) { 105b4b9d2ccSJeff Layton seq_release_private(inode, filp); 106b4b9d2ccSJeff Layton ret = -EINVAL; 107b4b9d2ccSJeff Layton } 108b4b9d2ccSJeff Layton } 109b4b9d2ccSJeff Layton 110b4b9d2ccSJeff Layton return ret; 111b4b9d2ccSJeff Layton } 112b4b9d2ccSJeff Layton 113b4b9d2ccSJeff Layton static int 114b4b9d2ccSJeff Layton tasks_release(struct inode *inode, struct file *filp) 115b4b9d2ccSJeff Layton { 116b4b9d2ccSJeff Layton struct seq_file *seq = filp->private_data; 117b4b9d2ccSJeff Layton struct rpc_clnt_iter *iter = seq->private; 118b4b9d2ccSJeff Layton 119b4b9d2ccSJeff Layton rpc_release_client(iter->clnt); 120b4b9d2ccSJeff Layton return seq_release_private(inode, filp); 121b4b9d2ccSJeff Layton } 122b4b9d2ccSJeff Layton 123b4b9d2ccSJeff Layton static const struct file_operations tasks_fops = { 124b4b9d2ccSJeff Layton .owner = THIS_MODULE, 125b4b9d2ccSJeff Layton .open = tasks_open, 126b4b9d2ccSJeff Layton .read = seq_read, 127b4b9d2ccSJeff Layton .llseek = seq_lseek, 128b4b9d2ccSJeff Layton .release = tasks_release, 129b4b9d2ccSJeff Layton }; 130b4b9d2ccSJeff Layton 131b4b9d2ccSJeff Layton int 132b4b9d2ccSJeff Layton rpc_clnt_debugfs_register(struct rpc_clnt *clnt) 133b4b9d2ccSJeff Layton { 134b4b9d2ccSJeff Layton int len; 135b4b9d2ccSJeff Layton char name[9]; /* 8 for hex digits + NULL terminator */ 136b4b9d2ccSJeff Layton 137b4b9d2ccSJeff Layton /* Already registered? */ 138b4b9d2ccSJeff Layton if (clnt->cl_debugfs) 139b4b9d2ccSJeff Layton return 0; 140b4b9d2ccSJeff Layton 141b4b9d2ccSJeff Layton len = snprintf(name, sizeof(name), "%x", clnt->cl_clid); 142b4b9d2ccSJeff Layton if (len >= sizeof(name)) 143b4b9d2ccSJeff Layton return -EINVAL; 144b4b9d2ccSJeff Layton 145b4b9d2ccSJeff Layton /* make the per-client dir */ 146b4b9d2ccSJeff Layton clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir); 147b4b9d2ccSJeff Layton if (!clnt->cl_debugfs) 148b4b9d2ccSJeff Layton return -ENOMEM; 149b4b9d2ccSJeff Layton 150b4b9d2ccSJeff Layton /* make tasks file */ 151b4b9d2ccSJeff Layton if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs, 152b4b9d2ccSJeff Layton clnt, &tasks_fops)) { 153b4b9d2ccSJeff Layton debugfs_remove_recursive(clnt->cl_debugfs); 154b4b9d2ccSJeff Layton clnt->cl_debugfs = NULL; 155b4b9d2ccSJeff Layton return -ENOMEM; 156b4b9d2ccSJeff Layton } 157b4b9d2ccSJeff Layton 158b4b9d2ccSJeff Layton return 0; 159b4b9d2ccSJeff Layton } 160b4b9d2ccSJeff Layton 161b4b9d2ccSJeff Layton void 162b4b9d2ccSJeff Layton rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt) 163b4b9d2ccSJeff Layton { 164b4b9d2ccSJeff Layton debugfs_remove_recursive(clnt->cl_debugfs); 165b4b9d2ccSJeff Layton clnt->cl_debugfs = NULL; 166b4b9d2ccSJeff Layton } 167b4b9d2ccSJeff Layton 168b4b9d2ccSJeff Layton void __exit 169b4b9d2ccSJeff Layton sunrpc_debugfs_exit(void) 170b4b9d2ccSJeff Layton { 171b4b9d2ccSJeff Layton debugfs_remove_recursive(topdir); 172b4b9d2ccSJeff Layton } 173b4b9d2ccSJeff Layton 174b4b9d2ccSJeff Layton int __init 175b4b9d2ccSJeff Layton sunrpc_debugfs_init(void) 176b4b9d2ccSJeff Layton { 177b4b9d2ccSJeff Layton topdir = debugfs_create_dir("sunrpc", NULL); 178b4b9d2ccSJeff Layton if (!topdir) 179b4b9d2ccSJeff Layton goto out; 180b4b9d2ccSJeff Layton 181b4b9d2ccSJeff Layton rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir); 182b4b9d2ccSJeff Layton if (!rpc_clnt_dir) 183b4b9d2ccSJeff Layton goto out_remove; 184b4b9d2ccSJeff Layton 185b4b9d2ccSJeff Layton return 0; 186b4b9d2ccSJeff Layton out_remove: 187b4b9d2ccSJeff Layton debugfs_remove_recursive(topdir); 188b4b9d2ccSJeff Layton topdir = NULL; 189b4b9d2ccSJeff Layton out: 190b4b9d2ccSJeff Layton return -ENOMEM; 191b4b9d2ccSJeff Layton } 192