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