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