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