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