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_sender.h>
42219820Sjeff#include <vendor/osm_vendor_mlx_transport.h>
43219820Sjeff#include <vendor/osm_vendor_mlx_svc.h>
44219820Sjeff#include <vendor/osm_pkt_randomizer.h>
45219820Sjeff
46219820Sjeffstatic ib_api_status_t
47219820Sjeff__osmv_rmpp_send_segment(IN osm_bind_handle_t h_bind,
48219820Sjeff			 IN osmv_txn_ctx_t * p_txn, IN uint32_t seg_num);
49219820Sjeff
50219820Sjeff/****d* OSM Vendor/osmv_simple_send_madw
51219820Sjeff * NAME
52219820Sjeff *   osmv_simple_send_madw
53219820Sjeff *
54219820Sjeff * DESCRIPTION
55219820Sjeff *   Send a single MAD (256 bytes).
56219820Sjeff *
57219820Sjeff *   If this MAD requires a response, set the timeout event.
58219820Sjeff *   The function call returns when the MAD's send completion is received.
59219820Sjeff *
60219820Sjeff */
61219820Sjeff
62219820Sjeffib_api_status_t
63219820Sjeffosmv_simple_send_madw(IN osm_bind_handle_t h_bind,
64219820Sjeff		      IN osm_madw_t * const p_madw,
65219820Sjeff		      IN osmv_txn_ctx_t * p_txn, IN boolean_t is_retry)
66219820Sjeff{
67219820Sjeff	ib_api_status_t ret;
68219820Sjeff	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
69219820Sjeff	osm_mad_addr_t *p_mad_addr = osm_madw_get_mad_addr_ptr(p_madw);
70219820Sjeff	uint8_t mad_buf[MAD_BLOCK_SIZE];
71219820Sjeff	ib_mad_t *p_mad = (ib_mad_t *) mad_buf;
72219820Sjeff	uint64_t key = 0;
73219820Sjeff
74219820Sjeff	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
75219820Sjeff
76219820Sjeff	CL_ASSERT(p_madw->mad_size <= MAD_BLOCK_SIZE);
77219820Sjeff
78219820Sjeff	memset(p_mad, 0, MAD_BLOCK_SIZE);
79219820Sjeff	memcpy(p_mad, osm_madw_get_mad_ptr(p_madw), p_madw->mad_size);
80219820Sjeff
81219820Sjeff	if (NULL != p_txn) {
82219820Sjeff		/* Push a fake txn id to the MAD */
83219820Sjeff		key = osmv_txn_get_key(p_txn);
84219820Sjeff		p_mad->trans_id = cl_hton64(key);
85219820Sjeff	}
86219820Sjeff
87219820Sjeff	/*
88219820Sjeff	   Add call for packet drop randomizer.
89219820Sjeff	   This is a testing feature. If run_randomizer flag is set to TRUE,
90219820Sjeff	   the randomizer will be called, and randomally will drop
91219820Sjeff	   a packet. This is used for simulating unstable fabric.
92219820Sjeff	 */
93219820Sjeff	if (p_bo->p_vendor->run_randomizer == TRUE) {
94219820Sjeff		/* Try the randomizer */
95219820Sjeff		if (osm_pkt_randomizer_mad_drop(p_bo->p_vendor->p_log,
96219820Sjeff						p_bo->p_vendor->
97219820Sjeff						p_pkt_randomizer,
98219820Sjeff						p_mad) == TRUE) {
99219820Sjeff			osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
100219820Sjeff				"The MAD will not be sent. \n");
101219820Sjeff			ret = IB_SUCCESS;
102219820Sjeff		} else {
103219820Sjeff			ret =
104219820Sjeff			    osmv_transport_mad_send(h_bind, p_mad, p_mad_addr);
105219820Sjeff		}
106219820Sjeff	} else {
107219820Sjeff		ret = osmv_transport_mad_send(h_bind, p_mad, p_mad_addr);
108219820Sjeff	}
109219820Sjeff
110219820Sjeff	if ((IB_SUCCESS == ret) && (NULL != p_txn) && (!is_retry)) {
111219820Sjeff		/* Set the timeout for receiving the response MAD */
112219820Sjeff		ret = osmv_txn_set_timeout_ev(h_bind, key,
113219820Sjeff					      p_bo->p_vendor->resp_timeout);
114219820Sjeff	}
115219820Sjeff
116219820Sjeff	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
117219820Sjeff	return ret;
118219820Sjeff}
119219820Sjeff
120219820Sjeff/***** OSM Vendor/osmv_rmpp_send_madw
121219820Sjeff * NAME
122219820Sjeff *   osmv_rmpp_send_madw
123219820Sjeff *
124219820Sjeff * DESCRIPTION
125219820Sjeff * Send a single message (MAD wrapper of arbitrary length).
126219820Sjeff *      Follow the RMPP semantics
127219820Sjeff *      (segmentation, send window, timeouts etc).
128219820Sjeff *
129219820Sjeff *      The function call returns either when the whole message
130219820Sjeff *      has been acknowledged, or upon error.
131219820Sjeff *
132219820Sjeff *  ASSUMPTIONS
133219820Sjeff *      The RMPP sender context is set up
134219820Sjeff */
135219820Sjeff
136219820Sjeffib_api_status_t
137219820Sjeffosmv_rmpp_send_madw(IN osm_bind_handle_t h_bind,
138219820Sjeff		    IN osm_madw_t * const p_madw,
139219820Sjeff		    IN osmv_txn_ctx_t * p_txn, IN boolean_t is_rmpp_ds)
140219820Sjeff{
141219820Sjeff	ib_api_status_t ret = IB_SUCCESS;
142219820Sjeff	uint32_t i, total_segs;
143219820Sjeff
144219820Sjeff	osmv_rmpp_send_ctx_t *p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
145219820Sjeff	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
146219820Sjeff
147219820Sjeff	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
148219820Sjeff
149219820Sjeff	total_segs = osmv_rmpp_send_ctx_get_num_segs(p_send_ctx);
150219820Sjeff	CL_ASSERT(total_segs >= 1);
151219820Sjeff
152219820Sjeff	/* In the double-sided transfer, wait for ACK 0 */
153219820Sjeff
154219820Sjeff	for (;;) {
155219820Sjeff
156219820Sjeff		if (p_send_ctx->window_first > total_segs) {
157219820Sjeff
158219820Sjeff			/* Every segment is acknowledged */
159219820Sjeff			break;
160219820Sjeff		}
161219820Sjeff
162219820Sjeff		/* Send the next burst. */
163219820Sjeff		for (i = p_send_ctx->window_first; i <= p_send_ctx->window_last;
164219820Sjeff		     i++) {
165219820Sjeff
166219820Sjeff			/* Send a segment and setup a timeout timer */
167219820Sjeff			ret = __osmv_rmpp_send_segment(h_bind, p_txn, i);
168219820Sjeff			if (IB_SUCCESS != ret) {
169219820Sjeff				goto send_done;
170219820Sjeff			}
171219820Sjeff		}
172219820Sjeff
173219820Sjeff		/* Set the Response Timeout for the ACK on the last DATA segment */
174219820Sjeff		ret = osmv_txn_set_timeout_ev(h_bind, osmv_txn_get_key(p_txn),
175219820Sjeff					      p_bo->p_vendor->resp_timeout);
176219820Sjeff		if (IB_SUCCESS != ret) {
177219820Sjeff			goto send_done;
178219820Sjeff		}
179219820Sjeff
180219820Sjeff		/* Going to sleep. Let the others access the transaction DB */
181219820Sjeff		osmv_txn_unlock(p_bo);
182219820Sjeff
183219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
184219820Sjeff			"RMPP Sender thread (madw=%p) going to sleep ...\n",
185219820Sjeff			p_madw);
186219820Sjeff
187219820Sjeff		/* Await the next event to happen */
188219820Sjeff		cl_event_wait_on(&p_send_ctx->event,
189219820Sjeff				 EVENT_NO_TIMEOUT, TRUE /* interruptible */ );
190219820Sjeff
191219820Sjeff		/* Got a signal from the MAD dispatcher/timeout handler */
192219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
193219820Sjeff			"RMPP Sender thread (madw=%p) waking up on a signal ...\n",
194219820Sjeff			p_madw);
195219820Sjeff
196219820Sjeff		/* Let's see what changed... Make this atomic - re-acquire the lock. */
197219820Sjeff		osmv_txn_lock(p_bo);
198219820Sjeff
199219820Sjeff		if (TRUE == p_bo->is_closing) {
200219820Sjeff			osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
201219820Sjeff				"osmv_rmpp_send_madw: ERR 6601: "
202219820Sjeff				"The bind handle %p is being closed. "
203219820Sjeff				"Stopping the RMPP Send of MADW %p\n",
204219820Sjeff				h_bind, p_madw);
205219820Sjeff
206219820Sjeff			ret = IB_TIMEOUT;
207219820Sjeff			return IB_INTERRUPTED;
208219820Sjeff		}
209219820Sjeff
210219820Sjeff		/* STOP? ABORT? TIMEOUT? */
211219820Sjeff		if (IB_SUCCESS != p_send_ctx->status) {
212219820Sjeff			osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
213219820Sjeff				"osmv_rmpp_send_madw: ERR 6602: "
214219820Sjeff				"An error (%s) happened during the RMPP send of %p. Bailing out.\n",
215219820Sjeff				ib_get_err_str(p_send_ctx->status), p_madw);
216219820Sjeff			ret = p_send_ctx->status;
217219820Sjeff			goto send_done;
218219820Sjeff		}
219219820Sjeff	}
220219820Sjeff
221219820Sjeff	if (TRUE == is_rmpp_ds) {
222219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
223219820Sjeff			"Double-sided RMPP - switching to be the receiver.\n");
224219820Sjeff
225219820Sjeff		ret = osmv_txn_init_rmpp_receiver(h_bind, p_txn, FALSE
226219820Sjeff						  /*Send was initiated by me */
227219820Sjeff						  );
228219820Sjeff
229219820Sjeff		if (IB_SUCCESS == ret) {
230219820Sjeff			/* Send ACK on the 0 segment */
231219820Sjeff			ret = __osmv_rmpp_send_segment(h_bind, p_txn, 0);
232219820Sjeff		}
233219820Sjeff	}
234219820Sjeff
235219820Sjeffsend_done:
236219820Sjeff	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
237219820Sjeff	return ret;
238219820Sjeff}
239219820Sjeff
240219820Sjeff/*
241219820Sjeff *  NAME                osmv_rmpp_send_ack
242219820Sjeff *
243219820Sjeff *  DESCRIPTION
244219820Sjeff *
245219820Sjeff */
246219820Sjeff
247219820Sjeffib_api_status_t
248219820Sjeffosmv_rmpp_send_ack(IN osm_bind_handle_t h_bind,
249219820Sjeff		   IN const ib_mad_t * p_req_mad,
250219820Sjeff		   IN uint32_t seg_num,
251219820Sjeff		   IN uint32_t nwl, IN const osm_mad_addr_t * p_mad_addr)
252219820Sjeff{
253219820Sjeff	uint8_t resp_mad[MAD_BLOCK_SIZE];
254219820Sjeff	ib_rmpp_mad_t *p_resp_mad = (ib_rmpp_mad_t *) resp_mad;
255219820Sjeff
256219820Sjeff#ifdef OSMV_RANDOM_DROP
257219820Sjeff	if (TRUE == osmv_random_drop()) {
258219820Sjeff		osm_log(((osmv_bind_obj_t *) h_bind)->p_vendor->p_log,
259219820Sjeff			OSM_LOG_DEBUG,
260219820Sjeff			"Error injection - dropping the RMPP ACK\n");
261219820Sjeff		return IB_SUCCESS;
262219820Sjeff	}
263219820Sjeff#endif
264219820Sjeff
265219820Sjeff	memcpy(p_resp_mad, p_req_mad, MAD_BLOCK_SIZE);
266219820Sjeff
267219820Sjeff	p_resp_mad->common_hdr.method = osmv_invert_method(p_req_mad->method);
268219820Sjeff	p_resp_mad->rmpp_type = IB_RMPP_TYPE_ACK;
269219820Sjeff	p_resp_mad->seg_num = cl_hton32(seg_num);
270219820Sjeff	p_resp_mad->paylen_newwin = cl_hton32(nwl);
271219820Sjeff	p_resp_mad->rmpp_flags = IB_RMPP_FLAG_ACTIVE;
272219820Sjeff
273219820Sjeff	return osmv_transport_mad_send(h_bind, p_resp_mad, p_mad_addr);
274219820Sjeff}
275219820Sjeff
276219820Sjeff/*
277219820Sjeff *  NAME            osmv_rmpp_send_nak
278219820Sjeff *
279219820Sjeff *  DESCRIPTION     Send the RMPP ABORT or STOP packet
280219820Sjeff */
281219820Sjeff
282219820Sjeffib_api_status_t
283219820Sjeffosmv_rmpp_send_nak(IN osm_bind_handle_t h_bind,
284219820Sjeff		   IN const ib_mad_t * p_req_mad,
285219820Sjeff		   IN const osm_mad_addr_t * p_mad_addr,
286219820Sjeff		   IN uint8_t nak_type, IN uint8_t status)
287219820Sjeff{
288219820Sjeff	uint8_t resp_mad[MAD_BLOCK_SIZE];
289219820Sjeff	ib_rmpp_mad_t *p_resp_mad = (ib_rmpp_mad_t *) resp_mad;
290219820Sjeff
291219820Sjeff	memcpy(p_resp_mad, p_req_mad, MAD_BLOCK_SIZE);
292219820Sjeff
293219820Sjeff	p_resp_mad->common_hdr.method = osmv_invert_method(p_req_mad->method);
294219820Sjeff	p_resp_mad->rmpp_type = nak_type;
295219820Sjeff	p_resp_mad->rmpp_status = status;
296219820Sjeff
297219820Sjeff	return osmv_transport_mad_send(h_bind, p_resp_mad, p_mad_addr);
298219820Sjeff}
299219820Sjeff
300219820Sjeff/*
301219820Sjeff *  NAME              __osmv_rmpp_send_segment
302219820Sjeff *
303219820Sjeff *  DESCRIPTION       Build a MAD for a specific segment and send it
304219820Sjeff */
305219820Sjeff
306219820Sjeffstatic ib_api_status_t
307219820Sjeff__osmv_rmpp_send_segment(IN osm_bind_handle_t h_bind,
308219820Sjeff			 IN osmv_txn_ctx_t * p_txn, IN uint32_t seg_num)
309219820Sjeff{
310219820Sjeff	ib_api_status_t ret;
311219820Sjeff	osmv_rmpp_send_ctx_t *p_send_ctx;
312219820Sjeff	uint8_t mad_buf[MAD_BLOCK_SIZE];
313219820Sjeff	ib_mad_t *p_mad = (ib_mad_t *) mad_buf;
314219820Sjeff	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
315219820Sjeff	osm_mad_addr_t *p_mad_addr =
316219820Sjeff	    osm_madw_get_mad_addr_ptr(osmv_txn_get_madw(p_txn));
317219820Sjeff	uint32_t timeout = p_bo->p_vendor->resp_timeout;
318219820Sjeff	uint64_t key;
319219820Sjeff
320219820Sjeff	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
321219820Sjeff
322219820Sjeff#ifdef OSMV_RANDOM_DROP
323219820Sjeff	if (TRUE == osmv_random_drop()) {
324219820Sjeff
325219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
326219820Sjeff			"Error injection - simulating the RMPP segment drop\n");
327219820Sjeff		return IB_SUCCESS;
328219820Sjeff	}
329219820Sjeff#endif
330219820Sjeff
331219820Sjeff	p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
332219820Sjeff	key = osmv_txn_get_key(p_txn);
333219820Sjeff
334219820Sjeff	if (0 != seg_num) {
335219820Sjeff		ret =
336219820Sjeff		    osmv_rmpp_send_ctx_get_seg(p_send_ctx, seg_num, timeout,
337219820Sjeff					       p_mad);
338219820Sjeff		CL_ASSERT(IB_SUCCESS == ret);
339219820Sjeff
340219820Sjeff		/* Put the segment to the wire ! */
341219820Sjeff		p_mad->trans_id = cl_hton64(key);
342219820Sjeff
343219820Sjeff		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
344219820Sjeff			"Sending RMPP segment #%d, on-wire TID=0x%llX\n",
345219820Sjeff			seg_num, p_mad->trans_id);
346219820Sjeff
347219820Sjeff		/*
348219820Sjeff		   Add call for packet drop randomizer.
349219820Sjeff		   This is a testing feature. If run_randomizer flag is set to TRUE,
350219820Sjeff		   the randomizer will be called, and randomally will drop
351219820Sjeff		   a packet. This is used for simulating unstable fabric.
352219820Sjeff		 */
353219820Sjeff		if (p_bo->p_vendor->run_randomizer == TRUE) {
354219820Sjeff			/* Try the randomizer */
355219820Sjeff			if (osm_pkt_randomizer_mad_drop(p_bo->p_vendor->p_log,
356219820Sjeff							p_bo->p_vendor->
357219820Sjeff							p_pkt_randomizer,
358219820Sjeff							p_mad) == TRUE) {
359219820Sjeff				osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
360219820Sjeff					"The MAD will not be sent. \n");
361219820Sjeff				ret = IB_SUCCESS;
362219820Sjeff			} else {
363219820Sjeff				ret =
364219820Sjeff				    osmv_transport_mad_send((osm_bind_handle_t)
365219820Sjeff							    p_bo, p_mad,
366219820Sjeff							    p_mad_addr);
367219820Sjeff			}
368219820Sjeff		} else {
369219820Sjeff			ret =
370219820Sjeff			    osmv_transport_mad_send((osm_bind_handle_t) p_bo,
371219820Sjeff						    p_mad, p_mad_addr);
372219820Sjeff		}
373219820Sjeff	} else {
374219820Sjeff		/* This is an ACK for double-sided handshake. Give it a special treatment. */
375219820Sjeff
376219820Sjeff		/* It doesn't really matter which data to put. Only the header matters. */
377219820Sjeff		ret = osmv_rmpp_send_ctx_get_seg(p_send_ctx, 1, timeout, p_mad);
378219820Sjeff		CL_ASSERT(IB_SUCCESS == ret);
379219820Sjeff
380219820Sjeff		p_mad->trans_id = cl_hton64(key);
381219820Sjeff		ret =
382219820Sjeff		    osmv_rmpp_send_ack((osm_bind_handle_t) p_bo, p_mad,
383219820Sjeff				       0 /* segnum */ ,
384219820Sjeff				       OSMV_RMPP_RECV_WIN /* NWL */ ,
385219820Sjeff				       p_mad_addr);
386219820Sjeff	}
387219820Sjeff
388219820Sjeff	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
389219820Sjeff	return ret;
390219820Sjeff}
391