xref: /illumos-gate/usr/src/uts/common/io/ib/mgt/ibmf/ibmf_timers.c (revision d01412971af32f806ad2e91e40c00b485e893b2c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file implements the timer setup and timeout handling functions.
31  */
32 
33 #include <sys/ib/mgt/ibmf/ibmf_impl.h>
34 
35 extern int ibmf_trace_level;
36 
37 /*
38  * ibmf_i_set_timer():
39  *	Set the timer to the response or transaction time interval
40  */
41 void
42 ibmf_i_set_timer(void (*func)(void *), ibmf_msg_impl_t *msgimplp,
43     ibmf_timer_t type)
44 {
45 	clock_t		interval;
46 	ibmf_rmpp_ctx_t	*rmpp_ctx;
47 
48 	ASSERT(MUTEX_HELD(&msgimplp->im_mutex));
49 
50 	IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_set_timer_start,
51 	    IBMF_TNF_TRACE, "", "ibmf_i_set_timer: msgp = %p, "
52 	    "timer_type = 0x%x, func_cb = 0x%p\n",
53 	    tnf_opaque, msgimplp, msgimplp, tnf_opaque, timer_type, type,
54 	    tnf_opaque, func_cb, func);
55 
56 	if (type == IBMF_RESP_TIMER) {
57 
58 		/*
59 		 * The response timer interval is the sum of the IBA
60 		 * defined RespTimeValue (Vol. 1, Section 13.4.6.2.2),
61 		 * and the round trip time value. Both values are determined
62 		 * by the IBMF client and passed in the retrans_rtv and
63 		 * retrans_rttv fields respectively, when calling
64 		 * ibmf_msg_transport()
65 		 */
66 		ASSERT(msgimplp->im_rp_timeout_id == 0);
67 		interval = msgimplp->im_retrans.retrans_rtv +
68 		    msgimplp->im_retrans.retrans_rttv;
69 
70 		IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_set_timer,
71 		    IBMF_TNF_TRACE, "", "ibmf_i_set_timer: %s, interval = %ld "
72 		    "resp_time %x round trip time %x\n",
73 		    tnf_string, msg, "setting response timer",
74 		    tnf_long, interval, interval,
75 		    tnf_uint, resp_time, msgimplp->im_retrans.retrans_rtv,
76 		    tnf_uint, interval, msgimplp->im_retrans.retrans_rttv);
77 
78 		msgimplp->im_rp_timeout_id = timeout(func,
79 		    (void *)msgimplp, drv_usectohz(interval));
80 	} else if (type == IBMF_TRANS_TIMER) {
81 		rmpp_ctx = &msgimplp->im_rmpp_ctx;
82 
83 		ASSERT(msgimplp->im_tr_timeout_id == 0);
84 		if (rmpp_ctx->rmpp_flags & IBMF_CTX_RMPP_FLAGS_DYN_PYLD) {
85 			/*
86 			 * if payload was not specified use IB spec default
87 			 * of 40 seconds
88 			 */
89 			interval = IBMF_RETRANS_DEF_TRANS_TO;
90 
91 			IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2,
92 			    ibmf_i_set_timer, IBMF_TNF_TRACE, "",
93 			    "ibmf_i_set_timer: %s, interval = %ld\n",
94 			    tnf_string, msg,
95 			    "payload size unknown.  Using default trans_to",
96 			    tnf_long, interval, interval);
97 		} else {
98 			/*
99 			 * if payload was specified, use a variation of IB
100 			 * spec equation (13.6.3.2) that accounts for average
101 			 * window size
102 			 */
103 			interval = (msgimplp->im_retrans.retrans_rtv +
104 			    msgimplp->im_retrans.retrans_rttv) /
105 			    IBMF_RMPP_DEFAULT_WIN_SZ * 4 *
106 			    msgimplp->im_rmpp_ctx.rmpp_num_pkts;
107 
108 			IBMF_TRACE_5(IBMF_TNF_DEBUG, DPRINT_L3,
109 			    ibmf_i_set_timer, IBMF_TNF_TRACE, "",
110 			    "ibmf_i_set_timer: %s, num_pkts = %d, rttv ="
111 			    " %x, window_size = %d, interval = %ld\n",
112 			    tnf_string, msg, "setting trans timer",
113 			    tnf_uint, num_pkts,
114 			    msgimplp->im_rmpp_ctx.rmpp_num_pkts, tnf_uint, rtv,
115 			    msgimplp->im_retrans.retrans_rttv,
116 			    tnf_uint, window_size, IBMF_RMPP_DEFAULT_WIN_SZ,
117 			    tnf_long, interval, interval);
118 		}
119 
120 		/*
121 		 * Use the client specified transaction timeout value if
122 		 * smaller than the calculated value
123 		 */
124 		if ((msgimplp->im_retrans.retrans_trans_to != 0) &&
125 		    (msgimplp->im_retrans.retrans_trans_to < interval)) {
126 
127 			interval = msgimplp->im_retrans.retrans_trans_to;
128 
129 			IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L2,
130 			    ibmf_i_set_timer, IBMF_TNF_TRACE, "",
131 			    "ibmf_i_set_timer: %s, new_interval = %ld\n",
132 			    tnf_string, msg, "user trans_to is smaller",
133 			    tnf_long, new_interval, interval);
134 		}
135 
136 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_set_timer,
137 		    IBMF_TNF_TRACE, "", "ibmf_i_set_timer: %s, interval = %ld"
138 		    "\n", tnf_string, msg, "setting transaction timer",
139 		    tnf_long, interval, interval);
140 
141 		msgimplp->im_tr_timeout_id = timeout(func,
142 		    (void *)msgimplp, drv_usectohz(interval));
143 	}
144 
145 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_set_timer_end,
146 	    IBMF_TNF_TRACE, "", "ibmf_i_set_timer() exit\n");
147 }
148 
149 /*
150  * ibmf_i_unset_timer():
151  *	Unset the timer
152  */
153 void
154 ibmf_i_unset_timer(ibmf_msg_impl_t *msgimplp, ibmf_timer_t type)
155 {
156 	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_unset_timer_start,
157 	    IBMF_TNF_TRACE, "", "ibmf_i_unset_timer(): msgp = %p, \n",
158 	    tnf_opaque, msgimplp, msgimplp);
159 
160 	ASSERT(MUTEX_HELD(&msgimplp->im_mutex));
161 
162 	if (type == IBMF_RESP_TIMER) {
163 		if (msgimplp->im_rp_timeout_id != 0) {
164 			msgimplp->im_rp_unset_timeout_id =
165 			    msgimplp->im_rp_timeout_id;
166 			msgimplp->im_rp_timeout_id = 0;
167 		}
168 	} else if (type == IBMF_TRANS_TIMER) {
169 		if (msgimplp->im_tr_timeout_id != 0) {
170 			msgimplp->im_tr_unset_timeout_id =
171 			    msgimplp->im_tr_timeout_id;
172 			msgimplp->im_tr_timeout_id = 0;
173 		}
174 	}
175 
176 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_unset_timer_end,
177 	    IBMF_TNF_TRACE, "", "ibmf_i_unset_timer() exit\n");
178 }
179 
180 /*
181  * ibmf_i_recv_timeout:
182  *
183  *	Perform "receive" timeout processing for the message.
184  *	This timeout handler is only used in RMPP processing.
185  */
186 void
187 ibmf_i_recv_timeout(void *argp)
188 {
189 	ibmf_msg_impl_t *msgimplp = (ibmf_msg_impl_t *)argp;
190 	ibmf_client_t	*clientp = (ibmf_client_t *)msgimplp->im_client;
191 	ibmf_rmpp_ctx_t	*rmpp_ctx;
192 	int		msg_flags;
193 	uint_t		ref_cnt;
194 	int		status;
195 
196 	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
197 	    ibmf_i_recv_timeout_start, IBMF_TNF_TRACE, "",
198 	    "ibmf_i_recv_timeout(): msgp = 0x%p\n", tnf_opaque, msg, msgimplp);
199 
200 	mutex_enter(&msgimplp->im_mutex);
201 
202 	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
203 	    ibmf_i_recv_timeout, IBMF_TNF_TRACE, "",
204 	    "ibmf_i_recv_timeout(): resetting id time %llx\n",
205 	    tnf_opaque, time, gethrtime());
206 
207 	/*
208 	 * If the message has been marked unitialized or done
209 	 * release the message mutex and return
210 	 */
211 	if ((msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_UNINIT) ||
212 	    (msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_DONE)) {
213 
214 		mutex_exit(&msgimplp->im_mutex);
215 
216 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
217 		    ibmf_i_recv_timeout, IBMF_TNF_TRACE, "",
218 		    "ibmf_i_recv_timeout(): %s, msgp = 0x%p\n", tnf_string, msg,
219 		    "Message marked for removal, return without processing "
220 		    "recv timeout",
221 		    tnf_opaque, msgimplp, msgimplp);
222 
223 		return;
224 	}
225 
226 	/*
227 	 * Unset the response and trans timers if they haven't fired (unlikely)
228 	 */
229 	ibmf_i_unset_timer(msgimplp, IBMF_RESP_TIMER);
230 	ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER);
231 
232 	rmpp_ctx = &msgimplp->im_rmpp_ctx;
233 
234 	/* Perform timeout processing for the RMPP transaction */
235 	if (rmpp_ctx->rmpp_state == IBMF_RMPP_STATE_RECEVR_ACTIVE) {
236 
237 		IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
238 		    ibmf_i_recv_timeout, IBMF_TNF_TRACE, "",
239 		    "ibmf_i_recv_timeout(): %s\n", tnf_string, msg,
240 		    "RMPP context is Receiver Active, sending ABORT T2L");
241 
242 		status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT,
243 		    IBMF_RMPP_STATUS_T2L, 0, 0, IBMF_NO_BLOCK);
244 		if (status != IBMF_SUCCESS) {
245 			IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
246 			    ibmf_i_recv_timeout_err, IBMF_TNF_ERROR, "",
247 			    "ibmf_i_recv_timeout(): %s\n", tnf_string, msg,
248 			    "RMPP ABORT send failed");
249 			msgimplp->im_trans_state_flags |=
250 			    IBMF_TRANS_STATE_FLAG_SEND_DONE;
251 		}
252 
253 		mutex_enter(&clientp->ic_kstat_mutex);
254 		IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1);
255 		mutex_exit(&clientp->ic_kstat_mutex);
256 
257 		rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT;
258 
259 		IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
260 		    ibmf_i_recv_timeout, IBMF_TNF_ERROR, "",
261 		    "ibmf_i_recv_timeout(): %s\n", tnf_string, msg,
262 		    "RMPP context is Receiver Active, terminating transaction");
263 
264 		ibmf_i_terminate_transaction(msgimplp->im_client,
265 		    msgimplp, IBMF_TRANS_TIMEOUT);
266 
267 	} else if (rmpp_ctx->rmpp_state == IBMF_RMPP_STATE_RECEVR_TERMINATE) {
268 
269 		IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
270 		    ibmf_i_recv_timeout, IBMF_TNF_TRACE, "",
271 		    "ibmf_i_recv_timeout(): %s\n", tnf_string, msg,
272 		    "RMPP context is Receiver Terminate, "
273 		    "terminating transaction");
274 		rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_DONE;
275 		ibmf_i_terminate_transaction(msgimplp->im_client, msgimplp,
276 		    IBMF_SUCCESS);
277 	}
278 
279 	/*
280 	 * Save the transaction state flags and the timeout IDs
281 	 * before releasing the mutex as they may be changed after that.
282 	 */
283 	msg_flags = msgimplp->im_trans_state_flags;
284 
285 	mutex_exit(&msgimplp->im_mutex);
286 
287 	IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3,
288 	    ibmf_i_recv_timeout, IBMF_TNF_TRACE, "",
289 	    "ibmf_i_recv_timeout(): %s, msgp = 0x%p, refcnt = %d\n", tnf_string,
290 	    msg, "recv timeout done.  Dec ref count", tnf_opaque, msgimplp,
291 	    msgimplp, tnf_uint, flags, msg_flags);
292 
293 	/*
294 	 * If the transaction flags indicate a completed transaction,
295 	 * notify the client
296 	 */
297 	if (msg_flags & IBMF_TRANS_STATE_FLAG_DONE) {
298 		/* Remove the message from the client's message list */
299 		ibmf_i_client_rem_msg(clientp, msgimplp, &ref_cnt);
300 
301 		/*
302 		 * Notify the client if the message reference count is zero.
303 		 * At this point, we know that the transaction is done and
304 		 * the message has been removed from the client's message list.
305 		 * So, we only need to make sure the reference count is zero
306 		 * before notifying the client.
307 		 */
308 		if (ref_cnt == 0) {
309 			_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*msgimplp))
310 			if (msgimplp->im_flags & IBMF_MSG_FLAGS_TERMINATION) {
311 
312 				/*
313 				 * If the message is a termination message,
314 				 * free it at this time.
315 				 */
316 				IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
317 				    ibmf_i_recv_timeout, IBMF_TNF_TRACE, "",
318 				    "ibmf_i_recv_timeout(): freeing terminate "
319 				    "message %p\n", tnf_opaque, msgp, msgimplp);
320 
321 				/* free up the UD destination resource */
322 				if (msgimplp->im_ibmf_ud_dest != NULL) {
323 					ibmf_i_free_ud_dest(clientp, msgimplp);
324 					ibmf_i_clean_ud_dest_list(
325 					    clientp->ic_myci, B_FALSE);
326 				}
327 
328 				/* Free the receive buffer */
329 				kmem_free(
330 				    msgimplp->im_msgbufs_recv.im_bufs_mad_hdr,
331 				    IBMF_MAD_SIZE);
332 
333 				/* destroy the message mutex */
334 				mutex_destroy(&msgimplp->im_mutex);
335 
336 				/* Free the termination message context */
337 				kmem_free(msgimplp, sizeof (ibmf_msg_impl_t));
338 
339 				/*
340 				 * Decrease the "messages allocated" count
341 				 * so that an ibmf_unregister() can succeed
342 				 * for this client.
343 				 */
344 				mutex_enter(&clientp->ic_mutex);
345 				clientp->ic_msgs_alloced--;
346 				mutex_exit(&clientp->ic_mutex);
347 
348 			} else {
349 
350 				IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
351 				    ibmf_i_recv_timeout, IBMF_TNF_TRACE, "",
352 				    "ibmf_i_recv_timeout(): calling "
353 				    "notify %p\n", tnf_opaque, msgp, msgimplp);
354 
355 				ibmf_i_notify_client(msgimplp);
356 			}
357 		}
358 	}
359 
360 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
361 	    ibmf_i_recv_timeout_end, IBMF_TNF_TRACE, "",
362 	    "ibmf_i_recv_timeout() exit\n");
363 }
364 
365 /*
366  * ibmf_i_send_timeout:
367  *
368  *	Perform "send" timeout processing for the message.
369  *	This timeout handler is used in non-RMPP and RMPP processing.
370  */
371 void
372 ibmf_i_send_timeout(void *argp)
373 {
374 	ibmf_msg_impl_t *msgimplp = (ibmf_msg_impl_t *)argp;
375 	ibmf_client_t	*clientp = (ibmf_client_t *)msgimplp->im_client;
376 	ibmf_rmpp_ctx_t	*rmpp_ctx;
377 	int		msg_flags;
378 	uint_t		ref_cnt;
379 	int		status;
380 
381 	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
382 	    ibmf_i_send_timeout_start, IBMF_TNF_TRACE, "",
383 	    "ibmf_i_send_timeout_client(): msgp = 0x%p\n",
384 	    tnf_opaque, msg, msgimplp);
385 
386 	mutex_enter(&msgimplp->im_mutex);
387 
388 	/*
389 	 * If the message has been marked uninitialized or done, release the
390 	 * message mutex and return
391 	 */
392 	if ((msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_UNINIT) ||
393 	    (msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_DONE)) {
394 
395 		mutex_exit(&msgimplp->im_mutex);
396 
397 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
398 		    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
399 		    "ibmf_i_send_timeout(): %s, msgp = 0x%p\n", tnf_string, msg,
400 		    "Message is done, return without processing send timeout",
401 		    tnf_opaque, msgimplp, msgimplp);
402 
403 		return;
404 	}
405 
406 	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_send_timeout,
407 	    IBMF_TNF_TRACE, "", "ibmf_i_send_timeout(): resetting id %d\n",
408 	    tnf_opaque, timeout_id, msgimplp->im_rp_timeout_id);
409 
410 	/*
411 	 * If the timer fired, but the corresponding MAD was received before
412 	 * we got to this point in the timeout code, then do nothing in the
413 	 * timeout handler and return
414 	 */
415 	if ((msgimplp->im_flags & IBMF_MSG_FLAGS_RECV_RMPP) &&
416 	    (msgimplp->im_rp_timeout_id == 0)) {
417 
418 		mutex_exit(&msgimplp->im_mutex);
419 
420 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
421 		    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
422 		    "ibmf_i_send_timeout(): %s, msgp = 0x%p\n", tnf_string, msg,
423 		    "Message not in undefined state, return without processing "
424 		    "send timeout",
425 		    tnf_opaque, msgimplp, msgimplp);
426 
427 		return;
428 	}
429 
430 	/* Clear the response timer */
431 	if (msgimplp->im_rp_timeout_id != 0)
432 		ibmf_i_unset_timer(msgimplp, IBMF_RESP_TIMER);
433 
434 	rmpp_ctx = &msgimplp->im_rmpp_ctx;
435 
436 	/*
437 	 * Non-RMPP send transaction timeout processing
438 	 */
439 	if ((msgimplp->im_flags & IBMF_MSG_FLAGS_SEND_RMPP) == 0) {
440 
441 		/*
442 		 * We use the RMPP context to store the retry count even if
443 		 * the response does not use RMPP
444 		 */
445 		if (rmpp_ctx->rmpp_retry_cnt <
446 		    msgimplp->im_retrans.retrans_retries) {
447 
448 			IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L3,
449 			    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
450 			    "ibmf_i_send_timeout(): %s, msgp = 0x%p, "
451 			    "retry_cnt = %d, max_retries = %d\n",
452 			    tnf_string, msg, "Non-RMPP send timed out",
453 			    tnf_opaque, msgimplp, msgimplp,
454 			    tnf_uint, retry_cnt, rmpp_ctx->rmpp_retry_cnt,
455 			    tnf_uint, max_retries,
456 			    msgimplp->im_retrans.retrans_retries);
457 
458 			rmpp_ctx->rmpp_retry_cnt++;
459 
460 			status = ibmf_i_send_single_pkt(msgimplp->im_client,
461 			    msgimplp->im_qp_hdl, msgimplp, IBMF_NO_BLOCK);
462 			if (status == IBMF_SUCCESS) {
463 
464 				mutex_exit(&msgimplp->im_mutex);
465 
466 				IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
467 				    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
468 				    "ibmf_i_send_timeout(): %s, msgp = 0x%p\n",
469 				    tnf_string, msg, "Resent send",
470 				    tnf_opaque, msgimplp, msgimplp);
471 
472 				return;
473 			}
474 
475 			IBMF_TRACE_3(IBMF_TNF_NODEBUG, DPRINT_L1,
476 			    ibmf_i_send_timeout, IBMF_TNF_ERROR, "",
477 			    "ibmf_i_send_timeout(): %s, msgp = 0x%p, "
478 			    "status = %d\n", tnf_string, msg,
479 			    "Retry send failed; terminating transaction",
480 			    tnf_opaque, msgimplp, msgimplp,
481 			    tnf_opaque, status, status);
482 
483 		} else {
484 
485 			IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
486 			    ibmf_i_send_timeout, IBMF_TNF_ERROR, "",
487 			    "ibmf_i_send_timeout(): %s\n",  tnf_string, msg,
488 			    "Not RMPP SEND, terminate transaction with "
489 			    "IBMF_TRANS_TIMEOUT");
490 		}
491 
492 		/*
493 		 * If we are in receive RMPP mode, then an ABORT should
494 		 * be sent after the required number of retries.
495 		 */
496 		if (msgimplp->im_flags & IBMF_MSG_FLAGS_RECV_RMPP) {
497 			status = ibmf_i_send_rmpp(msgimplp,
498 			    IBMF_RMPP_TYPE_ABORT, IBMF_RMPP_STATUS_TMR, 0, 0,
499 			    IBMF_NO_BLOCK);
500 			if (status != IBMF_SUCCESS) {
501 				IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
502 				    ibmf_i_send_timeout_err, IBMF_TNF_ERROR, "",
503 				    "ibmf_i_send_timeout(): %s\n", tnf_string,
504 				    msg, "RMPP ABORT send failed");
505 				msgimplp->im_trans_state_flags |=
506 				    IBMF_TRANS_STATE_FLAG_SEND_DONE;
507 			}
508 			rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT;
509 		}
510 
511 		ibmf_i_terminate_transaction(msgimplp->im_client,
512 		    msgimplp, IBMF_TRANS_TIMEOUT);
513 
514 		msg_flags = msgimplp->im_trans_state_flags;
515 
516 		mutex_exit(&msgimplp->im_mutex);
517 
518 		/* Notify the client if the transaction is done */
519 		if (msg_flags & IBMF_TRANS_STATE_FLAG_DONE) {
520 
521 			IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
522 			    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
523 			    "ibmf_i_send_timeout(): %s, msgp = 0x%p\n",
524 			    tnf_string, msg, "calling notify",
525 			    tnf_opaque, msgimplp, msgimplp);
526 			/* Remove the message from the client's message list */
527 			ibmf_i_client_rem_msg(clientp, msgimplp, &ref_cnt);
528 			/*
529 			 * Notify the client if the message reference count is
530 			 * zero. At this point, we know that the transaction is
531 			 * done and the message has been removed from the
532 			 * client's message list. So, we need to be sure the
533 			 * reference count is zero before notifying the client.
534 			 */
535 			if (ref_cnt == 0) {
536 				ibmf_i_notify_client(msgimplp);
537 			}
538 		}
539 
540 		IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_timeout,
541 		    IBMF_TNF_TRACE, "", "ibmf_i_send_timeout() exit\n");
542 
543 		return;
544 	}
545 
546 	IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L3,
547 	    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
548 	    "ibmf_i_send_timeout(): %s, msgp = 0x%p, retry_cnt = %d, "
549 	    "max_retries = %d\n", tnf_string, msg, "RMPP send timed out",
550 	    tnf_opaque, msgimplp, msgimplp,
551 	    tnf_uint, retry_cnt, rmpp_ctx->rmpp_retry_cnt,
552 	    tnf_uint, max_retries, msgimplp->im_retrans.retrans_retries);
553 
554 	/* RMPP send transaction timeout processing */
555 	if (rmpp_ctx->rmpp_retry_cnt == msgimplp->im_retrans.retrans_retries) {
556 
557 		IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
558 		    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
559 		    "ibmf_i_send_timeout(): %s\n", tnf_string, msg,
560 		    "Maximum retries done, sending ABORT TMR");
561 
562 		status = ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ABORT,
563 		    IBMF_RMPP_STATUS_TMR, 0, 0, IBMF_NO_BLOCK);
564 		if (status != IBMF_SUCCESS) {
565 			IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
566 			    ibmf_i_send_timeout_err, IBMF_TNF_ERROR, "",
567 			    "ibmf_i_send_timeout(): %s\n", tnf_string, msg,
568 			    "RMPP ABORT send failed");
569 			msgimplp->im_trans_state_flags |=
570 			    IBMF_TRANS_STATE_FLAG_SEND_DONE;
571 		}
572 
573 		rmpp_ctx->rmpp_state = IBMF_RMPP_STATE_ABORT;
574 
575 		mutex_enter(&clientp->ic_kstat_mutex);
576 		IBMF_ADD32_KSTATS(clientp, rmpp_errors, 1);
577 		mutex_exit(&clientp->ic_kstat_mutex);
578 
579 		IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
580 		    ibmf_i_send_timeout, IBMF_TNF_ERROR, "",
581 		    "ibmf_i_send_timeout(): %s\n", tnf_string, msg,
582 		    "Maximum retries done, terminate transaction with "
583 		    "IBMF_TRANS_TIMEOUT");
584 
585 		ibmf_i_terminate_transaction(msgimplp->im_client,
586 		    msgimplp, IBMF_TRANS_TIMEOUT);
587 
588 	} else {
589 
590 		if (rmpp_ctx->rmpp_state == IBMF_RMPP_STATE_SENDER_ACTIVE) {
591 
592 			IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
593 			    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
594 			    "ibmf_i_send_timeout(): %s\n", tnf_string, msg,
595 			    "RMPP context is Sender Active, Resending window");
596 
597 			/*
598 			 * resend the window
599 			 */
600 			rmpp_ctx->rmpp_ns = rmpp_ctx->rmpp_wf;
601 
602 			ibmf_i_send_rmpp_window(msgimplp, IBMF_NO_BLOCK);
603 		} else if (rmpp_ctx->rmpp_state ==
604 		    IBMF_RMPP_STATE_SENDER_SWITCH) {
605 
606 			IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
607 			    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
608 			    "ibmf_i_send_timeout(): %s\n", tnf_string, msg,
609 			    "RMPP context is Sender Terminate, sending ACK");
610 
611 			/* send ACK */
612 			(void) ibmf_i_send_rmpp(msgimplp, IBMF_RMPP_TYPE_ACK,
613 			    IBMF_RMPP_STATUS_NORMAL, 0, 1, IBMF_NO_BLOCK);
614 
615 			IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
616 			    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
617 			    "ibmf_i_send_timeout(): setting timer %d %p\n",
618 			    tnf_opaque, msgp, msgimplp, tnf_opaque,
619 			    timeout_id, msgimplp->im_rp_timeout_id);
620 
621 			/* set response timer */
622 			ibmf_i_set_timer(ibmf_i_send_timeout, msgimplp,
623 			    IBMF_RESP_TIMER);
624 		}
625 
626 		rmpp_ctx->rmpp_retry_cnt++;
627 
628 	}
629 
630 	msg_flags = msgimplp->im_trans_state_flags;
631 
632 	mutex_exit(&msgimplp->im_mutex);
633 
634 	clientp = (ibmf_client_t *)msgimplp->im_client;
635 
636 	IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
637 	    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
638 	    "ibmf_i_send_timeout(): %s, msgp = 0x%p\n", tnf_string, msg,
639 	    "Send timeout done", tnf_opaque, msgimplp, msgimplp);
640 
641 	if (msg_flags & IBMF_TRANS_STATE_FLAG_DONE) {
642 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
643 		    ibmf_i_send_timeout, IBMF_TNF_TRACE, "",
644 		    "ibmf_i_send_timeout(): %s, msgp = 0x%p\n", tnf_string, msg,
645 		    "calling notify", tnf_opaque, msgimplp, msgimplp);
646 		/* Remove the message from the client's message list */
647 		ibmf_i_client_rem_msg(clientp, msgimplp, &ref_cnt);
648 		/*
649 		 * Notify the client if the message reference count is zero.
650 		 * At this point, we know that the transaction is done and
651 		 * the message has been removed from the client's message list.
652 		 * So, we only need to make sure the reference count is zero
653 		 * before notifying the client.
654 		 */
655 		if (ref_cnt == 0) {
656 			ibmf_i_notify_client(msgimplp);
657 		}
658 	}
659 
660 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_send_timeout_end,
661 	    IBMF_TNF_TRACE, "", "ibmf_i_send_timeout() exit\n");
662 }
663 
664 void
665 ibmf_i_err_terminate_timeout(void *argp)
666 {
667 	ibmf_msg_impl_t *msgimplp = (ibmf_msg_impl_t *)argp;
668 	ibmf_client_t	*clientp = (ibmf_client_t *)msgimplp->im_client;
669 	int		msg_flags;
670 	uint_t		ref_cnt;
671 
672 	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
673 	    ibmf_i_err_terminate_timeout_start, IBMF_TNF_TRACE, "",
674 	    "ibmf_i_err_terminate_timeout_client(): msgp = 0x%p\n",
675 	    tnf_opaque, msg, msgimplp);
676 
677 	mutex_enter(&msgimplp->im_mutex);
678 
679 	/*
680 	 * If the message has been marked uninitialized or done, release the
681 	 * message mutex and return
682 	 */
683 	if ((msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_UNINIT) ||
684 	    (msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_DONE)) {
685 
686 		mutex_exit(&msgimplp->im_mutex);
687 
688 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
689 		    ibmf_i_err_terminate_timeout, IBMF_TNF_TRACE, "",
690 		    "ibmf_i_err_terminate_timeout(): %s, msgp = 0x%p\n",
691 		    tnf_string, msg, "Message is done, return without "
692 		    "processing error terminate timeout",
693 		    tnf_opaque, msgimplp, msgimplp);
694 
695 		return;
696 	}
697 
698 	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_err_terminate_timeout,
699 	    IBMF_TNF_TRACE, "", "ibmf_i_err_terminate_timeout(): resetting "
700 	    "id %d\n", tnf_opaque, timeout_id, msgimplp->im_rp_timeout_id);
701 
702 	/* Clear the response timer */
703 	if (msgimplp->im_rp_timeout_id != 0)
704 		msgimplp->im_rp_timeout_id = 0;
705 
706 	/* Mark the transaction as terminated */
707 	ibmf_i_terminate_transaction(msgimplp->im_client, msgimplp,
708 	    IBMF_TRANS_FAILURE);
709 
710 	msg_flags = msgimplp->im_trans_state_flags;
711 
712 	mutex_exit(&msgimplp->im_mutex);
713 
714 	clientp = (ibmf_client_t *)msgimplp->im_client;
715 
716 	IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_err_terminate_timeout,
717 	    IBMF_TNF_TRACE, "", "ibmf_i_err_terminate_timeout(): %s, "
718 	    "msgp = 0x%p\n", tnf_string, msg,
719 	    "Error terminate timeout done", tnf_opaque, msgimplp, msgimplp);
720 
721 	if (msg_flags & IBMF_TRANS_STATE_FLAG_DONE) {
722 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
723 		    ibmf_i_err_terminate_timeout, IBMF_TNF_TRACE, "",
724 		    "ibmf_i_err_terminate_timeout(): %s, msgp = 0x%p\n",
725 		    tnf_string, msg,
726 		    "calling notify", tnf_opaque, msgimplp, msgimplp);
727 		/* Remove the message from the client's message list */
728 		ibmf_i_client_rem_msg(clientp, msgimplp, &ref_cnt);
729 		/*
730 		 * Notify the client if the message reference count is zero.
731 		 * At this point, we know that the transaction is done and
732 		 * the message has been removed from the client's message list.
733 		 * So, we only need to make sure the reference count is zero
734 		 * before notifying the client.
735 		 */
736 		if (ref_cnt == 0) {
737 			_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*msgimplp))
738 			if (msgimplp->im_flags & IBMF_MSG_FLAGS_TERMINATION) {
739 
740 				/*
741 				 * If the message is a termination message,
742 				 * free it at this time.
743 				 */
744 
745 				IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
746 				    ibmf_i_err_terminate_timeout,
747 				    IBMF_TNF_TRACE, "",
748 				    "ibmf_i_recv_timeout(): freeing terminate "
749 				    "message %p\n", tnf_opaque, msgp, msgimplp);
750 
751 				/* free up the UD destination resource */
752 				if (msgimplp->im_ibmf_ud_dest != NULL) {
753 					ibmf_i_free_ud_dest(clientp, msgimplp);
754 					ibmf_i_clean_ud_dest_list(
755 					    clientp->ic_myci, B_FALSE);
756 				}
757 
758 				/* Free the receive buffer */
759 				kmem_free(
760 				    msgimplp->im_msgbufs_recv.im_bufs_mad_hdr,
761 				    IBMF_MAD_SIZE);
762 
763 				/* destroy the message mutex */
764 				mutex_destroy(&msgimplp->im_mutex);
765 
766 				/* Free the termination message context */
767 				kmem_free(msgimplp, sizeof (ibmf_msg_impl_t));
768 
769 				/*
770 				 * Decrease the "messages allocated" count
771 				 * so that an ibmf_unregister() can succeed
772 				 * for this client.
773 				 */
774 				mutex_enter(&clientp->ic_mutex);
775 				clientp->ic_msgs_alloced--;
776 				mutex_exit(&clientp->ic_mutex);
777 
778 			} else {
779 
780 				IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
781 				    ibmf_i_err_terminate_timeout,
782 				    IBMF_TNF_TRACE, "",
783 				    "ibmf_i_recv_timeout(): calling "
784 				    "notify %p\n", tnf_opaque, msgp, msgimplp);
785 
786 				ibmf_i_notify_client(msgimplp);
787 			}
788 		}
789 	}
790 
791 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
792 	    ibmf_i_err_terminate_timeout_end, IBMF_TNF_TRACE, "",
793 	    "ibmf_i_err_terminate_timeout() exit\n");
794 }
795