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