nfs-ganesha 1.4

nfs_rpc_callback.c

Go to the documentation of this file.
00001 /*
00002  * vim:expandtab:shiftwidth=8:tabstop=8:
00003  *
00004  * Copyright (C) 2012, The Linux Box Corporation
00005  * Contributor : Matt Benjamin <matt@linuxbox.com>
00006  *
00007  * Some portions Copyright CEA/DAM/DIF  (2008)
00008  * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
00009  *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
00010  *
00011  *
00012  * This program is free software; you can redistribute it and/or
00013  * modify it under the terms of the GNU Lesser General Public
00014  * License as published by the Free Software Foundation; either
00015  * version 3 of the License, or (at your option) any later version.
00016  *
00017  * This program is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00020  * Lesser General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU Lesser General Public
00023  * License along with this library; if not, write to the Free Software
00024  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00025  *
00026  * -------------
00027  */
00028 
00029 #ifdef HAVE_CONFIG_H
00030 #include "config.h"
00031 #endif
00032 
00033 #ifdef _SOLARIS
00034 #include "solaris_port.h"
00035 #endif                          /* _SOLARIS */
00036 
00037 #include <unistd.h>
00038 #include <sys/types.h>
00039 #include <sys/stat.h>
00040 #include <sys/param.h>
00041 #include <time.h>
00042 #include <pthread.h>
00043 #include <assert.h>
00044 #include <arpa/inet.h>
00045 #include "nlm_list.h"
00046 #include "fsal.h"
00047 #include "nfs_core.h"
00048 #include "log.h"
00049 #include "nfs_rpc_callback.h"
00050 #include "nfs4.h"
00051 #include "gssd.h"
00052 #include "gss_util.h"
00053 #include "krb5_util.h"
00054 #include "sal_data.h"
00055 
00069 static pool_t *rpc_call_pool;
00070 
00071 /* tried to re-use host_name in nfs_main.c, but linker became confused.
00072  * this is a quick fix */
00073 static char host_name[MAXHOSTNAMELEN];
00074 
00075 
00076 static inline void
00077 nfs_rpc_cb_init_ccache(const char *ccache)
00078 {
00079     int code = 0;
00080 
00081     mkdir(ccache, 700); /* XXX */
00082     ccachesearch[0] = nfs_param.krb5_param.ccache_dir;
00083 
00084     code = gssd_refresh_krb5_machine_credential(
00085         host_name, NULL, nfs_param.krb5_param.svc.principal);
00086     if (code) {
00087         LogDebug(COMPONENT_INIT, "gssd_refresh_krb5_machine_credential "
00088                  "failed (%d:%d)", code, errno);
00089         goto out;
00090     }
00091 
00092 out:
00093     return;
00094 }
00095 
00096 /*
00097  * Initialize subsystem
00098  */
00099 void nfs_rpc_cb_pkginit(void)
00100 {
00101     char localmachine[MAXHOSTNAMELEN];
00102 
00103     /* Create a pool of rpc_call_t */
00104     rpc_call_pool = pool_init("RPC Call Pool",
00105                               sizeof(rpc_call_t),
00106                               pool_basic_substrate,
00107                               NULL,
00108                               nfs_rpc_init_call,
00109                               NULL);
00110     if(!(rpc_call_pool)) {
00111         LogCrit(COMPONENT_INIT,
00112                 "Error while allocating rpc call pool");
00113         LogError(COMPONENT_INIT, ERR_SYS, ERR_MALLOC, errno);
00114         Fatal();
00115     }
00116 
00117     /* get host name */
00118     if(gethostname(localmachine, sizeof(localmachine)) != 0) {
00119         LogCrit(COMPONENT_INIT, "Failed to get local host name");
00120     }
00121     else
00122         strlcpy(host_name, localmachine, MAXHOSTNAMELEN);
00123 
00124     /* ccache */
00125     nfs_rpc_cb_init_ccache(nfs_param.krb5_param.ccache_dir);
00126 
00127     /* sanity check GSSAPI */
00128     if (gssd_check_mechs() != 0)
00129         LogCrit(COMPONENT_INIT,  "sanity check: gssd_check_mechs() failed");
00130 
00131     return;
00132 }
00133 
00134 /*
00135  * Shutdown subsystem
00136  */
00137 void nfs_rpc_cb_pkgshutdown(void)
00138 {
00139     /* do nothing */
00140 }
00141 
00142 /* XXXX this is automatically redundant, but in fact upstream TI-RPC is
00143  * not up-to-date with RFC 5665, will fix (Matt)
00144  *
00145  * (c) 2012, Linux Box Corp
00146  */
00147 
00148 nc_type nfs_netid_to_nc(const char *netid)
00149 {
00150     if (! strncmp(netid, netid_nc_table[_NC_TCP].netid,
00151                   netid_nc_table[_NC_TCP].netid_len))
00152         return(_NC_TCP);
00153 
00154     if (! strncmp(netid, netid_nc_table[_NC_TCP6].netid,
00155                   netid_nc_table[_NC_TCP6].netid_len))
00156         return(_NC_TCP6);
00157 
00158     if (! strncmp(netid, netid_nc_table[_NC_UDP].netid,
00159                   netid_nc_table[_NC_UDP].netid_len))
00160         return (_NC_UDP);
00161 
00162     if (! strncmp(netid, netid_nc_table[_NC_UDP6].netid,
00163                   netid_nc_table[_NC_UDP6].netid_len))
00164         return (_NC_UDP6);
00165 
00166     if (! strncmp(netid, netid_nc_table[_NC_RDMA].netid,
00167                   netid_nc_table[_NC_RDMA].netid_len))
00168         return (_NC_RDMA);
00169 
00170     if (! strncmp(netid, netid_nc_table[_NC_RDMA6].netid,
00171                  netid_nc_table[_NC_RDMA6].netid_len))
00172         return (_NC_RDMA6);
00173 
00174     if (! strncmp(netid, netid_nc_table[_NC_SCTP].netid,
00175                   netid_nc_table[_NC_SCTP].netid_len))
00176         return (_NC_SCTP);
00177 
00178     if (! strncmp(netid, netid_nc_table[_NC_SCTP6].netid,
00179                   netid_nc_table[_NC_SCTP6].netid_len))
00180         return (_NC_SCTP6);
00181 
00182     return (_NC_ERR);
00183 }
00184 
00185 static inline void
00186 setup_client_saddr(nfs_client_id_t *pclientid, const char *uaddr)
00187 {
00188     char addr_buf[SOCK_NAME_MAX];
00189     uint32_t bytes[11];
00190     int code;
00191 
00192     memset(&pclientid->cid_cb.cid_addr.ss, 0, sizeof(struct sockaddr_storage));
00193 
00194     switch (pclientid->cid_cb.cid_addr.nc) {
00195     case _NC_TCP:
00196     case _NC_RDMA:
00197     case _NC_SCTP:
00198     case _NC_UDP:
00199         /* IPv4 (ws inspired) */
00200         if (sscanf(uaddr, "%u.%u.%u.%u.%u.%u",
00201                    &bytes[1], &bytes[2], &bytes[3], &bytes[4],
00202                    &bytes[5], &bytes[6]) == 6) {
00203             struct sockaddr_in *sin =
00204                 (struct sockaddr_in *) &pclientid->cid_cb.cid_addr.ss;
00205             snprintf(addr_buf, SOCK_NAME_MAX, "%u.%u.%u.%u",
00206                      bytes[1], bytes[2],
00207                      bytes[3], bytes[4]);
00208             sin->sin_family = AF_INET;
00209             sin->sin_port = htons((bytes[5]<<8) | bytes[6]);
00210             code = inet_pton(AF_INET, addr_buf, &sin->sin_addr);
00211             if (code != 1)
00212                 LogDebug(COMPONENT_NFS_CB, "inet_pton failed (%d %s)",
00213                          code, addr_buf);
00214             else
00215                 LogDebug(COMPONENT_NFS_CB, "client callback addr:port %s:%d",
00216                          addr_buf, ntohs(sin->sin_port));
00217         }
00218         break;
00219     case _NC_TCP6:
00220     case _NC_RDMA6:
00221     case _NC_SCTP6:
00222     case _NC_UDP6:
00223         /* IPv6 (ws inspired) */
00224         if (sscanf(uaddr, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x.%u.%u",
00225                    &bytes[1], &bytes[2], &bytes[3], &bytes[4], &bytes[5],
00226                    &bytes[6], &bytes[7], &bytes[8],
00227                    &bytes[9], &bytes[10]) == 10) {
00228             struct sockaddr_in6 *sin6 =
00229                 (struct sockaddr_in6 *) &pclientid->cid_cb.cid_addr.ss;
00230             snprintf(addr_buf, SOCK_NAME_MAX,
00231                      "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x",
00232                      bytes[1], bytes[2], bytes[3], bytes[4], bytes[5],
00233                      bytes[6], bytes[7], bytes[8]);
00234             code = inet_pton(AF_INET6, addr_buf, &sin6->sin6_addr);
00235             sin6->sin6_port = htons((bytes[9]<<8) | bytes[10]);
00236             sin6->sin6_family = AF_INET6;
00237             if (code != 1)
00238                 LogDebug(COMPONENT_NFS_CB, "inet_pton failed (%d %s)",
00239                          code, addr_buf);
00240             else
00241                 LogDebug(COMPONENT_NFS_CB, "client callback addr:port %s:%d",
00242                          addr_buf, ntohs(sin6->sin6_port));
00243         }
00244         break;
00245     default:
00246         /* unknown netid */
00247         break;
00248     };
00249 }
00250 
00251 void nfs_set_client_location(nfs_client_id_t *pclientid, const clientaddr4 *addr4)
00252 {
00253     pclientid->cid_cb.cid_addr.nc = nfs_netid_to_nc(addr4->r_netid);
00254     strlcpy(pclientid->cid_cb.cid_client_r_addr, addr4->r_addr,
00255             SOCK_NAME_MAX);
00256     setup_client_saddr(pclientid, pclientid->cid_cb.cid_client_r_addr);
00257 }
00258 
00259 static inline int32_t
00260 nfs_clid_connected_socket(nfs_client_id_t *pclientid, int *fd, int *proto)
00261 {
00262     struct sockaddr_in *sin;
00263     struct sockaddr_in6 *sin6;
00264     int nfd, code = 0;
00265 
00266     *fd = 0;
00267     *proto = -1;
00268 
00269     switch (pclientid->cid_cb.cid_addr.ss.ss_family) {
00270     case AF_INET:
00271         sin = (struct sockaddr_in *) &pclientid->cid_cb.cid_addr.ss;
00272         switch (pclientid->cid_cb.cid_addr.nc) {
00273         case _NC_TCP:
00274             nfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
00275             *proto = IPPROTO_TCP;
00276             break;
00277         case _NC_UDP:
00278             nfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
00279             *proto = IPPROTO_UDP;
00280             break;
00281         default:
00282             code = EINVAL;
00283             goto out;
00284             break;
00285         }
00286 
00287         code = connect(nfd, (struct sockaddr *) sin,
00288                        sizeof(struct sockaddr_in));
00289         if (code == -1) {
00290             LogDebug(COMPONENT_NFS_CB, "connect fail errno %d", errno);
00291             goto out;
00292         }
00293         *fd = nfd;
00294         break;
00295     case AF_INET6:
00296         sin6 = (struct sockaddr_in6 *) &pclientid->cid_cb.cid_addr.ss;
00297         switch (pclientid->cid_cb.cid_addr.nc) {
00298         case _NC_TCP6:
00299             nfd = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
00300             *proto = IPPROTO_TCP;
00301             break;
00302         case _NC_UDP6:
00303             nfd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
00304             *proto = IPPROTO_UDP;
00305             break;
00306         default:
00307             code = EINVAL;
00308             goto out;
00309             break;
00310         }
00311         code = connect(nfd, (struct sockaddr *) sin6,
00312                        sizeof(struct sockaddr_in6));
00313         if (code == -1) {
00314             LogDebug(COMPONENT_NFS_CB, "connect fail errno %d", errno);
00315             goto out;
00316         }
00317         *fd = nfd;
00318         break;
00319     default:
00320         code = EINVAL;
00321         break;
00322     }
00323 
00324 out:
00325     return (code);
00326 }
00327 
00328 /* end refactorable RPC code */
00329 
00330 static inline bool_t
00331 supported_auth_flavor(int flavor)
00332 {
00333     bool_t code = FALSE;
00334 
00335     switch (flavor) {
00336     case RPCSEC_GSS:
00337     case AUTH_SYS:
00338     case AUTH_NONE:
00339         code = TRUE;
00340         break;
00341     default:        
00342         break;
00343     };
00344 
00345     return (code);
00346 }
00347 
00348 /* from kerberos source, gssapi_krb5.c (Umich) */
00349 gss_OID_desc krb5oid =
00350    {9, "\052\206\110\206\367\022\001\002\002"};
00351 
00352 static inline char *
00353 format_host_principal(rpc_call_channel_t *chan, char *buf, size_t len)
00354 {
00355     char addr_buf[SOCK_NAME_MAX];
00356     const char *host = NULL;
00357     char *princ = NULL;
00358 
00359     switch (chan->type) {
00360     case RPC_CHAN_V40:
00361     {
00362         nfs_client_id_t *pclientid = chan->nvu.v40.pclientid;
00363         switch (chan->nvu.v40.pclientid->cid_cb.cid_addr.ss.ss_family) {
00364         case AF_INET:
00365         {
00366             struct sockaddr_in *sin = (struct sockaddr_in *) &pclientid->cid_cb.cid_addr.ss;
00367             host = inet_ntop(AF_INET, &sin->sin_addr, addr_buf,
00368                              INET_ADDRSTRLEN);
00369             break;
00370         }
00371         case AF_INET6:
00372         {
00373             struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &pclientid->cid_cb.cid_addr.ss;
00374             host = inet_ntop(AF_INET6, &sin6->sin6_addr, addr_buf,
00375                              INET6_ADDRSTRLEN);
00376             break;
00377         }
00378         default:
00379             break;
00380         }
00381         break;
00382     }
00383     case RPC_CHAN_V41:
00384         /* XXX implement */
00385         goto out;
00386         break;
00387     }
00388 
00389     if (host) {
00390         snprintf(buf, len, "nfs@%s", host);
00391         princ = buf; 
00392     }
00393 
00394 out:
00395     return (princ);
00396 }
00397 
00398 static inline void
00399 nfs_rpc_callback_setup_gss(rpc_call_channel_t *chan,
00400                            nfs_client_cred_t *cred)
00401 {
00402     AUTH *auth;
00403     char hprinc[MAXPATHLEN];
00404     int32_t code = 0;
00405 
00406     assert(cred->flavor == RPCSEC_GSS);
00407 
00408     /* MUST RFC 3530bis, section 3.3.3 */
00409     chan->gss_sec.svc = cred->auth_union.auth_gss.svc;
00410     chan->gss_sec.qop = cred->auth_union.auth_gss.qop;
00411 
00412     /* the GSSAPI k5 mech needs to find an unexpired credential
00413      * for nfs/hostname in an accessible k5ccache */
00414     code = gssd_refresh_krb5_machine_credential(
00415         host_name, NULL, nfs_param.krb5_param.svc.principal);
00416     if (code) {
00417         LogDebug(COMPONENT_NFS_CB, "gssd_refresh_krb5_machine_credential "
00418                  "failed (%d:%d)", code, errno);
00419         goto out;
00420     }
00421 
00422     if (! format_host_principal(chan, hprinc, MAXPATHLEN)) {
00423         LogCrit(COMPONENT_NFS_CB, "format_host_principal failed");
00424         goto out;
00425     }
00426 
00427     chan->gss_sec.cred = GSS_C_NO_CREDENTIAL;
00428     chan->gss_sec.req_flags = 0;
00429 
00430     if (chan->gss_sec.svc != RPCSEC_GSS_SVC_NONE) {
00431         /* no more lipkey, spkm3 */
00432         chan->gss_sec.mech = (gss_OID) &krb5oid;
00433         chan->gss_sec.req_flags = GSS_C_MUTUAL_FLAG; /* XXX */
00434         auth = 
00435             authgss_create_default(chan->clnt,
00436                                    hprinc,
00437                                    &chan->gss_sec);
00438         /* authgss_create and authgss_create_default return NULL on
00439          * failure, don't assign NULL to clnt->cl_auth */
00440         if (auth)
00441             chan->clnt->cl_auth = auth;
00442     }
00443 
00444 out:
00445     return;
00446 }
00447 
00448 static inline bool_t
00449 nfs_rpc_callback_seccreate(rpc_call_channel_t *chan)
00450 {
00451     nfs_client_cred_t *credential = NULL;
00452     bool_t code = TRUE;
00453     AUTH *auth = NULL;
00454 
00455     switch (chan->type) {
00456     case RPC_CHAN_V40:
00457         assert(&chan->nvu.v40.pclientid);
00458         credential = &chan->nvu.v40.pclientid->cid_credential;
00459         break;
00460     case RPC_CHAN_V41:
00461         /* XXX implement */
00462         goto out;
00463         break;
00464     }
00465     
00466     switch (credential->flavor) {
00467     case RPCSEC_GSS:
00468         nfs_rpc_callback_setup_gss(chan, credential);
00469         break;
00470     case AUTH_SYS:
00471         auth = authunix_create_default();
00472         /* XXX see above */
00473         if (auth)
00474             chan->clnt->cl_auth = auth;
00475         break;
00476     case AUTH_NONE:
00477         break;
00478     default:
00479         /* XXX prevented by forward check */
00480         break;
00481     }
00482 
00483 out:
00484     return (code);
00485 }
00486 
00487 /* Create a channel for a new clientid (v4) or session, optionally
00488  * connecting it */
00489 int nfs_rpc_create_chan_v40(nfs_client_id_t *pclientid,
00490                             uint32_t flags)
00491 {
00492     struct netbuf raddr;
00493     int fd, proto, code = 0;
00494     rpc_call_channel_t *chan = &pclientid->cid_cb.cb_u.v40.cb_chan;
00495 
00496 
00497 
00498     assert(! chan->clnt);
00499 
00500     /* XXX we MUST error RFC 3530bis, sec. 3.3.3 */
00501     if (! supported_auth_flavor(pclientid->cid_credential.flavor)) {
00502         code = EINVAL;
00503         goto out;
00504     }
00505 
00506     chan->type = RPC_CHAN_V40;
00507     chan->nvu.v40.pclientid = pclientid;
00508 
00509     code = nfs_clid_connected_socket(pclientid, &fd, &proto);
00510     if (code) {
00511         LogDebug(COMPONENT_NFS_CB,
00512                  "Failed creating socket");
00513         goto out;
00514     }
00515 
00516     raddr.buf = &pclientid->cid_cb.cid_addr.ss;
00517 
00518     switch (proto) {
00519     case IPPROTO_TCP:
00520         raddr.maxlen = raddr.len = sizeof(struct sockaddr_in);
00521         chan->clnt = clnt_vc_create(fd,
00522                                     &raddr,
00523                                     pclientid->cid_cb.cid_program,
00524                                     1 /* Errata ID: 2291 */,
00525                                     0, 0);
00526         break;
00527     case IPPROTO_UDP:
00528         raddr.maxlen = raddr.len = sizeof(struct sockaddr_in6);
00529         chan->clnt = clnt_dg_create(fd,
00530                                     &raddr,
00531                                     pclientid->cid_cb.cid_program,
00532                                     1 /* Errata ID: 2291 */,
00533                                     0, 0);
00534         break;
00535     default:
00536         break;
00537     }
00538 
00539     if (! chan->clnt) {
00540         code = EINVAL;
00541         goto out;
00542     }
00543 
00544     /* channel protection */
00545     if (! nfs_rpc_callback_seccreate(chan)) {
00546         /* XXX */
00547         code = EINVAL;
00548     }
00549 
00550 out:
00551     return (code);
00552 }
00553 
00554 rpc_call_channel_t *
00555 nfs_rpc_get_chan(nfs_client_id_t *pclientid, uint32_t flags)
00556 {
00557     /* XXX v41 */
00558     rpc_call_channel_t *chan = &pclientid->cid_cb.cb_u.v40.cb_chan;
00559 
00560     if (! chan->clnt) {
00561         nfs_rpc_create_chan_v40(pclientid, flags);
00562     }
00563 
00564     return (chan);
00565 }
00566 
00567 /* Dispose a channel. */
00568 void nfs_rpc_destroy_chan(rpc_call_channel_t *chan)
00569 {
00570     assert(chan);
00571 
00572     /* XXX lock, wait for outstanding calls, etc */
00573 
00574     switch (chan->type) {
00575     case RPC_CHAN_V40:
00576         /* channel has a dedicated RPC client */
00577         if (chan->clnt) {
00578             /* clean up auth, if any */
00579             if (chan->clnt->cl_auth)
00580                 AUTH_DESTROY(chan->clnt->cl_auth);
00581             /* destroy it */
00582             clnt_destroy(chan->clnt);
00583             chan->clnt = NULL;
00584         }
00585         break;
00586     case RPC_CHAN_V41:
00587         /* XXX channel is shared */
00588         break;
00589     }
00590 
00591     chan->clnt = NULL;
00592     chan->last_called = 0;
00593 }
00594 
00595 /*
00596  * Call the NFSv4 client's CB_NULL procedure.
00597  */
00598 enum clnt_stat
00599 rpc_cb_null(rpc_call_channel_t *chan, struct timeval timeout)
00600 {
00601     enum clnt_stat stat = RPC_SUCCESS;
00602 
00603     /* XXX TI-RPC does the signal masking */
00604     pthread_mutex_lock(&chan->mtx);
00605 
00606     if (! chan->clnt) {
00607         stat = RPC_INTR;
00608         goto unlock;
00609     }
00610 
00611     stat = clnt_call(chan->clnt, CB_NULL,
00612                      (xdrproc_t) xdr_void, NULL,
00613                      (xdrproc_t) xdr_void, NULL, timeout);
00614 
00615     /* If a call fails, we have to assume path down, or equally fatal
00616      * error.  We may need back-off. */
00617     if (stat != RPC_SUCCESS) {
00618         if (chan->clnt) {
00619             clnt_destroy(chan->clnt);
00620             chan->clnt = NULL;
00621         }
00622     }
00623 
00624 unlock:
00625     pthread_mutex_unlock(&chan->mtx);
00626     
00627     return (stat);
00628 }
00629 
00630 static inline void free_argop(nfs_cb_argop4 *op)
00631 {
00632     gsh_free(op);
00633 }
00634 
00635 static inline void free_resop(nfs_cb_resop4 *op)
00636 {
00637     gsh_free(op);
00638 }
00639 
00640 rpc_call_t *alloc_rpc_call()
00641 {
00642     rpc_call_t *call;
00643 
00644     call = pool_alloc(rpc_call_pool, NULL);
00645 
00646     return (call);
00647 }
00648 
00649 void free_rpc_call(rpc_call_t *call)
00650 {
00651     free_argop(call->cbt.v_u.v4.args.argarray.argarray_val);
00652     free_resop(call->cbt.v_u.v4.res.resarray.resarray_val);
00653     pool_free(rpc_call_pool, call);
00654 }
00655 
00656 static inline void RPC_CALL_HOOK(rpc_call_t *call, rpc_call_hook hook,
00657                                  void* arg, uint32_t flags)
00658 {
00659     if (call)
00660         (void) call->call_hook(call, hook, arg, flags);
00661 }
00662 
00663 int32_t
00664 nfs_rpc_submit_call(rpc_call_t *call, uint32_t flags)
00665 {
00666     int32_t code = 0;
00667     request_data_t *pnfsreq = NULL;
00668     rpc_call_channel_t *chan = call->chan;
00669 
00670     assert(chan);
00671 
00672     if (flags & NFS_RPC_CALL_INLINE) {
00673         code = nfs_rpc_dispatch_call(call, NFS_RPC_CALL_NONE);
00674     }
00675     else {
00676         /* select a thread from the general thread pool */
00677         int32_t thrd_ix;
00678         nfs_worker_data_t *worker;
00679 
00680         thrd_ix = nfs_core_select_worker_queue( WORKER_INDEX_ANY );
00681         worker = &workers_data[thrd_ix];
00682 
00683         LogFullDebug(COMPONENT_NFS_CB,
00684                      "Use request from Worker Thread #%u's pool, thread has %d "
00685                      "pending requests",
00686                      thrd_ix,
00687                      worker->pending_request_len);
00688 
00689         pnfsreq = nfs_rpc_get_nfsreq(worker, 0 /* flags */);
00690         pthread_mutex_lock(&call->we.mtx);
00691         call->states = NFS_CB_CALL_QUEUED;
00692         pnfsreq->rtype = NFS_CALL;
00693         pnfsreq->r_u.call = call;
00694         DispatchWorkNFS(pnfsreq, thrd_ix);
00695         pthread_mutex_unlock(&call->we.mtx);
00696     }
00697 
00698     return (code);
00699 }
00700 
00701 int32_t
00702 nfs_rpc_dispatch_call(rpc_call_t *call, uint32_t flags)
00703 {
00704     int code = 0;
00705     struct timeval CB_TIMEOUT = {15, 0}; /* XXX */
00706 
00707     /* send the call, set states, wake waiters, etc */
00708     pthread_mutex_lock(&call->we.mtx);
00709 
00710     switch (call->states) {
00711     case NFS_CB_CALL_DISPATCH:
00712     case NFS_CB_CALL_FINISHED:
00713         /* XXX invalid entry states for nfs_rpc_dispatch_call */
00714         abort();
00715     }
00716 
00717     call->states = NFS_CB_CALL_DISPATCH;
00718     pthread_mutex_unlock(&call->we.mtx);
00719 
00720     /* XXX TI-RPC does the signal masking */
00721     pthread_mutex_lock(&call->chan->mtx);
00722 
00723     if (! call->chan->clnt) {
00724         call->stat = RPC_INTR;
00725         goto unlock;
00726     }
00727 
00728     call->stat = clnt_call(call->chan->clnt,
00729                            CB_COMPOUND,
00730                            (xdrproc_t) xdr_CB_COMPOUND4args,
00731                            &call->cbt.v_u.v4.args,
00732                            (xdrproc_t) xdr_CB_COMPOUND4res,
00733                            &call->cbt.v_u.v4.res,
00734                            CB_TIMEOUT);
00735 
00736     /* If a call fails, we have to assume path down, or equally fatal
00737      * error.  We may need back-off. */
00738     if (call->stat != RPC_SUCCESS) {
00739         if (call->chan->clnt) {
00740             clnt_destroy(call->chan->clnt);
00741             call->chan->clnt = NULL;
00742         }
00743     }
00744 
00745 unlock:
00746     pthread_mutex_unlock(&call->chan->mtx);
00747 
00748     /* signal waiter(s) */
00749     pthread_mutex_lock(&call->we.mtx);
00750     call->states |= NFS_CB_CALL_FINISHED;
00751 
00752     /* broadcast will generally be inexpensive */
00753     if (call->flags & NFS_RPC_CALL_BROADCAST)
00754         pthread_cond_broadcast(&call->we.cv);
00755     pthread_mutex_unlock(&call->we.mtx);
00756 
00757     /* call completion hook */
00758     RPC_CALL_HOOK(call, RPC_CALL_COMPLETE, NULL, NFS_RPC_CALL_NONE);
00759 
00760     return (code);
00761 }
00762 
00763 int32_t
00764 nfs_rpc_abort_call(rpc_call_t *call)
00765 {
00766     return (0);
00767 }
00768