xref: /titanic_51/usr/src/uts/common/io/softmac/softmac_fp.c (revision bd670b35a010421b6e1a5536c34453a827007c81)
15d460eafSCathy Zhou /*
25d460eafSCathy Zhou  * CDDL HEADER START
35d460eafSCathy Zhou  *
45d460eafSCathy Zhou  * The contents of this file are subject to the terms of the
55d460eafSCathy Zhou  * Common Development and Distribution License (the "License").
65d460eafSCathy Zhou  * You may not use this file except in compliance with the License.
75d460eafSCathy Zhou  *
85d460eafSCathy Zhou  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95d460eafSCathy Zhou  * or http://www.opensolaris.org/os/licensing.
105d460eafSCathy Zhou  * See the License for the specific language governing permissions
115d460eafSCathy Zhou  * and limitations under the License.
125d460eafSCathy Zhou  *
135d460eafSCathy Zhou  * When distributing Covered Code, include this CDDL HEADER in each
145d460eafSCathy Zhou  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155d460eafSCathy Zhou  * If applicable, add the following below this CDDL HEADER, with the
165d460eafSCathy Zhou  * fields enclosed by brackets "[]" replaced with your own identifying
175d460eafSCathy Zhou  * information: Portions Copyright [yyyy] [name of copyright owner]
185d460eafSCathy Zhou  *
195d460eafSCathy Zhou  * CDDL HEADER END
205d460eafSCathy Zhou  */
215d460eafSCathy Zhou /*
225d460eafSCathy Zhou  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235d460eafSCathy Zhou  * Use is subject to license terms.
245d460eafSCathy Zhou  */
255d460eafSCathy Zhou 
265d460eafSCathy Zhou /*
275d460eafSCathy Zhou  * Softmac data-path switching:
285d460eafSCathy Zhou  *
295d460eafSCathy Zhou  * - Fast-path model
305d460eafSCathy Zhou  *
315d460eafSCathy Zhou  * When the softmac fast-path is used, a dedicated lower-stream
325d460eafSCathy Zhou  * will be opened over the legacy device for each IP/ARP (upper-)stream
335d460eafSCathy Zhou  * over the softMAC, and all DLPI messages (including control messages
345d460eafSCathy Zhou  * and data messages) will be exchanged between the upper-stream and
355d460eafSCathy Zhou  * the corresponding lower-stream directly. Therefore, the data
365d460eafSCathy Zhou  * demultiplexing, filtering and classification processing will be done
375d460eafSCathy Zhou  * by the lower-stream, and the GLDv3 DLS/MAC layer processing will be
385d460eafSCathy Zhou  * no longer needed.
395d460eafSCathy Zhou  *
405d460eafSCathy Zhou  * - Slow-path model
415d460eafSCathy Zhou  *
425d460eafSCathy Zhou  * Some GLDv3 features requires the GLDv3 DLS/MAC layer processing to
435d460eafSCathy Zhou  * not be bypassed to assure its function correctness. For example,
445d460eafSCathy Zhou  * softmac fast-path must be disabled to support GLDv3 VNIC functionality.
455d460eafSCathy Zhou  * In this case, a shared lower-stream will be opened over the legacy
465d460eafSCathy Zhou  * device, which is responsible for implementing the GLDv3 callbacks
475d460eafSCathy Zhou  * and passing RAW data messages between the legacy devices and the GLDv3
485d460eafSCathy Zhou  * framework.
495d460eafSCathy Zhou  *
505d460eafSCathy Zhou  * By default, the softmac fast-path mode will be used to assure the
515d460eafSCathy Zhou  * performance; MAC clients will be able to request to disable the softmac
525d460eafSCathy Zhou  * fast-path mode to support certain features, and if that succeeds,
535d460eafSCathy Zhou  * the system will fallback to the slow-path softmac data-path model.
545d460eafSCathy Zhou  *
555d460eafSCathy Zhou  *
565d460eafSCathy Zhou  * The details of the softmac data fast-path model is stated as below
575d460eafSCathy Zhou  *
585d460eafSCathy Zhou  * 1. When a stream is opened on a softMAC, the softmac module will takes
595d460eafSCathy Zhou  *    over the DLPI processing on this stream;
605d460eafSCathy Zhou  *
615d460eafSCathy Zhou  * 2. For IP/ARP streams over a softMAC, softmac data fast-path will be
625d460eafSCathy Zhou  *    used by default, unless fast-path is disabled by any MAC client
635d460eafSCathy Zhou  *    explicitly. The softmac module first identifies an IP/ARP stream
645d460eafSCathy Zhou  *    by seeing whether there is a SIOCSLIFNAME ioctl sent from upstream,
655d460eafSCathy Zhou  *    if there is one, this stream is either an IP or an ARP stream
665d460eafSCathy Zhou  *    and will use fast-path potentially;
675d460eafSCathy Zhou  *
685d460eafSCathy Zhou  * 3. When the softmac fast-path is used, an dedicated lower-stream will
695d460eafSCathy Zhou  *    be setup for each IP/ARP stream (1-1 mapping). From that point on,
705d460eafSCathy Zhou  *    all control and data messages will be exchanged between the IP/ARP
715d460eafSCathy Zhou  *    upper-stream and the legacy device through this dedicated
725d460eafSCathy Zhou  *    lower-stream. As a result, the DLS/MAC layer processing in GLDv3
735d460eafSCathy Zhou  *    will be skipped, and this greatly improves the performance;
745d460eafSCathy Zhou  *
755d460eafSCathy Zhou  * 4. When the softmac data fast-path is disabled by a MAC client (e.g.,
765d460eafSCathy Zhou  *    by a VNIC), all the IP/ARP upper streams will try to switch from
775d460eafSCathy Zhou  *    the fast-path to the slow-path. The dedicated lower-stream will be
785d460eafSCathy Zhou  *    destroyed, and all the control and data-messages will go through the
795d460eafSCathy Zhou  *    existing GLDv3 code path and (in the end) the shared lower-stream;
805d460eafSCathy Zhou  *
815d460eafSCathy Zhou  * 5. On the other hand, when the last MAC client cancels its fast-path
825d460eafSCathy Zhou  *    disable request, all the IP/ARP streams will try to switch back to
835d460eafSCathy Zhou  *    the fast-path mode;
845d460eafSCathy Zhou  *
855d460eafSCathy Zhou  * Step 5 and 6 both rely on the data-path mode switching process
865d460eafSCathy Zhou  * described below:
875d460eafSCathy Zhou  *
885d460eafSCathy Zhou  * 1) To switch the softmac data-path mode (between fast-path and slow-path),
895d460eafSCathy Zhou  *    softmac will first send a DL_NOTE_REPLUMB DL_NOTIFY_IND message
905d460eafSCathy Zhou  *    upstream over each IP/ARP streams that needs data-path mode switching;
915d460eafSCathy Zhou  *
925d460eafSCathy Zhou  * 2) When IP receives this DL_NOTE_REPLUMB message, it will bring down
935d460eafSCathy Zhou  *    all the IP interfaces on the corresponding ill (IP Lower level
945d460eafSCathy Zhou  *    structure), and bring up those interfaces over again; this will in
955d460eafSCathy Zhou  *    turn cause the ARP to "replumb" the interface.
965d460eafSCathy Zhou  *
975d460eafSCathy Zhou  *    During the replumb process, both IP and ARP will send downstream the
985d460eafSCathy Zhou  *    necessary DL_DISABMULTI_REQ and DL_UNBIND_REQ messages and cleanup
995d460eafSCathy Zhou  *    the old state of the underlying softMAC, following with the necessary
1005d460eafSCathy Zhou  *    DL_BIND_REQ and DL_ENABMULTI_REQ messages to setup the new state.
1015d460eafSCathy Zhou  *    Between the cleanup and re-setup process, IP/ARP will also send down
1025d460eafSCathy Zhou  *    a DL_NOTE_REPLUMB_DONE DL_NOTIFY_CONF messages to the softMAC to
1035d460eafSCathy Zhou  *    indicate the *switching point*;
1045d460eafSCathy Zhou  *
1055d460eafSCathy Zhou  * 3) When softmac receives the DL_NOTE_REPLUMB_DONE message, it either
1065d460eafSCathy Zhou  *    creates or destroys the dedicated lower-stream (depending on which
1075d460eafSCathy Zhou  *    data-path mode the softMAC switches to), and change the softmac
1085d460eafSCathy Zhou  *    data-path mode. From then on, softmac will process all the succeeding
1095d460eafSCathy Zhou  *    control messages (including the DL_BIND_REQ and DL_ENABMULTI_REQ
1105d460eafSCathy Zhou  *    messages) and data messages based on new data-path mode.
1115d460eafSCathy Zhou  */
1125d460eafSCathy Zhou 
1135d460eafSCathy Zhou #include <sys/types.h>
1145d460eafSCathy Zhou #include <sys/disp.h>
1155d460eafSCathy Zhou #include <sys/callb.h>
1165d460eafSCathy Zhou #include <sys/sysmacros.h>
1175d460eafSCathy Zhou #include <sys/file.h>
1185d460eafSCathy Zhou #include <sys/vlan.h>
1195d460eafSCathy Zhou #include <sys/dld.h>
1205d460eafSCathy Zhou #include <sys/sockio.h>
1215d460eafSCathy Zhou #include <sys/softmac_impl.h>
12282a2fc47SJames Carlson #include <net/if.h>
1235d460eafSCathy Zhou 
1245d460eafSCathy Zhou static kmutex_t		softmac_taskq_lock;
1255d460eafSCathy Zhou static kcondvar_t	softmac_taskq_cv;
1265d460eafSCathy Zhou static list_t		softmac_taskq_list;	/* List of softmac_upper_t */
1275d460eafSCathy Zhou boolean_t		softmac_taskq_quit;
1285d460eafSCathy Zhou boolean_t		softmac_taskq_done;
1295d460eafSCathy Zhou 
1305d460eafSCathy Zhou static void		softmac_taskq_dispatch();
1315d460eafSCathy Zhou static int		softmac_fastpath_setup(softmac_upper_t *);
1325d460eafSCathy Zhou static mac_tx_cookie_t	softmac_fastpath_wput_data(softmac_upper_t *, mblk_t *,
1335d460eafSCathy Zhou 			    uintptr_t, uint16_t);
1345d460eafSCathy Zhou static void		softmac_datapath_switch_done(softmac_upper_t *);
1355d460eafSCathy Zhou 
1365d460eafSCathy Zhou void
1375d460eafSCathy Zhou softmac_fp_init()
1385d460eafSCathy Zhou {
1395d460eafSCathy Zhou 	mutex_init(&softmac_taskq_lock, NULL, MUTEX_DRIVER, NULL);
1405d460eafSCathy Zhou 	cv_init(&softmac_taskq_cv, NULL, CV_DRIVER, NULL);
1415d460eafSCathy Zhou 
1425d460eafSCathy Zhou 	softmac_taskq_quit = B_FALSE;
1435d460eafSCathy Zhou 	softmac_taskq_done = B_FALSE;
1445d460eafSCathy Zhou 	list_create(&softmac_taskq_list, sizeof (softmac_upper_t),
1455d460eafSCathy Zhou 	    offsetof(softmac_upper_t, su_taskq_list_node));
1465d460eafSCathy Zhou 	(void) thread_create(NULL, 0, softmac_taskq_dispatch, NULL, 0,
1475d460eafSCathy Zhou 	    &p0, TS_RUN, minclsyspri);
1485d460eafSCathy Zhou }
1495d460eafSCathy Zhou 
1505d460eafSCathy Zhou void
1515d460eafSCathy Zhou softmac_fp_fini()
1525d460eafSCathy Zhou {
1535d460eafSCathy Zhou 	/*
1545d460eafSCathy Zhou 	 * Request the softmac_taskq thread to quit and wait for it to be done.
1555d460eafSCathy Zhou 	 */
1565d460eafSCathy Zhou 	mutex_enter(&softmac_taskq_lock);
1575d460eafSCathy Zhou 	softmac_taskq_quit = B_TRUE;
1585d460eafSCathy Zhou 	cv_signal(&softmac_taskq_cv);
1595d460eafSCathy Zhou 	while (!softmac_taskq_done)
1605d460eafSCathy Zhou 		cv_wait(&softmac_taskq_cv, &softmac_taskq_lock);
1615d460eafSCathy Zhou 	mutex_exit(&softmac_taskq_lock);
1625d460eafSCathy Zhou 	list_destroy(&softmac_taskq_list);
1635d460eafSCathy Zhou 
1645d460eafSCathy Zhou 	mutex_destroy(&softmac_taskq_lock);
1655d460eafSCathy Zhou 	cv_destroy(&softmac_taskq_cv);
1665d460eafSCathy Zhou }
1675d460eafSCathy Zhou 
1685d460eafSCathy Zhou static boolean_t
1695d460eafSCathy Zhou check_ip_above(queue_t *q)
1705d460eafSCathy Zhou {
1715d460eafSCathy Zhou 	queue_t		*next_q;
1725d460eafSCathy Zhou 	boolean_t	ret = B_TRUE;
1735d460eafSCathy Zhou 
1745d460eafSCathy Zhou 	claimstr(q);
1755d460eafSCathy Zhou 	next_q = q->q_next;
1765d460eafSCathy Zhou 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
1775d460eafSCathy Zhou 		ret = B_FALSE;
1785d460eafSCathy Zhou 	releasestr(q);
1795d460eafSCathy Zhou 	return (ret);
1805d460eafSCathy Zhou }
1815d460eafSCathy Zhou 
1825d460eafSCathy Zhou /* ARGSUSED */
1835d460eafSCathy Zhou static int
1845d460eafSCathy Zhou softmac_capab_perim(softmac_upper_t *sup, void *data, uint_t flags)
1855d460eafSCathy Zhou {
1865d460eafSCathy Zhou 	switch (flags) {
1875d460eafSCathy Zhou 	case DLD_ENABLE:
1885d460eafSCathy Zhou 		mutex_enter(&sup->su_mutex);
1895d460eafSCathy Zhou 		break;
1905d460eafSCathy Zhou 	case DLD_DISABLE:
1915d460eafSCathy Zhou 		mutex_exit(&sup->su_mutex);
1925d460eafSCathy Zhou 		break;
1935d460eafSCathy Zhou 	case DLD_QUERY:
1945d460eafSCathy Zhou 		return (MUTEX_HELD(&sup->su_mutex));
1955d460eafSCathy Zhou 	}
1965d460eafSCathy Zhou 	return (0);
1975d460eafSCathy Zhou }
1985d460eafSCathy Zhou 
1995d460eafSCathy Zhou static mac_tx_notify_handle_t
20079eeb645SCathy Zhou softmac_client_tx_notify(softmac_upper_t *sup, mac_tx_notify_t func, void *arg)
2015d460eafSCathy Zhou {
20279eeb645SCathy Zhou 	ASSERT(MUTEX_HELD(&sup->su_mutex));
20379eeb645SCathy Zhou 
20479eeb645SCathy Zhou 	if (func != NULL) {
20579eeb645SCathy Zhou 		sup->su_tx_notify_func = func;
20679eeb645SCathy Zhou 		sup->su_tx_notify_arg = arg;
20779eeb645SCathy Zhou 	} else {
20879eeb645SCathy Zhou 		/*
20979eeb645SCathy Zhou 		 * Wait for all tx_notify_func call to be done.
21079eeb645SCathy Zhou 		 */
21179eeb645SCathy Zhou 		while (sup->su_tx_inprocess != 0)
21279eeb645SCathy Zhou 			cv_wait(&sup->su_cv, &sup->su_mutex);
21379eeb645SCathy Zhou 
21479eeb645SCathy Zhou 		sup->su_tx_notify_func = NULL;
21579eeb645SCathy Zhou 		sup->su_tx_notify_arg = NULL;
21679eeb645SCathy Zhou 	}
21779eeb645SCathy Zhou 	return ((mac_tx_notify_handle_t)sup);
21879eeb645SCathy Zhou }
21979eeb645SCathy Zhou 
22079eeb645SCathy Zhou static boolean_t
22179eeb645SCathy Zhou softmac_tx_is_flow_blocked(softmac_upper_t *sup, mac_tx_cookie_t cookie)
22279eeb645SCathy Zhou {
22379eeb645SCathy Zhou 	ASSERT(cookie == (mac_tx_cookie_t)sup);
22479eeb645SCathy Zhou 	return (sup->su_tx_busy);
2255d460eafSCathy Zhou }
2265d460eafSCathy Zhou 
2275d460eafSCathy Zhou static int
2285d460eafSCathy Zhou softmac_capab_direct(softmac_upper_t *sup, void *data, uint_t flags)
2295d460eafSCathy Zhou {
2305d460eafSCathy Zhou 	dld_capab_direct_t	*direct = data;
2315d460eafSCathy Zhou 	softmac_lower_t		*slp = sup->su_slp;
2325d460eafSCathy Zhou 
2335d460eafSCathy Zhou 	ASSERT(MUTEX_HELD(&sup->su_mutex));
2345d460eafSCathy Zhou 
2355d460eafSCathy Zhou 	ASSERT(sup->su_mode == SOFTMAC_FASTPATH);
2365d460eafSCathy Zhou 
2375d460eafSCathy Zhou 	switch (flags) {
2385d460eafSCathy Zhou 	case DLD_ENABLE:
2395d460eafSCathy Zhou 		if (sup->su_direct)
2405d460eafSCathy Zhou 			return (0);
2415d460eafSCathy Zhou 
2425d460eafSCathy Zhou 		sup->su_direct_rxinfo.slr_rx = (softmac_rx_t)direct->di_rx_cf;
2435d460eafSCathy Zhou 		sup->su_direct_rxinfo.slr_arg = direct->di_rx_ch;
2445d460eafSCathy Zhou 		slp->sl_rxinfo = &sup->su_direct_rxinfo;
2455d460eafSCathy Zhou 		direct->di_tx_df = (uintptr_t)softmac_fastpath_wput_data;
2465d460eafSCathy Zhou 		direct->di_tx_dh = sup;
24779eeb645SCathy Zhou 		direct->di_tx_fctl_df = (uintptr_t)softmac_tx_is_flow_blocked;
24879eeb645SCathy Zhou 		direct->di_tx_fctl_dh = sup;
2495d460eafSCathy Zhou 		direct->di_tx_cb_df = (uintptr_t)softmac_client_tx_notify;
25079eeb645SCathy Zhou 		direct->di_tx_cb_dh = sup;
2515d460eafSCathy Zhou 		sup->su_direct = B_TRUE;
2525d460eafSCathy Zhou 		return (0);
2535d460eafSCathy Zhou 
2545d460eafSCathy Zhou 	case DLD_DISABLE:
2555d460eafSCathy Zhou 		if (!sup->su_direct)
2565d460eafSCathy Zhou 			return (0);
2575d460eafSCathy Zhou 
2585d460eafSCathy Zhou 		slp->sl_rxinfo = &sup->su_rxinfo;
2595d460eafSCathy Zhou 		sup->su_direct = B_FALSE;
2605d460eafSCathy Zhou 		return (0);
2615d460eafSCathy Zhou 	}
2625d460eafSCathy Zhou 	return (ENOTSUP);
2635d460eafSCathy Zhou }
2645d460eafSCathy Zhou 
2655d460eafSCathy Zhou static int
2665d460eafSCathy Zhou softmac_dld_capab(softmac_upper_t *sup, uint_t type, void *data, uint_t flags)
2675d460eafSCathy Zhou {
2685d460eafSCathy Zhou 	int	err;
2695d460eafSCathy Zhou 
2705d460eafSCathy Zhou 	/*
2715d460eafSCathy Zhou 	 * Don't enable direct callback capabilities unless the caller is
2725d460eafSCathy Zhou 	 * the IP client. When a module is inserted in a stream (_I_INSERT)
2735d460eafSCathy Zhou 	 * the stack initiates capability disable, but due to races, the
2745d460eafSCathy Zhou 	 * module insertion may complete before the capability disable
2755d460eafSCathy Zhou 	 * completes. So we limit the check to DLD_ENABLE case.
2765d460eafSCathy Zhou 	 */
2775d460eafSCathy Zhou 	if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) &&
2785d460eafSCathy Zhou 	    !check_ip_above(sup->su_rq)) {
2795d460eafSCathy Zhou 		return (ENOTSUP);
2805d460eafSCathy Zhou 	}
2815d460eafSCathy Zhou 
2825d460eafSCathy Zhou 	switch (type) {
2835d460eafSCathy Zhou 	case DLD_CAPAB_DIRECT:
2845d460eafSCathy Zhou 		err = softmac_capab_direct(sup, data, flags);
2855d460eafSCathy Zhou 		break;
2865d460eafSCathy Zhou 
2875d460eafSCathy Zhou 	case DLD_CAPAB_PERIM:
2885d460eafSCathy Zhou 		err = softmac_capab_perim(sup, data, flags);
2895d460eafSCathy Zhou 		break;
2905d460eafSCathy Zhou 
2915d460eafSCathy Zhou 	default:
2925d460eafSCathy Zhou 		err = ENOTSUP;
2935d460eafSCathy Zhou 		break;
2945d460eafSCathy Zhou 	}
2955d460eafSCathy Zhou 	return (err);
2965d460eafSCathy Zhou }
2975d460eafSCathy Zhou 
2985d460eafSCathy Zhou static void
2995d460eafSCathy Zhou softmac_capability_advertise(softmac_upper_t *sup, mblk_t *mp)
3005d460eafSCathy Zhou {
3015d460eafSCathy Zhou 	dl_capability_ack_t	*dlap;
3025d460eafSCathy Zhou 	dl_capability_sub_t	*dlsp;
3035d460eafSCathy Zhou 	t_uscalar_t		subsize;
3045d460eafSCathy Zhou 	uint8_t			*ptr;
3055d460eafSCathy Zhou 	queue_t			*q = sup->su_wq;
3065d460eafSCathy Zhou 	mblk_t			*mp1;
3075d460eafSCathy Zhou 	softmac_t		*softmac = sup->su_softmac;
3085d460eafSCathy Zhou 	boolean_t		dld_capable = B_FALSE;
3095d460eafSCathy Zhou 	boolean_t		hcksum_capable = B_FALSE;
3105d460eafSCathy Zhou 	boolean_t		zcopy_capable = B_FALSE;
3115d460eafSCathy Zhou 	boolean_t		mdt_capable = B_FALSE;
3125d460eafSCathy Zhou 
3135d460eafSCathy Zhou 	ASSERT(sup->su_mode == SOFTMAC_FASTPATH);
3145d460eafSCathy Zhou 
3155d460eafSCathy Zhou 	/*
3165d460eafSCathy Zhou 	 * Initially assume no capabilities.
3175d460eafSCathy Zhou 	 */
3185d460eafSCathy Zhou 	subsize = 0;
3195d460eafSCathy Zhou 
3205d460eafSCathy Zhou 	/*
3215d460eafSCathy Zhou 	 * Direct capability negotiation interface between IP and softmac
3225d460eafSCathy Zhou 	 */
3235d460eafSCathy Zhou 	if (check_ip_above(sup->su_rq)) {
3245d460eafSCathy Zhou 		dld_capable = B_TRUE;
3255d460eafSCathy Zhou 		subsize += sizeof (dl_capability_sub_t) +
3265d460eafSCathy Zhou 		    sizeof (dl_capab_dld_t);
3275d460eafSCathy Zhou 	}
3285d460eafSCathy Zhou 
3295d460eafSCathy Zhou 	/*
3305d460eafSCathy Zhou 	 * Check if checksum offload is supported on this MAC.
3315d460eafSCathy Zhou 	 */
3325d460eafSCathy Zhou 	if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM) {
3335d460eafSCathy Zhou 		hcksum_capable = B_TRUE;
3345d460eafSCathy Zhou 		subsize += sizeof (dl_capability_sub_t) +
3355d460eafSCathy Zhou 		    sizeof (dl_capab_hcksum_t);
3365d460eafSCathy Zhou 	}
3375d460eafSCathy Zhou 
3385d460eafSCathy Zhou 	/*
3395d460eafSCathy Zhou 	 * Check if zerocopy is supported on this interface.
3405d460eafSCathy Zhou 	 */
3415d460eafSCathy Zhou 	if (!(softmac->smac_capab_flags & MAC_CAPAB_NO_ZCOPY)) {
3425d460eafSCathy Zhou 		zcopy_capable = B_TRUE;
3435d460eafSCathy Zhou 		subsize += sizeof (dl_capability_sub_t) +
3445d460eafSCathy Zhou 		    sizeof (dl_capab_zerocopy_t);
3455d460eafSCathy Zhou 	}
3465d460eafSCathy Zhou 
3475d460eafSCathy Zhou 	if (softmac->smac_mdt) {
3485d460eafSCathy Zhou 		mdt_capable = B_TRUE;
3495d460eafSCathy Zhou 		subsize += sizeof (dl_capability_sub_t) +
3505d460eafSCathy Zhou 		    sizeof (dl_capab_mdt_t);
3515d460eafSCathy Zhou 	}
3525d460eafSCathy Zhou 
3535d460eafSCathy Zhou 	/*
3545d460eafSCathy Zhou 	 * If there are no capabilities to advertise or if we
3555d460eafSCathy Zhou 	 * can't allocate a response, send a DL_ERROR_ACK.
3565d460eafSCathy Zhou 	 */
3575d460eafSCathy Zhou 	if ((subsize == 0) || (mp1 = reallocb(mp,
3585d460eafSCathy Zhou 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
3595d460eafSCathy Zhou 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
3605d460eafSCathy Zhou 		return;
3615d460eafSCathy Zhou 	}
3625d460eafSCathy Zhou 
3635d460eafSCathy Zhou 	mp = mp1;
3645d460eafSCathy Zhou 	DB_TYPE(mp) = M_PROTO;
3655d460eafSCathy Zhou 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
3665d460eafSCathy Zhou 	bzero(mp->b_rptr, MBLKL(mp));
3675d460eafSCathy Zhou 	dlap = (dl_capability_ack_t *)mp->b_rptr;
3685d460eafSCathy Zhou 	dlap->dl_primitive = DL_CAPABILITY_ACK;
3695d460eafSCathy Zhou 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
3705d460eafSCathy Zhou 	dlap->dl_sub_length = subsize;
3715d460eafSCathy Zhou 	ptr = (uint8_t *)&dlap[1];
3725d460eafSCathy Zhou 
3735d460eafSCathy Zhou 	/*
3745d460eafSCathy Zhou 	 * IP polling interface.
3755d460eafSCathy Zhou 	 */
3765d460eafSCathy Zhou 	if (dld_capable) {
3775d460eafSCathy Zhou 		dl_capab_dld_t		dld;
3785d460eafSCathy Zhou 
3795d460eafSCathy Zhou 		dlsp = (dl_capability_sub_t *)ptr;
3805d460eafSCathy Zhou 		dlsp->dl_cap = DL_CAPAB_DLD;
3815d460eafSCathy Zhou 		dlsp->dl_length = sizeof (dl_capab_dld_t);
3825d460eafSCathy Zhou 		ptr += sizeof (dl_capability_sub_t);
3835d460eafSCathy Zhou 
3845d460eafSCathy Zhou 		bzero(&dld, sizeof (dl_capab_dld_t));
3855d460eafSCathy Zhou 		dld.dld_version = DLD_CURRENT_VERSION;
3865d460eafSCathy Zhou 		dld.dld_capab = (uintptr_t)softmac_dld_capab;
3875d460eafSCathy Zhou 		dld.dld_capab_handle = (uintptr_t)sup;
3885d460eafSCathy Zhou 
3895d460eafSCathy Zhou 		dlcapabsetqid(&(dld.dld_mid), sup->su_rq);
3905d460eafSCathy Zhou 		bcopy(&dld, ptr, sizeof (dl_capab_dld_t));
3915d460eafSCathy Zhou 		ptr += sizeof (dl_capab_dld_t);
3925d460eafSCathy Zhou 	}
3935d460eafSCathy Zhou 
3945d460eafSCathy Zhou 	/*
3955d460eafSCathy Zhou 	 * TCP/IP checksum offload.
3965d460eafSCathy Zhou 	 */
3975d460eafSCathy Zhou 	if (hcksum_capable) {
3985d460eafSCathy Zhou 		dl_capab_hcksum_t	hcksum;
3995d460eafSCathy Zhou 
4005d460eafSCathy Zhou 		dlsp = (dl_capability_sub_t *)ptr;
4015d460eafSCathy Zhou 
4025d460eafSCathy Zhou 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
4035d460eafSCathy Zhou 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
4045d460eafSCathy Zhou 		ptr += sizeof (dl_capability_sub_t);
4055d460eafSCathy Zhou 
4065d460eafSCathy Zhou 		bzero(&hcksum, sizeof (dl_capab_hcksum_t));
4075d460eafSCathy Zhou 		hcksum.hcksum_version = HCKSUM_VERSION_1;
4085d460eafSCathy Zhou 		hcksum.hcksum_txflags = softmac->smac_hcksum_txflags;
4095d460eafSCathy Zhou 		dlcapabsetqid(&(hcksum.hcksum_mid), sup->su_rq);
4105d460eafSCathy Zhou 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
4115d460eafSCathy Zhou 		ptr += sizeof (dl_capab_hcksum_t);
4125d460eafSCathy Zhou 	}
4135d460eafSCathy Zhou 
4145d460eafSCathy Zhou 	/*
4155d460eafSCathy Zhou 	 * Zero copy
4165d460eafSCathy Zhou 	 */
4175d460eafSCathy Zhou 	if (zcopy_capable) {
4185d460eafSCathy Zhou 		dl_capab_zerocopy_t	zcopy;
4195d460eafSCathy Zhou 
4205d460eafSCathy Zhou 		dlsp = (dl_capability_sub_t *)ptr;
4215d460eafSCathy Zhou 
4225d460eafSCathy Zhou 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
4235d460eafSCathy Zhou 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
4245d460eafSCathy Zhou 		ptr += sizeof (dl_capability_sub_t);
4255d460eafSCathy Zhou 
4265d460eafSCathy Zhou 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
4275d460eafSCathy Zhou 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
4285d460eafSCathy Zhou 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
4295d460eafSCathy Zhou 		dlcapabsetqid(&(zcopy.zerocopy_mid), sup->su_rq);
4305d460eafSCathy Zhou 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
4315d460eafSCathy Zhou 		ptr += sizeof (dl_capab_zerocopy_t);
4325d460eafSCathy Zhou 	}
4335d460eafSCathy Zhou 
4345d460eafSCathy Zhou 	/*
4355d460eafSCathy Zhou 	 * MDT
4365d460eafSCathy Zhou 	 */
4375d460eafSCathy Zhou 	if (mdt_capable) {
4385d460eafSCathy Zhou 		dl_capab_mdt_t mdt;
4395d460eafSCathy Zhou 
4405d460eafSCathy Zhou 		dlsp = (dl_capability_sub_t *)ptr;
4415d460eafSCathy Zhou 
4425d460eafSCathy Zhou 		dlsp->dl_cap = DL_CAPAB_MDT;
4435d460eafSCathy Zhou 		dlsp->dl_length = sizeof (dl_capab_mdt_t);
4445d460eafSCathy Zhou 		ptr += sizeof (dl_capability_sub_t);
4455d460eafSCathy Zhou 
4465d460eafSCathy Zhou 		bzero(&mdt, sizeof (dl_capab_mdt_t));
4475d460eafSCathy Zhou 		mdt.mdt_version = MDT_VERSION_2;
4485d460eafSCathy Zhou 		mdt.mdt_flags = DL_CAPAB_MDT_ENABLE;
4495d460eafSCathy Zhou 		mdt.mdt_hdr_head = softmac->smac_mdt_capab.mdt_hdr_head;
4505d460eafSCathy Zhou 		mdt.mdt_hdr_tail = softmac->smac_mdt_capab.mdt_hdr_tail;
4515d460eafSCathy Zhou 		mdt.mdt_max_pld = softmac->smac_mdt_capab.mdt_max_pld;
4525d460eafSCathy Zhou 		mdt.mdt_span_limit = softmac->smac_mdt_capab.mdt_span_limit;
4535d460eafSCathy Zhou 		dlcapabsetqid(&(mdt.mdt_mid), sup->su_rq);
4545d460eafSCathy Zhou 		bcopy(&mdt, ptr, sizeof (dl_capab_mdt_t));
4555d460eafSCathy Zhou 		ptr += sizeof (dl_capab_mdt_t);
4565d460eafSCathy Zhou 	}
4575d460eafSCathy Zhou 
4585d460eafSCathy Zhou 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
4595d460eafSCathy Zhou 	qreply(q, mp);
4605d460eafSCathy Zhou }
4615d460eafSCathy Zhou 
4625d460eafSCathy Zhou static void
4635d460eafSCathy Zhou softmac_capability_req(softmac_upper_t *sup, mblk_t *mp)
4645d460eafSCathy Zhou {
4655d460eafSCathy Zhou 	dl_capability_req_t	*dlp = (dl_capability_req_t *)mp->b_rptr;
4665d460eafSCathy Zhou 	dl_capability_sub_t	*sp;
4675d460eafSCathy Zhou 	size_t			size, len;
4685d460eafSCathy Zhou 	offset_t		off, end;
4695d460eafSCathy Zhou 	t_uscalar_t		dl_err;
4705d460eafSCathy Zhou 	queue_t			*q = sup->su_wq;
4715d460eafSCathy Zhou 
4725d460eafSCathy Zhou 	ASSERT(sup->su_mode == SOFTMAC_FASTPATH);
4735d460eafSCathy Zhou 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
4745d460eafSCathy Zhou 		dl_err = DL_BADPRIM;
4755d460eafSCathy Zhou 		goto failed;
4765d460eafSCathy Zhou 	}
4775d460eafSCathy Zhou 
4785d460eafSCathy Zhou 	if (!sup->su_bound) {
4795d460eafSCathy Zhou 		dl_err = DL_OUTSTATE;
4805d460eafSCathy Zhou 		goto failed;
4815d460eafSCathy Zhou 	}
4825d460eafSCathy Zhou 
4835d460eafSCathy Zhou 	/*
4845d460eafSCathy Zhou 	 * This request is overloaded. If there are no requested capabilities
4855d460eafSCathy Zhou 	 * then we just want to acknowledge with all the capabilities we
4865d460eafSCathy Zhou 	 * support. Otherwise we enable the set of capabilities requested.
4875d460eafSCathy Zhou 	 */
4885d460eafSCathy Zhou 	if (dlp->dl_sub_length == 0) {
4895d460eafSCathy Zhou 		softmac_capability_advertise(sup, mp);
4905d460eafSCathy Zhou 		return;
4915d460eafSCathy Zhou 	}
4925d460eafSCathy Zhou 
4935d460eafSCathy Zhou 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
4945d460eafSCathy Zhou 		dl_err = DL_BADPRIM;
4955d460eafSCathy Zhou 		goto failed;
4965d460eafSCathy Zhou 	}
4975d460eafSCathy Zhou 
4985d460eafSCathy Zhou 	dlp->dl_primitive = DL_CAPABILITY_ACK;
4995d460eafSCathy Zhou 
5005d460eafSCathy Zhou 	off = dlp->dl_sub_offset;
5015d460eafSCathy Zhou 	len = dlp->dl_sub_length;
5025d460eafSCathy Zhou 
5035d460eafSCathy Zhou 	/*
5045d460eafSCathy Zhou 	 * Walk the list of capabilities to be enabled.
5055d460eafSCathy Zhou 	 */
5065d460eafSCathy Zhou 	for (end = off + len; off < end; ) {
5075d460eafSCathy Zhou 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
5085d460eafSCathy Zhou 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
5095d460eafSCathy Zhou 
5105d460eafSCathy Zhou 		if (off + size > end ||
5115d460eafSCathy Zhou 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
5125d460eafSCathy Zhou 			dl_err = DL_BADPRIM;
5135d460eafSCathy Zhou 			goto failed;
5145d460eafSCathy Zhou 		}
5155d460eafSCathy Zhou 
5165d460eafSCathy Zhou 		switch (sp->dl_cap) {
5175d460eafSCathy Zhou 		/*
5185d460eafSCathy Zhou 		 * TCP/IP checksum offload to hardware.
5195d460eafSCathy Zhou 		 */
5205d460eafSCathy Zhou 		case DL_CAPAB_HCKSUM: {
5215d460eafSCathy Zhou 			dl_capab_hcksum_t *hcksump;
5225d460eafSCathy Zhou 			dl_capab_hcksum_t hcksum;
5235d460eafSCathy Zhou 
5245d460eafSCathy Zhou 			hcksump = (dl_capab_hcksum_t *)&sp[1];
5255d460eafSCathy Zhou 			/*
5265d460eafSCathy Zhou 			 * Copy for alignment.
5275d460eafSCathy Zhou 			 */
5285d460eafSCathy Zhou 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
5295d460eafSCathy Zhou 			dlcapabsetqid(&(hcksum.hcksum_mid), sup->su_rq);
5305d460eafSCathy Zhou 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
5315d460eafSCathy Zhou 			break;
5325d460eafSCathy Zhou 		}
5335d460eafSCathy Zhou 
5345d460eafSCathy Zhou 		default:
5355d460eafSCathy Zhou 			break;
5365d460eafSCathy Zhou 		}
5375d460eafSCathy Zhou 
5385d460eafSCathy Zhou 		off += size;
5395d460eafSCathy Zhou 	}
5405d460eafSCathy Zhou 	qreply(q, mp);
5415d460eafSCathy Zhou 	return;
5425d460eafSCathy Zhou failed:
5435d460eafSCathy Zhou 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
5445d460eafSCathy Zhou }
5455d460eafSCathy Zhou 
5465d460eafSCathy Zhou static void
5475d460eafSCathy Zhou softmac_bind_req(softmac_upper_t *sup, mblk_t *mp)
5485d460eafSCathy Zhou {
5495d460eafSCathy Zhou 	softmac_lower_t	*slp = sup->su_slp;
5505d460eafSCathy Zhou 	softmac_t	*softmac = sup->su_softmac;
5515d460eafSCathy Zhou 	mblk_t		*ackmp, *mp1;
5525d460eafSCathy Zhou 	int		err;
5535d460eafSCathy Zhou 
5545d460eafSCathy Zhou 	if (MBLKL(mp) < DL_BIND_REQ_SIZE) {
5555d460eafSCathy Zhou 		freemsg(mp);
5565d460eafSCathy Zhou 		return;
5575d460eafSCathy Zhou 	}
5585d460eafSCathy Zhou 
5595d460eafSCathy Zhou 	/*
5605d460eafSCathy Zhou 	 * Allocate ackmp incase the underlying driver does not ack timely.
5615d460eafSCathy Zhou 	 */
5625d460eafSCathy Zhou 	if ((mp1 = allocb(sizeof (dl_error_ack_t), BPRI_HI)) == NULL) {
5635d460eafSCathy Zhou 		dlerrorack(sup->su_wq, mp, DL_BIND_REQ, DL_SYSERR, ENOMEM);
5645d460eafSCathy Zhou 		return;
5655d460eafSCathy Zhou 	}
5665d460eafSCathy Zhou 
5675d460eafSCathy Zhou 	err = softmac_output(slp, mp, DL_BIND_REQ, DL_BIND_ACK, &ackmp);
5685d460eafSCathy Zhou 	if (ackmp != NULL) {
5695d460eafSCathy Zhou 		freemsg(mp1);
5705d460eafSCathy Zhou 	} else {
5715d460eafSCathy Zhou 		/*
5725d460eafSCathy Zhou 		 * The driver does not ack timely.
5735d460eafSCathy Zhou 		 */
5745d460eafSCathy Zhou 		ASSERT(err == ENOMSG);
5755d460eafSCathy Zhou 		ackmp = mp1;
5765d460eafSCathy Zhou 	}
5775d460eafSCathy Zhou 	if (err != 0)
5785d460eafSCathy Zhou 		goto failed;
5795d460eafSCathy Zhou 
5805d460eafSCathy Zhou 	/*
5815d460eafSCathy Zhou 	 * Enable capabilities the underlying driver claims to support.
5825d460eafSCathy Zhou 	 */
5835d460eafSCathy Zhou 	if ((err = softmac_capab_enable(slp)) != 0)
5845d460eafSCathy Zhou 		goto failed;
5855d460eafSCathy Zhou 
5865d460eafSCathy Zhou 	/*
5875d460eafSCathy Zhou 	 * Check whether this softmac is already marked as exclusively used,
5885d460eafSCathy Zhou 	 * e.g., an aggregation is created over it. Fail the BIND_REQ if so.
5895d460eafSCathy Zhou 	 */
5905d460eafSCathy Zhou 	mutex_enter(&softmac->smac_active_mutex);
5915d460eafSCathy Zhou 	if (softmac->smac_active) {
5925d460eafSCathy Zhou 		mutex_exit(&softmac->smac_active_mutex);
5935d460eafSCathy Zhou 		err = EBUSY;
5945d460eafSCathy Zhou 		goto failed;
5955d460eafSCathy Zhou 	}
5965d460eafSCathy Zhou 	softmac->smac_nactive++;
5975d460eafSCathy Zhou 	sup->su_active = B_TRUE;
5985d460eafSCathy Zhou 	mutex_exit(&softmac->smac_active_mutex);
5995d460eafSCathy Zhou 	sup->su_bound = B_TRUE;
6005d460eafSCathy Zhou 
6015d460eafSCathy Zhou 	qreply(sup->su_wq, ackmp);
6025d460eafSCathy Zhou 	return;
6035d460eafSCathy Zhou failed:
6045d460eafSCathy Zhou 	if (err != 0) {
6055d460eafSCathy Zhou 		dlerrorack(sup->su_wq, ackmp, DL_BIND_REQ, DL_SYSERR, err);
6065d460eafSCathy Zhou 		return;
6075d460eafSCathy Zhou 	}
6085d460eafSCathy Zhou }
6095d460eafSCathy Zhou 
6105d460eafSCathy Zhou static void
6115d460eafSCathy Zhou softmac_unbind_req(softmac_upper_t *sup, mblk_t *mp)
6125d460eafSCathy Zhou {
6135d460eafSCathy Zhou 	softmac_lower_t	*slp = sup->su_slp;
6145d460eafSCathy Zhou 	softmac_t	*softmac = sup->su_softmac;
6155d460eafSCathy Zhou 	mblk_t		*ackmp, *mp1;
6165d460eafSCathy Zhou 	int		err;
6175d460eafSCathy Zhou 
6185d460eafSCathy Zhou 	if (MBLKL(mp) < DL_UNBIND_REQ_SIZE) {
6195d460eafSCathy Zhou 		freemsg(mp);
6205d460eafSCathy Zhou 		return;
6215d460eafSCathy Zhou 	}
6225d460eafSCathy Zhou 
6235d460eafSCathy Zhou 	if (!sup->su_bound) {
6245d460eafSCathy Zhou 		dlerrorack(sup->su_wq, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0);
6255d460eafSCathy Zhou 		return;
6265d460eafSCathy Zhou 	}
6275d460eafSCathy Zhou 
6285d460eafSCathy Zhou 	/*
6295d460eafSCathy Zhou 	 * Allocate ackmp incase the underlying driver does not ack timely.
6305d460eafSCathy Zhou 	 */
6315d460eafSCathy Zhou 	if ((mp1 = allocb(sizeof (dl_error_ack_t), BPRI_HI)) == NULL) {
6325d460eafSCathy Zhou 		dlerrorack(sup->su_wq, mp, DL_UNBIND_REQ, DL_SYSERR, ENOMEM);
6335d460eafSCathy Zhou 		return;
6345d460eafSCathy Zhou 	}
6355d460eafSCathy Zhou 
6365d460eafSCathy Zhou 	err = softmac_output(slp, mp, DL_UNBIND_REQ, DL_OK_ACK, &ackmp);
6375d460eafSCathy Zhou 	if (ackmp != NULL) {
6385d460eafSCathy Zhou 		freemsg(mp1);
6395d460eafSCathy Zhou 	} else {
6405d460eafSCathy Zhou 		/*
6415d460eafSCathy Zhou 		 * The driver does not ack timely.
6425d460eafSCathy Zhou 		 */
6435d460eafSCathy Zhou 		ASSERT(err == ENOMSG);
6445d460eafSCathy Zhou 		ackmp = mp1;
6455d460eafSCathy Zhou 	}
6465d460eafSCathy Zhou 	if (err != 0) {
6475d460eafSCathy Zhou 		dlerrorack(sup->su_wq, ackmp, DL_UNBIND_REQ, DL_SYSERR, err);
6485d460eafSCathy Zhou 		return;
6495d460eafSCathy Zhou 	}
6505d460eafSCathy Zhou 
6515d460eafSCathy Zhou 	sup->su_bound = B_FALSE;
6525d460eafSCathy Zhou 
6535d460eafSCathy Zhou 	mutex_enter(&softmac->smac_active_mutex);
6545d460eafSCathy Zhou 	if (sup->su_active) {
6555d460eafSCathy Zhou 		ASSERT(!softmac->smac_active);
6565d460eafSCathy Zhou 		softmac->smac_nactive--;
6575d460eafSCathy Zhou 		sup->su_active = B_FALSE;
6585d460eafSCathy Zhou 	}
6595d460eafSCathy Zhou 	mutex_exit(&softmac->smac_active_mutex);
6605d460eafSCathy Zhou 
6615d460eafSCathy Zhou done:
6625d460eafSCathy Zhou 	qreply(sup->su_wq, ackmp);
6635d460eafSCathy Zhou }
6645d460eafSCathy Zhou 
6655d460eafSCathy Zhou /*
6665d460eafSCathy Zhou  * Process the non-data mblk.
6675d460eafSCathy Zhou  */
6685d460eafSCathy Zhou static void
6695d460eafSCathy Zhou softmac_wput_single_nondata(softmac_upper_t *sup, mblk_t *mp)
6705d460eafSCathy Zhou {
6715d460eafSCathy Zhou 	softmac_t *softmac = sup->su_softmac;
6725d460eafSCathy Zhou 	softmac_lower_t	*slp = sup->su_slp;
6735d460eafSCathy Zhou 	unsigned char	dbtype;
6745d460eafSCathy Zhou 	t_uscalar_t	prim;
6755d460eafSCathy Zhou 
6765d460eafSCathy Zhou 	dbtype = DB_TYPE(mp);
677*bd670b35SErik Nordmark 	sup->su_is_arp = 0;
6785d460eafSCathy Zhou 	switch (dbtype) {
679*bd670b35SErik Nordmark 	case M_CTL:
680*bd670b35SErik Nordmark 		sup->su_is_arp = 1;
681*bd670b35SErik Nordmark 		/* FALLTHROUGH */
682*bd670b35SErik Nordmark 	case M_IOCTL: {
6835d460eafSCathy Zhou 		uint32_t	expected_mode;
6845d460eafSCathy Zhou 
6855d460eafSCathy Zhou 		if (((struct iocblk *)(mp->b_rptr))->ioc_cmd != SIOCSLIFNAME)
6865d460eafSCathy Zhou 			break;
6875d460eafSCathy Zhou 
6885d460eafSCathy Zhou 		/*
6895d460eafSCathy Zhou 		 * Nak the M_IOCTL based on the STREAMS specification.
6905d460eafSCathy Zhou 		 */
6915d460eafSCathy Zhou 		if (dbtype == M_IOCTL)
6925d460eafSCathy Zhou 			miocnak(sup->su_wq, mp, 0, EINVAL);
693f1956ffeSCathy Zhou 		else
694f1956ffeSCathy Zhou 			freemsg(mp);
6955d460eafSCathy Zhou 
6965d460eafSCathy Zhou 		/*
6975d460eafSCathy Zhou 		 * This stream is either IP or ARP. See whether
6985d460eafSCathy Zhou 		 * we need to setup a dedicated-lower-stream for it.
6995d460eafSCathy Zhou 		 */
7005d460eafSCathy Zhou 		mutex_enter(&softmac->smac_fp_mutex);
7015d460eafSCathy Zhou 
7025d460eafSCathy Zhou 		expected_mode = DATAPATH_MODE(softmac);
7035d460eafSCathy Zhou 		if (expected_mode == SOFTMAC_SLOWPATH)
7045d460eafSCathy Zhou 			sup->su_mode = SOFTMAC_SLOWPATH;
7055d460eafSCathy Zhou 		list_insert_head(&softmac->smac_sup_list, sup);
7065d460eafSCathy Zhou 		mutex_exit(&softmac->smac_fp_mutex);
7075d460eafSCathy Zhou 
7085d460eafSCathy Zhou 		/*
7095d460eafSCathy Zhou 		 * Setup the fast-path dedicated lower stream if fast-path
7105d460eafSCathy Zhou 		 * is expected. Note that no lock is held here, and if
7115d460eafSCathy Zhou 		 * smac_expected_mode is changed from SOFTMAC_FASTPATH to
7125d460eafSCathy Zhou 		 * SOFTMAC_SLOWPATH, the DL_NOTE_REPLUMB message used for
7135d460eafSCathy Zhou 		 * data-path switching would already be queued and will
7145d460eafSCathy Zhou 		 * be processed by softmac_wput_single_nondata() later.
7155d460eafSCathy Zhou 		 */
7165d460eafSCathy Zhou 		if (expected_mode == SOFTMAC_FASTPATH)
7175d460eafSCathy Zhou 			(void) softmac_fastpath_setup(sup);
7185d460eafSCathy Zhou 		return;
7195d460eafSCathy Zhou 	}
7205d460eafSCathy Zhou 	case M_PROTO:
7215d460eafSCathy Zhou 	case M_PCPROTO:
7225d460eafSCathy Zhou 		if (MBLKL(mp) < sizeof (t_uscalar_t)) {
7235d460eafSCathy Zhou 			freemsg(mp);
7245d460eafSCathy Zhou 			return;
7255d460eafSCathy Zhou 		}
7265d460eafSCathy Zhou 		prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
7275d460eafSCathy Zhou 		switch (prim) {
7285d460eafSCathy Zhou 		case DL_NOTIFY_IND:
7295d460eafSCathy Zhou 			if (MBLKL(mp) < sizeof (dl_notify_ind_t) ||
7305d460eafSCathy Zhou 			    ((dl_notify_ind_t *)mp->b_rptr)->dl_notification !=
7315d460eafSCathy Zhou 			    DL_NOTE_REPLUMB) {
7325d460eafSCathy Zhou 				freemsg(mp);
7335d460eafSCathy Zhou 				return;
7345d460eafSCathy Zhou 			}
7355d460eafSCathy Zhou 			/*
7365d460eafSCathy Zhou 			 * This DL_NOTE_REPLUMB message is initiated
7375d460eafSCathy Zhou 			 * and queued by the softmac itself, when the
7385d460eafSCathy Zhou 			 * sup is trying to switching its datapath mode
7395d460eafSCathy Zhou 			 * between SOFTMAC_SLOWPATH and SOFTMAC_FASTPATH.
7405d460eafSCathy Zhou 			 * Send this message upstream.
7415d460eafSCathy Zhou 			 */
7425d460eafSCathy Zhou 			qreply(sup->su_wq, mp);
7435d460eafSCathy Zhou 			return;
7445d460eafSCathy Zhou 		case DL_NOTIFY_CONF:
7455d460eafSCathy Zhou 			if (MBLKL(mp) < sizeof (dl_notify_conf_t) ||
7465d460eafSCathy Zhou 			    ((dl_notify_conf_t *)mp->b_rptr)->dl_notification !=
7475d460eafSCathy Zhou 			    DL_NOTE_REPLUMB_DONE) {
7485d460eafSCathy Zhou 				freemsg(mp);
7495d460eafSCathy Zhou 				return;
7505d460eafSCathy Zhou 			}
7515d460eafSCathy Zhou 			/*
7525d460eafSCathy Zhou 			 * This is an indication from IP/ARP that the
7535d460eafSCathy Zhou 			 * fastpath->slowpath switch is done.
7545d460eafSCathy Zhou 			 */
7555d460eafSCathy Zhou 			freemsg(mp);
7565d460eafSCathy Zhou 			softmac_datapath_switch_done(sup);
7575d460eafSCathy Zhou 			return;
7585d460eafSCathy Zhou 		}
7595d460eafSCathy Zhou 		break;
7605d460eafSCathy Zhou 	}
7615d460eafSCathy Zhou 
7625d460eafSCathy Zhou 	/*
7635d460eafSCathy Zhou 	 * No need to hold lock to check su_mode, since su_mode updating only
7645d460eafSCathy Zhou 	 * operation is is serialized by softmac_wput_nondata_task().
7655d460eafSCathy Zhou 	 */
7665d460eafSCathy Zhou 	if (sup->su_mode != SOFTMAC_FASTPATH) {
7675d460eafSCathy Zhou 		dld_wput(sup->su_wq, mp);
7685d460eafSCathy Zhou 		return;
7695d460eafSCathy Zhou 	}
7705d460eafSCathy Zhou 
7715d460eafSCathy Zhou 	/*
7725d460eafSCathy Zhou 	 * Fastpath non-data message processing. Most of non-data messages
7735d460eafSCathy Zhou 	 * can be directly passed down to the dedicated-lower-stream, aside
7745d460eafSCathy Zhou 	 * from the following M_PROTO/M_PCPROTO messages.
7755d460eafSCathy Zhou 	 */
7765d460eafSCathy Zhou 	switch (dbtype) {
7775d460eafSCathy Zhou 	case M_PROTO:
7785d460eafSCathy Zhou 	case M_PCPROTO:
7795d460eafSCathy Zhou 		switch (prim) {
7805d460eafSCathy Zhou 		case DL_BIND_REQ:
7815d460eafSCathy Zhou 			softmac_bind_req(sup, mp);
7825d460eafSCathy Zhou 			break;
7835d460eafSCathy Zhou 		case DL_UNBIND_REQ:
7845d460eafSCathy Zhou 			softmac_unbind_req(sup, mp);
7855d460eafSCathy Zhou 			break;
7865d460eafSCathy Zhou 		case DL_CAPABILITY_REQ:
7875d460eafSCathy Zhou 			softmac_capability_req(sup, mp);
7885d460eafSCathy Zhou 			break;
7895d460eafSCathy Zhou 		default:
7905d460eafSCathy Zhou 			putnext(slp->sl_wq, mp);
7915d460eafSCathy Zhou 			break;
7925d460eafSCathy Zhou 		}
7935d460eafSCathy Zhou 		break;
7945d460eafSCathy Zhou 	default:
7955d460eafSCathy Zhou 		putnext(slp->sl_wq, mp);
7965d460eafSCathy Zhou 		break;
7975d460eafSCathy Zhou 	}
7985d460eafSCathy Zhou }
7995d460eafSCathy Zhou 
8005d460eafSCathy Zhou /*
8015d460eafSCathy Zhou  * The worker thread which processes non-data messages. Note we only process
8025d460eafSCathy Zhou  * one message at one time in order to be able to "flush" the queued message
8035d460eafSCathy Zhou  * and serialize the processing.
8045d460eafSCathy Zhou  */
8055d460eafSCathy Zhou static void
8065d460eafSCathy Zhou softmac_wput_nondata_task(void *arg)
8075d460eafSCathy Zhou {
8085d460eafSCathy Zhou 	softmac_upper_t	*sup = arg;
8095d460eafSCathy Zhou 	mblk_t		*mp;
8105d460eafSCathy Zhou 
8115d460eafSCathy Zhou 	mutex_enter(&sup->su_disp_mutex);
8125d460eafSCathy Zhou 
8135d460eafSCathy Zhou 	while (sup->su_pending_head != NULL) {
8145d460eafSCathy Zhou 		if (sup->su_closing)
8155d460eafSCathy Zhou 			break;
8165d460eafSCathy Zhou 
8175d460eafSCathy Zhou 		SOFTMAC_DQ_PENDING(sup, &mp);
8185d460eafSCathy Zhou 		mutex_exit(&sup->su_disp_mutex);
8195d460eafSCathy Zhou 		softmac_wput_single_nondata(sup, mp);
8205d460eafSCathy Zhou 		mutex_enter(&sup->su_disp_mutex);
8215d460eafSCathy Zhou 	}
8225d460eafSCathy Zhou 
8235d460eafSCathy Zhou 	/*
8245d460eafSCathy Zhou 	 * If the stream is closing, flush all queued messages and inform
8255d460eafSCathy Zhou 	 * the stream to be closed.
8265d460eafSCathy Zhou 	 */
8275d460eafSCathy Zhou 	freemsgchain(sup->su_pending_head);
8285d460eafSCathy Zhou 	sup->su_pending_head = sup->su_pending_tail = NULL;
8295d460eafSCathy Zhou 	sup->su_dlpi_pending = B_FALSE;
8305d460eafSCathy Zhou 	cv_signal(&sup->su_disp_cv);
8315d460eafSCathy Zhou 	mutex_exit(&sup->su_disp_mutex);
8325d460eafSCathy Zhou }
8335d460eafSCathy Zhou 
8345d460eafSCathy Zhou /*
8355d460eafSCathy Zhou  * Kernel thread to handle taskq dispatch failures in softmac_wput_nondata().
8365d460eafSCathy Zhou  * This thread is started when the softmac module is first loaded.
8375d460eafSCathy Zhou  */
8385d460eafSCathy Zhou static void
8395d460eafSCathy Zhou softmac_taskq_dispatch(void)
8405d460eafSCathy Zhou {
8415d460eafSCathy Zhou 	callb_cpr_t	cprinfo;
8425d460eafSCathy Zhou 	softmac_upper_t	*sup;
8435d460eafSCathy Zhou 
8445d460eafSCathy Zhou 	CALLB_CPR_INIT(&cprinfo, &softmac_taskq_lock, callb_generic_cpr,
8455d460eafSCathy Zhou 	    "softmac_taskq_dispatch");
8465d460eafSCathy Zhou 	mutex_enter(&softmac_taskq_lock);
8475d460eafSCathy Zhou 
8485d460eafSCathy Zhou 	while (!softmac_taskq_quit) {
8495d460eafSCathy Zhou 		sup = list_head(&softmac_taskq_list);
8505d460eafSCathy Zhou 		while (sup != NULL) {
8515d460eafSCathy Zhou 			list_remove(&softmac_taskq_list, sup);
8525d460eafSCathy Zhou 			sup->su_taskq_scheduled = B_FALSE;
8535d460eafSCathy Zhou 			mutex_exit(&softmac_taskq_lock);
8545d460eafSCathy Zhou 			VERIFY(taskq_dispatch(system_taskq,
8555d460eafSCathy Zhou 			    softmac_wput_nondata_task, sup, TQ_SLEEP) != NULL);
8565d460eafSCathy Zhou 			mutex_enter(&softmac_taskq_lock);
8575d460eafSCathy Zhou 			sup = list_head(&softmac_taskq_list);
8585d460eafSCathy Zhou 		}
8595d460eafSCathy Zhou 
8605d460eafSCathy Zhou 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
8615d460eafSCathy Zhou 		cv_wait(&softmac_taskq_cv, &softmac_taskq_lock);
8625d460eafSCathy Zhou 		CALLB_CPR_SAFE_END(&cprinfo, &softmac_taskq_lock);
8635d460eafSCathy Zhou 	}
8645d460eafSCathy Zhou 
8655d460eafSCathy Zhou 	softmac_taskq_done = B_TRUE;
8665d460eafSCathy Zhou 	cv_signal(&softmac_taskq_cv);
8675d460eafSCathy Zhou 	CALLB_CPR_EXIT(&cprinfo);
8685d460eafSCathy Zhou 	thread_exit();
8695d460eafSCathy Zhou }
8705d460eafSCathy Zhou 
8715d460eafSCathy Zhou void
8725d460eafSCathy Zhou softmac_wput_nondata(softmac_upper_t *sup, mblk_t *mp)
8735d460eafSCathy Zhou {
8745d460eafSCathy Zhou 	/*
8755d460eafSCathy Zhou 	 * The processing of the message might block. Enqueue the
8765d460eafSCathy Zhou 	 * message for later processing.
8775d460eafSCathy Zhou 	 */
8785d460eafSCathy Zhou 	mutex_enter(&sup->su_disp_mutex);
8795d460eafSCathy Zhou 
8805d460eafSCathy Zhou 	if (sup->su_closing) {
8815d460eafSCathy Zhou 		mutex_exit(&sup->su_disp_mutex);
8825d460eafSCathy Zhou 		freemsg(mp);
8835d460eafSCathy Zhou 		return;
8845d460eafSCathy Zhou 	}
8855d460eafSCathy Zhou 
8865d460eafSCathy Zhou 	SOFTMAC_EQ_PENDING(sup, mp);
8875d460eafSCathy Zhou 
8885d460eafSCathy Zhou 	if (sup->su_dlpi_pending) {
8895d460eafSCathy Zhou 		mutex_exit(&sup->su_disp_mutex);
8905d460eafSCathy Zhou 		return;
8915d460eafSCathy Zhou 	}
8925d460eafSCathy Zhou 	sup->su_dlpi_pending = B_TRUE;
8935d460eafSCathy Zhou 	mutex_exit(&sup->su_disp_mutex);
8945d460eafSCathy Zhou 
8955d460eafSCathy Zhou 	if (taskq_dispatch(system_taskq, softmac_wput_nondata_task,
8965d460eafSCathy Zhou 	    sup, TQ_NOSLEEP) != NULL) {
8975d460eafSCathy Zhou 		return;
8985d460eafSCathy Zhou 	}
8995d460eafSCathy Zhou 
9005d460eafSCathy Zhou 	mutex_enter(&softmac_taskq_lock);
9015d460eafSCathy Zhou 	if (!sup->su_taskq_scheduled) {
9025d460eafSCathy Zhou 		list_insert_tail(&softmac_taskq_list, sup);
9035d460eafSCathy Zhou 		cv_signal(&softmac_taskq_cv);
9045d460eafSCathy Zhou 	}
9055d460eafSCathy Zhou 	sup->su_taskq_scheduled = B_TRUE;
9065d460eafSCathy Zhou 	mutex_exit(&softmac_taskq_lock);
9075d460eafSCathy Zhou }
9085d460eafSCathy Zhou 
9095d460eafSCathy Zhou /*
9105d460eafSCathy Zhou  * Setup the dedicated-lower-stream (fast-path) for the IP/ARP upperstream.
9115d460eafSCathy Zhou  */
9125d460eafSCathy Zhou static int
9135d460eafSCathy Zhou softmac_fastpath_setup(softmac_upper_t *sup)
9145d460eafSCathy Zhou {
9155d460eafSCathy Zhou 	softmac_t	*softmac = sup->su_softmac;
9165d460eafSCathy Zhou 	softmac_lower_t	*slp;
9175d460eafSCathy Zhou 	int		err;
9185d460eafSCathy Zhou 
9195d460eafSCathy Zhou 	err = softmac_lower_setup(softmac, sup, &slp);
9205d460eafSCathy Zhou 
9215d460eafSCathy Zhou 	mutex_enter(&sup->su_mutex);
9225d460eafSCathy Zhou 	/*
9235d460eafSCathy Zhou 	 * Wait for all data messages to be processed so that we can change
9245d460eafSCathy Zhou 	 * the su_mode.
9255d460eafSCathy Zhou 	 */
9265d460eafSCathy Zhou 	while (sup->su_tx_inprocess != 0)
9275d460eafSCathy Zhou 		cv_wait(&sup->su_cv, &sup->su_mutex);
9285d460eafSCathy Zhou 
9295d460eafSCathy Zhou 	ASSERT(sup->su_mode != SOFTMAC_FASTPATH);
9305d460eafSCathy Zhou 	ASSERT(sup->su_slp == NULL);
9315d460eafSCathy Zhou 	if (err != 0) {
9325d460eafSCathy Zhou 		sup->su_mode = SOFTMAC_SLOWPATH;
9335d460eafSCathy Zhou 	} else {
9345d460eafSCathy Zhou 		sup->su_slp = slp;
9355d460eafSCathy Zhou 		sup->su_mode = SOFTMAC_FASTPATH;
9365d460eafSCathy Zhou 	}
9375d460eafSCathy Zhou 	mutex_exit(&sup->su_mutex);
9385d460eafSCathy Zhou 	return (err);
9395d460eafSCathy Zhou }
9405d460eafSCathy Zhou 
9415d460eafSCathy Zhou /*
9425d460eafSCathy Zhou  * Tear down the dedicated-lower-stream (fast-path) for the IP/ARP upperstream.
9435d460eafSCathy Zhou  */
9445d460eafSCathy Zhou static void
9455d460eafSCathy Zhou softmac_fastpath_tear(softmac_upper_t *sup)
9465d460eafSCathy Zhou {
9475d460eafSCathy Zhou 	mutex_enter(&sup->su_mutex);
9485d460eafSCathy Zhou 	/*
9495d460eafSCathy Zhou 	 * Wait for all data messages in the dedicated-lower-stream
9505d460eafSCathy Zhou 	 * to be processed.
9515d460eafSCathy Zhou 	 */
9525d460eafSCathy Zhou 	while (sup->su_tx_inprocess != 0)
9535d460eafSCathy Zhou 		cv_wait(&sup->su_cv, &sup->su_mutex);
9545d460eafSCathy Zhou 
95579eeb645SCathy Zhou 	/*
95679eeb645SCathy Zhou 	 * Note that this function is called either when the stream is closed,
95779eeb645SCathy Zhou 	 * or the stream is unbound (fastpath-slowpath-switch). Therefore,
95879eeb645SCathy Zhou 	 * No need to call the tx_notify callback.
95979eeb645SCathy Zhou 	 */
96079eeb645SCathy Zhou 	sup->su_tx_notify_func = NULL;
96179eeb645SCathy Zhou 	sup->su_tx_notify_arg = NULL;
9625d460eafSCathy Zhou 	if (sup->su_tx_busy) {
9635d460eafSCathy Zhou 		ASSERT(sup->su_tx_flow_mp == NULL);
96479eeb645SCathy Zhou 		VERIFY((sup->su_tx_flow_mp = getq(sup->su_wq)) != NULL);
9655d460eafSCathy Zhou 		sup->su_tx_busy = B_FALSE;
9665d460eafSCathy Zhou 	}
9675d460eafSCathy Zhou 
9685d460eafSCathy Zhou 	sup->su_mode = SOFTMAC_SLOWPATH;
9695d460eafSCathy Zhou 
9705d460eafSCathy Zhou 	/*
9715d460eafSCathy Zhou 	 * Destroy the dedicated-lower-stream. Note that slp is destroyed
9725d460eafSCathy Zhou 	 * when lh is closed.
9735d460eafSCathy Zhou 	 */
9745d460eafSCathy Zhou 	(void) ldi_close(sup->su_slp->sl_lh, FREAD|FWRITE, kcred);
9755d460eafSCathy Zhou 	sup->su_slp = NULL;
9765d460eafSCathy Zhou 	mutex_exit(&sup->su_mutex);
9775d460eafSCathy Zhou }
9785d460eafSCathy Zhou 
9795d460eafSCathy Zhou void
9805d460eafSCathy Zhou softmac_wput_data(softmac_upper_t *sup, mblk_t *mp)
9815d460eafSCathy Zhou {
9825d460eafSCathy Zhou 	/*
9835d460eafSCathy Zhou 	 * No lock is required to access the su_mode field since the data
9845d460eafSCathy Zhou 	 * traffic is quiesce by IP when the data-path mode is in the
9855d460eafSCathy Zhou 	 * process of switching.
9865d460eafSCathy Zhou 	 */
9875d460eafSCathy Zhou 	if (sup->su_mode != SOFTMAC_FASTPATH)
9885d460eafSCathy Zhou 		dld_wput(sup->su_wq, mp);
9895d460eafSCathy Zhou 	else
9905d460eafSCathy Zhou 		(void) softmac_fastpath_wput_data(sup, mp, NULL, 0);
9915d460eafSCathy Zhou }
9925d460eafSCathy Zhou 
9935d460eafSCathy Zhou /*ARGSUSED*/
9945d460eafSCathy Zhou static mac_tx_cookie_t
9955d460eafSCathy Zhou softmac_fastpath_wput_data(softmac_upper_t *sup, mblk_t *mp, uintptr_t f_hint,
9965d460eafSCathy Zhou     uint16_t flag)
9975d460eafSCathy Zhou {
9985d460eafSCathy Zhou 	queue_t		*wq = sup->su_slp->sl_wq;
9995d460eafSCathy Zhou 
10005d460eafSCathy Zhou 	/*
10015d460eafSCathy Zhou 	 * This function is called from IP, only the MAC_DROP_ON_NO_DESC
10025d460eafSCathy Zhou 	 * flag can be specified.
10035d460eafSCathy Zhou 	 */
10045d460eafSCathy Zhou 	ASSERT((flag & ~MAC_DROP_ON_NO_DESC) == 0);
10055d460eafSCathy Zhou 	ASSERT(mp->b_next == NULL);
10065d460eafSCathy Zhou 
10075d460eafSCathy Zhou 	/*
10085d460eafSCathy Zhou 	 * Check wether the dedicated-lower-stream is able to handle more
10095d460eafSCathy Zhou 	 * messages, and enable the flow-control if it is not.
10105d460eafSCathy Zhou 	 *
10115d460eafSCathy Zhou 	 * Note that in order not to introduce any packet reordering, we
10125d460eafSCathy Zhou 	 * always send the message down to the dedicated-lower-stream:
10135d460eafSCathy Zhou 	 *
10145d460eafSCathy Zhou 	 * If the flow-control is already enabled, but we still get
10155d460eafSCathy Zhou 	 * the messages from the upper-stream, it means that the upper
10165d460eafSCathy Zhou 	 * stream does not respect STREAMS flow-control (e.g., TCP). Simply
10175d460eafSCathy Zhou 	 * pass the message down to the lower-stream in that case.
10185d460eafSCathy Zhou 	 */
10195d460eafSCathy Zhou 	if (SOFTMAC_CANPUTNEXT(wq)) {
10205d460eafSCathy Zhou 		putnext(wq, mp);
10215d460eafSCathy Zhou 		return (NULL);
10225d460eafSCathy Zhou 	}
10235d460eafSCathy Zhou 
10245d460eafSCathy Zhou 	if (sup->su_tx_busy) {
102579eeb645SCathy Zhou 		if ((flag & MAC_DROP_ON_NO_DESC) != 0)
102679eeb645SCathy Zhou 			freemsg(mp);
102779eeb645SCathy Zhou 		else
10285d460eafSCathy Zhou 			putnext(wq, mp);
102979eeb645SCathy Zhou 		return ((mac_tx_cookie_t)sup);
10305d460eafSCathy Zhou 	}
10315d460eafSCathy Zhou 
10325d460eafSCathy Zhou 	mutex_enter(&sup->su_mutex);
10335d460eafSCathy Zhou 	if (!sup->su_tx_busy) {
103479eeb645SCathy Zhou 		/*
103579eeb645SCathy Zhou 		 * If DLD_CAPAB_DIRECT is enabled, the notify callback will be
103679eeb645SCathy Zhou 		 * called when the flow control can be disabled. Otherwise,
103779eeb645SCathy Zhou 		 * put the tx_flow_mp into the wq to make use of the old
103879eeb645SCathy Zhou 		 * streams flow control.
103979eeb645SCathy Zhou 		 */
10405d460eafSCathy Zhou 		ASSERT(sup->su_tx_flow_mp != NULL);
10415d460eafSCathy Zhou 		(void) putq(sup->su_wq, sup->su_tx_flow_mp);
10425d460eafSCathy Zhou 		sup->su_tx_flow_mp = NULL;
10435d460eafSCathy Zhou 		sup->su_tx_busy = B_TRUE;
10445d460eafSCathy Zhou 		qenable(wq);
10455d460eafSCathy Zhou 	}
10465d460eafSCathy Zhou 	mutex_exit(&sup->su_mutex);
104779eeb645SCathy Zhou 
104879eeb645SCathy Zhou 	if ((flag & MAC_DROP_ON_NO_DESC) != 0)
104979eeb645SCathy Zhou 		freemsg(mp);
105079eeb645SCathy Zhou 	else
10515d460eafSCathy Zhou 		putnext(wq, mp);
105279eeb645SCathy Zhou 	return ((mac_tx_cookie_t)sup);
10535d460eafSCathy Zhou }
10545d460eafSCathy Zhou 
10555d460eafSCathy Zhou boolean_t
10565d460eafSCathy Zhou softmac_active_set(void *arg)
10575d460eafSCathy Zhou {
10585d460eafSCathy Zhou 	softmac_t	*softmac = arg;
10595d460eafSCathy Zhou 
10605d460eafSCathy Zhou 	mutex_enter(&softmac->smac_active_mutex);
10615d460eafSCathy Zhou 	if (softmac->smac_nactive != 0) {
10625d460eafSCathy Zhou 		mutex_exit(&softmac->smac_active_mutex);
10635d460eafSCathy Zhou 		return (B_FALSE);
10645d460eafSCathy Zhou 	}
10655d460eafSCathy Zhou 	softmac->smac_active = B_TRUE;
10665d460eafSCathy Zhou 	mutex_exit(&softmac->smac_active_mutex);
10675d460eafSCathy Zhou 	return (B_TRUE);
10685d460eafSCathy Zhou }
10695d460eafSCathy Zhou 
10705d460eafSCathy Zhou void
10715d460eafSCathy Zhou softmac_active_clear(void *arg)
10725d460eafSCathy Zhou {
10735d460eafSCathy Zhou 	softmac_t	*softmac = arg;
10745d460eafSCathy Zhou 
10755d460eafSCathy Zhou 	mutex_enter(&softmac->smac_active_mutex);
10765d460eafSCathy Zhou 	ASSERT(softmac->smac_active && (softmac->smac_nactive == 0));
10775d460eafSCathy Zhou 	softmac->smac_active = B_FALSE;
10785d460eafSCathy Zhou 	mutex_exit(&softmac->smac_active_mutex);
10795d460eafSCathy Zhou }
10805d460eafSCathy Zhou 
10815d460eafSCathy Zhou /*
10825d460eafSCathy Zhou  * Disable/reenable fastpath on given softmac. This request could come from a
10835d460eafSCathy Zhou  * MAC client or directly from administrators.
10845d460eafSCathy Zhou  */
10855d460eafSCathy Zhou int
10865d460eafSCathy Zhou softmac_datapath_switch(softmac_t *softmac, boolean_t disable, boolean_t admin)
10875d460eafSCathy Zhou {
10885d460eafSCathy Zhou 	softmac_upper_t		*sup;
10895d460eafSCathy Zhou 	mblk_t			*head = NULL, *tail = NULL, *mp;
10905d460eafSCathy Zhou 	list_t			reqlist;
10915d460eafSCathy Zhou 	softmac_switch_req_t	*req;
10925d460eafSCathy Zhou 	uint32_t		current_mode, expected_mode;
10935d460eafSCathy Zhou 	int			err = 0;
10945d460eafSCathy Zhou 
10955d460eafSCathy Zhou 	mutex_enter(&softmac->smac_fp_mutex);
10965d460eafSCathy Zhou 
10975d460eafSCathy Zhou 	current_mode = DATAPATH_MODE(softmac);
10985d460eafSCathy Zhou 	if (admin) {
10995d460eafSCathy Zhou 		if (softmac->smac_fastpath_admin_disabled == disable) {
11005d460eafSCathy Zhou 			mutex_exit(&softmac->smac_fp_mutex);
11015d460eafSCathy Zhou 			return (0);
11025d460eafSCathy Zhou 		}
11035d460eafSCathy Zhou 		softmac->smac_fastpath_admin_disabled = disable;
11045d460eafSCathy Zhou 	} else if (disable) {
11055d460eafSCathy Zhou 		softmac->smac_fp_disable_clients++;
11065d460eafSCathy Zhou 	} else {
11075d460eafSCathy Zhou 		ASSERT(softmac->smac_fp_disable_clients != 0);
11085d460eafSCathy Zhou 		softmac->smac_fp_disable_clients--;
11095d460eafSCathy Zhou 	}
11105d460eafSCathy Zhou 
11115d460eafSCathy Zhou 	expected_mode = DATAPATH_MODE(softmac);
11125d460eafSCathy Zhou 	if (current_mode == expected_mode) {
11135d460eafSCathy Zhou 		mutex_exit(&softmac->smac_fp_mutex);
11145d460eafSCathy Zhou 		return (0);
11155d460eafSCathy Zhou 	}
11165d460eafSCathy Zhou 
11175d460eafSCathy Zhou 	/*
11185d460eafSCathy Zhou 	 * The expected mode is different from whatever datapath mode
11195d460eafSCathy Zhou 	 * this softmac is expected from last request, enqueue the data-path
11205d460eafSCathy Zhou 	 * switch request.
11215d460eafSCathy Zhou 	 */
11225d460eafSCathy Zhou 	list_create(&reqlist, sizeof (softmac_switch_req_t),
11235d460eafSCathy Zhou 	    offsetof(softmac_switch_req_t, ssq_req_list_node));
11245d460eafSCathy Zhou 
11255d460eafSCathy Zhou 	/*
11265d460eafSCathy Zhou 	 * Allocate all DL_NOTIFY_IND messages and request structures that
11275d460eafSCathy Zhou 	 * are required to switch each IP/ARP stream to the expected mode.
11285d460eafSCathy Zhou 	 */
11295d460eafSCathy Zhou 	for (sup = list_head(&softmac->smac_sup_list); sup != NULL;
11305d460eafSCathy Zhou 	    sup = list_next(&softmac->smac_sup_list, sup)) {
11315d460eafSCathy Zhou 		dl_notify_ind_t	*dlip;
11325d460eafSCathy Zhou 
11335d460eafSCathy Zhou 		req = kmem_alloc(sizeof (softmac_switch_req_t), KM_NOSLEEP);
11345d460eafSCathy Zhou 		if (req == NULL)
11355d460eafSCathy Zhou 			break;
11365d460eafSCathy Zhou 
11375d460eafSCathy Zhou 		req->ssq_expected_mode = expected_mode;
1138*bd670b35SErik Nordmark 		if (sup->su_is_arp) {
1139*bd670b35SErik Nordmark 			list_insert_tail(&reqlist, req);
1140*bd670b35SErik Nordmark 			continue;
1141*bd670b35SErik Nordmark 		}
11425d460eafSCathy Zhou 		/*
11435d460eafSCathy Zhou 		 * Allocate the DL_NOTE_REPLUMB message.
11445d460eafSCathy Zhou 		 */
11455d460eafSCathy Zhou 		if ((mp = allocb(sizeof (dl_notify_ind_t), BPRI_LO)) == NULL) {
11465d460eafSCathy Zhou 			kmem_free(req, sizeof (softmac_switch_req_t));
11475d460eafSCathy Zhou 			break;
11485d460eafSCathy Zhou 		}
11495d460eafSCathy Zhou 
11505d460eafSCathy Zhou 		list_insert_tail(&reqlist, req);
11515d460eafSCathy Zhou 
11525d460eafSCathy Zhou 		mp->b_wptr = mp->b_rptr + sizeof (dl_notify_ind_t);
11535d460eafSCathy Zhou 		mp->b_datap->db_type = M_PROTO;
11545d460eafSCathy Zhou 		bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
11555d460eafSCathy Zhou 		dlip = (dl_notify_ind_t *)mp->b_rptr;
11565d460eafSCathy Zhou 		dlip->dl_primitive = DL_NOTIFY_IND;
11575d460eafSCathy Zhou 		dlip->dl_notification = DL_NOTE_REPLUMB;
11585d460eafSCathy Zhou 		if (head == NULL) {
11595d460eafSCathy Zhou 			head = tail = mp;
11605d460eafSCathy Zhou 		} else {
11615d460eafSCathy Zhou 			tail->b_next = mp;
11625d460eafSCathy Zhou 			tail = mp;
11635d460eafSCathy Zhou 		}
11645d460eafSCathy Zhou 	}
11655d460eafSCathy Zhou 
11665d460eafSCathy Zhou 	/*
11675d460eafSCathy Zhou 	 * Note that it is fine if the expected data-path mode is fast-path
11685d460eafSCathy Zhou 	 * and some of streams fails to switch. Only return failure if we
11695d460eafSCathy Zhou 	 * are expected to switch to the slow-path.
11705d460eafSCathy Zhou 	 */
11715d460eafSCathy Zhou 	if (sup != NULL && expected_mode == SOFTMAC_SLOWPATH) {
11725d460eafSCathy Zhou 		err = ENOMEM;
11735d460eafSCathy Zhou 		goto fail;
11745d460eafSCathy Zhou 	}
11755d460eafSCathy Zhou 
11765d460eafSCathy Zhou 	/*
11775d460eafSCathy Zhou 	 * Start switching for each IP/ARP stream. The switching operation
11785d460eafSCathy Zhou 	 * will eventually succeed and there is no need to wait for it
11795d460eafSCathy Zhou 	 * to finish.
11805d460eafSCathy Zhou 	 */
11815d460eafSCathy Zhou 	for (sup = list_head(&softmac->smac_sup_list); sup != NULL;
11825d460eafSCathy Zhou 	    sup = list_next(&softmac->smac_sup_list, sup)) {
1183*bd670b35SErik Nordmark 		if (!sup->su_is_arp) {
11845d460eafSCathy Zhou 			mp = head->b_next;
11855d460eafSCathy Zhou 			head->b_next = NULL;
1186*bd670b35SErik Nordmark 			softmac_wput_nondata(sup, head);
1187*bd670b35SErik Nordmark 			head = mp;
1188*bd670b35SErik Nordmark 		}
11895d460eafSCathy Zhou 		/*
1190*bd670b35SErik Nordmark 		 * Add the switch request to the requests list of the stream.
11915d460eafSCathy Zhou 		 */
11925d460eafSCathy Zhou 		req = list_head(&reqlist);
11935d460eafSCathy Zhou 		ASSERT(req != NULL);
11945d460eafSCathy Zhou 		list_remove(&reqlist, req);
11955d460eafSCathy Zhou 		list_insert_tail(&sup->su_req_list, req);
11965d460eafSCathy Zhou 	}
11975d460eafSCathy Zhou 
11985d460eafSCathy Zhou 	mutex_exit(&softmac->smac_fp_mutex);
11995d460eafSCathy Zhou 	ASSERT(list_is_empty(&reqlist));
12005d460eafSCathy Zhou 	list_destroy(&reqlist);
12015d460eafSCathy Zhou 	return (0);
12025d460eafSCathy Zhou fail:
12035d460eafSCathy Zhou 	if (admin) {
12045d460eafSCathy Zhou 		softmac->smac_fastpath_admin_disabled = !disable;
12055d460eafSCathy Zhou 	} else if (disable) {
12065d460eafSCathy Zhou 		softmac->smac_fp_disable_clients--;
12075d460eafSCathy Zhou 	} else {
12085d460eafSCathy Zhou 		softmac->smac_fp_disable_clients++;
12095d460eafSCathy Zhou 	}
12105d460eafSCathy Zhou 
12115d460eafSCathy Zhou 	mutex_exit(&softmac->smac_fp_mutex);
12125d460eafSCathy Zhou 	while ((req = list_head(&reqlist)) != NULL) {
12135d460eafSCathy Zhou 		list_remove(&reqlist, req);
12145d460eafSCathy Zhou 		kmem_free(req, sizeof (softmac_switch_req_t));
12155d460eafSCathy Zhou 	}
12165d460eafSCathy Zhou 	freemsgchain(head);
12175d460eafSCathy Zhou 	list_destroy(&reqlist);
12185d460eafSCathy Zhou 	return (err);
12195d460eafSCathy Zhou }
12205d460eafSCathy Zhou 
12215d460eafSCathy Zhou int
12225d460eafSCathy Zhou softmac_fastpath_disable(void *arg)
12235d460eafSCathy Zhou {
12245d460eafSCathy Zhou 	return (softmac_datapath_switch((softmac_t *)arg, B_TRUE, B_FALSE));
12255d460eafSCathy Zhou }
12265d460eafSCathy Zhou 
12275d460eafSCathy Zhou void
12285d460eafSCathy Zhou softmac_fastpath_enable(void *arg)
12295d460eafSCathy Zhou {
12305d460eafSCathy Zhou 	VERIFY(softmac_datapath_switch((softmac_t *)arg, B_FALSE,
12315d460eafSCathy Zhou 	    B_FALSE) == 0);
12325d460eafSCathy Zhou }
12335d460eafSCathy Zhou 
12345d460eafSCathy Zhou void
12355d460eafSCathy Zhou softmac_upperstream_close(softmac_upper_t *sup)
12365d460eafSCathy Zhou {
12375d460eafSCathy Zhou 	softmac_t		*softmac = sup->su_softmac;
12385d460eafSCathy Zhou 	softmac_switch_req_t	*req;
12395d460eafSCathy Zhou 
12405d460eafSCathy Zhou 	mutex_enter(&softmac->smac_fp_mutex);
12415d460eafSCathy Zhou 
12425d460eafSCathy Zhou 	if (sup->su_mode == SOFTMAC_FASTPATH)
12435d460eafSCathy Zhou 		softmac_fastpath_tear(sup);
12445d460eafSCathy Zhou 
12455d460eafSCathy Zhou 	if (sup->su_mode != SOFTMAC_UNKNOWN) {
12465d460eafSCathy Zhou 		list_remove(&softmac->smac_sup_list, sup);
12475d460eafSCathy Zhou 		sup->su_mode = SOFTMAC_UNKNOWN;
12485d460eafSCathy Zhou 	}
12495d460eafSCathy Zhou 
12505d460eafSCathy Zhou 	/*
12515d460eafSCathy Zhou 	 * Cleanup all the switch requests queueed on this stream.
12525d460eafSCathy Zhou 	 */
12535d460eafSCathy Zhou 	while ((req = list_head(&sup->su_req_list)) != NULL) {
12545d460eafSCathy Zhou 		list_remove(&sup->su_req_list, req);
12555d460eafSCathy Zhou 		kmem_free(req, sizeof (softmac_switch_req_t));
12565d460eafSCathy Zhou 	}
12575d460eafSCathy Zhou 	mutex_exit(&softmac->smac_fp_mutex);
12585d460eafSCathy Zhou }
12595d460eafSCathy Zhou 
12605d460eafSCathy Zhou /*
12615d460eafSCathy Zhou  * Handle the DL_NOTE_REPLUMB_DONE indication from IP/ARP. Change the upper
12625d460eafSCathy Zhou  * stream from the fastpath mode to the slowpath mode.
12635d460eafSCathy Zhou  */
12645d460eafSCathy Zhou static void
12655d460eafSCathy Zhou softmac_datapath_switch_done(softmac_upper_t *sup)
12665d460eafSCathy Zhou {
12675d460eafSCathy Zhou 	softmac_t		*softmac = sup->su_softmac;
12685d460eafSCathy Zhou 	softmac_switch_req_t	*req;
12695d460eafSCathy Zhou 	uint32_t		expected_mode;
12705d460eafSCathy Zhou 
12715d460eafSCathy Zhou 	mutex_enter(&softmac->smac_fp_mutex);
12725d460eafSCathy Zhou 	req = list_head(&sup->su_req_list);
12735d460eafSCathy Zhou 	list_remove(&sup->su_req_list, req);
12745d460eafSCathy Zhou 	expected_mode = req->ssq_expected_mode;
12755d460eafSCathy Zhou 	kmem_free(req, sizeof (softmac_switch_req_t));
12765d460eafSCathy Zhou 
12775d460eafSCathy Zhou 	if (expected_mode == sup->su_mode) {
12785d460eafSCathy Zhou 		mutex_exit(&softmac->smac_fp_mutex);
12795d460eafSCathy Zhou 		return;
12805d460eafSCathy Zhou 	}
12815d460eafSCathy Zhou 
12825d460eafSCathy Zhou 	ASSERT(!sup->su_bound);
12835d460eafSCathy Zhou 	mutex_exit(&softmac->smac_fp_mutex);
12845d460eafSCathy Zhou 
12855d460eafSCathy Zhou 	/*
12865d460eafSCathy Zhou 	 * It is fine if the expected mode is fast-path and we fail
12875d460eafSCathy Zhou 	 * to enable fastpath on this stream.
12885d460eafSCathy Zhou 	 */
12895d460eafSCathy Zhou 	if (expected_mode == SOFTMAC_SLOWPATH)
12905d460eafSCathy Zhou 		softmac_fastpath_tear(sup);
12915d460eafSCathy Zhou 	else
12925d460eafSCathy Zhou 		(void) softmac_fastpath_setup(sup);
12935d460eafSCathy Zhou }
1294