Old xdr_rdma.c
  1 /*
  2  * CDDL HEADER START
  3  *
  4  * The contents of this file are subject to the terms of the
  5  * Common Development and Distribution License, Version 1.0 only
  6  * (the "License").  You may not use this file except in compliance
  7  * with the License.
  8  *
  9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 10  * or http://www.opensolaris.org/os/licensing.
 11  * See the License for the specific language governing permissions
 12  * and limitations under the License.
 13  *
 14  * When distributing Covered Code, include this CDDL HEADER in each
 15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 16  * If applicable, add the following below this CDDL HEADER, with the
 17  * fields enclosed by brackets "[]" replaced with your own identifying
 18  * information: Portions Copyright [yyyy] [name of copyright owner]
 19  *
 20  * CDDL HEADER END
 21  */
 22 /*
 23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 24  * Use is subject to license terms.
 25  */
 26 
 27 #pragma ident   "@(#)xdr_rdma.c 1.4     05/06/08 SMI"
 28 
 29 /*
 30  * xdr_rdma.c, XDR implementation using RDMA to move large chunks
 31  */
 32 
 33 #include <sys/param.h>
 34 #include <sys/types.h>
 35 #include <sys/systm.h>
 36 #include <sys/kmem.h>
 37 
 38 #include <rpc/types.h>
 39 #include <rpc/xdr.h>
 40 #include <sys/cmn_err.h>
 41 #include <rpc/rpc_sztypes.h>
 42 #include <rpc/rpc_rdma.h>
 43 
 44 static struct xdr_ops *xdrrdma_ops(void);
 45 
 46 /*
 47  * A chunk list entry identifies a chunk
 48  * of opaque data to be moved separately
 49  * from the rest of the RPC message.
 50  * xp_min_chunk = 0, is a special case for ENCODING, which means
 51  * do not chunk the incoming stream of data.
 52  */
 53 
 54 struct private {
 55         caddr_t         xp_offp;
 56         int             xp_min_chunk;
 57         uint_t          xp_flags;       /* Controls setting for rdma xdr */
 58         int             xp_buf_size;            /* size of xdr buffer */
 59         struct clist    *xp_cl;                 /* head of chunk list */
 60         struct clist    **xp_cl_next;   /* location to place/find next chunk */
 61         CONN            *xp_conn;       /* connection for chunk data xfer */
 62 };
 63 
 64 
 65 /*
 66  * The procedure xdrrdma_create initializes a stream descriptor for a
 67  * memory buffer.
 68  */
 69 void
 70 xdrrdma_create(XDR *xdrs, caddr_t addr, uint_t size,
 71         int min_chunk, struct clist *cl, enum xdr_op op, CONN *conn)
 72 {
 73         struct private *xdrp;
 74         struct clist *cle;
 75 
 76         xdrs->x_op = op;
 77         xdrs->x_ops = xdrrdma_ops();
 78         xdrs->x_base = addr;
 79         xdrs->x_handy = size;
 80         xdrs->x_public = NULL;
 81 
 82         xdrp = (struct private *)kmem_zalloc(sizeof (struct private), KM_SLEEP);
 83         xdrs->x_private = (caddr_t)xdrp;
 84         xdrp->xp_offp = addr;
 85         xdrp->xp_min_chunk = min_chunk;
 86         xdrp->xp_flags = 0;
 87         xdrp->xp_buf_size = size;
 88         xdrp->xp_cl = cl;
 89         if (op == XDR_ENCODE && cl != NULL) {
 90                 /* Find last element in chunk list and set xp_cl_next */
 91                 for (cle = cl; cle->c_next != NULL; cle = cle->c_next);
 92                 xdrp->xp_cl_next = &(cle->c_next);
 93         } else
 94                 xdrp->xp_cl_next = &(xdrp->xp_cl);
 95         xdrp->xp_conn = conn;
 96         if (xdrp->xp_min_chunk == 0)
 97                 xdrp->xp_flags |= RDMA_NOCHUNK;
 98 }
 99 
100 /* ARGSUSED */
101 void
102 xdrrdma_destroy(XDR *xdrs)
103 {
104         (void) kmem_free(xdrs->x_private, sizeof (struct private));
105 }
106 
107 struct clist *
108 xdrrdma_clist(XDR *xdrs) {
109         return (((struct private *)(xdrs->x_private))->xp_cl);
110 }
111 
112 static bool_t
113 xdrrdma_getint32(XDR *xdrs, int32_t *int32p)
114 {
115         struct private *xdrp = (struct private *)(xdrs->x_private);
116 
117         if ((xdrs->x_handy -= (int)sizeof (int32_t)) < 0)
118                 return (FALSE);
119 
120         /* LINTED pointer alignment */
121         *int32p = (int32_t)ntohl((uint32_t)(*((int32_t *)(xdrp->xp_offp))));
122         xdrp->xp_offp += sizeof (int32_t);
123 
124         return (TRUE);
125 }
126 
127 static bool_t
128 xdrrdma_putint32(XDR *xdrs, int32_t *int32p)
129 {
130         struct private *xdrp = (struct private *)(xdrs->x_private);
131 
132         if ((xdrs->x_handy -= (int)sizeof (int32_t)) < 0)
133                 return (FALSE);
134 
135         /* LINTED pointer alignment */
136         *(int32_t *)xdrp->xp_offp = (int32_t)htonl((uint32_t)(*int32p));
137         xdrp->xp_offp += sizeof (int32_t);
138 
139         return (TRUE);
140 }
141 
142 /*
143  * DECODE some bytes from an XDR stream
144  */
145 static bool_t
146 xdrrdma_getbytes(XDR *xdrs, caddr_t addr, int len)
147 {
148         struct private *xdrp = (struct private *)(xdrs->x_private);
149         struct clist *cle = *(xdrp->xp_cl_next);
150         struct clist cl;
151         bool_t  retval = TRUE;
152 
153         /*
154          * If there was a chunk at the current offset
155          * first record the destination address and length
156          * in the chunk list that came with the message, then
157          * RDMA READ the chunk data.
158          */
159         if (cle != NULL &&
160                 cle->c_xdroff == (xdrp->xp_offp - xdrs->x_base)) {
161                 cle->c_daddr = (uint64)(uintptr_t)addr;
162                 cle->c_len  = len;
163                 xdrp->xp_cl_next = &cle->c_next;
164 
165                 /*
166                  * RDMA READ the chunk data from the remote end.
167                  * First prep the destination buffer by registering
168                  * it, then RDMA READ the chunk data. Since we are
169                  * doing streaming memory, sync the destination buffer
170                  * to CPU and deregister the buffer.
171                  */
172                 if (xdrp->xp_conn == NULL) {
173                         return (FALSE);
174                 }
175 
176                 cl = *cle;
177                 cl.c_next = NULL;
178                 if (clist_register(xdrp->xp_conn, &cl, 0) != RDMA_SUCCESS) {
179                         return (FALSE);
180                 }
181 
182                 /*
183                  * Now read the chunk in
184                  */
185                 if (RDMA_READ(xdrp->xp_conn, &cl, WAIT) != RDMA_SUCCESS) {
186 #ifdef DEBUG
187                         cmn_err(CE_WARN,
188                                 "xdrrdma_getbytes: RDMA_READ failed\n");
189 #endif
190                         retval = FALSE;
191                         goto out;
192                 }
193                 /*
194                  * sync the memory for cpu
195                  */
196                 if (clist_syncmem(xdrp->xp_conn, &cl, 0) != RDMA_SUCCESS) {
197                         retval = FALSE;
198                         goto out;
199                 }
200 
201 out:
202                 /*
203                  * Deregister the chunks
204                  */
205                 (void) clist_deregister(xdrp->xp_conn, &cl, 0);
206                 return (retval);
207         }
208 
209         if ((xdrs->x_handy -= len) < 0)
210                 return (FALSE);
211 
212         bcopy(xdrp->xp_offp, addr, len);
213         xdrp->xp_offp += len;
214 
215         return (TRUE);
216 }
217 
218 /*
219  * ENCODE some bytes into an XDR stream
220  * xp_min_chunk = 0, means the stream of bytes contain no chunks
221  * to seperate out, and if the bytes do not fit in the supplied
222  * buffer, grow the buffer and free the old buffer.
223  */
224 static bool_t
225 xdrrdma_putbytes(XDR *xdrs, caddr_t addr, int len)
226 {
227         struct private *xdrp = (struct private *)(xdrs->x_private);
228         struct clist *clzero = xdrp->xp_cl;
229 
230         /*
231          * If this chunk meets the minimum chunk size
232          * then don't encode it.  Just record its address
233          * and length in a chunk list entry so that it
234          * can be moved separately via RDMA.
235          */
236         if (!(xdrp->xp_flags & RDMA_NOCHUNK) && xdrp->xp_min_chunk != 0 &&
237             len >= xdrp->xp_min_chunk) {
238                 struct clist *cle;
239                 int offset = xdrp->xp_offp - xdrs->x_base;
240 
241                 cle = (struct clist *)kmem_zalloc(sizeof (struct clist),
242                                 KM_SLEEP);
243                 cle->c_xdroff = offset;
244                 cle->c_len  = len;
245                 cle->c_saddr = (uint64)(uintptr_t)addr;
246                 cle->c_next = NULL;
247 
248                 *(xdrp->xp_cl_next) = cle;
249                 xdrp->xp_cl_next = &(cle->c_next);
250 
251                 return (TRUE);
252         }
253 
254         if ((xdrs->x_handy -= len) < 0) {
255                 if (xdrp->xp_min_chunk == 0) {
256                         int  newbuflen, encodelen;
257                         caddr_t newbuf;
258 
259                         xdrs->x_handy += len;
260                         encodelen = xdrp->xp_offp - xdrs->x_base;
261                         newbuflen = xdrp->xp_buf_size + len;
262                         newbuf = kmem_zalloc(newbuflen, KM_SLEEP);
263                         bcopy(xdrs->x_base, newbuf, encodelen);
264                         (void) kmem_free(xdrs->x_base, xdrp->xp_buf_size);
265                         xdrs->x_base = newbuf;
266                         xdrp->xp_offp = newbuf + encodelen;
267                         xdrp->xp_buf_size = newbuflen;
268                         if (xdrp->xp_min_chunk == 0 && clzero->c_xdroff == 0) {
269                                 clzero->c_len = newbuflen;
270                                 clzero->c_saddr = (uint64)(uintptr_t)newbuf;
271                         }
272                 } else
273                         return (FALSE);
274         }
275 
276         bcopy(addr, xdrp->xp_offp, len);
277         xdrp->xp_offp += len;
278 
279         return (TRUE);
280 }
281 
282 uint_t
283 xdrrdma_getpos(XDR *xdrs)
284 {
285         struct private *xdrp = (struct private *)(xdrs->x_private);
286 
287         return ((uint_t)((uintptr_t)xdrp->xp_offp - (uintptr_t)xdrs->x_base));
288 }
289 
290 bool_t
291 xdrrdma_setpos(XDR *xdrs, uint_t pos)
292 {
293         struct private *xdrp = (struct private *)(xdrs->x_private);
294 
295         caddr_t newaddr = xdrs->x_base + pos;
296         caddr_t lastaddr = xdrp->xp_offp + xdrs->x_handy;
297         ptrdiff_t diff;
298 
299         if (newaddr > lastaddr)
300                 return (FALSE);
301 
302         xdrp->xp_offp = newaddr;
303         diff = lastaddr - newaddr;
304         xdrs->x_handy = (int)diff;
305 
306         return (TRUE);
307 }
308 
309 /* ARGSUSED */
310 static rpc_inline_t *
311 xdrrdma_inline(XDR *xdrs, int len)
312 {
313         rpc_inline_t *buf = NULL;
314         struct private *xdrp = (struct private *)(xdrs->x_private);
315         struct clist *cle = *(xdrp->xp_cl_next);
316 
317         if (xdrs->x_op == XDR_DECODE) {
318                 /*
319                  * Since chunks aren't in-line, check to see whether
320                  * there is a chunk in the inline range.
321                  */
322                 if (cle != NULL &&
323                         cle->c_xdroff <= (xdrp->xp_offp - xdrs->x_base + len))
324                 return (NULL);
325         }
326 
327         if ((xdrs->x_handy < len) || (xdrp->xp_min_chunk != 0 &&
328             len >= xdrp->xp_min_chunk)) {
329                 return (NULL);
330         } else {
331                 xdrs->x_handy -= len;
332                 /* LINTED pointer alignment */
333                 buf = (rpc_inline_t *)xdrp->xp_offp;
334                 xdrp->xp_offp += len;
335                 return (buf);
336         }
337 }
338 
339 static bool_t
340 xdrrdma_control(XDR *xdrs, int request, void *info)
341 {
342         int32_t *int32p;
343         int len;
344         uint_t in_flags;
345         struct private *xdrp = (struct private *)(xdrs->x_private);
346 
347         switch (request) {
348         case XDR_PEEK:
349                 /*
350                  * Return the next 4 byte unit in the XDR stream.
351                  */
352                 if (xdrs->x_handy < sizeof (int32_t))
353                         return (FALSE);
354 
355                 int32p = (int32_t *)info;
356                 *int32p = (int32_t)ntohl((uint32_t)
357                     (*((int32_t *)(xdrp->xp_offp))));
358 
359                 return (TRUE);
360 
361         case XDR_SKIPBYTES:
362                 /*
363                  * Skip the next N bytes in the XDR stream.
364                  */
365                 int32p = (int32_t *)info;
366                 len = RNDUP((int)(*int32p));
367                 if ((xdrs->x_handy -= len) < 0)
368                         return (FALSE);
369                 xdrp->xp_offp += len;
370 
371                 return (TRUE);
372 
373         case XDR_RDMASET:
374                 /*
375                  * Set the flags provided in the *info in xp_flags for rdma xdr
376                  * stream control.
377                  */
378                 int32p = (int32_t *)info;
379                 in_flags = (uint_t)(*int32p);
380 
381                 xdrp->xp_flags |= in_flags;
382                 return (TRUE);
383 
384         case XDR_RDMAGET:
385                 /*
386                  * Get the flags provided in xp_flags return through *info
387                  */
388                 int32p = (int32_t *)info;
389 
390                 *int32p = (int32_t)xdrp->xp_flags;
391                 return (TRUE);
392 
393         default:
394                 return (FALSE);
395         }
396 }
397 
398 static struct xdr_ops *
399 xdrrdma_ops(void)
400 {
401         static struct xdr_ops ops;
402 
403         if (ops.x_getint32 == NULL) {
404                 ops.x_getbytes = xdrrdma_getbytes;
405                 ops.x_putbytes = xdrrdma_putbytes;
406                 ops.x_getpostn = xdrrdma_getpos;
407                 ops.x_setpostn = xdrrdma_setpos;
408                 ops.x_inline = xdrrdma_inline;
409                 ops.x_destroy = xdrrdma_destroy;
410                 ops.x_control = xdrrdma_control;
411                 ops.x_getint32 = xdrrdma_getint32;
412                 ops.x_putint32 = xdrrdma_putint32;
413         }
414         return (&ops);
415 }
416 
417 /*
418  * Not all fields in struct clist are interesting to the
419  * RPC over RDMA protocol. Only XDR the interesting fields.
420  */
421 bool_t
422 xdr_clist(XDR *xdrs, clist *objp)
423 {
424 
425         if (!xdr_uint32(xdrs, &objp->c_xdroff))
426                 return (FALSE);
427         if (!xdr_uint32(xdrs, &objp->c_len))
428                 return (FALSE);
429         if (!xdr_uint32(xdrs, &objp->c_smemhandle.mrc_rmr))
430                 return (FALSE);
431         if (!xdr_uint64(xdrs, &objp->c_saddr))
432                 return (FALSE);
433         if (!xdr_pointer(xdrs, (char **)&objp->c_next, sizeof (clist),
434                 (xdrproc_t)xdr_clist))
435                 return (FALSE);
436         return (TRUE);
437 }
438 
439 bool_t
440 xdr_do_clist(XDR *xdrs, clist **clp)
441 {
442         return (xdr_pointer(xdrs, (char **)clp,
443                 sizeof (clist), (xdrproc_t)xdr_clist));
444 }
445 
446 uint_t
447 xdr_getbufsize(XDR *xdrs)
448 {
449         struct private *xdrp = (struct private *)(xdrs->x_private);
450 
451         return ((uint_t)xdrp->xp_buf_size);
452 }