xref: /illumos-gate/usr/src/uts/common/io/dld/dld_proto.c (revision 115f9ea8)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
56b6515e2Sericheng  * Common Development and Distribution License (the "License").
66b6515e2Sericheng  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
229056fcebSCathy Zhou  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23cabb4db9SDan McDonald  * Copyright 2012, Nexenta Systems, Inc. All rights reserved.
2415c07adcSJohn Levon  * Copyright (c) 2018, Joyent, Inc.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * Data-Link Driver
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
317c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
32da14cebeSEric Cheng #include <sys/strsun.h>
337c478bd9Sstevel@tonic-gate #include <sys/vlan.h>
347c478bd9Sstevel@tonic-gate #include <sys/dld_impl.h>
35da14cebeSEric Cheng #include <sys/mac_client.h>
36da14cebeSEric Cheng #include <sys/mac_client_impl.h>
37da14cebeSEric Cheng #include <sys/mac_client_priv.h>
387c478bd9Sstevel@tonic-gate 
39da14cebeSEric Cheng typedef void proto_reqfunc_t(dld_str_t *, mblk_t *);
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
427c478bd9Sstevel@tonic-gate     proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
437c478bd9Sstevel@tonic-gate     proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
447c478bd9Sstevel@tonic-gate     proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
45d62bc4baSyz     proto_notify_req, proto_passive_req;
467c478bd9Sstevel@tonic-gate 
47da14cebeSEric Cheng static void proto_capability_advertise(dld_str_t *, mblk_t *);
48da14cebeSEric Cheng static int dld_capab_poll_disable(dld_str_t *, dld_capab_poll_t *);
498d4cf8d8S static boolean_t check_mod_above(queue_t *, const char *);
504b46d1efSkrgopi 
517c478bd9Sstevel@tonic-gate #define	DL_ACK_PENDING(state) \
527c478bd9Sstevel@tonic-gate 	((state) == DL_ATTACH_PENDING || \
537c478bd9Sstevel@tonic-gate 	(state) == DL_DETACH_PENDING || \
547c478bd9Sstevel@tonic-gate 	(state) == DL_BIND_PENDING || \
557c478bd9Sstevel@tonic-gate 	(state) == DL_UNBIND_PENDING)
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate /*
58210db224Sericheng  * Process a DLPI protocol message.
59210db224Sericheng  * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ,
60210db224Sericheng  * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an
61210db224Sericheng  * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t
62210db224Sericheng  * as 'passive' and forbids it from being subsequently made 'active'
63210db224Sericheng  * by the above primitives.
647c478bd9Sstevel@tonic-gate  */
657c478bd9Sstevel@tonic-gate void
dld_proto(dld_str_t * dsp,mblk_t * mp)66da14cebeSEric Cheng dld_proto(dld_str_t *dsp, mblk_t *mp)
677c478bd9Sstevel@tonic-gate {
687c478bd9Sstevel@tonic-gate 	t_uscalar_t		prim;
697c478bd9Sstevel@tonic-gate 
70da14cebeSEric Cheng 	if (MBLKL(mp) < sizeof (t_uscalar_t)) {
71da14cebeSEric Cheng 		freemsg(mp);
72da14cebeSEric Cheng 		return;
73da14cebeSEric Cheng 	}
74da14cebeSEric Cheng 	prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
757c478bd9Sstevel@tonic-gate 
76210db224Sericheng 	switch (prim) {
77210db224Sericheng 	case DL_INFO_REQ:
78da14cebeSEric Cheng 		proto_info_req(dsp, mp);
79210db224Sericheng 		break;
80210db224Sericheng 	case DL_BIND_REQ:
81da14cebeSEric Cheng 		proto_bind_req(dsp, mp);
82210db224Sericheng 		break;
83210db224Sericheng 	case DL_UNBIND_REQ:
84da14cebeSEric Cheng 		proto_unbind_req(dsp, mp);
85da14cebeSEric Cheng 		break;
86da14cebeSEric Cheng 	case DL_UNITDATA_REQ:
87da14cebeSEric Cheng 		proto_unitdata_req(dsp, mp);
88210db224Sericheng 		break;
89210db224Sericheng 	case DL_UDQOS_REQ:
90da14cebeSEric Cheng 		proto_udqos_req(dsp, mp);
91210db224Sericheng 		break;
92210db224Sericheng 	case DL_ATTACH_REQ:
93da14cebeSEric Cheng 		proto_attach_req(dsp, mp);
94210db224Sericheng 		break;
95210db224Sericheng 	case DL_DETACH_REQ:
96da14cebeSEric Cheng 		proto_detach_req(dsp, mp);
97210db224Sericheng 		break;
98210db224Sericheng 	case DL_ENABMULTI_REQ:
99da14cebeSEric Cheng 		proto_enabmulti_req(dsp, mp);
100210db224Sericheng 		break;
101210db224Sericheng 	case DL_DISABMULTI_REQ:
102da14cebeSEric Cheng 		proto_disabmulti_req(dsp, mp);
103210db224Sericheng 		break;
104210db224Sericheng 	case DL_PROMISCON_REQ:
105da14cebeSEric Cheng 		proto_promiscon_req(dsp, mp);
106210db224Sericheng 		break;
107210db224Sericheng 	case DL_PROMISCOFF_REQ:
108da14cebeSEric Cheng 		proto_promiscoff_req(dsp, mp);
109210db224Sericheng 		break;
110210db224Sericheng 	case DL_PHYS_ADDR_REQ:
111da14cebeSEric Cheng 		proto_physaddr_req(dsp, mp);
112210db224Sericheng 		break;
113210db224Sericheng 	case DL_SET_PHYS_ADDR_REQ:
114da14cebeSEric Cheng 		proto_setphysaddr_req(dsp, mp);
115210db224Sericheng 		break;
116210db224Sericheng 	case DL_NOTIFY_REQ:
117da14cebeSEric Cheng 		proto_notify_req(dsp, mp);
118210db224Sericheng 		break;
119210db224Sericheng 	case DL_CAPABILITY_REQ:
120da14cebeSEric Cheng 		proto_capability_req(dsp, mp);
121210db224Sericheng 		break;
122210db224Sericheng 	case DL_PASSIVE_REQ:
123da14cebeSEric Cheng 		proto_passive_req(dsp, mp);
124210db224Sericheng 		break;
125210db224Sericheng 	default:
126da14cebeSEric Cheng 		proto_req(dsp, mp);
127210db224Sericheng 		break;
128210db224Sericheng 	}
129210db224Sericheng }
130210db224Sericheng 
131210db224Sericheng #define	NEG(x)	-(x)
132210db224Sericheng typedef struct dl_info_ack_wrapper {
133210db224Sericheng 	dl_info_ack_t		dl_info;
134ba2e4443Sseb 	uint8_t			dl_addr[MAXMACADDRLEN + sizeof (uint16_t)];
135ba2e4443Sseb 	uint8_t			dl_brdcst_addr[MAXMACADDRLEN];
136210db224Sericheng 	dl_qos_cl_range1_t	dl_qos_range1;
137210db224Sericheng 	dl_qos_cl_sel1_t	dl_qos_sel1;
138210db224Sericheng } dl_info_ack_wrapper_t;
139210db224Sericheng 
140210db224Sericheng /*
141210db224Sericheng  * DL_INFO_REQ
142210db224Sericheng  */
143da14cebeSEric Cheng static void
proto_info_req(dld_str_t * dsp,mblk_t * mp)144da14cebeSEric Cheng proto_info_req(dld_str_t *dsp, mblk_t *mp)
145210db224Sericheng {
146210db224Sericheng 	dl_info_ack_wrapper_t	*dlwp;
147210db224Sericheng 	dl_info_ack_t		*dlp;
148210db224Sericheng 	dl_qos_cl_sel1_t	*selp;
149210db224Sericheng 	dl_qos_cl_range1_t	*rangep;
150210db224Sericheng 	uint8_t			*addr;
151210db224Sericheng 	uint8_t			*brdcst_addr;
152210db224Sericheng 	uint_t			addr_length;
153210db224Sericheng 	uint_t			sap_length;
154210db224Sericheng 	mac_info_t		minfo;
155210db224Sericheng 	mac_info_t		*minfop;
156210db224Sericheng 	queue_t			*q = dsp->ds_wq;
157210db224Sericheng 
1587c478bd9Sstevel@tonic-gate 	/*
159210db224Sericheng 	 * Swap the request message for one large enough to contain the
160210db224Sericheng 	 * wrapper structure defined above.
1617c478bd9Sstevel@tonic-gate 	 */
162210db224Sericheng 	if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
163210db224Sericheng 	    M_PCPROTO, 0)) == NULL)
164da14cebeSEric Cheng 		return;
1657c478bd9Sstevel@tonic-gate 
166210db224Sericheng 	bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
167210db224Sericheng 	dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
1687c478bd9Sstevel@tonic-gate 
169210db224Sericheng 	dlp = &(dlwp->dl_info);
170210db224Sericheng 	ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
1717c478bd9Sstevel@tonic-gate 
172210db224Sericheng 	dlp->dl_primitive = DL_INFO_ACK;
173210db224Sericheng 
174210db224Sericheng 	/*
175210db224Sericheng 	 * Set up the sub-structure pointers.
176210db224Sericheng 	 */
177210db224Sericheng 	addr = dlwp->dl_addr;
178210db224Sericheng 	brdcst_addr = dlwp->dl_brdcst_addr;
179210db224Sericheng 	rangep = &(dlwp->dl_qos_range1);
180210db224Sericheng 	selp = &(dlwp->dl_qos_sel1);
1817c478bd9Sstevel@tonic-gate 
182210db224Sericheng 	/*
183210db224Sericheng 	 * This driver supports only version 2 connectionless DLPI provider
184210db224Sericheng 	 * nodes.
185210db224Sericheng 	 */
186210db224Sericheng 	dlp->dl_service_mode = DL_CLDLS;
187210db224Sericheng 	dlp->dl_version = DL_VERSION_2;
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	/*
190210db224Sericheng 	 * Set the style of the provider
1917c478bd9Sstevel@tonic-gate 	 */
192210db224Sericheng 	dlp->dl_provider_style = dsp->ds_style;
193210db224Sericheng 	ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
194210db224Sericheng 	    dlp->dl_provider_style == DL_STYLE2);
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	/*
197210db224Sericheng 	 * Set the current DLPI state.
1987c478bd9Sstevel@tonic-gate 	 */
199210db224Sericheng 	dlp->dl_current_state = dsp->ds_dlstate;
2007c478bd9Sstevel@tonic-gate 
201210db224Sericheng 	/*
202210db224Sericheng 	 * Gratuitously set the media type. This is to deal with modules
203210db224Sericheng 	 * that assume the media type is known prior to DL_ATTACH_REQ
204210db224Sericheng 	 * being completed.
205210db224Sericheng 	 */
206210db224Sericheng 	dlp->dl_mac_type = DL_ETHER;
2077c478bd9Sstevel@tonic-gate 
208210db224Sericheng 	/*
209210db224Sericheng 	 * If the stream is not at least attached we try to retrieve the
210210db224Sericheng 	 * mac_info using mac_info_get()
211210db224Sericheng 	 */
212210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
213210db224Sericheng 	    dsp->ds_dlstate == DL_ATTACH_PENDING ||
214210db224Sericheng 	    dsp->ds_dlstate == DL_DETACH_PENDING) {
215210db224Sericheng 		if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) {
216210db224Sericheng 			/*
217210db224Sericheng 			 * Cannot find mac_info. giving up.
218210db224Sericheng 			 */
219210db224Sericheng 			goto done;
220210db224Sericheng 		}
221210db224Sericheng 		minfop = &minfo;
222210db224Sericheng 	} else {
223210db224Sericheng 		minfop = (mac_info_t *)dsp->ds_mip;
224e7801d59Ssowmini 		/* We can only get the sdu if we're attached. */
225e7801d59Ssowmini 		mac_sdu_get(dsp->ds_mh, &dlp->dl_min_sdu, &dlp->dl_max_sdu);
2267c478bd9Sstevel@tonic-gate 	}
2277c478bd9Sstevel@tonic-gate 
228210db224Sericheng 	/*
229210db224Sericheng 	 * Set the media type (properly this time).
230210db224Sericheng 	 */
2310ba2cbe9Sxc 	if (dsp->ds_native)
2320ba2cbe9Sxc 		dlp->dl_mac_type = minfop->mi_nativemedia;
2330ba2cbe9Sxc 	else
2340ba2cbe9Sxc 		dlp->dl_mac_type = minfop->mi_media;
2357c478bd9Sstevel@tonic-gate 
236210db224Sericheng 	/*
237210db224Sericheng 	 * Set the DLSAP length. We only support 16 bit values and they
238210db224Sericheng 	 * appear after the MAC address portion of DLSAP addresses.
239210db224Sericheng 	 */
240210db224Sericheng 	sap_length = sizeof (uint16_t);
241210db224Sericheng 	dlp->dl_sap_length = NEG(sap_length);
2427c478bd9Sstevel@tonic-gate 
243210db224Sericheng 	addr_length = minfop->mi_addr_length;
2447c478bd9Sstevel@tonic-gate 
245210db224Sericheng 	/*
246210db224Sericheng 	 * Copy in the media broadcast address.
247210db224Sericheng 	 */
248ba2e4443Sseb 	if (minfop->mi_brdcst_addr != NULL) {
249ba2e4443Sseb 		dlp->dl_brdcst_addr_offset =
250ba2e4443Sseb 		    (uintptr_t)brdcst_addr - (uintptr_t)dlp;
251ba2e4443Sseb 		bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
252ba2e4443Sseb 		dlp->dl_brdcst_addr_length = addr_length;
253ba2e4443Sseb 	}
2547c478bd9Sstevel@tonic-gate 
255e75f0919SSebastien Roy 	/* Only VLAN links and links that have a normal tag mode support QOS. */
256a2da5912SSebastien Roy 	if ((dsp->ds_mch != NULL &&
257a2da5912SSebastien Roy 	    mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE) ||
258a2da5912SSebastien Roy 	    (dsp->ds_dlp != NULL &&
259a2da5912SSebastien Roy 	    dsp->ds_dlp->dl_tagmode == LINK_TAGMODE_NORMAL)) {
260e75f0919SSebastien Roy 		dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
261e75f0919SSebastien Roy 		dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
262210db224Sericheng 
263e75f0919SSebastien Roy 		rangep->dl_qos_type = DL_QOS_CL_RANGE1;
264e75f0919SSebastien Roy 		rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
265e75f0919SSebastien Roy 		rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
266e75f0919SSebastien Roy 		rangep->dl_protection.dl_min = DL_UNKNOWN;
267e75f0919SSebastien Roy 		rangep->dl_protection.dl_max = DL_UNKNOWN;
268e75f0919SSebastien Roy 		rangep->dl_residual_error = DL_UNKNOWN;
2697c478bd9Sstevel@tonic-gate 
270e75f0919SSebastien Roy 		/*
271e75f0919SSebastien Roy 		 * Specify the supported range of priorities.
272e75f0919SSebastien Roy 		 */
273e75f0919SSebastien Roy 		rangep->dl_priority.dl_min = 0;
274e75f0919SSebastien Roy 		rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
275210db224Sericheng 
276e75f0919SSebastien Roy 		dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
277e75f0919SSebastien Roy 		dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
278210db224Sericheng 
279e75f0919SSebastien Roy 		selp->dl_qos_type = DL_QOS_CL_SEL1;
280e75f0919SSebastien Roy 		selp->dl_trans_delay = DL_UNKNOWN;
281e75f0919SSebastien Roy 		selp->dl_protection = DL_UNKNOWN;
282e75f0919SSebastien Roy 		selp->dl_residual_error = DL_UNKNOWN;
2837c478bd9Sstevel@tonic-gate 
284e75f0919SSebastien Roy 		/*
285e75f0919SSebastien Roy 		 * Specify the current priority (which can be changed by
286e75f0919SSebastien Roy 		 * the DL_UDQOS_REQ primitive).
287e75f0919SSebastien Roy 		 */
288e75f0919SSebastien Roy 		selp->dl_priority = dsp->ds_pri;
289e75f0919SSebastien Roy 	}
2907c478bd9Sstevel@tonic-gate 
291210db224Sericheng 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
292210db224Sericheng 	if (dsp->ds_dlstate == DL_IDLE) {
293210db224Sericheng 		/*
294210db224Sericheng 		 * The stream is bound. Therefore we can formulate a valid
295210db224Sericheng 		 * DLSAP address.
296210db224Sericheng 		 */
297210db224Sericheng 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
298ba2e4443Sseb 		if (addr_length > 0)
299da14cebeSEric Cheng 			mac_unicast_primary_get(dsp->ds_mh, addr);
300da14cebeSEric Cheng 
301210db224Sericheng 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3027c478bd9Sstevel@tonic-gate 	}
3037c478bd9Sstevel@tonic-gate 
304210db224Sericheng done:
30556f33205SJonathan Adams 	IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0);
30656f33205SJonathan Adams 	IMPLY(dlp->dl_qos_range_offset != 0,
30756f33205SJonathan Adams 	    dlp->dl_qos_range_length != 0);
30856f33205SJonathan Adams 	IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0);
30956f33205SJonathan Adams 	IMPLY(dlp->dl_brdcst_addr_offset != 0,
31056f33205SJonathan Adams 	    dlp->dl_brdcst_addr_length != 0);
311210db224Sericheng 
312210db224Sericheng 	qreply(q, mp);
3137c478bd9Sstevel@tonic-gate }
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate /*
3167c478bd9Sstevel@tonic-gate  * DL_ATTACH_REQ
3177c478bd9Sstevel@tonic-gate  */
318da14cebeSEric Cheng static void
proto_attach_req(dld_str_t * dsp,mblk_t * mp)319da14cebeSEric Cheng proto_attach_req(dld_str_t *dsp, mblk_t *mp)
3207c478bd9Sstevel@tonic-gate {
321da14cebeSEric Cheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)mp->b_rptr;
322210db224Sericheng 	int		err = 0;
323210db224Sericheng 	t_uscalar_t	dl_err;
324210db224Sericheng 	queue_t		*q = dsp->ds_wq;
3257c478bd9Sstevel@tonic-gate 
326210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
327210db224Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
328210db224Sericheng 		dl_err = DL_BADPRIM;
329210db224Sericheng 		goto failed;
3307c478bd9Sstevel@tonic-gate 	}
3317c478bd9Sstevel@tonic-gate 
332210db224Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
333210db224Sericheng 		dl_err = DL_OUTSTATE;
334210db224Sericheng 		goto failed;
3357c478bd9Sstevel@tonic-gate 	}
3367c478bd9Sstevel@tonic-gate 
337210db224Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
3387c478bd9Sstevel@tonic-gate 
339210db224Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
340210db224Sericheng 	if (err != 0) {
341210db224Sericheng 		switch (err) {
342210db224Sericheng 		case ENOENT:
343210db224Sericheng 			dl_err = DL_BADPPA;
344210db224Sericheng 			err = 0;
345210db224Sericheng 			break;
346210db224Sericheng 		default:
347210db224Sericheng 			dl_err = DL_SYSERR;
348210db224Sericheng 			break;
349210db224Sericheng 		}
350210db224Sericheng 		dsp->ds_dlstate = DL_UNATTACHED;
351210db224Sericheng 		goto failed;
3527c478bd9Sstevel@tonic-gate 	}
353210db224Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
354210db224Sericheng 	dlokack(q, mp, DL_ATTACH_REQ);
355da14cebeSEric Cheng 	return;
356da14cebeSEric Cheng 
357210db224Sericheng failed:
358210db224Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
3597c478bd9Sstevel@tonic-gate }
3607c478bd9Sstevel@tonic-gate 
361da14cebeSEric Cheng /*
362da14cebeSEric Cheng  * DL_DETACH_REQ
363da14cebeSEric Cheng  */
364da14cebeSEric Cheng static void
proto_detach_req(dld_str_t * dsp,mblk_t * mp)365da14cebeSEric Cheng proto_detach_req(dld_str_t *dsp, mblk_t *mp)
3667c478bd9Sstevel@tonic-gate {
367210db224Sericheng 	queue_t		*q = dsp->ds_wq;
368210db224Sericheng 	t_uscalar_t	dl_err;
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
371210db224Sericheng 		dl_err = DL_BADPRIM;
372210db224Sericheng 		goto failed;
3737c478bd9Sstevel@tonic-gate 	}
3747c478bd9Sstevel@tonic-gate 
375210db224Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
376210db224Sericheng 		dl_err = DL_OUTSTATE;
377210db224Sericheng 		goto failed;
378210db224Sericheng 	}
379210db224Sericheng 
380210db224Sericheng 	if (dsp->ds_style == DL_STYLE1) {
381210db224Sericheng 		dl_err = DL_BADPRIM;
382210db224Sericheng 		goto failed;
3837c478bd9Sstevel@tonic-gate 	}
3847c478bd9Sstevel@tonic-gate 
385da14cebeSEric Cheng 	ASSERT(dsp->ds_datathr_cnt == 0);
3867c478bd9Sstevel@tonic-gate 	dsp->ds_dlstate = DL_DETACH_PENDING;
3877c478bd9Sstevel@tonic-gate 
388da14cebeSEric Cheng 	dld_str_detach(dsp);
389d62bc4baSyz 	dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
390da14cebeSEric Cheng 	return;
391da14cebeSEric Cheng 
392210db224Sericheng failed:
393210db224Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
3947c478bd9Sstevel@tonic-gate }
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate /*
3977c478bd9Sstevel@tonic-gate  * DL_BIND_REQ
3987c478bd9Sstevel@tonic-gate  */
399da14cebeSEric Cheng static void
proto_bind_req(dld_str_t * dsp,mblk_t * mp)400da14cebeSEric Cheng proto_bind_req(dld_str_t *dsp, mblk_t *mp)
4017c478bd9Sstevel@tonic-gate {
402da14cebeSEric Cheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)mp->b_rptr;
403210db224Sericheng 	int		err = 0;
4046f45d2aeSyz 	uint8_t		dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)];
4056f45d2aeSyz 	uint_t		dlsap_addr_length;
406210db224Sericheng 	t_uscalar_t	dl_err;
407210db224Sericheng 	t_scalar_t	sap;
408210db224Sericheng 	queue_t		*q = dsp->ds_wq;
409da14cebeSEric Cheng 	mac_perim_handle_t	mph;
410da14cebeSEric Cheng 	void		*mdip;
411da14cebeSEric Cheng 	int32_t		intr_cpu;
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
414210db224Sericheng 		dl_err = DL_BADPRIM;
415210db224Sericheng 		goto failed;
4167c478bd9Sstevel@tonic-gate 	}
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 	if (dlp->dl_xidtest_flg != 0) {
419210db224Sericheng 		dl_err = DL_NOAUTO;
420210db224Sericheng 		goto failed;
4217c478bd9Sstevel@tonic-gate 	}
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	if (dlp->dl_service_mode != DL_CLDLS) {
424210db224Sericheng 		dl_err = DL_UNSUPPORTED;
425210db224Sericheng 		goto failed;
4267c478bd9Sstevel@tonic-gate 	}
4277c478bd9Sstevel@tonic-gate 
428210db224Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
429210db224Sericheng 		dl_err = DL_OUTSTATE;
430210db224Sericheng 		goto failed;
431210db224Sericheng 	}
432210db224Sericheng 
433da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
434da14cebeSEric Cheng 
4355d460eafSCathy Zhou 	if ((err = dls_active_set(dsp)) != 0) {
436210db224Sericheng 		dl_err = DL_SYSERR;
437da14cebeSEric Cheng 		goto failed2;
438210db224Sericheng 	}
4397c478bd9Sstevel@tonic-gate 
440da14cebeSEric Cheng 	dsp->ds_dlstate = DL_BIND_PENDING;
4417c478bd9Sstevel@tonic-gate 	/*
4427c478bd9Sstevel@tonic-gate 	 * Set the receive callback.
4437c478bd9Sstevel@tonic-gate 	 */
444da14cebeSEric Cheng 	dls_rx_set(dsp, (dsp->ds_mode == DLD_RAW) ?
445fd69bb17Syz 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
446fd69bb17Syz 
447fd69bb17Syz 	/*
448210db224Sericheng 	 * Bind the channel such that it can receive packets.
449fd69bb17Syz 	 */
450d62bc4baSyz 	sap = dlp->dl_sap;
4518d4cf8d8S 	dsp->ds_nonip = !check_mod_above(dsp->ds_rq, "ip") &&
4528d4cf8d8S 	    !check_mod_above(dsp->ds_rq, "arp");
4538d4cf8d8S 
454da14cebeSEric Cheng 	err = dls_bind(dsp, sap);
455210db224Sericheng 	if (err != 0) {
456210db224Sericheng 		switch (err) {
457210db224Sericheng 		case EINVAL:
458210db224Sericheng 			dl_err = DL_BADADDR;
459210db224Sericheng 			err = 0;
460210db224Sericheng 			break;
461210db224Sericheng 		default:
462210db224Sericheng 			dl_err = DL_SYSERR;
463210db224Sericheng 			break;
464210db224Sericheng 		}
465d62bc4baSyz 
466da14cebeSEric Cheng 		dsp->ds_dlstate = DL_UNBOUND;
4675d460eafSCathy Zhou 		dls_active_clear(dsp, B_FALSE);
468da14cebeSEric Cheng 		goto failed2;
469210db224Sericheng 	}
4707c478bd9Sstevel@tonic-gate 
471da14cebeSEric Cheng 	intr_cpu = mac_client_intr_cpu(dsp->ds_mch);
472da14cebeSEric Cheng 	mdip = mac_get_devinfo(dsp->ds_mh);
473da14cebeSEric Cheng 	mac_perim_exit(mph);
474da14cebeSEric Cheng 
475da14cebeSEric Cheng 	/*
476da14cebeSEric Cheng 	 * We do this after we get out of the perim to avoid deadlocks
477da14cebeSEric Cheng 	 * etc. since part of mac_client_retarget_intr is to walk the
478da14cebeSEric Cheng 	 * device tree in order to find and retarget the interrupts.
479da14cebeSEric Cheng 	 */
4800dc2366fSVenugopal Iyer 	if (intr_cpu != -1)
4810dc2366fSVenugopal Iyer 		mac_client_set_intr_cpu(mdip, dsp->ds_mch, intr_cpu);
482da14cebeSEric Cheng 
4837c478bd9Sstevel@tonic-gate 	/*
484210db224Sericheng 	 * Copy in MAC address.
4857c478bd9Sstevel@tonic-gate 	 */
4866f45d2aeSyz 	dlsap_addr_length = dsp->ds_mip->mi_addr_length;
487da14cebeSEric Cheng 	mac_unicast_primary_get(dsp->ds_mh, dlsap_addr);
488210db224Sericheng 
489210db224Sericheng 	/*
4906f45d2aeSyz 	 * Copy in the SAP.
491210db224Sericheng 	 */
492d62bc4baSyz 	*(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap;
4936f45d2aeSyz 	dlsap_addr_length += sizeof (uint16_t);
4947c478bd9Sstevel@tonic-gate 
495210db224Sericheng 	dsp->ds_dlstate = DL_IDLE;
4966f45d2aeSyz 	dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0);
497da14cebeSEric Cheng 	return;
498da14cebeSEric Cheng 
499da14cebeSEric Cheng failed2:
500da14cebeSEric Cheng 	mac_perim_exit(mph);
501210db224Sericheng failed:
502210db224Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
5037c478bd9Sstevel@tonic-gate }
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate /*
5067c478bd9Sstevel@tonic-gate  * DL_UNBIND_REQ
5077c478bd9Sstevel@tonic-gate  */
508da14cebeSEric Cheng static void
proto_unbind_req(dld_str_t * dsp,mblk_t * mp)509da14cebeSEric Cheng proto_unbind_req(dld_str_t *dsp, mblk_t *mp)
5107c478bd9Sstevel@tonic-gate {
511d62bc4baSyz 	queue_t		*q = dsp->ds_wq;
512d62bc4baSyz 	t_uscalar_t	dl_err;
513da14cebeSEric Cheng 	mac_perim_handle_t	mph;
514210db224Sericheng 
515d62bc4baSyz 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
516d62bc4baSyz 		dl_err = DL_BADPRIM;
517d62bc4baSyz 		goto failed;
518d62bc4baSyz 	}
519d62bc4baSyz 
520d62bc4baSyz 	if (dsp->ds_dlstate != DL_IDLE) {
521d62bc4baSyz 		dl_err = DL_OUTSTATE;
522d62bc4baSyz 		goto failed;
523d62bc4baSyz 	}
5247c478bd9Sstevel@tonic-gate 
525da14cebeSEric Cheng 	mutex_enter(&dsp->ds_lock);
526da14cebeSEric Cheng 	while (dsp->ds_datathr_cnt != 0)
527da14cebeSEric Cheng 		cv_wait(&dsp->ds_datathr_cv, &dsp->ds_lock);
5287c478bd9Sstevel@tonic-gate 
529da14cebeSEric Cheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
530da14cebeSEric Cheng 	mutex_exit(&dsp->ds_lock);
5317c478bd9Sstevel@tonic-gate 
532da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
533d62bc4baSyz 	/*
534da14cebeSEric Cheng 	 * Unbind the channel to stop packets being received.
535d62bc4baSyz 	 */
5365ecc58b1SGirish Moodalbail 	dls_unbind(dsp);
537d62bc4baSyz 
5387c478bd9Sstevel@tonic-gate 	/*
5397c478bd9Sstevel@tonic-gate 	 * Disable polling mode, if it is enabled.
5407c478bd9Sstevel@tonic-gate 	 */
541da14cebeSEric Cheng 	(void) dld_capab_poll_disable(dsp, NULL);
5428347601bSyl 
5437c478bd9Sstevel@tonic-gate 	/*
544d62bc4baSyz 	 * Clear LSO flags.
5457c478bd9Sstevel@tonic-gate 	 */
546d62bc4baSyz 	dsp->ds_lso = B_FALSE;
547d62bc4baSyz 	dsp->ds_lso_max = 0;
5487c478bd9Sstevel@tonic-gate 
549da14cebeSEric Cheng 	/*
550da14cebeSEric Cheng 	 * Clear the receive callback.
551da14cebeSEric Cheng 	 */
552da14cebeSEric Cheng 	dls_rx_set(dsp, NULL, NULL);
553da14cebeSEric Cheng 	dsp->ds_direct = B_FALSE;
554da14cebeSEric Cheng 
5557c478bd9Sstevel@tonic-gate 	/*
5567c478bd9Sstevel@tonic-gate 	 * Set the mode back to the default (unitdata).
5577c478bd9Sstevel@tonic-gate 	 */
5587c478bd9Sstevel@tonic-gate 	dsp->ds_mode = DLD_UNITDATA;
559210db224Sericheng 	dsp->ds_dlstate = DL_UNBOUND;
560d62bc4baSyz 
5615d460eafSCathy Zhou 	dls_active_clear(dsp, B_FALSE);
562da14cebeSEric Cheng 	mac_perim_exit(mph);
563da14cebeSEric Cheng 	dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ);
564da14cebeSEric Cheng 	return;
565210db224Sericheng failed:
566210db224Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
5677c478bd9Sstevel@tonic-gate }
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate /*
5707c478bd9Sstevel@tonic-gate  * DL_PROMISCON_REQ
5717c478bd9Sstevel@tonic-gate  */
572da14cebeSEric Cheng static void
proto_promiscon_req(dld_str_t * dsp,mblk_t * mp)573da14cebeSEric Cheng proto_promiscon_req(dld_str_t *dsp, mblk_t *mp)
5747c478bd9Sstevel@tonic-gate {
575da14cebeSEric Cheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)mp->b_rptr;
576210db224Sericheng 	int		err = 0;
577210db224Sericheng 	t_uscalar_t	dl_err;
578cabb4db9SDan McDonald 	uint32_t	new_flags, promisc_saved;
579210db224Sericheng 	queue_t		*q = dsp->ds_wq;
580da14cebeSEric Cheng 	mac_perim_handle_t	mph;
581210db224Sericheng 
582210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
583210db224Sericheng 		dl_err = DL_BADPRIM;
584210db224Sericheng 		goto failed;
585210db224Sericheng 	}
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
5887c478bd9Sstevel@tonic-gate 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
589210db224Sericheng 		dl_err = DL_OUTSTATE;
590210db224Sericheng 		goto failed;
5917c478bd9Sstevel@tonic-gate 	}
5927c478bd9Sstevel@tonic-gate 
593cabb4db9SDan McDonald 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
594cabb4db9SDan McDonald 
595cabb4db9SDan McDonald 	new_flags = promisc_saved = dsp->ds_promisc;
5967c478bd9Sstevel@tonic-gate 	switch (dlp->dl_level) {
5977c478bd9Sstevel@tonic-gate 	case DL_PROMISC_SAP:
598cabb4db9SDan McDonald 		new_flags |= DLS_PROMISC_SAP;
5997c478bd9Sstevel@tonic-gate 		break;
600da14cebeSEric Cheng 
6017c478bd9Sstevel@tonic-gate 	case DL_PROMISC_MULTI:
602cabb4db9SDan McDonald 		new_flags |= DLS_PROMISC_MULTI;
6037c478bd9Sstevel@tonic-gate 		break;
604da14cebeSEric Cheng 
6057c478bd9Sstevel@tonic-gate 	case DL_PROMISC_PHYS:
606cabb4db9SDan McDonald 		new_flags |= DLS_PROMISC_PHYS;
6077c478bd9Sstevel@tonic-gate 		break;
608da14cebeSEric Cheng 
609*115f9ea8SRobert Mustacchi 	case DL_PROMISC_RX_ONLY:
610*115f9ea8SRobert Mustacchi 		new_flags |= DLS_PROMISC_RX_ONLY;
611*115f9ea8SRobert Mustacchi 		break;
612*115f9ea8SRobert Mustacchi 
6137c478bd9Sstevel@tonic-gate 	default:
614210db224Sericheng 		dl_err = DL_NOTSUPPORTED;
615ad7ed3feSRobert Mustacchi 		goto failed2;
616210db224Sericheng 	}
617210db224Sericheng 
6185d460eafSCathy Zhou 	if ((promisc_saved == 0) && (err = dls_active_set(dsp)) != 0) {
619cabb4db9SDan McDonald 		ASSERT(dsp->ds_promisc == promisc_saved);
620210db224Sericheng 		dl_err = DL_SYSERR;
621da14cebeSEric Cheng 		goto failed2;
6227c478bd9Sstevel@tonic-gate 	}
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	/*
6257c478bd9Sstevel@tonic-gate 	 * Adjust channel promiscuity.
6267c478bd9Sstevel@tonic-gate 	 */
627cabb4db9SDan McDonald 	err = dls_promisc(dsp, new_flags);
628da14cebeSEric Cheng 
629210db224Sericheng 	if (err != 0) {
630210db224Sericheng 		dl_err = DL_SYSERR;
631da14cebeSEric Cheng 		dsp->ds_promisc = promisc_saved;
6325d460eafSCathy Zhou 		if (promisc_saved == 0)
6335d460eafSCathy Zhou 			dls_active_clear(dsp, B_FALSE);
634da14cebeSEric Cheng 		goto failed2;
635210db224Sericheng 	}
636210db224Sericheng 
637da14cebeSEric Cheng 	mac_perim_exit(mph);
638da14cebeSEric Cheng 
639210db224Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
640da14cebeSEric Cheng 	return;
641da14cebeSEric Cheng 
642da14cebeSEric Cheng failed2:
643da14cebeSEric Cheng 	mac_perim_exit(mph);
644210db224Sericheng failed:
645210db224Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
6467c478bd9Sstevel@tonic-gate }
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate /*
6497c478bd9Sstevel@tonic-gate  * DL_PROMISCOFF_REQ
6507c478bd9Sstevel@tonic-gate  */
651da14cebeSEric Cheng static void
proto_promiscoff_req(dld_str_t * dsp,mblk_t * mp)652da14cebeSEric Cheng proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp)
6537c478bd9Sstevel@tonic-gate {
654da14cebeSEric Cheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)mp->b_rptr;
655210db224Sericheng 	int		err = 0;
656210db224Sericheng 	t_uscalar_t	dl_err;
657cabb4db9SDan McDonald 	uint32_t	new_flags;
658210db224Sericheng 	queue_t		*q = dsp->ds_wq;
659da14cebeSEric Cheng 	mac_perim_handle_t	mph;
6607c478bd9Sstevel@tonic-gate 
661210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
662210db224Sericheng 		dl_err = DL_BADPRIM;
663210db224Sericheng 		goto failed;
6647c478bd9Sstevel@tonic-gate 	}
6657c478bd9Sstevel@tonic-gate 
666210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
667210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
668210db224Sericheng 		dl_err = DL_OUTSTATE;
669210db224Sericheng 		goto failed;
6707c478bd9Sstevel@tonic-gate 	}
6717c478bd9Sstevel@tonic-gate 
672cabb4db9SDan McDonald 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
673cabb4db9SDan McDonald 
674cabb4db9SDan McDonald 	new_flags = dsp->ds_promisc;
6757c478bd9Sstevel@tonic-gate 	switch (dlp->dl_level) {
6767c478bd9Sstevel@tonic-gate 	case DL_PROMISC_SAP:
677da14cebeSEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
678da14cebeSEric Cheng 			dl_err = DL_NOTENAB;
679cb6094b4SDan McDonald 			goto failed2;
680da14cebeSEric Cheng 		}
681cabb4db9SDan McDonald 		new_flags &= ~DLS_PROMISC_SAP;
6827c478bd9Sstevel@tonic-gate 		break;
683da14cebeSEric Cheng 
6847c478bd9Sstevel@tonic-gate 	case DL_PROMISC_MULTI:
685da14cebeSEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
686da14cebeSEric Cheng 			dl_err = DL_NOTENAB;
687cb6094b4SDan McDonald 			goto failed2;
688da14cebeSEric Cheng 		}
689cabb4db9SDan McDonald 		new_flags &= ~DLS_PROMISC_MULTI;
6907c478bd9Sstevel@tonic-gate 		break;
691da14cebeSEric Cheng 
6927c478bd9Sstevel@tonic-gate 	case DL_PROMISC_PHYS:
693da14cebeSEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
694da14cebeSEric Cheng 			dl_err = DL_NOTENAB;
695cb6094b4SDan McDonald 			goto failed2;
696da14cebeSEric Cheng 		}
697cabb4db9SDan McDonald 		new_flags &= ~DLS_PROMISC_PHYS;
6987c478bd9Sstevel@tonic-gate 		break;
699da14cebeSEric Cheng 
700*115f9ea8SRobert Mustacchi 	case DL_PROMISC_RX_ONLY:
701*115f9ea8SRobert Mustacchi 		if (!(dsp->ds_promisc & DLS_PROMISC_RX_ONLY)) {
702*115f9ea8SRobert Mustacchi 			dl_err = DL_NOTENAB;
703*115f9ea8SRobert Mustacchi 			goto failed2;
704*115f9ea8SRobert Mustacchi 		}
705*115f9ea8SRobert Mustacchi 		new_flags &= ~DLS_PROMISC_RX_ONLY;
706*115f9ea8SRobert Mustacchi 		break;
707*115f9ea8SRobert Mustacchi 
7087c478bd9Sstevel@tonic-gate 	default:
709210db224Sericheng 		dl_err = DL_NOTSUPPORTED;
710cb6094b4SDan McDonald 		goto failed2;
7117c478bd9Sstevel@tonic-gate 	}
7127c478bd9Sstevel@tonic-gate 
713da14cebeSEric Cheng 	/*
714da14cebeSEric Cheng 	 * Adjust channel promiscuity.
715da14cebeSEric Cheng 	 */
716cabb4db9SDan McDonald 	err = dls_promisc(dsp, new_flags);
717d62bc4baSyz 
718210db224Sericheng 	if (err != 0) {
719210db224Sericheng 		dl_err = DL_SYSERR;
720cb6094b4SDan McDonald 		goto failed2;
721210db224Sericheng 	}
7225d460eafSCathy Zhou 
723cabb4db9SDan McDonald 	ASSERT(dsp->ds_promisc == new_flags);
7245d460eafSCathy Zhou 	if (dsp->ds_promisc == 0)
7255d460eafSCathy Zhou 		dls_active_clear(dsp, B_FALSE);
7265d460eafSCathy Zhou 
7275d460eafSCathy Zhou 	mac_perim_exit(mph);
7285d460eafSCathy Zhou 
729210db224Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
730da14cebeSEric Cheng 	return;
731cb6094b4SDan McDonald failed2:
732cb6094b4SDan McDonald 	mac_perim_exit(mph);
733210db224Sericheng failed:
734210db224Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
7357c478bd9Sstevel@tonic-gate }
7367c478bd9Sstevel@tonic-gate 
7377c478bd9Sstevel@tonic-gate /*
7387c478bd9Sstevel@tonic-gate  * DL_ENABMULTI_REQ
7397c478bd9Sstevel@tonic-gate  */
740da14cebeSEric Cheng static void
proto_enabmulti_req(dld_str_t * dsp,mblk_t * mp)741da14cebeSEric Cheng proto_enabmulti_req(dld_str_t *dsp, mblk_t *mp)
7427c478bd9Sstevel@tonic-gate {
743da14cebeSEric Cheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)mp->b_rptr;
744210db224Sericheng 	int		err = 0;
745210db224Sericheng 	t_uscalar_t	dl_err;
746210db224Sericheng 	queue_t		*q = dsp->ds_wq;
747da14cebeSEric Cheng 	mac_perim_handle_t	mph;
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
7507c478bd9Sstevel@tonic-gate 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
751210db224Sericheng 		dl_err = DL_OUTSTATE;
752210db224Sericheng 		goto failed;
7537c478bd9Sstevel@tonic-gate 	}
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
7567c478bd9Sstevel@tonic-gate 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
7577c478bd9Sstevel@tonic-gate 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
758210db224Sericheng 		dl_err = DL_BADPRIM;
759210db224Sericheng 		goto failed;
760210db224Sericheng 	}
761210db224Sericheng 
762da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
763da14cebeSEric Cheng 
7645d460eafSCathy Zhou 	if ((dsp->ds_dmap == NULL) && (err = dls_active_set(dsp)) != 0) {
765210db224Sericheng 		dl_err = DL_SYSERR;
766da14cebeSEric Cheng 		goto failed2;
7677c478bd9Sstevel@tonic-gate 	}
7687c478bd9Sstevel@tonic-gate 
769da14cebeSEric Cheng 	err = dls_multicst_add(dsp, mp->b_rptr + dlp->dl_addr_offset);
770210db224Sericheng 	if (err != 0) {
771210db224Sericheng 		switch (err) {
772210db224Sericheng 		case EINVAL:
773210db224Sericheng 			dl_err = DL_BADADDR;
774210db224Sericheng 			err = 0;
775210db224Sericheng 			break;
776210db224Sericheng 		case ENOSPC:
777210db224Sericheng 			dl_err = DL_TOOMANY;
778210db224Sericheng 			err = 0;
779210db224Sericheng 			break;
780210db224Sericheng 		default:
781210db224Sericheng 			dl_err = DL_SYSERR;
782210db224Sericheng 			break;
783210db224Sericheng 		}
7845d460eafSCathy Zhou 		if (dsp->ds_dmap == NULL)
7855d460eafSCathy Zhou 			dls_active_clear(dsp, B_FALSE);
786da14cebeSEric Cheng 		goto failed2;
787210db224Sericheng 	}
788210db224Sericheng 
789da14cebeSEric Cheng 	mac_perim_exit(mph);
790da14cebeSEric Cheng 
791210db224Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
792da14cebeSEric Cheng 	return;
793da14cebeSEric Cheng 
794da14cebeSEric Cheng failed2:
795da14cebeSEric Cheng 	mac_perim_exit(mph);
796210db224Sericheng failed:
797210db224Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
7987c478bd9Sstevel@tonic-gate }
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate /*
8017c478bd9Sstevel@tonic-gate  * DL_DISABMULTI_REQ
8027c478bd9Sstevel@tonic-gate  */
803da14cebeSEric Cheng static void
proto_disabmulti_req(dld_str_t * dsp,mblk_t * mp)804da14cebeSEric Cheng proto_disabmulti_req(dld_str_t *dsp, mblk_t *mp)
8057c478bd9Sstevel@tonic-gate {
806da14cebeSEric Cheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)mp->b_rptr;
807210db224Sericheng 	int		err = 0;
808210db224Sericheng 	t_uscalar_t	dl_err;
809210db224Sericheng 	queue_t		*q = dsp->ds_wq;
810da14cebeSEric Cheng 	mac_perim_handle_t	mph;
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
8137c478bd9Sstevel@tonic-gate 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
814210db224Sericheng 		dl_err = DL_OUTSTATE;
815210db224Sericheng 		goto failed;
8167c478bd9Sstevel@tonic-gate 	}
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
8197c478bd9Sstevel@tonic-gate 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
8207c478bd9Sstevel@tonic-gate 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
821210db224Sericheng 		dl_err = DL_BADPRIM;
822210db224Sericheng 		goto failed;
8237c478bd9Sstevel@tonic-gate 	}
8247c478bd9Sstevel@tonic-gate 
825da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
826da14cebeSEric Cheng 	err = dls_multicst_remove(dsp, mp->b_rptr + dlp->dl_addr_offset);
8275d460eafSCathy Zhou 	if ((err == 0) && (dsp->ds_dmap == NULL))
8285d460eafSCathy Zhou 		dls_active_clear(dsp, B_FALSE);
829da14cebeSEric Cheng 	mac_perim_exit(mph);
830da14cebeSEric Cheng 
831210db224Sericheng 	if (err != 0) {
83215c07adcSJohn Levon 		switch (err) {
833210db224Sericheng 		case EINVAL:
834210db224Sericheng 			dl_err = DL_BADADDR;
835210db224Sericheng 			err = 0;
836210db224Sericheng 			break;
837da14cebeSEric Cheng 
838210db224Sericheng 		case ENOENT:
839210db224Sericheng 			dl_err = DL_NOTENAB;
840210db224Sericheng 			err = 0;
841210db224Sericheng 			break;
842da14cebeSEric Cheng 
843210db224Sericheng 		default:
844210db224Sericheng 			dl_err = DL_SYSERR;
845210db224Sericheng 			break;
846210db224Sericheng 		}
847210db224Sericheng 		goto failed;
848210db224Sericheng 	}
849210db224Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
850da14cebeSEric Cheng 	return;
851210db224Sericheng failed:
852210db224Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
8537c478bd9Sstevel@tonic-gate }
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate /*
8567c478bd9Sstevel@tonic-gate  * DL_PHYS_ADDR_REQ
8577c478bd9Sstevel@tonic-gate  */
858da14cebeSEric Cheng static void
proto_physaddr_req(dld_str_t * dsp,mblk_t * mp)859da14cebeSEric Cheng proto_physaddr_req(dld_str_t *dsp, mblk_t *mp)
8607c478bd9Sstevel@tonic-gate {
861da14cebeSEric Cheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)mp->b_rptr;
862210db224Sericheng 	queue_t		*q = dsp->ds_wq;
8632b24ab6bSSebastien Roy 	t_uscalar_t	dl_err = 0;
8642b24ab6bSSebastien Roy 	char		*addr = NULL;
865210db224Sericheng 	uint_t		addr_length;
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
868210db224Sericheng 		dl_err = DL_BADPRIM;
8692b24ab6bSSebastien Roy 		goto done;
870210db224Sericheng 	}
871210db224Sericheng 
872210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
873210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
874210db224Sericheng 		dl_err = DL_OUTSTATE;
8752b24ab6bSSebastien Roy 		goto done;
876210db224Sericheng 	}
877210db224Sericheng 
878210db224Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
879c08e5e1aSdr 	if (addr_length > 0) {
880da14cebeSEric Cheng 		addr = kmem_alloc(addr_length, KM_SLEEP);
8812b24ab6bSSebastien Roy 		switch (dlp->dl_addr_type) {
8822b24ab6bSSebastien Roy 		case DL_CURR_PHYS_ADDR:
883da14cebeSEric Cheng 			mac_unicast_primary_get(dsp->ds_mh, (uint8_t *)addr);
8842b24ab6bSSebastien Roy 			break;
8852b24ab6bSSebastien Roy 		case DL_FACT_PHYS_ADDR:
886da14cebeSEric Cheng 			bcopy(dsp->ds_mip->mi_unicst_addr, addr, addr_length);
8872b24ab6bSSebastien Roy 			break;
8882b24ab6bSSebastien Roy 		case DL_CURR_DEST_ADDR:
8892b24ab6bSSebastien Roy 			if (!mac_dst_get(dsp->ds_mh, (uint8_t *)addr))
8902b24ab6bSSebastien Roy 				dl_err = DL_NOTSUPPORTED;
8912b24ab6bSSebastien Roy 			break;
8922b24ab6bSSebastien Roy 		default:
8932b24ab6bSSebastien Roy 			dl_err = DL_UNSUPPORTED;
8942b24ab6bSSebastien Roy 		}
8952b24ab6bSSebastien Roy 	}
8962b24ab6bSSebastien Roy done:
8972b24ab6bSSebastien Roy 	if (dl_err == 0)
898c08e5e1aSdr 		dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
8992b24ab6bSSebastien Roy 	else
9002b24ab6bSSebastien Roy 		dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
9012b24ab6bSSebastien Roy 	if (addr != NULL)
902c08e5e1aSdr 		kmem_free(addr, addr_length);
9037c478bd9Sstevel@tonic-gate }
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate /*
9067c478bd9Sstevel@tonic-gate  * DL_SET_PHYS_ADDR_REQ
9077c478bd9Sstevel@tonic-gate  */
908da14cebeSEric Cheng static void
proto_setphysaddr_req(dld_str_t * dsp,mblk_t * mp)909da14cebeSEric Cheng proto_setphysaddr_req(dld_str_t *dsp, mblk_t *mp)
9107c478bd9Sstevel@tonic-gate {
911da14cebeSEric Cheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)mp->b_rptr;
912210db224Sericheng 	int		err = 0;
913210db224Sericheng 	t_uscalar_t	dl_err;
914210db224Sericheng 	queue_t		*q = dsp->ds_wq;
915da14cebeSEric Cheng 	mac_perim_handle_t	mph;
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
9187c478bd9Sstevel@tonic-gate 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
919210db224Sericheng 		dl_err = DL_OUTSTATE;
920210db224Sericheng 		goto failed;
9217c478bd9Sstevel@tonic-gate 	}
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
9247c478bd9Sstevel@tonic-gate 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
9257c478bd9Sstevel@tonic-gate 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
926210db224Sericheng 		dl_err = DL_BADPRIM;
927210db224Sericheng 		goto failed;
928210db224Sericheng 	}
929210db224Sericheng 
930da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
931da14cebeSEric Cheng 
9325d460eafSCathy Zhou 	if ((err = dls_active_set(dsp)) != 0) {
933210db224Sericheng 		dl_err = DL_SYSERR;
934da14cebeSEric Cheng 		goto failed2;
9357c478bd9Sstevel@tonic-gate 	}
9367c478bd9Sstevel@tonic-gate 
93725ec3e3dSEric Cheng 	/*
93825ec3e3dSEric Cheng 	 * If mac-nospoof is enabled and the link is owned by a
93925ec3e3dSEric Cheng 	 * non-global zone, changing the mac address is not allowed.
94025ec3e3dSEric Cheng 	 */
94125ec3e3dSEric Cheng 	if (dsp->ds_dlp->dl_zid != GLOBAL_ZONEID &&
94225ec3e3dSEric Cheng 	    mac_protect_enabled(dsp->ds_mch, MPT_MACNOSPOOF)) {
94325ec3e3dSEric Cheng 		dls_active_clear(dsp, B_FALSE);
94425ec3e3dSEric Cheng 		err = EACCES;
94525ec3e3dSEric Cheng 		goto failed2;
94625ec3e3dSEric Cheng 	}
94725ec3e3dSEric Cheng 
948da14cebeSEric Cheng 	err = mac_unicast_primary_set(dsp->ds_mh,
949da14cebeSEric Cheng 	    mp->b_rptr + dlp->dl_addr_offset);
950210db224Sericheng 	if (err != 0) {
951210db224Sericheng 		switch (err) {
952210db224Sericheng 		case EINVAL:
953210db224Sericheng 			dl_err = DL_BADADDR;
954210db224Sericheng 			err = 0;
955210db224Sericheng 			break;
956210db224Sericheng 
957210db224Sericheng 		default:
958210db224Sericheng 			dl_err = DL_SYSERR;
959210db224Sericheng 			break;
960210db224Sericheng 		}
9615d460eafSCathy Zhou 		dls_active_clear(dsp, B_FALSE);
962da14cebeSEric Cheng 		goto failed2;
963210db224Sericheng 
964210db224Sericheng 	}
965d62bc4baSyz 
966da14cebeSEric Cheng 	mac_perim_exit(mph);
967da14cebeSEric Cheng 
968210db224Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
969da14cebeSEric Cheng 	return;
970da14cebeSEric Cheng 
971da14cebeSEric Cheng failed2:
972da14cebeSEric Cheng 	mac_perim_exit(mph);
973210db224Sericheng failed:
974210db224Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
9757c478bd9Sstevel@tonic-gate }
9767c478bd9Sstevel@tonic-gate 
9777c478bd9Sstevel@tonic-gate /*
9787c478bd9Sstevel@tonic-gate  * DL_UDQOS_REQ
9797c478bd9Sstevel@tonic-gate  */
980da14cebeSEric Cheng static void
proto_udqos_req(dld_str_t * dsp,mblk_t * mp)981da14cebeSEric Cheng proto_udqos_req(dld_str_t *dsp, mblk_t *mp)
9827c478bd9Sstevel@tonic-gate {
983da14cebeSEric Cheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)mp->b_rptr;
984210db224Sericheng 	dl_qos_cl_sel1_t *selp;
985210db224Sericheng 	int		off, len;
986210db224Sericheng 	t_uscalar_t	dl_err;
987210db224Sericheng 	queue_t		*q = dsp->ds_wq;
9887c478bd9Sstevel@tonic-gate 
9897c478bd9Sstevel@tonic-gate 	off = dlp->dl_qos_offset;
9907c478bd9Sstevel@tonic-gate 	len = dlp->dl_qos_length;
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
993210db224Sericheng 		dl_err = DL_BADPRIM;
994210db224Sericheng 		goto failed;
9957c478bd9Sstevel@tonic-gate 	}
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
9987c478bd9Sstevel@tonic-gate 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
999210db224Sericheng 		dl_err = DL_BADQOSTYPE;
1000210db224Sericheng 		goto failed;
10017c478bd9Sstevel@tonic-gate 	}
10027c478bd9Sstevel@tonic-gate 
1003605445d5Sdg 	if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
10047c478bd9Sstevel@tonic-gate 	    selp->dl_priority < 0) {
1005210db224Sericheng 		dl_err = DL_BADQOSPARAM;
1006210db224Sericheng 		goto failed;
10077c478bd9Sstevel@tonic-gate 	}
10087c478bd9Sstevel@tonic-gate 
1009d62bc4baSyz 	dsp->ds_pri = selp->dl_priority;
1010210db224Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1011da14cebeSEric Cheng 	return;
1012210db224Sericheng failed:
1013210db224Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
10147c478bd9Sstevel@tonic-gate }
10157c478bd9Sstevel@tonic-gate 
10164b46d1efSkrgopi static boolean_t
check_mod_above(queue_t * q,const char * mod)10178d4cf8d8S check_mod_above(queue_t *q, const char *mod)
10184b46d1efSkrgopi {
10194b46d1efSkrgopi 	queue_t		*next_q;
10204b46d1efSkrgopi 	boolean_t	ret = B_TRUE;
10214b46d1efSkrgopi 
10224b46d1efSkrgopi 	claimstr(q);
10234b46d1efSkrgopi 	next_q = q->q_next;
10248d4cf8d8S 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, mod) != 0)
10254b46d1efSkrgopi 		ret = B_FALSE;
10264b46d1efSkrgopi 	releasestr(q);
10274b46d1efSkrgopi 	return (ret);
10284b46d1efSkrgopi }
10294b46d1efSkrgopi 
10307c478bd9Sstevel@tonic-gate /*
10317c478bd9Sstevel@tonic-gate  * DL_CAPABILITY_REQ
10327c478bd9Sstevel@tonic-gate  */
1033da14cebeSEric Cheng static void
proto_capability_req(dld_str_t * dsp,mblk_t * mp)1034da14cebeSEric Cheng proto_capability_req(dld_str_t *dsp, mblk_t *mp)
10357c478bd9Sstevel@tonic-gate {
1036da14cebeSEric Cheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)mp->b_rptr;
1037210db224Sericheng 	dl_capability_sub_t *sp;
1038210db224Sericheng 	size_t		size, len;
1039210db224Sericheng 	offset_t	off, end;
1040210db224Sericheng 	t_uscalar_t	dl_err;
1041210db224Sericheng 	queue_t		*q = dsp->ds_wq;
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1044210db224Sericheng 		dl_err = DL_BADPRIM;
1045210db224Sericheng 		goto failed;
1046210db224Sericheng 	}
1047210db224Sericheng 
1048210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1049210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1050210db224Sericheng 		dl_err = DL_OUTSTATE;
1051210db224Sericheng 		goto failed;
10527c478bd9Sstevel@tonic-gate 	}
10537c478bd9Sstevel@tonic-gate 
10547c478bd9Sstevel@tonic-gate 	/*
10557c478bd9Sstevel@tonic-gate 	 * This request is overloaded. If there are no requested capabilities
10567c478bd9Sstevel@tonic-gate 	 * then we just want to acknowledge with all the capabilities we
10577c478bd9Sstevel@tonic-gate 	 * support. Otherwise we enable the set of capabilities requested.
10587c478bd9Sstevel@tonic-gate 	 */
10597c478bd9Sstevel@tonic-gate 	if (dlp->dl_sub_length == 0) {
1060da14cebeSEric Cheng 		proto_capability_advertise(dsp, mp);
1061da14cebeSEric Cheng 		return;
10627c478bd9Sstevel@tonic-gate 	}
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1065210db224Sericheng 		dl_err = DL_BADPRIM;
1066210db224Sericheng 		goto failed;
10677c478bd9Sstevel@tonic-gate 	}
10687c478bd9Sstevel@tonic-gate 
1069210db224Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
10707c478bd9Sstevel@tonic-gate 
1071210db224Sericheng 	off = dlp->dl_sub_offset;
1072210db224Sericheng 	len = dlp->dl_sub_length;
10737c478bd9Sstevel@tonic-gate 
1074210db224Sericheng 	/*
1075210db224Sericheng 	 * Walk the list of capabilities to be enabled.
1076210db224Sericheng 	 */
1077210db224Sericheng 	for (end = off + len; off < end; ) {
1078210db224Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1079210db224Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
10807c478bd9Sstevel@tonic-gate 
1081210db224Sericheng 		if (off + size > end ||
1082210db224Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1083210db224Sericheng 			dl_err = DL_BADPRIM;
1084210db224Sericheng 			goto failed;
1085210db224Sericheng 		}
10867c478bd9Sstevel@tonic-gate 
1087210db224Sericheng 		switch (sp->dl_cap) {
1088210db224Sericheng 		/*
1089210db224Sericheng 		 * TCP/IP checksum offload to hardware.
1090210db224Sericheng 		 */
1091210db224Sericheng 		case DL_CAPAB_HCKSUM: {
1092210db224Sericheng 			dl_capab_hcksum_t *hcksump;
1093210db224Sericheng 			dl_capab_hcksum_t hcksum;
10947c478bd9Sstevel@tonic-gate 
1095210db224Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1096210db224Sericheng 			/*
1097210db224Sericheng 			 * Copy for alignment.
1098210db224Sericheng 			 */
1099210db224Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1100210db224Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1101210db224Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1102210db224Sericheng 			break;
1103210db224Sericheng 		}
11047c478bd9Sstevel@tonic-gate 
1105da14cebeSEric Cheng 		case DL_CAPAB_DLD: {
1106da14cebeSEric Cheng 			dl_capab_dld_t	*dldp;
1107da14cebeSEric Cheng 			dl_capab_dld_t	dld;
11084b46d1efSkrgopi 
1109da14cebeSEric Cheng 			dldp = (dl_capab_dld_t *)&sp[1];
11104b46d1efSkrgopi 			/*
11114b46d1efSkrgopi 			 * Copy for alignment.
11124b46d1efSkrgopi 			 */
1113da14cebeSEric Cheng 			bcopy(dldp, &dld, sizeof (dl_capab_dld_t));
1114da14cebeSEric Cheng 			dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq);
1115da14cebeSEric Cheng 			bcopy(&dld, dldp, sizeof (dl_capab_dld_t));
1116210db224Sericheng 			break;
1117210db224Sericheng 		}
1118210db224Sericheng 		default:
1119210db224Sericheng 			break;
11207c478bd9Sstevel@tonic-gate 		}
1121210db224Sericheng 		off += size;
11227c478bd9Sstevel@tonic-gate 	}
1123210db224Sericheng 	qreply(q, mp);
1124da14cebeSEric Cheng 	return;
1125210db224Sericheng failed:
1126210db224Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
11277c478bd9Sstevel@tonic-gate }
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate /*
1130210db224Sericheng  * DL_NOTIFY_REQ
11317c478bd9Sstevel@tonic-gate  */
1132da14cebeSEric Cheng static void
proto_notify_req(dld_str_t * dsp,mblk_t * mp)1133da14cebeSEric Cheng proto_notify_req(dld_str_t *dsp, mblk_t *mp)
11347c478bd9Sstevel@tonic-gate {
1135da14cebeSEric Cheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)mp->b_rptr;
1136210db224Sericheng 	t_uscalar_t	dl_err;
1137210db224Sericheng 	queue_t		*q = dsp->ds_wq;
1138210db224Sericheng 	uint_t		note =
1139210db224Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1140210db224Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1141210db224Sericheng 	    DL_NOTE_PHYS_ADDR |
1142210db224Sericheng 	    DL_NOTE_LINK_UP |
1143210db224Sericheng 	    DL_NOTE_LINK_DOWN |
1144ba2e4443Sseb 	    DL_NOTE_CAPAB_RENEG |
1145cef310fdSGirish Moodalbail 	    DL_NOTE_FASTPATH_FLUSH |
11462b24ab6bSSebastien Roy 	    DL_NOTE_SPEED |
1147550b6e40SSowmini Varadhan 	    DL_NOTE_SDU_SIZE|
11481eee170aSErik Nordmark 	    DL_NOTE_SDU_SIZE2|
1149550b6e40SSowmini Varadhan 	    DL_NOTE_ALLOWED_IPS;
11507c478bd9Sstevel@tonic-gate 
1151210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1152210db224Sericheng 		dl_err = DL_BADPRIM;
1153210db224Sericheng 		goto failed;
11547c478bd9Sstevel@tonic-gate 	}
11557c478bd9Sstevel@tonic-gate 
11567c478bd9Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1157210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1158210db224Sericheng 		dl_err = DL_OUTSTATE;
1159210db224Sericheng 		goto failed;
1160210db224Sericheng 	}
11617c478bd9Sstevel@tonic-gate 
1162d62bc4baSyz 	note &= ~(mac_no_notification(dsp->ds_mh));
1163d62bc4baSyz 
11647c478bd9Sstevel@tonic-gate 	/*
1165210db224Sericheng 	 * Cache the notifications that are being enabled.
11667c478bd9Sstevel@tonic-gate 	 */
1167210db224Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
11687c478bd9Sstevel@tonic-gate 	/*
1169210db224Sericheng 	 * The ACK carries all notifications regardless of which set is
1170210db224Sericheng 	 * being enabled.
11717c478bd9Sstevel@tonic-gate 	 */
1172210db224Sericheng 	dlnotifyack(q, mp, note);
11737c478bd9Sstevel@tonic-gate 
11747c478bd9Sstevel@tonic-gate 	/*
1175da14cebeSEric Cheng 	 * Generate DL_NOTIFY_IND messages for each enabled notification.
11767c478bd9Sstevel@tonic-gate 	 */
1177210db224Sericheng 	if (dsp->ds_notifications != 0) {
1178210db224Sericheng 		dld_str_notify_ind(dsp);
11797c478bd9Sstevel@tonic-gate 	}
1180da14cebeSEric Cheng 	return;
11817c478bd9Sstevel@tonic-gate failed:
1182210db224Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
11837c478bd9Sstevel@tonic-gate }
11847c478bd9Sstevel@tonic-gate 
11857c478bd9Sstevel@tonic-gate /*
1186da14cebeSEric Cheng  * DL_UINTDATA_REQ
11877c478bd9Sstevel@tonic-gate  */
1188d62bc4baSyz void
proto_unitdata_req(dld_str_t * dsp,mblk_t * mp)1189da14cebeSEric Cheng proto_unitdata_req(dld_str_t *dsp, mblk_t *mp)
11907c478bd9Sstevel@tonic-gate {
1191210db224Sericheng 	queue_t			*q = dsp->ds_wq;
1192d62bc4baSyz 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)mp->b_rptr;
1193210db224Sericheng 	off_t			off;
1194210db224Sericheng 	size_t			len, size;
1195210db224Sericheng 	const uint8_t		*addr;
1196210db224Sericheng 	uint16_t		sap;
11977c478bd9Sstevel@tonic-gate 	uint_t			addr_length;
1198ba2e4443Sseb 	mblk_t			*bp, *payload;
1199210db224Sericheng 	t_uscalar_t		dl_err;
1200e7801d59Ssowmini 	uint_t			max_sdu;
12017c478bd9Sstevel@tonic-gate 
1202210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1203da14cebeSEric Cheng 		dlerrorack(q, mp, DL_UNITDATA_REQ, DL_BADPRIM, 0);
1204da14cebeSEric Cheng 		return;
1205210db224Sericheng 	}
12067c478bd9Sstevel@tonic-gate 
1207da14cebeSEric Cheng 	mutex_enter(&dsp->ds_lock);
1208da14cebeSEric Cheng 	if (dsp->ds_dlstate != DL_IDLE) {
1209da14cebeSEric Cheng 		mutex_exit(&dsp->ds_lock);
1210da14cebeSEric Cheng 		dlerrorack(q, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
1211da14cebeSEric Cheng 		return;
1212da14cebeSEric Cheng 	}
1213da14cebeSEric Cheng 	DLD_DATATHR_INC(dsp);
1214da14cebeSEric Cheng 	mutex_exit(&dsp->ds_lock);
1215da14cebeSEric Cheng 
1216210db224Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
12177c478bd9Sstevel@tonic-gate 
1218210db224Sericheng 	off = dlp->dl_dest_addr_offset;
1219210db224Sericheng 	len = dlp->dl_dest_addr_length;
12207c478bd9Sstevel@tonic-gate 
1221210db224Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1222210db224Sericheng 		dl_err = DL_BADPRIM;
1223210db224Sericheng 		goto failed;
1224210db224Sericheng 	}
12257c478bd9Sstevel@tonic-gate 
1226210db224Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1227210db224Sericheng 		dl_err = DL_BADADDR;
1228210db224Sericheng 		goto failed;
12297c478bd9Sstevel@tonic-gate 	}
12307c478bd9Sstevel@tonic-gate 
1231210db224Sericheng 	addr = mp->b_rptr + off;
1232210db224Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
12337c478bd9Sstevel@tonic-gate 
1234210db224Sericheng 	/*
1235210db224Sericheng 	 * Check the length of the packet and the block types.
1236210db224Sericheng 	 */
1237210db224Sericheng 	size = 0;
1238ba2e4443Sseb 	payload = mp->b_cont;
1239ba2e4443Sseb 	for (bp = payload; bp != NULL; bp = bp->b_cont) {
1240210db224Sericheng 		if (DB_TYPE(bp) != M_DATA)
1241210db224Sericheng 			goto baddata;
12427c478bd9Sstevel@tonic-gate 
1243210db224Sericheng 		size += MBLKL(bp);
1244210db224Sericheng 	}
12457c478bd9Sstevel@tonic-gate 
1246e7801d59Ssowmini 	mac_sdu_get(dsp->ds_mh, NULL, &max_sdu);
1247e7801d59Ssowmini 	if (size > max_sdu)
1248210db224Sericheng 		goto baddata;
12497c478bd9Sstevel@tonic-gate 
1250210db224Sericheng 	/*
1251210db224Sericheng 	 * Build a packet header.
1252210db224Sericheng 	 */
1253da14cebeSEric Cheng 	if ((bp = dls_header(dsp, addr, sap, dlp->dl_priority.dl_max,
1254605445d5Sdg 	    &payload)) == NULL) {
12557c478bd9Sstevel@tonic-gate 		dl_err = DL_BADADDR;
1256210db224Sericheng 		goto failed;
12577c478bd9Sstevel@tonic-gate 	}
12587c478bd9Sstevel@tonic-gate 
1259210db224Sericheng 	/*
1260210db224Sericheng 	 * We no longer need the M_PROTO header, so free it.
1261210db224Sericheng 	 */
1262210db224Sericheng 	freeb(mp);
12637c478bd9Sstevel@tonic-gate 
1264210db224Sericheng 	/*
1265210db224Sericheng 	 * Transfer the checksum offload information if it is present.
1266210db224Sericheng 	 */
1267ec71f88eSPatrick Mooney 	mac_hcksum_clone(payload, bp);
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	/*
1270210db224Sericheng 	 * Link the payload onto the new header.
12717c478bd9Sstevel@tonic-gate 	 */
1272210db224Sericheng 	ASSERT(bp->b_cont == NULL);
1273ba2e4443Sseb 	bp->b_cont = payload;
1274da14cebeSEric Cheng 
1275da14cebeSEric Cheng 	/*
1276da14cebeSEric Cheng 	 * No lock can be held across modules and putnext()'s,
1277da14cebeSEric Cheng 	 * which can happen here with the call from DLD_TX().
1278da14cebeSEric Cheng 	 */
12798648b7dbSToomas Soome 	if (DLD_TX(dsp, bp, 0, 0) != 0) {
1280da14cebeSEric Cheng 		/* flow-controlled */
1281da14cebeSEric Cheng 		DLD_SETQFULL(dsp);
1282da14cebeSEric Cheng 	}
1283da14cebeSEric Cheng 	DLD_DATATHR_DCR(dsp);
1284d62bc4baSyz 	return;
1285da14cebeSEric Cheng 
1286210db224Sericheng failed:
1287210db224Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1288da14cebeSEric Cheng 	DLD_DATATHR_DCR(dsp);
1289d62bc4baSyz 	return;
1290210db224Sericheng 
1291210db224Sericheng baddata:
1292210db224Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1293da14cebeSEric Cheng 	DLD_DATATHR_DCR(dsp);
12947c478bd9Sstevel@tonic-gate }
12957c478bd9Sstevel@tonic-gate 
12967c478bd9Sstevel@tonic-gate /*
1297210db224Sericheng  * DL_PASSIVE_REQ
12987c478bd9Sstevel@tonic-gate  */
1299da14cebeSEric Cheng static void
proto_passive_req(dld_str_t * dsp,mblk_t * mp)1300da14cebeSEric Cheng proto_passive_req(dld_str_t *dsp, mblk_t *mp)
13017c478bd9Sstevel@tonic-gate {
1302210db224Sericheng 	t_uscalar_t dl_err;
13037c478bd9Sstevel@tonic-gate 
1304210db224Sericheng 	/*
1305210db224Sericheng 	 * If we've already become active by issuing an active primitive,
1306210db224Sericheng 	 * then it's too late to try to become passive.
1307210db224Sericheng 	 */
1308210db224Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1309210db224Sericheng 		dl_err = DL_OUTSTATE;
13107c478bd9Sstevel@tonic-gate 		goto failed;
1311210db224Sericheng 	}
13127c478bd9Sstevel@tonic-gate 
1313210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1314210db224Sericheng 		dl_err = DL_BADPRIM;
1315210db224Sericheng 		goto failed;
13167c478bd9Sstevel@tonic-gate 	}
13177c478bd9Sstevel@tonic-gate 
1318210db224Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1319210db224Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1320da14cebeSEric Cheng 	return;
1321210db224Sericheng failed:
1322210db224Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
13237c478bd9Sstevel@tonic-gate }
13247c478bd9Sstevel@tonic-gate 
1325da14cebeSEric Cheng 
13267c478bd9Sstevel@tonic-gate /*
1327210db224Sericheng  * Catch-all handler.
13287c478bd9Sstevel@tonic-gate  */
1329da14cebeSEric Cheng static void
proto_req(dld_str_t * dsp,mblk_t * mp)1330da14cebeSEric Cheng proto_req(dld_str_t *dsp, mblk_t *mp)
13317c478bd9Sstevel@tonic-gate {
1332da14cebeSEric Cheng 	union DL_primitives	*dlp = (union DL_primitives *)mp->b_rptr;
1333da14cebeSEric Cheng 
1334210db224Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
13357c478bd9Sstevel@tonic-gate }
13367c478bd9Sstevel@tonic-gate 
1337da14cebeSEric Cheng static int
dld_capab_perim(dld_str_t * dsp,void * data,uint_t flags)1338da14cebeSEric Cheng dld_capab_perim(dld_str_t *dsp, void *data, uint_t flags)
13397c478bd9Sstevel@tonic-gate {
1340da14cebeSEric Cheng 	switch (flags) {
1341da14cebeSEric Cheng 	case DLD_ENABLE:
1342da14cebeSEric Cheng 		mac_perim_enter_by_mh(dsp->ds_mh, (mac_perim_handle_t *)data);
1343da14cebeSEric Cheng 		return (0);
13447c478bd9Sstevel@tonic-gate 
1345da14cebeSEric Cheng 	case DLD_DISABLE:
1346da14cebeSEric Cheng 		mac_perim_exit((mac_perim_handle_t)data);
1347da14cebeSEric Cheng 		return (0);
1348210db224Sericheng 
1349da14cebeSEric Cheng 	case DLD_QUERY:
1350da14cebeSEric Cheng 		return (mac_perim_held(dsp->ds_mh));
1351da14cebeSEric Cheng 	}
1352da14cebeSEric Cheng 	return (0);
1353da14cebeSEric Cheng }
13547c478bd9Sstevel@tonic-gate 
1355da14cebeSEric Cheng static int
dld_capab_direct(dld_str_t * dsp,void * data,uint_t flags)1356da14cebeSEric Cheng dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags)
1357da14cebeSEric Cheng {
1358da14cebeSEric Cheng 	dld_capab_direct_t	*direct = data;
13597c478bd9Sstevel@tonic-gate 
1360da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
13617c478bd9Sstevel@tonic-gate 
1362da14cebeSEric Cheng 	switch (flags) {
1363da14cebeSEric Cheng 	case DLD_ENABLE:
1364da14cebeSEric Cheng 		dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf,
1365da14cebeSEric Cheng 		    direct->di_rx_ch);
1366ae6aa22aSVenugopal Iyer 
1367da14cebeSEric Cheng 		direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put;
1368da14cebeSEric Cheng 		direct->di_tx_dh = dsp;
1369da14cebeSEric Cheng 		direct->di_tx_cb_df = (uintptr_t)mac_client_tx_notify;
1370da14cebeSEric Cheng 		direct->di_tx_cb_dh = dsp->ds_mch;
1371ae6aa22aSVenugopal Iyer 		direct->di_tx_fctl_df = (uintptr_t)mac_tx_is_flow_blocked;
1372ae6aa22aSVenugopal Iyer 		direct->di_tx_fctl_dh = dsp->ds_mch;
1373ae6aa22aSVenugopal Iyer 
1374da14cebeSEric Cheng 		dsp->ds_direct = B_TRUE;
1375da14cebeSEric Cheng 
1376da14cebeSEric Cheng 		return (0);
1377da14cebeSEric Cheng 
1378da14cebeSEric Cheng 	case DLD_DISABLE:
1379da14cebeSEric Cheng 		dls_rx_set(dsp, (dsp->ds_mode == DLD_FASTPATH) ?
1380da14cebeSEric Cheng 		    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
1381da14cebeSEric Cheng 		dsp->ds_direct = B_FALSE;
1382da14cebeSEric Cheng 
1383da14cebeSEric Cheng 		return (0);
1384da14cebeSEric Cheng 	}
1385da14cebeSEric Cheng 	return (ENOTSUP);
13867c478bd9Sstevel@tonic-gate }
13877c478bd9Sstevel@tonic-gate 
1388da14cebeSEric Cheng /*
138984de666eSRyan Zezeski  * This function is misnamed. All polling and fanouts are run out of
139084de666eSRyan Zezeski  * the lower MAC for VNICs and out of the MAC for NICs. The
139184de666eSRyan Zezeski  * availability of Rx rings and promiscous mode is taken care of
139284de666eSRyan Zezeski  * between the soft ring set (mac_srs), the Rx ring, and the SW
139384de666eSRyan Zezeski  * classifier. Fanout, if necessary, is done by the soft rings that
139484de666eSRyan Zezeski  * are part of the SRS. By default the SRS divvies up the packets
139584de666eSRyan Zezeski  * based on protocol: TCP, UDP, or Other (OTH).
1396da14cebeSEric Cheng  *
139784de666eSRyan Zezeski  * The SRS (or its associated soft rings) always store the ill_rx_ring
1398da14cebeSEric Cheng  * (the cookie returned when they registered with IP during plumb) as their
1399da14cebeSEric Cheng  * 2nd argument which is passed up as mac_resource_handle_t. The upcall
1400da14cebeSEric Cheng  * function and 1st argument is what the caller registered when they
1401da14cebeSEric Cheng  * called mac_rx_classify_flow_add() to register the flow. For VNIC,
1402da14cebeSEric Cheng  * the function is vnic_rx and argument is vnic_t. For regular NIC
1403da14cebeSEric Cheng  * case, it mac_rx_default and mac_handle_t. As explained above, the
140484de666eSRyan Zezeski  * SRS (or its soft ring) will add the ill_rx_ring (mac_resource_handle_t)
1405da14cebeSEric Cheng  * from its stored 2nd argument.
1406da14cebeSEric Cheng  */
1407da14cebeSEric Cheng static int
dld_capab_poll_enable(dld_str_t * dsp,dld_capab_poll_t * poll)1408da14cebeSEric Cheng dld_capab_poll_enable(dld_str_t *dsp, dld_capab_poll_t *poll)
14097c478bd9Sstevel@tonic-gate {
1410da14cebeSEric Cheng 	if (dsp->ds_polling)
1411da14cebeSEric Cheng 		return (EINVAL);
14127c478bd9Sstevel@tonic-gate 
1413da14cebeSEric Cheng 	if ((dld_opt & DLD_OPT_NO_POLL) != 0 || dsp->ds_mode == DLD_RAW)
1414da14cebeSEric Cheng 		return (ENOTSUP);
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 	/*
141784de666eSRyan Zezeski 	 * Enable client polling if and only if DLS bypass is
141884de666eSRyan Zezeski 	 * possible. Some traffic requires DLS processing in the Rx
141984de666eSRyan Zezeski 	 * data path. In such a case we can neither allow the client
142084de666eSRyan Zezeski 	 * (IP) to directly poll the soft ring (since DLS processing
142184de666eSRyan Zezeski 	 * hasn't been done) nor can we allow DLS bypass.
14227c478bd9Sstevel@tonic-gate 	 */
1423da14cebeSEric Cheng 	if (!mac_rx_bypass_set(dsp->ds_mch, dsp->ds_rx, dsp->ds_rx_arg))
1424da14cebeSEric Cheng 		return (ENOTSUP);
14257c478bd9Sstevel@tonic-gate 
14267c478bd9Sstevel@tonic-gate 	/*
1427da14cebeSEric Cheng 	 * Register soft ring resources. This will come in handy later if
1428da14cebeSEric Cheng 	 * the user decides to modify CPU bindings to use more CPUs for the
1429da14cebeSEric Cheng 	 * device in which case we will switch to fanout using soft rings.
14307c478bd9Sstevel@tonic-gate 	 */
1431da14cebeSEric Cheng 	mac_resource_set_common(dsp->ds_mch,
1432da14cebeSEric Cheng 	    (mac_resource_add_t)poll->poll_ring_add_cf,
1433da14cebeSEric Cheng 	    (mac_resource_remove_t)poll->poll_ring_remove_cf,
1434da14cebeSEric Cheng 	    (mac_resource_quiesce_t)poll->poll_ring_quiesce_cf,
1435da14cebeSEric Cheng 	    (mac_resource_restart_t)poll->poll_ring_restart_cf,
1436da14cebeSEric Cheng 	    (mac_resource_bind_t)poll->poll_ring_bind_cf,
1437da14cebeSEric Cheng 	    poll->poll_ring_ch);
14387c478bd9Sstevel@tonic-gate 
1439da14cebeSEric Cheng 	mac_client_poll_enable(dsp->ds_mch);
14407c478bd9Sstevel@tonic-gate 
14417c478bd9Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
1442da14cebeSEric Cheng 	return (0);
14437c478bd9Sstevel@tonic-gate }
14447c478bd9Sstevel@tonic-gate 
1445da14cebeSEric Cheng /* ARGSUSED */
1446da14cebeSEric Cheng static int
dld_capab_poll_disable(dld_str_t * dsp,dld_capab_poll_t * poll)1447da14cebeSEric Cheng dld_capab_poll_disable(dld_str_t *dsp, dld_capab_poll_t *poll)
14484b46d1efSkrgopi {
1449da14cebeSEric Cheng 	if (!dsp->ds_polling)
1450da14cebeSEric Cheng 		return (EINVAL);
14514b46d1efSkrgopi 
1452da14cebeSEric Cheng 	mac_client_poll_disable(dsp->ds_mch);
1453da14cebeSEric Cheng 	mac_resource_set(dsp->ds_mch, NULL, NULL);
14544b46d1efSkrgopi 
1455da14cebeSEric Cheng 	dsp->ds_polling = B_FALSE;
1456da14cebeSEric Cheng 	return (0);
14574b46d1efSkrgopi }
14584b46d1efSkrgopi 
1459da14cebeSEric Cheng static int
dld_capab_poll(dld_str_t * dsp,void * data,uint_t flags)1460da14cebeSEric Cheng dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags)
14614b46d1efSkrgopi {
1462da14cebeSEric Cheng 	dld_capab_poll_t	*poll = data;
14634b46d1efSkrgopi 
1464da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
14654b46d1efSkrgopi 
1466da14cebeSEric Cheng 	switch (flags) {
1467da14cebeSEric Cheng 	case DLD_ENABLE:
1468da14cebeSEric Cheng 		return (dld_capab_poll_enable(dsp, poll));
1469da14cebeSEric Cheng 	case DLD_DISABLE:
1470da14cebeSEric Cheng 		return (dld_capab_poll_disable(dsp, poll));
1471da14cebeSEric Cheng 	}
1472da14cebeSEric Cheng 	return (ENOTSUP);
1473da14cebeSEric Cheng }
14744b46d1efSkrgopi 
1475da14cebeSEric Cheng static int
dld_capab_lso(dld_str_t * dsp,void * data,uint_t flags)1476da14cebeSEric Cheng dld_capab_lso(dld_str_t *dsp, void *data, uint_t flags)
1477da14cebeSEric Cheng {
1478da14cebeSEric Cheng 	dld_capab_lso_t		*lso = data;
1479da14cebeSEric Cheng 
1480da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1481da14cebeSEric Cheng 
1482da14cebeSEric Cheng 	switch (flags) {
1483da14cebeSEric Cheng 	case DLD_ENABLE: {
1484da14cebeSEric Cheng 		mac_capab_lso_t		mac_lso;
1485da14cebeSEric Cheng 
1486da14cebeSEric Cheng 		/*
1487da14cebeSEric Cheng 		 * Check if LSO is supported on this MAC & enable LSO
1488da14cebeSEric Cheng 		 * accordingly.
1489da14cebeSEric Cheng 		 */
1490da14cebeSEric Cheng 		if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) {
149162366fbbSRobert Mustacchi 			lso->lso_max_tcpv4 = mac_lso.lso_basic_tcp_ipv4.lso_max;
149262366fbbSRobert Mustacchi 			lso->lso_max_tcpv6 = mac_lso.lso_basic_tcp_ipv6.lso_max;
1493da14cebeSEric Cheng 			lso->lso_flags = 0;
1494da14cebeSEric Cheng 			/* translate the flag for mac clients */
1495da14cebeSEric Cheng 			if ((mac_lso.lso_flags & LSO_TX_BASIC_TCP_IPV4) != 0)
1496bd670b35SErik Nordmark 				lso->lso_flags |= DLD_LSO_BASIC_TCP_IPV4;
149762366fbbSRobert Mustacchi 			if ((mac_lso.lso_flags & LSO_TX_BASIC_TCP_IPV6) != 0)
149862366fbbSRobert Mustacchi 				lso->lso_flags |= DLD_LSO_BASIC_TCP_IPV6;
149962366fbbSRobert Mustacchi 			dsp->ds_lso = lso->lso_flags != 0;
150062366fbbSRobert Mustacchi 			/*
150162366fbbSRobert Mustacchi 			 * DLS uses this to try and make sure that a raw ioctl
150262366fbbSRobert Mustacchi 			 * doesn't send too much data, but doesn't currently
150362366fbbSRobert Mustacchi 			 * check the actual SAP that is sending this (or that
150462366fbbSRobert Mustacchi 			 * it's TCP). So for now, just use the max value here.
150562366fbbSRobert Mustacchi 			 */
150662366fbbSRobert Mustacchi 			dsp->ds_lso_max = MAX(lso->lso_max_tcpv4,
150762366fbbSRobert Mustacchi 			    lso->lso_max_tcpv6);
1508da14cebeSEric Cheng 		} else {
1509da14cebeSEric Cheng 			dsp->ds_lso = B_FALSE;
1510da14cebeSEric Cheng 			dsp->ds_lso_max = 0;
1511da14cebeSEric Cheng 			return (ENOTSUP);
1512da14cebeSEric Cheng 		}
1513da14cebeSEric Cheng 		return (0);
1514da14cebeSEric Cheng 	}
1515da14cebeSEric Cheng 	case DLD_DISABLE: {
1516da14cebeSEric Cheng 		dsp->ds_lso = B_FALSE;
1517da14cebeSEric Cheng 		dsp->ds_lso_max = 0;
1518da14cebeSEric Cheng 		return (0);
1519da14cebeSEric Cheng 	}
1520da14cebeSEric Cheng 	}
1521da14cebeSEric Cheng 	return (ENOTSUP);
15224b46d1efSkrgopi }
15234b46d1efSkrgopi 
1524da14cebeSEric Cheng static int
dld_capab(dld_str_t * dsp,uint_t type,void * data,uint_t flags)1525da14cebeSEric Cheng dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags)
15264b46d1efSkrgopi {
1527da14cebeSEric Cheng 	int	err;
15284b46d1efSkrgopi 
1529da14cebeSEric Cheng 	/*
1530da14cebeSEric Cheng 	 * Don't enable direct callback capabilities unless the caller is
1531da14cebeSEric Cheng 	 * the IP client. When a module is inserted in a stream (_I_INSERT)
1532da14cebeSEric Cheng 	 * the stack initiates capability disable, but due to races, the
1533da14cebeSEric Cheng 	 * module insertion may complete before the capability disable
1534da14cebeSEric Cheng 	 * completes. So we limit the check to DLD_ENABLE case.
1535da14cebeSEric Cheng 	 */
1536da14cebeSEric Cheng 	if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) &&
153762366fbbSRobert Mustacchi 	    (!(dsp->ds_sap == ETHERTYPE_IP || dsp->ds_sap == ETHERTYPE_IPV6) ||
15388d4cf8d8S 	    !check_mod_above(dsp->ds_rq, "ip"))) {
1539da14cebeSEric Cheng 		return (ENOTSUP);
15404b46d1efSkrgopi 	}
1541da14cebeSEric Cheng 
1542da14cebeSEric Cheng 	switch (type) {
1543da14cebeSEric Cheng 	case DLD_CAPAB_DIRECT:
154462366fbbSRobert Mustacchi 		if (dsp->ds_sap == ETHERTYPE_IPV6) {
154562366fbbSRobert Mustacchi 			err = ENOTSUP;
154662366fbbSRobert Mustacchi 			break;
154762366fbbSRobert Mustacchi 		}
1548da14cebeSEric Cheng 		err = dld_capab_direct(dsp, data, flags);
1549da14cebeSEric Cheng 		break;
1550da14cebeSEric Cheng 
1551da14cebeSEric Cheng 	case DLD_CAPAB_POLL:
155262366fbbSRobert Mustacchi 		if (dsp->ds_sap == ETHERTYPE_IPV6) {
155362366fbbSRobert Mustacchi 			err = ENOTSUP;
155462366fbbSRobert Mustacchi 			break;
155562366fbbSRobert Mustacchi 		}
1556da14cebeSEric Cheng 		err =  dld_capab_poll(dsp, data, flags);
1557da14cebeSEric Cheng 		break;
1558da14cebeSEric Cheng 
1559da14cebeSEric Cheng 	case DLD_CAPAB_PERIM:
1560da14cebeSEric Cheng 		err = dld_capab_perim(dsp, data, flags);
1561da14cebeSEric Cheng 		break;
1562da14cebeSEric Cheng 
1563da14cebeSEric Cheng 	case DLD_CAPAB_LSO:
1564da14cebeSEric Cheng 		err = dld_capab_lso(dsp, data, flags);
1565da14cebeSEric Cheng 		break;
1566da14cebeSEric Cheng 
1567da14cebeSEric Cheng 	default:
1568da14cebeSEric Cheng 		err = ENOTSUP;
1569da14cebeSEric Cheng 		break;
1570da14cebeSEric Cheng 	}
1571da14cebeSEric Cheng 
1572da14cebeSEric Cheng 	return (err);
15734b46d1efSkrgopi }
15744b46d1efSkrgopi 
15757c478bd9Sstevel@tonic-gate /*
15767c478bd9Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
15777c478bd9Sstevel@tonic-gate  */
1578da14cebeSEric Cheng static void
proto_capability_advertise(dld_str_t * dsp,mblk_t * mp)1579210db224Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
15807c478bd9Sstevel@tonic-gate {
15817c478bd9Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
15827c478bd9Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
15837c478bd9Sstevel@tonic-gate 	size_t			subsize;
1584da14cebeSEric Cheng 	dl_capab_dld_t		dld;
15857c478bd9Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
15867c478bd9Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
15871cb875aeSCathy Zhou 	dl_capab_vrrp_t		vrrp;
15881cb875aeSCathy Zhou 	mac_capab_vrrp_t	vrrp_capab;
15897c478bd9Sstevel@tonic-gate 	uint8_t			*ptr;
1590210db224Sericheng 	queue_t			*q = dsp->ds_wq;
1591210db224Sericheng 	mblk_t			*mp1;
1592d62bc4baSyz 	boolean_t		hcksum_capable = B_FALSE;
1593d62bc4baSyz 	boolean_t		zcopy_capable = B_FALSE;
1594da14cebeSEric Cheng 	boolean_t		dld_capable = B_FALSE;
15951cb875aeSCathy Zhou 	boolean_t		vrrp_capable = B_FALSE;
15967c478bd9Sstevel@tonic-gate 
15977c478bd9Sstevel@tonic-gate 	/*
15987c478bd9Sstevel@tonic-gate 	 * Initially assume no capabilities.
15997c478bd9Sstevel@tonic-gate 	 */
16007c478bd9Sstevel@tonic-gate 	subsize = 0;
16017c478bd9Sstevel@tonic-gate 
16027c478bd9Sstevel@tonic-gate 	/*
16039056fcebSCathy Zhou 	 * Check if checksum offload is supported on this MAC.
16047c478bd9Sstevel@tonic-gate 	 */
1605d62bc4baSyz 	bzero(&hcksum, sizeof (dl_capab_hcksum_t));
16069056fcebSCathy Zhou 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
1607ba2e4443Sseb 	    &hcksum.hcksum_txflags)) {
1608d62bc4baSyz 		if (hcksum.hcksum_txflags != 0) {
1609d62bc4baSyz 			hcksum_capable = B_TRUE;
1610d62bc4baSyz 			subsize += sizeof (dl_capability_sub_t) +
1611d62bc4baSyz 			    sizeof (dl_capab_hcksum_t);
1612d62bc4baSyz 		}
16137c478bd9Sstevel@tonic-gate 	}
16147c478bd9Sstevel@tonic-gate 
16157c478bd9Sstevel@tonic-gate 	/*
1616d62bc4baSyz 	 * Check if zerocopy is supported on this interface.
1617d62bc4baSyz 	 * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled
1618d62bc4baSyz 	 * then reserve space for that capability.
16197c478bd9Sstevel@tonic-gate 	 */
1620d62bc4baSyz 	if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) &&
1621d62bc4baSyz 	    !(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
1622d62bc4baSyz 		zcopy_capable = B_TRUE;
16237c478bd9Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
16247c478bd9Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
16257c478bd9Sstevel@tonic-gate 	}
16267c478bd9Sstevel@tonic-gate 
1627da14cebeSEric Cheng 	/*
1628da14cebeSEric Cheng 	 * Direct capability negotiation interface between IP and DLD
1629da14cebeSEric Cheng 	 */
163062366fbbSRobert Mustacchi 	if ((dsp->ds_sap == ETHERTYPE_IP || dsp->ds_sap == ETHERTYPE_IPV6) &&
163162366fbbSRobert Mustacchi 	    check_mod_above(dsp->ds_rq, "ip")) {
1632da14cebeSEric Cheng 		dld_capable = B_TRUE;
1633da14cebeSEric Cheng 		subsize += sizeof (dl_capability_sub_t) +
1634da14cebeSEric Cheng 		    sizeof (dl_capab_dld_t);
1635da14cebeSEric Cheng 	}
1636da14cebeSEric Cheng 
16371cb875aeSCathy Zhou 	/*
16381cb875aeSCathy Zhou 	 * Check if vrrp is supported on this interface. If so, reserve
16391cb875aeSCathy Zhou 	 * space for that capability.
16401cb875aeSCathy Zhou 	 */
16411cb875aeSCathy Zhou 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_VRRP, &vrrp_capab)) {
16421cb875aeSCathy Zhou 		vrrp_capable = B_TRUE;
16431cb875aeSCathy Zhou 		subsize += sizeof (dl_capability_sub_t) +
16441cb875aeSCathy Zhou 		    sizeof (dl_capab_vrrp_t);
16451cb875aeSCathy Zhou 	}
16461cb875aeSCathy Zhou 
16477c478bd9Sstevel@tonic-gate 	/*
1648210db224Sericheng 	 * If there are no capabilities to advertise or if we
1649210db224Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
16507c478bd9Sstevel@tonic-gate 	 */
16514b46d1efSkrgopi 	if ((mp1 = reallocb(mp,
1652210db224Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1653210db224Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1654da14cebeSEric Cheng 		return;
16557c478bd9Sstevel@tonic-gate 	}
16567c478bd9Sstevel@tonic-gate 
1657210db224Sericheng 	mp = mp1;
1658210db224Sericheng 	DB_TYPE(mp) = M_PROTO;
1659210db224Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1660210db224Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
16617c478bd9Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
16627c478bd9Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
16637c478bd9Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
16647c478bd9Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
16657c478bd9Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
16667c478bd9Sstevel@tonic-gate 
16677c478bd9Sstevel@tonic-gate 	/*
16687c478bd9Sstevel@tonic-gate 	 * TCP/IP checksum offload.
16697c478bd9Sstevel@tonic-gate 	 */
1670d62bc4baSyz 	if (hcksum_capable) {
16717c478bd9Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
16747c478bd9Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
16757c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
16767c478bd9Sstevel@tonic-gate 
16777c478bd9Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
16787c478bd9Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
16797c478bd9Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
16807c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
16817c478bd9Sstevel@tonic-gate 	}
16827c478bd9Sstevel@tonic-gate 
16837c478bd9Sstevel@tonic-gate 	/*
16847c478bd9Sstevel@tonic-gate 	 * Zero copy
16857c478bd9Sstevel@tonic-gate 	 */
1686d62bc4baSyz 	if (zcopy_capable) {
16877c478bd9Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
16887c478bd9Sstevel@tonic-gate 
16897c478bd9Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
16907c478bd9Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
16917c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
16927c478bd9Sstevel@tonic-gate 
16937c478bd9Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
16947c478bd9Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
16957c478bd9Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
16967c478bd9Sstevel@tonic-gate 
16977c478bd9Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
16987c478bd9Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
16997c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
17007c478bd9Sstevel@tonic-gate 	}
17017c478bd9Sstevel@tonic-gate 
17021cb875aeSCathy Zhou 	/*
17031cb875aeSCathy Zhou 	 * VRRP capability negotiation
17041cb875aeSCathy Zhou 	 */
17051cb875aeSCathy Zhou 	if (vrrp_capable) {
17061cb875aeSCathy Zhou 		dlsp = (dl_capability_sub_t *)ptr;
17071cb875aeSCathy Zhou 		dlsp->dl_cap = DL_CAPAB_VRRP;
17081cb875aeSCathy Zhou 		dlsp->dl_length = sizeof (dl_capab_vrrp_t);
17091cb875aeSCathy Zhou 		ptr += sizeof (dl_capability_sub_t);
17101cb875aeSCathy Zhou 
17111cb875aeSCathy Zhou 		bzero(&vrrp, sizeof (dl_capab_vrrp_t));
17121cb875aeSCathy Zhou 		vrrp.vrrp_af = vrrp_capab.mcv_af;
17131cb875aeSCathy Zhou 		bcopy(&vrrp, ptr, sizeof (dl_capab_vrrp_t));
17141cb875aeSCathy Zhou 		ptr += sizeof (dl_capab_vrrp_t);
17151cb875aeSCathy Zhou 	}
17161cb875aeSCathy Zhou 
1717da14cebeSEric Cheng 	/*
1718da14cebeSEric Cheng 	 * Direct capability negotiation interface between IP and DLD.
1719da14cebeSEric Cheng 	 * Refer to dld.h for details.
1720da14cebeSEric Cheng 	 */
1721da14cebeSEric Cheng 	if (dld_capable) {
1722da14cebeSEric Cheng 		dlsp = (dl_capability_sub_t *)ptr;
1723da14cebeSEric Cheng 		dlsp->dl_cap = DL_CAPAB_DLD;
1724da14cebeSEric Cheng 		dlsp->dl_length = sizeof (dl_capab_dld_t);
1725da14cebeSEric Cheng 		ptr += sizeof (dl_capability_sub_t);
17267c478bd9Sstevel@tonic-gate 
1727da14cebeSEric Cheng 		bzero(&dld, sizeof (dl_capab_dld_t));
1728da14cebeSEric Cheng 		dld.dld_version = DLD_CURRENT_VERSION;
1729da14cebeSEric Cheng 		dld.dld_capab = (uintptr_t)dld_capab;
1730da14cebeSEric Cheng 		dld.dld_capab_handle = (uintptr_t)dsp;
1731da14cebeSEric Cheng 
1732da14cebeSEric Cheng 		dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq);
1733da14cebeSEric Cheng 		bcopy(&dld, ptr, sizeof (dl_capab_dld_t));
1734da14cebeSEric Cheng 		ptr += sizeof (dl_capab_dld_t);
1735da14cebeSEric Cheng 	}
1736da14cebeSEric Cheng 
1737da14cebeSEric Cheng 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1738210db224Sericheng 	qreply(q, mp);
17397c478bd9Sstevel@tonic-gate }
17408fb46f24Syz 
17418fb46f24Syz /*
17428fb46f24Syz  * Disable any enabled capabilities.
17438fb46f24Syz  */
17448fb46f24Syz void
dld_capabilities_disable(dld_str_t * dsp)17458fb46f24Syz dld_capabilities_disable(dld_str_t *dsp)
17468fb46f24Syz {
17478fb46f24Syz 	if (dsp->ds_polling)
1748da14cebeSEric Cheng 		(void) dld_capab_poll_disable(dsp, NULL);
17498fb46f24Syz }
1750