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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <rpc/types.h>
  27 #include <rpc/xdr.h>
  28 #include <sys/types.h>
  29 #include <sys/sdt.h>
  30 #include <rpc/auth.h>
  31 #include <rpc/rpc_rdma.h>
  32 
  33 struct private {
  34         int     min_chunk;
  35         uint_t  flags;                  /* controls setting for rdma xdr */
  36         int     num_chunk;
  37         caddr_t inline_buf;             /* temporary buffer for xdr inlining */
  38         int     inline_len;             /* inline buffer length */
  39         uint_t  xp_reply_chunk_len;
  40         uint_t  xp_reply_chunk_len_alt;
  41 };
  42 
  43 /* ARGSUSED */
  44 static bool_t
  45 x_putint32_t(XDR *xdrs, int32_t *ip)
  46 {
  47         xdrs->x_handy += BYTES_PER_XDR_UNIT;
  48         return (TRUE);
  49 }
  50 
  51 /* ARGSUSED */
  52 static bool_t
  53 x_putbytes(XDR *xdrs, char *bp, int len)
  54 {
  55         struct private *xdrp = (struct private *)xdrs->x_private;
  56 
  57         /*
  58          * min_chunk = 0, means that the stream of bytes, to estimate size of,
  59          * contains no chunks to seperate out. See xdrrdma_putbytes()
  60          */
  61         if (len < xdrp->min_chunk || !(xdrp->flags & XDR_RDMA_CHUNK)) {
  62                 xdrs->x_handy += len;
  63                 return (TRUE);
  64         }
  65         /*
  66          * Chunk item. No impact on xdr size.
  67          */
  68         xdrp->num_chunk++;
  69 
  70         return (TRUE);
  71 }
  72 
  73 static uint_t
  74 x_getpostn(XDR *xdrs)
  75 {
  76         return (xdrs->x_handy);
  77 }
  78 
  79 /* ARGSUSED */
  80 static bool_t
  81 x_setpostn(XDR *xdrs, uint_t pos)
  82 {
  83         /* This is not allowed */
  84         return (FALSE);
  85 }
  86 
  87 /* ARGSUSED */
  88 static bool_t
  89 x_control(XDR *xdrs, int request, void *info)
  90 {
  91         int32_t *int32p;
  92         uint_t in_flags;
  93         rdma_chunkinfo_t *rcip = NULL;
  94         rdma_chunkinfo_lengths_t *rcilp = NULL;
  95         struct private *xdrp = (struct private *)xdrs->x_private;
  96 
  97         switch (request) {
  98         case XDR_RDMA_SET_FLAGS:
  99                 /*
 100                  * Set the flags provided in the *info in xp_flags for rdma xdr
 101                  * stream control.
 102                  */
 103                 int32p = (int32_t *)info;
 104                 in_flags = (uint_t)(*int32p);
 105 
 106                 xdrp->flags = in_flags;
 107                 return (TRUE);
 108 
 109         case XDR_RDMA_GET_FLAGS:
 110                 /*
 111                  * Get the flags provided in xp_flags return through *info
 112                  */
 113                 int32p = (int32_t *)info;
 114 
 115                 *int32p = (int32_t)xdrp->flags;
 116                 return (TRUE);
 117 
 118         case XDR_RDMA_GET_CHUNK_LEN:
 119                 rcilp = (rdma_chunkinfo_lengths_t *)info;
 120                 rcilp->rcil_len = xdrp->xp_reply_chunk_len;
 121                 rcilp->rcil_len_alt = xdrp->xp_reply_chunk_len_alt;
 122 
 123                 return (TRUE);
 124 
 125         case XDR_RDMA_ADD_CHUNK:
 126                 rcip = (rdma_chunkinfo_t *)info;
 127 
 128                 switch (rcip->rci_type) {
 129                 case RCI_WRITE_UIO_CHUNK:
 130                         xdrp->xp_reply_chunk_len_alt += rcip->rci_len;
 131                         break;
 132 
 133                 case RCI_WRITE_ADDR_CHUNK:
 134                         xdrp->xp_reply_chunk_len_alt += rcip->rci_len;
 135                         break;
 136 
 137                 case RCI_REPLY_CHUNK:
 138                         xdrp->xp_reply_chunk_len += rcip->rci_len;
 139                         break;
 140                 }
 141                 return (TRUE);
 142 
 143         default:
 144                 return (FALSE);
 145         }
 146 }
 147 
 148 /* ARGSUSED */
 149 static rpc_inline_t *
 150 x_inline(XDR *xdrs, int len)
 151 {
 152         struct private *xdrp = (struct private *)xdrs->x_private;
 153 
 154         if (len == 0) {
 155                 return (NULL);
 156         }
 157         if (xdrs->x_op != XDR_ENCODE) {
 158                 return (NULL);
 159         }
 160         if (len >= xdrp->min_chunk) {
 161                 return (NULL);
 162         }
 163         if (len <= xdrp->inline_len) {
 164                 /* inline_buf was already allocated, just reuse it */
 165                 xdrs->x_handy += len;
 166                 return ((rpc_inline_t *)xdrp->inline_buf);
 167         } else {
 168                 /* Free the earlier space and allocate new area */
 169                 if (xdrp->inline_buf)
 170                         mem_free(xdrp->inline_buf, xdrp->inline_len);
 171                 if ((xdrp->inline_buf = (caddr_t)mem_alloc(len)) == NULL) {
 172                         xdrp->inline_len = 0;
 173                         return (NULL);
 174                 }
 175                 xdrp->inline_len = len;
 176                 xdrs->x_handy += len;
 177                 return ((rpc_inline_t *)xdrp->inline_buf);
 178         }
 179 }
 180 
 181 static int
 182 harmless()
 183 {
 184         /* Always return FALSE/NULL, as the case may be */
 185         return (0);
 186 }
 187 
 188 static void
 189 x_destroy(XDR *xdrs)
 190 {
 191         struct private *xdrp = (struct private *)xdrs->x_private;
 192 
 193         xdrs->x_handy = 0;
 194         if (xdrp) {
 195                 if (xdrp->inline_buf)
 196                         mem_free(xdrp->inline_buf, xdrp->inline_len);
 197                 mem_free(xdrp, sizeof (struct private));
 198                 xdrs->x_private = NULL;
 199         }
 200         xdrs->x_base = 0;
 201 }
 202 
 203 static bool_t
 204 xdrrdma_common(XDR *xdrs, int min_chunk)
 205 {
 206         struct private *xdrp;
 207 
 208         xdrs->x_ops = xdrrdma_xops();
 209         xdrs->x_op = XDR_ENCODE;
 210         xdrs->x_handy = 0;
 211         xdrs->x_base = NULL;
 212         xdrs->x_private = kmem_zalloc(sizeof (struct private), KM_SLEEP);
 213         xdrp = (struct private *)xdrs->x_private;
 214         xdrp->min_chunk = min_chunk;
 215         xdrp->flags = 0;
 216         if (xdrp->min_chunk != 0)
 217                 xdrp->flags |= XDR_RDMA_CHUNK;
 218 
 219         xdrp->xp_reply_chunk_len = 0;
 220         xdrp->xp_reply_chunk_len_alt = 0;
 221 
 222         return (TRUE);
 223 }
 224 
 225 unsigned int
 226 xdrrdma_sizeof(xdrproc_t func, void *data, int min_chunk,
 227     uint_t *reply_size, uint_t *reply_size_alt)
 228 {
 229         XDR x;
 230         struct xdr_ops ops;
 231         bool_t stat;
 232         struct private *xdrp;
 233 
 234         x.x_ops = &ops;
 235         (void) xdrrdma_common(&x, min_chunk);
 236 
 237         stat = func(&x, data);
 238         xdrp = (struct private *)x.x_private;
 239         if (xdrp) {
 240                 if (reply_size != NULL)
 241                         *reply_size = xdrp->xp_reply_chunk_len;
 242                 if (reply_size_alt != NULL)
 243                         *reply_size_alt = xdrp->xp_reply_chunk_len_alt;
 244                 if (xdrp->inline_buf)
 245                         mem_free(xdrp->inline_buf, xdrp->inline_len);
 246                 mem_free(xdrp, sizeof (struct private));
 247         }
 248         return (stat == TRUE ? (unsigned int)x.x_handy: 0);
 249 }
 250 
 251 unsigned int
 252 xdrrdma_authsize(AUTH *auth, struct cred *cred, int min_chunk)
 253 {
 254         XDR x;
 255         struct xdr_ops ops;
 256         bool_t stat;
 257         struct private *xdrp;
 258 
 259         x.x_ops = &ops;
 260         (void) xdrrdma_common(&x, min_chunk);
 261 
 262         stat = AUTH_MARSHALL(auth, &x, cred);
 263         xdrp = (struct private *)x.x_private;
 264         if (xdrp) {
 265                 if (xdrp->inline_buf)
 266                         mem_free(xdrp->inline_buf, xdrp->inline_len);
 267                 mem_free(xdrp, sizeof (struct private));
 268         }
 269         return (stat == TRUE ? (unsigned int)x.x_handy: 0);
 270 }
 271 
 272 struct xdr_ops *
 273 xdrrdma_xops(void)
 274 {
 275         static struct xdr_ops ops;
 276 
 277         /* to stop ANSI-C compiler from complaining */
 278         typedef  bool_t (* dummyfunc1)(XDR *, long *);
 279         typedef  bool_t (* dummyfunc2)(XDR *, caddr_t, int);
 280         typedef  bool_t (* dummyfunc3)(XDR *, int32_t *);
 281 
 282         ops.x_putbytes = x_putbytes;
 283         ops.x_inline = x_inline;
 284         ops.x_getpostn = x_getpostn;
 285         ops.x_setpostn = x_setpostn;
 286         ops.x_destroy = x_destroy;
 287         ops.x_control = x_control;
 288 
 289 #if defined(_LP64) || defined(_KERNEL)
 290         ops.x_getint32 = (dummyfunc3)harmless;
 291         ops.x_putint32 = x_putint32_t;
 292 #endif
 293 
 294         /* the other harmless ones */
 295         ops.x_getbytes = (dummyfunc2)harmless;
 296 
 297         return (&ops);
 298 }