xref: /illumos-gate/usr/src/uts/common/io/ib/mgt/ibmf/ibmf_trans.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 transaction processing logic common to send
31  * and receive transactions in IBMF.
32  */
33 
34 #include <sys/ib/mgt/ibmf/ibmf_impl.h>
35 
36 extern int ibmf_trace_level;
37 
38 /*
39  * ibmf_i_terminate_transaction():
40  *	Do transaction termination processing.
41  */
42 void
43 ibmf_i_terminate_transaction(ibmf_client_t *clientp, ibmf_msg_impl_t *msgimplp,
44     uint32_t status)
45 {
46 
47 	IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L4,
48 	    ibmf_i_terminate_transaction_start, IBMF_TNF_TRACE, "",
49 	    "ibmf_i_terminate_transaction(): clientp = 0x%p, msgp = 0x%p, "
50 	    "status = 0x%x\n", tnf_opaque, clientp, clientp,
51 	    tnf_opaque, msg, msgimplp, tnf_uint, status, status);
52 
53 	ASSERT(MUTEX_HELD(&msgimplp->im_mutex));
54 
55 	msgimplp->im_msg_status = status;
56 
57 	/*
58 	 * Cancel the transaction timer. timer is probably only active if status
59 	 * was not success and this is a recv operation, but unset_timer() will
60 	 * check.
61 	 */
62 	ibmf_i_unset_timer(msgimplp, IBMF_TRANS_TIMER);
63 
64 	/*
65 	 * For unsolicited messages, do not notify the client
66 	 * if an error was encontered in the transfer.
67 	 * For solicited messages, call the transaction callback
68 	 * provided by the client in the message context.
69 	 */
70 	if (msgimplp->im_unsolicited == B_TRUE) {
71 
72 		msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_DONE;
73 
74 	} else {
75 
76 		IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3,
77 		    ibmf_i_terminate_transaction, IBMF_TNF_TRACE, "",
78 		    "ibmf_i_terminate_transaction(): %s, "
79 		    "trans_state_flags = 0x%x, msg_flags = 0x%x\n",
80 		    tnf_string, msg, "solicted message callback",
81 		    tnf_opaque, trans_state_flags,
82 		    msgimplp->im_trans_state_flags,
83 		    tnf_opaque, flags, msgimplp->im_flags);
84 
85 		/* mark as recv_compl happened */
86 		msgimplp->im_trans_state_flags |=
87 		    IBMF_TRANS_STATE_FLAG_RECV_DONE;
88 
89 		/*
90 		 * Check if last send is done before marking as done.
91 		 * We should get here for sequenced transactions and
92 		 * non-sequenced send RMPP transaction.
93 		 */
94 		if (msgimplp->im_trans_state_flags &
95 		    IBMF_TRANS_STATE_FLAG_SEND_DONE) {
96 			msgimplp->im_trans_state_flags |=
97 			    IBMF_TRANS_STATE_FLAG_DONE;
98 		}
99 	}
100 
101 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
102 	    ibmf_i_terminate_transaction_end, IBMF_TNF_TRACE, "",
103 	    "ibmf_i_terminate_transaction() exit\n");
104 }
105 
106 /*
107  * ibmf_i_notify_client():
108  * 	If the transaction is done, call the appropriate callback
109  */
110 void
111 ibmf_i_notify_client(ibmf_msg_impl_t *msgimplp)
112 {
113 	ibmf_client_t	*clientp;
114 	ibmf_msg_cb_t	async_cb;
115 	void		*async_cb_arg;
116 
117 	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_notify_client_start,
118 	    IBMF_TNF_TRACE, "", "ibmf_i_notify_client(): msgp = 0x%p\n",
119 	    tnf_opaque, msgimplp, msgimplp);
120 
121 	clientp = msgimplp->im_client;
122 
123 	/*
124 	 * message is removed so no more threads will find message;
125 	 * wait for any current clients to finish
126 	 */
127 	mutex_enter(&msgimplp->im_mutex);
128 
129 	ASSERT(msgimplp->im_trans_state_flags & IBMF_TRANS_STATE_FLAG_DONE);
130 
131 	/*
132 	 * If the message reference count is not zero, then some duplicate
133 	 * MAD has arrived for this message. The thread processing the MAD
134 	 * found the message on the client's list before this thread was able
135 	 * to remove the message from the list. Since, we should not notify
136 	 * the client of the transaction completion until all the threads
137 	 * working on this message have completed (we don't want the client
138 	 * to free the message while a thread is working on it), we let one
139 	 * of the other threads notify the client of the completion once
140 	 * the message reference count is zero.
141 	 */
142 	if (msgimplp->im_ref_count != 0) {
143 		mutex_exit(&msgimplp->im_mutex);
144 		IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3,
145 		    ibmf_i_notify_client_err, IBMF_TNF_TRACE,
146 		    "", "ibmf_i_notify_client(): %s\n",
147 		    tnf_string, msg, "message reference count != 0");
148 		IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
149 		    ibmf_i_notify_client_end, IBMF_TNF_TRACE, "",
150 		    "ibmf_i_notify_client() exit\n");
151 		return;
152 	}
153 
154 	mutex_exit(&msgimplp->im_mutex);
155 
156 	/*
157 	 * Free up the UD dest resource so it is not tied down by
158 	 * the message in case the message is not freed immediately.
159 	 * Clean up the UD dest list as well so that excess UD dest
160 	 * resources are returned to the CI.
161 	 */
162 	if (msgimplp->im_ibmf_ud_dest != NULL) {
163 		ibmf_i_free_ud_dest(clientp, msgimplp);
164 		ibmf_i_clean_ud_dest_list(clientp->ic_myci, B_FALSE);
165 	}
166 
167 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*msgimplp))
168 
169 	if (msgimplp->im_unsolicited == B_TRUE) {
170 
171 		/*
172 		 * Do nothing if error status
173 		 */
174 		if (msgimplp->im_msg_status != IBMF_SUCCESS) {
175 
176 			if (msgimplp->im_qp_hdl == IBMF_QP_HANDLE_DEFAULT) {
177 				mutex_enter(&clientp->ic_mutex);
178 				IBMF_RECV_CB_CLEANUP(clientp);
179 				mutex_exit(&clientp->ic_mutex);
180 			} else {
181 				ibmf_alt_qp_t *qpp =
182 				    (ibmf_alt_qp_t *)msgimplp->im_qp_hdl;
183 				mutex_enter(&qpp->isq_mutex);
184 				IBMF_ALT_RECV_CB_CLEANUP(qpp);
185 				mutex_exit(&qpp->isq_mutex);
186 			}
187 
188 			IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
189 			    ibmf_i_notify_client_err, IBMF_TNF_ERROR, "",
190 			    "ibmf_i_notify_client(): %s, status = %d\n",
191 			    tnf_string, msg, "message status not success",
192 			    tnf_opaque, status, msgimplp->im_msg_status);
193 
194 			ibmf_i_free_msg(msgimplp);
195 
196 			IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
197 			    ibmf_i_notify_client_end, IBMF_TNF_TRACE, "",
198 			    "ibmf_i_notify_client() exit\n");
199 
200 			return;
201 		}
202 
203 		/*
204 		 * Check to see if
205 		 * a callback has been resgistered with the client
206 		 * for this unsolicited message.
207 		 * If one has been registered, up the recvs active
208 		 * count to get the teardown routine to wait until
209 		 * this callback is complete.
210 		 */
211 		if (msgimplp->im_qp_hdl == IBMF_QP_HANDLE_DEFAULT) {
212 
213 			mutex_enter(&clientp->ic_mutex);
214 
215 			if ((clientp->ic_recv_cb == NULL) ||
216 			    (clientp->ic_flags & IBMF_CLIENT_TEAR_DOWN_CB)) {
217 				IBMF_RECV_CB_CLEANUP(clientp);
218 				mutex_exit(&clientp->ic_mutex);
219 				ibmf_i_free_msg(msgimplp);
220 				IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
221 				    ibmf_i_notify_client_err, IBMF_TNF_ERROR,
222 				    "", "ibmf_i_notify_client(): %s\n",
223 				    tnf_string, msg,
224 				    "ibmf_tear_down_recv_cb already occurred");
225 				IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
226 				    ibmf_i_notify_client_end,
227 				    IBMF_TNF_TRACE, "",
228 				    "ibmf_i_notify_client() exit\n");
229 				return;
230 			}
231 
232 			clientp->ic_msgs_alloced++;
233 			mutex_enter(&clientp->ic_kstat_mutex);
234 			IBMF_ADD32_KSTATS(clientp, msgs_alloced, 1);
235 			mutex_exit(&clientp->ic_kstat_mutex);
236 
237 			async_cb = clientp->ic_recv_cb;
238 			async_cb_arg = clientp->ic_recv_cb_arg;
239 
240 			mutex_exit(&clientp->ic_mutex);
241 
242 			async_cb((ibmf_handle_t)clientp, (ibmf_msg_t *)msgimplp,
243 			    async_cb_arg);
244 
245 			mutex_enter(&clientp->ic_mutex);
246 			IBMF_RECV_CB_CLEANUP(clientp);
247 			mutex_exit(&clientp->ic_mutex);
248 
249 		} else {
250 			ibmf_alt_qp_t *qpp =
251 			    (ibmf_alt_qp_t *)msgimplp->im_qp_hdl;
252 
253 			mutex_enter(&qpp->isq_mutex);
254 
255 			if ((qpp->isq_recv_cb == NULL) ||
256 			    (qpp->isq_flags & IBMF_CLIENT_TEAR_DOWN_CB)) {
257 				IBMF_ALT_RECV_CB_CLEANUP(qpp);
258 				mutex_exit(&qpp->isq_mutex);
259 				ibmf_i_free_msg(msgimplp);
260 				IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
261 				    ibmf_i_notify_client_err, IBMF_TNF_ERROR,
262 				    "", "ibmf_i_notify_client(): %s\n",
263 				    tnf_string, msg,
264 				    "ibmf_tear_down_recv_cb already occurred");
265 				IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
266 				    ibmf_i_notify_client_end,
267 				    IBMF_TNF_TRACE, "",
268 				    "ibmf_i_notify_client() exit\n");
269 				return;
270 			}
271 
272 			async_cb = qpp->isq_recv_cb;
273 			async_cb_arg = qpp->isq_recv_cb_arg;
274 
275 			mutex_exit(&qpp->isq_mutex);
276 
277 			mutex_enter(&clientp->ic_mutex);
278 
279 			clientp->ic_msgs_alloced++;
280 
281 			mutex_exit(&clientp->ic_mutex);
282 
283 			mutex_enter(&clientp->ic_kstat_mutex);
284 			IBMF_ADD32_KSTATS(clientp, msgs_alloced, 1);
285 			mutex_exit(&clientp->ic_kstat_mutex);
286 
287 			async_cb((ibmf_handle_t)clientp, (ibmf_msg_t *)msgimplp,
288 			    async_cb_arg);
289 
290 			mutex_enter(&qpp->isq_mutex);
291 			IBMF_ALT_RECV_CB_CLEANUP(qpp);
292 			mutex_exit(&qpp->isq_mutex);
293 		}
294 	} else {
295 
296 		/* Solicited transaction processing */
297 
298 		if (msgimplp->im_trans_cb == NULL) {
299 
300 			/* Processing for a blocking transaction */
301 
302 			mutex_enter(&msgimplp->im_mutex);
303 
304 			if (msgimplp->im_trans_state_flags &
305 			    IBMF_TRANS_STATE_FLAG_WAIT) {
306 
307 				IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
308 				    ibmf_i_notify_client, IBMF_TNF_TRACE, "",
309 				    "ibmf_i_notify_client(): %s, msg = 0x%p\n",
310 				    tnf_string, msg, "Awaking thread",
311 				    tnf_opaque, msgimplp, msgimplp);
312 
313 				cv_signal(&msgimplp->im_trans_cv);
314 			} else {
315 				IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
316 				    ibmf_i_notify_client, IBMF_TNF_TRACE, "",
317 				    "ibmf_i_notify_client(): %s, msg = 0x%p\n",
318 				    tnf_string, msg, "Notify client, no wait",
319 				    tnf_opaque, msgimplp, msgimplp);
320 			}
321 
322 			msgimplp->im_trans_state_flags |=
323 			    IBMF_TRANS_STATE_FLAG_SIGNALED;
324 
325 			mutex_exit(&msgimplp->im_mutex);
326 
327 		} else {
328 
329 			/* Processing for a non-blocking transaction */
330 
331 			mutex_enter(&msgimplp->im_mutex);
332 			msgimplp->im_flags &= ~IBMF_MSG_FLAGS_BUSY;
333 			mutex_exit(&msgimplp->im_mutex);
334 
335 			IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
336 			    ibmf_i_notify_client, IBMF_TNF_TRACE, "",
337 			    "ibmf_i_notify_client(): %s, msg = 0x%p\n",
338 			    tnf_string, msg, "No thread is blocking",
339 			    tnf_opaque, msgimplp, msgimplp);
340 
341 			if (msgimplp->im_trans_cb != NULL) {
342 				msgimplp->im_trans_cb(
343 				    (ibmf_handle_t)clientp,
344 				    (ibmf_msg_t *)msgimplp,
345 				    msgimplp->im_trans_cb_arg);
346 			}
347 		}
348 	}
349 
350 	IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,	ibmf_i_notify_client_end,
351 	    IBMF_TNF_TRACE, "", "ibmf_i_notify_client() exit\n");
352 }
353 
354 /*
355  * ibmf_i_notify_sequence()
356  *	Checks for the need to create a termination context before
357  *	notifying the client.
358  */
359 void
360 ibmf_i_notify_sequence(ibmf_client_t *clientp, ibmf_msg_impl_t *msgimplp,
361     int msg_flags)
362 {
363 	int status;
364 
365 	IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4,
366 	    ibmf_i_notify_sequence_start, IBMF_TNF_TRACE, "",
367 	    "ibmf_i_notify_sequence() enter, clientp = %p, msgimplp = %p\n",
368 	    tnf_opaque, clientp, clientp, tnf_opaque, msgimplp, msgimplp);
369 
370 	if (msg_flags & IBMF_MSG_FLAGS_TERMINATION) {
371 		IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_notify_sequence,
372 		    IBMF_TNF_TRACE, "", "ibmf_i_notify_sequence(): %s, "
373 		    "msgimplp = %p\n", tnf_string, msg,
374 		    "IBMF_MSG_FLAGS_TERMINATION already set",
375 		    tnf_opaque, msgimplp, msgimplp);
376 
377 		return;
378 	}
379 
380 	if (msg_flags & IBMF_MSG_FLAGS_SET_TERMINATION) {
381 
382 		/*
383 		 * In some cases, we need to check if the termination context
384 		 * needs to be set up for early termination of non-double-sided
385 		 * RMPP receiver transactions. In these cases we set up the
386 		 * termination context, and then notify the client.
387 		 * If the set up of the termination context fails, attempt to
388 		 * reverse state to the regular context, and set the response
389 		 * timer for the termination timeout and exit without notifying
390 		 * the client in this failure case. If the setting of the
391 		 * response timer fails, simply notify the client without
392 		 * going through the process of timing out in the response
393 		 * timer.
394 		 */
395 		status = ibmf_setup_term_ctx(clientp, msgimplp);
396 		if (status != IBMF_SUCCESS) {
397 
398 			IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
399 			    ibmf_i_notify_sequence, IBMF_TNF_TRACE,
400 			    "", "ibmf_i_notify_sequence(): %s, "
401 			    "msgimplp = %p\n", tnf_string, msg,
402 			    "ibmf_setup_term_ctx() failed,"
403 			    "reversing to regular termination",
404 			    tnf_opaque, msgimplp, msgimplp);
405 
406 			mutex_enter(&msgimplp->im_mutex);
407 
408 			ibmf_i_set_timer(ibmf_i_recv_timeout, msgimplp,
409 			    IBMF_RESP_TIMER);
410 
411 			/*
412 			 * Set the flags cleared in
413 			 * ibmf_i_terminate_transaction()
414 			 */
415 			msgimplp->im_trans_state_flags &=
416 			    ~IBMF_TRANS_STATE_FLAG_DONE;
417 			msgimplp->im_trans_state_flags &=
418 			    ~IBMF_TRANS_STATE_FLAG_RECV_DONE;
419 
420 			mutex_exit(&msgimplp->im_mutex);
421 
422 			/* Re-add the message to the list */
423 			ibmf_i_client_add_msg(clientp, msgimplp);
424 		} else {
425 			/*
426 			 * The termination context has been
427 			 * set up. Notify the client that the
428 			 * regular message is done.
429 			 */
430 			ibmf_i_notify_client(msgimplp);
431 		}
432 	} else {
433 		ibmf_i_notify_client(msgimplp);
434 	}
435 
436 	IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
437 	    ibmf_i_notify_sequence_end, IBMF_TNF_TRACE, "",
438 	    "ibmf_i_notify_sequence() exit, msgimplp = %p\n",
439 	    tnf_opaque, msgimplp, msgimplp);
440 }
441