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