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