1 /*
2  * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  *
6  * This software is available to you under a choice of one of two
7  * licenses.  You may choose to be licensed under the terms of the GNU
8  * General Public License (GPL) Version 2, available from the file
9  * COPYING in the main directory of this source tree, or the
10  * OpenIB.org BSD license below:
11  *
12  *     Redistribution and use in source and binary forms, with or
13  *     without modification, are permitted provided that the following
14  *     conditions are met:
15  *
16  *      - Redistributions of source code must retain the above
17  *        copyright notice, this list of conditions and the following
18  *        disclaimer.
19  *
20  *      - Redistributions in binary form must reproduce the above
21  *        copyright notice, this list of conditions and the following
22  *        disclaimer in the documentation and/or other materials
23  *        provided with the distribution.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32  * SOFTWARE.
33  *
34  */
35 
36 #if HAVE_CONFIG_H
37 #  include <config.h>
38 #endif				/* HAVE_CONFIG_H */
39 
40 #include <string.h>
41 #include <vendor/osm_vendor_mlx.h>
42 #include <vendor/osm_vendor_mlx_defs.h>
43 #include <vendor/osm_vendor_mlx_svc.h>
44 #include <vendor/osm_vendor_mlx_transport.h>
45 #include <vendor/osm_vendor_mlx_sender.h>
46 #include <vendor/osm_pkt_randomizer.h>
47 
48 typedef enum _osmv_disp_route {
49 
50 	OSMV_ROUTE_DROP,
51 	OSMV_ROUTE_SIMPLE,
52 	OSMV_ROUTE_RMPP,
53 
54 } osmv_disp_route_t;
55 
56 /**
57  *   FORWARD REFERENCES TO PRIVATE FUNCTIONS
58  */
59 
60 static osmv_disp_route_t
61 __osmv_dispatch_route(IN osm_bind_handle_t h_bind,
62 		      IN const ib_mad_t * p_mad, OUT osmv_txn_ctx_t ** pp_txn);
63 
64 static void
65 __osmv_dispatch_simple_mad(IN osm_bind_handle_t h_bind,
66 			   IN const ib_mad_t * p_mad,
67 			   IN osmv_txn_ctx_t * p_txn,
68 			   IN const osm_mad_addr_t * p_mad_addr);
69 
70 static void
71 __osmv_dispatch_rmpp_mad(IN osm_bind_handle_t h_bind,
72 			 IN const ib_mad_t * p_mad,
73 			 IN osmv_txn_ctx_t * p_txn,
74 			 IN const osm_mad_addr_t * p_mad_addr);
75 
76 static void
77 __osmv_dispatch_rmpp_snd(IN osm_bind_handle_t h_bind,
78 			 IN const ib_mad_t * p_mad,
79 			 IN osmv_txn_ctx_t * p_txn,
80 			 IN const osm_mad_addr_t * p_mad_addr);
81 
82 static ib_api_status_t
83 __osmv_dispatch_rmpp_rcv(IN osm_bind_handle_t h_bind,
84 			 IN const ib_mad_t * p_mad,
85 			 IN osmv_txn_ctx_t * p_txn,
86 			 IN const osm_mad_addr_t * p_mad_addr);
87 
88 static ib_api_status_t
89 __osmv_dispatch_accept_seg(IN osm_bind_handle_t h_bind,
90 			   IN osmv_txn_ctx_t * p_txn,
91 			   IN const ib_mad_t * p_mad);
92 static void
93 __osmv_dispatch_send_ack(IN osm_bind_handle_t h_bind,
94 			 IN const ib_mad_t * p_req_mad,
95 			 IN osmv_txn_ctx_t * p_txn,
96 			 IN const osm_mad_addr_t * p_mad_addr);
97 
98 /*
99  * NAME
100  *   osmv_dispatch_mad
101  *
102  * DESCRIPTION
103  *   Lower-level MAD dispatcher.
104  *   Implements a switch between the following MAD consumers:
105  *   (1) Non-RMPP consumer (DATA)
106  *   (2) RMPP receiver     (DATA/ABORT/STOP)
107  *   (3) RMPP sender       (ACK/ABORT/STOP)
108  *
109  * PARAMETERS
110  *   h_bind                The bind handle
111  *   p_mad_buf             The 256 byte buffer of individual MAD
112  *   p_mad_addr            The MAD originator's address
113  */
114 
115 ib_api_status_t
116 osmv_dispatch_mad(IN osm_bind_handle_t h_bind,
117 		  IN const void *p_mad_buf,
118 		  IN const osm_mad_addr_t * p_mad_addr)
119 {
120 	ib_api_status_t ret = IB_SUCCESS;
121 	const ib_mad_t *p_mad = (ib_mad_t *) p_mad_buf;
122 	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
123 	osmv_txn_ctx_t *p_txn = NULL;
124 	osm_log_t *p_log = p_bo->p_vendor->p_log;
125 
126 	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
127 
128 	CL_ASSERT(NULL != h_bind && NULL != p_mad && NULL != p_mad_addr);
129 
130 	osmv_txn_lock(p_bo);
131 
132 	if (TRUE == p_bo->is_closing) {
133 
134 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
135 			"The bind handle %p is being closed. "
136 			"The MAD will not be dispatched.\n", p_bo);
137 
138 		ret = IB_INTERRUPTED;
139 		goto dispatch_mad_done;
140 	}
141 
142 	/*
143 	   Add call for packet drop randomizer.
144 	   This is a testing feature. If run_randomizer flag is set to TRUE,
145 	   the randomizer will be called, and randomally will drop
146 	   a packet. This is used for simulating unstable fabric.
147 	 */
148 	if (p_bo->p_vendor->run_randomizer == TRUE) {
149 		/* Try the randomizer */
150 		if (osm_pkt_randomizer_mad_drop(p_bo->p_vendor->p_log,
151 						p_bo->p_vendor->
152 						p_pkt_randomizer,
153 						p_mad) == TRUE) {
154 			osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
155 				"The MAD will not be dispatched.\n");
156 			goto dispatch_mad_done;
157 		}
158 	}
159 
160 	switch (__osmv_dispatch_route(h_bind, p_mad, &p_txn)) {
161 
162 	case OSMV_ROUTE_DROP:
163 		break;		/* Do nothing */
164 
165 	case OSMV_ROUTE_SIMPLE:
166 		__osmv_dispatch_simple_mad(h_bind, p_mad, p_txn, p_mad_addr);
167 		break;
168 
169 	case OSMV_ROUTE_RMPP:
170 		__osmv_dispatch_rmpp_mad(h_bind, p_mad, p_txn, p_mad_addr);
171 		break;
172 
173 	default:
174 		CL_ASSERT(FALSE);
175 	}
176 
177 dispatch_mad_done:
178 	osmv_txn_unlock(p_bo);
179 
180 	OSM_LOG_EXIT(p_log);
181 	return ret;
182 }
183 
184 /*
185  *  NAME            __osmv_dispatch_route()
186  *
187  *  DESCRIPTION     Decide which way to handle the received MAD: simple txn/RMPP/drop
188  */
189 
190 static osmv_disp_route_t
191 __osmv_dispatch_route(IN osm_bind_handle_t h_bind,
192 		      IN const ib_mad_t * p_mad, OUT osmv_txn_ctx_t ** pp_txn)
193 {
194 	ib_api_status_t ret;
195 	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
196 	boolean_t is_resp = ib_mad_is_response(p_mad);
197 	boolean_t is_txn;
198 	uint64_t key = cl_ntoh64(p_mad->trans_id);
199 
200 	CL_ASSERT(NULL != pp_txn);
201 
202 	ret = osmv_txn_lookup(h_bind, key, pp_txn);
203 	is_txn = (IB_SUCCESS == ret);
204 
205 	if (FALSE == is_txn && TRUE == is_resp) {
206 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
207 			"Received a response to a non-started/aged-out transaction (tid=0x%" PRIx64 "). "
208 			"Dropping the MAD.\n", key);
209 		return OSMV_ROUTE_DROP;
210 	}
211 
212 	if (TRUE == osmv_mad_is_rmpp(p_mad)) {
213 		/* An RMPP transaction. The filtering is more delicate there */
214 		return OSMV_ROUTE_RMPP;
215 	}
216 
217 	if (TRUE == is_txn && FALSE == is_resp) {
218 		/* Does this MAD try to start a transaction with duplicate tid? */
219 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
220 			"Duplicate TID 0x%" PRIx64 " received (not a response). "
221 			"Dropping the MAD.\n", key);
222 
223 		return OSMV_ROUTE_DROP;
224 	}
225 
226 	return OSMV_ROUTE_SIMPLE;
227 }
228 
229 /*
230  *  NAME            __osmv_dispatch_simple_mad()
231  *
232  *  DESCRIPTION     Handle a MAD that is part of non-RMPP transfer
233  */
234 
235 static void
236 __osmv_dispatch_simple_mad(IN osm_bind_handle_t h_bind,
237 			   IN const ib_mad_t * p_mad,
238 			   IN osmv_txn_ctx_t * p_txn,
239 			   IN const osm_mad_addr_t * p_mad_addr)
240 {
241 	osm_madw_t *p_madw;
242 	ib_mad_t *p_mad_buf;
243 	osm_madw_t *p_req_madw = NULL;
244 	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
245 
246 	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
247 
248 	/* Build the MAD wrapper to be returned to the user.
249 	 * The actual storage for the MAD is allocated there.
250 	 */
251 	p_madw =
252 	    osm_mad_pool_get(p_bo->p_osm_pool, h_bind, MAD_BLOCK_SIZE,
253 			     p_mad_addr);
254 
255 	if (NULL == p_madw) {
256 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
257 			"__osmv_dispatch_simple_mad: ERR 6501: "
258 			"Out Of Memory - could not allocate a buffer of size %d\n",
259 			MAD_BLOCK_SIZE);
260 
261 		goto dispatch_simple_mad_done;
262 	}
263 
264 	p_mad_buf = osm_madw_get_mad_ptr(p_madw);
265 	/* Copy the payload to the MAD buffer */
266 	memcpy((void *)p_mad_buf, (void *)p_mad, MAD_BLOCK_SIZE);
267 
268 	if (NULL != p_txn) {
269 		/* This is a RESPONSE MAD. Pair it with the REQUEST MAD, pass upstream */
270 		p_req_madw = p_txn->p_madw;
271 		CL_ASSERT(NULL != p_req_madw);
272 
273 		p_mad_buf->trans_id = cl_hton64(osmv_txn_get_tid(p_txn));
274 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
275 			"Restoring the original TID to 0x%" PRIx64 "\n",
276 			cl_ntoh64(p_mad_buf->trans_id));
277 
278 		/* Reply matched, transaction complete */
279 		osmv_txn_done(h_bind, osmv_txn_get_key(p_txn), FALSE);
280 	} else {
281 		/* This is a REQUEST  MAD. Don't create a context, pass upstream */
282 	}
283 
284 	/* Do the job ! */
285 	p_bo->recv_cb(p_madw, p_bo->cb_context, p_req_madw);
286 
287 dispatch_simple_mad_done:
288 	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
289 }
290 
291 /*
292  *  NAME            __osmv_dispatch_rmpp_mad()
293  *
294  *  DESCRIPTION     Handle a MAD that is part of RMPP transfer
295  */
296 
297 static void
298 __osmv_dispatch_rmpp_mad(IN osm_bind_handle_t h_bind,
299 			 IN const ib_mad_t * p_mad,
300 			 IN osmv_txn_ctx_t * p_txn,
301 			 IN const osm_mad_addr_t * p_mad_addr)
302 {
303 	ib_api_status_t status = IB_SUCCESS;
304 	uint64_t key = cl_ntoh64(p_mad->trans_id);
305 	boolean_t is_init_by_peer = FALSE;
306 	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
307 	osm_madw_t *p_madw;
308 
309 	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
310 
311 	if (NULL == p_txn) {
312 		if (FALSE == osmv_rmpp_is_data(p_mad)
313 		    || FALSE == osmv_rmpp_is_first(p_mad)) {
314 			osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
315 				"The MAD does not match any transaction "
316 				"and does not start a sender-initiated RMPP transfer.\n");
317 			goto dispatch_rmpp_mad_done;
318 		}
319 
320 		/* IB Spec 13.6.2.2. This is a Sender Initiated Transfer.
321 		   My peer is the requester and RMPP Sender. I am the RMPP Receiver.
322 		 */
323 		status = osmv_txn_init(h_bind, /*tid==key */ key, key, &p_txn);
324 		if (IB_SUCCESS != status) {
325 			goto dispatch_rmpp_mad_done;
326 		}
327 
328 		is_init_by_peer = TRUE;
329 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
330 			"A new sender-initiated transfer (TID=0x%" PRIx64 ") started\n",
331 			key);
332 	}
333 
334 	if (OSMV_TXN_RMPP_NONE == osmv_txn_get_rmpp_state(p_txn)) {
335 		/* Case 1: Fall through from above.
336 		 * Case 2: When the transaction was initiated by me
337 		 *         (a single request MAD), there was an uncertainty
338 		 *         whether the reply will be RMPP. Now it's resolved,
339 		 *         since the reply is RMPP!
340 		 */
341 		status =
342 		    osmv_txn_init_rmpp_receiver(h_bind, p_txn, is_init_by_peer);
343 		if (IB_SUCCESS != status) {
344 			goto dispatch_rmpp_mad_done;
345 		}
346 	}
347 
348 	switch (osmv_txn_get_rmpp_state(p_txn)) {
349 
350 	case OSMV_TXN_RMPP_RECEIVER:
351 		status =
352 		    __osmv_dispatch_rmpp_rcv(h_bind, p_mad, p_txn, p_mad_addr);
353 		if (IB_SUCCESS != status) {
354 			if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn)) {
355 				/* This is a requester, still waiting for the reply. Apply the callback */
356 				/* update the status of the p_madw */
357 				p_madw = osmv_txn_get_madw(p_txn);
358 				p_madw->status = status;
359 				p_bo->send_err_cb(p_bo->cb_context, p_madw);
360 			}
361 
362 			/* ABORT/STOP/LOCAL ERROR */
363 			osmv_txn_done(h_bind, osmv_txn_get_key(p_txn), FALSE);
364 		}
365 		break;
366 
367 	case OSMV_TXN_RMPP_SENDER:
368 		__osmv_dispatch_rmpp_snd(h_bind, p_mad, p_txn, p_mad_addr);
369 		/* If an error happens here, it's the sender thread to cleanup the txn */
370 		break;
371 
372 	default:
373 		CL_ASSERT(FALSE);
374 	}
375 
376 dispatch_rmpp_mad_done:
377 	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
378 }
379 
380 /*
381  *  NAME            __osmv_dispatch_rmpp_snd()
382  *
383  *  DESCRIPTION     MAD handling by an RMPP sender (ACK/ABORT/STOP)
384  */
385 
386 static void
387 __osmv_dispatch_rmpp_snd(IN osm_bind_handle_t h_bind,
388 			 IN const ib_mad_t * p_mad,
389 			 IN osmv_txn_ctx_t * p_txn,
390 			 IN const osm_mad_addr_t * p_mad_addr)
391 {
392 	osmv_rmpp_send_ctx_t *p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
393 
394 	uint32_t old_wl = p_send_ctx->window_last;
395 	uint32_t total_segs = osmv_rmpp_send_ctx_get_num_segs(p_send_ctx);
396 	uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->seg_num);
397 	uint32_t new_wl = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->paylen_newwin);
398 	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
399 
400 	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
401 
402 	if (TRUE == osmv_rmpp_is_abort_stop(p_mad)) {
403 
404 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
405 			"__osmv_dispatch_rmpp_snd: ERR 6502: "
406 			"The remote side sent an ABORT/STOP indication.\n");
407 		osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
408 		goto dispatch_rmpp_snd_done;
409 	}
410 
411 	if (FALSE == osmv_rmpp_is_ack(p_mad)) {
412 
413 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
414 			"Not supposed to receive DATA packets --> dropping the MAD\n");
415 		goto dispatch_rmpp_snd_done;
416 	}
417 
418 	/* Continue processing the ACK */
419 	if (seg_num > old_wl) {
420 
421 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
422 			"__osmv_dispatch_rmpp_snd: ERR 6503: "
423 			"ACK received for a non-sent segment %d\n", seg_num);
424 
425 		osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
426 				   IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_S2B);
427 
428 		osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
429 		goto dispatch_rmpp_snd_done;
430 	}
431 
432 	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
433 		"__osmv_dispatch_rmpp_snd: "
434 		"New WL = %u Old WL = %u Total Segs = %u\n",
435 		new_wl, old_wl, total_segs);
436 
437 	if (new_wl < old_wl) {
438 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
439 			"__osmv_dispatch_rmpp_snd: ERR 6508: "
440 			"The receiver requests a smaller WL (%d) than before (%d)\n",
441 			new_wl, old_wl);
442 
443 		osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
444 				   IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_W2S);
445 
446 		osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
447 		goto dispatch_rmpp_snd_done;
448 	}
449 
450 	/* Update the sender's window, and optionally wake up the sender thread
451 	 * Note! A single ACK can acknowledge a whole range of segments: [WF..SEG_NUM]
452 	 */
453 	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
454 		"ACK for seg_num #%d accepted.\n", seg_num);
455 
456 	if (seg_num == old_wl) {
457 
458 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
459 			"The send window [%d:%d] is totally acknowledged.\n",
460 			p_send_ctx->window_first, old_wl);
461 
462 		p_send_ctx->window_first = seg_num + 1;
463 		p_send_ctx->window_last =
464 		    (new_wl < total_segs) ? new_wl : total_segs;
465 
466 		/* Remove the response timeout event for the window */
467 		osmv_txn_remove_timeout_ev(h_bind, osmv_txn_get_key(p_txn));
468 
469 		/* Wake up the sending thread */
470 		cl_event_signal(&p_send_ctx->event);
471 	}
472 
473 dispatch_rmpp_snd_done:
474 	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
475 }
476 
477 /*
478  *  NAME           __osmv_dispatch_rmpp_rcv()
479  *
480  *  DESCRIPTION    MAD handling by an RMPP receiver (DATA/ABORT/STOP)
481  */
482 
483 static ib_api_status_t
484 __osmv_dispatch_rmpp_rcv(IN osm_bind_handle_t h_bind,
485 			 IN const ib_mad_t * p_mad,
486 			 IN osmv_txn_ctx_t * p_txn,
487 			 IN const osm_mad_addr_t * p_mad_addr)
488 {
489 	ib_api_status_t status = IB_SUCCESS;
490 	osmv_rmpp_recv_ctx_t *p_recv_ctx = osmv_txn_get_rmpp_recv_ctx(p_txn);
491 	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
492 	boolean_t is_last1 = FALSE, is_last2 = FALSE;
493 	osm_madw_t *p_new_madw = NULL, *p_req_madw = NULL;
494 	ib_mad_t *p_mad_buf;
495 	uint32_t size = 0;
496 	uint64_t key = osmv_txn_get_key(p_txn);
497 	uint64_t tid = osmv_txn_get_tid(p_txn);
498 
499 	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
500 
501 	if (TRUE == osmv_rmpp_is_ack(p_mad)) {
502 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
503 			"Not supposed to receive ACK's --> dropping the MAD\n");
504 
505 		goto dispatch_rmpp_rcv_done;
506 	}
507 
508 	if (TRUE == osmv_rmpp_is_abort_stop(p_mad)) {
509 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
510 			"__osmv_dispatch_rmpp_rcv: ERR 6504: "
511 			"The Remote Side stopped sending\n");
512 
513 		status = IB_REMOTE_ERROR;
514 		goto dispatch_rmpp_rcv_done;
515 	}
516 
517 	status = __osmv_dispatch_accept_seg(h_bind, p_txn, p_mad);
518 	switch (status) {
519 
520 	case IB_SUCCESS:
521 
522 		/* Check wheter this is the legal last MAD */
523 		/* Criteria #1: the received MAD is marked last */
524 		is_last1 = osmv_rmpp_is_last(p_mad);
525 
526 		/* Criteria #2: the total accumulated length hits the advertised one */
527 		is_last2 = is_last1;
528 
529 		size = osmv_rmpp_recv_ctx_get_byte_num_from_first(p_recv_ctx);
530 		if (size > 0) {
531 			is_last2 =
532 			    (osmv_rmpp_recv_ctx_get_cur_byte_num(p_recv_ctx) >=
533 			     size);
534 		}
535 
536 		if (is_last1 != is_last2) {
537 
538 			osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
539 					   IB_RMPP_TYPE_ABORT,
540 					   IB_RMPP_STATUS_BAD_LEN);
541 
542 			status = IB_ERROR;
543 			goto dispatch_rmpp_rcv_done;
544 		}
545 
546 		/* TBD Consider an optimization - sending an ACK
547 		 * only for the last segment in the window
548 		 */
549 		__osmv_dispatch_send_ack(h_bind, p_mad, p_txn, p_mad_addr);
550 		break;
551 
552 	case IB_INSUFFICIENT_RESOURCES:
553 		/* An out-of-order segment received. Send the ACK anyway */
554 		__osmv_dispatch_send_ack(h_bind, p_mad, p_txn, p_mad_addr);
555 		status = IB_SUCCESS;
556 		goto dispatch_rmpp_rcv_done;
557 
558 	case IB_INSUFFICIENT_MEMORY:
559 		osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
560 				   IB_RMPP_TYPE_STOP, IB_RMPP_STATUS_RESX);
561 		goto dispatch_rmpp_rcv_done;
562 
563 	default:
564 		/* Illegal return code */
565 		CL_ASSERT(FALSE);
566 	}
567 
568 	if (TRUE != is_last1) {
569 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
570 			"RMPP MADW assembly continues, TID=0x%" PRIx64 "\n", tid);
571 		goto dispatch_rmpp_rcv_done;
572 	}
573 
574 	/* This is the last packet. */
575 	if (0 == size) {
576 		/* The total size was not advertised in the first packet */
577 		size = osmv_rmpp_recv_ctx_get_byte_num_from_last(p_recv_ctx);
578 	}
579 
580 	/*
581 	   NOTE: the received mad might not be >= 256 bytes.
582 	   some MADs might contain several SA records but still be
583 	   less then a full MAD.
584 	   We have to use RMPP to send them over since on a regular
585 	   "simple" MAD there is no way to know how many records were sent
586 	 */
587 
588 	/* Build the MAD wrapper to be returned to the user.
589 	 * The actual storage for the MAD is allocated there.
590 	 */
591 	p_new_madw =
592 	    osm_mad_pool_get(p_bo->p_osm_pool, h_bind, size, p_mad_addr);
593 	if (NULL == p_new_madw) {
594 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
595 			"__osmv_dispatch_rmpp_rcv: ERR 6506: "
596 			"Out Of Memory - could not allocate %d bytes for the MADW\n",
597 			size);
598 
599 		status = IB_INSUFFICIENT_MEMORY;
600 		goto dispatch_rmpp_rcv_done;
601 	}
602 
603 	p_req_madw = osmv_txn_get_madw(p_txn);
604 	p_mad_buf = osm_madw_get_mad_ptr(p_new_madw);
605 	status = osmv_rmpp_recv_ctx_reassemble_arbt_mad(p_recv_ctx, size,
606 							(uint8_t *) p_mad_buf);
607 	if (IB_SUCCESS != status) {
608 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
609 			"__osmv_dispatch_rmpp_rcv: ERR 6507: "
610 			"Internal error - could not reassemble the result MAD\n");
611 		goto dispatch_rmpp_rcv_done;	/* What can happen here? */
612 	}
613 
614 	/* The MAD is assembled, we are about to apply the callback.
615 	 * Delete the transaction context, unless the transaction is double sided */
616 	if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn)
617 	    || FALSE == osmv_mad_is_multi_resp(p_mad)) {
618 
619 		osmv_txn_done(h_bind, key, FALSE);
620 	}
621 
622 	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
623 		"RMPP MADW %p assembly complete, TID=0x%" PRIx64 "\n", p_new_madw,
624 		tid);
625 
626 	p_mad_buf->trans_id = cl_hton64(tid);
627 	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
628 		"Restoring the original TID to 0x%" PRIx64 "\n",
629 		cl_ntoh64(p_mad_buf->trans_id));
630 
631 	/* Finally, do the job! */
632 	p_bo->recv_cb(p_new_madw, p_bo->cb_context, p_req_madw);
633 
634 dispatch_rmpp_rcv_done:
635 	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
636 	return status;
637 }
638 
639 /*
640  *  NAME            __osmv_dispatch_accept_seg()
641  *
642  *  DESCRIPTION     Store a DATA segment at the RMPP receiver side,
643  *                  if one is received in order.
644  */
645 
646 static ib_api_status_t
647 __osmv_dispatch_accept_seg(IN osm_bind_handle_t h_bind,
648 			   IN osmv_txn_ctx_t * p_txn, IN const ib_mad_t * p_mad)
649 {
650 	ib_api_status_t ret = IB_SUCCESS;
651 	uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->seg_num);
652 	osmv_rmpp_recv_ctx_t *p_recv_ctx = osmv_txn_get_rmpp_recv_ctx(p_txn);
653 	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
654 	uint64_t tid = osmv_txn_get_tid(p_txn);
655 
656 	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
657 
658 	if (seg_num != p_recv_ctx->expected_seg) {
659 		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
660 			"TID 0x%" PRIx64 ": can't accept this segment (%d) - "
661 			"this is a Go-Back-N implementation\n", tid, seg_num);
662 		return IB_INSUFFICIENT_RESOURCES;
663 	}
664 
665 	/* Store the packet's copy in the reassembly list.
666 	 * Promote the expected segment counter.
667 	 */
668 	ret = osmv_rmpp_recv_ctx_store_mad_seg(p_recv_ctx, (uint8_t *) p_mad);
669 	if (IB_SUCCESS != ret) {
670 		return ret;
671 	}
672 
673 	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
674 		"TID 0x%" PRIx64 ": segment %d accepted\n", tid, seg_num);
675 	p_recv_ctx->expected_seg = seg_num + 1;
676 
677 	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
678 	return IB_SUCCESS;
679 }
680 
681 /*
682  *  NAME              __osmv_dispatch_send_ack()
683  *
684  *  DESCRIPTION
685  *
686  *  ISSUES
687  *    Consider sending the ACK from an async thread
688  *    if problems with the receiving side processing arise.
689  */
690 
691 static void
692 __osmv_dispatch_send_ack(IN osm_bind_handle_t h_bind,
693 			 IN const ib_mad_t * p_req_mad,
694 			 IN osmv_txn_ctx_t * p_txn,
695 			 IN const osm_mad_addr_t * p_mad_addr)
696 {
697 	osmv_rmpp_recv_ctx_t *p_recv_ctx = osmv_txn_get_rmpp_recv_ctx(p_txn);
698 
699 	/* ACK the segment # that was accepted */
700 	uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_req_mad)->seg_num);
701 
702 	/* NOTE! The receiver can publish the New Window Last (NWL) value
703 	 * that is greater than the total number of segments to be sent.
704 	 * It's the sender's responsibility to compute the correct number
705 	 * of segments to send in the next burst.
706 	 */
707 	uint32_t nwl = p_recv_ctx->expected_seg + OSMV_RMPP_RECV_WIN - 1;
708 
709 	osmv_rmpp_send_ack(h_bind, p_req_mad, seg_num, nwl, p_mad_addr);
710 }
711