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 (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 27 /* 28 * sol_umad.c 29 * 30 * ofuv user MAD kernel agent module 31 * 32 * Enables functionality of the OFED 1.3 Linux based MAD application code. 33 */ 34 35 #include <sys/open.h> 36 #include <sys/stat.h> 37 #include <sys/file.h> 38 #include <sys/conf.h> 39 #include <sys/modctl.h> 40 #include <sys/sysmacros.h> 41 #include <sys/ib/ibtl/ibti.h> 42 #include <sys/ib/mgt/ibmf/ibmf.h> 43 #include <sys/ib/mgt/ibmf/ibmf_rmpp.h> 44 45 #include <sys/types.h> 46 #include <sys/ib/clients/of/ofed_kernel.h> 47 #include <sys/ib/clients/of/sol_ofs/sol_ofs_common.h> 48 #include <sys/ib/clients/of/rdma/ib_user_mad.h> 49 #include <sys/ib/clients/of/sol_umad/sol_umad.h> 50 #include <sys/policy.h> 51 #include <sys/priv_const.h> /* sys/policy.h should include this, but... */ 52 53 54 #define MAX_NAME_LEN 32 55 56 #if defined(DEBUG) 57 static char *sol_umad_dbg_str = "sol_umad"; 58 #endif 59 60 /* Local definitions */ 61 static void *umad_statep; 62 63 static struct cb_ops umad_cb_ops = { 64 .cb_open = umad_open, 65 .cb_close = umad_close, 66 .cb_strategy = nodev, 67 .cb_print = nodev, 68 .cb_dump = nodev, 69 .cb_read = umad_read, 70 .cb_write = umad_write, 71 .cb_ioctl = umad_ioctl, 72 .cb_devmap = nodev, 73 .cb_mmap = nodev, 74 .cb_segmap = nodev, 75 .cb_chpoll = umad_poll, 76 .cb_prop_op = umad_prop_op, 77 .cb_str = NULL, 78 .cb_flag = D_NEW | D_MP, 79 .cb_rev = CB_REV, 80 .cb_aread = nodev, 81 .cb_awrite = nodev 82 }; 83 84 static struct dev_ops umad_dev_ops = { 85 .devo_rev = DEVO_REV, 86 .devo_refcnt = 0, 87 .devo_getinfo = umad_getinfo, 88 .devo_identify = nulldev, 89 .devo_probe = nulldev, 90 .devo_attach = umad_attach, 91 .devo_detach = umad_detach, 92 .devo_reset = nodev, 93 .devo_cb_ops = &umad_cb_ops, 94 .devo_bus_ops = NULL, 95 .devo_power = nodev, 96 .devo_quiesce = ddi_quiesce_not_needed 97 }; 98 99 static struct modldrv umad_modldrv = { 100 .drv_modops = &mod_driverops, 101 .drv_linkinfo = "Solaris IB user MAD kernel driver", 102 .drv_dev_ops = &umad_dev_ops 103 }; 104 105 static struct modlinkage modlinkage = { 106 .ml_rev = MODREV_1, 107 .ml_linkage = { 108 [0] = &umad_modldrv, 109 [1] = NULL, 110 } 111 }; 112 113 static ibt_clnt_modinfo_t ibt_clnt_modinfo = { 114 .mi_ibt_version = IBTI_V_CURR, 115 .mi_clnt_class = IBT_USER, 116 .mi_async_handler = umad_async_handler, 117 .mi_reserved = NULL, 118 .mi_clnt_name = "sol_umad" 119 }; 120 121 #define MAX_MAD_TO_IBMF_MAPPINGS 4 /* Max of 4 MADs to 1 IBMF */ 122 const struct ibmf_class_to_mad_type { 123 enum _ibmf_client_type_t ibmf_class; 124 uint8_t mad_types[MAX_MAD_TO_IBMF_MAPPINGS]; 125 } ibmf_class_to_mad_types[] = { 126 {SUBN_MANAGER, 127 {MAD_MGMT_CLASS_SUBN_LID_ROUTED, 128 MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE, 129 0}}, 130 {0, 131 {0}} 132 }; 133 134 const enum _ibmf_client_type_t umad_type_to_ibmf_class[256] = { 135 0, /* 0x00 Reserved */ 136 SUBN_MANAGER, /* 0x01 CLASS_SUBN_LID_ROUTED */ 137 0, /* 0x02 Reserved */ 138 SUBN_ADM_AGENT, /* 0x03 CLASS_SUBN_ADM */ 139 PERF_MANAGER, /* 0x04 CLASS_PERF_MGMT */ 140 BM_AGENT, /* 0x05 CLASS_BM */ 141 DEV_MGT_AGENT, /* 0x06 CLASS_DEVICE_MGMT */ 142 COMM_MGT_MANAGER_AGENT, /* 0x07 CLASS_CM */ 143 SNMP_MANAGER_AGENT, /* 0x08 CLASS_SNMP */ 144 145 VENDOR_09_MANAGER_AGENT, /* 0x09 */ 146 VENDOR_0A_MANAGER_AGENT, /* 0x0A */ 147 VENDOR_0B_MANAGER_AGENT, /* 0x0B */ 148 VENDOR_0C_MANAGER_AGENT, /* 0x0C */ 149 VENDOR_0D_MANAGER_AGENT, /* 0x0D */ 150 VENDOR_0E_MANAGER_AGENT, /* 0x0E */ 151 VENDOR_0F_MANAGER_AGENT, /* 0x0F */ 152 153 APPLICATION_10_MANAGER_AGENT, /* 0x10 */ 154 APPLICATION_11_MANAGER_AGENT, /* 0x11 */ 155 APPLICATION_12_MANAGER_AGENT, /* 0x12 */ 156 APPLICATION_13_MANAGER_AGENT, /* 0x13 */ 157 APPLICATION_14_MANAGER_AGENT, /* 0x14 */ 158 APPLICATION_15_MANAGER_AGENT, /* 0x15 */ 159 APPLICATION_16_MANAGER_AGENT, /* 0x16 */ 160 APPLICATION_17_MANAGER_AGENT, /* 0x17 */ 161 APPLICATION_18_MANAGER_AGENT, /* 0x18 */ 162 APPLICATION_19_MANAGER_AGENT, /* 0x19 */ 163 APPLICATION_1A_MANAGER_AGENT, /* 0x1A */ 164 APPLICATION_1B_MANAGER_AGENT, /* 0x1B */ 165 APPLICATION_1C_MANAGER_AGENT, /* 0x1C */ 166 APPLICATION_1D_MANAGER_AGENT, /* 0x1D */ 167 APPLICATION_1E_MANAGER_AGENT, /* 0x1E */ 168 APPLICATION_1F_MANAGER_AGENT, /* 0x1F */ 169 APPLICATION_20_MANAGER_AGENT, /* 0x20 */ 170 APPLICATION_21_MANAGER_AGENT, /* 0x21 */ 171 APPLICATION_22_MANAGER_AGENT, /* 0x22 */ 172 APPLICATION_23_MANAGER_AGENT, /* 0x23 */ 173 APPLICATION_24_MANAGER_AGENT, /* 0x24 */ 174 APPLICATION_25_MANAGER_AGENT, /* 0x25 */ 175 APPLICATION_26_MANAGER_AGENT, /* 0x26 */ 176 APPLICATION_27_MANAGER_AGENT, /* 0x27 */ 177 APPLICATION_28_MANAGER_AGENT, /* 0x28 */ 178 APPLICATION_29_MANAGER_AGENT, /* 0x29 */ 179 APPLICATION_2A_MANAGER_AGENT, /* 0x2A */ 180 APPLICATION_2B_MANAGER_AGENT, /* 0x2B */ 181 APPLICATION_2C_MANAGER_AGENT, /* 0x2C */ 182 APPLICATION_2D_MANAGER_AGENT, /* 0x2D */ 183 APPLICATION_2E_MANAGER_AGENT, /* 0x2E */ 184 APPLICATION_2F_MANAGER_AGENT, /* 0x2F */ 185 186 VENDOR_30_MANAGER_AGENT, /* 0x30 */ 187 VENDOR_31_MANAGER_AGENT, /* 0x31 */ 188 VENDOR_32_MANAGER_AGENT, /* 0x32 */ 189 VENDOR_33_MANAGER_AGENT, /* 0x33 */ 190 VENDOR_34_MANAGER_AGENT, /* 0x34 */ 191 VENDOR_35_MANAGER_AGENT, /* 0x35 */ 192 VENDOR_36_MANAGER_AGENT, /* 0x36 */ 193 VENDOR_37_MANAGER_AGENT, /* 0x37 */ 194 VENDOR_38_MANAGER_AGENT, /* 0x38 */ 195 VENDOR_39_MANAGER_AGENT, /* 0x39 */ 196 VENDOR_3A_MANAGER_AGENT, /* 0x3A */ 197 VENDOR_3B_MANAGER_AGENT, /* 0x3B */ 198 VENDOR_3C_MANAGER_AGENT, /* 0x3C */ 199 VENDOR_3D_MANAGER_AGENT, /* 0x3D */ 200 VENDOR_3E_MANAGER_AGENT, /* 0x3E */ 201 VENDOR_3F_MANAGER_AGENT, /* 0x3F */ 202 VENDOR_40_MANAGER_AGENT, 203 VENDOR_41_MANAGER_AGENT, 204 VENDOR_42_MANAGER_AGENT, 205 VENDOR_43_MANAGER_AGENT, 206 VENDOR_44_MANAGER_AGENT, 207 VENDOR_45_MANAGER_AGENT, 208 VENDOR_46_MANAGER_AGENT, 209 VENDOR_47_MANAGER_AGENT, 210 VENDOR_48_MANAGER_AGENT, 211 VENDOR_49_MANAGER_AGENT, 212 VENDOR_4A_MANAGER_AGENT, 213 VENDOR_4B_MANAGER_AGENT, 214 VENDOR_4C_MANAGER_AGENT, 215 VENDOR_4D_MANAGER_AGENT, 216 VENDOR_4E_MANAGER_AGENT, 217 VENDOR_4F_MANAGER_AGENT, 218 219 0, /* 0x50 Reserved */ 220 0, /* 0x51 Reserved */ 221 0, /* 0x52 Reserved */ 222 0, /* 0x53 Reserved */ 223 0, /* 0x54 Reserved */ 224 0, /* 0x55 Reserved */ 225 0, /* 0x56 Reserved */ 226 0, /* 0x57 Reserved */ 227 0, /* 0x58 Reserved */ 228 0, /* 0x59 Reserved */ 229 0, /* 0x5A Reserved */ 230 0, /* 0x5B Reserved */ 231 0, /* 0x5C Reserved */ 232 0, /* 0x5D Reserved */ 233 0, /* 0x5E Reserved */ 234 0, /* 0x5F Reserved */ 235 0, /* 0x60 Reserved */ 236 0, /* 0x61 Reserved */ 237 0, /* 0x62 Reserved */ 238 0, /* 0x63 Reserved */ 239 0, /* 0x64 Reserved */ 240 0, /* 0x65 Reserved */ 241 0, /* 0x66 Reserved */ 242 0, /* 0x67 Reserved */ 243 0, /* 0x68 Reserved */ 244 0, /* 0x69 Reserved */ 245 0, /* 0x6A Reserved */ 246 0, /* 0x6B Reserved */ 247 0, /* 0x6C Reserved */ 248 0, /* 0x6D Reserved */ 249 0, /* 0x6E Reserved */ 250 0, /* 0x6F Reserved */ 251 0, /* 0x70 Reserved */ 252 0, /* 0x71 Reserved */ 253 0, /* 0x72 Reserved */ 254 0, /* 0x73 Reserved */ 255 0, /* 0x74 Reserved */ 256 0, /* 0x75 Reserved */ 257 0, /* 0x76 Reserved */ 258 0, /* 0x77 Reserved */ 259 0, /* 0x78 Reserved */ 260 0, /* 0x79 Reserved */ 261 0, /* 0x7A Reserved */ 262 0, /* 0x7B Reserved */ 263 0, /* 0x7C Reserved */ 264 0, /* 0x7D Reserved */ 265 0, /* 0x7E Reserved */ 266 0, /* 0x7F Reserved */ 267 0, /* 0x80 Reserved */ 268 269 SUBN_MANAGER, /* 0x81 CLASS_SUBN_DIRECT_ROUTE */ 270 271 0, /* 0x82 Reserved */ 272 0, /* 0x82 Reserved */ 273 0, /* 0x84 Reserved */ 274 0, /* 0x85 Reserved */ 275 0, /* 0x86 Reserved */ 276 0, /* 0x87 Reserved */ 277 0, /* 0x88 Reserved */ 278 0, /* 0x89 Reserved */ 279 0, /* 0x8A Reserved */ 280 0, /* 0x8B Reserved */ 281 0, /* 0x8C Reserved */ 282 0, /* 0x8D Reserved */ 283 0, /* 0x8E Reserved */ 284 0, /* 0x8f Reserved */ 285 0, /* 0x90 Reserved */ 286 0, /* 0x91 Reserved */ 287 0, /* 0x92 Reserved */ 288 0, /* 0x93 Reserved */ 289 0, /* 0x94 Reserved */ 290 0, /* 0x95 Reserved */ 291 0, /* 0x96 Reserved */ 292 0, /* 0x97 Reserved */ 293 0, /* 0x98 Reserved */ 294 0, /* 0x99 Reserved */ 295 0, /* 0x9A Reserved */ 296 0, /* 0x9B Reserved */ 297 0, /* 0x9C Reserved */ 298 0, /* 0x9D Reserved */ 299 0, /* 0x9E Reserved */ 300 0, /* 0x9F Reserved */ 301 0, /* 0xA0 Reserved */ 302 0, /* 0xA1 Reserved */ 303 0, /* 0xA2 Reserved */ 304 0, /* 0xA3 Reserved */ 305 0, /* 0xA4 Reserved */ 306 0, /* 0xA5 Reserved */ 307 0, /* 0xA6 Reserved */ 308 0, /* 0xA7 Reserved */ 309 0, /* 0xA8 Reserved */ 310 0, /* 0xA9 Reserved */ 311 0, /* 0xAA Reserved */ 312 0, /* 0xAB Reserved */ 313 0, /* 0xAC Reserved */ 314 0, /* 0xAD Reserved */ 315 0, /* 0xAE Reserved */ 316 0, /* 0xAF Reserved */ 317 0, /* 0xB0 Reserved */ 318 0, /* 0xB1 Reserved */ 319 0, /* 0xB2 Reserved */ 320 0, /* 0xB3 Reserved */ 321 0, /* 0xB4 Reserved */ 322 0, /* 0xB5 Reserved */ 323 0, /* 0xB6 Reserved */ 324 0, /* 0xB7 Reserved */ 325 0, /* 0xB8 Reserved */ 326 0, /* 0xB9 Reserved */ 327 0, /* 0xBA Reserved */ 328 0, /* 0xBB Reserved */ 329 0, /* 0xBC Reserved */ 330 0, /* 0xBD Reserved */ 331 0, /* 0xBE Reserved */ 332 0, /* 0xBF Reserved */ 333 0, /* 0xC0 Reserved */ 334 0, /* 0xC1 Reserved */ 335 0, /* 0xC2 Reserved */ 336 0, /* 0xC3 Reserved */ 337 0, /* 0xC4 Reserved */ 338 0, /* 0xC5 Reserved */ 339 0, /* 0xC6 Reserved */ 340 0, /* 0xC7 Reserved */ 341 0, /* 0xC8 Reserved */ 342 0, /* 0xC9 Reserved */ 343 0, /* 0xCA Reserved */ 344 0, /* 0xCB Reserved */ 345 0, /* 0xCC Reserved */ 346 0, /* 0xCD Reserved */ 347 0, /* 0xCE Reserved */ 348 0, /* 0xCF Reserved */ 349 0, /* 0xD0 Reserved */ 350 0, /* 0xD1 Reserved */ 351 0, /* 0xD2 Reserved */ 352 0, /* 0xD3 Reserved */ 353 0, /* 0xD4 Reserved */ 354 0, /* 0xD5 Reserved */ 355 0, /* 0xD6 Reserved */ 356 0, /* 0xD7 Reserved */ 357 0, /* 0xD8 Reserved */ 358 0, /* 0xD9 Reserved */ 359 0, /* 0xDA Reserved */ 360 0, /* 0xDB Reserved */ 361 0, /* 0xDC Reserved */ 362 0, /* 0xDD Reserved */ 363 0, /* 0xDE Reserved */ 364 0, /* 0xDF Reserved */ 365 0, /* 0xE0 Reserved */ 366 0, /* 0xE1 Reserved */ 367 0, /* 0xE2 Reserved */ 368 0, /* 0xE3 Reserved */ 369 0, /* 0xE4 Reserved */ 370 0, /* 0xE5 Reserved */ 371 0, /* 0xE6 Reserved */ 372 0, /* 0xE7 Reserved */ 373 0, /* 0xE8 Reserved */ 374 0, /* 0xE9 Reserved */ 375 0, /* 0xEA Reserved */ 376 0, /* 0xEB Reserved */ 377 0, /* 0xEC Reserved */ 378 0, /* 0xED Reserved */ 379 0, /* 0xEE Reserved */ 380 0, /* 0xEF Reserved */ 381 0, /* 0xF0 Reserved */ 382 0, /* 0xF1 Reserved */ 383 0, /* 0xF2 Reserved */ 384 0, /* 0xF3 Reserved */ 385 0, /* 0xF4 Reserved */ 386 0, /* 0xF5 Reserved */ 387 0, /* 0xF6 Reserved */ 388 0, /* 0xF7 Reserved */ 389 0, /* 0xF8 Reserved */ 390 0, /* 0xF9 Reserved */ 391 0, /* 0xFA Reserved */ 392 0, /* 0xFB Reserved */ 393 0, /* 0xFC Reserved */ 394 0, /* 0xFD Reserved */ 395 0, /* 0xFE Reserved */ 396 0, /* 0xFF Reserved */ 397 }; 398 399 /* 400 * Function: 401 * umad_init_port_info 402 * Input: 403 * info - driver info 404 * hca - hca info 405 * Output: 406 * port - port info 407 * Returns: 408 * None 409 * Called by: 410 * umad_init_hca_info 411 * Description: 412 * - Associates an hca to a port. 413 * - Initializes user context list for the port passed in 414 * - Initializes mutex to protect the user context list 415 */ 416 static void 417 umad_init_port_info(const umad_hca_info_t *hca, umad_port_info_t *port) 418 { 419 port->port_hca = hca; 420 llist_head_init(&port->port_ibmf_regs, NULL); 421 mutex_init(&port->port_lock, NULL, MUTEX_DRIVER, NULL); 422 } 423 424 /* 425 * Function: 426 * umad_release_hca_info 427 * Input: 428 * hca - hca info 429 * Output: 430 * Returns: 431 * None 432 * Called by: 433 * - umad_init_hca_info in case of error 434 * - umad_init_driver_info in case of error 435 * - umad_context_destroyed in normal case 436 * Description: 437 * - For every port associated with this hca destory the mutex assicated 438 * with the port and relese port info structure. 439 * - Closes hca handle and resets the GUID 440 */ 441 static void 442 umad_release_hca_info(umad_hca_info_t *hca) 443 { 444 unsigned int j; 445 umad_port_info_t *port; 446 #if defined(DEBUG) 447 ibt_status_t rc; 448 #endif 449 450 if (hca->hca_ports) { 451 for (j = 0; j < hca->hca_nports; j++) { 452 port = &(hca->hca_ports[j]); 453 if (port->port_num) 454 mutex_destroy(&port->port_lock); 455 } 456 kmem_free(hca->hca_ports, hca->hca_nports * 457 sizeof (umad_port_info_t)); 458 hca->hca_ports = NULL; 459 } 460 if (hca->hca_handle) { 461 #if defined(DEBUG) 462 rc = ibt_close_hca(hca->hca_handle); 463 if (rc != IBT_SUCCESS) { 464 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 465 "umad_release_hca: ibt_close_hca() returned %d\n", 466 rc); 467 } 468 #else 469 (void) ibt_close_hca(hca->hca_handle); 470 #endif 471 hca->hca_handle = 0; 472 } 473 474 hca->hca_guid = 0; 475 } 476 477 /* 478 * Function: 479 * umad_init_hca_info 480 * Input: 481 * info pointer to umad info instructure 482 * Output: 483 * hca handle associated with this hca 484 * Returns: 485 * IBT_SUCCESS 486 * IBT_HCA_IN_USE 487 * IBT_HCA_INVALID 488 * IBT_INVALID_PARAM 489 * IBT_HCA_INVALID 490 * Called by: 491 * - umad_init_driver_info in case of error 492 * Description: 493 * - It calls ibt_open_hca to get handle associated wit this hca 494 * - Determines how many port this hca has by calling ibt_query_hca 495 * - Allocates space for each port associated with this hca. 496 * - For every port it calls umad_init_port_info with the hca port 497 * structure. 498 * - It assigns port # index starting at 1 (1-N, zero is reserved, means 499 * it does not exist). 500 */ 501 static int 502 umad_init_hca_info(const umad_info_t *info, umad_hca_info_t *hca) 503 { 504 int rc; 505 unsigned int j; 506 umad_port_info_t *port; 507 508 rc = ibt_open_hca(info->info_clnt_hdl, hca->hca_guid, &hca->hca_handle); 509 if (rc != IBT_SUCCESS) 510 goto error; 511 512 rc = ibt_query_hca(hca->hca_handle, &hca->hca_attr); 513 if (rc != IBT_SUCCESS) 514 goto error; 515 516 hca->hca_nports = hca->hca_attr.hca_nports; 517 518 hca->hca_ports = 519 kmem_zalloc(sizeof (umad_port_info_t) * hca->hca_nports, KM_SLEEP); 520 521 /* Initialize ports structures. */ 522 for (j = 0; j < hca->hca_nports; j++) { 523 port = &hca->hca_ports[j]; 524 umad_init_port_info(hca, port); 525 526 /* 527 * Note: A port number different than 0 means the port has been 528 * initialized. 529 */ 530 port->port_num = j + 1; 531 } 532 533 error: 534 if (rc) 535 umad_release_hca_info(hca); 536 537 return (rc); 538 } 539 540 /* 541 * Function: 542 * umad_init_driver_info 543 * Output: 544 * info - driver info 545 * Returns: 546 * IBT_SUCCESS 547 * IBT_INVALID_PARAM 548 * IBT_HCA_IN_USE 549 * IBT_HCA_INVALID 550 * IBT_INVALID_PARAM 551 * Called by: 552 * umad_attach 553 * Description: 554 * - Registers sol_umad instance with IBTF 555 * - Calls ibt_get_hca_list to get hca count 556 * - Allocates each hca and associate it with umad_info structure 557 * - For every hca it assign GUID which was returned by ibt_get_hca_list 558 * then calls umad_init_hca_info . 559 * - Error case undone what was done, which calls umad_release_hca_info 560 */ 561 static ibt_status_t 562 umad_init_driver_info(umad_info_t *info) 563 { 564 ibt_status_t rc; 565 #if defined(DEBUG) 566 ibt_status_t rc2; 567 #endif 568 unsigned int i; 569 uint32_t hca_count; 570 ib_guid_t *hca_guids = NULL; 571 umad_hca_info_t *hca; 572 573 info->info_hca_count = 0; 574 info->info_clnt_hdl = NULL; 575 info->info_hcas = NULL; 576 577 rc = ibt_attach(&ibt_clnt_modinfo, info->info_dip, info, 578 &info->info_clnt_hdl); 579 580 if (rc != IBT_SUCCESS) 581 goto err1; 582 583 hca_count = info->info_hca_count = ibt_get_hca_list(&hca_guids); 584 585 if (hca_count == 0) { 586 rc = IBT_HCA_INVALID; 587 goto err2; 588 } 589 590 info->info_hcas = kmem_zalloc(sizeof (umad_hca_info_t) * hca_count, 591 KM_SLEEP); 592 593 for (i = 0; i < hca_count; i++) { 594 hca = &info->info_hcas[i]; 595 596 /* Note: A non zero guid means the hca has been allocated. */ 597 hca->hca_guid = hca_guids[i]; 598 599 rc = umad_init_hca_info(info, hca); 600 601 if (rc) 602 goto err3; 603 } 604 605 ibt_free_hca_list(hca_guids, hca_count); 606 607 return (0); 608 609 err3: 610 for (i = 0; i < info->info_hca_count; i++) { 611 hca = &info->info_hcas[i]; 612 613 if (hca->hca_guid) 614 umad_release_hca_info(hca); 615 } 616 kmem_free(info->info_hcas, 617 info->info_hca_count * sizeof (umad_hca_info_t)); 618 info->info_hcas = NULL; 619 620 if (hca_guids) 621 ibt_free_hca_list(hca_guids, hca_count); 622 err2: 623 624 #if defined(DEBUG) 625 rc2 = ibt_detach(info->info_clnt_hdl); 626 if (rc2 != IBT_SUCCESS) { 627 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 628 "umad_init_driver_info: ibt_detach failed: %d\n", rc2); 629 } 630 #else 631 (void) ibt_detach(info->info_clnt_hdl); 632 #endif 633 info->info_clnt_hdl = NULL; 634 635 err1: 636 return (rc); 637 } 638 639 /* 640 * Function: 641 * umad_context_destroy 642 * Input: 643 * dip - device info 644 * info - driver info 645 * Output: 646 * None 647 * Returns: 648 * None 649 * Called by: 650 * umad_attach 651 * umad_detach 652 * Description: 653 * frees driver info resources 654 */ 655 static void 656 umad_context_destroy(dev_info_t *dip, umad_info_t *info) 657 { 658 unsigned int i; 659 unsigned int j; 660 size_t n; 661 662 for (i = 0; i < info->info_hca_count; i++) { 663 umad_hca_info_t *hca = &info->info_hcas[i]; 664 665 if (! hca->hca_guid) 666 continue; 667 668 for (j = 0; j < hca->hca_nports; j++) { 669 umad_port_info_t *port = &hca->hca_ports[j]; 670 char name[MAX_NAME_LEN]; 671 672 if (port->port_has_umad_minor_node) { 673 n = snprintf(name, sizeof (name), 674 "umad%d", port->port_minor_name); 675 #if defined(DEBUG) 676 if (n > sizeof (name)) { 677 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 678 "umad_context_destroy:" 679 " minor name \"%s\": is longer than" 680 " %d characters!\n", 681 name, MAX_NAME_LEN); 682 } 683 #endif 684 685 ddi_remove_minor_node(dip, name); 686 } 687 688 if (port->port_has_issm_minor_node) { 689 n = snprintf(name, sizeof (name), 690 "issm%d", port->port_minor_name); 691 #if defined(DEBUG) 692 if (n > sizeof (name)) { 693 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 694 "umad_context_destroy:" 695 " minor name \"%s\" is longer than" 696 " %d characters!\n", 697 name, MAX_NAME_LEN); 698 } 699 #endif 700 ddi_remove_minor_node(dip, name); 701 } 702 } 703 704 umad_release_hca_info(hca); 705 } 706 707 if (info->info_hcas) { 708 kmem_free(info->info_hcas, 709 info->info_hca_count * sizeof (umad_hca_info_t)); 710 info->info_hca_count = 0; 711 info->info_hcas = NULL; 712 } 713 714 if (info->info_clnt_hdl != NULL) { 715 (void) ibt_detach(info->info_clnt_hdl); 716 info->info_clnt_hdl = NULL; 717 } 718 719 mutex_destroy(&info->info_mutex); 720 } 721 722 /* 723 * Function: 724 * _init 725 * Input: 726 * None 727 * Output: 728 * None 729 * Returns: 730 * status 731 * Called by: 732 * Framework 733 * Description: 734 * driver initialization function 735 * inits debug tracing, river info and calls mod_install 736 */ 737 int 738 _init(void) 739 { 740 int rc; 741 742 rc = ddi_soft_state_init(&umad_statep, sizeof (umad_info_t), 0); 743 744 if (rc != 0) 745 goto err; 746 747 rc = mod_install(&modlinkage); 748 749 if (rc != 0) 750 ddi_soft_state_fini(&umad_statep); 751 752 err: 753 return (rc); 754 } 755 756 /* 757 * Function: 758 * _info 759 * Input: 760 * None 761 * Output: 762 * modinfop Module information 763 * Returns: 764 * status 765 * Called by: 766 * Framework 767 * Description: 768 * Provides module information 769 */ 770 int 771 _info(struct modinfo *modinfop) 772 { 773 int rc; 774 775 rc = mod_info(&modlinkage, modinfop); 776 777 return (rc); 778 } 779 780 /* 781 * Function: 782 * _fini 783 * Input: 784 * None 785 * Output: 786 * None 787 * Returns: 788 * status 789 * Called by: 790 * Framework 791 * Description: 792 * Cleans up upon module unloading 793 */ 794 int 795 _fini(void) 796 { 797 int rc; 798 799 if ((rc = mod_remove(&modlinkage)) == 0) 800 ddi_soft_state_fini(&umad_statep); 801 802 return (rc); 803 } 804 805 /* 806 * Function: 807 * umad_attach 808 * Input: 809 * dip device info 810 * cmd DDI_ATTACH all others are invalid 811 * Output: 812 * None 813 * Returns: 814 * DDI_SUCCESS or DDI_FAILURE 815 * Called by: 816 * Framwork 817 * Description: 818 * Device attach routine 819 */ 820 static int 821 umad_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 822 { 823 int rc; 824 unsigned int i; 825 unsigned int j; 826 umad_hca_info_t hca; 827 umad_info_t *info; 828 char name[MAX_NAME_LEN]; 829 unsigned int minor_name; 830 831 switch (cmd) { 832 case DDI_ATTACH: 833 if (ddi_soft_state_zalloc(umad_statep, UMAD_INSTANCE) 834 != DDI_SUCCESS) 835 goto err1; 836 837 info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); 838 if (info == NULL) 839 goto err2; 840 841 info->info_dip = dip; 842 mutex_init(&info->info_mutex, NULL, MUTEX_DRIVER, NULL); 843 844 /* initialize our data and per HCA info */ 845 rc = umad_init_driver_info(info); 846 847 if (rc != 0) 848 goto err3; 849 850 rc = ddi_prop_update_int(DDI_DEV_T_NONE, dip, 851 "abi_version", IB_USER_MAD_ABI_VERSION); 852 853 if (rc != 0) 854 goto err3; 855 856 /* 857 * create a minor node for each node/port pair 858 * device names are consistent with OFA 859 * conventions, e.g. umad0 for port 1 on the first HCA. 860 */ 861 minor_name = 0; 862 for (i = 0; i < info->info_hca_count; i++) { 863 hca = info->info_hcas[i]; 864 for (j = 0; j < hca.hca_nports; j++) { 865 size_t n; 866 dev_t minor_dev; 867 868 umad_port_info_t *port = &hca.hca_ports[j]; 869 870 port->port_minor_name = minor_name; 871 872 n = snprintf(name, sizeof (name), "umad%d", 873 minor_name); 874 #if defined(DEBUG) 875 if (n > sizeof (name)) { 876 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 877 "umad_attach: " 878 "name \"%s\" longer than %d!\n", 879 name, MAX_NAME_LEN); 880 } 881 #endif 882 rc = ddi_create_minor_node(dip, name, S_IFCHR, 883 GET_UMAD_MINOR(i, j), DDI_PSEUDO, 0); 884 if (rc != DDI_SUCCESS) 885 goto err3; 886 887 minor_dev = makedevice(ddi_driver_major(dip), 888 GET_UMAD_MINOR(i, j)); 889 rc = ddi_prop_update_int(minor_dev, dip, 890 "vendor-id", hca.hca_attr.hca_vendor_id); 891 if (rc != DDI_SUCCESS) 892 goto err3; 893 rc = ddi_prop_update_int(minor_dev, dip, 894 "device-id", hca.hca_attr.hca_device_id); 895 if (rc != DDI_SUCCESS) 896 goto err3; 897 rc = ddi_prop_update_int(minor_dev, dip, 898 "hca-instance", i); 899 if (rc != DDI_SUCCESS) 900 goto err3; 901 rc = ddi_prop_update_int(minor_dev, dip, 902 "port", j + 1); 903 if (rc != DDI_SUCCESS) 904 goto err3; 905 906 port->port_has_umad_minor_node = 1; 907 908 n = snprintf(name, sizeof (name), "issm%d", 909 minor_name); 910 #if defined(DEBUG) 911 if (n > sizeof (name)) { 912 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 913 "umad_attach: " 914 "name \"%s\" longer than %d!\n", 915 name, MAX_NAME_LEN); 916 } 917 #endif 918 rc = ddi_create_minor_node(dip, name, S_IFCHR, 919 GET_ISSM_MINOR(i, j), DDI_PSEUDO, 0); 920 921 if (rc != DDI_SUCCESS) 922 goto err3; 923 924 minor_dev = makedevice(ddi_driver_major(dip), 925 GET_ISSM_MINOR(i, j)); 926 rc = ddi_prop_update_int(minor_dev, dip, 927 "vendor-id", hca.hca_attr.hca_vendor_id); 928 if (rc != DDI_SUCCESS) 929 goto err3; 930 rc = ddi_prop_update_int(minor_dev, dip, 931 "device-id", hca.hca_attr.hca_device_id); 932 if (rc != DDI_SUCCESS) 933 goto err3; 934 rc = ddi_prop_update_int(minor_dev, dip, 935 "hca-instance", i); 936 if (rc != DDI_SUCCESS) 937 goto err3; 938 rc = ddi_prop_update_int(minor_dev, dip, 939 "port", j + 1); 940 if (rc != DDI_SUCCESS) 941 goto err3; 942 943 port->port_has_issm_minor_node = 1; 944 minor_name++; 945 } 946 } 947 948 ddi_report_dev(dip); 949 break; 950 951 default: 952 goto err1; 953 } 954 955 rc = DDI_SUCCESS; 956 957 return (rc); 958 959 err3: 960 umad_context_destroy(dip, info); 961 err2: 962 ddi_soft_state_free(umad_statep, UMAD_INSTANCE); 963 err1: 964 rc = DDI_FAILURE; 965 966 return (rc); 967 } 968 969 /* 970 * Function: 971 * umad_detach 972 * Input: 973 * dip Device pointer 974 * cmd DDI_DETACH all others are an error 975 * Output: 976 * None 977 * Returns: 978 * DDI_SUCCESS or DDI_FAILURE 979 * Called by: 980 * Framework 981 * Description: 982 * Used when a device is removed 983 */ 984 static int 985 umad_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 986 { 987 int rc = DDI_SUCCESS; 988 umad_info_t *info; 989 990 991 switch (cmd) { 992 case DDI_DETACH: 993 info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); 994 umad_context_destroy(dip, info); 995 ddi_soft_state_free(umad_statep, UMAD_INSTANCE); 996 break; 997 998 default: 999 rc = DDI_FAILURE; 1000 break; 1001 } 1002 1003 return (rc); 1004 } 1005 1006 /* 1007 * Function: 1008 * umad_getinfo 1009 * Input: 1010 * dip device pointer 1011 * cmd DDI_INFO_DEVT2DEVINFO or DDI_INFO_DEV2INSTANCE 1012 * arg Unused 1013 * Output: 1014 * resultp device pointer or device instance as per cmd 1015 * Returns: 1016 * status 1017 * Called by: 1018 * Framework 1019 * Description: 1020 * Gets information about specific device 1021 */ 1022 static int 1023 umad_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 1024 { 1025 int rc; 1026 1027 #if defined(__lint) 1028 extern void dummy2(void *); 1029 1030 dummy2(arg); 1031 #endif 1032 1033 switch (cmd) { 1034 case DDI_INFO_DEVT2DEVINFO: 1035 *resultp = (void *)dip; 1036 break; 1037 1038 case DDI_INFO_DEVT2INSTANCE: 1039 *resultp = (void *)UMAD_INSTANCE; 1040 rc = DDI_SUCCESS; 1041 break; 1042 1043 default: 1044 rc = DDI_FAILURE; 1045 break; 1046 } 1047 1048 return (rc); 1049 } 1050 1051 /* 1052 * Function: 1053 * umad_prop_op 1054 * Input: 1055 * dev device 1056 * dip device pointer 1057 * prop_op which property operation 1058 * flags property flags 1059 * name proper name 1060 * Output: 1061 * valuep - property value 1062 * lengthp - propery length 1063 * Returns: 1064 * status 1065 * Called by: 1066 * Framework 1067 * Description: 1068 * Passes straight through to default ddi_prop_op() 1069 */ 1070 static int 1071 umad_prop_op( 1072 dev_t dev, 1073 dev_info_t *dip, 1074 ddi_prop_op_t prop_op, 1075 int flags, 1076 char *name, 1077 caddr_t valuep, 1078 int *lengthp) 1079 { 1080 int rc; 1081 1082 rc = ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp); 1083 1084 return (rc); 1085 } 1086 1087 1088 /* Returns an array of mad classes associated with IBMF class */ 1089 static const uint8_t * 1090 umad_get_mad_classes_by_ibmf_class(enum _ibmf_client_type_t ibmf_class) 1091 { 1092 const struct ibmf_class_to_mad_type *entry; 1093 1094 for (entry = &ibmf_class_to_mad_types[0]; 1095 entry->ibmf_class != 0; 1096 ++entry) { 1097 if (ibmf_class == entry->ibmf_class) 1098 return (entry->mad_types); 1099 } 1100 return (NULL); 1101 } 1102 1103 /* Returns an agent from its ID. */ 1104 static umad_agent_t * 1105 umad_get_agent_by_id(umad_uctx_t *uctx, uint32_t agent_id) 1106 { 1107 umad_agent_t *agent; 1108 llist_head_t *entry; 1109 1110 ASSERT(MUTEX_HELD(&uctx->uctx_lock)); 1111 1112 /* Look for the agent */ 1113 list_for_each(entry, &uctx->uctx_agent_list) { 1114 agent = entry->ptr; 1115 1116 if (agent_id == agent->agent_req.id) 1117 return (agent); 1118 } 1119 1120 return (NULL); 1121 } 1122 1123 /* Returns an agent from its MAD class. */ 1124 static umad_agent_t * 1125 umad_get_agent_by_class(umad_uctx_t *uctx, uint8_t agent_class) 1126 { 1127 umad_agent_t *agent; 1128 llist_head_t *entry; 1129 1130 ASSERT(MUTEX_HELD(&uctx->uctx_lock)); 1131 1132 /* Look for the agent */ 1133 list_for_each(entry, &uctx->uctx_agent_list) { 1134 agent = entry->ptr; 1135 if (agent_class == agent->agent_req.mgmt_class) 1136 return (agent); 1137 } 1138 1139 return (NULL); 1140 } 1141 1142 /* 1143 * Register the agent with a class. 1144 * mgmt_class is given from userspace. 1145 */ 1146 static int 1147 umad_register_agent(struct umad_agent_s *agent) 1148 { 1149 uint8_t mgmt_class_num = agent->agent_req.mgmt_class; 1150 umad_port_info_t *port = agent->agent_uctx->uctx_port; 1151 const umad_hca_info_t *hca = port->port_hca; 1152 int rc; 1153 ibmf_register_info_t reg_info = {0, }; 1154 ibmf_impl_caps_t impl_caps = {0, }; 1155 uint_t flags = 0; 1156 enum _ibmf_client_type_t ibmf_class; 1157 const uint8_t *umad_types; 1158 struct ibmf_reg_info *ibmf_info; 1159 llist_head_t *entry; 1160 boolean_t found = B_FALSE; 1161 1162 ASSERT(MUTEX_HELD(&agent->agent_uctx->uctx_lock)); 1163 1164 /* 1165 * Map MAD class to IBMF class 1166 */ 1167 1168 ibmf_class = umad_type_to_ibmf_class[mgmt_class_num]; 1169 1170 /* 1171 * It is is reserved, bail 1172 */ 1173 if (ibmf_class == 0) { 1174 rc = EINVAL; 1175 goto done; 1176 } 1177 1178 /* Check to see if any other mad classes also map to this IBMF class */ 1179 umad_types = umad_get_mad_classes_by_ibmf_class(ibmf_class); 1180 if (umad_types != NULL) { 1181 struct umad_agent_s *other_agent; 1182 1183 for (; *umad_types != 0; ++umad_types) { 1184 other_agent = umad_get_agent_by_class(agent->agent_uctx, 1185 *umad_types); 1186 if (other_agent != NULL) { 1187 struct ibmf_reg_info *ibmf_reg; 1188 1189 ibmf_reg = other_agent->agent_reg; 1190 agent->agent_reg = ibmf_reg; 1191 if (other_agent->agent_flags 1192 & UMAD_HANDLING_ASYNC) { 1193 agent->agent_flags |= 1194 UMAD_HANDLING_ASYNC; 1195 } 1196 1197 mutex_enter(&ibmf_reg->ibmf_reg_lock); 1198 while (ibmf_reg->ibmf_flags 1199 & UMAD_IBMF_UNREGISTERING) { 1200 cv_wait(&ibmf_reg->ibmf_cv, 1201 &ibmf_reg->ibmf_reg_lock); 1202 } 1203 ibmf_reg->ibmf_reg_refcnt++; 1204 mutex_exit(&ibmf_reg->ibmf_reg_lock); 1205 return (0); 1206 } 1207 } 1208 } 1209 1210 /* 1211 * At this point we need to check if there is already an 1212 * ibmf_info already associated with this HCA, port and ibmf 1213 * class. If so, simply increment the reference count 1214 * and set the agent's agent_reg field to point to the 1215 * ibmf_info structure that was found. (under locking) 1216 */ 1217 mutex_enter(&port->port_lock); 1218 if (! llist_empty(&port->port_ibmf_regs)) { 1219 list_for_each(entry, &port->port_ibmf_regs) { 1220 ibmf_info = (struct ibmf_reg_info *)entry->ptr; 1221 if (ibmf_info->ibmf_class == ibmf_class) { 1222 found = B_TRUE; 1223 break; 1224 } 1225 } 1226 } 1227 mutex_exit(&port->port_lock); 1228 1229 if (found) { 1230 mutex_enter(&ibmf_info->ibmf_reg_lock); 1231 ibmf_info->ibmf_reg_refcnt++; 1232 agent->agent_reg = ibmf_info; 1233 mutex_exit(&ibmf_info->ibmf_reg_lock); 1234 1235 return (0); 1236 } 1237 1238 ibmf_info = kmem_zalloc(sizeof (struct ibmf_reg_info), KM_SLEEP); 1239 1240 mutex_init(&ibmf_info->ibmf_reg_lock, NULL, MUTEX_DRIVER, NULL); 1241 cv_init(&ibmf_info->ibmf_cv, NULL, CV_DRIVER, NULL); 1242 1243 if (agent->agent_req.rmpp_version) 1244 flags = IBMF_REG_FLAG_RMPP; 1245 1246 reg_info.ir_ci_guid = hca->hca_guid; 1247 reg_info.ir_port_num = port->port_num; 1248 reg_info.ir_client_class = ibmf_class; 1249 1250 mutex_enter(&ibmf_info->ibmf_reg_lock); 1251 rc = ibmf_register(®_info, IBMF_VERSION, flags, NULL, NULL, 1252 &ibmf_info->ibmf_reg_handle, &impl_caps); 1253 1254 if (rc != IBMF_SUCCESS) { 1255 mutex_exit(&ibmf_info->ibmf_reg_lock); 1256 kmem_free(ibmf_info, sizeof (*ibmf_info)); 1257 } else { 1258 /* The client wants to receive some unsolicited MADs. */ 1259 rc = ibmf_setup_async_cb(ibmf_info->ibmf_reg_handle, 1260 IBMF_QP_HANDLE_DEFAULT, umad_unsolicited_cb, 1261 (void *)ibmf_info, 0); 1262 1263 if (rc != IBMF_SUCCESS) { 1264 (void) ibmf_unregister(&ibmf_info->ibmf_reg_handle, 0); 1265 mutex_exit(&ibmf_info->ibmf_reg_lock); 1266 kmem_free(ibmf_info, sizeof (*ibmf_info)); 1267 } else { 1268 ibmf_info->ibmf_reg_refcnt++; 1269 ibmf_info->ibmf_reg_uctx = agent->agent_uctx; 1270 ibmf_info->ibmf_class = ibmf_class; 1271 agent->agent_reg = ibmf_info; 1272 agent->agent_flags |= UMAD_HANDLING_ASYNC; 1273 mutex_exit(&ibmf_info->ibmf_reg_lock); 1274 1275 entry = kmem_zalloc(sizeof (llist_head_t), KM_SLEEP); 1276 entry->ptr = ibmf_info; 1277 mutex_enter(&port->port_lock); 1278 llist_add(entry, &port->port_ibmf_regs); 1279 mutex_exit(&port->port_lock); 1280 } 1281 } 1282 1283 done: 1284 return (rc); 1285 } 1286 1287 /* 1288 * Function: 1289 * umad_queue_mad_msg 1290 * Input: 1291 * port - handle to ibmf 1292 * ibmf_msg - The incoming SM MAD 1293 * Output: 1294 * None 1295 * Returns: 1296 * 0 on success, otherwise error number 1297 * Called by: 1298 * umad_solicitied_cb and umad_unsolicited_cb 1299 * Description: 1300 * creates a umad_msg and adds it to the appropriate user's context 1301 */ 1302 1303 static int 1304 umad_queue_mad_msg(struct umad_agent_s *agent, ibmf_msg_t *ibmf_msg) 1305 { 1306 int rc; 1307 ib_umad_msg_t *umad_msg; 1308 umad_uctx_t *uctx = agent->agent_uctx; 1309 1310 if (agent->agent_uctx == NULL) { 1311 rc = ENOENT; 1312 goto err1; 1313 } 1314 1315 umad_msg = kmem_zalloc(sizeof (*umad_msg), KM_NOSLEEP); 1316 if (umad_msg == NULL) { 1317 rc = ENOMEM; 1318 goto err1; 1319 } 1320 1321 umad_msg->umad_msg_hdr.id = agent->agent_req.id; 1322 umad_msg->umad_msg_hdr.status = ibmf_msg->im_msg_status; 1323 umad_msg->umad_msg_hdr.length = IB_MGMT_MAD_HDR + 1324 ibmf_msg->im_msgbufs_recv.im_bufs_cl_hdr_len + 1325 ibmf_msg->im_msgbufs_recv.im_bufs_cl_data_len; 1326 1327 umad_msg->umad_msg_hdr.qpn = 1328 htonl(ibmf_msg->im_local_addr.ia_remote_qno); 1329 umad_msg->umad_msg_hdr.lid = 1330 htons(ibmf_msg->im_local_addr.ia_remote_lid); 1331 umad_msg->umad_msg_hdr.sl = 1332 htonl(ibmf_msg->im_local_addr.ia_service_level); 1333 1334 umad_msg->umad_msg_ibmf_msg = ibmf_msg; 1335 1336 mutex_enter(&uctx->uctx_recv_lock); 1337 if (! add_genlist(&uctx->uctx_recv_list, (uintptr_t)umad_msg, agent)) { 1338 kmem_free(umad_msg, sizeof (*umad_msg)); 1339 mutex_exit(&uctx->uctx_recv_lock); 1340 rc = ENOMEM; 1341 goto err1; 1342 } 1343 mutex_exit(&uctx->uctx_recv_lock); 1344 1345 cv_broadcast(&uctx->uctx_recv_cv); 1346 pollwakeup(&uctx->uctx_pollhead, POLLIN | POLLRDNORM); 1347 1348 rc = 0; 1349 1350 err1: 1351 return (rc); 1352 } 1353 1354 /* Frees up user context state */ 1355 static void 1356 umad_release_uctx(umad_uctx_t *uctx) 1357 { 1358 ASSERT(genlist_empty(&uctx->uctx_recv_list)); 1359 ASSERT(llist_empty(&uctx->uctx_agent_list)); 1360 1361 cv_destroy(&uctx->uctx_recv_cv); 1362 mutex_destroy(&uctx->uctx_lock); 1363 mutex_destroy(&uctx->uctx_recv_lock); 1364 } 1365 1366 /* 1367 * Function: 1368 * umad_open 1369 * Input: 1370 * devp device pointer 1371 * flag Unused 1372 * otyp Open type (just validated) 1373 * cred Unused 1374 * Output: 1375 * None 1376 * Returns: 1377 * status 1378 * Called by: 1379 * Device open framework 1380 * Description: 1381 * If this is the issm device, modify the port to indicate that this is 1382 * a subnet manager. If regular umad device, allocate and initialize 1383 * a new user context and connect it to the hca info. Return the new 1384 * dev_t for the new minor. 1385 */ 1386 static int 1387 umad_open(dev_t *dev, int flag, int otyp, cred_t *cred) 1388 { 1389 umad_info_t *info; 1390 minor_t minor; 1391 minor_t ctx_minor; 1392 int node_id, port_num; 1393 int rc = DDI_SUCCESS; 1394 umad_hca_info_t *hca; 1395 umad_port_info_t *port; 1396 umad_uctx_t *uctx; 1397 1398 #if defined(__lint) 1399 extern void dummy(int); 1400 1401 dummy(flag); 1402 #endif 1403 1404 rc = priv_policy(cred, PRIV_SYS_NET_CONFIG, B_FALSE, EACCES, NULL); 1405 if (rc != 0) 1406 return (rc); 1407 1408 info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); 1409 if (info == NULL) { 1410 rc = ENXIO; 1411 goto err1; 1412 } 1413 if (otyp != OTYP_CHR) 1414 return (EINVAL); 1415 1416 /* lookup the node and port #s */ 1417 minor = getminor(*dev); 1418 1419 node_id = GET_NODE(minor); 1420 port_num = GET_PORT(minor); 1421 1422 hca = &info->info_hcas[node_id]; 1423 port = &hca->hca_ports[port_num]; 1424 1425 if (ISSM_MINOR(minor)) { 1426 ibt_status_t rc; 1427 1428 mutex_enter(&port->port_lock); 1429 1430 if (port->port_issm_open_cnt) { 1431 mutex_exit(&port->port_lock); 1432 rc = EBUSY; 1433 goto err1; 1434 } 1435 1436 port->port_issm_open_cnt++; 1437 1438 mutex_exit(&port->port_lock); 1439 1440 rc = ibt_modify_port(hca->hca_handle, port->port_num, 1441 IBT_PORT_SET_SM, 0); 1442 1443 if (rc) { 1444 mutex_enter(&port->port_lock); 1445 port->port_issm_open_cnt--; 1446 mutex_exit(&port->port_lock); 1447 goto err1; 1448 } 1449 } else { 1450 unsigned int uctx_num; 1451 1452 uctx = kmem_zalloc(sizeof (umad_uctx_t), KM_SLEEP); 1453 1454 mutex_init(&uctx->uctx_lock, NULL, MUTEX_DRIVER, NULL); 1455 cv_init(&uctx->uctx_recv_cv, NULL, CV_DRIVER, NULL); 1456 init_genlist(&uctx->uctx_recv_list); 1457 mutex_init(&uctx->uctx_recv_lock, NULL, MUTEX_DRIVER, NULL); 1458 llist_head_init(&uctx->uctx_agent_list, NULL); 1459 uctx->uctx_port = port; 1460 1461 mutex_enter(&info->info_mutex); 1462 mutex_enter(&port->port_lock); 1463 1464 /* Find a free entry in uctx list */ 1465 for (uctx_num = 0; uctx_num < MAX_UCTX; uctx_num++) { 1466 if (info->info_uctx[uctx_num] == NULL) 1467 break; 1468 } 1469 1470 if (uctx_num == MAX_UCTX) { 1471 /* No room found */ 1472 mutex_exit(&port->port_lock); 1473 mutex_exit(&info->info_mutex); 1474 1475 umad_release_uctx(uctx); 1476 1477 rc = EBUSY; 1478 goto err1; 1479 } 1480 1481 ctx_minor = GET_NEW_UCTX_MINOR(minor, uctx_num); 1482 info->info_uctx[uctx_num] = uctx; 1483 *dev = makedevice(getmajor(*dev), ctx_minor); 1484 1485 mutex_exit(&port->port_lock); 1486 mutex_exit(&info->info_mutex); 1487 } 1488 err1: 1489 return (rc); 1490 } 1491 1492 /* 1493 * Function: 1494 * umad_close 1495 * Input: 1496 * dev device 1497 * flag Unused 1498 * otyp Unused 1499 * cred Unused 1500 * Output: 1501 * None 1502 * Returns: 1503 * status 1504 * Called by: 1505 * Device close framework 1506 * Description: 1507 * Unwinds open while waiting for any pending I/O to complete. 1508 */ 1509 /* ARGSUSED1 */ 1510 static int 1511 umad_close(dev_t dev, int flag, int otyp, cred_t *cred) 1512 { 1513 umad_info_t *info; 1514 minor_t minor; 1515 int rc = DDI_SUCCESS; 1516 umad_port_info_t *port; 1517 umad_uctx_t *uctx; 1518 llist_head_t *lentry; 1519 llist_head_t *lentry_temp; 1520 umad_agent_t *agent; 1521 int port_num; 1522 umad_hca_info_t *hca; 1523 int node_id; 1524 1525 info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); 1526 if (info == NULL) { 1527 rc = ENXIO; 1528 goto err1; 1529 } 1530 minor = getminor(dev); 1531 1532 node_id = GET_NODE(minor); 1533 port_num = GET_PORT(minor); 1534 1535 hca = &info->info_hcas[node_id]; 1536 port = &hca->hca_ports[port_num]; 1537 1538 ASSERT(port != NULL); 1539 1540 if (ISSM_MINOR(minor)) { 1541 (void) ibt_modify_port(hca->hca_handle, port->port_num, 1542 IBT_PORT_RESET_SM, 0); 1543 1544 mutex_enter(&port->port_lock); 1545 port->port_issm_open_cnt--; 1546 mutex_exit(&port->port_lock); 1547 1548 ASSERT(port->port_issm_open_cnt == 0); 1549 } else { 1550 1551 mutex_enter(&info->info_mutex); 1552 uctx = info->info_uctx[GET_UCTX(minor)]; 1553 ASSERT(uctx != NULL); 1554 1555 mutex_enter(&uctx->uctx_lock); 1556 1557 /* Unregister the agents. Cancel the pending operations. */ 1558 lentry = uctx->uctx_agent_list.nxt; 1559 lentry_temp = lentry->nxt; 1560 while (lentry != &uctx->uctx_agent_list) { 1561 ASSERT(lentry); 1562 agent = lentry->ptr; 1563 1564 (void) umad_unregister(&agent->agent_req, uctx); 1565 lentry = lentry_temp; 1566 lentry_temp = lentry->nxt; 1567 } 1568 1569 mutex_exit(&uctx->uctx_lock); 1570 1571 umad_release_uctx(uctx); 1572 kmem_free(uctx, sizeof (umad_uctx_t)); 1573 1574 info->info_uctx[GET_UCTX(minor)] = NULL; 1575 mutex_exit(&info->info_mutex); 1576 } 1577 1578 err1: 1579 return (rc); 1580 } 1581 1582 /* 1583 * return where optional header starts relative to the start 1584 * of the transmited mad 1585 */ 1586 static int 1587 umad_get_mad_clhdr_offset(uint8_t mgmt_class) 1588 { 1589 switch (mgmt_class) { 1590 case MAD_MGMT_CLASS_SUBN_LID_ROUTED: 1591 case MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE: 1592 case MAD_MGMT_CLASS_PERF: 1593 case MAD_MGMT_CLASS_BM: 1594 case MAD_MGMT_CLASS_DEV_MGT: 1595 case MAD_MGMT_CLASS_COMM_MGT: 1596 return (IB_MGMT_MAD_HDR); 1597 case MAD_MGMT_CLASS_SUBN_ADM: 1598 return (IB_MGMT_RMPP_HDR); 1599 case MAD_MGMT_CLASS_SNMP: 1600 return (IB_MGMT_SNMP_HDR); 1601 default: 1602 if (((mgmt_class >= MAD_MGMT_CLASS_VENDOR_START) && 1603 (mgmt_class <= MAD_MGMT_CLASS_VENDOR_END)) || 1604 ((mgmt_class >= MAD_MGMT_CLASS_APPLICATION_START) && 1605 (mgmt_class <= MAD_MGMT_CLASS_APPLICATION_END))) 1606 return (IB_MGMT_MAD_HDR); 1607 else if ((mgmt_class >= MAD_MGMT_CLASS_VENDOR2_START) && 1608 (mgmt_class <= MAD_MGMT_CLASS_VENDOR2_END)) 1609 return (IB_MGMT_RMPP_HDR); 1610 else { 1611 #if defined(DEBUG) 1612 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 1613 "umad_get_mad_clhdr_offset:" 1614 " got illegal management class %d", mgmt_class); 1615 #endif 1616 return (0); /* invalid mad */ 1617 } 1618 } 1619 } 1620 1621 /* 1622 * return the offset of the mad data in the transmited mad 1623 * following all headers 1624 */ 1625 static int 1626 umad_get_mad_data_offset(uint8_t mgmt_class) 1627 { 1628 switch (mgmt_class) { 1629 case MAD_MGMT_CLASS_SUBN_LID_ROUTED: 1630 case MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE: 1631 case MAD_MGMT_CLASS_PERF: 1632 case MAD_MGMT_CLASS_BM: 1633 case MAD_MGMT_CLASS_DEV_MGT: 1634 case MAD_MGMT_CLASS_COMM_MGT: 1635 return (IB_MGMT_MAD_HDR); 1636 case MAD_MGMT_CLASS_SUBN_ADM: 1637 return (IB_MGMT_SA_HDR); 1638 case MAD_MGMT_CLASS_SNMP: 1639 return (IB_MGMT_SNMP_DATA); 1640 default: 1641 if (((mgmt_class >= MAD_MGMT_CLASS_VENDOR_START) && 1642 (mgmt_class <= MAD_MGMT_CLASS_VENDOR_END)) || 1643 ((mgmt_class >= MAD_MGMT_CLASS_APPLICATION_START) && 1644 (mgmt_class <= MAD_MGMT_CLASS_APPLICATION_END))) 1645 return (IB_MGMT_MAD_HDR); 1646 else if ((mgmt_class >= MAD_MGMT_CLASS_VENDOR2_START) && 1647 (mgmt_class <= MAD_MGMT_CLASS_VENDOR2_END)) 1648 return (IB_MGMT_VENDOR_HDR); 1649 else { 1650 #if defined(DEBUG) 1651 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 1652 "umad_get_mad_clhdr_offset:" 1653 " got illegal management class %d", mgmt_class); 1654 #endif 1655 return (0); /* invalid mad */ 1656 } 1657 } 1658 1659 } 1660 1661 /* 1662 * Function: 1663 * umad_read 1664 * Input: 1665 * dev device 1666 * uiop User I/O pointer 1667 * credp Unused 1668 * Output: 1669 * None 1670 * Returns: 1671 * status 1672 * Called by: 1673 * Device read framework 1674 * Description: 1675 * Cannot read from ISSM device. Read from UMAD device 1676 * does usual checks for blocking and when data is present, 1677 * removes message from user context receive list, fills in user 1678 * space with message and frees kernel copy of the message. 1679 */ 1680 /* ARGSUSED2 */ 1681 static int 1682 umad_read(dev_t dev, struct uio *uiop, cred_t *credp) 1683 { 1684 int minor; 1685 size_t data_len; 1686 int rc = 0; 1687 umad_port_info_t *port; 1688 umad_info_t *info; 1689 umad_uctx_t *uctx; 1690 genlist_entry_t *entry; 1691 ib_umad_msg_t *umad_msg; 1692 ibmf_msg_t *ibmf_msg; 1693 struct umad_agent_s *agent; 1694 ib_mad_hdr_t *ib_mad_hdr; 1695 ssize_t start_resid; 1696 1697 1698 info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); 1699 if (info == NULL) { 1700 rc = ENXIO; 1701 goto err1; 1702 } 1703 1704 minor = getminor(dev); 1705 1706 if (ISSM_MINOR(minor)) { 1707 rc = ENXIO; 1708 goto err1; 1709 } 1710 1711 mutex_enter(&info->info_mutex); 1712 uctx = info->info_uctx[GET_UCTX(minor)]; 1713 mutex_exit(&info->info_mutex); 1714 ASSERT(uctx != NULL); 1715 port = uctx->uctx_port; 1716 ASSERT(port != NULL); 1717 1718 start_resid = uiop->uio_resid; 1719 while (rc == 0 && uiop->uio_resid > 0) { 1720 mutex_enter(&uctx->uctx_recv_lock); 1721 1722 /* Check to see if we are in blocking mode or not */ 1723 if (! (uiop->uio_fmode & (FNDELAY | FNONBLOCK))) { 1724 while (genlist_empty(&uctx->uctx_recv_list)) { 1725 if (cv_wait_sig(&uctx->uctx_recv_cv, 1726 &uctx->uctx_recv_lock) == 0) { 1727 mutex_exit(&uctx->uctx_recv_lock); 1728 return (EINTR); 1729 } 1730 } 1731 } else if (genlist_empty(&uctx->uctx_recv_list)) { 1732 mutex_exit(&uctx->uctx_recv_lock); 1733 /* Check for a short read */ 1734 if (uiop->uio_resid != start_resid) 1735 return (0); 1736 return (EAGAIN); 1737 } 1738 1739 entry = remove_genlist_head(&uctx->uctx_recv_list); 1740 mutex_exit(&uctx->uctx_recv_lock); 1741 1742 ASSERT(entry != NULL); 1743 agent = entry->data_context; 1744 1745 umad_msg = (ib_umad_msg_t *)entry->data; 1746 ibmf_msg = (ibmf_msg_t *)umad_msg->umad_msg_ibmf_msg; 1747 1748 data_len = min(uiop->uio_resid, sizeof (struct ib_user_mad)); 1749 rc = uiomove(umad_msg, data_len, UIO_READ, uiop); 1750 if (rc) 1751 goto err2; 1752 1753 if (ibmf_msg->im_msg_status == IBMF_SUCCESS) { 1754 ib_mad_hdr = (ib_mad_hdr_t *) 1755 ibmf_msg->im_msgbufs_recv.im_bufs_mad_hdr; 1756 data_len = 1757 umad_get_mad_clhdr_offset(ib_mad_hdr->MgmtClass); 1758 data_len = min(uiop->uio_resid, data_len); 1759 1760 rc = uiomove(ibmf_msg->im_msgbufs_recv.im_bufs_mad_hdr, 1761 data_len, UIO_READ, uiop); 1762 if (rc) 1763 goto err2; 1764 1765 data_len = min(uiop->uio_resid, 1766 ibmf_msg->im_msgbufs_recv.im_bufs_cl_hdr_len); 1767 rc = uiomove(ibmf_msg->im_msgbufs_recv.im_bufs_cl_hdr, 1768 data_len, UIO_READ, uiop); 1769 if (rc) 1770 goto err2; 1771 1772 data_len = min(uiop->uio_resid, 1773 ibmf_msg->im_msgbufs_recv.im_bufs_cl_data_len); 1774 rc = uiomove(ibmf_msg->im_msgbufs_recv.im_bufs_cl_data, 1775 data_len, UIO_READ, uiop); 1776 if (rc) 1777 goto err2; 1778 } 1779 rc = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle, 1780 &ibmf_msg); 1781 1782 kmem_free(umad_msg, sizeof (*umad_msg)); 1783 if (rc != IBMF_SUCCESS) { 1784 #if defined(DEBUG) 1785 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 1786 "umad_read:" 1787 " ibmf_free_msg failed %d", rc); 1788 #endif 1789 goto err1; 1790 } 1791 } 1792 err2: 1793 if (rc) { 1794 rc = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle, 1795 &ibmf_msg); 1796 1797 kmem_free(umad_msg, sizeof (*umad_msg)); 1798 1799 if (rc != IBMF_SUCCESS) { 1800 #if defined(DEBUG) 1801 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 1802 "umad_read:" 1803 " ibmf_free_msg failed %d", rc); 1804 #endif 1805 } 1806 1807 } 1808 err1: 1809 return (rc); 1810 } 1811 1812 /* 1813 * Function: 1814 * umad_solicited_cb 1815 * Input: 1816 * ibmf_handle - handle to ibmf 1817 * msgp - The incoming SM MAD 1818 * args - umad_port_info_t object that the MAD cam in on 1819 * Output: 1820 * None 1821 * Returns: 1822 * none 1823 * Called by: 1824 * Description: 1825 * Callback function (ibmf_msg_cb_t) that is invoked when the 1826 * ibmf receives a SM MAD for the given Port. 1827 * This function copies the MAD into the port recv queue. 1828 */ 1829 static void 1830 umad_solicited_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp, void *args) 1831 { 1832 struct umad_send *umad_ctx = (struct umad_send *)args; 1833 umad_agent_t *agent = umad_ctx->send_agent; 1834 int rc; 1835 1836 #if defined(__lint) 1837 ibmf_handle = 0; 1838 #endif 1839 msgp->im_msgbufs_send.im_bufs_mad_hdr = NULL; 1840 msgp->im_msgbufs_send.im_bufs_cl_hdr = NULL; 1841 msgp->im_msgbufs_send.im_bufs_cl_hdr_len = 0; 1842 msgp->im_msgbufs_send.im_bufs_cl_data = NULL; 1843 msgp->im_msgbufs_send.im_bufs_cl_data_len = 0; 1844 kmem_free(umad_ctx, umad_ctx->send_len); 1845 1846 mutex_enter(&agent->agent_lock); 1847 agent->agent_outstanding_msgs--; 1848 ASSERT(agent->agent_outstanding_msgs >= 0); 1849 if (agent->agent_flags & UMAD_AGENT_UNREGISTERING) { 1850 if (agent->agent_outstanding_msgs == 0) 1851 cv_signal(&agent->agent_cv); 1852 } 1853 mutex_exit(&agent->agent_lock); 1854 if (umad_queue_mad_msg(agent, msgp)) 1855 goto bad; 1856 1857 return; 1858 1859 bad: 1860 rc = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle, &msgp); 1861 ASSERT(rc == IBMF_SUCCESS); 1862 } 1863 1864 /* 1865 * Function: 1866 * umad_write 1867 * Input: 1868 * dev device 1869 * uiop User I/O pointer 1870 * credp Unused 1871 * Output: 1872 * None 1873 * Returns: 1874 * status 1875 * Called by: 1876 * Device write framework 1877 * Description: 1878 * Cannot write to ISSM device. Allocate new umad_send structure 1879 * and ibmf message and copy from user space into allocated message. 1880 * Fill in required fields. If this is a request make sure 1881 * umad_solicited_cb() is passed. 1882 */ 1883 /* ARGSUSED1 */ 1884 static int 1885 umad_write(dev_t dev, struct uio *uiop, cred_t *credp) 1886 { 1887 int rc, rc2; 1888 int mad_offset, flags = 0; 1889 int hdr_len; 1890 size_t len = uiop->uio_resid; 1891 minor_t minor; 1892 ibmf_retrans_t mad_retrans; 1893 umad_info_t *info; 1894 umad_port_info_t *port; 1895 umad_uctx_t *uctx; 1896 umad_agent_t *agent; 1897 struct ib_user_mad *user_mad; /* incoming uMAD hdr */ 1898 ibmf_msg_t *ibmf_msg; /* outbound MAD mesg */ 1899 ib_mad_hdr_t *ib_mad_hdr; /* outbound MAD hdrs */ 1900 struct umad_send *umad_ctx; 1901 boolean_t need_callback; 1902 ibt_status_t status; 1903 ib_pkey_t pkey; 1904 1905 info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); 1906 if (info == NULL) { 1907 rc = ENXIO; 1908 goto err1; 1909 } 1910 1911 /* lookup the node and port #s */ 1912 minor = getminor(dev); 1913 1914 if (ISSM_MINOR(minor)) { 1915 rc = ENXIO; 1916 goto err1; 1917 } 1918 1919 mutex_enter(&info->info_mutex); 1920 uctx = info->info_uctx[GET_UCTX(minor)]; 1921 mutex_exit(&info->info_mutex); 1922 ASSERT(uctx != NULL); 1923 port = uctx->uctx_port; 1924 ASSERT(port != NULL); 1925 1926 umad_ctx = kmem_zalloc(sizeof (struct umad_send) + len, KM_SLEEP); 1927 umad_ctx->send_len = sizeof (struct umad_send) + len; 1928 1929 /* copy the MAD data in from user space */ 1930 /* data = user_mad + mad_hdrs + class_hdrs + class data */ 1931 /* LINTED */ 1932 user_mad = (struct ib_user_mad *)umad_ctx->send_umad; 1933 rc = uiomove(user_mad, len, UIO_WRITE, uiop); 1934 if (rc != 0) 1935 goto err3; 1936 1937 1938 /* Look for the agent */ 1939 mutex_enter(&uctx->uctx_lock); 1940 agent = umad_get_agent_by_id(uctx, user_mad->hdr.id); 1941 mutex_exit(&uctx->uctx_lock); 1942 if (! agent) { 1943 rc = EINVAL; 1944 goto err3; 1945 } 1946 1947 mutex_enter(&agent->agent_lock); 1948 if (agent->agent_flags & UMAD_AGENT_UNREGISTERING) { 1949 mutex_exit(&agent->agent_lock); 1950 rc = EINVAL; 1951 goto err3; 1952 } 1953 1954 /* Allocate the msg buf for IBMF */ 1955 rc = ibmf_alloc_msg(agent->agent_reg->ibmf_reg_handle, 1956 IBMF_ALLOC_NOSLEEP, &ibmf_msg); 1957 if (rc != IBMF_SUCCESS) { 1958 mutex_exit(&agent->agent_lock); 1959 goto err3; 1960 } 1961 1962 ib_mad_hdr = (ib_mad_hdr_t *)user_mad->data; 1963 1964 hdr_len = umad_get_mad_data_offset(ib_mad_hdr->MgmtClass); 1965 1966 /* 1967 * build the IBMF msg from the mad data passed in 1968 * construct the addr info 1969 */ 1970 #if defined(__FUTURE_FEATURE__) 1971 /* TODO Proper GRH handling (non-smp traffic only) */ 1972 if (mad.addr.grh_present) { 1973 memcpy(&ibmf_msg->im_global_addr.ig_recver_gid, mad.addr.gid, 1974 16); 1975 // where can we get the GID?? 1976 im_global_addr.ig_sender_gid = get_gid(umad->addr.gid_index); 1977 ibmf_msg->im_global_addr.ig_tclass = mad.addr.traffic_class; 1978 ibmf_msg->im_global_addr.ig_hop_limit = mad.addr.hop_limit; 1979 ibmf_msg->im_global_addr.ig_flow_label = mad.addr.flow_label; 1980 } 1981 #endif 1982 1983 /* 1984 * Note: umad lid, qpn and qkey are in network order, so we need 1985 * to revert them to give them to ibmf. See userspace 1986 * umad_set_addr() and umad_set_addr_net(). 1987 */ 1988 ibmf_msg->im_local_addr.ia_local_lid = port->port_lid; 1989 ibmf_msg->im_local_addr.ia_remote_lid = ntohs(user_mad->hdr.lid); 1990 ibmf_msg->im_local_addr.ia_remote_qno = ntohl(user_mad->hdr.qpn); 1991 ibmf_msg->im_local_addr.ia_q_key = ntohl(user_mad->hdr.qkey); 1992 ibmf_msg->im_local_addr.ia_service_level = user_mad->hdr.sl; 1993 1994 status = ibt_index2pkey(port->port_hca->hca_handle, 1995 port->port_num, user_mad->hdr.pkey_index, &pkey); 1996 if (status != IBT_SUCCESS) { 1997 #if defined(DEBUG) 1998 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 1999 "umad_write: ibt_index2pkey failed %d", 2000 status); 2001 #endif 2002 } 2003 else 2004 ibmf_msg->im_local_addr.ia_p_key = ntohs(pkey); 2005 2006 if ((ib_mad_hdr->R_Method & 0x80) == 0) 2007 flags = IBMF_MSG_TRANS_FLAG_SEQ; 2008 2009 /* 2010 * This code is only correct for the cases of 2011 * no headers beyond the MAD header or the case of 2012 * MAD_MGMT_CLASS_SUBN_ADM (SA type) which has both 2013 * an RMPP header and an SA header. Other header combinations 2014 * are simply not dealt with correctly, but no applications 2015 * utilize them either, so we should be ok. 2016 */ 2017 2018 /* set use RMPP if UserAgent registered for it */ 2019 if (agent->agent_req.rmpp_version > 0) { 2020 ibmf_rmpp_hdr_t *rmpp_hdr; 2021 2022 rmpp_hdr = (ibmf_rmpp_hdr_t *)(ib_mad_hdr + 1); 2023 2024 if (rmpp_hdr->rmpp_flags != 0) 2025 flags |= IBMF_MSG_TRANS_FLAG_RMPP; 2026 } 2027 2028 /* construct the msg bufs */ 2029 ibmf_msg->im_msgbufs_send.im_bufs_mad_hdr = ib_mad_hdr; 2030 2031 hdr_len = umad_get_mad_clhdr_offset(ib_mad_hdr->MgmtClass); 2032 mad_offset = umad_get_mad_data_offset(ib_mad_hdr->MgmtClass); 2033 2034 /* Class headers and len, rmpp? */ 2035 ibmf_msg->im_msgbufs_send.im_bufs_cl_hdr = 2036 (unsigned char *)user_mad + 2037 offsetof(struct ib_user_mad, data) + hdr_len; 2038 ibmf_msg->im_msgbufs_send.im_bufs_cl_hdr_len = 2039 mad_offset - hdr_len; 2040 2041 ibmf_msg->im_msgbufs_send.im_bufs_cl_data = 2042 (unsigned char *) user_mad + (sizeof (struct ib_user_mad) + 2043 mad_offset); 2044 ibmf_msg->im_msgbufs_send.im_bufs_cl_data_len = 2045 len - sizeof (struct ib_user_mad) - mad_offset; 2046 2047 mad_retrans.retrans_retries = user_mad->hdr.retries; 2048 mad_retrans.retrans_rtv = 0; 2049 mad_retrans.retrans_rttv = 0; 2050 mad_retrans.retrans_trans_to = 0; 2051 2052 umad_ctx->send_agent = agent; 2053 2054 need_callback = (flags & IBMF_MSG_TRANS_FLAG_SEQ) != 0; 2055 2056 if (need_callback) 2057 agent->agent_outstanding_msgs++; 2058 2059 mutex_exit(&agent->agent_lock); 2060 2061 /* pass the MAD down to the IBMF layer */ 2062 rc = ibmf_msg_transport(agent->agent_reg->ibmf_reg_handle, 2063 IBMF_QP_HANDLE_DEFAULT, 2064 ibmf_msg, &mad_retrans, 2065 need_callback ? umad_solicited_cb : NULL, 2066 umad_ctx, flags); 2067 2068 if (! need_callback) { 2069 rc2 = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle, 2070 &ibmf_msg); 2071 ASSERT(rc2 == IBMF_SUCCESS); 2072 2073 if (rc != IBMF_SUCCESS) { 2074 rc = EIO; 2075 goto err3; 2076 } 2077 } else if (rc != IBMF_SUCCESS) { 2078 mutex_enter(&agent->agent_lock); 2079 agent->agent_outstanding_msgs--; 2080 ASSERT(agent->agent_outstanding_msgs >= 0); 2081 if (agent->agent_flags & UMAD_AGENT_UNREGISTERING) { 2082 if (agent->agent_outstanding_msgs == 0) 2083 cv_signal(&agent->agent_cv); 2084 } 2085 mutex_exit(&agent->agent_lock); 2086 2087 rc2 = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle, 2088 &ibmf_msg); 2089 ASSERT(rc2 == IBMF_SUCCESS); 2090 2091 rc = EIO; 2092 goto err3; 2093 } 2094 2095 return (0); 2096 2097 err3: 2098 kmem_free(umad_ctx, umad_ctx->send_len); 2099 2100 err1: 2101 return (rc); 2102 } 2103 2104 /* 2105 * Function: 2106 * umad_async_handler 2107 * Input: 2108 * private Unused 2109 * hca_hdl Unused 2110 * code Unused 2111 * event Unused 2112 * Output: 2113 * None 2114 * Returns: 2115 * None 2116 * Called by: 2117 * IBTL framework for asynchronous events. 2118 * Description: 2119 * No special event handling currently. 2120 */ 2121 /* ARGSUSED */ 2122 static void 2123 umad_async_handler( 2124 void *private, 2125 ibt_hca_hdl_t hca_hdl, 2126 ibt_async_code_t code, 2127 ibt_async_event_t *event) 2128 { 2129 } 2130 2131 /* 2132 * Need this ioctl to enable the newer interface (pkey_index and some 2133 * reserved key). Since OFED changed the abi without changing the abi 2134 * version. This resulted in wo abi interfaces (with and without the 2135 * pkey_index and some reserved bytes, but one abi version number. The 2136 * application then tries to do an ioctl() to enable the "newwer" interface 2137 * and it that ioctl succeeds, the application code assumes the newer abi 2138 * interface otherwise it assumes the older abi intrface (Uggggggg). 2139 */ 2140 static int 2141 umad_pkey_enable() 2142 { 2143 /* When we move to later releases of OFED, this will go away */ 2144 return (DDI_SUCCESS); 2145 2146 } 2147 2148 /* 2149 * Function: 2150 * umad_ioctl 2151 * Input: 2152 * dev device 2153 * cmd IB_USER_MAD_ENABLE_PKEY, IB_USER_MAD_REGISTER_AGENT or 2154 * IB_USER_MAD_UNREGISTER_AGENT 2155 * arg which agent to register or unregister 2156 * mode passed on to ddi_copyin() 2157 * credp Unused 2158 * rvalp Unused 2159 * Output: 2160 * None 2161 * Returns: 2162 * Error status 2163 * Called by: 2164 * Device ioctl framework 2165 * Description: 2166 * IB_USER_MAD_ENABLE_PKEY just allows the ioctl to succed to 2167 * indicate that we are at ABI version 5+, not really 5. 2168 * IB_USER_MAD_REGISTER_AGENT requests that a specific MAD class 2169 * for this device be handled by this process. 2170 * IB_USER_MAD_UNREGISTER_AGENT undoes the request above. 2171 */ 2172 /* ARGSUSED3 */ 2173 static int 2174 umad_ioctl( 2175 dev_t dev, 2176 int cmd, 2177 intptr_t arg, 2178 int mode, 2179 cred_t *credp, 2180 int *rvalp) 2181 { 2182 int rc = 0; 2183 int minor; 2184 umad_info_t *info; 2185 umad_port_info_t *port; 2186 umad_uctx_t *uctx; 2187 struct ib_user_mad_reg_req req = {0}; 2188 2189 info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); 2190 if (info == NULL) { 2191 rc = ENXIO; 2192 goto err1; 2193 } 2194 2195 /* lookup the node and port #s */ 2196 minor = getminor(dev); 2197 2198 if (ISSM_MINOR(minor)) { 2199 rc = ENXIO; 2200 goto err1; 2201 } 2202 2203 mutex_enter(&info->info_mutex); 2204 uctx = info->info_uctx[GET_UCTX(minor)]; 2205 mutex_exit(&info->info_mutex); 2206 ASSERT(uctx != NULL); 2207 port = uctx->uctx_port; 2208 ASSERT(port != NULL); 2209 2210 if (cmd == IB_USER_MAD_ENABLE_PKEY) 2211 return (umad_pkey_enable()); 2212 2213 if (ddi_copyin((void *) arg, &req, sizeof (req), mode) != 0) { 2214 rc = EFAULT; 2215 goto err1; 2216 } 2217 2218 switch (cmd) { 2219 case IB_USER_MAD_REGISTER_AGENT: 2220 mutex_enter(&uctx->uctx_lock); 2221 rc = umad_register(&req, uctx); 2222 mutex_exit(&uctx->uctx_lock); 2223 if (rc) 2224 goto err1; 2225 2226 /* return agent ID to user */ 2227 rc = ddi_copyout(&req, (void *) arg, sizeof (req), mode); 2228 2229 if (rc) { 2230 mutex_enter(&uctx->uctx_lock); 2231 (void) umad_unregister(&req, uctx); 2232 mutex_exit(&uctx->uctx_lock); 2233 2234 rc = EFAULT; 2235 goto err1; 2236 } 2237 break; 2238 2239 case IB_USER_MAD_UNREGISTER_AGENT: 2240 mutex_enter(&uctx->uctx_lock); 2241 rc = umad_unregister(&req, uctx); 2242 mutex_exit(&uctx->uctx_lock); 2243 break; 2244 2245 default: 2246 rc = DDI_FAILURE; 2247 } 2248 2249 2250 err1: 2251 return (rc); 2252 } 2253 2254 /* 2255 * Get a new unique agent ID. The agent list is already locked. The 2256 * complexity is not ideal, but the number of agents should be small 2257 * (ie 2 or 3) so it shouldn't matter. 2258 */ 2259 static int 2260 umad_get_new_agent_id(umad_uctx_t *uctx) 2261 { 2262 boolean_t found; 2263 unsigned int agent_id; 2264 llist_head_t *entry; 2265 2266 agent_id = 0; 2267 2268 ASSERT(MUTEX_HELD(&uctx->uctx_lock)); 2269 2270 for (;;) { 2271 found = B_FALSE; 2272 list_for_each(entry, &uctx->uctx_agent_list) { 2273 umad_agent_t *agent = entry->ptr; 2274 2275 if (agent_id == agent->agent_req.id) { 2276 found = B_TRUE; 2277 break; 2278 } 2279 } 2280 2281 if (! found) 2282 break; 2283 2284 agent_id++; 2285 } 2286 2287 return (agent_id); 2288 } 2289 2290 /* 2291 * Function: 2292 * umad_register 2293 * Input: 2294 * req User registration request 2295 * uctx User context 2296 * Output: 2297 * None 2298 * Returns: 2299 * status 2300 * Called by: 2301 * umad_ioctl 2302 * Description: 2303 * Handles the registration of user agents from userspace. 2304 * Each call will result in the creation of a new agent object for 2305 * the given HCA/port. If UMAD_CA_MAX_AGENTS has been reached then an 2306 * error is raised. 2307 */ 2308 static int 2309 umad_register(struct ib_user_mad_reg_req *req, umad_uctx_t *uctx) 2310 { 2311 int rc = IBMF_SUCCESS; 2312 umad_agent_t *agent = NULL; 2313 umad_port_info_t *port; 2314 2315 /* check for valid QP */ 2316 if ((req->qpn != 0) && (req->qpn != 1)) { 2317 rc = EINVAL; 2318 goto err1; 2319 } 2320 2321 2322 ASSERT(MUTEX_HELD(&uctx->uctx_lock)); 2323 2324 port = uctx->uctx_port; 2325 ASSERT(port != NULL); 2326 2327 agent = umad_get_agent_by_class(uctx, req->mgmt_class); 2328 if (agent != NULL) 2329 return (IBMF_PORT_IN_USE); 2330 2331 agent = kmem_zalloc(sizeof (umad_agent_t), KM_SLEEP); 2332 mutex_init(&agent->agent_lock, NULL, MUTEX_DRIVER, NULL); 2333 cv_init(&agent->agent_cv, NULL, CV_DRIVER, NULL); 2334 2335 agent->agent_req = *req; 2336 agent->agent_uctx = uctx; 2337 2338 llist_head_init(&agent->agent_list, agent); 2339 2340 agent->agent_req.id = req->id = umad_get_new_agent_id(uctx); 2341 2342 rc = umad_register_agent(agent); 2343 if (rc) 2344 goto err1; 2345 2346 llist_add(&agent->agent_list, &uctx->uctx_agent_list); 2347 2348 return (0); 2349 2350 err1: 2351 if (rc) { 2352 if (agent) { 2353 cv_destroy(&agent->agent_cv); 2354 mutex_destroy(&agent->agent_lock); 2355 kmem_free(agent, sizeof (umad_agent_t)); 2356 } 2357 } 2358 2359 return (rc); 2360 } 2361 2362 /* 2363 * Function: 2364 * umad_unregister 2365 * Input: 2366 * req - user unregister request 2367 * info - user context 2368 * Output: 2369 * None 2370 * Returns: 2371 * Status 2372 * Called by: 2373 * umad_ioct 2374 * Description: 2375 * Undoes registration. Waits for pending operations before completing. 2376 */ 2377 static int 2378 umad_unregister(struct ib_user_mad_reg_req *req, umad_uctx_t *uctx) 2379 { 2380 int agent_id = req->id; 2381 umad_agent_t *agent; 2382 int rc; 2383 genlist_entry_t *entry; 2384 struct ibmf_reg_info *ibmf_info; 2385 boolean_t did_ibmf_unregister; 2386 umad_port_info_t *port; 2387 2388 ASSERT(MUTEX_HELD(&uctx->uctx_lock)); 2389 2390 agent = umad_get_agent_by_id(uctx, agent_id); 2391 if (agent == NULL) { 2392 rc = EINVAL; 2393 goto done; 2394 } 2395 2396 mutex_enter(&agent->agent_lock); 2397 while (agent->agent_outstanding_msgs != 0) { 2398 agent->agent_flags |= UMAD_AGENT_UNREGISTERING; 2399 cv_wait(&agent->agent_cv, &agent->agent_lock); 2400 } 2401 if (agent->agent_flags & UMAD_HANDLING_ASYNC) 2402 agent->agent_reg->ibmf_reg_uctx = NULL; 2403 2404 mutex_exit(&agent->agent_lock); 2405 2406 /* Remove agent from the uctx list. */ 2407 llist_del(&agent->agent_list); 2408 2409 /* Get the IBMF registration information */ 2410 ibmf_info = agent->agent_reg; 2411 2412 mutex_enter(&ibmf_info->ibmf_reg_lock); 2413 2414 /* Remove the pending received MADs. */ 2415 mutex_enter(&uctx->uctx_recv_lock); 2416 while ((entry = remove_genlist_head(&uctx->uctx_recv_list))) { 2417 ib_umad_msg_t *msg; 2418 ibmf_msg_t *ibmf_msg; 2419 2420 mutex_exit(&uctx->uctx_recv_lock); 2421 2422 msg = (ib_umad_msg_t *)entry->data; 2423 ibmf_msg = msg->umad_msg_ibmf_msg; 2424 2425 rc = ibmf_free_msg(ibmf_info->ibmf_reg_handle, &ibmf_msg); 2426 ASSERT(rc == IBMF_SUCCESS); 2427 2428 kmem_free(msg, sizeof (*msg)); 2429 2430 mutex_enter(&uctx->uctx_recv_lock); 2431 } 2432 mutex_exit(&uctx->uctx_recv_lock); 2433 2434 /* If no more references, tear down the ibmf registration */ 2435 if (--ibmf_info->ibmf_reg_refcnt == 0) { 2436 ibmf_info->ibmf_flags |= UMAD_IBMF_UNREGISTERING; 2437 mutex_exit(&ibmf_info->ibmf_reg_lock); 2438 /* Remove the callback */ 2439 rc = ibmf_tear_down_async_cb(ibmf_info->ibmf_reg_handle, 2440 IBMF_QP_HANDLE_DEFAULT, 0); 2441 #if defined(DEBUG) 2442 if (rc) { 2443 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 2444 "umad_unregister: failed " 2445 "ibmf_tear_down_async_cb() error %d\n", rc); 2446 } 2447 #endif 2448 2449 /* Remove the pending received MADs. */ 2450 mutex_enter(&uctx->uctx_recv_lock); 2451 while ((entry = remove_genlist_head(&uctx->uctx_recv_list))) { 2452 ib_umad_msg_t *msg; 2453 ibmf_msg_t *ibmf_msg; 2454 2455 mutex_exit(&uctx->uctx_recv_lock); 2456 2457 msg = (ib_umad_msg_t *)entry->data; 2458 ibmf_msg = msg->umad_msg_ibmf_msg; 2459 2460 rc = ibmf_free_msg(ibmf_info->ibmf_reg_handle, 2461 &ibmf_msg); 2462 ASSERT(rc == IBMF_SUCCESS); 2463 2464 kmem_free(msg, sizeof (*msg)); 2465 2466 mutex_enter(&uctx->uctx_recv_lock); 2467 } 2468 mutex_exit(&uctx->uctx_recv_lock); 2469 2470 2471 /* unregister from IBMF */ 2472 rc = ibmf_unregister(&ibmf_info->ibmf_reg_handle, 0); 2473 #if defined(DEBUG) 2474 if (rc) { 2475 SOL_OFS_DPRINTF_L5(sol_umad_dbg_str, 2476 "umad_unregister: failed " 2477 "ibmf_unregister() error %d\n", rc); 2478 } 2479 #endif 2480 mutex_enter(&ibmf_info->ibmf_reg_lock); 2481 ibmf_info->ibmf_flags &= ~UMAD_IBMF_UNREGISTERING; 2482 cv_signal(&ibmf_info->ibmf_cv); 2483 mutex_exit(&ibmf_info->ibmf_reg_lock); 2484 did_ibmf_unregister = B_TRUE; 2485 } else { 2486 mutex_exit(&ibmf_info->ibmf_reg_lock); 2487 did_ibmf_unregister = B_FALSE; 2488 } 2489 2490 if (did_ibmf_unregister) { 2491 llist_head_t *entry; 2492 struct ibmf_reg_info *ibmf_entry = NULL; 2493 #if defined(DEBUG) 2494 boolean_t found = B_FALSE; 2495 #endif 2496 2497 port = uctx->uctx_port; 2498 mutex_enter(&port->port_lock); 2499 list_for_each(entry, &port->port_ibmf_regs) { 2500 ibmf_entry = entry->ptr; 2501 2502 if (ibmf_info == ibmf_entry) { 2503 #if defined(DEBUG) 2504 found = B_TRUE; 2505 #endif 2506 break; 2507 } 2508 } 2509 ASSERT(found); 2510 llist_del(entry); 2511 kmem_free(entry, sizeof (*entry)); 2512 2513 mutex_exit(&port->port_lock); 2514 /* Release the registration memory */ 2515 kmem_free(ibmf_info, sizeof (*ibmf_info)); 2516 } 2517 agent->agent_uctx = NULL; 2518 cv_destroy(&agent->agent_cv); 2519 mutex_destroy(&agent->agent_lock); 2520 kmem_free(agent, sizeof (*agent)); 2521 2522 rc = 0; 2523 2524 done: 2525 return (rc); 2526 } 2527 2528 2529 /* 2530 * Function: 2531 * umad_poll 2532 * Input: 2533 * dev device 2534 * events which events 2535 * anyyet any events yet? 2536 * Output: 2537 * reventsp return of which events 2538 * phpp poll head pointer 2539 * Returns: 2540 * return 0 for success, or the appropriate error number 2541 * Called by: 2542 * Device poll framework 2543 * Description: 2544 * Fails for ISSM device. POLLOUT is always true. POLLIN or POLLRDNORM 2545 * is true if a message has been queued for the user context receive list. 2546 */ 2547 static int 2548 umad_poll( 2549 dev_t dev, 2550 short events, 2551 int anyyet, 2552 short *reventsp, 2553 struct pollhead **phpp) 2554 { 2555 int rc = 0; 2556 int minor; 2557 umad_uctx_t *uctx; 2558 umad_port_info_t *port; 2559 umad_info_t *info; 2560 short revent = 0; 2561 2562 info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE); 2563 if (info == NULL) { 2564 rc = ENXIO; 2565 goto err1; 2566 } 2567 2568 /* lookup the node and port #s */ 2569 minor = getminor(dev); 2570 2571 if (ISSM_MINOR(minor)) { 2572 rc = ENXIO; 2573 goto err1; 2574 } 2575 2576 mutex_enter(&info->info_mutex); 2577 uctx = info->info_uctx[GET_UCTX(minor)]; 2578 mutex_exit(&info->info_mutex); 2579 ASSERT(uctx != NULL); 2580 port = uctx->uctx_port; 2581 ASSERT(port != NULL); 2582 2583 /* 2584 * Always signal ready for POLLOUT / POLLWRNORM. 2585 * Signal for POLLIN / POLLRDNORM whenever there is something in 2586 * the receive list. 2587 */ 2588 if (events & POLLOUT) { 2589 revent = POLLOUT; 2590 } else if (events & (POLLIN | POLLRDNORM)) { 2591 mutex_enter(&uctx->uctx_recv_lock); 2592 if (! genlist_empty(&uctx->uctx_recv_list)) { 2593 revent |= POLLIN | POLLRDNORM; 2594 } 2595 mutex_exit(&uctx->uctx_recv_lock); 2596 } 2597 2598 if (revent == 0) { 2599 if (! anyyet) 2600 *phpp = &uctx->uctx_pollhead; 2601 } 2602 2603 *reventsp = revent; 2604 err1: 2605 2606 return (rc); 2607 } 2608 2609 /* 2610 * Function: 2611 * umad_unsolicited_cb 2612 * Input: 2613 * ibmf_handle - handle to ibmf 2614 * msgp - The incoming SM MAD 2615 * args - umad_port_info_t object that the MAD came in on 2616 * Output: 2617 * None 2618 * Returns: 2619 * none 2620 * Called by: 2621 * IBMF from below 2622 * Description: 2623 * Callback function (ibmf_msg_cb_t) that is invoked when the 2624 * ibmf receives a response MAD and passes it up if requested. 2625 * The message is tossed if no one wants it or queued if requested. 2626 */ 2627 static void 2628 umad_unsolicited_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp, void *args) 2629 { 2630 struct ibmf_reg_info *ibmf_info = (struct ibmf_reg_info *)args; 2631 struct umad_agent_s *agent; 2632 ib_mad_hdr_t *mad_hdr; 2633 int rc; 2634 2635 #if defined(__lint) 2636 ibmf_handle = 0; 2637 #endif 2638 2639 ASSERT(msgp->im_msgbufs_send.im_bufs_mad_hdr == NULL); 2640 ASSERT(msgp->im_msgbufs_send.im_bufs_cl_data == NULL); 2641 ASSERT(msgp->im_msgbufs_send.im_bufs_cl_data_len == 0); 2642 2643 /* Apply the filters to this MAD. */ 2644 mad_hdr = msgp->im_msgbufs_recv.im_bufs_mad_hdr; 2645 2646 mutex_enter(&ibmf_info->ibmf_reg_lock); 2647 2648 /* 2649 * Make sure the user context that was receiving the unsolicited 2650 * messages is still present. 2651 */ 2652 if (ibmf_info->ibmf_reg_uctx == NULL) 2653 goto reject; 2654 2655 mutex_enter(&ibmf_info->ibmf_reg_uctx->uctx_lock); 2656 agent = umad_get_agent_by_class(ibmf_info->ibmf_reg_uctx, 2657 mad_hdr->MgmtClass); 2658 mutex_exit(&ibmf_info->ibmf_reg_uctx->uctx_lock); 2659 if (agent == NULL) 2660 goto reject; 2661 2662 if (mad_hdr->ClassVersion != agent->agent_req.mgmt_class_version) 2663 goto reject; 2664 2665 if (! is_supported_mad_method(mad_hdr->R_Method & MAD_METHOD_MASK, 2666 agent->agent_req.method_mask)) 2667 goto reject; 2668 2669 if (umad_queue_mad_msg(agent, msgp)) 2670 goto reject; 2671 2672 mutex_exit(&ibmf_info->ibmf_reg_lock); 2673 return; 2674 2675 reject: 2676 rc = ibmf_free_msg(ibmf_info->ibmf_reg_handle, &msgp); 2677 ASSERT(rc == IBMF_SUCCESS); 2678 2679 mutex_exit(&ibmf_info->ibmf_reg_lock); 2680 } 2681 2682 #if defined(__lint) 2683 /* 2684 * This is needed because rdma/ib_verbs.h and sol_ofs/sol_ofs_common.h 2685 * both implement static functions. Not all of those functions are 2686 * used by sol_umad, but lint doesn't like seeing static function that 2687 * are defined but not used. 2688 */ 2689 void 2690 lint_function(llist_head_t *a, llist_head_t *b) 2691 { 2692 (void) llist_is_last(a, b); 2693 llist_add_tail(a, b); 2694 (void) ib_width_enum_to_int(IB_WIDTH_1X); 2695 } 2696 #endif 2697