xref: /illumos-gate/usr/src/uts/common/io/ib/adapters/tavor/tavor_agents.c (revision 915894ef19890baaed00080f85f6b69e225cda98)
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 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * tavor_agents.c
29  *    Tavor InfiniBand Management Agent (SMA, PMA, BMA) routines
30  *
31  *    Implements all the routines necessary for initializing, handling,
32  *    and (later) tearing down all the infrastructure necessary for Tavor
33  *    MAD processing.
34  */
35 
36 #include <sys/types.h>
37 #include <sys/conf.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/modctl.h>
41 
42 #include <sys/ib/adapters/tavor/tavor.h>
43 #include <sys/ib/mgt/ibmf/ibmf.h>
44 #include <sys/disp.h>
45 
46 static void tavor_agent_request_cb(ibmf_handle_t ibmf_handle,
47     ibmf_msg_t *msgp, void *args);
48 static void tavor_agent_handle_req(void *cb_args);
49 static void tavor_agent_response_cb(ibmf_handle_t ibmf_handle,
50     ibmf_msg_t *msgp, void *args);
51 static int tavor_agent_list_init(tavor_state_t *state);
52 static void tavor_agent_list_fini(tavor_state_t *state);
53 static int tavor_agent_register_all(tavor_state_t *state);
54 static int tavor_agent_unregister_all(tavor_state_t *state, int num_reg);
55 static void tavor_agent_mad_resp_handling(tavor_state_t *state,
56     ibmf_msg_t *msgp, uint_t port);
57 
58 /*
59  * tavor_agent_handlers_init()
60  *    Context: Only called from attach() and/or detach() path contexts
61  */
62 int
63 tavor_agent_handlers_init(tavor_state_t *state)
64 {
65 	int		status;
66 	char		*errormsg, *rsrc_name;
67 
68 	TAVOR_TNF_ENTER(tavor_agent_handlers_init);
69 
70 	/* Determine if we need to register any agents with the IBMF */
71 	if ((state->ts_cfg_profile->cp_qp0_agents_in_fw) &&
72 	    (state->ts_cfg_profile->cp_qp1_agents_in_fw)) {
73 		TAVOR_TNF_EXIT(tavor_agent_handlers_init);
74 		return (DDI_SUCCESS);
75 	}
76 
77 	/*
78 	 * Build a unique name for the Tavor task queue from the Tavor driver
79 	 * instance number and TAVOR_TASKQ_NAME
80 	 */
81 	rsrc_name = (char *)kmem_zalloc(TAVOR_RSRC_NAME_MAXLEN, KM_SLEEP);
82 	TAVOR_RSRC_NAME(rsrc_name, TAVOR_TASKQ_NAME);
83 
84 	/* Initialize the Tavor IB management agent list */
85 	status = tavor_agent_list_init(state);
86 	if (status != DDI_SUCCESS) {
87 		/* Set "status" and "errormsg" and goto failure */
88 		TAVOR_TNF_FAIL(DDI_FAILURE, "failed agent list init");
89 		goto agentsinit_fail;
90 	}
91 
92 	/*
93 	 * Initialize the agent handling task queue.  Note: We set the task
94 	 * queue priority to the minimum system priority.  At this point this
95 	 * is considered acceptable because MADs are unreliable datagrams
96 	 * and could get lost (in general) anyway.
97 	 */
98 	state->ts_taskq_agents = ddi_taskq_create(state->ts_dip,
99 	    rsrc_name, TAVOR_TASKQ_NTHREADS, TASKQ_DEFAULTPRI, 0);
100 	if (state->ts_taskq_agents == NULL) {
101 		tavor_agent_list_fini(state);
102 		/* Set "status" and "errormsg" and goto failure */
103 		TAVOR_TNF_FAIL(DDI_FAILURE, "failed task queue");
104 		goto agentsinit_fail;
105 	}
106 
107 	/* Now attempt to register all of the agents with the IBMF */
108 	status = tavor_agent_register_all(state);
109 	if (status != DDI_SUCCESS) {
110 		ddi_taskq_destroy(state->ts_taskq_agents);
111 		tavor_agent_list_fini(state);
112 		/* Set "status" and "errormsg" and goto failure */
113 		TAVOR_TNF_FAIL(DDI_FAILURE, "failed IBMF register");
114 		goto agentsinit_fail;
115 	}
116 
117 	kmem_free(rsrc_name, TAVOR_RSRC_NAME_MAXLEN);
118 	TAVOR_TNF_EXIT(tavor_agent_handlers_init);
119 	return (DDI_SUCCESS);
120 
121 agentsinit_fail:
122 	kmem_free(rsrc_name, TAVOR_RSRC_NAME_MAXLEN);
123 	TNF_PROBE_1(tavor_agent_handlers_init_fail, TAVOR_TNF_ERROR, "",
124 	    tnf_string, msg, errormsg);
125 	TAVOR_TNF_EXIT(tavor_agent_handlers_init);
126 	return (status);
127 }
128 
129 
130 /*
131  * tavor_agent_handlers_fini()
132  *    Context: Only called from detach() path context
133  */
134 int
135 tavor_agent_handlers_fini(tavor_state_t *state)
136 {
137 	int		status;
138 
139 	TAVOR_TNF_ENTER(tavor_agent_handlers_fini);
140 
141 	/* Determine if we need to unregister any agents from the IBMF */
142 	if ((state->ts_cfg_profile->cp_qp0_agents_in_fw) &&
143 	    (state->ts_cfg_profile->cp_qp1_agents_in_fw)) {
144 		TAVOR_TNF_EXIT(tavor_agent_handlers_fini);
145 		return (DDI_SUCCESS);
146 	}
147 
148 	/* Now attempt to unregister all of the agents from the IBMF */
149 	status = tavor_agent_unregister_all(state, state->ts_num_agents);
150 	if (status != DDI_SUCCESS) {
151 		TNF_PROBE_0(tavor_agent_handlers_fini_unreg_fail,
152 		    TAVOR_TNF_ERROR, "");
153 		TAVOR_TNF_EXIT(tavor_agent_handlers_fini);
154 		return (DDI_FAILURE);
155 	}
156 
157 	/*
158 	 * Destroy the task queue.  The task queue destroy is guaranteed to
159 	 * wait until any scheduled tasks have completed.  We are able to
160 	 * guarantee that no _new_ tasks will be added the task queue while
161 	 * we are in the ddi_taskq_destroy() call because we have
162 	 * (at this point) successfully unregistered from IBMF (in
163 	 * tavor_agent_unregister_all() above).
164 	 */
165 	ddi_taskq_destroy(state->ts_taskq_agents);
166 
167 	/* Teardown the Tavor IB management agent list */
168 	tavor_agent_list_fini(state);
169 
170 	TAVOR_TNF_EXIT(tavor_agent_handlers_fini);
171 	return (DDI_SUCCESS);
172 }
173 
174 
175 /*
176  * tavor_agent_request_cb()
177  *    Context: Called from the IBMF context
178  */
179 static void
180 tavor_agent_request_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp,
181     void *args)
182 {
183 	tavor_agent_handler_arg_t	*cb_args;
184 	tavor_agent_list_t		*curr;
185 	tavor_state_t			*state;
186 	int				status;
187 	int				ibmf_status;
188 
189 	TAVOR_TNF_ENTER(tavor_agent_request_cb);
190 
191 	curr  = (tavor_agent_list_t *)args;
192 	state = curr->agl_state;
193 
194 	/*
195 	 * Allocate space to hold the callback args (for passing to the
196 	 * task queue).  Note: If we are unable to allocate space for the
197 	 * the callback args here, then we just return.  But we must ensure
198 	 * that we call ibmf_free_msg() to free up the message.
199 	 */
200 	cb_args = (tavor_agent_handler_arg_t *)kmem_zalloc(
201 	    sizeof (tavor_agent_handler_arg_t), KM_NOSLEEP);
202 	if (cb_args == NULL) {
203 		ibmf_status = ibmf_free_msg(ibmf_handle, &msgp);
204 		if (ibmf_status != IBMF_SUCCESS) {
205 			TNF_PROBE_1(tavor_agent_request_cb_ibmf_free_msg_fail,
206 			    TAVOR_TNF_ERROR, "", tnf_uint, ibmf_status,
207 			    ibmf_status);
208 		}
209 		TNF_PROBE_0(tavor_agent_request_cb_kma_fail,
210 		    TAVOR_TNF_ERROR, "");
211 		TAVOR_TNF_EXIT(tavor_agent_request_cb);
212 		return;
213 	}
214 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cb_args))
215 
216 	/* Fill in the callback args */
217 	cb_args->ahd_ibmfhdl	= ibmf_handle;
218 	cb_args->ahd_ibmfmsg	= msgp;
219 	cb_args->ahd_agentlist	= args;
220 
221 	/*
222 	 * Dispatch the message to the task queue.  Note: Just like above,
223 	 * if this request fails for any reason then make sure to free up
224 	 * the IBMF message and then return
225 	 */
226 	status = ddi_taskq_dispatch(state->ts_taskq_agents,
227 	    tavor_agent_handle_req, cb_args, DDI_NOSLEEP);
228 	if (status == DDI_FAILURE) {
229 		kmem_free(cb_args, sizeof (tavor_agent_handler_arg_t));
230 		ibmf_status = ibmf_free_msg(ibmf_handle, &msgp);
231 		if (ibmf_status != IBMF_SUCCESS) {
232 			TNF_PROBE_1(tavor_agent_request_cb_ibmf_free_msg_fail,
233 			    TAVOR_TNF_ERROR, "", tnf_uint, ibmf_status,
234 			    ibmf_status);
235 		}
236 		TNF_PROBE_0(tavor_agent_request_cb_taskq_fail,
237 		    TAVOR_TNF_ERROR, "");
238 	}
239 	TAVOR_TNF_EXIT(tavor_agent_request_cb);
240 }
241 
242 /*
243  * tavor_agent_handle_req()
244  *    Context: Called with priority of taskQ thread
245  */
246 static void
247 tavor_agent_handle_req(void *cb_args)
248 {
249 	tavor_agent_handler_arg_t	*agent_args;
250 	tavor_agent_list_t		*curr;
251 	tavor_state_t			*state;
252 	ibmf_handle_t			ibmf_handle;
253 	ibmf_msg_t			*msgp;
254 	ibmf_msg_bufs_t			*recv_msgbufp;
255 	ibmf_msg_bufs_t			*send_msgbufp;
256 	ibmf_retrans_t			retrans;
257 	uint_t				port;
258 	int				status;
259 
260 	TAVOR_TNF_ENTER(tavor_agent_handle_req);
261 
262 	/* Extract the necessary info from the callback args parameter */
263 	agent_args  = (tavor_agent_handler_arg_t *)cb_args;
264 	ibmf_handle = agent_args->ahd_ibmfhdl;
265 	msgp	    = agent_args->ahd_ibmfmsg;
266 	curr	    = agent_args->ahd_agentlist;
267 	state	    = curr->agl_state;
268 	port	    = curr->agl_port;
269 
270 	/*
271 	 * Set the message send buffer pointers to the message receive buffer
272 	 * pointers to reuse the IBMF provided buffers for the sender
273 	 * information.
274 	 */
275 	recv_msgbufp = &msgp->im_msgbufs_recv;
276 	send_msgbufp = &msgp->im_msgbufs_send;
277 	bcopy(recv_msgbufp, send_msgbufp, sizeof (ibmf_msg_bufs_t));
278 
279 	/*
280 	 * Check if the incoming packet is a special "Tavor Trap" MAD.  If it
281 	 * is, then do the special handling.  If it isn't, then simply pass it
282 	 * on to the firmware and forward the response back to the IBMF.
283 	 *
284 	 * Note: Tavor has a unique method for handling internally generated
285 	 * Traps.  All internally detected/generated Trap messages are
286 	 * automatically received by the IBMF (as receive completions on QP0),
287 	 * which (because all Tavor Trap MADs have SLID == 0) detects it as a
288 	 * special "Tavor Trap" and forwards it here to the driver's SMA.
289 	 * It is then our responsibility here to fill in the Trap MAD's DLID
290 	 * for forwarding to the real Master SM (as programmed in the port's
291 	 * PortInfo.MasterSMLID field.)
292 	 */
293 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(msgp->im_local_addr))
294 	if (TAVOR_IS_SPECIAL_TRAP_MAD(msgp)) {
295 		msgp->im_local_addr.ia_remote_lid =
296 		    TAVOR_PORT_MASTERSMLID_GET(state, port - 1);
297 	} else {
298 		/*
299 		 * Post the command to the firmware (using the MAD_IFC
300 		 * command).  Note: We also reuse the command that was passed
301 		 * in.  We pass the pointer to the original MAD payload as if
302 		 * it were both the source of the incoming MAD as well as the
303 		 * destination for the response.  This is acceptable and saves
304 		 * us the step of one additional copy.  Note:  If this command
305 		 * fails for any reason other than TAVOR_CMD_BAD_PKT, it
306 		 * probably indicates a serious problem.
307 		 */
308 		status = tavor_mad_ifc_cmd_post(state, port,
309 		    TAVOR_CMD_SLEEP_NOSPIN,
310 		    (uint32_t *)recv_msgbufp->im_bufs_mad_hdr,
311 		    (uint32_t *)send_msgbufp->im_bufs_mad_hdr);
312 		if (status != TAVOR_CMD_SUCCESS) {
313 			if ((status != TAVOR_CMD_BAD_PKT) &&
314 			    (status != TAVOR_CMD_INSUFF_RSRC)) {
315 				cmn_err(CE_CONT, "Tavor: MAD_IFC (port %02d) "
316 				    "command failed: %08x\n", port, status);
317 				TNF_PROBE_1(tavor_agent_handle_req_madifc_fail,
318 				    TAVOR_TNF_ERROR, "", tnf_uint, cmd_status,
319 				    status);
320 			}
321 
322 			/* finish cleanup */
323 			goto tavor_agent_handle_req_skip_response;
324 		}
325 	}
326 
327 	/*
328 	 * If incoming MAD was "TrapRepress", then no response is necessary.
329 	 * Free the IBMF message and return.
330 	 */
331 	if (TAVOR_IS_TRAP_REPRESS_MAD(msgp)) {
332 		goto tavor_agent_handle_req_skip_response;
333 	}
334 
335 	/*
336 	 * Modify the response MAD as necessary (for any special cases).
337 	 * Specifically, if this MAD was a directed route MAD, then some
338 	 * additional packet manipulation may be necessary because the Tavor
339 	 * firmware does not do all the required steps to respond to the
340 	 * MAD.
341 	 */
342 	tavor_agent_mad_resp_handling(state, msgp, port);
343 
344 	/*
345 	 * Send response (or forwarded "Trap" MAD) back to IBMF.  We use the
346 	 * "response callback" to indicate when it is appropriate (later) to
347 	 * free the IBMF msg.
348 	 */
349 	status = ibmf_msg_transport(ibmf_handle, IBMF_QP_HANDLE_DEFAULT,
350 	    msgp, &retrans, tavor_agent_response_cb, state, 0);
351 	if (status != IBMF_SUCCESS) {
352 		TNF_PROBE_1(tavor_ibmf_send_msg_fail, TAVOR_TNF_ERROR, "",
353 		    tnf_uint, ibmf_status, status);
354 		goto tavor_agent_handle_req_skip_response;
355 	}
356 
357 	/* Free up the callback args parameter */
358 	kmem_free(agent_args, sizeof (tavor_agent_handler_arg_t));
359 	TAVOR_TNF_EXIT(tavor_agent_handle_req);
360 	return;
361 
362 tavor_agent_handle_req_skip_response:
363 	/* Free up the ibmf message */
364 	status = ibmf_free_msg(ibmf_handle, &msgp);
365 	if (status != IBMF_SUCCESS) {
366 		TNF_PROBE_1(tavor_agent_handle_req_ibmf_free_msg_fail,
367 		    TAVOR_TNF_ERROR, "", tnf_uint, ibmf_status,
368 		    status);
369 	}
370 	/* Free up the callback args parameter */
371 	kmem_free(agent_args, sizeof (tavor_agent_handler_arg_t));
372 	TAVOR_TNF_EXIT(tavor_agent_handle_req);
373 }
374 
375 
376 /*
377  * tavor_agent_response_cb()
378  *    Context: Called from the IBMF context
379  */
380 /* ARGSUSED */
381 static void
382 tavor_agent_response_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp,
383     void *args)
384 {
385 	int		status;
386 
387 	TAVOR_TNF_ENTER(tavor_agent_response_cb);
388 
389 	/*
390 	 * It is the responsibility of each IBMF callback recipient to free
391 	 * the packets that it has been given.  Now that we are in the
392 	 * response callback, we can be assured that it is safe to do so.
393 	 */
394 	status = ibmf_free_msg(ibmf_handle, &msgp);
395 	if (status != IBMF_SUCCESS) {
396 		TNF_PROBE_1(tavor_agent_response_cb_ibmf_free_msg_fail,
397 		    TAVOR_TNF_ERROR, "", tnf_uint, ibmf_status, status);
398 	}
399 
400 	TAVOR_TNF_EXIT(tavor_agent_response_cb);
401 }
402 
403 
404 /*
405  * tavor_agent_list_init()
406  *    Context: Only called from attach() path context
407  */
408 static int
409 tavor_agent_list_init(tavor_state_t *state)
410 {
411 	tavor_agent_list_t	*curr;
412 	uint_t			num_ports, num_agents, num_agents_per_port;
413 	uint_t			num_sma_agents = 0;
414 	uint_t			num_pma_agents = 0;
415 	uint_t			num_bma_agents = 0;
416 	uint_t			do_qp0, do_qp1;
417 	int			i, j, indx;
418 
419 	TAVOR_TNF_ENTER(tavor_agent_list_init);
420 
421 	/*
422 	 * Calculate the number of registered agents for each port
423 	 * (SMA, PMA, and BMA) and determine whether or not to register
424 	 * a given agent with the IBMF (or whether to let the Tavor firmware
425 	 * handle it)
426 	 */
427 	num_ports	    = state->ts_cfg_profile->cp_num_ports;
428 	num_agents	    = 0;
429 	num_agents_per_port = 0;
430 	do_qp0		    = state->ts_cfg_profile->cp_qp0_agents_in_fw;
431 	do_qp1		    = state->ts_cfg_profile->cp_qp1_agents_in_fw;
432 	if (do_qp0 == 0) {
433 		num_agents += (num_ports * TAVOR_NUM_QP0_AGENTS_PER_PORT);
434 		num_agents_per_port += TAVOR_NUM_QP0_AGENTS_PER_PORT;
435 		num_sma_agents = num_ports;
436 	}
437 	if (do_qp1 == 0) {
438 		num_agents += (num_ports * TAVOR_NUM_QP1_AGENTS_PER_PORT);
439 		num_agents_per_port += TAVOR_NUM_QP1_AGENTS_PER_PORT;
440 		num_pma_agents = num_ports;
441 		/*
442 		 * The following line is commented out because the Tavor
443 		 * firmware does not currently support a BMA.  If it did,
444 		 * then we would want to register the agent with the IBMF.
445 		 * (We would also need to have TAVOR_NUM_QP1_AGENTS_PER_PORT
446 		 * set to 2, instead of 1.)
447 		 *
448 		 * num_bma_agents = num_ports;
449 		 */
450 	}
451 
452 	state->ts_num_agents = num_agents;
453 
454 	/*
455 	 * Allocate the memory for all of the agent list entries
456 	 */
457 	state->ts_agents = (tavor_agent_list_t *)kmem_zalloc(num_agents *
458 	    sizeof (tavor_agent_list_t), KM_SLEEP);
459 	if (state->ts_agents == NULL) {
460 		TNF_PROBE_0(tavor_agent_list_init_kma_fail,
461 		    TAVOR_TNF_ERROR, "");
462 		TAVOR_TNF_EXIT(tavor_agent_list_init);
463 		return (DDI_FAILURE);
464 	}
465 
466 	/*
467 	 * Fill in each of the agent list entries with the agent's
468 	 * MgmtClass, port number, and Tavor softstate pointer
469 	 */
470 	indx = 0;
471 	for (i = 0; i < num_agents_per_port; i++) {
472 		for (j = 0; j < num_ports; j++) {
473 			curr = &state->ts_agents[indx];
474 			curr->agl_state = state;
475 			curr->agl_port  = j + 1;
476 
477 			if ((do_qp0 == 0) && num_sma_agents) {
478 				curr->agl_mgmtclass = SUBN_AGENT;
479 				num_sma_agents--;
480 				indx++;
481 			} else if ((do_qp1 == 0) && (num_pma_agents)) {
482 				curr->agl_mgmtclass = PERF_AGENT;
483 				num_pma_agents--;
484 				indx++;
485 			} else if ((do_qp1 == 0) && (num_bma_agents)) {
486 				curr->agl_mgmtclass = BM_AGENT;
487 				num_bma_agents--;
488 				indx++;
489 			}
490 		}
491 	}
492 
493 	TAVOR_TNF_EXIT(tavor_agent_list_init);
494 	return (DDI_SUCCESS);
495 }
496 
497 
498 /*
499  * tavor_agent_list_fini()
500  *    Context: Only called from attach() and/or detach() path contexts
501  */
502 static void
503 tavor_agent_list_fini(tavor_state_t *state)
504 {
505 	TAVOR_TNF_ENTER(tavor_agent_list_fini);
506 
507 	/* Free up the memory for the agent list entries */
508 	kmem_free(state->ts_agents,
509 	    state->ts_num_agents * sizeof (tavor_agent_list_t));
510 
511 	TAVOR_TNF_EXIT(tavor_agent_list_fini);
512 }
513 
514 
515 /*
516  * tavor_agent_register_all()
517  *    Context: Only called from attach() path context
518  */
519 static int
520 tavor_agent_register_all(tavor_state_t *state)
521 {
522 	tavor_agent_list_t	*curr;
523 	ibmf_register_info_t	ibmf_reg;
524 	ibmf_impl_caps_t	impl_caps;
525 	ib_guid_t		nodeguid;
526 	int			i, status, num_registered;
527 
528 	TAVOR_TNF_ENTER(tavor_agent_register_all);
529 
530 	/* Get the Tavor NodeGUID from the softstate */
531 	nodeguid = state->ts_ibtfinfo.hca_attr->hca_node_guid;
532 
533 	/*
534 	 * Register each of the agents with the IBMF (and add callbacks for
535 	 * each to the tavor_agent_request_cb() routine).  Note:  If we
536 	 * fail somewhere along the line here, we attempt to cleanup as much
537 	 * of the mess as we can and then jump to tavor_agent_unregister_all()
538 	 * to cleanup the rest.
539 	 */
540 	num_registered = 0;
541 	for (i = 0; i < state->ts_num_agents; i++) {
542 
543 		/* Register each agent with the IBMF */
544 		curr = &state->ts_agents[i];
545 		ibmf_reg.ir_ci_guid	 = nodeguid;
546 		ibmf_reg.ir_port_num	 = curr->agl_port;
547 		ibmf_reg.ir_client_class = curr->agl_mgmtclass;
548 		status = ibmf_register(&ibmf_reg, IBMF_VERSION, 0,
549 		    NULL, NULL, &curr->agl_ibmfhdl, &impl_caps);
550 		if (status != IBMF_SUCCESS) {
551 			TNF_PROBE_0(tavor_agent_register_all_ibmf_reg_fail,
552 			    TAVOR_TNF_ERROR, "");
553 			goto agents_reg_fail;
554 		}
555 
556 		/* Setup callbacks with the IBMF */
557 		status  = ibmf_setup_async_cb(curr->agl_ibmfhdl,
558 		    IBMF_QP_HANDLE_DEFAULT, tavor_agent_request_cb, curr, 0);
559 		if (status != IBMF_SUCCESS) {
560 			(void) ibmf_unregister(&curr->agl_ibmfhdl, 0);
561 			TNF_PROBE_0(tavor_agent_register_all_ibmf_cb_fail,
562 			    TAVOR_TNF_ERROR, "");
563 			goto agents_reg_fail;
564 		}
565 		num_registered++;
566 	}
567 
568 	TAVOR_TNF_EXIT(tavor_agent_register_all);
569 	return (DDI_SUCCESS);
570 
571 agents_reg_fail:
572 	(void) tavor_agent_unregister_all(state, num_registered);
573 	TAVOR_TNF_EXIT(tavor_agent_register_all);
574 	return (DDI_FAILURE);
575 }
576 
577 
578 /*
579  * tavor_agent_unregister_all()
580  *    Context: Only called from detach() path context
581  */
582 static int
583 tavor_agent_unregister_all(tavor_state_t *state, int num_reg)
584 {
585 	tavor_agent_list_t	*curr;
586 	int			i, status;
587 
588 	TAVOR_TNF_ENTER(tavor_agent_unregister_all);
589 
590 	/*
591 	 * For each registered agent in the agent list, teardown the
592 	 * callbacks from the IBMF and unregister.
593 	 */
594 	for (i = 0; i < num_reg; i++) {
595 		curr = &state->ts_agents[i];
596 
597 		/* Teardown the IBMF callback */
598 		status = ibmf_tear_down_async_cb(curr->agl_ibmfhdl,
599 		    IBMF_QP_HANDLE_DEFAULT, 0);
600 		if (status != IBMF_SUCCESS) {
601 			TNF_PROBE_0(tavor_agents_unreg_teardown_cb_fail,
602 			    TAVOR_TNF_ERROR, "");
603 			TAVOR_TNF_EXIT(tavor_agent_unregister_all);
604 			return (DDI_FAILURE);
605 		}
606 
607 		/* Unregister the agent from the IBMF */
608 		status = ibmf_unregister(&curr->agl_ibmfhdl, 0);
609 		if (status != IBMF_SUCCESS) {
610 			TNF_PROBE_0(tavor_agents_unreg_ibmf_fail,
611 			    TAVOR_TNF_ERROR, "");
612 			TAVOR_TNF_EXIT(tavor_agent_unregister_all);
613 			return (DDI_FAILURE);
614 		}
615 	}
616 
617 	TAVOR_TNF_EXIT(tavor_agent_unregister_all);
618 	return (DDI_SUCCESS);
619 }
620 
621 
622 /*
623  * tavor_agent_mad_resp_handling()
624  *    Context: Called with priority of taskQ thread
625  */
626 /* ARGSUSED */
627 static void
628 tavor_agent_mad_resp_handling(tavor_state_t *state, ibmf_msg_t *msgp,
629     uint_t port)
630 {
631 	ib_mad_hdr_t	*rmadhdrp = msgp->im_msgbufs_recv.im_bufs_mad_hdr;
632 	ib_mad_hdr_t	*smadhdrp = msgp->im_msgbufs_send.im_bufs_mad_hdr;
633 	uint_t		hop_count, hop_point;
634 	uchar_t		*resp, *ret_path;
635 
636 	resp = (uchar_t *)msgp->im_msgbufs_send.im_bufs_cl_data;
637 
638 	/*
639 	 * Handle directed route MADs as a special case.  Tavor firmware
640 	 * does not update the "direction" bit, "hop pointer", "Return
641 	 * Path" or, in fact, any of the "directed route" parameters.  So
642 	 * the responsibility falls on Tavor driver software to inspect the
643 	 * MADs and update those fields as appropriate (see section 14.2.2
644 	 * of the IBA specification, rev 1.1)
645 	 */
646 	if (TAVOR_MAD_IS_DR(rmadhdrp)) {
647 
648 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*((sm_dr_mad_hdr_t *)rmadhdrp)))
649 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*((sm_dr_mad_hdr_t *)smadhdrp)))
650 
651 		/*
652 		 * Set the "Direction" bit to one.  This indicates that this
653 		 * is now directed route response
654 		 */
655 		TAVOR_DRMAD_SET_DIRECTION(rmadhdrp);
656 
657 		/* Extract the "hop pointer" and "hop count" from the MAD */
658 		hop_count = TAVOR_DRMAD_GET_HOPCOUNT(rmadhdrp);
659 		hop_point = TAVOR_DRMAD_GET_HOPPOINTER(rmadhdrp);
660 
661 		/* Append the port we came in on to the "Return Path" */
662 		if ((hop_count != 0) && ((hop_point == hop_count) ||
663 		    (hop_point == hop_count + 1))) {
664 			ret_path = &resp[TAVOR_DRMAD_RETURN_PATH_OFFSET];
665 			ret_path[hop_point] = port;
666 		}
667 
668 		/* Then increment the "hop pointer" in the MAD */
669 		hop_point++;
670 		TAVOR_DRMAD_SET_HOPPOINTER(smadhdrp, hop_point);
671 	}
672 }
673