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 }
299