cregit-Linux how code gets into the kernel

Release 4.8 net/sunrpc/clnt.c

Directory: net/sunrpc
/*
 *  linux/net/sunrpc/clnt.c
 *
 *  This file contains the high-level RPC interface.
 *  It is modeled as a finite state machine to support both synchronous
 *  and asynchronous requests.
 *
 *  -   RPC header generation and argument serialization.
 *  -   Credential refresh.
 *  -   TCP connect handling.
 *  -   Retry of operation when it is suspected the operation failed because
 *      of uid squashing on the server, or when the credentials were stale
 *      and need to be refreshed, or when a packet was damaged in transit.
 *      This may be have to be moved to the VFS layer.
 *
 *  Copyright (C) 1992,1993 Rick Sladkey <jrs@world.std.com>
 *  Copyright (C) 1995,1996 Olaf Kirch <okir@monad.swb.de>
 */


#include <linux/module.h>
#include <linux/types.h>
#include <linux/kallsyms.h>
#include <linux/mm.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/slab.h>
#include <linux/rcupdate.h>
#include <linux/utsname.h>
#include <linux/workqueue.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/un.h>

#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/sunrpc/metrics.h>
#include <linux/sunrpc/bc_xprt.h>
#include <trace/events/sunrpc.h>

#include "sunrpc.h"
#include "netns.h"

#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)

# define RPCDBG_FACILITY	RPCDBG_CALL
#endif


#define dprint_status(t)					\
	dprintk("RPC: %5u %s (status %d)\n", t->tk_pid,         \
                        __func__, t->tk_status)

/*
 * All RPC clients are linked into this list
 */

static DECLARE_WAIT_QUEUE_HEAD(destroy_wait);


static void	call_start(struct rpc_task *task);
static void	call_reserve(struct rpc_task *task);
static void	call_reserveresult(struct rpc_task *task);
static void	call_allocate(struct rpc_task *task);
static void	call_decode(struct rpc_task *task);
static void	call_bind(struct rpc_task *task);
static void	call_bind_status(struct rpc_task *task);
static void	call_transmit(struct rpc_task *task);
#if defined(CONFIG_SUNRPC_BACKCHANNEL)
static void	call_bc_transmit(struct rpc_task *task);
#endif /* CONFIG_SUNRPC_BACKCHANNEL */
static void	call_status(struct rpc_task *task);
static void	call_transmit_status(struct rpc_task *task);
static void	call_refresh(struct rpc_task *task);
static void	call_refreshresult(struct rpc_task *task);
static void	call_timeout(struct rpc_task *task);
static void	call_connect(struct rpc_task *task);
static void	call_connect_status(struct rpc_task *task);

static __be32	*rpc_encode_header(struct rpc_task *task);
static __be32	*rpc_verify_header(struct rpc_task *task);
static int	rpc_ping(struct rpc_clnt *clnt);


static void rpc_register_client(struct rpc_clnt *clnt) { struct net *net = rpc_net_ns(clnt); struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); spin_lock(&sn->rpc_client_lock); list_add(&clnt->cl_clients, &sn->all_clients); spin_unlock(&sn->rpc_client_lock); }

Contributors

PersonTokensPropCommitsCommitProp
trond myklebusttrond myklebust4572.58%266.67%
stanislav kinsburskystanislav kinsbursky1727.42%133.33%
Total62100.00%3100.00%


static void rpc_unregister_client(struct rpc_clnt *clnt) { struct net *net = rpc_net_ns(clnt); struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); spin_lock(&sn->rpc_client_lock); list_del(&clnt->cl_clients); spin_unlock(&sn->rpc_client_lock); }

Contributors

PersonTokensPropCommitsCommitProp
trond myklebusttrond myklebust4273.68%266.67%
stanislav kinsburskystanislav kinsbursky1526.32%133.33%
Total57100.00%3100.00%


static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) { rpc_remove_client_dir(clnt); }

Contributors

PersonTokensPropCommitsCommitProp
stanislav kinsburskystanislav kinsbursky1593.75%266.67%
trond myklebusttrond myklebust16.25%133.33%
Total16100.00%3100.00%


static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) { struct net *net = rpc_net_ns(clnt); struct super_block *pipefs_sb; pipefs_sb = rpc_get_sb_net(net); if (pipefs_sb) { __rpc_clnt_remove_pipedir(clnt); rpc_put_sb_net(net); } }

Contributors

PersonTokensPropCommitsCommitProp
stanislav kinsburskystanislav kinsbursky3775.51%150.00%
trond myklebusttrond myklebust1224.49%150.00%
Total49100.00%2100.00%


static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb, struct rpc_clnt *clnt) { static uint32_t clntid; const char *dir_name = clnt->cl_program->pipe_dir_name; char name[15]; struct dentry *dir, *dentry; dir = rpc_d_lookup_sb(sb, dir_name); if (dir == NULL) { pr_info("RPC: pipefs directory doesn't exist: %s\n", dir_name); return dir; } for (;;) { snprintf(name, sizeof(name), "clnt%x", (unsigned int)clntid++); name[sizeof(name) - 1] = '\0'; dentry = rpc_create_client_dir(dir, name, clnt); if (!IS_ERR(dentry)) break; if (dentry == ERR_PTR(-EEXIST)) continue; printk(KERN_INFO "RPC: Couldn't create pipefs entry" " %s/%s, error %ld\n", dir_name, name, PTR_ERR(dentry)); break; } dput(dir); return dentry; }

Contributors

PersonTokensPropCommitsCommitProp
trond myklebusttrond myklebust10461.90%758.33%
stanislav kinsburskystanislav kinsbursky3017.86%18.33%
al viroal viro158.93%216.67%
christoph hellwigchristoph hellwig105.95%18.33%
weston andros adamsonweston andros adamson95.36%18.33%
Total168100.00%12100.00%


static int rpc_setup_pipedir(struct super_block *pipefs_sb, struct rpc_clnt *clnt) { struct dentry *dentry; if (clnt->cl_program->pipe_dir_name != NULL) { dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt); if (IS_ERR(dentry)) return PTR_ERR(dentry); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
stanislav kinsburskystanislav kinsbursky4374.14%342.86%
trond myklebusttrond myklebust1525.86%457.14%
Total58100.00%7100.00%


static int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event) { if (clnt->cl_program->pipe_dir_name == NULL) return 1; switch (event) { case RPC_PIPEFS_MOUNT: if (clnt->cl_pipedir_objects.pdh_dentry != NULL) return 1; if (atomic_read(&clnt->cl_count) == 0) return 1; break; case RPC_PIPEFS_UMOUNT: if (clnt->cl_pipedir_objects.pdh_dentry == NULL) return 1; break; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
stanislav kinsburskystanislav kinsbursky4653.49%360.00%
trond myklebusttrond myklebust4046.51%240.00%
Total86100.00%5100.00%


static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event, struct super_block *sb) { struct dentry *dentry; int err = 0; switch (event) { case RPC_PIPEFS_MOUNT: dentry = rpc_setup_pipedir_sb(sb, clnt); if (!dentry) return -ENOENT; if (IS_ERR(dentry)) return PTR_ERR(dentry); break; case RPC_PIPEFS_UMOUNT: __rpc_clnt_remove_pipedir(clnt); break; default: printk(KERN_ERR "%s: unknown event: %ld\n", __func__, event); return -ENOTSUPP; } return err; }

Contributors

PersonTokensPropCommitsCommitProp
stanislav kinsburskystanislav kinsbursky9091.84%266.67%
weston andros adamsonweston andros adamson88.16%133.33%
Total98100.00%3100.00%


static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event, struct super_block *sb) { int error = 0; for (;; clnt = clnt->cl_parent) { if (!rpc_clnt_skip_event(clnt, event)) error = __rpc_clnt_handle_event(clnt, event, sb); if (error || clnt == clnt->cl_parent) break; } return error; }

Contributors

PersonTokensPropCommitsCommitProp
stanislav kinsburskystanislav kinsbursky72100.00%1100.00%
Total72100.00%1100.00%


static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event) { struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); struct rpc_clnt *clnt; spin_lock(&sn->rpc_client_lock); list_for_each_entry(clnt, &sn->all_clients, cl_clients) { if (rpc_clnt_skip_event(clnt, event)) continue; spin_unlock(&sn->rpc_client_lock); return clnt; } spin_unlock(&sn->rpc_client_lock); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
stanislav kinsburskystanislav kinsbursky83100.00%2100.00%
Total83100.00%2100.00%


static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr) { struct super_block *sb = ptr; struct rpc_clnt *clnt; int error = 0; while ((clnt = rpc_get_client_for_event(sb->s_fs_info, event))) { error = __rpc_pipefs_event(clnt, event, sb); if (error) break; } return error; }

Contributors

PersonTokensPropCommitsCommitProp
stanislav kinsburskystanislav kinsbursky72100.00%2100.00%
Total72100.00%2100.00%

static struct notifier_block rpc_clients_block = { .notifier_call = rpc_pipefs_event, .priority = SUNRPC_PIPEFS_RPC_PRIO, };
int rpc_clients_notifier_register(void) { return rpc_pipefs_notifier_register(&rpc_clients_block); }

Contributors

PersonTokensPropCommitsCommitProp
stanislav kinsburskystanislav kinsbursky14100.00%1100.00%
Total14100.00%1100.00%


void rpc_clients_notifier_unregister(void) { return rpc_pipefs_notifier_unregister(&rpc_clients_block); }

Contributors

PersonTokensPropCommitsCommitProp
stanislav kinsburskystanislav kinsbursky14100.00%1100.00%
Total14100.00%1100.00%


static struct rpc_xprt *rpc_clnt_set_transport(struct rpc_clnt *clnt, struct rpc_xprt *xprt, const struct rpc_timeout *timeout) { struct rpc_xprt *old; spin_lock(&clnt->cl_lock); old = rcu_dereference_protected(clnt->cl_xprt, lockdep_is_held(&clnt->cl_lock)); if (!xprt_bound(xprt)) clnt->cl_autobind = 1; clnt->cl_timeout = timeout; rcu_assign_pointer(clnt->cl_xprt, xprt); spin_unlock(&clnt->cl_lock); return old; }

Contributors

PersonTokensPropCommitsCommitProp
trond myklebusttrond myklebust94100.00%2100.00%
Total94100.00%2100.00%


static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename) { clnt->cl_nodelen = strlcpy(clnt->cl_nodename, nodename, sizeof(clnt->cl_nodename)); }

Contributors

PersonTokensPropCommitsCommitProp
trond myklebusttrond myklebust36100.00%2100.00%
Total36100.00%2100.00%


static int rpc_client_register(struct rpc_clnt *clnt, rpc_authflavor_t pseudoflavor, const char *client_name) { struct rpc_auth_create_args auth_args = { .pseudoflavor = pseudoflavor, .target_name = client_name, }; struct rpc_auth *auth; struct net *net = rpc_net_ns(clnt); struct super_block *pipefs_sb; int err; rpc_clnt_debugfs_register(clnt); pipefs_sb = rpc_get_sb_net(net); if (pipefs_sb) { err = rpc_setup_pipedir(pipefs_sb, clnt); if (err) goto out; } rpc_register_client(clnt); if (pipefs_sb) rpc_put_sb_net(net); auth = rpcauth_create(&auth_args, clnt); if (IS_ERR(auth)) { dprintk("RPC: Couldn't create auth handle (flavor %u)\n", pseudoflavor); err = PTR_ERR(auth); goto err_auth; } return 0; err_auth: pipefs_sb = rpc_get_sb_net(net); rpc_unregister_client(clnt); __rpc_clnt_remove_pipedir(clnt); out: if (pipefs_sb) rpc_put_sb_net(net); rpc_clnt_debugfs_unregister(clnt); return err; }

Contributors

PersonTokensPropCommitsCommitProp
stanislav kinsburskystanislav kinsbursky11562.84%114.29%
trond myklebusttrond myklebust4826.23%457.14%
jeff laytonjeff layton105.46%114.29%
chuck leverchuck lever105.46%114.29%
Total183100.00%7100.00%

static DEFINE_IDA(rpc_clids);
static int rpc_alloc_clid(struct rpc_clnt *clnt) { int clid; clid = ida_simple_get(&rpc_clids, 0, 0, GFP_KERNEL); if (clid < 0) return clid; clnt->cl_clid = clid; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
trond myklebusttrond myklebust46100.00%1100.00%
Total46100.00%1100.00%


static void rpc_free_clid(struct rpc_clnt *clnt) { ida_simple_remove(&rpc_clids, clnt->cl_clid); }

Contributors

PersonTokensPropCommitsCommitProp
trond myklebusttrond myklebust21100.00%1100.00%
Total21100.00%1100.00%


static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt_switch *xps, struct rpc_xprt *xprt, struct rpc_clnt *parent) { const struct rpc_program *program = args->program; const struct rpc_version *version; struct rpc_clnt *clnt = NULL; const struct rpc_timeout *timeout; const char *nodename = args->nodename; int err; /* sanity check the name before trying to print it */ dprintk("RPC: creating %s client for %s (xprt %p)\n", program->name, args->servername, xprt); err = rpciod_up(); if (err) goto out_no_rpciod; err = -EINVAL; if (args->version >= program->nrvers) goto out_err; version = program->version[args->version]; if (version == NULL) goto out_err; err = -ENOMEM; clnt = kzalloc(sizeof(*clnt), GFP_KERNEL); if (!clnt) goto out_err; clnt->cl_parent = parent ? : clnt; err = rpc_alloc_clid(clnt); if (err) goto out_no_clid; clnt->cl_procinfo = version->procs; clnt->cl_maxproc = version->nrprocs; clnt->cl_prog = args->prognumber ? : program->number; clnt->cl_vers = version->number; clnt->cl_stats = program->stats; clnt->cl_metrics = rpc_alloc_iostats(clnt); rpc_init_pipe_dir_head(&clnt->cl_pipedir_objects); err = -ENOMEM; if (clnt->cl_metrics == NULL) goto out_no_stats; clnt->cl_program = program; INIT_LIST_HEAD(&clnt->cl_tasks); spin_lock_init(&clnt->cl_lock); timeout = xprt->timeout; if (args->timeout != NULL) { memcpy(&clnt->cl_timeout_default, args->timeout, sizeof(clnt->cl_timeout_default)); timeout = &clnt->cl_timeout_default; } rpc_clnt_set_transport(clnt, xprt, timeout); xprt_iter_init(&clnt->cl_xpi, xps); xprt_switch_put(xps); clnt->cl_rtt = &clnt->cl_rtt_default; rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval); atomic_set(&clnt->cl_count, 1); if (nodename == NULL) nodename = utsname()->nodename; /* save the nodename */ rpc_clnt_set_nodename(clnt, nodename); err = rpc_client_register(clnt, args->authflavor, args->client_name); if (err) goto out_no_path; if (parent) atomic_inc(&parent->cl_count); return clnt; out_no_path: rpc_free_iostats(clnt->cl_metrics); out_no_stats: rpc_free_clid(clnt); out_no_clid: kfree(clnt); out_err: rpciod_down(); out_no_rpciod: xprt_switch_put(xps); xprt_put(xprt); return ERR_PTR(err); }

Contributors

PersonTokensPropCommitsCommitProp
trond myklebusttrond myklebust32867.21%2365.71%
pre-gitpre-git11924.39%25.71%
chuck leverchuck lever193.89%514.29%
olga kornievskaiaolga kornievskaia61.23%12.86%
stanislav kinsburskystanislav kinsbursky61.23%12.86%
benny halevybenny halevy51.02%12.86%
j. bruce fieldsj. bruce fields40.82%12.86%
panagiotis issaris*panagiotis issaris*10.20%12.86%
Total488100.00%35100.00%


static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args, struct rpc_xprt *xprt) { struct rpc_clnt *clnt = NULL; struct rpc_xprt_switch *xps; if (args->bc_xprt && args->bc_xprt->xpt_bc_xps) { WARN_ON_ONCE(!(args->protocol & XPRT_TRANSPORT_BC)); xps = args->bc_xprt->xpt_bc_xps; xprt_switch_get(xps); } else { xps = xprt_switch_alloc(xprt, GFP_KERNEL); if (xps == NULL) { xprt_put(xprt); return ERR_PTR(-ENOMEM); } if (xprt->bc_xprt) { xprt_switch_get(xps); xprt->bc_xprt->xpt_bc_xps = xps; } } clnt = rpc_new_client(args, xps, xprt, NULL); if (IS_ERR(clnt)) return clnt; if (!(args->flags & RPC_CLNT_CREATE_NOPING)) { int err = rpc_ping(clnt); if (err != 0) { rpc_shutdown_client(clnt); return ERR_PTR(err); } } clnt->cl_softrtry = 1; if (args->flags & RPC_CLNT_CREATE_HARDRTRY) clnt->cl_softrtry = 0; if (args->flags & RPC_CLNT_CREATE_AUTOBIND) clnt->cl_autobind = 1; if (args->flags & RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT) clnt->cl_noretranstimeo = 1; if (args->flags & RPC_CLNT_CREATE_DISCRTRY) clnt->cl_discrtry = 1; if (!(args->flags & RPC_CLNT_CREATE_QUIET)) clnt->cl_chatty = 1; return clnt; }

Contributors

PersonTokensPropCommitsCommitProp
kinglong meekinglong mee15357.30%114.29%
j. bruce fieldsj. bruce fields6524.34%342.86%
trond myklebusttrond myklebust4316.10%228.57%
chuck leverchuck lever62.25%114.29%
Total267100.00%7100.00%

/** * rpc_create - create an RPC client and transport with one call * @args: rpc_clnt create argument structure * * Creates and initializes an RPC transport and an RPC client. * * It can ping the server in order to determine if it is up, and to see if * it supports this program and version. RPC_CLNT_CREATE_NOPING disables * this behavior so asynchronous tasks can also use rpc_create. */
struct rpc_clnt *rpc_create(struct rpc_create_args *args) { struct rpc_xprt *xprt; struct xprt_create xprtargs = { .net = args->net, .ident = args->protocol, .srcaddr = args->saddress, .dstaddr = args->address, .addrlen = args->addrsize, .servername = args->servername, .bc_xprt = args->bc_xprt, }; char servername[48]; if (args->bc_xprt) { WARN_ON_ONCE(!(args->protocol & XPRT_TRANSPORT_BC)); xprt = args->bc_xprt->xpt_bc_xprt; if (xprt) { xprt_get(xprt); return rpc_create_xprt(args, xprt); } } if (args->flags & RPC_CLNT_CREATE_INFINITE_SLOTS) xprtargs.flags |= XPRT_CREATE_INFINITE_SLOTS; if (args->flags & RPC_CLNT_CREATE_NO_IDLE_TIMEOUT) xprtargs.flags |= XPRT_CREATE_NO_IDLE_TIMEOUT; /* * If the caller chooses not to specify a hostname, whip * up a string representation of the passed-in address. */ if (xprtargs.servername == NULL) { struct sockaddr_un *sun = (struct sockaddr_un *)args->address; struct sockaddr_in *sin = (struct sockaddr_in *)args->address; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)args->address; servername[0] = '\0'; switch (args->address->sa_family) { case AF_LOCAL: snprintf(servername, sizeof(servername), "%s", sun->sun_path); break; case AF_INET: snprintf(servername, sizeof(servername), "%pI4", &sin->sin_addr.s_addr); break; case AF_INET6: snprintf(servername, sizeof(servername), "%pI6", &sin6->sin6_addr); break; default: /* caller wants default server name, but * address family isn't recognized. */ return ERR_PTR(-EINVAL); } xprtargs.servername = servername; } xprt = xprt_create_transport(&xprtargs); if (IS_ERR(xprt)) return (struct rpc_clnt *)xprt; /* * By default, kernel RPC client connects from a reserved port. * CAP_NET_BIND_SERVICE will not be set for unprivileged requesters, * but it is always enabled for rpciod, which handles the connect * operation. */ xprt->resvport = 1; if (args->flags & RPC_CLNT_CREATE_NONPRIVPORT) xprt->resvport = 0; return rpc_create_xprt(args, xprt); }

Contributors

PersonTokensPropCommitsCommitProp
chuck leverchuck lever22563.56%627.27%
j. bruce fieldsj. bruce fields5515.54%29.09%
trond myklebusttrond myklebust277.63%418.18%
frank van maarseveenfrank van maarseveen257.06%29.09%
alexandros batsakisalexandros batsakis71.98%14.55%
pavel emelianovpavel emelianov71.98%14.55%
harvey harrisonharvey harrison41.13%313.64%
thomas talpeythomas talpey20.56%29.09%
kinglong meekinglong mee20.56%14.55%
Total354100.00%22100.00%

EXPORT_SYMBOL_GPL(rpc_create); /* * This function clones the RPC client structure. It allows us to share the * same transport while varying parameters such as the authentication * flavour. */
static struct rpc_clnt *__rpc_clone_client(struct rpc_create_args *args, struct rpc_clnt *clnt) { struct rpc_xprt_switch *xps; struct rpc_xprt *xprt; struct rpc_clnt *new; int err; err = -ENOMEM; rcu_read_lock(); xprt = xprt_get(rcu_dereference(clnt->cl_xprt)); xps = xprt_switch_get(rcu_dereference(clnt->cl_xpi.xpi_xpswitch)); rcu_read_unlock(); if (xprt == NULL || xps == NULL) { xprt_put(xprt); xprt_switch_put(xps); goto out_err; } args->servername = xprt->servername; args->nodename = clnt->cl_nodename; new = rpc_new_client(args, xps, xprt, clnt); if (IS_ERR(new)) { err = PTR_ERR(new); goto out_err; } /* Turn off autobind on clones */ new->cl_autobind = 0; new->cl_softrtry = clnt->cl_softrtry; new->cl_noretranstimeo = clnt->cl_noretranstimeo; new->cl_discrtry = clnt->cl_discrtry; new->cl_chatty = clnt->cl_chatty; return new; out_err: dprintk("RPC: %s: returned error %d\n", __func__, err