/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Portions of this source code were derived from Berkeley
* 4.3 BSD under license from the Regents of the University of
* California.
*/
#include <sys/sysmacros.h>
#include <sys/isa_defs.h>
#include <rpc/rpc_rdma.h>
#include <nfs/nfs4_kprot.h>
static void clnt_check_credit(CONN *);
static void clnt_return_credit(CONN *);
static void clnt_rdma_kabort(CLIENT *);
static void clnt_rdma_kdestroy(CLIENT *);
/*
* Operations vector for RDMA based RPC
*/
clnt_rdma_kcallit, /* do rpc call */
clnt_rdma_kabort, /* abort call */
clnt_rdma_kerror, /* return error status */
clnt_rdma_kfreeres, /* free results */
clnt_rdma_kdestroy, /* destroy rpc handle */
clnt_rdma_kcontrol, /* the ioctl() of rpc */
clnt_rdma_ksettimers, /* set retry timers */
};
/*
* The size of the preserialized RPC header information.
*/
#define CLNT_RDMA_SUCCESS 0
/*
* Per RPC RDMA endpoint details
*/
typedef struct cku_private {
struct {
} rdmarcstat = {
{ "calls", KSTAT_DATA_UINT64 },
{ "badcalls", KSTAT_DATA_UINT64 },
{ "badxids", KSTAT_DATA_UINT64 },
{ "timeouts", KSTAT_DATA_UINT64 },
{ "newcreds", KSTAT_DATA_UINT64 },
{ "badverfs", KSTAT_DATA_UINT64 },
{ "timers", KSTAT_DATA_UINT64 },
{ "cantconn", KSTAT_DATA_UINT64 },
{ "nomem", KSTAT_DATA_UINT64 },
{ "interrupts", KSTAT_DATA_UINT64 },
{ "longrpc", KSTAT_DATA_UINT64 }
};
#ifdef DEBUG
int rdma_clnt_debug = 0;
#endif
#ifdef accurate_stats
#define RCSTAT_INCR(x) \
#else
#define RCSTAT_INCR(x) \
#endif
{
} else {
} else {
} else {
}
}
}
}
return (len);
}
int
{
CLIENT *h;
struct cku_private *p;
return (EINVAL);
p = kmem_zalloc(sizeof (*p), KM_SLEEP);
/*
* Find underlying RDMATF plugin
*/
rp = rdma_mod_head;
else {
p->cku_rd_handle = handle;
break;
}
}
if (p->cku_rd_mod == NULL) {
/*
* Should not happen.
* No matching RDMATF plugin.
*/
kmem_free(p, sizeof (struct cku_private));
return (EINVAL);
}
h = ptoh(p);
h->cl_ops = &rdma_clnt_ops;
h->cl_private = (caddr_t)p;
h->cl_auth = authkern_create();
/* call message, just used to pre-serialize below */
/* pre-serialize call message header */
XDR_DESTROY(&p->cku_outxdr);
auth_destroy(h->cl_auth);
kmem_free(p, sizeof (struct cku_private));
return (EINVAL);
}
/*
* Set up the rpc information
*/
p->cku_srcaddr.len = 0;
p->cku_addrfmly = family;
*cl = h;
return (0);
}
static void
{
struct cku_private *p = htop(h);
kmem_free(p, sizeof (*p));
}
void
{
struct cku_private *p = htop(h);
/*
* Find underlying RDMATF plugin
*/
p->cku_rd_mod = NULL;
rp = rdma_mod_head;
else {
p->cku_rd_handle = handle;
break;
}
}
/*
* Set up the rpc information
*/
p->cku_xid = 0;
}
p->cku_srcaddr.len = 0;
h->cl_ops = &rdma_clnt_ops;
}
static int
{
cku_private_t *p = htop(h);
/*
* Copy in the preserialized RPC header
* information.
*/
/*
* transaction id is the 1st thing in the output
* buffer.
*/
/* LINTED pointer alignment */
/* Skip the preserialized stuff. */
/* Serialize dynamic stuff into the output buffer. */
return (CLNT_RDMA_FAIL);
}
} else {
XDR_SETPOS(xdrs, 0);
/* Serialize the procedure number and the arguments. */
}
return (CLNT_RDMA_FAIL);
}
/*
* If we had to allocate a new buffer while encoding
* then update the addr and len.
*/
}
}
return (CLNT_RDMA_SUCCESS);
}
static int
{
cku_private_t *p = htop(h);
vers = RPCRDMA_VERS;
return (CLNT_RDMA_FAIL);
}
*xdrs = &p->cku_outxdr;
return (CLNT_RDMA_SUCCESS);
}
/*
* If xp_cl is NULL value, then the RPC payload will NOT carry
* an RDMA READ chunk list, in this case we insert FALSE into
* the XDR stream. Otherwise we use the clist and RDMA register
* the memory and encode the clist into the outbound XDR stream.
*/
static int
{
int status;
if (status != RDMA_SUCCESS) {
return (CLNT_RDMA_FAIL);
}
}
return (CLNT_RDMA_SUCCESS);
}
/*
* If xp_wcl is NULL value, then the RPC payload will NOT carry
* an RDMA WRITE chunk list, in this case we insert FALSE into
* the XDR stream. Otherwise we use the clist and RDMA register
* the memory and encode the clist into the outbound XDR stream.
*/
static int
{
int status;
/*
* If we are sending a non 4-byte alligned length
* the server will roundup the length to 4-byte
* boundary. In such a case, a trailing chunk is
* added to take any spill over roundup bytes.
*/
if (rndlen) {
rndcl = clist_alloc();
/*
* calc_length() will allocate a PAGESIZE
* buffer below.
*/
return (CLNT_RDMA_FAIL);
}
/* Roundup buffer freed back in caller */
}
if (status != RDMA_SUCCESS) {
return (CLNT_RDMA_FAIL);
}
}
if (rndlen) {
}
return (CLNT_RDMA_FAIL);
}
return (CLNT_RDMA_SUCCESS);
}
static int
{
if (length == 0) {
return (CLNT_RDMA_SUCCESS);
}
*clpp = clist_alloc();
clist_free(*clpp);
return (CLNT_RDMA_FAIL);
}
clist_free(*clpp);
return (CLNT_RDMA_FAIL);
}
return (CLNT_RDMA_SUCCESS);
}
/* ARGSUSED */
static enum clnt_stat
{
cku_private_t *p = htop(h);
int try_call_again;
int status;
int msglen;
rdma_registry_t *m;
try_call_again = 0;
cl_sendlist = NULL;
cl_recvlist = NULL;
reply_xdrp = NULL;
rcil.rcil_len_alt = 0;
long_reply_len = 0;
m = (rdma_registry_t *)p->cku_rd_handle;
if (m->r_mod_state == RDMA_MOD_INACTIVE) {
/*
* If we didn't find a matching RDMA module in the registry
* then there is no transport.
*/
if (h->cl_nosignal == TRUE) {
} else {
}
}
return (RPC_CANTSEND);
}
/*
* Get unique xid
*/
if (p->cku_xid == 0)
/*
* If there is a problem with the connection reflect the issue
* back to the higher level to address, we MAY delay for a short
* period so that we are kind to the transport.
*/
/*
* Connect failed to server. Could be because of one
* of several things. In some cases we don't want
* the caller to retry immediately - delay before
* returning to caller.
*/
switch (status) {
case RDMA_TIMEDOUT:
/*
* Already timed out. No need to delay
* some more.
*/
break;
case RDMA_INTR:
/*
* Failed because of an signal. Very likely
* the caller will not retry.
*/
break;
default:
/*
* All other failures - server down or service
* down or temporary resource failure. Delay before
* returning to caller.
*/
if (h->cl_nosignal == TRUE) {
} else {
}
}
break;
}
}
if ((p->cku_srcaddr.maxlen != 0) &&
KM_SLEEP);
}
if (IS_RPCSEC_GSS(h)) {
rpcsec_gss = TRUE;
if (rpc_gss_get_service_type(h->cl_auth) ==
rpc_gss_get_service_type(h->cl_auth) ==
gss_i_or_p = TRUE;
}
/*
* Try a regular RDMA message if RPCSEC_GSS is not being used
* or if RPCSEC_GSS is being used for authentication only.
*/
if (rpcsec_gss == FALSE ||
/*
* Grab a send buffer for the request. Try to
* encode it to see if it fits. If not, then it
* needs to be sent in a chunk.
*/
goto done;
}
/* First try to encode into regular send buffer */
if (status != CLNT_RDMA_SUCCESS) {
/* Clean up from previous encode attempt */
} else {
}
}
/* If the encode didn't work, then try a NOMSG */
if (status != CLNT_RDMA_SUCCESS) {
/* pick up the lengths for the reply buffer needed */
/*
* Construct a clist to describe the CHUNK_BUFFER
* for the rpcmsg.
*/
cl_rpcmsg = clist_alloc();
goto done;
}
op = RDMA_NOMSG;
int, msglen);
if (status != CLNT_RDMA_SUCCESS) {
goto done;
}
}
/*
* During the XDR_ENCODE we may have "allocated" an RDMA READ or
* RDMA WRITE clist.
*
* First pull the RDMA READ chunk list from the XDR private
* area to keep it handy.
*/
if (gss_i_or_p) {
} else {
}
/*
* Update the chunk size information for the Long RPC msg.
*/
/*
* Prepare the RDMA header. On success xdrs will hold the result
* of xdrmem_create() for a SEND_BUFFER.
*/
&rdmahdr_o_xdrs, &op);
if (status != CLNT_RDMA_SUCCESS) {
goto done;
}
/*
* Now insert the RDMA READ list iff present
*/
if (status != CLNT_RDMA_SUCCESS) {
goto done;
}
/*
* Setup RDMA WRITE chunk list for nfs read operation
* other operations will have a NULL which will result
* as a NULL list in the XDR stream.
*/
if (status != CLNT_RDMA_SUCCESS) {
goto done;
}
/*
* If NULL call and RPCSEC_GSS, provide a chunk such that
* large responses can flow back to the client.
* If RPCSEC_GSS with integrity or privacy is in use, get chunk.
*/
long_reply_len += 1024;
int, long_reply_len);
if (status != CLNT_RDMA_SUCCESS) {
goto done;
}
/*
* XDR encode the RDMA_REPLY write chunk
*/
/*
* Construct a clist in "sendlist" that represents what we
* will push over the wire.
*
* Start with the RDMA header and clist (if any)
*/
/*
* Put the RPC call message in sendlist if small RPC
*/
} else {
/* Long RPC already in chunk list */
}
/*
* Set up a reply buffer ready for the reply
*/
if (status != RDMA_SUCCESS) {
goto done;
}
/*
* sync the memory for dma
*/
if (status != RDMA_SUCCESS) {
goto done;
}
}
/*
* Send the RDMA Header and RPC call message to the server
*/
if (status != RDMA_SUCCESS) {
goto done;
}
/*
* RDMA plugin now owns the send msg buffers.
* Clear them out and don't free them.
*/
/*
* Recv rpc reply
*/
/*
* Now check recv status
*/
if (status != 0) {
} else if (status == RPC_TIMEDOUT) {
} else {
}
goto done;
}
/*
* Process the reply message.
*
* First the chunk list (if any)
*/
rdmahdr_i_xdrs = &(p->cku_inxdr);
/*
* Treat xid as opaque (xid is the first entity
* in the rpc rdma message).
* Skip xid and set the xdr position accordingly.
*/
&wlist_exists_reply)) {
goto done;
}
/*
* The server shouldn't have sent a RDMA_SEND that
* the client needs to RDMA_WRITE a reply back to
* the server. So silently ignoring what the
* server returns in the rdma_reply section of the
* header.
*/
if (reply_xdrp == NULL)
goto done;
if (wlist_exists_reply) {
}
/*
* xdr_results will be done in AUTH_UNWRAP.
*/
/*
* Decode and validate the response.
*/
if (re_status == RPC_SUCCESS) {
/*
* Reply is good, check auth.
*/
if (!AUTH_VALIDATE(h->cl_auth,
xdr_results, resultsp)) {
}
} else {
/* set errno in case we can't recover */
if (re_status != RPC_VERSMISMATCH &&
re_status != RPC_AUTHERROR &&
if (re_status == RPC_AUTHERROR) {
if ((refresh_attempt > 0) &&
p->cku_cred)) {
try_call_again = 1;
goto done;
}
try_call_again = 0;
/*
* We have used the client handle to
* do an AUTH_REFRESH and the RPC status may
* be set to RPC_SUCCESS; Let's make sure to
* set it to RPC_AUTHERROR.
*/
/*
* Map recoverable and unrecoverable
* authentication errors to appropriate
* errno
*/
case AUTH_BADCRED:
case AUTH_BADVERF:
case AUTH_INVALIDRESP:
case AUTH_TOOWEAK:
case AUTH_FAILED:
case RPCSEC_GSS_NOCRED:
case RPCSEC_GSS_FAILED:
break;
case AUTH_REJECTEDCRED:
case AUTH_REJECTEDVERF:
default:
break;
}
}
}
} else {
}
done:
if (cl_sendlist != NULL)
/*
* If rpc reply is in a chunk, free it now.
*/
if (cl_long_reply) {
}
if (call_xdrp)
if (rndup.rb_private) {
}
if (reply_xdrp) {
}
if (cl_rdma_reply) {
}
if (cl_recvlist) {
}
if (try_call_again)
goto call_again;
}
}
static void
struct clist *cl_long_reply,
struct clist *cl_recvlist,
{
if (op != RDMA_NOMSG) {
return;
}
/* op must be RDMA_NOMSG */
if (cl) {
return;
}
if (cl_long_reply->u.c_daddr) {
int, cl_rdma_reply->c_len);
}
}
static void
{
}
static void
{
/*
* If the granted has not altered, avoid taking the
* mutex, to essentially do nothing..
*/
return;
/*
* Get the granted number of buffers for credit control.
*/
}
static void
{
/*
* Make sure we are not going over our allowed buffer use
* (and make sure we have gotten a granted value before).
*/
cc_info->clnt_cc_granted_ops != 0) {
/*
* Client has maxed out its granted buffers due to
* credit control. Current handling is to block and wait.
*/
}
}
/* ARGSUSED */
static void
{
}
static void
{
struct cku_private *p = htop(h);
}
static bool_t
{
struct cku_private *p = htop(h);
xdrs = &(p->cku_outxdr);
}
/* ARGSUSED */
static bool_t
{
return (TRUE);
}
/* ARGSUSED */
static int
{
return (0);
}
int
{
char *pf, *p;
int error = 0;
if (!INGLOBALZONE(curproc))
return (-1);
/*
* modload the RDMA plugins if not already done.
*/
if (!rdma_modloaded) {
if (!rdma_modloaded) {
error = rdma_modload();
}
if (error)
return (-1);
}
if (!rdma_dev_available)
return (-1);
rp = rdma_mod_head;
continue;
}
&handle);
if (status == RDMA_SUCCESS) {
KM_SLEEP);
return (0);
}
}
return (-1);
}