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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Copyright 2019, Joyent, Inc. 28 */ 29 30 #include <sys/types.h> 31 #include <inet/common.h> 32 #include <sys/stropts.h> 33 #include <sys/modctl.h> 34 #include <sys/dld.h> 35 #include <sys/softmac_impl.h> 36 37 dev_info_t *softmac_dip = NULL; 38 static kmem_cache_t *softmac_upper_cachep; 39 40 /* 41 * This function is a generic open(9E) entry point into the softmac for 42 * both the softmac module and the softmac driver. 43 */ 44 static int softmac_cmn_open(queue_t *, dev_t *, int, int, cred_t *); 45 46 /* 47 * The following softmac_mod_xxx() functions are (9E) entry point functions for 48 * the softmac module. 49 */ 50 static int softmac_mod_close(queue_t *, int, cred_t *); 51 static int softmac_mod_rput(queue_t *, mblk_t *); 52 static int softmac_mod_wput(queue_t *, mblk_t *); 53 static int softmac_mod_wsrv(queue_t *); 54 55 /* 56 * The following softmac_drv_xxx() functions are (9E) entry point functions for 57 * the softmac driver. 58 */ 59 static int softmac_drv_open(queue_t *, dev_t *, int, int, cred_t *); 60 static int softmac_drv_close(queue_t *, int, cred_t *); 61 static int softmac_drv_wput(queue_t *, mblk_t *); 62 static int softmac_drv_wsrv(queue_t *); 63 64 static int softmac_attach(dev_info_t *, ddi_attach_cmd_t); 65 static int softmac_detach(dev_info_t *, ddi_detach_cmd_t); 66 static int softmac_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 67 68 static struct module_info softmac_modinfo = { 69 0, 70 SOFTMAC_DEV_NAME, 71 0, 72 INFPSZ, 73 65536, 74 1024 75 }; 76 77 /* 78 * hi-water mark is 1 because of the flow control mechanism implemented in 79 * dld. Refer to the comments in dld_str.c for details. 80 */ 81 static struct module_info softmac_dld_modinfo = { 82 0, 83 SOFTMAC_DEV_NAME, 84 0, 85 INFPSZ, 86 1, 87 0 88 }; 89 90 static struct qinit softmac_urinit = { 91 softmac_mod_rput, /* qi_putp */ 92 NULL, /* qi_srvp */ 93 softmac_cmn_open, /* qi_qopen */ 94 softmac_mod_close, /* qi_qclose */ 95 NULL, /* qi_qadmin */ 96 &softmac_modinfo /* qi_minfo */ 97 }; 98 99 static struct qinit softmac_uwinit = { 100 softmac_mod_wput, /* qi_putp */ 101 softmac_mod_wsrv, /* qi_srvp */ 102 NULL, /* qi_qopen */ 103 NULL, /* qi_qclose */ 104 NULL, /* qi_qadmin */ 105 &softmac_modinfo /* qi_minfo */ 106 }; 107 108 static struct streamtab softmac_tab = { 109 &softmac_urinit, /* st_rdinit */ 110 &softmac_uwinit /* st_wrinit */ 111 }; 112 113 DDI_DEFINE_STREAM_OPS(softmac_ops, nulldev, nulldev, softmac_attach, 114 softmac_detach, nodev, softmac_info, D_MP, &softmac_tab, 115 ddi_quiesce_not_supported); 116 117 static struct qinit softmac_dld_r_qinit = { 118 NULL, NULL, softmac_drv_open, softmac_drv_close, NULL, 119 &softmac_dld_modinfo 120 }; 121 122 static struct qinit softmac_dld_w_qinit = { 123 softmac_drv_wput, softmac_drv_wsrv, NULL, NULL, NULL, 124 &softmac_dld_modinfo 125 }; 126 127 static struct fmodsw softmac_fmodsw = { 128 SOFTMAC_DEV_NAME, 129 &softmac_tab, 130 D_MP 131 }; 132 133 static struct modldrv softmac_modldrv = { 134 &mod_driverops, 135 "softmac driver", 136 &softmac_ops 137 }; 138 139 static struct modlstrmod softmac_modlstrmod = { 140 &mod_strmodops, 141 "softmac module", 142 &softmac_fmodsw 143 }; 144 145 static struct modlinkage softmac_modlinkage = { 146 MODREV_1, 147 &softmac_modlstrmod, 148 &softmac_modldrv, 149 NULL 150 }; 151 152 static void softmac_dedicated_rx(void *, mac_resource_handle_t, mblk_t *, 153 mac_header_info_t *); 154 155 /*ARGSUSED*/ 156 static int 157 softmac_upper_constructor(void *buf, void *arg, int kmflag) 158 { 159 softmac_upper_t *sup = buf; 160 161 bzero(buf, sizeof (softmac_upper_t)); 162 163 mutex_init(&sup->su_mutex, NULL, MUTEX_DEFAULT, NULL); 164 cv_init(&sup->su_cv, NULL, CV_DEFAULT, NULL); 165 mutex_init(&sup->su_disp_mutex, NULL, MUTEX_DEFAULT, NULL); 166 cv_init(&sup->su_disp_cv, NULL, CV_DEFAULT, NULL); 167 list_create(&sup->su_req_list, sizeof (softmac_switch_req_t), 168 offsetof(softmac_switch_req_t, ssq_req_list_node)); 169 return (0); 170 } 171 172 /*ARGSUSED*/ 173 static void 174 softmac_upper_destructor(void *buf, void *arg) 175 { 176 softmac_upper_t *sup = buf; 177 178 ASSERT(sup->su_slp == NULL); 179 ASSERT(sup->su_pending_head == NULL && sup->su_pending_tail == NULL); 180 ASSERT(!sup->su_dlpi_pending); 181 ASSERT(!sup->su_active); 182 ASSERT(!sup->su_closing); 183 ASSERT(sup->su_tx_flow_mp == NULL); 184 ASSERT(sup->su_tx_inprocess == 0); 185 ASSERT(sup->su_mode == SOFTMAC_UNKNOWN); 186 ASSERT(!sup->su_tx_busy); 187 ASSERT(!sup->su_bound); 188 ASSERT(!sup->su_taskq_scheduled); 189 ASSERT(sup->su_tx_notify_func == NULL); 190 ASSERT(sup->su_tx_notify_arg == NULL); 191 ASSERT(list_is_empty(&sup->su_req_list)); 192 193 list_destroy(&sup->su_req_list); 194 mutex_destroy(&sup->su_mutex); 195 cv_destroy(&sup->su_cv); 196 mutex_destroy(&sup->su_disp_mutex); 197 cv_destroy(&sup->su_disp_cv); 198 } 199 200 int 201 _init(void) 202 { 203 int err; 204 205 mac_init_ops(NULL, SOFTMAC_DEV_NAME); 206 softmac_init(); 207 208 softmac_upper_cachep = kmem_cache_create("softmac_upper_cache", 209 sizeof (softmac_upper_t), 0, softmac_upper_constructor, 210 softmac_upper_destructor, NULL, NULL, NULL, 0); 211 ASSERT(softmac_upper_cachep != NULL); 212 213 if ((err = mod_install(&softmac_modlinkage)) != 0) { 214 softmac_fini(); 215 return (err); 216 } 217 218 return (0); 219 } 220 221 int 222 _fini(void) 223 { 224 int err; 225 226 if (softmac_busy()) 227 return (EBUSY); 228 229 if ((err = mod_remove(&softmac_modlinkage)) != 0) 230 return (err); 231 232 kmem_cache_destroy(softmac_upper_cachep); 233 softmac_fini(); 234 235 return (0); 236 } 237 238 int 239 _info(struct modinfo *modinfop) 240 { 241 return (mod_info(&softmac_modlinkage, modinfop)); 242 } 243 244 static int 245 softmac_cmn_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp) 246 { 247 softmac_lower_t *slp; 248 /* 249 * This is a self-cloning driver so that each queue should only 250 * get opened once. 251 */ 252 if (rq->q_ptr != NULL) 253 return (EBUSY); 254 255 if (sflag == MODOPEN) { 256 /* 257 * This is the softmac module pushed over an underlying 258 * legacy device. Initialize the lower structure. 259 */ 260 if ((slp = kmem_zalloc(sizeof (*slp), KM_NOSLEEP)) == NULL) 261 return (ENOMEM); 262 263 slp->sl_wq = WR(rq); 264 cv_init(&slp->sl_cv, NULL, CV_DRIVER, NULL); 265 mutex_init(&slp->sl_mutex, NULL, MUTEX_DRIVER, NULL); 266 slp->sl_pending_prim = DL_PRIM_INVAL; 267 rq->q_ptr = WR(rq)->q_ptr = slp; 268 qprocson(rq); 269 return (0); 270 } 271 272 /* 273 * Regular device open of a softmac DLPI node. We modify 274 * the queues' q_qinfo pointer such that all future STREAMS 275 * operations will go through another set of entry points 276 */ 277 rq->q_qinfo = &softmac_dld_r_qinit; 278 WR(rq)->q_qinfo = &softmac_dld_w_qinit; 279 return (softmac_drv_open(rq, devp, flag, sflag, credp)); 280 } 281 282 /* ARGSUSED */ 283 static int 284 softmac_mod_close(queue_t *rq, int flags __unused, cred_t *credp __unused) 285 { 286 softmac_lower_t *slp = rq->q_ptr; 287 288 /* 289 * Call the appropriate delete routine depending on whether this is 290 * a module or device. 291 */ 292 ASSERT(WR(rq)->q_next != NULL); 293 294 qprocsoff(rq); 295 296 slp->sl_softmac = NULL; 297 slp->sl_lh = NULL; 298 299 ASSERT(slp->sl_ack_mp == NULL); 300 ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL); 301 ASSERT(slp->sl_pending_ioctl == B_FALSE); 302 303 cv_destroy(&slp->sl_cv); 304 mutex_destroy(&slp->sl_mutex); 305 306 kmem_free(slp, sizeof (*slp)); 307 return (0); 308 } 309 310 static int 311 softmac_mod_rput(queue_t *rq, mblk_t *mp) 312 { 313 softmac_lower_t *slp = rq->q_ptr; 314 softmac_lower_rxinfo_t *rxinfo; 315 union DL_primitives *dlp; 316 317 /* 318 * This is the softmac module. 319 */ 320 ASSERT(WR(rq)->q_next != NULL); 321 ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL)); 322 323 switch (DB_TYPE(mp)) { 324 case M_DATA: { 325 326 /* 327 * If sl_rxinfo is non-NULL. This is dedicated-lower-stream 328 * created for fastpath. Directly call the rx callback. 329 */ 330 if ((rxinfo = slp->sl_rxinfo) != NULL) { 331 rxinfo->slr_rx(rxinfo->slr_arg, NULL, mp, NULL); 332 break; 333 } 334 335 /* 336 * A shared-lower-stream. Some driver starts to send up 337 * packets even it not in the DL_IDLE state, where 338 * sl_softmac is not set yet. Drop the packet in this case. 339 */ 340 if (slp->sl_softmac == NULL) { 341 freemsg(mp); 342 return (0); 343 } 344 345 /* 346 * If this message is looped back from the legacy devices, 347 * drop it as the Nemo framework will be responsible for 348 * looping it back by the mac_txloop() function. 349 */ 350 if (mp->b_flag & MSGNOLOOP) { 351 freemsg(mp); 352 return (0); 353 } 354 355 /* 356 * This is the most common case. 357 */ 358 if (DB_REF(mp) == 1) { 359 ASSERT(slp->sl_softmac != NULL); 360 mac_rx(slp->sl_softmac->smac_mh, NULL, mp); 361 return (0); 362 } else { 363 softmac_rput_process_data(slp, mp); 364 } 365 break; 366 } 367 case M_PROTO: 368 case M_PCPROTO: 369 if (MBLKL(mp) < sizeof (dlp->dl_primitive)) { 370 freemsg(mp); 371 break; 372 } 373 dlp = (union DL_primitives *)mp->b_rptr; 374 if (dlp->dl_primitive == DL_UNITDATA_IND) { 375 376 if ((rxinfo = slp->sl_rxinfo) != NULL) { 377 softmac_dedicated_rx(slp->sl_sup, NULL, mp, 378 NULL); 379 break; 380 } 381 382 cmn_err(CE_WARN, "got unexpected %s message", 383 dl_primstr(DL_UNITDATA_IND)); 384 freemsg(mp); 385 break; 386 } 387 /*FALLTHROUGH*/ 388 default: 389 softmac_rput_process_notdata(rq, slp->sl_sup, mp); 390 break; 391 } 392 return (0); 393 } 394 395 static int 396 softmac_mod_wput(queue_t *wq, mblk_t *mp) 397 { 398 /* 399 * This is the softmac module 400 */ 401 ASSERT(wq->q_next != NULL); 402 403 switch (DB_TYPE(mp)) { 404 case M_IOCTL: { 405 struct iocblk *ioc = (struct iocblk *)mp->b_rptr; 406 407 switch (ioc->ioc_cmd) { 408 case SMAC_IOC_START: { 409 softmac_lower_t *slp = wq->q_ptr; 410 smac_ioc_start_t *arg; 411 412 if (ioc->ioc_count != sizeof (*arg)) { 413 miocnak(wq, mp, 0, EINVAL); 414 break; 415 } 416 417 /* 418 * Assign the devname and perstream handle of the 419 * specific lower stream and return it as a part 420 * of the ioctl. 421 */ 422 arg = (smac_ioc_start_t *)mp->b_cont->b_rptr; 423 arg->si_slp = slp; 424 miocack(wq, mp, sizeof (*arg), 0); 425 break; 426 } 427 default: 428 miocnak(wq, mp, 0, EINVAL); 429 break; 430 } 431 break; 432 } 433 default: 434 freemsg(mp); 435 break; 436 } 437 return (0); 438 } 439 440 static int 441 softmac_mod_wsrv(queue_t *wq) 442 { 443 softmac_lower_t *slp = wq->q_ptr; 444 445 /* 446 * This is the softmac module 447 */ 448 ASSERT(wq->q_next != NULL); 449 450 /* 451 * Inform that the tx resource is available; mac_tx_update() will 452 * inform all the upper streams sharing this lower stream. 453 */ 454 if (slp->sl_sup != NULL) 455 qenable(slp->sl_sup->su_wq); 456 else if (slp->sl_softmac != NULL) 457 mac_tx_update(slp->sl_softmac->smac_mh); 458 return (0); 459 } 460 461 static int 462 softmac_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 463 { 464 ASSERT(ddi_get_instance(dip) == 0); 465 466 if (cmd != DDI_ATTACH) 467 return (DDI_FAILURE); 468 469 softmac_dip = dip; 470 471 return (DDI_SUCCESS); 472 } 473 474 /* ARGSUSED */ 475 static int 476 softmac_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 477 { 478 if (cmd != DDI_DETACH) 479 return (DDI_FAILURE); 480 481 softmac_dip = NULL; 482 return (DDI_SUCCESS); 483 } 484 485 /* ARGSUSED */ 486 static int 487 softmac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 488 { 489 switch (infocmd) { 490 case DDI_INFO_DEVT2DEVINFO: 491 if (softmac_dip != NULL) { 492 *result = softmac_dip; 493 return (DDI_SUCCESS); 494 } 495 break; 496 497 case DDI_INFO_DEVT2INSTANCE: 498 *result = NULL; 499 return (DDI_SUCCESS); 500 501 } 502 503 return (DDI_FAILURE); 504 } 505 506 /*ARGSUSED*/ 507 static void 508 softmac_dedicated_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp, 509 mac_header_info_t *mhip) 510 { 511 queue_t *rq = ((softmac_upper_t *)arg)->su_rq; 512 513 if (canputnext(rq)) 514 putnext(rq, mp); 515 else 516 freemsg(mp); 517 } 518 519 /*ARGSUSED*/ 520 static int 521 softmac_drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp) 522 { 523 softmac_upper_t *sup = NULL; 524 softmac_t *softmac; 525 int err = 0; 526 527 /* 528 * This is a softmac device created for a legacy device, find the 529 * associated softmac and initialize the softmac_upper_t structure. 530 */ 531 if ((err = softmac_hold(*devp, &softmac)) != 0) 532 return (err); 533 534 sup = kmem_cache_alloc(softmac_upper_cachep, KM_NOSLEEP); 535 if (sup == NULL) { 536 err = ENOMEM; 537 goto fail; 538 } 539 540 ASSERT(list_is_empty(&sup->su_req_list)); 541 542 if ((sup->su_tx_flow_mp = allocb(1, BPRI_HI)) == NULL) { 543 err = ENOMEM; 544 goto fail; 545 } 546 547 sup->su_rq = rq; 548 sup->su_wq = WR(rq); 549 sup->su_softmac = softmac; 550 sup->su_mode = SOFTMAC_UNKNOWN; 551 552 sup->su_rxinfo.slr_arg = sup; 553 sup->su_rxinfo.slr_rx = softmac_dedicated_rx; 554 sup->su_direct_rxinfo.slr_arg = sup; 555 sup->su_direct_rxinfo.slr_rx = softmac_dedicated_rx; 556 557 if ((err = dld_str_open(rq, devp, sup)) != 0) { 558 freeb(sup->su_tx_flow_mp); 559 sup->su_tx_flow_mp = NULL; 560 goto fail; 561 } 562 563 return (0); 564 565 fail: 566 if (sup != NULL) 567 kmem_cache_free(softmac_upper_cachep, sup); 568 softmac_rele(softmac); 569 return (err); 570 } 571 572 /* ARGSUSED */ 573 static int 574 softmac_drv_close(queue_t *rq, int flags __unused, cred_t *credp __unused) 575 { 576 softmac_upper_t *sup = dld_str_private(rq); 577 softmac_t *softmac = sup->su_softmac; 578 579 ASSERT(WR(rq)->q_next == NULL); 580 581 qprocsoff(rq); 582 583 ASSERT(sup->su_tx_inprocess == 0); 584 585 /* 586 * Wait until the pending request are processed by the worker thread. 587 */ 588 mutex_enter(&sup->su_disp_mutex); 589 sup->su_closing = B_TRUE; 590 while (sup->su_dlpi_pending) 591 cv_wait(&sup->su_disp_cv, &sup->su_disp_mutex); 592 mutex_exit(&sup->su_disp_mutex); 593 594 softmac_upperstream_close(sup); 595 596 if (sup->su_tx_flow_mp != NULL) { 597 freeb(sup->su_tx_flow_mp); 598 sup->su_tx_flow_mp = NULL; 599 } 600 601 if (sup->su_active) { 602 mutex_enter(&softmac->smac_active_mutex); 603 softmac->smac_nactive--; 604 mutex_exit(&softmac->smac_active_mutex); 605 sup->su_active = B_FALSE; 606 } 607 608 sup->su_bound = B_FALSE; 609 sup->su_softmac = NULL; 610 sup->su_closing = B_FALSE; 611 612 kmem_cache_free(softmac_upper_cachep, sup); 613 614 softmac_rele(softmac); 615 return (dld_str_close(rq)); 616 } 617 618 static int 619 softmac_drv_wput(queue_t *wq, mblk_t *mp) 620 { 621 softmac_upper_t *sup = dld_str_private(wq); 622 t_uscalar_t prim; 623 624 ASSERT(wq->q_next == NULL); 625 626 switch (DB_TYPE(mp)) { 627 case M_DATA: 628 case M_MULTIDATA: 629 softmac_wput_data(sup, mp); 630 break; 631 case M_PROTO: 632 case M_PCPROTO: 633 634 if (MBLKL(mp) < sizeof (t_uscalar_t)) { 635 freemsg(mp); 636 return (0); 637 } 638 639 prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; 640 if (prim == DL_UNITDATA_REQ) { 641 softmac_wput_data(sup, mp); 642 return (0); 643 } 644 645 softmac_wput_nondata(sup, mp); 646 break; 647 default: 648 softmac_wput_nondata(sup, mp); 649 break; 650 } 651 return (0); 652 } 653 654 static int 655 softmac_drv_wsrv(queue_t *wq) 656 { 657 softmac_upper_t *sup = dld_str_private(wq); 658 659 ASSERT(wq->q_next == NULL); 660 661 mutex_enter(&sup->su_mutex); 662 if (sup->su_mode != SOFTMAC_FASTPATH) { 663 /* 664 * Bump su_tx_inprocess so that su_mode won't change. 665 */ 666 sup->su_tx_inprocess++; 667 mutex_exit(&sup->su_mutex); 668 (void) dld_wsrv(wq); 669 mutex_enter(&sup->su_mutex); 670 if (--sup->su_tx_inprocess == 0) 671 cv_signal(&sup->su_cv); 672 } else if (sup->su_tx_busy && SOFTMAC_CANPUTNEXT(sup->su_slp->sl_wq)) { 673 /* 674 * The flow-conctol of the dedicated-lower-stream is 675 * relieved. If DLD_CAPAB_DIRECT is enabled, call tx_notify 676 * callback to relieve the flow-control of the specific client, 677 * otherwise relieve the flow-control of all the upper-stream 678 * using the traditional STREAM mechanism. 679 */ 680 if (sup->su_tx_notify_func != NULL) { 681 sup->su_tx_inprocess++; 682 mutex_exit(&sup->su_mutex); 683 sup->su_tx_notify_func(sup->su_tx_notify_arg, 684 (mac_tx_cookie_t)sup); 685 mutex_enter(&sup->su_mutex); 686 if (--sup->su_tx_inprocess == 0) 687 cv_signal(&sup->su_cv); 688 } 689 ASSERT(sup->su_tx_flow_mp == NULL); 690 VERIFY((sup->su_tx_flow_mp = getq(wq)) != NULL); 691 sup->su_tx_busy = B_FALSE; 692 } 693 mutex_exit(&sup->su_mutex); 694 return (0); 695 }