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_sender.h>
42#include <vendor/osm_vendor_mlx_transport.h>
43#include <vendor/osm_vendor_mlx_svc.h>
44#include <vendor/osm_pkt_randomizer.h>
45
46static ib_api_status_t
47__osmv_rmpp_send_segment(IN osm_bind_handle_t h_bind,
48			 IN osmv_txn_ctx_t * p_txn, IN uint32_t seg_num);
49
50/****d* OSM Vendor/osmv_simple_send_madw
51 * NAME
52 *   osmv_simple_send_madw
53 *
54 * DESCRIPTION
55 *   Send a single MAD (256 bytes).
56 *
57 *   If this MAD requires a response, set the timeout event.
58 *   The function call returns when the MAD's send completion is received.
59 *
60 */
61
62ib_api_status_t
63osmv_simple_send_madw(IN osm_bind_handle_t h_bind,
64		      IN osm_madw_t * const p_madw,
65		      IN osmv_txn_ctx_t * p_txn, IN boolean_t is_retry)
66{
67	ib_api_status_t ret;
68	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
69	osm_mad_addr_t *p_mad_addr = osm_madw_get_mad_addr_ptr(p_madw);
70	uint8_t mad_buf[MAD_BLOCK_SIZE];
71	ib_mad_t *p_mad = (ib_mad_t *) mad_buf;
72	uint64_t key = 0;
73
74	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
75
76	CL_ASSERT(p_madw->mad_size <= MAD_BLOCK_SIZE);
77
78	memset(p_mad, 0, MAD_BLOCK_SIZE);
79	memcpy(p_mad, osm_madw_get_mad_ptr(p_madw), p_madw->mad_size);
80
81	if (NULL != p_txn) {
82		/* Push a fake txn id to the MAD */
83		key = osmv_txn_get_key(p_txn);
84		p_mad->trans_id = cl_hton64(key);
85	}
86
87	/*
88	   Add call for packet drop randomizer.
89	   This is a testing feature. If run_randomizer flag is set to TRUE,
90	   the randomizer will be called, and randomally will drop
91	   a packet. This is used for simulating unstable fabric.
92	 */
93	if (p_bo->p_vendor->run_randomizer == TRUE) {
94		/* Try the randomizer */
95		if (osm_pkt_randomizer_mad_drop(p_bo->p_vendor->p_log,
96						p_bo->p_vendor->
97						p_pkt_randomizer,
98						p_mad) == TRUE) {
99			osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
100				"The MAD will not be sent. \n");
101			ret = IB_SUCCESS;
102		} else {
103			ret =
104			    osmv_transport_mad_send(h_bind, p_mad, p_mad_addr);
105		}
106	} else {
107		ret = osmv_transport_mad_send(h_bind, p_mad, p_mad_addr);
108	}
109
110	if ((IB_SUCCESS == ret) && (NULL != p_txn) && (!is_retry)) {
111		/* Set the timeout for receiving the response MAD */
112		ret = osmv_txn_set_timeout_ev(h_bind, key,
113					      p_bo->p_vendor->resp_timeout);
114	}
115
116	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
117	return ret;
118}
119
120/***** OSM Vendor/osmv_rmpp_send_madw
121 * NAME
122 *   osmv_rmpp_send_madw
123 *
124 * DESCRIPTION
125 * Send a single message (MAD wrapper of arbitrary length).
126 *      Follow the RMPP semantics
127 *      (segmentation, send window, timeouts etc).
128 *
129 *      The function call returns either when the whole message
130 *      has been acknowledged, or upon error.
131 *
132 *  ASSUMPTIONS
133 *      The RMPP sender context is set up
134 */
135
136ib_api_status_t
137osmv_rmpp_send_madw(IN osm_bind_handle_t h_bind,
138		    IN osm_madw_t * const p_madw,
139		    IN osmv_txn_ctx_t * p_txn, IN boolean_t is_rmpp_ds)
140{
141	ib_api_status_t ret = IB_SUCCESS;
142	uint32_t i, total_segs;
143
144	osmv_rmpp_send_ctx_t *p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
145	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
146
147	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
148
149	total_segs = osmv_rmpp_send_ctx_get_num_segs(p_send_ctx);
150	CL_ASSERT(total_segs >= 1);
151
152	/* In the double-sided transfer, wait for ACK 0 */
153
154	for (;;) {
155
156		if (p_send_ctx->window_first > total_segs) {
157
158			/* Every segment is acknowledged */
159			break;
160		}
161
162		/* Send the next burst. */
163		for (i = p_send_ctx->window_first; i <= p_send_ctx->window_last;
164		     i++) {
165
166			/* Send a segment and setup a timeout timer */
167			ret = __osmv_rmpp_send_segment(h_bind, p_txn, i);
168			if (IB_SUCCESS != ret) {
169				goto send_done;
170			}
171		}
172
173		/* Set the Response Timeout for the ACK on the last DATA segment */
174		ret = osmv_txn_set_timeout_ev(h_bind, osmv_txn_get_key(p_txn),
175					      p_bo->p_vendor->resp_timeout);
176		if (IB_SUCCESS != ret) {
177			goto send_done;
178		}
179
180		/* Going to sleep. Let the others access the transaction DB */
181		osmv_txn_unlock(p_bo);
182
183		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
184			"RMPP Sender thread (madw=%p) going to sleep ...\n",
185			p_madw);
186
187		/* Await the next event to happen */
188		cl_event_wait_on(&p_send_ctx->event,
189				 EVENT_NO_TIMEOUT, TRUE /* interruptible */ );
190
191		/* Got a signal from the MAD dispatcher/timeout handler */
192		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
193			"RMPP Sender thread (madw=%p) waking up on a signal ...\n",
194			p_madw);
195
196		/* Let's see what changed... Make this atomic - re-acquire the lock. */
197		osmv_txn_lock(p_bo);
198
199		if (TRUE == p_bo->is_closing) {
200			osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
201				"osmv_rmpp_send_madw: ERR 6601: "
202				"The bind handle %p is being closed. "
203				"Stopping the RMPP Send of MADW %p\n",
204				h_bind, p_madw);
205
206			ret = IB_TIMEOUT;
207			return IB_INTERRUPTED;
208		}
209
210		/* STOP? ABORT? TIMEOUT? */
211		if (IB_SUCCESS != p_send_ctx->status) {
212			osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
213				"osmv_rmpp_send_madw: ERR 6602: "
214				"An error (%s) happened during the RMPP send of %p. Bailing out.\n",
215				ib_get_err_str(p_send_ctx->status), p_madw);
216			ret = p_send_ctx->status;
217			goto send_done;
218		}
219	}
220
221	if (TRUE == is_rmpp_ds) {
222		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
223			"Double-sided RMPP - switching to be the receiver.\n");
224
225		ret = osmv_txn_init_rmpp_receiver(h_bind, p_txn, FALSE
226						  /*Send was initiated by me */
227						  );
228
229		if (IB_SUCCESS == ret) {
230			/* Send ACK on the 0 segment */
231			ret = __osmv_rmpp_send_segment(h_bind, p_txn, 0);
232		}
233	}
234
235send_done:
236	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
237	return ret;
238}
239
240/*
241 *  NAME                osmv_rmpp_send_ack
242 *
243 *  DESCRIPTION
244 *
245 */
246
247ib_api_status_t
248osmv_rmpp_send_ack(IN osm_bind_handle_t h_bind,
249		   IN const ib_mad_t * p_req_mad,
250		   IN uint32_t seg_num,
251		   IN uint32_t nwl, IN const osm_mad_addr_t * p_mad_addr)
252{
253	uint8_t resp_mad[MAD_BLOCK_SIZE];
254	ib_rmpp_mad_t *p_resp_mad = (ib_rmpp_mad_t *) resp_mad;
255
256#ifdef OSMV_RANDOM_DROP
257	if (TRUE == osmv_random_drop()) {
258		osm_log(((osmv_bind_obj_t *) h_bind)->p_vendor->p_log,
259			OSM_LOG_DEBUG,
260			"Error injection - dropping the RMPP ACK\n");
261		return IB_SUCCESS;
262	}
263#endif
264
265	memcpy(p_resp_mad, p_req_mad, MAD_BLOCK_SIZE);
266
267	p_resp_mad->common_hdr.method = osmv_invert_method(p_req_mad->method);
268	p_resp_mad->rmpp_type = IB_RMPP_TYPE_ACK;
269	p_resp_mad->seg_num = cl_hton32(seg_num);
270	p_resp_mad->paylen_newwin = cl_hton32(nwl);
271	p_resp_mad->rmpp_flags = IB_RMPP_FLAG_ACTIVE;
272
273	return osmv_transport_mad_send(h_bind, p_resp_mad, p_mad_addr);
274}
275
276/*
277 *  NAME            osmv_rmpp_send_nak
278 *
279 *  DESCRIPTION     Send the RMPP ABORT or STOP packet
280 */
281
282ib_api_status_t
283osmv_rmpp_send_nak(IN osm_bind_handle_t h_bind,
284		   IN const ib_mad_t * p_req_mad,
285		   IN const osm_mad_addr_t * p_mad_addr,
286		   IN uint8_t nak_type, IN uint8_t status)
287{
288	uint8_t resp_mad[MAD_BLOCK_SIZE];
289	ib_rmpp_mad_t *p_resp_mad = (ib_rmpp_mad_t *) resp_mad;
290
291	memcpy(p_resp_mad, p_req_mad, MAD_BLOCK_SIZE);
292
293	p_resp_mad->common_hdr.method = osmv_invert_method(p_req_mad->method);
294	p_resp_mad->rmpp_type = nak_type;
295	p_resp_mad->rmpp_status = status;
296
297	return osmv_transport_mad_send(h_bind, p_resp_mad, p_mad_addr);
298}
299
300/*
301 *  NAME              __osmv_rmpp_send_segment
302 *
303 *  DESCRIPTION       Build a MAD for a specific segment and send it
304 */
305
306static ib_api_status_t
307__osmv_rmpp_send_segment(IN osm_bind_handle_t h_bind,
308			 IN osmv_txn_ctx_t * p_txn, IN uint32_t seg_num)
309{
310	ib_api_status_t ret;
311	osmv_rmpp_send_ctx_t *p_send_ctx;
312	uint8_t mad_buf[MAD_BLOCK_SIZE];
313	ib_mad_t *p_mad = (ib_mad_t *) mad_buf;
314	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
315	osm_mad_addr_t *p_mad_addr =
316	    osm_madw_get_mad_addr_ptr(osmv_txn_get_madw(p_txn));
317	uint32_t timeout = p_bo->p_vendor->resp_timeout;
318	uint64_t key;
319
320	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
321
322#ifdef OSMV_RANDOM_DROP
323	if (TRUE == osmv_random_drop()) {
324
325		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
326			"Error injection - simulating the RMPP segment drop\n");
327		return IB_SUCCESS;
328	}
329#endif
330
331	p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
332	key = osmv_txn_get_key(p_txn);
333
334	if (0 != seg_num) {
335		ret =
336		    osmv_rmpp_send_ctx_get_seg(p_send_ctx, seg_num, timeout,
337					       p_mad);
338		CL_ASSERT(IB_SUCCESS == ret);
339
340		/* Put the segment to the wire ! */
341		p_mad->trans_id = cl_hton64(key);
342
343		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
344			"Sending RMPP segment #%d, on-wire TID=0x%" PRIx64 "\n",
345			seg_num, p_mad->trans_id);
346
347		/*
348		   Add call for packet drop randomizer.
349		   This is a testing feature. If run_randomizer flag is set to TRUE,
350		   the randomizer will be called, and randomally will drop
351		   a packet. This is used for simulating unstable fabric.
352		 */
353		if (p_bo->p_vendor->run_randomizer == TRUE) {
354			/* Try the randomizer */
355			if (osm_pkt_randomizer_mad_drop(p_bo->p_vendor->p_log,
356							p_bo->p_vendor->
357							p_pkt_randomizer,
358							p_mad) == TRUE) {
359				osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
360					"The MAD will not be sent. \n");
361				ret = IB_SUCCESS;
362			} else {
363				ret =
364				    osmv_transport_mad_send((osm_bind_handle_t)
365							    p_bo, p_mad,
366							    p_mad_addr);
367			}
368		} else {
369			ret =
370			    osmv_transport_mad_send((osm_bind_handle_t) p_bo,
371						    p_mad, p_mad_addr);
372		}
373	} else {
374		/* This is an ACK for double-sided handshake. Give it a special treatment. */
375
376		/* It doesn't really matter which data to put. Only the header matters. */
377		ret = osmv_rmpp_send_ctx_get_seg(p_send_ctx, 1, timeout, p_mad);
378		CL_ASSERT(IB_SUCCESS == ret);
379
380		p_mad->trans_id = cl_hton64(key);
381		ret =
382		    osmv_rmpp_send_ack((osm_bind_handle_t) p_bo, p_mad,
383				       0 /* segnum */ ,
384				       OSMV_RMPP_RECV_WIN /* NWL */ ,
385				       p_mad_addr);
386	}
387
388	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
389	return ret;
390}
391