1/*
2 * Copyright (c) 2004-2006 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
48typedef 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
60static 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
64static 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
70static 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
76static 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
82static 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
88static 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);
92static 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
115ib_api_status_t
116osmv_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
177dispatch_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
190static 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 = osmv_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%llX). "
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%llX 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
235static 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%llX\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
287dispatch_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
297static 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%llX) 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
376dispatch_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
386static 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
473dispatch_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
483static 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%llX\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%llX\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%llX\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
634dispatch_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
646static 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%llX: 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%llX: 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
691static 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