/*
* 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.
*
* NB: BSD uses a more intelligent approach to guessing when a request
* or reply has been lost by keeping the RTO estimate for each procedure.
* We currently make do with a constant timeout value.
*
* Copyright (C) 1992,1993 Rick Sladkey <jrs@world.std.com>
* Copyright (C) 1995,1996 Olaf Kirch <okir@monad.swb.de>
*/
#include <asm/system.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/utsname.h>
#include <linux/workqueue.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
#include <linux/sunrpc/metrics.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_CALL
#endif
#define dprint_status(t) \
dprintk("RPC: %5u %s (status %d)\n", t->tk_pid, \
__FUNCTION__, t->tk_status)
/*
* All RPC clients are linked into this list
*/
static LIST_HEAD(all_clients);
static DEFINE_SPINLOCK(rpc_client_lock);
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_encode(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);
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 * call_header(struct rpc_task *task);
static __be32 * call_verify(struct rpc_task *task);
static int rpc_ping(struct rpc_clnt *clnt, int flags);
static void rpc_register_client(struct rpc_clnt *clnt)
{
spin_lock(&rpc_client_lock);
list_add(&clnt->cl_clients, &all_clients);
spin_unlock(&rpc_client_lock);
}
static void rpc_unregister_client(struct rpc_clnt *clnt)
{
spin_lock(&rpc_client_lock);
list_del(&clnt->cl_clients);
spin_unlock(&rpc_client_lock);
}
static int
rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
{
static uint32_t clntid;
int error;
clnt->cl_vfsmnt = ERR_PTR(-ENOENT);
clnt->cl_dentry = ERR_PTR(-ENOENT);
if (dir_name == NULL)
return 0;
clnt->cl_vfsmnt = rpc_get_mount();
if (IS_ERR(clnt->cl_vfsmnt))
return PTR_ERR(clnt->cl_vfsmnt);
for (;;) {
snprintf(clnt->cl_pathname, sizeof(clnt->cl_pathname),
"%s/clnt%x", dir_name,
(unsigned int)clntid++);
clnt->cl_pathname[sizeof(clnt->cl_pathname) - 1] = '\0';
clnt->cl_dentry = rpc_mkdir(clnt->cl_pathname, clnt);
if (!IS_ERR(clnt->cl_dentry))
return 0;
error = PTR_ERR(clnt->cl_dentry);
if (error != -EEXIST) {
printk(KERN_INFO "RPC: Couldn't create pipefs entry %s, error %d\n",
clnt->cl_pathname, error);
rpc_put_mount();
return error;
}
}
}
static struct rpc_clnt * rpc_new_client(struct rpc_xprt *xprt, char *servname, struct rpc_program *program, u32 vers, rpc_authflavor_t flavor)
{
struct rpc_version *version;
struct rpc_clnt *clnt = NULL;
struct rpc_auth *auth;
int err;
size_t len;
/* sanity check the name before trying to print it */
err = -EINVAL;
len = strlen(servname);
if (len > RPC_MAXNETNAMELEN)
goto out_no_rpciod;
len++;
dprintk("RPC: creating %s client for %s (xprt %p)\n",
program->name, servname, xprt);
err = rpciod_up();
if (err)
goto out_no_rpciod;
err = -EINVAL;
if (!xprt)
goto out_no_xprt;
if (vers >= program->nrvers || !(version = program->version[vers]))
goto out_err;
err = -ENOMEM;
clnt = kzalloc(sizeof(*clnt), GFP_KERNEL);
if (!clnt)
goto out_err;
clnt->cl_parent = clnt;
clnt->cl_server = clnt->cl_inline_name;
if (len > sizeof(clnt->cl_inline_name)) {
char *buf = kmalloc(len, GFP_KERNEL);
if (buf != 0)
clnt->cl_server = buf;
else
len = sizeof(clnt->cl_inline_name);
}
strlcpy(clnt->cl_server, servname, len);
clnt->cl_xprt = xprt;
clnt->cl_procinfo = version->procs;
clnt->cl_maxproc = version->nrprocs;
clnt->cl_protname = program->name;
clnt->cl_prog = program->number;
clnt->cl_vers = version->number;
clnt->cl_stats = program->stats;
clnt->cl_metrics = rpc_alloc_iostats(clnt);
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);
if (!xprt_bound(clnt->cl_xprt))
clnt->cl_autobind = 1;
clnt->cl_rtt = &clnt->cl_rtt_default;
rpc_init_rtt(&clnt->cl_rtt_default, xprt->timeout.to_initval);
kref_init(&clnt->cl_kref);
err = rpc_setup_pipedir(clnt, program->pipe_dir_name);
if (err < 0)
goto out_no_path;
auth = rpcauth_create(flavor, clnt);
if (IS_ERR(auth)) {
printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %u)\n",
flavor);
err = PTR_ERR(auth);
goto out_no_auth;
}
/* save the nodename */
clnt->cl_nodelen = strlen(utsn
|