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;
179*2311Sseb 	uint8_t			dl_addr[MAXMACADDRLEN + sizeof (uint16_t)];
180*2311Sseb 	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 	 */
297*2311Sseb 	if (minfop->mi_brdcst_addr != NULL) {
298*2311Sseb 		dlp->dl_brdcst_addr_offset =
299*2311Sseb 		    (uintptr_t)brdcst_addr - (uintptr_t)dlp;
300*2311Sseb 		bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
301*2311Sseb 		dlp->dl_brdcst_addr_length = addr_length;
302*2311Sseb 	}
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate 	/*
3050Sstevel@tonic-gate 	 * We only support QoS information for VLAN interfaces.
3060Sstevel@tonic-gate 	 */
3070Sstevel@tonic-gate 	if (dsp->ds_vid != VLAN_ID_NONE) {
3080Sstevel@tonic-gate 		dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
3090Sstevel@tonic-gate 		dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 		rangep->dl_qos_type = DL_QOS_CL_RANGE1;
3120Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
3130Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
3140Sstevel@tonic-gate 		rangep->dl_protection.dl_min = DL_UNKNOWN;
3150Sstevel@tonic-gate 		rangep->dl_protection.dl_max = DL_UNKNOWN;
3160Sstevel@tonic-gate 		rangep->dl_residual_error = DL_UNKNOWN;
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 		/*
3190Sstevel@tonic-gate 		 * Specify the supported range of priorities.
3200Sstevel@tonic-gate 		 */
3210Sstevel@tonic-gate 		rangep->dl_priority.dl_min = 0;
3220Sstevel@tonic-gate 		rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 		dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
3250Sstevel@tonic-gate 		dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate 		selp->dl_qos_type = DL_QOS_CL_SEL1;
3280Sstevel@tonic-gate 		selp->dl_trans_delay = DL_UNKNOWN;
3290Sstevel@tonic-gate 		selp->dl_protection = DL_UNKNOWN;
3300Sstevel@tonic-gate 		selp->dl_residual_error = DL_UNKNOWN;
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 		/*
3330Sstevel@tonic-gate 		 * Specify the current priority (which can be changed by
3340Sstevel@tonic-gate 		 * the DL_UDQOS_REQ primitive).
3350Sstevel@tonic-gate 		 */
3360Sstevel@tonic-gate 		selp->dl_priority = dsp->ds_pri;
3370Sstevel@tonic-gate 	} else {
3380Sstevel@tonic-gate 		/*
3390Sstevel@tonic-gate 		 * Shorten the buffer to lose the unused QoS information
340269Sericheng 		 * structures.
3410Sstevel@tonic-gate 		 */
3420Sstevel@tonic-gate 		mp->b_wptr = (uint8_t *)rangep;
3430Sstevel@tonic-gate 	}
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
3460Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
3470Sstevel@tonic-gate 		/*
3480Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
3490Sstevel@tonic-gate 		 * DLSAP address.
3500Sstevel@tonic-gate 		 */
3510Sstevel@tonic-gate 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
352*2311Sseb 		if (addr_length > 0)
353*2311Sseb 			bcopy(dsp->ds_curr_addr, addr, addr_length);
3540Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3550Sstevel@tonic-gate 	}
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate done:
3580Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0));
3590Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_range_offset != 0,
3600Sstevel@tonic-gate 	    dlp->dl_qos_range_length != 0));
3610Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0));
3620Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0,
3630Sstevel@tonic-gate 	    dlp->dl_brdcst_addr_length != 0));
3640Sstevel@tonic-gate 
365269Sericheng 	rw_exit(&dsp->ds_lock);
366269Sericheng 
367269Sericheng 	qreply(q, mp);
368269Sericheng 	return (B_TRUE);
369269Sericheng }
370269Sericheng 
371269Sericheng /*
372269Sericheng  * DL_ATTACH_REQ
373269Sericheng  */
374269Sericheng static boolean_t
375269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
376269Sericheng {
377269Sericheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)udlp;
378269Sericheng 	int		err = 0;
379269Sericheng 	t_uscalar_t	dl_err;
380269Sericheng 	queue_t		*q = dsp->ds_wq;
381269Sericheng 
382269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
383269Sericheng 
384269Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
385269Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
386269Sericheng 		dl_err = DL_BADPRIM;
387269Sericheng 		goto failed;
388269Sericheng 	}
389269Sericheng 
390269Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
391269Sericheng 		dl_err = DL_OUTSTATE;
392269Sericheng 		goto failed;
393269Sericheng 	}
394269Sericheng 
395269Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
396269Sericheng 
397269Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
398269Sericheng 	if (err != 0) {
399269Sericheng 		switch (err) {
400269Sericheng 		case ENOENT:
401269Sericheng 			dl_err = DL_BADPPA;
402269Sericheng 			err = 0;
403269Sericheng 			break;
404269Sericheng 		default:
405269Sericheng 			dl_err = DL_SYSERR;
406269Sericheng 			break;
407269Sericheng 		}
408269Sericheng 		dsp->ds_dlstate = DL_UNATTACHED;
409269Sericheng 		goto failed;
410269Sericheng 	}
411269Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
412269Sericheng 	rw_exit(&dsp->ds_lock);
413269Sericheng 
414269Sericheng 	dlokack(q, mp, DL_ATTACH_REQ);
415269Sericheng 	return (B_TRUE);
416269Sericheng failed:
417269Sericheng 	rw_exit(&dsp->ds_lock);
418269Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
419269Sericheng 	return (B_FALSE);
4200Sstevel@tonic-gate }
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate /*
423269Sericheng  * DL_DETACH_REQ
4240Sstevel@tonic-gate  */
4251353Sericheng static void
4261353Sericheng proto_process_detach_req(void *arg)
4271353Sericheng {
4281353Sericheng 	dld_str_t	*dsp = arg;
4291353Sericheng 	mblk_t		*mp;
4301353Sericheng 
4311353Sericheng 	/*
4321353Sericheng 	 * We don't need to hold locks because no other thread
4331353Sericheng 	 * would manipulate dsp while it is in a PENDING state.
4341353Sericheng 	 */
4351353Sericheng 	ASSERT(dsp->ds_pending_req != NULL);
4361353Sericheng 	ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING);
4371353Sericheng 
4381353Sericheng 	mp = dsp->ds_pending_req;
4391353Sericheng 	dsp->ds_pending_req = NULL;
4401353Sericheng 	dld_str_detach(dsp);
4411353Sericheng 	dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
4421353Sericheng 
4431353Sericheng 	DLD_WAKEUP(dsp);
4441353Sericheng }
4451353Sericheng 
446269Sericheng /*ARGSUSED*/
447269Sericheng static boolean_t
448269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4490Sstevel@tonic-gate {
450269Sericheng 	queue_t		*q = dsp->ds_wq;
451269Sericheng 	t_uscalar_t	dl_err;
4520Sstevel@tonic-gate 
453269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
4540Sstevel@tonic-gate 
455269Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
456269Sericheng 		dl_err = DL_BADPRIM;
457269Sericheng 		goto failed;
458269Sericheng 	}
4590Sstevel@tonic-gate 
460269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
461269Sericheng 		dl_err = DL_OUTSTATE;
462269Sericheng 		goto failed;
4630Sstevel@tonic-gate 	}
4640Sstevel@tonic-gate 
465269Sericheng 	if (dsp->ds_style == DL_STYLE1) {
466269Sericheng 		dl_err = DL_BADPRIM;
467269Sericheng 		goto failed;
468269Sericheng 	}
469269Sericheng 
470269Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
471269Sericheng 
472269Sericheng 	/*
473269Sericheng 	 * Complete the detach when the driver is single-threaded.
474269Sericheng 	 */
475269Sericheng 	mutex_enter(&dsp->ds_thr_lock);
4761353Sericheng 	ASSERT(dsp->ds_pending_req == NULL);
4771353Sericheng 	dsp->ds_pending_req = mp;
4781353Sericheng 	dsp->ds_pending_op = proto_process_detach_req;
4791353Sericheng 	dsp->ds_pending_cnt++;
480269Sericheng 	mutex_exit(&dsp->ds_thr_lock);
481269Sericheng 	rw_exit(&dsp->ds_lock);
482269Sericheng 
483269Sericheng 	return (B_TRUE);
484269Sericheng failed:
485269Sericheng 	rw_exit(&dsp->ds_lock);
486269Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
487269Sericheng 	return (B_FALSE);
4880Sstevel@tonic-gate }
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate /*
491269Sericheng  * DL_BIND_REQ
4920Sstevel@tonic-gate  */
493269Sericheng static boolean_t
494269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4950Sstevel@tonic-gate {
496269Sericheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)udlp;
497269Sericheng 	int		err = 0;
498*2311Sseb 	uint8_t		addr[MAXMACADDRLEN];
499269Sericheng 	uint_t		addr_length;
500269Sericheng 	t_uscalar_t	dl_err;
501269Sericheng 	t_scalar_t	sap;
502269Sericheng 	queue_t		*q = dsp->ds_wq;
503269Sericheng 
5041521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
5051521Syz147064 
506269Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
507269Sericheng 		dl_err = DL_BADPRIM;
508269Sericheng 		goto failed;
509269Sericheng 	}
510269Sericheng 
511269Sericheng 	if (dlp->dl_xidtest_flg != 0) {
512269Sericheng 		dl_err = DL_NOAUTO;
513269Sericheng 		goto failed;
514269Sericheng 	}
515269Sericheng 
516269Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
517269Sericheng 		dl_err = DL_UNSUPPORTED;
518269Sericheng 		goto failed;
519269Sericheng 	}
520269Sericheng 
521269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
522269Sericheng 		dl_err = DL_OUTSTATE;
523269Sericheng 		goto failed;
524269Sericheng 	}
5250Sstevel@tonic-gate 
526269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
527269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
528269Sericheng 		dl_err = DL_SYSERR;
529269Sericheng 		err = EBUSY;
530269Sericheng 		goto failed;
531269Sericheng 	}
532269Sericheng 
533269Sericheng 	dsp->ds_dlstate = DL_BIND_PENDING;
534269Sericheng 	/*
535269Sericheng 	 * Set the receive callback.
536269Sericheng 	 */
537269Sericheng 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ?
538269Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
5390Sstevel@tonic-gate 
540269Sericheng 	/*
541269Sericheng 	 * Bind the channel such that it can receive packets.
542269Sericheng 	 */
543269Sericheng 	sap = dsp->ds_sap = dlp->dl_sap;
544269Sericheng 	err = dls_bind(dsp->ds_dc, dlp->dl_sap);
545269Sericheng 	if (err != 0) {
546269Sericheng 		switch (err) {
547269Sericheng 		case EINVAL:
548269Sericheng 			dl_err = DL_BADADDR;
549269Sericheng 			err = 0;
550269Sericheng 			break;
551269Sericheng 		default:
552269Sericheng 			dl_err = DL_SYSERR;
553269Sericheng 			break;
554269Sericheng 		}
555269Sericheng 		dsp->ds_dlstate = DL_UNBOUND;
556269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
557269Sericheng 			dls_active_clear(dsp->ds_dc);
558269Sericheng 
5590Sstevel@tonic-gate 		goto failed;
560269Sericheng 	}
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	/*
5630Sstevel@tonic-gate 	 * Copy in MAC address.
5640Sstevel@tonic-gate 	 */
5650Sstevel@tonic-gate 	addr_length = dsp->ds_mip->mi_addr_length;
5660Sstevel@tonic-gate 	bcopy(dsp->ds_curr_addr, addr, addr_length);
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 	/*
5690Sstevel@tonic-gate 	 * Copy in the DLSAP.
5700Sstevel@tonic-gate 	 */
5710Sstevel@tonic-gate 	*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
5720Sstevel@tonic-gate 	addr_length += sizeof (uint16_t);
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
575269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
576269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
5770Sstevel@tonic-gate 
578269Sericheng 	rw_exit(&dsp->ds_lock);
579269Sericheng 
580269Sericheng 	dlbindack(q, mp, sap, (void *)addr, addr_length, 0, 0);
581269Sericheng 	return (B_TRUE);
5820Sstevel@tonic-gate failed:
583269Sericheng 	rw_exit(&dsp->ds_lock);
584269Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
585269Sericheng 	return (B_FALSE);
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate /*
589269Sericheng  * DL_UNBIND_REQ
5900Sstevel@tonic-gate  */
591269Sericheng /*ARGSUSED*/
5921353Sericheng static void
5931353Sericheng proto_process_unbind_req(void *arg)
5940Sstevel@tonic-gate {
5951353Sericheng 	dld_str_t	*dsp = arg;
5961353Sericheng 	mblk_t		*mp;
597269Sericheng 
5981353Sericheng 	/*
5991353Sericheng 	 * We don't need to hold locks because no other thread
6001353Sericheng 	 * would manipulate dsp while it is in a PENDING state.
6011353Sericheng 	 */
6021353Sericheng 	ASSERT(dsp->ds_pending_req != NULL);
6031353Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBIND_PENDING);
604269Sericheng 
605269Sericheng 	/*
606269Sericheng 	 * Flush any remaining packets scheduled for transmission.
607269Sericheng 	 */
608269Sericheng 	dld_tx_flush(dsp);
609269Sericheng 
610269Sericheng 	/*
611269Sericheng 	 * Unbind the channel to stop packets being received.
612269Sericheng 	 */
613269Sericheng 	dls_unbind(dsp->ds_dc);
614269Sericheng 
615269Sericheng 	/*
616269Sericheng 	 * Disable polling mode, if it is enabled.
617269Sericheng 	 */
618269Sericheng 	proto_poll_disable(dsp);
619269Sericheng 
620269Sericheng 	/*
621269Sericheng 	 * Clear the receive callback.
622269Sericheng 	 */
623269Sericheng 	dls_rx_set(dsp->ds_dc, NULL, NULL);
624269Sericheng 
625269Sericheng 	/*
626269Sericheng 	 * Set the mode back to the default (unitdata).
627269Sericheng 	 */
628269Sericheng 	dsp->ds_mode = DLD_UNITDATA;
629269Sericheng 
6301184Skrgopi 	/*
6311184Skrgopi 	 * If soft rings were enabled, the workers
6321353Sericheng 	 * should be quiesced. We cannot check for
6331184Skrgopi 	 * ds_soft_ring flag because
6341184Skrgopi 	 * proto_soft_ring_disable() called from
6351184Skrgopi 	 * proto_capability_req() would have reset it.
6361184Skrgopi 	 */
6371353Sericheng 	if (dls_soft_ring_workers(dsp->ds_dc))
6381353Sericheng 		dls_soft_ring_disable(dsp->ds_dc);
6391353Sericheng 
6401353Sericheng 	mp = dsp->ds_pending_req;
6411353Sericheng 	dsp->ds_pending_req = NULL;
6421353Sericheng 	dsp->ds_dlstate = DL_UNBOUND;
6431353Sericheng 	dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ);
6441353Sericheng 
6451353Sericheng 	DLD_WAKEUP(dsp);
6461353Sericheng }
6471353Sericheng 
6481353Sericheng /*ARGSUSED*/
6491353Sericheng static boolean_t
6501353Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6511353Sericheng {
6521353Sericheng 	queue_t		*q = dsp->ds_wq;
6531353Sericheng 	t_uscalar_t	dl_err;
6541353Sericheng 
6551353Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
6561353Sericheng 
6571353Sericheng 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
6581353Sericheng 		dl_err = DL_BADPRIM;
6591353Sericheng 		goto failed;
6601184Skrgopi 	}
6611184Skrgopi 
6621353Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
6631353Sericheng 		dl_err = DL_OUTSTATE;
6641353Sericheng 		goto failed;
6651353Sericheng 	}
6661353Sericheng 
6671353Sericheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
6681353Sericheng 
6691353Sericheng 	mutex_enter(&dsp->ds_thr_lock);
6701353Sericheng 	ASSERT(dsp->ds_pending_req == NULL);
6711353Sericheng 	dsp->ds_pending_req = mp;
6721353Sericheng 	dsp->ds_pending_op = proto_process_unbind_req;
6731353Sericheng 	dsp->ds_pending_cnt++;
6741353Sericheng 	mutex_exit(&dsp->ds_thr_lock);
675269Sericheng 	rw_exit(&dsp->ds_lock);
676269Sericheng 
677269Sericheng 	return (B_TRUE);
678269Sericheng failed:
679269Sericheng 	rw_exit(&dsp->ds_lock);
680269Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
681269Sericheng 	return (B_FALSE);
6820Sstevel@tonic-gate }
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate /*
685269Sericheng  * DL_PROMISCON_REQ
6860Sstevel@tonic-gate  */
687269Sericheng static boolean_t
688269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6890Sstevel@tonic-gate {
690269Sericheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp;
691269Sericheng 	int		err = 0;
692269Sericheng 	t_uscalar_t	dl_err;
693269Sericheng 	uint32_t	promisc_saved;
694269Sericheng 	queue_t		*q = dsp->ds_wq;
695269Sericheng 
6961521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
6971521Syz147064 
698269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
699269Sericheng 		dl_err = DL_BADPRIM;
700269Sericheng 		goto failed;
701269Sericheng 	}
702269Sericheng 
703269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
704269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
705269Sericheng 		dl_err = DL_OUTSTATE;
7060Sstevel@tonic-gate 		goto failed;
707269Sericheng 	}
7080Sstevel@tonic-gate 
709269Sericheng 	promisc_saved = dsp->ds_promisc;
710269Sericheng 	switch (dlp->dl_level) {
711269Sericheng 	case DL_PROMISC_SAP:
712269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_SAP;
713269Sericheng 		break;
714269Sericheng 
715269Sericheng 	case DL_PROMISC_MULTI:
716269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_MULTI;
717269Sericheng 		break;
718269Sericheng 
719269Sericheng 	case DL_PROMISC_PHYS:
720269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_PHYS;
721269Sericheng 		break;
722269Sericheng 
723269Sericheng 	default:
724269Sericheng 		dl_err = DL_NOTSUPPORTED;
725269Sericheng 		goto failed;
726269Sericheng 	}
7270Sstevel@tonic-gate 
728269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
729269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
730269Sericheng 		dsp->ds_promisc = promisc_saved;
731269Sericheng 		dl_err = DL_SYSERR;
732269Sericheng 		err = EBUSY;
733269Sericheng 		goto failed;
734269Sericheng 	}
735269Sericheng 
736269Sericheng 	/*
737269Sericheng 	 * Adjust channel promiscuity.
738269Sericheng 	 */
739269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
740269Sericheng 	if (err != 0) {
741269Sericheng 		dl_err = DL_SYSERR;
742269Sericheng 		dsp->ds_promisc = promisc_saved;
743269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
744269Sericheng 			dls_active_clear(dsp->ds_dc);
745269Sericheng 
746269Sericheng 		goto failed;
747269Sericheng 	}
748269Sericheng 
749269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
750269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
751269Sericheng 
752269Sericheng 	rw_exit(&dsp->ds_lock);
753269Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
754269Sericheng 	return (B_TRUE);
7550Sstevel@tonic-gate failed:
756269Sericheng 	rw_exit(&dsp->ds_lock);
757269Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
758269Sericheng 	return (B_FALSE);
7590Sstevel@tonic-gate }
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate /*
762269Sericheng  * DL_PROMISCOFF_REQ
7630Sstevel@tonic-gate  */
764269Sericheng static boolean_t
765269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
7660Sstevel@tonic-gate {
767269Sericheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp;
768269Sericheng 	int		err = 0;
769269Sericheng 	t_uscalar_t	dl_err;
770269Sericheng 	uint32_t	promisc_saved;
771269Sericheng 	queue_t		*q = dsp->ds_wq;
772269Sericheng 
7731521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
774269Sericheng 
775269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
776269Sericheng 		dl_err = DL_BADPRIM;
7770Sstevel@tonic-gate 		goto failed;
778269Sericheng 	}
7790Sstevel@tonic-gate 
780269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
781269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
782269Sericheng 		dl_err = DL_OUTSTATE;
7830Sstevel@tonic-gate 		goto failed;
784269Sericheng 	}
7850Sstevel@tonic-gate 
786269Sericheng 	promisc_saved = dsp->ds_promisc;
787269Sericheng 	switch (dlp->dl_level) {
788269Sericheng 	case DL_PROMISC_SAP:
789269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
790269Sericheng 			dl_err = DL_NOTENAB;
791269Sericheng 			goto failed;
792269Sericheng 		}
793269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_SAP;
7940Sstevel@tonic-gate 		break;
7950Sstevel@tonic-gate 
796269Sericheng 	case DL_PROMISC_MULTI:
797269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
798269Sericheng 			dl_err = DL_NOTENAB;
799269Sericheng 			goto failed;
800269Sericheng 		}
801269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_MULTI;
802269Sericheng 		break;
803269Sericheng 
804269Sericheng 	case DL_PROMISC_PHYS:
805269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
806269Sericheng 			dl_err = DL_NOTENAB;
807269Sericheng 			goto failed;
808269Sericheng 		}
809269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_PHYS;
8100Sstevel@tonic-gate 		break;
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 	default:
813269Sericheng 		dl_err = DL_NOTSUPPORTED;
814269Sericheng 		goto failed;
815269Sericheng 	}
816269Sericheng 
817269Sericheng 	/*
818269Sericheng 	 * Adjust channel promiscuity.
819269Sericheng 	 */
820269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
821269Sericheng 	if (err != 0) {
822269Sericheng 		dsp->ds_promisc = promisc_saved;
8230Sstevel@tonic-gate 		dl_err = DL_SYSERR;
824269Sericheng 		goto failed;
825269Sericheng 	}
826269Sericheng 
827269Sericheng 	rw_exit(&dsp->ds_lock);
828269Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
829269Sericheng 	return (B_TRUE);
830269Sericheng failed:
831269Sericheng 	rw_exit(&dsp->ds_lock);
832269Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
833269Sericheng 	return (B_FALSE);
834269Sericheng }
835269Sericheng 
836269Sericheng /*
837269Sericheng  * DL_ENABMULTI_REQ
838269Sericheng  */
839269Sericheng static boolean_t
840269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
841269Sericheng {
842269Sericheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp;
843269Sericheng 	int		err = 0;
844269Sericheng 	t_uscalar_t	dl_err;
845269Sericheng 	queue_t		*q = dsp->ds_wq;
846269Sericheng 
847269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
848269Sericheng 
849269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
850269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
851269Sericheng 		dl_err = DL_OUTSTATE;
852269Sericheng 		goto failed;
853269Sericheng 	}
854269Sericheng 
855269Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
856269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
857269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
858269Sericheng 		dl_err = DL_BADPRIM;
859269Sericheng 		goto failed;
860269Sericheng 	}
861269Sericheng 
862269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
863269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
864269Sericheng 		dl_err = DL_SYSERR;
865269Sericheng 		err = EBUSY;
866269Sericheng 		goto failed;
8670Sstevel@tonic-gate 	}
8680Sstevel@tonic-gate 
869269Sericheng 	err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
870269Sericheng 	if (err != 0) {
871269Sericheng 		switch (err) {
872269Sericheng 		case EINVAL:
873269Sericheng 			dl_err = DL_BADADDR;
874269Sericheng 			err = 0;
875269Sericheng 			break;
876269Sericheng 		case ENOSPC:
877269Sericheng 			dl_err = DL_TOOMANY;
878269Sericheng 			err = 0;
879269Sericheng 			break;
880269Sericheng 		default:
881269Sericheng 			dl_err = DL_SYSERR;
882269Sericheng 			break;
883269Sericheng 		}
884269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
885269Sericheng 			dls_active_clear(dsp->ds_dc);
886269Sericheng 
887269Sericheng 		goto failed;
888269Sericheng 	}
889269Sericheng 
890269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
891269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
892269Sericheng 
893269Sericheng 	rw_exit(&dsp->ds_lock);
894269Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
895269Sericheng 	return (B_TRUE);
896269Sericheng failed:
897269Sericheng 	rw_exit(&dsp->ds_lock);
898269Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
899269Sericheng 	return (B_FALSE);
900269Sericheng }
901269Sericheng 
902269Sericheng /*
903269Sericheng  * DL_DISABMULTI_REQ
904269Sericheng  */
905269Sericheng static boolean_t
906269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
907269Sericheng {
908269Sericheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp;
909269Sericheng 	int		err = 0;
910269Sericheng 	t_uscalar_t	dl_err;
911269Sericheng 	queue_t		*q = dsp->ds_wq;
912269Sericheng 
913269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
914269Sericheng 
915269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
916269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
917269Sericheng 		dl_err = DL_OUTSTATE;
918269Sericheng 		goto failed;
919269Sericheng 	}
920269Sericheng 
921269Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
922269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
923269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
924269Sericheng 		dl_err = DL_BADPRIM;
925269Sericheng 		goto failed;
926269Sericheng 	}
927269Sericheng 
928269Sericheng 	err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
929269Sericheng 	if (err != 0) {
930269Sericheng 	switch (err) {
931269Sericheng 		case EINVAL:
932269Sericheng 			dl_err = DL_BADADDR;
933269Sericheng 			err = 0;
934269Sericheng 			break;
935269Sericheng 
936269Sericheng 		case ENOENT:
937269Sericheng 			dl_err = DL_NOTENAB;
938269Sericheng 			err = 0;
939269Sericheng 			break;
940269Sericheng 
941269Sericheng 		default:
942269Sericheng 			dl_err = DL_SYSERR;
943269Sericheng 			break;
944269Sericheng 		}
945269Sericheng 		goto failed;
946269Sericheng 	}
947269Sericheng 
948269Sericheng 	rw_exit(&dsp->ds_lock);
949269Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
950269Sericheng 	return (B_TRUE);
951269Sericheng failed:
952269Sericheng 	rw_exit(&dsp->ds_lock);
953269Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
954269Sericheng 	return (B_FALSE);
9550Sstevel@tonic-gate }
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate /*
958269Sericheng  * DL_PHYS_ADDR_REQ
9590Sstevel@tonic-gate  */
960269Sericheng static boolean_t
961269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
9620Sstevel@tonic-gate {
963269Sericheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp;
964269Sericheng 	queue_t		*q = dsp->ds_wq;
965269Sericheng 	t_uscalar_t	dl_err;
966269Sericheng 	char		*addr;
967269Sericheng 	uint_t		addr_length;
968269Sericheng 
969269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
970269Sericheng 
971269Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
972269Sericheng 		dl_err = DL_BADPRIM;
973269Sericheng 		goto failed;
974269Sericheng 	}
975269Sericheng 
976269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
977269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
978269Sericheng 		dl_err = DL_OUTSTATE;
979269Sericheng 		goto failed;
980269Sericheng 	}
9810Sstevel@tonic-gate 
982269Sericheng 	if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR &&
983269Sericheng 	    dlp->dl_addr_type != DL_FACT_PHYS_ADDR) {
984269Sericheng 		dl_err = DL_UNSUPPORTED;
9850Sstevel@tonic-gate 		goto failed;
986269Sericheng 	}
9870Sstevel@tonic-gate 
988269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
989269Sericheng 	addr = kmem_alloc(addr_length, KM_NOSLEEP);
990269Sericheng 	if (addr == NULL) {
991269Sericheng 		rw_exit(&dsp->ds_lock);
992269Sericheng 		merror(q, mp, ENOSR);
993269Sericheng 		return (B_FALSE);
994269Sericheng 	}
9950Sstevel@tonic-gate 
996269Sericheng 	/*
997269Sericheng 	 * Copy out the address before we drop the lock; we don't
998269Sericheng 	 * want to call dlphysaddrack() while holding ds_lock.
999269Sericheng 	 */
1000269Sericheng 	bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ?
1001269Sericheng 	    dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length);
1002269Sericheng 
1003269Sericheng 	rw_exit(&dsp->ds_lock);
1004269Sericheng 	dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
1005269Sericheng 	kmem_free(addr, addr_length);
1006269Sericheng 	return (B_TRUE);
10070Sstevel@tonic-gate failed:
1008269Sericheng 	rw_exit(&dsp->ds_lock);
1009269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
1010269Sericheng 	return (B_FALSE);
1011269Sericheng }
10120Sstevel@tonic-gate 
1013269Sericheng /*
1014269Sericheng  * DL_SET_PHYS_ADDR_REQ
1015269Sericheng  */
1016269Sericheng static boolean_t
1017269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1018269Sericheng {
1019269Sericheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp;
1020269Sericheng 	int		err = 0;
1021269Sericheng 	t_uscalar_t	dl_err;
1022269Sericheng 	queue_t		*q = dsp->ds_wq;
10230Sstevel@tonic-gate 
1024269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1025269Sericheng 
1026269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1027269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1028269Sericheng 		dl_err = DL_OUTSTATE;
1029269Sericheng 		goto failed;
1030269Sericheng 	}
1031269Sericheng 
1032269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
1033269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
1034269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
1035269Sericheng 		dl_err = DL_BADPRIM;
1036269Sericheng 		goto failed;
10370Sstevel@tonic-gate 	}
10380Sstevel@tonic-gate 
1039269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
1040269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
1041269Sericheng 		dl_err = DL_SYSERR;
1042269Sericheng 		err = EBUSY;
1043269Sericheng 		goto failed;
1044269Sericheng 	}
1045269Sericheng 
1046269Sericheng 	err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset);
1047269Sericheng 	if (err != 0) {
1048269Sericheng 		switch (err) {
1049269Sericheng 		case EINVAL:
1050269Sericheng 			dl_err = DL_BADADDR;
1051269Sericheng 			err = 0;
1052269Sericheng 			break;
1053269Sericheng 
1054269Sericheng 		default:
1055269Sericheng 			dl_err = DL_SYSERR;
1056269Sericheng 			break;
1057269Sericheng 		}
1058269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1059269Sericheng 			dls_active_clear(dsp->ds_dc);
1060269Sericheng 
1061269Sericheng 		goto failed;
1062269Sericheng 	}
1063269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1064269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
1065269Sericheng 
1066269Sericheng 	rw_exit(&dsp->ds_lock);
1067269Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
1068269Sericheng 	return (B_TRUE);
1069269Sericheng failed:
1070269Sericheng 	rw_exit(&dsp->ds_lock);
1071269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
1072269Sericheng 	return (B_FALSE);
1073269Sericheng }
1074269Sericheng 
1075269Sericheng /*
1076269Sericheng  * DL_UDQOS_REQ
1077269Sericheng  */
1078269Sericheng static boolean_t
1079269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1080269Sericheng {
1081269Sericheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp;
1082269Sericheng 	dl_qos_cl_sel1_t *selp;
1083269Sericheng 	int		off, len;
1084269Sericheng 	t_uscalar_t	dl_err;
1085269Sericheng 	queue_t		*q = dsp->ds_wq;
1086269Sericheng 
1087269Sericheng 	off = dlp->dl_qos_offset;
1088269Sericheng 	len = dlp->dl_qos_length;
1089269Sericheng 
10901521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
10911521Syz147064 
1092269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
1093269Sericheng 		dl_err = DL_BADPRIM;
1094269Sericheng 		goto failed;
1095269Sericheng 	}
1096269Sericheng 
1097269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
1098269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
1099269Sericheng 		dl_err = DL_BADQOSTYPE;
1100269Sericheng 		goto failed;
1101269Sericheng 	}
1102269Sericheng 
1103269Sericheng 	if (dsp->ds_vid == VLAN_ID_NONE ||
1104269Sericheng 	    selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1105269Sericheng 	    selp->dl_priority < 0) {
1106269Sericheng 		dl_err = DL_BADQOSPARAM;
1107269Sericheng 		goto failed;
1108269Sericheng 	}
1109269Sericheng 
1110269Sericheng 	dsp->ds_pri = selp->dl_priority;
1111269Sericheng 
1112269Sericheng 	rw_exit(&dsp->ds_lock);
1113269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1114269Sericheng 	return (B_TRUE);
1115269Sericheng failed:
1116269Sericheng 	rw_exit(&dsp->ds_lock);
1117269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1118269Sericheng 	return (B_FALSE);
11190Sstevel@tonic-gate }
11200Sstevel@tonic-gate 
11211184Skrgopi static boolean_t
11221184Skrgopi check_ip_above(queue_t *q)
11231184Skrgopi {
11241184Skrgopi 	queue_t		*next_q;
11251184Skrgopi 	boolean_t	ret = B_TRUE;
11261184Skrgopi 
11271184Skrgopi 	claimstr(q);
11281184Skrgopi 	next_q = q->q_next;
11291184Skrgopi 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
11301184Skrgopi 		ret = B_FALSE;
11311184Skrgopi 	releasestr(q);
11321184Skrgopi 	return (ret);
11331184Skrgopi }
11341184Skrgopi 
11350Sstevel@tonic-gate /*
1136269Sericheng  * DL_CAPABILITY_REQ
11370Sstevel@tonic-gate  */
1138269Sericheng /*ARGSUSED*/
1139269Sericheng static boolean_t
1140269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
11410Sstevel@tonic-gate {
1142269Sericheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)udlp;
1143269Sericheng 	dl_capability_sub_t *sp;
1144269Sericheng 	size_t		size, len;
1145269Sericheng 	offset_t	off, end;
1146269Sericheng 	t_uscalar_t	dl_err;
1147269Sericheng 	queue_t		*q = dsp->ds_wq;
1148269Sericheng 	boolean_t	upgraded;
1149269Sericheng 
1150269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1151269Sericheng 
1152269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1153269Sericheng 		dl_err = DL_BADPRIM;
1154269Sericheng 		goto failed;
1155269Sericheng 	}
1156269Sericheng 
1157269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1158269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1159269Sericheng 		dl_err = DL_OUTSTATE;
1160269Sericheng 		goto failed;
1161269Sericheng 	}
1162269Sericheng 
1163269Sericheng 	/*
1164269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1165269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1166269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1167269Sericheng 	 */
1168269Sericheng 	if (dlp->dl_sub_length == 0) {
1169269Sericheng 		/* callee drops lock */
1170269Sericheng 		return (proto_capability_advertise(dsp, mp));
1171269Sericheng 	}
1172269Sericheng 
1173269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1174269Sericheng 		dl_err = DL_BADPRIM;
1175269Sericheng 		goto failed;
1176269Sericheng 	}
1177269Sericheng 
1178269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1179269Sericheng 
1180269Sericheng 	off = dlp->dl_sub_offset;
1181269Sericheng 	len = dlp->dl_sub_length;
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate 	/*
1184269Sericheng 	 * Walk the list of capabilities to be enabled.
11850Sstevel@tonic-gate 	 */
1186269Sericheng 	upgraded = B_FALSE;
1187269Sericheng 	for (end = off + len; off < end; ) {
1188269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1189269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1190269Sericheng 
1191269Sericheng 		if (off + size > end ||
1192269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1193269Sericheng 			dl_err = DL_BADPRIM;
1194269Sericheng 			goto failed;
1195269Sericheng 		}
1196269Sericheng 
1197269Sericheng 		switch (sp->dl_cap) {
1198269Sericheng 		/*
1199269Sericheng 		 * TCP/IP checksum offload to hardware.
1200269Sericheng 		 */
1201269Sericheng 		case DL_CAPAB_HCKSUM: {
1202269Sericheng 			dl_capab_hcksum_t *hcksump;
1203269Sericheng 			dl_capab_hcksum_t hcksum;
1204269Sericheng 
1205269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1206269Sericheng 			/*
1207269Sericheng 			 * Copy for alignment.
1208269Sericheng 			 */
1209269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1210269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1211269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1212269Sericheng 			break;
1213269Sericheng 		}
1214269Sericheng 
1215269Sericheng 		/*
1216269Sericheng 		 * IP polling interface.
1217269Sericheng 		 */
1218269Sericheng 		case DL_CAPAB_POLL: {
12191184Skrgopi 			dl_capab_dls_t *pollp;
12201184Skrgopi 			dl_capab_dls_t	poll;
1221269Sericheng 
12221184Skrgopi 			pollp = (dl_capab_dls_t *)&sp[1];
1223269Sericheng 			/*
1224269Sericheng 			 * Copy for alignment.
1225269Sericheng 			 */
12261184Skrgopi 			bcopy(pollp, &poll, sizeof (dl_capab_dls_t));
1227269Sericheng 
1228269Sericheng 			/*
1229269Sericheng 			 * We need to become writer before enabling and/or
1230269Sericheng 			 * disabling the polling interface.  If we couldn'
1231269Sericheng 			 * upgrade, check state again after re-acquiring the
1232269Sericheng 			 * lock to make sure we can proceed.
1233269Sericheng 			 */
1234269Sericheng 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
1235269Sericheng 				rw_exit(&dsp->ds_lock);
1236269Sericheng 				rw_enter(&dsp->ds_lock, RW_WRITER);
1237269Sericheng 
1238269Sericheng 				if (dsp->ds_dlstate == DL_UNATTACHED ||
1239269Sericheng 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
1240269Sericheng 					dl_err = DL_OUTSTATE;
1241269Sericheng 					goto failed;
1242269Sericheng 				}
1243269Sericheng 			}
1244269Sericheng 			upgraded = B_TRUE;
1245269Sericheng 
12461184Skrgopi 			switch (poll.dls_flags) {
1247269Sericheng 			default:
1248269Sericheng 				/*FALLTHRU*/
1249269Sericheng 			case POLL_DISABLE:
1250269Sericheng 				proto_poll_disable(dsp);
1251269Sericheng 				break;
1252269Sericheng 
1253269Sericheng 			case POLL_ENABLE:
1254269Sericheng 				ASSERT(!(dld_opt & DLD_OPT_NO_POLL));
1255269Sericheng 
1256269Sericheng 				/*
1257269Sericheng 				 * Make sure polling is disabled.
1258269Sericheng 				 */
1259269Sericheng 				proto_poll_disable(dsp);
1260269Sericheng 
1261269Sericheng 				/*
1262269Sericheng 				 * Now attempt enable it.
1263269Sericheng 				 */
12641184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
12651184Skrgopi 				    proto_poll_enable(dsp, &poll)) {
12661184Skrgopi 					bzero(&poll, sizeof (dl_capab_dls_t));
12671184Skrgopi 					poll.dls_flags = POLL_ENABLE;
12681184Skrgopi 				}
1269269Sericheng 				break;
1270269Sericheng 			}
1271269Sericheng 
12721184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
12731184Skrgopi 			bcopy(&poll, pollp, sizeof (dl_capab_dls_t));
12741184Skrgopi 			break;
12751184Skrgopi 		}
12761184Skrgopi 		case DL_CAPAB_SOFT_RING: {
12771184Skrgopi 			dl_capab_dls_t *soft_ringp;
12781184Skrgopi 			dl_capab_dls_t soft_ring;
12791184Skrgopi 
12801184Skrgopi 			soft_ringp = (dl_capab_dls_t *)&sp[1];
12811184Skrgopi 			/*
12821184Skrgopi 			 * Copy for alignment.
12831184Skrgopi 			 */
12841184Skrgopi 			bcopy(soft_ringp, &soft_ring,
12851184Skrgopi 			    sizeof (dl_capab_dls_t));
12861184Skrgopi 
12871184Skrgopi 			/*
12881184Skrgopi 			 * We need to become writer before enabling and/or
12891184Skrgopi 			 * disabling the soft_ring interface.  If we couldn'
12901184Skrgopi 			 * upgrade, check state again after re-acquiring the
12911184Skrgopi 			 * lock to make sure we can proceed.
12921184Skrgopi 			 */
12931184Skrgopi 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
12941184Skrgopi 				rw_exit(&dsp->ds_lock);
12951184Skrgopi 				rw_enter(&dsp->ds_lock, RW_WRITER);
12961184Skrgopi 
12971184Skrgopi 				if (dsp->ds_dlstate == DL_UNATTACHED ||
12981184Skrgopi 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
12991184Skrgopi 					dl_err = DL_OUTSTATE;
13001184Skrgopi 					goto failed;
13011184Skrgopi 				}
13021184Skrgopi 			}
13031184Skrgopi 			upgraded = B_TRUE;
13041184Skrgopi 
13051184Skrgopi 			switch (soft_ring.dls_flags) {
13061184Skrgopi 			default:
13071184Skrgopi 				/*FALLTHRU*/
13081184Skrgopi 			case SOFT_RING_DISABLE:
13091184Skrgopi 				proto_soft_ring_disable(dsp);
13101184Skrgopi 				break;
13111184Skrgopi 
13121184Skrgopi 			case SOFT_RING_ENABLE:
13131184Skrgopi 				/*
13141184Skrgopi 				 * Make sure soft_ring is disabled.
13151184Skrgopi 				 */
13161184Skrgopi 				proto_soft_ring_disable(dsp);
13171184Skrgopi 
13181184Skrgopi 				/*
13191184Skrgopi 				 * Now attempt enable it.
13201184Skrgopi 				 */
13211184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
13221184Skrgopi 				    proto_soft_ring_enable(dsp, &soft_ring)) {
13231184Skrgopi 					bzero(&soft_ring,
13241184Skrgopi 					    sizeof (dl_capab_dls_t));
13251184Skrgopi 					soft_ring.dls_flags =
13261184Skrgopi 					    SOFT_RING_ENABLE;
13271184Skrgopi 				} else {
13281184Skrgopi 					bzero(&soft_ring,
13291184Skrgopi 					    sizeof (dl_capab_dls_t));
13301184Skrgopi 					soft_ring.dls_flags =
13311184Skrgopi 					    SOFT_RING_DISABLE;
13321184Skrgopi 				}
13331184Skrgopi 				break;
13341184Skrgopi 			}
13351184Skrgopi 
13361184Skrgopi 			dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
13371184Skrgopi 			bcopy(&soft_ring, soft_ringp,
13381184Skrgopi 			    sizeof (dl_capab_dls_t));
1339269Sericheng 			break;
1340269Sericheng 		}
1341269Sericheng 		default:
1342269Sericheng 			break;
1343269Sericheng 		}
1344269Sericheng 
1345269Sericheng 		off += size;
1346269Sericheng 	}
1347269Sericheng 	rw_exit(&dsp->ds_lock);
1348269Sericheng 	qreply(q, mp);
1349269Sericheng 	return (B_TRUE);
1350269Sericheng failed:
1351269Sericheng 	rw_exit(&dsp->ds_lock);
1352269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1353269Sericheng 	return (B_FALSE);
13540Sstevel@tonic-gate }
13550Sstevel@tonic-gate 
13560Sstevel@tonic-gate /*
1357269Sericheng  * DL_NOTIFY_REQ
13580Sstevel@tonic-gate  */
1359269Sericheng static boolean_t
1360269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
13610Sstevel@tonic-gate {
1362269Sericheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)udlp;
1363269Sericheng 	t_uscalar_t	dl_err;
1364269Sericheng 	queue_t		*q = dsp->ds_wq;
1365269Sericheng 	uint_t		note =
1366269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1367269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1368269Sericheng 	    DL_NOTE_PHYS_ADDR |
1369269Sericheng 	    DL_NOTE_LINK_UP |
1370269Sericheng 	    DL_NOTE_LINK_DOWN |
1371*2311Sseb 	    DL_NOTE_CAPAB_RENEG |
1372*2311Sseb 	    DL_NOTE_SPEED;
13730Sstevel@tonic-gate 
13741521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
13751521Syz147064 
1376269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1377269Sericheng 		dl_err = DL_BADPRIM;
1378269Sericheng 		goto failed;
1379269Sericheng 	}
13800Sstevel@tonic-gate 
1381269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1382269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1383269Sericheng 		dl_err = DL_OUTSTATE;
1384269Sericheng 		goto failed;
13850Sstevel@tonic-gate 	}
13860Sstevel@tonic-gate 
1387269Sericheng 	/*
1388269Sericheng 	 * Cache the notifications that are being enabled.
1389269Sericheng 	 */
1390269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1391269Sericheng 	rw_exit(&dsp->ds_lock);
1392269Sericheng 	/*
1393269Sericheng 	 * The ACK carries all notifications regardless of which set is
1394269Sericheng 	 * being enabled.
1395269Sericheng 	 */
1396269Sericheng 	dlnotifyack(q, mp, note);
1397269Sericheng 
1398269Sericheng 	/*
1399269Sericheng 	 * Solicit DL_NOTIFY_IND messages for each enabled notification.
1400269Sericheng 	 */
1401269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1402269Sericheng 	if (dsp->ds_notifications != 0) {
1403269Sericheng 		rw_exit(&dsp->ds_lock);
1404269Sericheng 		dld_str_notify_ind(dsp);
1405269Sericheng 	} else {
1406269Sericheng 		rw_exit(&dsp->ds_lock);
1407269Sericheng 	}
1408269Sericheng 	return (B_TRUE);
1409269Sericheng failed:
1410269Sericheng 	rw_exit(&dsp->ds_lock);
1411269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1412269Sericheng 	return (B_FALSE);
14130Sstevel@tonic-gate }
14140Sstevel@tonic-gate 
14150Sstevel@tonic-gate /*
1416269Sericheng  * DL_UINTDATA_REQ
14170Sstevel@tonic-gate  */
1418269Sericheng static boolean_t
1419269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
14200Sstevel@tonic-gate {
1421269Sericheng 	queue_t			*q = dsp->ds_wq;
1422269Sericheng 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)udlp;
1423269Sericheng 	off_t			off;
1424269Sericheng 	size_t			len, size;
1425269Sericheng 	const uint8_t		*addr;
1426269Sericheng 	uint16_t		sap;
1427269Sericheng 	uint_t			addr_length;
1428*2311Sseb 	mblk_t			*bp, *payload;
1429269Sericheng 	uint32_t		start, stuff, end, value, flags;
1430269Sericheng 	t_uscalar_t		dl_err;
1431269Sericheng 
1432269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1433269Sericheng 
1434269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1435269Sericheng 		dl_err = DL_BADPRIM;
1436269Sericheng 		goto failed;
1437269Sericheng 	}
1438269Sericheng 
1439269Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
1440269Sericheng 		dl_err = DL_OUTSTATE;
1441269Sericheng 		goto failed;
1442269Sericheng 	}
1443269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1444269Sericheng 
1445269Sericheng 	off = dlp->dl_dest_addr_offset;
1446269Sericheng 	len = dlp->dl_dest_addr_length;
1447269Sericheng 
1448269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1449269Sericheng 		dl_err = DL_BADPRIM;
1450269Sericheng 		goto failed;
1451269Sericheng 	}
1452269Sericheng 
1453269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1454269Sericheng 		dl_err = DL_BADADDR;
1455269Sericheng 		goto failed;
1456269Sericheng 	}
1457269Sericheng 
1458269Sericheng 	addr = mp->b_rptr + off;
1459269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1460269Sericheng 
1461269Sericheng 	/*
1462269Sericheng 	 * Check the length of the packet and the block types.
1463269Sericheng 	 */
1464269Sericheng 	size = 0;
1465*2311Sseb 	payload = mp->b_cont;
1466*2311Sseb 	for (bp = payload; bp != NULL; bp = bp->b_cont) {
1467269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1468269Sericheng 			goto baddata;
1469269Sericheng 
1470269Sericheng 		size += MBLKL(bp);
1471269Sericheng 	}
1472269Sericheng 
1473269Sericheng 	if (size > dsp->ds_mip->mi_sdu_max)
1474269Sericheng 		goto baddata;
1475269Sericheng 
1476269Sericheng 	/*
1477269Sericheng 	 * Build a packet header.
1478269Sericheng 	 */
1479*2311Sseb 	bp = dls_header(dsp->ds_dc, addr, sap, dsp->ds_pri, payload);
1480*2311Sseb 	if (bp == NULL) {
1481269Sericheng 		dl_err = DL_BADADDR;
1482269Sericheng 		goto failed;
1483269Sericheng 	}
1484269Sericheng 
1485269Sericheng 	/*
1486269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1487269Sericheng 	 */
1488269Sericheng 	freeb(mp);
1489269Sericheng 
1490269Sericheng 	/*
1491269Sericheng 	 * Transfer the checksum offload information if it is present.
1492269Sericheng 	 */
1493*2311Sseb 	hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value,
1494269Sericheng 	    &flags);
1495*2311Sseb 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0);
1496269Sericheng 
1497269Sericheng 	/*
1498269Sericheng 	 * Link the payload onto the new header.
1499269Sericheng 	 */
1500269Sericheng 	ASSERT(bp->b_cont == NULL);
1501*2311Sseb 	bp->b_cont = payload;
1502269Sericheng 
1503269Sericheng 	str_mdata_fastpath_put(dsp, bp);
1504269Sericheng 	rw_exit(&dsp->ds_lock);
1505269Sericheng 	return (B_TRUE);
1506269Sericheng failed:
1507269Sericheng 	rw_exit(&dsp->ds_lock);
1508269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1509269Sericheng 	return (B_FALSE);
1510269Sericheng 
1511269Sericheng baddata:
1512269Sericheng 	rw_exit(&dsp->ds_lock);
1513269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1514269Sericheng 	return (B_FALSE);
1515269Sericheng }
1516269Sericheng 
1517269Sericheng /*
1518269Sericheng  * DL_PASSIVE_REQ
1519269Sericheng  */
1520269Sericheng /* ARGSUSED */
1521269Sericheng static boolean_t
1522269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1523269Sericheng {
1524269Sericheng 	t_uscalar_t dl_err;
1525269Sericheng 
1526269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1527269Sericheng 	/*
1528269Sericheng 	 * If we've already become active by issuing an active primitive,
1529269Sericheng 	 * then it's too late to try to become passive.
1530269Sericheng 	 */
1531269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1532269Sericheng 		dl_err = DL_OUTSTATE;
1533269Sericheng 		goto failed;
1534269Sericheng 	}
1535269Sericheng 
1536269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1537269Sericheng 		dl_err = DL_BADPRIM;
1538269Sericheng 		goto failed;
1539269Sericheng 	}
1540269Sericheng 
1541269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1542269Sericheng 	rw_exit(&dsp->ds_lock);
1543269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1544269Sericheng 	return (B_TRUE);
1545269Sericheng failed:
1546269Sericheng 	rw_exit(&dsp->ds_lock);
1547269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1548269Sericheng 	return (B_FALSE);
1549269Sericheng }
1550269Sericheng 
1551269Sericheng 
1552269Sericheng /*
1553269Sericheng  * Catch-all handler.
1554269Sericheng  */
1555269Sericheng static boolean_t
1556269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp)
1557269Sericheng {
1558269Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1559269Sericheng 	return (B_FALSE);
15600Sstevel@tonic-gate }
15610Sstevel@tonic-gate 
15620Sstevel@tonic-gate static void
15630Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp)
15640Sstevel@tonic-gate {
15650Sstevel@tonic-gate 	mac_handle_t	mh;
15660Sstevel@tonic-gate 
15671353Sericheng 	ASSERT(dsp->ds_pending_req != NULL || RW_WRITE_HELD(&dsp->ds_lock));
1568269Sericheng 
15690Sstevel@tonic-gate 	if (!dsp->ds_polling)
15700Sstevel@tonic-gate 		return;
15710Sstevel@tonic-gate 
15720Sstevel@tonic-gate 	/*
15730Sstevel@tonic-gate 	 * It should be impossible to enable raw mode if polling is turned on.
15740Sstevel@tonic-gate 	 */
15750Sstevel@tonic-gate 	ASSERT(dsp->ds_mode != DLD_RAW);
15760Sstevel@tonic-gate 
15770Sstevel@tonic-gate 	/*
15780Sstevel@tonic-gate 	 * Reset the resource_add callback.
15790Sstevel@tonic-gate 	 */
15800Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
15810Sstevel@tonic-gate 	mac_resource_set(mh, NULL, NULL);
15821184Skrgopi 	mac_resources(mh);
15830Sstevel@tonic-gate 
15840Sstevel@tonic-gate 	/*
15850Sstevel@tonic-gate 	 * Set receive function back to default.
15860Sstevel@tonic-gate 	 */
15870Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ?
15880Sstevel@tonic-gate 	    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
15890Sstevel@tonic-gate 
15900Sstevel@tonic-gate 	/*
15910Sstevel@tonic-gate 	 * Note that polling is disabled.
15920Sstevel@tonic-gate 	 */
15930Sstevel@tonic-gate 	dsp->ds_polling = B_FALSE;
15940Sstevel@tonic-gate }
15950Sstevel@tonic-gate 
15960Sstevel@tonic-gate static boolean_t
15971184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp)
15980Sstevel@tonic-gate {
15990Sstevel@tonic-gate 	mac_handle_t	mh;
16000Sstevel@tonic-gate 
1601269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16020Sstevel@tonic-gate 	ASSERT(!dsp->ds_polling);
16030Sstevel@tonic-gate 
16040Sstevel@tonic-gate 	/*
16050Sstevel@tonic-gate 	 * We cannot enable polling if raw mode
16060Sstevel@tonic-gate 	 * has been enabled.
16070Sstevel@tonic-gate 	 */
16080Sstevel@tonic-gate 	if (dsp->ds_mode == DLD_RAW)
16090Sstevel@tonic-gate 		return (B_FALSE);
16100Sstevel@tonic-gate 
16110Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
16120Sstevel@tonic-gate 
16130Sstevel@tonic-gate 	/*
16140Sstevel@tonic-gate 	 * Register resources.
16150Sstevel@tonic-gate 	 */
16161184Skrgopi 	mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add,
16171184Skrgopi 	    (void *)pollp->dls_rx_handle);
16180Sstevel@tonic-gate 	mac_resources(mh);
16190Sstevel@tonic-gate 
16200Sstevel@tonic-gate 	/*
16210Sstevel@tonic-gate 	 * Set the receive function.
16220Sstevel@tonic-gate 	 */
16231184Skrgopi 	dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx,
16241184Skrgopi 	    (void *)pollp->dls_rx_handle);
16250Sstevel@tonic-gate 
16260Sstevel@tonic-gate 	/*
16270Sstevel@tonic-gate 	 * Note that polling is enabled. This prevents further DLIOCHDRINFO
16280Sstevel@tonic-gate 	 * ioctls from overwriting the receive function pointer.
16290Sstevel@tonic-gate 	 */
16300Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
16310Sstevel@tonic-gate 	return (B_TRUE);
16320Sstevel@tonic-gate }
16330Sstevel@tonic-gate 
16341184Skrgopi static void
16351184Skrgopi proto_soft_ring_disable(dld_str_t *dsp)
16361184Skrgopi {
16371184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16381184Skrgopi 
16391184Skrgopi 	if (!dsp->ds_soft_ring)
16401184Skrgopi 		return;
16411184Skrgopi 
16421184Skrgopi 	/*
16431184Skrgopi 	 * It should be impossible to enable raw mode if soft_ring is turned on.
16441184Skrgopi 	 */
16451184Skrgopi 	ASSERT(dsp->ds_mode != DLD_RAW);
16461184Skrgopi 	proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE);
16471184Skrgopi 	/*
16481184Skrgopi 	 * Note that fanout is disabled.
16491184Skrgopi 	 */
16501184Skrgopi 	dsp->ds_soft_ring = B_FALSE;
16511184Skrgopi }
16521184Skrgopi 
16531184Skrgopi static boolean_t
16541184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp)
16551184Skrgopi {
16561184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16571184Skrgopi 	ASSERT(!dsp->ds_soft_ring);
16581184Skrgopi 
16591184Skrgopi 	/*
16601184Skrgopi 	 * We cannot enable soft_ring if raw mode
16611184Skrgopi 	 * has been enabled.
16621184Skrgopi 	 */
16631184Skrgopi 	if (dsp->ds_mode == DLD_RAW)
16641184Skrgopi 		return (B_FALSE);
16651184Skrgopi 
16661184Skrgopi 	if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE)
16671184Skrgopi 		return (B_FALSE);
16681184Skrgopi 
16691184Skrgopi 	dsp->ds_soft_ring = B_TRUE;
16701184Skrgopi 	return (B_TRUE);
16711184Skrgopi }
16721184Skrgopi 
16731184Skrgopi static void
16741184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type)
16751184Skrgopi {
16761184Skrgopi 	dls_rx_t	rx;
16771184Skrgopi 
16781184Skrgopi 	if (type == SOFT_RING_NONE) {
16791184Skrgopi 		rx = (dsp->ds_mode == DLD_FASTPATH) ?
16801184Skrgopi 			    dld_str_rx_fastpath : dld_str_rx_unitdata;
16811184Skrgopi 	} else {
16821184Skrgopi 		rx = (dls_rx_t)dls_ether_soft_ring_fanout;
16831184Skrgopi 	}
16841184Skrgopi 	dls_soft_ring_rx_set(dsp->ds_dc, rx, dsp, type);
16851184Skrgopi }
16861184Skrgopi 
16870Sstevel@tonic-gate /*
16880Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
16890Sstevel@tonic-gate  */
1690269Sericheng static boolean_t
1691269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
16920Sstevel@tonic-gate {
16930Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
16940Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
16950Sstevel@tonic-gate 	size_t			subsize;
16961184Skrgopi 	dl_capab_dls_t		poll;
16971184Skrgopi 	dl_capab_dls_t	soft_ring;
16980Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
16990Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
17000Sstevel@tonic-gate 	uint8_t			*ptr;
1701*2311Sseb 	boolean_t		cksum_cap;
17020Sstevel@tonic-gate 	boolean_t		poll_cap;
1703269Sericheng 	queue_t			*q = dsp->ds_wq;
1704269Sericheng 	mblk_t			*mp1;
1705269Sericheng 
1706269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
17070Sstevel@tonic-gate 
17080Sstevel@tonic-gate 	/*
17090Sstevel@tonic-gate 	 * Initially assume no capabilities.
17100Sstevel@tonic-gate 	 */
17110Sstevel@tonic-gate 	subsize = 0;
17120Sstevel@tonic-gate 
17131555Skrgopi 	/*
17141555Skrgopi 	 * Advertize soft ring capability if
17151555Skrgopi 	 * VLAN_ID_NONE for GLDv3 drivers
17161555Skrgopi 	 */
17171555Skrgopi 	if (dsp->ds_vid == VLAN_ID_NONE)
17181555Skrgopi 		subsize += sizeof (dl_capability_sub_t) +
17191555Skrgopi 				    sizeof (dl_capab_dls_t);
17201184Skrgopi 
17210Sstevel@tonic-gate 	/*
17220Sstevel@tonic-gate 	 * Check if polling can be enabled on this interface.
17230Sstevel@tonic-gate 	 * If advertising DL_CAPAB_POLL has not been explicitly disabled
17240Sstevel@tonic-gate 	 * then reserve space for that capability.
17250Sstevel@tonic-gate 	 */
1726*2311Sseb 	poll_cap = (mac_capab_get(dsp->ds_mh, MAC_CAPAB_POLL, NULL) &&
17270Sstevel@tonic-gate 	    !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE));
17280Sstevel@tonic-gate 	if (poll_cap) {
17290Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17301184Skrgopi 		    sizeof (dl_capab_dls_t);
17310Sstevel@tonic-gate 	}
17320Sstevel@tonic-gate 
17330Sstevel@tonic-gate 	/*
17340Sstevel@tonic-gate 	 * If the MAC interface supports checksum offload then reserve
17350Sstevel@tonic-gate 	 * space for the DL_CAPAB_HCKSUM capability.
17360Sstevel@tonic-gate 	 */
1737*2311Sseb 	if (cksum_cap = mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
1738*2311Sseb 	    &hcksum.hcksum_txflags)) {
17390Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17400Sstevel@tonic-gate 		    sizeof (dl_capab_hcksum_t);
17410Sstevel@tonic-gate 	}
17420Sstevel@tonic-gate 
17430Sstevel@tonic-gate 	/*
17440Sstevel@tonic-gate 	 * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then
17450Sstevel@tonic-gate 	 * reserve space for it.
17460Sstevel@tonic-gate 	 */
17470Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
17480Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17490Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
17500Sstevel@tonic-gate 	}
17510Sstevel@tonic-gate 
17520Sstevel@tonic-gate 	/*
1753269Sericheng 	 * If there are no capabilities to advertise or if we
1754269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
17550Sstevel@tonic-gate 	 */
17561184Skrgopi 	if ((mp1 = reallocb(mp,
1757269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1758269Sericheng 		rw_exit(&dsp->ds_lock);
1759269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1760269Sericheng 		return (B_FALSE);
17610Sstevel@tonic-gate 	}
17620Sstevel@tonic-gate 
1763269Sericheng 	mp = mp1;
1764269Sericheng 	DB_TYPE(mp) = M_PROTO;
1765269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1766269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
17670Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
17680Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
17690Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
17700Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
17710Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
17720Sstevel@tonic-gate 
17730Sstevel@tonic-gate 	/*
17740Sstevel@tonic-gate 	 * IP polling interface.
17750Sstevel@tonic-gate 	 */
17760Sstevel@tonic-gate 	if (poll_cap) {
17770Sstevel@tonic-gate 		/*
1778269Sericheng 		 * Attempt to disable just in case this is a re-negotiation;
1779269Sericheng 		 * we need to become writer before doing so.
17800Sstevel@tonic-gate 		 */
1781269Sericheng 		if (!rw_tryupgrade(&dsp->ds_lock)) {
1782269Sericheng 			rw_exit(&dsp->ds_lock);
1783269Sericheng 			rw_enter(&dsp->ds_lock, RW_WRITER);
1784269Sericheng 		}
17850Sstevel@tonic-gate 
1786269Sericheng 		/*
1787269Sericheng 		 * Check if polling state has changed after we re-acquired
1788269Sericheng 		 * the lock above, so that we don't mis-advertise it.
1789269Sericheng 		 */
1790*2311Sseb 		poll_cap = !(dld_opt & DLD_OPT_NO_POLL) &&
1791*2311Sseb 		    (dsp->ds_vid == VLAN_ID_NONE);
17920Sstevel@tonic-gate 
1793269Sericheng 		if (!poll_cap) {
1794269Sericheng 			int poll_capab_size;
1795269Sericheng 
1796269Sericheng 			rw_downgrade(&dsp->ds_lock);
1797269Sericheng 
1798269Sericheng 			poll_capab_size = sizeof (dl_capability_sub_t) +
17991184Skrgopi 			    sizeof (dl_capab_dls_t);
18000Sstevel@tonic-gate 
1801269Sericheng 			mp->b_wptr -= poll_capab_size;
1802269Sericheng 			subsize -= poll_capab_size;
1803269Sericheng 			dlap->dl_sub_length = subsize;
1804269Sericheng 		} else {
1805269Sericheng 			proto_poll_disable(dsp);
1806269Sericheng 
1807269Sericheng 			rw_downgrade(&dsp->ds_lock);
1808269Sericheng 
1809269Sericheng 			dlsp = (dl_capability_sub_t *)ptr;
1810269Sericheng 
1811269Sericheng 			dlsp->dl_cap = DL_CAPAB_POLL;
18121184Skrgopi 			dlsp->dl_length = sizeof (dl_capab_dls_t);
1813269Sericheng 			ptr += sizeof (dl_capability_sub_t);
18140Sstevel@tonic-gate 
18151184Skrgopi 			bzero(&poll, sizeof (dl_capab_dls_t));
18161184Skrgopi 			poll.dls_version = POLL_VERSION_1;
18171184Skrgopi 			poll.dls_flags = POLL_CAPABLE;
18181184Skrgopi 			poll.dls_tx_handle = (uintptr_t)dsp;
18191184Skrgopi 			poll.dls_tx = (uintptr_t)str_mdata_fastpath_put;
1820269Sericheng 
18211184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
18221184Skrgopi 			bcopy(&poll, ptr, sizeof (dl_capab_dls_t));
18231184Skrgopi 			ptr += sizeof (dl_capab_dls_t);
1824269Sericheng 		}
18250Sstevel@tonic-gate 	}
18260Sstevel@tonic-gate 
1827269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
1828269Sericheng 
18291555Skrgopi 	if (dsp->ds_vid == VLAN_ID_NONE) {
18301555Skrgopi 		dlsp = (dl_capability_sub_t *)ptr;
18311184Skrgopi 
18321555Skrgopi 		dlsp->dl_cap = DL_CAPAB_SOFT_RING;
18331555Skrgopi 		dlsp->dl_length = sizeof (dl_capab_dls_t);
18341555Skrgopi 		ptr += sizeof (dl_capability_sub_t);
18351184Skrgopi 
18361555Skrgopi 		bzero(&soft_ring, sizeof (dl_capab_dls_t));
18371555Skrgopi 		soft_ring.dls_version = SOFT_RING_VERSION_1;
18381555Skrgopi 		soft_ring.dls_flags = SOFT_RING_CAPABLE;
18391555Skrgopi 		soft_ring.dls_tx_handle = (uintptr_t)dsp;
18401555Skrgopi 		soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put;
18411555Skrgopi 		soft_ring.dls_ring_change_status =
18421555Skrgopi 		    (uintptr_t)proto_change_soft_ring_fanout;
18431555Skrgopi 		soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind;
18441555Skrgopi 		soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind;
18451184Skrgopi 
18461555Skrgopi 		dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
18471555Skrgopi 		bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t));
18481555Skrgopi 		ptr += sizeof (dl_capab_dls_t);
18491555Skrgopi 	}
18501184Skrgopi 
18510Sstevel@tonic-gate 	/*
18520Sstevel@tonic-gate 	 * TCP/IP checksum offload.
18530Sstevel@tonic-gate 	 */
1854*2311Sseb 	if (cksum_cap) {
18550Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18560Sstevel@tonic-gate 
18570Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
18580Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
18590Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18600Sstevel@tonic-gate 
18610Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
18620Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
18630Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
18640Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
18650Sstevel@tonic-gate 	}
18660Sstevel@tonic-gate 
18670Sstevel@tonic-gate 	/*
18680Sstevel@tonic-gate 	 * Zero copy
18690Sstevel@tonic-gate 	 */
18700Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
18710Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
18740Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
18750Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18760Sstevel@tonic-gate 
18770Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
18780Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
18790Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
18800Sstevel@tonic-gate 
18810Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
18820Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
18830Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
18840Sstevel@tonic-gate 	}
18850Sstevel@tonic-gate 
18860Sstevel@tonic-gate 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1887269Sericheng 
1888269Sericheng 	rw_exit(&dsp->ds_lock);
1889269Sericheng 	qreply(q, mp);
1890269Sericheng 	return (B_TRUE);
18910Sstevel@tonic-gate }
1892