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