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