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
ibmf_i_terminate_transaction(ibmf_client_t * clientp,ibmf_msg_impl_t * msgimplp,uint32_t status)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
ibmf_i_notify_client(ibmf_msg_impl_t * msgimplp)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
ibmf_i_notify_sequence(ibmf_client_t * clientp,ibmf_msg_impl_t * msgimplp,int msg_flags)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