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