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