1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2006 Voltaire, Inc. All rights reserved.
3219820Sjeff * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4219820Sjeff * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5219820Sjeff *
6219820Sjeff * This software is available to you under a choice of one of two
7219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
8219820Sjeff * General Public License (GPL) Version 2, available from the file
9219820Sjeff * COPYING in the main directory of this source tree, or the
10219820Sjeff * OpenIB.org BSD license below:
11219820Sjeff *
12219820Sjeff *     Redistribution and use in source and binary forms, with or
13219820Sjeff *     without modification, are permitted provided that the following
14219820Sjeff *     conditions are met:
15219820Sjeff *
16219820Sjeff *      - Redistributions of source code must retain the above
17219820Sjeff *        copyright notice, this list of conditions and the following
18219820Sjeff *        disclaimer.
19219820Sjeff *
20219820Sjeff *      - Redistributions in binary form must reproduce the above
21219820Sjeff *        copyright notice, this list of conditions and the following
22219820Sjeff *        disclaimer in the documentation and/or other materials
23219820Sjeff *        provided with the distribution.
24219820Sjeff *
25219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32219820Sjeff * SOFTWARE.
33219820Sjeff *
34219820Sjeff */
35219820Sjeff
36219820Sjeff#if HAVE_CONFIG_H
37219820Sjeff#  include <config.h>
38219820Sjeff#endif				/* HAVE_CONFIG_H */
39219820Sjeff
40219820Sjeff#include <string.h>
41219820Sjeff#include <vendor/osm_vendor_mlx.h>
42219820Sjeff#include <vendor/osm_vendor_mlx_defs.h>
43219820Sjeff#include <vendor/osm_vendor_mlx_svc.h>
44219820Sjeff#include <vendor/osm_vendor_mlx_transport.h>
45219820Sjeff#include <vendor/osm_vendor_mlx_sender.h>
46219820Sjeff#include <vendor/osm_pkt_randomizer.h>
47219820Sjeff
48219820Sjefftypedef enum _osmv_disp_route {
49219820Sjeff
50219820Sjeff	OSMV_ROUTE_DROP,
51219820Sjeff	OSMV_ROUTE_SIMPLE,
52219820Sjeff	OSMV_ROUTE_RMPP,
53219820Sjeff
54219820Sjeff} osmv_disp_route_t;
55219820Sjeff
56219820Sjeff/**
57219820Sjeff *   FORWARD REFERENCES TO PRIVATE FUNCTIONS
58219820Sjeff */
59219820Sjeff
60219820Sjeffstatic osmv_disp_route_t
61219820Sjeff__osmv_dispatch_route(IN osm_bind_handle_t h_bind,
62219820Sjeff		      IN const ib_mad_t * p_mad, OUT osmv_txn_ctx_t ** pp_txn);
63219820Sjeff
64219820Sjeffstatic void
65219820Sjeff__osmv_dispatch_simple_mad(IN osm_bind_handle_t h_bind,
66219820Sjeff			   IN const ib_mad_t * p_mad,
67219820Sjeff			   IN osmv_txn_ctx_t * p_txn,
68219820Sjeff			   IN const osm_mad_addr_t * p_mad_addr);
69219820Sjeff
70219820Sjeffstatic void
71219820Sjeff__osmv_dispatch_rmpp_mad(IN osm_bind_handle_t h_bind,
72219820Sjeff			 IN const ib_mad_t * p_mad,
73219820Sjeff			 IN osmv_txn_ctx_t * p_txn,
74219820Sjeff			 IN const osm_mad_addr_t * p_mad_addr);
75219820Sjeff
76219820Sjeffstatic void
77219820Sjeff__osmv_dispatch_rmpp_snd(IN osm_bind_handle_t h_bind,
78219820Sjeff			 IN const ib_mad_t * p_mad,
79219820Sjeff			 IN osmv_txn_ctx_t * p_txn,
80219820Sjeff			 IN const osm_mad_addr_t * p_mad_addr);
81219820Sjeff
82219820Sjeffstatic ib_api_status_t
83219820Sjeff__osmv_dispatch_rmpp_rcv(IN osm_bind_handle_t h_bind,
84219820Sjeff			 IN const ib_mad_t * p_mad,
85219820Sjeff			 IN osmv_txn_ctx_t * p_txn,
86219820Sjeff			 IN const osm_mad_addr_t * p_mad_addr);
87219820Sjeff
88219820Sjeffstatic ib_api_status_t
89219820Sjeff__osmv_dispatch_accept_seg(IN osm_bind_handle_t h_bind,
90219820Sjeff			   IN osmv_txn_ctx_t * p_txn,
91219820Sjeff			   IN const ib_mad_t * p_mad);
92219820Sjeffstatic void
93219820Sjeff__osmv_dispatch_send_ack(IN osm_bind_handle_t h_bind,
94219820Sjeff			 IN const ib_mad_t * p_req_mad,
95219820Sjeff			 IN osmv_txn_ctx_t * p_txn,
96219820Sjeff			 IN const osm_mad_addr_t * p_mad_addr);
97219820Sjeff
98219820Sjeff/*
99219820Sjeff * NAME
100219820Sjeff *   osmv_dispatch_mad
101219820Sjeff *
102219820Sjeff * DESCRIPTION
103219820Sjeff *   Lower-level MAD dispatcher.
104219820Sjeff *   Implements a switch between the following MAD consumers:
105219820Sjeff *   (1) Non-RMPP consumer (DATA)
106219820Sjeff *   (2) RMPP receiver     (DATA/ABORT/STOP)
107219820Sjeff *   (3) RMPP sender       (ACK/ABORT/STOP)
108219820Sjeff *
109219820Sjeff * PARAMETERS
110219820Sjeff *   h_bind                The bind handle
111219820Sjeff *   p_mad_buf             The 256 byte buffer of individual MAD
112219820Sjeff *   p_mad_addr            The MAD originator's address
113219820Sjeff */
114219820Sjeff
115219820Sjeffib_api_status_t
116219820Sjeffosmv_dispatch_mad(IN osm_bind_handle_t h_bind,
117219820Sjeff		  IN const void *p_mad_buf,
118219820Sjeff		  IN const osm_mad_addr_t * p_mad_addr)
119219820Sjeff{
120219820Sjeff	ib_api_status_t ret = IB_SUCCESS;
121219820Sjeff	const ib_mad_t *p_mad = (ib_mad_t *) p_mad_buf;
122219820Sjeff	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
123219820Sjeff	osmv_txn_ctx_t *p_txn = NULL;
124219820Sjeff	osm_log_t *p_log = p_bo->p_vendor->p_log;
125219820Sjeff
126219820Sjeff	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
127219820Sjeff
128219820Sjeff	CL_ASSERT(NULL != h_bind && NULL != p_mad && NULL != p_mad_addr);
129219820Sjeff
130219820Sjeff	osmv_txn_lock(p_bo);
131219820Sjeff
132219820Sjeff	if (TRUE == p_bo->is_closing) {
133219820Sjeff
134219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
135219820Sjeff			"The bind handle %p is being closed. "
136219820Sjeff			"The MAD will not be dispatched.\n", p_bo);
137219820Sjeff
138219820Sjeff		ret = IB_INTERRUPTED;
139219820Sjeff		goto dispatch_mad_done;
140219820Sjeff	}
141219820Sjeff
142219820Sjeff	/*
143219820Sjeff	   Add call for packet drop randomizer.
144219820Sjeff	   This is a testing feature. If run_randomizer flag is set to TRUE,
145219820Sjeff	   the randomizer will be called, and randomally will drop
146219820Sjeff	   a packet. This is used for simulating unstable fabric.
147219820Sjeff	 */
148219820Sjeff	if (p_bo->p_vendor->run_randomizer == TRUE) {
149219820Sjeff		/* Try the randomizer */
150219820Sjeff		if (osm_pkt_randomizer_mad_drop(p_bo->p_vendor->p_log,
151219820Sjeff						p_bo->p_vendor->
152219820Sjeff						p_pkt_randomizer,
153219820Sjeff						p_mad) == TRUE) {
154219820Sjeff			osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
155219820Sjeff				"The MAD will not be dispatched.\n");
156219820Sjeff			goto dispatch_mad_done;
157219820Sjeff		}
158219820Sjeff	}
159219820Sjeff
160219820Sjeff	switch (__osmv_dispatch_route(h_bind, p_mad, &p_txn)) {
161219820Sjeff
162219820Sjeff	case OSMV_ROUTE_DROP:
163219820Sjeff		break;		/* Do nothing */
164219820Sjeff
165219820Sjeff	case OSMV_ROUTE_SIMPLE:
166219820Sjeff		__osmv_dispatch_simple_mad(h_bind, p_mad, p_txn, p_mad_addr);
167219820Sjeff		break;
168219820Sjeff
169219820Sjeff	case OSMV_ROUTE_RMPP:
170219820Sjeff		__osmv_dispatch_rmpp_mad(h_bind, p_mad, p_txn, p_mad_addr);
171219820Sjeff		break;
172219820Sjeff
173219820Sjeff	default:
174219820Sjeff		CL_ASSERT(FALSE);
175219820Sjeff	}
176219820Sjeff
177219820Sjeffdispatch_mad_done:
178219820Sjeff	osmv_txn_unlock(p_bo);
179219820Sjeff
180219820Sjeff	OSM_LOG_EXIT(p_log);
181219820Sjeff	return ret;
182219820Sjeff}
183219820Sjeff
184219820Sjeff/*
185219820Sjeff *  NAME            __osmv_dispatch_route()
186219820Sjeff *
187219820Sjeff *  DESCRIPTION     Decide which way to handle the received MAD: simple txn/RMPP/drop
188219820Sjeff */
189219820Sjeff
190219820Sjeffstatic osmv_disp_route_t
191219820Sjeff__osmv_dispatch_route(IN osm_bind_handle_t h_bind,
192219820Sjeff		      IN const ib_mad_t * p_mad, OUT osmv_txn_ctx_t ** pp_txn)
193219820Sjeff{
194219820Sjeff	ib_api_status_t ret;
195219820Sjeff	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
196219820Sjeff	boolean_t is_resp = osmv_mad_is_response(p_mad);
197219820Sjeff	boolean_t is_txn;
198219820Sjeff	uint64_t key = cl_ntoh64(p_mad->trans_id);
199219820Sjeff
200219820Sjeff	CL_ASSERT(NULL != pp_txn);
201219820Sjeff
202219820Sjeff	ret = osmv_txn_lookup(h_bind, key, pp_txn);
203219820Sjeff	is_txn = (IB_SUCCESS == ret);
204219820Sjeff
205219820Sjeff	if (FALSE == is_txn && TRUE == is_resp) {
206219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
207219820Sjeff			"Received a response to a non-started/aged-out transaction (tid=0x%llX). "
208219820Sjeff			"Dropping the MAD.\n", key);
209219820Sjeff		return OSMV_ROUTE_DROP;
210219820Sjeff	}
211219820Sjeff
212219820Sjeff	if (TRUE == osmv_mad_is_rmpp(p_mad)) {
213219820Sjeff		/* An RMPP transaction. The filtering is more delicate there */
214219820Sjeff		return OSMV_ROUTE_RMPP;
215219820Sjeff	}
216219820Sjeff
217219820Sjeff	if (TRUE == is_txn && FALSE == is_resp) {
218219820Sjeff		/* Does this MAD try to start a transaction with duplicate tid? */
219219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
220219820Sjeff			"Duplicate TID 0x%llX received (not a response). "
221219820Sjeff			"Dropping the MAD.\n", key);
222219820Sjeff
223219820Sjeff		return OSMV_ROUTE_DROP;
224219820Sjeff	}
225219820Sjeff
226219820Sjeff	return OSMV_ROUTE_SIMPLE;
227219820Sjeff}
228219820Sjeff
229219820Sjeff/*
230219820Sjeff *  NAME            __osmv_dispatch_simple_mad()
231219820Sjeff *
232219820Sjeff *  DESCRIPTION     Handle a MAD that is part of non-RMPP transfer
233219820Sjeff */
234219820Sjeff
235219820Sjeffstatic void
236219820Sjeff__osmv_dispatch_simple_mad(IN osm_bind_handle_t h_bind,
237219820Sjeff			   IN const ib_mad_t * p_mad,
238219820Sjeff			   IN osmv_txn_ctx_t * p_txn,
239219820Sjeff			   IN const osm_mad_addr_t * p_mad_addr)
240219820Sjeff{
241219820Sjeff	osm_madw_t *p_madw;
242219820Sjeff	ib_mad_t *p_mad_buf;
243219820Sjeff	osm_madw_t *p_req_madw = NULL;
244219820Sjeff	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
245219820Sjeff
246219820Sjeff	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
247219820Sjeff
248219820Sjeff	/* Build the MAD wrapper to be returned to the user.
249219820Sjeff	 * The actual storage for the MAD is allocated there.
250219820Sjeff	 */
251219820Sjeff	p_madw =
252219820Sjeff	    osm_mad_pool_get(p_bo->p_osm_pool, h_bind, MAD_BLOCK_SIZE,
253219820Sjeff			     p_mad_addr);
254219820Sjeff
255219820Sjeff	if (NULL == p_madw) {
256219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
257219820Sjeff			"__osmv_dispatch_simple_mad: ERR 6501: "
258219820Sjeff			"Out Of Memory - could not allocate a buffer of size %d\n",
259219820Sjeff			MAD_BLOCK_SIZE);
260219820Sjeff
261219820Sjeff		goto dispatch_simple_mad_done;
262219820Sjeff	}
263219820Sjeff
264219820Sjeff	p_mad_buf = osm_madw_get_mad_ptr(p_madw);
265219820Sjeff	/* Copy the payload to the MAD buffer */
266219820Sjeff	memcpy((void *)p_mad_buf, (void *)p_mad, MAD_BLOCK_SIZE);
267219820Sjeff
268219820Sjeff	if (NULL != p_txn) {
269219820Sjeff		/* This is a RESPONSE MAD. Pair it with the REQUEST MAD, pass upstream */
270219820Sjeff		p_req_madw = p_txn->p_madw;
271219820Sjeff		CL_ASSERT(NULL != p_req_madw);
272219820Sjeff
273219820Sjeff		p_mad_buf->trans_id = cl_hton64(osmv_txn_get_tid(p_txn));
274219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
275219820Sjeff			"Restoring the original TID to 0x%llX\n",
276219820Sjeff			cl_ntoh64(p_mad_buf->trans_id));
277219820Sjeff
278219820Sjeff		/* Reply matched, transaction complete */
279219820Sjeff		osmv_txn_done(h_bind, osmv_txn_get_key(p_txn), FALSE);
280219820Sjeff	} else {
281219820Sjeff		/* This is a REQUEST  MAD. Don't create a context, pass upstream */
282219820Sjeff	}
283219820Sjeff
284219820Sjeff	/* Do the job ! */
285219820Sjeff	p_bo->recv_cb(p_madw, p_bo->cb_context, p_req_madw);
286219820Sjeff
287219820Sjeffdispatch_simple_mad_done:
288219820Sjeff	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
289219820Sjeff}
290219820Sjeff
291219820Sjeff/*
292219820Sjeff *  NAME            __osmv_dispatch_rmpp_mad()
293219820Sjeff *
294219820Sjeff *  DESCRIPTION     Handle a MAD that is part of RMPP transfer
295219820Sjeff */
296219820Sjeff
297219820Sjeffstatic void
298219820Sjeff__osmv_dispatch_rmpp_mad(IN osm_bind_handle_t h_bind,
299219820Sjeff			 IN const ib_mad_t * p_mad,
300219820Sjeff			 IN osmv_txn_ctx_t * p_txn,
301219820Sjeff			 IN const osm_mad_addr_t * p_mad_addr)
302219820Sjeff{
303219820Sjeff	ib_api_status_t status = IB_SUCCESS;
304219820Sjeff	uint64_t key = cl_ntoh64(p_mad->trans_id);
305219820Sjeff	boolean_t is_init_by_peer = FALSE;
306219820Sjeff	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
307219820Sjeff	osm_madw_t *p_madw;
308219820Sjeff
309219820Sjeff	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
310219820Sjeff
311219820Sjeff	if (NULL == p_txn) {
312219820Sjeff		if (FALSE == osmv_rmpp_is_data(p_mad)
313219820Sjeff		    || FALSE == osmv_rmpp_is_first(p_mad)) {
314219820Sjeff			osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
315219820Sjeff				"The MAD does not match any transaction "
316219820Sjeff				"and does not start a sender-initiated RMPP transfer.\n");
317219820Sjeff			goto dispatch_rmpp_mad_done;
318219820Sjeff		}
319219820Sjeff
320219820Sjeff		/* IB Spec 13.6.2.2. This is a Sender Initiated Transfer.
321219820Sjeff		   My peer is the requester and RMPP Sender. I am the RMPP Receiver.
322219820Sjeff		 */
323219820Sjeff		status = osmv_txn_init(h_bind, /*tid==key */ key, key, &p_txn);
324219820Sjeff		if (IB_SUCCESS != status) {
325219820Sjeff			goto dispatch_rmpp_mad_done;
326219820Sjeff		}
327219820Sjeff
328219820Sjeff		is_init_by_peer = TRUE;
329219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
330219820Sjeff			"A new sender-initiated transfer (TID=0x%llX) started\n",
331219820Sjeff			key);
332219820Sjeff	}
333219820Sjeff
334219820Sjeff	if (OSMV_TXN_RMPP_NONE == osmv_txn_get_rmpp_state(p_txn)) {
335219820Sjeff		/* Case 1: Fall through from above.
336219820Sjeff		 * Case 2: When the transaction was initiated by me
337219820Sjeff		 *         (a single request MAD), there was an uncertainty
338219820Sjeff		 *         whether the reply will be RMPP. Now it's resolved,
339219820Sjeff		 *         since the reply is RMPP!
340219820Sjeff		 */
341219820Sjeff		status =
342219820Sjeff		    osmv_txn_init_rmpp_receiver(h_bind, p_txn, is_init_by_peer);
343219820Sjeff		if (IB_SUCCESS != status) {
344219820Sjeff			goto dispatch_rmpp_mad_done;
345219820Sjeff		}
346219820Sjeff	}
347219820Sjeff
348219820Sjeff	switch (osmv_txn_get_rmpp_state(p_txn)) {
349219820Sjeff
350219820Sjeff	case OSMV_TXN_RMPP_RECEIVER:
351219820Sjeff		status =
352219820Sjeff		    __osmv_dispatch_rmpp_rcv(h_bind, p_mad, p_txn, p_mad_addr);
353219820Sjeff		if (IB_SUCCESS != status) {
354219820Sjeff			if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn)) {
355219820Sjeff				/* This is a requester, still waiting for the reply. Apply the callback */
356219820Sjeff				/* update the status of the p_madw */
357219820Sjeff				p_madw = osmv_txn_get_madw(p_txn);
358219820Sjeff				p_madw->status = status;
359219820Sjeff				p_bo->send_err_cb(p_bo->cb_context, p_madw);
360219820Sjeff			}
361219820Sjeff
362219820Sjeff			/* ABORT/STOP/LOCAL ERROR */
363219820Sjeff			osmv_txn_done(h_bind, osmv_txn_get_key(p_txn), FALSE);
364219820Sjeff		}
365219820Sjeff		break;
366219820Sjeff
367219820Sjeff	case OSMV_TXN_RMPP_SENDER:
368219820Sjeff		__osmv_dispatch_rmpp_snd(h_bind, p_mad, p_txn, p_mad_addr);
369219820Sjeff		/* If an error happens here, it's the sender thread to cleanup the txn */
370219820Sjeff		break;
371219820Sjeff
372219820Sjeff	default:
373219820Sjeff		CL_ASSERT(FALSE);
374219820Sjeff	}
375219820Sjeff
376219820Sjeffdispatch_rmpp_mad_done:
377219820Sjeff	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
378219820Sjeff}
379219820Sjeff
380219820Sjeff/*
381219820Sjeff *  NAME            __osmv_dispatch_rmpp_snd()
382219820Sjeff *
383219820Sjeff *  DESCRIPTION     MAD handling by an RMPP sender (ACK/ABORT/STOP)
384219820Sjeff */
385219820Sjeff
386219820Sjeffstatic void
387219820Sjeff__osmv_dispatch_rmpp_snd(IN osm_bind_handle_t h_bind,
388219820Sjeff			 IN const ib_mad_t * p_mad,
389219820Sjeff			 IN osmv_txn_ctx_t * p_txn,
390219820Sjeff			 IN const osm_mad_addr_t * p_mad_addr)
391219820Sjeff{
392219820Sjeff	osmv_rmpp_send_ctx_t *p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
393219820Sjeff
394219820Sjeff	uint32_t old_wl = p_send_ctx->window_last;
395219820Sjeff	uint32_t total_segs = osmv_rmpp_send_ctx_get_num_segs(p_send_ctx);
396219820Sjeff	uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->seg_num);
397219820Sjeff	uint32_t new_wl = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->paylen_newwin);
398219820Sjeff	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
399219820Sjeff
400219820Sjeff	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
401219820Sjeff
402219820Sjeff	if (TRUE == osmv_rmpp_is_abort_stop(p_mad)) {
403219820Sjeff
404219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
405219820Sjeff			"__osmv_dispatch_rmpp_snd: ERR 6502: "
406219820Sjeff			"The remote side sent an ABORT/STOP indication.\n");
407219820Sjeff		osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
408219820Sjeff		goto dispatch_rmpp_snd_done;
409219820Sjeff	}
410219820Sjeff
411219820Sjeff	if (FALSE == osmv_rmpp_is_ack(p_mad)) {
412219820Sjeff
413219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
414219820Sjeff			"Not supposed to receive DATA packets --> dropping the MAD\n");
415219820Sjeff		goto dispatch_rmpp_snd_done;
416219820Sjeff	}
417219820Sjeff
418219820Sjeff	/* Continue processing the ACK */
419219820Sjeff	if (seg_num > old_wl) {
420219820Sjeff
421219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
422219820Sjeff			"__osmv_dispatch_rmpp_snd: ERR 6503: "
423219820Sjeff			"ACK received for a non-sent segment %d\n", seg_num);
424219820Sjeff
425219820Sjeff		osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
426219820Sjeff				   IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_S2B);
427219820Sjeff
428219820Sjeff		osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
429219820Sjeff		goto dispatch_rmpp_snd_done;
430219820Sjeff	}
431219820Sjeff
432219820Sjeff	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
433219820Sjeff		"__osmv_dispatch_rmpp_snd: "
434219820Sjeff		"New WL = %u Old WL = %u Total Segs = %u\n",
435219820Sjeff		new_wl, old_wl, total_segs);
436219820Sjeff
437219820Sjeff	if (new_wl < old_wl) {
438219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
439219820Sjeff			"__osmv_dispatch_rmpp_snd: ERR 6508: "
440219820Sjeff			"The receiver requests a smaller WL (%d) than before (%d)\n",
441219820Sjeff			new_wl, old_wl);
442219820Sjeff
443219820Sjeff		osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
444219820Sjeff				   IB_RMPP_TYPE_ABORT, IB_RMPP_STATUS_W2S);
445219820Sjeff
446219820Sjeff		osmv_rmpp_snd_error(p_send_ctx, IB_REMOTE_ERROR);
447219820Sjeff		goto dispatch_rmpp_snd_done;
448219820Sjeff	}
449219820Sjeff
450219820Sjeff	/* Update the sender's window, and optionally wake up the sender thread
451219820Sjeff	 * Note! A single ACK can acknowledge a whole range of segments: [WF..SEG_NUM]
452219820Sjeff	 */
453219820Sjeff	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
454219820Sjeff		"ACK for seg_num #%d accepted.\n", seg_num);
455219820Sjeff
456219820Sjeff	if (seg_num == old_wl) {
457219820Sjeff
458219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
459219820Sjeff			"The send window [%d:%d] is totally acknowledged.\n",
460219820Sjeff			p_send_ctx->window_first, old_wl);
461219820Sjeff
462219820Sjeff		p_send_ctx->window_first = seg_num + 1;
463219820Sjeff		p_send_ctx->window_last =
464219820Sjeff		    (new_wl < total_segs) ? new_wl : total_segs;
465219820Sjeff
466219820Sjeff		/* Remove the response timeout event for the window */
467219820Sjeff		osmv_txn_remove_timeout_ev(h_bind, osmv_txn_get_key(p_txn));
468219820Sjeff
469219820Sjeff		/* Wake up the sending thread */
470219820Sjeff		cl_event_signal(&p_send_ctx->event);
471219820Sjeff	}
472219820Sjeff
473219820Sjeffdispatch_rmpp_snd_done:
474219820Sjeff	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
475219820Sjeff}
476219820Sjeff
477219820Sjeff/*
478219820Sjeff *  NAME           __osmv_dispatch_rmpp_rcv()
479219820Sjeff *
480219820Sjeff *  DESCRIPTION    MAD handling by an RMPP receiver (DATA/ABORT/STOP)
481219820Sjeff */
482219820Sjeff
483219820Sjeffstatic ib_api_status_t
484219820Sjeff__osmv_dispatch_rmpp_rcv(IN osm_bind_handle_t h_bind,
485219820Sjeff			 IN const ib_mad_t * p_mad,
486219820Sjeff			 IN osmv_txn_ctx_t * p_txn,
487219820Sjeff			 IN const osm_mad_addr_t * p_mad_addr)
488219820Sjeff{
489219820Sjeff	ib_api_status_t status = IB_SUCCESS;
490219820Sjeff	osmv_rmpp_recv_ctx_t *p_recv_ctx = osmv_txn_get_rmpp_recv_ctx(p_txn);
491219820Sjeff	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
492219820Sjeff	boolean_t is_last1 = FALSE, is_last2 = FALSE;
493219820Sjeff	osm_madw_t *p_new_madw = NULL, *p_req_madw = NULL;
494219820Sjeff	ib_mad_t *p_mad_buf;
495219820Sjeff	uint32_t size = 0;
496219820Sjeff	uint64_t key = osmv_txn_get_key(p_txn);
497219820Sjeff	uint64_t tid = osmv_txn_get_tid(p_txn);
498219820Sjeff
499219820Sjeff	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
500219820Sjeff
501219820Sjeff	if (TRUE == osmv_rmpp_is_ack(p_mad)) {
502219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
503219820Sjeff			"Not supposed to receive ACK's --> dropping the MAD\n");
504219820Sjeff
505219820Sjeff		goto dispatch_rmpp_rcv_done;
506219820Sjeff	}
507219820Sjeff
508219820Sjeff	if (TRUE == osmv_rmpp_is_abort_stop(p_mad)) {
509219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
510219820Sjeff			"__osmv_dispatch_rmpp_rcv: ERR 6504: "
511219820Sjeff			"The Remote Side stopped sending\n");
512219820Sjeff
513219820Sjeff		status = IB_REMOTE_ERROR;
514219820Sjeff		goto dispatch_rmpp_rcv_done;
515219820Sjeff	}
516219820Sjeff
517219820Sjeff	status = __osmv_dispatch_accept_seg(h_bind, p_txn, p_mad);
518219820Sjeff	switch (status) {
519219820Sjeff
520219820Sjeff	case IB_SUCCESS:
521219820Sjeff
522219820Sjeff		/* Check wheter this is the legal last MAD */
523219820Sjeff		/* Criteria #1: the received MAD is marked last */
524219820Sjeff		is_last1 = osmv_rmpp_is_last(p_mad);
525219820Sjeff
526219820Sjeff		/* Criteria #2: the total accumulated length hits the advertised one */
527219820Sjeff		is_last2 = is_last1;
528219820Sjeff
529219820Sjeff		size = osmv_rmpp_recv_ctx_get_byte_num_from_first(p_recv_ctx);
530219820Sjeff		if (size > 0) {
531219820Sjeff			is_last2 =
532219820Sjeff			    (osmv_rmpp_recv_ctx_get_cur_byte_num(p_recv_ctx) >=
533219820Sjeff			     size);
534219820Sjeff		}
535219820Sjeff
536219820Sjeff		if (is_last1 != is_last2) {
537219820Sjeff
538219820Sjeff			osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
539219820Sjeff					   IB_RMPP_TYPE_ABORT,
540219820Sjeff					   IB_RMPP_STATUS_BAD_LEN);
541219820Sjeff
542219820Sjeff			status = IB_ERROR;
543219820Sjeff			goto dispatch_rmpp_rcv_done;
544219820Sjeff		}
545219820Sjeff
546219820Sjeff		/* TBD Consider an optimization - sending an ACK
547219820Sjeff		 * only for the last segment in the window
548219820Sjeff		 */
549219820Sjeff		__osmv_dispatch_send_ack(h_bind, p_mad, p_txn, p_mad_addr);
550219820Sjeff		break;
551219820Sjeff
552219820Sjeff	case IB_INSUFFICIENT_RESOURCES:
553219820Sjeff		/* An out-of-order segment received. Send the ACK anyway */
554219820Sjeff		__osmv_dispatch_send_ack(h_bind, p_mad, p_txn, p_mad_addr);
555219820Sjeff		status = IB_SUCCESS;
556219820Sjeff		goto dispatch_rmpp_rcv_done;
557219820Sjeff
558219820Sjeff	case IB_INSUFFICIENT_MEMORY:
559219820Sjeff		osmv_rmpp_send_nak(h_bind, p_mad, p_mad_addr,
560219820Sjeff				   IB_RMPP_TYPE_STOP, IB_RMPP_STATUS_RESX);
561219820Sjeff		goto dispatch_rmpp_rcv_done;
562219820Sjeff
563219820Sjeff	default:
564219820Sjeff		/* Illegal return code */
565219820Sjeff		CL_ASSERT(FALSE);
566219820Sjeff	}
567219820Sjeff
568219820Sjeff	if (TRUE != is_last1) {
569219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
570219820Sjeff			"RMPP MADW assembly continues, TID=0x%llX\n", tid);
571219820Sjeff		goto dispatch_rmpp_rcv_done;
572219820Sjeff	}
573219820Sjeff
574219820Sjeff	/* This is the last packet. */
575219820Sjeff	if (0 == size) {
576219820Sjeff		/* The total size was not advertised in the first packet */
577219820Sjeff		size = osmv_rmpp_recv_ctx_get_byte_num_from_last(p_recv_ctx);
578219820Sjeff	}
579219820Sjeff
580219820Sjeff	/*
581219820Sjeff	   NOTE: the received mad might not be >= 256 bytes.
582219820Sjeff	   some MADs might contain several SA records but still be
583219820Sjeff	   less then a full MAD.
584219820Sjeff	   We have to use RMPP to send them over since on a regular
585219820Sjeff	   "simple" MAD there is no way to know how many records were sent
586219820Sjeff	 */
587219820Sjeff
588219820Sjeff	/* Build the MAD wrapper to be returned to the user.
589219820Sjeff	 * The actual storage for the MAD is allocated there.
590219820Sjeff	 */
591219820Sjeff	p_new_madw =
592219820Sjeff	    osm_mad_pool_get(p_bo->p_osm_pool, h_bind, size, p_mad_addr);
593219820Sjeff	if (NULL == p_new_madw) {
594219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
595219820Sjeff			"__osmv_dispatch_rmpp_rcv: ERR 6506: "
596219820Sjeff			"Out Of Memory - could not allocate %d bytes for the MADW\n",
597219820Sjeff			size);
598219820Sjeff
599219820Sjeff		status = IB_INSUFFICIENT_MEMORY;
600219820Sjeff		goto dispatch_rmpp_rcv_done;
601219820Sjeff	}
602219820Sjeff
603219820Sjeff	p_req_madw = osmv_txn_get_madw(p_txn);
604219820Sjeff	p_mad_buf = osm_madw_get_mad_ptr(p_new_madw);
605219820Sjeff	status = osmv_rmpp_recv_ctx_reassemble_arbt_mad(p_recv_ctx, size,
606219820Sjeff							(uint8_t *) p_mad_buf);
607219820Sjeff	if (IB_SUCCESS != status) {
608219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
609219820Sjeff			"__osmv_dispatch_rmpp_rcv: ERR 6507: "
610219820Sjeff			"Internal error - could not reassemble the result MAD\n");
611219820Sjeff		goto dispatch_rmpp_rcv_done;	/* What can happen here? */
612219820Sjeff	}
613219820Sjeff
614219820Sjeff	/* The MAD is assembled, we are about to apply the callback.
615219820Sjeff	 * Delete the transaction context, unless the transaction is double sided */
616219820Sjeff	if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn)
617219820Sjeff	    || FALSE == osmv_mad_is_multi_resp(p_mad)) {
618219820Sjeff
619219820Sjeff		osmv_txn_done(h_bind, key, FALSE);
620219820Sjeff	}
621219820Sjeff
622219820Sjeff	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
623219820Sjeff		"RMPP MADW %p assembly complete, TID=0x%llX\n", p_new_madw,
624219820Sjeff		tid);
625219820Sjeff
626219820Sjeff	p_mad_buf->trans_id = cl_hton64(tid);
627219820Sjeff	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
628219820Sjeff		"Restoring the original TID to 0x%llX\n",
629219820Sjeff		cl_ntoh64(p_mad_buf->trans_id));
630219820Sjeff
631219820Sjeff	/* Finally, do the job! */
632219820Sjeff	p_bo->recv_cb(p_new_madw, p_bo->cb_context, p_req_madw);
633219820Sjeff
634219820Sjeffdispatch_rmpp_rcv_done:
635219820Sjeff	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
636219820Sjeff	return status;
637219820Sjeff}
638219820Sjeff
639219820Sjeff/*
640219820Sjeff *  NAME            __osmv_dispatch_accept_seg()
641219820Sjeff *
642219820Sjeff *  DESCRIPTION     Store a DATA segment at the RMPP receiver side,
643219820Sjeff *                  if one is received in order.
644219820Sjeff */
645219820Sjeff
646219820Sjeffstatic ib_api_status_t
647219820Sjeff__osmv_dispatch_accept_seg(IN osm_bind_handle_t h_bind,
648219820Sjeff			   IN osmv_txn_ctx_t * p_txn, IN const ib_mad_t * p_mad)
649219820Sjeff{
650219820Sjeff	ib_api_status_t ret = IB_SUCCESS;
651219820Sjeff	uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_mad)->seg_num);
652219820Sjeff	osmv_rmpp_recv_ctx_t *p_recv_ctx = osmv_txn_get_rmpp_recv_ctx(p_txn);
653219820Sjeff	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
654219820Sjeff	uint64_t tid = osmv_txn_get_tid(p_txn);
655219820Sjeff
656219820Sjeff	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
657219820Sjeff
658219820Sjeff	if (seg_num != p_recv_ctx->expected_seg) {
659219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
660219820Sjeff			"TID 0x%llX: can't accept this segment (%d) - "
661219820Sjeff			"this is a Go-Back-N implementation\n", tid, seg_num);
662219820Sjeff		return IB_INSUFFICIENT_RESOURCES;
663219820Sjeff	}
664219820Sjeff
665219820Sjeff	/* Store the packet's copy in the reassembly list.
666219820Sjeff	 * Promote the expected segment counter.
667219820Sjeff	 */
668219820Sjeff	ret = osmv_rmpp_recv_ctx_store_mad_seg(p_recv_ctx, (uint8_t *) p_mad);
669219820Sjeff	if (IB_SUCCESS != ret) {
670219820Sjeff		return ret;
671219820Sjeff	}
672219820Sjeff
673219820Sjeff	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
674219820Sjeff		"TID 0x%llX: segment %d accepted\n", tid, seg_num);
675219820Sjeff	p_recv_ctx->expected_seg = seg_num + 1;
676219820Sjeff
677219820Sjeff	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
678219820Sjeff	return IB_SUCCESS;
679219820Sjeff}
680219820Sjeff
681219820Sjeff/*
682219820Sjeff *  NAME              __osmv_dispatch_send_ack()
683219820Sjeff *
684219820Sjeff *  DESCRIPTION
685219820Sjeff *
686219820Sjeff *  ISSUES
687219820Sjeff *    Consider sending the ACK from an async thread
688219820Sjeff *    if problems with the receiving side processing arise.
689219820Sjeff */
690219820Sjeff
691219820Sjeffstatic void
692219820Sjeff__osmv_dispatch_send_ack(IN osm_bind_handle_t h_bind,
693219820Sjeff			 IN const ib_mad_t * p_req_mad,
694219820Sjeff			 IN osmv_txn_ctx_t * p_txn,
695219820Sjeff			 IN const osm_mad_addr_t * p_mad_addr)
696219820Sjeff{
697219820Sjeff	osmv_rmpp_recv_ctx_t *p_recv_ctx = osmv_txn_get_rmpp_recv_ctx(p_txn);
698219820Sjeff
699219820Sjeff	/* ACK the segment # that was accepted */
700219820Sjeff	uint32_t seg_num = cl_ntoh32(((ib_rmpp_mad_t *) p_req_mad)->seg_num);
701219820Sjeff
702219820Sjeff	/* NOTE! The receiver can publish the New Window Last (NWL) value
703219820Sjeff	 * that is greater than the total number of segments to be sent.
704219820Sjeff	 * It's the sender's responsibility to compute the correct number
705219820Sjeff	 * of segments to send in the next burst.
706219820Sjeff	 */
707219820Sjeff	uint32_t nwl = p_recv_ctx->expected_seg + OSMV_RMPP_RECV_WIN - 1;
708219820Sjeff
709219820Sjeff	osmv_rmpp_send_ack(h_bind, p_req_mad, seg_num, nwl, p_mad_addr);
710219820Sjeff}
711