1/*
2 * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2005,2009 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 <stdlib.h>
41
42#include <vendor/osm_vendor_mlx.h>
43#include <vendor/osm_vendor_mlx_defs.h>
44#include <vendor/osm_vendor_mlx_txn.h>
45#include <vendor/osm_vendor_mlx_svc.h>
46#include <vendor/osm_vendor_mlx_sender.h>
47
48static ib_api_status_t
49__osmv_txnmgr_lookup(IN osmv_txn_mgr_t * p_tx_mgr,
50		     IN uint64_t key, OUT osmv_txn_ctx_t ** pp_txn);
51
52static ib_api_status_t
53__osmv_txnmgr_insert_txn(IN osmv_txn_mgr_t * p_tx_mgr,
54			 IN osmv_txn_ctx_t * p_txn, IN uint64_t key);
55
56static ib_api_status_t
57__osmv_txnmgr_remove_txn(IN osmv_txn_mgr_t * p_tx_mgr,
58			 IN uint64_t key, OUT osmv_txn_ctx_t ** pp_txn);
59
60static void __osmv_txn_all_done(osm_bind_handle_t h_bind);
61
62static uint64_t
63__osmv_txn_timeout_cb(IN uint64_t key,
64		      IN uint32_t num_regs, IN void *cb_context);
65
66ib_api_status_t
67osmv_txn_init(IN osm_bind_handle_t h_bind,
68	      IN uint64_t tid, IN uint64_t key, OUT osmv_txn_ctx_t ** pp_txn)
69{
70	ib_api_status_t st;
71	osmv_txn_ctx_t *p_txn;
72	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
73
74	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
75
76	CL_ASSERT(NULL != h_bind && NULL != pp_txn);
77
78	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
79		"Starting transaction 0x%016" PRIx64
80		" (key=0x%016" PRIx64 ")\n", tid, key);
81
82	p_txn = malloc(sizeof(osmv_txn_ctx_t));
83	if (!p_txn) {
84		return IB_INSUFFICIENT_MEMORY;
85	}
86
87	memset(p_txn, 0, sizeof(osmv_txn_ctx_t));
88	p_txn->p_log = p_bo->txn_mgr.p_log;
89	p_txn->tid = tid;
90	p_txn->key = key;
91	p_txn->p_madw = NULL;
92	p_txn->rmpp_txfr.rmpp_state = OSMV_TXN_RMPP_NONE;
93
94	/* insert into transaction manager DB */
95	st = __osmv_txnmgr_insert_txn(&p_bo->txn_mgr, p_txn, key);
96	if (IB_SUCCESS != st) {
97		osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
98			"osmv_txn_init: ERR 6703: "
99			"Failed to insert to transaction 0x%016" PRIx64
100			" (key=0x%016" PRIx64 ") to manager DB\n",
101			tid, key);
102		goto insert_txn_failed;
103	}
104
105	*pp_txn = p_txn;
106	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
107	return IB_SUCCESS;
108
109insert_txn_failed:
110	free(p_txn);
111
112	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
113	return st;
114}
115
116ib_api_status_t
117osmv_txn_init_rmpp_sender(IN osm_bind_handle_t h_bind,
118			  IN osmv_txn_ctx_t * p_txn, IN osm_madw_t * p_madw)
119{
120	ib_api_status_t st;
121
122	CL_ASSERT(p_txn);
123
124	/* Double-Sided RMPP Direction Switch */
125	osmv_txn_remove_timeout_ev(h_bind, osmv_txn_get_key(p_txn));
126
127	p_txn->rmpp_txfr.rmpp_state = OSMV_TXN_RMPP_SENDER;
128	p_txn->rmpp_txfr.p_rmpp_send_ctx = malloc(sizeof(osmv_rmpp_send_ctx_t));
129
130	if (!p_txn->rmpp_txfr.p_rmpp_send_ctx) {
131		return IB_INSUFFICIENT_MEMORY;
132	}
133
134	memset(p_txn->rmpp_txfr.p_rmpp_send_ctx, 0,
135	       sizeof(osmv_rmpp_send_ctx_t));
136
137	st = osmv_rmpp_send_ctx_init(p_txn->rmpp_txfr.p_rmpp_send_ctx,
138				     (void *)p_madw->p_mad,
139				     p_madw->mad_size, p_txn->p_log);
140	return st;
141}
142
143ib_api_status_t
144osmv_txn_init_rmpp_receiver(IN osm_bind_handle_t h_bind,
145			    IN osmv_txn_ctx_t * p_txn,
146			    IN boolean_t is_init_by_peer)
147{
148	ib_api_status_t st;
149	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
150	uint64_t key = osmv_txn_get_key(p_txn);
151
152	CL_ASSERT(p_txn);
153
154	/* Double-Sided RMPP Direction Switch */
155	osmv_txn_remove_timeout_ev(h_bind, key);
156
157	/* Set the Transaction Timeout value */
158	st = osmv_txn_set_timeout_ev(h_bind, key,
159				     p_bo->p_vendor->ttime_timeout);
160	if (IB_SUCCESS != st) {
161
162		return st;
163	}
164
165	p_txn->rmpp_txfr.rmpp_state = OSMV_TXN_RMPP_RECEIVER;
166	p_txn->rmpp_txfr.is_rmpp_init_by_peer = is_init_by_peer;
167
168	p_txn->rmpp_txfr.p_rmpp_recv_ctx = malloc(sizeof(osmv_rmpp_recv_ctx_t));
169
170	if (!p_txn->rmpp_txfr.p_rmpp_recv_ctx) {
171
172		osmv_txn_remove_timeout_ev(h_bind, key);
173		return IB_INSUFFICIENT_MEMORY;
174	}
175
176	memset(p_txn->rmpp_txfr.p_rmpp_recv_ctx, 0,
177	       sizeof(osmv_rmpp_recv_ctx_t));
178
179	st = osmv_rmpp_recv_ctx_init(p_txn->rmpp_txfr.p_rmpp_recv_ctx,
180				     p_txn->p_log);
181
182	return st;
183}
184
185/*
186 * NAME
187 *  osmv_txn_set_timeout_ev
188 *
189 * DESCRIPTION
190 *
191 * SEE ALSO
192 *
193 */
194ib_api_status_t
195osmv_txn_set_timeout_ev(IN osm_bind_handle_t h_bind,
196			IN uint64_t key, IN uint64_t msec)
197{
198	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
199	cl_event_wheel_t *p_event_wheel = p_bo->txn_mgr.p_event_wheel;
200	cl_status_t status;
201
202	status = cl_event_wheel_reg(p_event_wheel, key, cl_get_time_stamp() + 1000 * msec,	/* TTL */
203				    __osmv_txn_timeout_cb,
204				    p_bo /* The context */ );
205
206	return (ib_api_status_t) status;
207}
208
209/*
210 * NAME
211 *  osmv_txn_remove_timeout_ev
212 *
213 * DESCRIPTION
214
215 * SEE ALSO
216 *
217 */
218void osmv_txn_remove_timeout_ev(IN osm_bind_handle_t h_bind, IN uint64_t key)
219{
220	cl_event_wheel_t *p_event_wheel =
221	    ((osmv_bind_obj_t *) h_bind)->txn_mgr.p_event_wheel;
222	cl_event_wheel_unreg(p_event_wheel, key);
223}
224
225void
226osmv_txn_done(IN osm_bind_handle_t h_bind,
227	      IN uint64_t key, IN boolean_t is_in_cb)
228{
229	osmv_txn_ctx_t *p_ctx;
230	osmv_bind_obj_t *const p_bo = (osmv_bind_obj_t *) h_bind;
231
232	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
233
234	CL_ASSERT(h_bind);
235
236	/* Cancel the (single) timeout possibly outstanding for this txn
237	 * Don't do this if you are in the callback context, for 2 reasons:
238	 * (1) The event wheel will remove the context itself.
239	 * (2) If we try to, there is a deadlock in the event wheel
240	 */
241	if (FALSE == is_in_cb) {
242		osmv_txn_remove_timeout_ev(h_bind, key);
243	}
244
245	/* Remove from DB */
246	if (IB_NOT_FOUND ==
247	    __osmv_txnmgr_remove_txn(&p_bo->txn_mgr, key, &p_ctx)) {
248		return;
249	}
250
251	/* Destroy the transaction's RMPP contexts
252	 * (can be more than one in the case of double sided transfer)
253	 */
254
255	if (p_ctx->rmpp_txfr.p_rmpp_send_ctx) {
256		osmv_rmpp_send_ctx_done(p_ctx->rmpp_txfr.p_rmpp_send_ctx);
257	}
258
259	if (p_ctx->rmpp_txfr.p_rmpp_recv_ctx) {
260		osmv_rmpp_recv_ctx_done(p_ctx->rmpp_txfr.p_rmpp_recv_ctx);
261	}
262
263	free(p_ctx);
264
265	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
266}
267
268ib_api_status_t
269osmv_txn_lookup(IN osm_bind_handle_t h_bind,
270		IN uint64_t key, OUT osmv_txn_ctx_t ** pp_txn)
271{
272	return __osmv_txnmgr_lookup(&(((osmv_bind_obj_t *) h_bind)->txn_mgr),
273				    key, pp_txn);
274}
275
276void osmv_txn_abort_rmpp_txns(osm_bind_handle_t h_bind)
277{
278	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
279	cl_map_item_t *p_item;
280	cl_map_obj_t *p_obj;
281	osmv_txn_ctx_t *p_txn;
282	osmv_rmpp_send_ctx_t *p_send_ctx;
283	cl_qmap_t *p_map = p_bo->txn_mgr.p_txn_map;
284
285	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
286
287	while (FALSE == cl_is_qmap_empty(p_map)) {
288
289		p_item = cl_qmap_head(p_map);
290		p_obj = PARENT_STRUCT(p_item, cl_map_obj_t, item);
291		p_txn = (osmv_txn_ctx_t *) cl_qmap_obj(p_obj);
292		p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
293
294		if (NULL != p_send_ctx) {
295
296			p_send_ctx->status = IB_INTERRUPTED;
297
298			/* Wake up the sender thread to let it break out */
299			cl_event_signal(&p_send_ctx->event);
300		}
301
302		cl_qmap_remove_item(p_map, p_item);
303	}
304
305	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
306}
307
308ib_api_status_t
309osmv_txnmgr_init(IN osmv_txn_mgr_t * p_tx_mgr,
310		 IN osm_log_t * p_log, IN cl_spinlock_t * p_lock)
311{
312	cl_status_t cl_st = CL_SUCCESS;
313
314	p_tx_mgr->p_event_wheel = malloc(sizeof(cl_event_wheel_t));
315	if (!p_tx_mgr->p_event_wheel) {
316		return IB_INSUFFICIENT_MEMORY;
317	}
318
319	memset(p_tx_mgr->p_event_wheel, 0, sizeof(cl_event_wheel_t));
320
321	cl_event_wheel_construct(p_tx_mgr->p_event_wheel);
322
323	/* NOTE! We are using an extended constructor.
324	 * We tell the Event Wheel run in a non-protected manner in the reg/unreg calls,
325	 * and acquire an external lock in the asynchronous callback.
326	 */
327	cl_st = cl_event_wheel_init_ex(p_tx_mgr->p_event_wheel, p_lock);
328	if (cl_st != CL_SUCCESS) {
329		free(p_tx_mgr->p_event_wheel);
330		return (ib_api_status_t) cl_st;
331	}
332
333	p_tx_mgr->p_txn_map = malloc(sizeof(cl_qmap_t));
334	if (!p_tx_mgr->p_txn_map) {
335		cl_event_wheel_destroy(p_tx_mgr->p_event_wheel);
336		free(p_tx_mgr->p_event_wheel);
337		return IB_INSUFFICIENT_MEMORY;
338	}
339
340	memset(p_tx_mgr->p_txn_map, 0, sizeof(cl_qmap_t));
341
342	cl_qmap_init(p_tx_mgr->p_txn_map);
343	p_tx_mgr->p_log = p_log;
344
345	return cl_st;
346}
347
348void osmv_txnmgr_done(IN osm_bind_handle_t h_bind)
349{
350	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
351
352	__osmv_txn_all_done(h_bind);
353	free(p_bo->txn_mgr.p_txn_map);
354
355	cl_event_wheel_destroy(p_bo->txn_mgr.p_event_wheel);
356	free(p_bo->txn_mgr.p_event_wheel);
357}
358
359ib_api_status_t
360__osmv_txnmgr_lookup(IN osmv_txn_mgr_t * p_tx_mgr,
361		     IN uint64_t key, OUT osmv_txn_ctx_t ** pp_txn)
362{
363	ib_api_status_t status = IB_SUCCESS;
364	cl_map_item_t *p_item;
365	cl_map_obj_t *p_obj;
366
367	uint64_t tmp_key;
368
369	OSM_LOG_ENTER(p_tx_mgr->p_log);
370
371	CL_ASSERT(p_tx_mgr);
372	CL_ASSERT(pp_txn);
373
374	osm_log(p_tx_mgr->p_log, OSM_LOG_DEBUG,
375		"__osmv_txnmgr_lookup: "
376		"Looking for key: 0x%016" PRIx64 " in map ptr:%p\n", key,
377		p_tx_mgr->p_txn_map);
378
379	p_item = cl_qmap_head(p_tx_mgr->p_txn_map);
380	while (p_item != cl_qmap_end(p_tx_mgr->p_txn_map)) {
381		tmp_key = cl_qmap_key(p_item);
382		osm_log(p_tx_mgr->p_log, OSM_LOG_DEBUG,
383			"__osmv_txnmgr_lookup: "
384			"Found key 0x%016" PRIx64 "\n", tmp_key);
385		p_item = cl_qmap_next(p_item);
386	}
387
388	p_item = cl_qmap_get(p_tx_mgr->p_txn_map, key);
389	if (cl_qmap_end(p_tx_mgr->p_txn_map) == p_item) {
390		status = IB_NOT_FOUND;
391	} else {
392		p_obj = PARENT_STRUCT(p_item, cl_map_obj_t, item);
393		*pp_txn = cl_qmap_obj(p_obj);
394	}
395
396	OSM_LOG_EXIT(p_tx_mgr->p_log);
397	return status;
398}
399
400ib_api_status_t
401__osmv_txnmgr_insert_txn(IN osmv_txn_mgr_t * p_tx_mgr,
402			 IN osmv_txn_ctx_t * p_txn, IN uint64_t key)
403{
404	cl_map_obj_t *p_obj = NULL;
405	cl_map_item_t *p_item;
406	uint64_t tmp_key;
407
408	CL_ASSERT(p_tx_mgr);
409	CL_ASSERT(p_txn);
410
411	key = osmv_txn_get_key(p_txn);
412	p_obj = malloc(sizeof(cl_map_obj_t));
413	if (NULL == p_obj)
414		return IB_INSUFFICIENT_MEMORY;
415
416	osm_log(p_tx_mgr->p_log, OSM_LOG_DEBUG,
417		"__osmv_txnmgr_insert_txn: "
418		"Inserting key: 0x%016" PRIx64 " to map ptr:%p\n", key,
419		p_tx_mgr->p_txn_map);
420
421	memset(p_obj, 0, sizeof(cl_map_obj_t));
422
423	cl_qmap_set_obj(p_obj, p_txn);
424	/* assuming lookup with this key was made and the result was IB_NOT_FOUND */
425	cl_qmap_insert(p_tx_mgr->p_txn_map, key, &p_obj->item);
426
427	p_item = cl_qmap_head(p_tx_mgr->p_txn_map);
428	while (p_item != cl_qmap_end(p_tx_mgr->p_txn_map)) {
429		tmp_key = cl_qmap_key(p_item);
430		osm_log(p_tx_mgr->p_log, OSM_LOG_DEBUG,
431			"__osmv_txnmgr_insert_txn: "
432			"Found key 0x%016" PRIx64 "\n", tmp_key);
433		p_item = cl_qmap_next(p_item);
434	}
435
436	return IB_SUCCESS;
437}
438
439ib_api_status_t
440__osmv_txnmgr_remove_txn(IN osmv_txn_mgr_t * p_tx_mgr,
441			 IN uint64_t key, OUT osmv_txn_ctx_t ** pp_txn)
442{
443	cl_map_obj_t *p_obj;
444	cl_map_item_t *p_item;
445
446	OSM_LOG_ENTER(p_tx_mgr->p_log);
447
448	CL_ASSERT(p_tx_mgr);
449	CL_ASSERT(pp_txn);
450
451	p_item = cl_qmap_remove(p_tx_mgr->p_txn_map, key);
452
453	if (p_item == cl_qmap_end(p_tx_mgr->p_txn_map)) {
454
455		osm_log(p_tx_mgr->p_log, OSM_LOG_ERROR,
456			"__osmv_txnmgr_remove_txn: ERR 6701: "
457			"Could not remove the transaction 0x%016" PRIx64 " - "
458			"something is really wrong!\n", key);
459		OSM_LOG_EXIT(p_tx_mgr->p_log);
460		return IB_NOT_FOUND;
461	}
462
463	p_obj = PARENT_STRUCT(p_item, cl_map_obj_t, item);
464	*pp_txn = cl_qmap_obj(p_obj);
465
466	free(p_obj);
467
468	OSM_LOG_EXIT(p_tx_mgr->p_log);
469	return IB_SUCCESS;
470}
471
472void __osmv_txn_all_done(osm_bind_handle_t h_bind)
473{
474	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
475	cl_map_item_t *p_item;
476	cl_map_obj_t *p_obj;
477	osmv_txn_ctx_t *p_txn;
478
479	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
480
481	p_item = cl_qmap_head(p_bo->txn_mgr.p_txn_map);
482	while (p_item != cl_qmap_end(p_bo->txn_mgr.p_txn_map)) {
483
484		p_obj = PARENT_STRUCT(p_item, cl_map_obj_t, item);
485		p_txn = (osmv_txn_ctx_t *) cl_qmap_obj(p_obj);
486		osmv_txn_done(h_bind, osmv_txn_get_key(p_txn), FALSE);
487		free(p_obj);
488		/* assuming osmv_txn_done has removed the txn from the map */
489		p_item = cl_qmap_head(p_bo->txn_mgr.p_txn_map);
490	}
491
492	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
493}
494
495/******************************************************************************/
496
497void osmv_txn_lock(IN osm_bind_handle_t h_bind)
498{
499	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
500
501	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
502		"--> Acquiring lock %p on bind handle %p\n", &p_bo->lock, p_bo);
503
504	cl_spinlock_acquire(&p_bo->lock);
505
506	osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
507		"--> Acquired lock %p on bind handle %p\n", &p_bo->lock, p_bo);
508}
509
510void osmv_txn_unlock(IN osm_bind_handle_t h_bind)
511{
512	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) h_bind;
513	cl_spinlock_t *p_lock = &p_bo->lock;
514	osm_log_t *p_log = p_bo->p_vendor->p_log;
515
516	osm_log(p_log, OSM_LOG_DEBUG,
517		"<-- Releasing lock %p on bind handle %p\n", p_lock, p_bo);
518
519	cl_spinlock_release(&p_bo->lock);
520
521	/* We'll use the saved ptrs, since now the p_bo can be destroyed already */
522	osm_log(p_log, OSM_LOG_DEBUG,
523		"<-- Released lock %p on bind handle %p\n", p_lock, p_bo);
524
525}
526
527static uint64_t
528__osmv_txn_timeout_cb(IN uint64_t key,
529		      IN uint32_t num_regs, IN void *cb_context)
530{
531	osmv_bind_obj_t *p_bo = (osmv_bind_obj_t *) cb_context;
532	uint64_t ret = 0;
533	osmv_txn_ctx_t *p_txn;
534	osmv_rmpp_send_ctx_t *p_send_ctx;
535	osm_madw_t *p_madw = NULL;
536	ib_mad_t *p_mad;
537	osm_mad_addr_t *p_mad_addr;
538	boolean_t invoke_err_cb = FALSE;
539
540	OSM_LOG_ENTER(p_bo->p_vendor->p_log);
541
542	/* Don't try to acquire a lock on the Bind Object -
543	 * it's taken by the mechanism that drives the timeout based events!
544	 * (Recall the special constructor that the Event Wheel is applied with)
545	 */
546	if (p_bo->is_closing) {
547		goto txn_done;
548	}
549
550	ret = osmv_txn_lookup(p_bo, key, &p_txn);
551	if (IB_NOT_FOUND == ret) {
552		/* Prevent a race - the transaction is already destroyed */
553		goto txn_done;
554	}
555
556	p_madw = p_txn->p_madw;
557
558	switch (osmv_txn_get_rmpp_state(p_txn)) {
559
560	case OSMV_TXN_RMPP_NONE:
561		if (num_regs <= OSM_DEFAULT_RETRY_COUNT) {
562			/* We still did not exceed the limit of retransmissions.
563			 * Set the next timeout's value.
564			 */
565			osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
566				"__osmv_txn_timeout_cb: "
567				"The transaction request (tid=0x%016" PRIx64 ")"
568				" timed out %d times. Retrying the send.\n",
569				osmv_txn_get_tid(p_txn), num_regs);
570
571			/* resend this mad */
572			ret = osmv_simple_send_madw((osm_bind_handle_t *) p_bo,
573						    p_madw, p_txn, TRUE);
574			if (ret != IB_SUCCESS) {
575				osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
576					"__osmv_txn_timeout_cb: "
577					"Fail to send retry for transaction"
578					"request (tid=0x%016" PRIx64 ").\n",
579					osmv_txn_get_tid(p_txn));
580
581				osmv_txn_done((osm_bind_handle_t) p_bo, key,
582					      TRUE /*in timeout callback */ );
583
584				/* This is a requester. Always apply the callback */
585				invoke_err_cb = TRUE;
586			} else {
587				uint64_t next_timeout_ms;
588				next_timeout_ms =
589				    p_bo->p_vendor->resp_timeout * (num_regs +
590								    1) *
591				    (num_regs + 1);
592				/* when do we need to timeout again */
593				ret =
594				    cl_get_time_stamp() +
595				    (uint64_t) (1000 * next_timeout_ms);
596
597				osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
598					"__osmv_txn_timeout_cb: "
599					"Retry request timout in : %lu [msec].\n",
600					next_timeout_ms);
601			}
602		} else {
603			osm_log(p_bo->p_vendor->p_log, OSM_LOG_ERROR,
604				"__osmv_txn_timeout_cb: ERR 6702: "
605				"The transaction request (0x%016" PRIx64 ") "
606				"timed out (after %d retries). "
607				"Invoking the error callback.\n",
608				osmv_txn_get_tid(p_txn), num_regs);
609
610			osmv_txn_done((osm_bind_handle_t) p_bo, key,
611				      TRUE /*in timeout callback */ );
612
613			/* This is a requester. Always apply the callback */
614			invoke_err_cb = TRUE;
615		}
616		break;
617
618	case OSMV_TXN_RMPP_SENDER:
619		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
620			"RMPP sender (tid=0x%016" PRIx64 ") did not receive ACK "
621			"on every segment in the current send window.\n",
622			osmv_txn_get_tid(p_txn));
623
624		p_send_ctx = osmv_txn_get_rmpp_send_ctx(p_txn);
625		if (num_regs <= OSM_DEFAULT_RETRY_COUNT) {
626			/* We still did not exceed the limit of retransmissions.
627			 * Set the next timeout's value.
628			 */
629			ret =
630			    cl_get_time_stamp() +
631			    1000 * p_bo->p_vendor->resp_timeout;
632		} else {
633			p_send_ctx->status = IB_TIMEOUT;
634
635			p_mad = osm_madw_get_mad_ptr(p_madw);
636			p_mad_addr = osm_madw_get_mad_addr_ptr(p_madw);
637
638			/* Send an ABORT to the other side */
639			osmv_rmpp_send_nak((osm_bind_handle_t) p_bo, p_mad,
640					   p_mad_addr, IB_RMPP_TYPE_ABORT,
641					   IB_RMPP_STATUS_T2L);
642		}
643
644		/* Wake the RMPP sender thread up */
645		cl_event_signal(&p_send_ctx->event);
646		break;
647
648	case OSMV_TXN_RMPP_RECEIVER:
649		osm_log(p_bo->p_vendor->p_log, OSM_LOG_DEBUG,
650			"Transaction timeout on an RMPP receiver "
651			"(tid=0x%016" PRIx64 "). Dropping the transaction.\n",
652			osmv_txn_get_tid(p_txn));
653
654		osmv_txn_done((osm_bind_handle_t) p_bo, key,
655			      TRUE /*in timeout callback */ );
656
657		if (FALSE == osmv_txn_is_rmpp_init_by_peer(p_txn)) {
658			/* This is a requester, still waiting for the reply. Apply the callback */
659			invoke_err_cb = TRUE;
660		}
661
662		break;
663
664	default:
665		CL_ASSERT(FALSE);
666	}
667
668	if (TRUE == invoke_err_cb) {
669		CL_ASSERT(NULL != p_madw);
670		/* update the status in the p_madw */
671		p_madw->status = IB_TIMEOUT;
672		p_bo->send_err_cb(p_bo->cb_context, p_madw);
673		/* no re-registration */
674		ret = 0;
675	}
676
677txn_done:
678	OSM_LOG_EXIT(p_bo->p_vendor->p_log);
679	return ret;
680}
681