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