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