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
tavor_agent_handlers_init(tavor_state_t * state)63 tavor_agent_handlers_init(tavor_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->ts_cfg_profile->cp_qp0_agents_in_fw) &&
70 (state->ts_cfg_profile->cp_qp1_agents_in_fw)) {
71 return (DDI_SUCCESS);
72 }
73
74 /*
75 * Build a unique name for the Tavor task queue from the Tavor driver
76 * instance number and TAVOR_TASKQ_NAME
77 */
78 rsrc_name = (char *)kmem_zalloc(TAVOR_RSRC_NAME_MAXLEN, KM_SLEEP);
79 TAVOR_RSRC_NAME(rsrc_name, TAVOR_TASKQ_NAME);
80
81 /* Initialize the Tavor IB management agent list */
82 status = tavor_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->ts_taskq_agents = ddi_taskq_create(state->ts_dip,
94 rsrc_name, TAVOR_TASKQ_NTHREADS, TASKQ_DEFAULTPRI, 0);
95 if (state->ts_taskq_agents == NULL) {
96 tavor_agent_list_fini(state);
97 goto agentsinit_fail;
98 }
99
100 /* Now attempt to register all of the agents with the IBMF */
101 status = tavor_agent_register_all(state);
102 if (status != DDI_SUCCESS) {
103 ddi_taskq_destroy(state->ts_taskq_agents);
104 tavor_agent_list_fini(state);
105 goto agentsinit_fail;
106 }
107
108 kmem_free(rsrc_name, TAVOR_RSRC_NAME_MAXLEN);
109 return (DDI_SUCCESS);
110
111 agentsinit_fail:
112 kmem_free(rsrc_name, TAVOR_RSRC_NAME_MAXLEN);
113 return (status);
114 }
115
116
117 /*
118 * tavor_agent_handlers_fini()
119 * Context: Only called from detach() path context
120 */
121 int
tavor_agent_handlers_fini(tavor_state_t * state)122 tavor_agent_handlers_fini(tavor_state_t *state)
123 {
124 int status;
125
126 /* Determine if we need to unregister any agents from the IBMF */
127 if ((state->ts_cfg_profile->cp_qp0_agents_in_fw) &&
128 (state->ts_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 = tavor_agent_unregister_all(state, state->ts_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 * tavor_agent_unregister_all() above).
145 */
146 ddi_taskq_destroy(state->ts_taskq_agents);
147
148 /* Teardown the Tavor IB management agent list */
149 tavor_agent_list_fini(state);
150
151 return (DDI_SUCCESS);
152 }
153
154
155 /*
156 * tavor_agent_request_cb()
157 * Context: Called from the IBMF context
158 */
159 static void
tavor_agent_request_cb(ibmf_handle_t ibmf_handle,ibmf_msg_t * msgp,void * args)160 tavor_agent_request_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp,
161 void *args)
162 {
163 tavor_agent_handler_arg_t *cb_args;
164 tavor_agent_list_t *curr;
165 tavor_state_t *state;
166 int status;
167
168 curr = (tavor_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 = (tavor_agent_handler_arg_t *)kmem_zalloc(
178 sizeof (tavor_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->ts_taskq_agents,
196 tavor_agent_handle_req, cb_args, DDI_NOSLEEP);
197 if (status == DDI_FAILURE) {
198 kmem_free(cb_args, sizeof (tavor_agent_handler_arg_t));
199 (void) ibmf_free_msg(ibmf_handle, &msgp);
200 }
201 }
202
203 /*
204 * tavor_agent_handle_req()
205 * Context: Called with priority of taskQ thread
206 */
207 static void
tavor_agent_handle_req(void * cb_args)208 tavor_agent_handle_req(void *cb_args)
209 {
210 tavor_agent_handler_arg_t *agent_args;
211 tavor_agent_list_t *curr;
212 tavor_state_t *state;
213 ibmf_handle_t ibmf_handle;
214 ibmf_msg_t *msgp;
215 ibmf_msg_bufs_t *recv_msgbufp;
216 ibmf_msg_bufs_t *send_msgbufp;
217 ibmf_retrans_t retrans;
218 uint_t port;
219 int status;
220
221 /* Extract the necessary info from the callback args parameter */
222 agent_args = (tavor_agent_handler_arg_t *)cb_args;
223 ibmf_handle = agent_args->ahd_ibmfhdl;
224 msgp = agent_args->ahd_ibmfmsg;
225 curr = agent_args->ahd_agentlist;
226 state = curr->agl_state;
227 port = curr->agl_port;
228
229 /*
230 * Set the message send buffer pointers to the message receive buffer
231 * pointers to reuse the IBMF provided buffers for the sender
232 * information.
233 */
234 recv_msgbufp = &msgp->im_msgbufs_recv;
235 send_msgbufp = &msgp->im_msgbufs_send;
236 bcopy(recv_msgbufp, send_msgbufp, sizeof (ibmf_msg_bufs_t));
237
238 /*
239 * Check if the incoming packet is a special "Tavor Trap" MAD. If it
240 * is, then do the special handling. If it isn't, then simply pass it
241 * on to the firmware and forward the response back to the IBMF.
242 *
243 * Note: Tavor has a unique method for handling internally generated
244 * Traps. All internally detected/generated Trap messages are
245 * automatically received by the IBMF (as receive completions on QP0),
246 * which (because all Tavor Trap MADs have SLID == 0) detects it as a
247 * special "Tavor Trap" and forwards it here to the driver's SMA.
248 * It is then our responsibility here to fill in the Trap MAD's DLID
249 * for forwarding to the real Master SM (as programmed in the port's
250 * PortInfo.MasterSMLID field.)
251 */
252 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(msgp->im_local_addr))
253 if (TAVOR_IS_SPECIAL_TRAP_MAD(msgp)) {
254 msgp->im_local_addr.ia_remote_lid =
255 TAVOR_PORT_MASTERSMLID_GET(state, port - 1);
256 } else {
257 /*
258 * Post the command to the firmware (using the MAD_IFC
259 * command). Note: We also reuse the command that was passed
260 * in. We pass the pointer to the original MAD payload as if
261 * it were both the source of the incoming MAD as well as the
262 * destination for the response. This is acceptable and saves
263 * us the step of one additional copy. Note: If this command
264 * fails for any reason other than TAVOR_CMD_BAD_PKT, it
265 * probably indicates a serious problem.
266 */
267 status = tavor_mad_ifc_cmd_post(state, port,
268 TAVOR_CMD_SLEEP_NOSPIN,
269 (uint32_t *)recv_msgbufp->im_bufs_mad_hdr,
270 (uint32_t *)send_msgbufp->im_bufs_mad_hdr);
271 if (status != TAVOR_CMD_SUCCESS) {
272 if ((status != TAVOR_CMD_BAD_PKT) &&
273 (status != TAVOR_CMD_INSUFF_RSRC)) {
274 cmn_err(CE_CONT, "Tavor: MAD_IFC (port %02d) "
275 "command failed: %08x\n", port, status);
276 }
277
278 /* finish cleanup */
279 goto tavor_agent_handle_req_skip_response;
280 }
281 }
282
283 /*
284 * If incoming MAD was "TrapRepress", then no response is necessary.
285 * Free the IBMF message and return.
286 */
287 if (TAVOR_IS_TRAP_REPRESS_MAD(msgp)) {
288 goto tavor_agent_handle_req_skip_response;
289 }
290
291 /*
292 * Modify the response MAD as necessary (for any special cases).
293 * Specifically, if this MAD was a directed route MAD, then some
294 * additional packet manipulation may be necessary because the Tavor
295 * firmware does not do all the required steps to respond to the
296 * MAD.
297 */
298 tavor_agent_mad_resp_handling(state, msgp, port);
299
300 /*
301 * Send response (or forwarded "Trap" MAD) back to IBMF. We use the
302 * "response callback" to indicate when it is appropriate (later) to
303 * free the IBMF msg.
304 */
305 status = ibmf_msg_transport(ibmf_handle, IBMF_QP_HANDLE_DEFAULT,
306 msgp, &retrans, tavor_agent_response_cb, state, 0);
307 if (status != IBMF_SUCCESS) {
308 goto tavor_agent_handle_req_skip_response;
309 }
310
311 /* Free up the callback args parameter */
312 kmem_free(agent_args, sizeof (tavor_agent_handler_arg_t));
313 return;
314
315 tavor_agent_handle_req_skip_response:
316 /* Free up the ibmf message */
317 status = ibmf_free_msg(ibmf_handle, &msgp);
318 /* Free up the callback args parameter */
319 kmem_free(agent_args, sizeof (tavor_agent_handler_arg_t));
320 }
321
322
323 /*
324 * tavor_agent_response_cb()
325 * Context: Called from the IBMF context
326 */
327 /* ARGSUSED */
328 static void
tavor_agent_response_cb(ibmf_handle_t ibmf_handle,ibmf_msg_t * msgp,void * args)329 tavor_agent_response_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp,
330 void *args)
331 {
332 /*
333 * It is the responsibility of each IBMF callback recipient to free
334 * the packets that it has been given. Now that we are in the
335 * response callback, we can be assured that it is safe to do so.
336 */
337 (void) ibmf_free_msg(ibmf_handle, &msgp);
338 }
339
340
341 /*
342 * tavor_agent_list_init()
343 * Context: Only called from attach() path context
344 */
345 static int
tavor_agent_list_init(tavor_state_t * state)346 tavor_agent_list_init(tavor_state_t *state)
347 {
348 tavor_agent_list_t *curr;
349 uint_t num_ports, num_agents, num_agents_per_port;
350 uint_t num_sma_agents = 0;
351 uint_t num_pma_agents = 0;
352 uint_t num_bma_agents = 0;
353 uint_t do_qp0, do_qp1;
354 int i, j, indx;
355
356 /*
357 * Calculate the number of registered agents for each port
358 * (SMA, PMA, and BMA) and determine whether or not to register
359 * a given agent with the IBMF (or whether to let the Tavor firmware
360 * handle it)
361 */
362 num_ports = state->ts_cfg_profile->cp_num_ports;
363 num_agents = 0;
364 num_agents_per_port = 0;
365 do_qp0 = state->ts_cfg_profile->cp_qp0_agents_in_fw;
366 do_qp1 = state->ts_cfg_profile->cp_qp1_agents_in_fw;
367 if (do_qp0 == 0) {
368 num_agents += (num_ports * TAVOR_NUM_QP0_AGENTS_PER_PORT);
369 num_agents_per_port += TAVOR_NUM_QP0_AGENTS_PER_PORT;
370 num_sma_agents = num_ports;
371 }
372 if (do_qp1 == 0) {
373 num_agents += (num_ports * TAVOR_NUM_QP1_AGENTS_PER_PORT);
374 num_agents_per_port += TAVOR_NUM_QP1_AGENTS_PER_PORT;
375 num_pma_agents = num_ports;
376 /*
377 * The following line is commented out because the Tavor
378 * firmware does not currently support a BMA. If it did,
379 * then we would want to register the agent with the IBMF.
380 * (We would also need to have TAVOR_NUM_QP1_AGENTS_PER_PORT
381 * set to 2, instead of 1.)
382 *
383 * num_bma_agents = num_ports;
384 */
385 }
386
387 state->ts_num_agents = num_agents;
388
389 /*
390 * Allocate the memory for all of the agent list entries
391 */
392 state->ts_agents = (tavor_agent_list_t *)kmem_zalloc(num_agents *
393 sizeof (tavor_agent_list_t), KM_SLEEP);
394 if (state->ts_agents == NULL) {
395 return (DDI_FAILURE);
396 }
397
398 /*
399 * Fill in each of the agent list entries with the agent's
400 * MgmtClass, port number, and Tavor softstate pointer
401 */
402 indx = 0;
403 for (i = 0; i < num_agents_per_port; i++) {
404 for (j = 0; j < num_ports; j++) {
405 curr = &state->ts_agents[indx];
406 curr->agl_state = state;
407 curr->agl_port = j + 1;
408
409 if ((do_qp0 == 0) && num_sma_agents) {
410 curr->agl_mgmtclass = SUBN_AGENT;
411 num_sma_agents--;
412 indx++;
413 } else if ((do_qp1 == 0) && (num_pma_agents)) {
414 curr->agl_mgmtclass = PERF_AGENT;
415 num_pma_agents--;
416 indx++;
417 } else if ((do_qp1 == 0) && (num_bma_agents)) {
418 curr->agl_mgmtclass = BM_AGENT;
419 num_bma_agents--;
420 indx++;
421 }
422 }
423 }
424
425 return (DDI_SUCCESS);
426 }
427
428
429 /*
430 * tavor_agent_list_fini()
431 * Context: Only called from attach() and/or detach() path contexts
432 */
433 static void
tavor_agent_list_fini(tavor_state_t * state)434 tavor_agent_list_fini(tavor_state_t *state)
435 {
436 /* Free up the memory for the agent list entries */
437 kmem_free(state->ts_agents,
438 state->ts_num_agents * sizeof (tavor_agent_list_t));
439 }
440
441
442 /*
443 * tavor_agent_register_all()
444 * Context: Only called from attach() path context
445 */
446 static int
tavor_agent_register_all(tavor_state_t * state)447 tavor_agent_register_all(tavor_state_t *state)
448 {
449 tavor_agent_list_t *curr;
450 ibmf_register_info_t ibmf_reg;
451 ibmf_impl_caps_t impl_caps;
452 ib_guid_t nodeguid;
453 int i, status, num_registered;
454
455 /* Get the Tavor NodeGUID from the softstate */
456 nodeguid = state->ts_ibtfinfo.hca_attr->hca_node_guid;
457
458 /*
459 * Register each of the agents with the IBMF (and add callbacks for
460 * each to the tavor_agent_request_cb() routine). Note: If we
461 * fail somewhere along the line here, we attempt to cleanup as much
462 * of the mess as we can and then jump to tavor_agent_unregister_all()
463 * to cleanup the rest.
464 */
465 num_registered = 0;
466 for (i = 0; i < state->ts_num_agents; i++) {
467
468 /* Register each agent with the IBMF */
469 curr = &state->ts_agents[i];
470 ibmf_reg.ir_ci_guid = nodeguid;
471 ibmf_reg.ir_port_num = curr->agl_port;
472 ibmf_reg.ir_client_class = curr->agl_mgmtclass;
473 status = ibmf_register(&ibmf_reg, IBMF_VERSION, 0,
474 NULL, NULL, &curr->agl_ibmfhdl, &impl_caps);
475 if (status != IBMF_SUCCESS) {
476 goto agents_reg_fail;
477 }
478
479 /* Setup callbacks with the IBMF */
480 status = ibmf_setup_async_cb(curr->agl_ibmfhdl,
481 IBMF_QP_HANDLE_DEFAULT, tavor_agent_request_cb, curr, 0);
482 if (status != IBMF_SUCCESS) {
483 (void) ibmf_unregister(&curr->agl_ibmfhdl, 0);
484 goto agents_reg_fail;
485 }
486 num_registered++;
487 }
488
489 return (DDI_SUCCESS);
490
491 agents_reg_fail:
492 (void) tavor_agent_unregister_all(state, num_registered);
493 return (DDI_FAILURE);
494 }
495
496
497 /*
498 * tavor_agent_unregister_all()
499 * Context: Only called from detach() path context
500 */
501 static int
tavor_agent_unregister_all(tavor_state_t * state,int num_reg)502 tavor_agent_unregister_all(tavor_state_t *state, int num_reg)
503 {
504 tavor_agent_list_t *curr;
505 int i, status;
506
507 /*
508 * For each registered agent in the agent list, teardown the
509 * callbacks from the IBMF and unregister.
510 */
511 for (i = 0; i < num_reg; i++) {
512 curr = &state->ts_agents[i];
513
514 /* Teardown the IBMF callback */
515 status = ibmf_tear_down_async_cb(curr->agl_ibmfhdl,
516 IBMF_QP_HANDLE_DEFAULT, 0);
517 if (status != IBMF_SUCCESS) {
518 return (DDI_FAILURE);
519 }
520
521 /* Unregister the agent from the IBMF */
522 status = ibmf_unregister(&curr->agl_ibmfhdl, 0);
523 if (status != IBMF_SUCCESS) {
524 return (DDI_FAILURE);
525 }
526 }
527
528 return (DDI_SUCCESS);
529 }
530
531
532 /*
533 * tavor_agent_mad_resp_handling()
534 * Context: Called with priority of taskQ thread
535 */
536 /* ARGSUSED */
537 static void
tavor_agent_mad_resp_handling(tavor_state_t * state,ibmf_msg_t * msgp,uint_t port)538 tavor_agent_mad_resp_handling(tavor_state_t *state, ibmf_msg_t *msgp,
539 uint_t port)
540 {
541 ib_mad_hdr_t *rmadhdrp = msgp->im_msgbufs_recv.im_bufs_mad_hdr;
542 ib_mad_hdr_t *smadhdrp = msgp->im_msgbufs_send.im_bufs_mad_hdr;
543 uint_t hop_count, hop_point;
544 uchar_t *resp, *ret_path;
545
546 resp = (uchar_t *)msgp->im_msgbufs_send.im_bufs_cl_data;
547
548 /*
549 * Handle directed route MADs as a special case. Tavor firmware
550 * does not update the "direction" bit, "hop pointer", "Return
551 * Path" or, in fact, any of the "directed route" parameters. So
552 * the responsibility falls on Tavor driver software to inspect the
553 * MADs and update those fields as appropriate (see section 14.2.2
554 * of the IBA specification, rev 1.1)
555 */
556 if (TAVOR_MAD_IS_DR(rmadhdrp)) {
557
558 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*((sm_dr_mad_hdr_t *)rmadhdrp)))
559 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*((sm_dr_mad_hdr_t *)smadhdrp)))
560
561 /*
562 * Set the "Direction" bit to one. This indicates that this
563 * is now directed route response
564 */
565 TAVOR_DRMAD_SET_DIRECTION(rmadhdrp);
566
567 /* Extract the "hop pointer" and "hop count" from the MAD */
568 hop_count = TAVOR_DRMAD_GET_HOPCOUNT(rmadhdrp);
569 hop_point = TAVOR_DRMAD_GET_HOPPOINTER(rmadhdrp);
570
571 /* Append the port we came in on to the "Return Path" */
572 if ((hop_count != 0) && ((hop_point == hop_count) ||
573 (hop_point == hop_count + 1))) {
574 ret_path = &resp[TAVOR_DRMAD_RETURN_PATH_OFFSET];
575 ret_path[hop_point] = port;
576 }
577
578 /* Then increment the "hop pointer" in the MAD */
579 hop_point++;
580 TAVOR_DRMAD_SET_HOPPOINTER(smadhdrp, hop_point);
581 }
582 }
583