]> err.no Git - linux-2.6/blobdiff - net/sunrpc/clnt.c
[PATCH] RPC: introduce client-side transport switch
[linux-2.6] / net / sunrpc / clnt.c
index 99515d7727a61fb7bc1788dd2e42fc0ad39bafda..ab50c3c9e6a877cb26f2f53ced7df51d2e0dd5cb 100644 (file)
@@ -53,6 +53,7 @@ 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_refresh(struct rpc_task *task);
@@ -97,12 +98,13 @@ rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
  * made to sleep too long.
  */
 struct rpc_clnt *
-rpc_create_client(struct rpc_xprt *xprt, char *servname,
+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;
        int len;
 
@@ -157,10 +159,11 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname,
        if (err < 0)
                goto out_no_path;
 
-       err = -ENOMEM;
-       if (!rpcauth_create(flavor, clnt)) {
+       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;
        }
 
@@ -182,6 +185,36 @@ out_err:
        return ERR_PTR(err);
 }
 
+/**
+ * Create an RPC client
+ * @xprt - pointer to xprt struct
+ * @servname - name of server
+ * @info - rpc_program
+ * @version - rpc_program version
+ * @authflavor - rpc_auth flavour to use
+ *
+ * Creates an RPC client structure, then pings the server in order to
+ * determine if it is up, and if it supports this program and version.
+ *
+ * This function should never be called by asynchronous tasks such as
+ * the portmapper.
+ */
+struct rpc_clnt *rpc_create_client(struct rpc_xprt *xprt, char *servname,
+               struct rpc_program *info, u32 version, rpc_authflavor_t authflavor)
+{
+       struct rpc_clnt *clnt;
+       int err;
+       
+       clnt = rpc_new_client(xprt, servname, info, version, authflavor);
+       if (IS_ERR(clnt))
+               return clnt;
+       err = rpc_ping(clnt, RPC_TASK_SOFT|RPC_TASK_NOINTR);
+       if (err == 0)
+               return clnt;
+       rpc_shutdown_client(clnt);
+       return ERR_PTR(err);
+}
+
 /*
  * This function clones the RPC client structure. It allows us to share the
  * same transport while varying parameters such as the authentication
@@ -209,6 +242,8 @@ rpc_clone_client(struct rpc_clnt *clnt)
        rpc_init_rtt(&new->cl_rtt_default, clnt->cl_xprt->timeout.to_initval);
        if (new->cl_auth)
                atomic_inc(&new->cl_auth->au_count);
+       new->cl_pmap            = &new->cl_pmap_default;
+       rpc_init_wait_queue(&new->cl_pmap_default.pm_bindwait, "bindwait");
        return new;
 out_no_clnt:
        printk(KERN_INFO "RPC: out of memory in %s\n", __FUNCTION__);
@@ -297,6 +332,44 @@ rpc_release_client(struct rpc_clnt *clnt)
                rpc_destroy_client(clnt);
 }
 
+/**
+ * rpc_bind_new_program - bind a new RPC program to an existing client
+ * @old - old rpc_client
+ * @program - rpc program to set
+ * @vers - rpc program version
+ *
+ * Clones the rpc client and sets up a new RPC program. This is mainly
+ * of use for enabling different RPC programs to share the same transport.
+ * The Sun NFSv2/v3 ACL protocol can do this.
+ */
+struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old,
+                                     struct rpc_program *program,
+                                     int vers)
+{
+       struct rpc_clnt *clnt;
+       struct rpc_version *version;
+       int err;
+
+       BUG_ON(vers >= program->nrvers || !program->version[vers]);
+       version = program->version[vers];
+       clnt = rpc_clone_client(old);
+       if (IS_ERR(clnt))
+               goto out;
+       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;
+       err = rpc_ping(clnt, RPC_TASK_SOFT|RPC_TASK_NOINTR);
+       if (err != 0) {
+               rpc_shutdown_client(clnt);
+               clnt = ERR_PTR(err);
+       }
+out:   
+       return clnt;
+}
+
 /*
  * Default callback for async RPC calls
  */
@@ -306,38 +379,41 @@ rpc_default_callback(struct rpc_task *task)
 }
 
 /*
- *     Export the signal mask handling for aysnchronous code that
+ *     Export the signal mask handling for synchronous code that
  *     sleeps on RPC calls
  */
+#define RPC_INTR_SIGNALS (sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGKILL))
  
+static void rpc_save_sigmask(sigset_t *oldset, int intr)
+{
+       unsigned long   sigallow = 0;
+       sigset_t sigmask;
+
+       /* Block all signals except those listed in sigallow */
+       if (intr)
+               sigallow |= RPC_INTR_SIGNALS;
+       siginitsetinv(&sigmask, sigallow);
+       sigprocmask(SIG_BLOCK, &sigmask, oldset);
+}
+
+static inline void rpc_task_sigmask(struct rpc_task *task, sigset_t *oldset)
+{
+       rpc_save_sigmask(oldset, !RPC_TASK_UNINTERRUPTIBLE(task));
+}
+
+static inline void rpc_restore_sigmask(sigset_t *oldset)
+{
+       sigprocmask(SIG_SETMASK, oldset, NULL);
+}
+
 void rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset)
 {
-       unsigned long   sigallow = sigmask(SIGKILL);
-       unsigned long   irqflags;
-       
-       /* Turn off various signals */
-       if (clnt->cl_intr) {
-               struct k_sigaction *action = current->sighand->action;
-               if (action[SIGINT-1].sa.sa_handler == SIG_DFL)
-                       sigallow |= sigmask(SIGINT);
-               if (action[SIGQUIT-1].sa.sa_handler == SIG_DFL)
-                       sigallow |= sigmask(SIGQUIT);
-       }
-       spin_lock_irqsave(&current->sighand->siglock, irqflags);
-       *oldset = current->blocked;
-       siginitsetinv(&current->blocked, sigallow & ~oldset->sig[0]);
-       recalc_sigpending();
-       spin_unlock_irqrestore(&current->sighand->siglock, irqflags);
+       rpc_save_sigmask(oldset, clnt->cl_intr);
 }
 
 void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset)
 {
-       unsigned long   irqflags;
-       
-       spin_lock_irqsave(&current->sighand->siglock, irqflags);
-       current->blocked = *oldset;
-       recalc_sigpending();
-       spin_unlock_irqrestore(&current->sighand->siglock, irqflags);
+       rpc_restore_sigmask(oldset);
 }
 
 /*
@@ -355,26 +431,26 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
 
        BUG_ON(flags & RPC_TASK_ASYNC);
 
-       rpc_clnt_sigmask(clnt, &oldset);                
-
        status = -ENOMEM;
        task = rpc_new_task(clnt, NULL, flags);
        if (task == NULL)
                goto out;
 
+       /* Mask signals on RPC calls _and_ GSS_AUTH upcalls */
+       rpc_task_sigmask(task, &oldset);
+
        rpc_call_setup(task, msg, 0);
 
        /* Set up the call info struct and execute the task */
-       if (task->tk_status == 0)
+       if (task->tk_status == 0) {
                status = rpc_execute(task);
-       else {
+       else {
                status = task->tk_status;
                rpc_release_task(task);
        }
 
+       rpc_restore_sigmask(&oldset);
 out:
-       rpc_clnt_sigunmask(clnt, &oldset);              
-
        return status;
 }
 
@@ -395,8 +471,6 @@ rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
 
        flags |= RPC_TASK_ASYNC;
 
-       rpc_clnt_sigmask(clnt, &oldset);                
-
        /* Create/initialize a new RPC task */
        if (!callback)
                callback = rpc_default_callback;
@@ -405,6 +479,9 @@ rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
                goto out;
        task->tk_calldata = data;
 
+       /* Mask signals on GSS_AUTH upcalls */
+       rpc_task_sigmask(task, &oldset);                
+
        rpc_call_setup(task, msg, 0);
 
        /* Set up the call info struct and execute the task */
@@ -414,9 +491,8 @@ rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags,
        else
                rpc_release_task(task);
 
+       rpc_restore_sigmask(&oldset);           
 out:
-       rpc_clnt_sigunmask(clnt, &oldset);              
-
        return status;
 }
 
@@ -449,8 +525,7 @@ rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize
        xprt->rcvsize = 0;
        if (rcvsize)
                xprt->rcvsize = rcvsize + RPC_SLACK_SPACE;
-       if (xprt_connected(xprt))
-               xprt_sock_setbufsize(xprt);
+       xprt->ops->set_buffer_size(xprt);
 }
 
 /*
@@ -594,7 +669,7 @@ call_allocate(struct rpc_task *task)
                return;
        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) || !signalled()) {
                xprt_release(task);
                task->tk_action = call_reserve;
                rpc_delay(task, HZ>>4);
@@ -659,43 +734,94 @@ static void
 call_bind(struct rpc_task *task)
 {
        struct rpc_clnt *clnt = task->tk_client;
-       struct rpc_xprt *xprt = clnt->cl_xprt;
 
-       dprintk("RPC: %4d call_bind xprt %p %s connected\n", task->tk_pid,
-                       xprt, (xprt_connected(xprt) ? "is" : "is not"));
-
-       task->tk_action = (xprt_connected(xprt)) ? call_transmit : call_connect;
+       dprintk("RPC: %4d call_bind (status %d)\n",
+                               task->tk_pid, task->tk_status);
 
+       task->tk_action = call_connect;
        if (!clnt->cl_port) {
-               task->tk_action = call_connect;
+               task->tk_action = call_bind_status;
                task->tk_timeout = RPC_CONNECT_TIMEOUT;
                rpc_getport(task, clnt);
        }
 }
 
 /*
- * 4a. Connect to the RPC server (TCP case)
+ * 4a. Sort out bind result
+ */
+static void
+call_bind_status(struct rpc_task *task)
+{
+       int status = -EACCES;
+
+       if (task->tk_status >= 0) {
+               dprintk("RPC: %4d call_bind_status (status %d)\n",
+                                       task->tk_pid, task->tk_status);
+               task->tk_status = 0;
+               task->tk_action = call_connect;
+               return;
+       }
+
+       switch (task->tk_status) {
+       case -EACCES:
+               dprintk("RPC: %4d remote rpcbind: RPC program/version unavailable\n",
+                               task->tk_pid);
+               break;
+       case -ETIMEDOUT:
+               dprintk("RPC: %4d rpcbind request timed out\n",
+                               task->tk_pid);
+               if (RPC_IS_SOFT(task)) {
+                       status = -EIO;
+                       break;
+               }
+               goto retry_bind;
+       case -EPFNOSUPPORT:
+               dprintk("RPC: %4d remote rpcbind service unavailable\n",
+                               task->tk_pid);
+               break;
+       case -EPROTONOSUPPORT:
+               dprintk("RPC: %4d remote rpcbind version 2 unavailable\n",
+                               task->tk_pid);
+               break;
+       default:
+               dprintk("RPC: %4d unrecognized rpcbind error (%d)\n",
+                               task->tk_pid, -task->tk_status);
+               status = -EIO;
+               break;
+       }
+
+       rpc_exit(task, status);
+       return;
+
+retry_bind:
+       task->tk_status = 0;
+       task->tk_action = call_bind;
+       return;
+}
+
+/*
+ * 4b. Connect to the RPC server
  */
 static void
 call_connect(struct rpc_task *task)
 {
-       struct rpc_clnt *clnt = task->tk_client;
+       struct rpc_xprt *xprt = task->tk_xprt;
 
-       dprintk("RPC: %4d call_connect status %d\n",
-                               task->tk_pid, task->tk_status);
+       dprintk("RPC: %4d call_connect xprt %p %s connected\n",
+                       task->tk_pid, xprt,
+                       (xprt_connected(xprt) ? "is" : "is not"));
 
-       if (xprt_connected(clnt->cl_xprt)) {
-               task->tk_action = call_transmit;
-               return;
+       task->tk_action = call_transmit;
+       if (!xprt_connected(xprt)) {
+               task->tk_action = call_connect_status;
+               if (task->tk_status < 0)
+                       return;
+               xprt_connect(task);
        }
-       task->tk_action = call_connect_status;
-       if (task->tk_status < 0)
-               return;
-       xprt_connect(task);
 }
 
 /*
- * 4b. Sort out connect result
+ * 4c. Sort out connect result
  */
 static void
 call_connect_status(struct rpc_task *task)
@@ -703,6 +829,9 @@ call_connect_status(struct rpc_task *task)
        struct rpc_clnt *clnt = task->tk_client;
        int status = task->tk_status;
 
+       dprintk("RPC: %5u call_connect_status (status %d)\n", 
+                               task->tk_pid, task->tk_status);
+
        task->tk_status = 0;
        if (status >= 0) {
                clnt->cl_stats->netreconn++;
@@ -710,17 +839,19 @@ call_connect_status(struct rpc_task *task)
                return;
        }
 
-       /* Something failed: we may have to rebind */
+       /* Something failed: remote service port may have changed */
        if (clnt->cl_autobind)
                clnt->cl_port = 0;
+
        switch (status) {
        case -ENOTCONN:
        case -ETIMEDOUT:
        case -EAGAIN:
-               task->tk_action = (clnt->cl_port == 0) ? call_bind : call_connect;
+               task->tk_action = call_bind;
                break;
        default:
                rpc_exit(task, -EIO);
+               break;
        }
 }
 
@@ -989,10 +1120,11 @@ call_verify(struct rpc_task *task)
                        case RPC_AUTH_ERROR:
                                break;
                        case RPC_MISMATCH:
-                               printk(KERN_WARNING "%s: RPC call version mismatch!\n", __FUNCTION__);
-                               goto out_eio;
+                               dprintk("%s: RPC call version mismatch!\n", __FUNCTION__);
+                               error = -EPROTONOSUPPORT;
+                               goto out_err;
                        default:
-                               printk(KERN_WARNING "%s: RPC call rejected, unknown error: %x\n", __FUNCTION__, n);
+                               dprintk("%s: RPC call rejected, unknown error: %x\n", __FUNCTION__, n);
                                goto out_eio;
                }
                if (--len < 0)
@@ -1043,23 +1175,26 @@ call_verify(struct rpc_task *task)
        case RPC_SUCCESS:
                return p;
        case RPC_PROG_UNAVAIL:
-               printk(KERN_WARNING "RPC: call_verify: program %u is unsupported by server %s\n",
+               dprintk("RPC: call_verify: program %u is unsupported by server %s\n",
                                (unsigned int)task->tk_client->cl_prog,
                                task->tk_client->cl_server);
-               goto out_eio;
+               error = -EPFNOSUPPORT;
+               goto out_err;
        case RPC_PROG_MISMATCH:
-               printk(KERN_WARNING "RPC: call_verify: program %u, version %u unsupported by server %s\n",
+               dprintk("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_vers,
                                task->tk_client->cl_server);
-               goto out_eio;
+               error = -EPROTONOSUPPORT;
+               goto out_err;
        case RPC_PROC_UNAVAIL:
-               printk(KERN_WARNING "RPC: call_verify: proc %p unsupported by program %u, version %u on server %s\n",
+               dprintk("RPC: call_verify: proc %p unsupported by program %u, version %u on server %s\n",
                                task->tk_msg.rpc_proc,
                                task->tk_client->cl_prog,
                                task->tk_client->cl_vers,
                                task->tk_client->cl_server);
-               goto out_eio;
+               error = -EOPNOTSUPP;
+               goto out_err;
        case RPC_GARBAGE_ARGS:
                dprintk("RPC: %4d %s: server saw garbage\n", task->tk_pid, __FUNCTION__);
                break;                  /* retry */
@@ -1072,7 +1207,7 @@ out_retry:
        task->tk_client->cl_stats->rpcgarbage++;
        if (task->tk_garb_retry) {
                task->tk_garb_retry--;
-               dprintk(KERN_WARNING "RPC %s: retrying %4d\n", __FUNCTION__, task->tk_pid);
+               dprintk("RPC %s: retrying %4d\n", __FUNCTION__, task->tk_pid);
                task->tk_action = call_bind;
                return NULL;
        }
@@ -1086,3 +1221,30 @@ out_overflow:
        printk(KERN_WARNING "RPC %s: server reply was truncated.\n", __FUNCTION__);
        goto out_retry;
 }
+
+static int rpcproc_encode_null(void *rqstp, u32 *data, void *obj)
+{
+       return 0;
+}
+
+static int rpcproc_decode_null(void *rqstp, u32 *data, void *obj)
+{
+       return 0;
+}
+
+static struct rpc_procinfo rpcproc_null = {
+       .p_encode = rpcproc_encode_null,
+       .p_decode = rpcproc_decode_null,
+};
+
+int rpc_ping(struct rpc_clnt *clnt, int flags)
+{
+       struct rpc_message msg = {
+               .rpc_proc = &rpcproc_null,
+       };
+       int err;
+       msg.rpc_cred = authnull_ops.lookup_cred(NULL, NULL, 0);
+       err = rpc_call_sync(clnt, &msg, flags);
+       put_rpccred(msg.rpc_cred);
+       return err;
+}