OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [sunrpc/] [clnt.c] - Diff between revs 1275 and 1765

Only display areas with differences | Details | Blame | View Log

Rev 1275 Rev 1765
/*
/*
 *  linux/net/sunrpc/rpcclnt.c
 *  linux/net/sunrpc/rpcclnt.c
 *
 *
 *  This file contains the high-level RPC interface.
 *  This file contains the high-level RPC interface.
 *  It is modeled as a finite state machine to support both synchronous
 *  It is modeled as a finite state machine to support both synchronous
 *  and asynchronous requests.
 *  and asynchronous requests.
 *
 *
 *  -   RPC header generation and argument serialization.
 *  -   RPC header generation and argument serialization.
 *  -   Credential refresh.
 *  -   Credential refresh.
 *  -   TCP reconnect handling (when finished).
 *  -   TCP reconnect handling (when finished).
 *  -   Retry of operation when it is suspected the operation failed because
 *  -   Retry of operation when it is suspected the operation failed because
 *      of uid squashing on the server, or when the credentials were stale
 *      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.
 *      and need to be refreshed, or when a packet was damaged in transit.
 *      This may be have to be moved to the VFS layer.
 *      This may be have to be moved to the VFS layer.
 *
 *
 *  NB: BSD uses a more intelligent approach to guessing when a request
 *  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.
 *  or reply has been lost by keeping the RTO estimate for each procedure.
 *  We currently make do with a constant timeout value.
 *  We currently make do with a constant timeout value.
 *
 *
 *  Copyright (C) 1992,1993 Rick Sladkey <jrs@world.std.com>
 *  Copyright (C) 1992,1993 Rick Sladkey <jrs@world.std.com>
 *  Copyright (C) 1995,1996 Olaf Kirch <okir@monad.swb.de>
 *  Copyright (C) 1995,1996 Olaf Kirch <okir@monad.swb.de>
 */
 */
 
 
#include <asm/system.h>
#include <asm/system.h>
 
 
#include <linux/types.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/in.h>
#include <linux/in.h>
#include <linux/utsname.h>
#include <linux/utsname.h>
 
 
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/clnt.h>
 
 
#include <linux/nfs.h>
#include <linux/nfs.h>
 
 
 
 
#define RPC_SLACK_SPACE         512     /* total overkill */
#define RPC_SLACK_SPACE         512     /* total overkill */
 
 
#ifdef RPC_DEBUG
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY        RPCDBG_CALL
# define RPCDBG_FACILITY        RPCDBG_CALL
#endif
#endif
 
 
static DECLARE_WAIT_QUEUE_HEAD(destroy_wait);
static DECLARE_WAIT_QUEUE_HEAD(destroy_wait);
 
 
 
 
static void     call_start(struct rpc_task *task);
static void     call_start(struct rpc_task *task);
static void     call_reserve(struct rpc_task *task);
static void     call_reserve(struct rpc_task *task);
static void     call_reserveresult(struct rpc_task *task);
static void     call_reserveresult(struct rpc_task *task);
static void     call_allocate(struct rpc_task *task);
static void     call_allocate(struct rpc_task *task);
static void     call_encode(struct rpc_task *task);
static void     call_encode(struct rpc_task *task);
static void     call_decode(struct rpc_task *task);
static void     call_decode(struct rpc_task *task);
static void     call_bind(struct rpc_task *task);
static void     call_bind(struct rpc_task *task);
static void     call_transmit(struct rpc_task *task);
static void     call_transmit(struct rpc_task *task);
static void     call_status(struct rpc_task *task);
static void     call_status(struct rpc_task *task);
static void     call_refresh(struct rpc_task *task);
static void     call_refresh(struct rpc_task *task);
static void     call_refreshresult(struct rpc_task *task);
static void     call_refreshresult(struct rpc_task *task);
static void     call_timeout(struct rpc_task *task);
static void     call_timeout(struct rpc_task *task);
static void     call_connect(struct rpc_task *task);
static void     call_connect(struct rpc_task *task);
static void     call_connect_status(struct rpc_task *);
static void     call_connect_status(struct rpc_task *);
static u32 *    call_header(struct rpc_task *task);
static u32 *    call_header(struct rpc_task *task);
static u32 *    call_verify(struct rpc_task *task);
static u32 *    call_verify(struct rpc_task *task);
 
 
 
 
/*
/*
 * Create an RPC client
 * Create an RPC client
 * FIXME: This should also take a flags argument (as in task->tk_flags).
 * FIXME: This should also take a flags argument (as in task->tk_flags).
 * It's called (among others) from pmap_create_client, which may in
 * It's called (among others) from pmap_create_client, which may in
 * turn be called by an async task. In this case, rpciod should not be
 * turn be called by an async task. In this case, rpciod should not be
 * made to sleep too long.
 * made to sleep too long.
 */
 */
struct rpc_clnt *
struct rpc_clnt *
rpc_create_client(struct rpc_xprt *xprt, char *servname,
rpc_create_client(struct rpc_xprt *xprt, char *servname,
                  struct rpc_program *program, u32 vers, int flavor)
                  struct rpc_program *program, u32 vers, int flavor)
{
{
        struct rpc_version      *version;
        struct rpc_version      *version;
        struct rpc_clnt         *clnt = NULL;
        struct rpc_clnt         *clnt = NULL;
 
 
        dprintk("RPC: creating %s client for %s (xprt %p)\n",
        dprintk("RPC: creating %s client for %s (xprt %p)\n",
                program->name, servname, xprt);
                program->name, servname, xprt);
 
 
        if (!xprt)
        if (!xprt)
                goto out;
                goto out;
        if (vers >= program->nrvers || !(version = program->version[vers]))
        if (vers >= program->nrvers || !(version = program->version[vers]))
                goto out;
                goto out;
 
 
        clnt = (struct rpc_clnt *) rpc_allocate(0, sizeof(*clnt));
        clnt = (struct rpc_clnt *) rpc_allocate(0, sizeof(*clnt));
        if (!clnt)
        if (!clnt)
                goto out_no_clnt;
                goto out_no_clnt;
        memset(clnt, 0, sizeof(*clnt));
        memset(clnt, 0, sizeof(*clnt));
        atomic_set(&clnt->cl_users, 0);
        atomic_set(&clnt->cl_users, 0);
 
 
        clnt->cl_xprt     = xprt;
        clnt->cl_xprt     = xprt;
        clnt->cl_procinfo = version->procs;
        clnt->cl_procinfo = version->procs;
        clnt->cl_maxproc  = version->nrprocs;
        clnt->cl_maxproc  = version->nrprocs;
        clnt->cl_server   = servname;
        clnt->cl_server   = servname;
        clnt->cl_protname = program->name;
        clnt->cl_protname = program->name;
        clnt->cl_port     = xprt->addr.sin_port;
        clnt->cl_port     = xprt->addr.sin_port;
        clnt->cl_prog     = program->number;
        clnt->cl_prog     = program->number;
        clnt->cl_vers     = version->number;
        clnt->cl_vers     = version->number;
        clnt->cl_prot     = xprt->prot;
        clnt->cl_prot     = xprt->prot;
        clnt->cl_stats    = program->stats;
        clnt->cl_stats    = program->stats;
        INIT_RPC_WAITQ(&clnt->cl_bindwait, "bindwait");
        INIT_RPC_WAITQ(&clnt->cl_bindwait, "bindwait");
 
 
        if (!clnt->cl_port)
        if (!clnt->cl_port)
                clnt->cl_autobind = 1;
                clnt->cl_autobind = 1;
 
 
        rpc_init_rtt(&clnt->cl_rtt, xprt->timeout.to_initval);
        rpc_init_rtt(&clnt->cl_rtt, xprt->timeout.to_initval);
 
 
        if (!rpcauth_create(flavor, clnt))
        if (!rpcauth_create(flavor, clnt))
                goto out_no_auth;
                goto out_no_auth;
 
 
        /* save the nodename */
        /* save the nodename */
        clnt->cl_nodelen = strlen(system_utsname.nodename);
        clnt->cl_nodelen = strlen(system_utsname.nodename);
        if (clnt->cl_nodelen > UNX_MAXNODENAME)
        if (clnt->cl_nodelen > UNX_MAXNODENAME)
                clnt->cl_nodelen = UNX_MAXNODENAME;
                clnt->cl_nodelen = UNX_MAXNODENAME;
        memcpy(clnt->cl_nodename, system_utsname.nodename, clnt->cl_nodelen);
        memcpy(clnt->cl_nodename, system_utsname.nodename, clnt->cl_nodelen);
out:
out:
        return clnt;
        return clnt;
 
 
out_no_clnt:
out_no_clnt:
        printk(KERN_INFO "RPC: out of memory in rpc_create_client\n");
        printk(KERN_INFO "RPC: out of memory in rpc_create_client\n");
        goto out;
        goto out;
out_no_auth:
out_no_auth:
        printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %d)\n",
        printk(KERN_INFO "RPC: Couldn't create auth handle (flavor %d)\n",
                flavor);
                flavor);
        rpc_free(clnt);
        rpc_free(clnt);
        clnt = NULL;
        clnt = NULL;
        goto out;
        goto out;
}
}
 
 
/*
/*
 * Properly shut down an RPC client, terminating all outstanding
 * Properly shut down an RPC client, terminating all outstanding
 * requests. Note that we must be certain that cl_oneshot and
 * requests. Note that we must be certain that cl_oneshot and
 * cl_dead are cleared, or else the client would be destroyed
 * cl_dead are cleared, or else the client would be destroyed
 * when the last task releases it.
 * when the last task releases it.
 */
 */
int
int
rpc_shutdown_client(struct rpc_clnt *clnt)
rpc_shutdown_client(struct rpc_clnt *clnt)
{
{
        dprintk("RPC: shutting down %s client for %s\n",
        dprintk("RPC: shutting down %s client for %s\n",
                clnt->cl_protname, clnt->cl_server);
                clnt->cl_protname, clnt->cl_server);
        while (atomic_read(&clnt->cl_users)) {
        while (atomic_read(&clnt->cl_users)) {
#ifdef RPC_DEBUG
#ifdef RPC_DEBUG
                dprintk("RPC: rpc_shutdown_client: client %s, tasks=%d\n",
                dprintk("RPC: rpc_shutdown_client: client %s, tasks=%d\n",
                        clnt->cl_protname, atomic_read(&clnt->cl_users));
                        clnt->cl_protname, atomic_read(&clnt->cl_users));
#endif
#endif
                /* Don't let rpc_release_client destroy us */
                /* Don't let rpc_release_client destroy us */
                clnt->cl_oneshot = 0;
                clnt->cl_oneshot = 0;
                clnt->cl_dead = 0;
                clnt->cl_dead = 0;
                rpc_killall_tasks(clnt);
                rpc_killall_tasks(clnt);
                sleep_on_timeout(&destroy_wait, 1*HZ);
                sleep_on_timeout(&destroy_wait, 1*HZ);
        }
        }
        return rpc_destroy_client(clnt);
        return rpc_destroy_client(clnt);
}
}
 
 
/*
/*
 * Delete an RPC client
 * Delete an RPC client
 */
 */
int
int
rpc_destroy_client(struct rpc_clnt *clnt)
rpc_destroy_client(struct rpc_clnt *clnt)
{
{
        dprintk("RPC: destroying %s client for %s\n",
        dprintk("RPC: destroying %s client for %s\n",
                        clnt->cl_protname, clnt->cl_server);
                        clnt->cl_protname, clnt->cl_server);
 
 
        if (clnt->cl_auth) {
        if (clnt->cl_auth) {
                rpcauth_destroy(clnt->cl_auth);
                rpcauth_destroy(clnt->cl_auth);
                clnt->cl_auth = NULL;
                clnt->cl_auth = NULL;
        }
        }
        if (clnt->cl_xprt) {
        if (clnt->cl_xprt) {
                xprt_destroy(clnt->cl_xprt);
                xprt_destroy(clnt->cl_xprt);
                clnt->cl_xprt = NULL;
                clnt->cl_xprt = NULL;
        }
        }
        rpc_free(clnt);
        rpc_free(clnt);
        return 0;
        return 0;
}
}
 
 
/*
/*
 * Release an RPC client
 * Release an RPC client
 */
 */
void
void
rpc_release_client(struct rpc_clnt *clnt)
rpc_release_client(struct rpc_clnt *clnt)
{
{
        dprintk("RPC:      rpc_release_client(%p, %d)\n",
        dprintk("RPC:      rpc_release_client(%p, %d)\n",
                                clnt, atomic_read(&clnt->cl_users));
                                clnt, atomic_read(&clnt->cl_users));
 
 
        if (!atomic_dec_and_test(&clnt->cl_users))
        if (!atomic_dec_and_test(&clnt->cl_users))
                return;
                return;
        wake_up(&destroy_wait);
        wake_up(&destroy_wait);
        if (clnt->cl_oneshot || clnt->cl_dead)
        if (clnt->cl_oneshot || clnt->cl_dead)
                rpc_destroy_client(clnt);
                rpc_destroy_client(clnt);
}
}
 
 
/*
/*
 * Default callback for async RPC calls
 * Default callback for async RPC calls
 */
 */
static void
static void
rpc_default_callback(struct rpc_task *task)
rpc_default_callback(struct rpc_task *task)
{
{
}
}
 
 
/*
/*
 *      Export the signal mask handling for aysnchronous code that
 *      Export the signal mask handling for aysnchronous code that
 *      sleeps on RPC calls
 *      sleeps on RPC calls
 */
 */
 
 
void rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset)
void rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset)
{
{
        unsigned long   sigallow = sigmask(SIGKILL);
        unsigned long   sigallow = sigmask(SIGKILL);
        unsigned long   irqflags;
        unsigned long   irqflags;
 
 
        /* Turn off various signals */
        /* Turn off various signals */
        if (clnt->cl_intr) {
        if (clnt->cl_intr) {
                struct k_sigaction *action = current->sig->action;
                struct k_sigaction *action = current->sig->action;
                if (action[SIGINT-1].sa.sa_handler == SIG_DFL)
                if (action[SIGINT-1].sa.sa_handler == SIG_DFL)
                        sigallow |= sigmask(SIGINT);
                        sigallow |= sigmask(SIGINT);
                if (action[SIGQUIT-1].sa.sa_handler == SIG_DFL)
                if (action[SIGQUIT-1].sa.sa_handler == SIG_DFL)
                        sigallow |= sigmask(SIGQUIT);
                        sigallow |= sigmask(SIGQUIT);
        }
        }
        spin_lock_irqsave(&current->sigmask_lock, irqflags);
        spin_lock_irqsave(&current->sigmask_lock, irqflags);
        *oldset = current->blocked;
        *oldset = current->blocked;
        siginitsetinv(&current->blocked, sigallow & ~oldset->sig[0]);
        siginitsetinv(&current->blocked, sigallow & ~oldset->sig[0]);
        recalc_sigpending(current);
        recalc_sigpending(current);
        spin_unlock_irqrestore(&current->sigmask_lock, irqflags);
        spin_unlock_irqrestore(&current->sigmask_lock, irqflags);
}
}
 
 
void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset)
void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset)
{
{
        unsigned long   irqflags;
        unsigned long   irqflags;
 
 
        spin_lock_irqsave(&current->sigmask_lock, irqflags);
        spin_lock_irqsave(&current->sigmask_lock, irqflags);
        current->blocked = *oldset;
        current->blocked = *oldset;
        recalc_sigpending(current);
        recalc_sigpending(current);
        spin_unlock_irqrestore(&current->sigmask_lock, irqflags);
        spin_unlock_irqrestore(&current->sigmask_lock, irqflags);
}
}
 
 
/*
/*
 * New rpc_call implementation
 * New rpc_call implementation
 */
 */
int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
{
{
        struct rpc_task my_task, *task = &my_task;
        struct rpc_task my_task, *task = &my_task;
        sigset_t        oldset;
        sigset_t        oldset;
        int             status;
        int             status;
 
 
        /* If this client is slain all further I/O fails */
        /* If this client is slain all further I/O fails */
        if (clnt->cl_dead)
        if (clnt->cl_dead)
                return -EIO;
                return -EIO;
 
 
        if (flags & RPC_TASK_ASYNC) {
        if (flags & RPC_TASK_ASYNC) {
                printk("rpc_call_sync: Illegal flag combination for synchronous task\n");
                printk("rpc_call_sync: Illegal flag combination for synchronous task\n");
                flags &= ~RPC_TASK_ASYNC;
                flags &= ~RPC_TASK_ASYNC;
        }
        }
 
 
        rpc_clnt_sigmask(clnt, &oldset);
        rpc_clnt_sigmask(clnt, &oldset);
 
 
        /* Create/initialize a new RPC task */
        /* Create/initialize a new RPC task */
        rpc_init_task(task, clnt, NULL, flags);
        rpc_init_task(task, clnt, NULL, flags);
        rpc_call_setup(task, msg, 0);
        rpc_call_setup(task, msg, 0);
 
 
        /* Set up the call info struct and execute the task */
        /* Set up the call info struct and execute the task */
        if (task->tk_status == 0)
        if (task->tk_status == 0)
                status = rpc_execute(task);
                status = rpc_execute(task);
        else {
        else {
                status = task->tk_status;
                status = task->tk_status;
                rpc_release_task(task);
                rpc_release_task(task);
        }
        }
 
 
        rpc_clnt_sigunmask(clnt, &oldset);
        rpc_clnt_sigunmask(clnt, &oldset);
 
 
        return status;
        return status;
}
}
 
 
/*
/*
 * New rpc_call implementation
 * New rpc_call implementation
 */
 */
int
int
rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
               rpc_action callback, void *data)
               rpc_action callback, void *data)
{
{
        struct rpc_task *task;
        struct rpc_task *task;
        sigset_t        oldset;
        sigset_t        oldset;
        int             status;
        int             status;
 
 
        /* If this client is slain all further I/O fails */
        /* If this client is slain all further I/O fails */
        if (clnt->cl_dead)
        if (clnt->cl_dead)
                return -EIO;
                return -EIO;
 
 
        flags |= RPC_TASK_ASYNC;
        flags |= RPC_TASK_ASYNC;
 
 
        rpc_clnt_sigmask(clnt, &oldset);
        rpc_clnt_sigmask(clnt, &oldset);
 
 
        /* Create/initialize a new RPC task */
        /* Create/initialize a new RPC task */
        if (!callback)
        if (!callback)
                callback = rpc_default_callback;
                callback = rpc_default_callback;
        status = -ENOMEM;
        status = -ENOMEM;
        if (!(task = rpc_new_task(clnt, callback, flags)))
        if (!(task = rpc_new_task(clnt, callback, flags)))
                goto out;
                goto out;
        task->tk_calldata = data;
        task->tk_calldata = data;
 
 
        rpc_call_setup(task, msg, 0);
        rpc_call_setup(task, msg, 0);
 
 
        /* Set up the call info struct and execute the task */
        /* Set up the call info struct and execute the task */
        if (task->tk_status == 0)
        if (task->tk_status == 0)
                status = rpc_execute(task);
                status = rpc_execute(task);
        else {
        else {
                status = task->tk_status;
                status = task->tk_status;
                rpc_release_task(task);
                rpc_release_task(task);
        }
        }
 
 
out:
out:
        rpc_clnt_sigunmask(clnt, &oldset);
        rpc_clnt_sigunmask(clnt, &oldset);
 
 
        return status;
        return status;
}
}
 
 
 
 
void
void
rpc_call_setup(struct rpc_task *task, struct rpc_message *msg, int flags)
rpc_call_setup(struct rpc_task *task, struct rpc_message *msg, int flags)
{
{
        task->tk_msg   = *msg;
        task->tk_msg   = *msg;
        task->tk_flags |= flags;
        task->tk_flags |= flags;
        /* Bind the user cred */
        /* Bind the user cred */
        if (task->tk_msg.rpc_cred != NULL) {
        if (task->tk_msg.rpc_cred != NULL) {
                rpcauth_holdcred(task);
                rpcauth_holdcred(task);
        } else
        } else
                rpcauth_bindcred(task);
                rpcauth_bindcred(task);
 
 
        if (task->tk_status == 0)
        if (task->tk_status == 0)
                task->tk_action = call_start;
                task->tk_action = call_start;
        else
        else
                task->tk_action = NULL;
                task->tk_action = NULL;
}
}
 
 
void
void
rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize)
rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize)
{
{
        struct rpc_xprt *xprt = clnt->cl_xprt;
        struct rpc_xprt *xprt = clnt->cl_xprt;
 
 
        xprt->sndsize = 0;
        xprt->sndsize = 0;
        if (sndsize)
        if (sndsize)
                xprt->sndsize = sndsize + RPC_SLACK_SPACE;
                xprt->sndsize = sndsize + RPC_SLACK_SPACE;
        xprt->rcvsize = 0;
        xprt->rcvsize = 0;
        if (rcvsize)
        if (rcvsize)
                xprt->rcvsize = rcvsize + RPC_SLACK_SPACE;
                xprt->rcvsize = rcvsize + RPC_SLACK_SPACE;
        xprt_sock_setbufsize(xprt);
        xprt_sock_setbufsize(xprt);
}
}
 
 
/*
/*
 * Restart an (async) RPC call. Usually called from within the
 * Restart an (async) RPC call. Usually called from within the
 * exit handler.
 * exit handler.
 */
 */
void
void
rpc_restart_call(struct rpc_task *task)
rpc_restart_call(struct rpc_task *task)
{
{
        if (RPC_ASSASSINATED(task))
        if (RPC_ASSASSINATED(task))
                return;
                return;
 
 
        task->tk_action = call_start;
        task->tk_action = call_start;
}
}
 
 
/*
/*
 * 0.  Initial state
 * 0.  Initial state
 *
 *
 *     Other FSM states can be visited zero or more times, but
 *     Other FSM states can be visited zero or more times, but
 *     this state is visited exactly once for each RPC.
 *     this state is visited exactly once for each RPC.
 */
 */
static void
static void
call_start(struct rpc_task *task)
call_start(struct rpc_task *task)
{
{
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_clnt *clnt = task->tk_client;
 
 
        if (task->tk_msg.rpc_proc > clnt->cl_maxproc) {
        if (task->tk_msg.rpc_proc > clnt->cl_maxproc) {
                printk(KERN_ERR "%s (vers %d): bad procedure number %d\n",
                printk(KERN_ERR "%s (vers %d): bad procedure number %d\n",
                                clnt->cl_protname, clnt->cl_vers,
                                clnt->cl_protname, clnt->cl_vers,
                                task->tk_msg.rpc_proc);
                                task->tk_msg.rpc_proc);
                rpc_exit(task, -EIO);
                rpc_exit(task, -EIO);
                return;
                return;
        }
        }
 
 
        dprintk("RPC: %4d call_start %s%d proc %d (%s)\n", task->tk_pid,
        dprintk("RPC: %4d call_start %s%d proc %d (%s)\n", task->tk_pid,
                clnt->cl_protname, clnt->cl_vers, task->tk_msg.rpc_proc,
                clnt->cl_protname, clnt->cl_vers, task->tk_msg.rpc_proc,
                (RPC_IS_ASYNC(task) ? "async" : "sync"));
                (RPC_IS_ASYNC(task) ? "async" : "sync"));
 
 
        /* Increment call count */
        /* Increment call count */
        rpcproc_count(clnt, task->tk_msg.rpc_proc)++;
        rpcproc_count(clnt, task->tk_msg.rpc_proc)++;
        clnt->cl_stats->rpccnt++;
        clnt->cl_stats->rpccnt++;
        task->tk_action = call_reserve;
        task->tk_action = call_reserve;
}
}
 
 
/*
/*
 * 1.   Reserve an RPC call slot
 * 1.   Reserve an RPC call slot
 */
 */
static void
static void
call_reserve(struct rpc_task *task)
call_reserve(struct rpc_task *task)
{
{
        dprintk("RPC: %4d call_reserve\n", task->tk_pid);
        dprintk("RPC: %4d call_reserve\n", task->tk_pid);
 
 
        if (!rpcauth_uptodatecred(task)) {
        if (!rpcauth_uptodatecred(task)) {
                task->tk_action = call_refresh;
                task->tk_action = call_refresh;
                return;
                return;
        }
        }
 
 
        task->tk_status  = 0;
        task->tk_status  = 0;
        task->tk_action  = call_reserveresult;
        task->tk_action  = call_reserveresult;
        xprt_reserve(task);
        xprt_reserve(task);
}
}
 
 
/*
/*
 * 1b.  Grok the result of xprt_reserve()
 * 1b.  Grok the result of xprt_reserve()
 */
 */
static void
static void
call_reserveresult(struct rpc_task *task)
call_reserveresult(struct rpc_task *task)
{
{
        int status = task->tk_status;
        int status = task->tk_status;
 
 
        dprintk("RPC: %4d call_reserveresult (status %d)\n",
        dprintk("RPC: %4d call_reserveresult (status %d)\n",
                                task->tk_pid, task->tk_status);
                                task->tk_pid, task->tk_status);
 
 
        /*
        /*
         * After a call to xprt_reserve(), we must have either
         * After a call to xprt_reserve(), we must have either
         * a request slot or else an error status.
         * a request slot or else an error status.
         */
         */
        task->tk_status = 0;
        task->tk_status = 0;
        if (status >= 0) {
        if (status >= 0) {
                if (task->tk_rqstp) {
                if (task->tk_rqstp) {
                        task->tk_action = call_allocate;
                        task->tk_action = call_allocate;
                        return;
                        return;
                }
                }
 
 
                printk(KERN_ERR "%s: status=%d, but no request slot, exiting\n",
                printk(KERN_ERR "%s: status=%d, but no request slot, exiting\n",
                                __FUNCTION__, status);
                                __FUNCTION__, status);
                rpc_exit(task, -EIO);
                rpc_exit(task, -EIO);
                return;
                return;
        }
        }
 
 
        /*
        /*
         * Even though there was an error, we may have acquired
         * Even though there was an error, we may have acquired
         * a request slot somehow.  Make sure not to leak it.
         * a request slot somehow.  Make sure not to leak it.
         */
         */
        if (task->tk_rqstp) {
        if (task->tk_rqstp) {
                printk(KERN_ERR "%s: status=%d, request allocated anyway\n",
                printk(KERN_ERR "%s: status=%d, request allocated anyway\n",
                                __FUNCTION__, status);
                                __FUNCTION__, status);
                xprt_release(task);
                xprt_release(task);
        }
        }
 
 
        switch (status) {
        switch (status) {
        case -EAGAIN:   /* woken up; retry */
        case -EAGAIN:   /* woken up; retry */
                task->tk_action = call_reserve;
                task->tk_action = call_reserve;
                return;
                return;
        case -EIO:      /* probably a shutdown */
        case -EIO:      /* probably a shutdown */
                break;
                break;
        default:
        default:
                printk(KERN_ERR "%s: unrecognized error %d, exiting\n",
                printk(KERN_ERR "%s: unrecognized error %d, exiting\n",
                                __FUNCTION__, status);
                                __FUNCTION__, status);
                break;
                break;
        }
        }
        rpc_exit(task, status);
        rpc_exit(task, status);
}
}
 
 
/*
/*
 * 2.   Allocate the buffer. For details, see sched.c:rpc_malloc.
 * 2.   Allocate the buffer. For details, see sched.c:rpc_malloc.
 *      (Note: buffer memory is freed in rpc_task_release).
 *      (Note: buffer memory is freed in rpc_task_release).
 */
 */
static void
static void
call_allocate(struct rpc_task *task)
call_allocate(struct rpc_task *task)
{
{
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_clnt *clnt = task->tk_client;
        unsigned int    bufsiz;
        unsigned int    bufsiz;
 
 
        dprintk("RPC: %4d call_allocate (status %d)\n",
        dprintk("RPC: %4d call_allocate (status %d)\n",
                                task->tk_pid, task->tk_status);
                                task->tk_pid, task->tk_status);
        task->tk_action = call_encode;
        task->tk_action = call_encode;
        if (task->tk_buffer)
        if (task->tk_buffer)
                return;
                return;
 
 
        /* FIXME: compute buffer requirements more exactly using
        /* FIXME: compute buffer requirements more exactly using
         * auth->au_wslack */
         * auth->au_wslack */
        bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc) + RPC_SLACK_SPACE;
        bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc) + RPC_SLACK_SPACE;
 
 
        if ((task->tk_buffer = rpc_malloc(task, bufsiz << 1)) != NULL)
        if ((task->tk_buffer = rpc_malloc(task, bufsiz << 1)) != NULL)
                return;
                return;
        printk(KERN_INFO "RPC: buffer allocation failed for task %p\n", task);
        printk(KERN_INFO "RPC: buffer allocation failed for task %p\n", task);
 
 
        if (RPC_IS_ASYNC(task) || !(task->tk_client->cl_intr && signalled())) {
        if (RPC_IS_ASYNC(task) || !(task->tk_client->cl_intr && signalled())) {
                xprt_release(task);
                xprt_release(task);
                task->tk_action = call_reserve;
                task->tk_action = call_reserve;
                rpc_delay(task, HZ>>4);
                rpc_delay(task, HZ>>4);
                return;
                return;
        }
        }
 
 
        rpc_exit(task, -ERESTARTSYS);
        rpc_exit(task, -ERESTARTSYS);
}
}
 
 
/*
/*
 * 3.   Encode arguments of an RPC call
 * 3.   Encode arguments of an RPC call
 */
 */
static void
static void
call_encode(struct rpc_task *task)
call_encode(struct rpc_task *task)
{
{
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_rqst *req = task->tk_rqstp;
        struct rpc_rqst *req = task->tk_rqstp;
        struct xdr_buf *sndbuf = &req->rq_snd_buf;
        struct xdr_buf *sndbuf = &req->rq_snd_buf;
        struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
        struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
        unsigned int    bufsiz;
        unsigned int    bufsiz;
        kxdrproc_t      encode;
        kxdrproc_t      encode;
        int             status;
        int             status;
        u32             *p;
        u32             *p;
 
 
        dprintk("RPC: %4d call_encode (status %d)\n",
        dprintk("RPC: %4d call_encode (status %d)\n",
                                task->tk_pid, task->tk_status);
                                task->tk_pid, task->tk_status);
 
 
        task->tk_action = call_bind;
        task->tk_action = call_bind;
 
 
        /* Default buffer setup */
        /* Default buffer setup */
        bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc)+RPC_SLACK_SPACE;
        bufsiz = rpcproc_bufsiz(clnt, task->tk_msg.rpc_proc)+RPC_SLACK_SPACE;
        sndbuf->head[0].iov_base = (void *)task->tk_buffer;
        sndbuf->head[0].iov_base = (void *)task->tk_buffer;
        sndbuf->head[0].iov_len  = bufsiz;
        sndbuf->head[0].iov_len  = bufsiz;
        sndbuf->tail[0].iov_len  = 0;
        sndbuf->tail[0].iov_len  = 0;
        sndbuf->page_len         = 0;
        sndbuf->page_len         = 0;
        sndbuf->len              = 0;
        sndbuf->len              = 0;
        rcvbuf->head[0].iov_base = (void *)((char *)task->tk_buffer + bufsiz);
        rcvbuf->head[0].iov_base = (void *)((char *)task->tk_buffer + bufsiz);
        rcvbuf->head[0].iov_len  = bufsiz;
        rcvbuf->head[0].iov_len  = bufsiz;
        rcvbuf->tail[0].iov_len  = 0;
        rcvbuf->tail[0].iov_len  = 0;
        rcvbuf->page_len         = 0;
        rcvbuf->page_len         = 0;
        rcvbuf->len              = bufsiz;
        rcvbuf->len              = bufsiz;
 
 
        /* Zero buffer so we have automatic zero-padding of opaque & string */
        /* Zero buffer so we have automatic zero-padding of opaque & string */
        memset(task->tk_buffer, 0, bufsiz);
        memset(task->tk_buffer, 0, bufsiz);
 
 
        /* Encode header and provided arguments */
        /* Encode header and provided arguments */
        encode = rpcproc_encode(clnt, task->tk_msg.rpc_proc);
        encode = rpcproc_encode(clnt, task->tk_msg.rpc_proc);
        if (!(p = call_header(task))) {
        if (!(p = call_header(task))) {
                printk(KERN_INFO "RPC: call_header failed, exit EIO\n");
                printk(KERN_INFO "RPC: call_header failed, exit EIO\n");
                rpc_exit(task, -EIO);
                rpc_exit(task, -EIO);
        } else
        } else
        if (encode && (status = encode(req, p, task->tk_msg.rpc_argp)) < 0) {
        if (encode && (status = encode(req, p, task->tk_msg.rpc_argp)) < 0) {
                printk(KERN_WARNING "%s: can't encode arguments: %d\n",
                printk(KERN_WARNING "%s: can't encode arguments: %d\n",
                                clnt->cl_protname, -status);
                                clnt->cl_protname, -status);
                rpc_exit(task, status);
                rpc_exit(task, status);
        }
        }
}
}
 
 
/*
/*
 * 4.   Get the server port number if not yet set
 * 4.   Get the server port number if not yet set
 */
 */
static void
static void
call_bind(struct rpc_task *task)
call_bind(struct rpc_task *task)
{
{
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_xprt *xprt = clnt->cl_xprt;
        struct rpc_xprt *xprt = clnt->cl_xprt;
 
 
        dprintk("RPC: %4d call_bind xprt %p %s connected\n", task->tk_pid,
        dprintk("RPC: %4d call_bind xprt %p %s connected\n", task->tk_pid,
                        xprt, (xprt_connected(xprt) ? "is" : "is not"));
                        xprt, (xprt_connected(xprt) ? "is" : "is not"));
 
 
        task->tk_action = (xprt_connected(xprt)) ? call_transmit : call_connect;
        task->tk_action = (xprt_connected(xprt)) ? call_transmit : call_connect;
 
 
        if (!clnt->cl_port) {
        if (!clnt->cl_port) {
                task->tk_action = call_connect;
                task->tk_action = call_connect;
                task->tk_timeout = clnt->cl_timeout.to_maxval;
                task->tk_timeout = clnt->cl_timeout.to_maxval;
                rpc_getport(task, clnt);
                rpc_getport(task, clnt);
        }
        }
}
}
 
 
/*
/*
 * 4a.  Establish socket
 * 4a.  Establish socket
 *      Connect to the RPC server (TCP case)
 *      Connect to the RPC server (TCP case)
 */
 */
static void
static void
call_connect(struct rpc_task *task)
call_connect(struct rpc_task *task)
{
{
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_clnt *clnt = task->tk_client;
 
 
        dprintk("RPC: %4d call_connect status %d\n",
        dprintk("RPC: %4d call_connect status %d\n",
                                task->tk_pid, task->tk_status);
                                task->tk_pid, task->tk_status);
 
 
        if (xprt_connected(clnt->cl_xprt)) {
        if (xprt_connected(clnt->cl_xprt)) {
                task->tk_action = call_transmit;
                task->tk_action = call_transmit;
                return;
                return;
        }
        }
        task->tk_action = call_connect_status;
        task->tk_action = call_connect_status;
        if (task->tk_status < 0)
        if (task->tk_status < 0)
                return;
                return;
        xprt_connect(task);
        xprt_connect(task);
}
}
 
 
/*
/*
 * 4b.  Sort out reconnection result
 * 4b.  Sort out reconnection result
 */
 */
static void call_connect_status(struct rpc_task *task)
static void call_connect_status(struct rpc_task *task)
{
{
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_clnt *clnt = task->tk_client;
        int status = task->tk_status;
        int status = task->tk_status;
 
 
        task->tk_status = 0;
        task->tk_status = 0;
        if (status >= 0) {
        if (status >= 0) {
                clnt->cl_stats->netreconn++;
                clnt->cl_stats->netreconn++;
                task->tk_action = call_transmit;
                task->tk_action = call_transmit;
                return;
                return;
        }
        }
 
 
        /* Something failed: we may have to rebind */
        /* Something failed: we may have to rebind */
        if (clnt->cl_autobind)
        if (clnt->cl_autobind)
                clnt->cl_port = 0;
                clnt->cl_port = 0;
        switch (status) {
        switch (status) {
        case -ECONNREFUSED:
        case -ECONNREFUSED:
        case -ECONNRESET:
        case -ECONNRESET:
        case -ENOTCONN:
        case -ENOTCONN:
        case -ETIMEDOUT:
        case -ETIMEDOUT:
        case -EAGAIN:
        case -EAGAIN:
                task->tk_action = (clnt->cl_port == 0) ? call_bind : call_connect;
                task->tk_action = (clnt->cl_port == 0) ? call_bind : call_connect;
                break;
                break;
        default:
        default:
                rpc_exit(task, status);
                rpc_exit(task, status);
        }
        }
}
}
 
 
/*
/*
 * 5.   Transmit the RPC request, and wait for reply
 * 5.   Transmit the RPC request, and wait for reply
 */
 */
static void
static void
call_transmit(struct rpc_task *task)
call_transmit(struct rpc_task *task)
{
{
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_clnt *clnt = task->tk_client;
 
 
        dprintk("RPC: %4d call_transmit (status %d)\n",
        dprintk("RPC: %4d call_transmit (status %d)\n",
                                task->tk_pid, task->tk_status);
                                task->tk_pid, task->tk_status);
 
 
        task->tk_action = call_status;
        task->tk_action = call_status;
        if (task->tk_status < 0)
        if (task->tk_status < 0)
                return;
                return;
        xprt_transmit(task);
        xprt_transmit(task);
        if (!rpcproc_decode(clnt, task->tk_msg.rpc_proc) && task->tk_status >= 0) {
        if (!rpcproc_decode(clnt, task->tk_msg.rpc_proc) && task->tk_status >= 0) {
                task->tk_action = NULL;
                task->tk_action = NULL;
                rpc_wake_up_task(task);
                rpc_wake_up_task(task);
        }
        }
}
}
 
 
/*
/*
 * 6.   Sort out the RPC call status
 * 6.   Sort out the RPC call status
 */
 */
static void
static void
call_status(struct rpc_task *task)
call_status(struct rpc_task *task)
{
{
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_xprt *xprt = clnt->cl_xprt;
        struct rpc_xprt *xprt = clnt->cl_xprt;
        struct rpc_rqst *req = task->tk_rqstp;
        struct rpc_rqst *req = task->tk_rqstp;
        int             status;
        int             status;
 
 
        smp_rmb();
        smp_rmb();
        if (req->rq_received > 0 && !req->rq_bytes_sent)
        if (req->rq_received > 0 && !req->rq_bytes_sent)
                task->tk_status = req->rq_received;
                task->tk_status = req->rq_received;
 
 
        dprintk("RPC: %4d call_status (status %d)\n",
        dprintk("RPC: %4d call_status (status %d)\n",
                                task->tk_pid, task->tk_status);
                                task->tk_pid, task->tk_status);
 
 
        status = task->tk_status;
        status = task->tk_status;
        if (status >= 0) {
        if (status >= 0) {
                task->tk_action = call_decode;
                task->tk_action = call_decode;
                return;
                return;
        }
        }
 
 
        task->tk_status = 0;
        task->tk_status = 0;
        switch(status) {
        switch(status) {
        case -ETIMEDOUT:
        case -ETIMEDOUT:
                task->tk_action = call_timeout;
                task->tk_action = call_timeout;
                break;
                break;
        case -ECONNREFUSED:
        case -ECONNREFUSED:
        case -ENOTCONN:
        case -ENOTCONN:
                req->rq_bytes_sent = 0;
                req->rq_bytes_sent = 0;
                if (clnt->cl_autobind || !clnt->cl_port) {
                if (clnt->cl_autobind || !clnt->cl_port) {
                        clnt->cl_port = 0;
                        clnt->cl_port = 0;
                        task->tk_action = call_bind;
                        task->tk_action = call_bind;
                        break;
                        break;
                }
                }
                task->tk_action = call_connect;
                task->tk_action = call_connect;
                break;
                break;
                /*
                /*
                 * Sleep and dream of an open connection
                 * Sleep and dream of an open connection
                 */
                 */
                task->tk_timeout = 5 * HZ;
                task->tk_timeout = 5 * HZ;
                rpc_sleep_on(&xprt->sending, task, NULL, NULL);
                rpc_sleep_on(&xprt->sending, task, NULL, NULL);
        case -ENOMEM:
        case -ENOMEM:
        case -EAGAIN:
        case -EAGAIN:
                task->tk_action = call_transmit;
                task->tk_action = call_transmit;
                break;
                break;
        default:
        default:
                if (clnt->cl_chatty)
                if (clnt->cl_chatty)
                        printk("%s: RPC call returned error %d\n",
                        printk("%s: RPC call returned error %d\n",
                               clnt->cl_protname, -status);
                               clnt->cl_protname, -status);
                rpc_exit(task, status);
                rpc_exit(task, status);
        }
        }
}
}
 
 
/*
/*
 * 6a.  Handle RPC timeout
 * 6a.  Handle RPC timeout
 *      We do not release the request slot, so we keep using the
 *      We do not release the request slot, so we keep using the
 *      same XID for all retransmits.
 *      same XID for all retransmits.
 */
 */
static void
static void
call_timeout(struct rpc_task *task)
call_timeout(struct rpc_task *task)
{
{
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_timeout *to = &task->tk_rqstp->rq_timeout;
        struct rpc_timeout *to = &task->tk_rqstp->rq_timeout;
 
 
        if (xprt_adjust_timeout(to)) {
        if (xprt_adjust_timeout(to)) {
                dprintk("RPC: %4d call_timeout (minor)\n", task->tk_pid);
                dprintk("RPC: %4d call_timeout (minor)\n", task->tk_pid);
                goto retry;
                goto retry;
        }
        }
        to->to_retries = clnt->cl_timeout.to_retries;
        to->to_retries = clnt->cl_timeout.to_retries;
 
 
        dprintk("RPC: %4d call_timeout (major)\n", task->tk_pid);
        dprintk("RPC: %4d call_timeout (major)\n", task->tk_pid);
        if (clnt->cl_softrtry) {
        if (clnt->cl_softrtry) {
                if (clnt->cl_chatty)
                if (clnt->cl_chatty)
                        printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
                        printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
                                clnt->cl_protname, clnt->cl_server);
                                clnt->cl_protname, clnt->cl_server);
                rpc_exit(task, -EIO);
                rpc_exit(task, -EIO);
                return;
                return;
        }
        }
 
 
        if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN)) {
        if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN)) {
                task->tk_flags |= RPC_CALL_MAJORSEEN;
                task->tk_flags |= RPC_CALL_MAJORSEEN;
                printk(KERN_NOTICE "%s: server %s not responding, still trying\n",
                printk(KERN_NOTICE "%s: server %s not responding, still trying\n",
                        clnt->cl_protname, clnt->cl_server);
                        clnt->cl_protname, clnt->cl_server);
        }
        }
        if (clnt->cl_autobind)
        if (clnt->cl_autobind)
                clnt->cl_port = 0;
                clnt->cl_port = 0;
 
 
retry:
retry:
        clnt->cl_stats->rpcretrans++;
        clnt->cl_stats->rpcretrans++;
        task->tk_action = call_bind;
        task->tk_action = call_bind;
        task->tk_status = 0;
        task->tk_status = 0;
}
}
 
 
/*
/*
 * 7.   Decode the RPC reply
 * 7.   Decode the RPC reply
 */
 */
static void
static void
call_decode(struct rpc_task *task)
call_decode(struct rpc_task *task)
{
{
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_rqst *req = task->tk_rqstp;
        struct rpc_rqst *req = task->tk_rqstp;
        kxdrproc_t      decode = rpcproc_decode(clnt, task->tk_msg.rpc_proc);
        kxdrproc_t      decode = rpcproc_decode(clnt, task->tk_msg.rpc_proc);
        u32             *p;
        u32             *p;
 
 
        dprintk("RPC: %4d call_decode (status %d)\n",
        dprintk("RPC: %4d call_decode (status %d)\n",
                                task->tk_pid, task->tk_status);
                                task->tk_pid, task->tk_status);
 
 
        if (clnt->cl_chatty && (task->tk_flags & RPC_CALL_MAJORSEEN)) {
        if (clnt->cl_chatty && (task->tk_flags & RPC_CALL_MAJORSEEN)) {
                printk(KERN_NOTICE "%s: server %s OK\n",
                printk(KERN_NOTICE "%s: server %s OK\n",
                        clnt->cl_protname, clnt->cl_server);
                        clnt->cl_protname, clnt->cl_server);
                task->tk_flags &= ~RPC_CALL_MAJORSEEN;
                task->tk_flags &= ~RPC_CALL_MAJORSEEN;
        }
        }
 
 
        if (task->tk_status < 12) {
        if (task->tk_status < 12) {
                if (!clnt->cl_softrtry) {
                if (!clnt->cl_softrtry) {
                        task->tk_action = call_transmit;
                        task->tk_action = call_transmit;
                        clnt->cl_stats->rpcretrans++;
                        clnt->cl_stats->rpcretrans++;
                        goto out_retry;
                        goto out_retry;
                }
                }
                printk(KERN_WARNING "%s: too small RPC reply size (%d bytes)\n",
                printk(KERN_WARNING "%s: too small RPC reply size (%d bytes)\n",
                        clnt->cl_protname, task->tk_status);
                        clnt->cl_protname, task->tk_status);
                rpc_exit(task, -EIO);
                rpc_exit(task, -EIO);
                return;
                return;
        }
        }
 
 
        /* Check that the softirq receive buffer is valid */
        /* Check that the softirq receive buffer is valid */
        if (unlikely(memcmp(&req->rq_rcv_buf, &req->rq_private_buf,
        if (unlikely(memcmp(&req->rq_rcv_buf, &req->rq_private_buf,
                                sizeof(req->rq_rcv_buf)) != 0))
                                sizeof(req->rq_rcv_buf)) != 0))
                printk(KERN_WARNING "%s: receive buffer is inconsistent. Please contact maintainer.\n",
                printk(KERN_WARNING "%s: receive buffer is inconsistent. Please contact maintainer.\n",
                                __FUNCTION__);
                                __FUNCTION__);
 
 
        /* Verify the RPC header */
        /* Verify the RPC header */
        if (!(p = call_verify(task))) {
        if (!(p = call_verify(task))) {
                /*
                /*
                 * When call_verfiy sets tk_action to NULL (via task_exit)
                 * When call_verfiy sets tk_action to NULL (via task_exit)
                 * a non-retry-able error has occurred (like the server
                 * a non-retry-able error has occurred (like the server
                 * not supporting a particular procedure call).
                 * not supporting a particular procedure call).
                 */
                 */
                if (task->tk_action == NULL)
                if (task->tk_action == NULL)
                        return;
                        return;
                goto out_retry;
                goto out_retry;
        }
        }
        /*
        /*
         * The following is an NFS-specific hack to cater for setuid
         * The following is an NFS-specific hack to cater for setuid
         * processes whose uid is mapped to nobody on the server.
         * processes whose uid is mapped to nobody on the server.
         */
         */
        if (task->tk_client->cl_droppriv &&
        if (task->tk_client->cl_droppriv &&
            (ntohl(*p) == NFSERR_ACCES || ntohl(*p) == NFSERR_PERM)) {
            (ntohl(*p) == NFSERR_ACCES || ntohl(*p) == NFSERR_PERM)) {
                if (RPC_IS_SETUID(task) && task->tk_suid_retry) {
                if (RPC_IS_SETUID(task) && task->tk_suid_retry) {
                        dprintk("RPC: %4d retry squashed uid\n", task->tk_pid);
                        dprintk("RPC: %4d retry squashed uid\n", task->tk_pid);
                        task->tk_flags ^= RPC_CALL_REALUID;
                        task->tk_flags ^= RPC_CALL_REALUID;
                        task->tk_action = call_encode;
                        task->tk_action = call_encode;
                        task->tk_suid_retry--;
                        task->tk_suid_retry--;
                        goto out_retry;
                        goto out_retry;
                }
                }
        }
        }
 
 
        task->tk_action = NULL;
        task->tk_action = NULL;
 
 
        if (decode)
        if (decode)
                task->tk_status = decode(req, p, task->tk_msg.rpc_resp);
                task->tk_status = decode(req, p, task->tk_msg.rpc_resp);
        dprintk("RPC: %4d call_decode result %d\n", task->tk_pid,
        dprintk("RPC: %4d call_decode result %d\n", task->tk_pid,
                                        task->tk_status);
                                        task->tk_status);
        return;
        return;
out_retry:
out_retry:
        req->rq_received = 0;
        req->rq_received = 0;
        task->tk_status = 0;
        task->tk_status = 0;
}
}
 
 
/*
/*
 * 8.   Refresh the credentials if rejected by the server
 * 8.   Refresh the credentials if rejected by the server
 */
 */
static void
static void
call_refresh(struct rpc_task *task)
call_refresh(struct rpc_task *task)
{
{
        dprintk("RPC: %4d call_refresh\n", task->tk_pid);
        dprintk("RPC: %4d call_refresh\n", task->tk_pid);
 
 
        xprt_release(task);     /* Must do to obtain new XID */
        xprt_release(task);     /* Must do to obtain new XID */
        task->tk_action = call_refreshresult;
        task->tk_action = call_refreshresult;
        task->tk_status = 0;
        task->tk_status = 0;
        task->tk_client->cl_stats->rpcauthrefresh++;
        task->tk_client->cl_stats->rpcauthrefresh++;
        rpcauth_refreshcred(task);
        rpcauth_refreshcred(task);
}
}
 
 
/*
/*
 * 8a.  Process the results of a credential refresh
 * 8a.  Process the results of a credential refresh
 */
 */
static void
static void
call_refreshresult(struct rpc_task *task)
call_refreshresult(struct rpc_task *task)
{
{
        dprintk("RPC: %4d call_refreshresult (status %d)\n",
        dprintk("RPC: %4d call_refreshresult (status %d)\n",
                                task->tk_pid, task->tk_status);
                                task->tk_pid, task->tk_status);
 
 
        if (task->tk_status < 0)
        if (task->tk_status < 0)
                rpc_exit(task, -EACCES);
                rpc_exit(task, -EACCES);
        else
        else
                task->tk_action = call_reserve;
                task->tk_action = call_reserve;
}
}
 
 
/*
/*
 * Call header serialization
 * Call header serialization
 */
 */
static u32 *
static u32 *
call_header(struct rpc_task *task)
call_header(struct rpc_task *task)
{
{
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_xprt *xprt = clnt->cl_xprt;
        struct rpc_xprt *xprt = clnt->cl_xprt;
        struct rpc_rqst *req = task->tk_rqstp;
        struct rpc_rqst *req = task->tk_rqstp;
        u32             *p = req->rq_svec[0].iov_base;
        u32             *p = req->rq_svec[0].iov_base;
 
 
        /* FIXME: check buffer size? */
        /* FIXME: check buffer size? */
        if (xprt->stream)
        if (xprt->stream)
                *p++ = 0;                /* fill in later */
                *p++ = 0;                /* fill in later */
        *p++ = req->rq_xid;             /* XID */
        *p++ = req->rq_xid;             /* XID */
        *p++ = htonl(RPC_CALL);         /* CALL */
        *p++ = htonl(RPC_CALL);         /* CALL */
        *p++ = htonl(RPC_VERSION);      /* RPC version */
        *p++ = htonl(RPC_VERSION);      /* RPC version */
        *p++ = htonl(clnt->cl_prog);    /* program number */
        *p++ = htonl(clnt->cl_prog);    /* program number */
        *p++ = htonl(clnt->cl_vers);    /* program version */
        *p++ = htonl(clnt->cl_vers);    /* program version */
        *p++ = htonl(task->tk_msg.rpc_proc);    /* procedure */
        *p++ = htonl(task->tk_msg.rpc_proc);    /* procedure */
        return rpcauth_marshcred(task, p);
        return rpcauth_marshcred(task, p);
}
}
 
 
/*
/*
 * Reply header verification
 * Reply header verification
 */
 */
static u32 *
static u32 *
call_verify(struct rpc_task *task)
call_verify(struct rpc_task *task)
{
{
        u32     *p = task->tk_rqstp->rq_rvec[0].iov_base, n;
        u32     *p = task->tk_rqstp->rq_rvec[0].iov_base, n;
 
 
        p += 1; /* skip XID */
        p += 1; /* skip XID */
 
 
        if ((n = ntohl(*p++)) != RPC_REPLY) {
        if ((n = ntohl(*p++)) != RPC_REPLY) {
                printk(KERN_WARNING "call_verify: not an RPC reply: %x\n", n);
                printk(KERN_WARNING "call_verify: not an RPC reply: %x\n", n);
                goto garbage;
                goto garbage;
        }
        }
        if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
        if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
                int     error = -EACCES;
                int     error = -EACCES;
 
 
                if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) {
                if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) {
                        printk(KERN_WARNING "call_verify: RPC call rejected: %x\n", n);
                        printk(KERN_WARNING "call_verify: RPC call rejected: %x\n", n);
                } else
                } else
                switch ((n = ntohl(*p++))) {
                switch ((n = ntohl(*p++))) {
                case RPC_AUTH_REJECTEDCRED:
                case RPC_AUTH_REJECTEDCRED:
                case RPC_AUTH_REJECTEDVERF:
                case RPC_AUTH_REJECTEDVERF:
                        if (!task->tk_cred_retry)
                        if (!task->tk_cred_retry)
                                break;
                                break;
                        task->tk_cred_retry--;
                        task->tk_cred_retry--;
                        dprintk("RPC: %4d call_verify: retry stale creds\n",
                        dprintk("RPC: %4d call_verify: retry stale creds\n",
                                                        task->tk_pid);
                                                        task->tk_pid);
                        rpcauth_invalcred(task);
                        rpcauth_invalcred(task);
                        task->tk_action = call_refresh;
                        task->tk_action = call_refresh;
                        return NULL;
                        return NULL;
                case RPC_AUTH_BADCRED:
                case RPC_AUTH_BADCRED:
                case RPC_AUTH_BADVERF:
                case RPC_AUTH_BADVERF:
                        /* possibly garbled cred/verf? */
                        /* possibly garbled cred/verf? */
                        if (!task->tk_garb_retry)
                        if (!task->tk_garb_retry)
                                break;
                                break;
                        task->tk_garb_retry--;
                        task->tk_garb_retry--;
                        dprintk("RPC: %4d call_verify: retry garbled creds\n",
                        dprintk("RPC: %4d call_verify: retry garbled creds\n",
                                                        task->tk_pid);
                                                        task->tk_pid);
                        task->tk_action = call_encode;
                        task->tk_action = call_encode;
                        return NULL;
                        return NULL;
                case RPC_AUTH_TOOWEAK:
                case RPC_AUTH_TOOWEAK:
                        printk(KERN_NOTICE "call_verify: server requires stronger "
                        printk(KERN_NOTICE "call_verify: server requires stronger "
                               "authentication.\n");
                               "authentication.\n");
                        break;
                        break;
                default:
                default:
                        printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
                        printk(KERN_WARNING "call_verify: unknown auth error: %x\n", n);
                        error = -EIO;
                        error = -EIO;
                }
                }
                dprintk("RPC: %4d call_verify: call rejected %d\n",
                dprintk("RPC: %4d call_verify: call rejected %d\n",
                                                task->tk_pid, n);
                                                task->tk_pid, n);
                rpc_exit(task, error);
                rpc_exit(task, error);
                return NULL;
                return NULL;
        }
        }
        if (!(p = rpcauth_checkverf(task, p))) {
        if (!(p = rpcauth_checkverf(task, p))) {
                printk(KERN_WARNING "call_verify: auth check failed\n");
                printk(KERN_WARNING "call_verify: auth check failed\n");
                goto garbage;           /* bad verifier, retry */
                goto garbage;           /* bad verifier, retry */
        }
        }
        switch ((n = ntohl(*p++))) {
        switch ((n = ntohl(*p++))) {
        case RPC_SUCCESS:
        case RPC_SUCCESS:
                return p;
                return p;
        case RPC_PROG_UNAVAIL:
        case RPC_PROG_UNAVAIL:
                printk(KERN_WARNING "RPC: call_verify: program %u is unsupported by server %s\n",
                printk(KERN_WARNING "RPC: call_verify: program %u is unsupported by server %s\n",
                                (unsigned int)task->tk_client->cl_prog,
                                (unsigned int)task->tk_client->cl_prog,
                                task->tk_client->cl_server);
                                task->tk_client->cl_server);
                goto out_eio;
                goto out_eio;
        case RPC_PROG_MISMATCH:
        case RPC_PROG_MISMATCH:
                printk(KERN_WARNING "RPC: call_verify: program %u, version %u unsupported by server %s\n",
                printk(KERN_WARNING "RPC: call_verify: program %u, version %u unsupported by server %s\n",
                                (unsigned int)task->tk_client->cl_prog,
                                (unsigned int)task->tk_client->cl_prog,
                                (unsigned int)task->tk_client->cl_vers,
                                (unsigned int)task->tk_client->cl_vers,
                                task->tk_client->cl_server);
                                task->tk_client->cl_server);
                goto out_eio;
                goto out_eio;
        case RPC_PROC_UNAVAIL:
        case RPC_PROC_UNAVAIL:
                printk(KERN_WARNING "RPC: call_verify: proc %u unsupported by program %u, version %u on server %s\n",
                printk(KERN_WARNING "RPC: call_verify: proc %u unsupported by program %u, version %u on server %s\n",
                                (unsigned int)task->tk_msg.rpc_proc,
                                (unsigned int)task->tk_msg.rpc_proc,
                                (unsigned int)task->tk_client->cl_prog,
                                (unsigned int)task->tk_client->cl_prog,
                                (unsigned int)task->tk_client->cl_vers,
                                (unsigned int)task->tk_client->cl_vers,
                                task->tk_client->cl_server);
                                task->tk_client->cl_server);
                goto out_eio;
                goto out_eio;
        case RPC_GARBAGE_ARGS:
        case RPC_GARBAGE_ARGS:
                break;                  /* retry */
                break;                  /* retry */
        default:
        default:
                printk(KERN_WARNING "call_verify: server accept status: %x\n", n);
                printk(KERN_WARNING "call_verify: server accept status: %x\n", n);
                /* Also retry */
                /* Also retry */
        }
        }
 
 
garbage:
garbage:
        dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid);
        dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid);
        task->tk_client->cl_stats->rpcgarbage++;
        task->tk_client->cl_stats->rpcgarbage++;
        if (task->tk_garb_retry) {
        if (task->tk_garb_retry) {
                task->tk_garb_retry--;
                task->tk_garb_retry--;
                dprintk(KERN_WARNING "RPC: garbage, retrying %4d\n", task->tk_pid);
                dprintk(KERN_WARNING "RPC: garbage, retrying %4d\n", task->tk_pid);
                task->tk_action = call_encode;
                task->tk_action = call_encode;
                return NULL;
                return NULL;
        }
        }
        printk(KERN_WARNING "RPC: garbage, exit EIO\n");
        printk(KERN_WARNING "RPC: garbage, exit EIO\n");
out_eio:
out_eio:
        rpc_exit(task, -EIO);
        rpc_exit(task, -EIO);
        return NULL;
        return NULL;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.