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