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