10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51502Sericheng  * Common Development and Distribution License (the "License").
61502Sericheng  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
221353Sericheng  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * Data-Link Driver
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/debug.h>
340Sstevel@tonic-gate #include <sys/sysmacros.h>
350Sstevel@tonic-gate #include <sys/stream.h>
360Sstevel@tonic-gate #include <sys/ddi.h>
370Sstevel@tonic-gate #include <sys/sunddi.h>
380Sstevel@tonic-gate #include <sys/strsun.h>
391184Skrgopi #include <sys/cpuvar.h>
400Sstevel@tonic-gate #include <sys/dlpi.h>
410Sstevel@tonic-gate #include <netinet/in.h>
420Sstevel@tonic-gate #include <sys/sdt.h>
430Sstevel@tonic-gate #include <sys/strsubr.h>
440Sstevel@tonic-gate #include <sys/vlan.h>
450Sstevel@tonic-gate #include <sys/mac.h>
460Sstevel@tonic-gate #include <sys/dls.h>
470Sstevel@tonic-gate #include <sys/dld.h>
480Sstevel@tonic-gate #include <sys/dld_impl.h>
491184Skrgopi #include <sys/dls_soft_ring.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate typedef boolean_t proto_reqfunc_t(dld_str_t *, union DL_primitives *, mblk_t *);
520Sstevel@tonic-gate 
530Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
540Sstevel@tonic-gate     proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
550Sstevel@tonic-gate     proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
560Sstevel@tonic-gate     proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
570Sstevel@tonic-gate     proto_notify_req, proto_unitdata_req, proto_passive_req;
580Sstevel@tonic-gate 
59269Sericheng static void proto_poll_disable(dld_str_t *);
601184Skrgopi static boolean_t proto_poll_enable(dld_str_t *, dl_capab_dls_t *);
61269Sericheng static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
620Sstevel@tonic-gate 
631353Sericheng static task_func_t proto_process_unbind_req, proto_process_detach_req;
641353Sericheng 
651184Skrgopi static void proto_soft_ring_disable(dld_str_t *);
661184Skrgopi static boolean_t proto_soft_ring_enable(dld_str_t *, dl_capab_dls_t *);
671184Skrgopi static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
681184Skrgopi static void proto_change_soft_ring_fanout(dld_str_t *, int);
691184Skrgopi 
700Sstevel@tonic-gate #define	DL_ACK_PENDING(state) \
710Sstevel@tonic-gate 	((state) == DL_ATTACH_PENDING || \
720Sstevel@tonic-gate 	(state) == DL_DETACH_PENDING || \
730Sstevel@tonic-gate 	(state) == DL_BIND_PENDING || \
740Sstevel@tonic-gate 	(state) == DL_UNBIND_PENDING)
750Sstevel@tonic-gate 
760Sstevel@tonic-gate /*
77269Sericheng  * Process a DLPI protocol message.
78269Sericheng  * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ,
79269Sericheng  * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an
80269Sericheng  * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t
81269Sericheng  * as 'passive' and forbids it from being subsequently made 'active'
82269Sericheng  * by the above primitives.
830Sstevel@tonic-gate  */
840Sstevel@tonic-gate void
850Sstevel@tonic-gate dld_proto(dld_str_t *dsp, mblk_t *mp)
860Sstevel@tonic-gate {
870Sstevel@tonic-gate 	union DL_primitives	*udlp;
880Sstevel@tonic-gate 	t_uscalar_t		prim;
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (t_uscalar_t)) {
910Sstevel@tonic-gate 		freemsg(mp);
920Sstevel@tonic-gate 		return;
930Sstevel@tonic-gate 	}
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 	udlp = (union DL_primitives *)mp->b_rptr;
960Sstevel@tonic-gate 	prim = udlp->dl_primitive;
970Sstevel@tonic-gate 
98269Sericheng 	switch (prim) {
99269Sericheng 	case DL_INFO_REQ:
100269Sericheng 		(void) proto_info_req(dsp, udlp, mp);
101269Sericheng 		break;
102269Sericheng 	case DL_BIND_REQ:
103269Sericheng 		(void) proto_bind_req(dsp, udlp, mp);
104269Sericheng 		break;
105269Sericheng 	case DL_UNBIND_REQ:
106269Sericheng 		(void) proto_unbind_req(dsp, udlp, mp);
107269Sericheng 		break;
108269Sericheng 	case DL_UNITDATA_REQ:
109269Sericheng 		(void) proto_unitdata_req(dsp, udlp, mp);
110269Sericheng 		break;
111269Sericheng 	case DL_UDQOS_REQ:
112269Sericheng 		(void) proto_udqos_req(dsp, udlp, mp);
113269Sericheng 		break;
114269Sericheng 	case DL_ATTACH_REQ:
115269Sericheng 		(void) proto_attach_req(dsp, udlp, mp);
116269Sericheng 		break;
117269Sericheng 	case DL_DETACH_REQ:
118269Sericheng 		(void) proto_detach_req(dsp, udlp, mp);
119269Sericheng 		break;
120269Sericheng 	case DL_ENABMULTI_REQ:
121269Sericheng 		(void) proto_enabmulti_req(dsp, udlp, mp);
122269Sericheng 		break;
123269Sericheng 	case DL_DISABMULTI_REQ:
124269Sericheng 		(void) proto_disabmulti_req(dsp, udlp, mp);
125269Sericheng 		break;
126269Sericheng 	case DL_PROMISCON_REQ:
127269Sericheng 		(void) proto_promiscon_req(dsp, udlp, mp);
128269Sericheng 		break;
129269Sericheng 	case DL_PROMISCOFF_REQ:
130269Sericheng 		(void) proto_promiscoff_req(dsp, udlp, mp);
131269Sericheng 		break;
132269Sericheng 	case DL_PHYS_ADDR_REQ:
133269Sericheng 		(void) proto_physaddr_req(dsp, udlp, mp);
134269Sericheng 		break;
135269Sericheng 	case DL_SET_PHYS_ADDR_REQ:
136269Sericheng 		(void) proto_setphysaddr_req(dsp, udlp, mp);
137269Sericheng 		break;
138269Sericheng 	case DL_NOTIFY_REQ:
139269Sericheng 		(void) proto_notify_req(dsp, udlp, mp);
140269Sericheng 		break;
141269Sericheng 	case DL_CAPABILITY_REQ:
142269Sericheng 		(void) proto_capability_req(dsp, udlp, mp);
143269Sericheng 		break;
144269Sericheng 	case DL_PASSIVE_REQ:
145269Sericheng 		(void) proto_passive_req(dsp, udlp, mp);
146269Sericheng 		break;
147269Sericheng 	default:
148269Sericheng 		(void) proto_req(dsp, udlp, mp);
149269Sericheng 		break;
1500Sstevel@tonic-gate 	}
1510Sstevel@tonic-gate }
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate /*
1541353Sericheng  * Finish any pending operations.
1551353Sericheng  * Requests that need to be processed asynchronously will be handled
1561353Sericheng  * by a separate thread. After this function returns, other threads
1571353Sericheng  * will be allowed to enter dld; they will not be able to do anything
1581353Sericheng  * until ds_dlstate transitions to a non-pending state.
1590Sstevel@tonic-gate  */
160269Sericheng void
161269Sericheng dld_finish_pending_ops(dld_str_t *dsp)
1620Sstevel@tonic-gate {
1631353Sericheng 	task_func_t *op = NULL;
1641353Sericheng 
165269Sericheng 	ASSERT(MUTEX_HELD(&dsp->ds_thr_lock));
166269Sericheng 	ASSERT(dsp->ds_thr == 0);
1670Sstevel@tonic-gate 
1681353Sericheng 	op = dsp->ds_pending_op;
1691353Sericheng 	dsp->ds_pending_op = NULL;
1701353Sericheng 	mutex_exit(&dsp->ds_thr_lock);
1711353Sericheng 	if (op != NULL)
1721353Sericheng 		(void) taskq_dispatch(system_taskq, op, dsp, TQ_SLEEP);
1730Sstevel@tonic-gate }
1740Sstevel@tonic-gate 
175269Sericheng #define	NEG(x)	-(x)
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate typedef struct dl_info_ack_wrapper {
1780Sstevel@tonic-gate 	dl_info_ack_t		dl_info;
1792311Sseb 	uint8_t			dl_addr[MAXMACADDRLEN + sizeof (uint16_t)];
1802311Sseb 	uint8_t			dl_brdcst_addr[MAXMACADDRLEN];
1810Sstevel@tonic-gate 	dl_qos_cl_range1_t	dl_qos_range1;
1820Sstevel@tonic-gate 	dl_qos_cl_sel1_t	dl_qos_sel1;
1830Sstevel@tonic-gate } dl_info_ack_wrapper_t;
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate /*
186269Sericheng  * DL_INFO_REQ
1870Sstevel@tonic-gate  */
188269Sericheng /*ARGSUSED*/
189269Sericheng static boolean_t
190269Sericheng proto_info_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1910Sstevel@tonic-gate {
1920Sstevel@tonic-gate 	dl_info_ack_wrapper_t	*dlwp;
1930Sstevel@tonic-gate 	dl_info_ack_t		*dlp;
1940Sstevel@tonic-gate 	dl_qos_cl_sel1_t	*selp;
1950Sstevel@tonic-gate 	dl_qos_cl_range1_t	*rangep;
1960Sstevel@tonic-gate 	uint8_t			*addr;
1970Sstevel@tonic-gate 	uint8_t			*brdcst_addr;
1980Sstevel@tonic-gate 	uint_t			addr_length;
1990Sstevel@tonic-gate 	uint_t			sap_length;
200269Sericheng 	mac_info_t		minfo;
201269Sericheng 	mac_info_t		*minfop;
202269Sericheng 	queue_t			*q = dsp->ds_wq;
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	/*
2050Sstevel@tonic-gate 	 * Swap the request message for one large enough to contain the
2060Sstevel@tonic-gate 	 * wrapper structure defined above.
2070Sstevel@tonic-gate 	 */
208269Sericheng 	if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
2090Sstevel@tonic-gate 	    M_PCPROTO, 0)) == NULL)
210269Sericheng 		return (B_FALSE);
211269Sericheng 
212269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
2150Sstevel@tonic-gate 	dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	dlp = &(dlwp->dl_info);
2180Sstevel@tonic-gate 	ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	dlp->dl_primitive = DL_INFO_ACK;
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	/*
2230Sstevel@tonic-gate 	 * Set up the sub-structure pointers.
2240Sstevel@tonic-gate 	 */
2250Sstevel@tonic-gate 	addr = dlwp->dl_addr;
2260Sstevel@tonic-gate 	brdcst_addr = dlwp->dl_brdcst_addr;
2270Sstevel@tonic-gate 	rangep = &(dlwp->dl_qos_range1);
2280Sstevel@tonic-gate 	selp = &(dlwp->dl_qos_sel1);
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 	/*
2310Sstevel@tonic-gate 	 * This driver supports only version 2 connectionless DLPI provider
2320Sstevel@tonic-gate 	 * nodes.
2330Sstevel@tonic-gate 	 */
2340Sstevel@tonic-gate 	dlp->dl_service_mode = DL_CLDLS;
2350Sstevel@tonic-gate 	dlp->dl_version = DL_VERSION_2;
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	/*
238269Sericheng 	 * Set the style of the provider
2390Sstevel@tonic-gate 	 */
240269Sericheng 	dlp->dl_provider_style = dsp->ds_style;
2410Sstevel@tonic-gate 	ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
2420Sstevel@tonic-gate 	    dlp->dl_provider_style == DL_STYLE2);
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 	/*
2450Sstevel@tonic-gate 	 * Set the current DLPI state.
2460Sstevel@tonic-gate 	 */
2470Sstevel@tonic-gate 	dlp->dl_current_state = dsp->ds_dlstate;
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	/*
250269Sericheng 	 * Gratuitously set the media type. This is to deal with modules
251269Sericheng 	 * that assume the media type is known prior to DL_ATTACH_REQ
2520Sstevel@tonic-gate 	 * being completed.
2530Sstevel@tonic-gate 	 */
2540Sstevel@tonic-gate 	dlp->dl_mac_type = DL_ETHER;
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	/*
257269Sericheng 	 * If the stream is not at least attached we try to retrieve the
258269Sericheng 	 * mac_info using mac_info_get()
2590Sstevel@tonic-gate 	 */
2600Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
2610Sstevel@tonic-gate 	    dsp->ds_dlstate == DL_ATTACH_PENDING ||
262269Sericheng 	    dsp->ds_dlstate == DL_DETACH_PENDING) {
263269Sericheng 		if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) {
264269Sericheng 			/*
265269Sericheng 			 * Cannot find mac_info. giving up.
266269Sericheng 			 */
267269Sericheng 			goto done;
268269Sericheng 		}
269269Sericheng 		minfop = &minfo;
270269Sericheng 	} else {
271269Sericheng 		minfop = (mac_info_t *)dsp->ds_mip;
272269Sericheng 	}
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	/*
2750Sstevel@tonic-gate 	 * Set the media type (properly this time).
2760Sstevel@tonic-gate 	 */
277269Sericheng 	dlp->dl_mac_type = minfop->mi_media;
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	/*
2800Sstevel@tonic-gate 	 * Set the DLSAP length. We only support 16 bit values and they
2810Sstevel@tonic-gate 	 * appear after the MAC address portion of DLSAP addresses.
2820Sstevel@tonic-gate 	 */
2830Sstevel@tonic-gate 	sap_length = sizeof (uint16_t);
2840Sstevel@tonic-gate 	dlp->dl_sap_length = NEG(sap_length);
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	/*
2870Sstevel@tonic-gate 	 * Set the minimum and maximum payload sizes.
2880Sstevel@tonic-gate 	 */
289269Sericheng 	dlp->dl_min_sdu = minfop->mi_sdu_min;
290269Sericheng 	dlp->dl_max_sdu = minfop->mi_sdu_max;
2910Sstevel@tonic-gate 
292269Sericheng 	addr_length = minfop->mi_addr_length;
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	/*
2950Sstevel@tonic-gate 	 * Copy in the media broadcast address.
2960Sstevel@tonic-gate 	 */
2972311Sseb 	if (minfop->mi_brdcst_addr != NULL) {
2982311Sseb 		dlp->dl_brdcst_addr_offset =
2992311Sseb 		    (uintptr_t)brdcst_addr - (uintptr_t)dlp;
3002311Sseb 		bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
3012311Sseb 		dlp->dl_brdcst_addr_length = addr_length;
3022311Sseb 	}
3030Sstevel@tonic-gate 
3042760Sdg199075 	dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
3052760Sdg199075 	dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
3060Sstevel@tonic-gate 
3072760Sdg199075 	rangep->dl_qos_type = DL_QOS_CL_RANGE1;
3082760Sdg199075 	rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
3092760Sdg199075 	rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
3102760Sdg199075 	rangep->dl_protection.dl_min = DL_UNKNOWN;
3112760Sdg199075 	rangep->dl_protection.dl_max = DL_UNKNOWN;
3122760Sdg199075 	rangep->dl_residual_error = DL_UNKNOWN;
3130Sstevel@tonic-gate 
3142760Sdg199075 	/*
3152760Sdg199075 	 * Specify the supported range of priorities.
3162760Sdg199075 	 */
3172760Sdg199075 	rangep->dl_priority.dl_min = 0;
3182760Sdg199075 	rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
3190Sstevel@tonic-gate 
3202760Sdg199075 	dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
3212760Sdg199075 	dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
3220Sstevel@tonic-gate 
3232760Sdg199075 	selp->dl_qos_type = DL_QOS_CL_SEL1;
3242760Sdg199075 	selp->dl_trans_delay = DL_UNKNOWN;
3252760Sdg199075 	selp->dl_protection = DL_UNKNOWN;
3262760Sdg199075 	selp->dl_residual_error = DL_UNKNOWN;
3272760Sdg199075 
3282760Sdg199075 	/*
3292760Sdg199075 	 * Specify the current priority (which can be changed by
3302760Sdg199075 	 * the DL_UDQOS_REQ primitive).
3312760Sdg199075 	 */
3322760Sdg199075 	selp->dl_priority = dsp->ds_pri;
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
3350Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
3360Sstevel@tonic-gate 		/*
3370Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
3380Sstevel@tonic-gate 		 * DLSAP address.
3390Sstevel@tonic-gate 		 */
3400Sstevel@tonic-gate 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
3412311Sseb 		if (addr_length > 0)
3422311Sseb 			bcopy(dsp->ds_curr_addr, addr, addr_length);
3430Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3440Sstevel@tonic-gate 	}
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate done:
3470Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0));
3480Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_range_offset != 0,
3490Sstevel@tonic-gate 	    dlp->dl_qos_range_length != 0));
3500Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0));
3510Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0,
3520Sstevel@tonic-gate 	    dlp->dl_brdcst_addr_length != 0));
3530Sstevel@tonic-gate 
354269Sericheng 	rw_exit(&dsp->ds_lock);
355269Sericheng 
356269Sericheng 	qreply(q, mp);
357269Sericheng 	return (B_TRUE);
358269Sericheng }
359269Sericheng 
360269Sericheng /*
361269Sericheng  * DL_ATTACH_REQ
362269Sericheng  */
363269Sericheng static boolean_t
364269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
365269Sericheng {
366269Sericheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)udlp;
367269Sericheng 	int		err = 0;
368269Sericheng 	t_uscalar_t	dl_err;
369269Sericheng 	queue_t		*q = dsp->ds_wq;
370269Sericheng 
371269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
372269Sericheng 
373269Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
374269Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
375269Sericheng 		dl_err = DL_BADPRIM;
376269Sericheng 		goto failed;
377269Sericheng 	}
378269Sericheng 
379269Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
380269Sericheng 		dl_err = DL_OUTSTATE;
381269Sericheng 		goto failed;
382269Sericheng 	}
383269Sericheng 
384269Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
385269Sericheng 
386269Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
387269Sericheng 	if (err != 0) {
388269Sericheng 		switch (err) {
389269Sericheng 		case ENOENT:
390269Sericheng 			dl_err = DL_BADPPA;
391269Sericheng 			err = 0;
392269Sericheng 			break;
393269Sericheng 		default:
394269Sericheng 			dl_err = DL_SYSERR;
395269Sericheng 			break;
396269Sericheng 		}
397269Sericheng 		dsp->ds_dlstate = DL_UNATTACHED;
398269Sericheng 		goto failed;
399269Sericheng 	}
400269Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
401269Sericheng 	rw_exit(&dsp->ds_lock);
402269Sericheng 
403269Sericheng 	dlokack(q, mp, DL_ATTACH_REQ);
404269Sericheng 	return (B_TRUE);
405269Sericheng failed:
406269Sericheng 	rw_exit(&dsp->ds_lock);
407269Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
408269Sericheng 	return (B_FALSE);
4090Sstevel@tonic-gate }
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate /*
412269Sericheng  * DL_DETACH_REQ
4130Sstevel@tonic-gate  */
4141353Sericheng static void
4151353Sericheng proto_process_detach_req(void *arg)
4161353Sericheng {
4171353Sericheng 	dld_str_t	*dsp = arg;
4181353Sericheng 	mblk_t		*mp;
4191353Sericheng 
4201353Sericheng 	/*
4211353Sericheng 	 * We don't need to hold locks because no other thread
4221353Sericheng 	 * would manipulate dsp while it is in a PENDING state.
4231353Sericheng 	 */
4241353Sericheng 	ASSERT(dsp->ds_pending_req != NULL);
4251353Sericheng 	ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING);
4261353Sericheng 
4271353Sericheng 	mp = dsp->ds_pending_req;
4281353Sericheng 	dsp->ds_pending_req = NULL;
4291353Sericheng 	dld_str_detach(dsp);
4301353Sericheng 	dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
4311353Sericheng 
4321353Sericheng 	DLD_WAKEUP(dsp);
4331353Sericheng }
4341353Sericheng 
435269Sericheng /*ARGSUSED*/
436269Sericheng static boolean_t
437269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4380Sstevel@tonic-gate {
439269Sericheng 	queue_t		*q = dsp->ds_wq;
440269Sericheng 	t_uscalar_t	dl_err;
4410Sstevel@tonic-gate 
442269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
4430Sstevel@tonic-gate 
444269Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
445269Sericheng 		dl_err = DL_BADPRIM;
446269Sericheng 		goto failed;
447269Sericheng 	}
4480Sstevel@tonic-gate 
449269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
450269Sericheng 		dl_err = DL_OUTSTATE;
451269Sericheng 		goto failed;
4520Sstevel@tonic-gate 	}
4530Sstevel@tonic-gate 
454269Sericheng 	if (dsp->ds_style == DL_STYLE1) {
455269Sericheng 		dl_err = DL_BADPRIM;
456269Sericheng 		goto failed;
457269Sericheng 	}
458269Sericheng 
459269Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
460269Sericheng 
461269Sericheng 	/*
462269Sericheng 	 * Complete the detach when the driver is single-threaded.
463269Sericheng 	 */
464269Sericheng 	mutex_enter(&dsp->ds_thr_lock);
4651353Sericheng 	ASSERT(dsp->ds_pending_req == NULL);
4661353Sericheng 	dsp->ds_pending_req = mp;
4671353Sericheng 	dsp->ds_pending_op = proto_process_detach_req;
4681353Sericheng 	dsp->ds_pending_cnt++;
469269Sericheng 	mutex_exit(&dsp->ds_thr_lock);
470269Sericheng 	rw_exit(&dsp->ds_lock);
471269Sericheng 
472269Sericheng 	return (B_TRUE);
473269Sericheng failed:
474269Sericheng 	rw_exit(&dsp->ds_lock);
475269Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
476269Sericheng 	return (B_FALSE);
4770Sstevel@tonic-gate }
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate /*
480269Sericheng  * DL_BIND_REQ
4810Sstevel@tonic-gate  */
482269Sericheng static boolean_t
483269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4840Sstevel@tonic-gate {
485269Sericheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)udlp;
486269Sericheng 	int		err = 0;
4873037Syz147064 	uint8_t		dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)];
4883037Syz147064 	uint_t		dlsap_addr_length;
489269Sericheng 	t_uscalar_t	dl_err;
490269Sericheng 	t_scalar_t	sap;
491269Sericheng 	queue_t		*q = dsp->ds_wq;
492269Sericheng 
4931521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
4941521Syz147064 
495269Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
496269Sericheng 		dl_err = DL_BADPRIM;
497269Sericheng 		goto failed;
498269Sericheng 	}
499269Sericheng 
500269Sericheng 	if (dlp->dl_xidtest_flg != 0) {
501269Sericheng 		dl_err = DL_NOAUTO;
502269Sericheng 		goto failed;
503269Sericheng 	}
504269Sericheng 
505269Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
506269Sericheng 		dl_err = DL_UNSUPPORTED;
507269Sericheng 		goto failed;
508269Sericheng 	}
509269Sericheng 
510269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
511269Sericheng 		dl_err = DL_OUTSTATE;
512269Sericheng 		goto failed;
513269Sericheng 	}
5140Sstevel@tonic-gate 
515269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
516269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
517269Sericheng 		dl_err = DL_SYSERR;
518269Sericheng 		err = EBUSY;
519269Sericheng 		goto failed;
520269Sericheng 	}
521269Sericheng 
522269Sericheng 	dsp->ds_dlstate = DL_BIND_PENDING;
523269Sericheng 	/*
524269Sericheng 	 * Set the receive callback.
525269Sericheng 	 */
526269Sericheng 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ?
527269Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
5280Sstevel@tonic-gate 
529269Sericheng 	/*
530269Sericheng 	 * Bind the channel such that it can receive packets.
531269Sericheng 	 */
532269Sericheng 	sap = dsp->ds_sap = dlp->dl_sap;
533269Sericheng 	err = dls_bind(dsp->ds_dc, dlp->dl_sap);
534269Sericheng 	if (err != 0) {
535269Sericheng 		switch (err) {
536269Sericheng 		case EINVAL:
537269Sericheng 			dl_err = DL_BADADDR;
538269Sericheng 			err = 0;
539269Sericheng 			break;
540269Sericheng 		default:
541269Sericheng 			dl_err = DL_SYSERR;
542269Sericheng 			break;
543269Sericheng 		}
544269Sericheng 		dsp->ds_dlstate = DL_UNBOUND;
545269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
546269Sericheng 			dls_active_clear(dsp->ds_dc);
547269Sericheng 
5480Sstevel@tonic-gate 		goto failed;
549269Sericheng 	}
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 	/*
5520Sstevel@tonic-gate 	 * Copy in MAC address.
5530Sstevel@tonic-gate 	 */
5543037Syz147064 	dlsap_addr_length = dsp->ds_mip->mi_addr_length;
5553037Syz147064 	bcopy(dsp->ds_curr_addr, dlsap_addr, dlsap_addr_length);
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	/*
5583037Syz147064 	 * Copy in the SAP.
5590Sstevel@tonic-gate 	 */
5603037Syz147064 	*(uint16_t *)(dlsap_addr + dlsap_addr_length) = dsp->ds_sap;
5613037Syz147064 	dlsap_addr_length += sizeof (uint16_t);
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
564269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
565269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
5660Sstevel@tonic-gate 
567269Sericheng 	rw_exit(&dsp->ds_lock);
568269Sericheng 
5693037Syz147064 	dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0);
570269Sericheng 	return (B_TRUE);
5710Sstevel@tonic-gate failed:
572269Sericheng 	rw_exit(&dsp->ds_lock);
573269Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
574269Sericheng 	return (B_FALSE);
5750Sstevel@tonic-gate }
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate /*
578269Sericheng  * DL_UNBIND_REQ
5790Sstevel@tonic-gate  */
580269Sericheng /*ARGSUSED*/
5811353Sericheng static void
5821353Sericheng proto_process_unbind_req(void *arg)
5830Sstevel@tonic-gate {
5841353Sericheng 	dld_str_t	*dsp = arg;
5851353Sericheng 	mblk_t		*mp;
586269Sericheng 
5871353Sericheng 	/*
5881353Sericheng 	 * We don't need to hold locks because no other thread
5891353Sericheng 	 * would manipulate dsp while it is in a PENDING state.
5901353Sericheng 	 */
5911353Sericheng 	ASSERT(dsp->ds_pending_req != NULL);
5921353Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBIND_PENDING);
593269Sericheng 
594269Sericheng 	/*
595269Sericheng 	 * Flush any remaining packets scheduled for transmission.
596269Sericheng 	 */
597269Sericheng 	dld_tx_flush(dsp);
598269Sericheng 
599269Sericheng 	/*
600269Sericheng 	 * Unbind the channel to stop packets being received.
601269Sericheng 	 */
602269Sericheng 	dls_unbind(dsp->ds_dc);
603269Sericheng 
604269Sericheng 	/*
605269Sericheng 	 * Disable polling mode, if it is enabled.
606269Sericheng 	 */
607269Sericheng 	proto_poll_disable(dsp);
608269Sericheng 
609269Sericheng 	/*
610*3115Syl150051 	 * Clear LSO flags.
611*3115Syl150051 	 */
612*3115Syl150051 	dsp->ds_lso = B_FALSE;
613*3115Syl150051 	dsp->ds_lso_max = 0;
614*3115Syl150051 
615*3115Syl150051 	/*
616269Sericheng 	 * Clear the receive callback.
617269Sericheng 	 */
618269Sericheng 	dls_rx_set(dsp->ds_dc, NULL, NULL);
619269Sericheng 
620269Sericheng 	/*
621269Sericheng 	 * Set the mode back to the default (unitdata).
622269Sericheng 	 */
623269Sericheng 	dsp->ds_mode = DLD_UNITDATA;
624269Sericheng 
6251184Skrgopi 	/*
6261184Skrgopi 	 * If soft rings were enabled, the workers
6271353Sericheng 	 * should be quiesced. We cannot check for
6281184Skrgopi 	 * ds_soft_ring flag because
6291184Skrgopi 	 * proto_soft_ring_disable() called from
6301184Skrgopi 	 * proto_capability_req() would have reset it.
6311184Skrgopi 	 */
6321353Sericheng 	if (dls_soft_ring_workers(dsp->ds_dc))
6331353Sericheng 		dls_soft_ring_disable(dsp->ds_dc);
6341353Sericheng 
6351353Sericheng 	mp = dsp->ds_pending_req;
6361353Sericheng 	dsp->ds_pending_req = NULL;
6371353Sericheng 	dsp->ds_dlstate = DL_UNBOUND;
6381353Sericheng 	dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ);
6391353Sericheng 
6401353Sericheng 	DLD_WAKEUP(dsp);
6411353Sericheng }
6421353Sericheng 
6431353Sericheng /*ARGSUSED*/
6441353Sericheng static boolean_t
6451353Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6461353Sericheng {
6471353Sericheng 	queue_t		*q = dsp->ds_wq;
6481353Sericheng 	t_uscalar_t	dl_err;
6491353Sericheng 
6501353Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
6511353Sericheng 
6521353Sericheng 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
6531353Sericheng 		dl_err = DL_BADPRIM;
6541353Sericheng 		goto failed;
6551184Skrgopi 	}
6561184Skrgopi 
6571353Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
6581353Sericheng 		dl_err = DL_OUTSTATE;
6591353Sericheng 		goto failed;
6601353Sericheng 	}
6611353Sericheng 
6621353Sericheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
6631353Sericheng 
6641353Sericheng 	mutex_enter(&dsp->ds_thr_lock);
6651353Sericheng 	ASSERT(dsp->ds_pending_req == NULL);
6661353Sericheng 	dsp->ds_pending_req = mp;
6671353Sericheng 	dsp->ds_pending_op = proto_process_unbind_req;
6681353Sericheng 	dsp->ds_pending_cnt++;
6691353Sericheng 	mutex_exit(&dsp->ds_thr_lock);
670269Sericheng 	rw_exit(&dsp->ds_lock);
671269Sericheng 
672269Sericheng 	return (B_TRUE);
673269Sericheng failed:
674269Sericheng 	rw_exit(&dsp->ds_lock);
675269Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
676269Sericheng 	return (B_FALSE);
6770Sstevel@tonic-gate }
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate /*
680269Sericheng  * DL_PROMISCON_REQ
6810Sstevel@tonic-gate  */
682269Sericheng static boolean_t
683269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6840Sstevel@tonic-gate {
685269Sericheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp;
686269Sericheng 	int		err = 0;
687269Sericheng 	t_uscalar_t	dl_err;
688269Sericheng 	uint32_t	promisc_saved;
689269Sericheng 	queue_t		*q = dsp->ds_wq;
690269Sericheng 
6911521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
6921521Syz147064 
693269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
694269Sericheng 		dl_err = DL_BADPRIM;
695269Sericheng 		goto failed;
696269Sericheng 	}
697269Sericheng 
698269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
699269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
700269Sericheng 		dl_err = DL_OUTSTATE;
7010Sstevel@tonic-gate 		goto failed;
702269Sericheng 	}
7030Sstevel@tonic-gate 
704269Sericheng 	promisc_saved = dsp->ds_promisc;
705269Sericheng 	switch (dlp->dl_level) {
706269Sericheng 	case DL_PROMISC_SAP:
707269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_SAP;
708269Sericheng 		break;
709269Sericheng 
710269Sericheng 	case DL_PROMISC_MULTI:
711269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_MULTI;
712269Sericheng 		break;
713269Sericheng 
714269Sericheng 	case DL_PROMISC_PHYS:
715269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_PHYS;
716269Sericheng 		break;
717269Sericheng 
718269Sericheng 	default:
719269Sericheng 		dl_err = DL_NOTSUPPORTED;
720269Sericheng 		goto failed;
721269Sericheng 	}
7220Sstevel@tonic-gate 
723269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
724269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
725269Sericheng 		dsp->ds_promisc = promisc_saved;
726269Sericheng 		dl_err = DL_SYSERR;
727269Sericheng 		err = EBUSY;
728269Sericheng 		goto failed;
729269Sericheng 	}
730269Sericheng 
731269Sericheng 	/*
732269Sericheng 	 * Adjust channel promiscuity.
733269Sericheng 	 */
734269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
735269Sericheng 	if (err != 0) {
736269Sericheng 		dl_err = DL_SYSERR;
737269Sericheng 		dsp->ds_promisc = promisc_saved;
738269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
739269Sericheng 			dls_active_clear(dsp->ds_dc);
740269Sericheng 
741269Sericheng 		goto failed;
742269Sericheng 	}
743269Sericheng 
744269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
745269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
746269Sericheng 
747269Sericheng 	rw_exit(&dsp->ds_lock);
748269Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
749269Sericheng 	return (B_TRUE);
7500Sstevel@tonic-gate failed:
751269Sericheng 	rw_exit(&dsp->ds_lock);
752269Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
753269Sericheng 	return (B_FALSE);
7540Sstevel@tonic-gate }
7550Sstevel@tonic-gate 
7560Sstevel@tonic-gate /*
757269Sericheng  * DL_PROMISCOFF_REQ
7580Sstevel@tonic-gate  */
759269Sericheng static boolean_t
760269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
7610Sstevel@tonic-gate {
762269Sericheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp;
763269Sericheng 	int		err = 0;
764269Sericheng 	t_uscalar_t	dl_err;
765269Sericheng 	uint32_t	promisc_saved;
766269Sericheng 	queue_t		*q = dsp->ds_wq;
767269Sericheng 
7681521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
769269Sericheng 
770269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
771269Sericheng 		dl_err = DL_BADPRIM;
7720Sstevel@tonic-gate 		goto failed;
773269Sericheng 	}
7740Sstevel@tonic-gate 
775269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
776269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
777269Sericheng 		dl_err = DL_OUTSTATE;
7780Sstevel@tonic-gate 		goto failed;
779269Sericheng 	}
7800Sstevel@tonic-gate 
781269Sericheng 	promisc_saved = dsp->ds_promisc;
782269Sericheng 	switch (dlp->dl_level) {
783269Sericheng 	case DL_PROMISC_SAP:
784269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
785269Sericheng 			dl_err = DL_NOTENAB;
786269Sericheng 			goto failed;
787269Sericheng 		}
788269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_SAP;
7890Sstevel@tonic-gate 		break;
7900Sstevel@tonic-gate 
791269Sericheng 	case DL_PROMISC_MULTI:
792269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
793269Sericheng 			dl_err = DL_NOTENAB;
794269Sericheng 			goto failed;
795269Sericheng 		}
796269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_MULTI;
797269Sericheng 		break;
798269Sericheng 
799269Sericheng 	case DL_PROMISC_PHYS:
800269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
801269Sericheng 			dl_err = DL_NOTENAB;
802269Sericheng 			goto failed;
803269Sericheng 		}
804269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_PHYS;
8050Sstevel@tonic-gate 		break;
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 	default:
808269Sericheng 		dl_err = DL_NOTSUPPORTED;
809269Sericheng 		goto failed;
810269Sericheng 	}
811269Sericheng 
812269Sericheng 	/*
813269Sericheng 	 * Adjust channel promiscuity.
814269Sericheng 	 */
815269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
816269Sericheng 	if (err != 0) {
817269Sericheng 		dsp->ds_promisc = promisc_saved;
8180Sstevel@tonic-gate 		dl_err = DL_SYSERR;
819269Sericheng 		goto failed;
820269Sericheng 	}
821269Sericheng 
822269Sericheng 	rw_exit(&dsp->ds_lock);
823269Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
824269Sericheng 	return (B_TRUE);
825269Sericheng failed:
826269Sericheng 	rw_exit(&dsp->ds_lock);
827269Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
828269Sericheng 	return (B_FALSE);
829269Sericheng }
830269Sericheng 
831269Sericheng /*
832269Sericheng  * DL_ENABMULTI_REQ
833269Sericheng  */
834269Sericheng static boolean_t
835269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
836269Sericheng {
837269Sericheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp;
838269Sericheng 	int		err = 0;
839269Sericheng 	t_uscalar_t	dl_err;
840269Sericheng 	queue_t		*q = dsp->ds_wq;
841269Sericheng 
842269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
843269Sericheng 
844269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
845269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
846269Sericheng 		dl_err = DL_OUTSTATE;
847269Sericheng 		goto failed;
848269Sericheng 	}
849269Sericheng 
850269Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
851269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
852269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
853269Sericheng 		dl_err = DL_BADPRIM;
854269Sericheng 		goto failed;
855269Sericheng 	}
856269Sericheng 
857269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
858269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
859269Sericheng 		dl_err = DL_SYSERR;
860269Sericheng 		err = EBUSY;
861269Sericheng 		goto failed;
8620Sstevel@tonic-gate 	}
8630Sstevel@tonic-gate 
864269Sericheng 	err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
865269Sericheng 	if (err != 0) {
866269Sericheng 		switch (err) {
867269Sericheng 		case EINVAL:
868269Sericheng 			dl_err = DL_BADADDR;
869269Sericheng 			err = 0;
870269Sericheng 			break;
871269Sericheng 		case ENOSPC:
872269Sericheng 			dl_err = DL_TOOMANY;
873269Sericheng 			err = 0;
874269Sericheng 			break;
875269Sericheng 		default:
876269Sericheng 			dl_err = DL_SYSERR;
877269Sericheng 			break;
878269Sericheng 		}
879269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
880269Sericheng 			dls_active_clear(dsp->ds_dc);
881269Sericheng 
882269Sericheng 		goto failed;
883269Sericheng 	}
884269Sericheng 
885269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
886269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
887269Sericheng 
888269Sericheng 	rw_exit(&dsp->ds_lock);
889269Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
890269Sericheng 	return (B_TRUE);
891269Sericheng failed:
892269Sericheng 	rw_exit(&dsp->ds_lock);
893269Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
894269Sericheng 	return (B_FALSE);
895269Sericheng }
896269Sericheng 
897269Sericheng /*
898269Sericheng  * DL_DISABMULTI_REQ
899269Sericheng  */
900269Sericheng static boolean_t
901269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
902269Sericheng {
903269Sericheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp;
904269Sericheng 	int		err = 0;
905269Sericheng 	t_uscalar_t	dl_err;
906269Sericheng 	queue_t		*q = dsp->ds_wq;
907269Sericheng 
908269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
909269Sericheng 
910269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
911269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
912269Sericheng 		dl_err = DL_OUTSTATE;
913269Sericheng 		goto failed;
914269Sericheng 	}
915269Sericheng 
916269Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
917269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
918269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
919269Sericheng 		dl_err = DL_BADPRIM;
920269Sericheng 		goto failed;
921269Sericheng 	}
922269Sericheng 
923269Sericheng 	err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
924269Sericheng 	if (err != 0) {
925269Sericheng 	switch (err) {
926269Sericheng 		case EINVAL:
927269Sericheng 			dl_err = DL_BADADDR;
928269Sericheng 			err = 0;
929269Sericheng 			break;
930269Sericheng 
931269Sericheng 		case ENOENT:
932269Sericheng 			dl_err = DL_NOTENAB;
933269Sericheng 			err = 0;
934269Sericheng 			break;
935269Sericheng 
936269Sericheng 		default:
937269Sericheng 			dl_err = DL_SYSERR;
938269Sericheng 			break;
939269Sericheng 		}
940269Sericheng 		goto failed;
941269Sericheng 	}
942269Sericheng 
943269Sericheng 	rw_exit(&dsp->ds_lock);
944269Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
945269Sericheng 	return (B_TRUE);
946269Sericheng failed:
947269Sericheng 	rw_exit(&dsp->ds_lock);
948269Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
949269Sericheng 	return (B_FALSE);
9500Sstevel@tonic-gate }
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate /*
953269Sericheng  * DL_PHYS_ADDR_REQ
9540Sstevel@tonic-gate  */
955269Sericheng static boolean_t
956269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
9570Sstevel@tonic-gate {
958269Sericheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp;
959269Sericheng 	queue_t		*q = dsp->ds_wq;
960269Sericheng 	t_uscalar_t	dl_err;
961269Sericheng 	char		*addr;
962269Sericheng 	uint_t		addr_length;
963269Sericheng 
964269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
965269Sericheng 
966269Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
967269Sericheng 		dl_err = DL_BADPRIM;
968269Sericheng 		goto failed;
969269Sericheng 	}
970269Sericheng 
971269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
972269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
973269Sericheng 		dl_err = DL_OUTSTATE;
974269Sericheng 		goto failed;
975269Sericheng 	}
9760Sstevel@tonic-gate 
977269Sericheng 	if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR &&
978269Sericheng 	    dlp->dl_addr_type != DL_FACT_PHYS_ADDR) {
979269Sericheng 		dl_err = DL_UNSUPPORTED;
9800Sstevel@tonic-gate 		goto failed;
981269Sericheng 	}
9820Sstevel@tonic-gate 
983269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
984269Sericheng 	addr = kmem_alloc(addr_length, KM_NOSLEEP);
985269Sericheng 	if (addr == NULL) {
986269Sericheng 		rw_exit(&dsp->ds_lock);
987269Sericheng 		merror(q, mp, ENOSR);
988269Sericheng 		return (B_FALSE);
989269Sericheng 	}
9900Sstevel@tonic-gate 
991269Sericheng 	/*
992269Sericheng 	 * Copy out the address before we drop the lock; we don't
993269Sericheng 	 * want to call dlphysaddrack() while holding ds_lock.
994269Sericheng 	 */
995269Sericheng 	bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ?
996269Sericheng 	    dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length);
997269Sericheng 
998269Sericheng 	rw_exit(&dsp->ds_lock);
999269Sericheng 	dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
1000269Sericheng 	kmem_free(addr, addr_length);
1001269Sericheng 	return (B_TRUE);
10020Sstevel@tonic-gate failed:
1003269Sericheng 	rw_exit(&dsp->ds_lock);
1004269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
1005269Sericheng 	return (B_FALSE);
1006269Sericheng }
10070Sstevel@tonic-gate 
1008269Sericheng /*
1009269Sericheng  * DL_SET_PHYS_ADDR_REQ
1010269Sericheng  */
1011269Sericheng static boolean_t
1012269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1013269Sericheng {
1014269Sericheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp;
1015269Sericheng 	int		err = 0;
1016269Sericheng 	t_uscalar_t	dl_err;
1017269Sericheng 	queue_t		*q = dsp->ds_wq;
10180Sstevel@tonic-gate 
1019269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1020269Sericheng 
1021269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1022269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1023269Sericheng 		dl_err = DL_OUTSTATE;
1024269Sericheng 		goto failed;
1025269Sericheng 	}
1026269Sericheng 
1027269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
1028269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
1029269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
1030269Sericheng 		dl_err = DL_BADPRIM;
1031269Sericheng 		goto failed;
10320Sstevel@tonic-gate 	}
10330Sstevel@tonic-gate 
1034269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
1035269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
1036269Sericheng 		dl_err = DL_SYSERR;
1037269Sericheng 		err = EBUSY;
1038269Sericheng 		goto failed;
1039269Sericheng 	}
1040269Sericheng 
1041269Sericheng 	err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset);
1042269Sericheng 	if (err != 0) {
1043269Sericheng 		switch (err) {
1044269Sericheng 		case EINVAL:
1045269Sericheng 			dl_err = DL_BADADDR;
1046269Sericheng 			err = 0;
1047269Sericheng 			break;
1048269Sericheng 
1049269Sericheng 		default:
1050269Sericheng 			dl_err = DL_SYSERR;
1051269Sericheng 			break;
1052269Sericheng 		}
1053269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1054269Sericheng 			dls_active_clear(dsp->ds_dc);
1055269Sericheng 
1056269Sericheng 		goto failed;
1057269Sericheng 	}
1058269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1059269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
1060269Sericheng 
1061269Sericheng 	rw_exit(&dsp->ds_lock);
1062269Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
1063269Sericheng 	return (B_TRUE);
1064269Sericheng failed:
1065269Sericheng 	rw_exit(&dsp->ds_lock);
1066269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
1067269Sericheng 	return (B_FALSE);
1068269Sericheng }
1069269Sericheng 
1070269Sericheng /*
1071269Sericheng  * DL_UDQOS_REQ
1072269Sericheng  */
1073269Sericheng static boolean_t
1074269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1075269Sericheng {
1076269Sericheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp;
1077269Sericheng 	dl_qos_cl_sel1_t *selp;
1078269Sericheng 	int		off, len;
1079269Sericheng 	t_uscalar_t	dl_err;
1080269Sericheng 	queue_t		*q = dsp->ds_wq;
1081269Sericheng 
1082269Sericheng 	off = dlp->dl_qos_offset;
1083269Sericheng 	len = dlp->dl_qos_length;
1084269Sericheng 
10851521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
10861521Syz147064 
1087269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
1088269Sericheng 		dl_err = DL_BADPRIM;
1089269Sericheng 		goto failed;
1090269Sericheng 	}
1091269Sericheng 
1092269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
1093269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
1094269Sericheng 		dl_err = DL_BADQOSTYPE;
1095269Sericheng 		goto failed;
1096269Sericheng 	}
1097269Sericheng 
10982760Sdg199075 	if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1099269Sericheng 	    selp->dl_priority < 0) {
1100269Sericheng 		dl_err = DL_BADQOSPARAM;
1101269Sericheng 		goto failed;
1102269Sericheng 	}
1103269Sericheng 
1104269Sericheng 	dsp->ds_pri = selp->dl_priority;
1105269Sericheng 
1106269Sericheng 	rw_exit(&dsp->ds_lock);
1107269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1108269Sericheng 	return (B_TRUE);
1109269Sericheng failed:
1110269Sericheng 	rw_exit(&dsp->ds_lock);
1111269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1112269Sericheng 	return (B_FALSE);
11130Sstevel@tonic-gate }
11140Sstevel@tonic-gate 
11151184Skrgopi static boolean_t
11161184Skrgopi check_ip_above(queue_t *q)
11171184Skrgopi {
11181184Skrgopi 	queue_t		*next_q;
11191184Skrgopi 	boolean_t	ret = B_TRUE;
11201184Skrgopi 
11211184Skrgopi 	claimstr(q);
11221184Skrgopi 	next_q = q->q_next;
11231184Skrgopi 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
11241184Skrgopi 		ret = B_FALSE;
11251184Skrgopi 	releasestr(q);
11261184Skrgopi 	return (ret);
11271184Skrgopi }
11281184Skrgopi 
11290Sstevel@tonic-gate /*
1130269Sericheng  * DL_CAPABILITY_REQ
11310Sstevel@tonic-gate  */
1132269Sericheng /*ARGSUSED*/
1133269Sericheng static boolean_t
1134269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
11350Sstevel@tonic-gate {
1136269Sericheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)udlp;
1137269Sericheng 	dl_capability_sub_t *sp;
1138269Sericheng 	size_t		size, len;
1139269Sericheng 	offset_t	off, end;
1140269Sericheng 	t_uscalar_t	dl_err;
1141269Sericheng 	queue_t		*q = dsp->ds_wq;
1142269Sericheng 	boolean_t	upgraded;
1143269Sericheng 
1144269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1145269Sericheng 
1146269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1147269Sericheng 		dl_err = DL_BADPRIM;
1148269Sericheng 		goto failed;
1149269Sericheng 	}
1150269Sericheng 
1151269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1152269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1153269Sericheng 		dl_err = DL_OUTSTATE;
1154269Sericheng 		goto failed;
1155269Sericheng 	}
1156269Sericheng 
1157269Sericheng 	/*
1158269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1159269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1160269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1161269Sericheng 	 */
1162269Sericheng 	if (dlp->dl_sub_length == 0) {
1163269Sericheng 		/* callee drops lock */
1164269Sericheng 		return (proto_capability_advertise(dsp, mp));
1165269Sericheng 	}
1166269Sericheng 
1167269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1168269Sericheng 		dl_err = DL_BADPRIM;
1169269Sericheng 		goto failed;
1170269Sericheng 	}
1171269Sericheng 
1172269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1173269Sericheng 
1174269Sericheng 	off = dlp->dl_sub_offset;
1175269Sericheng 	len = dlp->dl_sub_length;
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 	/*
1178269Sericheng 	 * Walk the list of capabilities to be enabled.
11790Sstevel@tonic-gate 	 */
1180269Sericheng 	upgraded = B_FALSE;
1181269Sericheng 	for (end = off + len; off < end; ) {
1182269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1183269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1184269Sericheng 
1185269Sericheng 		if (off + size > end ||
1186269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1187269Sericheng 			dl_err = DL_BADPRIM;
1188269Sericheng 			goto failed;
1189269Sericheng 		}
1190269Sericheng 
1191269Sericheng 		switch (sp->dl_cap) {
1192269Sericheng 		/*
1193269Sericheng 		 * TCP/IP checksum offload to hardware.
1194269Sericheng 		 */
1195269Sericheng 		case DL_CAPAB_HCKSUM: {
1196269Sericheng 			dl_capab_hcksum_t *hcksump;
1197269Sericheng 			dl_capab_hcksum_t hcksum;
1198269Sericheng 
1199269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1200269Sericheng 			/*
1201269Sericheng 			 * Copy for alignment.
1202269Sericheng 			 */
1203269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1204269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1205269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1206269Sericheng 			break;
1207269Sericheng 		}
1208269Sericheng 
1209269Sericheng 		/*
1210*3115Syl150051 		 * Large segment offload. (LSO)
1211*3115Syl150051 		 */
1212*3115Syl150051 		case DL_CAPAB_LSO: {
1213*3115Syl150051 			dl_capab_lso_t *lsop;
1214*3115Syl150051 			dl_capab_lso_t lso;
1215*3115Syl150051 
1216*3115Syl150051 			lsop = (dl_capab_lso_t *)&sp[1];
1217*3115Syl150051 			/*
1218*3115Syl150051 			 * Copy for alignment.
1219*3115Syl150051 			 */
1220*3115Syl150051 			bcopy(lsop, &lso, sizeof (dl_capab_lso_t));
1221*3115Syl150051 			dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq);
1222*3115Syl150051 			bcopy(&lso, lsop, sizeof (dl_capab_lso_t));
1223*3115Syl150051 			break;
1224*3115Syl150051 		}
1225*3115Syl150051 
1226*3115Syl150051 		/*
1227269Sericheng 		 * IP polling interface.
1228269Sericheng 		 */
1229269Sericheng 		case DL_CAPAB_POLL: {
12301184Skrgopi 			dl_capab_dls_t *pollp;
12311184Skrgopi 			dl_capab_dls_t	poll;
1232269Sericheng 
12331184Skrgopi 			pollp = (dl_capab_dls_t *)&sp[1];
1234269Sericheng 			/*
1235269Sericheng 			 * Copy for alignment.
1236269Sericheng 			 */
12371184Skrgopi 			bcopy(pollp, &poll, sizeof (dl_capab_dls_t));
1238269Sericheng 
1239269Sericheng 			/*
1240269Sericheng 			 * We need to become writer before enabling and/or
1241269Sericheng 			 * disabling the polling interface.  If we couldn'
1242269Sericheng 			 * upgrade, check state again after re-acquiring the
1243269Sericheng 			 * lock to make sure we can proceed.
1244269Sericheng 			 */
1245269Sericheng 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
1246269Sericheng 				rw_exit(&dsp->ds_lock);
1247269Sericheng 				rw_enter(&dsp->ds_lock, RW_WRITER);
1248269Sericheng 
1249269Sericheng 				if (dsp->ds_dlstate == DL_UNATTACHED ||
1250269Sericheng 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
1251269Sericheng 					dl_err = DL_OUTSTATE;
1252269Sericheng 					goto failed;
1253269Sericheng 				}
1254269Sericheng 			}
1255269Sericheng 			upgraded = B_TRUE;
1256269Sericheng 
12571184Skrgopi 			switch (poll.dls_flags) {
1258269Sericheng 			default:
1259269Sericheng 				/*FALLTHRU*/
1260269Sericheng 			case POLL_DISABLE:
1261269Sericheng 				proto_poll_disable(dsp);
1262269Sericheng 				break;
1263269Sericheng 
1264269Sericheng 			case POLL_ENABLE:
1265269Sericheng 				ASSERT(!(dld_opt & DLD_OPT_NO_POLL));
1266269Sericheng 
1267269Sericheng 				/*
1268269Sericheng 				 * Make sure polling is disabled.
1269269Sericheng 				 */
1270269Sericheng 				proto_poll_disable(dsp);
1271269Sericheng 
1272269Sericheng 				/*
1273269Sericheng 				 * Now attempt enable it.
1274269Sericheng 				 */
12751184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
12761184Skrgopi 				    proto_poll_enable(dsp, &poll)) {
12771184Skrgopi 					bzero(&poll, sizeof (dl_capab_dls_t));
12781184Skrgopi 					poll.dls_flags = POLL_ENABLE;
12791184Skrgopi 				}
1280269Sericheng 				break;
1281269Sericheng 			}
1282269Sericheng 
12831184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
12841184Skrgopi 			bcopy(&poll, pollp, sizeof (dl_capab_dls_t));
12851184Skrgopi 			break;
12861184Skrgopi 		}
12871184Skrgopi 		case DL_CAPAB_SOFT_RING: {
12881184Skrgopi 			dl_capab_dls_t *soft_ringp;
12891184Skrgopi 			dl_capab_dls_t soft_ring;
12901184Skrgopi 
12911184Skrgopi 			soft_ringp = (dl_capab_dls_t *)&sp[1];
12921184Skrgopi 			/*
12931184Skrgopi 			 * Copy for alignment.
12941184Skrgopi 			 */
12951184Skrgopi 			bcopy(soft_ringp, &soft_ring,
12961184Skrgopi 			    sizeof (dl_capab_dls_t));
12971184Skrgopi 
12981184Skrgopi 			/*
12991184Skrgopi 			 * We need to become writer before enabling and/or
13001184Skrgopi 			 * disabling the soft_ring interface.  If we couldn'
13011184Skrgopi 			 * upgrade, check state again after re-acquiring the
13021184Skrgopi 			 * lock to make sure we can proceed.
13031184Skrgopi 			 */
13041184Skrgopi 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
13051184Skrgopi 				rw_exit(&dsp->ds_lock);
13061184Skrgopi 				rw_enter(&dsp->ds_lock, RW_WRITER);
13071184Skrgopi 
13081184Skrgopi 				if (dsp->ds_dlstate == DL_UNATTACHED ||
13091184Skrgopi 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
13101184Skrgopi 					dl_err = DL_OUTSTATE;
13111184Skrgopi 					goto failed;
13121184Skrgopi 				}
13131184Skrgopi 			}
13141184Skrgopi 			upgraded = B_TRUE;
13151184Skrgopi 
13161184Skrgopi 			switch (soft_ring.dls_flags) {
13171184Skrgopi 			default:
13181184Skrgopi 				/*FALLTHRU*/
13191184Skrgopi 			case SOFT_RING_DISABLE:
13201184Skrgopi 				proto_soft_ring_disable(dsp);
13211184Skrgopi 				break;
13221184Skrgopi 
13231184Skrgopi 			case SOFT_RING_ENABLE:
13241184Skrgopi 				/*
13251184Skrgopi 				 * Make sure soft_ring is disabled.
13261184Skrgopi 				 */
13271184Skrgopi 				proto_soft_ring_disable(dsp);
13281184Skrgopi 
13291184Skrgopi 				/*
13301184Skrgopi 				 * Now attempt enable it.
13311184Skrgopi 				 */
13321184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
13331184Skrgopi 				    proto_soft_ring_enable(dsp, &soft_ring)) {
13341184Skrgopi 					bzero(&soft_ring,
13351184Skrgopi 					    sizeof (dl_capab_dls_t));
13361184Skrgopi 					soft_ring.dls_flags =
13371184Skrgopi 					    SOFT_RING_ENABLE;
13381184Skrgopi 				} else {
13391184Skrgopi 					bzero(&soft_ring,
13401184Skrgopi 					    sizeof (dl_capab_dls_t));
13411184Skrgopi 					soft_ring.dls_flags =
13421184Skrgopi 					    SOFT_RING_DISABLE;
13431184Skrgopi 				}
13441184Skrgopi 				break;
13451184Skrgopi 			}
13461184Skrgopi 
13471184Skrgopi 			dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
13481184Skrgopi 			bcopy(&soft_ring, soft_ringp,
13491184Skrgopi 			    sizeof (dl_capab_dls_t));
1350269Sericheng 			break;
1351269Sericheng 		}
1352269Sericheng 		default:
1353269Sericheng 			break;
1354269Sericheng 		}
1355269Sericheng 
1356269Sericheng 		off += size;
1357269Sericheng 	}
1358269Sericheng 	rw_exit(&dsp->ds_lock);
1359269Sericheng 	qreply(q, mp);
1360269Sericheng 	return (B_TRUE);
1361269Sericheng failed:
1362269Sericheng 	rw_exit(&dsp->ds_lock);
1363269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1364269Sericheng 	return (B_FALSE);
13650Sstevel@tonic-gate }
13660Sstevel@tonic-gate 
13670Sstevel@tonic-gate /*
1368269Sericheng  * DL_NOTIFY_REQ
13690Sstevel@tonic-gate  */
1370269Sericheng static boolean_t
1371269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
13720Sstevel@tonic-gate {
1373269Sericheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)udlp;
1374269Sericheng 	t_uscalar_t	dl_err;
1375269Sericheng 	queue_t		*q = dsp->ds_wq;
1376269Sericheng 	uint_t		note =
1377269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1378269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1379269Sericheng 	    DL_NOTE_PHYS_ADDR |
1380269Sericheng 	    DL_NOTE_LINK_UP |
1381269Sericheng 	    DL_NOTE_LINK_DOWN |
13822311Sseb 	    DL_NOTE_CAPAB_RENEG |
13832311Sseb 	    DL_NOTE_SPEED;
13840Sstevel@tonic-gate 
13851521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
13861521Syz147064 
1387269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1388269Sericheng 		dl_err = DL_BADPRIM;
1389269Sericheng 		goto failed;
1390269Sericheng 	}
13910Sstevel@tonic-gate 
1392269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1393269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1394269Sericheng 		dl_err = DL_OUTSTATE;
1395269Sericheng 		goto failed;
13960Sstevel@tonic-gate 	}
13970Sstevel@tonic-gate 
1398269Sericheng 	/*
1399269Sericheng 	 * Cache the notifications that are being enabled.
1400269Sericheng 	 */
1401269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1402269Sericheng 	rw_exit(&dsp->ds_lock);
1403269Sericheng 	/*
1404269Sericheng 	 * The ACK carries all notifications regardless of which set is
1405269Sericheng 	 * being enabled.
1406269Sericheng 	 */
1407269Sericheng 	dlnotifyack(q, mp, note);
1408269Sericheng 
1409269Sericheng 	/*
1410269Sericheng 	 * Solicit DL_NOTIFY_IND messages for each enabled notification.
1411269Sericheng 	 */
1412269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1413269Sericheng 	if (dsp->ds_notifications != 0) {
1414269Sericheng 		rw_exit(&dsp->ds_lock);
1415269Sericheng 		dld_str_notify_ind(dsp);
1416269Sericheng 	} else {
1417269Sericheng 		rw_exit(&dsp->ds_lock);
1418269Sericheng 	}
1419269Sericheng 	return (B_TRUE);
1420269Sericheng failed:
1421269Sericheng 	rw_exit(&dsp->ds_lock);
1422269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1423269Sericheng 	return (B_FALSE);
14240Sstevel@tonic-gate }
14250Sstevel@tonic-gate 
14260Sstevel@tonic-gate /*
1427269Sericheng  * DL_UINTDATA_REQ
14280Sstevel@tonic-gate  */
1429269Sericheng static boolean_t
1430269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
14310Sstevel@tonic-gate {
1432269Sericheng 	queue_t			*q = dsp->ds_wq;
1433269Sericheng 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)udlp;
1434269Sericheng 	off_t			off;
1435269Sericheng 	size_t			len, size;
1436269Sericheng 	const uint8_t		*addr;
1437269Sericheng 	uint16_t		sap;
1438269Sericheng 	uint_t			addr_length;
14392311Sseb 	mblk_t			*bp, *payload;
1440269Sericheng 	uint32_t		start, stuff, end, value, flags;
1441269Sericheng 	t_uscalar_t		dl_err;
1442269Sericheng 
1443269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1444269Sericheng 
1445269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1446269Sericheng 		dl_err = DL_BADPRIM;
1447269Sericheng 		goto failed;
1448269Sericheng 	}
1449269Sericheng 
1450269Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
1451269Sericheng 		dl_err = DL_OUTSTATE;
1452269Sericheng 		goto failed;
1453269Sericheng 	}
1454269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1455269Sericheng 
1456269Sericheng 	off = dlp->dl_dest_addr_offset;
1457269Sericheng 	len = dlp->dl_dest_addr_length;
1458269Sericheng 
1459269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1460269Sericheng 		dl_err = DL_BADPRIM;
1461269Sericheng 		goto failed;
1462269Sericheng 	}
1463269Sericheng 
1464269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1465269Sericheng 		dl_err = DL_BADADDR;
1466269Sericheng 		goto failed;
1467269Sericheng 	}
1468269Sericheng 
1469269Sericheng 	addr = mp->b_rptr + off;
1470269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1471269Sericheng 
1472269Sericheng 	/*
1473269Sericheng 	 * Check the length of the packet and the block types.
1474269Sericheng 	 */
1475269Sericheng 	size = 0;
14762311Sseb 	payload = mp->b_cont;
14772311Sseb 	for (bp = payload; bp != NULL; bp = bp->b_cont) {
1478269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1479269Sericheng 			goto baddata;
1480269Sericheng 
1481269Sericheng 		size += MBLKL(bp);
1482269Sericheng 	}
1483269Sericheng 
1484269Sericheng 	if (size > dsp->ds_mip->mi_sdu_max)
1485269Sericheng 		goto baddata;
1486269Sericheng 
1487269Sericheng 	/*
1488269Sericheng 	 * Build a packet header.
1489269Sericheng 	 */
14902760Sdg199075 	if ((bp = dls_header(dsp->ds_dc, addr, sap, dlp->dl_priority.dl_max,
14912760Sdg199075 	    &payload)) == NULL) {
1492269Sericheng 		dl_err = DL_BADADDR;
1493269Sericheng 		goto failed;
1494269Sericheng 	}
1495269Sericheng 
1496269Sericheng 	/*
1497269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1498269Sericheng 	 */
1499269Sericheng 	freeb(mp);
1500269Sericheng 
1501269Sericheng 	/*
1502269Sericheng 	 * Transfer the checksum offload information if it is present.
1503269Sericheng 	 */
15042311Sseb 	hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value,
1505269Sericheng 	    &flags);
15062311Sseb 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0);
1507269Sericheng 
1508269Sericheng 	/*
1509269Sericheng 	 * Link the payload onto the new header.
1510269Sericheng 	 */
1511269Sericheng 	ASSERT(bp->b_cont == NULL);
15122311Sseb 	bp->b_cont = payload;
1513269Sericheng 
15142760Sdg199075 	dld_tx_single(dsp, bp);
1515269Sericheng 	rw_exit(&dsp->ds_lock);
1516269Sericheng 	return (B_TRUE);
1517269Sericheng failed:
1518269Sericheng 	rw_exit(&dsp->ds_lock);
1519269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1520269Sericheng 	return (B_FALSE);
1521269Sericheng 
1522269Sericheng baddata:
1523269Sericheng 	rw_exit(&dsp->ds_lock);
1524269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1525269Sericheng 	return (B_FALSE);
1526269Sericheng }
1527269Sericheng 
1528269Sericheng /*
1529269Sericheng  * DL_PASSIVE_REQ
1530269Sericheng  */
1531269Sericheng /* ARGSUSED */
1532269Sericheng static boolean_t
1533269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1534269Sericheng {
1535269Sericheng 	t_uscalar_t dl_err;
1536269Sericheng 
1537269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1538269Sericheng 	/*
1539269Sericheng 	 * If we've already become active by issuing an active primitive,
1540269Sericheng 	 * then it's too late to try to become passive.
1541269Sericheng 	 */
1542269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1543269Sericheng 		dl_err = DL_OUTSTATE;
1544269Sericheng 		goto failed;
1545269Sericheng 	}
1546269Sericheng 
1547269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1548269Sericheng 		dl_err = DL_BADPRIM;
1549269Sericheng 		goto failed;
1550269Sericheng 	}
1551269Sericheng 
1552269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1553269Sericheng 	rw_exit(&dsp->ds_lock);
1554269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1555269Sericheng 	return (B_TRUE);
1556269Sericheng failed:
1557269Sericheng 	rw_exit(&dsp->ds_lock);
1558269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1559269Sericheng 	return (B_FALSE);
1560269Sericheng }
1561269Sericheng 
1562269Sericheng 
1563269Sericheng /*
1564269Sericheng  * Catch-all handler.
1565269Sericheng  */
1566269Sericheng static boolean_t
1567269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp)
1568269Sericheng {
1569269Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1570269Sericheng 	return (B_FALSE);
15710Sstevel@tonic-gate }
15720Sstevel@tonic-gate 
15730Sstevel@tonic-gate static void
15740Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp)
15750Sstevel@tonic-gate {
15760Sstevel@tonic-gate 	mac_handle_t	mh;
15770Sstevel@tonic-gate 
15781353Sericheng 	ASSERT(dsp->ds_pending_req != NULL || RW_WRITE_HELD(&dsp->ds_lock));
1579269Sericheng 
15800Sstevel@tonic-gate 	if (!dsp->ds_polling)
15810Sstevel@tonic-gate 		return;
15820Sstevel@tonic-gate 
15830Sstevel@tonic-gate 	/*
15840Sstevel@tonic-gate 	 * It should be impossible to enable raw mode if polling is turned on.
15850Sstevel@tonic-gate 	 */
15860Sstevel@tonic-gate 	ASSERT(dsp->ds_mode != DLD_RAW);
15870Sstevel@tonic-gate 
15880Sstevel@tonic-gate 	/*
15890Sstevel@tonic-gate 	 * Reset the resource_add callback.
15900Sstevel@tonic-gate 	 */
15910Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
15920Sstevel@tonic-gate 	mac_resource_set(mh, NULL, NULL);
15931184Skrgopi 	mac_resources(mh);
15940Sstevel@tonic-gate 
15950Sstevel@tonic-gate 	/*
15960Sstevel@tonic-gate 	 * Set receive function back to default.
15970Sstevel@tonic-gate 	 */
15980Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ?
15990Sstevel@tonic-gate 	    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
16000Sstevel@tonic-gate 
16010Sstevel@tonic-gate 	/*
16020Sstevel@tonic-gate 	 * Note that polling is disabled.
16030Sstevel@tonic-gate 	 */
16040Sstevel@tonic-gate 	dsp->ds_polling = B_FALSE;
16050Sstevel@tonic-gate }
16060Sstevel@tonic-gate 
16070Sstevel@tonic-gate static boolean_t
16081184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp)
16090Sstevel@tonic-gate {
16100Sstevel@tonic-gate 	mac_handle_t	mh;
16110Sstevel@tonic-gate 
1612269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16130Sstevel@tonic-gate 	ASSERT(!dsp->ds_polling);
16140Sstevel@tonic-gate 
16150Sstevel@tonic-gate 	/*
16160Sstevel@tonic-gate 	 * We cannot enable polling if raw mode
16170Sstevel@tonic-gate 	 * has been enabled.
16180Sstevel@tonic-gate 	 */
16190Sstevel@tonic-gate 	if (dsp->ds_mode == DLD_RAW)
16200Sstevel@tonic-gate 		return (B_FALSE);
16210Sstevel@tonic-gate 
16220Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
16230Sstevel@tonic-gate 
16240Sstevel@tonic-gate 	/*
16250Sstevel@tonic-gate 	 * Register resources.
16260Sstevel@tonic-gate 	 */
16271184Skrgopi 	mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add,
16281184Skrgopi 	    (void *)pollp->dls_rx_handle);
16290Sstevel@tonic-gate 	mac_resources(mh);
16300Sstevel@tonic-gate 
16310Sstevel@tonic-gate 	/*
16320Sstevel@tonic-gate 	 * Set the receive function.
16330Sstevel@tonic-gate 	 */
16341184Skrgopi 	dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx,
16351184Skrgopi 	    (void *)pollp->dls_rx_handle);
16360Sstevel@tonic-gate 
16370Sstevel@tonic-gate 	/*
16380Sstevel@tonic-gate 	 * Note that polling is enabled. This prevents further DLIOCHDRINFO
16390Sstevel@tonic-gate 	 * ioctls from overwriting the receive function pointer.
16400Sstevel@tonic-gate 	 */
16410Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
16420Sstevel@tonic-gate 	return (B_TRUE);
16430Sstevel@tonic-gate }
16440Sstevel@tonic-gate 
16451184Skrgopi static void
16461184Skrgopi proto_soft_ring_disable(dld_str_t *dsp)
16471184Skrgopi {
16481184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16491184Skrgopi 
16501184Skrgopi 	if (!dsp->ds_soft_ring)
16511184Skrgopi 		return;
16521184Skrgopi 
16531184Skrgopi 	/*
16541184Skrgopi 	 * It should be impossible to enable raw mode if soft_ring is turned on.
16551184Skrgopi 	 */
16561184Skrgopi 	ASSERT(dsp->ds_mode != DLD_RAW);
16571184Skrgopi 	proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE);
16581184Skrgopi 	/*
16591184Skrgopi 	 * Note that fanout is disabled.
16601184Skrgopi 	 */
16611184Skrgopi 	dsp->ds_soft_ring = B_FALSE;
16621184Skrgopi }
16631184Skrgopi 
16641184Skrgopi static boolean_t
16651184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp)
16661184Skrgopi {
16671184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16681184Skrgopi 	ASSERT(!dsp->ds_soft_ring);
16691184Skrgopi 
16701184Skrgopi 	/*
16711184Skrgopi 	 * We cannot enable soft_ring if raw mode
16721184Skrgopi 	 * has been enabled.
16731184Skrgopi 	 */
16741184Skrgopi 	if (dsp->ds_mode == DLD_RAW)
16751184Skrgopi 		return (B_FALSE);
16761184Skrgopi 
16771184Skrgopi 	if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE)
16781184Skrgopi 		return (B_FALSE);
16791184Skrgopi 
16801184Skrgopi 	dsp->ds_soft_ring = B_TRUE;
16811184Skrgopi 	return (B_TRUE);
16821184Skrgopi }
16831184Skrgopi 
16841184Skrgopi static void
16851184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type)
16861184Skrgopi {
16871184Skrgopi 	dls_rx_t	rx;
16881184Skrgopi 
16891184Skrgopi 	if (type == SOFT_RING_NONE) {
16901184Skrgopi 		rx = (dsp->ds_mode == DLD_FASTPATH) ?
16911184Skrgopi 			    dld_str_rx_fastpath : dld_str_rx_unitdata;
16921184Skrgopi 	} else {
16931184Skrgopi 		rx = (dls_rx_t)dls_ether_soft_ring_fanout;
16941184Skrgopi 	}
16951184Skrgopi 	dls_soft_ring_rx_set(dsp->ds_dc, rx, dsp, type);
16961184Skrgopi }
16971184Skrgopi 
16980Sstevel@tonic-gate /*
16990Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
17000Sstevel@tonic-gate  */
1701269Sericheng static boolean_t
1702269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
17030Sstevel@tonic-gate {
17040Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
17050Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
17060Sstevel@tonic-gate 	size_t			subsize;
17071184Skrgopi 	dl_capab_dls_t		poll;
1708*3115Syl150051 	dl_capab_dls_t		soft_ring;
17090Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
1710*3115Syl150051 	dl_capab_lso_t		lso;
17110Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
17120Sstevel@tonic-gate 	uint8_t			*ptr;
17132311Sseb 	boolean_t		cksum_cap;
17140Sstevel@tonic-gate 	boolean_t		poll_cap;
1715*3115Syl150051 	boolean_t		lso_cap;
1716*3115Syl150051 	mac_capab_lso_t		mac_lso;
1717269Sericheng 	queue_t			*q = dsp->ds_wq;
1718269Sericheng 	mblk_t			*mp1;
1719269Sericheng 
1720269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
17210Sstevel@tonic-gate 
17220Sstevel@tonic-gate 	/*
17230Sstevel@tonic-gate 	 * Initially assume no capabilities.
17240Sstevel@tonic-gate 	 */
17250Sstevel@tonic-gate 	subsize = 0;
17260Sstevel@tonic-gate 
17271555Skrgopi 	/*
17281555Skrgopi 	 * Advertize soft ring capability if
17291555Skrgopi 	 * VLAN_ID_NONE for GLDv3 drivers
17301555Skrgopi 	 */
17311555Skrgopi 	if (dsp->ds_vid == VLAN_ID_NONE)
17321555Skrgopi 		subsize += sizeof (dl_capability_sub_t) +
17331555Skrgopi 				    sizeof (dl_capab_dls_t);
17341184Skrgopi 
17350Sstevel@tonic-gate 	/*
17360Sstevel@tonic-gate 	 * Check if polling can be enabled on this interface.
17370Sstevel@tonic-gate 	 * If advertising DL_CAPAB_POLL has not been explicitly disabled
17380Sstevel@tonic-gate 	 * then reserve space for that capability.
17390Sstevel@tonic-gate 	 */
17402311Sseb 	poll_cap = (mac_capab_get(dsp->ds_mh, MAC_CAPAB_POLL, NULL) &&
17410Sstevel@tonic-gate 	    !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE));
17420Sstevel@tonic-gate 	if (poll_cap) {
17430Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17441184Skrgopi 		    sizeof (dl_capab_dls_t);
17450Sstevel@tonic-gate 	}
17460Sstevel@tonic-gate 
17470Sstevel@tonic-gate 	/*
17480Sstevel@tonic-gate 	 * If the MAC interface supports checksum offload then reserve
17490Sstevel@tonic-gate 	 * space for the DL_CAPAB_HCKSUM capability.
17500Sstevel@tonic-gate 	 */
17512311Sseb 	if (cksum_cap = mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
17522311Sseb 	    &hcksum.hcksum_txflags)) {
17530Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17540Sstevel@tonic-gate 		    sizeof (dl_capab_hcksum_t);
17550Sstevel@tonic-gate 	}
17560Sstevel@tonic-gate 
17570Sstevel@tonic-gate 	/*
1758*3115Syl150051 	 * If LSO is usable for MAC, reserve space for the DL_CAPAB_LSO
1759*3115Syl150051 	 * capability.
1760*3115Syl150051 	 */
1761*3115Syl150051 	if (lso_cap = mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) {
1762*3115Syl150051 		subsize += sizeof (dl_capability_sub_t) +
1763*3115Syl150051 		    sizeof (dl_capab_lso_t);
1764*3115Syl150051 	}
1765*3115Syl150051 
1766*3115Syl150051 	/*
17670Sstevel@tonic-gate 	 * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then
17680Sstevel@tonic-gate 	 * reserve space for it.
17690Sstevel@tonic-gate 	 */
17700Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
17710Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17720Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
17730Sstevel@tonic-gate 	}
17740Sstevel@tonic-gate 
17750Sstevel@tonic-gate 	/*
1776269Sericheng 	 * If there are no capabilities to advertise or if we
1777269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
17780Sstevel@tonic-gate 	 */
17791184Skrgopi 	if ((mp1 = reallocb(mp,
1780269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1781269Sericheng 		rw_exit(&dsp->ds_lock);
1782269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1783269Sericheng 		return (B_FALSE);
17840Sstevel@tonic-gate 	}
17850Sstevel@tonic-gate 
1786269Sericheng 	mp = mp1;
1787269Sericheng 	DB_TYPE(mp) = M_PROTO;
1788269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1789269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
17900Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
17910Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
17920Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
17930Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
17940Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
17950Sstevel@tonic-gate 
17960Sstevel@tonic-gate 	/*
17970Sstevel@tonic-gate 	 * IP polling interface.
17980Sstevel@tonic-gate 	 */
17990Sstevel@tonic-gate 	if (poll_cap) {
18000Sstevel@tonic-gate 		/*
1801269Sericheng 		 * Attempt to disable just in case this is a re-negotiation;
1802269Sericheng 		 * we need to become writer before doing so.
18030Sstevel@tonic-gate 		 */
1804269Sericheng 		if (!rw_tryupgrade(&dsp->ds_lock)) {
1805269Sericheng 			rw_exit(&dsp->ds_lock);
1806269Sericheng 			rw_enter(&dsp->ds_lock, RW_WRITER);
1807269Sericheng 		}
18080Sstevel@tonic-gate 
1809269Sericheng 		/*
1810269Sericheng 		 * Check if polling state has changed after we re-acquired
1811269Sericheng 		 * the lock above, so that we don't mis-advertise it.
1812269Sericheng 		 */
18132311Sseb 		poll_cap = !(dld_opt & DLD_OPT_NO_POLL) &&
18142311Sseb 		    (dsp->ds_vid == VLAN_ID_NONE);
18150Sstevel@tonic-gate 
1816269Sericheng 		if (!poll_cap) {
1817269Sericheng 			int poll_capab_size;
1818269Sericheng 
1819269Sericheng 			rw_downgrade(&dsp->ds_lock);
1820269Sericheng 
1821269Sericheng 			poll_capab_size = sizeof (dl_capability_sub_t) +
18221184Skrgopi 			    sizeof (dl_capab_dls_t);
18230Sstevel@tonic-gate 
1824269Sericheng 			mp->b_wptr -= poll_capab_size;
1825269Sericheng 			subsize -= poll_capab_size;
1826269Sericheng 			dlap->dl_sub_length = subsize;
1827269Sericheng 		} else {
1828269Sericheng 			proto_poll_disable(dsp);
1829269Sericheng 
1830269Sericheng 			rw_downgrade(&dsp->ds_lock);
1831269Sericheng 
1832269Sericheng 			dlsp = (dl_capability_sub_t *)ptr;
1833269Sericheng 
1834269Sericheng 			dlsp->dl_cap = DL_CAPAB_POLL;
18351184Skrgopi 			dlsp->dl_length = sizeof (dl_capab_dls_t);
1836269Sericheng 			ptr += sizeof (dl_capability_sub_t);
18370Sstevel@tonic-gate 
18381184Skrgopi 			bzero(&poll, sizeof (dl_capab_dls_t));
18391184Skrgopi 			poll.dls_version = POLL_VERSION_1;
18401184Skrgopi 			poll.dls_flags = POLL_CAPABLE;
18411184Skrgopi 			poll.dls_tx_handle = (uintptr_t)dsp;
18421184Skrgopi 			poll.dls_tx = (uintptr_t)str_mdata_fastpath_put;
1843269Sericheng 
18441184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
18451184Skrgopi 			bcopy(&poll, ptr, sizeof (dl_capab_dls_t));
18461184Skrgopi 			ptr += sizeof (dl_capab_dls_t);
1847269Sericheng 		}
18480Sstevel@tonic-gate 	}
18490Sstevel@tonic-gate 
1850269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
1851269Sericheng 
18521555Skrgopi 	if (dsp->ds_vid == VLAN_ID_NONE) {
18531555Skrgopi 		dlsp = (dl_capability_sub_t *)ptr;
18541184Skrgopi 
18551555Skrgopi 		dlsp->dl_cap = DL_CAPAB_SOFT_RING;
18561555Skrgopi 		dlsp->dl_length = sizeof (dl_capab_dls_t);
18571555Skrgopi 		ptr += sizeof (dl_capability_sub_t);
18581184Skrgopi 
18591555Skrgopi 		bzero(&soft_ring, sizeof (dl_capab_dls_t));
18601555Skrgopi 		soft_ring.dls_version = SOFT_RING_VERSION_1;
18611555Skrgopi 		soft_ring.dls_flags = SOFT_RING_CAPABLE;
18621555Skrgopi 		soft_ring.dls_tx_handle = (uintptr_t)dsp;
18631555Skrgopi 		soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put;
18641555Skrgopi 		soft_ring.dls_ring_change_status =
18651555Skrgopi 		    (uintptr_t)proto_change_soft_ring_fanout;
18661555Skrgopi 		soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind;
18671555Skrgopi 		soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind;
18681184Skrgopi 
18691555Skrgopi 		dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
18701555Skrgopi 		bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t));
18711555Skrgopi 		ptr += sizeof (dl_capab_dls_t);
18721555Skrgopi 	}
18731184Skrgopi 
18740Sstevel@tonic-gate 	/*
18750Sstevel@tonic-gate 	 * TCP/IP checksum offload.
18760Sstevel@tonic-gate 	 */
18772311Sseb 	if (cksum_cap) {
18780Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18790Sstevel@tonic-gate 
18800Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
18810Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
18820Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18830Sstevel@tonic-gate 
18840Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
18850Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
18860Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
18870Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
18880Sstevel@tonic-gate 	}
18890Sstevel@tonic-gate 
18900Sstevel@tonic-gate 	/*
1891*3115Syl150051 	 * Large segment offload. (LSO)
1892*3115Syl150051 	 */
1893*3115Syl150051 	if (lso_cap) {
1894*3115Syl150051 		dlsp = (dl_capability_sub_t *)ptr;
1895*3115Syl150051 
1896*3115Syl150051 		dlsp->dl_cap = DL_CAPAB_LSO;
1897*3115Syl150051 		dlsp->dl_length = sizeof (dl_capab_lso_t);
1898*3115Syl150051 		ptr += sizeof (dl_capability_sub_t);
1899*3115Syl150051 
1900*3115Syl150051 		lso.lso_version = LSO_VERSION_1;
1901*3115Syl150051 		lso.lso_flags = mac_lso.lso_flags;
1902*3115Syl150051 		lso.lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max;
1903*3115Syl150051 
1904*3115Syl150051 		/* Simply enable LSO with DLD */
1905*3115Syl150051 		dsp->ds_lso = B_TRUE;
1906*3115Syl150051 		dsp->ds_lso_max = lso.lso_max;
1907*3115Syl150051 
1908*3115Syl150051 		dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq);
1909*3115Syl150051 		bcopy(&lso, ptr, sizeof (dl_capab_lso_t));
1910*3115Syl150051 		ptr += sizeof (dl_capab_lso_t);
1911*3115Syl150051 	} else {
1912*3115Syl150051 		dsp->ds_lso = B_FALSE;
1913*3115Syl150051 		dsp->ds_lso_max = 0;
1914*3115Syl150051 	}
1915*3115Syl150051 
1916*3115Syl150051 	/*
19170Sstevel@tonic-gate 	 * Zero copy
19180Sstevel@tonic-gate 	 */
19190Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
19200Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
19210Sstevel@tonic-gate 
19220Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
19230Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
19240Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
19250Sstevel@tonic-gate 
19260Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
19270Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
19280Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
19290Sstevel@tonic-gate 
19300Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
19310Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
19320Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
19330Sstevel@tonic-gate 	}
19340Sstevel@tonic-gate 
19350Sstevel@tonic-gate 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1936269Sericheng 
1937269Sericheng 	rw_exit(&dsp->ds_lock);
1938269Sericheng 	qreply(q, mp);
1939269Sericheng 	return (B_TRUE);
19400Sstevel@tonic-gate }
1941