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