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
ibmf_i_check_for_loopback(ibmf_msg_impl_t * msgimplp,ibmf_msg_cb_t msg_cb,void * msg_cb_args,ibmf_retrans_t * retrans,boolean_t * loopback)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
ibmf_i_dr_loopback_term(ibmf_client_t * clientp,ibmf_msg_impl_t * msgimplp,int blocking)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
ibmf_i_dr_loopback_filter(ibmf_client_t * clientp,ibmf_msg_impl_t * msgimplp,int blocking)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