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
33extern int ibmf_trace_level;
34
35/*
36 * ibmf_i_terminate_transaction():
37 *	Do transaction termination processing.
38 */
39void
40ibmf_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 */
107void
108ibmf_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 */
356void
357ibmf_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