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 Directed Route (DR) loopback support in IBMF. 28 */ 29 30 #include <sys/ib/mgt/ibmf/ibmf_impl.h> 31 #include <sys/ib/mgt/ib_mad.h> 32 33 #define MELLANOX_VENDOR 0x15b3 34 35 extern int ibmf_trace_level; 36 37 static int ibmf_i_dr_loopback_filter(ibmf_client_t *clientp, 38 ibmf_msg_impl_t *msgimplp, int blocking); 39 static void ibmf_i_dr_loopback_term(ibmf_client_t *clientp, 40 ibmf_msg_impl_t *msgimplp, int blocking); 41 42 /* 43 * ibmf_i_check_for_loopback(): 44 * Check for DR loopback traffic 45 */ 46 int 47 ibmf_i_check_for_loopback(ibmf_msg_impl_t *msgimplp, ibmf_msg_cb_t msg_cb, 48 void *msg_cb_args, ibmf_retrans_t *retrans, boolean_t *loopback) 49 { 50 sm_dr_mad_hdr_t *dr_hdr; 51 boolean_t blocking; 52 int status; 53 ibmf_ci_t *cip = ((ibmf_client_t *)msgimplp->im_client)->ic_myci; 54 55 IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, 56 ibmf_i_check_for_loopback_start, IBMF_TNF_TRACE, "", 57 "ibmf_i_check_for_loopback() enter, msg = 0x%p\n", 58 tnf_opaque, msg, msgimplp); 59 60 *loopback = B_FALSE; 61 dr_hdr = (sm_dr_mad_hdr_t *)msgimplp->im_msgbufs_send.im_bufs_mad_hdr; 62 63 /* 64 * Some HCAs do not handle directed route loopback MADs. 65 * Such MADs are sent out on the wire instead of being looped back. 66 * This behavior causes the SM to hang since the SM starts 67 * its sweep with loopback DR MADs. 68 * This ibmf workaround does the loopback without passing the MAD 69 * into the transport layer. 70 * We should really check a property of the hardware to determine 71 * whether or not an IB HCA can "hear" itself rather than 72 * checking for specific HCAs or vendor of HCAs. 73 */ 74 if ((dr_hdr->MgmtClass == MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE) && 75 (dr_hdr->HopCount == 0) && (cip->ci_vendor_id == MELLANOX_VENDOR)) { 76 if (msg_cb == NULL) { 77 blocking = B_TRUE; 78 } else { 79 blocking = B_FALSE; 80 } 81 82 ibmf_i_init_msg(msgimplp, msg_cb, msg_cb_args, retrans, 83 blocking); 84 85 status = ibmf_i_dr_loopback_filter(msgimplp->im_client, 86 msgimplp, blocking); 87 if (status != IBMF_SUCCESS) { 88 IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1, 89 ibmf_i_check_for_loopback_err, 90 "ibmf_i_check_for_loopback(): %s\n", 91 IBMF_TNF_ERROR, "", tnf_string, msg, 92 "Failure in DR loopback filter"); 93 IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, 94 ibmf_i_check_for_loopback_end, IBMF_TNF_TRACE, "", 95 "ibmf_i_check_for_loopback() exit\n"); 96 return (status); 97 } 98 99 *loopback = B_TRUE; 100 } 101 102 IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_check_for_loopback_end, 103 IBMF_TNF_TRACE, "", "ibmf_i_check_for_loopback() exit\n"); 104 105 return (IBMF_SUCCESS); 106 107 } 108 109 /* 110 * ibmf_i_dr_loopback_term(): 111 * Perform termination processing of a DR loopback transaction 112 */ 113 static void 114 ibmf_i_dr_loopback_term(ibmf_client_t *clientp, ibmf_msg_impl_t *msgimplp, 115 int blocking) 116 { 117 uint_t refcnt; 118 119 IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4, 120 ibmf_i_dr_loopback_term_start, IBMF_TNF_TRACE, "", 121 "ibmf_i_dr_loopback_term() enter, clientp = 0x%p, msg = 0x%p\n", 122 tnf_opaque, clientp, clientp, tnf_opaque, msg, msgimplp); 123 124 mutex_enter(&msgimplp->im_mutex); 125 126 if (blocking) { 127 /* 128 * For sequenced, and blocking transactions, we wait for 129 * the response. For non-sequenced, and blocking transactions, 130 * we are done since the send has completed (no send completion 131 * as when calling into IBTF). 132 */ 133 if ((msgimplp->im_flags & IBMF_MSG_FLAGS_SEQUENCED) && 134 ((msgimplp->im_trans_state_flags & 135 IBMF_TRANS_STATE_FLAG_SIGNALED) == 0)) { 136 137 msgimplp->im_trans_state_flags |= 138 IBMF_TRANS_STATE_FLAG_WAIT; 139 140 IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, 141 ibmf_i_dr_loopback, IBMF_TNF_TRACE, "", 142 "ibmf_i_dr_loopback_term(): %s\n", 143 tnf_string, msg, "Blocking for completion"); 144 145 cv_wait(&msgimplp->im_trans_cv, &msgimplp->im_mutex); 146 147 msgimplp->im_trans_state_flags &= 148 ~IBMF_TRANS_STATE_FLAG_WAIT; 149 150 msgimplp->im_flags &= ~IBMF_MSG_FLAGS_BUSY; 151 152 mutex_exit(&msgimplp->im_mutex); 153 154 } else if ((msgimplp->im_flags & 155 IBMF_MSG_FLAGS_SEQUENCED) == 0) { 156 157 msgimplp->im_trans_state_flags |= 158 IBMF_TRANS_STATE_FLAG_DONE; 159 msgimplp->im_flags &= ~IBMF_MSG_FLAGS_BUSY; 160 161 mutex_exit(&msgimplp->im_mutex); 162 163 ibmf_i_client_rem_msg(clientp, msgimplp, &refcnt); 164 } else { 165 166 msgimplp->im_flags &= ~IBMF_MSG_FLAGS_BUSY; 167 mutex_exit(&msgimplp->im_mutex); 168 } 169 170 } else if ((msgimplp->im_flags & IBMF_MSG_FLAGS_SEQUENCED) == 0) { 171 172 IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L3, 173 ibmf_i_dr_loopback, IBMF_TNF_TRACE, "", 174 "ibmf_i_dr_loopback_term(): %s\n", 175 tnf_string, msg, "Not sequenced, returning to caller"); 176 msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_DONE; 177 msgimplp->im_flags &= ~IBMF_MSG_FLAGS_BUSY; 178 mutex_exit(&msgimplp->im_mutex); 179 180 ibmf_i_client_rem_msg(clientp, msgimplp, &refcnt); 181 182 if (msgimplp->im_trans_cb) { 183 msgimplp->im_trans_cb((ibmf_handle_t)clientp, 184 (ibmf_msg_t *)msgimplp, msgimplp->im_trans_cb_arg); 185 } 186 } else { 187 188 msgimplp->im_flags &= ~IBMF_MSG_FLAGS_BUSY; 189 mutex_exit(&msgimplp->im_mutex); 190 } 191 192 IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_dr_loopback_term_end, 193 IBMF_TNF_TRACE, "", "ibmf_i_dr_loopback_term() exit\n"); 194 195 } 196 197 /* 198 * ibmf_i_dr_loopback_filter(): 199 * This function intercepts Directed Route MADs with zero hop count, 200 * or loopback DR MADs. If the MAD is outbound from the SM, the SMA's 201 * client handle is located, and the receive callback invoked. 202 * If the MAD is outbound from the SMA, the SM's client handle is located 203 * and the receive callback invoked. 204 * 205 * This filtering is needed for some HCAs where the SMA cannot handle DR 206 * MAD's that need to be treated as a loopback MAD. On these HCAs, we see 207 * the zero hopcount MAD being sent out on the wire which it should not. 208 */ 209 static int 210 ibmf_i_dr_loopback_filter(ibmf_client_t *clientp, ibmf_msg_impl_t *msgimplp, 211 int blocking) 212 { 213 ibmf_client_t *rclientp; 214 sm_dr_mad_hdr_t *dr_hdr; 215 ibmf_msg_impl_t *rmsgimplp; 216 boolean_t rbuf_alloced; 217 int msg_trans_state_flags, msg_flags; 218 uint_t ref_cnt; 219 int ret; 220 221 IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4, 222 ibmf_i_dr_loopback_filter_start, IBMF_TNF_TRACE, "", 223 "ibmf_i_dr_loopback_filter() enter, clientp = 0x%p, msg = 0x%p\n", 224 tnf_opaque, clientp, clientp, tnf_opaque, msg, msgimplp); 225 226 dr_hdr = (sm_dr_mad_hdr_t *)msgimplp->im_msgbufs_send.im_bufs_mad_hdr; 227 228 /* set transaction flag for a sequenced transaction */ 229 if (msgimplp->im_transp_op_flags & IBMF_MSG_TRANS_FLAG_SEQ) 230 msgimplp->im_flags |= IBMF_MSG_FLAGS_SEQUENCED; 231 232 /* 233 * If the DR SMP method is a Get or a Set, the target is the SMA, else, 234 * if the method is a GetResponse, the target is the SM. If the 235 * Attribute is SMInfo, the target is always the SM. 236 */ 237 if ((((dr_hdr->R_Method == MAD_METHOD_GET) || 238 (dr_hdr->R_Method == MAD_METHOD_SET)) && 239 (dr_hdr->AttributeID != SM_SMINFO_ATTRID)) || 240 (dr_hdr->R_Method == MAD_METHOD_TRAP_REPRESS)) { 241 242 ret = ibmf_i_lookup_client_by_mgmt_class(clientp->ic_myci, 243 clientp->ic_client_info.port_num, SUBN_AGENT, &rclientp); 244 if (ret != IBMF_SUCCESS) { 245 IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1, 246 ibmf_recv_pkt_cb_err, IBMF_TNF_TRACE, "", 247 "ibmf_i_dr_loopback_filter(): %s\n", 248 tnf_string, msg, 249 "Client for Mgt Class Subnet Agent not found"); 250 IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, 251 ibmf_i_dr_loopback_filter_end, IBMF_TNF_TRACE, "", 252 "ibmf_i_dr_loopback_filter() exit\n"); 253 return (ret); 254 } 255 256 } else if ((dr_hdr->R_Method == MAD_METHOD_GET_RESPONSE) || 257 (dr_hdr->R_Method == MAD_METHOD_TRAP) || 258 (dr_hdr->AttributeID == SM_SMINFO_ATTRID)) { 259 260 ret = ibmf_i_lookup_client_by_mgmt_class(clientp->ic_myci, 261 clientp->ic_client_info.port_num, SUBN_MANAGER, &rclientp); 262 if (ret != IBMF_SUCCESS) { 263 IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1, 264 ibmf_recv_pkt_cb_err, IBMF_TNF_TRACE, "", 265 "ibmf_i_dr_loopback_filter(): %s\n", 266 tnf_string, msg, 267 "Client for Mgt Class Subnet Manager not found") 268 IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, 269 ibmf_i_dr_loopback_filter_end, IBMF_TNF_TRACE, "", 270 "ibmf_i_dr_loopback_filter() exit\n"); 271 return (ret); 272 } 273 } else { 274 IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1, 275 ibmf_recv_pkt_cb_err, IBMF_TNF_TRACE, "", 276 "ibmf_i_dr_loopback_filter(): %s, method = 0x%x\n", 277 tnf_string, msg, "Unexpected dr method", 278 tnf_opaque, method, dr_hdr->R_Method); 279 IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, 280 ibmf_i_dr_loopback_filter_end, IBMF_TNF_TRACE, "", 281 "ibmf_i_dr_loopback_filter() exit\n"); 282 283 return (IBMF_FAILURE); 284 } 285 286 /* 287 * Initialize the Transaction ID and Mgmt Class fields in the 288 * message context. 289 * NOTE: The IB MAD header in the incoming MAD is in wire (big-endian) 290 * format and needs to be converted to the host endian format where 291 * applicable (multi-byte fields) 292 */ 293 msgimplp->im_tid = b2h64(dr_hdr->TransactionID); 294 msgimplp->im_mgt_class = dr_hdr->MgmtClass; 295 296 /* 297 * Find the message context in the target client corresponding to the 298 * transaction ID and management class in the source message context 299 */ 300 rmsgimplp = ibmf_i_find_msg(rclientp, msgimplp->im_tid, 301 dr_hdr->MgmtClass, dr_hdr->R_Method, 302 msgimplp->im_local_addr.ia_remote_lid, NULL, B_FALSE, NULL, 303 IBMF_REG_MSG_LIST); 304 305 if (rmsgimplp != NULL) { 306 307 mutex_enter(&rmsgimplp->im_mutex); 308 309 /* 310 * If the message has been marked unitialized or done 311 * release the message mutex and return 312 */ 313 if ((rmsgimplp->im_trans_state_flags & 314 IBMF_TRANS_STATE_FLAG_DONE) || 315 (rmsgimplp->im_trans_state_flags & 316 IBMF_TRANS_STATE_FLAG_UNINIT)) { 317 IBMF_MSG_DECR_REFCNT(rmsgimplp); 318 msg_trans_state_flags = rmsgimplp->im_trans_state_flags; 319 msg_flags = rmsgimplp->im_flags; 320 ref_cnt = rmsgimplp->im_ref_count; 321 mutex_exit(&rmsgimplp->im_mutex); 322 /* 323 * This thread may notify the client only if the 324 * transaction is done, the message has been removed 325 * from the client's message list, and the message 326 * reference count is 0. 327 * If the transaction is done, and the message reference 328 * count = 0, there is still a possibility that a 329 * packet could arrive for the message and its reference 330 * count increased if the message is still on the list. 331 * If the message is still on the list, it will be 332 * removed by a call to ibmf_i_client_rem_msg() at 333 * the completion point of the transaction. 334 * So, the reference count should be checked after the 335 * message has been removed. 336 */ 337 if ((msg_trans_state_flags & 338 IBMF_TRANS_STATE_FLAG_DONE) && 339 !(msg_flags & IBMF_MSG_FLAGS_ON_LIST) && 340 (ref_cnt == 0)) { 341 ibmf_i_notify_client(rmsgimplp); 342 } 343 IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1, 344 ibmf_recv_pkt_cb_err, IBMF_TNF_TRACE, "", 345 "ibmf_i_dr_loopback_filter(): %s, msg = 0x%p\n", 346 tnf_string, msg, 347 "Message already marked for removal, dropping MAD", 348 tnf_opaque, msgimplp, msgimplp); 349 IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, 350 ibmf_i_dr_loopback_filter_end, IBMF_TNF_TRACE, "", 351 "ibmf_i_dr_loopback_filter() exit\n"); 352 return (IBMF_FAILURE); 353 } 354 } else { 355 356 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*rmsgimplp)) 357 358 /* This is an unsolicited message */ 359 360 rmsgimplp = (ibmf_msg_impl_t *)kmem_zalloc( 361 sizeof (ibmf_msg_impl_t), KM_NOSLEEP); 362 if (rmsgimplp == NULL) { 363 IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1, 364 ibmf_recv_pkt_cb_err, IBMF_TNF_TRACE, "", 365 "ibmf_i_dr_loopback_filter(): %s\n", 366 tnf_string, msg, "Failed to alloc packet"); 367 IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, 368 ibmf_i_dr_loopback_filter_end, IBMF_TNF_TRACE, "", 369 "ibmf_i_dr_loopback_filter() exit\n"); 370 return (IBMF_NO_RESOURCES); 371 } 372 373 mutex_init(&rmsgimplp->im_mutex, NULL, MUTEX_DRIVER, NULL); 374 375 rmsgimplp->im_client = rclientp; 376 rmsgimplp->im_qp_hdl = msgimplp->im_qp_hdl; 377 rmsgimplp->im_unsolicited = B_TRUE; 378 rmsgimplp->im_tid = b2h64(dr_hdr->TransactionID); 379 rmsgimplp->im_mgt_class = dr_hdr->MgmtClass; 380 381 /* indicate the client callback is active */ 382 if (rmsgimplp->im_qp_hdl == IBMF_QP_HANDLE_DEFAULT) { 383 mutex_enter(&rclientp->ic_mutex); 384 IBMF_RECV_CB_SETUP(rclientp); 385 mutex_exit(&rclientp->ic_mutex); 386 } else { 387 ibmf_alt_qp_t *qpp; 388 389 qpp = (ibmf_alt_qp_t *)rmsgimplp->im_qp_hdl; 390 mutex_enter(&qpp->isq_mutex); 391 IBMF_ALT_RECV_CB_SETUP(qpp); 392 mutex_exit(&qpp->isq_mutex); 393 } 394 395 /* Increment the message reference count */ 396 IBMF_MSG_INCR_REFCNT(rmsgimplp); 397 rmsgimplp->im_trans_state_flags = IBMF_TRANS_STATE_FLAG_UNINIT; 398 399 _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*rmsgimplp)) 400 401 /* add message to client's list; will acquire im_mutex */ 402 ibmf_i_client_add_msg(rclientp, rmsgimplp); 403 404 mutex_enter(&rmsgimplp->im_mutex); 405 406 /* no one should have touched our state */ 407 ASSERT(rmsgimplp->im_trans_state_flags == 408 IBMF_TRANS_STATE_FLAG_UNINIT); 409 410 /* transition out of uninit state */ 411 rmsgimplp->im_trans_state_flags = IBMF_TRANS_STATE_FLAG_INIT; 412 } 413 414 /* Allocate memory for the receive buffers */ 415 if (rmsgimplp->im_msgbufs_recv.im_bufs_mad_hdr == NULL) { 416 rmsgimplp->im_msgbufs_recv.im_bufs_mad_hdr = 417 (ib_mad_hdr_t *)kmem_zalloc(IBMF_MAD_SIZE, KM_NOSLEEP); 418 if (rmsgimplp->im_msgbufs_recv.im_bufs_mad_hdr == NULL) { 419 IBMF_MSG_DECR_REFCNT(rmsgimplp); 420 mutex_exit(&rmsgimplp->im_mutex); 421 kmem_free(rmsgimplp, sizeof (ibmf_msg_impl_t)); 422 IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1, 423 ibmf_recv_pkt_cb_err, IBMF_TNF_TRACE, "", 424 "ibmf_i_dr_loopback_filter(): %s\n", 425 tnf_string, msg, "mem allocation failure"); 426 IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, 427 ibmf_i_dr_loopback_filter_end, IBMF_TNF_TRACE, "", 428 "ibmf_i_dr_loopback_filter() exit\n"); 429 return (IBMF_NO_RESOURCES); 430 } 431 rbuf_alloced = B_TRUE; 432 } 433 434 /* Copy the send buffers into the receive buffers */ 435 436 /* Copy the MAD header */ 437 bcopy((void *)msgimplp->im_msgbufs_send.im_bufs_mad_hdr, 438 (void *)rmsgimplp->im_msgbufs_recv.im_bufs_mad_hdr, 439 sizeof (ib_mad_hdr_t)); 440 441 /* 442 * Copy the management class header 443 * For DR MADs, class header is of size 40 bytes and start 444 * right after the MAD header. 445 */ 446 rmsgimplp->im_msgbufs_recv.im_bufs_cl_hdr = 447 (uchar_t *)rmsgimplp->im_msgbufs_recv.im_bufs_mad_hdr + 448 sizeof (ib_mad_hdr_t); 449 rmsgimplp->im_msgbufs_recv.im_bufs_cl_hdr_len = 450 msgimplp->im_msgbufs_send.im_bufs_cl_hdr_len; 451 bcopy((void *)msgimplp->im_msgbufs_send.im_bufs_cl_hdr, 452 (void *)rmsgimplp->im_msgbufs_recv.im_bufs_cl_hdr, 453 msgimplp->im_msgbufs_send.im_bufs_cl_hdr_len); 454 455 /* Copy the management class data */ 456 rmsgimplp->im_msgbufs_recv.im_bufs_cl_data = 457 (uchar_t *)rmsgimplp->im_msgbufs_recv.im_bufs_mad_hdr + 458 sizeof (ib_mad_hdr_t) + 459 rmsgimplp->im_msgbufs_recv.im_bufs_cl_hdr_len; 460 rmsgimplp->im_msgbufs_recv.im_bufs_cl_data_len = 461 msgimplp->im_msgbufs_send.im_bufs_cl_data_len; 462 bcopy((void *)msgimplp->im_msgbufs_send.im_bufs_cl_data, 463 (void *)rmsgimplp->im_msgbufs_recv.im_bufs_cl_data, 464 msgimplp->im_msgbufs_send.im_bufs_cl_data_len); 465 466 /* Copy the global address information from the source message */ 467 bcopy((void *)&msgimplp->im_global_addr, 468 (void *)&rmsgimplp->im_global_addr, 469 sizeof (ibmf_global_addr_info_t)); 470 471 /* Copy the local address information from the source message */ 472 bcopy((void *)&msgimplp->im_local_addr, 473 (void *)&rmsgimplp->im_local_addr, 474 sizeof (ibmf_addr_info_t)); 475 476 /* 477 * Call the receive callback for the agent/manager the packet is 478 * destined for. 479 */ 480 rmsgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_DONE; 481 482 /* 483 * Decrement the message reference count 484 * This count was incremented either when the message was found 485 * on the client's message list (ibmf_i_find_msg()) or when 486 * a new message was created for unsolicited data 487 */ 488 IBMF_MSG_DECR_REFCNT(rmsgimplp); 489 490 mutex_exit(&rmsgimplp->im_mutex); 491 492 if (rbuf_alloced) { 493 mutex_enter(&clientp->ic_kstat_mutex); 494 IBMF_ADD32_KSTATS(clientp, recv_bufs_alloced, 1); 495 mutex_exit(&clientp->ic_kstat_mutex); 496 } 497 498 /* add the source message to the source client's list */ 499 ibmf_i_client_add_msg(clientp, msgimplp); 500 501 /* remove the destination message from the list */ 502 ibmf_i_client_rem_msg(rclientp, rmsgimplp, &ref_cnt); 503 504 /* 505 * Notify the client if the message reference count is zero. 506 * At this point, we know that the transaction is done and 507 * the message has been removed from the client's message list. 508 * So, we only need to make sure the reference count is zero 509 * before notifying the client. 510 */ 511 if (ref_cnt == 0) 512 ibmf_i_notify_client(rmsgimplp); 513 514 /* perform source client transaction termination processing */ 515 ibmf_i_dr_loopback_term(clientp, msgimplp, blocking); 516 517 IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_dr_loopback_filter_end, 518 IBMF_TNF_TRACE, "", "ibmf_i_dr_loopback_filter() exit, ret = %d\n", 519 tnf_uint, status, ret); 520 521 return (IBMF_SUCCESS); 522 } 523