xref: /illumos-gate/usr/src/uts/common/io/ib/mgt/ibmf/ibmf_dr.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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