xref: /titanic_50/usr/src/uts/common/io/dld/dld_proto.c (revision 4962fb4c48c1a13939e376e773be60fe6273e296)
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.
24*4962fb4cSDan McDonald  * Copyright 2017 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,
45d62bc4baSyz147064     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 	}
1297c478bd9Sstevel@tonic-gate }
1307c478bd9Sstevel@tonic-gate 
131210db224Sericheng #define	NEG(x)	-(x)
1327c478bd9Sstevel@tonic-gate typedef struct dl_info_ack_wrapper {
1337c478bd9Sstevel@tonic-gate 	dl_info_ack_t		dl_info;
134ba2e4443Sseb 	uint8_t			dl_addr[MAXMACADDRLEN + sizeof (uint16_t)];
135ba2e4443Sseb 	uint8_t			dl_brdcst_addr[MAXMACADDRLEN];
1367c478bd9Sstevel@tonic-gate 	dl_qos_cl_range1_t	dl_qos_range1;
1377c478bd9Sstevel@tonic-gate 	dl_qos_cl_sel1_t	dl_qos_sel1;
1387c478bd9Sstevel@tonic-gate } dl_info_ack_wrapper_t;
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate /*
141210db224Sericheng  * DL_INFO_REQ
1427c478bd9Sstevel@tonic-gate  */
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)
1457c478bd9Sstevel@tonic-gate {
1467c478bd9Sstevel@tonic-gate 	dl_info_ack_wrapper_t	*dlwp;
1477c478bd9Sstevel@tonic-gate 	dl_info_ack_t		*dlp;
1487c478bd9Sstevel@tonic-gate 	dl_qos_cl_sel1_t	*selp;
1497c478bd9Sstevel@tonic-gate 	dl_qos_cl_range1_t	*rangep;
1507c478bd9Sstevel@tonic-gate 	uint8_t			*addr;
1517c478bd9Sstevel@tonic-gate 	uint8_t			*brdcst_addr;
1527c478bd9Sstevel@tonic-gate 	uint_t			addr_length;
1537c478bd9Sstevel@tonic-gate 	uint_t			sap_length;
154210db224Sericheng 	mac_info_t		minfo;
155210db224Sericheng 	mac_info_t		*minfop;
156210db224Sericheng 	queue_t			*q = dsp->ds_wq;
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	/*
1597c478bd9Sstevel@tonic-gate 	 * Swap the request message for one large enough to contain the
1607c478bd9Sstevel@tonic-gate 	 * wrapper structure defined above.
1617c478bd9Sstevel@tonic-gate 	 */
162210db224Sericheng 	if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
1637c478bd9Sstevel@tonic-gate 	    M_PCPROTO, 0)) == NULL)
164da14cebeSEric Cheng 		return;
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
1677c478bd9Sstevel@tonic-gate 	dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 	dlp = &(dlwp->dl_info);
1707c478bd9Sstevel@tonic-gate 	ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	dlp->dl_primitive = DL_INFO_ACK;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	/*
1757c478bd9Sstevel@tonic-gate 	 * Set up the sub-structure pointers.
1767c478bd9Sstevel@tonic-gate 	 */
1777c478bd9Sstevel@tonic-gate 	addr = dlwp->dl_addr;
1787c478bd9Sstevel@tonic-gate 	brdcst_addr = dlwp->dl_brdcst_addr;
1797c478bd9Sstevel@tonic-gate 	rangep = &(dlwp->dl_qos_range1);
1807c478bd9Sstevel@tonic-gate 	selp = &(dlwp->dl_qos_sel1);
1817c478bd9Sstevel@tonic-gate 
1827c478bd9Sstevel@tonic-gate 	/*
1837c478bd9Sstevel@tonic-gate 	 * This driver supports only version 2 connectionless DLPI provider
1847c478bd9Sstevel@tonic-gate 	 * nodes.
1857c478bd9Sstevel@tonic-gate 	 */
1867c478bd9Sstevel@tonic-gate 	dlp->dl_service_mode = DL_CLDLS;
1877c478bd9Sstevel@tonic-gate 	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;
1937c478bd9Sstevel@tonic-gate 	ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
1947c478bd9Sstevel@tonic-gate 	    dlp->dl_provider_style == DL_STYLE2);
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	/*
1977c478bd9Sstevel@tonic-gate 	 * Set the current DLPI state.
1987c478bd9Sstevel@tonic-gate 	 */
1997c478bd9Sstevel@tonic-gate 	dlp->dl_current_state = dsp->ds_dlstate;
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	/*
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
2047c478bd9Sstevel@tonic-gate 	 * being completed.
2057c478bd9Sstevel@tonic-gate 	 */
2067c478bd9Sstevel@tonic-gate 	dlp->dl_mac_type = DL_ETHER;
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	/*
209210db224Sericheng 	 * If the stream is not at least attached we try to retrieve the
210210db224Sericheng 	 * mac_info using mac_info_get()
2117c478bd9Sstevel@tonic-gate 	 */
2127c478bd9Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
2137c478bd9Sstevel@tonic-gate 	    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 			 */
2197c478bd9Sstevel@tonic-gate 			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);
226210db224Sericheng 	}
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 	/*
2297c478bd9Sstevel@tonic-gate 	 * Set the media type (properly this time).
2307c478bd9Sstevel@tonic-gate 	 */
2310ba2cbe9Sxc151355 	if (dsp->ds_native)
2320ba2cbe9Sxc151355 		dlp->dl_mac_type = minfop->mi_nativemedia;
2330ba2cbe9Sxc151355 	else
234210db224Sericheng 		dlp->dl_mac_type = minfop->mi_media;
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 	/*
2377c478bd9Sstevel@tonic-gate 	 * Set the DLSAP length. We only support 16 bit values and they
2387c478bd9Sstevel@tonic-gate 	 * appear after the MAC address portion of DLSAP addresses.
2397c478bd9Sstevel@tonic-gate 	 */
2407c478bd9Sstevel@tonic-gate 	sap_length = sizeof (uint16_t);
2417c478bd9Sstevel@tonic-gate 	dlp->dl_sap_length = NEG(sap_length);
2427c478bd9Sstevel@tonic-gate 
243210db224Sericheng 	addr_length = minfop->mi_addr_length;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	/*
2467c478bd9Sstevel@tonic-gate 	 * Copy in the media broadcast address.
2477c478bd9Sstevel@tonic-gate 	 */
248ba2e4443Sseb 	if (minfop->mi_brdcst_addr != NULL) {
249ba2e4443Sseb 		dlp->dl_brdcst_addr_offset =
250ba2e4443Sseb 		    (uintptr_t)brdcst_addr - (uintptr_t)dlp;
251210db224Sericheng 		bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
2527c478bd9Sstevel@tonic-gate 		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)) {
2607c478bd9Sstevel@tonic-gate 		dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
2617c478bd9Sstevel@tonic-gate 		dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 		rangep->dl_qos_type = DL_QOS_CL_RANGE1;
2647c478bd9Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
2657c478bd9Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
2667c478bd9Sstevel@tonic-gate 		rangep->dl_protection.dl_min = DL_UNKNOWN;
2677c478bd9Sstevel@tonic-gate 		rangep->dl_protection.dl_max = DL_UNKNOWN;
2687c478bd9Sstevel@tonic-gate 		rangep->dl_residual_error = DL_UNKNOWN;
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate 		/*
2717c478bd9Sstevel@tonic-gate 		 * Specify the supported range of priorities.
2727c478bd9Sstevel@tonic-gate 		 */
2737c478bd9Sstevel@tonic-gate 		rangep->dl_priority.dl_min = 0;
2747c478bd9Sstevel@tonic-gate 		rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 		dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
2777c478bd9Sstevel@tonic-gate 		dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 		selp->dl_qos_type = DL_QOS_CL_SEL1;
2807c478bd9Sstevel@tonic-gate 		selp->dl_trans_delay = DL_UNKNOWN;
2817c478bd9Sstevel@tonic-gate 		selp->dl_protection = DL_UNKNOWN;
2827c478bd9Sstevel@tonic-gate 		selp->dl_residual_error = DL_UNKNOWN;
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 		/*
2857c478bd9Sstevel@tonic-gate 		 * Specify the current priority (which can be changed by
2867c478bd9Sstevel@tonic-gate 		 * the DL_UDQOS_REQ primitive).
2877c478bd9Sstevel@tonic-gate 		 */
2887c478bd9Sstevel@tonic-gate 		selp->dl_priority = dsp->ds_pri;
289e75f0919SSebastien Roy 	}
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
2927c478bd9Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
2937c478bd9Sstevel@tonic-gate 		/*
2947c478bd9Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
2957c478bd9Sstevel@tonic-gate 		 * DLSAP address.
2967c478bd9Sstevel@tonic-gate 		 */
2977c478bd9Sstevel@tonic-gate 		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 
3017c478bd9Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3027c478bd9Sstevel@tonic-gate 	}
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 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);
3117c478bd9Sstevel@tonic-gate 
312210db224Sericheng 	qreply(q, mp);
3137c478bd9Sstevel@tonic-gate }
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate /*
316210db224Sericheng  * 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;
3297c478bd9Sstevel@tonic-gate 		goto failed;
330210db224Sericheng 	}
3317c478bd9Sstevel@tonic-gate 
332210db224Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
333210db224Sericheng 		dl_err = DL_OUTSTATE;
334210db224Sericheng 		goto failed;
335210db224Sericheng 	}
3367c478bd9Sstevel@tonic-gate 
337210db224Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
338210db224Sericheng 
339210db224Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
340210db224Sericheng 	if (err != 0) {
3417c478bd9Sstevel@tonic-gate 		switch (err) {
3427c478bd9Sstevel@tonic-gate 		case ENOENT:
3437c478bd9Sstevel@tonic-gate 			dl_err = DL_BADPPA;
3447c478bd9Sstevel@tonic-gate 			err = 0;
3457c478bd9Sstevel@tonic-gate 			break;
3467c478bd9Sstevel@tonic-gate 		default:
3477c478bd9Sstevel@tonic-gate 			dl_err = DL_SYSERR;
3487c478bd9Sstevel@tonic-gate 			break;
3497c478bd9Sstevel@tonic-gate 		}
3507c478bd9Sstevel@tonic-gate 		dsp->ds_dlstate = DL_UNATTACHED;
351210db224Sericheng 		goto failed;
352210db224Sericheng 	}
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;
369210db224Sericheng 
370210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
371210db224Sericheng 		dl_err = DL_BADPRIM;
372210db224Sericheng 		goto failed;
373210db224Sericheng 	}
374210db224Sericheng 
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;
383210db224Sericheng 	}
384210db224Sericheng 
385da14cebeSEric Cheng 	ASSERT(dsp->ds_datathr_cnt == 0);
386210db224Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
387210db224Sericheng 
388da14cebeSEric Cheng 	dld_str_detach(dsp);
389d62bc4baSyz147064 	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 /*
397210db224Sericheng  * 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;
4046f45d2aeSyz147064 	uint8_t		dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)];
4056f45d2aeSyz147064 	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 
413210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
414210db224Sericheng 		dl_err = DL_BADPRIM;
4157c478bd9Sstevel@tonic-gate 		goto failed;
416210db224Sericheng 	}
417210db224Sericheng 
418210db224Sericheng 	if (dlp->dl_xidtest_flg != 0) {
419210db224Sericheng 		dl_err = DL_NOAUTO;
420210db224Sericheng 		goto failed;
421210db224Sericheng 	}
422210db224Sericheng 
423210db224Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
424210db224Sericheng 		dl_err = DL_UNSUPPORTED;
425210db224Sericheng 		goto failed;
426210db224Sericheng 	}
427210db224Sericheng 
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 	}
439210db224Sericheng 
440da14cebeSEric Cheng 	dsp->ds_dlstate = DL_BIND_PENDING;
441210db224Sericheng 	/*
442210db224Sericheng 	 * Set the receive callback.
443210db224Sericheng 	 */
444da14cebeSEric Cheng 	dls_rx_set(dsp, (dsp->ds_mode == DLD_RAW) ?
445210db224Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
446210db224Sericheng 
447210db224Sericheng 	/*
448210db224Sericheng 	 * Bind the channel such that it can receive packets.
449210db224Sericheng 	 */
450d62bc4baSyz147064 	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 		}
465d62bc4baSyz147064 
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)
481da14cebeSEric Cheng 		mac_client_set_intr_cpu(mdip, dsp->ds_mch, intr_cpu);
482da14cebeSEric Cheng 
4837c478bd9Sstevel@tonic-gate 	/*
4847c478bd9Sstevel@tonic-gate 	 * Copy in MAC address.
4857c478bd9Sstevel@tonic-gate 	 */
4866f45d2aeSyz147064 	dlsap_addr_length = dsp->ds_mip->mi_addr_length;
487da14cebeSEric Cheng 	mac_unicast_primary_get(dsp->ds_mh, dlsap_addr);
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 	/*
4906f45d2aeSyz147064 	 * Copy in the SAP.
4917c478bd9Sstevel@tonic-gate 	 */
492d62bc4baSyz147064 	*(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap;
4936f45d2aeSyz147064 	dlsap_addr_length += sizeof (uint16_t);
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
4966f45d2aeSyz147064 	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);
5017c478bd9Sstevel@tonic-gate failed:
502210db224Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
503210db224Sericheng }
504210db224Sericheng 
505210db224Sericheng /*
506210db224Sericheng  * DL_UNBIND_REQ
507210db224Sericheng  */
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)
510210db224Sericheng {
511d62bc4baSyz147064 	queue_t		*q = dsp->ds_wq;
512d62bc4baSyz147064 	t_uscalar_t	dl_err;
513da14cebeSEric Cheng 	mac_perim_handle_t	mph;
514210db224Sericheng 
515d62bc4baSyz147064 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
516d62bc4baSyz147064 		dl_err = DL_BADPRIM;
517d62bc4baSyz147064 		goto failed;
518d62bc4baSyz147064 	}
519d62bc4baSyz147064 
520d62bc4baSyz147064 	if (dsp->ds_dlstate != DL_IDLE) {
521d62bc4baSyz147064 		dl_err = DL_OUTSTATE;
522d62bc4baSyz147064 		goto failed;
523d62bc4baSyz147064 	}
524210db224Sericheng 
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);
528210db224Sericheng 
529da14cebeSEric Cheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
530da14cebeSEric Cheng 	mutex_exit(&dsp->ds_lock);
531da14cebeSEric Cheng 
532da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
533210db224Sericheng 	/*
534210db224Sericheng 	 * Unbind the channel to stop packets being received.
535210db224Sericheng 	 */
5365ecc58b1SGirish Moodalbail 	dls_unbind(dsp);
537d62bc4baSyz147064 
538d62bc4baSyz147064 	/*
539210db224Sericheng 	 * Disable polling mode, if it is enabled.
540210db224Sericheng 	 */
541da14cebeSEric Cheng 	(void) dld_capab_poll_disable(dsp, NULL);
542d62bc4baSyz147064 
543d62bc4baSyz147064 	/*
5448347601bSyl150051 	 * Clear LSO flags.
5458347601bSyl150051 	 */
5468347601bSyl150051 	dsp->ds_lso = B_FALSE;
5478347601bSyl150051 	dsp->ds_lso_max = 0;
5488347601bSyl150051 
5498347601bSyl150051 	/*
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 
555da14cebeSEric Cheng 	/*
556210db224Sericheng 	 * Set the mode back to the default (unitdata).
557210db224Sericheng 	 */
558210db224Sericheng 	dsp->ds_mode = DLD_UNITDATA;
5596a0b2badSericheng 	dsp->ds_dlstate = DL_UNBOUND;
560210db224Sericheng 
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);
567210db224Sericheng }
568210db224Sericheng 
569210db224Sericheng /*
570210db224Sericheng  * DL_PROMISCON_REQ
571210db224Sericheng  */
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)
574210db224Sericheng {
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 	}
586210db224Sericheng 
587210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
588210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
589210db224Sericheng 		dl_err = DL_OUTSTATE;
590210db224Sericheng 		goto failed;
591210db224Sericheng 	}
592210db224Sericheng 
593cabb4db9SDan McDonald 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
594cabb4db9SDan McDonald 
595cabb4db9SDan McDonald 	new_flags = promisc_saved = dsp->ds_promisc;
596210db224Sericheng 	switch (dlp->dl_level) {
597210db224Sericheng 	case DL_PROMISC_SAP:
598cabb4db9SDan McDonald 		new_flags |= DLS_PROMISC_SAP;
599210db224Sericheng 		break;
600da14cebeSEric Cheng 
601210db224Sericheng 	case DL_PROMISC_MULTI:
602cabb4db9SDan McDonald 		new_flags |= DLS_PROMISC_MULTI;
603210db224Sericheng 		break;
604da14cebeSEric Cheng 
605210db224Sericheng 	case DL_PROMISC_PHYS:
606cabb4db9SDan McDonald 		new_flags |= DLS_PROMISC_PHYS;
6077c478bd9Sstevel@tonic-gate 		break;
608da14cebeSEric Cheng 
6097c478bd9Sstevel@tonic-gate 	default:
610210db224Sericheng 		dl_err = DL_NOTSUPPORTED;
611ad7ed3feSRobert Mustacchi 		goto failed2;
612210db224Sericheng 	}
613210db224Sericheng 
6145d460eafSCathy Zhou 	if ((promisc_saved == 0) && (err = dls_active_set(dsp)) != 0) {
615cabb4db9SDan McDonald 		ASSERT(dsp->ds_promisc == promisc_saved);
6167c478bd9Sstevel@tonic-gate 		dl_err = DL_SYSERR;
617da14cebeSEric Cheng 		goto failed2;
618210db224Sericheng 	}
619210db224Sericheng 
620210db224Sericheng 	/*
621210db224Sericheng 	 * Adjust channel promiscuity.
622210db224Sericheng 	 */
623cabb4db9SDan McDonald 	err = dls_promisc(dsp, new_flags);
624da14cebeSEric Cheng 
625210db224Sericheng 	if (err != 0) {
626210db224Sericheng 		dl_err = DL_SYSERR;
627da14cebeSEric Cheng 		dsp->ds_promisc = promisc_saved;
6285d460eafSCathy Zhou 		if (promisc_saved == 0)
6295d460eafSCathy Zhou 			dls_active_clear(dsp, B_FALSE);
630da14cebeSEric Cheng 		goto failed2;
631210db224Sericheng 	}
632210db224Sericheng 
633da14cebeSEric Cheng 	mac_perim_exit(mph);
634da14cebeSEric Cheng 
635210db224Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
636da14cebeSEric Cheng 	return;
637da14cebeSEric Cheng 
638da14cebeSEric Cheng failed2:
639da14cebeSEric Cheng 	mac_perim_exit(mph);
640210db224Sericheng failed:
641210db224Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
642210db224Sericheng }
643210db224Sericheng 
644210db224Sericheng /*
645210db224Sericheng  * DL_PROMISCOFF_REQ
646210db224Sericheng  */
647da14cebeSEric Cheng static void
proto_promiscoff_req(dld_str_t * dsp,mblk_t * mp)648da14cebeSEric Cheng proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp)
649210db224Sericheng {
650da14cebeSEric Cheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)mp->b_rptr;
651210db224Sericheng 	int		err = 0;
652210db224Sericheng 	t_uscalar_t	dl_err;
653cabb4db9SDan McDonald 	uint32_t	new_flags;
654210db224Sericheng 	queue_t		*q = dsp->ds_wq;
655da14cebeSEric Cheng 	mac_perim_handle_t	mph;
656210db224Sericheng 
657210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
658210db224Sericheng 		dl_err = DL_BADPRIM;
659210db224Sericheng 		goto failed;
660210db224Sericheng 	}
661210db224Sericheng 
662210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
663210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
664210db224Sericheng 		dl_err = DL_OUTSTATE;
665210db224Sericheng 		goto failed;
666210db224Sericheng 	}
667210db224Sericheng 
668cabb4db9SDan McDonald 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
669cabb4db9SDan McDonald 
670cabb4db9SDan McDonald 	new_flags = dsp->ds_promisc;
671210db224Sericheng 	switch (dlp->dl_level) {
672210db224Sericheng 	case DL_PROMISC_SAP:
673da14cebeSEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
674da14cebeSEric Cheng 			dl_err = DL_NOTENAB;
675*4962fb4cSDan McDonald 			goto failed2;
676da14cebeSEric Cheng 		}
677cabb4db9SDan McDonald 		new_flags &= ~DLS_PROMISC_SAP;
6787c478bd9Sstevel@tonic-gate 		break;
679da14cebeSEric Cheng 
680210db224Sericheng 	case DL_PROMISC_MULTI:
681da14cebeSEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
682da14cebeSEric Cheng 			dl_err = DL_NOTENAB;
683*4962fb4cSDan McDonald 			goto failed2;
684da14cebeSEric Cheng 		}
685cabb4db9SDan McDonald 		new_flags &= ~DLS_PROMISC_MULTI;
686210db224Sericheng 		break;
687da14cebeSEric Cheng 
688210db224Sericheng 	case DL_PROMISC_PHYS:
689da14cebeSEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
690da14cebeSEric Cheng 			dl_err = DL_NOTENAB;
691*4962fb4cSDan McDonald 			goto failed2;
692da14cebeSEric Cheng 		}
693cabb4db9SDan McDonald 		new_flags &= ~DLS_PROMISC_PHYS;
694210db224Sericheng 		break;
695da14cebeSEric Cheng 
696210db224Sericheng 	default:
697210db224Sericheng 		dl_err = DL_NOTSUPPORTED;
698*4962fb4cSDan McDonald 		goto failed2;
6997c478bd9Sstevel@tonic-gate 	}
7007c478bd9Sstevel@tonic-gate 
701da14cebeSEric Cheng 	/*
702da14cebeSEric Cheng 	 * Adjust channel promiscuity.
703da14cebeSEric Cheng 	 */
704cabb4db9SDan McDonald 	err = dls_promisc(dsp, new_flags);
705d62bc4baSyz147064 
706210db224Sericheng 	if (err != 0) {
707210db224Sericheng 		dl_err = DL_SYSERR;
708*4962fb4cSDan McDonald 		goto failed2;
709210db224Sericheng 	}
7105d460eafSCathy Zhou 
711cabb4db9SDan McDonald 	ASSERT(dsp->ds_promisc == new_flags);
7125d460eafSCathy Zhou 	if (dsp->ds_promisc == 0)
7135d460eafSCathy Zhou 		dls_active_clear(dsp, B_FALSE);
7145d460eafSCathy Zhou 
7155d460eafSCathy Zhou 	mac_perim_exit(mph);
7165d460eafSCathy Zhou 
717210db224Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
718da14cebeSEric Cheng 	return;
719*4962fb4cSDan McDonald failed2:
720*4962fb4cSDan McDonald 	mac_perim_exit(mph);
7217c478bd9Sstevel@tonic-gate failed:
722210db224Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
7237c478bd9Sstevel@tonic-gate }
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate /*
726210db224Sericheng  * DL_ENABMULTI_REQ
7277c478bd9Sstevel@tonic-gate  */
728da14cebeSEric Cheng static void
proto_enabmulti_req(dld_str_t * dsp,mblk_t * mp)729da14cebeSEric Cheng proto_enabmulti_req(dld_str_t *dsp, mblk_t *mp)
7307c478bd9Sstevel@tonic-gate {
731da14cebeSEric Cheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)mp->b_rptr;
732210db224Sericheng 	int		err = 0;
733210db224Sericheng 	t_uscalar_t	dl_err;
734210db224Sericheng 	queue_t		*q = dsp->ds_wq;
735da14cebeSEric Cheng 	mac_perim_handle_t	mph;
7367c478bd9Sstevel@tonic-gate 
737210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
738210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
739210db224Sericheng 		dl_err = DL_OUTSTATE;
7407c478bd9Sstevel@tonic-gate 		goto failed;
741210db224Sericheng 	}
7427c478bd9Sstevel@tonic-gate 
743210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
744210db224Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
745210db224Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
746210db224Sericheng 		dl_err = DL_BADPRIM;
747210db224Sericheng 		goto failed;
748210db224Sericheng 	}
7497c478bd9Sstevel@tonic-gate 
750da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
751da14cebeSEric Cheng 
7525d460eafSCathy Zhou 	if ((dsp->ds_dmap == NULL) && (err = dls_active_set(dsp)) != 0) {
753210db224Sericheng 		dl_err = DL_SYSERR;
754da14cebeSEric Cheng 		goto failed2;
755210db224Sericheng 	}
756210db224Sericheng 
757da14cebeSEric Cheng 	err = dls_multicst_add(dsp, mp->b_rptr + dlp->dl_addr_offset);
758210db224Sericheng 	if (err != 0) {
7597c478bd9Sstevel@tonic-gate 		switch (err) {
7607c478bd9Sstevel@tonic-gate 		case EINVAL:
7617c478bd9Sstevel@tonic-gate 			dl_err = DL_BADADDR;
7627c478bd9Sstevel@tonic-gate 			err = 0;
7637c478bd9Sstevel@tonic-gate 			break;
7647c478bd9Sstevel@tonic-gate 		case ENOSPC:
7657c478bd9Sstevel@tonic-gate 			dl_err = DL_TOOMANY;
7667c478bd9Sstevel@tonic-gate 			err = 0;
7677c478bd9Sstevel@tonic-gate 			break;
7687c478bd9Sstevel@tonic-gate 		default:
7697c478bd9Sstevel@tonic-gate 			dl_err = DL_SYSERR;
7707c478bd9Sstevel@tonic-gate 			break;
7717c478bd9Sstevel@tonic-gate 		}
7725d460eafSCathy Zhou 		if (dsp->ds_dmap == NULL)
7735d460eafSCathy Zhou 			dls_active_clear(dsp, B_FALSE);
774da14cebeSEric Cheng 		goto failed2;
775210db224Sericheng 	}
776210db224Sericheng 
777da14cebeSEric Cheng 	mac_perim_exit(mph);
778da14cebeSEric Cheng 
779210db224Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
780da14cebeSEric Cheng 	return;
781da14cebeSEric Cheng 
782da14cebeSEric Cheng failed2:
783da14cebeSEric Cheng 	mac_perim_exit(mph);
784210db224Sericheng failed:
785210db224Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
7867c478bd9Sstevel@tonic-gate }
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate /*
789210db224Sericheng  * DL_DISABMULTI_REQ
7907c478bd9Sstevel@tonic-gate  */
791da14cebeSEric Cheng static void
proto_disabmulti_req(dld_str_t * dsp,mblk_t * mp)792da14cebeSEric Cheng proto_disabmulti_req(dld_str_t *dsp, mblk_t *mp)
7937c478bd9Sstevel@tonic-gate {
794da14cebeSEric Cheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)mp->b_rptr;
795210db224Sericheng 	int		err = 0;
796210db224Sericheng 	t_uscalar_t	dl_err;
797210db224Sericheng 	queue_t		*q = dsp->ds_wq;
798da14cebeSEric Cheng 	mac_perim_handle_t	mph;
7997c478bd9Sstevel@tonic-gate 
800210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
801210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
802210db224Sericheng 		dl_err = DL_OUTSTATE;
8037c478bd9Sstevel@tonic-gate 		goto failed;
804210db224Sericheng 	}
8057c478bd9Sstevel@tonic-gate 
806210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
807210db224Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
808210db224Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
809210db224Sericheng 		dl_err = DL_BADPRIM;
810210db224Sericheng 		goto failed;
811210db224Sericheng 	}
8127c478bd9Sstevel@tonic-gate 
813da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
814da14cebeSEric Cheng 	err = dls_multicst_remove(dsp, mp->b_rptr + dlp->dl_addr_offset);
8155d460eafSCathy Zhou 	if ((err == 0) && (dsp->ds_dmap == NULL))
8165d460eafSCathy Zhou 		dls_active_clear(dsp, B_FALSE);
817da14cebeSEric Cheng 	mac_perim_exit(mph);
818da14cebeSEric Cheng 
819210db224Sericheng 	if (err != 0) {
8207c478bd9Sstevel@tonic-gate 	switch (err) {
8217c478bd9Sstevel@tonic-gate 		case EINVAL:
8227c478bd9Sstevel@tonic-gate 			dl_err = DL_BADADDR;
8237c478bd9Sstevel@tonic-gate 			err = 0;
8247c478bd9Sstevel@tonic-gate 			break;
825da14cebeSEric Cheng 
8267c478bd9Sstevel@tonic-gate 		case ENOENT:
8277c478bd9Sstevel@tonic-gate 			dl_err = DL_NOTENAB;
8287c478bd9Sstevel@tonic-gate 			err = 0;
8297c478bd9Sstevel@tonic-gate 			break;
830da14cebeSEric Cheng 
8317c478bd9Sstevel@tonic-gate 		default:
8327c478bd9Sstevel@tonic-gate 			dl_err = DL_SYSERR;
8337c478bd9Sstevel@tonic-gate 			break;
8347c478bd9Sstevel@tonic-gate 		}
835210db224Sericheng 		goto failed;
836210db224Sericheng 	}
837210db224Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
838da14cebeSEric Cheng 	return;
839210db224Sericheng failed:
840210db224Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
8417c478bd9Sstevel@tonic-gate }
8427c478bd9Sstevel@tonic-gate 
8437c478bd9Sstevel@tonic-gate /*
844210db224Sericheng  * DL_PHYS_ADDR_REQ
8457c478bd9Sstevel@tonic-gate  */
846da14cebeSEric Cheng static void
proto_physaddr_req(dld_str_t * dsp,mblk_t * mp)847da14cebeSEric Cheng proto_physaddr_req(dld_str_t *dsp, mblk_t *mp)
8487c478bd9Sstevel@tonic-gate {
849da14cebeSEric Cheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)mp->b_rptr;
850210db224Sericheng 	queue_t		*q = dsp->ds_wq;
8512b24ab6bSSebastien Roy 	t_uscalar_t	dl_err = 0;
8522b24ab6bSSebastien Roy 	char		*addr = NULL;
8537c478bd9Sstevel@tonic-gate 	uint_t		addr_length;
8547c478bd9Sstevel@tonic-gate 
855210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
856210db224Sericheng 		dl_err = DL_BADPRIM;
8572b24ab6bSSebastien Roy 		goto done;
858210db224Sericheng 	}
859210db224Sericheng 
860210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
861210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
862210db224Sericheng 		dl_err = DL_OUTSTATE;
8632b24ab6bSSebastien Roy 		goto done;
864210db224Sericheng 	}
865210db224Sericheng 
8667c478bd9Sstevel@tonic-gate 	addr_length = dsp->ds_mip->mi_addr_length;
867c08e5e1aSdr146992 	if (addr_length > 0) {
868da14cebeSEric Cheng 		addr = kmem_alloc(addr_length, KM_SLEEP);
8692b24ab6bSSebastien Roy 		switch (dlp->dl_addr_type) {
8702b24ab6bSSebastien Roy 		case DL_CURR_PHYS_ADDR:
871da14cebeSEric Cheng 			mac_unicast_primary_get(dsp->ds_mh, (uint8_t *)addr);
8722b24ab6bSSebastien Roy 			break;
8732b24ab6bSSebastien Roy 		case DL_FACT_PHYS_ADDR:
874da14cebeSEric Cheng 			bcopy(dsp->ds_mip->mi_unicst_addr, addr, addr_length);
8752b24ab6bSSebastien Roy 			break;
8762b24ab6bSSebastien Roy 		case DL_CURR_DEST_ADDR:
8772b24ab6bSSebastien Roy 			if (!mac_dst_get(dsp->ds_mh, (uint8_t *)addr))
8782b24ab6bSSebastien Roy 				dl_err = DL_NOTSUPPORTED;
8792b24ab6bSSebastien Roy 			break;
8802b24ab6bSSebastien Roy 		default:
8812b24ab6bSSebastien Roy 			dl_err = DL_UNSUPPORTED;
882c08e5e1aSdr146992 		}
8832b24ab6bSSebastien Roy 	}
8842b24ab6bSSebastien Roy done:
8852b24ab6bSSebastien Roy 	if (dl_err == 0)
8862b24ab6bSSebastien Roy 		dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
8872b24ab6bSSebastien Roy 	else
888210db224Sericheng 		dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
8892b24ab6bSSebastien Roy 	if (addr != NULL)
8902b24ab6bSSebastien Roy 		kmem_free(addr, addr_length);
891210db224Sericheng }
892210db224Sericheng 
893210db224Sericheng /*
894210db224Sericheng  * DL_SET_PHYS_ADDR_REQ
895210db224Sericheng  */
896da14cebeSEric Cheng static void
proto_setphysaddr_req(dld_str_t * dsp,mblk_t * mp)897da14cebeSEric Cheng proto_setphysaddr_req(dld_str_t *dsp, mblk_t *mp)
898210db224Sericheng {
899da14cebeSEric Cheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)mp->b_rptr;
900210db224Sericheng 	int		err = 0;
901210db224Sericheng 	t_uscalar_t	dl_err;
902210db224Sericheng 	queue_t		*q = dsp->ds_wq;
903da14cebeSEric Cheng 	mac_perim_handle_t	mph;
904210db224Sericheng 
905210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
906210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
907210db224Sericheng 		dl_err = DL_OUTSTATE;
908210db224Sericheng 		goto failed;
909210db224Sericheng 	}
910210db224Sericheng 
911210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
912210db224Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
913210db224Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
914210db224Sericheng 		dl_err = DL_BADPRIM;
915210db224Sericheng 		goto failed;
916210db224Sericheng 	}
917210db224Sericheng 
918da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
919da14cebeSEric Cheng 
9205d460eafSCathy Zhou 	if ((err = dls_active_set(dsp)) != 0) {
921210db224Sericheng 		dl_err = DL_SYSERR;
922da14cebeSEric Cheng 		goto failed2;
923210db224Sericheng 	}
924210db224Sericheng 
92525ec3e3dSEric Cheng 	/*
92625ec3e3dSEric Cheng 	 * If mac-nospoof is enabled and the link is owned by a
92725ec3e3dSEric Cheng 	 * non-global zone, changing the mac address is not allowed.
92825ec3e3dSEric Cheng 	 */
92925ec3e3dSEric Cheng 	if (dsp->ds_dlp->dl_zid != GLOBAL_ZONEID &&
93025ec3e3dSEric Cheng 	    mac_protect_enabled(dsp->ds_mch, MPT_MACNOSPOOF)) {
93125ec3e3dSEric Cheng 		dls_active_clear(dsp, B_FALSE);
93225ec3e3dSEric Cheng 		err = EACCES;
93325ec3e3dSEric Cheng 		goto failed2;
93425ec3e3dSEric Cheng 	}
93525ec3e3dSEric Cheng 
936da14cebeSEric Cheng 	err = mac_unicast_primary_set(dsp->ds_mh,
937da14cebeSEric Cheng 	    mp->b_rptr + dlp->dl_addr_offset);
938210db224Sericheng 	if (err != 0) {
9397c478bd9Sstevel@tonic-gate 		switch (err) {
9407c478bd9Sstevel@tonic-gate 		case EINVAL:
9417c478bd9Sstevel@tonic-gate 			dl_err = DL_BADADDR;
9427c478bd9Sstevel@tonic-gate 			err = 0;
9437c478bd9Sstevel@tonic-gate 			break;
9447c478bd9Sstevel@tonic-gate 
9457c478bd9Sstevel@tonic-gate 		default:
9467c478bd9Sstevel@tonic-gate 			dl_err = DL_SYSERR;
9477c478bd9Sstevel@tonic-gate 			break;
9487c478bd9Sstevel@tonic-gate 		}
9495d460eafSCathy Zhou 		dls_active_clear(dsp, B_FALSE);
950da14cebeSEric Cheng 		goto failed2;
951da14cebeSEric Cheng 
952210db224Sericheng 	}
953d62bc4baSyz147064 
954da14cebeSEric Cheng 	mac_perim_exit(mph);
955da14cebeSEric Cheng 
956210db224Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
957da14cebeSEric Cheng 	return;
958da14cebeSEric Cheng 
959da14cebeSEric Cheng failed2:
960da14cebeSEric Cheng 	mac_perim_exit(mph);
961210db224Sericheng failed:
962210db224Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
9637c478bd9Sstevel@tonic-gate }
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate /*
966210db224Sericheng  * DL_UDQOS_REQ
9677c478bd9Sstevel@tonic-gate  */
968da14cebeSEric Cheng static void
proto_udqos_req(dld_str_t * dsp,mblk_t * mp)969da14cebeSEric Cheng proto_udqos_req(dld_str_t *dsp, mblk_t *mp)
9707c478bd9Sstevel@tonic-gate {
971da14cebeSEric Cheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)mp->b_rptr;
972210db224Sericheng 	dl_qos_cl_sel1_t *selp;
973210db224Sericheng 	int		off, len;
974210db224Sericheng 	t_uscalar_t	dl_err;
975210db224Sericheng 	queue_t		*q = dsp->ds_wq;
976210db224Sericheng 
977210db224Sericheng 	off = dlp->dl_qos_offset;
978210db224Sericheng 	len = dlp->dl_qos_length;
979210db224Sericheng 
980210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
981210db224Sericheng 		dl_err = DL_BADPRIM;
982210db224Sericheng 		goto failed;
983210db224Sericheng 	}
984210db224Sericheng 
985210db224Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
986210db224Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
987210db224Sericheng 		dl_err = DL_BADQOSTYPE;
988210db224Sericheng 		goto failed;
989210db224Sericheng 	}
990210db224Sericheng 
991605445d5Sdg199075 	if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
992210db224Sericheng 	    selp->dl_priority < 0) {
993210db224Sericheng 		dl_err = DL_BADQOSPARAM;
994210db224Sericheng 		goto failed;
995210db224Sericheng 	}
996210db224Sericheng 
997d62bc4baSyz147064 	dsp->ds_pri = selp->dl_priority;
998210db224Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
999da14cebeSEric Cheng 	return;
1000210db224Sericheng failed:
1001210db224Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1002210db224Sericheng }
1003210db224Sericheng 
10044b46d1efSkrgopi static boolean_t
check_mod_above(queue_t * q,const char * mod)10058d4cf8d8S check_mod_above(queue_t *q, const char *mod)
10064b46d1efSkrgopi {
10074b46d1efSkrgopi 	queue_t		*next_q;
10084b46d1efSkrgopi 	boolean_t	ret = B_TRUE;
10094b46d1efSkrgopi 
10104b46d1efSkrgopi 	claimstr(q);
10114b46d1efSkrgopi 	next_q = q->q_next;
10128d4cf8d8S 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, mod) != 0)
10134b46d1efSkrgopi 		ret = B_FALSE;
10144b46d1efSkrgopi 	releasestr(q);
10154b46d1efSkrgopi 	return (ret);
10164b46d1efSkrgopi }
10174b46d1efSkrgopi 
1018210db224Sericheng /*
1019210db224Sericheng  * DL_CAPABILITY_REQ
1020210db224Sericheng  */
1021da14cebeSEric Cheng static void
proto_capability_req(dld_str_t * dsp,mblk_t * mp)1022da14cebeSEric Cheng proto_capability_req(dld_str_t *dsp, mblk_t *mp)
1023210db224Sericheng {
1024da14cebeSEric Cheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)mp->b_rptr;
1025210db224Sericheng 	dl_capability_sub_t *sp;
1026210db224Sericheng 	size_t		size, len;
1027210db224Sericheng 	offset_t	off, end;
1028210db224Sericheng 	t_uscalar_t	dl_err;
1029210db224Sericheng 	queue_t		*q = dsp->ds_wq;
1030210db224Sericheng 
1031210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1032210db224Sericheng 		dl_err = DL_BADPRIM;
1033210db224Sericheng 		goto failed;
1034210db224Sericheng 	}
1035210db224Sericheng 
1036210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1037210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1038210db224Sericheng 		dl_err = DL_OUTSTATE;
1039210db224Sericheng 		goto failed;
1040210db224Sericheng 	}
1041210db224Sericheng 
1042210db224Sericheng 	/*
1043210db224Sericheng 	 * This request is overloaded. If there are no requested capabilities
1044210db224Sericheng 	 * then we just want to acknowledge with all the capabilities we
1045210db224Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1046210db224Sericheng 	 */
1047210db224Sericheng 	if (dlp->dl_sub_length == 0) {
1048da14cebeSEric Cheng 		proto_capability_advertise(dsp, mp);
1049da14cebeSEric Cheng 		return;
1050210db224Sericheng 	}
1051210db224Sericheng 
1052210db224Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1053210db224Sericheng 		dl_err = DL_BADPRIM;
1054210db224Sericheng 		goto failed;
1055210db224Sericheng 	}
1056210db224Sericheng 
1057210db224Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1058210db224Sericheng 
1059210db224Sericheng 	off = dlp->dl_sub_offset;
1060210db224Sericheng 	len = dlp->dl_sub_length;
1061210db224Sericheng 
1062210db224Sericheng 	/*
1063210db224Sericheng 	 * Walk the list of capabilities to be enabled.
1064210db224Sericheng 	 */
1065210db224Sericheng 	for (end = off + len; off < end; ) {
1066210db224Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1067210db224Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1068210db224Sericheng 
1069210db224Sericheng 		if (off + size > end ||
1070210db224Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1071210db224Sericheng 			dl_err = DL_BADPRIM;
1072210db224Sericheng 			goto failed;
1073210db224Sericheng 		}
1074210db224Sericheng 
1075210db224Sericheng 		switch (sp->dl_cap) {
1076210db224Sericheng 		/*
1077210db224Sericheng 		 * TCP/IP checksum offload to hardware.
1078210db224Sericheng 		 */
1079210db224Sericheng 		case DL_CAPAB_HCKSUM: {
1080210db224Sericheng 			dl_capab_hcksum_t *hcksump;
1081210db224Sericheng 			dl_capab_hcksum_t hcksum;
1082210db224Sericheng 
1083210db224Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1084210db224Sericheng 			/*
1085210db224Sericheng 			 * Copy for alignment.
1086210db224Sericheng 			 */
1087210db224Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1088210db224Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1089210db224Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1090210db224Sericheng 			break;
1091210db224Sericheng 		}
1092210db224Sericheng 
1093da14cebeSEric Cheng 		case DL_CAPAB_DLD: {
1094da14cebeSEric Cheng 			dl_capab_dld_t	*dldp;
1095da14cebeSEric Cheng 			dl_capab_dld_t	dld;
10968347601bSyl150051 
1097da14cebeSEric Cheng 			dldp = (dl_capab_dld_t *)&sp[1];
10988347601bSyl150051 			/*
10998347601bSyl150051 			 * Copy for alignment.
11008347601bSyl150051 			 */
1101da14cebeSEric Cheng 			bcopy(dldp, &dld, sizeof (dl_capab_dld_t));
1102da14cebeSEric Cheng 			dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq);
1103da14cebeSEric Cheng 			bcopy(&dld, dldp, sizeof (dl_capab_dld_t));
1104210db224Sericheng 			break;
1105210db224Sericheng 		}
1106210db224Sericheng 		default:
1107210db224Sericheng 			break;
1108210db224Sericheng 		}
1109210db224Sericheng 		off += size;
1110210db224Sericheng 	}
1111210db224Sericheng 	qreply(q, mp);
1112da14cebeSEric Cheng 	return;
1113210db224Sericheng failed:
1114210db224Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1115210db224Sericheng }
1116210db224Sericheng 
1117210db224Sericheng /*
1118210db224Sericheng  * DL_NOTIFY_REQ
1119210db224Sericheng  */
1120da14cebeSEric Cheng static void
proto_notify_req(dld_str_t * dsp,mblk_t * mp)1121da14cebeSEric Cheng proto_notify_req(dld_str_t *dsp, mblk_t *mp)
1122210db224Sericheng {
1123da14cebeSEric Cheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)mp->b_rptr;
1124210db224Sericheng 	t_uscalar_t	dl_err;
1125210db224Sericheng 	queue_t		*q = dsp->ds_wq;
1126210db224Sericheng 	uint_t		note =
1127210db224Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1128210db224Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1129210db224Sericheng 	    DL_NOTE_PHYS_ADDR |
1130210db224Sericheng 	    DL_NOTE_LINK_UP |
1131210db224Sericheng 	    DL_NOTE_LINK_DOWN |
1132ba2e4443Sseb 	    DL_NOTE_CAPAB_RENEG |
1133cef310fdSGirish Moodalbail 	    DL_NOTE_FASTPATH_FLUSH |
11342b24ab6bSSebastien Roy 	    DL_NOTE_SPEED |
1135550b6e40SSowmini Varadhan 	    DL_NOTE_SDU_SIZE|
11361eee170aSErik Nordmark 	    DL_NOTE_SDU_SIZE2|
1137550b6e40SSowmini Varadhan 	    DL_NOTE_ALLOWED_IPS;
1138210db224Sericheng 
1139210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1140210db224Sericheng 		dl_err = DL_BADPRIM;
1141210db224Sericheng 		goto failed;
1142210db224Sericheng 	}
1143210db224Sericheng 
1144210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1145210db224Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1146210db224Sericheng 		dl_err = DL_OUTSTATE;
1147210db224Sericheng 		goto failed;
1148210db224Sericheng 	}
1149210db224Sericheng 
1150d62bc4baSyz147064 	note &= ~(mac_no_notification(dsp->ds_mh));
1151d62bc4baSyz147064 
1152210db224Sericheng 	/*
1153210db224Sericheng 	 * Cache the notifications that are being enabled.
1154210db224Sericheng 	 */
1155210db224Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1156210db224Sericheng 	/*
1157210db224Sericheng 	 * The ACK carries all notifications regardless of which set is
1158210db224Sericheng 	 * being enabled.
1159210db224Sericheng 	 */
1160210db224Sericheng 	dlnotifyack(q, mp, note);
1161210db224Sericheng 
1162210db224Sericheng 	/*
1163da14cebeSEric Cheng 	 * Generate DL_NOTIFY_IND messages for each enabled notification.
1164210db224Sericheng 	 */
1165210db224Sericheng 	if (dsp->ds_notifications != 0) {
1166210db224Sericheng 		dld_str_notify_ind(dsp);
1167210db224Sericheng 	}
1168da14cebeSEric Cheng 	return;
1169210db224Sericheng failed:
1170210db224Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1171210db224Sericheng }
1172210db224Sericheng 
1173210db224Sericheng /*
1174da14cebeSEric Cheng  * DL_UINTDATA_REQ
1175210db224Sericheng  */
1176d62bc4baSyz147064 void
proto_unitdata_req(dld_str_t * dsp,mblk_t * mp)1177da14cebeSEric Cheng proto_unitdata_req(dld_str_t *dsp, mblk_t *mp)
1178210db224Sericheng {
1179210db224Sericheng 	queue_t			*q = dsp->ds_wq;
1180d62bc4baSyz147064 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)mp->b_rptr;
1181210db224Sericheng 	off_t			off;
1182210db224Sericheng 	size_t			len, size;
1183210db224Sericheng 	const uint8_t		*addr;
1184210db224Sericheng 	uint16_t		sap;
1185210db224Sericheng 	uint_t			addr_length;
1186ba2e4443Sseb 	mblk_t			*bp, *payload;
1187210db224Sericheng 	uint32_t		start, stuff, end, value, flags;
1188210db224Sericheng 	t_uscalar_t		dl_err;
1189e7801d59Ssowmini 	uint_t			max_sdu;
1190210db224Sericheng 
1191210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1192da14cebeSEric Cheng 		dlerrorack(q, mp, DL_UNITDATA_REQ, DL_BADPRIM, 0);
1193da14cebeSEric Cheng 		return;
1194210db224Sericheng 	}
1195210db224Sericheng 
1196da14cebeSEric Cheng 	mutex_enter(&dsp->ds_lock);
1197da14cebeSEric Cheng 	if (dsp->ds_dlstate != DL_IDLE) {
1198da14cebeSEric Cheng 		mutex_exit(&dsp->ds_lock);
1199da14cebeSEric Cheng 		dlerrorack(q, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
1200da14cebeSEric Cheng 		return;
1201da14cebeSEric Cheng 	}
1202da14cebeSEric Cheng 	DLD_DATATHR_INC(dsp);
1203da14cebeSEric Cheng 	mutex_exit(&dsp->ds_lock);
1204da14cebeSEric Cheng 
1205210db224Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1206210db224Sericheng 
1207210db224Sericheng 	off = dlp->dl_dest_addr_offset;
1208210db224Sericheng 	len = dlp->dl_dest_addr_length;
1209210db224Sericheng 
1210210db224Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1211210db224Sericheng 		dl_err = DL_BADPRIM;
1212210db224Sericheng 		goto failed;
1213210db224Sericheng 	}
1214210db224Sericheng 
1215210db224Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1216210db224Sericheng 		dl_err = DL_BADADDR;
1217210db224Sericheng 		goto failed;
1218210db224Sericheng 	}
1219210db224Sericheng 
1220210db224Sericheng 	addr = mp->b_rptr + off;
1221210db224Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1222210db224Sericheng 
1223210db224Sericheng 	/*
1224210db224Sericheng 	 * Check the length of the packet and the block types.
1225210db224Sericheng 	 */
1226210db224Sericheng 	size = 0;
1227ba2e4443Sseb 	payload = mp->b_cont;
1228ba2e4443Sseb 	for (bp = payload; bp != NULL; bp = bp->b_cont) {
1229210db224Sericheng 		if (DB_TYPE(bp) != M_DATA)
1230210db224Sericheng 			goto baddata;
1231210db224Sericheng 
1232210db224Sericheng 		size += MBLKL(bp);
1233210db224Sericheng 	}
1234210db224Sericheng 
1235e7801d59Ssowmini 	mac_sdu_get(dsp->ds_mh, NULL, &max_sdu);
1236e7801d59Ssowmini 	if (size > max_sdu)
1237210db224Sericheng 		goto baddata;
1238210db224Sericheng 
1239210db224Sericheng 	/*
1240210db224Sericheng 	 * Build a packet header.
1241210db224Sericheng 	 */
1242da14cebeSEric Cheng 	if ((bp = dls_header(dsp, addr, sap, dlp->dl_priority.dl_max,
1243605445d5Sdg199075 	    &payload)) == NULL) {
1244210db224Sericheng 		dl_err = DL_BADADDR;
1245210db224Sericheng 		goto failed;
1246210db224Sericheng 	}
1247210db224Sericheng 
1248210db224Sericheng 	/*
1249210db224Sericheng 	 * We no longer need the M_PROTO header, so free it.
1250210db224Sericheng 	 */
1251210db224Sericheng 	freeb(mp);
1252210db224Sericheng 
1253210db224Sericheng 	/*
1254210db224Sericheng 	 * Transfer the checksum offload information if it is present.
1255210db224Sericheng 	 */
1256ba2e4443Sseb 	hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value,
1257210db224Sericheng 	    &flags);
1258ba2e4443Sseb 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0);
1259210db224Sericheng 
1260210db224Sericheng 	/*
1261210db224Sericheng 	 * Link the payload onto the new header.
1262210db224Sericheng 	 */
1263210db224Sericheng 	ASSERT(bp->b_cont == NULL);
1264ba2e4443Sseb 	bp->b_cont = payload;
1265da14cebeSEric Cheng 
1266da14cebeSEric Cheng 	/*
1267da14cebeSEric Cheng 	 * No lock can be held across modules and putnext()'s,
1268da14cebeSEric Cheng 	 * which can happen here with the call from DLD_TX().
1269da14cebeSEric Cheng 	 */
1270da14cebeSEric Cheng 	if (DLD_TX(dsp, bp, 0, 0) != NULL) {
1271da14cebeSEric Cheng 		/* flow-controlled */
1272da14cebeSEric Cheng 		DLD_SETQFULL(dsp);
1273da14cebeSEric Cheng 	}
1274da14cebeSEric Cheng 	DLD_DATATHR_DCR(dsp);
1275d62bc4baSyz147064 	return;
1276da14cebeSEric Cheng 
1277210db224Sericheng failed:
1278210db224Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1279da14cebeSEric Cheng 	DLD_DATATHR_DCR(dsp);
1280d62bc4baSyz147064 	return;
1281210db224Sericheng 
1282210db224Sericheng baddata:
1283210db224Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1284da14cebeSEric Cheng 	DLD_DATATHR_DCR(dsp);
1285210db224Sericheng }
1286210db224Sericheng 
1287210db224Sericheng /*
1288210db224Sericheng  * DL_PASSIVE_REQ
1289210db224Sericheng  */
1290da14cebeSEric Cheng static void
proto_passive_req(dld_str_t * dsp,mblk_t * mp)1291da14cebeSEric Cheng proto_passive_req(dld_str_t *dsp, mblk_t *mp)
1292210db224Sericheng {
1293210db224Sericheng 	t_uscalar_t dl_err;
1294210db224Sericheng 
1295d62bc4baSyz147064 	/*
1296210db224Sericheng 	 * If we've already become active by issuing an active primitive,
1297210db224Sericheng 	 * then it's too late to try to become passive.
1298210db224Sericheng 	 */
1299210db224Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1300210db224Sericheng 		dl_err = DL_OUTSTATE;
1301210db224Sericheng 		goto failed;
1302210db224Sericheng 	}
1303210db224Sericheng 
1304210db224Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1305210db224Sericheng 		dl_err = DL_BADPRIM;
1306210db224Sericheng 		goto failed;
1307210db224Sericheng 	}
1308210db224Sericheng 
1309210db224Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1310210db224Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1311da14cebeSEric Cheng 	return;
1312210db224Sericheng failed:
1313210db224Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1314210db224Sericheng }
1315210db224Sericheng 
1316da14cebeSEric Cheng 
1317210db224Sericheng /*
1318210db224Sericheng  * Catch-all handler.
1319210db224Sericheng  */
1320da14cebeSEric Cheng static void
proto_req(dld_str_t * dsp,mblk_t * mp)1321da14cebeSEric Cheng proto_req(dld_str_t *dsp, mblk_t *mp)
1322210db224Sericheng {
1323da14cebeSEric Cheng 	union DL_primitives	*dlp = (union DL_primitives *)mp->b_rptr;
1324da14cebeSEric Cheng 
1325210db224Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
13267c478bd9Sstevel@tonic-gate }
13277c478bd9Sstevel@tonic-gate 
1328da14cebeSEric Cheng static int
dld_capab_perim(dld_str_t * dsp,void * data,uint_t flags)1329da14cebeSEric Cheng dld_capab_perim(dld_str_t *dsp, void *data, uint_t flags)
13307c478bd9Sstevel@tonic-gate {
1331da14cebeSEric Cheng 	switch (flags) {
1332da14cebeSEric Cheng 	case DLD_ENABLE:
1333da14cebeSEric Cheng 		mac_perim_enter_by_mh(dsp->ds_mh, (mac_perim_handle_t *)data);
1334da14cebeSEric Cheng 		return (0);
13357c478bd9Sstevel@tonic-gate 
1336da14cebeSEric Cheng 	case DLD_DISABLE:
1337da14cebeSEric Cheng 		mac_perim_exit((mac_perim_handle_t)data);
1338da14cebeSEric Cheng 		return (0);
1339210db224Sericheng 
1340da14cebeSEric Cheng 	case DLD_QUERY:
1341da14cebeSEric Cheng 		return (mac_perim_held(dsp->ds_mh));
1342da14cebeSEric Cheng 	}
1343da14cebeSEric Cheng 	return (0);
13447c478bd9Sstevel@tonic-gate }
13457c478bd9Sstevel@tonic-gate 
1346da14cebeSEric Cheng static int
dld_capab_direct(dld_str_t * dsp,void * data,uint_t flags)1347da14cebeSEric Cheng dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags)
13487c478bd9Sstevel@tonic-gate {
1349da14cebeSEric Cheng 	dld_capab_direct_t	*direct = data;
13507c478bd9Sstevel@tonic-gate 
1351da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1352da14cebeSEric Cheng 
1353da14cebeSEric Cheng 	switch (flags) {
1354da14cebeSEric Cheng 	case DLD_ENABLE:
1355da14cebeSEric Cheng 		dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf,
1356da14cebeSEric Cheng 		    direct->di_rx_ch);
1357ae6aa22aSVenugopal Iyer 
1358da14cebeSEric Cheng 		direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put;
1359da14cebeSEric Cheng 		direct->di_tx_dh = dsp;
1360da14cebeSEric Cheng 		direct->di_tx_cb_df = (uintptr_t)mac_client_tx_notify;
1361da14cebeSEric Cheng 		direct->di_tx_cb_dh = dsp->ds_mch;
1362ae6aa22aSVenugopal Iyer 		direct->di_tx_fctl_df = (uintptr_t)mac_tx_is_flow_blocked;
1363ae6aa22aSVenugopal Iyer 		direct->di_tx_fctl_dh = dsp->ds_mch;
1364ae6aa22aSVenugopal Iyer 
1365da14cebeSEric Cheng 		dsp->ds_direct = B_TRUE;
1366da14cebeSEric Cheng 
1367da14cebeSEric Cheng 		return (0);
1368da14cebeSEric Cheng 
1369da14cebeSEric Cheng 	case DLD_DISABLE:
1370da14cebeSEric Cheng 		dls_rx_set(dsp, (dsp->ds_mode == DLD_FASTPATH) ?
1371da14cebeSEric Cheng 		    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
1372da14cebeSEric Cheng 		dsp->ds_direct = B_FALSE;
1373da14cebeSEric Cheng 
1374da14cebeSEric Cheng 		return (0);
1375da14cebeSEric Cheng 	}
1376da14cebeSEric Cheng 	return (ENOTSUP);
1377da14cebeSEric Cheng }
13787c478bd9Sstevel@tonic-gate 
13797c478bd9Sstevel@tonic-gate /*
1380da14cebeSEric Cheng  * dld_capab_poll_enable()
1381da14cebeSEric Cheng  *
1382da14cebeSEric Cheng  * This function is misnamed. All polling  and fanouts are run out of the
1383da14cebeSEric Cheng  * lower mac (in case of VNIC and the only mac in case of NICs). The
1384da14cebeSEric Cheng  * availability of Rx ring and promiscous mode is all taken care between
1385da14cebeSEric Cheng  * the soft ring set (mac_srs), the Rx ring, and S/W classifier. Any
1386da14cebeSEric Cheng  * fanout necessary is done by the soft rings that are part of the
1387da14cebeSEric Cheng  * mac_srs (by default mac_srs sends the packets up via a TCP and
1388da14cebeSEric Cheng  * non TCP soft ring).
1389da14cebeSEric Cheng  *
1390da14cebeSEric Cheng  * The mac_srs (or its associated soft rings) always store the ill_rx_ring
1391da14cebeSEric Cheng  * (the cookie returned when they registered with IP during plumb) as their
1392da14cebeSEric Cheng  * 2nd argument which is passed up as mac_resource_handle_t. The upcall
1393da14cebeSEric Cheng  * function and 1st argument is what the caller registered when they
1394da14cebeSEric Cheng  * called mac_rx_classify_flow_add() to register the flow. For VNIC,
1395da14cebeSEric Cheng  * the function is vnic_rx and argument is vnic_t. For regular NIC
1396da14cebeSEric Cheng  * case, it mac_rx_default and mac_handle_t. As explained above, the
1397da14cebeSEric Cheng  * mac_srs (or its soft ring) will add the ill_rx_ring (mac_resource_handle_t)
1398da14cebeSEric Cheng  * from its stored 2nd argument.
13997c478bd9Sstevel@tonic-gate  */
1400da14cebeSEric Cheng static int
dld_capab_poll_enable(dld_str_t * dsp,dld_capab_poll_t * poll)1401da14cebeSEric Cheng dld_capab_poll_enable(dld_str_t *dsp, dld_capab_poll_t *poll)
1402da14cebeSEric Cheng {
1403da14cebeSEric Cheng 	if (dsp->ds_polling)
1404da14cebeSEric Cheng 		return (EINVAL);
14057c478bd9Sstevel@tonic-gate 
1406da14cebeSEric Cheng 	if ((dld_opt & DLD_OPT_NO_POLL) != 0 || dsp->ds_mode == DLD_RAW)
1407da14cebeSEric Cheng 		return (ENOTSUP);
14087c478bd9Sstevel@tonic-gate 
14097c478bd9Sstevel@tonic-gate 	/*
1410da14cebeSEric Cheng 	 * Enable client polling if and only if DLS bypass is possible.
1411da14cebeSEric Cheng 	 * Special cases like VLANs need DLS processing in the Rx data path.
1412da14cebeSEric Cheng 	 * In such a case we can neither allow the client (IP) to directly
1413da14cebeSEric Cheng 	 * poll the softring (since DLS processing hasn't been done) nor can
1414da14cebeSEric Cheng 	 * we allow DLS bypass.
14157c478bd9Sstevel@tonic-gate 	 */
1416da14cebeSEric Cheng 	if (!mac_rx_bypass_set(dsp->ds_mch, dsp->ds_rx, dsp->ds_rx_arg))
1417da14cebeSEric Cheng 		return (ENOTSUP);
14187c478bd9Sstevel@tonic-gate 
14197c478bd9Sstevel@tonic-gate 	/*
1420da14cebeSEric Cheng 	 * Register soft ring resources. This will come in handy later if
1421da14cebeSEric Cheng 	 * the user decides to modify CPU bindings to use more CPUs for the
1422da14cebeSEric Cheng 	 * device in which case we will switch to fanout using soft rings.
14237c478bd9Sstevel@tonic-gate 	 */
1424da14cebeSEric Cheng 	mac_resource_set_common(dsp->ds_mch,
1425da14cebeSEric Cheng 	    (mac_resource_add_t)poll->poll_ring_add_cf,
1426da14cebeSEric Cheng 	    (mac_resource_remove_t)poll->poll_ring_remove_cf,
1427da14cebeSEric Cheng 	    (mac_resource_quiesce_t)poll->poll_ring_quiesce_cf,
1428da14cebeSEric Cheng 	    (mac_resource_restart_t)poll->poll_ring_restart_cf,
1429da14cebeSEric Cheng 	    (mac_resource_bind_t)poll->poll_ring_bind_cf,
1430da14cebeSEric Cheng 	    poll->poll_ring_ch);
14317c478bd9Sstevel@tonic-gate 
1432da14cebeSEric Cheng 	mac_client_poll_enable(dsp->ds_mch);
1433da14cebeSEric Cheng 
14347c478bd9Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
1435da14cebeSEric Cheng 	return (0);
14367c478bd9Sstevel@tonic-gate }
14377c478bd9Sstevel@tonic-gate 
1438da14cebeSEric Cheng /* ARGSUSED */
1439da14cebeSEric Cheng static int
dld_capab_poll_disable(dld_str_t * dsp,dld_capab_poll_t * poll)1440da14cebeSEric Cheng dld_capab_poll_disable(dld_str_t *dsp, dld_capab_poll_t *poll)
14414b46d1efSkrgopi {
1442da14cebeSEric Cheng 	if (!dsp->ds_polling)
1443da14cebeSEric Cheng 		return (EINVAL);
14444b46d1efSkrgopi 
1445da14cebeSEric Cheng 	mac_client_poll_disable(dsp->ds_mch);
1446da14cebeSEric Cheng 	mac_resource_set(dsp->ds_mch, NULL, NULL);
1447da14cebeSEric Cheng 
1448da14cebeSEric Cheng 	dsp->ds_polling = B_FALSE;
1449da14cebeSEric Cheng 	return (0);
1450da14cebeSEric Cheng }
1451da14cebeSEric Cheng 
1452da14cebeSEric Cheng static int
dld_capab_poll(dld_str_t * dsp,void * data,uint_t flags)1453da14cebeSEric Cheng dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags)
1454da14cebeSEric Cheng {
1455da14cebeSEric Cheng 	dld_capab_poll_t	*poll = data;
1456da14cebeSEric Cheng 
1457da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1458da14cebeSEric Cheng 
1459da14cebeSEric Cheng 	switch (flags) {
1460da14cebeSEric Cheng 	case DLD_ENABLE:
1461da14cebeSEric Cheng 		return (dld_capab_poll_enable(dsp, poll));
1462da14cebeSEric Cheng 	case DLD_DISABLE:
1463da14cebeSEric Cheng 		return (dld_capab_poll_disable(dsp, poll));
1464da14cebeSEric Cheng 	}
1465da14cebeSEric Cheng 	return (ENOTSUP);
1466da14cebeSEric Cheng }
1467da14cebeSEric Cheng 
1468da14cebeSEric Cheng static int
dld_capab_lso(dld_str_t * dsp,void * data,uint_t flags)1469da14cebeSEric Cheng dld_capab_lso(dld_str_t *dsp, void *data, uint_t flags)
1470da14cebeSEric Cheng {
1471da14cebeSEric Cheng 	dld_capab_lso_t		*lso = data;
1472da14cebeSEric Cheng 
1473da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1474da14cebeSEric Cheng 
1475da14cebeSEric Cheng 	switch (flags) {
1476da14cebeSEric Cheng 	case DLD_ENABLE: {
1477da14cebeSEric Cheng 		mac_capab_lso_t		mac_lso;
14784b46d1efSkrgopi 
14794b46d1efSkrgopi 		/*
1480da14cebeSEric Cheng 		 * Check if LSO is supported on this MAC & enable LSO
1481da14cebeSEric Cheng 		 * accordingly.
14824b46d1efSkrgopi 		 */
1483da14cebeSEric Cheng 		if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) {
1484da14cebeSEric Cheng 			lso->lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max;
1485da14cebeSEric Cheng 			lso->lso_flags = 0;
1486da14cebeSEric Cheng 			/* translate the flag for mac clients */
1487da14cebeSEric Cheng 			if ((mac_lso.lso_flags & LSO_TX_BASIC_TCP_IPV4) != 0)
1488bd670b35SErik Nordmark 				lso->lso_flags |= DLD_LSO_BASIC_TCP_IPV4;
1489da14cebeSEric Cheng 			dsp->ds_lso = B_TRUE;
1490da14cebeSEric Cheng 			dsp->ds_lso_max = lso->lso_max;
1491da14cebeSEric Cheng 		} else {
1492da14cebeSEric Cheng 			dsp->ds_lso = B_FALSE;
1493da14cebeSEric Cheng 			dsp->ds_lso_max = 0;
1494da14cebeSEric Cheng 			return (ENOTSUP);
1495da14cebeSEric Cheng 		}
1496da14cebeSEric Cheng 		return (0);
1497da14cebeSEric Cheng 	}
1498da14cebeSEric Cheng 	case DLD_DISABLE: {
1499da14cebeSEric Cheng 		dsp->ds_lso = B_FALSE;
1500da14cebeSEric Cheng 		dsp->ds_lso_max = 0;
1501da14cebeSEric Cheng 		return (0);
1502da14cebeSEric Cheng 	}
1503da14cebeSEric Cheng 	}
1504da14cebeSEric Cheng 	return (ENOTSUP);
15054b46d1efSkrgopi }
15064b46d1efSkrgopi 
1507da14cebeSEric Cheng static int
dld_capab(dld_str_t * dsp,uint_t type,void * data,uint_t flags)1508da14cebeSEric Cheng dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags)
15094b46d1efSkrgopi {
1510da14cebeSEric Cheng 	int	err;
15114b46d1efSkrgopi 
15124b46d1efSkrgopi 	/*
1513da14cebeSEric Cheng 	 * Don't enable direct callback capabilities unless the caller is
1514da14cebeSEric Cheng 	 * the IP client. When a module is inserted in a stream (_I_INSERT)
1515da14cebeSEric Cheng 	 * the stack initiates capability disable, but due to races, the
1516da14cebeSEric Cheng 	 * module insertion may complete before the capability disable
1517da14cebeSEric Cheng 	 * completes. So we limit the check to DLD_ENABLE case.
15184b46d1efSkrgopi 	 */
1519da14cebeSEric Cheng 	if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) &&
15208d4cf8d8S 	    (dsp->ds_sap != ETHERTYPE_IP ||
15218d4cf8d8S 	    !check_mod_above(dsp->ds_rq, "ip"))) {
1522da14cebeSEric Cheng 		return (ENOTSUP);
15234b46d1efSkrgopi 	}
15244b46d1efSkrgopi 
1525da14cebeSEric Cheng 	switch (type) {
1526da14cebeSEric Cheng 	case DLD_CAPAB_DIRECT:
1527da14cebeSEric Cheng 		err = dld_capab_direct(dsp, data, flags);
1528da14cebeSEric Cheng 		break;
15294b46d1efSkrgopi 
1530da14cebeSEric Cheng 	case DLD_CAPAB_POLL:
1531da14cebeSEric Cheng 		err =  dld_capab_poll(dsp, data, flags);
1532da14cebeSEric Cheng 		break;
1533da14cebeSEric Cheng 
1534da14cebeSEric Cheng 	case DLD_CAPAB_PERIM:
1535da14cebeSEric Cheng 		err = dld_capab_perim(dsp, data, flags);
1536da14cebeSEric Cheng 		break;
1537da14cebeSEric Cheng 
1538da14cebeSEric Cheng 	case DLD_CAPAB_LSO:
1539da14cebeSEric Cheng 		err = dld_capab_lso(dsp, data, flags);
1540da14cebeSEric Cheng 		break;
1541da14cebeSEric Cheng 
1542da14cebeSEric Cheng 	default:
1543da14cebeSEric Cheng 		err = ENOTSUP;
1544da14cebeSEric Cheng 		break;
15454b46d1efSkrgopi 	}
1546da14cebeSEric Cheng 
1547da14cebeSEric Cheng 	return (err);
15484b46d1efSkrgopi }
15494b46d1efSkrgopi 
15507c478bd9Sstevel@tonic-gate /*
15517c478bd9Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
15527c478bd9Sstevel@tonic-gate  */
1553da14cebeSEric Cheng static void
proto_capability_advertise(dld_str_t * dsp,mblk_t * mp)1554210db224Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
15557c478bd9Sstevel@tonic-gate {
15567c478bd9Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
15577c478bd9Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
15587c478bd9Sstevel@tonic-gate 	size_t			subsize;
1559da14cebeSEric Cheng 	dl_capab_dld_t		dld;
15607c478bd9Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
15617c478bd9Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
15621cb875aeSCathy Zhou 	dl_capab_vrrp_t		vrrp;
15631cb875aeSCathy Zhou 	mac_capab_vrrp_t	vrrp_capab;
15647c478bd9Sstevel@tonic-gate 	uint8_t			*ptr;
1565210db224Sericheng 	queue_t			*q = dsp->ds_wq;
1566210db224Sericheng 	mblk_t			*mp1;
1567d62bc4baSyz147064 	boolean_t		hcksum_capable = B_FALSE;
1568d62bc4baSyz147064 	boolean_t		zcopy_capable = B_FALSE;
1569da14cebeSEric Cheng 	boolean_t		dld_capable = B_FALSE;
15701cb875aeSCathy Zhou 	boolean_t		vrrp_capable = B_FALSE;
15717c478bd9Sstevel@tonic-gate 
15727c478bd9Sstevel@tonic-gate 	/*
15737c478bd9Sstevel@tonic-gate 	 * Initially assume no capabilities.
15747c478bd9Sstevel@tonic-gate 	 */
15757c478bd9Sstevel@tonic-gate 	subsize = 0;
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate 	/*
15789056fcebSCathy Zhou 	 * Check if checksum offload is supported on this MAC.
15797c478bd9Sstevel@tonic-gate 	 */
1580d62bc4baSyz147064 	bzero(&hcksum, sizeof (dl_capab_hcksum_t));
15819056fcebSCathy Zhou 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
1582ba2e4443Sseb 	    &hcksum.hcksum_txflags)) {
1583d62bc4baSyz147064 		if (hcksum.hcksum_txflags != 0) {
1584d62bc4baSyz147064 			hcksum_capable = B_TRUE;
15857c478bd9Sstevel@tonic-gate 			subsize += sizeof (dl_capability_sub_t) +
15867c478bd9Sstevel@tonic-gate 			    sizeof (dl_capab_hcksum_t);
15877c478bd9Sstevel@tonic-gate 		}
1588d62bc4baSyz147064 	}
15897c478bd9Sstevel@tonic-gate 
15907c478bd9Sstevel@tonic-gate 	/*
1591d62bc4baSyz147064 	 * Check if zerocopy is supported on this interface.
1592d62bc4baSyz147064 	 * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled
1593d62bc4baSyz147064 	 * then reserve space for that capability.
15947c478bd9Sstevel@tonic-gate 	 */
1595d62bc4baSyz147064 	if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) &&
1596d62bc4baSyz147064 	    !(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
1597d62bc4baSyz147064 		zcopy_capable = B_TRUE;
15987c478bd9Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
15997c478bd9Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
16007c478bd9Sstevel@tonic-gate 	}
16017c478bd9Sstevel@tonic-gate 
16027c478bd9Sstevel@tonic-gate 	/*
1603da14cebeSEric Cheng 	 * Direct capability negotiation interface between IP and DLD
1604da14cebeSEric Cheng 	 */
16058d4cf8d8S 	if (dsp->ds_sap == ETHERTYPE_IP && check_mod_above(dsp->ds_rq, "ip")) {
1606da14cebeSEric Cheng 		dld_capable = B_TRUE;
1607da14cebeSEric Cheng 		subsize += sizeof (dl_capability_sub_t) +
1608da14cebeSEric Cheng 		    sizeof (dl_capab_dld_t);
1609da14cebeSEric Cheng 	}
1610da14cebeSEric Cheng 
1611da14cebeSEric Cheng 	/*
16121cb875aeSCathy Zhou 	 * Check if vrrp is supported on this interface. If so, reserve
16131cb875aeSCathy Zhou 	 * space for that capability.
16141cb875aeSCathy Zhou 	 */
16151cb875aeSCathy Zhou 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_VRRP, &vrrp_capab)) {
16161cb875aeSCathy Zhou 		vrrp_capable = B_TRUE;
16171cb875aeSCathy Zhou 		subsize += sizeof (dl_capability_sub_t) +
16181cb875aeSCathy Zhou 		    sizeof (dl_capab_vrrp_t);
16191cb875aeSCathy Zhou 	}
16201cb875aeSCathy Zhou 
16211cb875aeSCathy Zhou 	/*
1622210db224Sericheng 	 * If there are no capabilities to advertise or if we
1623210db224Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
16247c478bd9Sstevel@tonic-gate 	 */
16254b46d1efSkrgopi 	if ((mp1 = reallocb(mp,
1626210db224Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1627210db224Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1628da14cebeSEric Cheng 		return;
16297c478bd9Sstevel@tonic-gate 	}
16307c478bd9Sstevel@tonic-gate 
1631210db224Sericheng 	mp = mp1;
1632210db224Sericheng 	DB_TYPE(mp) = M_PROTO;
1633210db224Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1634210db224Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
16357c478bd9Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
16367c478bd9Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
16377c478bd9Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
16387c478bd9Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
16397c478bd9Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
16407c478bd9Sstevel@tonic-gate 
16417c478bd9Sstevel@tonic-gate 	/*
16427c478bd9Sstevel@tonic-gate 	 * TCP/IP checksum offload.
16437c478bd9Sstevel@tonic-gate 	 */
1644d62bc4baSyz147064 	if (hcksum_capable) {
16457c478bd9Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
16467c478bd9Sstevel@tonic-gate 
16477c478bd9Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
16487c478bd9Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
16497c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
16527c478bd9Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
16537c478bd9Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
16547c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
16557c478bd9Sstevel@tonic-gate 	}
16567c478bd9Sstevel@tonic-gate 
16577c478bd9Sstevel@tonic-gate 	/*
16587c478bd9Sstevel@tonic-gate 	 * Zero copy
16597c478bd9Sstevel@tonic-gate 	 */
1660d62bc4baSyz147064 	if (zcopy_capable) {
16617c478bd9Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
16627c478bd9Sstevel@tonic-gate 
16637c478bd9Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
16647c478bd9Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
16657c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
16667c478bd9Sstevel@tonic-gate 
16677c478bd9Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
16687c478bd9Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
16697c478bd9Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
16707c478bd9Sstevel@tonic-gate 
16717c478bd9Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
16727c478bd9Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
16737c478bd9Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
16747c478bd9Sstevel@tonic-gate 	}
16757c478bd9Sstevel@tonic-gate 
1676da14cebeSEric Cheng 	/*
16771cb875aeSCathy Zhou 	 * VRRP capability negotiation
16781cb875aeSCathy Zhou 	 */
16791cb875aeSCathy Zhou 	if (vrrp_capable) {
16801cb875aeSCathy Zhou 		dlsp = (dl_capability_sub_t *)ptr;
16811cb875aeSCathy Zhou 		dlsp->dl_cap = DL_CAPAB_VRRP;
16821cb875aeSCathy Zhou 		dlsp->dl_length = sizeof (dl_capab_vrrp_t);
16831cb875aeSCathy Zhou 		ptr += sizeof (dl_capability_sub_t);
16841cb875aeSCathy Zhou 
16851cb875aeSCathy Zhou 		bzero(&vrrp, sizeof (dl_capab_vrrp_t));
16861cb875aeSCathy Zhou 		vrrp.vrrp_af = vrrp_capab.mcv_af;
16871cb875aeSCathy Zhou 		bcopy(&vrrp, ptr, sizeof (dl_capab_vrrp_t));
16881cb875aeSCathy Zhou 		ptr += sizeof (dl_capab_vrrp_t);
16891cb875aeSCathy Zhou 	}
16901cb875aeSCathy Zhou 
16911cb875aeSCathy Zhou 	/*
1692da14cebeSEric Cheng 	 * Direct capability negotiation interface between IP and DLD.
1693da14cebeSEric Cheng 	 * Refer to dld.h for details.
1694da14cebeSEric Cheng 	 */
1695da14cebeSEric Cheng 	if (dld_capable) {
1696da14cebeSEric Cheng 		dlsp = (dl_capability_sub_t *)ptr;
1697da14cebeSEric Cheng 		dlsp->dl_cap = DL_CAPAB_DLD;
1698da14cebeSEric Cheng 		dlsp->dl_length = sizeof (dl_capab_dld_t);
1699da14cebeSEric Cheng 		ptr += sizeof (dl_capability_sub_t);
17007c478bd9Sstevel@tonic-gate 
1701da14cebeSEric Cheng 		bzero(&dld, sizeof (dl_capab_dld_t));
1702da14cebeSEric Cheng 		dld.dld_version = DLD_CURRENT_VERSION;
1703da14cebeSEric Cheng 		dld.dld_capab = (uintptr_t)dld_capab;
1704da14cebeSEric Cheng 		dld.dld_capab_handle = (uintptr_t)dsp;
1705da14cebeSEric Cheng 
1706da14cebeSEric Cheng 		dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq);
1707da14cebeSEric Cheng 		bcopy(&dld, ptr, sizeof (dl_capab_dld_t));
1708da14cebeSEric Cheng 		ptr += sizeof (dl_capab_dld_t);
1709da14cebeSEric Cheng 	}
1710da14cebeSEric Cheng 
1711da14cebeSEric Cheng 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1712210db224Sericheng 	qreply(q, mp);
17138fb46f24Syz147064 }
17148fb46f24Syz147064 
17158fb46f24Syz147064 /*
17168fb46f24Syz147064  * Disable any enabled capabilities.
17178fb46f24Syz147064  */
17188fb46f24Syz147064 void
dld_capabilities_disable(dld_str_t * dsp)17198fb46f24Syz147064 dld_capabilities_disable(dld_str_t *dsp)
17208fb46f24Syz147064 {
17218fb46f24Syz147064 	if (dsp->ds_polling)
1722da14cebeSEric Cheng 		(void) dld_capab_poll_disable(dsp, NULL);
17237c478bd9Sstevel@tonic-gate }
1724