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 /* 32 * Softmac data-path switching: 33 * 34 * - Fast-path model 35 * 36 * When the softmac fast-path is used, a dedicated lower-stream 37 * will be opened over the legacy device for each IP/ARP (upper-)stream 38 * over the softMAC, and all DLPI messages (including control messages 39 * and data messages) will be exchanged between the upper-stream and 40 * the corresponding lower-stream directly. Therefore, the data 41 * demultiplexing, filtering and classification processing will be done 42 * by the lower-stream, and the GLDv3 DLS/MAC layer processing will be 43 * no longer needed. 44 * 45 * - Slow-path model 46 * 47 * Some GLDv3 features requires the GLDv3 DLS/MAC layer processing to 48 * not be bypassed to assure its function correctness. For example, 49 * softmac fast-path must be disabled to support GLDv3 VNIC functionality. 50 * In this case, a shared lower-stream will be opened over the legacy 51 * device, which is responsible for implementing the GLDv3 callbacks 52 * and passing RAW data messages between the legacy devices and the GLDv3 53 * framework. 54 * 55 * By default, the softmac fast-path mode will be used to assure the 56 * performance; MAC clients will be able to request to disable the softmac 57 * fast-path mode to support certain features, and if that succeeds, 58 * the system will fallback to the slow-path softmac data-path model. 59 * 60 * 61 * The details of the softmac data fast-path model is stated as below 62 * 63 * 1. When a stream is opened on a softMAC, the softmac module will takes 64 * over the DLPI processing on this stream; 65 * 66 * 2. For IP/ARP streams over a softMAC, softmac data fast-path will be 67 * used by default, unless fast-path is disabled by any MAC client 68 * explicitly. The softmac module first identifies an IP/ARP stream 69 * by seeing whether there is a SIOCSLIFNAME ioctl sent from upstream, 70 * if there is one, this stream is either an IP or an ARP stream 71 * and will use fast-path potentially; 72 * 73 * 3. When the softmac fast-path is used, an dedicated lower-stream will 74 * be setup for each IP/ARP stream (1-1 mapping). From that point on, 75 * all control and data messages will be exchanged between the IP/ARP 76 * upper-stream and the legacy device through this dedicated 77 * lower-stream. As a result, the DLS/MAC layer processing in GLDv3 78 * will be skipped, and this greatly improves the performance; 79 * 80 * 4. When the softmac data fast-path is disabled by a MAC client (e.g., 81 * by a VNIC), all the IP/ARP upper streams will try to switch from 82 * the fast-path to the slow-path. The dedicated lower-stream will be 83 * destroyed, and all the control and data-messages will go through the 84 * existing GLDv3 code path and (in the end) the shared lower-stream; 85 * 86 * 5. On the other hand, when the last MAC client cancels its fast-path 87 * disable request, all the IP/ARP streams will try to switch back to 88 * the fast-path mode; 89 * 90 * Step 5 and 6 both rely on the data-path mode switching process 91 * described below: 92 * 93 * 1) To switch the softmac data-path mode (between fast-path and slow-path), 94 * softmac will first send a DL_NOTE_REPLUMB DL_NOTIFY_IND message 95 * upstream over each IP/ARP streams that needs data-path mode switching; 96 * 97 * 2) When IP receives this DL_NOTE_REPLUMB message, it will bring down 98 * all the IP interfaces on the corresponding ill (IP Lower level 99 * structure), and bring up those interfaces over again; this will in 100 * turn cause the ARP to "replumb" the interface. 101 * 102 * During the replumb process, both IP and ARP will send downstream the 103 * necessary DL_DISABMULTI_REQ and DL_UNBIND_REQ messages and cleanup 104 * the old state of the underlying softMAC, following with the necessary 105 * DL_BIND_REQ and DL_ENABMULTI_REQ messages to setup the new state. 106 * Between the cleanup and re-setup process, IP/ARP will also send down 107 * a DL_NOTE_REPLUMB_DONE DL_NOTIFY_CONF messages to the softMAC to 108 * indicate the *switching point*; 109 * 110 * 3) When softmac receives the DL_NOTE_REPLUMB_DONE message, it either 111 * creates or destroys the dedicated lower-stream (depending on which 112 * data-path mode the softMAC switches to), and change the softmac 113 * data-path mode. From then on, softmac will process all the succeeding 114 * control messages (including the DL_BIND_REQ and DL_ENABMULTI_REQ 115 * messages) and data messages based on new data-path mode. 116 */ 117 118 #include <sys/types.h> 119 #include <sys/disp.h> 120 #include <sys/callb.h> 121 #include <sys/sysmacros.h> 122 #include <sys/file.h> 123 #include <sys/vlan.h> 124 #include <sys/dld.h> 125 #include <sys/sockio.h> 126 #include <sys/softmac_impl.h> 127 #include <net/if.h> 128 129 static kmutex_t softmac_taskq_lock; 130 static kcondvar_t softmac_taskq_cv; 131 static list_t softmac_taskq_list; /* List of softmac_upper_t */ 132 boolean_t softmac_taskq_quit; 133 boolean_t softmac_taskq_done; 134 135 static void softmac_taskq_dispatch(); 136 static int softmac_fastpath_setup(softmac_upper_t *); 137 static mac_tx_cookie_t softmac_fastpath_wput_data(softmac_upper_t *, mblk_t *, 138 uintptr_t, uint16_t); 139 static void softmac_datapath_switch_done(softmac_upper_t *); 140 141 void 142 softmac_fp_init() 143 { 144 mutex_init(&softmac_taskq_lock, NULL, MUTEX_DRIVER, NULL); 145 cv_init(&softmac_taskq_cv, NULL, CV_DRIVER, NULL); 146 147 softmac_taskq_quit = B_FALSE; 148 softmac_taskq_done = B_FALSE; 149 list_create(&softmac_taskq_list, sizeof (softmac_upper_t), 150 offsetof(softmac_upper_t, su_taskq_list_node)); 151 (void) thread_create(NULL, 0, softmac_taskq_dispatch, NULL, 0, 152 &p0, TS_RUN, minclsyspri); 153 } 154 155 void 156 softmac_fp_fini() 157 { 158 /* 159 * Request the softmac_taskq thread to quit and wait for it to be done. 160 */ 161 mutex_enter(&softmac_taskq_lock); 162 softmac_taskq_quit = B_TRUE; 163 cv_signal(&softmac_taskq_cv); 164 while (!softmac_taskq_done) 165 cv_wait(&softmac_taskq_cv, &softmac_taskq_lock); 166 mutex_exit(&softmac_taskq_lock); 167 list_destroy(&softmac_taskq_list); 168 169 mutex_destroy(&softmac_taskq_lock); 170 cv_destroy(&softmac_taskq_cv); 171 } 172 173 static boolean_t 174 check_ip_above(queue_t *q) 175 { 176 queue_t *next_q; 177 boolean_t ret = B_TRUE; 178 179 claimstr(q); 180 next_q = q->q_next; 181 if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0) 182 ret = B_FALSE; 183 releasestr(q); 184 return (ret); 185 } 186 187 /* ARGSUSED */ 188 static int 189 softmac_capab_perim(softmac_upper_t *sup, void *data, uint_t flags) 190 { 191 switch (flags) { 192 case DLD_ENABLE: 193 mutex_enter(&sup->su_mutex); 194 break; 195 case DLD_DISABLE: 196 mutex_exit(&sup->su_mutex); 197 break; 198 case DLD_QUERY: 199 return (MUTEX_HELD(&sup->su_mutex)); 200 } 201 return (0); 202 } 203 204 static mac_tx_notify_handle_t 205 softmac_client_tx_notify(softmac_upper_t *sup, mac_tx_notify_t func, void *arg) 206 { 207 ASSERT(MUTEX_HELD(&sup->su_mutex)); 208 209 if (func != NULL) { 210 sup->su_tx_notify_func = func; 211 sup->su_tx_notify_arg = arg; 212 } else { 213 /* 214 * Wait for all tx_notify_func call to be done. 215 */ 216 while (sup->su_tx_inprocess != 0) 217 cv_wait(&sup->su_cv, &sup->su_mutex); 218 219 sup->su_tx_notify_func = NULL; 220 sup->su_tx_notify_arg = NULL; 221 } 222 return ((mac_tx_notify_handle_t)sup); 223 } 224 225 static boolean_t 226 softmac_tx_is_flow_blocked(softmac_upper_t *sup, mac_tx_cookie_t cookie) 227 { 228 ASSERT(cookie == (mac_tx_cookie_t)sup); 229 return (sup->su_tx_busy); 230 } 231 232 static int 233 softmac_capab_direct(softmac_upper_t *sup, void *data, uint_t flags) 234 { 235 dld_capab_direct_t *direct = data; 236 softmac_lower_t *slp = sup->su_slp; 237 238 ASSERT(MUTEX_HELD(&sup->su_mutex)); 239 240 ASSERT(sup->su_mode == SOFTMAC_FASTPATH); 241 242 switch (flags) { 243 case DLD_ENABLE: 244 if (sup->su_direct) 245 return (0); 246 247 sup->su_direct_rxinfo.slr_rx = (softmac_rx_t)direct->di_rx_cf; 248 sup->su_direct_rxinfo.slr_arg = direct->di_rx_ch; 249 slp->sl_rxinfo = &sup->su_direct_rxinfo; 250 direct->di_tx_df = (uintptr_t)softmac_fastpath_wput_data; 251 direct->di_tx_dh = sup; 252 direct->di_tx_fctl_df = (uintptr_t)softmac_tx_is_flow_blocked; 253 direct->di_tx_fctl_dh = sup; 254 direct->di_tx_cb_df = (uintptr_t)softmac_client_tx_notify; 255 direct->di_tx_cb_dh = sup; 256 sup->su_direct = B_TRUE; 257 return (0); 258 259 case DLD_DISABLE: 260 if (!sup->su_direct) 261 return (0); 262 263 slp->sl_rxinfo = &sup->su_rxinfo; 264 sup->su_direct = B_FALSE; 265 return (0); 266 } 267 return (ENOTSUP); 268 } 269 270 static int 271 softmac_dld_capab(softmac_upper_t *sup, uint_t type, void *data, uint_t flags) 272 { 273 int err; 274 275 /* 276 * Don't enable direct callback capabilities unless the caller is 277 * the IP client. When a module is inserted in a stream (_I_INSERT) 278 * the stack initiates capability disable, but due to races, the 279 * module insertion may complete before the capability disable 280 * completes. So we limit the check to DLD_ENABLE case. 281 */ 282 if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) && 283 !check_ip_above(sup->su_rq)) { 284 return (ENOTSUP); 285 } 286 287 switch (type) { 288 case DLD_CAPAB_DIRECT: 289 err = softmac_capab_direct(sup, data, flags); 290 break; 291 292 case DLD_CAPAB_PERIM: 293 err = softmac_capab_perim(sup, data, flags); 294 break; 295 296 default: 297 err = ENOTSUP; 298 break; 299 } 300 return (err); 301 } 302 303 static void 304 softmac_capability_advertise(softmac_upper_t *sup, mblk_t *mp) 305 { 306 dl_capability_ack_t *dlap; 307 dl_capability_sub_t *dlsp; 308 t_uscalar_t subsize; 309 uint8_t *ptr; 310 queue_t *q = sup->su_wq; 311 mblk_t *mp1; 312 softmac_t *softmac = sup->su_softmac; 313 boolean_t dld_capable = B_FALSE; 314 boolean_t hcksum_capable = B_FALSE; 315 boolean_t zcopy_capable = B_FALSE; 316 317 ASSERT(sup->su_mode == SOFTMAC_FASTPATH); 318 319 /* 320 * Initially assume no capabilities. 321 */ 322 subsize = 0; 323 324 /* 325 * Direct capability negotiation interface between IP and softmac 326 */ 327 if (check_ip_above(sup->su_rq)) { 328 dld_capable = B_TRUE; 329 subsize += sizeof (dl_capability_sub_t) + 330 sizeof (dl_capab_dld_t); 331 } 332 333 /* 334 * Check if checksum offload is supported on this MAC. 335 */ 336 if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM) { 337 hcksum_capable = B_TRUE; 338 subsize += sizeof (dl_capability_sub_t) + 339 sizeof (dl_capab_hcksum_t); 340 } 341 342 /* 343 * Check if zerocopy is supported on this interface. 344 */ 345 if (!(softmac->smac_capab_flags & MAC_CAPAB_NO_ZCOPY)) { 346 zcopy_capable = B_TRUE; 347 subsize += sizeof (dl_capability_sub_t) + 348 sizeof (dl_capab_zerocopy_t); 349 } 350 351 /* 352 * If there are no capabilities to advertise or if we 353 * can't allocate a response, send a DL_ERROR_ACK. 354 */ 355 if ((subsize == 0) || (mp1 = reallocb(mp, 356 sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) { 357 dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0); 358 return; 359 } 360 361 mp = mp1; 362 DB_TYPE(mp) = M_PROTO; 363 mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize; 364 bzero(mp->b_rptr, MBLKL(mp)); 365 dlap = (dl_capability_ack_t *)mp->b_rptr; 366 dlap->dl_primitive = DL_CAPABILITY_ACK; 367 dlap->dl_sub_offset = sizeof (dl_capability_ack_t); 368 dlap->dl_sub_length = subsize; 369 ptr = (uint8_t *)&dlap[1]; 370 371 /* 372 * IP polling interface. 373 */ 374 if (dld_capable) { 375 dl_capab_dld_t dld; 376 377 dlsp = (dl_capability_sub_t *)ptr; 378 dlsp->dl_cap = DL_CAPAB_DLD; 379 dlsp->dl_length = sizeof (dl_capab_dld_t); 380 ptr += sizeof (dl_capability_sub_t); 381 382 bzero(&dld, sizeof (dl_capab_dld_t)); 383 dld.dld_version = DLD_CURRENT_VERSION; 384 dld.dld_capab = (uintptr_t)softmac_dld_capab; 385 dld.dld_capab_handle = (uintptr_t)sup; 386 387 dlcapabsetqid(&(dld.dld_mid), sup->su_rq); 388 bcopy(&dld, ptr, sizeof (dl_capab_dld_t)); 389 ptr += sizeof (dl_capab_dld_t); 390 } 391 392 /* 393 * TCP/IP checksum offload. 394 */ 395 if (hcksum_capable) { 396 dl_capab_hcksum_t hcksum; 397 398 dlsp = (dl_capability_sub_t *)ptr; 399 400 dlsp->dl_cap = DL_CAPAB_HCKSUM; 401 dlsp->dl_length = sizeof (dl_capab_hcksum_t); 402 ptr += sizeof (dl_capability_sub_t); 403 404 bzero(&hcksum, sizeof (dl_capab_hcksum_t)); 405 hcksum.hcksum_version = HCKSUM_VERSION_1; 406 hcksum.hcksum_txflags = softmac->smac_hcksum_txflags; 407 dlcapabsetqid(&(hcksum.hcksum_mid), sup->su_rq); 408 bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t)); 409 ptr += sizeof (dl_capab_hcksum_t); 410 } 411 412 /* 413 * Zero copy 414 */ 415 if (zcopy_capable) { 416 dl_capab_zerocopy_t zcopy; 417 418 dlsp = (dl_capability_sub_t *)ptr; 419 420 dlsp->dl_cap = DL_CAPAB_ZEROCOPY; 421 dlsp->dl_length = sizeof (dl_capab_zerocopy_t); 422 ptr += sizeof (dl_capability_sub_t); 423 424 bzero(&zcopy, sizeof (dl_capab_zerocopy_t)); 425 zcopy.zerocopy_version = ZEROCOPY_VERSION_1; 426 zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM; 427 dlcapabsetqid(&(zcopy.zerocopy_mid), sup->su_rq); 428 bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t)); 429 ptr += sizeof (dl_capab_zerocopy_t); 430 } 431 432 ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize); 433 qreply(q, mp); 434 } 435 436 static void 437 softmac_capability_req(softmac_upper_t *sup, mblk_t *mp) 438 { 439 dl_capability_req_t *dlp = (dl_capability_req_t *)mp->b_rptr; 440 dl_capability_sub_t *sp; 441 size_t size, len; 442 offset_t off, end; 443 t_uscalar_t dl_err; 444 queue_t *q = sup->su_wq; 445 446 ASSERT(sup->su_mode == SOFTMAC_FASTPATH); 447 if (MBLKL(mp) < sizeof (dl_capability_req_t)) { 448 dl_err = DL_BADPRIM; 449 goto failed; 450 } 451 452 if (!sup->su_bound) { 453 dl_err = DL_OUTSTATE; 454 goto failed; 455 } 456 457 /* 458 * This request is overloaded. If there are no requested capabilities 459 * then we just want to acknowledge with all the capabilities we 460 * support. Otherwise we enable the set of capabilities requested. 461 */ 462 if (dlp->dl_sub_length == 0) { 463 softmac_capability_advertise(sup, mp); 464 return; 465 } 466 467 if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) { 468 dl_err = DL_BADPRIM; 469 goto failed; 470 } 471 472 dlp->dl_primitive = DL_CAPABILITY_ACK; 473 474 off = dlp->dl_sub_offset; 475 len = dlp->dl_sub_length; 476 477 /* 478 * Walk the list of capabilities to be enabled. 479 */ 480 for (end = off + len; off < end; ) { 481 sp = (dl_capability_sub_t *)(mp->b_rptr + off); 482 size = sizeof (dl_capability_sub_t) + sp->dl_length; 483 484 if (off + size > end || 485 !IS_P2ALIGNED(off, sizeof (uint32_t))) { 486 dl_err = DL_BADPRIM; 487 goto failed; 488 } 489 490 switch (sp->dl_cap) { 491 /* 492 * TCP/IP checksum offload to hardware. 493 */ 494 case DL_CAPAB_HCKSUM: { 495 dl_capab_hcksum_t *hcksump; 496 dl_capab_hcksum_t hcksum; 497 498 hcksump = (dl_capab_hcksum_t *)&sp[1]; 499 /* 500 * Copy for alignment. 501 */ 502 bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t)); 503 dlcapabsetqid(&(hcksum.hcksum_mid), sup->su_rq); 504 bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t)); 505 break; 506 } 507 508 default: 509 break; 510 } 511 512 off += size; 513 } 514 qreply(q, mp); 515 return; 516 failed: 517 dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0); 518 } 519 520 static void 521 softmac_bind_req(softmac_upper_t *sup, mblk_t *mp) 522 { 523 softmac_lower_t *slp = sup->su_slp; 524 softmac_t *softmac = sup->su_softmac; 525 mblk_t *ackmp, *mp1; 526 int err; 527 528 if (MBLKL(mp) < DL_BIND_REQ_SIZE) { 529 freemsg(mp); 530 return; 531 } 532 533 /* 534 * Allocate ackmp incase the underlying driver does not ack timely. 535 */ 536 if ((mp1 = allocb(sizeof (dl_error_ack_t), BPRI_HI)) == NULL) { 537 dlerrorack(sup->su_wq, mp, DL_BIND_REQ, DL_SYSERR, ENOMEM); 538 return; 539 } 540 541 err = softmac_output(slp, mp, DL_BIND_REQ, DL_BIND_ACK, &ackmp); 542 if (ackmp != NULL) { 543 freemsg(mp1); 544 } else { 545 /* 546 * The driver does not ack timely. 547 */ 548 ASSERT(err == ENOMSG); 549 ackmp = mp1; 550 } 551 if (err != 0) 552 goto failed; 553 554 /* 555 * Enable capabilities the underlying driver claims to support. 556 */ 557 if ((err = softmac_capab_enable(slp)) != 0) 558 goto failed; 559 560 /* 561 * Check whether this softmac is already marked as exclusively used, 562 * e.g., an aggregation is created over it. Fail the BIND_REQ if so. 563 */ 564 mutex_enter(&softmac->smac_active_mutex); 565 if (softmac->smac_active) { 566 mutex_exit(&softmac->smac_active_mutex); 567 err = EBUSY; 568 goto failed; 569 } 570 softmac->smac_nactive++; 571 sup->su_active = B_TRUE; 572 mutex_exit(&softmac->smac_active_mutex); 573 sup->su_bound = B_TRUE; 574 575 qreply(sup->su_wq, ackmp); 576 return; 577 failed: 578 if (err != 0) { 579 dlerrorack(sup->su_wq, ackmp, DL_BIND_REQ, DL_SYSERR, err); 580 return; 581 } 582 } 583 584 static void 585 softmac_unbind_req(softmac_upper_t *sup, mblk_t *mp) 586 { 587 softmac_lower_t *slp = sup->su_slp; 588 softmac_t *softmac = sup->su_softmac; 589 mblk_t *ackmp, *mp1; 590 int err; 591 592 if (MBLKL(mp) < DL_UNBIND_REQ_SIZE) { 593 freemsg(mp); 594 return; 595 } 596 597 if (!sup->su_bound) { 598 dlerrorack(sup->su_wq, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0); 599 return; 600 } 601 602 /* 603 * Allocate ackmp incase the underlying driver does not ack timely. 604 */ 605 if ((mp1 = allocb(sizeof (dl_error_ack_t), BPRI_HI)) == NULL) { 606 dlerrorack(sup->su_wq, mp, DL_UNBIND_REQ, DL_SYSERR, ENOMEM); 607 return; 608 } 609 610 err = softmac_output(slp, mp, DL_UNBIND_REQ, DL_OK_ACK, &ackmp); 611 if (ackmp != NULL) { 612 freemsg(mp1); 613 } else { 614 /* 615 * The driver does not ack timely. 616 */ 617 ASSERT(err == ENOMSG); 618 ackmp = mp1; 619 } 620 if (err != 0) { 621 dlerrorack(sup->su_wq, ackmp, DL_UNBIND_REQ, DL_SYSERR, err); 622 return; 623 } 624 625 sup->su_bound = B_FALSE; 626 627 mutex_enter(&softmac->smac_active_mutex); 628 if (sup->su_active) { 629 ASSERT(!softmac->smac_active); 630 softmac->smac_nactive--; 631 sup->su_active = B_FALSE; 632 } 633 mutex_exit(&softmac->smac_active_mutex); 634 635 done: 636 qreply(sup->su_wq, ackmp); 637 } 638 639 /* 640 * Process the non-data mblk. 641 */ 642 static void 643 softmac_wput_single_nondata(softmac_upper_t *sup, mblk_t *mp) 644 { 645 softmac_t *softmac = sup->su_softmac; 646 softmac_lower_t *slp = sup->su_slp; 647 unsigned char dbtype; 648 t_uscalar_t prim; 649 650 dbtype = DB_TYPE(mp); 651 sup->su_is_arp = 0; 652 switch (dbtype) { 653 case M_CTL: 654 sup->su_is_arp = 1; 655 /* FALLTHROUGH */ 656 case M_IOCTL: { 657 uint32_t expected_mode; 658 659 if (((struct iocblk *)(mp->b_rptr))->ioc_cmd != SIOCSLIFNAME) 660 break; 661 662 /* 663 * Nak the M_IOCTL based on the STREAMS specification. 664 */ 665 if (dbtype == M_IOCTL) 666 miocnak(sup->su_wq, mp, 0, EINVAL); 667 else 668 freemsg(mp); 669 670 /* 671 * This stream is either IP or ARP. See whether 672 * we need to setup a dedicated-lower-stream for it. 673 */ 674 mutex_enter(&softmac->smac_fp_mutex); 675 676 expected_mode = DATAPATH_MODE(softmac); 677 if (expected_mode == SOFTMAC_SLOWPATH) 678 sup->su_mode = SOFTMAC_SLOWPATH; 679 list_insert_head(&softmac->smac_sup_list, sup); 680 mutex_exit(&softmac->smac_fp_mutex); 681 682 /* 683 * Setup the fast-path dedicated lower stream if fast-path 684 * is expected. Note that no lock is held here, and if 685 * smac_expected_mode is changed from SOFTMAC_FASTPATH to 686 * SOFTMAC_SLOWPATH, the DL_NOTE_REPLUMB message used for 687 * data-path switching would already be queued and will 688 * be processed by softmac_wput_single_nondata() later. 689 */ 690 if (expected_mode == SOFTMAC_FASTPATH) 691 (void) softmac_fastpath_setup(sup); 692 return; 693 } 694 case M_PROTO: 695 case M_PCPROTO: 696 if (MBLKL(mp) < sizeof (t_uscalar_t)) { 697 freemsg(mp); 698 return; 699 } 700 prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; 701 switch (prim) { 702 case DL_NOTIFY_IND: 703 if (MBLKL(mp) < sizeof (dl_notify_ind_t) || 704 ((dl_notify_ind_t *)mp->b_rptr)->dl_notification != 705 DL_NOTE_REPLUMB) { 706 freemsg(mp); 707 return; 708 } 709 /* 710 * This DL_NOTE_REPLUMB message is initiated 711 * and queued by the softmac itself, when the 712 * sup is trying to switching its datapath mode 713 * between SOFTMAC_SLOWPATH and SOFTMAC_FASTPATH. 714 * Send this message upstream. 715 */ 716 qreply(sup->su_wq, mp); 717 return; 718 case DL_NOTIFY_CONF: 719 if (MBLKL(mp) < sizeof (dl_notify_conf_t) || 720 ((dl_notify_conf_t *)mp->b_rptr)->dl_notification != 721 DL_NOTE_REPLUMB_DONE) { 722 freemsg(mp); 723 return; 724 } 725 /* 726 * This is an indication from IP/ARP that the 727 * fastpath->slowpath switch is done. 728 */ 729 freemsg(mp); 730 softmac_datapath_switch_done(sup); 731 return; 732 } 733 break; 734 } 735 736 /* 737 * No need to hold lock to check su_mode, since su_mode updating only 738 * operation is is serialized by softmac_wput_nondata_task(). 739 */ 740 if (sup->su_mode != SOFTMAC_FASTPATH) { 741 (void) dld_wput(sup->su_wq, mp); 742 return; 743 } 744 745 /* 746 * Fastpath non-data message processing. Most of non-data messages 747 * can be directly passed down to the dedicated-lower-stream, aside 748 * from the following M_PROTO/M_PCPROTO messages. 749 */ 750 switch (dbtype) { 751 case M_PROTO: 752 case M_PCPROTO: 753 switch (prim) { 754 case DL_BIND_REQ: 755 softmac_bind_req(sup, mp); 756 break; 757 case DL_UNBIND_REQ: 758 softmac_unbind_req(sup, mp); 759 break; 760 case DL_CAPABILITY_REQ: 761 softmac_capability_req(sup, mp); 762 break; 763 default: 764 putnext(slp->sl_wq, mp); 765 break; 766 } 767 break; 768 default: 769 putnext(slp->sl_wq, mp); 770 break; 771 } 772 } 773 774 /* 775 * The worker thread which processes non-data messages. Note we only process 776 * one message at one time in order to be able to "flush" the queued message 777 * and serialize the processing. 778 */ 779 static void 780 softmac_wput_nondata_task(void *arg) 781 { 782 softmac_upper_t *sup = arg; 783 mblk_t *mp; 784 785 mutex_enter(&sup->su_disp_mutex); 786 787 while (sup->su_pending_head != NULL) { 788 if (sup->su_closing) 789 break; 790 791 SOFTMAC_DQ_PENDING(sup, &mp); 792 mutex_exit(&sup->su_disp_mutex); 793 softmac_wput_single_nondata(sup, mp); 794 mutex_enter(&sup->su_disp_mutex); 795 } 796 797 /* 798 * If the stream is closing, flush all queued messages and inform 799 * the stream to be closed. 800 */ 801 freemsgchain(sup->su_pending_head); 802 sup->su_pending_head = sup->su_pending_tail = NULL; 803 sup->su_dlpi_pending = B_FALSE; 804 cv_signal(&sup->su_disp_cv); 805 mutex_exit(&sup->su_disp_mutex); 806 } 807 808 /* 809 * Kernel thread to handle taskq dispatch failures in softmac_wput_nondata(). 810 * This thread is started when the softmac module is first loaded. 811 */ 812 static void 813 softmac_taskq_dispatch(void) 814 { 815 callb_cpr_t cprinfo; 816 softmac_upper_t *sup; 817 818 CALLB_CPR_INIT(&cprinfo, &softmac_taskq_lock, callb_generic_cpr, 819 "softmac_taskq_dispatch"); 820 mutex_enter(&softmac_taskq_lock); 821 822 while (!softmac_taskq_quit) { 823 sup = list_head(&softmac_taskq_list); 824 while (sup != NULL) { 825 list_remove(&softmac_taskq_list, sup); 826 sup->su_taskq_scheduled = B_FALSE; 827 mutex_exit(&softmac_taskq_lock); 828 VERIFY(taskq_dispatch(system_taskq, 829 softmac_wput_nondata_task, sup, TQ_SLEEP) != 830 TASKQID_INVALID); 831 mutex_enter(&softmac_taskq_lock); 832 sup = list_head(&softmac_taskq_list); 833 } 834 835 CALLB_CPR_SAFE_BEGIN(&cprinfo); 836 cv_wait(&softmac_taskq_cv, &softmac_taskq_lock); 837 CALLB_CPR_SAFE_END(&cprinfo, &softmac_taskq_lock); 838 } 839 840 softmac_taskq_done = B_TRUE; 841 cv_signal(&softmac_taskq_cv); 842 CALLB_CPR_EXIT(&cprinfo); 843 thread_exit(); 844 } 845 846 void 847 softmac_wput_nondata(softmac_upper_t *sup, mblk_t *mp) 848 { 849 /* 850 * The processing of the message might block. Enqueue the 851 * message for later processing. 852 */ 853 mutex_enter(&sup->su_disp_mutex); 854 855 if (sup->su_closing) { 856 mutex_exit(&sup->su_disp_mutex); 857 freemsg(mp); 858 return; 859 } 860 861 SOFTMAC_EQ_PENDING(sup, mp); 862 863 if (sup->su_dlpi_pending) { 864 mutex_exit(&sup->su_disp_mutex); 865 return; 866 } 867 sup->su_dlpi_pending = B_TRUE; 868 mutex_exit(&sup->su_disp_mutex); 869 870 if (taskq_dispatch(system_taskq, softmac_wput_nondata_task, 871 sup, TQ_NOSLEEP) != TASKQID_INVALID) { 872 return; 873 } 874 875 mutex_enter(&softmac_taskq_lock); 876 if (!sup->su_taskq_scheduled) { 877 list_insert_tail(&softmac_taskq_list, sup); 878 cv_signal(&softmac_taskq_cv); 879 } 880 sup->su_taskq_scheduled = B_TRUE; 881 mutex_exit(&softmac_taskq_lock); 882 } 883 884 /* 885 * Setup the dedicated-lower-stream (fast-path) for the IP/ARP upperstream. 886 */ 887 static int 888 softmac_fastpath_setup(softmac_upper_t *sup) 889 { 890 softmac_t *softmac = sup->su_softmac; 891 softmac_lower_t *slp; 892 int err; 893 894 err = softmac_lower_setup(softmac, sup, &slp); 895 896 mutex_enter(&sup->su_mutex); 897 /* 898 * Wait for all data messages to be processed so that we can change 899 * the su_mode. 900 */ 901 while (sup->su_tx_inprocess != 0) 902 cv_wait(&sup->su_cv, &sup->su_mutex); 903 904 ASSERT(sup->su_mode != SOFTMAC_FASTPATH); 905 ASSERT(sup->su_slp == NULL); 906 if (err != 0) { 907 sup->su_mode = SOFTMAC_SLOWPATH; 908 } else { 909 sup->su_slp = slp; 910 sup->su_mode = SOFTMAC_FASTPATH; 911 } 912 mutex_exit(&sup->su_mutex); 913 return (err); 914 } 915 916 /* 917 * Tear down the dedicated-lower-stream (fast-path) for the IP/ARP upperstream. 918 */ 919 static void 920 softmac_fastpath_tear(softmac_upper_t *sup) 921 { 922 mutex_enter(&sup->su_mutex); 923 /* 924 * Wait for all data messages in the dedicated-lower-stream 925 * to be processed. 926 */ 927 while (sup->su_tx_inprocess != 0) 928 cv_wait(&sup->su_cv, &sup->su_mutex); 929 930 /* 931 * Note that this function is called either when the stream is closed, 932 * or the stream is unbound (fastpath-slowpath-switch). Therefore, 933 * No need to call the tx_notify callback. 934 */ 935 sup->su_tx_notify_func = NULL; 936 sup->su_tx_notify_arg = NULL; 937 if (sup->su_tx_busy) { 938 ASSERT(sup->su_tx_flow_mp == NULL); 939 VERIFY((sup->su_tx_flow_mp = getq(sup->su_wq)) != NULL); 940 sup->su_tx_busy = B_FALSE; 941 } 942 943 sup->su_mode = SOFTMAC_SLOWPATH; 944 945 /* 946 * Destroy the dedicated-lower-stream. Note that slp is destroyed 947 * when lh is closed. 948 */ 949 (void) ldi_close(sup->su_slp->sl_lh, FREAD|FWRITE, kcred); 950 sup->su_slp = NULL; 951 mutex_exit(&sup->su_mutex); 952 } 953 954 void 955 softmac_wput_data(softmac_upper_t *sup, mblk_t *mp) 956 { 957 /* 958 * No lock is required to access the su_mode field since the data 959 * traffic is quiesce by IP when the data-path mode is in the 960 * process of switching. 961 */ 962 if (sup->su_mode != SOFTMAC_FASTPATH) 963 (void) dld_wput(sup->su_wq, mp); 964 else 965 (void) softmac_fastpath_wput_data(sup, mp, (uintptr_t)NULL, 0); 966 } 967 968 /*ARGSUSED*/ 969 static mac_tx_cookie_t 970 softmac_fastpath_wput_data(softmac_upper_t *sup, mblk_t *mp, uintptr_t f_hint, 971 uint16_t flag) 972 { 973 queue_t *wq = sup->su_slp->sl_wq; 974 975 /* 976 * This function is called from IP, only the MAC_DROP_ON_NO_DESC 977 * flag can be specified. 978 */ 979 ASSERT((flag & ~MAC_DROP_ON_NO_DESC) == 0); 980 ASSERT(mp->b_next == NULL); 981 982 /* 983 * Check wether the dedicated-lower-stream is able to handle more 984 * messages, and enable the flow-control if it is not. 985 * 986 * Note that in order not to introduce any packet reordering, we 987 * always send the message down to the dedicated-lower-stream: 988 * 989 * If the flow-control is already enabled, but we still get 990 * the messages from the upper-stream, it means that the upper 991 * stream does not respect STREAMS flow-control (e.g., TCP). Simply 992 * pass the message down to the lower-stream in that case. 993 */ 994 if (SOFTMAC_CANPUTNEXT(wq)) { 995 putnext(wq, mp); 996 return ((mac_tx_cookie_t)NULL); 997 } 998 999 if (sup->su_tx_busy) { 1000 if ((flag & MAC_DROP_ON_NO_DESC) != 0) 1001 freemsg(mp); 1002 else 1003 putnext(wq, mp); 1004 return ((mac_tx_cookie_t)sup); 1005 } 1006 1007 mutex_enter(&sup->su_mutex); 1008 if (!sup->su_tx_busy) { 1009 /* 1010 * If DLD_CAPAB_DIRECT is enabled, the notify callback will be 1011 * called when the flow control can be disabled. Otherwise, 1012 * put the tx_flow_mp into the wq to make use of the old 1013 * streams flow control. 1014 */ 1015 ASSERT(sup->su_tx_flow_mp != NULL); 1016 (void) putq(sup->su_wq, sup->su_tx_flow_mp); 1017 sup->su_tx_flow_mp = NULL; 1018 sup->su_tx_busy = B_TRUE; 1019 qenable(wq); 1020 } 1021 mutex_exit(&sup->su_mutex); 1022 1023 if ((flag & MAC_DROP_ON_NO_DESC) != 0) 1024 freemsg(mp); 1025 else 1026 putnext(wq, mp); 1027 return ((mac_tx_cookie_t)sup); 1028 } 1029 1030 boolean_t 1031 softmac_active_set(void *arg) 1032 { 1033 softmac_t *softmac = arg; 1034 1035 mutex_enter(&softmac->smac_active_mutex); 1036 if (softmac->smac_nactive != 0) { 1037 mutex_exit(&softmac->smac_active_mutex); 1038 return (B_FALSE); 1039 } 1040 softmac->smac_active = B_TRUE; 1041 mutex_exit(&softmac->smac_active_mutex); 1042 return (B_TRUE); 1043 } 1044 1045 void 1046 softmac_active_clear(void *arg) 1047 { 1048 softmac_t *softmac = arg; 1049 1050 mutex_enter(&softmac->smac_active_mutex); 1051 ASSERT(softmac->smac_active && (softmac->smac_nactive == 0)); 1052 softmac->smac_active = B_FALSE; 1053 mutex_exit(&softmac->smac_active_mutex); 1054 } 1055 1056 /* 1057 * Disable/reenable fastpath on given softmac. This request could come from a 1058 * MAC client or directly from administrators. 1059 */ 1060 int 1061 softmac_datapath_switch(softmac_t *softmac, boolean_t disable, boolean_t admin) 1062 { 1063 softmac_upper_t *sup; 1064 mblk_t *head = NULL, *tail = NULL, *mp; 1065 list_t reqlist; 1066 softmac_switch_req_t *req; 1067 uint32_t current_mode, expected_mode; 1068 int err = 0; 1069 1070 mutex_enter(&softmac->smac_fp_mutex); 1071 1072 current_mode = DATAPATH_MODE(softmac); 1073 if (admin) { 1074 if (softmac->smac_fastpath_admin_disabled == disable) { 1075 mutex_exit(&softmac->smac_fp_mutex); 1076 return (0); 1077 } 1078 softmac->smac_fastpath_admin_disabled = disable; 1079 } else if (disable) { 1080 softmac->smac_fp_disable_clients++; 1081 } else { 1082 ASSERT(softmac->smac_fp_disable_clients != 0); 1083 softmac->smac_fp_disable_clients--; 1084 } 1085 1086 expected_mode = DATAPATH_MODE(softmac); 1087 if (current_mode == expected_mode) { 1088 mutex_exit(&softmac->smac_fp_mutex); 1089 return (0); 1090 } 1091 1092 /* 1093 * The expected mode is different from whatever datapath mode 1094 * this softmac is expected from last request, enqueue the data-path 1095 * switch request. 1096 */ 1097 list_create(&reqlist, sizeof (softmac_switch_req_t), 1098 offsetof(softmac_switch_req_t, ssq_req_list_node)); 1099 1100 /* 1101 * Allocate all DL_NOTIFY_IND messages and request structures that 1102 * are required to switch each IP/ARP stream to the expected mode. 1103 */ 1104 for (sup = list_head(&softmac->smac_sup_list); sup != NULL; 1105 sup = list_next(&softmac->smac_sup_list, sup)) { 1106 dl_notify_ind_t *dlip; 1107 1108 req = kmem_alloc(sizeof (softmac_switch_req_t), KM_NOSLEEP); 1109 if (req == NULL) 1110 break; 1111 1112 req->ssq_expected_mode = expected_mode; 1113 if (sup->su_is_arp) { 1114 list_insert_tail(&reqlist, req); 1115 continue; 1116 } 1117 /* 1118 * Allocate the DL_NOTE_REPLUMB message. 1119 */ 1120 if ((mp = allocb(sizeof (dl_notify_ind_t), BPRI_LO)) == NULL) { 1121 kmem_free(req, sizeof (softmac_switch_req_t)); 1122 break; 1123 } 1124 1125 list_insert_tail(&reqlist, req); 1126 1127 mp->b_wptr = mp->b_rptr + sizeof (dl_notify_ind_t); 1128 mp->b_datap->db_type = M_PROTO; 1129 bzero(mp->b_rptr, sizeof (dl_notify_ind_t)); 1130 dlip = (dl_notify_ind_t *)mp->b_rptr; 1131 dlip->dl_primitive = DL_NOTIFY_IND; 1132 dlip->dl_notification = DL_NOTE_REPLUMB; 1133 if (head == NULL) { 1134 head = tail = mp; 1135 } else { 1136 tail->b_next = mp; 1137 tail = mp; 1138 } 1139 } 1140 1141 /* 1142 * Note that it is fine if the expected data-path mode is fast-path 1143 * and some of streams fails to switch. Only return failure if we 1144 * are expected to switch to the slow-path. 1145 */ 1146 if (sup != NULL && expected_mode == SOFTMAC_SLOWPATH) { 1147 err = ENOMEM; 1148 goto fail; 1149 } 1150 1151 /* 1152 * Start switching for each IP/ARP stream. The switching operation 1153 * will eventually succeed and there is no need to wait for it 1154 * to finish. 1155 */ 1156 for (sup = list_head(&softmac->smac_sup_list); sup != NULL; 1157 sup = list_next(&softmac->smac_sup_list, sup)) { 1158 if (!sup->su_is_arp) { 1159 mp = head->b_next; 1160 head->b_next = NULL; 1161 softmac_wput_nondata(sup, head); 1162 head = mp; 1163 } 1164 /* 1165 * Add the switch request to the requests list of the stream. 1166 */ 1167 req = list_head(&reqlist); 1168 ASSERT(req != NULL); 1169 list_remove(&reqlist, req); 1170 list_insert_tail(&sup->su_req_list, req); 1171 } 1172 1173 mutex_exit(&softmac->smac_fp_mutex); 1174 ASSERT(list_is_empty(&reqlist)); 1175 list_destroy(&reqlist); 1176 return (0); 1177 fail: 1178 if (admin) { 1179 softmac->smac_fastpath_admin_disabled = !disable; 1180 } else if (disable) { 1181 softmac->smac_fp_disable_clients--; 1182 } else { 1183 softmac->smac_fp_disable_clients++; 1184 } 1185 1186 mutex_exit(&softmac->smac_fp_mutex); 1187 while ((req = list_head(&reqlist)) != NULL) { 1188 list_remove(&reqlist, req); 1189 kmem_free(req, sizeof (softmac_switch_req_t)); 1190 } 1191 freemsgchain(head); 1192 list_destroy(&reqlist); 1193 return (err); 1194 } 1195 1196 int 1197 softmac_fastpath_disable(void *arg) 1198 { 1199 return (softmac_datapath_switch((softmac_t *)arg, B_TRUE, B_FALSE)); 1200 } 1201 1202 void 1203 softmac_fastpath_enable(void *arg) 1204 { 1205 VERIFY(softmac_datapath_switch((softmac_t *)arg, B_FALSE, 1206 B_FALSE) == 0); 1207 } 1208 1209 void 1210 softmac_upperstream_close(softmac_upper_t *sup) 1211 { 1212 softmac_t *softmac = sup->su_softmac; 1213 softmac_switch_req_t *req; 1214 1215 mutex_enter(&softmac->smac_fp_mutex); 1216 1217 if (sup->su_mode == SOFTMAC_FASTPATH) 1218 softmac_fastpath_tear(sup); 1219 1220 if (sup->su_mode != SOFTMAC_UNKNOWN) { 1221 list_remove(&softmac->smac_sup_list, sup); 1222 sup->su_mode = SOFTMAC_UNKNOWN; 1223 } 1224 1225 /* 1226 * Cleanup all the switch requests queueed on this stream. 1227 */ 1228 while ((req = list_head(&sup->su_req_list)) != NULL) { 1229 list_remove(&sup->su_req_list, req); 1230 kmem_free(req, sizeof (softmac_switch_req_t)); 1231 } 1232 mutex_exit(&softmac->smac_fp_mutex); 1233 } 1234 1235 /* 1236 * Handle the DL_NOTE_REPLUMB_DONE indication from IP/ARP. Change the upper 1237 * stream from the fastpath mode to the slowpath mode. 1238 */ 1239 static void 1240 softmac_datapath_switch_done(softmac_upper_t *sup) 1241 { 1242 softmac_t *softmac = sup->su_softmac; 1243 softmac_switch_req_t *req; 1244 uint32_t expected_mode; 1245 1246 mutex_enter(&softmac->smac_fp_mutex); 1247 req = list_head(&sup->su_req_list); 1248 list_remove(&sup->su_req_list, req); 1249 expected_mode = req->ssq_expected_mode; 1250 kmem_free(req, sizeof (softmac_switch_req_t)); 1251 1252 if (expected_mode == sup->su_mode) { 1253 mutex_exit(&softmac->smac_fp_mutex); 1254 return; 1255 } 1256 1257 ASSERT(!sup->su_bound); 1258 mutex_exit(&softmac->smac_fp_mutex); 1259 1260 /* 1261 * It is fine if the expected mode is fast-path and we fail 1262 * to enable fastpath on this stream. 1263 */ 1264 if (expected_mode == SOFTMAC_SLOWPATH) 1265 softmac_fastpath_tear(sup); 1266 else 1267 (void) softmac_fastpath_setup(sup); 1268 } 1269