18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * debugfs interface for sunrpc 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) 2014 Jeff Layton <jlayton@primarydata.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 98c2ecf20Sopenharmony_ci#include <linux/sunrpc/sched.h> 108c2ecf20Sopenharmony_ci#include <linux/sunrpc/clnt.h> 118c2ecf20Sopenharmony_ci#include "netns.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic struct dentry *topdir; 148c2ecf20Sopenharmony_cistatic struct dentry *rpc_clnt_dir; 158c2ecf20Sopenharmony_cistatic struct dentry *rpc_xprt_dir; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ciunsigned int rpc_inject_disconnect; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic int 208c2ecf20Sopenharmony_citasks_show(struct seq_file *f, void *v) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci u32 xid = 0; 238c2ecf20Sopenharmony_ci struct rpc_task *task = v; 248c2ecf20Sopenharmony_ci struct rpc_clnt *clnt = task->tk_client; 258c2ecf20Sopenharmony_ci const char *rpc_waitq = "none"; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci if (RPC_IS_QUEUED(task)) 288c2ecf20Sopenharmony_ci rpc_waitq = rpc_qname(task->tk_waitqueue); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (task->tk_rqstp) 318c2ecf20Sopenharmony_ci xid = be32_to_cpu(task->tk_rqstp->rq_xid); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci seq_printf(f, "%5u %04x %6d 0x%x 0x%x %8ld %ps %sv%u %s a:%ps q:%s\n", 348c2ecf20Sopenharmony_ci task->tk_pid, task->tk_flags, task->tk_status, 358c2ecf20Sopenharmony_ci clnt->cl_clid, xid, rpc_task_timeout(task), task->tk_ops, 368c2ecf20Sopenharmony_ci clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task), 378c2ecf20Sopenharmony_ci task->tk_action, rpc_waitq); 388c2ecf20Sopenharmony_ci return 0; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void * 428c2ecf20Sopenharmony_citasks_start(struct seq_file *f, loff_t *ppos) 438c2ecf20Sopenharmony_ci __acquires(&clnt->cl_lock) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct rpc_clnt *clnt = f->private; 468c2ecf20Sopenharmony_ci loff_t pos = *ppos; 478c2ecf20Sopenharmony_ci struct rpc_task *task; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci spin_lock(&clnt->cl_lock); 508c2ecf20Sopenharmony_ci list_for_each_entry(task, &clnt->cl_tasks, tk_task) 518c2ecf20Sopenharmony_ci if (pos-- == 0) 528c2ecf20Sopenharmony_ci return task; 538c2ecf20Sopenharmony_ci return NULL; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic void * 578c2ecf20Sopenharmony_citasks_next(struct seq_file *f, void *v, loff_t *pos) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct rpc_clnt *clnt = f->private; 608c2ecf20Sopenharmony_ci struct rpc_task *task = v; 618c2ecf20Sopenharmony_ci struct list_head *next = task->tk_task.next; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci ++*pos; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* If there's another task on list, return it */ 668c2ecf20Sopenharmony_ci if (next == &clnt->cl_tasks) 678c2ecf20Sopenharmony_ci return NULL; 688c2ecf20Sopenharmony_ci return list_entry(next, struct rpc_task, tk_task); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void 728c2ecf20Sopenharmony_citasks_stop(struct seq_file *f, void *v) 738c2ecf20Sopenharmony_ci __releases(&clnt->cl_lock) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct rpc_clnt *clnt = f->private; 768c2ecf20Sopenharmony_ci spin_unlock(&clnt->cl_lock); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic const struct seq_operations tasks_seq_operations = { 808c2ecf20Sopenharmony_ci .start = tasks_start, 818c2ecf20Sopenharmony_ci .next = tasks_next, 828c2ecf20Sopenharmony_ci .stop = tasks_stop, 838c2ecf20Sopenharmony_ci .show = tasks_show, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int tasks_open(struct inode *inode, struct file *filp) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int ret = seq_open(filp, &tasks_seq_operations); 898c2ecf20Sopenharmony_ci if (!ret) { 908c2ecf20Sopenharmony_ci struct seq_file *seq = filp->private_data; 918c2ecf20Sopenharmony_ci struct rpc_clnt *clnt = seq->private = inode->i_private; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (!atomic_inc_not_zero(&clnt->cl_count)) { 948c2ecf20Sopenharmony_ci seq_release(inode, filp); 958c2ecf20Sopenharmony_ci ret = -EINVAL; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return ret; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int 1038c2ecf20Sopenharmony_citasks_release(struct inode *inode, struct file *filp) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct seq_file *seq = filp->private_data; 1068c2ecf20Sopenharmony_ci struct rpc_clnt *clnt = seq->private; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci rpc_release_client(clnt); 1098c2ecf20Sopenharmony_ci return seq_release(inode, filp); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic const struct file_operations tasks_fops = { 1138c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1148c2ecf20Sopenharmony_ci .open = tasks_open, 1158c2ecf20Sopenharmony_ci .read = seq_read, 1168c2ecf20Sopenharmony_ci .llseek = seq_lseek, 1178c2ecf20Sopenharmony_ci .release = tasks_release, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int do_xprt_debugfs(struct rpc_clnt *clnt, struct rpc_xprt *xprt, void *numv) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int len; 1238c2ecf20Sopenharmony_ci char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */ 1248c2ecf20Sopenharmony_ci char link[9]; /* enough for 8 hex digits + NULL */ 1258c2ecf20Sopenharmony_ci int *nump = numv; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(xprt->debugfs)) 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci len = snprintf(name, sizeof(name), "../../rpc_xprt/%s", 1308c2ecf20Sopenharmony_ci xprt->debugfs->d_name.name); 1318c2ecf20Sopenharmony_ci if (len >= sizeof(name)) 1328c2ecf20Sopenharmony_ci return -1; 1338c2ecf20Sopenharmony_ci if (*nump == 0) 1348c2ecf20Sopenharmony_ci strcpy(link, "xprt"); 1358c2ecf20Sopenharmony_ci else { 1368c2ecf20Sopenharmony_ci len = snprintf(link, sizeof(link), "xprt%d", *nump); 1378c2ecf20Sopenharmony_ci if (len >= sizeof(link)) 1388c2ecf20Sopenharmony_ci return -1; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci debugfs_create_symlink(link, clnt->cl_debugfs, name); 1418c2ecf20Sopenharmony_ci (*nump)++; 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_civoid 1468c2ecf20Sopenharmony_cirpc_clnt_debugfs_register(struct rpc_clnt *clnt) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci int len; 1498c2ecf20Sopenharmony_ci char name[9]; /* enough for 8 hex digits + NULL */ 1508c2ecf20Sopenharmony_ci int xprtnum = 0; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci len = snprintf(name, sizeof(name), "%x", clnt->cl_clid); 1538c2ecf20Sopenharmony_ci if (len >= sizeof(name)) 1548c2ecf20Sopenharmony_ci return; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* make the per-client dir */ 1578c2ecf20Sopenharmony_ci clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* make tasks file */ 1608c2ecf20Sopenharmony_ci debugfs_create_file("tasks", S_IFREG | 0400, clnt->cl_debugfs, clnt, 1618c2ecf20Sopenharmony_ci &tasks_fops); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci rpc_clnt_iterate_for_each_xprt(clnt, do_xprt_debugfs, &xprtnum); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_civoid 1678c2ecf20Sopenharmony_cirpc_clnt_debugfs_unregister(struct rpc_clnt *clnt) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci debugfs_remove_recursive(clnt->cl_debugfs); 1708c2ecf20Sopenharmony_ci clnt->cl_debugfs = NULL; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int 1748c2ecf20Sopenharmony_cixprt_info_show(struct seq_file *f, void *v) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct rpc_xprt *xprt = f->private; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci seq_printf(f, "netid: %s\n", xprt->address_strings[RPC_DISPLAY_NETID]); 1798c2ecf20Sopenharmony_ci seq_printf(f, "addr: %s\n", xprt->address_strings[RPC_DISPLAY_ADDR]); 1808c2ecf20Sopenharmony_ci seq_printf(f, "port: %s\n", xprt->address_strings[RPC_DISPLAY_PORT]); 1818c2ecf20Sopenharmony_ci seq_printf(f, "state: 0x%lx\n", xprt->state); 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int 1868c2ecf20Sopenharmony_cixprt_info_open(struct inode *inode, struct file *filp) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci int ret; 1898c2ecf20Sopenharmony_ci struct rpc_xprt *xprt = inode->i_private; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci ret = single_open(filp, xprt_info_show, xprt); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (!ret) { 1948c2ecf20Sopenharmony_ci if (!xprt_get(xprt)) { 1958c2ecf20Sopenharmony_ci single_release(inode, filp); 1968c2ecf20Sopenharmony_ci ret = -EINVAL; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci return ret; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int 2038c2ecf20Sopenharmony_cixprt_info_release(struct inode *inode, struct file *filp) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct rpc_xprt *xprt = inode->i_private; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci xprt_put(xprt); 2088c2ecf20Sopenharmony_ci return single_release(inode, filp); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic const struct file_operations xprt_info_fops = { 2128c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2138c2ecf20Sopenharmony_ci .open = xprt_info_open, 2148c2ecf20Sopenharmony_ci .read = seq_read, 2158c2ecf20Sopenharmony_ci .llseek = seq_lseek, 2168c2ecf20Sopenharmony_ci .release = xprt_info_release, 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_civoid 2208c2ecf20Sopenharmony_cirpc_xprt_debugfs_register(struct rpc_xprt *xprt) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci int len, id; 2238c2ecf20Sopenharmony_ci static atomic_t cur_id; 2248c2ecf20Sopenharmony_ci char name[9]; /* 8 hex digits + NULL term */ 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci id = (unsigned int)atomic_inc_return(&cur_id); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci len = snprintf(name, sizeof(name), "%x", id); 2298c2ecf20Sopenharmony_ci if (len >= sizeof(name)) 2308c2ecf20Sopenharmony_ci return; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* make the per-client dir */ 2338c2ecf20Sopenharmony_ci xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* make tasks file */ 2368c2ecf20Sopenharmony_ci debugfs_create_file("info", S_IFREG | 0400, xprt->debugfs, xprt, 2378c2ecf20Sopenharmony_ci &xprt_info_fops); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_civoid 2438c2ecf20Sopenharmony_cirpc_xprt_debugfs_unregister(struct rpc_xprt *xprt) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci debugfs_remove_recursive(xprt->debugfs); 2468c2ecf20Sopenharmony_ci xprt->debugfs = NULL; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int 2508c2ecf20Sopenharmony_cifault_open(struct inode *inode, struct file *filp) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci filp->private_data = kmalloc(128, GFP_KERNEL); 2538c2ecf20Sopenharmony_ci if (!filp->private_data) 2548c2ecf20Sopenharmony_ci return -ENOMEM; 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int 2598c2ecf20Sopenharmony_cifault_release(struct inode *inode, struct file *filp) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci kfree(filp->private_data); 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic ssize_t 2668c2ecf20Sopenharmony_cifault_disconnect_read(struct file *filp, char __user *user_buf, 2678c2ecf20Sopenharmony_ci size_t len, loff_t *offset) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci char *buffer = (char *)filp->private_data; 2708c2ecf20Sopenharmony_ci size_t size; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci size = sprintf(buffer, "%u\n", rpc_inject_disconnect); 2738c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, len, offset, buffer, size); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic ssize_t 2778c2ecf20Sopenharmony_cifault_disconnect_write(struct file *filp, const char __user *user_buf, 2788c2ecf20Sopenharmony_ci size_t len, loff_t *offset) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci char buffer[16]; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (len >= sizeof(buffer)) 2838c2ecf20Sopenharmony_ci len = sizeof(buffer) - 1; 2848c2ecf20Sopenharmony_ci if (copy_from_user(buffer, user_buf, len)) 2858c2ecf20Sopenharmony_ci return -EFAULT; 2868c2ecf20Sopenharmony_ci buffer[len] = '\0'; 2878c2ecf20Sopenharmony_ci if (kstrtouint(buffer, 10, &rpc_inject_disconnect)) 2888c2ecf20Sopenharmony_ci return -EINVAL; 2898c2ecf20Sopenharmony_ci return len; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic const struct file_operations fault_disconnect_fops = { 2938c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2948c2ecf20Sopenharmony_ci .open = fault_open, 2958c2ecf20Sopenharmony_ci .read = fault_disconnect_read, 2968c2ecf20Sopenharmony_ci .write = fault_disconnect_write, 2978c2ecf20Sopenharmony_ci .release = fault_release, 2988c2ecf20Sopenharmony_ci}; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_civoid __exit 3018c2ecf20Sopenharmony_cisunrpc_debugfs_exit(void) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci debugfs_remove_recursive(topdir); 3048c2ecf20Sopenharmony_ci topdir = NULL; 3058c2ecf20Sopenharmony_ci rpc_clnt_dir = NULL; 3068c2ecf20Sopenharmony_ci rpc_xprt_dir = NULL; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_civoid __init 3108c2ecf20Sopenharmony_cisunrpc_debugfs_init(void) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct dentry *rpc_fault_dir; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci topdir = debugfs_create_dir("sunrpc", NULL); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci rpc_fault_dir = debugfs_create_dir("inject_fault", topdir); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci debugfs_create_file("disconnect", S_IFREG | 0400, rpc_fault_dir, NULL, 3238c2ecf20Sopenharmony_ci &fault_disconnect_fops); 3248c2ecf20Sopenharmony_ci} 325