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