xref: /onnv-gate/usr/src/uts/common/io/softmac/softmac_fp.c (revision 11042:2d6e217af1b4)
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
softmac_fp_init()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
softmac_fp_fini()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
check_ip_above(queue_t * q)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
softmac_capab_perim(softmac_upper_t * sup,void * data,uint_t flags)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 static mac_tx_notify_handle_t
softmac_client_tx_notify(softmac_upper_t * sup,mac_tx_notify_t func,void * arg)2009738SCathy.Zhou@Sun.COM softmac_client_tx_notify(softmac_upper_t *sup, mac_tx_notify_t func, void *arg)
2019073SCathy.Zhou@Sun.COM {
2029738SCathy.Zhou@Sun.COM 	ASSERT(MUTEX_HELD(&sup->su_mutex));
2039738SCathy.Zhou@Sun.COM 
2049738SCathy.Zhou@Sun.COM 	if (func != NULL) {
2059738SCathy.Zhou@Sun.COM 		sup->su_tx_notify_func = func;
2069738SCathy.Zhou@Sun.COM 		sup->su_tx_notify_arg = arg;
2079738SCathy.Zhou@Sun.COM 	} else {
2089738SCathy.Zhou@Sun.COM 		/*
2099738SCathy.Zhou@Sun.COM 		 * Wait for all tx_notify_func call to be done.
2109738SCathy.Zhou@Sun.COM 		 */
2119738SCathy.Zhou@Sun.COM 		while (sup->su_tx_inprocess != 0)
2129738SCathy.Zhou@Sun.COM 			cv_wait(&sup->su_cv, &sup->su_mutex);
2139738SCathy.Zhou@Sun.COM 
2149738SCathy.Zhou@Sun.COM 		sup->su_tx_notify_func = NULL;
2159738SCathy.Zhou@Sun.COM 		sup->su_tx_notify_arg = NULL;
2169738SCathy.Zhou@Sun.COM 	}
2179738SCathy.Zhou@Sun.COM 	return ((mac_tx_notify_handle_t)sup);
2189738SCathy.Zhou@Sun.COM }
2199738SCathy.Zhou@Sun.COM 
2209738SCathy.Zhou@Sun.COM static boolean_t
softmac_tx_is_flow_blocked(softmac_upper_t * sup,mac_tx_cookie_t cookie)2219738SCathy.Zhou@Sun.COM softmac_tx_is_flow_blocked(softmac_upper_t *sup, mac_tx_cookie_t cookie)
2229738SCathy.Zhou@Sun.COM {
2239738SCathy.Zhou@Sun.COM 	ASSERT(cookie == (mac_tx_cookie_t)sup);
2249738SCathy.Zhou@Sun.COM 	return (sup->su_tx_busy);
2259073SCathy.Zhou@Sun.COM }
2269073SCathy.Zhou@Sun.COM 
2279073SCathy.Zhou@Sun.COM static int
softmac_capab_direct(softmac_upper_t * sup,void * data,uint_t flags)2289073SCathy.Zhou@Sun.COM softmac_capab_direct(softmac_upper_t *sup, void *data, uint_t flags)
2299073SCathy.Zhou@Sun.COM {
2309073SCathy.Zhou@Sun.COM 	dld_capab_direct_t	*direct = data;
2319073SCathy.Zhou@Sun.COM 	softmac_lower_t		*slp = sup->su_slp;
2329073SCathy.Zhou@Sun.COM 
2339073SCathy.Zhou@Sun.COM 	ASSERT(MUTEX_HELD(&sup->su_mutex));
2349073SCathy.Zhou@Sun.COM 
2359073SCathy.Zhou@Sun.COM 	ASSERT(sup->su_mode == SOFTMAC_FASTPATH);
2369073SCathy.Zhou@Sun.COM 
2379073SCathy.Zhou@Sun.COM 	switch (flags) {
2389073SCathy.Zhou@Sun.COM 	case DLD_ENABLE:
2399073SCathy.Zhou@Sun.COM 		if (sup->su_direct)
2409073SCathy.Zhou@Sun.COM 			return (0);
2419073SCathy.Zhou@Sun.COM 
2429073SCathy.Zhou@Sun.COM 		sup->su_direct_rxinfo.slr_rx = (softmac_rx_t)direct->di_rx_cf;
2439073SCathy.Zhou@Sun.COM 		sup->su_direct_rxinfo.slr_arg = direct->di_rx_ch;
2449073SCathy.Zhou@Sun.COM 		slp->sl_rxinfo = &sup->su_direct_rxinfo;
2459073SCathy.Zhou@Sun.COM 		direct->di_tx_df = (uintptr_t)softmac_fastpath_wput_data;
2469073SCathy.Zhou@Sun.COM 		direct->di_tx_dh = sup;
2479738SCathy.Zhou@Sun.COM 		direct->di_tx_fctl_df = (uintptr_t)softmac_tx_is_flow_blocked;
2489738SCathy.Zhou@Sun.COM 		direct->di_tx_fctl_dh = sup;
2499073SCathy.Zhou@Sun.COM 		direct->di_tx_cb_df = (uintptr_t)softmac_client_tx_notify;
2509738SCathy.Zhou@Sun.COM 		direct->di_tx_cb_dh = sup;
2519073SCathy.Zhou@Sun.COM 		sup->su_direct = B_TRUE;
2529073SCathy.Zhou@Sun.COM 		return (0);
2539073SCathy.Zhou@Sun.COM 
2549073SCathy.Zhou@Sun.COM 	case DLD_DISABLE:
2559073SCathy.Zhou@Sun.COM 		if (!sup->su_direct)
2569073SCathy.Zhou@Sun.COM 			return (0);
2579073SCathy.Zhou@Sun.COM 
2589073SCathy.Zhou@Sun.COM 		slp->sl_rxinfo = &sup->su_rxinfo;
2599073SCathy.Zhou@Sun.COM 		sup->su_direct = B_FALSE;
2609073SCathy.Zhou@Sun.COM 		return (0);
2619073SCathy.Zhou@Sun.COM 	}
2629073SCathy.Zhou@Sun.COM 	return (ENOTSUP);
2639073SCathy.Zhou@Sun.COM }
2649073SCathy.Zhou@Sun.COM 
2659073SCathy.Zhou@Sun.COM static int
softmac_dld_capab(softmac_upper_t * sup,uint_t type,void * data,uint_t flags)2669073SCathy.Zhou@Sun.COM softmac_dld_capab(softmac_upper_t *sup, uint_t type, void *data, uint_t flags)
2679073SCathy.Zhou@Sun.COM {
2689073SCathy.Zhou@Sun.COM 	int	err;
2699073SCathy.Zhou@Sun.COM 
2709073SCathy.Zhou@Sun.COM 	/*
2719073SCathy.Zhou@Sun.COM 	 * Don't enable direct callback capabilities unless the caller is
2729073SCathy.Zhou@Sun.COM 	 * the IP client. When a module is inserted in a stream (_I_INSERT)
2739073SCathy.Zhou@Sun.COM 	 * the stack initiates capability disable, but due to races, the
2749073SCathy.Zhou@Sun.COM 	 * module insertion may complete before the capability disable
2759073SCathy.Zhou@Sun.COM 	 * completes. So we limit the check to DLD_ENABLE case.
2769073SCathy.Zhou@Sun.COM 	 */
2779073SCathy.Zhou@Sun.COM 	if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) &&
2789073SCathy.Zhou@Sun.COM 	    !check_ip_above(sup->su_rq)) {
2799073SCathy.Zhou@Sun.COM 		return (ENOTSUP);
2809073SCathy.Zhou@Sun.COM 	}
2819073SCathy.Zhou@Sun.COM 
2829073SCathy.Zhou@Sun.COM 	switch (type) {
2839073SCathy.Zhou@Sun.COM 	case DLD_CAPAB_DIRECT:
2849073SCathy.Zhou@Sun.COM 		err = softmac_capab_direct(sup, data, flags);
2859073SCathy.Zhou@Sun.COM 		break;
2869073SCathy.Zhou@Sun.COM 
2879073SCathy.Zhou@Sun.COM 	case DLD_CAPAB_PERIM:
2889073SCathy.Zhou@Sun.COM 		err = softmac_capab_perim(sup, data, flags);
2899073SCathy.Zhou@Sun.COM 		break;
2909073SCathy.Zhou@Sun.COM 
2919073SCathy.Zhou@Sun.COM 	default:
2929073SCathy.Zhou@Sun.COM 		err = ENOTSUP;
2939073SCathy.Zhou@Sun.COM 		break;
2949073SCathy.Zhou@Sun.COM 	}
2959073SCathy.Zhou@Sun.COM 	return (err);
2969073SCathy.Zhou@Sun.COM }
2979073SCathy.Zhou@Sun.COM 
2989073SCathy.Zhou@Sun.COM static void
softmac_capability_advertise(softmac_upper_t * sup,mblk_t * mp)2999073SCathy.Zhou@Sun.COM softmac_capability_advertise(softmac_upper_t *sup, mblk_t *mp)
3009073SCathy.Zhou@Sun.COM {
3019073SCathy.Zhou@Sun.COM 	dl_capability_ack_t	*dlap;
3029073SCathy.Zhou@Sun.COM 	dl_capability_sub_t	*dlsp;
3039073SCathy.Zhou@Sun.COM 	t_uscalar_t		subsize;
3049073SCathy.Zhou@Sun.COM 	uint8_t			*ptr;
3059073SCathy.Zhou@Sun.COM 	queue_t			*q = sup->su_wq;
3069073SCathy.Zhou@Sun.COM 	mblk_t			*mp1;
3079073SCathy.Zhou@Sun.COM 	softmac_t		*softmac = sup->su_softmac;
3089073SCathy.Zhou@Sun.COM 	boolean_t		dld_capable = B_FALSE;
3099073SCathy.Zhou@Sun.COM 	boolean_t		hcksum_capable = B_FALSE;
3109073SCathy.Zhou@Sun.COM 	boolean_t		zcopy_capable = B_FALSE;
3119073SCathy.Zhou@Sun.COM 	boolean_t		mdt_capable = B_FALSE;
3129073SCathy.Zhou@Sun.COM 
3139073SCathy.Zhou@Sun.COM 	ASSERT(sup->su_mode == SOFTMAC_FASTPATH);
3149073SCathy.Zhou@Sun.COM 
3159073SCathy.Zhou@Sun.COM 	/*
3169073SCathy.Zhou@Sun.COM 	 * Initially assume no capabilities.
3179073SCathy.Zhou@Sun.COM 	 */
3189073SCathy.Zhou@Sun.COM 	subsize = 0;
3199073SCathy.Zhou@Sun.COM 
3209073SCathy.Zhou@Sun.COM 	/*
3219073SCathy.Zhou@Sun.COM 	 * Direct capability negotiation interface between IP and softmac
3229073SCathy.Zhou@Sun.COM 	 */
3239073SCathy.Zhou@Sun.COM 	if (check_ip_above(sup->su_rq)) {
3249073SCathy.Zhou@Sun.COM 		dld_capable = B_TRUE;
3259073SCathy.Zhou@Sun.COM 		subsize += sizeof (dl_capability_sub_t) +
3269073SCathy.Zhou@Sun.COM 		    sizeof (dl_capab_dld_t);
3279073SCathy.Zhou@Sun.COM 	}
3289073SCathy.Zhou@Sun.COM 
3299073SCathy.Zhou@Sun.COM 	/*
3309073SCathy.Zhou@Sun.COM 	 * Check if checksum offload is supported on this MAC.
3319073SCathy.Zhou@Sun.COM 	 */
3329073SCathy.Zhou@Sun.COM 	if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM) {
3339073SCathy.Zhou@Sun.COM 		hcksum_capable = B_TRUE;
3349073SCathy.Zhou@Sun.COM 		subsize += sizeof (dl_capability_sub_t) +
3359073SCathy.Zhou@Sun.COM 		    sizeof (dl_capab_hcksum_t);
3369073SCathy.Zhou@Sun.COM 	}
3379073SCathy.Zhou@Sun.COM 
3389073SCathy.Zhou@Sun.COM 	/*
3399073SCathy.Zhou@Sun.COM 	 * Check if zerocopy is supported on this interface.
3409073SCathy.Zhou@Sun.COM 	 */
3419073SCathy.Zhou@Sun.COM 	if (!(softmac->smac_capab_flags & MAC_CAPAB_NO_ZCOPY)) {
3429073SCathy.Zhou@Sun.COM 		zcopy_capable = B_TRUE;
3439073SCathy.Zhou@Sun.COM 		subsize += sizeof (dl_capability_sub_t) +
3449073SCathy.Zhou@Sun.COM 		    sizeof (dl_capab_zerocopy_t);
3459073SCathy.Zhou@Sun.COM 	}
3469073SCathy.Zhou@Sun.COM 
3479073SCathy.Zhou@Sun.COM 	if (softmac->smac_mdt) {
3489073SCathy.Zhou@Sun.COM 		mdt_capable = B_TRUE;
3499073SCathy.Zhou@Sun.COM 		subsize += sizeof (dl_capability_sub_t) +
3509073SCathy.Zhou@Sun.COM 		    sizeof (dl_capab_mdt_t);
3519073SCathy.Zhou@Sun.COM 	}
3529073SCathy.Zhou@Sun.COM 
3539073SCathy.Zhou@Sun.COM 	/*
3549073SCathy.Zhou@Sun.COM 	 * If there are no capabilities to advertise or if we
3559073SCathy.Zhou@Sun.COM 	 * can't allocate a response, send a DL_ERROR_ACK.
3569073SCathy.Zhou@Sun.COM 	 */
3579073SCathy.Zhou@Sun.COM 	if ((subsize == 0) || (mp1 = reallocb(mp,
3589073SCathy.Zhou@Sun.COM 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
3599073SCathy.Zhou@Sun.COM 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
3609073SCathy.Zhou@Sun.COM 		return;
3619073SCathy.Zhou@Sun.COM 	}
3629073SCathy.Zhou@Sun.COM 
3639073SCathy.Zhou@Sun.COM 	mp = mp1;
3649073SCathy.Zhou@Sun.COM 	DB_TYPE(mp) = M_PROTO;
3659073SCathy.Zhou@Sun.COM 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
3669073SCathy.Zhou@Sun.COM 	bzero(mp->b_rptr, MBLKL(mp));
3679073SCathy.Zhou@Sun.COM 	dlap = (dl_capability_ack_t *)mp->b_rptr;
3689073SCathy.Zhou@Sun.COM 	dlap->dl_primitive = DL_CAPABILITY_ACK;
3699073SCathy.Zhou@Sun.COM 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
3709073SCathy.Zhou@Sun.COM 	dlap->dl_sub_length = subsize;
3719073SCathy.Zhou@Sun.COM 	ptr = (uint8_t *)&dlap[1];
3729073SCathy.Zhou@Sun.COM 
3739073SCathy.Zhou@Sun.COM 	/*
3749073SCathy.Zhou@Sun.COM 	 * IP polling interface.
3759073SCathy.Zhou@Sun.COM 	 */
3769073SCathy.Zhou@Sun.COM 	if (dld_capable) {
3779073SCathy.Zhou@Sun.COM 		dl_capab_dld_t		dld;
3789073SCathy.Zhou@Sun.COM 
3799073SCathy.Zhou@Sun.COM 		dlsp = (dl_capability_sub_t *)ptr;
3809073SCathy.Zhou@Sun.COM 		dlsp->dl_cap = DL_CAPAB_DLD;
3819073SCathy.Zhou@Sun.COM 		dlsp->dl_length = sizeof (dl_capab_dld_t);
3829073SCathy.Zhou@Sun.COM 		ptr += sizeof (dl_capability_sub_t);
3839073SCathy.Zhou@Sun.COM 
3849073SCathy.Zhou@Sun.COM 		bzero(&dld, sizeof (dl_capab_dld_t));
3859073SCathy.Zhou@Sun.COM 		dld.dld_version = DLD_CURRENT_VERSION;
3869073SCathy.Zhou@Sun.COM 		dld.dld_capab = (uintptr_t)softmac_dld_capab;
3879073SCathy.Zhou@Sun.COM 		dld.dld_capab_handle = (uintptr_t)sup;
3889073SCathy.Zhou@Sun.COM 
3899073SCathy.Zhou@Sun.COM 		dlcapabsetqid(&(dld.dld_mid), sup->su_rq);
3909073SCathy.Zhou@Sun.COM 		bcopy(&dld, ptr, sizeof (dl_capab_dld_t));
3919073SCathy.Zhou@Sun.COM 		ptr += sizeof (dl_capab_dld_t);
3929073SCathy.Zhou@Sun.COM 	}
3939073SCathy.Zhou@Sun.COM 
3949073SCathy.Zhou@Sun.COM 	/*
3959073SCathy.Zhou@Sun.COM 	 * TCP/IP checksum offload.
3969073SCathy.Zhou@Sun.COM 	 */
3979073SCathy.Zhou@Sun.COM 	if (hcksum_capable) {
3989073SCathy.Zhou@Sun.COM 		dl_capab_hcksum_t	hcksum;
3999073SCathy.Zhou@Sun.COM 
4009073SCathy.Zhou@Sun.COM 		dlsp = (dl_capability_sub_t *)ptr;
4019073SCathy.Zhou@Sun.COM 
4029073SCathy.Zhou@Sun.COM 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
4039073SCathy.Zhou@Sun.COM 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
4049073SCathy.Zhou@Sun.COM 		ptr += sizeof (dl_capability_sub_t);
4059073SCathy.Zhou@Sun.COM 
4069073SCathy.Zhou@Sun.COM 		bzero(&hcksum, sizeof (dl_capab_hcksum_t));
4079073SCathy.Zhou@Sun.COM 		hcksum.hcksum_version = HCKSUM_VERSION_1;
4089073SCathy.Zhou@Sun.COM 		hcksum.hcksum_txflags = softmac->smac_hcksum_txflags;
4099073SCathy.Zhou@Sun.COM 		dlcapabsetqid(&(hcksum.hcksum_mid), sup->su_rq);
4109073SCathy.Zhou@Sun.COM 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
4119073SCathy.Zhou@Sun.COM 		ptr += sizeof (dl_capab_hcksum_t);
4129073SCathy.Zhou@Sun.COM 	}
4139073SCathy.Zhou@Sun.COM 
4149073SCathy.Zhou@Sun.COM 	/*
4159073SCathy.Zhou@Sun.COM 	 * Zero copy
4169073SCathy.Zhou@Sun.COM 	 */
4179073SCathy.Zhou@Sun.COM 	if (zcopy_capable) {
4189073SCathy.Zhou@Sun.COM 		dl_capab_zerocopy_t	zcopy;
4199073SCathy.Zhou@Sun.COM 
4209073SCathy.Zhou@Sun.COM 		dlsp = (dl_capability_sub_t *)ptr;
4219073SCathy.Zhou@Sun.COM 
4229073SCathy.Zhou@Sun.COM 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
4239073SCathy.Zhou@Sun.COM 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
4249073SCathy.Zhou@Sun.COM 		ptr += sizeof (dl_capability_sub_t);
4259073SCathy.Zhou@Sun.COM 
4269073SCathy.Zhou@Sun.COM 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
4279073SCathy.Zhou@Sun.COM 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
4289073SCathy.Zhou@Sun.COM 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
4299073SCathy.Zhou@Sun.COM 		dlcapabsetqid(&(zcopy.zerocopy_mid), sup->su_rq);
4309073SCathy.Zhou@Sun.COM 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
4319073SCathy.Zhou@Sun.COM 		ptr += sizeof (dl_capab_zerocopy_t);
4329073SCathy.Zhou@Sun.COM 	}
4339073SCathy.Zhou@Sun.COM 
4349073SCathy.Zhou@Sun.COM 	/*
4359073SCathy.Zhou@Sun.COM 	 * MDT
4369073SCathy.Zhou@Sun.COM 	 */
4379073SCathy.Zhou@Sun.COM 	if (mdt_capable) {
4389073SCathy.Zhou@Sun.COM 		dl_capab_mdt_t mdt;
4399073SCathy.Zhou@Sun.COM 
4409073SCathy.Zhou@Sun.COM 		dlsp = (dl_capability_sub_t *)ptr;
4419073SCathy.Zhou@Sun.COM 
4429073SCathy.Zhou@Sun.COM 		dlsp->dl_cap = DL_CAPAB_MDT;
4439073SCathy.Zhou@Sun.COM 		dlsp->dl_length = sizeof (dl_capab_mdt_t);
4449073SCathy.Zhou@Sun.COM 		ptr += sizeof (dl_capability_sub_t);
4459073SCathy.Zhou@Sun.COM 
4469073SCathy.Zhou@Sun.COM 		bzero(&mdt, sizeof (dl_capab_mdt_t));
4479073SCathy.Zhou@Sun.COM 		mdt.mdt_version = MDT_VERSION_2;
4489073SCathy.Zhou@Sun.COM 		mdt.mdt_flags = DL_CAPAB_MDT_ENABLE;
4499073SCathy.Zhou@Sun.COM 		mdt.mdt_hdr_head = softmac->smac_mdt_capab.mdt_hdr_head;
4509073SCathy.Zhou@Sun.COM 		mdt.mdt_hdr_tail = softmac->smac_mdt_capab.mdt_hdr_tail;
4519073SCathy.Zhou@Sun.COM 		mdt.mdt_max_pld = softmac->smac_mdt_capab.mdt_max_pld;
4529073SCathy.Zhou@Sun.COM 		mdt.mdt_span_limit = softmac->smac_mdt_capab.mdt_span_limit;
4539073SCathy.Zhou@Sun.COM 		dlcapabsetqid(&(mdt.mdt_mid), sup->su_rq);
4549073SCathy.Zhou@Sun.COM 		bcopy(&mdt, ptr, sizeof (dl_capab_mdt_t));
4559073SCathy.Zhou@Sun.COM 		ptr += sizeof (dl_capab_mdt_t);
4569073SCathy.Zhou@Sun.COM 	}
4579073SCathy.Zhou@Sun.COM 
4589073SCathy.Zhou@Sun.COM 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
4599073SCathy.Zhou@Sun.COM 	qreply(q, mp);
4609073SCathy.Zhou@Sun.COM }
4619073SCathy.Zhou@Sun.COM 
4629073SCathy.Zhou@Sun.COM static void
softmac_capability_req(softmac_upper_t * sup,mblk_t * mp)4639073SCathy.Zhou@Sun.COM softmac_capability_req(softmac_upper_t *sup, mblk_t *mp)
4649073SCathy.Zhou@Sun.COM {
4659073SCathy.Zhou@Sun.COM 	dl_capability_req_t	*dlp = (dl_capability_req_t *)mp->b_rptr;
4669073SCathy.Zhou@Sun.COM 	dl_capability_sub_t	*sp;
4679073SCathy.Zhou@Sun.COM 	size_t			size, len;
4689073SCathy.Zhou@Sun.COM 	offset_t		off, end;
4699073SCathy.Zhou@Sun.COM 	t_uscalar_t		dl_err;
4709073SCathy.Zhou@Sun.COM 	queue_t			*q = sup->su_wq;
4719073SCathy.Zhou@Sun.COM 
4729073SCathy.Zhou@Sun.COM 	ASSERT(sup->su_mode == SOFTMAC_FASTPATH);
4739073SCathy.Zhou@Sun.COM 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
4749073SCathy.Zhou@Sun.COM 		dl_err = DL_BADPRIM;
4759073SCathy.Zhou@Sun.COM 		goto failed;
4769073SCathy.Zhou@Sun.COM 	}
4779073SCathy.Zhou@Sun.COM 
4789073SCathy.Zhou@Sun.COM 	if (!sup->su_bound) {
4799073SCathy.Zhou@Sun.COM 		dl_err = DL_OUTSTATE;
4809073SCathy.Zhou@Sun.COM 		goto failed;
4819073SCathy.Zhou@Sun.COM 	}
4829073SCathy.Zhou@Sun.COM 
4839073SCathy.Zhou@Sun.COM 	/*
4849073SCathy.Zhou@Sun.COM 	 * This request is overloaded. If there are no requested capabilities
4859073SCathy.Zhou@Sun.COM 	 * then we just want to acknowledge with all the capabilities we
4869073SCathy.Zhou@Sun.COM 	 * support. Otherwise we enable the set of capabilities requested.
4879073SCathy.Zhou@Sun.COM 	 */
4889073SCathy.Zhou@Sun.COM 	if (dlp->dl_sub_length == 0) {
4899073SCathy.Zhou@Sun.COM 		softmac_capability_advertise(sup, mp);
4909073SCathy.Zhou@Sun.COM 		return;
4919073SCathy.Zhou@Sun.COM 	}
4929073SCathy.Zhou@Sun.COM 
4939073SCathy.Zhou@Sun.COM 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
4949073SCathy.Zhou@Sun.COM 		dl_err = DL_BADPRIM;
4959073SCathy.Zhou@Sun.COM 		goto failed;
4969073SCathy.Zhou@Sun.COM 	}
4979073SCathy.Zhou@Sun.COM 
4989073SCathy.Zhou@Sun.COM 	dlp->dl_primitive = DL_CAPABILITY_ACK;
4999073SCathy.Zhou@Sun.COM 
5009073SCathy.Zhou@Sun.COM 	off = dlp->dl_sub_offset;
5019073SCathy.Zhou@Sun.COM 	len = dlp->dl_sub_length;
5029073SCathy.Zhou@Sun.COM 
5039073SCathy.Zhou@Sun.COM 	/*
5049073SCathy.Zhou@Sun.COM 	 * Walk the list of capabilities to be enabled.
5059073SCathy.Zhou@Sun.COM 	 */
5069073SCathy.Zhou@Sun.COM 	for (end = off + len; off < end; ) {
5079073SCathy.Zhou@Sun.COM 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
5089073SCathy.Zhou@Sun.COM 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
5099073SCathy.Zhou@Sun.COM 
5109073SCathy.Zhou@Sun.COM 		if (off + size > end ||
5119073SCathy.Zhou@Sun.COM 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
5129073SCathy.Zhou@Sun.COM 			dl_err = DL_BADPRIM;
5139073SCathy.Zhou@Sun.COM 			goto failed;
5149073SCathy.Zhou@Sun.COM 		}
5159073SCathy.Zhou@Sun.COM 
5169073SCathy.Zhou@Sun.COM 		switch (sp->dl_cap) {
5179073SCathy.Zhou@Sun.COM 		/*
5189073SCathy.Zhou@Sun.COM 		 * TCP/IP checksum offload to hardware.
5199073SCathy.Zhou@Sun.COM 		 */
5209073SCathy.Zhou@Sun.COM 		case DL_CAPAB_HCKSUM: {
5219073SCathy.Zhou@Sun.COM 			dl_capab_hcksum_t *hcksump;
5229073SCathy.Zhou@Sun.COM 			dl_capab_hcksum_t hcksum;
5239073SCathy.Zhou@Sun.COM 
5249073SCathy.Zhou@Sun.COM 			hcksump = (dl_capab_hcksum_t *)&sp[1];
5259073SCathy.Zhou@Sun.COM 			/*
5269073SCathy.Zhou@Sun.COM 			 * Copy for alignment.
5279073SCathy.Zhou@Sun.COM 			 */
5289073SCathy.Zhou@Sun.COM 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
5299073SCathy.Zhou@Sun.COM 			dlcapabsetqid(&(hcksum.hcksum_mid), sup->su_rq);
5309073SCathy.Zhou@Sun.COM 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
5319073SCathy.Zhou@Sun.COM 			break;
5329073SCathy.Zhou@Sun.COM 		}
5339073SCathy.Zhou@Sun.COM 
5349073SCathy.Zhou@Sun.COM 		default:
5359073SCathy.Zhou@Sun.COM 			break;
5369073SCathy.Zhou@Sun.COM 		}
5379073SCathy.Zhou@Sun.COM 
5389073SCathy.Zhou@Sun.COM 		off += size;
5399073SCathy.Zhou@Sun.COM 	}
5409073SCathy.Zhou@Sun.COM 	qreply(q, mp);
5419073SCathy.Zhou@Sun.COM 	return;
5429073SCathy.Zhou@Sun.COM failed:
5439073SCathy.Zhou@Sun.COM 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
5449073SCathy.Zhou@Sun.COM }
5459073SCathy.Zhou@Sun.COM 
5469073SCathy.Zhou@Sun.COM static void
softmac_bind_req(softmac_upper_t * sup,mblk_t * mp)5479073SCathy.Zhou@Sun.COM softmac_bind_req(softmac_upper_t *sup, mblk_t *mp)
5489073SCathy.Zhou@Sun.COM {
5499073SCathy.Zhou@Sun.COM 	softmac_lower_t	*slp = sup->su_slp;
5509073SCathy.Zhou@Sun.COM 	softmac_t	*softmac = sup->su_softmac;
5519073SCathy.Zhou@Sun.COM 	mblk_t		*ackmp, *mp1;
5529073SCathy.Zhou@Sun.COM 	int		err;
5539073SCathy.Zhou@Sun.COM 
5549073SCathy.Zhou@Sun.COM 	if (MBLKL(mp) < DL_BIND_REQ_SIZE) {
5559073SCathy.Zhou@Sun.COM 		freemsg(mp);
5569073SCathy.Zhou@Sun.COM 		return;
5579073SCathy.Zhou@Sun.COM 	}
5589073SCathy.Zhou@Sun.COM 
5599073SCathy.Zhou@Sun.COM 	/*
5609073SCathy.Zhou@Sun.COM 	 * Allocate ackmp incase the underlying driver does not ack timely.
5619073SCathy.Zhou@Sun.COM 	 */
5629073SCathy.Zhou@Sun.COM 	if ((mp1 = allocb(sizeof (dl_error_ack_t), BPRI_HI)) == NULL) {
5639073SCathy.Zhou@Sun.COM 		dlerrorack(sup->su_wq, mp, DL_BIND_REQ, DL_SYSERR, ENOMEM);
5649073SCathy.Zhou@Sun.COM 		return;
5659073SCathy.Zhou@Sun.COM 	}
5669073SCathy.Zhou@Sun.COM 
5679073SCathy.Zhou@Sun.COM 	err = softmac_output(slp, mp, DL_BIND_REQ, DL_BIND_ACK, &ackmp);
5689073SCathy.Zhou@Sun.COM 	if (ackmp != NULL) {
5699073SCathy.Zhou@Sun.COM 		freemsg(mp1);
5709073SCathy.Zhou@Sun.COM 	} else {
5719073SCathy.Zhou@Sun.COM 		/*
5729073SCathy.Zhou@Sun.COM 		 * The driver does not ack timely.
5739073SCathy.Zhou@Sun.COM 		 */
5749073SCathy.Zhou@Sun.COM 		ASSERT(err == ENOMSG);
5759073SCathy.Zhou@Sun.COM 		ackmp = mp1;
5769073SCathy.Zhou@Sun.COM 	}
5779073SCathy.Zhou@Sun.COM 	if (err != 0)
5789073SCathy.Zhou@Sun.COM 		goto failed;
5799073SCathy.Zhou@Sun.COM 
5809073SCathy.Zhou@Sun.COM 	/*
5819073SCathy.Zhou@Sun.COM 	 * Enable capabilities the underlying driver claims to support.
5829073SCathy.Zhou@Sun.COM 	 */
5839073SCathy.Zhou@Sun.COM 	if ((err = softmac_capab_enable(slp)) != 0)
5849073SCathy.Zhou@Sun.COM 		goto failed;
5859073SCathy.Zhou@Sun.COM 
5869073SCathy.Zhou@Sun.COM 	/*
5879073SCathy.Zhou@Sun.COM 	 * Check whether this softmac is already marked as exclusively used,
5889073SCathy.Zhou@Sun.COM 	 * e.g., an aggregation is created over it. Fail the BIND_REQ if so.
5899073SCathy.Zhou@Sun.COM 	 */
5909073SCathy.Zhou@Sun.COM 	mutex_enter(&softmac->smac_active_mutex);
5919073SCathy.Zhou@Sun.COM 	if (softmac->smac_active) {
5929073SCathy.Zhou@Sun.COM 		mutex_exit(&softmac->smac_active_mutex);
5939073SCathy.Zhou@Sun.COM 		err = EBUSY;
5949073SCathy.Zhou@Sun.COM 		goto failed;
5959073SCathy.Zhou@Sun.COM 	}
5969073SCathy.Zhou@Sun.COM 	softmac->smac_nactive++;
5979073SCathy.Zhou@Sun.COM 	sup->su_active = B_TRUE;
5989073SCathy.Zhou@Sun.COM 	mutex_exit(&softmac->smac_active_mutex);
5999073SCathy.Zhou@Sun.COM 	sup->su_bound = B_TRUE;
6009073SCathy.Zhou@Sun.COM 
6019073SCathy.Zhou@Sun.COM 	qreply(sup->su_wq, ackmp);
6029073SCathy.Zhou@Sun.COM 	return;
6039073SCathy.Zhou@Sun.COM failed:
6049073SCathy.Zhou@Sun.COM 	if (err != 0) {
6059073SCathy.Zhou@Sun.COM 		dlerrorack(sup->su_wq, ackmp, DL_BIND_REQ, DL_SYSERR, err);
6069073SCathy.Zhou@Sun.COM 		return;
6079073SCathy.Zhou@Sun.COM 	}
6089073SCathy.Zhou@Sun.COM }
6099073SCathy.Zhou@Sun.COM 
6109073SCathy.Zhou@Sun.COM static void
softmac_unbind_req(softmac_upper_t * sup,mblk_t * mp)6119073SCathy.Zhou@Sun.COM softmac_unbind_req(softmac_upper_t *sup, mblk_t *mp)
6129073SCathy.Zhou@Sun.COM {
6139073SCathy.Zhou@Sun.COM 	softmac_lower_t	*slp = sup->su_slp;
6149073SCathy.Zhou@Sun.COM 	softmac_t	*softmac = sup->su_softmac;
6159073SCathy.Zhou@Sun.COM 	mblk_t		*ackmp, *mp1;
6169073SCathy.Zhou@Sun.COM 	int		err;
6179073SCathy.Zhou@Sun.COM 
6189073SCathy.Zhou@Sun.COM 	if (MBLKL(mp) < DL_UNBIND_REQ_SIZE) {
6199073SCathy.Zhou@Sun.COM 		freemsg(mp);
6209073SCathy.Zhou@Sun.COM 		return;
6219073SCathy.Zhou@Sun.COM 	}
6229073SCathy.Zhou@Sun.COM 
6239073SCathy.Zhou@Sun.COM 	if (!sup->su_bound) {
6249073SCathy.Zhou@Sun.COM 		dlerrorack(sup->su_wq, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0);
6259073SCathy.Zhou@Sun.COM 		return;
6269073SCathy.Zhou@Sun.COM 	}
6279073SCathy.Zhou@Sun.COM 
6289073SCathy.Zhou@Sun.COM 	/*
6299073SCathy.Zhou@Sun.COM 	 * Allocate ackmp incase the underlying driver does not ack timely.
6309073SCathy.Zhou@Sun.COM 	 */
6319073SCathy.Zhou@Sun.COM 	if ((mp1 = allocb(sizeof (dl_error_ack_t), BPRI_HI)) == NULL) {
6329073SCathy.Zhou@Sun.COM 		dlerrorack(sup->su_wq, mp, DL_UNBIND_REQ, DL_SYSERR, ENOMEM);
6339073SCathy.Zhou@Sun.COM 		return;
6349073SCathy.Zhou@Sun.COM 	}
6359073SCathy.Zhou@Sun.COM 
6369073SCathy.Zhou@Sun.COM 	err = softmac_output(slp, mp, DL_UNBIND_REQ, DL_OK_ACK, &ackmp);
6379073SCathy.Zhou@Sun.COM 	if (ackmp != NULL) {
6389073SCathy.Zhou@Sun.COM 		freemsg(mp1);
6399073SCathy.Zhou@Sun.COM 	} else {
6409073SCathy.Zhou@Sun.COM 		/*
6419073SCathy.Zhou@Sun.COM 		 * The driver does not ack timely.
6429073SCathy.Zhou@Sun.COM 		 */
6439073SCathy.Zhou@Sun.COM 		ASSERT(err == ENOMSG);
6449073SCathy.Zhou@Sun.COM 		ackmp = mp1;
6459073SCathy.Zhou@Sun.COM 	}
6469073SCathy.Zhou@Sun.COM 	if (err != 0) {
6479073SCathy.Zhou@Sun.COM 		dlerrorack(sup->su_wq, ackmp, DL_UNBIND_REQ, DL_SYSERR, err);
6489073SCathy.Zhou@Sun.COM 		return;
6499073SCathy.Zhou@Sun.COM 	}
6509073SCathy.Zhou@Sun.COM 
6519073SCathy.Zhou@Sun.COM 	sup->su_bound = B_FALSE;
6529073SCathy.Zhou@Sun.COM 
6539073SCathy.Zhou@Sun.COM 	mutex_enter(&softmac->smac_active_mutex);
6549073SCathy.Zhou@Sun.COM 	if (sup->su_active) {
6559073SCathy.Zhou@Sun.COM 		ASSERT(!softmac->smac_active);
6569073SCathy.Zhou@Sun.COM 		softmac->smac_nactive--;
6579073SCathy.Zhou@Sun.COM 		sup->su_active = B_FALSE;
6589073SCathy.Zhou@Sun.COM 	}
6599073SCathy.Zhou@Sun.COM 	mutex_exit(&softmac->smac_active_mutex);
6609073SCathy.Zhou@Sun.COM 
6619073SCathy.Zhou@Sun.COM done:
6629073SCathy.Zhou@Sun.COM 	qreply(sup->su_wq, ackmp);
6639073SCathy.Zhou@Sun.COM }
6649073SCathy.Zhou@Sun.COM 
6659073SCathy.Zhou@Sun.COM /*
6669073SCathy.Zhou@Sun.COM  * Process the non-data mblk.
6679073SCathy.Zhou@Sun.COM  */
6689073SCathy.Zhou@Sun.COM static void
softmac_wput_single_nondata(softmac_upper_t * sup,mblk_t * mp)6699073SCathy.Zhou@Sun.COM softmac_wput_single_nondata(softmac_upper_t *sup, mblk_t *mp)
6709073SCathy.Zhou@Sun.COM {
6719073SCathy.Zhou@Sun.COM 	softmac_t *softmac = sup->su_softmac;
6729073SCathy.Zhou@Sun.COM 	softmac_lower_t	*slp = sup->su_slp;
6739073SCathy.Zhou@Sun.COM 	unsigned char	dbtype;
6749073SCathy.Zhou@Sun.COM 	t_uscalar_t	prim;
6759073SCathy.Zhou@Sun.COM 
6769073SCathy.Zhou@Sun.COM 	dbtype = DB_TYPE(mp);
677*11042SErik.Nordmark@Sun.COM 	sup->su_is_arp = 0;
6789073SCathy.Zhou@Sun.COM 	switch (dbtype) {
679*11042SErik.Nordmark@Sun.COM 	case M_CTL:
680*11042SErik.Nordmark@Sun.COM 		sup->su_is_arp = 1;
681*11042SErik.Nordmark@Sun.COM 		/* FALLTHROUGH */
682*11042SErik.Nordmark@Sun.COM 	case M_IOCTL: {
6839073SCathy.Zhou@Sun.COM 		uint32_t	expected_mode;
6849073SCathy.Zhou@Sun.COM 
6859073SCathy.Zhou@Sun.COM 		if (((struct iocblk *)(mp->b_rptr))->ioc_cmd != SIOCSLIFNAME)
6869073SCathy.Zhou@Sun.COM 			break;
6879073SCathy.Zhou@Sun.COM 
6889073SCathy.Zhou@Sun.COM 		/*
6899073SCathy.Zhou@Sun.COM 		 * Nak the M_IOCTL based on the STREAMS specification.
6909073SCathy.Zhou@Sun.COM 		 */
6919073SCathy.Zhou@Sun.COM 		if (dbtype == M_IOCTL)
6929073SCathy.Zhou@Sun.COM 			miocnak(sup->su_wq, mp, 0, EINVAL);
6939682SCathy.Zhou@Sun.COM 		else
6949682SCathy.Zhou@Sun.COM 			freemsg(mp);
6959073SCathy.Zhou@Sun.COM 
6969073SCathy.Zhou@Sun.COM 		/*
6979073SCathy.Zhou@Sun.COM 		 * This stream is either IP or ARP. See whether
6989073SCathy.Zhou@Sun.COM 		 * we need to setup a dedicated-lower-stream for it.
6999073SCathy.Zhou@Sun.COM 		 */
7009073SCathy.Zhou@Sun.COM 		mutex_enter(&softmac->smac_fp_mutex);
7019073SCathy.Zhou@Sun.COM 
7029073SCathy.Zhou@Sun.COM 		expected_mode = DATAPATH_MODE(softmac);
7039073SCathy.Zhou@Sun.COM 		if (expected_mode == SOFTMAC_SLOWPATH)
7049073SCathy.Zhou@Sun.COM 			sup->su_mode = SOFTMAC_SLOWPATH;
7059073SCathy.Zhou@Sun.COM 		list_insert_head(&softmac->smac_sup_list, sup);
7069073SCathy.Zhou@Sun.COM 		mutex_exit(&softmac->smac_fp_mutex);
7079073SCathy.Zhou@Sun.COM 
7089073SCathy.Zhou@Sun.COM 		/*
7099073SCathy.Zhou@Sun.COM 		 * Setup the fast-path dedicated lower stream if fast-path
7109073SCathy.Zhou@Sun.COM 		 * is expected. Note that no lock is held here, and if
7119073SCathy.Zhou@Sun.COM 		 * smac_expected_mode is changed from SOFTMAC_FASTPATH to
7129073SCathy.Zhou@Sun.COM 		 * SOFTMAC_SLOWPATH, the DL_NOTE_REPLUMB message used for
7139073SCathy.Zhou@Sun.COM 		 * data-path switching would already be queued and will
7149073SCathy.Zhou@Sun.COM 		 * be processed by softmac_wput_single_nondata() later.
7159073SCathy.Zhou@Sun.COM 		 */
7169073SCathy.Zhou@Sun.COM 		if (expected_mode == SOFTMAC_FASTPATH)
7179073SCathy.Zhou@Sun.COM 			(void) softmac_fastpath_setup(sup);
7189073SCathy.Zhou@Sun.COM 		return;
7199073SCathy.Zhou@Sun.COM 	}
7209073SCathy.Zhou@Sun.COM 	case M_PROTO:
7219073SCathy.Zhou@Sun.COM 	case M_PCPROTO:
7229073SCathy.Zhou@Sun.COM 		if (MBLKL(mp) < sizeof (t_uscalar_t)) {
7239073SCathy.Zhou@Sun.COM 			freemsg(mp);
7249073SCathy.Zhou@Sun.COM 			return;
7259073SCathy.Zhou@Sun.COM 		}
7269073SCathy.Zhou@Sun.COM 		prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
7279073SCathy.Zhou@Sun.COM 		switch (prim) {
7289073SCathy.Zhou@Sun.COM 		case DL_NOTIFY_IND:
7299073SCathy.Zhou@Sun.COM 			if (MBLKL(mp) < sizeof (dl_notify_ind_t) ||
7309073SCathy.Zhou@Sun.COM 			    ((dl_notify_ind_t *)mp->b_rptr)->dl_notification !=
7319073SCathy.Zhou@Sun.COM 			    DL_NOTE_REPLUMB) {
7329073SCathy.Zhou@Sun.COM 				freemsg(mp);
7339073SCathy.Zhou@Sun.COM 				return;
7349073SCathy.Zhou@Sun.COM 			}
7359073SCathy.Zhou@Sun.COM 			/*
7369073SCathy.Zhou@Sun.COM 			 * This DL_NOTE_REPLUMB message is initiated
7379073SCathy.Zhou@Sun.COM 			 * and queued by the softmac itself, when the
7389073SCathy.Zhou@Sun.COM 			 * sup is trying to switching its datapath mode
7399073SCathy.Zhou@Sun.COM 			 * between SOFTMAC_SLOWPATH and SOFTMAC_FASTPATH.
7409073SCathy.Zhou@Sun.COM 			 * Send this message upstream.
7419073SCathy.Zhou@Sun.COM 			 */
7429073SCathy.Zhou@Sun.COM 			qreply(sup->su_wq, mp);
7439073SCathy.Zhou@Sun.COM 			return;
7449073SCathy.Zhou@Sun.COM 		case DL_NOTIFY_CONF:
7459073SCathy.Zhou@Sun.COM 			if (MBLKL(mp) < sizeof (dl_notify_conf_t) ||
7469073SCathy.Zhou@Sun.COM 			    ((dl_notify_conf_t *)mp->b_rptr)->dl_notification !=
7479073SCathy.Zhou@Sun.COM 			    DL_NOTE_REPLUMB_DONE) {
7489073SCathy.Zhou@Sun.COM 				freemsg(mp);
7499073SCathy.Zhou@Sun.COM 				return;
7509073SCathy.Zhou@Sun.COM 			}
7519073SCathy.Zhou@Sun.COM 			/*
7529073SCathy.Zhou@Sun.COM 			 * This is an indication from IP/ARP that the
7539073SCathy.Zhou@Sun.COM 			 * fastpath->slowpath switch is done.
7549073SCathy.Zhou@Sun.COM 			 */
7559073SCathy.Zhou@Sun.COM 			freemsg(mp);
7569073SCathy.Zhou@Sun.COM 			softmac_datapath_switch_done(sup);
7579073SCathy.Zhou@Sun.COM 			return;
7589073SCathy.Zhou@Sun.COM 		}
7599073SCathy.Zhou@Sun.COM 		break;
7609073SCathy.Zhou@Sun.COM 	}
7619073SCathy.Zhou@Sun.COM 
7629073SCathy.Zhou@Sun.COM 	/*
7639073SCathy.Zhou@Sun.COM 	 * No need to hold lock to check su_mode, since su_mode updating only
7649073SCathy.Zhou@Sun.COM 	 * operation is is serialized by softmac_wput_nondata_task().
7659073SCathy.Zhou@Sun.COM 	 */
7669073SCathy.Zhou@Sun.COM 	if (sup->su_mode != SOFTMAC_FASTPATH) {
7679073SCathy.Zhou@Sun.COM 		dld_wput(sup->su_wq, mp);
7689073SCathy.Zhou@Sun.COM 		return;
7699073SCathy.Zhou@Sun.COM 	}
7709073SCathy.Zhou@Sun.COM 
7719073SCathy.Zhou@Sun.COM 	/*
7729073SCathy.Zhou@Sun.COM 	 * Fastpath non-data message processing. Most of non-data messages
7739073SCathy.Zhou@Sun.COM 	 * can be directly passed down to the dedicated-lower-stream, aside
7749073SCathy.Zhou@Sun.COM 	 * from the following M_PROTO/M_PCPROTO messages.
7759073SCathy.Zhou@Sun.COM 	 */
7769073SCathy.Zhou@Sun.COM 	switch (dbtype) {
7779073SCathy.Zhou@Sun.COM 	case M_PROTO:
7789073SCathy.Zhou@Sun.COM 	case M_PCPROTO:
7799073SCathy.Zhou@Sun.COM 		switch (prim) {
7809073SCathy.Zhou@Sun.COM 		case DL_BIND_REQ:
7819073SCathy.Zhou@Sun.COM 			softmac_bind_req(sup, mp);
7829073SCathy.Zhou@Sun.COM 			break;
7839073SCathy.Zhou@Sun.COM 		case DL_UNBIND_REQ:
7849073SCathy.Zhou@Sun.COM 			softmac_unbind_req(sup, mp);
7859073SCathy.Zhou@Sun.COM 			break;
7869073SCathy.Zhou@Sun.COM 		case DL_CAPABILITY_REQ:
7879073SCathy.Zhou@Sun.COM 			softmac_capability_req(sup, mp);
7889073SCathy.Zhou@Sun.COM 			break;
7899073SCathy.Zhou@Sun.COM 		default:
7909073SCathy.Zhou@Sun.COM 			putnext(slp->sl_wq, mp);
7919073SCathy.Zhou@Sun.COM 			break;
7929073SCathy.Zhou@Sun.COM 		}
7939073SCathy.Zhou@Sun.COM 		break;
7949073SCathy.Zhou@Sun.COM 	default:
7959073SCathy.Zhou@Sun.COM 		putnext(slp->sl_wq, mp);
7969073SCathy.Zhou@Sun.COM 		break;
7979073SCathy.Zhou@Sun.COM 	}
7989073SCathy.Zhou@Sun.COM }
7999073SCathy.Zhou@Sun.COM 
8009073SCathy.Zhou@Sun.COM /*
8019073SCathy.Zhou@Sun.COM  * The worker thread which processes non-data messages. Note we only process
8029073SCathy.Zhou@Sun.COM  * one message at one time in order to be able to "flush" the queued message
8039073SCathy.Zhou@Sun.COM  * and serialize the processing.
8049073SCathy.Zhou@Sun.COM  */
8059073SCathy.Zhou@Sun.COM static void
softmac_wput_nondata_task(void * arg)8069073SCathy.Zhou@Sun.COM softmac_wput_nondata_task(void *arg)
8079073SCathy.Zhou@Sun.COM {
8089073SCathy.Zhou@Sun.COM 	softmac_upper_t	*sup = arg;
8099073SCathy.Zhou@Sun.COM 	mblk_t		*mp;
8109073SCathy.Zhou@Sun.COM 
8119073SCathy.Zhou@Sun.COM 	mutex_enter(&sup->su_disp_mutex);
8129073SCathy.Zhou@Sun.COM 
8139073SCathy.Zhou@Sun.COM 	while (sup->su_pending_head != NULL) {
8149073SCathy.Zhou@Sun.COM 		if (sup->su_closing)
8159073SCathy.Zhou@Sun.COM 			break;
8169073SCathy.Zhou@Sun.COM 
8179073SCathy.Zhou@Sun.COM 		SOFTMAC_DQ_PENDING(sup, &mp);
8189073SCathy.Zhou@Sun.COM 		mutex_exit(&sup->su_disp_mutex);
8199073SCathy.Zhou@Sun.COM 		softmac_wput_single_nondata(sup, mp);
8209073SCathy.Zhou@Sun.COM 		mutex_enter(&sup->su_disp_mutex);
8219073SCathy.Zhou@Sun.COM 	}
8229073SCathy.Zhou@Sun.COM 
8239073SCathy.Zhou@Sun.COM 	/*
8249073SCathy.Zhou@Sun.COM 	 * If the stream is closing, flush all queued messages and inform
8259073SCathy.Zhou@Sun.COM 	 * the stream to be closed.
8269073SCathy.Zhou@Sun.COM 	 */
8279073SCathy.Zhou@Sun.COM 	freemsgchain(sup->su_pending_head);
8289073SCathy.Zhou@Sun.COM 	sup->su_pending_head = sup->su_pending_tail = NULL;
8299073SCathy.Zhou@Sun.COM 	sup->su_dlpi_pending = B_FALSE;
8309073SCathy.Zhou@Sun.COM 	cv_signal(&sup->su_disp_cv);
8319073SCathy.Zhou@Sun.COM 	mutex_exit(&sup->su_disp_mutex);
8329073SCathy.Zhou@Sun.COM }
8339073SCathy.Zhou@Sun.COM 
8349073SCathy.Zhou@Sun.COM /*
8359073SCathy.Zhou@Sun.COM  * Kernel thread to handle taskq dispatch failures in softmac_wput_nondata().
8369073SCathy.Zhou@Sun.COM  * This thread is started when the softmac module is first loaded.
8379073SCathy.Zhou@Sun.COM  */
8389073SCathy.Zhou@Sun.COM static void
softmac_taskq_dispatch(void)8399073SCathy.Zhou@Sun.COM softmac_taskq_dispatch(void)
8409073SCathy.Zhou@Sun.COM {
8419073SCathy.Zhou@Sun.COM 	callb_cpr_t	cprinfo;
8429073SCathy.Zhou@Sun.COM 	softmac_upper_t	*sup;
8439073SCathy.Zhou@Sun.COM 
8449073SCathy.Zhou@Sun.COM 	CALLB_CPR_INIT(&cprinfo, &softmac_taskq_lock, callb_generic_cpr,
8459073SCathy.Zhou@Sun.COM 	    "softmac_taskq_dispatch");
8469073SCathy.Zhou@Sun.COM 	mutex_enter(&softmac_taskq_lock);
8479073SCathy.Zhou@Sun.COM 
8489073SCathy.Zhou@Sun.COM 	while (!softmac_taskq_quit) {
8499073SCathy.Zhou@Sun.COM 		sup = list_head(&softmac_taskq_list);
8509073SCathy.Zhou@Sun.COM 		while (sup != NULL) {
8519073SCathy.Zhou@Sun.COM 			list_remove(&softmac_taskq_list, sup);
8529073SCathy.Zhou@Sun.COM 			sup->su_taskq_scheduled = B_FALSE;
8539073SCathy.Zhou@Sun.COM 			mutex_exit(&softmac_taskq_lock);
8549073SCathy.Zhou@Sun.COM 			VERIFY(taskq_dispatch(system_taskq,
8559073SCathy.Zhou@Sun.COM 			    softmac_wput_nondata_task, sup, TQ_SLEEP) != NULL);
8569073SCathy.Zhou@Sun.COM 			mutex_enter(&softmac_taskq_lock);
8579073SCathy.Zhou@Sun.COM 			sup = list_head(&softmac_taskq_list);
8589073SCathy.Zhou@Sun.COM 		}
8599073SCathy.Zhou@Sun.COM 
8609073SCathy.Zhou@Sun.COM 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
8619073SCathy.Zhou@Sun.COM 		cv_wait(&softmac_taskq_cv, &softmac_taskq_lock);
8629073SCathy.Zhou@Sun.COM 		CALLB_CPR_SAFE_END(&cprinfo, &softmac_taskq_lock);
8639073SCathy.Zhou@Sun.COM 	}
8649073SCathy.Zhou@Sun.COM 
8659073SCathy.Zhou@Sun.COM 	softmac_taskq_done = B_TRUE;
8669073SCathy.Zhou@Sun.COM 	cv_signal(&softmac_taskq_cv);
8679073SCathy.Zhou@Sun.COM 	CALLB_CPR_EXIT(&cprinfo);
8689073SCathy.Zhou@Sun.COM 	thread_exit();
8699073SCathy.Zhou@Sun.COM }
8709073SCathy.Zhou@Sun.COM 
8719073SCathy.Zhou@Sun.COM void
softmac_wput_nondata(softmac_upper_t * sup,mblk_t * mp)8729073SCathy.Zhou@Sun.COM softmac_wput_nondata(softmac_upper_t *sup, mblk_t *mp)
8739073SCathy.Zhou@Sun.COM {
8749073SCathy.Zhou@Sun.COM 	/*
8759073SCathy.Zhou@Sun.COM 	 * The processing of the message might block. Enqueue the
8769073SCathy.Zhou@Sun.COM 	 * message for later processing.
8779073SCathy.Zhou@Sun.COM 	 */
8789073SCathy.Zhou@Sun.COM 	mutex_enter(&sup->su_disp_mutex);
8799073SCathy.Zhou@Sun.COM 
8809073SCathy.Zhou@Sun.COM 	if (sup->su_closing) {
8819073SCathy.Zhou@Sun.COM 		mutex_exit(&sup->su_disp_mutex);
8829073SCathy.Zhou@Sun.COM 		freemsg(mp);
8839073SCathy.Zhou@Sun.COM 		return;
8849073SCathy.Zhou@Sun.COM 	}
8859073SCathy.Zhou@Sun.COM 
8869073SCathy.Zhou@Sun.COM 	SOFTMAC_EQ_PENDING(sup, mp);
8879073SCathy.Zhou@Sun.COM 
8889073SCathy.Zhou@Sun.COM 	if (sup->su_dlpi_pending) {
8899073SCathy.Zhou@Sun.COM 		mutex_exit(&sup->su_disp_mutex);
8909073SCathy.Zhou@Sun.COM 		return;
8919073SCathy.Zhou@Sun.COM 	}
8929073SCathy.Zhou@Sun.COM 	sup->su_dlpi_pending = B_TRUE;
8939073SCathy.Zhou@Sun.COM 	mutex_exit(&sup->su_disp_mutex);
8949073SCathy.Zhou@Sun.COM 
8959073SCathy.Zhou@Sun.COM 	if (taskq_dispatch(system_taskq, softmac_wput_nondata_task,
8969073SCathy.Zhou@Sun.COM 	    sup, TQ_NOSLEEP) != NULL) {
8979073SCathy.Zhou@Sun.COM 		return;
8989073SCathy.Zhou@Sun.COM 	}
8999073SCathy.Zhou@Sun.COM 
9009073SCathy.Zhou@Sun.COM 	mutex_enter(&softmac_taskq_lock);
9019073SCathy.Zhou@Sun.COM 	if (!sup->su_taskq_scheduled) {
9029073SCathy.Zhou@Sun.COM 		list_insert_tail(&softmac_taskq_list, sup);
9039073SCathy.Zhou@Sun.COM 		cv_signal(&softmac_taskq_cv);
9049073SCathy.Zhou@Sun.COM 	}
9059073SCathy.Zhou@Sun.COM 	sup->su_taskq_scheduled = B_TRUE;
9069073SCathy.Zhou@Sun.COM 	mutex_exit(&softmac_taskq_lock);
9079073SCathy.Zhou@Sun.COM }
9089073SCathy.Zhou@Sun.COM 
9099073SCathy.Zhou@Sun.COM /*
9109073SCathy.Zhou@Sun.COM  * Setup the dedicated-lower-stream (fast-path) for the IP/ARP upperstream.
9119073SCathy.Zhou@Sun.COM  */
9129073SCathy.Zhou@Sun.COM static int
softmac_fastpath_setup(softmac_upper_t * sup)9139073SCathy.Zhou@Sun.COM softmac_fastpath_setup(softmac_upper_t *sup)
9149073SCathy.Zhou@Sun.COM {
9159073SCathy.Zhou@Sun.COM 	softmac_t	*softmac = sup->su_softmac;
9169073SCathy.Zhou@Sun.COM 	softmac_lower_t	*slp;
9179073SCathy.Zhou@Sun.COM 	int		err;
9189073SCathy.Zhou@Sun.COM 
9199073SCathy.Zhou@Sun.COM 	err = softmac_lower_setup(softmac, sup, &slp);
9209073SCathy.Zhou@Sun.COM 
9219073SCathy.Zhou@Sun.COM 	mutex_enter(&sup->su_mutex);
9229073SCathy.Zhou@Sun.COM 	/*
9239073SCathy.Zhou@Sun.COM 	 * Wait for all data messages to be processed so that we can change
9249073SCathy.Zhou@Sun.COM 	 * the su_mode.
9259073SCathy.Zhou@Sun.COM 	 */
9269073SCathy.Zhou@Sun.COM 	while (sup->su_tx_inprocess != 0)
9279073SCathy.Zhou@Sun.COM 		cv_wait(&sup->su_cv, &sup->su_mutex);
9289073SCathy.Zhou@Sun.COM 
9299073SCathy.Zhou@Sun.COM 	ASSERT(sup->su_mode != SOFTMAC_FASTPATH);
9309073SCathy.Zhou@Sun.COM 	ASSERT(sup->su_slp == NULL);
9319073SCathy.Zhou@Sun.COM 	if (err != 0) {
9329073SCathy.Zhou@Sun.COM 		sup->su_mode = SOFTMAC_SLOWPATH;
9339073SCathy.Zhou@Sun.COM 	} else {
9349073SCathy.Zhou@Sun.COM 		sup->su_slp = slp;
9359073SCathy.Zhou@Sun.COM 		sup->su_mode = SOFTMAC_FASTPATH;
9369073SCathy.Zhou@Sun.COM 	}
9379073SCathy.Zhou@Sun.COM 	mutex_exit(&sup->su_mutex);
9389073SCathy.Zhou@Sun.COM 	return (err);
9399073SCathy.Zhou@Sun.COM }
9409073SCathy.Zhou@Sun.COM 
9419073SCathy.Zhou@Sun.COM /*
9429073SCathy.Zhou@Sun.COM  * Tear down the dedicated-lower-stream (fast-path) for the IP/ARP upperstream.
9439073SCathy.Zhou@Sun.COM  */
9449073SCathy.Zhou@Sun.COM static void
softmac_fastpath_tear(softmac_upper_t * sup)9459073SCathy.Zhou@Sun.COM softmac_fastpath_tear(softmac_upper_t *sup)
9469073SCathy.Zhou@Sun.COM {
9479073SCathy.Zhou@Sun.COM 	mutex_enter(&sup->su_mutex);
9489073SCathy.Zhou@Sun.COM 	/*
9499073SCathy.Zhou@Sun.COM 	 * Wait for all data messages in the dedicated-lower-stream
9509073SCathy.Zhou@Sun.COM 	 * to be processed.
9519073SCathy.Zhou@Sun.COM 	 */
9529073SCathy.Zhou@Sun.COM 	while (sup->su_tx_inprocess != 0)
9539073SCathy.Zhou@Sun.COM 		cv_wait(&sup->su_cv, &sup->su_mutex);
9549073SCathy.Zhou@Sun.COM 
9559738SCathy.Zhou@Sun.COM 	/*
9569738SCathy.Zhou@Sun.COM 	 * Note that this function is called either when the stream is closed,
9579738SCathy.Zhou@Sun.COM 	 * or the stream is unbound (fastpath-slowpath-switch). Therefore,
9589738SCathy.Zhou@Sun.COM 	 * No need to call the tx_notify callback.
9599738SCathy.Zhou@Sun.COM 	 */
9609738SCathy.Zhou@Sun.COM 	sup->su_tx_notify_func = NULL;
9619738SCathy.Zhou@Sun.COM 	sup->su_tx_notify_arg = NULL;
9629073SCathy.Zhou@Sun.COM 	if (sup->su_tx_busy) {
9639073SCathy.Zhou@Sun.COM 		ASSERT(sup->su_tx_flow_mp == NULL);
9649738SCathy.Zhou@Sun.COM 		VERIFY((sup->su_tx_flow_mp = getq(sup->su_wq)) != NULL);
9659073SCathy.Zhou@Sun.COM 		sup->su_tx_busy = B_FALSE;
9669073SCathy.Zhou@Sun.COM 	}
9679073SCathy.Zhou@Sun.COM 
9689073SCathy.Zhou@Sun.COM 	sup->su_mode = SOFTMAC_SLOWPATH;
9699073SCathy.Zhou@Sun.COM 
9709073SCathy.Zhou@Sun.COM 	/*
9719073SCathy.Zhou@Sun.COM 	 * Destroy the dedicated-lower-stream. Note that slp is destroyed
9729073SCathy.Zhou@Sun.COM 	 * when lh is closed.
9739073SCathy.Zhou@Sun.COM 	 */
9749073SCathy.Zhou@Sun.COM 	(void) ldi_close(sup->su_slp->sl_lh, FREAD|FWRITE, kcred);
9759073SCathy.Zhou@Sun.COM 	sup->su_slp = NULL;
9769073SCathy.Zhou@Sun.COM 	mutex_exit(&sup->su_mutex);
9779073SCathy.Zhou@Sun.COM }
9789073SCathy.Zhou@Sun.COM 
9799073SCathy.Zhou@Sun.COM void
softmac_wput_data(softmac_upper_t * sup,mblk_t * mp)9809073SCathy.Zhou@Sun.COM softmac_wput_data(softmac_upper_t *sup, mblk_t *mp)
9819073SCathy.Zhou@Sun.COM {
9829073SCathy.Zhou@Sun.COM 	/*
9839073SCathy.Zhou@Sun.COM 	 * No lock is required to access the su_mode field since the data
9849073SCathy.Zhou@Sun.COM 	 * traffic is quiesce by IP when the data-path mode is in the
9859073SCathy.Zhou@Sun.COM 	 * process of switching.
9869073SCathy.Zhou@Sun.COM 	 */
9879073SCathy.Zhou@Sun.COM 	if (sup->su_mode != SOFTMAC_FASTPATH)
9889073SCathy.Zhou@Sun.COM 		dld_wput(sup->su_wq, mp);
9899073SCathy.Zhou@Sun.COM 	else
9909073SCathy.Zhou@Sun.COM 		(void) softmac_fastpath_wput_data(sup, mp, NULL, 0);
9919073SCathy.Zhou@Sun.COM }
9929073SCathy.Zhou@Sun.COM 
9939073SCathy.Zhou@Sun.COM /*ARGSUSED*/
9949073SCathy.Zhou@Sun.COM static mac_tx_cookie_t
softmac_fastpath_wput_data(softmac_upper_t * sup,mblk_t * mp,uintptr_t f_hint,uint16_t flag)9959073SCathy.Zhou@Sun.COM softmac_fastpath_wput_data(softmac_upper_t *sup, mblk_t *mp, uintptr_t f_hint,
9969073SCathy.Zhou@Sun.COM     uint16_t flag)
9979073SCathy.Zhou@Sun.COM {
9989073SCathy.Zhou@Sun.COM 	queue_t		*wq = sup->su_slp->sl_wq;
9999073SCathy.Zhou@Sun.COM 
10009073SCathy.Zhou@Sun.COM 	/*
10019073SCathy.Zhou@Sun.COM 	 * This function is called from IP, only the MAC_DROP_ON_NO_DESC
10029073SCathy.Zhou@Sun.COM 	 * flag can be specified.
10039073SCathy.Zhou@Sun.COM 	 */
10049073SCathy.Zhou@Sun.COM 	ASSERT((flag & ~MAC_DROP_ON_NO_DESC) == 0);
10059073SCathy.Zhou@Sun.COM 	ASSERT(mp->b_next == NULL);
10069073SCathy.Zhou@Sun.COM 
10079073SCathy.Zhou@Sun.COM 	/*
10089073SCathy.Zhou@Sun.COM 	 * Check wether the dedicated-lower-stream is able to handle more
10099073SCathy.Zhou@Sun.COM 	 * messages, and enable the flow-control if it is not.
10109073SCathy.Zhou@Sun.COM 	 *
10119073SCathy.Zhou@Sun.COM 	 * Note that in order not to introduce any packet reordering, we
10129073SCathy.Zhou@Sun.COM 	 * always send the message down to the dedicated-lower-stream:
10139073SCathy.Zhou@Sun.COM 	 *
10149073SCathy.Zhou@Sun.COM 	 * If the flow-control is already enabled, but we still get
10159073SCathy.Zhou@Sun.COM 	 * the messages from the upper-stream, it means that the upper
10169073SCathy.Zhou@Sun.COM 	 * stream does not respect STREAMS flow-control (e.g., TCP). Simply
10179073SCathy.Zhou@Sun.COM 	 * pass the message down to the lower-stream in that case.
10189073SCathy.Zhou@Sun.COM 	 */
10199073SCathy.Zhou@Sun.COM 	if (SOFTMAC_CANPUTNEXT(wq)) {
10209073SCathy.Zhou@Sun.COM 		putnext(wq, mp);
10219073SCathy.Zhou@Sun.COM 		return (NULL);
10229073SCathy.Zhou@Sun.COM 	}
10239073SCathy.Zhou@Sun.COM 
10249073SCathy.Zhou@Sun.COM 	if (sup->su_tx_busy) {
10259738SCathy.Zhou@Sun.COM 		if ((flag & MAC_DROP_ON_NO_DESC) != 0)
10269738SCathy.Zhou@Sun.COM 			freemsg(mp);
10279738SCathy.Zhou@Sun.COM 		else
10289738SCathy.Zhou@Sun.COM 			putnext(wq, mp);
10299738SCathy.Zhou@Sun.COM 		return ((mac_tx_cookie_t)sup);
10309073SCathy.Zhou@Sun.COM 	}
10319073SCathy.Zhou@Sun.COM 
10329073SCathy.Zhou@Sun.COM 	mutex_enter(&sup->su_mutex);
10339073SCathy.Zhou@Sun.COM 	if (!sup->su_tx_busy) {
10349738SCathy.Zhou@Sun.COM 		/*
10359738SCathy.Zhou@Sun.COM 		 * If DLD_CAPAB_DIRECT is enabled, the notify callback will be
10369738SCathy.Zhou@Sun.COM 		 * called when the flow control can be disabled. Otherwise,
10379738SCathy.Zhou@Sun.COM 		 * put the tx_flow_mp into the wq to make use of the old
10389738SCathy.Zhou@Sun.COM 		 * streams flow control.
10399738SCathy.Zhou@Sun.COM 		 */
10409073SCathy.Zhou@Sun.COM 		ASSERT(sup->su_tx_flow_mp != NULL);
10419073SCathy.Zhou@Sun.COM 		(void) putq(sup->su_wq, sup->su_tx_flow_mp);
10429073SCathy.Zhou@Sun.COM 		sup->su_tx_flow_mp = NULL;
10439073SCathy.Zhou@Sun.COM 		sup->su_tx_busy = B_TRUE;
10449073SCathy.Zhou@Sun.COM 		qenable(wq);
10459073SCathy.Zhou@Sun.COM 	}
10469073SCathy.Zhou@Sun.COM 	mutex_exit(&sup->su_mutex);
10479738SCathy.Zhou@Sun.COM 
10489738SCathy.Zhou@Sun.COM 	if ((flag & MAC_DROP_ON_NO_DESC) != 0)
10499738SCathy.Zhou@Sun.COM 		freemsg(mp);
10509738SCathy.Zhou@Sun.COM 	else
10519738SCathy.Zhou@Sun.COM 		putnext(wq, mp);
10529738SCathy.Zhou@Sun.COM 	return ((mac_tx_cookie_t)sup);
10539073SCathy.Zhou@Sun.COM }
10549073SCathy.Zhou@Sun.COM 
10559073SCathy.Zhou@Sun.COM boolean_t
softmac_active_set(void * arg)10569073SCathy.Zhou@Sun.COM softmac_active_set(void *arg)
10579073SCathy.Zhou@Sun.COM {
10589073SCathy.Zhou@Sun.COM 	softmac_t	*softmac = arg;
10599073SCathy.Zhou@Sun.COM 
10609073SCathy.Zhou@Sun.COM 	mutex_enter(&softmac->smac_active_mutex);
10619073SCathy.Zhou@Sun.COM 	if (softmac->smac_nactive != 0) {
10629073SCathy.Zhou@Sun.COM 		mutex_exit(&softmac->smac_active_mutex);
10639073SCathy.Zhou@Sun.COM 		return (B_FALSE);
10649073SCathy.Zhou@Sun.COM 	}
10659073SCathy.Zhou@Sun.COM 	softmac->smac_active = B_TRUE;
10669073SCathy.Zhou@Sun.COM 	mutex_exit(&softmac->smac_active_mutex);
10679073SCathy.Zhou@Sun.COM 	return (B_TRUE);
10689073SCathy.Zhou@Sun.COM }
10699073SCathy.Zhou@Sun.COM 
10709073SCathy.Zhou@Sun.COM void
softmac_active_clear(void * arg)10719073SCathy.Zhou@Sun.COM softmac_active_clear(void *arg)
10729073SCathy.Zhou@Sun.COM {
10739073SCathy.Zhou@Sun.COM 	softmac_t	*softmac = arg;
10749073SCathy.Zhou@Sun.COM 
10759073SCathy.Zhou@Sun.COM 	mutex_enter(&softmac->smac_active_mutex);
10769073SCathy.Zhou@Sun.COM 	ASSERT(softmac->smac_active && (softmac->smac_nactive == 0));
10779073SCathy.Zhou@Sun.COM 	softmac->smac_active = B_FALSE;
10789073SCathy.Zhou@Sun.COM 	mutex_exit(&softmac->smac_active_mutex);
10799073SCathy.Zhou@Sun.COM }
10809073SCathy.Zhou@Sun.COM 
10819073SCathy.Zhou@Sun.COM /*
10829073SCathy.Zhou@Sun.COM  * Disable/reenable fastpath on given softmac. This request could come from a
10839073SCathy.Zhou@Sun.COM  * MAC client or directly from administrators.
10849073SCathy.Zhou@Sun.COM  */
10859073SCathy.Zhou@Sun.COM int
softmac_datapath_switch(softmac_t * softmac,boolean_t disable,boolean_t admin)10869073SCathy.Zhou@Sun.COM softmac_datapath_switch(softmac_t *softmac, boolean_t disable, boolean_t admin)
10879073SCathy.Zhou@Sun.COM {
10889073SCathy.Zhou@Sun.COM 	softmac_upper_t		*sup;
10899073SCathy.Zhou@Sun.COM 	mblk_t			*head = NULL, *tail = NULL, *mp;
10909073SCathy.Zhou@Sun.COM 	list_t			reqlist;
10919073SCathy.Zhou@Sun.COM 	softmac_switch_req_t	*req;
10929073SCathy.Zhou@Sun.COM 	uint32_t		current_mode, expected_mode;
10939073SCathy.Zhou@Sun.COM 	int			err = 0;
10949073SCathy.Zhou@Sun.COM 
10959073SCathy.Zhou@Sun.COM 	mutex_enter(&softmac->smac_fp_mutex);
10969073SCathy.Zhou@Sun.COM 
10979073SCathy.Zhou@Sun.COM 	current_mode = DATAPATH_MODE(softmac);
10989073SCathy.Zhou@Sun.COM 	if (admin) {
10999073SCathy.Zhou@Sun.COM 		if (softmac->smac_fastpath_admin_disabled == disable) {
11009073SCathy.Zhou@Sun.COM 			mutex_exit(&softmac->smac_fp_mutex);
11019073SCathy.Zhou@Sun.COM 			return (0);
11029073SCathy.Zhou@Sun.COM 		}
11039073SCathy.Zhou@Sun.COM 		softmac->smac_fastpath_admin_disabled = disable;
11049073SCathy.Zhou@Sun.COM 	} else if (disable) {
11059073SCathy.Zhou@Sun.COM 		softmac->smac_fp_disable_clients++;
11069073SCathy.Zhou@Sun.COM 	} else {
11079073SCathy.Zhou@Sun.COM 		ASSERT(softmac->smac_fp_disable_clients != 0);
11089073SCathy.Zhou@Sun.COM 		softmac->smac_fp_disable_clients--;
11099073SCathy.Zhou@Sun.COM 	}
11109073SCathy.Zhou@Sun.COM 
11119073SCathy.Zhou@Sun.COM 	expected_mode = DATAPATH_MODE(softmac);
11129073SCathy.Zhou@Sun.COM 	if (current_mode == expected_mode) {
11139073SCathy.Zhou@Sun.COM 		mutex_exit(&softmac->smac_fp_mutex);
11149073SCathy.Zhou@Sun.COM 		return (0);
11159073SCathy.Zhou@Sun.COM 	}
11169073SCathy.Zhou@Sun.COM 
11179073SCathy.Zhou@Sun.COM 	/*
11189073SCathy.Zhou@Sun.COM 	 * The expected mode is different from whatever datapath mode
11199073SCathy.Zhou@Sun.COM 	 * this softmac is expected from last request, enqueue the data-path
11209073SCathy.Zhou@Sun.COM 	 * switch request.
11219073SCathy.Zhou@Sun.COM 	 */
11229073SCathy.Zhou@Sun.COM 	list_create(&reqlist, sizeof (softmac_switch_req_t),
11239073SCathy.Zhou@Sun.COM 	    offsetof(softmac_switch_req_t, ssq_req_list_node));
11249073SCathy.Zhou@Sun.COM 
11259073SCathy.Zhou@Sun.COM 	/*
11269073SCathy.Zhou@Sun.COM 	 * Allocate all DL_NOTIFY_IND messages and request structures that
11279073SCathy.Zhou@Sun.COM 	 * are required to switch each IP/ARP stream to the expected mode.
11289073SCathy.Zhou@Sun.COM 	 */
11299073SCathy.Zhou@Sun.COM 	for (sup = list_head(&softmac->smac_sup_list); sup != NULL;
11309073SCathy.Zhou@Sun.COM 	    sup = list_next(&softmac->smac_sup_list, sup)) {
11319073SCathy.Zhou@Sun.COM 		dl_notify_ind_t	*dlip;
11329073SCathy.Zhou@Sun.COM 
11339073SCathy.Zhou@Sun.COM 		req = kmem_alloc(sizeof (softmac_switch_req_t), KM_NOSLEEP);
11349073SCathy.Zhou@Sun.COM 		if (req == NULL)
11359073SCathy.Zhou@Sun.COM 			break;
11369073SCathy.Zhou@Sun.COM 
11379073SCathy.Zhou@Sun.COM 		req->ssq_expected_mode = expected_mode;
1138*11042SErik.Nordmark@Sun.COM 		if (sup->su_is_arp) {
1139*11042SErik.Nordmark@Sun.COM 			list_insert_tail(&reqlist, req);
1140*11042SErik.Nordmark@Sun.COM 			continue;
1141*11042SErik.Nordmark@Sun.COM 		}
11429073SCathy.Zhou@Sun.COM 		/*
11439073SCathy.Zhou@Sun.COM 		 * Allocate the DL_NOTE_REPLUMB message.
11449073SCathy.Zhou@Sun.COM 		 */
11459073SCathy.Zhou@Sun.COM 		if ((mp = allocb(sizeof (dl_notify_ind_t), BPRI_LO)) == NULL) {
11469073SCathy.Zhou@Sun.COM 			kmem_free(req, sizeof (softmac_switch_req_t));
11479073SCathy.Zhou@Sun.COM 			break;
11489073SCathy.Zhou@Sun.COM 		}
11499073SCathy.Zhou@Sun.COM 
11509073SCathy.Zhou@Sun.COM 		list_insert_tail(&reqlist, req);
11519073SCathy.Zhou@Sun.COM 
11529073SCathy.Zhou@Sun.COM 		mp->b_wptr = mp->b_rptr + sizeof (dl_notify_ind_t);
11539073SCathy.Zhou@Sun.COM 		mp->b_datap->db_type = M_PROTO;
11549073SCathy.Zhou@Sun.COM 		bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
11559073SCathy.Zhou@Sun.COM 		dlip = (dl_notify_ind_t *)mp->b_rptr;
11569073SCathy.Zhou@Sun.COM 		dlip->dl_primitive = DL_NOTIFY_IND;
11579073SCathy.Zhou@Sun.COM 		dlip->dl_notification = DL_NOTE_REPLUMB;
11589073SCathy.Zhou@Sun.COM 		if (head == NULL) {
11599073SCathy.Zhou@Sun.COM 			head = tail = mp;
11609073SCathy.Zhou@Sun.COM 		} else {
11619073SCathy.Zhou@Sun.COM 			tail->b_next = mp;
11629073SCathy.Zhou@Sun.COM 			tail = mp;
11639073SCathy.Zhou@Sun.COM 		}
11649073SCathy.Zhou@Sun.COM 	}
11659073SCathy.Zhou@Sun.COM 
11669073SCathy.Zhou@Sun.COM 	/*
11679073SCathy.Zhou@Sun.COM 	 * Note that it is fine if the expected data-path mode is fast-path
11689073SCathy.Zhou@Sun.COM 	 * and some of streams fails to switch. Only return failure if we
11699073SCathy.Zhou@Sun.COM 	 * are expected to switch to the slow-path.
11709073SCathy.Zhou@Sun.COM 	 */
11719073SCathy.Zhou@Sun.COM 	if (sup != NULL && expected_mode == SOFTMAC_SLOWPATH) {
11729073SCathy.Zhou@Sun.COM 		err = ENOMEM;
11739073SCathy.Zhou@Sun.COM 		goto fail;
11749073SCathy.Zhou@Sun.COM 	}
11759073SCathy.Zhou@Sun.COM 
11769073SCathy.Zhou@Sun.COM 	/*
11779073SCathy.Zhou@Sun.COM 	 * Start switching for each IP/ARP stream. The switching operation
11789073SCathy.Zhou@Sun.COM 	 * will eventually succeed and there is no need to wait for it
11799073SCathy.Zhou@Sun.COM 	 * to finish.
11809073SCathy.Zhou@Sun.COM 	 */
11819073SCathy.Zhou@Sun.COM 	for (sup = list_head(&softmac->smac_sup_list); sup != NULL;
11829073SCathy.Zhou@Sun.COM 	    sup = list_next(&softmac->smac_sup_list, sup)) {
1183*11042SErik.Nordmark@Sun.COM 		if (!sup->su_is_arp) {
1184*11042SErik.Nordmark@Sun.COM 			mp = head->b_next;
1185*11042SErik.Nordmark@Sun.COM 			head->b_next = NULL;
1186*11042SErik.Nordmark@Sun.COM 			softmac_wput_nondata(sup, head);
1187*11042SErik.Nordmark@Sun.COM 			head = mp;
1188*11042SErik.Nordmark@Sun.COM 		}
11899073SCathy.Zhou@Sun.COM 		/*
1190*11042SErik.Nordmark@Sun.COM 		 * Add the switch request to the requests list of the stream.
11919073SCathy.Zhou@Sun.COM 		 */
11929073SCathy.Zhou@Sun.COM 		req = list_head(&reqlist);
11939073SCathy.Zhou@Sun.COM 		ASSERT(req != NULL);
11949073SCathy.Zhou@Sun.COM 		list_remove(&reqlist, req);
11959073SCathy.Zhou@Sun.COM 		list_insert_tail(&sup->su_req_list, req);
11969073SCathy.Zhou@Sun.COM 	}
11979073SCathy.Zhou@Sun.COM 
11989073SCathy.Zhou@Sun.COM 	mutex_exit(&softmac->smac_fp_mutex);
11999073SCathy.Zhou@Sun.COM 	ASSERT(list_is_empty(&reqlist));
12009073SCathy.Zhou@Sun.COM 	list_destroy(&reqlist);
12019073SCathy.Zhou@Sun.COM 	return (0);
12029073SCathy.Zhou@Sun.COM fail:
12039073SCathy.Zhou@Sun.COM 	if (admin) {
12049073SCathy.Zhou@Sun.COM 		softmac->smac_fastpath_admin_disabled = !disable;
12059073SCathy.Zhou@Sun.COM 	} else if (disable) {
12069073SCathy.Zhou@Sun.COM 		softmac->smac_fp_disable_clients--;
12079073SCathy.Zhou@Sun.COM 	} else {
12089073SCathy.Zhou@Sun.COM 		softmac->smac_fp_disable_clients++;
12099073SCathy.Zhou@Sun.COM 	}
12109073SCathy.Zhou@Sun.COM 
12119073SCathy.Zhou@Sun.COM 	mutex_exit(&softmac->smac_fp_mutex);
12129073SCathy.Zhou@Sun.COM 	while ((req = list_head(&reqlist)) != NULL) {
12139073SCathy.Zhou@Sun.COM 		list_remove(&reqlist, req);
12149073SCathy.Zhou@Sun.COM 		kmem_free(req, sizeof (softmac_switch_req_t));
12159073SCathy.Zhou@Sun.COM 	}
12169073SCathy.Zhou@Sun.COM 	freemsgchain(head);
12179073SCathy.Zhou@Sun.COM 	list_destroy(&reqlist);
12189073SCathy.Zhou@Sun.COM 	return (err);
12199073SCathy.Zhou@Sun.COM }
12209073SCathy.Zhou@Sun.COM 
12219073SCathy.Zhou@Sun.COM int
softmac_fastpath_disable(void * arg)12229073SCathy.Zhou@Sun.COM softmac_fastpath_disable(void *arg)
12239073SCathy.Zhou@Sun.COM {
12249073SCathy.Zhou@Sun.COM 	return (softmac_datapath_switch((softmac_t *)arg, B_TRUE, B_FALSE));
12259073SCathy.Zhou@Sun.COM }
12269073SCathy.Zhou@Sun.COM 
12279073SCathy.Zhou@Sun.COM void
softmac_fastpath_enable(void * arg)12289073SCathy.Zhou@Sun.COM softmac_fastpath_enable(void *arg)
12299073SCathy.Zhou@Sun.COM {
12309073SCathy.Zhou@Sun.COM 	VERIFY(softmac_datapath_switch((softmac_t *)arg, B_FALSE,
12319073SCathy.Zhou@Sun.COM 	    B_FALSE) == 0);
12329073SCathy.Zhou@Sun.COM }
12339073SCathy.Zhou@Sun.COM 
12349073SCathy.Zhou@Sun.COM void
softmac_upperstream_close(softmac_upper_t * sup)12359073SCathy.Zhou@Sun.COM softmac_upperstream_close(softmac_upper_t *sup)
12369073SCathy.Zhou@Sun.COM {
12379073SCathy.Zhou@Sun.COM 	softmac_t		*softmac = sup->su_softmac;
12389073SCathy.Zhou@Sun.COM 	softmac_switch_req_t	*req;
12399073SCathy.Zhou@Sun.COM 
12409073SCathy.Zhou@Sun.COM 	mutex_enter(&softmac->smac_fp_mutex);
12419073SCathy.Zhou@Sun.COM 
12429073SCathy.Zhou@Sun.COM 	if (sup->su_mode == SOFTMAC_FASTPATH)
12439073SCathy.Zhou@Sun.COM 		softmac_fastpath_tear(sup);
12449073SCathy.Zhou@Sun.COM 
12459073SCathy.Zhou@Sun.COM 	if (sup->su_mode != SOFTMAC_UNKNOWN) {
12469073SCathy.Zhou@Sun.COM 		list_remove(&softmac->smac_sup_list, sup);
12479073SCathy.Zhou@Sun.COM 		sup->su_mode = SOFTMAC_UNKNOWN;
12489073SCathy.Zhou@Sun.COM 	}
12499073SCathy.Zhou@Sun.COM 
12509073SCathy.Zhou@Sun.COM 	/*
12519073SCathy.Zhou@Sun.COM 	 * Cleanup all the switch requests queueed on this stream.
12529073SCathy.Zhou@Sun.COM 	 */
12539073SCathy.Zhou@Sun.COM 	while ((req = list_head(&sup->su_req_list)) != NULL) {
12549073SCathy.Zhou@Sun.COM 		list_remove(&sup->su_req_list, req);
12559073SCathy.Zhou@Sun.COM 		kmem_free(req, sizeof (softmac_switch_req_t));
12569073SCathy.Zhou@Sun.COM 	}
12579073SCathy.Zhou@Sun.COM 	mutex_exit(&softmac->smac_fp_mutex);
12589073SCathy.Zhou@Sun.COM }
12599073SCathy.Zhou@Sun.COM 
12609073SCathy.Zhou@Sun.COM /*
12619073SCathy.Zhou@Sun.COM  * Handle the DL_NOTE_REPLUMB_DONE indication from IP/ARP. Change the upper
12629073SCathy.Zhou@Sun.COM  * stream from the fastpath mode to the slowpath mode.
12639073SCathy.Zhou@Sun.COM  */
12649073SCathy.Zhou@Sun.COM static void
softmac_datapath_switch_done(softmac_upper_t * sup)12659073SCathy.Zhou@Sun.COM softmac_datapath_switch_done(softmac_upper_t *sup)
12669073SCathy.Zhou@Sun.COM {
12679073SCathy.Zhou@Sun.COM 	softmac_t		*softmac = sup->su_softmac;
12689073SCathy.Zhou@Sun.COM 	softmac_switch_req_t	*req;
12699073SCathy.Zhou@Sun.COM 	uint32_t		expected_mode;
12709073SCathy.Zhou@Sun.COM 
12719073SCathy.Zhou@Sun.COM 	mutex_enter(&softmac->smac_fp_mutex);
12729073SCathy.Zhou@Sun.COM 	req = list_head(&sup->su_req_list);
12739073SCathy.Zhou@Sun.COM 	list_remove(&sup->su_req_list, req);
12749073SCathy.Zhou@Sun.COM 	expected_mode = req->ssq_expected_mode;
12759073SCathy.Zhou@Sun.COM 	kmem_free(req, sizeof (softmac_switch_req_t));
12769073SCathy.Zhou@Sun.COM 
12779073SCathy.Zhou@Sun.COM 	if (expected_mode == sup->su_mode) {
12789073SCathy.Zhou@Sun.COM 		mutex_exit(&softmac->smac_fp_mutex);
12799073SCathy.Zhou@Sun.COM 		return;
12809073SCathy.Zhou@Sun.COM 	}
12819073SCathy.Zhou@Sun.COM 
12829073SCathy.Zhou@Sun.COM 	ASSERT(!sup->su_bound);
12839073SCathy.Zhou@Sun.COM 	mutex_exit(&softmac->smac_fp_mutex);
12849073SCathy.Zhou@Sun.COM 
12859073SCathy.Zhou@Sun.COM 	/*
12869073SCathy.Zhou@Sun.COM 	 * It is fine if the expected mode is fast-path and we fail
12879073SCathy.Zhou@Sun.COM 	 * to enable fastpath on this stream.
12889073SCathy.Zhou@Sun.COM 	 */
12899073SCathy.Zhou@Sun.COM 	if (expected_mode == SOFTMAC_SLOWPATH)
12909073SCathy.Zhou@Sun.COM 		softmac_fastpath_tear(sup);
12919073SCathy.Zhou@Sun.COM 	else
12929073SCathy.Zhou@Sun.COM 		(void) softmac_fastpath_setup(sup);
12939073SCathy.Zhou@Sun.COM }
1294