xref: /illumos-gate/usr/src/uts/common/io/ib/clients/rdsv3/message.c (revision 3d78e6ab42c6ffc02ee9dbd101ff2551b77cb45f)
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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * Copyright (c) 2006 Oracle.  All rights reserved.
27  *
28  * This software is available to you under a choice of one of two
29  * licenses.  You may choose to be licensed under the terms of the GNU
30  * General Public License (GPL) Version 2, available from the file
31  * COPYING in the main directory of this source tree, or the
32  * OpenIB.org BSD license below:
33  *
34  *     Redistribution and use in source and binary forms, with or
35  *     without modification, are permitted provided that the following
36  *     conditions are met:
37  *
38  *      - Redistributions of source code must retain the above
39  *        copyright notice, this list of conditions and the following
40  *        disclaimer.
41  *
42  *      - Redistributions in binary form must reproduce the above
43  *        copyright notice, this list of conditions and the following
44  *        disclaimer in the documentation and/or other materials
45  *        provided with the distribution.
46  *
47  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
48  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
49  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
50  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
51  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
52  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
53  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
54  * SOFTWARE.
55  *
56  */
57 #include <sys/rds.h>
58 
59 #include <sys/ib/clients/rdsv3/rdsv3.h>
60 #include <sys/ib/clients/rdsv3/rdma.h>
61 #include <sys/ib/clients/rdsv3/rdsv3_debug.h>
62 
63 static rdsv3_wait_queue_t rdsv3_message_flush_waitq;
64 
65 #ifndef __lock_lint
66 static unsigned int	rdsv3_exthdr_size[__RDSV3_EXTHDR_MAX] = {
67 [RDSV3_EXTHDR_NONE]	= 0,
68 [RDSV3_EXTHDR_VERSION]	= sizeof (struct rdsv3_ext_header_version),
69 [RDSV3_EXTHDR_RDMA]	= sizeof (struct rdsv3_ext_header_rdma),
70 [RDSV3_EXTHDR_RDMA_DEST]	= sizeof (struct rdsv3_ext_header_rdma_dest),
71 };
72 #else
73 static unsigned int	rdsv3_exthdr_size[__RDSV3_EXTHDR_MAX] = {
74 			0,
75 			sizeof (struct rdsv3_ext_header_version),
76 			sizeof (struct rdsv3_ext_header_rdma),
77 			sizeof (struct rdsv3_ext_header_rdma_dest),
78 };
79 #endif
80 
81 void
82 rdsv3_message_addref(struct rdsv3_message *rm)
83 {
84 	RDSV3_DPRINTF5("rdsv3_message_addref", "addref rm %p ref %d",
85 	    rm, atomic_get(&rm->m_refcount));
86 	atomic_add_32(&rm->m_refcount, 1);
87 }
88 
89 /*
90  * This relies on dma_map_sg() not touching sg[].page during merging.
91  */
92 static void
93 rdsv3_message_purge(struct rdsv3_message *rm)
94 {
95 	unsigned long i;
96 
97 	RDSV3_DPRINTF4("rdsv3_message_purge", "Enter(rm: %p)", rm);
98 
99 	if (test_bit(RDSV3_MSG_PAGEVEC, &rm->m_flags))
100 		return;
101 
102 	for (i = 0; i < rm->m_nents; i++) {
103 		RDSV3_DPRINTF5("rdsv3_message_purge", "putting data page %p\n",
104 		    (void *)rdsv3_sg_page(&rm->m_sg[i]));
105 		/* XXX will have to put_page for page refs */
106 		kmem_free(rdsv3_sg_page(&rm->m_sg[i]),
107 		    rdsv3_sg_len(&rm->m_sg[i]));
108 	}
109 
110 	if (rm->m_rdma_op)
111 		rdsv3_rdma_free_op(rm->m_rdma_op);
112 	if (rm->m_rdma_mr) {
113 		struct rdsv3_mr *mr = rm->m_rdma_mr;
114 		if (mr->r_refcount == 0) {
115 			RDSV3_DPRINTF4("rdsv3_message_purge ASSERT 0",
116 			    "rm %p mr %p", rm, mr);
117 			return;
118 		}
119 		if (mr->r_refcount == 0xdeadbeef) {
120 			RDSV3_DPRINTF4("rdsv3_message_purge ASSERT deadbeef",
121 			    "rm %p mr %p", rm, mr);
122 			return;
123 		}
124 		if (atomic_dec_and_test(&mr->r_refcount)) {
125 			rm->m_rdma_mr = NULL;
126 			__rdsv3_put_mr_final(mr);
127 		}
128 	}
129 
130 	RDSV3_DPRINTF4("rdsv3_message_purge", "Return(rm: %p)", rm);
131 
132 }
133 
134 void
135 rdsv3_message_inc_purge(struct rdsv3_incoming *inc)
136 {
137 	struct rdsv3_message *rm =
138 	    container_of(inc, struct rdsv3_message, m_inc);
139 	rdsv3_message_purge(rm);
140 }
141 
142 void
143 rdsv3_message_put(struct rdsv3_message *rm)
144 {
145 	RDSV3_DPRINTF5("rdsv3_message_put",
146 	    "put rm %p ref %d\n", rm, atomic_get(&rm->m_refcount));
147 
148 	if (atomic_dec_and_test(&rm->m_refcount)) {
149 		ASSERT(!list_link_active(&rm->m_sock_item));
150 		ASSERT(!list_link_active(&rm->m_conn_item));
151 		rdsv3_message_purge(rm);
152 
153 		kmem_free(rm, sizeof (struct rdsv3_message) +
154 		    (rm->m_nents * sizeof (struct rdsv3_scatterlist)));
155 	}
156 }
157 
158 void
159 rdsv3_message_inc_free(struct rdsv3_incoming *inc)
160 {
161 	struct rdsv3_message *rm =
162 	    container_of(inc, struct rdsv3_message, m_inc);
163 	rdsv3_message_put(rm);
164 }
165 
166 void
167 rdsv3_message_populate_header(struct rdsv3_header *hdr, uint16_be_t sport,
168     uint16_be_t dport, uint64_t seq)
169 {
170 	hdr->h_flags = 0;
171 	hdr->h_sport = sport;
172 	hdr->h_dport = dport;
173 	hdr->h_sequence = htonll(seq);
174 	hdr->h_exthdr[0] = RDSV3_EXTHDR_NONE;
175 }
176 
177 int
178 rdsv3_message_add_extension(struct rdsv3_header *hdr,
179     unsigned int type, const void *data, unsigned int len)
180 {
181 	unsigned int ext_len = sizeof (uint8_t) + len;
182 	unsigned char *dst;
183 
184 	RDSV3_DPRINTF4("rdsv3_message_add_extension", "Enter");
185 
186 	/* For now, refuse to add more than one extension header */
187 	if (hdr->h_exthdr[0] != RDSV3_EXTHDR_NONE)
188 		return (0);
189 
190 	if (type >= __RDSV3_EXTHDR_MAX ||
191 	    len != rdsv3_exthdr_size[type])
192 		return (0);
193 
194 	if (ext_len >= RDSV3_HEADER_EXT_SPACE)
195 		return (0);
196 	dst = hdr->h_exthdr;
197 
198 	*dst++ = type;
199 	(void) memcpy(dst, data, len);
200 
201 	dst[len] = RDSV3_EXTHDR_NONE;
202 
203 	RDSV3_DPRINTF4("rdsv3_message_add_extension", "Return");
204 	return (1);
205 }
206 
207 /*
208  * If a message has extension headers, retrieve them here.
209  * Call like this:
210  *
211  * unsigned int pos = 0;
212  *
213  * while (1) {
214  *	buflen = sizeof(buffer);
215  *	type = rdsv3_message_next_extension(hdr, &pos, buffer, &buflen);
216  *	if (type == RDSV3_EXTHDR_NONE)
217  *		break;
218  *	...
219  * }
220  */
221 int
222 rdsv3_message_next_extension(struct rdsv3_header *hdr,
223     unsigned int *pos, void *buf, unsigned int *buflen)
224 {
225 	unsigned int offset, ext_type, ext_len;
226 	uint8_t *src = hdr->h_exthdr;
227 
228 	RDSV3_DPRINTF4("rdsv3_message_next_extension", "Enter");
229 
230 	offset = *pos;
231 	if (offset >= RDSV3_HEADER_EXT_SPACE)
232 		goto none;
233 
234 	/*
235 	 * Get the extension type and length. For now, the
236 	 * length is implied by the extension type.
237 	 */
238 	ext_type = src[offset++];
239 
240 	if (ext_type == RDSV3_EXTHDR_NONE || ext_type >= __RDSV3_EXTHDR_MAX)
241 		goto none;
242 	ext_len = rdsv3_exthdr_size[ext_type];
243 	if (offset + ext_len > RDSV3_HEADER_EXT_SPACE)
244 		goto none;
245 
246 	*pos = offset + ext_len;
247 	if (ext_len < *buflen)
248 		*buflen = ext_len;
249 	(void) memcpy(buf, src + offset, *buflen);
250 	return (ext_type);
251 
252 none:
253 	*pos = RDSV3_HEADER_EXT_SPACE;
254 	*buflen = 0;
255 	return (RDSV3_EXTHDR_NONE);
256 }
257 
258 int
259 rdsv3_message_add_version_extension(struct rdsv3_header *hdr,
260     unsigned int version)
261 {
262 	struct rdsv3_ext_header_version ext_hdr;
263 
264 	ext_hdr.h_version = htonl(version);
265 	return (rdsv3_message_add_extension(hdr, RDSV3_EXTHDR_VERSION,
266 	    &ext_hdr, sizeof (ext_hdr)));
267 }
268 
269 int
270 rdsv3_message_get_version_extension(struct rdsv3_header *hdr,
271     unsigned int *version)
272 {
273 	struct rdsv3_ext_header_version ext_hdr;
274 	unsigned int pos = 0, len = sizeof (ext_hdr);
275 
276 	RDSV3_DPRINTF4("rdsv3_message_get_version_extension", "Enter");
277 
278 	/*
279 	 * We assume the version extension is the only one present
280 	 */
281 	if (rdsv3_message_next_extension(hdr, &pos, &ext_hdr, &len) !=
282 	    RDSV3_EXTHDR_VERSION)
283 		return (0);
284 	*version = ntohl(ext_hdr.h_version);
285 	return (1);
286 }
287 
288 int
289 rdsv3_message_add_rdma_dest_extension(struct rdsv3_header *hdr, uint32_t r_key,
290     uint32_t offset)
291 {
292 	struct rdsv3_ext_header_rdma_dest ext_hdr;
293 
294 	ext_hdr.h_rdma_rkey = htonl(r_key);
295 	ext_hdr.h_rdma_offset = htonl(offset);
296 	return (rdsv3_message_add_extension(hdr, RDSV3_EXTHDR_RDMA_DEST,
297 	    &ext_hdr, sizeof (ext_hdr)));
298 }
299 
300 struct rdsv3_message *
301 rdsv3_message_alloc(unsigned int nents, int gfp)
302 {
303 	struct rdsv3_message *rm;
304 
305 	RDSV3_DPRINTF4("rdsv3_message_alloc", "Enter(nents: %d)", nents);
306 
307 	rm = kmem_zalloc(sizeof (struct rdsv3_message) +
308 	    (nents * sizeof (struct rdsv3_scatterlist)), gfp);
309 	if (!rm)
310 		goto out;
311 
312 	rm->m_refcount = 1;
313 	list_link_init(&rm->m_sock_item);
314 	list_link_init(&rm->m_conn_item);
315 	mutex_init(&rm->m_rs_lock, NULL, MUTEX_DRIVER, NULL);
316 
317 	RDSV3_DPRINTF4("rdsv3_message_alloc", "Return(rm: %p)", rm);
318 out:
319 	return (rm);
320 }
321 
322 struct rdsv3_message *
323 rdsv3_message_map_pages(unsigned long *page_addrs, unsigned int total_len)
324 {
325 	struct rdsv3_message *rm;
326 	unsigned int i;
327 
328 	RDSV3_DPRINTF4("rdsv3_message_map_pages", "Enter(len: %d)", total_len);
329 
330 #ifndef __lock_lint
331 	rm = rdsv3_message_alloc(ceil(total_len, PAGE_SIZE), KM_NOSLEEP);
332 #else
333 	rm = NULL;
334 #endif
335 	if (rm == NULL)
336 		return (ERR_PTR(-ENOMEM));
337 
338 	set_bit(RDSV3_MSG_PAGEVEC, &rm->m_flags);
339 	rm->m_inc.i_hdr.h_len = htonl(total_len);
340 #ifndef __lock_lint
341 	rm->m_nents = ceil(total_len, PAGE_SIZE);
342 #else
343 	rm->m_nents = 0;
344 #endif
345 
346 	for (i = 0; i < rm->m_nents; ++i) {
347 		rdsv3_sg_set_page(&rm->m_sg[i],
348 		    page_addrs[i],
349 		    PAGE_SIZE, 0);
350 	}
351 
352 	return (rm);
353 }
354 
355 struct rdsv3_message *
356 rdsv3_message_copy_from_user(struct uio *uiop,
357     size_t total_len)
358 {
359 	struct rdsv3_message *rm;
360 	struct rdsv3_scatterlist *sg;
361 	int ret;
362 
363 	RDSV3_DPRINTF4("rdsv3_message_copy_from_user", "Enter: %d", total_len);
364 
365 #ifndef __lock_lint
366 	rm = rdsv3_message_alloc(ceil(total_len, PAGE_SIZE), KM_NOSLEEP);
367 #else
368 	rm = NULL;
369 #endif
370 	if (rm == NULL) {
371 		ret = -ENOMEM;
372 		goto out;
373 	}
374 
375 	rm->m_inc.i_hdr.h_len = htonl(total_len);
376 
377 	/*
378 	 * now allocate and copy in the data payload.
379 	 */
380 	sg = rm->m_sg;
381 
382 	while (total_len) {
383 		if (rdsv3_sg_page(sg) == NULL) {
384 			ret = rdsv3_page_remainder_alloc(sg, total_len, 0);
385 			if (ret)
386 				goto out;
387 			rm->m_nents++;
388 		}
389 
390 		ret = uiomove(rdsv3_sg_page(sg), rdsv3_sg_len(sg), UIO_WRITE,
391 		    uiop);
392 		if (ret)
393 			goto out;
394 
395 		total_len -= rdsv3_sg_len(sg);
396 		sg++;
397 	}
398 
399 	ret = 0;
400 out:
401 	if (ret) {
402 		if (rm)
403 			rdsv3_message_put(rm);
404 		rm = ERR_PTR(ret);
405 	}
406 	return (rm);
407 }
408 
409 int
410 rdsv3_message_inc_copy_to_user(struct rdsv3_incoming *inc,
411     uio_t *uiop, size_t size)
412 {
413 	struct rdsv3_message *rm;
414 	struct rdsv3_scatterlist *sg;
415 	unsigned long to_copy;
416 	unsigned long vec_off;
417 	int copied;
418 	int ret;
419 	uint32_t len;
420 
421 	rm = container_of(inc, struct rdsv3_message, m_inc);
422 	len = ntohl(rm->m_inc.i_hdr.h_len);
423 
424 	RDSV3_DPRINTF4("rdsv3_message_inc_copy_to_user",
425 	    "Enter(rm: %p, len: %d)", rm, len);
426 
427 	sg = rm->m_sg;
428 	vec_off = 0;
429 	copied = 0;
430 
431 	while (copied < size && copied < len) {
432 
433 		to_copy = min(len - copied, sg->length - vec_off);
434 		to_copy = min(size - copied, to_copy);
435 
436 		RDSV3_DPRINTF5("rdsv3_message_inc_copy_to_user",
437 		    "copying %lu bytes to user iov %p from sg [%p, %u] + %lu\n",
438 		    to_copy, uiop,
439 		    rdsv3_sg_page(sg), sg->length, vec_off);
440 
441 		ret = uiomove(rdsv3_sg_page(sg), to_copy, UIO_READ, uiop);
442 		if (ret)
443 			break;
444 
445 		vec_off += to_copy;
446 		copied += to_copy;
447 
448 		if (vec_off == sg->length) {
449 			vec_off = 0;
450 			sg++;
451 		}
452 	}
453 
454 	return (copied);
455 }
456 
457 /*
458  * If the message is still on the send queue, wait until the transport
459  * is done with it. This is particularly important for RDMA operations.
460  */
461 void
462 rdsv3_message_wait(struct rdsv3_message *rm)
463 {
464 	rdsv3_wait_event(rdsv3_message_flush_waitq,
465 	    !test_bit(RDSV3_MSG_MAPPED, &rm->m_flags));
466 }
467 
468 void
469 rdsv3_message_unmapped(struct rdsv3_message *rm)
470 {
471 	clear_bit(RDSV3_MSG_MAPPED, &rm->m_flags);
472 	rdsv3_wake_up_all(&rdsv3_message_flush_waitq);
473 }
474