xref: /illumos-gate/usr/src/uts/common/io/dld/dld_str.c (revision 86e5bb79)
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
50ef0bcfbSyz  * Common Development and Distribution License (the "License").
60ef0bcfbSyz  * 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 /*
22550b6e40SSowmini Varadhan  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23*86e5bb79SLuqman Aden  * Copyright 2023 Oxide Computer Company
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * Data-Link Driver
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
30da14cebeSEric Cheng #include	<inet/common.h>
31da14cebeSEric Cheng #include	<sys/strsubr.h>
327c478bd9Sstevel@tonic-gate #include	<sys/stropts.h>
337c478bd9Sstevel@tonic-gate #include	<sys/strsun.h>
34210db224Sericheng #include	<sys/vlan.h>
357c478bd9Sstevel@tonic-gate #include	<sys/dld_impl.h>
36da14cebeSEric Cheng #include	<sys/cpuvar.h>
37da14cebeSEric Cheng #include	<sys/callb.h>
38da14cebeSEric Cheng #include	<sys/list.h>
39da14cebeSEric Cheng #include	<sys/mac_client.h>
40da14cebeSEric Cheng #include	<sys/mac_client_priv.h>
41550b6e40SSowmini Varadhan #include	<sys/mac_flow.h>
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate static int	str_constructor(void *, void *, int);
447c478bd9Sstevel@tonic-gate static void	str_destructor(void *, void *);
45605445d5Sdg static mblk_t	*str_unitdata_ind(dld_str_t *, mblk_t *, boolean_t);
467c478bd9Sstevel@tonic-gate static void	str_notify_promisc_on_phys(dld_str_t *);
477c478bd9Sstevel@tonic-gate static void	str_notify_promisc_off_phys(dld_str_t *);
482b24ab6bSSebastien Roy static void	str_notify_phys_addr(dld_str_t *, uint_t, const uint8_t *);
497c478bd9Sstevel@tonic-gate static void	str_notify_link_up(dld_str_t *);
507c478bd9Sstevel@tonic-gate static void	str_notify_link_down(dld_str_t *);
517c478bd9Sstevel@tonic-gate static void	str_notify_capab_reneg(dld_str_t *);
527c478bd9Sstevel@tonic-gate static void	str_notify_speed(dld_str_t *, uint32_t);
53210db224Sericheng 
540ba2cbe9Sxc static void	ioc_native(dld_str_t *,  mblk_t *);
55d62bc4baSyz static void	ioc_margin(dld_str_t *, mblk_t *);
56210db224Sericheng static void	ioc_raw(dld_str_t *, mblk_t *);
57210db224Sericheng static void	ioc_fast(dld_str_t *,  mblk_t *);
584eaa4710SRishi Srivatsavai static void	ioc_lowlink(dld_str_t *,  mblk_t *);
59210db224Sericheng static void	ioc(dld_str_t *, mblk_t *);
60da14cebeSEric Cheng static void	dld_ioc(dld_str_t *, mblk_t *);
61d62bc4baSyz static void	dld_wput_nondata(dld_str_t *, mblk_t *);
62da14cebeSEric Cheng 
63da14cebeSEric Cheng static void	str_mdata_raw_put(dld_str_t *, mblk_t *);
64e75f0919SSebastien Roy static mblk_t	*i_dld_ether_header_update_tag(mblk_t *, uint_t, uint16_t,
65e75f0919SSebastien Roy     link_tagmode_t);
6672782355SNicolas Droux static mblk_t	*i_dld_ether_header_strip_tag(mblk_t *, boolean_t);
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate static uint32_t		str_count;
697c478bd9Sstevel@tonic-gate static kmem_cache_t	*str_cachep;
70c0192a57Sericheng static mod_hash_t	*str_hashp;
71210db224Sericheng 
72c0192a57Sericheng #define	STR_HASHSZ		64
73c0192a57Sericheng #define	STR_HASH_KEY(key)	((mod_hash_key_t)(uintptr_t)(key))
74c0192a57Sericheng 
75da14cebeSEric Cheng #define	dld_taskq	system_taskq
76d62bc4baSyz 
77da14cebeSEric Cheng static kmutex_t		dld_taskq_lock;
78da14cebeSEric Cheng static kcondvar_t	dld_taskq_cv;
79da14cebeSEric Cheng static list_t		dld_taskq_list;		/* List of dld_str_t */
80da14cebeSEric Cheng boolean_t		dld_taskq_quit;
81da14cebeSEric Cheng boolean_t		dld_taskq_done;
82da14cebeSEric Cheng 
83da14cebeSEric Cheng static void		dld_taskq_dispatch(void);
84d62bc4baSyz 
85210db224Sericheng /*
86da14cebeSEric Cheng  * Some notes on entry points, flow-control, queueing.
87210db224Sericheng  *
88210db224Sericheng  * This driver exports the traditional STREAMS put entry point as well as
89210db224Sericheng  * the non-STREAMS fast-path transmit routine which is provided to IP via
90210db224Sericheng  * the DL_CAPAB_POLL negotiation.  The put procedure handles all control
91210db224Sericheng  * and data operations, while the fast-path routine deals only with M_DATA
92210db224Sericheng  * fast-path packets.  Regardless of the entry point, all outbound packets
93da14cebeSEric Cheng  * will end up in DLD_TX(), where they will be delivered to the MAC layer.
94210db224Sericheng  *
95da14cebeSEric Cheng  * The transmit logic operates in the following way: All packets coming
96da14cebeSEric Cheng  * into DLD will be sent to the MAC layer through DLD_TX(). Flow-control
97da14cebeSEric Cheng  * happens when the MAC layer indicates the packets couldn't be
98da14cebeSEric Cheng  * transmitted due to 1) lack of resources (e.g. running out of
99da14cebeSEric Cheng  * descriptors),  or 2) reaching the allowed bandwidth limit for this
100da14cebeSEric Cheng  * particular flow. The indication comes in the form of a Tx cookie that
101da14cebeSEric Cheng  * identifies the blocked ring. In such case, DLD will place a
102da14cebeSEric Cheng  * dummy message on its write-side STREAMS queue so that the queue is
103da14cebeSEric Cheng  * marked as "full". Any subsequent packets arriving at the driver will
104da14cebeSEric Cheng  * still be sent to the MAC layer where it either gets queued in the Tx
105da14cebeSEric Cheng  * SRS or discarded it if queue limit is exceeded. The write-side STREAMS
106da14cebeSEric Cheng  * queue gets enabled when MAC layer notifies DLD through MAC_NOTE_TX.
107da14cebeSEric Cheng  * When the write service procedure runs, it will remove the dummy
108da14cebeSEric Cheng  * message from the write-side STREAMS queue; in effect this will trigger
109da14cebeSEric Cheng  * backenabling. The sizes of q_hiwat and q_lowat are set to 1 and 0,
110da14cebeSEric Cheng  * respectively, due to the above reasons.
111210db224Sericheng  *
112da14cebeSEric Cheng  * All non-data operations, both DLPI and ioctls are single threaded on a per
113da14cebeSEric Cheng  * dld_str_t endpoint. This is done using a taskq so that the control operation
114da14cebeSEric Cheng  * has kernel context and can cv_wait for resources. In addition all set type
115da14cebeSEric Cheng  * operations that involve mac level state modification are serialized on a
116da14cebeSEric Cheng  * per mac end point using the perimeter mechanism provided by the mac layer.
117da14cebeSEric Cheng  * This serializes all mac clients trying to modify a single mac end point over
118da14cebeSEric Cheng  * the entire sequence of mac calls made by that client as an atomic unit. The
119da14cebeSEric Cheng  * mac framework locking is described in mac.c. A critical element is that
120da14cebeSEric Cheng  * DLD/DLS does not hold any locks across the mac perimeter.
121210db224Sericheng  *
122c0192a57Sericheng  * dld_finddevinfo() returns the dev_info_t * corresponding to a particular
123c0192a57Sericheng  * dev_t. It searches str_hashp (a table of dld_str_t's) for streams that
124c0192a57Sericheng  * match dev_t. If a stream is found and it is attached, its dev_info_t *
125da14cebeSEric Cheng  * is returned. If the mac handle is non-null, it can be safely accessed
126da14cebeSEric Cheng  * below. The mac handle won't be freed until the mac_unregister which
127da14cebeSEric Cheng  * won't happen until the driver detaches. The DDI framework ensures that
128da14cebeSEric Cheng  * the detach won't happen while a getinfo is in progress.
129c0192a57Sericheng  */
130c0192a57Sericheng typedef struct i_dld_str_state_s {
131c0192a57Sericheng 	major_t		ds_major;
132c0192a57Sericheng 	minor_t		ds_minor;
13361af1958SGarrett D'Amore 	int		ds_instance;
134c0192a57Sericheng 	dev_info_t	*ds_dip;
135c0192a57Sericheng } i_dld_str_state_t;
136c0192a57Sericheng 
137c0192a57Sericheng /* ARGSUSED */
138c0192a57Sericheng static uint_t
i_dld_str_walker(mod_hash_key_t key,mod_hash_val_t * val,void * arg)139c0192a57Sericheng i_dld_str_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
140c0192a57Sericheng {
141c0192a57Sericheng 	i_dld_str_state_t	*statep = arg;
142c0192a57Sericheng 	dld_str_t		*dsp = (dld_str_t *)val;
143da14cebeSEric Cheng 	mac_handle_t		mh;
144c0192a57Sericheng 
145c0192a57Sericheng 	if (statep->ds_major != dsp->ds_major)
146c0192a57Sericheng 		return (MH_WALK_CONTINUE);
147c0192a57Sericheng 
148c0192a57Sericheng 	ASSERT(statep->ds_minor != 0);
149da14cebeSEric Cheng 	mh = dsp->ds_mh;
150c0192a57Sericheng 
151d62bc4baSyz 	if (statep->ds_minor == dsp->ds_minor) {
152c0192a57Sericheng 		/*
153c0192a57Sericheng 		 * Clone: a clone minor is unique. we can terminate the
154c0192a57Sericheng 		 * walk if we find a matching stream -- even if we fail
155c0192a57Sericheng 		 * to obtain the devinfo.
156c0192a57Sericheng 		 */
15761af1958SGarrett D'Amore 		if (mh != NULL) {
158da14cebeSEric Cheng 			statep->ds_dip = mac_devinfo_get(mh);
1593ade6e84SGarrett D'Amore 			statep->ds_instance = DLS_MINOR2INST(mac_minor(mh));
16061af1958SGarrett D'Amore 		}
161d62bc4baSyz 		return (MH_WALK_TERMINATE);
162c0192a57Sericheng 	}
163c0192a57Sericheng 	return (MH_WALK_CONTINUE);
164c0192a57Sericheng }
165c0192a57Sericheng 
166210db224Sericheng static dev_info_t *
dld_finddevinfo(dev_t dev)167210db224Sericheng dld_finddevinfo(dev_t dev)
168210db224Sericheng {
169da14cebeSEric Cheng 	dev_info_t		*dip;
170c0192a57Sericheng 	i_dld_str_state_t	state;
171210db224Sericheng 
172d62bc4baSyz 	if (getminor(dev) == 0)
173d62bc4baSyz 		return (NULL);
174d62bc4baSyz 
175d62bc4baSyz 	/*
176d62bc4baSyz 	 * See if it's a minor node of a link
177d62bc4baSyz 	 */
178da14cebeSEric Cheng 	if ((dip = dls_link_devinfo(dev)) != NULL)
179d62bc4baSyz 		return (dip);
180d62bc4baSyz 
181c0192a57Sericheng 	state.ds_minor = getminor(dev);
182c0192a57Sericheng 	state.ds_major = getmajor(dev);
183c0192a57Sericheng 	state.ds_dip = NULL;
18461af1958SGarrett D'Amore 	state.ds_instance = -1;
185210db224Sericheng 
186c0192a57Sericheng 	mod_hash_walk(str_hashp, i_dld_str_walker, &state);
187d62bc4baSyz 	return (state.ds_dip);
188f4b3ec61Sdh }
189c0192a57Sericheng 
19061af1958SGarrett D'Amore int
dld_devt_to_instance(dev_t dev)19161af1958SGarrett D'Amore dld_devt_to_instance(dev_t dev)
19261af1958SGarrett D'Amore {
19361af1958SGarrett D'Amore 	minor_t			minor;
19461af1958SGarrett D'Amore 	i_dld_str_state_t	state;
19561af1958SGarrett D'Amore 
19661af1958SGarrett D'Amore 	/*
19761af1958SGarrett D'Amore 	 * GLDv3 numbers DLPI style 1 node as the instance number + 1.
19861af1958SGarrett D'Amore 	 * Minor number 0 is reserved for the DLPI style 2 unattached
19961af1958SGarrett D'Amore 	 * node.
20061af1958SGarrett D'Amore 	 */
20161af1958SGarrett D'Amore 
20261af1958SGarrett D'Amore 	if ((minor = getminor(dev)) == 0)
20361af1958SGarrett D'Amore 		return (-1);
20461af1958SGarrett D'Amore 
20561af1958SGarrett D'Amore 	/*
2063ade6e84SGarrett D'Amore 	 * Check for unopened style 1 node.
2073ade6e84SGarrett D'Amore 	 * Note that this doesn't *necessarily* work for legacy
20861af1958SGarrett D'Amore 	 * devices, but this code is only called within the
20961af1958SGarrett D'Amore 	 * getinfo(9e) implementation for true GLDv3 devices, so it
21061af1958SGarrett D'Amore 	 * doesn't matter.
21161af1958SGarrett D'Amore 	 */
21261af1958SGarrett D'Amore 	if (minor > 0 && minor <= DLS_MAX_MINOR) {
21361af1958SGarrett D'Amore 		return (DLS_MINOR2INST(minor));
21461af1958SGarrett D'Amore 	}
21561af1958SGarrett D'Amore 
21661af1958SGarrett D'Amore 	state.ds_minor = getminor(dev);
21761af1958SGarrett D'Amore 	state.ds_major = getmajor(dev);
21861af1958SGarrett D'Amore 	state.ds_dip = NULL;
21961af1958SGarrett D'Amore 	state.ds_instance = -1;
22061af1958SGarrett D'Amore 
22161af1958SGarrett D'Amore 	mod_hash_walk(str_hashp, i_dld_str_walker, &state);
22261af1958SGarrett D'Amore 	return (state.ds_instance);
22361af1958SGarrett D'Amore }
22461af1958SGarrett D'Amore 
225210db224Sericheng /*
226210db224Sericheng  * devo_getinfo: getinfo(9e)
22761af1958SGarrett D'Amore  *
22861af1958SGarrett D'Amore  * NB: This may be called for a provider before the provider's
22961af1958SGarrett D'Amore  * instances are attached.  Hence, if a particular provider needs a
23061af1958SGarrett D'Amore  * special mapping (the mac instance != ddi_get_instance()), then it
231*86e5bb79SLuqman Aden  * may need to provide its own implementation using the
2323ade6e84SGarrett D'Amore  * mac_devt_to_instance() function, and translating the returned mac
23361af1958SGarrett D'Amore  * instance to a devinfo instance.  For dev_t's where the minor number
23461af1958SGarrett D'Amore  * is too large (i.e. > MAC_MAX_MINOR), the provider can call this
23561af1958SGarrett D'Amore  * function indirectly via the mac_getinfo() function.
236210db224Sericheng  */
237210db224Sericheng /*ARGSUSED*/
238210db224Sericheng int
dld_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resp)239210db224Sericheng dld_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
240210db224Sericheng {
241210db224Sericheng 	dev_info_t	*devinfo;
242210db224Sericheng 	minor_t		minor = getminor((dev_t)arg);
243210db224Sericheng 	int		rc = DDI_FAILURE;
244210db224Sericheng 
245210db224Sericheng 	switch (cmd) {
246210db224Sericheng 	case DDI_INFO_DEVT2DEVINFO:
247210db224Sericheng 		if ((devinfo = dld_finddevinfo((dev_t)arg)) != NULL) {
248210db224Sericheng 			*(dev_info_t **)resp = devinfo;
249210db224Sericheng 			rc = DDI_SUCCESS;
250210db224Sericheng 		}
251210db224Sericheng 		break;
252210db224Sericheng 	case DDI_INFO_DEVT2INSTANCE:
253d62bc4baSyz 		if (minor > 0 && minor <= DLS_MAX_MINOR) {
254c0192a57Sericheng 			*resp = (void *)(uintptr_t)DLS_MINOR2INST(minor);
255c0192a57Sericheng 			rc = DDI_SUCCESS;
256d62bc4baSyz 		} else if (minor > DLS_MAX_MINOR &&
257c0192a57Sericheng 		    (devinfo = dld_finddevinfo((dev_t)arg)) != NULL) {
258c0192a57Sericheng 			*resp = (void *)(uintptr_t)ddi_get_instance(devinfo);
259210db224Sericheng 			rc = DDI_SUCCESS;
260210db224Sericheng 		}
261210db224Sericheng 		break;
262210db224Sericheng 	}
263210db224Sericheng 	return (rc);
264210db224Sericheng }
265210db224Sericheng 
2665d460eafSCathy Zhou void *
dld_str_private(queue_t * q)2675d460eafSCathy Zhou dld_str_private(queue_t *q)
2685d460eafSCathy Zhou {
2695d460eafSCathy Zhou 	return (((dld_str_t *)(q->q_ptr))->ds_private);
2705d460eafSCathy Zhou }
2715d460eafSCathy Zhou 
272210db224Sericheng int
dld_str_open(queue_t * rq,dev_t * devp,void * private)2735d460eafSCathy Zhou dld_str_open(queue_t *rq, dev_t *devp, void *private)
274210db224Sericheng {
275210db224Sericheng 	dld_str_t	*dsp;
276210db224Sericheng 	major_t		major;
277210db224Sericheng 	minor_t		minor;
278210db224Sericheng 	int		err;
279210db224Sericheng 
280210db224Sericheng 	major = getmajor(*devp);
281210db224Sericheng 	minor = getminor(*devp);
282210db224Sericheng 
283*86e5bb79SLuqman Aden 	/*
284*86e5bb79SLuqman Aden 	 * Half the 32-bit minor space is reserved for private use by the driver
285*86e5bb79SLuqman Aden 	 * so we bail out here with `ENOSTR` to indicate specfs should retry the
286*86e5bb79SLuqman Aden 	 * open with the driver's character based `open(9E)`. For a typical
287*86e5bb79SLuqman Aden 	 * STREAMS driver, that would just be `nodev` which would simply return
288*86e5bb79SLuqman Aden 	 * `ENODEV`. But a dual-personality device can choose to implement the
289*86e5bb79SLuqman Aden 	 * character based `open(9E)` for some minor nodes. A driver wanting a
290*86e5bb79SLuqman Aden 	 * separate STREAMS interface altogether would presumably have already
291*86e5bb79SLuqman Aden 	 * provided its own `streamtab`.
292*86e5bb79SLuqman Aden 	 */
293*86e5bb79SLuqman Aden 	if (minor >= mac_private_minor())
294*86e5bb79SLuqman Aden 		return (ENOSTR);
295*86e5bb79SLuqman Aden 
296210db224Sericheng 	/*
297210db224Sericheng 	 * Create a new dld_str_t for the stream. This will grab a new minor
298210db224Sericheng 	 * number that will be handed back in the cloned dev_t.  Creation may
299210db224Sericheng 	 * fail if we can't allocate the dummy mblk used for flow-control.
300210db224Sericheng 	 */
301210db224Sericheng 	dsp = dld_str_create(rq, DLD_DLPI, major,
302210db224Sericheng 	    ((minor == 0) ? DL_STYLE2 : DL_STYLE1));
303210db224Sericheng 	if (dsp == NULL)
304210db224Sericheng 		return (ENOSR);
305210db224Sericheng 
306210db224Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNATTACHED);
3075d460eafSCathy Zhou 	dsp->ds_private = private;
308210db224Sericheng 	if (minor != 0) {
309210db224Sericheng 		/*
310210db224Sericheng 		 * Style 1 open
311210db224Sericheng 		 */
312d62bc4baSyz 		if ((err = dld_str_attach(dsp, (t_uscalar_t)minor - 1)) != 0)
313210db224Sericheng 			goto failed;
3145d460eafSCathy Zhou 
315210db224Sericheng 		ASSERT(dsp->ds_dlstate == DL_UNBOUND);
316cd93090eSericheng 	} else {
317cd93090eSericheng 		(void) qassociate(rq, -1);
318210db224Sericheng 	}
319210db224Sericheng 
320210db224Sericheng 	/*
321210db224Sericheng 	 * Enable the queue srv(9e) routine.
322210db224Sericheng 	 */
323210db224Sericheng 	qprocson(rq);
324210db224Sericheng 
325210db224Sericheng 	/*
326210db224Sericheng 	 * Construct a cloned dev_t to hand back.
327210db224Sericheng 	 */
328210db224Sericheng 	*devp = makedevice(getmajor(*devp), dsp->ds_minor);
329210db224Sericheng 	return (0);
330210db224Sericheng 
331210db224Sericheng failed:
332210db224Sericheng 	dld_str_destroy(dsp);
333210db224Sericheng 	return (err);
334210db224Sericheng }
335210db224Sericheng 
336210db224Sericheng int
dld_str_close(queue_t * rq)3375d460eafSCathy Zhou dld_str_close(queue_t *rq)
338210db224Sericheng {
339210db224Sericheng 	dld_str_t	*dsp = rq->q_ptr;
340210db224Sericheng 
341da14cebeSEric Cheng 	/*
342da14cebeSEric Cheng 	 * All modules on top have been popped off. So there can't be any
343da14cebeSEric Cheng 	 * threads from the top.
344da14cebeSEric Cheng 	 */
345da14cebeSEric Cheng 	ASSERT(dsp->ds_datathr_cnt == 0);
346da14cebeSEric Cheng 
347da14cebeSEric Cheng 	/*
348da14cebeSEric Cheng 	 * Wait until pending DLPI requests are processed.
349da14cebeSEric Cheng 	 */
350da14cebeSEric Cheng 	mutex_enter(&dsp->ds_lock);
351da14cebeSEric Cheng 	while (dsp->ds_dlpi_pending)
352da14cebeSEric Cheng 		cv_wait(&dsp->ds_dlpi_pending_cv, &dsp->ds_lock);
353da14cebeSEric Cheng 	mutex_exit(&dsp->ds_lock);
354da14cebeSEric Cheng 
355210db224Sericheng 
356210db224Sericheng 	/*
357210db224Sericheng 	 * This stream was open to a provider node. Check to see
358210db224Sericheng 	 * if it has been cleanly shut down.
359210db224Sericheng 	 */
360210db224Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
361210db224Sericheng 		/*
362210db224Sericheng 		 * The stream is either open to a style 1 provider or
363210db224Sericheng 		 * this is not clean shutdown. Detach from the PPA.
364210db224Sericheng 		 * (This is still ok even in the style 1 case).
365210db224Sericheng 		 */
366210db224Sericheng 		dld_str_detach(dsp);
367210db224Sericheng 	}
368210db224Sericheng 
369210db224Sericheng 	dld_str_destroy(dsp);
370210db224Sericheng 	return (0);
371210db224Sericheng }
3727c478bd9Sstevel@tonic-gate 
3735d460eafSCathy Zhou /*
3745d460eafSCathy Zhou  * qi_qopen: open(9e)
3755d460eafSCathy Zhou  */
3765d460eafSCathy Zhou /*ARGSUSED*/
3775d460eafSCathy Zhou int
dld_open(queue_t * rq,dev_t * devp,int flag,int sflag,cred_t * credp)3785d460eafSCathy Zhou dld_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
3795d460eafSCathy Zhou {
3805d460eafSCathy Zhou 	if (sflag == MODOPEN)
3815d460eafSCathy Zhou 		return (ENOTSUP);
3825d460eafSCathy Zhou 
3835d460eafSCathy Zhou 	/*
3845d460eafSCathy Zhou 	 * This is a cloning driver and therefore each queue should only
3855d460eafSCathy Zhou 	 * ever get opened once.
3865d460eafSCathy Zhou 	 */
3875d460eafSCathy Zhou 	if (rq->q_ptr != NULL)
3885d460eafSCathy Zhou 		return (EBUSY);
3895d460eafSCathy Zhou 
3905d460eafSCathy Zhou 	return (dld_str_open(rq, devp, NULL));
3915d460eafSCathy Zhou }
3925d460eafSCathy Zhou 
3935d460eafSCathy Zhou /*
3945d460eafSCathy Zhou  * qi_qclose: close(9e)
3955d460eafSCathy Zhou  */
3965e1743f0SToomas Soome /* ARGSUSED */
3975d460eafSCathy Zhou int
dld_close(queue_t * rq,int flags __unused,cred_t * credp __unused)3985e1743f0SToomas Soome dld_close(queue_t *rq, int flags __unused, cred_t *credp __unused)
3995d460eafSCathy Zhou {
4005d460eafSCathy Zhou 	/*
4015d460eafSCathy Zhou 	 * Disable the queue srv(9e) routine.
4025d460eafSCathy Zhou 	 */
4035d460eafSCathy Zhou 	qprocsoff(rq);
4045d460eafSCathy Zhou 
4055d460eafSCathy Zhou 	return (dld_str_close(rq));
4065d460eafSCathy Zhou }
4075d460eafSCathy Zhou 
4087c478bd9Sstevel@tonic-gate /*
409210db224Sericheng  * qi_qputp: put(9e)
4107c478bd9Sstevel@tonic-gate  */
411f1ccfd86SToomas Soome int
dld_wput(queue_t * wq,mblk_t * mp)412210db224Sericheng dld_wput(queue_t *wq, mblk_t *mp)
413210db224Sericheng {
414da14cebeSEric Cheng 	dld_str_t *dsp = (dld_str_t *)wq->q_ptr;
415da14cebeSEric Cheng 	dld_str_mode_t	mode;
416210db224Sericheng 
417210db224Sericheng 	switch (DB_TYPE(mp)) {
418da14cebeSEric Cheng 	case M_DATA:
419da14cebeSEric Cheng 		mutex_enter(&dsp->ds_lock);
420b53ab68fSCathy Zhou 		mode = dsp->ds_mode;
421b53ab68fSCathy Zhou 		if ((dsp->ds_dlstate != DL_IDLE) ||
422b53ab68fSCathy Zhou 		    (mode != DLD_FASTPATH && mode != DLD_RAW)) {
423b53ab68fSCathy Zhou 			mutex_exit(&dsp->ds_lock);
424b53ab68fSCathy Zhou 			freemsg(mp);
425b53ab68fSCathy Zhou 			break;
426da14cebeSEric Cheng 		}
427b53ab68fSCathy Zhou 
428b53ab68fSCathy Zhou 		DLD_DATATHR_INC(dsp);
429da14cebeSEric Cheng 		mutex_exit(&dsp->ds_lock);
430b53ab68fSCathy Zhou 		if (mode == DLD_FASTPATH) {
431b53ab68fSCathy Zhou 			if (dsp->ds_mip->mi_media == DL_ETHER &&
432b53ab68fSCathy Zhou 			    (MBLKL(mp) < sizeof (struct ether_header))) {
433b53ab68fSCathy Zhou 				freemsg(mp);
434b53ab68fSCathy Zhou 			} else {
435b53ab68fSCathy Zhou 				(void) str_mdata_fastpath_put(dsp, mp, 0, 0);
436b53ab68fSCathy Zhou 			}
437b53ab68fSCathy Zhou 		} else {
438b53ab68fSCathy Zhou 			str_mdata_raw_put(dsp, mp);
439b53ab68fSCathy Zhou 		}
440b53ab68fSCathy Zhou 		DLD_DATATHR_DCR(dsp);
441210db224Sericheng 		break;
442210db224Sericheng 	case M_PROTO:
443d62bc4baSyz 	case M_PCPROTO: {
444d62bc4baSyz 		t_uscalar_t	prim;
445d62bc4baSyz 
446da14cebeSEric Cheng 		if (MBLKL(mp) < sizeof (t_uscalar_t))
447da14cebeSEric Cheng 			break;
448d62bc4baSyz 
449d62bc4baSyz 		prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
450da14cebeSEric Cheng 
451da14cebeSEric Cheng 		if (prim == DL_UNITDATA_REQ) {
452da14cebeSEric Cheng 			proto_unitdata_req(dsp, mp);
453da14cebeSEric Cheng 		} else {
454d62bc4baSyz 			dld_wput_nondata(dsp, mp);
455d62bc4baSyz 		}
456210db224Sericheng 		break;
457d62bc4baSyz 	}
458da14cebeSEric Cheng 
459210db224Sericheng 	case M_IOCTL:
460d62bc4baSyz 		dld_wput_nondata(dsp, mp);
461210db224Sericheng 		break;
462da14cebeSEric Cheng 
463210db224Sericheng 	case M_FLUSH:
464210db224Sericheng 		if (*mp->b_rptr & FLUSHW) {
465da14cebeSEric Cheng 			DLD_CLRQFULL(dsp);
466210db224Sericheng 			*mp->b_rptr &= ~FLUSHW;
467210db224Sericheng 		}
468210db224Sericheng 
469210db224Sericheng 		if (*mp->b_rptr & FLUSHR) {
470210db224Sericheng 			qreply(wq, mp);
471210db224Sericheng 		} else {
472210db224Sericheng 			freemsg(mp);
473210db224Sericheng 		}
474210db224Sericheng 		break;
475da14cebeSEric Cheng 
476210db224Sericheng 	default:
477210db224Sericheng 		freemsg(mp);
478210db224Sericheng 		break;
479210db224Sericheng 	}
480f1ccfd86SToomas Soome 	return (0);
481d62bc4baSyz }
482210db224Sericheng 
4837c478bd9Sstevel@tonic-gate /*
484210db224Sericheng  * qi_srvp: srv(9e)
4857c478bd9Sstevel@tonic-gate  */
486f1ccfd86SToomas Soome int
dld_wsrv(queue_t * wq)487210db224Sericheng dld_wsrv(queue_t *wq)
488210db224Sericheng {
489210db224Sericheng 	dld_str_t	*dsp = wq->q_ptr;
490d62bc4baSyz 
491da14cebeSEric Cheng 	DLD_CLRQFULL(dsp);
492f1ccfd86SToomas Soome 	return (0);
493210db224Sericheng }
494210db224Sericheng 
495210db224Sericheng void
dld_init_ops(struct dev_ops * ops,const char * name)496210db224Sericheng dld_init_ops(struct dev_ops *ops, const char *name)
497210db224Sericheng {
498210db224Sericheng 	struct streamtab *stream;
499210db224Sericheng 	struct qinit *rq, *wq;
500210db224Sericheng 	struct module_info *modinfo;
501210db224Sericheng 
502210db224Sericheng 	modinfo = kmem_zalloc(sizeof (struct module_info), KM_SLEEP);
503210db224Sericheng 	modinfo->mi_idname = kmem_zalloc(FMNAMESZ, KM_SLEEP);
504210db224Sericheng 	(void) snprintf(modinfo->mi_idname, FMNAMESZ, "%s", name);
505210db224Sericheng 	modinfo->mi_minpsz = 0;
506210db224Sericheng 	modinfo->mi_maxpsz = 64*1024;
507210db224Sericheng 	modinfo->mi_hiwat  = 1;
508210db224Sericheng 	modinfo->mi_lowat = 0;
509210db224Sericheng 
510210db224Sericheng 	rq = kmem_zalloc(sizeof (struct qinit), KM_SLEEP);
511210db224Sericheng 	rq->qi_qopen = dld_open;
512210db224Sericheng 	rq->qi_qclose = dld_close;
513210db224Sericheng 	rq->qi_minfo = modinfo;
514210db224Sericheng 
515210db224Sericheng 	wq = kmem_zalloc(sizeof (struct qinit), KM_SLEEP);
516210db224Sericheng 	wq->qi_putp = (pfi_t)dld_wput;
517210db224Sericheng 	wq->qi_srvp = (pfi_t)dld_wsrv;
518210db224Sericheng 	wq->qi_minfo = modinfo;
519210db224Sericheng 
520210db224Sericheng 	stream = kmem_zalloc(sizeof (struct streamtab), KM_SLEEP);
521210db224Sericheng 	stream->st_rdinit = rq;
522210db224Sericheng 	stream->st_wrinit = wq;
523210db224Sericheng 	ops->devo_cb_ops->cb_str = stream;
524210db224Sericheng 
525eae72b5bSSebastien Roy 	if (ops->devo_getinfo == NULL)
526eae72b5bSSebastien Roy 		ops->devo_getinfo = &dld_getinfo;
527210db224Sericheng }
528210db224Sericheng 
529210db224Sericheng void
dld_fini_ops(struct dev_ops * ops)530210db224Sericheng dld_fini_ops(struct dev_ops *ops)
531210db224Sericheng {
532210db224Sericheng 	struct streamtab *stream;
533210db224Sericheng 	struct qinit *rq, *wq;
534210db224Sericheng 	struct module_info *modinfo;
535210db224Sericheng 
536210db224Sericheng 	stream = ops->devo_cb_ops->cb_str;
537210db224Sericheng 	rq = stream->st_rdinit;
538210db224Sericheng 	wq = stream->st_wrinit;
539210db224Sericheng 	modinfo = rq->qi_minfo;
540210db224Sericheng 	ASSERT(wq->qi_minfo == modinfo);
541210db224Sericheng 
542210db224Sericheng 	kmem_free(stream, sizeof (struct streamtab));
543210db224Sericheng 	kmem_free(wq, sizeof (struct qinit));
544210db224Sericheng 	kmem_free(rq, sizeof (struct qinit));
545210db224Sericheng 	kmem_free(modinfo->mi_idname, FMNAMESZ);
546210db224Sericheng 	kmem_free(modinfo, sizeof (struct module_info));
547210db224Sericheng }
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate /*
5507c478bd9Sstevel@tonic-gate  * Initialize this module's data structures.
5517c478bd9Sstevel@tonic-gate  */
5527c478bd9Sstevel@tonic-gate void
dld_str_init(void)5537c478bd9Sstevel@tonic-gate dld_str_init(void)
5547c478bd9Sstevel@tonic-gate {
5557c478bd9Sstevel@tonic-gate 	/*
5567c478bd9Sstevel@tonic-gate 	 * Create dld_str_t object cache.
5577c478bd9Sstevel@tonic-gate 	 */
5587c478bd9Sstevel@tonic-gate 	str_cachep = kmem_cache_create("dld_str_cache", sizeof (dld_str_t),
5597c478bd9Sstevel@tonic-gate 	    0, str_constructor, str_destructor, NULL, NULL, NULL, 0);
5607c478bd9Sstevel@tonic-gate 	ASSERT(str_cachep != NULL);
561210db224Sericheng 
562c0192a57Sericheng 	/*
563c0192a57Sericheng 	 * Create a hash table for maintaining dld_str_t's.
564c0192a57Sericheng 	 * The ds_minor field (the clone minor number) of a dld_str_t
565c0192a57Sericheng 	 * is used as a key for this hash table because this number is
566f4b3ec61Sdh 	 * globally unique (allocated from "dls_minor_arena").
567c0192a57Sericheng 	 */
568c0192a57Sericheng 	str_hashp = mod_hash_create_idhash("dld_str_hash", STR_HASHSZ,
569c0192a57Sericheng 	    mod_hash_null_valdtor);
570da14cebeSEric Cheng 
571da14cebeSEric Cheng 	mutex_init(&dld_taskq_lock, NULL, MUTEX_DRIVER, NULL);
572da14cebeSEric Cheng 	cv_init(&dld_taskq_cv, NULL, CV_DRIVER, NULL);
573da14cebeSEric Cheng 
574da14cebeSEric Cheng 	dld_taskq_quit = B_FALSE;
575da14cebeSEric Cheng 	dld_taskq_done = B_FALSE;
576da14cebeSEric Cheng 	list_create(&dld_taskq_list, sizeof (dld_str_t),
577da14cebeSEric Cheng 	    offsetof(dld_str_t, ds_tqlist));
578da14cebeSEric Cheng 	(void) thread_create(NULL, 0, dld_taskq_dispatch, NULL, 0,
579da14cebeSEric Cheng 	    &p0, TS_RUN, minclsyspri);
5807c478bd9Sstevel@tonic-gate }
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate /*
5837c478bd9Sstevel@tonic-gate  * Tear down this module's data structures.
5847c478bd9Sstevel@tonic-gate  */
5857c478bd9Sstevel@tonic-gate int
dld_str_fini(void)5867c478bd9Sstevel@tonic-gate dld_str_fini(void)
5877c478bd9Sstevel@tonic-gate {
5887c478bd9Sstevel@tonic-gate 	/*
5897c478bd9Sstevel@tonic-gate 	 * Make sure that there are no objects in use.
5907c478bd9Sstevel@tonic-gate 	 */
5917c478bd9Sstevel@tonic-gate 	if (str_count != 0)
5927c478bd9Sstevel@tonic-gate 		return (EBUSY);
5937c478bd9Sstevel@tonic-gate 
594da14cebeSEric Cheng 	/*
595da14cebeSEric Cheng 	 * Ask the dld_taskq thread to quit and wait for it to be done
596da14cebeSEric Cheng 	 */
597da14cebeSEric Cheng 	mutex_enter(&dld_taskq_lock);
598da14cebeSEric Cheng 	dld_taskq_quit = B_TRUE;
599da14cebeSEric Cheng 	cv_signal(&dld_taskq_cv);
600da14cebeSEric Cheng 	while (!dld_taskq_done)
601da14cebeSEric Cheng 		cv_wait(&dld_taskq_cv, &dld_taskq_lock);
602da14cebeSEric Cheng 	mutex_exit(&dld_taskq_lock);
603da14cebeSEric Cheng 	list_destroy(&dld_taskq_list);
6047c478bd9Sstevel@tonic-gate 	/*
6057c478bd9Sstevel@tonic-gate 	 * Destroy object cache.
6067c478bd9Sstevel@tonic-gate 	 */
6077c478bd9Sstevel@tonic-gate 	kmem_cache_destroy(str_cachep);
608c0192a57Sericheng 	mod_hash_destroy_idhash(str_hashp);
6097c478bd9Sstevel@tonic-gate 	return (0);
6107c478bd9Sstevel@tonic-gate }
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate /*
6137c478bd9Sstevel@tonic-gate  * Create a new dld_str_t object.
6147c478bd9Sstevel@tonic-gate  */
6157c478bd9Sstevel@tonic-gate dld_str_t *
dld_str_create(queue_t * rq,uint_t type,major_t major,t_uscalar_t style)616210db224Sericheng dld_str_create(queue_t *rq, uint_t type, major_t major, t_uscalar_t style)
6177c478bd9Sstevel@tonic-gate {
6187c478bd9Sstevel@tonic-gate 	dld_str_t	*dsp;
619c0192a57Sericheng 	int		err;
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	/*
6227c478bd9Sstevel@tonic-gate 	 * Allocate an object from the cache.
6237c478bd9Sstevel@tonic-gate 	 */
6241a5e258fSJosef 'Jeff' Sipek 	atomic_inc_32(&str_count);
625210db224Sericheng 	dsp = kmem_cache_alloc(str_cachep, KM_SLEEP);
626210db224Sericheng 
627210db224Sericheng 	/*
628210db224Sericheng 	 * Allocate the dummy mblk for flow-control.
629210db224Sericheng 	 */
630210db224Sericheng 	dsp->ds_tx_flow_mp = allocb(1, BPRI_HI);
631210db224Sericheng 	if (dsp->ds_tx_flow_mp == NULL) {
632210db224Sericheng 		kmem_cache_free(str_cachep, dsp);
6331a5e258fSJosef 'Jeff' Sipek 		atomic_dec_32(&str_count);
634210db224Sericheng 		return (NULL);
635210db224Sericheng 	}
636210db224Sericheng 	dsp->ds_type = type;
637210db224Sericheng 	dsp->ds_major = major;
638210db224Sericheng 	dsp->ds_style = style;
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate 	/*
6417c478bd9Sstevel@tonic-gate 	 * Initialize the queue pointers.
6427c478bd9Sstevel@tonic-gate 	 */
6437c478bd9Sstevel@tonic-gate 	ASSERT(RD(rq) == rq);
6447c478bd9Sstevel@tonic-gate 	dsp->ds_rq = rq;
6457c478bd9Sstevel@tonic-gate 	dsp->ds_wq = WR(rq);
6467c478bd9Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = (void *)dsp;
6477c478bd9Sstevel@tonic-gate 
648210db224Sericheng 	/*
649210db224Sericheng 	 * We want explicit control over our write-side STREAMS queue
650210db224Sericheng 	 * where the dummy mblk gets added/removed for flow-control.
651210db224Sericheng 	 */
652210db224Sericheng 	noenable(WR(rq));
653210db224Sericheng 
654c0192a57Sericheng 	err = mod_hash_insert(str_hashp, STR_HASH_KEY(dsp->ds_minor),
655c0192a57Sericheng 	    (mod_hash_val_t)dsp);
656c0192a57Sericheng 	ASSERT(err == 0);
6577c478bd9Sstevel@tonic-gate 	return (dsp);
6587c478bd9Sstevel@tonic-gate }
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate /*
6617c478bd9Sstevel@tonic-gate  * Destroy a dld_str_t object.
6627c478bd9Sstevel@tonic-gate  */
6637c478bd9Sstevel@tonic-gate void
dld_str_destroy(dld_str_t * dsp)6647c478bd9Sstevel@tonic-gate dld_str_destroy(dld_str_t *dsp)
6657c478bd9Sstevel@tonic-gate {
6667c478bd9Sstevel@tonic-gate 	queue_t		*rq;
6677c478bd9Sstevel@tonic-gate 	queue_t		*wq;
668c0192a57Sericheng 	mod_hash_val_t	val;
669da14cebeSEric Cheng 
6707c478bd9Sstevel@tonic-gate 	/*
6717c478bd9Sstevel@tonic-gate 	 * Clear the queue pointers.
6727c478bd9Sstevel@tonic-gate 	 */
6737c478bd9Sstevel@tonic-gate 	rq = dsp->ds_rq;
6747c478bd9Sstevel@tonic-gate 	wq = dsp->ds_wq;
6757c478bd9Sstevel@tonic-gate 	ASSERT(wq == WR(rq));
6767c478bd9Sstevel@tonic-gate 	rq->q_ptr = wq->q_ptr = NULL;
6777c478bd9Sstevel@tonic-gate 	dsp->ds_rq = dsp->ds_wq = NULL;
6787c478bd9Sstevel@tonic-gate 
679da14cebeSEric Cheng 	ASSERT(dsp->ds_dlstate == DL_UNATTACHED);
680da14cebeSEric Cheng 	ASSERT(dsp->ds_sap == 0);
681da14cebeSEric Cheng 	ASSERT(dsp->ds_mh == NULL);
682da14cebeSEric Cheng 	ASSERT(dsp->ds_mch == NULL);
683da14cebeSEric Cheng 	ASSERT(dsp->ds_promisc == 0);
684da14cebeSEric Cheng 	ASSERT(dsp->ds_mph == NULL);
685da14cebeSEric Cheng 	ASSERT(dsp->ds_mip == NULL);
686da14cebeSEric Cheng 	ASSERT(dsp->ds_mnh == NULL);
687210db224Sericheng 
688da14cebeSEric Cheng 	ASSERT(dsp->ds_polling == B_FALSE);
689da14cebeSEric Cheng 	ASSERT(dsp->ds_direct == B_FALSE);
690da14cebeSEric Cheng 	ASSERT(dsp->ds_lso == B_FALSE);
691da14cebeSEric Cheng 	ASSERT(dsp->ds_lso_max == 0);
6925d460eafSCathy Zhou 	ASSERT(dsp->ds_passivestate != DLD_ACTIVE);
693210db224Sericheng 
6947c478bd9Sstevel@tonic-gate 	/*
695fd69bb17Syz 	 * Reinitialize all the flags.
6967c478bd9Sstevel@tonic-gate 	 */
6977c478bd9Sstevel@tonic-gate 	dsp->ds_notifications = 0;
698fd69bb17Syz 	dsp->ds_passivestate = DLD_UNINITIALIZED;
699fd69bb17Syz 	dsp->ds_mode = DLD_UNITDATA;
7000ba2cbe9Sxc 	dsp->ds_native = B_FALSE;
7018d4cf8d8S 	dsp->ds_nonip = B_FALSE;
7027c478bd9Sstevel@tonic-gate 
703da14cebeSEric Cheng 	ASSERT(dsp->ds_datathr_cnt == 0);
704da14cebeSEric Cheng 	ASSERT(dsp->ds_pending_head == NULL);
705da14cebeSEric Cheng 	ASSERT(dsp->ds_pending_tail == NULL);
706da14cebeSEric Cheng 	ASSERT(!dsp->ds_dlpi_pending);
707da14cebeSEric Cheng 
708da14cebeSEric Cheng 	ASSERT(dsp->ds_dlp == NULL);
709da14cebeSEric Cheng 	ASSERT(dsp->ds_dmap == NULL);
710da14cebeSEric Cheng 	ASSERT(dsp->ds_rx == NULL);
711da14cebeSEric Cheng 	ASSERT(dsp->ds_rx_arg == NULL);
712da14cebeSEric Cheng 	ASSERT(dsp->ds_next == NULL);
713da14cebeSEric Cheng 	ASSERT(dsp->ds_head == NULL);
714da14cebeSEric Cheng 
715210db224Sericheng 	/*
716210db224Sericheng 	 * Free the dummy mblk if exists.
717210db224Sericheng 	 */
718210db224Sericheng 	if (dsp->ds_tx_flow_mp != NULL) {
719210db224Sericheng 		freeb(dsp->ds_tx_flow_mp);
720210db224Sericheng 		dsp->ds_tx_flow_mp = NULL;
721210db224Sericheng 	}
722c0192a57Sericheng 
723c0192a57Sericheng 	(void) mod_hash_remove(str_hashp, STR_HASH_KEY(dsp->ds_minor), &val);
724c0192a57Sericheng 	ASSERT(dsp == (dld_str_t *)val);
725c0192a57Sericheng 
7267c478bd9Sstevel@tonic-gate 	/*
7277c478bd9Sstevel@tonic-gate 	 * Free the object back to the cache.
7287c478bd9Sstevel@tonic-gate 	 */
7297c478bd9Sstevel@tonic-gate 	kmem_cache_free(str_cachep, dsp);
7301a5e258fSJosef 'Jeff' Sipek 	atomic_dec_32(&str_count);
7317c478bd9Sstevel@tonic-gate }
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate /*
7347c478bd9Sstevel@tonic-gate  * kmem_cache contructor function: see kmem_cache_create(9f).
7357c478bd9Sstevel@tonic-gate  */
7367c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7377c478bd9Sstevel@tonic-gate static int
str_constructor(void * buf,void * cdrarg,int kmflags)7387c478bd9Sstevel@tonic-gate str_constructor(void *buf, void *cdrarg, int kmflags)
7397c478bd9Sstevel@tonic-gate {
7407c478bd9Sstevel@tonic-gate 	dld_str_t	*dsp = buf;
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 	bzero(buf, sizeof (dld_str_t));
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 	/*
7457c478bd9Sstevel@tonic-gate 	 * Allocate a new minor number.
7467c478bd9Sstevel@tonic-gate 	 */
747d62bc4baSyz 	if ((dsp->ds_minor = mac_minor_hold(kmflags == KM_SLEEP)) == 0)
7487c478bd9Sstevel@tonic-gate 		return (-1);
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 	/*
7517c478bd9Sstevel@tonic-gate 	 * Initialize the DLPI state machine.
7527c478bd9Sstevel@tonic-gate 	 */
7537c478bd9Sstevel@tonic-gate 	dsp->ds_dlstate = DL_UNATTACHED;
7547c478bd9Sstevel@tonic-gate 
755da14cebeSEric Cheng 	mutex_init(&dsp->ds_lock, NULL, MUTEX_DRIVER, NULL);
756da14cebeSEric Cheng 	cv_init(&dsp->ds_datathr_cv, NULL, CV_DRIVER, NULL);
757da14cebeSEric Cheng 	cv_init(&dsp->ds_dlpi_pending_cv, NULL, CV_DRIVER, NULL);
758210db224Sericheng 
7597c478bd9Sstevel@tonic-gate 	return (0);
7607c478bd9Sstevel@tonic-gate }
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate /*
7637c478bd9Sstevel@tonic-gate  * kmem_cache destructor function.
7647c478bd9Sstevel@tonic-gate  */
7657c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7667c478bd9Sstevel@tonic-gate static void
str_destructor(void * buf,void * cdrarg)7677c478bd9Sstevel@tonic-gate str_destructor(void *buf, void *cdrarg)
7687c478bd9Sstevel@tonic-gate {
7697c478bd9Sstevel@tonic-gate 	dld_str_t	*dsp = buf;
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	/*
7727c478bd9Sstevel@tonic-gate 	 * Release the minor number.
7737c478bd9Sstevel@tonic-gate 	 */
774d62bc4baSyz 	mac_minor_rele(dsp->ds_minor);
7757c478bd9Sstevel@tonic-gate 
776210db224Sericheng 	ASSERT(dsp->ds_tx_flow_mp == NULL);
777d62bc4baSyz 
778da14cebeSEric Cheng 	mutex_destroy(&dsp->ds_lock);
779da14cebeSEric Cheng 	cv_destroy(&dsp->ds_datathr_cv);
780da14cebeSEric Cheng 	cv_destroy(&dsp->ds_dlpi_pending_cv);
7817c478bd9Sstevel@tonic-gate }
7827c478bd9Sstevel@tonic-gate 
7837c478bd9Sstevel@tonic-gate /*
784605445d5Sdg  * Update the priority bits and VID (may need to insert tag if mp points
785da14cebeSEric Cheng  * to an untagged packet.
786605445d5Sdg  * If vid is VLAN_ID_NONE, use the VID encoded in the packet.
787605445d5Sdg  */
788605445d5Sdg static mblk_t *
i_dld_ether_header_update_tag(mblk_t * mp,uint_t pri,uint16_t vid,link_tagmode_t tagmode)789e75f0919SSebastien Roy i_dld_ether_header_update_tag(mblk_t *mp, uint_t pri, uint16_t vid,
790e75f0919SSebastien Roy     link_tagmode_t tagmode)
791605445d5Sdg {
792605445d5Sdg 	mblk_t *hmp;
793605445d5Sdg 	struct ether_vlan_header *evhp;
794605445d5Sdg 	struct ether_header *ehp;
795605445d5Sdg 	uint16_t old_tci = 0;
796605445d5Sdg 	size_t len;
797605445d5Sdg 
798605445d5Sdg 	ASSERT(pri != 0 || vid != VLAN_ID_NONE);
799605445d5Sdg 
800605445d5Sdg 	evhp = (struct ether_vlan_header *)mp->b_rptr;
801605445d5Sdg 	if (ntohs(evhp->ether_tpid) == ETHERTYPE_VLAN) {
802605445d5Sdg 		/*
803605445d5Sdg 		 * Tagged packet, update the priority bits.
804605445d5Sdg 		 */
805605445d5Sdg 		len = sizeof (struct ether_vlan_header);
806605445d5Sdg 
807605445d5Sdg 		if ((DB_REF(mp) > 1) || (MBLKL(mp) < len)) {
808605445d5Sdg 			/*
809605445d5Sdg 			 * In case some drivers only check the db_ref
810605445d5Sdg 			 * count of the first mblk, we pullup the
811605445d5Sdg 			 * message into a single mblk.
812605445d5Sdg 			 */
813605445d5Sdg 			hmp = msgpullup(mp, -1);
814605445d5Sdg 			if ((hmp == NULL) || (MBLKL(hmp) < len)) {
815605445d5Sdg 				freemsg(hmp);
816605445d5Sdg 				return (NULL);
817605445d5Sdg 			} else {
818605445d5Sdg 				freemsg(mp);
819605445d5Sdg 				mp = hmp;
820605445d5Sdg 			}
821605445d5Sdg 		}
822605445d5Sdg 
823605445d5Sdg 		evhp = (struct ether_vlan_header *)mp->b_rptr;
824b53ab68fSCathy Zhou 		old_tci = ntohs(evhp->ether_tci);
825605445d5Sdg 	} else {
826605445d5Sdg 		/*
827e75f0919SSebastien Roy 		 * Untagged packet.  Two factors will cause us to insert a
828e75f0919SSebastien Roy 		 * VLAN header:
829e75f0919SSebastien Roy 		 * - This is a VLAN link (vid is specified)
830e75f0919SSebastien Roy 		 * - The link supports user priority tagging and the priority
831e75f0919SSebastien Roy 		 *   is non-zero.
832605445d5Sdg 		 */
833e75f0919SSebastien Roy 		if (vid == VLAN_ID_NONE && tagmode == LINK_TAGMODE_VLANONLY)
834e75f0919SSebastien Roy 			return (mp);
835e75f0919SSebastien Roy 
836605445d5Sdg 		hmp = allocb(sizeof (struct ether_vlan_header), BPRI_MED);
837605445d5Sdg 		if (hmp == NULL)
838605445d5Sdg 			return (NULL);
839605445d5Sdg 
840605445d5Sdg 		evhp = (struct ether_vlan_header *)hmp->b_rptr;
841605445d5Sdg 		ehp = (struct ether_header *)mp->b_rptr;
842605445d5Sdg 
843605445d5Sdg 		/*
844605445d5Sdg 		 * Copy the MAC addresses and typelen
845605445d5Sdg 		 */
846605445d5Sdg 		bcopy(ehp, evhp, (ETHERADDRL * 2));
847605445d5Sdg 		evhp->ether_type = ehp->ether_type;
848605445d5Sdg 		evhp->ether_tpid = htons(ETHERTYPE_VLAN);
849605445d5Sdg 
850605445d5Sdg 		hmp->b_wptr += sizeof (struct ether_vlan_header);
851605445d5Sdg 		mp->b_rptr += sizeof (struct ether_header);
852605445d5Sdg 
853605445d5Sdg 		/*
854605445d5Sdg 		 * Free the original message if it's now empty. Link the
855d62bc4baSyz 		 * rest of the messages to the header message.
856605445d5Sdg 		 */
857605445d5Sdg 		if (MBLKL(mp) == 0) {
858605445d5Sdg 			hmp->b_cont = mp->b_cont;
859605445d5Sdg 			freeb(mp);
860605445d5Sdg 		} else {
861605445d5Sdg 			hmp->b_cont = mp;
862605445d5Sdg 		}
863605445d5Sdg 		mp = hmp;
864605445d5Sdg 	}
865605445d5Sdg 
866605445d5Sdg 	if (pri == 0)
867605445d5Sdg 		pri = VLAN_PRI(old_tci);
868605445d5Sdg 	if (vid == VLAN_ID_NONE)
869605445d5Sdg 		vid = VLAN_ID(old_tci);
870605445d5Sdg 	evhp->ether_tci = htons(VLAN_TCI(pri, VLAN_CFI(old_tci), vid));
871605445d5Sdg 	return (mp);
872605445d5Sdg }
873605445d5Sdg 
874605445d5Sdg /*
875da14cebeSEric Cheng  * M_DATA put (IP fast-path mode)
8767c478bd9Sstevel@tonic-gate  */
877da14cebeSEric Cheng mac_tx_cookie_t
str_mdata_fastpath_put(dld_str_t * dsp,mblk_t * mp,uintptr_t f_hint,uint16_t flag)878da14cebeSEric Cheng str_mdata_fastpath_put(dld_str_t *dsp, mblk_t *mp, uintptr_t f_hint,
879da14cebeSEric Cheng     uint16_t flag)
880605445d5Sdg {
881605445d5Sdg 	boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
882605445d5Sdg 	mblk_t *newmp;
883605445d5Sdg 	uint_t pri;
884da14cebeSEric Cheng 	mac_tx_cookie_t cookie;
885605445d5Sdg 
886605445d5Sdg 	if (is_ethernet) {
887605445d5Sdg 		/*
888605445d5Sdg 		 * Update the priority bits to the assigned priority.
889605445d5Sdg 		 */
890605445d5Sdg 		pri = (VLAN_MBLKPRI(mp) == 0) ? dsp->ds_pri : VLAN_MBLKPRI(mp);
891605445d5Sdg 
892605445d5Sdg 		if (pri != 0) {
893605445d5Sdg 			newmp = i_dld_ether_header_update_tag(mp, pri,
894e75f0919SSebastien Roy 			    VLAN_ID_NONE, dsp->ds_dlp->dl_tagmode);
895605445d5Sdg 			if (newmp == NULL)
896605445d5Sdg 				goto discard;
897605445d5Sdg 			mp = newmp;
898605445d5Sdg 		}
899605445d5Sdg 	}
900605445d5Sdg 
9018648b7dbSToomas Soome 	if ((cookie = DLD_TX(dsp, mp, f_hint, flag)) != 0) {
902da14cebeSEric Cheng 		DLD_SETQFULL(dsp);
903da14cebeSEric Cheng 	}
904da14cebeSEric Cheng 	return (cookie);
905605445d5Sdg 
906605445d5Sdg discard:
907605445d5Sdg 	/* TODO: bump kstat? */
908605445d5Sdg 	freemsg(mp);
9098648b7dbSToomas Soome 	return (0);
910605445d5Sdg }
911605445d5Sdg 
912605445d5Sdg /*
913da14cebeSEric Cheng  * M_DATA put (DLIOCRAW mode)
914605445d5Sdg  */
915da14cebeSEric Cheng static void
str_mdata_raw_put(dld_str_t * dsp,mblk_t * mp)9167c478bd9Sstevel@tonic-gate str_mdata_raw_put(dld_str_t *dsp, mblk_t *mp)
9177c478bd9Sstevel@tonic-gate {
918605445d5Sdg 	boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
919605445d5Sdg 	mblk_t *bp, *newmp;
920605445d5Sdg 	size_t size;
921605445d5Sdg 	mac_header_info_t mhi;
922da14cebeSEric Cheng 	uint_t pri, vid, dvid;
923e7801d59Ssowmini 	uint_t max_sdu;
9247c478bd9Sstevel@tonic-gate 
925ba2e4443Sseb 	/*
926ba2e4443Sseb 	 * Certain MAC type plugins provide an illusion for raw DLPI
927ba2e4443Sseb 	 * consumers.  They pretend that the MAC layer is something that
9280ba2cbe9Sxc 	 * it's not for the benefit of observability tools.  For example,
9290ba2cbe9Sxc 	 * mac_wifi pretends that it's Ethernet for such consumers.
9300ba2cbe9Sxc 	 * Here, unless native mode is enabled, we call into the MAC layer so
9310ba2cbe9Sxc 	 * that this illusion can be maintained.  The plugin will optionally
9320ba2cbe9Sxc 	 * transform the MAC header here into something that can be passed
9330ba2cbe9Sxc 	 * down.  The header goes from raw mode to "cooked" mode.
934ba2e4443Sseb 	 */
9350ba2cbe9Sxc 	if (!dsp->ds_native) {
9360ba2cbe9Sxc 		if ((newmp = mac_header_cook(dsp->ds_mh, mp)) == NULL)
9370ba2cbe9Sxc 			goto discard;
9380ba2cbe9Sxc 		mp = newmp;
9390ba2cbe9Sxc 	}
9407c478bd9Sstevel@tonic-gate 
941ba2e4443Sseb 	size = MBLKL(mp);
9427c478bd9Sstevel@tonic-gate 
9437c478bd9Sstevel@tonic-gate 	/*
9447c478bd9Sstevel@tonic-gate 	 * Check the packet is not too big and that any remaining
9457c478bd9Sstevel@tonic-gate 	 * fragment list is composed entirely of M_DATA messages. (We
9467c478bd9Sstevel@tonic-gate 	 * know the first fragment was M_DATA otherwise we could not
9477c478bd9Sstevel@tonic-gate 	 * have got here).
9487c478bd9Sstevel@tonic-gate 	 */
949210db224Sericheng 	for (bp = mp->b_cont; bp != NULL; bp = bp->b_cont) {
9507c478bd9Sstevel@tonic-gate 		if (DB_TYPE(bp) != M_DATA)
9517c478bd9Sstevel@tonic-gate 			goto discard;
9527c478bd9Sstevel@tonic-gate 		size += MBLKL(bp);
9537c478bd9Sstevel@tonic-gate 	}
9547c478bd9Sstevel@tonic-gate 
95525ec3e3dSEric Cheng 	if (mac_vlan_header_info(dsp->ds_mh, mp, &mhi) != 0)
956ba2e4443Sseb 		goto discard;
957ba2e4443Sseb 
958e7801d59Ssowmini 	mac_sdu_get(dsp->ds_mh, NULL, &max_sdu);
9598347601bSyl 	/*
9608347601bSyl 	 * If LSO is enabled, check the size against lso_max. Otherwise,
961e7801d59Ssowmini 	 * compare the packet size with max_sdu.
9628347601bSyl 	 */
963e7801d59Ssowmini 	max_sdu = dsp->ds_lso ? dsp->ds_lso_max : max_sdu;
964e7801d59Ssowmini 	if (size > max_sdu + mhi.mhi_hdrsize)
9657c478bd9Sstevel@tonic-gate 		goto discard;
9667c478bd9Sstevel@tonic-gate 
967605445d5Sdg 	if (is_ethernet) {
968da14cebeSEric Cheng 		dvid = mac_client_vid(dsp->ds_mch);
969da14cebeSEric Cheng 
970605445d5Sdg 		/*
971605445d5Sdg 		 * Discard the packet if this is a VLAN stream but the VID in
972605445d5Sdg 		 * the packet is not correct.
973605445d5Sdg 		 */
974605445d5Sdg 		vid = VLAN_ID(mhi.mhi_tci);
975da14cebeSEric Cheng 		if ((dvid != VLAN_ID_NONE) && (vid != VLAN_ID_NONE))
976605445d5Sdg 			goto discard;
977ba2e4443Sseb 
978605445d5Sdg 		/*
979605445d5Sdg 		 * Discard the packet if this packet is a tagged packet
980605445d5Sdg 		 * but both pri and VID are 0.
981605445d5Sdg 		 */
982605445d5Sdg 		pri = VLAN_PRI(mhi.mhi_tci);
9834eaa4710SRishi Srivatsavai 		if (mhi.mhi_istagged && !mhi.mhi_ispvid && pri == 0 &&
9844eaa4710SRishi Srivatsavai 		    vid == VLAN_ID_NONE)
985ba2e4443Sseb 			goto discard;
986605445d5Sdg 
987ba2e4443Sseb 		/*
988605445d5Sdg 		 * Update the priority bits to the per-stream priority if
989605445d5Sdg 		 * priority is not set in the packet. Update the VID for
990605445d5Sdg 		 * packets on a VLAN stream.
991ba2e4443Sseb 		 */
992605445d5Sdg 		pri = (pri == 0) ? dsp->ds_pri : 0;
993da14cebeSEric Cheng 		if ((pri != 0) || (dvid != VLAN_ID_NONE)) {
994e75f0919SSebastien Roy 			if ((newmp = i_dld_ether_header_update_tag(mp, pri,
995e75f0919SSebastien Roy 			    dvid, dsp->ds_dlp->dl_tagmode)) == NULL) {
996605445d5Sdg 				goto discard;
997605445d5Sdg 			}
998605445d5Sdg 			mp = newmp;
999605445d5Sdg 		}
1000ba2e4443Sseb 	}
1001ba2e4443Sseb 
10028648b7dbSToomas Soome 	if (DLD_TX(dsp, mp, 0, 0) != 0) {
1003da14cebeSEric Cheng 		/* Turn on flow-control for dld */
1004da14cebeSEric Cheng 		DLD_SETQFULL(dsp);
1005da14cebeSEric Cheng 	}
10067c478bd9Sstevel@tonic-gate 	return;
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate discard:
1009605445d5Sdg 	/* TODO: bump kstat? */
10107c478bd9Sstevel@tonic-gate 	freemsg(mp);
10117c478bd9Sstevel@tonic-gate }
10127c478bd9Sstevel@tonic-gate 
10137c478bd9Sstevel@tonic-gate /*
10147c478bd9Sstevel@tonic-gate  * Process DL_ATTACH_REQ (style 2) or open(2) (style 1).
10157c478bd9Sstevel@tonic-gate  */
10167c478bd9Sstevel@tonic-gate int
dld_str_attach(dld_str_t * dsp,t_uscalar_t ppa)1017210db224Sericheng dld_str_attach(dld_str_t *dsp, t_uscalar_t ppa)
10187c478bd9Sstevel@tonic-gate {
1019da14cebeSEric Cheng 	dev_t			dev;
1020da14cebeSEric Cheng 	int			err;
1021da14cebeSEric Cheng 	const char		*drvname;
10225d460eafSCathy Zhou 	mac_perim_handle_t	mph = NULL;
1023da14cebeSEric Cheng 	boolean_t		qassociated = B_FALSE;
1024da14cebeSEric Cheng 	dls_link_t		*dlp = NULL;
1025da14cebeSEric Cheng 	dls_dl_handle_t		ddp = NULL;
10267c478bd9Sstevel@tonic-gate 
1027210db224Sericheng 	if ((drvname = ddi_major_to_name(dsp->ds_major)) == NULL)
1028210db224Sericheng 		return (EINVAL);
1029210db224Sericheng 
1030da14cebeSEric Cheng 	if (dsp->ds_style == DL_STYLE2 && ppa > DLS_MAX_PPA)
1031da14cebeSEric Cheng 		return (ENOTSUP);
1032da14cebeSEric Cheng 
1033d62bc4baSyz 	/*
1034d62bc4baSyz 	 * /dev node access. This will still be supported for backward
1035d62bc4baSyz 	 * compatibility reason.
1036d62bc4baSyz 	 */
1037d62bc4baSyz 	if ((dsp->ds_style == DL_STYLE2) && (strcmp(drvname, "aggr") != 0) &&
1038d62bc4baSyz 	    (strcmp(drvname, "vnic") != 0)) {
1039d62bc4baSyz 		if (qassociate(dsp->ds_wq, DLS_PPA2INST(ppa)) != 0)
1040d62bc4baSyz 			return (EINVAL);
1041d62bc4baSyz 		qassociated = B_TRUE;
1042d62bc4baSyz 	}
1043210db224Sericheng 
1044da14cebeSEric Cheng 	dev = makedevice(dsp->ds_major, (minor_t)ppa + 1);
1045da14cebeSEric Cheng 	if ((err = dls_devnet_hold_by_dev(dev, &ddp)) != 0)
1046da14cebeSEric Cheng 		goto failed;
10477c478bd9Sstevel@tonic-gate 
1048da14cebeSEric Cheng 	if ((err = mac_perim_enter_by_macname(dls_devnet_mac(ddp), &mph)) != 0)
1049da14cebeSEric Cheng 		goto failed;
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 	/*
1052da14cebeSEric Cheng 	 * Open a channel.
10537c478bd9Sstevel@tonic-gate 	 */
1054da14cebeSEric Cheng 	if ((err = dls_link_hold(dls_devnet_mac(ddp), &dlp)) != 0)
1055da14cebeSEric Cheng 		goto failed;
1056da14cebeSEric Cheng 
1057da14cebeSEric Cheng 	if ((err = dls_open(dlp, ddp, dsp)) != 0)
1058da14cebeSEric Cheng 		goto failed;
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 	/*
10617c478bd9Sstevel@tonic-gate 	 * Set the default packet priority.
10627c478bd9Sstevel@tonic-gate 	 */
10637c478bd9Sstevel@tonic-gate 	dsp->ds_pri = 0;
10647c478bd9Sstevel@tonic-gate 
10657c478bd9Sstevel@tonic-gate 	/*
10667c478bd9Sstevel@tonic-gate 	 * Add a notify function so that the we get updates from the MAC.
10677c478bd9Sstevel@tonic-gate 	 */
1068da14cebeSEric Cheng 	dsp->ds_mnh = mac_notify_add(dsp->ds_mh, str_notify, dsp);
1069210db224Sericheng 	dsp->ds_dlstate = DL_UNBOUND;
1070da14cebeSEric Cheng 	mac_perim_exit(mph);
10717c478bd9Sstevel@tonic-gate 	return (0);
1072da14cebeSEric Cheng 
1073da14cebeSEric Cheng failed:
1074da14cebeSEric Cheng 	if (dlp != NULL)
1075da14cebeSEric Cheng 		dls_link_rele(dlp);
10765d460eafSCathy Zhou 	if (mph != NULL)
1077da14cebeSEric Cheng 		mac_perim_exit(mph);
1078da14cebeSEric Cheng 	if (ddp != NULL)
1079da14cebeSEric Cheng 		dls_devnet_rele(ddp);
1080da14cebeSEric Cheng 	if (qassociated)
1081da14cebeSEric Cheng 		(void) qassociate(dsp->ds_wq, -1);
1082da14cebeSEric Cheng 
1083da14cebeSEric Cheng 	return (err);
10847c478bd9Sstevel@tonic-gate }
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate /*
10877c478bd9Sstevel@tonic-gate  * Process DL_DETACH_REQ (style 2) or close(2) (style 1). Can also be called
10887c478bd9Sstevel@tonic-gate  * from close(2) for style 2.
10897c478bd9Sstevel@tonic-gate  */
10907c478bd9Sstevel@tonic-gate void
dld_str_detach(dld_str_t * dsp)10917c478bd9Sstevel@tonic-gate dld_str_detach(dld_str_t *dsp)
10927c478bd9Sstevel@tonic-gate {
1093da14cebeSEric Cheng 	mac_perim_handle_t	mph;
1094da14cebeSEric Cheng 	int			err;
1095da14cebeSEric Cheng 
1096da14cebeSEric Cheng 	ASSERT(dsp->ds_datathr_cnt == 0);
1097da14cebeSEric Cheng 
1098da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
10997c478bd9Sstevel@tonic-gate 	/*
11007c478bd9Sstevel@tonic-gate 	 * Remove the notify function.
1101da14cebeSEric Cheng 	 *
1102da14cebeSEric Cheng 	 * Note that we cannot wait for the notification callback to be removed
1103da14cebeSEric Cheng 	 * since it could cause the deadlock with str_notify() since they both
1104da14cebeSEric Cheng 	 * need the mac perimeter. Continue if we cannot remove the
1105da14cebeSEric Cheng 	 * notification callback right now and wait after we leave the
1106da14cebeSEric Cheng 	 * perimeter.
11077c478bd9Sstevel@tonic-gate 	 */
1108da14cebeSEric Cheng 	err = mac_notify_remove(dsp->ds_mnh, B_FALSE);
1109da14cebeSEric Cheng 	dsp->ds_mnh = NULL;
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	/*
1112da14cebeSEric Cheng 	 * Disable the capabilities
11137c478bd9Sstevel@tonic-gate 	 */
11148fb46f24Syz 	dld_capabilities_disable(dsp);
11157c478bd9Sstevel@tonic-gate 
1116da14cebeSEric Cheng 	/*
1117da14cebeSEric Cheng 	 * Clear LSO flags.
1118da14cebeSEric Cheng 	 */
1119da14cebeSEric Cheng 	dsp->ds_lso = B_FALSE;
1120da14cebeSEric Cheng 	dsp->ds_lso_max = 0;
1121da14cebeSEric Cheng 
1122da14cebeSEric Cheng 	dls_close(dsp);
1123da14cebeSEric Cheng 	mac_perim_exit(mph);
1124d62bc4baSyz 
1125d62bc4baSyz 	/*
1126da14cebeSEric Cheng 	 * Now we leave the mac perimeter. If mac_notify_remove() failed
1127da14cebeSEric Cheng 	 * because the notification callback was in progress, wait for
1128da14cebeSEric Cheng 	 * it to finish before we proceed.
1129d62bc4baSyz 	 */
1130da14cebeSEric Cheng 	if (err != 0)
1131da14cebeSEric Cheng 		mac_notify_remove_wait(dsp->ds_mh);
1132d62bc4baSyz 
11338347601bSyl 	/*
1134da14cebeSEric Cheng 	 * An unreferenced tagged (non-persistent) vlan gets destroyed
1135da14cebeSEric Cheng 	 * automatically in the call to dls_devnet_rele.
11368347601bSyl 	 */
1137da14cebeSEric Cheng 	dls_devnet_rele(dsp->ds_ddh);
11388347601bSyl 
1139da14cebeSEric Cheng 	dsp->ds_sap = 0;
11407c478bd9Sstevel@tonic-gate 	dsp->ds_mh = NULL;
1141da14cebeSEric Cheng 	dsp->ds_mch = NULL;
1142da14cebeSEric Cheng 	dsp->ds_mip = NULL;
11437c478bd9Sstevel@tonic-gate 
1144d62bc4baSyz 	if (dsp->ds_style == DL_STYLE2)
1145d62bc4baSyz 		(void) qassociate(dsp->ds_wq, -1);
11466a0b2badSericheng 
11476a0b2badSericheng 	/*
11486a0b2badSericheng 	 * Re-initialize the DLPI state machine.
11496a0b2badSericheng 	 */
11506a0b2badSericheng 	dsp->ds_dlstate = DL_UNATTACHED;
11517c478bd9Sstevel@tonic-gate }
11527c478bd9Sstevel@tonic-gate 
1153605445d5Sdg /*
1154605445d5Sdg  * This function is only called for VLAN streams. In raw mode, we strip VLAN
1155605445d5Sdg  * tags before sending packets up to the DLS clients, with the exception of
1156605445d5Sdg  * special priority tagged packets, in that case, we set the VID to 0.
1157605445d5Sdg  * mp must be a VLAN tagged packet.
1158605445d5Sdg  */
1159605445d5Sdg static mblk_t *
i_dld_ether_header_strip_tag(mblk_t * mp,boolean_t keep_pri)116072782355SNicolas Droux i_dld_ether_header_strip_tag(mblk_t *mp, boolean_t keep_pri)
1161605445d5Sdg {
1162605445d5Sdg 	mblk_t *newmp;
1163605445d5Sdg 	struct ether_vlan_header *evhp;
1164605445d5Sdg 	uint16_t tci, new_tci;
1165605445d5Sdg 
1166605445d5Sdg 	ASSERT(MBLKL(mp) >= sizeof (struct ether_vlan_header));
1167605445d5Sdg 	if (DB_REF(mp) > 1) {
1168605445d5Sdg 		newmp = copymsg(mp);
1169605445d5Sdg 		if (newmp == NULL)
1170605445d5Sdg 			return (NULL);
1171605445d5Sdg 		freemsg(mp);
1172605445d5Sdg 		mp = newmp;
1173605445d5Sdg 	}
1174605445d5Sdg 	evhp = (struct ether_vlan_header *)mp->b_rptr;
1175605445d5Sdg 
1176605445d5Sdg 	tci = ntohs(evhp->ether_tci);
117772782355SNicolas Droux 	if (VLAN_PRI(tci) == 0 || !keep_pri) {
1178605445d5Sdg 		/*
1179605445d5Sdg 		 * Priority is 0, strip the tag.
1180605445d5Sdg 		 */
1181605445d5Sdg 		ovbcopy(mp->b_rptr, mp->b_rptr + VLAN_TAGSZ, 2 * ETHERADDRL);
1182605445d5Sdg 		mp->b_rptr += VLAN_TAGSZ;
1183605445d5Sdg 	} else {
1184605445d5Sdg 		/*
1185605445d5Sdg 		 * Priority is not 0, update the VID to 0.
1186605445d5Sdg 		 */
1187605445d5Sdg 		new_tci = VLAN_TCI(VLAN_PRI(tci), VLAN_CFI(tci), VLAN_ID_NONE);
1188605445d5Sdg 		evhp->ether_tci = htons(new_tci);
1189605445d5Sdg 	}
1190605445d5Sdg 	return (mp);
1191605445d5Sdg }
1192605445d5Sdg 
11937c478bd9Sstevel@tonic-gate /*
11947c478bd9Sstevel@tonic-gate  * Raw mode receive function.
11957c478bd9Sstevel@tonic-gate  */
11967c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11977c478bd9Sstevel@tonic-gate void
dld_str_rx_raw(void * arg,mac_resource_handle_t mrh,mblk_t * mp,mac_header_info_t * mhip)11987c478bd9Sstevel@tonic-gate dld_str_rx_raw(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
1199605445d5Sdg     mac_header_info_t *mhip)
12007c478bd9Sstevel@tonic-gate {
1201605445d5Sdg 	dld_str_t *dsp = (dld_str_t *)arg;
1202605445d5Sdg 	boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
1203605445d5Sdg 	mblk_t *next, *newmp;
12047c478bd9Sstevel@tonic-gate 
12057c478bd9Sstevel@tonic-gate 	ASSERT(mp != NULL);
12067c478bd9Sstevel@tonic-gate 	do {
12077c478bd9Sstevel@tonic-gate 		/*
12087c478bd9Sstevel@tonic-gate 		 * Get the pointer to the next packet in the chain and then
12097c478bd9Sstevel@tonic-gate 		 * clear b_next before the packet gets passed on.
12107c478bd9Sstevel@tonic-gate 		 */
12117c478bd9Sstevel@tonic-gate 		next = mp->b_next;
12127c478bd9Sstevel@tonic-gate 		mp->b_next = NULL;
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 		/*
12157c478bd9Sstevel@tonic-gate 		 * Wind back b_rptr to point at the MAC header.
12167c478bd9Sstevel@tonic-gate 		 */
1217605445d5Sdg 		ASSERT(mp->b_rptr >= DB_BASE(mp) + mhip->mhi_hdrsize);
1218605445d5Sdg 		mp->b_rptr -= mhip->mhi_hdrsize;
12197c478bd9Sstevel@tonic-gate 
1220ba2e4443Sseb 		/*
1221ba2e4443Sseb 		 * Certain MAC type plugins provide an illusion for raw
1222ba2e4443Sseb 		 * DLPI consumers.  They pretend that the MAC layer is
1223ba2e4443Sseb 		 * something that it's not for the benefit of observability
12240ba2cbe9Sxc 		 * tools.  For example, mac_wifi pretends that it's Ethernet
12250ba2cbe9Sxc 		 * for such consumers.	Here, unless native mode is enabled,
12260ba2cbe9Sxc 		 * we call into the MAC layer so that this illusion can be
12270ba2cbe9Sxc 		 * maintained.	The plugin will optionally transform the MAC
12280ba2cbe9Sxc 		 * header here into something that can be passed up to raw
12290ba2cbe9Sxc 		 * consumers.  The header goes from "cooked" mode to raw mode.
1230ba2e4443Sseb 		 */
12310ba2cbe9Sxc 		if (!dsp->ds_native) {
12320ba2cbe9Sxc 			newmp = mac_header_uncook(dsp->ds_mh, mp);
12330ba2cbe9Sxc 			if (newmp == NULL) {
12340ba2cbe9Sxc 				freemsg(mp);
12350ba2cbe9Sxc 				goto next;
12360ba2cbe9Sxc 			}
12370ba2cbe9Sxc 			mp = newmp;
1238ba2e4443Sseb 		}
1239ba2e4443Sseb 
1240605445d5Sdg 		/*
1241605445d5Sdg 		 * Strip the VLAN tag for VLAN streams.
1242605445d5Sdg 		 */
1243da14cebeSEric Cheng 		if (is_ethernet &&
1244da14cebeSEric Cheng 		    mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE) {
124572782355SNicolas Droux 			/*
124672782355SNicolas Droux 			 * The priority should be kept only for VLAN
124772782355SNicolas Droux 			 * data-links.
124872782355SNicolas Droux 			 */
124972782355SNicolas Droux 			newmp = i_dld_ether_header_strip_tag(mp,
125072782355SNicolas Droux 			    mac_client_is_vlan_vnic(dsp->ds_mch));
1251605445d5Sdg 			if (newmp == NULL) {
1252605445d5Sdg 				freemsg(mp);
1253605445d5Sdg 				goto next;
1254ba2e4443Sseb 			}
1255605445d5Sdg 			mp = newmp;
1256ba2e4443Sseb 		}
1257605445d5Sdg 
12587c478bd9Sstevel@tonic-gate 		/*
12597c478bd9Sstevel@tonic-gate 		 * Pass the packet on.
12607c478bd9Sstevel@tonic-gate 		 */
1261c0192a57Sericheng 		if (canputnext(dsp->ds_rq))
1262c0192a57Sericheng 			putnext(dsp->ds_rq, mp);
1263c0192a57Sericheng 		else
1264c0192a57Sericheng 			freemsg(mp);
12657c478bd9Sstevel@tonic-gate 
1266605445d5Sdg next:
12677c478bd9Sstevel@tonic-gate 		/*
12687c478bd9Sstevel@tonic-gate 		 * Move on to the next packet in the chain.
12697c478bd9Sstevel@tonic-gate 		 */
12707c478bd9Sstevel@tonic-gate 		mp = next;
12717c478bd9Sstevel@tonic-gate 	} while (mp != NULL);
12727c478bd9Sstevel@tonic-gate }
12737c478bd9Sstevel@tonic-gate 
12747c478bd9Sstevel@tonic-gate /*
12757c478bd9Sstevel@tonic-gate  * Fast-path receive function.
12767c478bd9Sstevel@tonic-gate  */
12777c478bd9Sstevel@tonic-gate /*ARGSUSED*/
12787c478bd9Sstevel@tonic-gate void
dld_str_rx_fastpath(void * arg,mac_resource_handle_t mrh,mblk_t * mp,mac_header_info_t * mhip)12797c478bd9Sstevel@tonic-gate dld_str_rx_fastpath(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
1280605445d5Sdg     mac_header_info_t *mhip)
12817c478bd9Sstevel@tonic-gate {
1282605445d5Sdg 	dld_str_t *dsp = (dld_str_t *)arg;
1283605445d5Sdg 	mblk_t *next;
1284605445d5Sdg 	size_t offset = 0;
1285605445d5Sdg 
1286605445d5Sdg 	/*
1287605445d5Sdg 	 * MAC header stripping rules:
1288605445d5Sdg 	 *    - Tagged packets:
1289605445d5Sdg 	 *	a. VLAN streams. Strip the whole VLAN header including the tag.
1290605445d5Sdg 	 *	b. Physical streams
1291605445d5Sdg 	 *	- VLAN packets (non-zero VID). The stream must be either a
1292605445d5Sdg 	 *	  DL_PROMISC_SAP listener or a ETHERTYPE_VLAN listener.
1293605445d5Sdg 	 *	  Strip the Ethernet header but keep the VLAN header.
1294605445d5Sdg 	 *	- Special tagged packets (zero VID)
1295605445d5Sdg 	 *	  * The stream is either a DL_PROMISC_SAP listener or a
1296605445d5Sdg 	 *	    ETHERTYPE_VLAN listener, strip the Ethernet header but
1297605445d5Sdg 	 *	    keep the VLAN header.
1298605445d5Sdg 	 *	  * Otherwise, strip the whole VLAN header.
1299605445d5Sdg 	 *    - Untagged packets. Strip the whole MAC header.
1300605445d5Sdg 	 */
1301da14cebeSEric Cheng 	if (mhip->mhi_istagged &&
1302da14cebeSEric Cheng 	    (mac_client_vid(dsp->ds_mch) == VLAN_ID_NONE) &&
1303605445d5Sdg 	    ((dsp->ds_sap == ETHERTYPE_VLAN) ||
1304605445d5Sdg 	    (dsp->ds_promisc & DLS_PROMISC_SAP))) {
1305605445d5Sdg 		offset = VLAN_TAGSZ;
1306605445d5Sdg 	}
13077c478bd9Sstevel@tonic-gate 
13087c478bd9Sstevel@tonic-gate 	ASSERT(mp != NULL);
13097c478bd9Sstevel@tonic-gate 	do {
13107c478bd9Sstevel@tonic-gate 		/*
13117c478bd9Sstevel@tonic-gate 		 * Get the pointer to the next packet in the chain and then
13127c478bd9Sstevel@tonic-gate 		 * clear b_next before the packet gets passed on.
13137c478bd9Sstevel@tonic-gate 		 */
13147c478bd9Sstevel@tonic-gate 		next = mp->b_next;
13157c478bd9Sstevel@tonic-gate 		mp->b_next = NULL;
13167c478bd9Sstevel@tonic-gate 
1317605445d5Sdg 		/*
1318605445d5Sdg 		 * Wind back b_rptr to point at the VLAN header.
1319605445d5Sdg 		 */
1320605445d5Sdg 		ASSERT(mp->b_rptr >= DB_BASE(mp) + offset);
1321605445d5Sdg 		mp->b_rptr -= offset;
1322605445d5Sdg 
13237c478bd9Sstevel@tonic-gate 		/*
13247c478bd9Sstevel@tonic-gate 		 * Pass the packet on.
13257c478bd9Sstevel@tonic-gate 		 */
1326c0192a57Sericheng 		if (canputnext(dsp->ds_rq))
1327c0192a57Sericheng 			putnext(dsp->ds_rq, mp);
1328c0192a57Sericheng 		else
1329c0192a57Sericheng 			freemsg(mp);
13307c478bd9Sstevel@tonic-gate 		/*
13317c478bd9Sstevel@tonic-gate 		 * Move on to the next packet in the chain.
13327c478bd9Sstevel@tonic-gate 		 */
13337c478bd9Sstevel@tonic-gate 		mp = next;
13347c478bd9Sstevel@tonic-gate 	} while (mp != NULL);
13357c478bd9Sstevel@tonic-gate }
13367c478bd9Sstevel@tonic-gate 
13377c478bd9Sstevel@tonic-gate /*
13387c478bd9Sstevel@tonic-gate  * Default receive function (send DL_UNITDATA_IND messages).
13397c478bd9Sstevel@tonic-gate  */
13407c478bd9Sstevel@tonic-gate /*ARGSUSED*/
13417c478bd9Sstevel@tonic-gate void
dld_str_rx_unitdata(void * arg,mac_resource_handle_t mrh,mblk_t * mp,mac_header_info_t * mhip)13427c478bd9Sstevel@tonic-gate dld_str_rx_unitdata(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
1343605445d5Sdg     mac_header_info_t *mhip)
13447c478bd9Sstevel@tonic-gate {
13457c478bd9Sstevel@tonic-gate 	dld_str_t		*dsp = (dld_str_t *)arg;
13467c478bd9Sstevel@tonic-gate 	mblk_t			*ud_mp;
13477c478bd9Sstevel@tonic-gate 	mblk_t			*next;
1348605445d5Sdg 	size_t			offset = 0;
1349605445d5Sdg 	boolean_t		strip_vlan = B_TRUE;
1350605445d5Sdg 
1351605445d5Sdg 	/*
1352605445d5Sdg 	 * See MAC header stripping rules in the dld_str_rx_fastpath() function.
1353605445d5Sdg 	 */
1354da14cebeSEric Cheng 	if (mhip->mhi_istagged &&
1355da14cebeSEric Cheng 	    (mac_client_vid(dsp->ds_mch) == VLAN_ID_NONE) &&
1356605445d5Sdg 	    ((dsp->ds_sap == ETHERTYPE_VLAN) ||
1357605445d5Sdg 	    (dsp->ds_promisc & DLS_PROMISC_SAP))) {
1358605445d5Sdg 		offset = VLAN_TAGSZ;
1359605445d5Sdg 		strip_vlan = B_FALSE;
1360605445d5Sdg 	}
13617c478bd9Sstevel@tonic-gate 
13627c478bd9Sstevel@tonic-gate 	ASSERT(mp != NULL);
13637c478bd9Sstevel@tonic-gate 	do {
13647c478bd9Sstevel@tonic-gate 		/*
13657c478bd9Sstevel@tonic-gate 		 * Get the pointer to the next packet in the chain and then
13667c478bd9Sstevel@tonic-gate 		 * clear b_next before the packet gets passed on.
13677c478bd9Sstevel@tonic-gate 		 */
13687c478bd9Sstevel@tonic-gate 		next = mp->b_next;
13697c478bd9Sstevel@tonic-gate 		mp->b_next = NULL;
13707c478bd9Sstevel@tonic-gate 
13717c478bd9Sstevel@tonic-gate 		/*
13727c478bd9Sstevel@tonic-gate 		 * Wind back b_rptr to point at the MAC header.
13737c478bd9Sstevel@tonic-gate 		 */
1374605445d5Sdg 		ASSERT(mp->b_rptr >= DB_BASE(mp) + mhip->mhi_hdrsize);
1375605445d5Sdg 		mp->b_rptr -= mhip->mhi_hdrsize;
13767c478bd9Sstevel@tonic-gate 
13777c478bd9Sstevel@tonic-gate 		/*
13787c478bd9Sstevel@tonic-gate 		 * Create the DL_UNITDATA_IND M_PROTO.
13797c478bd9Sstevel@tonic-gate 		 */
1380605445d5Sdg 		if ((ud_mp = str_unitdata_ind(dsp, mp, strip_vlan)) == NULL) {
13817c478bd9Sstevel@tonic-gate 			freemsgchain(mp);
13827c478bd9Sstevel@tonic-gate 			return;
13837c478bd9Sstevel@tonic-gate 		}
13847c478bd9Sstevel@tonic-gate 
13857c478bd9Sstevel@tonic-gate 		/*
1386605445d5Sdg 		 * Advance b_rptr to point at the payload (or the VLAN header).
13877c478bd9Sstevel@tonic-gate 		 */
1388605445d5Sdg 		mp->b_rptr += (mhip->mhi_hdrsize - offset);
13897c478bd9Sstevel@tonic-gate 
13907c478bd9Sstevel@tonic-gate 		/*
13917c478bd9Sstevel@tonic-gate 		 * Prepend the DL_UNITDATA_IND.
13927c478bd9Sstevel@tonic-gate 		 */
13937c478bd9Sstevel@tonic-gate 		ud_mp->b_cont = mp;
13947c478bd9Sstevel@tonic-gate 
13957c478bd9Sstevel@tonic-gate 		/*
13967c478bd9Sstevel@tonic-gate 		 * Send the message.
13977c478bd9Sstevel@tonic-gate 		 */
1398c0192a57Sericheng 		if (canputnext(dsp->ds_rq))
1399c0192a57Sericheng 			putnext(dsp->ds_rq, ud_mp);
1400c0192a57Sericheng 		else
1401c0192a57Sericheng 			freemsg(ud_mp);
14027c478bd9Sstevel@tonic-gate 
14037c478bd9Sstevel@tonic-gate 		/*
14047c478bd9Sstevel@tonic-gate 		 * Move on to the next packet in the chain.
14057c478bd9Sstevel@tonic-gate 		 */
14067c478bd9Sstevel@tonic-gate 		mp = next;
14077c478bd9Sstevel@tonic-gate 	} while (mp != NULL);
14087c478bd9Sstevel@tonic-gate }
14097c478bd9Sstevel@tonic-gate 
1410e7801d59Ssowmini /*
1411e7801d59Ssowmini  * DL_NOTIFY_IND: DL_NOTE_SDU_SIZE
1412e7801d59Ssowmini  */
1413e7801d59Ssowmini static void
str_notify_sdu_size(dld_str_t * dsp,uint_t max_sdu,uint_t multicast_sdu)14141eee170aSErik Nordmark str_notify_sdu_size(dld_str_t *dsp, uint_t max_sdu, uint_t multicast_sdu)
1415e7801d59Ssowmini {
1416e7801d59Ssowmini 	mblk_t		*mp;
1417e7801d59Ssowmini 	dl_notify_ind_t *dlip;
1418e7801d59Ssowmini 
14191eee170aSErik Nordmark 	if (!(dsp->ds_notifications & (DL_NOTE_SDU_SIZE|DL_NOTE_SDU_SIZE2)))
1420e7801d59Ssowmini 		return;
1421e7801d59Ssowmini 
1422e7801d59Ssowmini 	if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
1423e7801d59Ssowmini 	    M_PROTO, 0)) == NULL)
1424e7801d59Ssowmini 		return;
1425e7801d59Ssowmini 
1426e7801d59Ssowmini 	bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
1427e7801d59Ssowmini 	dlip = (dl_notify_ind_t *)mp->b_rptr;
1428e7801d59Ssowmini 	dlip->dl_primitive = DL_NOTIFY_IND;
14291eee170aSErik Nordmark 	if (dsp->ds_notifications & DL_NOTE_SDU_SIZE2) {
14301eee170aSErik Nordmark 		dlip->dl_notification = DL_NOTE_SDU_SIZE2;
14311eee170aSErik Nordmark 		dlip->dl_data1 = max_sdu;
14321eee170aSErik Nordmark 		dlip->dl_data2 = multicast_sdu;
14331eee170aSErik Nordmark 	} else {
14341eee170aSErik Nordmark 		dlip->dl_notification = DL_NOTE_SDU_SIZE;
14351eee170aSErik Nordmark 		dlip->dl_data = max_sdu;
14361eee170aSErik Nordmark 	}
1437e7801d59Ssowmini 
1438e7801d59Ssowmini 	qreply(dsp->ds_wq, mp);
1439e7801d59Ssowmini }
1440e7801d59Ssowmini 
14417c478bd9Sstevel@tonic-gate /*
14427c478bd9Sstevel@tonic-gate  * Generate DL_NOTIFY_IND messages to notify the DLPI consumer of the
14437c478bd9Sstevel@tonic-gate  * current state of the interface.
14447c478bd9Sstevel@tonic-gate  */
14457c478bd9Sstevel@tonic-gate void
dld_str_notify_ind(dld_str_t * dsp)14467c478bd9Sstevel@tonic-gate dld_str_notify_ind(dld_str_t *dsp)
14477c478bd9Sstevel@tonic-gate {
14487c478bd9Sstevel@tonic-gate 	mac_notify_type_t	type;
14497c478bd9Sstevel@tonic-gate 
14507c478bd9Sstevel@tonic-gate 	for (type = 0; type < MAC_NNOTE; type++)
14517c478bd9Sstevel@tonic-gate 		str_notify(dsp, type);
14527c478bd9Sstevel@tonic-gate }
14537c478bd9Sstevel@tonic-gate 
14547c478bd9Sstevel@tonic-gate typedef struct dl_unitdata_ind_wrapper {
14557c478bd9Sstevel@tonic-gate 	dl_unitdata_ind_t	dl_unitdata;
1456ba2e4443Sseb 	uint8_t			dl_dest_addr[MAXMACADDRLEN + sizeof (uint16_t)];
1457ba2e4443Sseb 	uint8_t			dl_src_addr[MAXMACADDRLEN + sizeof (uint16_t)];
14587c478bd9Sstevel@tonic-gate } dl_unitdata_ind_wrapper_t;
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate /*
14617c478bd9Sstevel@tonic-gate  * Create a DL_UNITDATA_IND M_PROTO message.
14627c478bd9Sstevel@tonic-gate  */
14637c478bd9Sstevel@tonic-gate static mblk_t *
str_unitdata_ind(dld_str_t * dsp,mblk_t * mp,boolean_t strip_vlan)1464605445d5Sdg str_unitdata_ind(dld_str_t *dsp, mblk_t *mp, boolean_t strip_vlan)
14657c478bd9Sstevel@tonic-gate {
14667c478bd9Sstevel@tonic-gate 	mblk_t				*nmp;
14677c478bd9Sstevel@tonic-gate 	dl_unitdata_ind_wrapper_t	*dlwp;
14687c478bd9Sstevel@tonic-gate 	dl_unitdata_ind_t		*dlp;
1469ba2e4443Sseb 	mac_header_info_t		mhi;
14707c478bd9Sstevel@tonic-gate 	uint_t				addr_length;
14717c478bd9Sstevel@tonic-gate 	uint8_t				*daddr;
14727c478bd9Sstevel@tonic-gate 	uint8_t				*saddr;
14737c478bd9Sstevel@tonic-gate 
14747c478bd9Sstevel@tonic-gate 	/*
14757c478bd9Sstevel@tonic-gate 	 * Get the packet header information.
14767c478bd9Sstevel@tonic-gate 	 */
147725ec3e3dSEric Cheng 	if (mac_vlan_header_info(dsp->ds_mh, mp, &mhi) != 0)
1478ba2e4443Sseb 		return (NULL);
14797c478bd9Sstevel@tonic-gate 
14807c478bd9Sstevel@tonic-gate 	/*
14817c478bd9Sstevel@tonic-gate 	 * Allocate a message large enough to contain the wrapper structure
14827c478bd9Sstevel@tonic-gate 	 * defined above.
14837c478bd9Sstevel@tonic-gate 	 */
14847c478bd9Sstevel@tonic-gate 	if ((nmp = mexchange(dsp->ds_wq, NULL,
14857c478bd9Sstevel@tonic-gate 	    sizeof (dl_unitdata_ind_wrapper_t), M_PROTO,
14867c478bd9Sstevel@tonic-gate 	    DL_UNITDATA_IND)) == NULL)
14877c478bd9Sstevel@tonic-gate 		return (NULL);
14887c478bd9Sstevel@tonic-gate 
14897c478bd9Sstevel@tonic-gate 	dlwp = (dl_unitdata_ind_wrapper_t *)nmp->b_rptr;
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate 	dlp = &(dlwp->dl_unitdata);
14927c478bd9Sstevel@tonic-gate 	ASSERT(dlp == (dl_unitdata_ind_t *)nmp->b_rptr);
14937c478bd9Sstevel@tonic-gate 	ASSERT(dlp->dl_primitive == DL_UNITDATA_IND);
14947c478bd9Sstevel@tonic-gate 
14957c478bd9Sstevel@tonic-gate 	/*
14967c478bd9Sstevel@tonic-gate 	 * Copy in the destination address.
14977c478bd9Sstevel@tonic-gate 	 */
14987c478bd9Sstevel@tonic-gate 	addr_length = dsp->ds_mip->mi_addr_length;
14997c478bd9Sstevel@tonic-gate 	daddr = dlwp->dl_dest_addr;
15007c478bd9Sstevel@tonic-gate 	dlp->dl_dest_addr_offset = (uintptr_t)daddr - (uintptr_t)dlp;
1501ba2e4443Sseb 	bcopy(mhi.mhi_daddr, daddr, addr_length);
15027c478bd9Sstevel@tonic-gate 
15037c478bd9Sstevel@tonic-gate 	/*
1504605445d5Sdg 	 * Set the destination DLSAP to the SAP value encoded in the packet.
15057c478bd9Sstevel@tonic-gate 	 */
1506605445d5Sdg 	if (mhi.mhi_istagged && !strip_vlan)
1507605445d5Sdg 		*(uint16_t *)(daddr + addr_length) = ETHERTYPE_VLAN;
1508605445d5Sdg 	else
1509605445d5Sdg 		*(uint16_t *)(daddr + addr_length) = mhi.mhi_bindsap;
15107c478bd9Sstevel@tonic-gate 	dlp->dl_dest_addr_length = addr_length + sizeof (uint16_t);
15117c478bd9Sstevel@tonic-gate 
15127c478bd9Sstevel@tonic-gate 	/*
1513ba2e4443Sseb 	 * If the destination address was multicast or broadcast then the
15147c478bd9Sstevel@tonic-gate 	 * dl_group_address field should be non-zero.
15157c478bd9Sstevel@tonic-gate 	 */
1516ba2e4443Sseb 	dlp->dl_group_address = (mhi.mhi_dsttype == MAC_ADDRTYPE_MULTICAST) ||
1517ba2e4443Sseb 	    (mhi.mhi_dsttype == MAC_ADDRTYPE_BROADCAST);
15187c478bd9Sstevel@tonic-gate 
15197c478bd9Sstevel@tonic-gate 	/*
1520ba2e4443Sseb 	 * Copy in the source address if one exists.  Some MAC types (DL_IB
1521ba2e4443Sseb 	 * for example) may not have access to source information.
15227c478bd9Sstevel@tonic-gate 	 */
1523ba2e4443Sseb 	if (mhi.mhi_saddr == NULL) {
1524ba2e4443Sseb 		dlp->dl_src_addr_offset = dlp->dl_src_addr_length = 0;
1525ba2e4443Sseb 	} else {
1526ba2e4443Sseb 		saddr = dlwp->dl_src_addr;
1527ba2e4443Sseb 		dlp->dl_src_addr_offset = (uintptr_t)saddr - (uintptr_t)dlp;
1528ba2e4443Sseb 		bcopy(mhi.mhi_saddr, saddr, addr_length);
15297c478bd9Sstevel@tonic-gate 
1530ba2e4443Sseb 		/*
1531ba2e4443Sseb 		 * Set the source DLSAP to the packet ethertype.
1532ba2e4443Sseb 		 */
1533ba2e4443Sseb 		*(uint16_t *)(saddr + addr_length) = mhi.mhi_origsap;
1534ba2e4443Sseb 		dlp->dl_src_addr_length = addr_length + sizeof (uint16_t);
1535ba2e4443Sseb 	}
15367c478bd9Sstevel@tonic-gate 
15377c478bd9Sstevel@tonic-gate 	return (nmp);
15387c478bd9Sstevel@tonic-gate }
15397c478bd9Sstevel@tonic-gate 
15407c478bd9Sstevel@tonic-gate /*
15417c478bd9Sstevel@tonic-gate  * DL_NOTIFY_IND: DL_NOTE_PROMISC_ON_PHYS
15427c478bd9Sstevel@tonic-gate  */
15437c478bd9Sstevel@tonic-gate static void
str_notify_promisc_on_phys(dld_str_t * dsp)15447c478bd9Sstevel@tonic-gate str_notify_promisc_on_phys(dld_str_t *dsp)
15457c478bd9Sstevel@tonic-gate {
15467c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
15477c478bd9Sstevel@tonic-gate 	dl_notify_ind_t	*dlip;
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate 	if (!(dsp->ds_notifications & DL_NOTE_PROMISC_ON_PHYS))
15507c478bd9Sstevel@tonic-gate 		return;
15517c478bd9Sstevel@tonic-gate 
15527c478bd9Sstevel@tonic-gate 	if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
15537c478bd9Sstevel@tonic-gate 	    M_PROTO, 0)) == NULL)
15547c478bd9Sstevel@tonic-gate 		return;
15557c478bd9Sstevel@tonic-gate 
15567c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
15577c478bd9Sstevel@tonic-gate 	dlip = (dl_notify_ind_t *)mp->b_rptr;
15587c478bd9Sstevel@tonic-gate 	dlip->dl_primitive = DL_NOTIFY_IND;
15597c478bd9Sstevel@tonic-gate 	dlip->dl_notification = DL_NOTE_PROMISC_ON_PHYS;
15607c478bd9Sstevel@tonic-gate 
15617c478bd9Sstevel@tonic-gate 	qreply(dsp->ds_wq, mp);
15627c478bd9Sstevel@tonic-gate }
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate /*
15657c478bd9Sstevel@tonic-gate  * DL_NOTIFY_IND: DL_NOTE_PROMISC_OFF_PHYS
15667c478bd9Sstevel@tonic-gate  */
15677c478bd9Sstevel@tonic-gate static void
str_notify_promisc_off_phys(dld_str_t * dsp)15687c478bd9Sstevel@tonic-gate str_notify_promisc_off_phys(dld_str_t *dsp)
15697c478bd9Sstevel@tonic-gate {
15707c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
15717c478bd9Sstevel@tonic-gate 	dl_notify_ind_t	*dlip;
15727c478bd9Sstevel@tonic-gate 
15737c478bd9Sstevel@tonic-gate 	if (!(dsp->ds_notifications & DL_NOTE_PROMISC_OFF_PHYS))
15747c478bd9Sstevel@tonic-gate 		return;
15757c478bd9Sstevel@tonic-gate 
15767c478bd9Sstevel@tonic-gate 	if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
15777c478bd9Sstevel@tonic-gate 	    M_PROTO, 0)) == NULL)
15787c478bd9Sstevel@tonic-gate 		return;
15797c478bd9Sstevel@tonic-gate 
15807c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
15817c478bd9Sstevel@tonic-gate 	dlip = (dl_notify_ind_t *)mp->b_rptr;
15827c478bd9Sstevel@tonic-gate 	dlip->dl_primitive = DL_NOTIFY_IND;
15837c478bd9Sstevel@tonic-gate 	dlip->dl_notification = DL_NOTE_PROMISC_OFF_PHYS;
15847c478bd9Sstevel@tonic-gate 
15857c478bd9Sstevel@tonic-gate 	qreply(dsp->ds_wq, mp);
15867c478bd9Sstevel@tonic-gate }
15877c478bd9Sstevel@tonic-gate 
15887c478bd9Sstevel@tonic-gate /*
15897c478bd9Sstevel@tonic-gate  * DL_NOTIFY_IND: DL_NOTE_PHYS_ADDR
15907c478bd9Sstevel@tonic-gate  */
15917c478bd9Sstevel@tonic-gate static void
str_notify_phys_addr(dld_str_t * dsp,uint_t addr_type,const uint8_t * addr)15922b24ab6bSSebastien Roy str_notify_phys_addr(dld_str_t *dsp, uint_t addr_type, const uint8_t *addr)
15937c478bd9Sstevel@tonic-gate {
15947c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
15957c478bd9Sstevel@tonic-gate 	dl_notify_ind_t	*dlip;
15967c478bd9Sstevel@tonic-gate 	uint_t		addr_length;
15977c478bd9Sstevel@tonic-gate 	uint16_t	ethertype;
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate 	if (!(dsp->ds_notifications & DL_NOTE_PHYS_ADDR))
16007c478bd9Sstevel@tonic-gate 		return;
16017c478bd9Sstevel@tonic-gate 
16027c478bd9Sstevel@tonic-gate 	addr_length = dsp->ds_mip->mi_addr_length;
16037c478bd9Sstevel@tonic-gate 	if ((mp = mexchange(dsp->ds_wq, NULL,
16047c478bd9Sstevel@tonic-gate 	    sizeof (dl_notify_ind_t) + addr_length + sizeof (uint16_t),
16057c478bd9Sstevel@tonic-gate 	    M_PROTO, 0)) == NULL)
16067c478bd9Sstevel@tonic-gate 		return;
16077c478bd9Sstevel@tonic-gate 
16087c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
16097c478bd9Sstevel@tonic-gate 	dlip = (dl_notify_ind_t *)mp->b_rptr;
16107c478bd9Sstevel@tonic-gate 	dlip->dl_primitive = DL_NOTIFY_IND;
16117c478bd9Sstevel@tonic-gate 	dlip->dl_notification = DL_NOTE_PHYS_ADDR;
16122b24ab6bSSebastien Roy 	dlip->dl_data = addr_type;
16137c478bd9Sstevel@tonic-gate 	dlip->dl_addr_offset = sizeof (dl_notify_ind_t);
16147c478bd9Sstevel@tonic-gate 	dlip->dl_addr_length = addr_length + sizeof (uint16_t);
16157c478bd9Sstevel@tonic-gate 
16167c478bd9Sstevel@tonic-gate 	bcopy(addr, &dlip[1], addr_length);
16177c478bd9Sstevel@tonic-gate 
16187c478bd9Sstevel@tonic-gate 	ethertype = (dsp->ds_sap < ETHERTYPE_802_MIN) ? 0 : dsp->ds_sap;
1619843e1988Sjohnlev 	*(uint16_t *)((uchar_t *)(dlip + 1) + addr_length) = ethertype;
16207c478bd9Sstevel@tonic-gate 
16217c478bd9Sstevel@tonic-gate 	qreply(dsp->ds_wq, mp);
16227c478bd9Sstevel@tonic-gate }
16237c478bd9Sstevel@tonic-gate 
16247c478bd9Sstevel@tonic-gate /*
16257c478bd9Sstevel@tonic-gate  * DL_NOTIFY_IND: DL_NOTE_LINK_UP
16267c478bd9Sstevel@tonic-gate  */
16277c478bd9Sstevel@tonic-gate static void
str_notify_link_up(dld_str_t * dsp)16287c478bd9Sstevel@tonic-gate str_notify_link_up(dld_str_t *dsp)
16297c478bd9Sstevel@tonic-gate {
16307c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
16317c478bd9Sstevel@tonic-gate 	dl_notify_ind_t	*dlip;
16327c478bd9Sstevel@tonic-gate 
16337c478bd9Sstevel@tonic-gate 	if (!(dsp->ds_notifications & DL_NOTE_LINK_UP))
16347c478bd9Sstevel@tonic-gate 		return;
16357c478bd9Sstevel@tonic-gate 
16367c478bd9Sstevel@tonic-gate 	if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
16377c478bd9Sstevel@tonic-gate 	    M_PROTO, 0)) == NULL)
16387c478bd9Sstevel@tonic-gate 		return;
16397c478bd9Sstevel@tonic-gate 
16407c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
16417c478bd9Sstevel@tonic-gate 	dlip = (dl_notify_ind_t *)mp->b_rptr;
16427c478bd9Sstevel@tonic-gate 	dlip->dl_primitive = DL_NOTIFY_IND;
16437c478bd9Sstevel@tonic-gate 	dlip->dl_notification = DL_NOTE_LINK_UP;
16447c478bd9Sstevel@tonic-gate 
16457c478bd9Sstevel@tonic-gate 	qreply(dsp->ds_wq, mp);
16467c478bd9Sstevel@tonic-gate }
16477c478bd9Sstevel@tonic-gate 
16487c478bd9Sstevel@tonic-gate /*
16497c478bd9Sstevel@tonic-gate  * DL_NOTIFY_IND: DL_NOTE_LINK_DOWN
16507c478bd9Sstevel@tonic-gate  */
16517c478bd9Sstevel@tonic-gate static void
str_notify_link_down(dld_str_t * dsp)16527c478bd9Sstevel@tonic-gate str_notify_link_down(dld_str_t *dsp)
16537c478bd9Sstevel@tonic-gate {
16547c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
16557c478bd9Sstevel@tonic-gate 	dl_notify_ind_t	*dlip;
16567c478bd9Sstevel@tonic-gate 
16577c478bd9Sstevel@tonic-gate 	if (!(dsp->ds_notifications & DL_NOTE_LINK_DOWN))
16587c478bd9Sstevel@tonic-gate 		return;
16597c478bd9Sstevel@tonic-gate 
16607c478bd9Sstevel@tonic-gate 	if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
16617c478bd9Sstevel@tonic-gate 	    M_PROTO, 0)) == NULL)
16627c478bd9Sstevel@tonic-gate 		return;
16637c478bd9Sstevel@tonic-gate 
16647c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
16657c478bd9Sstevel@tonic-gate 	dlip = (dl_notify_ind_t *)mp->b_rptr;
16667c478bd9Sstevel@tonic-gate 	dlip->dl_primitive = DL_NOTIFY_IND;
16677c478bd9Sstevel@tonic-gate 	dlip->dl_notification = DL_NOTE_LINK_DOWN;
16687c478bd9Sstevel@tonic-gate 
16697c478bd9Sstevel@tonic-gate 	qreply(dsp->ds_wq, mp);
16707c478bd9Sstevel@tonic-gate }
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate /*
16737c478bd9Sstevel@tonic-gate  * DL_NOTIFY_IND: DL_NOTE_SPEED
16747c478bd9Sstevel@tonic-gate  */
16757c478bd9Sstevel@tonic-gate static void
str_notify_speed(dld_str_t * dsp,uint32_t speed)16767c478bd9Sstevel@tonic-gate str_notify_speed(dld_str_t *dsp, uint32_t speed)
16777c478bd9Sstevel@tonic-gate {
16787c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
16797c478bd9Sstevel@tonic-gate 	dl_notify_ind_t	*dlip;
16807c478bd9Sstevel@tonic-gate 
16817c478bd9Sstevel@tonic-gate 	if (!(dsp->ds_notifications & DL_NOTE_SPEED))
16827c478bd9Sstevel@tonic-gate 		return;
16837c478bd9Sstevel@tonic-gate 
16847c478bd9Sstevel@tonic-gate 	if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
16857c478bd9Sstevel@tonic-gate 	    M_PROTO, 0)) == NULL)
16867c478bd9Sstevel@tonic-gate 		return;
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
16897c478bd9Sstevel@tonic-gate 	dlip = (dl_notify_ind_t *)mp->b_rptr;
16907c478bd9Sstevel@tonic-gate 	dlip->dl_primitive = DL_NOTIFY_IND;
16917c478bd9Sstevel@tonic-gate 	dlip->dl_notification = DL_NOTE_SPEED;
16927c478bd9Sstevel@tonic-gate 	dlip->dl_data = speed;
16937c478bd9Sstevel@tonic-gate 
16947c478bd9Sstevel@tonic-gate 	qreply(dsp->ds_wq, mp);
16957c478bd9Sstevel@tonic-gate }
16967c478bd9Sstevel@tonic-gate 
16977c478bd9Sstevel@tonic-gate /*
16987c478bd9Sstevel@tonic-gate  * DL_NOTIFY_IND: DL_NOTE_CAPAB_RENEG
16997c478bd9Sstevel@tonic-gate  */
17007c478bd9Sstevel@tonic-gate static void
str_notify_capab_reneg(dld_str_t * dsp)17017c478bd9Sstevel@tonic-gate str_notify_capab_reneg(dld_str_t *dsp)
17027c478bd9Sstevel@tonic-gate {
17037c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
17047c478bd9Sstevel@tonic-gate 	dl_notify_ind_t	*dlip;
17057c478bd9Sstevel@tonic-gate 
17067c478bd9Sstevel@tonic-gate 	if (!(dsp->ds_notifications & DL_NOTE_CAPAB_RENEG))
17077c478bd9Sstevel@tonic-gate 		return;
17087c478bd9Sstevel@tonic-gate 
17097c478bd9Sstevel@tonic-gate 	if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
17107c478bd9Sstevel@tonic-gate 	    M_PROTO, 0)) == NULL)
17117c478bd9Sstevel@tonic-gate 		return;
17127c478bd9Sstevel@tonic-gate 
17137c478bd9Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
17147c478bd9Sstevel@tonic-gate 	dlip = (dl_notify_ind_t *)mp->b_rptr;
17157c478bd9Sstevel@tonic-gate 	dlip->dl_primitive = DL_NOTIFY_IND;
17167c478bd9Sstevel@tonic-gate 	dlip->dl_notification = DL_NOTE_CAPAB_RENEG;
17177c478bd9Sstevel@tonic-gate 
17187c478bd9Sstevel@tonic-gate 	qreply(dsp->ds_wq, mp);
17197c478bd9Sstevel@tonic-gate }
17207c478bd9Sstevel@tonic-gate 
1721ba2e4443Sseb /*
1722ba2e4443Sseb  * DL_NOTIFY_IND: DL_NOTE_FASTPATH_FLUSH
1723ba2e4443Sseb  */
1724ba2e4443Sseb static void
str_notify_fastpath_flush(dld_str_t * dsp)1725ba2e4443Sseb str_notify_fastpath_flush(dld_str_t *dsp)
1726ba2e4443Sseb {
1727ba2e4443Sseb 	mblk_t		*mp;
1728ba2e4443Sseb 	dl_notify_ind_t	*dlip;
1729ba2e4443Sseb 
1730ba2e4443Sseb 	if (!(dsp->ds_notifications & DL_NOTE_FASTPATH_FLUSH))
1731ba2e4443Sseb 		return;
1732ba2e4443Sseb 
1733ba2e4443Sseb 	if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
1734ba2e4443Sseb 	    M_PROTO, 0)) == NULL)
1735ba2e4443Sseb 		return;
1736ba2e4443Sseb 
1737ba2e4443Sseb 	bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
1738ba2e4443Sseb 	dlip = (dl_notify_ind_t *)mp->b_rptr;
1739ba2e4443Sseb 	dlip->dl_primitive = DL_NOTIFY_IND;
1740ba2e4443Sseb 	dlip->dl_notification = DL_NOTE_FASTPATH_FLUSH;
1741ba2e4443Sseb 
1742ba2e4443Sseb 	qreply(dsp->ds_wq, mp);
1743ba2e4443Sseb }
1744ba2e4443Sseb 
1745550b6e40SSowmini Varadhan static void
str_notify_allowed_ips(dld_str_t * dsp)1746550b6e40SSowmini Varadhan str_notify_allowed_ips(dld_str_t *dsp)
1747550b6e40SSowmini Varadhan {
1748550b6e40SSowmini Varadhan 	mblk_t		*mp;
1749550b6e40SSowmini Varadhan 	dl_notify_ind_t	*dlip;
1750550b6e40SSowmini Varadhan 	size_t		mp_size;
175189c6130dSSowmini Varadhan 	mac_protect_t	*mrp;
1752550b6e40SSowmini Varadhan 
1753550b6e40SSowmini Varadhan 	if (!(dsp->ds_notifications & DL_NOTE_ALLOWED_IPS))
1754550b6e40SSowmini Varadhan 		return;
1755550b6e40SSowmini Varadhan 
1756550b6e40SSowmini Varadhan 	mp_size = sizeof (mac_protect_t) + sizeof (dl_notify_ind_t);
1757550b6e40SSowmini Varadhan 	if ((mp = mexchange(dsp->ds_wq, NULL, mp_size, M_PROTO, 0)) == NULL)
1758550b6e40SSowmini Varadhan 		return;
1759550b6e40SSowmini Varadhan 
176089c6130dSSowmini Varadhan 	mrp = mac_protect_get(dsp->ds_mh);
1761550b6e40SSowmini Varadhan 	bzero(mp->b_rptr, mp_size);
1762550b6e40SSowmini Varadhan 	dlip = (dl_notify_ind_t *)mp->b_rptr;
1763550b6e40SSowmini Varadhan 	dlip->dl_primitive = DL_NOTIFY_IND;
1764550b6e40SSowmini Varadhan 	dlip->dl_notification = DL_NOTE_ALLOWED_IPS;
1765550b6e40SSowmini Varadhan 	dlip->dl_data = 0;
1766550b6e40SSowmini Varadhan 	dlip->dl_addr_offset = sizeof (dl_notify_ind_t);
1767550b6e40SSowmini Varadhan 	dlip->dl_addr_length = sizeof (mac_protect_t);
176889c6130dSSowmini Varadhan 	bcopy(mrp, mp->b_rptr + sizeof (dl_notify_ind_t),
1769550b6e40SSowmini Varadhan 	    sizeof (mac_protect_t));
1770550b6e40SSowmini Varadhan 
1771550b6e40SSowmini Varadhan 	qreply(dsp->ds_wq, mp);
1772550b6e40SSowmini Varadhan }
1773550b6e40SSowmini Varadhan 
17747c478bd9Sstevel@tonic-gate /*
17757c478bd9Sstevel@tonic-gate  * MAC notification callback.
17767c478bd9Sstevel@tonic-gate  */
1777da14cebeSEric Cheng void
str_notify(void * arg,mac_notify_type_t type)17787c478bd9Sstevel@tonic-gate str_notify(void *arg, mac_notify_type_t type)
17797c478bd9Sstevel@tonic-gate {
17807c478bd9Sstevel@tonic-gate 	dld_str_t		*dsp = (dld_str_t *)arg;
17817c478bd9Sstevel@tonic-gate 	queue_t			*q = dsp->ds_wq;
1782da14cebeSEric Cheng 	mac_handle_t		mh = dsp->ds_mh;
1783da14cebeSEric Cheng 	mac_client_handle_t	mch = dsp->ds_mch;
1784da14cebeSEric Cheng 	uint8_t			addr[MAXMACADDRLEN];
17857c478bd9Sstevel@tonic-gate 
17867c478bd9Sstevel@tonic-gate 	switch (type) {
17877c478bd9Sstevel@tonic-gate 	case MAC_NOTE_TX:
17887c478bd9Sstevel@tonic-gate 		qenable(q);
17897c478bd9Sstevel@tonic-gate 		break;
17907c478bd9Sstevel@tonic-gate 
17917c478bd9Sstevel@tonic-gate 	case MAC_NOTE_DEVPROMISC:
17927c478bd9Sstevel@tonic-gate 		/*
17937c478bd9Sstevel@tonic-gate 		 * Send the appropriate DL_NOTIFY_IND.
17947c478bd9Sstevel@tonic-gate 		 */
1795d91a22bfSGirish Moodalbail 		if (mac_promisc_get(mh))
17967c478bd9Sstevel@tonic-gate 			str_notify_promisc_on_phys(dsp);
17977c478bd9Sstevel@tonic-gate 		else
17987c478bd9Sstevel@tonic-gate 			str_notify_promisc_off_phys(dsp);
17997c478bd9Sstevel@tonic-gate 		break;
18007c478bd9Sstevel@tonic-gate 
18017c478bd9Sstevel@tonic-gate 	case MAC_NOTE_UNICST:
18027c478bd9Sstevel@tonic-gate 		/*
1803da14cebeSEric Cheng 		 * This notification is sent whenever the MAC unicast
1804da14cebeSEric Cheng 		 * address changes.
18057c478bd9Sstevel@tonic-gate 		 */
1806da14cebeSEric Cheng 		mac_unicast_primary_get(mh, addr);
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 		/*
18097c478bd9Sstevel@tonic-gate 		 * Send the appropriate DL_NOTIFY_IND.
18107c478bd9Sstevel@tonic-gate 		 */
18112b24ab6bSSebastien Roy 		str_notify_phys_addr(dsp, DL_CURR_PHYS_ADDR, addr);
18122b24ab6bSSebastien Roy 		break;
18132b24ab6bSSebastien Roy 
18142b24ab6bSSebastien Roy 	case MAC_NOTE_DEST:
18152b24ab6bSSebastien Roy 		/*
18162b24ab6bSSebastien Roy 		 * Only send up DL_NOTE_DEST_ADDR if the link has a
18172b24ab6bSSebastien Roy 		 * destination address.
18182b24ab6bSSebastien Roy 		 */
18192b24ab6bSSebastien Roy 		if (mac_dst_get(dsp->ds_mh, addr))
18202b24ab6bSSebastien Roy 			str_notify_phys_addr(dsp, DL_CURR_DEST_ADDR, addr);
18217c478bd9Sstevel@tonic-gate 		break;
18227c478bd9Sstevel@tonic-gate 
18234eaa4710SRishi Srivatsavai 	case MAC_NOTE_LOWLINK:
18247c478bd9Sstevel@tonic-gate 	case MAC_NOTE_LINK:
18254eaa4710SRishi Srivatsavai 		/*
18264eaa4710SRishi Srivatsavai 		 * LOWLINK refers to the actual link status. For links that
18274eaa4710SRishi Srivatsavai 		 * are not part of a bridge instance LOWLINK and LINK state
18284eaa4710SRishi Srivatsavai 		 * are the same. But for a link part of a bridge instance
18294eaa4710SRishi Srivatsavai 		 * LINK state refers to the aggregate link status: "up" when
18304eaa4710SRishi Srivatsavai 		 * at least one link part of the bridge is up and is "down"
18314eaa4710SRishi Srivatsavai 		 * when all links part of the bridge are down.
18324eaa4710SRishi Srivatsavai 		 *
18334eaa4710SRishi Srivatsavai 		 * Clients can request to be notified of the LOWLINK state
18344eaa4710SRishi Srivatsavai 		 * using the DLIOCLOWLINK ioctl. Clients such as the bridge
18354eaa4710SRishi Srivatsavai 		 * daemon request lowlink state changes and upper layer clients
18364eaa4710SRishi Srivatsavai 		 * receive notifications of the aggregate link state changes
18374eaa4710SRishi Srivatsavai 		 * which is the default when requesting LINK UP/DOWN state
18384eaa4710SRishi Srivatsavai 		 * notifications.
18394eaa4710SRishi Srivatsavai 		 */
18404eaa4710SRishi Srivatsavai 
18414eaa4710SRishi Srivatsavai 		/*
18424eaa4710SRishi Srivatsavai 		 * Check that the notification type matches the one that we
18434eaa4710SRishi Srivatsavai 		 * want.  If we want lower-level link notifications, and this
18444eaa4710SRishi Srivatsavai 		 * is upper, or if we want upper and this is lower, then
18454eaa4710SRishi Srivatsavai 		 * ignore.
18464eaa4710SRishi Srivatsavai 		 */
18474eaa4710SRishi Srivatsavai 		if ((type == MAC_NOTE_LOWLINK) != dsp->ds_lowlink)
18484eaa4710SRishi Srivatsavai 			break;
18497c478bd9Sstevel@tonic-gate 		/*
18507c478bd9Sstevel@tonic-gate 		 * This notification is sent every time the MAC driver
18517c478bd9Sstevel@tonic-gate 		 * updates the link state.
18527c478bd9Sstevel@tonic-gate 		 */
18534eaa4710SRishi Srivatsavai 		switch (mac_client_stat_get(mch, dsp->ds_lowlink ?
18544eaa4710SRishi Srivatsavai 		    MAC_STAT_LOWLINK_STATE : MAC_STAT_LINK_STATE)) {
1855ba2e4443Sseb 		case LINK_STATE_UP: {
1856ba2e4443Sseb 			uint64_t speed;
18577c478bd9Sstevel@tonic-gate 			/*
18587c478bd9Sstevel@tonic-gate 			 * The link is up so send the appropriate
18597c478bd9Sstevel@tonic-gate 			 * DL_NOTIFY_IND.
18607c478bd9Sstevel@tonic-gate 			 */
18617c478bd9Sstevel@tonic-gate 			str_notify_link_up(dsp);
18627c478bd9Sstevel@tonic-gate 
1863da14cebeSEric Cheng 			speed = mac_stat_get(mh, MAC_STAT_IFSPEED);
1864ba2e4443Sseb 			str_notify_speed(dsp, (uint32_t)(speed / 1000ull));
18657c478bd9Sstevel@tonic-gate 			break;
1866ba2e4443Sseb 		}
18677c478bd9Sstevel@tonic-gate 		case LINK_STATE_DOWN:
18687c478bd9Sstevel@tonic-gate 			/*
18697c478bd9Sstevel@tonic-gate 			 * The link is down so send the appropriate
18707c478bd9Sstevel@tonic-gate 			 * DL_NOTIFY_IND.
18717c478bd9Sstevel@tonic-gate 			 */
18727c478bd9Sstevel@tonic-gate 			str_notify_link_down(dsp);
18737c478bd9Sstevel@tonic-gate 			break;
18747c478bd9Sstevel@tonic-gate 
18757c478bd9Sstevel@tonic-gate 		default:
18767c478bd9Sstevel@tonic-gate 			break;
18777c478bd9Sstevel@tonic-gate 		}
18787c478bd9Sstevel@tonic-gate 		break;
18797c478bd9Sstevel@tonic-gate 
1880da14cebeSEric Cheng 	case MAC_NOTE_CAPAB_CHG:
18817c478bd9Sstevel@tonic-gate 		/*
18827c478bd9Sstevel@tonic-gate 		 * This notification is sent whenever the MAC resources
1883843e1988Sjohnlev 		 * change or capabilities change. We need to renegotiate
1884843e1988Sjohnlev 		 * the capabilities. Send the appropriate DL_NOTIFY_IND.
18857c478bd9Sstevel@tonic-gate 		 */
18867c478bd9Sstevel@tonic-gate 		str_notify_capab_reneg(dsp);
18877c478bd9Sstevel@tonic-gate 		break;
18887c478bd9Sstevel@tonic-gate 
1889e7801d59Ssowmini 	case MAC_NOTE_SDU_SIZE: {
1890e7801d59Ssowmini 		uint_t  max_sdu;
18911eee170aSErik Nordmark 		uint_t	multicast_sdu;
18921eee170aSErik Nordmark 		mac_sdu_get2(dsp->ds_mh, NULL, &max_sdu, &multicast_sdu);
18931eee170aSErik Nordmark 		str_notify_sdu_size(dsp, max_sdu, multicast_sdu);
1894e7801d59Ssowmini 		break;
1895e7801d59Ssowmini 	}
1896e7801d59Ssowmini 
1897ba2e4443Sseb 	case MAC_NOTE_FASTPATH_FLUSH:
1898ba2e4443Sseb 		str_notify_fastpath_flush(dsp);
1899ba2e4443Sseb 		break;
1900ba2e4443Sseb 
19014eaa4710SRishi Srivatsavai 	/* Unused notifications */
1902d62bc4baSyz 	case MAC_NOTE_MARGIN:
1903d62bc4baSyz 		break;
1904e7801d59Ssowmini 
1905550b6e40SSowmini Varadhan 	case MAC_NOTE_ALLOWED_IPS:
1906550b6e40SSowmini Varadhan 		str_notify_allowed_ips(dsp);
1907550b6e40SSowmini Varadhan 		break;
1908550b6e40SSowmini Varadhan 
19097c478bd9Sstevel@tonic-gate 	default:
19107c478bd9Sstevel@tonic-gate 		ASSERT(B_FALSE);
19117c478bd9Sstevel@tonic-gate 		break;
19127c478bd9Sstevel@tonic-gate 	}
19137c478bd9Sstevel@tonic-gate }
19147c478bd9Sstevel@tonic-gate 
19157c478bd9Sstevel@tonic-gate /*
1916da14cebeSEric Cheng  * This function is called via a taskq mechansim to process all control
1917da14cebeSEric Cheng  * messages on a per 'dsp' end point.
19187c478bd9Sstevel@tonic-gate  */
1919d62bc4baSyz static void
dld_wput_nondata_task(void * arg)1920da14cebeSEric Cheng dld_wput_nondata_task(void *arg)
19217c478bd9Sstevel@tonic-gate {
1922da14cebeSEric Cheng 	dld_str_t	*dsp = arg;
1923da14cebeSEric Cheng 	mblk_t		*mp;
1924da14cebeSEric Cheng 
1925da14cebeSEric Cheng 	mutex_enter(&dsp->ds_lock);
1926da14cebeSEric Cheng 	while (dsp->ds_pending_head != NULL) {
1927da14cebeSEric Cheng 		mp = dsp->ds_pending_head;
1928da14cebeSEric Cheng 		dsp->ds_pending_head = mp->b_next;
1929da14cebeSEric Cheng 		mp->b_next = NULL;
1930da14cebeSEric Cheng 		if (dsp->ds_pending_head == NULL)
1931da14cebeSEric Cheng 			dsp->ds_pending_tail = NULL;
1932da14cebeSEric Cheng 		mutex_exit(&dsp->ds_lock);
1933da14cebeSEric Cheng 
1934da14cebeSEric Cheng 		switch (DB_TYPE(mp)) {
1935da14cebeSEric Cheng 		case M_PROTO:
1936da14cebeSEric Cheng 		case M_PCPROTO:
1937da14cebeSEric Cheng 			dld_proto(dsp, mp);
1938d62bc4baSyz 			break;
1939da14cebeSEric Cheng 		case M_IOCTL:
1940da14cebeSEric Cheng 			dld_ioc(dsp, mp);
1941da14cebeSEric Cheng 			break;
1942da14cebeSEric Cheng 		default:
1943da14cebeSEric Cheng 			ASSERT(0);
1944d62bc4baSyz 		}
1945da14cebeSEric Cheng 
1946da14cebeSEric Cheng 		mutex_enter(&dsp->ds_lock);
1947210db224Sericheng 	}
1948da14cebeSEric Cheng 	ASSERT(dsp->ds_pending_tail == NULL);
1949da14cebeSEric Cheng 	dsp->ds_dlpi_pending = 0;
1950da14cebeSEric Cheng 	cv_broadcast(&dsp->ds_dlpi_pending_cv);
1951da14cebeSEric Cheng 	mutex_exit(&dsp->ds_lock);
1952d62bc4baSyz }
1953d62bc4baSyz 
1954d62bc4baSyz /*
1955da14cebeSEric Cheng  * Kernel thread to handle taskq dispatch failures in dld_wput_data. This
1956da14cebeSEric Cheng  * thread is started at boot time.
1957d62bc4baSyz  */
1958d62bc4baSyz static void
dld_taskq_dispatch(void)1959da14cebeSEric Cheng dld_taskq_dispatch(void)
1960d62bc4baSyz {
1961da14cebeSEric Cheng 	callb_cpr_t	cprinfo;
1962da14cebeSEric Cheng 	dld_str_t	*dsp;
1963d62bc4baSyz 
1964da14cebeSEric Cheng 	CALLB_CPR_INIT(&cprinfo, &dld_taskq_lock, callb_generic_cpr,
1965da14cebeSEric Cheng 	    "dld_taskq_dispatch");
1966da14cebeSEric Cheng 	mutex_enter(&dld_taskq_lock);
1967da14cebeSEric Cheng 
1968da14cebeSEric Cheng 	while (!dld_taskq_quit) {
1969da14cebeSEric Cheng 		dsp = list_head(&dld_taskq_list);
1970da14cebeSEric Cheng 		while (dsp != NULL) {
1971da14cebeSEric Cheng 			list_remove(&dld_taskq_list, dsp);
1972da14cebeSEric Cheng 			mutex_exit(&dld_taskq_lock);
1973da14cebeSEric Cheng 			VERIFY(taskq_dispatch(dld_taskq, dld_wput_nondata_task,
1974fc8ae2ecSToomas Soome 			    dsp, TQ_SLEEP) != TASKQID_INVALID);
1975da14cebeSEric Cheng 			mutex_enter(&dld_taskq_lock);
1976da14cebeSEric Cheng 			dsp = list_head(&dld_taskq_list);
1977d62bc4baSyz 		}
1978210db224Sericheng 
1979da14cebeSEric Cheng 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
1980da14cebeSEric Cheng 		cv_wait(&dld_taskq_cv, &dld_taskq_lock);
1981da14cebeSEric Cheng 		CALLB_CPR_SAFE_END(&cprinfo, &dld_taskq_lock);
1982210db224Sericheng 	}
1983d62bc4baSyz 
1984da14cebeSEric Cheng 	dld_taskq_done = B_TRUE;
1985da14cebeSEric Cheng 	cv_signal(&dld_taskq_cv);
1986da14cebeSEric Cheng 	CALLB_CPR_EXIT(&cprinfo);
1987da14cebeSEric Cheng 	thread_exit();
1988210db224Sericheng }
1989210db224Sericheng 
1990210db224Sericheng /*
1991da14cebeSEric Cheng  * All control operations are serialized on the 'dsp' and are also funneled
1992da14cebeSEric Cheng  * through a taskq mechanism to ensure that subsequent processing has kernel
1993da14cebeSEric Cheng  * context and can safely use cv_wait.
1994da14cebeSEric Cheng  *
1995da14cebeSEric Cheng  * Mechanisms to handle taskq dispatch failures
1996da14cebeSEric Cheng  *
1997da14cebeSEric Cheng  * The only way to be sure that taskq dispatch does not fail is to either
1998da14cebeSEric Cheng  * specify TQ_SLEEP or to use a static taskq and prepopulate it with
1999da14cebeSEric Cheng  * some number of entries and make sure that the number of outstanding requests
2000da14cebeSEric Cheng  * are less than that number. We can't use TQ_SLEEP since we don't know the
2001da14cebeSEric Cheng  * context. Nor can we bound the total number of 'dsp' end points. So we are
2002da14cebeSEric Cheng  * unable to use either of the above schemes, and are forced to deal with
2003da14cebeSEric Cheng  * taskq dispatch failures. Note that even dynamic taskq could fail in
2004da14cebeSEric Cheng  * dispatch if TQ_NOSLEEP is specified, since this flag is translated
2005da14cebeSEric Cheng  * eventually to KM_NOSLEEP and kmem allocations could fail in the taskq
2006da14cebeSEric Cheng  * framework.
2007da14cebeSEric Cheng  *
2008da14cebeSEric Cheng  * We maintain a queue of 'dsp's that encountered taskq dispatch failure.
2009da14cebeSEric Cheng  * We also have a single global thread to retry the taskq dispatch. This
2010da14cebeSEric Cheng  * thread loops in 'dld_taskq_dispatch' and retries the taskq dispatch, but
2011da14cebeSEric Cheng  * uses TQ_SLEEP to ensure eventual success of the dispatch operation.
2012210db224Sericheng  */
2013210db224Sericheng static void
dld_wput_nondata(dld_str_t * dsp,mblk_t * mp)2014d62bc4baSyz dld_wput_nondata(dld_str_t *dsp, mblk_t *mp)
2015210db224Sericheng {
2016da14cebeSEric Cheng 	ASSERT(mp->b_next == NULL);
2017da14cebeSEric Cheng 	mutex_enter(&dsp->ds_lock);
2018da14cebeSEric Cheng 	if (dsp->ds_pending_head != NULL) {
2019da14cebeSEric Cheng 		ASSERT(dsp->ds_dlpi_pending);
2020d62bc4baSyz 		dsp->ds_pending_tail->b_next = mp;
2021d62bc4baSyz 		dsp->ds_pending_tail = mp;
2022da14cebeSEric Cheng 		mutex_exit(&dsp->ds_lock);
2023da14cebeSEric Cheng 		return;
2024d62bc4baSyz 	}
2025da14cebeSEric Cheng 	ASSERT(dsp->ds_pending_tail == NULL);
2026da14cebeSEric Cheng 	dsp->ds_pending_head = dsp->ds_pending_tail = mp;
2027d62bc4baSyz 	/*
2028da14cebeSEric Cheng 	 * At this point if ds_dlpi_pending is set, it implies that the taskq
2029da14cebeSEric Cheng 	 * thread is still active and is processing the last message, though
2030da14cebeSEric Cheng 	 * the pending queue has been emptied.
2031d62bc4baSyz 	 */
2032da14cebeSEric Cheng 	if (dsp->ds_dlpi_pending) {
2033da14cebeSEric Cheng 		mutex_exit(&dsp->ds_lock);
2034da14cebeSEric Cheng 		return;
2035d62bc4baSyz 	}
2036da14cebeSEric Cheng 
2037da14cebeSEric Cheng 	dsp->ds_dlpi_pending = 1;
2038da14cebeSEric Cheng 	mutex_exit(&dsp->ds_lock);
2039da14cebeSEric Cheng 
2040da14cebeSEric Cheng 	if (taskq_dispatch(dld_taskq, dld_wput_nondata_task, dsp,
2041fc8ae2ecSToomas Soome 	    TQ_NOSLEEP) != TASKQID_INVALID)
2042da14cebeSEric Cheng 		return;
2043da14cebeSEric Cheng 
2044da14cebeSEric Cheng 	mutex_enter(&dld_taskq_lock);
2045da14cebeSEric Cheng 	list_insert_tail(&dld_taskq_list, dsp);
2046da14cebeSEric Cheng 	cv_signal(&dld_taskq_cv);
2047da14cebeSEric Cheng 	mutex_exit(&dld_taskq_lock);
2048d62bc4baSyz }
2049d62bc4baSyz 
2050d62bc4baSyz /*
2051da14cebeSEric Cheng  * Process an M_IOCTL message.
2052d62bc4baSyz  */
2053d62bc4baSyz static void
dld_ioc(dld_str_t * dsp,mblk_t * mp)2054da14cebeSEric Cheng dld_ioc(dld_str_t *dsp, mblk_t *mp)
2055d62bc4baSyz {
2056da14cebeSEric Cheng 	uint_t			cmd;
2057d62bc4baSyz 
2058da14cebeSEric Cheng 	cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
2059da14cebeSEric Cheng 	ASSERT(dsp->ds_type == DLD_DLPI);
2060d62bc4baSyz 
2061da14cebeSEric Cheng 	switch (cmd) {
2062da14cebeSEric Cheng 	case DLIOCNATIVE:
2063da14cebeSEric Cheng 		ioc_native(dsp, mp);
2064210db224Sericheng 		break;
2065da14cebeSEric Cheng 	case DLIOCMARGININFO:
2066da14cebeSEric Cheng 		ioc_margin(dsp, mp);
2067210db224Sericheng 		break;
2068da14cebeSEric Cheng 	case DLIOCRAW:
2069da14cebeSEric Cheng 		ioc_raw(dsp, mp);
2070d62bc4baSyz 		break;
2071da14cebeSEric Cheng 	case DLIOCHDRINFO:
2072da14cebeSEric Cheng 		ioc_fast(dsp, mp);
2073da14cebeSEric Cheng 		break;
20744eaa4710SRishi Srivatsavai 	case DLIOCLOWLINK:
20754eaa4710SRishi Srivatsavai 		ioc_lowlink(dsp, mp);
20764eaa4710SRishi Srivatsavai 		break;
2077da14cebeSEric Cheng 	default:
2078da14cebeSEric Cheng 		ioc(dsp, mp);
2079210db224Sericheng 	}
2080210db224Sericheng }
2081210db224Sericheng 
20820ba2cbe9Sxc /*
20830ba2cbe9Sxc  * DLIOCNATIVE
20840ba2cbe9Sxc  */
20850ba2cbe9Sxc static void
ioc_native(dld_str_t * dsp,mblk_t * mp)20860ba2cbe9Sxc ioc_native(dld_str_t *dsp, mblk_t *mp)
20870ba2cbe9Sxc {
20880ba2cbe9Sxc 	queue_t *q = dsp->ds_wq;
20890ba2cbe9Sxc 	const mac_info_t *mip = dsp->ds_mip;
20900ba2cbe9Sxc 
20910ba2cbe9Sxc 	/*
20920ba2cbe9Sxc 	 * Native mode can be enabled if it's disabled and if the
20930ba2cbe9Sxc 	 * native media type is different.
20940ba2cbe9Sxc 	 */
20950ba2cbe9Sxc 	if (!dsp->ds_native && mip->mi_media != mip->mi_nativemedia)
20960ba2cbe9Sxc 		dsp->ds_native = B_TRUE;
20970ba2cbe9Sxc 
20980ba2cbe9Sxc 	if (dsp->ds_native)
20990ba2cbe9Sxc 		miocack(q, mp, 0, mip->mi_nativemedia);
21000ba2cbe9Sxc 	else
21010ba2cbe9Sxc 		miocnak(q, mp, 0, ENOTSUP);
21020ba2cbe9Sxc }
21030ba2cbe9Sxc 
2104d62bc4baSyz /*
2105d62bc4baSyz  * DLIOCMARGININFO
2106d62bc4baSyz  */
2107d62bc4baSyz static void
ioc_margin(dld_str_t * dsp,mblk_t * mp)2108d62bc4baSyz ioc_margin(dld_str_t *dsp, mblk_t *mp)
2109d62bc4baSyz {
2110d62bc4baSyz 	queue_t *q = dsp->ds_wq;
2111d62bc4baSyz 	uint32_t margin;
2112d62bc4baSyz 	int err;
2113d62bc4baSyz 
2114d62bc4baSyz 	if (dsp->ds_dlstate == DL_UNATTACHED) {
2115d62bc4baSyz 		err = EINVAL;
2116d62bc4baSyz 		goto failed;
2117d62bc4baSyz 	}
2118d62bc4baSyz 	if ((err = miocpullup(mp, sizeof (uint32_t))) != 0)
2119d62bc4baSyz 		goto failed;
2120d62bc4baSyz 
2121d62bc4baSyz 	mac_margin_get(dsp->ds_mh, &margin);
2122d62bc4baSyz 	*((uint32_t *)mp->b_cont->b_rptr) = margin;
2123d62bc4baSyz 	miocack(q, mp, sizeof (uint32_t), 0);
2124d62bc4baSyz 	return;
2125d62bc4baSyz 
2126d62bc4baSyz failed:
2127d62bc4baSyz 	miocnak(q, mp, 0, err);
2128d62bc4baSyz }
2129d62bc4baSyz 
2130210db224Sericheng /*
2131210db224Sericheng  * DLIOCRAW
2132210db224Sericheng  */
2133210db224Sericheng static void
ioc_raw(dld_str_t * dsp,mblk_t * mp)2134210db224Sericheng ioc_raw(dld_str_t *dsp, mblk_t *mp)
2135210db224Sericheng {
2136210db224Sericheng 	queue_t *q = dsp->ds_wq;
2137da14cebeSEric Cheng 	mac_perim_handle_t	mph;
2138da14cebeSEric Cheng 
2139da14cebeSEric Cheng 	if (dsp->ds_mh == NULL) {
2140da14cebeSEric Cheng 		dsp->ds_mode = DLD_RAW;
2141da14cebeSEric Cheng 		miocack(q, mp, 0, 0);
2142da14cebeSEric Cheng 		return;
2143da14cebeSEric Cheng 	}
2144210db224Sericheng 
2145da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
2146da14cebeSEric Cheng 	if (dsp->ds_polling || dsp->ds_direct) {
2147da14cebeSEric Cheng 		mac_perim_exit(mph);
2148210db224Sericheng 		miocnak(q, mp, 0, EPROTO);
2149210db224Sericheng 		return;
2150210db224Sericheng 	}
2151210db224Sericheng 
2152da14cebeSEric Cheng 	if (dsp->ds_mode != DLD_RAW && dsp->ds_dlstate == DL_IDLE) {
2153210db224Sericheng 		/*
2154210db224Sericheng 		 * Set the receive callback.
2155210db224Sericheng 		 */
2156da14cebeSEric Cheng 		dls_rx_set(dsp, dld_str_rx_raw, dsp);
2157210db224Sericheng 	}
2158da14cebeSEric Cheng 
2159da14cebeSEric Cheng 	/*
2160da14cebeSEric Cheng 	 * Note that raw mode is enabled.
2161da14cebeSEric Cheng 	 */
2162490ed22dSyz 	dsp->ds_mode = DLD_RAW;
2163da14cebeSEric Cheng 	mac_perim_exit(mph);
2164da14cebeSEric Cheng 
2165210db224Sericheng 	miocack(q, mp, 0, 0);
2166210db224Sericheng }
2167210db224Sericheng 
2168210db224Sericheng /*
2169210db224Sericheng  * DLIOCHDRINFO
2170210db224Sericheng  */
2171210db224Sericheng static void
ioc_fast(dld_str_t * dsp,mblk_t * mp)2172210db224Sericheng ioc_fast(dld_str_t *dsp, mblk_t *mp)
2173210db224Sericheng {
2174210db224Sericheng 	dl_unitdata_req_t *dlp;
2175210db224Sericheng 	off_t		off;
2176210db224Sericheng 	size_t		len;
2177210db224Sericheng 	const uint8_t	*addr;
2178210db224Sericheng 	uint16_t	sap;
2179210db224Sericheng 	mblk_t		*nmp;
2180210db224Sericheng 	mblk_t		*hmp;
2181210db224Sericheng 	uint_t		addr_length;
2182210db224Sericheng 	queue_t		*q = dsp->ds_wq;
2183210db224Sericheng 	int		err;
2184da14cebeSEric Cheng 	mac_perim_handle_t	mph;
2185210db224Sericheng 
2186210db224Sericheng 	if (dld_opt & DLD_OPT_NO_FASTPATH) {
2187210db224Sericheng 		err = ENOTSUP;
2188210db224Sericheng 		goto failed;
2189210db224Sericheng 	}
2190210db224Sericheng 
2191605445d5Sdg 	/*
2192605445d5Sdg 	 * DLIOCHDRINFO should only come from IP. The one initiated from
2193605445d5Sdg 	 * user-land should not be allowed.
2194605445d5Sdg 	 */
2195605445d5Sdg 	if (((struct iocblk *)mp->b_rptr)->ioc_cr != kcred) {
2196605445d5Sdg 		err = EINVAL;
2197605445d5Sdg 		goto failed;
2198605445d5Sdg 	}
2199605445d5Sdg 
2200210db224Sericheng 	nmp = mp->b_cont;
2201210db224Sericheng 	if (nmp == NULL || MBLKL(nmp) < sizeof (dl_unitdata_req_t) ||
2202210db224Sericheng 	    (dlp = (dl_unitdata_req_t *)nmp->b_rptr,
2203210db224Sericheng 	    dlp->dl_primitive != DL_UNITDATA_REQ)) {
2204210db224Sericheng 		err = EINVAL;
2205210db224Sericheng 		goto failed;
2206210db224Sericheng 	}
2207210db224Sericheng 
2208210db224Sericheng 	off = dlp->dl_dest_addr_offset;
2209210db224Sericheng 	len = dlp->dl_dest_addr_length;
2210210db224Sericheng 
2211210db224Sericheng 	if (!MBLKIN(nmp, off, len)) {
2212210db224Sericheng 		err = EINVAL;
2213210db224Sericheng 		goto failed;
2214210db224Sericheng 	}
2215210db224Sericheng 
2216210db224Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
2217210db224Sericheng 		err = ENOTSUP;
2218210db224Sericheng 		goto failed;
2219210db224Sericheng 	}
2220210db224Sericheng 
2221210db224Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
2222210db224Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
2223210db224Sericheng 		err = EINVAL;
2224210db224Sericheng 		goto failed;
2225210db224Sericheng 	}
2226210db224Sericheng 
2227210db224Sericheng 	addr = nmp->b_rptr + off;
2228210db224Sericheng 	sap = *(uint16_t *)(nmp->b_rptr + off + addr_length);
2229210db224Sericheng 
2230da14cebeSEric Cheng 	if ((hmp = dls_header(dsp, addr, sap, 0, NULL)) == NULL) {
2231210db224Sericheng 		err = ENOMEM;
2232210db224Sericheng 		goto failed;
22337c478bd9Sstevel@tonic-gate 	}
22347c478bd9Sstevel@tonic-gate 
2235da14cebeSEric Cheng 	/*
2236da14cebeSEric Cheng 	 * This ioctl might happen concurrently with a direct call to dld_capab
2237da14cebeSEric Cheng 	 * that tries to enable direct and/or poll capabilities. Since the
2238da14cebeSEric Cheng 	 * stack does not serialize them, we do so here to avoid mixing
2239da14cebeSEric Cheng 	 * the callbacks.
2240da14cebeSEric Cheng 	 */
2241da14cebeSEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
2242210db224Sericheng 	if (dsp->ds_mode != DLD_FASTPATH) {
2243210db224Sericheng 		/*
2244da14cebeSEric Cheng 		 * Set the receive callback (unless polling is enabled).
2245da14cebeSEric Cheng 		 */
2246da14cebeSEric Cheng 		if (!dsp->ds_polling && !dsp->ds_direct)
2247da14cebeSEric Cheng 			dls_rx_set(dsp, dld_str_rx_fastpath, dsp);
2248da14cebeSEric Cheng 
2249da14cebeSEric Cheng 		/*
2250da14cebeSEric Cheng 		 * Note that fast-path mode is enabled.
2251210db224Sericheng 		 */
2252210db224Sericheng 		dsp->ds_mode = DLD_FASTPATH;
22537c478bd9Sstevel@tonic-gate 	}
2254da14cebeSEric Cheng 	mac_perim_exit(mph);
2255210db224Sericheng 
2256210db224Sericheng 	freemsg(nmp->b_cont);
2257210db224Sericheng 	nmp->b_cont = hmp;
2258210db224Sericheng 
2259210db224Sericheng 	miocack(q, mp, MBLKL(nmp) + MBLKL(hmp), 0);
2260210db224Sericheng 	return;
2261210db224Sericheng failed:
2262210db224Sericheng 	miocnak(q, mp, 0, err);
2263210db224Sericheng }
2264210db224Sericheng 
22654eaa4710SRishi Srivatsavai /*
22664eaa4710SRishi Srivatsavai  * DLIOCLOWLINK: request actual link state changes. When the
22674eaa4710SRishi Srivatsavai  * link is part of a bridge instance the client receives actual
22684eaa4710SRishi Srivatsavai  * link state changes and not the aggregate link status. Used by
22694eaa4710SRishi Srivatsavai  * the bridging daemon (bridged) for proper RSTP operation.
22704eaa4710SRishi Srivatsavai  */
22714eaa4710SRishi Srivatsavai static void
ioc_lowlink(dld_str_t * dsp,mblk_t * mp)22724eaa4710SRishi Srivatsavai ioc_lowlink(dld_str_t *dsp, mblk_t *mp)
22734eaa4710SRishi Srivatsavai {
22744eaa4710SRishi Srivatsavai 	queue_t *q = dsp->ds_wq;
22754eaa4710SRishi Srivatsavai 	int err;
22764eaa4710SRishi Srivatsavai 
22774eaa4710SRishi Srivatsavai 	if ((err = miocpullup(mp, sizeof (int))) != 0) {
22784eaa4710SRishi Srivatsavai 		miocnak(q, mp, 0, err);
22794eaa4710SRishi Srivatsavai 	} else {
22804eaa4710SRishi Srivatsavai 		/* LINTED: alignment */
22814eaa4710SRishi Srivatsavai 		dsp->ds_lowlink = *(boolean_t *)mp->b_cont->b_rptr;
22824eaa4710SRishi Srivatsavai 		miocack(q, mp, 0, 0);
22834eaa4710SRishi Srivatsavai 	}
22844eaa4710SRishi Srivatsavai }
22854eaa4710SRishi Srivatsavai 
2286da14cebeSEric Cheng /*
2287da14cebeSEric Cheng  * Catch-all handler.
2288da14cebeSEric Cheng  */
2289210db224Sericheng static void
ioc(dld_str_t * dsp,mblk_t * mp)2290210db224Sericheng ioc(dld_str_t *dsp, mblk_t *mp)
2291210db224Sericheng {
2292210db224Sericheng 	queue_t	*q = dsp->ds_wq;
2293210db224Sericheng 
2294210db224Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED) {
2295210db224Sericheng 		miocnak(q, mp, 0, EINVAL);
2296210db224Sericheng 		return;
2297210db224Sericheng 	}
2298da14cebeSEric Cheng 	mac_ioctl(dsp->ds_mh, q, mp);
2299210db224Sericheng }
2300