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