xref: /onnv-gate/usr/src/uts/common/io/softmac/softmac_fp.c (revision 9682:6573382ca7cc)
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