xref: /onnv-gate/usr/src/uts/common/io/dld/dld_proto.c (revision 3037:8affbdf165c1)
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;
487*3037Syz147064 	uint8_t		dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)];
488*3037Syz147064 	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 	 */
554*3037Syz147064 	dlsap_addr_length = dsp->ds_mip->mi_addr_length;
555*3037Syz147064 	bcopy(dsp->ds_curr_addr, dlsap_addr, dlsap_addr_length);
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	/*
558*3037Syz147064 	 * Copy in the SAP.
5590Sstevel@tonic-gate 	 */
560*3037Syz147064 	*(uint16_t *)(dlsap_addr + dlsap_addr_length) = dsp->ds_sap;
561*3037Syz147064 	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 
569*3037Syz147064 	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 	/*
610269Sericheng 	 * Clear the receive callback.
611269Sericheng 	 */
612269Sericheng 	dls_rx_set(dsp->ds_dc, NULL, NULL);
613269Sericheng 
614269Sericheng 	/*
615269Sericheng 	 * Set the mode back to the default (unitdata).
616269Sericheng 	 */
617269Sericheng 	dsp->ds_mode = DLD_UNITDATA;
618269Sericheng 
6191184Skrgopi 	/*
6201184Skrgopi 	 * If soft rings were enabled, the workers
6211353Sericheng 	 * should be quiesced. We cannot check for
6221184Skrgopi 	 * ds_soft_ring flag because
6231184Skrgopi 	 * proto_soft_ring_disable() called from
6241184Skrgopi 	 * proto_capability_req() would have reset it.
6251184Skrgopi 	 */
6261353Sericheng 	if (dls_soft_ring_workers(dsp->ds_dc))
6271353Sericheng 		dls_soft_ring_disable(dsp->ds_dc);
6281353Sericheng 
6291353Sericheng 	mp = dsp->ds_pending_req;
6301353Sericheng 	dsp->ds_pending_req = NULL;
6311353Sericheng 	dsp->ds_dlstate = DL_UNBOUND;
6321353Sericheng 	dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ);
6331353Sericheng 
6341353Sericheng 	DLD_WAKEUP(dsp);
6351353Sericheng }
6361353Sericheng 
6371353Sericheng /*ARGSUSED*/
6381353Sericheng static boolean_t
6391353Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6401353Sericheng {
6411353Sericheng 	queue_t		*q = dsp->ds_wq;
6421353Sericheng 	t_uscalar_t	dl_err;
6431353Sericheng 
6441353Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
6451353Sericheng 
6461353Sericheng 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
6471353Sericheng 		dl_err = DL_BADPRIM;
6481353Sericheng 		goto failed;
6491184Skrgopi 	}
6501184Skrgopi 
6511353Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
6521353Sericheng 		dl_err = DL_OUTSTATE;
6531353Sericheng 		goto failed;
6541353Sericheng 	}
6551353Sericheng 
6561353Sericheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
6571353Sericheng 
6581353Sericheng 	mutex_enter(&dsp->ds_thr_lock);
6591353Sericheng 	ASSERT(dsp->ds_pending_req == NULL);
6601353Sericheng 	dsp->ds_pending_req = mp;
6611353Sericheng 	dsp->ds_pending_op = proto_process_unbind_req;
6621353Sericheng 	dsp->ds_pending_cnt++;
6631353Sericheng 	mutex_exit(&dsp->ds_thr_lock);
664269Sericheng 	rw_exit(&dsp->ds_lock);
665269Sericheng 
666269Sericheng 	return (B_TRUE);
667269Sericheng failed:
668269Sericheng 	rw_exit(&dsp->ds_lock);
669269Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
670269Sericheng 	return (B_FALSE);
6710Sstevel@tonic-gate }
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate /*
674269Sericheng  * DL_PROMISCON_REQ
6750Sstevel@tonic-gate  */
676269Sericheng static boolean_t
677269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6780Sstevel@tonic-gate {
679269Sericheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp;
680269Sericheng 	int		err = 0;
681269Sericheng 	t_uscalar_t	dl_err;
682269Sericheng 	uint32_t	promisc_saved;
683269Sericheng 	queue_t		*q = dsp->ds_wq;
684269Sericheng 
6851521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
6861521Syz147064 
687269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
688269Sericheng 		dl_err = DL_BADPRIM;
689269Sericheng 		goto failed;
690269Sericheng 	}
691269Sericheng 
692269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
693269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
694269Sericheng 		dl_err = DL_OUTSTATE;
6950Sstevel@tonic-gate 		goto failed;
696269Sericheng 	}
6970Sstevel@tonic-gate 
698269Sericheng 	promisc_saved = dsp->ds_promisc;
699269Sericheng 	switch (dlp->dl_level) {
700269Sericheng 	case DL_PROMISC_SAP:
701269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_SAP;
702269Sericheng 		break;
703269Sericheng 
704269Sericheng 	case DL_PROMISC_MULTI:
705269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_MULTI;
706269Sericheng 		break;
707269Sericheng 
708269Sericheng 	case DL_PROMISC_PHYS:
709269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_PHYS;
710269Sericheng 		break;
711269Sericheng 
712269Sericheng 	default:
713269Sericheng 		dl_err = DL_NOTSUPPORTED;
714269Sericheng 		goto failed;
715269Sericheng 	}
7160Sstevel@tonic-gate 
717269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
718269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
719269Sericheng 		dsp->ds_promisc = promisc_saved;
720269Sericheng 		dl_err = DL_SYSERR;
721269Sericheng 		err = EBUSY;
722269Sericheng 		goto failed;
723269Sericheng 	}
724269Sericheng 
725269Sericheng 	/*
726269Sericheng 	 * Adjust channel promiscuity.
727269Sericheng 	 */
728269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
729269Sericheng 	if (err != 0) {
730269Sericheng 		dl_err = DL_SYSERR;
731269Sericheng 		dsp->ds_promisc = promisc_saved;
732269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
733269Sericheng 			dls_active_clear(dsp->ds_dc);
734269Sericheng 
735269Sericheng 		goto failed;
736269Sericheng 	}
737269Sericheng 
738269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
739269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
740269Sericheng 
741269Sericheng 	rw_exit(&dsp->ds_lock);
742269Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
743269Sericheng 	return (B_TRUE);
7440Sstevel@tonic-gate failed:
745269Sericheng 	rw_exit(&dsp->ds_lock);
746269Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
747269Sericheng 	return (B_FALSE);
7480Sstevel@tonic-gate }
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate /*
751269Sericheng  * DL_PROMISCOFF_REQ
7520Sstevel@tonic-gate  */
753269Sericheng static boolean_t
754269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
7550Sstevel@tonic-gate {
756269Sericheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp;
757269Sericheng 	int		err = 0;
758269Sericheng 	t_uscalar_t	dl_err;
759269Sericheng 	uint32_t	promisc_saved;
760269Sericheng 	queue_t		*q = dsp->ds_wq;
761269Sericheng 
7621521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
763269Sericheng 
764269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
765269Sericheng 		dl_err = DL_BADPRIM;
7660Sstevel@tonic-gate 		goto failed;
767269Sericheng 	}
7680Sstevel@tonic-gate 
769269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
770269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
771269Sericheng 		dl_err = DL_OUTSTATE;
7720Sstevel@tonic-gate 		goto failed;
773269Sericheng 	}
7740Sstevel@tonic-gate 
775269Sericheng 	promisc_saved = dsp->ds_promisc;
776269Sericheng 	switch (dlp->dl_level) {
777269Sericheng 	case DL_PROMISC_SAP:
778269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
779269Sericheng 			dl_err = DL_NOTENAB;
780269Sericheng 			goto failed;
781269Sericheng 		}
782269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_SAP;
7830Sstevel@tonic-gate 		break;
7840Sstevel@tonic-gate 
785269Sericheng 	case DL_PROMISC_MULTI:
786269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
787269Sericheng 			dl_err = DL_NOTENAB;
788269Sericheng 			goto failed;
789269Sericheng 		}
790269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_MULTI;
791269Sericheng 		break;
792269Sericheng 
793269Sericheng 	case DL_PROMISC_PHYS:
794269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
795269Sericheng 			dl_err = DL_NOTENAB;
796269Sericheng 			goto failed;
797269Sericheng 		}
798269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_PHYS;
7990Sstevel@tonic-gate 		break;
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 	default:
802269Sericheng 		dl_err = DL_NOTSUPPORTED;
803269Sericheng 		goto failed;
804269Sericheng 	}
805269Sericheng 
806269Sericheng 	/*
807269Sericheng 	 * Adjust channel promiscuity.
808269Sericheng 	 */
809269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
810269Sericheng 	if (err != 0) {
811269Sericheng 		dsp->ds_promisc = promisc_saved;
8120Sstevel@tonic-gate 		dl_err = DL_SYSERR;
813269Sericheng 		goto failed;
814269Sericheng 	}
815269Sericheng 
816269Sericheng 	rw_exit(&dsp->ds_lock);
817269Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
818269Sericheng 	return (B_TRUE);
819269Sericheng failed:
820269Sericheng 	rw_exit(&dsp->ds_lock);
821269Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
822269Sericheng 	return (B_FALSE);
823269Sericheng }
824269Sericheng 
825269Sericheng /*
826269Sericheng  * DL_ENABMULTI_REQ
827269Sericheng  */
828269Sericheng static boolean_t
829269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
830269Sericheng {
831269Sericheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp;
832269Sericheng 	int		err = 0;
833269Sericheng 	t_uscalar_t	dl_err;
834269Sericheng 	queue_t		*q = dsp->ds_wq;
835269Sericheng 
836269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
837269Sericheng 
838269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
839269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
840269Sericheng 		dl_err = DL_OUTSTATE;
841269Sericheng 		goto failed;
842269Sericheng 	}
843269Sericheng 
844269Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
845269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
846269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
847269Sericheng 		dl_err = DL_BADPRIM;
848269Sericheng 		goto failed;
849269Sericheng 	}
850269Sericheng 
851269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
852269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
853269Sericheng 		dl_err = DL_SYSERR;
854269Sericheng 		err = EBUSY;
855269Sericheng 		goto failed;
8560Sstevel@tonic-gate 	}
8570Sstevel@tonic-gate 
858269Sericheng 	err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
859269Sericheng 	if (err != 0) {
860269Sericheng 		switch (err) {
861269Sericheng 		case EINVAL:
862269Sericheng 			dl_err = DL_BADADDR;
863269Sericheng 			err = 0;
864269Sericheng 			break;
865269Sericheng 		case ENOSPC:
866269Sericheng 			dl_err = DL_TOOMANY;
867269Sericheng 			err = 0;
868269Sericheng 			break;
869269Sericheng 		default:
870269Sericheng 			dl_err = DL_SYSERR;
871269Sericheng 			break;
872269Sericheng 		}
873269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
874269Sericheng 			dls_active_clear(dsp->ds_dc);
875269Sericheng 
876269Sericheng 		goto failed;
877269Sericheng 	}
878269Sericheng 
879269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
880269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
881269Sericheng 
882269Sericheng 	rw_exit(&dsp->ds_lock);
883269Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
884269Sericheng 	return (B_TRUE);
885269Sericheng failed:
886269Sericheng 	rw_exit(&dsp->ds_lock);
887269Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
888269Sericheng 	return (B_FALSE);
889269Sericheng }
890269Sericheng 
891269Sericheng /*
892269Sericheng  * DL_DISABMULTI_REQ
893269Sericheng  */
894269Sericheng static boolean_t
895269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
896269Sericheng {
897269Sericheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp;
898269Sericheng 	int		err = 0;
899269Sericheng 	t_uscalar_t	dl_err;
900269Sericheng 	queue_t		*q = dsp->ds_wq;
901269Sericheng 
902269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
903269Sericheng 
904269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
905269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
906269Sericheng 		dl_err = DL_OUTSTATE;
907269Sericheng 		goto failed;
908269Sericheng 	}
909269Sericheng 
910269Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
911269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
912269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
913269Sericheng 		dl_err = DL_BADPRIM;
914269Sericheng 		goto failed;
915269Sericheng 	}
916269Sericheng 
917269Sericheng 	err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
918269Sericheng 	if (err != 0) {
919269Sericheng 	switch (err) {
920269Sericheng 		case EINVAL:
921269Sericheng 			dl_err = DL_BADADDR;
922269Sericheng 			err = 0;
923269Sericheng 			break;
924269Sericheng 
925269Sericheng 		case ENOENT:
926269Sericheng 			dl_err = DL_NOTENAB;
927269Sericheng 			err = 0;
928269Sericheng 			break;
929269Sericheng 
930269Sericheng 		default:
931269Sericheng 			dl_err = DL_SYSERR;
932269Sericheng 			break;
933269Sericheng 		}
934269Sericheng 		goto failed;
935269Sericheng 	}
936269Sericheng 
937269Sericheng 	rw_exit(&dsp->ds_lock);
938269Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
939269Sericheng 	return (B_TRUE);
940269Sericheng failed:
941269Sericheng 	rw_exit(&dsp->ds_lock);
942269Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
943269Sericheng 	return (B_FALSE);
9440Sstevel@tonic-gate }
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate /*
947269Sericheng  * DL_PHYS_ADDR_REQ
9480Sstevel@tonic-gate  */
949269Sericheng static boolean_t
950269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
9510Sstevel@tonic-gate {
952269Sericheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp;
953269Sericheng 	queue_t		*q = dsp->ds_wq;
954269Sericheng 	t_uscalar_t	dl_err;
955269Sericheng 	char		*addr;
956269Sericheng 	uint_t		addr_length;
957269Sericheng 
958269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
959269Sericheng 
960269Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
961269Sericheng 		dl_err = DL_BADPRIM;
962269Sericheng 		goto failed;
963269Sericheng 	}
964269Sericheng 
965269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
966269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
967269Sericheng 		dl_err = DL_OUTSTATE;
968269Sericheng 		goto failed;
969269Sericheng 	}
9700Sstevel@tonic-gate 
971269Sericheng 	if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR &&
972269Sericheng 	    dlp->dl_addr_type != DL_FACT_PHYS_ADDR) {
973269Sericheng 		dl_err = DL_UNSUPPORTED;
9740Sstevel@tonic-gate 		goto failed;
975269Sericheng 	}
9760Sstevel@tonic-gate 
977269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
978269Sericheng 	addr = kmem_alloc(addr_length, KM_NOSLEEP);
979269Sericheng 	if (addr == NULL) {
980269Sericheng 		rw_exit(&dsp->ds_lock);
981269Sericheng 		merror(q, mp, ENOSR);
982269Sericheng 		return (B_FALSE);
983269Sericheng 	}
9840Sstevel@tonic-gate 
985269Sericheng 	/*
986269Sericheng 	 * Copy out the address before we drop the lock; we don't
987269Sericheng 	 * want to call dlphysaddrack() while holding ds_lock.
988269Sericheng 	 */
989269Sericheng 	bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ?
990269Sericheng 	    dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length);
991269Sericheng 
992269Sericheng 	rw_exit(&dsp->ds_lock);
993269Sericheng 	dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
994269Sericheng 	kmem_free(addr, addr_length);
995269Sericheng 	return (B_TRUE);
9960Sstevel@tonic-gate failed:
997269Sericheng 	rw_exit(&dsp->ds_lock);
998269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
999269Sericheng 	return (B_FALSE);
1000269Sericheng }
10010Sstevel@tonic-gate 
1002269Sericheng /*
1003269Sericheng  * DL_SET_PHYS_ADDR_REQ
1004269Sericheng  */
1005269Sericheng static boolean_t
1006269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1007269Sericheng {
1008269Sericheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp;
1009269Sericheng 	int		err = 0;
1010269Sericheng 	t_uscalar_t	dl_err;
1011269Sericheng 	queue_t		*q = dsp->ds_wq;
10120Sstevel@tonic-gate 
1013269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1014269Sericheng 
1015269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1016269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1017269Sericheng 		dl_err = DL_OUTSTATE;
1018269Sericheng 		goto failed;
1019269Sericheng 	}
1020269Sericheng 
1021269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
1022269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
1023269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
1024269Sericheng 		dl_err = DL_BADPRIM;
1025269Sericheng 		goto failed;
10260Sstevel@tonic-gate 	}
10270Sstevel@tonic-gate 
1028269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
1029269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
1030269Sericheng 		dl_err = DL_SYSERR;
1031269Sericheng 		err = EBUSY;
1032269Sericheng 		goto failed;
1033269Sericheng 	}
1034269Sericheng 
1035269Sericheng 	err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset);
1036269Sericheng 	if (err != 0) {
1037269Sericheng 		switch (err) {
1038269Sericheng 		case EINVAL:
1039269Sericheng 			dl_err = DL_BADADDR;
1040269Sericheng 			err = 0;
1041269Sericheng 			break;
1042269Sericheng 
1043269Sericheng 		default:
1044269Sericheng 			dl_err = DL_SYSERR;
1045269Sericheng 			break;
1046269Sericheng 		}
1047269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1048269Sericheng 			dls_active_clear(dsp->ds_dc);
1049269Sericheng 
1050269Sericheng 		goto failed;
1051269Sericheng 	}
1052269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1053269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
1054269Sericheng 
1055269Sericheng 	rw_exit(&dsp->ds_lock);
1056269Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
1057269Sericheng 	return (B_TRUE);
1058269Sericheng failed:
1059269Sericheng 	rw_exit(&dsp->ds_lock);
1060269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
1061269Sericheng 	return (B_FALSE);
1062269Sericheng }
1063269Sericheng 
1064269Sericheng /*
1065269Sericheng  * DL_UDQOS_REQ
1066269Sericheng  */
1067269Sericheng static boolean_t
1068269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1069269Sericheng {
1070269Sericheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp;
1071269Sericheng 	dl_qos_cl_sel1_t *selp;
1072269Sericheng 	int		off, len;
1073269Sericheng 	t_uscalar_t	dl_err;
1074269Sericheng 	queue_t		*q = dsp->ds_wq;
1075269Sericheng 
1076269Sericheng 	off = dlp->dl_qos_offset;
1077269Sericheng 	len = dlp->dl_qos_length;
1078269Sericheng 
10791521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
10801521Syz147064 
1081269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
1082269Sericheng 		dl_err = DL_BADPRIM;
1083269Sericheng 		goto failed;
1084269Sericheng 	}
1085269Sericheng 
1086269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
1087269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
1088269Sericheng 		dl_err = DL_BADQOSTYPE;
1089269Sericheng 		goto failed;
1090269Sericheng 	}
1091269Sericheng 
10922760Sdg199075 	if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1093269Sericheng 	    selp->dl_priority < 0) {
1094269Sericheng 		dl_err = DL_BADQOSPARAM;
1095269Sericheng 		goto failed;
1096269Sericheng 	}
1097269Sericheng 
1098269Sericheng 	dsp->ds_pri = selp->dl_priority;
1099269Sericheng 
1100269Sericheng 	rw_exit(&dsp->ds_lock);
1101269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1102269Sericheng 	return (B_TRUE);
1103269Sericheng failed:
1104269Sericheng 	rw_exit(&dsp->ds_lock);
1105269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1106269Sericheng 	return (B_FALSE);
11070Sstevel@tonic-gate }
11080Sstevel@tonic-gate 
11091184Skrgopi static boolean_t
11101184Skrgopi check_ip_above(queue_t *q)
11111184Skrgopi {
11121184Skrgopi 	queue_t		*next_q;
11131184Skrgopi 	boolean_t	ret = B_TRUE;
11141184Skrgopi 
11151184Skrgopi 	claimstr(q);
11161184Skrgopi 	next_q = q->q_next;
11171184Skrgopi 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
11181184Skrgopi 		ret = B_FALSE;
11191184Skrgopi 	releasestr(q);
11201184Skrgopi 	return (ret);
11211184Skrgopi }
11221184Skrgopi 
11230Sstevel@tonic-gate /*
1124269Sericheng  * DL_CAPABILITY_REQ
11250Sstevel@tonic-gate  */
1126269Sericheng /*ARGSUSED*/
1127269Sericheng static boolean_t
1128269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
11290Sstevel@tonic-gate {
1130269Sericheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)udlp;
1131269Sericheng 	dl_capability_sub_t *sp;
1132269Sericheng 	size_t		size, len;
1133269Sericheng 	offset_t	off, end;
1134269Sericheng 	t_uscalar_t	dl_err;
1135269Sericheng 	queue_t		*q = dsp->ds_wq;
1136269Sericheng 	boolean_t	upgraded;
1137269Sericheng 
1138269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1139269Sericheng 
1140269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1141269Sericheng 		dl_err = DL_BADPRIM;
1142269Sericheng 		goto failed;
1143269Sericheng 	}
1144269Sericheng 
1145269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1146269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1147269Sericheng 		dl_err = DL_OUTSTATE;
1148269Sericheng 		goto failed;
1149269Sericheng 	}
1150269Sericheng 
1151269Sericheng 	/*
1152269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1153269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1154269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1155269Sericheng 	 */
1156269Sericheng 	if (dlp->dl_sub_length == 0) {
1157269Sericheng 		/* callee drops lock */
1158269Sericheng 		return (proto_capability_advertise(dsp, mp));
1159269Sericheng 	}
1160269Sericheng 
1161269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1162269Sericheng 		dl_err = DL_BADPRIM;
1163269Sericheng 		goto failed;
1164269Sericheng 	}
1165269Sericheng 
1166269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1167269Sericheng 
1168269Sericheng 	off = dlp->dl_sub_offset;
1169269Sericheng 	len = dlp->dl_sub_length;
11700Sstevel@tonic-gate 
11710Sstevel@tonic-gate 	/*
1172269Sericheng 	 * Walk the list of capabilities to be enabled.
11730Sstevel@tonic-gate 	 */
1174269Sericheng 	upgraded = B_FALSE;
1175269Sericheng 	for (end = off + len; off < end; ) {
1176269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1177269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1178269Sericheng 
1179269Sericheng 		if (off + size > end ||
1180269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1181269Sericheng 			dl_err = DL_BADPRIM;
1182269Sericheng 			goto failed;
1183269Sericheng 		}
1184269Sericheng 
1185269Sericheng 		switch (sp->dl_cap) {
1186269Sericheng 		/*
1187269Sericheng 		 * TCP/IP checksum offload to hardware.
1188269Sericheng 		 */
1189269Sericheng 		case DL_CAPAB_HCKSUM: {
1190269Sericheng 			dl_capab_hcksum_t *hcksump;
1191269Sericheng 			dl_capab_hcksum_t hcksum;
1192269Sericheng 
1193269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1194269Sericheng 			/*
1195269Sericheng 			 * Copy for alignment.
1196269Sericheng 			 */
1197269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1198269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1199269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1200269Sericheng 			break;
1201269Sericheng 		}
1202269Sericheng 
1203269Sericheng 		/*
1204269Sericheng 		 * IP polling interface.
1205269Sericheng 		 */
1206269Sericheng 		case DL_CAPAB_POLL: {
12071184Skrgopi 			dl_capab_dls_t *pollp;
12081184Skrgopi 			dl_capab_dls_t	poll;
1209269Sericheng 
12101184Skrgopi 			pollp = (dl_capab_dls_t *)&sp[1];
1211269Sericheng 			/*
1212269Sericheng 			 * Copy for alignment.
1213269Sericheng 			 */
12141184Skrgopi 			bcopy(pollp, &poll, sizeof (dl_capab_dls_t));
1215269Sericheng 
1216269Sericheng 			/*
1217269Sericheng 			 * We need to become writer before enabling and/or
1218269Sericheng 			 * disabling the polling interface.  If we couldn'
1219269Sericheng 			 * upgrade, check state again after re-acquiring the
1220269Sericheng 			 * lock to make sure we can proceed.
1221269Sericheng 			 */
1222269Sericheng 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
1223269Sericheng 				rw_exit(&dsp->ds_lock);
1224269Sericheng 				rw_enter(&dsp->ds_lock, RW_WRITER);
1225269Sericheng 
1226269Sericheng 				if (dsp->ds_dlstate == DL_UNATTACHED ||
1227269Sericheng 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
1228269Sericheng 					dl_err = DL_OUTSTATE;
1229269Sericheng 					goto failed;
1230269Sericheng 				}
1231269Sericheng 			}
1232269Sericheng 			upgraded = B_TRUE;
1233269Sericheng 
12341184Skrgopi 			switch (poll.dls_flags) {
1235269Sericheng 			default:
1236269Sericheng 				/*FALLTHRU*/
1237269Sericheng 			case POLL_DISABLE:
1238269Sericheng 				proto_poll_disable(dsp);
1239269Sericheng 				break;
1240269Sericheng 
1241269Sericheng 			case POLL_ENABLE:
1242269Sericheng 				ASSERT(!(dld_opt & DLD_OPT_NO_POLL));
1243269Sericheng 
1244269Sericheng 				/*
1245269Sericheng 				 * Make sure polling is disabled.
1246269Sericheng 				 */
1247269Sericheng 				proto_poll_disable(dsp);
1248269Sericheng 
1249269Sericheng 				/*
1250269Sericheng 				 * Now attempt enable it.
1251269Sericheng 				 */
12521184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
12531184Skrgopi 				    proto_poll_enable(dsp, &poll)) {
12541184Skrgopi 					bzero(&poll, sizeof (dl_capab_dls_t));
12551184Skrgopi 					poll.dls_flags = POLL_ENABLE;
12561184Skrgopi 				}
1257269Sericheng 				break;
1258269Sericheng 			}
1259269Sericheng 
12601184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
12611184Skrgopi 			bcopy(&poll, pollp, sizeof (dl_capab_dls_t));
12621184Skrgopi 			break;
12631184Skrgopi 		}
12641184Skrgopi 		case DL_CAPAB_SOFT_RING: {
12651184Skrgopi 			dl_capab_dls_t *soft_ringp;
12661184Skrgopi 			dl_capab_dls_t soft_ring;
12671184Skrgopi 
12681184Skrgopi 			soft_ringp = (dl_capab_dls_t *)&sp[1];
12691184Skrgopi 			/*
12701184Skrgopi 			 * Copy for alignment.
12711184Skrgopi 			 */
12721184Skrgopi 			bcopy(soft_ringp, &soft_ring,
12731184Skrgopi 			    sizeof (dl_capab_dls_t));
12741184Skrgopi 
12751184Skrgopi 			/*
12761184Skrgopi 			 * We need to become writer before enabling and/or
12771184Skrgopi 			 * disabling the soft_ring interface.  If we couldn'
12781184Skrgopi 			 * upgrade, check state again after re-acquiring the
12791184Skrgopi 			 * lock to make sure we can proceed.
12801184Skrgopi 			 */
12811184Skrgopi 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
12821184Skrgopi 				rw_exit(&dsp->ds_lock);
12831184Skrgopi 				rw_enter(&dsp->ds_lock, RW_WRITER);
12841184Skrgopi 
12851184Skrgopi 				if (dsp->ds_dlstate == DL_UNATTACHED ||
12861184Skrgopi 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
12871184Skrgopi 					dl_err = DL_OUTSTATE;
12881184Skrgopi 					goto failed;
12891184Skrgopi 				}
12901184Skrgopi 			}
12911184Skrgopi 			upgraded = B_TRUE;
12921184Skrgopi 
12931184Skrgopi 			switch (soft_ring.dls_flags) {
12941184Skrgopi 			default:
12951184Skrgopi 				/*FALLTHRU*/
12961184Skrgopi 			case SOFT_RING_DISABLE:
12971184Skrgopi 				proto_soft_ring_disable(dsp);
12981184Skrgopi 				break;
12991184Skrgopi 
13001184Skrgopi 			case SOFT_RING_ENABLE:
13011184Skrgopi 				/*
13021184Skrgopi 				 * Make sure soft_ring is disabled.
13031184Skrgopi 				 */
13041184Skrgopi 				proto_soft_ring_disable(dsp);
13051184Skrgopi 
13061184Skrgopi 				/*
13071184Skrgopi 				 * Now attempt enable it.
13081184Skrgopi 				 */
13091184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
13101184Skrgopi 				    proto_soft_ring_enable(dsp, &soft_ring)) {
13111184Skrgopi 					bzero(&soft_ring,
13121184Skrgopi 					    sizeof (dl_capab_dls_t));
13131184Skrgopi 					soft_ring.dls_flags =
13141184Skrgopi 					    SOFT_RING_ENABLE;
13151184Skrgopi 				} else {
13161184Skrgopi 					bzero(&soft_ring,
13171184Skrgopi 					    sizeof (dl_capab_dls_t));
13181184Skrgopi 					soft_ring.dls_flags =
13191184Skrgopi 					    SOFT_RING_DISABLE;
13201184Skrgopi 				}
13211184Skrgopi 				break;
13221184Skrgopi 			}
13231184Skrgopi 
13241184Skrgopi 			dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
13251184Skrgopi 			bcopy(&soft_ring, soft_ringp,
13261184Skrgopi 			    sizeof (dl_capab_dls_t));
1327269Sericheng 			break;
1328269Sericheng 		}
1329269Sericheng 		default:
1330269Sericheng 			break;
1331269Sericheng 		}
1332269Sericheng 
1333269Sericheng 		off += size;
1334269Sericheng 	}
1335269Sericheng 	rw_exit(&dsp->ds_lock);
1336269Sericheng 	qreply(q, mp);
1337269Sericheng 	return (B_TRUE);
1338269Sericheng failed:
1339269Sericheng 	rw_exit(&dsp->ds_lock);
1340269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1341269Sericheng 	return (B_FALSE);
13420Sstevel@tonic-gate }
13430Sstevel@tonic-gate 
13440Sstevel@tonic-gate /*
1345269Sericheng  * DL_NOTIFY_REQ
13460Sstevel@tonic-gate  */
1347269Sericheng static boolean_t
1348269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
13490Sstevel@tonic-gate {
1350269Sericheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)udlp;
1351269Sericheng 	t_uscalar_t	dl_err;
1352269Sericheng 	queue_t		*q = dsp->ds_wq;
1353269Sericheng 	uint_t		note =
1354269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1355269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1356269Sericheng 	    DL_NOTE_PHYS_ADDR |
1357269Sericheng 	    DL_NOTE_LINK_UP |
1358269Sericheng 	    DL_NOTE_LINK_DOWN |
13592311Sseb 	    DL_NOTE_CAPAB_RENEG |
13602311Sseb 	    DL_NOTE_SPEED;
13610Sstevel@tonic-gate 
13621521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
13631521Syz147064 
1364269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1365269Sericheng 		dl_err = DL_BADPRIM;
1366269Sericheng 		goto failed;
1367269Sericheng 	}
13680Sstevel@tonic-gate 
1369269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1370269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1371269Sericheng 		dl_err = DL_OUTSTATE;
1372269Sericheng 		goto failed;
13730Sstevel@tonic-gate 	}
13740Sstevel@tonic-gate 
1375269Sericheng 	/*
1376269Sericheng 	 * Cache the notifications that are being enabled.
1377269Sericheng 	 */
1378269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1379269Sericheng 	rw_exit(&dsp->ds_lock);
1380269Sericheng 	/*
1381269Sericheng 	 * The ACK carries all notifications regardless of which set is
1382269Sericheng 	 * being enabled.
1383269Sericheng 	 */
1384269Sericheng 	dlnotifyack(q, mp, note);
1385269Sericheng 
1386269Sericheng 	/*
1387269Sericheng 	 * Solicit DL_NOTIFY_IND messages for each enabled notification.
1388269Sericheng 	 */
1389269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1390269Sericheng 	if (dsp->ds_notifications != 0) {
1391269Sericheng 		rw_exit(&dsp->ds_lock);
1392269Sericheng 		dld_str_notify_ind(dsp);
1393269Sericheng 	} else {
1394269Sericheng 		rw_exit(&dsp->ds_lock);
1395269Sericheng 	}
1396269Sericheng 	return (B_TRUE);
1397269Sericheng failed:
1398269Sericheng 	rw_exit(&dsp->ds_lock);
1399269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1400269Sericheng 	return (B_FALSE);
14010Sstevel@tonic-gate }
14020Sstevel@tonic-gate 
14030Sstevel@tonic-gate /*
1404269Sericheng  * DL_UINTDATA_REQ
14050Sstevel@tonic-gate  */
1406269Sericheng static boolean_t
1407269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
14080Sstevel@tonic-gate {
1409269Sericheng 	queue_t			*q = dsp->ds_wq;
1410269Sericheng 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)udlp;
1411269Sericheng 	off_t			off;
1412269Sericheng 	size_t			len, size;
1413269Sericheng 	const uint8_t		*addr;
1414269Sericheng 	uint16_t		sap;
1415269Sericheng 	uint_t			addr_length;
14162311Sseb 	mblk_t			*bp, *payload;
1417269Sericheng 	uint32_t		start, stuff, end, value, flags;
1418269Sericheng 	t_uscalar_t		dl_err;
1419269Sericheng 
1420269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1421269Sericheng 
1422269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1423269Sericheng 		dl_err = DL_BADPRIM;
1424269Sericheng 		goto failed;
1425269Sericheng 	}
1426269Sericheng 
1427269Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
1428269Sericheng 		dl_err = DL_OUTSTATE;
1429269Sericheng 		goto failed;
1430269Sericheng 	}
1431269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1432269Sericheng 
1433269Sericheng 	off = dlp->dl_dest_addr_offset;
1434269Sericheng 	len = dlp->dl_dest_addr_length;
1435269Sericheng 
1436269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1437269Sericheng 		dl_err = DL_BADPRIM;
1438269Sericheng 		goto failed;
1439269Sericheng 	}
1440269Sericheng 
1441269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1442269Sericheng 		dl_err = DL_BADADDR;
1443269Sericheng 		goto failed;
1444269Sericheng 	}
1445269Sericheng 
1446269Sericheng 	addr = mp->b_rptr + off;
1447269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1448269Sericheng 
1449269Sericheng 	/*
1450269Sericheng 	 * Check the length of the packet and the block types.
1451269Sericheng 	 */
1452269Sericheng 	size = 0;
14532311Sseb 	payload = mp->b_cont;
14542311Sseb 	for (bp = payload; bp != NULL; bp = bp->b_cont) {
1455269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1456269Sericheng 			goto baddata;
1457269Sericheng 
1458269Sericheng 		size += MBLKL(bp);
1459269Sericheng 	}
1460269Sericheng 
1461269Sericheng 	if (size > dsp->ds_mip->mi_sdu_max)
1462269Sericheng 		goto baddata;
1463269Sericheng 
1464269Sericheng 	/*
1465269Sericheng 	 * Build a packet header.
1466269Sericheng 	 */
14672760Sdg199075 	if ((bp = dls_header(dsp->ds_dc, addr, sap, dlp->dl_priority.dl_max,
14682760Sdg199075 	    &payload)) == NULL) {
1469269Sericheng 		dl_err = DL_BADADDR;
1470269Sericheng 		goto failed;
1471269Sericheng 	}
1472269Sericheng 
1473269Sericheng 	/*
1474269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1475269Sericheng 	 */
1476269Sericheng 	freeb(mp);
1477269Sericheng 
1478269Sericheng 	/*
1479269Sericheng 	 * Transfer the checksum offload information if it is present.
1480269Sericheng 	 */
14812311Sseb 	hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value,
1482269Sericheng 	    &flags);
14832311Sseb 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0);
1484269Sericheng 
1485269Sericheng 	/*
1486269Sericheng 	 * Link the payload onto the new header.
1487269Sericheng 	 */
1488269Sericheng 	ASSERT(bp->b_cont == NULL);
14892311Sseb 	bp->b_cont = payload;
1490269Sericheng 
14912760Sdg199075 	dld_tx_single(dsp, bp);
1492269Sericheng 	rw_exit(&dsp->ds_lock);
1493269Sericheng 	return (B_TRUE);
1494269Sericheng failed:
1495269Sericheng 	rw_exit(&dsp->ds_lock);
1496269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1497269Sericheng 	return (B_FALSE);
1498269Sericheng 
1499269Sericheng baddata:
1500269Sericheng 	rw_exit(&dsp->ds_lock);
1501269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1502269Sericheng 	return (B_FALSE);
1503269Sericheng }
1504269Sericheng 
1505269Sericheng /*
1506269Sericheng  * DL_PASSIVE_REQ
1507269Sericheng  */
1508269Sericheng /* ARGSUSED */
1509269Sericheng static boolean_t
1510269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1511269Sericheng {
1512269Sericheng 	t_uscalar_t dl_err;
1513269Sericheng 
1514269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1515269Sericheng 	/*
1516269Sericheng 	 * If we've already become active by issuing an active primitive,
1517269Sericheng 	 * then it's too late to try to become passive.
1518269Sericheng 	 */
1519269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1520269Sericheng 		dl_err = DL_OUTSTATE;
1521269Sericheng 		goto failed;
1522269Sericheng 	}
1523269Sericheng 
1524269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1525269Sericheng 		dl_err = DL_BADPRIM;
1526269Sericheng 		goto failed;
1527269Sericheng 	}
1528269Sericheng 
1529269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1530269Sericheng 	rw_exit(&dsp->ds_lock);
1531269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1532269Sericheng 	return (B_TRUE);
1533269Sericheng failed:
1534269Sericheng 	rw_exit(&dsp->ds_lock);
1535269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1536269Sericheng 	return (B_FALSE);
1537269Sericheng }
1538269Sericheng 
1539269Sericheng 
1540269Sericheng /*
1541269Sericheng  * Catch-all handler.
1542269Sericheng  */
1543269Sericheng static boolean_t
1544269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp)
1545269Sericheng {
1546269Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1547269Sericheng 	return (B_FALSE);
15480Sstevel@tonic-gate }
15490Sstevel@tonic-gate 
15500Sstevel@tonic-gate static void
15510Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp)
15520Sstevel@tonic-gate {
15530Sstevel@tonic-gate 	mac_handle_t	mh;
15540Sstevel@tonic-gate 
15551353Sericheng 	ASSERT(dsp->ds_pending_req != NULL || RW_WRITE_HELD(&dsp->ds_lock));
1556269Sericheng 
15570Sstevel@tonic-gate 	if (!dsp->ds_polling)
15580Sstevel@tonic-gate 		return;
15590Sstevel@tonic-gate 
15600Sstevel@tonic-gate 	/*
15610Sstevel@tonic-gate 	 * It should be impossible to enable raw mode if polling is turned on.
15620Sstevel@tonic-gate 	 */
15630Sstevel@tonic-gate 	ASSERT(dsp->ds_mode != DLD_RAW);
15640Sstevel@tonic-gate 
15650Sstevel@tonic-gate 	/*
15660Sstevel@tonic-gate 	 * Reset the resource_add callback.
15670Sstevel@tonic-gate 	 */
15680Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
15690Sstevel@tonic-gate 	mac_resource_set(mh, NULL, NULL);
15701184Skrgopi 	mac_resources(mh);
15710Sstevel@tonic-gate 
15720Sstevel@tonic-gate 	/*
15730Sstevel@tonic-gate 	 * Set receive function back to default.
15740Sstevel@tonic-gate 	 */
15750Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ?
15760Sstevel@tonic-gate 	    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
15770Sstevel@tonic-gate 
15780Sstevel@tonic-gate 	/*
15790Sstevel@tonic-gate 	 * Note that polling is disabled.
15800Sstevel@tonic-gate 	 */
15810Sstevel@tonic-gate 	dsp->ds_polling = B_FALSE;
15820Sstevel@tonic-gate }
15830Sstevel@tonic-gate 
15840Sstevel@tonic-gate static boolean_t
15851184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp)
15860Sstevel@tonic-gate {
15870Sstevel@tonic-gate 	mac_handle_t	mh;
15880Sstevel@tonic-gate 
1589269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
15900Sstevel@tonic-gate 	ASSERT(!dsp->ds_polling);
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate 	/*
15930Sstevel@tonic-gate 	 * We cannot enable polling if raw mode
15940Sstevel@tonic-gate 	 * has been enabled.
15950Sstevel@tonic-gate 	 */
15960Sstevel@tonic-gate 	if (dsp->ds_mode == DLD_RAW)
15970Sstevel@tonic-gate 		return (B_FALSE);
15980Sstevel@tonic-gate 
15990Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
16000Sstevel@tonic-gate 
16010Sstevel@tonic-gate 	/*
16020Sstevel@tonic-gate 	 * Register resources.
16030Sstevel@tonic-gate 	 */
16041184Skrgopi 	mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add,
16051184Skrgopi 	    (void *)pollp->dls_rx_handle);
16060Sstevel@tonic-gate 	mac_resources(mh);
16070Sstevel@tonic-gate 
16080Sstevel@tonic-gate 	/*
16090Sstevel@tonic-gate 	 * Set the receive function.
16100Sstevel@tonic-gate 	 */
16111184Skrgopi 	dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx,
16121184Skrgopi 	    (void *)pollp->dls_rx_handle);
16130Sstevel@tonic-gate 
16140Sstevel@tonic-gate 	/*
16150Sstevel@tonic-gate 	 * Note that polling is enabled. This prevents further DLIOCHDRINFO
16160Sstevel@tonic-gate 	 * ioctls from overwriting the receive function pointer.
16170Sstevel@tonic-gate 	 */
16180Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
16190Sstevel@tonic-gate 	return (B_TRUE);
16200Sstevel@tonic-gate }
16210Sstevel@tonic-gate 
16221184Skrgopi static void
16231184Skrgopi proto_soft_ring_disable(dld_str_t *dsp)
16241184Skrgopi {
16251184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16261184Skrgopi 
16271184Skrgopi 	if (!dsp->ds_soft_ring)
16281184Skrgopi 		return;
16291184Skrgopi 
16301184Skrgopi 	/*
16311184Skrgopi 	 * It should be impossible to enable raw mode if soft_ring is turned on.
16321184Skrgopi 	 */
16331184Skrgopi 	ASSERT(dsp->ds_mode != DLD_RAW);
16341184Skrgopi 	proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE);
16351184Skrgopi 	/*
16361184Skrgopi 	 * Note that fanout is disabled.
16371184Skrgopi 	 */
16381184Skrgopi 	dsp->ds_soft_ring = B_FALSE;
16391184Skrgopi }
16401184Skrgopi 
16411184Skrgopi static boolean_t
16421184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp)
16431184Skrgopi {
16441184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16451184Skrgopi 	ASSERT(!dsp->ds_soft_ring);
16461184Skrgopi 
16471184Skrgopi 	/*
16481184Skrgopi 	 * We cannot enable soft_ring if raw mode
16491184Skrgopi 	 * has been enabled.
16501184Skrgopi 	 */
16511184Skrgopi 	if (dsp->ds_mode == DLD_RAW)
16521184Skrgopi 		return (B_FALSE);
16531184Skrgopi 
16541184Skrgopi 	if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE)
16551184Skrgopi 		return (B_FALSE);
16561184Skrgopi 
16571184Skrgopi 	dsp->ds_soft_ring = B_TRUE;
16581184Skrgopi 	return (B_TRUE);
16591184Skrgopi }
16601184Skrgopi 
16611184Skrgopi static void
16621184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type)
16631184Skrgopi {
16641184Skrgopi 	dls_rx_t	rx;
16651184Skrgopi 
16661184Skrgopi 	if (type == SOFT_RING_NONE) {
16671184Skrgopi 		rx = (dsp->ds_mode == DLD_FASTPATH) ?
16681184Skrgopi 			    dld_str_rx_fastpath : dld_str_rx_unitdata;
16691184Skrgopi 	} else {
16701184Skrgopi 		rx = (dls_rx_t)dls_ether_soft_ring_fanout;
16711184Skrgopi 	}
16721184Skrgopi 	dls_soft_ring_rx_set(dsp->ds_dc, rx, dsp, type);
16731184Skrgopi }
16741184Skrgopi 
16750Sstevel@tonic-gate /*
16760Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
16770Sstevel@tonic-gate  */
1678269Sericheng static boolean_t
1679269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
16800Sstevel@tonic-gate {
16810Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
16820Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
16830Sstevel@tonic-gate 	size_t			subsize;
16841184Skrgopi 	dl_capab_dls_t		poll;
16851184Skrgopi 	dl_capab_dls_t	soft_ring;
16860Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
16870Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
16880Sstevel@tonic-gate 	uint8_t			*ptr;
16892311Sseb 	boolean_t		cksum_cap;
16900Sstevel@tonic-gate 	boolean_t		poll_cap;
1691269Sericheng 	queue_t			*q = dsp->ds_wq;
1692269Sericheng 	mblk_t			*mp1;
1693269Sericheng 
1694269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
16950Sstevel@tonic-gate 
16960Sstevel@tonic-gate 	/*
16970Sstevel@tonic-gate 	 * Initially assume no capabilities.
16980Sstevel@tonic-gate 	 */
16990Sstevel@tonic-gate 	subsize = 0;
17000Sstevel@tonic-gate 
17011555Skrgopi 	/*
17021555Skrgopi 	 * Advertize soft ring capability if
17031555Skrgopi 	 * VLAN_ID_NONE for GLDv3 drivers
17041555Skrgopi 	 */
17051555Skrgopi 	if (dsp->ds_vid == VLAN_ID_NONE)
17061555Skrgopi 		subsize += sizeof (dl_capability_sub_t) +
17071555Skrgopi 				    sizeof (dl_capab_dls_t);
17081184Skrgopi 
17090Sstevel@tonic-gate 	/*
17100Sstevel@tonic-gate 	 * Check if polling can be enabled on this interface.
17110Sstevel@tonic-gate 	 * If advertising DL_CAPAB_POLL has not been explicitly disabled
17120Sstevel@tonic-gate 	 * then reserve space for that capability.
17130Sstevel@tonic-gate 	 */
17142311Sseb 	poll_cap = (mac_capab_get(dsp->ds_mh, MAC_CAPAB_POLL, NULL) &&
17150Sstevel@tonic-gate 	    !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE));
17160Sstevel@tonic-gate 	if (poll_cap) {
17170Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17181184Skrgopi 		    sizeof (dl_capab_dls_t);
17190Sstevel@tonic-gate 	}
17200Sstevel@tonic-gate 
17210Sstevel@tonic-gate 	/*
17220Sstevel@tonic-gate 	 * If the MAC interface supports checksum offload then reserve
17230Sstevel@tonic-gate 	 * space for the DL_CAPAB_HCKSUM capability.
17240Sstevel@tonic-gate 	 */
17252311Sseb 	if (cksum_cap = mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
17262311Sseb 	    &hcksum.hcksum_txflags)) {
17270Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17280Sstevel@tonic-gate 		    sizeof (dl_capab_hcksum_t);
17290Sstevel@tonic-gate 	}
17300Sstevel@tonic-gate 
17310Sstevel@tonic-gate 	/*
17320Sstevel@tonic-gate 	 * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then
17330Sstevel@tonic-gate 	 * reserve space for it.
17340Sstevel@tonic-gate 	 */
17350Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
17360Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17370Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
17380Sstevel@tonic-gate 	}
17390Sstevel@tonic-gate 
17400Sstevel@tonic-gate 	/*
1741269Sericheng 	 * If there are no capabilities to advertise or if we
1742269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
17430Sstevel@tonic-gate 	 */
17441184Skrgopi 	if ((mp1 = reallocb(mp,
1745269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1746269Sericheng 		rw_exit(&dsp->ds_lock);
1747269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1748269Sericheng 		return (B_FALSE);
17490Sstevel@tonic-gate 	}
17500Sstevel@tonic-gate 
1751269Sericheng 	mp = mp1;
1752269Sericheng 	DB_TYPE(mp) = M_PROTO;
1753269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1754269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
17550Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
17560Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
17570Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
17580Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
17590Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
17600Sstevel@tonic-gate 
17610Sstevel@tonic-gate 	/*
17620Sstevel@tonic-gate 	 * IP polling interface.
17630Sstevel@tonic-gate 	 */
17640Sstevel@tonic-gate 	if (poll_cap) {
17650Sstevel@tonic-gate 		/*
1766269Sericheng 		 * Attempt to disable just in case this is a re-negotiation;
1767269Sericheng 		 * we need to become writer before doing so.
17680Sstevel@tonic-gate 		 */
1769269Sericheng 		if (!rw_tryupgrade(&dsp->ds_lock)) {
1770269Sericheng 			rw_exit(&dsp->ds_lock);
1771269Sericheng 			rw_enter(&dsp->ds_lock, RW_WRITER);
1772269Sericheng 		}
17730Sstevel@tonic-gate 
1774269Sericheng 		/*
1775269Sericheng 		 * Check if polling state has changed after we re-acquired
1776269Sericheng 		 * the lock above, so that we don't mis-advertise it.
1777269Sericheng 		 */
17782311Sseb 		poll_cap = !(dld_opt & DLD_OPT_NO_POLL) &&
17792311Sseb 		    (dsp->ds_vid == VLAN_ID_NONE);
17800Sstevel@tonic-gate 
1781269Sericheng 		if (!poll_cap) {
1782269Sericheng 			int poll_capab_size;
1783269Sericheng 
1784269Sericheng 			rw_downgrade(&dsp->ds_lock);
1785269Sericheng 
1786269Sericheng 			poll_capab_size = sizeof (dl_capability_sub_t) +
17871184Skrgopi 			    sizeof (dl_capab_dls_t);
17880Sstevel@tonic-gate 
1789269Sericheng 			mp->b_wptr -= poll_capab_size;
1790269Sericheng 			subsize -= poll_capab_size;
1791269Sericheng 			dlap->dl_sub_length = subsize;
1792269Sericheng 		} else {
1793269Sericheng 			proto_poll_disable(dsp);
1794269Sericheng 
1795269Sericheng 			rw_downgrade(&dsp->ds_lock);
1796269Sericheng 
1797269Sericheng 			dlsp = (dl_capability_sub_t *)ptr;
1798269Sericheng 
1799269Sericheng 			dlsp->dl_cap = DL_CAPAB_POLL;
18001184Skrgopi 			dlsp->dl_length = sizeof (dl_capab_dls_t);
1801269Sericheng 			ptr += sizeof (dl_capability_sub_t);
18020Sstevel@tonic-gate 
18031184Skrgopi 			bzero(&poll, sizeof (dl_capab_dls_t));
18041184Skrgopi 			poll.dls_version = POLL_VERSION_1;
18051184Skrgopi 			poll.dls_flags = POLL_CAPABLE;
18061184Skrgopi 			poll.dls_tx_handle = (uintptr_t)dsp;
18071184Skrgopi 			poll.dls_tx = (uintptr_t)str_mdata_fastpath_put;
1808269Sericheng 
18091184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
18101184Skrgopi 			bcopy(&poll, ptr, sizeof (dl_capab_dls_t));
18111184Skrgopi 			ptr += sizeof (dl_capab_dls_t);
1812269Sericheng 		}
18130Sstevel@tonic-gate 	}
18140Sstevel@tonic-gate 
1815269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
1816269Sericheng 
18171555Skrgopi 	if (dsp->ds_vid == VLAN_ID_NONE) {
18181555Skrgopi 		dlsp = (dl_capability_sub_t *)ptr;
18191184Skrgopi 
18201555Skrgopi 		dlsp->dl_cap = DL_CAPAB_SOFT_RING;
18211555Skrgopi 		dlsp->dl_length = sizeof (dl_capab_dls_t);
18221555Skrgopi 		ptr += sizeof (dl_capability_sub_t);
18231184Skrgopi 
18241555Skrgopi 		bzero(&soft_ring, sizeof (dl_capab_dls_t));
18251555Skrgopi 		soft_ring.dls_version = SOFT_RING_VERSION_1;
18261555Skrgopi 		soft_ring.dls_flags = SOFT_RING_CAPABLE;
18271555Skrgopi 		soft_ring.dls_tx_handle = (uintptr_t)dsp;
18281555Skrgopi 		soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put;
18291555Skrgopi 		soft_ring.dls_ring_change_status =
18301555Skrgopi 		    (uintptr_t)proto_change_soft_ring_fanout;
18311555Skrgopi 		soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind;
18321555Skrgopi 		soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind;
18331184Skrgopi 
18341555Skrgopi 		dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
18351555Skrgopi 		bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t));
18361555Skrgopi 		ptr += sizeof (dl_capab_dls_t);
18371555Skrgopi 	}
18381184Skrgopi 
18390Sstevel@tonic-gate 	/*
18400Sstevel@tonic-gate 	 * TCP/IP checksum offload.
18410Sstevel@tonic-gate 	 */
18422311Sseb 	if (cksum_cap) {
18430Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18440Sstevel@tonic-gate 
18450Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
18460Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
18470Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18480Sstevel@tonic-gate 
18490Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
18500Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
18510Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
18520Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
18530Sstevel@tonic-gate 	}
18540Sstevel@tonic-gate 
18550Sstevel@tonic-gate 	/*
18560Sstevel@tonic-gate 	 * Zero copy
18570Sstevel@tonic-gate 	 */
18580Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
18590Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18600Sstevel@tonic-gate 
18610Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
18620Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
18630Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18640Sstevel@tonic-gate 
18650Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
18660Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
18670Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
18680Sstevel@tonic-gate 
18690Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
18700Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
18710Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
18720Sstevel@tonic-gate 	}
18730Sstevel@tonic-gate 
18740Sstevel@tonic-gate 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1875269Sericheng 
1876269Sericheng 	rw_exit(&dsp->ds_lock);
1877269Sericheng 	qreply(q, mp);
1878269Sericheng 	return (B_TRUE);
18790Sstevel@tonic-gate }
1880