19073SCathy.Zhou@Sun.COM /* 29073SCathy.Zhou@Sun.COM * CDDL HEADER START 39073SCathy.Zhou@Sun.COM * 49073SCathy.Zhou@Sun.COM * The contents of this file are subject to the terms of the 59073SCathy.Zhou@Sun.COM * Common Development and Distribution License (the "License"). 69073SCathy.Zhou@Sun.COM * You may not use this file except in compliance with the License. 79073SCathy.Zhou@Sun.COM * 89073SCathy.Zhou@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 99073SCathy.Zhou@Sun.COM * or http://www.opensolaris.org/os/licensing. 109073SCathy.Zhou@Sun.COM * See the License for the specific language governing permissions 119073SCathy.Zhou@Sun.COM * and limitations under the License. 129073SCathy.Zhou@Sun.COM * 139073SCathy.Zhou@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 149073SCathy.Zhou@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 159073SCathy.Zhou@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 169073SCathy.Zhou@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 179073SCathy.Zhou@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 189073SCathy.Zhou@Sun.COM * 199073SCathy.Zhou@Sun.COM * CDDL HEADER END 209073SCathy.Zhou@Sun.COM */ 219073SCathy.Zhou@Sun.COM /* 229073SCathy.Zhou@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 239073SCathy.Zhou@Sun.COM * Use is subject to license terms. 249073SCathy.Zhou@Sun.COM */ 259073SCathy.Zhou@Sun.COM 269073SCathy.Zhou@Sun.COM /* 279073SCathy.Zhou@Sun.COM * Softmac data-path switching: 289073SCathy.Zhou@Sun.COM * 299073SCathy.Zhou@Sun.COM * - Fast-path model 309073SCathy.Zhou@Sun.COM * 319073SCathy.Zhou@Sun.COM * When the softmac fast-path is used, a dedicated lower-stream 329073SCathy.Zhou@Sun.COM * will be opened over the legacy device for each IP/ARP (upper-)stream 339073SCathy.Zhou@Sun.COM * over the softMAC, and all DLPI messages (including control messages 349073SCathy.Zhou@Sun.COM * and data messages) will be exchanged between the upper-stream and 359073SCathy.Zhou@Sun.COM * the corresponding lower-stream directly. Therefore, the data 369073SCathy.Zhou@Sun.COM * demultiplexing, filtering and classification processing will be done 379073SCathy.Zhou@Sun.COM * by the lower-stream, and the GLDv3 DLS/MAC layer processing will be 389073SCathy.Zhou@Sun.COM * no longer needed. 399073SCathy.Zhou@Sun.COM * 409073SCathy.Zhou@Sun.COM * - Slow-path model 419073SCathy.Zhou@Sun.COM * 429073SCathy.Zhou@Sun.COM * Some GLDv3 features requires the GLDv3 DLS/MAC layer processing to 439073SCathy.Zhou@Sun.COM * not be bypassed to assure its function correctness. For example, 449073SCathy.Zhou@Sun.COM * softmac fast-path must be disabled to support GLDv3 VNIC functionality. 459073SCathy.Zhou@Sun.COM * In this case, a shared lower-stream will be opened over the legacy 469073SCathy.Zhou@Sun.COM * device, which is responsible for implementing the GLDv3 callbacks 479073SCathy.Zhou@Sun.COM * and passing RAW data messages between the legacy devices and the GLDv3 489073SCathy.Zhou@Sun.COM * framework. 499073SCathy.Zhou@Sun.COM * 509073SCathy.Zhou@Sun.COM * By default, the softmac fast-path mode will be used to assure the 519073SCathy.Zhou@Sun.COM * performance; MAC clients will be able to request to disable the softmac 529073SCathy.Zhou@Sun.COM * fast-path mode to support certain features, and if that succeeds, 539073SCathy.Zhou@Sun.COM * the system will fallback to the slow-path softmac data-path model. 549073SCathy.Zhou@Sun.COM * 559073SCathy.Zhou@Sun.COM * 569073SCathy.Zhou@Sun.COM * The details of the softmac data fast-path model is stated as below 579073SCathy.Zhou@Sun.COM * 589073SCathy.Zhou@Sun.COM * 1. When a stream is opened on a softMAC, the softmac module will takes 599073SCathy.Zhou@Sun.COM * over the DLPI processing on this stream; 609073SCathy.Zhou@Sun.COM * 619073SCathy.Zhou@Sun.COM * 2. For IP/ARP streams over a softMAC, softmac data fast-path will be 629073SCathy.Zhou@Sun.COM * used by default, unless fast-path is disabled by any MAC client 639073SCathy.Zhou@Sun.COM * explicitly. The softmac module first identifies an IP/ARP stream 649073SCathy.Zhou@Sun.COM * by seeing whether there is a SIOCSLIFNAME ioctl sent from upstream, 659073SCathy.Zhou@Sun.COM * if there is one, this stream is either an IP or an ARP stream 669073SCathy.Zhou@Sun.COM * and will use fast-path potentially; 679073SCathy.Zhou@Sun.COM * 689073SCathy.Zhou@Sun.COM * 3. When the softmac fast-path is used, an dedicated lower-stream will 699073SCathy.Zhou@Sun.COM * be setup for each IP/ARP stream (1-1 mapping). From that point on, 709073SCathy.Zhou@Sun.COM * all control and data messages will be exchanged between the IP/ARP 719073SCathy.Zhou@Sun.COM * upper-stream and the legacy device through this dedicated 729073SCathy.Zhou@Sun.COM * lower-stream. As a result, the DLS/MAC layer processing in GLDv3 739073SCathy.Zhou@Sun.COM * will be skipped, and this greatly improves the performance; 749073SCathy.Zhou@Sun.COM * 759073SCathy.Zhou@Sun.COM * 4. When the softmac data fast-path is disabled by a MAC client (e.g., 769073SCathy.Zhou@Sun.COM * by a VNIC), all the IP/ARP upper streams will try to switch from 779073SCathy.Zhou@Sun.COM * the fast-path to the slow-path. The dedicated lower-stream will be 789073SCathy.Zhou@Sun.COM * destroyed, and all the control and data-messages will go through the 799073SCathy.Zhou@Sun.COM * existing GLDv3 code path and (in the end) the shared lower-stream; 809073SCathy.Zhou@Sun.COM * 819073SCathy.Zhou@Sun.COM * 5. On the other hand, when the last MAC client cancels its fast-path 829073SCathy.Zhou@Sun.COM * disable request, all the IP/ARP streams will try to switch back to 839073SCathy.Zhou@Sun.COM * the fast-path mode; 849073SCathy.Zhou@Sun.COM * 859073SCathy.Zhou@Sun.COM * Step 5 and 6 both rely on the data-path mode switching process 869073SCathy.Zhou@Sun.COM * described below: 879073SCathy.Zhou@Sun.COM * 889073SCathy.Zhou@Sun.COM * 1) To switch the softmac data-path mode (between fast-path and slow-path), 899073SCathy.Zhou@Sun.COM * softmac will first send a DL_NOTE_REPLUMB DL_NOTIFY_IND message 909073SCathy.Zhou@Sun.COM * upstream over each IP/ARP streams that needs data-path mode switching; 919073SCathy.Zhou@Sun.COM * 929073SCathy.Zhou@Sun.COM * 2) When IP receives this DL_NOTE_REPLUMB message, it will bring down 939073SCathy.Zhou@Sun.COM * all the IP interfaces on the corresponding ill (IP Lower level 949073SCathy.Zhou@Sun.COM * structure), and bring up those interfaces over again; this will in 959073SCathy.Zhou@Sun.COM * turn cause the ARP to "replumb" the interface. 969073SCathy.Zhou@Sun.COM * 979073SCathy.Zhou@Sun.COM * During the replumb process, both IP and ARP will send downstream the 989073SCathy.Zhou@Sun.COM * necessary DL_DISABMULTI_REQ and DL_UNBIND_REQ messages and cleanup 999073SCathy.Zhou@Sun.COM * the old state of the underlying softMAC, following with the necessary 1009073SCathy.Zhou@Sun.COM * DL_BIND_REQ and DL_ENABMULTI_REQ messages to setup the new state. 1019073SCathy.Zhou@Sun.COM * Between the cleanup and re-setup process, IP/ARP will also send down 1029073SCathy.Zhou@Sun.COM * a DL_NOTE_REPLUMB_DONE DL_NOTIFY_CONF messages to the softMAC to 1039073SCathy.Zhou@Sun.COM * indicate the *switching point*; 1049073SCathy.Zhou@Sun.COM * 1059073SCathy.Zhou@Sun.COM * 3) When softmac receives the DL_NOTE_REPLUMB_DONE message, it either 1069073SCathy.Zhou@Sun.COM * creates or destroys the dedicated lower-stream (depending on which 1079073SCathy.Zhou@Sun.COM * data-path mode the softMAC switches to), and change the softmac 1089073SCathy.Zhou@Sun.COM * data-path mode. From then on, softmac will process all the succeeding 1099073SCathy.Zhou@Sun.COM * control messages (including the DL_BIND_REQ and DL_ENABMULTI_REQ 1109073SCathy.Zhou@Sun.COM * messages) and data messages based on new data-path mode. 1119073SCathy.Zhou@Sun.COM */ 1129073SCathy.Zhou@Sun.COM 1139073SCathy.Zhou@Sun.COM #include <sys/types.h> 1149073SCathy.Zhou@Sun.COM #include <sys/disp.h> 1159073SCathy.Zhou@Sun.COM #include <sys/callb.h> 1169073SCathy.Zhou@Sun.COM #include <sys/sysmacros.h> 1179073SCathy.Zhou@Sun.COM #include <sys/file.h> 1189073SCathy.Zhou@Sun.COM #include <sys/vlan.h> 1199073SCathy.Zhou@Sun.COM #include <sys/dld.h> 1209073SCathy.Zhou@Sun.COM #include <sys/sockio.h> 1219073SCathy.Zhou@Sun.COM #include <sys/softmac_impl.h> 1229107Sjames.d.carlson@sun.com #include <net/if.h> 1239073SCathy.Zhou@Sun.COM 1249073SCathy.Zhou@Sun.COM static kmutex_t softmac_taskq_lock; 1259073SCathy.Zhou@Sun.COM static kcondvar_t softmac_taskq_cv; 1269073SCathy.Zhou@Sun.COM static list_t softmac_taskq_list; /* List of softmac_upper_t */ 1279073SCathy.Zhou@Sun.COM boolean_t softmac_taskq_quit; 1289073SCathy.Zhou@Sun.COM boolean_t softmac_taskq_done; 1299073SCathy.Zhou@Sun.COM 1309073SCathy.Zhou@Sun.COM static void softmac_taskq_dispatch(); 1319073SCathy.Zhou@Sun.COM static int softmac_fastpath_setup(softmac_upper_t *); 1329073SCathy.Zhou@Sun.COM static mac_tx_cookie_t softmac_fastpath_wput_data(softmac_upper_t *, mblk_t *, 1339073SCathy.Zhou@Sun.COM uintptr_t, uint16_t); 1349073SCathy.Zhou@Sun.COM static void softmac_datapath_switch_done(softmac_upper_t *); 1359073SCathy.Zhou@Sun.COM 1369073SCathy.Zhou@Sun.COM void 1379073SCathy.Zhou@Sun.COM softmac_fp_init() 1389073SCathy.Zhou@Sun.COM { 1399073SCathy.Zhou@Sun.COM mutex_init(&softmac_taskq_lock, NULL, MUTEX_DRIVER, NULL); 1409073SCathy.Zhou@Sun.COM cv_init(&softmac_taskq_cv, NULL, CV_DRIVER, NULL); 1419073SCathy.Zhou@Sun.COM 1429073SCathy.Zhou@Sun.COM softmac_taskq_quit = B_FALSE; 1439073SCathy.Zhou@Sun.COM softmac_taskq_done = B_FALSE; 1449073SCathy.Zhou@Sun.COM list_create(&softmac_taskq_list, sizeof (softmac_upper_t), 1459073SCathy.Zhou@Sun.COM offsetof(softmac_upper_t, su_taskq_list_node)); 1469073SCathy.Zhou@Sun.COM (void) thread_create(NULL, 0, softmac_taskq_dispatch, NULL, 0, 1479073SCathy.Zhou@Sun.COM &p0, TS_RUN, minclsyspri); 1489073SCathy.Zhou@Sun.COM } 1499073SCathy.Zhou@Sun.COM 1509073SCathy.Zhou@Sun.COM void 1519073SCathy.Zhou@Sun.COM softmac_fp_fini() 1529073SCathy.Zhou@Sun.COM { 1539073SCathy.Zhou@Sun.COM /* 1549073SCathy.Zhou@Sun.COM * Request the softmac_taskq thread to quit and wait for it to be done. 1559073SCathy.Zhou@Sun.COM */ 1569073SCathy.Zhou@Sun.COM mutex_enter(&softmac_taskq_lock); 1579073SCathy.Zhou@Sun.COM softmac_taskq_quit = B_TRUE; 1589073SCathy.Zhou@Sun.COM cv_signal(&softmac_taskq_cv); 1599073SCathy.Zhou@Sun.COM while (!softmac_taskq_done) 1609073SCathy.Zhou@Sun.COM cv_wait(&softmac_taskq_cv, &softmac_taskq_lock); 1619073SCathy.Zhou@Sun.COM mutex_exit(&softmac_taskq_lock); 1629073SCathy.Zhou@Sun.COM list_destroy(&softmac_taskq_list); 1639073SCathy.Zhou@Sun.COM 1649073SCathy.Zhou@Sun.COM mutex_destroy(&softmac_taskq_lock); 1659073SCathy.Zhou@Sun.COM cv_destroy(&softmac_taskq_cv); 1669073SCathy.Zhou@Sun.COM } 1679073SCathy.Zhou@Sun.COM 1689073SCathy.Zhou@Sun.COM static boolean_t 1699073SCathy.Zhou@Sun.COM check_ip_above(queue_t *q) 1709073SCathy.Zhou@Sun.COM { 1719073SCathy.Zhou@Sun.COM queue_t *next_q; 1729073SCathy.Zhou@Sun.COM boolean_t ret = B_TRUE; 1739073SCathy.Zhou@Sun.COM 1749073SCathy.Zhou@Sun.COM claimstr(q); 1759073SCathy.Zhou@Sun.COM next_q = q->q_next; 1769073SCathy.Zhou@Sun.COM if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0) 1779073SCathy.Zhou@Sun.COM ret = B_FALSE; 1789073SCathy.Zhou@Sun.COM releasestr(q); 1799073SCathy.Zhou@Sun.COM return (ret); 1809073SCathy.Zhou@Sun.COM } 1819073SCathy.Zhou@Sun.COM 1829073SCathy.Zhou@Sun.COM /* ARGSUSED */ 1839073SCathy.Zhou@Sun.COM static int 1849073SCathy.Zhou@Sun.COM softmac_capab_perim(softmac_upper_t *sup, void *data, uint_t flags) 1859073SCathy.Zhou@Sun.COM { 1869073SCathy.Zhou@Sun.COM switch (flags) { 1879073SCathy.Zhou@Sun.COM case DLD_ENABLE: 1889073SCathy.Zhou@Sun.COM mutex_enter(&sup->su_mutex); 1899073SCathy.Zhou@Sun.COM break; 1909073SCathy.Zhou@Sun.COM case DLD_DISABLE: 1919073SCathy.Zhou@Sun.COM mutex_exit(&sup->su_mutex); 1929073SCathy.Zhou@Sun.COM break; 1939073SCathy.Zhou@Sun.COM case DLD_QUERY: 1949073SCathy.Zhou@Sun.COM return (MUTEX_HELD(&sup->su_mutex)); 1959073SCathy.Zhou@Sun.COM } 1969073SCathy.Zhou@Sun.COM return (0); 1979073SCathy.Zhou@Sun.COM } 1989073SCathy.Zhou@Sun.COM 1999073SCathy.Zhou@Sun.COM /* ARGSUSED */ 2009073SCathy.Zhou@Sun.COM static mac_tx_notify_handle_t 2019073SCathy.Zhou@Sun.COM softmac_client_tx_notify(void *txcb, mac_tx_notify_t func, void *arg) 2029073SCathy.Zhou@Sun.COM { 2039073SCathy.Zhou@Sun.COM return (NULL); 2049073SCathy.Zhou@Sun.COM } 2059073SCathy.Zhou@Sun.COM 2069073SCathy.Zhou@Sun.COM static int 2079073SCathy.Zhou@Sun.COM softmac_capab_direct(softmac_upper_t *sup, void *data, uint_t flags) 2089073SCathy.Zhou@Sun.COM { 2099073SCathy.Zhou@Sun.COM dld_capab_direct_t *direct = data; 2109073SCathy.Zhou@Sun.COM softmac_lower_t *slp = sup->su_slp; 2119073SCathy.Zhou@Sun.COM 2129073SCathy.Zhou@Sun.COM ASSERT(MUTEX_HELD(&sup->su_mutex)); 2139073SCathy.Zhou@Sun.COM 2149073SCathy.Zhou@Sun.COM ASSERT(sup->su_mode == SOFTMAC_FASTPATH); 2159073SCathy.Zhou@Sun.COM 2169073SCathy.Zhou@Sun.COM switch (flags) { 2179073SCathy.Zhou@Sun.COM case DLD_ENABLE: 2189073SCathy.Zhou@Sun.COM if (sup->su_direct) 2199073SCathy.Zhou@Sun.COM return (0); 2209073SCathy.Zhou@Sun.COM 2219073SCathy.Zhou@Sun.COM sup->su_direct_rxinfo.slr_rx = (softmac_rx_t)direct->di_rx_cf; 2229073SCathy.Zhou@Sun.COM sup->su_direct_rxinfo.slr_arg = direct->di_rx_ch; 2239073SCathy.Zhou@Sun.COM slp->sl_rxinfo = &sup->su_direct_rxinfo; 2249073SCathy.Zhou@Sun.COM direct->di_tx_df = (uintptr_t)softmac_fastpath_wput_data; 2259073SCathy.Zhou@Sun.COM direct->di_tx_dh = sup; 2269073SCathy.Zhou@Sun.COM 2279073SCathy.Zhou@Sun.COM /* 2289073SCathy.Zhou@Sun.COM * We relying on the STREAM flow-control to backenable 2299073SCathy.Zhou@Sun.COM * the IP stream. Therefore, no notify callback needs to 2309073SCathy.Zhou@Sun.COM * be registered. But IP requires this to be a valid function 2319073SCathy.Zhou@Sun.COM * pointer. 2329073SCathy.Zhou@Sun.COM */ 2339073SCathy.Zhou@Sun.COM direct->di_tx_cb_df = (uintptr_t)softmac_client_tx_notify; 2349073SCathy.Zhou@Sun.COM direct->di_tx_cb_dh = NULL; 2359073SCathy.Zhou@Sun.COM sup->su_direct = B_TRUE; 2369073SCathy.Zhou@Sun.COM return (0); 2379073SCathy.Zhou@Sun.COM 2389073SCathy.Zhou@Sun.COM case DLD_DISABLE: 2399073SCathy.Zhou@Sun.COM if (!sup->su_direct) 2409073SCathy.Zhou@Sun.COM return (0); 2419073SCathy.Zhou@Sun.COM 2429073SCathy.Zhou@Sun.COM slp->sl_rxinfo = &sup->su_rxinfo; 2439073SCathy.Zhou@Sun.COM sup->su_direct = B_FALSE; 2449073SCathy.Zhou@Sun.COM return (0); 2459073SCathy.Zhou@Sun.COM } 2469073SCathy.Zhou@Sun.COM return (ENOTSUP); 2479073SCathy.Zhou@Sun.COM } 2489073SCathy.Zhou@Sun.COM 2499073SCathy.Zhou@Sun.COM static int 2509073SCathy.Zhou@Sun.COM softmac_dld_capab(softmac_upper_t *sup, uint_t type, void *data, uint_t flags) 2519073SCathy.Zhou@Sun.COM { 2529073SCathy.Zhou@Sun.COM int err; 2539073SCathy.Zhou@Sun.COM 2549073SCathy.Zhou@Sun.COM /* 2559073SCathy.Zhou@Sun.COM * Don't enable direct callback capabilities unless the caller is 2569073SCathy.Zhou@Sun.COM * the IP client. When a module is inserted in a stream (_I_INSERT) 2579073SCathy.Zhou@Sun.COM * the stack initiates capability disable, but due to races, the 2589073SCathy.Zhou@Sun.COM * module insertion may complete before the capability disable 2599073SCathy.Zhou@Sun.COM * completes. So we limit the check to DLD_ENABLE case. 2609073SCathy.Zhou@Sun.COM */ 2619073SCathy.Zhou@Sun.COM if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) && 2629073SCathy.Zhou@Sun.COM !check_ip_above(sup->su_rq)) { 2639073SCathy.Zhou@Sun.COM return (ENOTSUP); 2649073SCathy.Zhou@Sun.COM } 2659073SCathy.Zhou@Sun.COM 2669073SCathy.Zhou@Sun.COM switch (type) { 2679073SCathy.Zhou@Sun.COM case DLD_CAPAB_DIRECT: 2689073SCathy.Zhou@Sun.COM err = softmac_capab_direct(sup, data, flags); 2699073SCathy.Zhou@Sun.COM break; 2709073SCathy.Zhou@Sun.COM 2719073SCathy.Zhou@Sun.COM case DLD_CAPAB_PERIM: 2729073SCathy.Zhou@Sun.COM err = softmac_capab_perim(sup, data, flags); 2739073SCathy.Zhou@Sun.COM break; 2749073SCathy.Zhou@Sun.COM 2759073SCathy.Zhou@Sun.COM default: 2769073SCathy.Zhou@Sun.COM err = ENOTSUP; 2779073SCathy.Zhou@Sun.COM break; 2789073SCathy.Zhou@Sun.COM } 2799073SCathy.Zhou@Sun.COM return (err); 2809073SCathy.Zhou@Sun.COM } 2819073SCathy.Zhou@Sun.COM 2829073SCathy.Zhou@Sun.COM static void 2839073SCathy.Zhou@Sun.COM softmac_capability_advertise(softmac_upper_t *sup, mblk_t *mp) 2849073SCathy.Zhou@Sun.COM { 2859073SCathy.Zhou@Sun.COM dl_capability_ack_t *dlap; 2869073SCathy.Zhou@Sun.COM dl_capability_sub_t *dlsp; 2879073SCathy.Zhou@Sun.COM t_uscalar_t subsize; 2889073SCathy.Zhou@Sun.COM uint8_t *ptr; 2899073SCathy.Zhou@Sun.COM queue_t *q = sup->su_wq; 2909073SCathy.Zhou@Sun.COM mblk_t *mp1; 2919073SCathy.Zhou@Sun.COM softmac_t *softmac = sup->su_softmac; 2929073SCathy.Zhou@Sun.COM boolean_t dld_capable = B_FALSE; 2939073SCathy.Zhou@Sun.COM boolean_t hcksum_capable = B_FALSE; 2949073SCathy.Zhou@Sun.COM boolean_t zcopy_capable = B_FALSE; 2959073SCathy.Zhou@Sun.COM boolean_t mdt_capable = B_FALSE; 2969073SCathy.Zhou@Sun.COM 2979073SCathy.Zhou@Sun.COM ASSERT(sup->su_mode == SOFTMAC_FASTPATH); 2989073SCathy.Zhou@Sun.COM 2999073SCathy.Zhou@Sun.COM /* 3009073SCathy.Zhou@Sun.COM * Initially assume no capabilities. 3019073SCathy.Zhou@Sun.COM */ 3029073SCathy.Zhou@Sun.COM subsize = 0; 3039073SCathy.Zhou@Sun.COM 3049073SCathy.Zhou@Sun.COM /* 3059073SCathy.Zhou@Sun.COM * Direct capability negotiation interface between IP and softmac 3069073SCathy.Zhou@Sun.COM */ 3079073SCathy.Zhou@Sun.COM if (check_ip_above(sup->su_rq)) { 3089073SCathy.Zhou@Sun.COM dld_capable = B_TRUE; 3099073SCathy.Zhou@Sun.COM subsize += sizeof (dl_capability_sub_t) + 3109073SCathy.Zhou@Sun.COM sizeof (dl_capab_dld_t); 3119073SCathy.Zhou@Sun.COM } 3129073SCathy.Zhou@Sun.COM 3139073SCathy.Zhou@Sun.COM /* 3149073SCathy.Zhou@Sun.COM * Check if checksum offload is supported on this MAC. 3159073SCathy.Zhou@Sun.COM */ 3169073SCathy.Zhou@Sun.COM if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM) { 3179073SCathy.Zhou@Sun.COM hcksum_capable = B_TRUE; 3189073SCathy.Zhou@Sun.COM subsize += sizeof (dl_capability_sub_t) + 3199073SCathy.Zhou@Sun.COM sizeof (dl_capab_hcksum_t); 3209073SCathy.Zhou@Sun.COM } 3219073SCathy.Zhou@Sun.COM 3229073SCathy.Zhou@Sun.COM /* 3239073SCathy.Zhou@Sun.COM * Check if zerocopy is supported on this interface. 3249073SCathy.Zhou@Sun.COM */ 3259073SCathy.Zhou@Sun.COM if (!(softmac->smac_capab_flags & MAC_CAPAB_NO_ZCOPY)) { 3269073SCathy.Zhou@Sun.COM zcopy_capable = B_TRUE; 3279073SCathy.Zhou@Sun.COM subsize += sizeof (dl_capability_sub_t) + 3289073SCathy.Zhou@Sun.COM sizeof (dl_capab_zerocopy_t); 3299073SCathy.Zhou@Sun.COM } 3309073SCathy.Zhou@Sun.COM 3319073SCathy.Zhou@Sun.COM if (softmac->smac_mdt) { 3329073SCathy.Zhou@Sun.COM mdt_capable = B_TRUE; 3339073SCathy.Zhou@Sun.COM subsize += sizeof (dl_capability_sub_t) + 3349073SCathy.Zhou@Sun.COM sizeof (dl_capab_mdt_t); 3359073SCathy.Zhou@Sun.COM } 3369073SCathy.Zhou@Sun.COM 3379073SCathy.Zhou@Sun.COM /* 3389073SCathy.Zhou@Sun.COM * If there are no capabilities to advertise or if we 3399073SCathy.Zhou@Sun.COM * can't allocate a response, send a DL_ERROR_ACK. 3409073SCathy.Zhou@Sun.COM */ 3419073SCathy.Zhou@Sun.COM if ((subsize == 0) || (mp1 = reallocb(mp, 3429073SCathy.Zhou@Sun.COM sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) { 3439073SCathy.Zhou@Sun.COM dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0); 3449073SCathy.Zhou@Sun.COM return; 3459073SCathy.Zhou@Sun.COM } 3469073SCathy.Zhou@Sun.COM 3479073SCathy.Zhou@Sun.COM mp = mp1; 3489073SCathy.Zhou@Sun.COM DB_TYPE(mp) = M_PROTO; 3499073SCathy.Zhou@Sun.COM mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize; 3509073SCathy.Zhou@Sun.COM bzero(mp->b_rptr, MBLKL(mp)); 3519073SCathy.Zhou@Sun.COM dlap = (dl_capability_ack_t *)mp->b_rptr; 3529073SCathy.Zhou@Sun.COM dlap->dl_primitive = DL_CAPABILITY_ACK; 3539073SCathy.Zhou@Sun.COM dlap->dl_sub_offset = sizeof (dl_capability_ack_t); 3549073SCathy.Zhou@Sun.COM dlap->dl_sub_length = subsize; 3559073SCathy.Zhou@Sun.COM ptr = (uint8_t *)&dlap[1]; 3569073SCathy.Zhou@Sun.COM 3579073SCathy.Zhou@Sun.COM /* 3589073SCathy.Zhou@Sun.COM * IP polling interface. 3599073SCathy.Zhou@Sun.COM */ 3609073SCathy.Zhou@Sun.COM if (dld_capable) { 3619073SCathy.Zhou@Sun.COM dl_capab_dld_t dld; 3629073SCathy.Zhou@Sun.COM 3639073SCathy.Zhou@Sun.COM dlsp = (dl_capability_sub_t *)ptr; 3649073SCathy.Zhou@Sun.COM dlsp->dl_cap = DL_CAPAB_DLD; 3659073SCathy.Zhou@Sun.COM dlsp->dl_length = sizeof (dl_capab_dld_t); 3669073SCathy.Zhou@Sun.COM ptr += sizeof (dl_capability_sub_t); 3679073SCathy.Zhou@Sun.COM 3689073SCathy.Zhou@Sun.COM bzero(&dld, sizeof (dl_capab_dld_t)); 3699073SCathy.Zhou@Sun.COM dld.dld_version = DLD_CURRENT_VERSION; 3709073SCathy.Zhou@Sun.COM dld.dld_capab = (uintptr_t)softmac_dld_capab; 3719073SCathy.Zhou@Sun.COM dld.dld_capab_handle = (uintptr_t)sup; 3729073SCathy.Zhou@Sun.COM 3739073SCathy.Zhou@Sun.COM dlcapabsetqid(&(dld.dld_mid), sup->su_rq); 3749073SCathy.Zhou@Sun.COM bcopy(&dld, ptr, sizeof (dl_capab_dld_t)); 3759073SCathy.Zhou@Sun.COM ptr += sizeof (dl_capab_dld_t); 3769073SCathy.Zhou@Sun.COM } 3779073SCathy.Zhou@Sun.COM 3789073SCathy.Zhou@Sun.COM /* 3799073SCathy.Zhou@Sun.COM * TCP/IP checksum offload. 3809073SCathy.Zhou@Sun.COM */ 3819073SCathy.Zhou@Sun.COM if (hcksum_capable) { 3829073SCathy.Zhou@Sun.COM dl_capab_hcksum_t hcksum; 3839073SCathy.Zhou@Sun.COM 3849073SCathy.Zhou@Sun.COM dlsp = (dl_capability_sub_t *)ptr; 3859073SCathy.Zhou@Sun.COM 3869073SCathy.Zhou@Sun.COM dlsp->dl_cap = DL_CAPAB_HCKSUM; 3879073SCathy.Zhou@Sun.COM dlsp->dl_length = sizeof (dl_capab_hcksum_t); 3889073SCathy.Zhou@Sun.COM ptr += sizeof (dl_capability_sub_t); 3899073SCathy.Zhou@Sun.COM 3909073SCathy.Zhou@Sun.COM bzero(&hcksum, sizeof (dl_capab_hcksum_t)); 3919073SCathy.Zhou@Sun.COM hcksum.hcksum_version = HCKSUM_VERSION_1; 3929073SCathy.Zhou@Sun.COM hcksum.hcksum_txflags = softmac->smac_hcksum_txflags; 3939073SCathy.Zhou@Sun.COM dlcapabsetqid(&(hcksum.hcksum_mid), sup->su_rq); 3949073SCathy.Zhou@Sun.COM bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t)); 3959073SCathy.Zhou@Sun.COM ptr += sizeof (dl_capab_hcksum_t); 3969073SCathy.Zhou@Sun.COM } 3979073SCathy.Zhou@Sun.COM 3989073SCathy.Zhou@Sun.COM /* 3999073SCathy.Zhou@Sun.COM * Zero copy 4009073SCathy.Zhou@Sun.COM */ 4019073SCathy.Zhou@Sun.COM if (zcopy_capable) { 4029073SCathy.Zhou@Sun.COM dl_capab_zerocopy_t zcopy; 4039073SCathy.Zhou@Sun.COM 4049073SCathy.Zhou@Sun.COM dlsp = (dl_capability_sub_t *)ptr; 4059073SCathy.Zhou@Sun.COM 4069073SCathy.Zhou@Sun.COM dlsp->dl_cap = DL_CAPAB_ZEROCOPY; 4079073SCathy.Zhou@Sun.COM dlsp->dl_length = sizeof (dl_capab_zerocopy_t); 4089073SCathy.Zhou@Sun.COM ptr += sizeof (dl_capability_sub_t); 4099073SCathy.Zhou@Sun.COM 4109073SCathy.Zhou@Sun.COM bzero(&zcopy, sizeof (dl_capab_zerocopy_t)); 4119073SCathy.Zhou@Sun.COM zcopy.zerocopy_version = ZEROCOPY_VERSION_1; 4129073SCathy.Zhou@Sun.COM zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM; 4139073SCathy.Zhou@Sun.COM dlcapabsetqid(&(zcopy.zerocopy_mid), sup->su_rq); 4149073SCathy.Zhou@Sun.COM bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t)); 4159073SCathy.Zhou@Sun.COM ptr += sizeof (dl_capab_zerocopy_t); 4169073SCathy.Zhou@Sun.COM } 4179073SCathy.Zhou@Sun.COM 4189073SCathy.Zhou@Sun.COM /* 4199073SCathy.Zhou@Sun.COM * MDT 4209073SCathy.Zhou@Sun.COM */ 4219073SCathy.Zhou@Sun.COM if (mdt_capable) { 4229073SCathy.Zhou@Sun.COM dl_capab_mdt_t mdt; 4239073SCathy.Zhou@Sun.COM 4249073SCathy.Zhou@Sun.COM dlsp = (dl_capability_sub_t *)ptr; 4259073SCathy.Zhou@Sun.COM 4269073SCathy.Zhou@Sun.COM dlsp->dl_cap = DL_CAPAB_MDT; 4279073SCathy.Zhou@Sun.COM dlsp->dl_length = sizeof (dl_capab_mdt_t); 4289073SCathy.Zhou@Sun.COM ptr += sizeof (dl_capability_sub_t); 4299073SCathy.Zhou@Sun.COM 4309073SCathy.Zhou@Sun.COM bzero(&mdt, sizeof (dl_capab_mdt_t)); 4319073SCathy.Zhou@Sun.COM mdt.mdt_version = MDT_VERSION_2; 4329073SCathy.Zhou@Sun.COM mdt.mdt_flags = DL_CAPAB_MDT_ENABLE; 4339073SCathy.Zhou@Sun.COM mdt.mdt_hdr_head = softmac->smac_mdt_capab.mdt_hdr_head; 4349073SCathy.Zhou@Sun.COM mdt.mdt_hdr_tail = softmac->smac_mdt_capab.mdt_hdr_tail; 4359073SCathy.Zhou@Sun.COM mdt.mdt_max_pld = softmac->smac_mdt_capab.mdt_max_pld; 4369073SCathy.Zhou@Sun.COM mdt.mdt_span_limit = softmac->smac_mdt_capab.mdt_span_limit; 4379073SCathy.Zhou@Sun.COM dlcapabsetqid(&(mdt.mdt_mid), sup->su_rq); 4389073SCathy.Zhou@Sun.COM bcopy(&mdt, ptr, sizeof (dl_capab_mdt_t)); 4399073SCathy.Zhou@Sun.COM ptr += sizeof (dl_capab_mdt_t); 4409073SCathy.Zhou@Sun.COM } 4419073SCathy.Zhou@Sun.COM 4429073SCathy.Zhou@Sun.COM ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize); 4439073SCathy.Zhou@Sun.COM qreply(q, mp); 4449073SCathy.Zhou@Sun.COM } 4459073SCathy.Zhou@Sun.COM 4469073SCathy.Zhou@Sun.COM static void 4479073SCathy.Zhou@Sun.COM softmac_capability_req(softmac_upper_t *sup, mblk_t *mp) 4489073SCathy.Zhou@Sun.COM { 4499073SCathy.Zhou@Sun.COM dl_capability_req_t *dlp = (dl_capability_req_t *)mp->b_rptr; 4509073SCathy.Zhou@Sun.COM dl_capability_sub_t *sp; 4519073SCathy.Zhou@Sun.COM size_t size, len; 4529073SCathy.Zhou@Sun.COM offset_t off, end; 4539073SCathy.Zhou@Sun.COM t_uscalar_t dl_err; 4549073SCathy.Zhou@Sun.COM queue_t *q = sup->su_wq; 4559073SCathy.Zhou@Sun.COM 4569073SCathy.Zhou@Sun.COM ASSERT(sup->su_mode == SOFTMAC_FASTPATH); 4579073SCathy.Zhou@Sun.COM if (MBLKL(mp) < sizeof (dl_capability_req_t)) { 4589073SCathy.Zhou@Sun.COM dl_err = DL_BADPRIM; 4599073SCathy.Zhou@Sun.COM goto failed; 4609073SCathy.Zhou@Sun.COM } 4619073SCathy.Zhou@Sun.COM 4629073SCathy.Zhou@Sun.COM if (!sup->su_bound) { 4639073SCathy.Zhou@Sun.COM dl_err = DL_OUTSTATE; 4649073SCathy.Zhou@Sun.COM goto failed; 4659073SCathy.Zhou@Sun.COM } 4669073SCathy.Zhou@Sun.COM 4679073SCathy.Zhou@Sun.COM /* 4689073SCathy.Zhou@Sun.COM * This request is overloaded. If there are no requested capabilities 4699073SCathy.Zhou@Sun.COM * then we just want to acknowledge with all the capabilities we 4709073SCathy.Zhou@Sun.COM * support. Otherwise we enable the set of capabilities requested. 4719073SCathy.Zhou@Sun.COM */ 4729073SCathy.Zhou@Sun.COM if (dlp->dl_sub_length == 0) { 4739073SCathy.Zhou@Sun.COM softmac_capability_advertise(sup, mp); 4749073SCathy.Zhou@Sun.COM return; 4759073SCathy.Zhou@Sun.COM } 4769073SCathy.Zhou@Sun.COM 4779073SCathy.Zhou@Sun.COM if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) { 4789073SCathy.Zhou@Sun.COM dl_err = DL_BADPRIM; 4799073SCathy.Zhou@Sun.COM goto failed; 4809073SCathy.Zhou@Sun.COM } 4819073SCathy.Zhou@Sun.COM 4829073SCathy.Zhou@Sun.COM dlp->dl_primitive = DL_CAPABILITY_ACK; 4839073SCathy.Zhou@Sun.COM 4849073SCathy.Zhou@Sun.COM off = dlp->dl_sub_offset; 4859073SCathy.Zhou@Sun.COM len = dlp->dl_sub_length; 4869073SCathy.Zhou@Sun.COM 4879073SCathy.Zhou@Sun.COM /* 4889073SCathy.Zhou@Sun.COM * Walk the list of capabilities to be enabled. 4899073SCathy.Zhou@Sun.COM */ 4909073SCathy.Zhou@Sun.COM for (end = off + len; off < end; ) { 4919073SCathy.Zhou@Sun.COM sp = (dl_capability_sub_t *)(mp->b_rptr + off); 4929073SCathy.Zhou@Sun.COM size = sizeof (dl_capability_sub_t) + sp->dl_length; 4939073SCathy.Zhou@Sun.COM 4949073SCathy.Zhou@Sun.COM if (off + size > end || 4959073SCathy.Zhou@Sun.COM !IS_P2ALIGNED(off, sizeof (uint32_t))) { 4969073SCathy.Zhou@Sun.COM dl_err = DL_BADPRIM; 4979073SCathy.Zhou@Sun.COM goto failed; 4989073SCathy.Zhou@Sun.COM } 4999073SCathy.Zhou@Sun.COM 5009073SCathy.Zhou@Sun.COM switch (sp->dl_cap) { 5019073SCathy.Zhou@Sun.COM /* 5029073SCathy.Zhou@Sun.COM * TCP/IP checksum offload to hardware. 5039073SCathy.Zhou@Sun.COM */ 5049073SCathy.Zhou@Sun.COM case DL_CAPAB_HCKSUM: { 5059073SCathy.Zhou@Sun.COM dl_capab_hcksum_t *hcksump; 5069073SCathy.Zhou@Sun.COM dl_capab_hcksum_t hcksum; 5079073SCathy.Zhou@Sun.COM 5089073SCathy.Zhou@Sun.COM hcksump = (dl_capab_hcksum_t *)&sp[1]; 5099073SCathy.Zhou@Sun.COM /* 5109073SCathy.Zhou@Sun.COM * Copy for alignment. 5119073SCathy.Zhou@Sun.COM */ 5129073SCathy.Zhou@Sun.COM bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t)); 5139073SCathy.Zhou@Sun.COM dlcapabsetqid(&(hcksum.hcksum_mid), sup->su_rq); 5149073SCathy.Zhou@Sun.COM bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t)); 5159073SCathy.Zhou@Sun.COM break; 5169073SCathy.Zhou@Sun.COM } 5179073SCathy.Zhou@Sun.COM 5189073SCathy.Zhou@Sun.COM default: 5199073SCathy.Zhou@Sun.COM break; 5209073SCathy.Zhou@Sun.COM } 5219073SCathy.Zhou@Sun.COM 5229073SCathy.Zhou@Sun.COM off += size; 5239073SCathy.Zhou@Sun.COM } 5249073SCathy.Zhou@Sun.COM qreply(q, mp); 5259073SCathy.Zhou@Sun.COM return; 5269073SCathy.Zhou@Sun.COM failed: 5279073SCathy.Zhou@Sun.COM dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0); 5289073SCathy.Zhou@Sun.COM } 5299073SCathy.Zhou@Sun.COM 5309073SCathy.Zhou@Sun.COM static void 5319073SCathy.Zhou@Sun.COM softmac_bind_req(softmac_upper_t *sup, mblk_t *mp) 5329073SCathy.Zhou@Sun.COM { 5339073SCathy.Zhou@Sun.COM softmac_lower_t *slp = sup->su_slp; 5349073SCathy.Zhou@Sun.COM softmac_t *softmac = sup->su_softmac; 5359073SCathy.Zhou@Sun.COM mblk_t *ackmp, *mp1; 5369073SCathy.Zhou@Sun.COM int err; 5379073SCathy.Zhou@Sun.COM 5389073SCathy.Zhou@Sun.COM if (MBLKL(mp) < DL_BIND_REQ_SIZE) { 5399073SCathy.Zhou@Sun.COM freemsg(mp); 5409073SCathy.Zhou@Sun.COM return; 5419073SCathy.Zhou@Sun.COM } 5429073SCathy.Zhou@Sun.COM 5439073SCathy.Zhou@Sun.COM /* 5449073SCathy.Zhou@Sun.COM * Allocate ackmp incase the underlying driver does not ack timely. 5459073SCathy.Zhou@Sun.COM */ 5469073SCathy.Zhou@Sun.COM if ((mp1 = allocb(sizeof (dl_error_ack_t), BPRI_HI)) == NULL) { 5479073SCathy.Zhou@Sun.COM dlerrorack(sup->su_wq, mp, DL_BIND_REQ, DL_SYSERR, ENOMEM); 5489073SCathy.Zhou@Sun.COM return; 5499073SCathy.Zhou@Sun.COM } 5509073SCathy.Zhou@Sun.COM 5519073SCathy.Zhou@Sun.COM err = softmac_output(slp, mp, DL_BIND_REQ, DL_BIND_ACK, &ackmp); 5529073SCathy.Zhou@Sun.COM if (ackmp != NULL) { 5539073SCathy.Zhou@Sun.COM freemsg(mp1); 5549073SCathy.Zhou@Sun.COM } else { 5559073SCathy.Zhou@Sun.COM /* 5569073SCathy.Zhou@Sun.COM * The driver does not ack timely. 5579073SCathy.Zhou@Sun.COM */ 5589073SCathy.Zhou@Sun.COM ASSERT(err == ENOMSG); 5599073SCathy.Zhou@Sun.COM ackmp = mp1; 5609073SCathy.Zhou@Sun.COM } 5619073SCathy.Zhou@Sun.COM if (err != 0) 5629073SCathy.Zhou@Sun.COM goto failed; 5639073SCathy.Zhou@Sun.COM 5649073SCathy.Zhou@Sun.COM /* 5659073SCathy.Zhou@Sun.COM * Enable capabilities the underlying driver claims to support. 5669073SCathy.Zhou@Sun.COM */ 5679073SCathy.Zhou@Sun.COM if ((err = softmac_capab_enable(slp)) != 0) 5689073SCathy.Zhou@Sun.COM goto failed; 5699073SCathy.Zhou@Sun.COM 5709073SCathy.Zhou@Sun.COM /* 5719073SCathy.Zhou@Sun.COM * Check whether this softmac is already marked as exclusively used, 5729073SCathy.Zhou@Sun.COM * e.g., an aggregation is created over it. Fail the BIND_REQ if so. 5739073SCathy.Zhou@Sun.COM */ 5749073SCathy.Zhou@Sun.COM mutex_enter(&softmac->smac_active_mutex); 5759073SCathy.Zhou@Sun.COM if (softmac->smac_active) { 5769073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_active_mutex); 5779073SCathy.Zhou@Sun.COM err = EBUSY; 5789073SCathy.Zhou@Sun.COM goto failed; 5799073SCathy.Zhou@Sun.COM } 5809073SCathy.Zhou@Sun.COM softmac->smac_nactive++; 5819073SCathy.Zhou@Sun.COM sup->su_active = B_TRUE; 5829073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_active_mutex); 5839073SCathy.Zhou@Sun.COM sup->su_bound = B_TRUE; 5849073SCathy.Zhou@Sun.COM 5859073SCathy.Zhou@Sun.COM qreply(sup->su_wq, ackmp); 5869073SCathy.Zhou@Sun.COM return; 5879073SCathy.Zhou@Sun.COM failed: 5889073SCathy.Zhou@Sun.COM if (err != 0) { 5899073SCathy.Zhou@Sun.COM dlerrorack(sup->su_wq, ackmp, DL_BIND_REQ, DL_SYSERR, err); 5909073SCathy.Zhou@Sun.COM return; 5919073SCathy.Zhou@Sun.COM } 5929073SCathy.Zhou@Sun.COM } 5939073SCathy.Zhou@Sun.COM 5949073SCathy.Zhou@Sun.COM static void 5959073SCathy.Zhou@Sun.COM softmac_unbind_req(softmac_upper_t *sup, mblk_t *mp) 5969073SCathy.Zhou@Sun.COM { 5979073SCathy.Zhou@Sun.COM softmac_lower_t *slp = sup->su_slp; 5989073SCathy.Zhou@Sun.COM softmac_t *softmac = sup->su_softmac; 5999073SCathy.Zhou@Sun.COM mblk_t *ackmp, *mp1; 6009073SCathy.Zhou@Sun.COM int err; 6019073SCathy.Zhou@Sun.COM 6029073SCathy.Zhou@Sun.COM if (MBLKL(mp) < DL_UNBIND_REQ_SIZE) { 6039073SCathy.Zhou@Sun.COM freemsg(mp); 6049073SCathy.Zhou@Sun.COM return; 6059073SCathy.Zhou@Sun.COM } 6069073SCathy.Zhou@Sun.COM 6079073SCathy.Zhou@Sun.COM if (!sup->su_bound) { 6089073SCathy.Zhou@Sun.COM dlerrorack(sup->su_wq, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0); 6099073SCathy.Zhou@Sun.COM return; 6109073SCathy.Zhou@Sun.COM } 6119073SCathy.Zhou@Sun.COM 6129073SCathy.Zhou@Sun.COM /* 6139073SCathy.Zhou@Sun.COM * Allocate ackmp incase the underlying driver does not ack timely. 6149073SCathy.Zhou@Sun.COM */ 6159073SCathy.Zhou@Sun.COM if ((mp1 = allocb(sizeof (dl_error_ack_t), BPRI_HI)) == NULL) { 6169073SCathy.Zhou@Sun.COM dlerrorack(sup->su_wq, mp, DL_UNBIND_REQ, DL_SYSERR, ENOMEM); 6179073SCathy.Zhou@Sun.COM return; 6189073SCathy.Zhou@Sun.COM } 6199073SCathy.Zhou@Sun.COM 6209073SCathy.Zhou@Sun.COM err = softmac_output(slp, mp, DL_UNBIND_REQ, DL_OK_ACK, &ackmp); 6219073SCathy.Zhou@Sun.COM if (ackmp != NULL) { 6229073SCathy.Zhou@Sun.COM freemsg(mp1); 6239073SCathy.Zhou@Sun.COM } else { 6249073SCathy.Zhou@Sun.COM /* 6259073SCathy.Zhou@Sun.COM * The driver does not ack timely. 6269073SCathy.Zhou@Sun.COM */ 6279073SCathy.Zhou@Sun.COM ASSERT(err == ENOMSG); 6289073SCathy.Zhou@Sun.COM ackmp = mp1; 6299073SCathy.Zhou@Sun.COM } 6309073SCathy.Zhou@Sun.COM if (err != 0) { 6319073SCathy.Zhou@Sun.COM dlerrorack(sup->su_wq, ackmp, DL_UNBIND_REQ, DL_SYSERR, err); 6329073SCathy.Zhou@Sun.COM return; 6339073SCathy.Zhou@Sun.COM } 6349073SCathy.Zhou@Sun.COM 6359073SCathy.Zhou@Sun.COM sup->su_bound = B_FALSE; 6369073SCathy.Zhou@Sun.COM 6379073SCathy.Zhou@Sun.COM mutex_enter(&softmac->smac_active_mutex); 6389073SCathy.Zhou@Sun.COM if (sup->su_active) { 6399073SCathy.Zhou@Sun.COM ASSERT(!softmac->smac_active); 6409073SCathy.Zhou@Sun.COM softmac->smac_nactive--; 6419073SCathy.Zhou@Sun.COM sup->su_active = B_FALSE; 6429073SCathy.Zhou@Sun.COM } 6439073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_active_mutex); 6449073SCathy.Zhou@Sun.COM 6459073SCathy.Zhou@Sun.COM done: 6469073SCathy.Zhou@Sun.COM qreply(sup->su_wq, ackmp); 6479073SCathy.Zhou@Sun.COM } 6489073SCathy.Zhou@Sun.COM 6499073SCathy.Zhou@Sun.COM /* 6509073SCathy.Zhou@Sun.COM * Process the non-data mblk. 6519073SCathy.Zhou@Sun.COM */ 6529073SCathy.Zhou@Sun.COM static void 6539073SCathy.Zhou@Sun.COM softmac_wput_single_nondata(softmac_upper_t *sup, mblk_t *mp) 6549073SCathy.Zhou@Sun.COM { 6559073SCathy.Zhou@Sun.COM softmac_t *softmac = sup->su_softmac; 6569073SCathy.Zhou@Sun.COM softmac_lower_t *slp = sup->su_slp; 6579073SCathy.Zhou@Sun.COM unsigned char dbtype; 6589073SCathy.Zhou@Sun.COM t_uscalar_t prim; 6599073SCathy.Zhou@Sun.COM 6609073SCathy.Zhou@Sun.COM dbtype = DB_TYPE(mp); 6619073SCathy.Zhou@Sun.COM switch (dbtype) { 6629073SCathy.Zhou@Sun.COM case M_IOCTL: 6639073SCathy.Zhou@Sun.COM case M_CTL: { 6649073SCathy.Zhou@Sun.COM uint32_t expected_mode; 6659073SCathy.Zhou@Sun.COM 6669073SCathy.Zhou@Sun.COM if (((struct iocblk *)(mp->b_rptr))->ioc_cmd != SIOCSLIFNAME) 6679073SCathy.Zhou@Sun.COM break; 6689073SCathy.Zhou@Sun.COM 6699073SCathy.Zhou@Sun.COM /* 6709073SCathy.Zhou@Sun.COM * Nak the M_IOCTL based on the STREAMS specification. 6719073SCathy.Zhou@Sun.COM */ 6729073SCathy.Zhou@Sun.COM if (dbtype == M_IOCTL) 6739073SCathy.Zhou@Sun.COM miocnak(sup->su_wq, mp, 0, EINVAL); 674*9682SCathy.Zhou@Sun.COM else 675*9682SCathy.Zhou@Sun.COM freemsg(mp); 6769073SCathy.Zhou@Sun.COM 6779073SCathy.Zhou@Sun.COM /* 6789073SCathy.Zhou@Sun.COM * This stream is either IP or ARP. See whether 6799073SCathy.Zhou@Sun.COM * we need to setup a dedicated-lower-stream for it. 6809073SCathy.Zhou@Sun.COM */ 6819073SCathy.Zhou@Sun.COM mutex_enter(&softmac->smac_fp_mutex); 6829073SCathy.Zhou@Sun.COM 6839073SCathy.Zhou@Sun.COM expected_mode = DATAPATH_MODE(softmac); 6849073SCathy.Zhou@Sun.COM if (expected_mode == SOFTMAC_SLOWPATH) 6859073SCathy.Zhou@Sun.COM sup->su_mode = SOFTMAC_SLOWPATH; 6869073SCathy.Zhou@Sun.COM list_insert_head(&softmac->smac_sup_list, sup); 6879073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_fp_mutex); 6889073SCathy.Zhou@Sun.COM 6899073SCathy.Zhou@Sun.COM /* 6909073SCathy.Zhou@Sun.COM * Setup the fast-path dedicated lower stream if fast-path 6919073SCathy.Zhou@Sun.COM * is expected. Note that no lock is held here, and if 6929073SCathy.Zhou@Sun.COM * smac_expected_mode is changed from SOFTMAC_FASTPATH to 6939073SCathy.Zhou@Sun.COM * SOFTMAC_SLOWPATH, the DL_NOTE_REPLUMB message used for 6949073SCathy.Zhou@Sun.COM * data-path switching would already be queued and will 6959073SCathy.Zhou@Sun.COM * be processed by softmac_wput_single_nondata() later. 6969073SCathy.Zhou@Sun.COM */ 6979073SCathy.Zhou@Sun.COM if (expected_mode == SOFTMAC_FASTPATH) 6989073SCathy.Zhou@Sun.COM (void) softmac_fastpath_setup(sup); 6999073SCathy.Zhou@Sun.COM return; 7009073SCathy.Zhou@Sun.COM } 7019073SCathy.Zhou@Sun.COM case M_PROTO: 7029073SCathy.Zhou@Sun.COM case M_PCPROTO: 7039073SCathy.Zhou@Sun.COM if (MBLKL(mp) < sizeof (t_uscalar_t)) { 7049073SCathy.Zhou@Sun.COM freemsg(mp); 7059073SCathy.Zhou@Sun.COM return; 7069073SCathy.Zhou@Sun.COM } 7079073SCathy.Zhou@Sun.COM prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; 7089073SCathy.Zhou@Sun.COM switch (prim) { 7099073SCathy.Zhou@Sun.COM case DL_NOTIFY_IND: 7109073SCathy.Zhou@Sun.COM if (MBLKL(mp) < sizeof (dl_notify_ind_t) || 7119073SCathy.Zhou@Sun.COM ((dl_notify_ind_t *)mp->b_rptr)->dl_notification != 7129073SCathy.Zhou@Sun.COM DL_NOTE_REPLUMB) { 7139073SCathy.Zhou@Sun.COM freemsg(mp); 7149073SCathy.Zhou@Sun.COM return; 7159073SCathy.Zhou@Sun.COM } 7169073SCathy.Zhou@Sun.COM /* 7179073SCathy.Zhou@Sun.COM * This DL_NOTE_REPLUMB message is initiated 7189073SCathy.Zhou@Sun.COM * and queued by the softmac itself, when the 7199073SCathy.Zhou@Sun.COM * sup is trying to switching its datapath mode 7209073SCathy.Zhou@Sun.COM * between SOFTMAC_SLOWPATH and SOFTMAC_FASTPATH. 7219073SCathy.Zhou@Sun.COM * Send this message upstream. 7229073SCathy.Zhou@Sun.COM */ 7239073SCathy.Zhou@Sun.COM qreply(sup->su_wq, mp); 7249073SCathy.Zhou@Sun.COM return; 7259073SCathy.Zhou@Sun.COM case DL_NOTIFY_CONF: 7269073SCathy.Zhou@Sun.COM if (MBLKL(mp) < sizeof (dl_notify_conf_t) || 7279073SCathy.Zhou@Sun.COM ((dl_notify_conf_t *)mp->b_rptr)->dl_notification != 7289073SCathy.Zhou@Sun.COM DL_NOTE_REPLUMB_DONE) { 7299073SCathy.Zhou@Sun.COM freemsg(mp); 7309073SCathy.Zhou@Sun.COM return; 7319073SCathy.Zhou@Sun.COM } 7329073SCathy.Zhou@Sun.COM /* 7339073SCathy.Zhou@Sun.COM * This is an indication from IP/ARP that the 7349073SCathy.Zhou@Sun.COM * fastpath->slowpath switch is done. 7359073SCathy.Zhou@Sun.COM */ 7369073SCathy.Zhou@Sun.COM freemsg(mp); 7379073SCathy.Zhou@Sun.COM softmac_datapath_switch_done(sup); 7389073SCathy.Zhou@Sun.COM return; 7399073SCathy.Zhou@Sun.COM } 7409073SCathy.Zhou@Sun.COM break; 7419073SCathy.Zhou@Sun.COM } 7429073SCathy.Zhou@Sun.COM 7439073SCathy.Zhou@Sun.COM /* 7449073SCathy.Zhou@Sun.COM * No need to hold lock to check su_mode, since su_mode updating only 7459073SCathy.Zhou@Sun.COM * operation is is serialized by softmac_wput_nondata_task(). 7469073SCathy.Zhou@Sun.COM */ 7479073SCathy.Zhou@Sun.COM if (sup->su_mode != SOFTMAC_FASTPATH) { 7489073SCathy.Zhou@Sun.COM dld_wput(sup->su_wq, mp); 7499073SCathy.Zhou@Sun.COM return; 7509073SCathy.Zhou@Sun.COM } 7519073SCathy.Zhou@Sun.COM 7529073SCathy.Zhou@Sun.COM /* 7539073SCathy.Zhou@Sun.COM * Fastpath non-data message processing. Most of non-data messages 7549073SCathy.Zhou@Sun.COM * can be directly passed down to the dedicated-lower-stream, aside 7559073SCathy.Zhou@Sun.COM * from the following M_PROTO/M_PCPROTO messages. 7569073SCathy.Zhou@Sun.COM */ 7579073SCathy.Zhou@Sun.COM switch (dbtype) { 7589073SCathy.Zhou@Sun.COM case M_PROTO: 7599073SCathy.Zhou@Sun.COM case M_PCPROTO: 7609073SCathy.Zhou@Sun.COM switch (prim) { 7619073SCathy.Zhou@Sun.COM case DL_BIND_REQ: 7629073SCathy.Zhou@Sun.COM softmac_bind_req(sup, mp); 7639073SCathy.Zhou@Sun.COM break; 7649073SCathy.Zhou@Sun.COM case DL_UNBIND_REQ: 7659073SCathy.Zhou@Sun.COM softmac_unbind_req(sup, mp); 7669073SCathy.Zhou@Sun.COM break; 7679073SCathy.Zhou@Sun.COM case DL_CAPABILITY_REQ: 7689073SCathy.Zhou@Sun.COM softmac_capability_req(sup, mp); 7699073SCathy.Zhou@Sun.COM break; 7709073SCathy.Zhou@Sun.COM default: 7719073SCathy.Zhou@Sun.COM putnext(slp->sl_wq, mp); 7729073SCathy.Zhou@Sun.COM break; 7739073SCathy.Zhou@Sun.COM } 7749073SCathy.Zhou@Sun.COM break; 7759073SCathy.Zhou@Sun.COM default: 7769073SCathy.Zhou@Sun.COM putnext(slp->sl_wq, mp); 7779073SCathy.Zhou@Sun.COM break; 7789073SCathy.Zhou@Sun.COM } 7799073SCathy.Zhou@Sun.COM } 7809073SCathy.Zhou@Sun.COM 7819073SCathy.Zhou@Sun.COM /* 7829073SCathy.Zhou@Sun.COM * The worker thread which processes non-data messages. Note we only process 7839073SCathy.Zhou@Sun.COM * one message at one time in order to be able to "flush" the queued message 7849073SCathy.Zhou@Sun.COM * and serialize the processing. 7859073SCathy.Zhou@Sun.COM */ 7869073SCathy.Zhou@Sun.COM static void 7879073SCathy.Zhou@Sun.COM softmac_wput_nondata_task(void *arg) 7889073SCathy.Zhou@Sun.COM { 7899073SCathy.Zhou@Sun.COM softmac_upper_t *sup = arg; 7909073SCathy.Zhou@Sun.COM mblk_t *mp; 7919073SCathy.Zhou@Sun.COM 7929073SCathy.Zhou@Sun.COM mutex_enter(&sup->su_disp_mutex); 7939073SCathy.Zhou@Sun.COM 7949073SCathy.Zhou@Sun.COM while (sup->su_pending_head != NULL) { 7959073SCathy.Zhou@Sun.COM if (sup->su_closing) 7969073SCathy.Zhou@Sun.COM break; 7979073SCathy.Zhou@Sun.COM 7989073SCathy.Zhou@Sun.COM SOFTMAC_DQ_PENDING(sup, &mp); 7999073SCathy.Zhou@Sun.COM mutex_exit(&sup->su_disp_mutex); 8009073SCathy.Zhou@Sun.COM softmac_wput_single_nondata(sup, mp); 8019073SCathy.Zhou@Sun.COM mutex_enter(&sup->su_disp_mutex); 8029073SCathy.Zhou@Sun.COM } 8039073SCathy.Zhou@Sun.COM 8049073SCathy.Zhou@Sun.COM /* 8059073SCathy.Zhou@Sun.COM * If the stream is closing, flush all queued messages and inform 8069073SCathy.Zhou@Sun.COM * the stream to be closed. 8079073SCathy.Zhou@Sun.COM */ 8089073SCathy.Zhou@Sun.COM freemsgchain(sup->su_pending_head); 8099073SCathy.Zhou@Sun.COM sup->su_pending_head = sup->su_pending_tail = NULL; 8109073SCathy.Zhou@Sun.COM sup->su_dlpi_pending = B_FALSE; 8119073SCathy.Zhou@Sun.COM cv_signal(&sup->su_disp_cv); 8129073SCathy.Zhou@Sun.COM mutex_exit(&sup->su_disp_mutex); 8139073SCathy.Zhou@Sun.COM } 8149073SCathy.Zhou@Sun.COM 8159073SCathy.Zhou@Sun.COM /* 8169073SCathy.Zhou@Sun.COM * Kernel thread to handle taskq dispatch failures in softmac_wput_nondata(). 8179073SCathy.Zhou@Sun.COM * This thread is started when the softmac module is first loaded. 8189073SCathy.Zhou@Sun.COM */ 8199073SCathy.Zhou@Sun.COM static void 8209073SCathy.Zhou@Sun.COM softmac_taskq_dispatch(void) 8219073SCathy.Zhou@Sun.COM { 8229073SCathy.Zhou@Sun.COM callb_cpr_t cprinfo; 8239073SCathy.Zhou@Sun.COM softmac_upper_t *sup; 8249073SCathy.Zhou@Sun.COM 8259073SCathy.Zhou@Sun.COM CALLB_CPR_INIT(&cprinfo, &softmac_taskq_lock, callb_generic_cpr, 8269073SCathy.Zhou@Sun.COM "softmac_taskq_dispatch"); 8279073SCathy.Zhou@Sun.COM mutex_enter(&softmac_taskq_lock); 8289073SCathy.Zhou@Sun.COM 8299073SCathy.Zhou@Sun.COM while (!softmac_taskq_quit) { 8309073SCathy.Zhou@Sun.COM sup = list_head(&softmac_taskq_list); 8319073SCathy.Zhou@Sun.COM while (sup != NULL) { 8329073SCathy.Zhou@Sun.COM list_remove(&softmac_taskq_list, sup); 8339073SCathy.Zhou@Sun.COM sup->su_taskq_scheduled = B_FALSE; 8349073SCathy.Zhou@Sun.COM mutex_exit(&softmac_taskq_lock); 8359073SCathy.Zhou@Sun.COM VERIFY(taskq_dispatch(system_taskq, 8369073SCathy.Zhou@Sun.COM softmac_wput_nondata_task, sup, TQ_SLEEP) != NULL); 8379073SCathy.Zhou@Sun.COM mutex_enter(&softmac_taskq_lock); 8389073SCathy.Zhou@Sun.COM sup = list_head(&softmac_taskq_list); 8399073SCathy.Zhou@Sun.COM } 8409073SCathy.Zhou@Sun.COM 8419073SCathy.Zhou@Sun.COM CALLB_CPR_SAFE_BEGIN(&cprinfo); 8429073SCathy.Zhou@Sun.COM cv_wait(&softmac_taskq_cv, &softmac_taskq_lock); 8439073SCathy.Zhou@Sun.COM CALLB_CPR_SAFE_END(&cprinfo, &softmac_taskq_lock); 8449073SCathy.Zhou@Sun.COM } 8459073SCathy.Zhou@Sun.COM 8469073SCathy.Zhou@Sun.COM softmac_taskq_done = B_TRUE; 8479073SCathy.Zhou@Sun.COM cv_signal(&softmac_taskq_cv); 8489073SCathy.Zhou@Sun.COM CALLB_CPR_EXIT(&cprinfo); 8499073SCathy.Zhou@Sun.COM thread_exit(); 8509073SCathy.Zhou@Sun.COM } 8519073SCathy.Zhou@Sun.COM 8529073SCathy.Zhou@Sun.COM void 8539073SCathy.Zhou@Sun.COM softmac_wput_nondata(softmac_upper_t *sup, mblk_t *mp) 8549073SCathy.Zhou@Sun.COM { 8559073SCathy.Zhou@Sun.COM /* 8569073SCathy.Zhou@Sun.COM * The processing of the message might block. Enqueue the 8579073SCathy.Zhou@Sun.COM * message for later processing. 8589073SCathy.Zhou@Sun.COM */ 8599073SCathy.Zhou@Sun.COM mutex_enter(&sup->su_disp_mutex); 8609073SCathy.Zhou@Sun.COM 8619073SCathy.Zhou@Sun.COM if (sup->su_closing) { 8629073SCathy.Zhou@Sun.COM mutex_exit(&sup->su_disp_mutex); 8639073SCathy.Zhou@Sun.COM freemsg(mp); 8649073SCathy.Zhou@Sun.COM return; 8659073SCathy.Zhou@Sun.COM } 8669073SCathy.Zhou@Sun.COM 8679073SCathy.Zhou@Sun.COM SOFTMAC_EQ_PENDING(sup, mp); 8689073SCathy.Zhou@Sun.COM 8699073SCathy.Zhou@Sun.COM if (sup->su_dlpi_pending) { 8709073SCathy.Zhou@Sun.COM mutex_exit(&sup->su_disp_mutex); 8719073SCathy.Zhou@Sun.COM return; 8729073SCathy.Zhou@Sun.COM } 8739073SCathy.Zhou@Sun.COM sup->su_dlpi_pending = B_TRUE; 8749073SCathy.Zhou@Sun.COM mutex_exit(&sup->su_disp_mutex); 8759073SCathy.Zhou@Sun.COM 8769073SCathy.Zhou@Sun.COM if (taskq_dispatch(system_taskq, softmac_wput_nondata_task, 8779073SCathy.Zhou@Sun.COM sup, TQ_NOSLEEP) != NULL) { 8789073SCathy.Zhou@Sun.COM return; 8799073SCathy.Zhou@Sun.COM } 8809073SCathy.Zhou@Sun.COM 8819073SCathy.Zhou@Sun.COM mutex_enter(&softmac_taskq_lock); 8829073SCathy.Zhou@Sun.COM if (!sup->su_taskq_scheduled) { 8839073SCathy.Zhou@Sun.COM list_insert_tail(&softmac_taskq_list, sup); 8849073SCathy.Zhou@Sun.COM cv_signal(&softmac_taskq_cv); 8859073SCathy.Zhou@Sun.COM } 8869073SCathy.Zhou@Sun.COM sup->su_taskq_scheduled = B_TRUE; 8879073SCathy.Zhou@Sun.COM mutex_exit(&softmac_taskq_lock); 8889073SCathy.Zhou@Sun.COM } 8899073SCathy.Zhou@Sun.COM 8909073SCathy.Zhou@Sun.COM /* 8919073SCathy.Zhou@Sun.COM * Setup the dedicated-lower-stream (fast-path) for the IP/ARP upperstream. 8929073SCathy.Zhou@Sun.COM */ 8939073SCathy.Zhou@Sun.COM static int 8949073SCathy.Zhou@Sun.COM softmac_fastpath_setup(softmac_upper_t *sup) 8959073SCathy.Zhou@Sun.COM { 8969073SCathy.Zhou@Sun.COM softmac_t *softmac = sup->su_softmac; 8979073SCathy.Zhou@Sun.COM softmac_lower_t *slp; 8989073SCathy.Zhou@Sun.COM int err; 8999073SCathy.Zhou@Sun.COM 9009073SCathy.Zhou@Sun.COM err = softmac_lower_setup(softmac, sup, &slp); 9019073SCathy.Zhou@Sun.COM 9029073SCathy.Zhou@Sun.COM mutex_enter(&sup->su_mutex); 9039073SCathy.Zhou@Sun.COM /* 9049073SCathy.Zhou@Sun.COM * Wait for all data messages to be processed so that we can change 9059073SCathy.Zhou@Sun.COM * the su_mode. 9069073SCathy.Zhou@Sun.COM */ 9079073SCathy.Zhou@Sun.COM while (sup->su_tx_inprocess != 0) 9089073SCathy.Zhou@Sun.COM cv_wait(&sup->su_cv, &sup->su_mutex); 9099073SCathy.Zhou@Sun.COM 9109073SCathy.Zhou@Sun.COM ASSERT(sup->su_mode != SOFTMAC_FASTPATH); 9119073SCathy.Zhou@Sun.COM ASSERT(sup->su_slp == NULL); 9129073SCathy.Zhou@Sun.COM if (err != 0) { 9139073SCathy.Zhou@Sun.COM sup->su_mode = SOFTMAC_SLOWPATH; 9149073SCathy.Zhou@Sun.COM } else { 9159073SCathy.Zhou@Sun.COM sup->su_slp = slp; 9169073SCathy.Zhou@Sun.COM sup->su_mode = SOFTMAC_FASTPATH; 9179073SCathy.Zhou@Sun.COM } 9189073SCathy.Zhou@Sun.COM mutex_exit(&sup->su_mutex); 9199073SCathy.Zhou@Sun.COM return (err); 9209073SCathy.Zhou@Sun.COM } 9219073SCathy.Zhou@Sun.COM 9229073SCathy.Zhou@Sun.COM /* 9239073SCathy.Zhou@Sun.COM * Tear down the dedicated-lower-stream (fast-path) for the IP/ARP upperstream. 9249073SCathy.Zhou@Sun.COM */ 9259073SCathy.Zhou@Sun.COM static void 9269073SCathy.Zhou@Sun.COM softmac_fastpath_tear(softmac_upper_t *sup) 9279073SCathy.Zhou@Sun.COM { 9289073SCathy.Zhou@Sun.COM mutex_enter(&sup->su_mutex); 9299073SCathy.Zhou@Sun.COM /* 9309073SCathy.Zhou@Sun.COM * Wait for all data messages in the dedicated-lower-stream 9319073SCathy.Zhou@Sun.COM * to be processed. 9329073SCathy.Zhou@Sun.COM */ 9339073SCathy.Zhou@Sun.COM while (sup->su_tx_inprocess != 0) 9349073SCathy.Zhou@Sun.COM cv_wait(&sup->su_cv, &sup->su_mutex); 9359073SCathy.Zhou@Sun.COM 9369073SCathy.Zhou@Sun.COM if (sup->su_tx_busy) { 9379073SCathy.Zhou@Sun.COM ASSERT(sup->su_tx_flow_mp == NULL); 9389073SCathy.Zhou@Sun.COM sup->su_tx_flow_mp = getq(sup->su_wq); 9399073SCathy.Zhou@Sun.COM sup->su_tx_busy = B_FALSE; 9409073SCathy.Zhou@Sun.COM } 9419073SCathy.Zhou@Sun.COM 9429073SCathy.Zhou@Sun.COM sup->su_mode = SOFTMAC_SLOWPATH; 9439073SCathy.Zhou@Sun.COM 9449073SCathy.Zhou@Sun.COM /* 9459073SCathy.Zhou@Sun.COM * Destroy the dedicated-lower-stream. Note that slp is destroyed 9469073SCathy.Zhou@Sun.COM * when lh is closed. 9479073SCathy.Zhou@Sun.COM */ 9489073SCathy.Zhou@Sun.COM (void) ldi_close(sup->su_slp->sl_lh, FREAD|FWRITE, kcred); 9499073SCathy.Zhou@Sun.COM sup->su_slp = NULL; 9509073SCathy.Zhou@Sun.COM mutex_exit(&sup->su_mutex); 9519073SCathy.Zhou@Sun.COM } 9529073SCathy.Zhou@Sun.COM 9539073SCathy.Zhou@Sun.COM void 9549073SCathy.Zhou@Sun.COM softmac_wput_data(softmac_upper_t *sup, mblk_t *mp) 9559073SCathy.Zhou@Sun.COM { 9569073SCathy.Zhou@Sun.COM /* 9579073SCathy.Zhou@Sun.COM * No lock is required to access the su_mode field since the data 9589073SCathy.Zhou@Sun.COM * traffic is quiesce by IP when the data-path mode is in the 9599073SCathy.Zhou@Sun.COM * process of switching. 9609073SCathy.Zhou@Sun.COM */ 9619073SCathy.Zhou@Sun.COM if (sup->su_mode != SOFTMAC_FASTPATH) 9629073SCathy.Zhou@Sun.COM dld_wput(sup->su_wq, mp); 9639073SCathy.Zhou@Sun.COM else 9649073SCathy.Zhou@Sun.COM (void) softmac_fastpath_wput_data(sup, mp, NULL, 0); 9659073SCathy.Zhou@Sun.COM } 9669073SCathy.Zhou@Sun.COM 9679073SCathy.Zhou@Sun.COM /*ARGSUSED*/ 9689073SCathy.Zhou@Sun.COM static mac_tx_cookie_t 9699073SCathy.Zhou@Sun.COM softmac_fastpath_wput_data(softmac_upper_t *sup, mblk_t *mp, uintptr_t f_hint, 9709073SCathy.Zhou@Sun.COM uint16_t flag) 9719073SCathy.Zhou@Sun.COM { 9729073SCathy.Zhou@Sun.COM queue_t *wq = sup->su_slp->sl_wq; 9739073SCathy.Zhou@Sun.COM 9749073SCathy.Zhou@Sun.COM /* 9759073SCathy.Zhou@Sun.COM * This function is called from IP, only the MAC_DROP_ON_NO_DESC 9769073SCathy.Zhou@Sun.COM * flag can be specified. 9779073SCathy.Zhou@Sun.COM */ 9789073SCathy.Zhou@Sun.COM ASSERT((flag & ~MAC_DROP_ON_NO_DESC) == 0); 9799073SCathy.Zhou@Sun.COM ASSERT(mp->b_next == NULL); 9809073SCathy.Zhou@Sun.COM 9819073SCathy.Zhou@Sun.COM /* 9829073SCathy.Zhou@Sun.COM * Check wether the dedicated-lower-stream is able to handle more 9839073SCathy.Zhou@Sun.COM * messages, and enable the flow-control if it is not. 9849073SCathy.Zhou@Sun.COM * 9859073SCathy.Zhou@Sun.COM * Note that in order not to introduce any packet reordering, we 9869073SCathy.Zhou@Sun.COM * always send the message down to the dedicated-lower-stream: 9879073SCathy.Zhou@Sun.COM * 9889073SCathy.Zhou@Sun.COM * If the flow-control is already enabled, but we still get 9899073SCathy.Zhou@Sun.COM * the messages from the upper-stream, it means that the upper 9909073SCathy.Zhou@Sun.COM * stream does not respect STREAMS flow-control (e.g., TCP). Simply 9919073SCathy.Zhou@Sun.COM * pass the message down to the lower-stream in that case. 9929073SCathy.Zhou@Sun.COM */ 9939073SCathy.Zhou@Sun.COM if (SOFTMAC_CANPUTNEXT(wq)) { 9949073SCathy.Zhou@Sun.COM putnext(wq, mp); 9959073SCathy.Zhou@Sun.COM return (NULL); 9969073SCathy.Zhou@Sun.COM } 9979073SCathy.Zhou@Sun.COM 9989073SCathy.Zhou@Sun.COM if ((flag & MAC_DROP_ON_NO_DESC) != 0) { 9999073SCathy.Zhou@Sun.COM freemsg(mp); 10009073SCathy.Zhou@Sun.COM return ((mac_tx_cookie_t)wq); 10019073SCathy.Zhou@Sun.COM } 10029073SCathy.Zhou@Sun.COM 10039073SCathy.Zhou@Sun.COM if (sup->su_tx_busy) { 10049073SCathy.Zhou@Sun.COM putnext(wq, mp); 10059073SCathy.Zhou@Sun.COM return ((mac_tx_cookie_t)wq); 10069073SCathy.Zhou@Sun.COM } 10079073SCathy.Zhou@Sun.COM 10089073SCathy.Zhou@Sun.COM mutex_enter(&sup->su_mutex); 10099073SCathy.Zhou@Sun.COM if (!sup->su_tx_busy) { 10109073SCathy.Zhou@Sun.COM ASSERT(sup->su_tx_flow_mp != NULL); 10119073SCathy.Zhou@Sun.COM (void) putq(sup->su_wq, sup->su_tx_flow_mp); 10129073SCathy.Zhou@Sun.COM sup->su_tx_flow_mp = NULL; 10139073SCathy.Zhou@Sun.COM sup->su_tx_busy = B_TRUE; 10149073SCathy.Zhou@Sun.COM qenable(wq); 10159073SCathy.Zhou@Sun.COM } 10169073SCathy.Zhou@Sun.COM mutex_exit(&sup->su_mutex); 10179073SCathy.Zhou@Sun.COM putnext(wq, mp); 10189073SCathy.Zhou@Sun.COM return ((mac_tx_cookie_t)wq); 10199073SCathy.Zhou@Sun.COM } 10209073SCathy.Zhou@Sun.COM 10219073SCathy.Zhou@Sun.COM boolean_t 10229073SCathy.Zhou@Sun.COM softmac_active_set(void *arg) 10239073SCathy.Zhou@Sun.COM { 10249073SCathy.Zhou@Sun.COM softmac_t *softmac = arg; 10259073SCathy.Zhou@Sun.COM 10269073SCathy.Zhou@Sun.COM mutex_enter(&softmac->smac_active_mutex); 10279073SCathy.Zhou@Sun.COM if (softmac->smac_nactive != 0) { 10289073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_active_mutex); 10299073SCathy.Zhou@Sun.COM return (B_FALSE); 10309073SCathy.Zhou@Sun.COM } 10319073SCathy.Zhou@Sun.COM softmac->smac_active = B_TRUE; 10329073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_active_mutex); 10339073SCathy.Zhou@Sun.COM return (B_TRUE); 10349073SCathy.Zhou@Sun.COM } 10359073SCathy.Zhou@Sun.COM 10369073SCathy.Zhou@Sun.COM void 10379073SCathy.Zhou@Sun.COM softmac_active_clear(void *arg) 10389073SCathy.Zhou@Sun.COM { 10399073SCathy.Zhou@Sun.COM softmac_t *softmac = arg; 10409073SCathy.Zhou@Sun.COM 10419073SCathy.Zhou@Sun.COM mutex_enter(&softmac->smac_active_mutex); 10429073SCathy.Zhou@Sun.COM ASSERT(softmac->smac_active && (softmac->smac_nactive == 0)); 10439073SCathy.Zhou@Sun.COM softmac->smac_active = B_FALSE; 10449073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_active_mutex); 10459073SCathy.Zhou@Sun.COM } 10469073SCathy.Zhou@Sun.COM 10479073SCathy.Zhou@Sun.COM /* 10489073SCathy.Zhou@Sun.COM * Disable/reenable fastpath on given softmac. This request could come from a 10499073SCathy.Zhou@Sun.COM * MAC client or directly from administrators. 10509073SCathy.Zhou@Sun.COM */ 10519073SCathy.Zhou@Sun.COM int 10529073SCathy.Zhou@Sun.COM softmac_datapath_switch(softmac_t *softmac, boolean_t disable, boolean_t admin) 10539073SCathy.Zhou@Sun.COM { 10549073SCathy.Zhou@Sun.COM softmac_upper_t *sup; 10559073SCathy.Zhou@Sun.COM mblk_t *head = NULL, *tail = NULL, *mp; 10569073SCathy.Zhou@Sun.COM list_t reqlist; 10579073SCathy.Zhou@Sun.COM softmac_switch_req_t *req; 10589073SCathy.Zhou@Sun.COM uint32_t current_mode, expected_mode; 10599073SCathy.Zhou@Sun.COM int err = 0; 10609073SCathy.Zhou@Sun.COM 10619073SCathy.Zhou@Sun.COM mutex_enter(&softmac->smac_fp_mutex); 10629073SCathy.Zhou@Sun.COM 10639073SCathy.Zhou@Sun.COM current_mode = DATAPATH_MODE(softmac); 10649073SCathy.Zhou@Sun.COM if (admin) { 10659073SCathy.Zhou@Sun.COM if (softmac->smac_fastpath_admin_disabled == disable) { 10669073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_fp_mutex); 10679073SCathy.Zhou@Sun.COM return (0); 10689073SCathy.Zhou@Sun.COM } 10699073SCathy.Zhou@Sun.COM softmac->smac_fastpath_admin_disabled = disable; 10709073SCathy.Zhou@Sun.COM } else if (disable) { 10719073SCathy.Zhou@Sun.COM softmac->smac_fp_disable_clients++; 10729073SCathy.Zhou@Sun.COM } else { 10739073SCathy.Zhou@Sun.COM ASSERT(softmac->smac_fp_disable_clients != 0); 10749073SCathy.Zhou@Sun.COM softmac->smac_fp_disable_clients--; 10759073SCathy.Zhou@Sun.COM } 10769073SCathy.Zhou@Sun.COM 10779073SCathy.Zhou@Sun.COM expected_mode = DATAPATH_MODE(softmac); 10789073SCathy.Zhou@Sun.COM if (current_mode == expected_mode) { 10799073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_fp_mutex); 10809073SCathy.Zhou@Sun.COM return (0); 10819073SCathy.Zhou@Sun.COM } 10829073SCathy.Zhou@Sun.COM 10839073SCathy.Zhou@Sun.COM /* 10849073SCathy.Zhou@Sun.COM * The expected mode is different from whatever datapath mode 10859073SCathy.Zhou@Sun.COM * this softmac is expected from last request, enqueue the data-path 10869073SCathy.Zhou@Sun.COM * switch request. 10879073SCathy.Zhou@Sun.COM */ 10889073SCathy.Zhou@Sun.COM list_create(&reqlist, sizeof (softmac_switch_req_t), 10899073SCathy.Zhou@Sun.COM offsetof(softmac_switch_req_t, ssq_req_list_node)); 10909073SCathy.Zhou@Sun.COM 10919073SCathy.Zhou@Sun.COM /* 10929073SCathy.Zhou@Sun.COM * Allocate all DL_NOTIFY_IND messages and request structures that 10939073SCathy.Zhou@Sun.COM * are required to switch each IP/ARP stream to the expected mode. 10949073SCathy.Zhou@Sun.COM */ 10959073SCathy.Zhou@Sun.COM for (sup = list_head(&softmac->smac_sup_list); sup != NULL; 10969073SCathy.Zhou@Sun.COM sup = list_next(&softmac->smac_sup_list, sup)) { 10979073SCathy.Zhou@Sun.COM dl_notify_ind_t *dlip; 10989073SCathy.Zhou@Sun.COM 10999073SCathy.Zhou@Sun.COM req = kmem_alloc(sizeof (softmac_switch_req_t), KM_NOSLEEP); 11009073SCathy.Zhou@Sun.COM if (req == NULL) 11019073SCathy.Zhou@Sun.COM break; 11029073SCathy.Zhou@Sun.COM 11039073SCathy.Zhou@Sun.COM req->ssq_expected_mode = expected_mode; 11049073SCathy.Zhou@Sun.COM 11059073SCathy.Zhou@Sun.COM /* 11069073SCathy.Zhou@Sun.COM * Allocate the DL_NOTE_REPLUMB message. 11079073SCathy.Zhou@Sun.COM */ 11089073SCathy.Zhou@Sun.COM if ((mp = allocb(sizeof (dl_notify_ind_t), BPRI_LO)) == NULL) { 11099073SCathy.Zhou@Sun.COM kmem_free(req, sizeof (softmac_switch_req_t)); 11109073SCathy.Zhou@Sun.COM break; 11119073SCathy.Zhou@Sun.COM } 11129073SCathy.Zhou@Sun.COM 11139073SCathy.Zhou@Sun.COM list_insert_tail(&reqlist, req); 11149073SCathy.Zhou@Sun.COM 11159073SCathy.Zhou@Sun.COM mp->b_wptr = mp->b_rptr + sizeof (dl_notify_ind_t); 11169073SCathy.Zhou@Sun.COM mp->b_datap->db_type = M_PROTO; 11179073SCathy.Zhou@Sun.COM bzero(mp->b_rptr, sizeof (dl_notify_ind_t)); 11189073SCathy.Zhou@Sun.COM dlip = (dl_notify_ind_t *)mp->b_rptr; 11199073SCathy.Zhou@Sun.COM dlip->dl_primitive = DL_NOTIFY_IND; 11209073SCathy.Zhou@Sun.COM dlip->dl_notification = DL_NOTE_REPLUMB; 11219073SCathy.Zhou@Sun.COM if (head == NULL) { 11229073SCathy.Zhou@Sun.COM head = tail = mp; 11239073SCathy.Zhou@Sun.COM } else { 11249073SCathy.Zhou@Sun.COM tail->b_next = mp; 11259073SCathy.Zhou@Sun.COM tail = mp; 11269073SCathy.Zhou@Sun.COM } 11279073SCathy.Zhou@Sun.COM } 11289073SCathy.Zhou@Sun.COM 11299073SCathy.Zhou@Sun.COM /* 11309073SCathy.Zhou@Sun.COM * Note that it is fine if the expected data-path mode is fast-path 11319073SCathy.Zhou@Sun.COM * and some of streams fails to switch. Only return failure if we 11329073SCathy.Zhou@Sun.COM * are expected to switch to the slow-path. 11339073SCathy.Zhou@Sun.COM */ 11349073SCathy.Zhou@Sun.COM if (sup != NULL && expected_mode == SOFTMAC_SLOWPATH) { 11359073SCathy.Zhou@Sun.COM err = ENOMEM; 11369073SCathy.Zhou@Sun.COM goto fail; 11379073SCathy.Zhou@Sun.COM } 11389073SCathy.Zhou@Sun.COM 11399073SCathy.Zhou@Sun.COM /* 11409073SCathy.Zhou@Sun.COM * Start switching for each IP/ARP stream. The switching operation 11419073SCathy.Zhou@Sun.COM * will eventually succeed and there is no need to wait for it 11429073SCathy.Zhou@Sun.COM * to finish. 11439073SCathy.Zhou@Sun.COM */ 11449073SCathy.Zhou@Sun.COM for (sup = list_head(&softmac->smac_sup_list); sup != NULL; 11459073SCathy.Zhou@Sun.COM sup = list_next(&softmac->smac_sup_list, sup)) { 11469073SCathy.Zhou@Sun.COM mp = head->b_next; 11479073SCathy.Zhou@Sun.COM head->b_next = NULL; 11489073SCathy.Zhou@Sun.COM 11499073SCathy.Zhou@Sun.COM /* 11509073SCathy.Zhou@Sun.COM * Add the swtich request to the requests list of the stream. 11519073SCathy.Zhou@Sun.COM */ 11529073SCathy.Zhou@Sun.COM req = list_head(&reqlist); 11539073SCathy.Zhou@Sun.COM ASSERT(req != NULL); 11549073SCathy.Zhou@Sun.COM list_remove(&reqlist, req); 11559073SCathy.Zhou@Sun.COM list_insert_tail(&sup->su_req_list, req); 11569073SCathy.Zhou@Sun.COM softmac_wput_nondata(sup, head); 11579073SCathy.Zhou@Sun.COM head = mp; 11589073SCathy.Zhou@Sun.COM } 11599073SCathy.Zhou@Sun.COM 11609073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_fp_mutex); 11619073SCathy.Zhou@Sun.COM ASSERT(list_is_empty(&reqlist)); 11629073SCathy.Zhou@Sun.COM list_destroy(&reqlist); 11639073SCathy.Zhou@Sun.COM return (0); 11649073SCathy.Zhou@Sun.COM fail: 11659073SCathy.Zhou@Sun.COM if (admin) { 11669073SCathy.Zhou@Sun.COM softmac->smac_fastpath_admin_disabled = !disable; 11679073SCathy.Zhou@Sun.COM } else if (disable) { 11689073SCathy.Zhou@Sun.COM softmac->smac_fp_disable_clients--; 11699073SCathy.Zhou@Sun.COM } else { 11709073SCathy.Zhou@Sun.COM softmac->smac_fp_disable_clients++; 11719073SCathy.Zhou@Sun.COM } 11729073SCathy.Zhou@Sun.COM 11739073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_fp_mutex); 11749073SCathy.Zhou@Sun.COM while ((req = list_head(&reqlist)) != NULL) { 11759073SCathy.Zhou@Sun.COM list_remove(&reqlist, req); 11769073SCathy.Zhou@Sun.COM kmem_free(req, sizeof (softmac_switch_req_t)); 11779073SCathy.Zhou@Sun.COM } 11789073SCathy.Zhou@Sun.COM freemsgchain(head); 11799073SCathy.Zhou@Sun.COM list_destroy(&reqlist); 11809073SCathy.Zhou@Sun.COM return (err); 11819073SCathy.Zhou@Sun.COM } 11829073SCathy.Zhou@Sun.COM 11839073SCathy.Zhou@Sun.COM int 11849073SCathy.Zhou@Sun.COM softmac_fastpath_disable(void *arg) 11859073SCathy.Zhou@Sun.COM { 11869073SCathy.Zhou@Sun.COM return (softmac_datapath_switch((softmac_t *)arg, B_TRUE, B_FALSE)); 11879073SCathy.Zhou@Sun.COM } 11889073SCathy.Zhou@Sun.COM 11899073SCathy.Zhou@Sun.COM void 11909073SCathy.Zhou@Sun.COM softmac_fastpath_enable(void *arg) 11919073SCathy.Zhou@Sun.COM { 11929073SCathy.Zhou@Sun.COM VERIFY(softmac_datapath_switch((softmac_t *)arg, B_FALSE, 11939073SCathy.Zhou@Sun.COM B_FALSE) == 0); 11949073SCathy.Zhou@Sun.COM } 11959073SCathy.Zhou@Sun.COM 11969073SCathy.Zhou@Sun.COM void 11979073SCathy.Zhou@Sun.COM softmac_upperstream_close(softmac_upper_t *sup) 11989073SCathy.Zhou@Sun.COM { 11999073SCathy.Zhou@Sun.COM softmac_t *softmac = sup->su_softmac; 12009073SCathy.Zhou@Sun.COM softmac_switch_req_t *req; 12019073SCathy.Zhou@Sun.COM 12029073SCathy.Zhou@Sun.COM mutex_enter(&softmac->smac_fp_mutex); 12039073SCathy.Zhou@Sun.COM 12049073SCathy.Zhou@Sun.COM if (sup->su_mode == SOFTMAC_FASTPATH) 12059073SCathy.Zhou@Sun.COM softmac_fastpath_tear(sup); 12069073SCathy.Zhou@Sun.COM 12079073SCathy.Zhou@Sun.COM if (sup->su_mode != SOFTMAC_UNKNOWN) { 12089073SCathy.Zhou@Sun.COM list_remove(&softmac->smac_sup_list, sup); 12099073SCathy.Zhou@Sun.COM sup->su_mode = SOFTMAC_UNKNOWN; 12109073SCathy.Zhou@Sun.COM } 12119073SCathy.Zhou@Sun.COM 12129073SCathy.Zhou@Sun.COM /* 12139073SCathy.Zhou@Sun.COM * Cleanup all the switch requests queueed on this stream. 12149073SCathy.Zhou@Sun.COM */ 12159073SCathy.Zhou@Sun.COM while ((req = list_head(&sup->su_req_list)) != NULL) { 12169073SCathy.Zhou@Sun.COM list_remove(&sup->su_req_list, req); 12179073SCathy.Zhou@Sun.COM kmem_free(req, sizeof (softmac_switch_req_t)); 12189073SCathy.Zhou@Sun.COM } 12199073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_fp_mutex); 12209073SCathy.Zhou@Sun.COM } 12219073SCathy.Zhou@Sun.COM 12229073SCathy.Zhou@Sun.COM /* 12239073SCathy.Zhou@Sun.COM * Handle the DL_NOTE_REPLUMB_DONE indication from IP/ARP. Change the upper 12249073SCathy.Zhou@Sun.COM * stream from the fastpath mode to the slowpath mode. 12259073SCathy.Zhou@Sun.COM */ 12269073SCathy.Zhou@Sun.COM static void 12279073SCathy.Zhou@Sun.COM softmac_datapath_switch_done(softmac_upper_t *sup) 12289073SCathy.Zhou@Sun.COM { 12299073SCathy.Zhou@Sun.COM softmac_t *softmac = sup->su_softmac; 12309073SCathy.Zhou@Sun.COM softmac_switch_req_t *req; 12319073SCathy.Zhou@Sun.COM uint32_t expected_mode; 12329073SCathy.Zhou@Sun.COM 12339073SCathy.Zhou@Sun.COM mutex_enter(&softmac->smac_fp_mutex); 12349073SCathy.Zhou@Sun.COM req = list_head(&sup->su_req_list); 12359073SCathy.Zhou@Sun.COM list_remove(&sup->su_req_list, req); 12369073SCathy.Zhou@Sun.COM expected_mode = req->ssq_expected_mode; 12379073SCathy.Zhou@Sun.COM kmem_free(req, sizeof (softmac_switch_req_t)); 12389073SCathy.Zhou@Sun.COM 12399073SCathy.Zhou@Sun.COM if (expected_mode == sup->su_mode) { 12409073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_fp_mutex); 12419073SCathy.Zhou@Sun.COM return; 12429073SCathy.Zhou@Sun.COM } 12439073SCathy.Zhou@Sun.COM 12449073SCathy.Zhou@Sun.COM ASSERT(!sup->su_bound); 12459073SCathy.Zhou@Sun.COM mutex_exit(&softmac->smac_fp_mutex); 12469073SCathy.Zhou@Sun.COM 12479073SCathy.Zhou@Sun.COM /* 12489073SCathy.Zhou@Sun.COM * It is fine if the expected mode is fast-path and we fail 12499073SCathy.Zhou@Sun.COM * to enable fastpath on this stream. 12509073SCathy.Zhou@Sun.COM */ 12519073SCathy.Zhou@Sun.COM if (expected_mode == SOFTMAC_SLOWPATH) 12529073SCathy.Zhou@Sun.COM softmac_fastpath_tear(sup); 12539073SCathy.Zhou@Sun.COM else 12549073SCathy.Zhou@Sun.COM (void) softmac_fastpath_setup(sup); 12559073SCathy.Zhou@Sun.COM } 1256