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