xref: /illumos-gate/usr/src/uts/common/io/softmac/softmac_main.c (revision 3ade6e843b7f9e2656892a172ecd9e302b0dee09)
1d62bc4baSyz147064 /*
2d62bc4baSyz147064  * CDDL HEADER START
3d62bc4baSyz147064  *
4d62bc4baSyz147064  * The contents of this file are subject to the terms of the
5d62bc4baSyz147064  * Common Development and Distribution License (the "License").
6d62bc4baSyz147064  * You may not use this file except in compliance with the License.
7d62bc4baSyz147064  *
8d62bc4baSyz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d62bc4baSyz147064  * or http://www.opensolaris.org/os/licensing.
10d62bc4baSyz147064  * See the License for the specific language governing permissions
11d62bc4baSyz147064  * and limitations under the License.
12d62bc4baSyz147064  *
13d62bc4baSyz147064  * When distributing Covered Code, include this CDDL HEADER in each
14d62bc4baSyz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d62bc4baSyz147064  * If applicable, add the following below this CDDL HEADER, with the
16d62bc4baSyz147064  * fields enclosed by brackets "[]" replaced with your own identifying
17d62bc4baSyz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
18d62bc4baSyz147064  *
19d62bc4baSyz147064  * CDDL HEADER END
20d62bc4baSyz147064  */
21d62bc4baSyz147064 /*
2265041820SCathy Zhou  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23d62bc4baSyz147064  * Use is subject to license terms.
24d62bc4baSyz147064  */
25d62bc4baSyz147064 
26d62bc4baSyz147064 /*
27d62bc4baSyz147064  * The softmac driver is used to "unify" non-GLDv3 drivers to the GLDv3
28d62bc4baSyz147064  * framework.  It also creates the kernel datalink structure for each
29d62bc4baSyz147064  * physical network device.
30d62bc4baSyz147064  *
31d62bc4baSyz147064  * Specifically, a softmac will be created for each physical network device
32d62bc4baSyz147064  * (dip) during the device's post-attach process.  When this softmac is
33d62bc4baSyz147064  * created, the following will also be done:
34d62bc4baSyz147064  *   - create the device's <link name, linkid> mapping;
35d62bc4baSyz147064  *   - register the mac if this is a non-GLDv3 device and the media type is
36d62bc4baSyz147064  *     supported by the GLDv3 framework;
37d62bc4baSyz147064  *   - create the kernel data-link structure for this physical device;
38d62bc4baSyz147064  *
39d62bc4baSyz147064  * This softmac will be destroyed during the device's pre-detach process,
40d62bc4baSyz147064  * and all the above will be undone.
41d62bc4baSyz147064  */
42d62bc4baSyz147064 
43d62bc4baSyz147064 #include <sys/types.h>
44d62bc4baSyz147064 #include <sys/file.h>
45d62bc4baSyz147064 #include <sys/cred.h>
46d62bc4baSyz147064 #include <sys/dlpi.h>
47da14cebeSEric Cheng #include <sys/mac_provider.h>
48da14cebeSEric Cheng #include <sys/disp.h>
49d62bc4baSyz147064 #include <sys/sunndi.h>
50d62bc4baSyz147064 #include <sys/modhash.h>
51d62bc4baSyz147064 #include <sys/stropts.h>
52d62bc4baSyz147064 #include <sys/sysmacros.h>
53d62bc4baSyz147064 #include <sys/vlan.h>
54d62bc4baSyz147064 #include <sys/softmac_impl.h>
55d62bc4baSyz147064 #include <sys/softmac.h>
56d62bc4baSyz147064 #include <sys/dls.h>
57d62bc4baSyz147064 
58da14cebeSEric Cheng /* Used as a parameter to the mod hash walk of softmac structures */
59da14cebeSEric Cheng typedef struct {
60da14cebeSEric Cheng 	softmac_t	*smw_softmac;
61da14cebeSEric Cheng 	boolean_t	smw_retry;
62da14cebeSEric Cheng } softmac_walk_t;
63da14cebeSEric Cheng 
64d62bc4baSyz147064 /*
65d62bc4baSyz147064  * Softmac hash table including softmacs for both style-2 and style-1 devices.
66d62bc4baSyz147064  */
67d62bc4baSyz147064 static krwlock_t	softmac_hash_lock;
68d62bc4baSyz147064 static mod_hash_t	*softmac_hash;
69da14cebeSEric Cheng static kmutex_t		smac_global_lock;
70da14cebeSEric Cheng static kcondvar_t	smac_global_cv;
71d62bc4baSyz147064 
725d460eafSCathy Zhou static kmem_cache_t	*softmac_cachep;
735d460eafSCathy Zhou 
74d62bc4baSyz147064 #define	SOFTMAC_HASHSZ		64
75d62bc4baSyz147064 
760dc974a9SCathy Zhou static void softmac_create_task(void *);
770dc974a9SCathy Zhou static void softmac_mac_register(softmac_t *);
78d62bc4baSyz147064 static int softmac_create_datalink(softmac_t *);
79d62bc4baSyz147064 static int softmac_m_start(void *);
80d62bc4baSyz147064 static void softmac_m_stop(void *);
81d62bc4baSyz147064 static int softmac_m_open(void *);
82d62bc4baSyz147064 static void softmac_m_close(void *);
83d62bc4baSyz147064 static boolean_t softmac_m_getcapab(void *, mac_capab_t, void *);
845d460eafSCathy Zhou static int softmac_m_setprop(void *, const char *, mac_prop_id_t,
855d460eafSCathy Zhou     uint_t, const void *);
865d460eafSCathy Zhou static int softmac_m_getprop(void *, const char *, mac_prop_id_t,
875d460eafSCathy Zhou     uint_t, uint_t, void *, uint_t *);
885d460eafSCathy Zhou 
89d62bc4baSyz147064 
90d62bc4baSyz147064 #define	SOFTMAC_M_CALLBACK_FLAGS	\
915d460eafSCathy Zhou 	(MC_IOCTL | MC_GETCAPAB | MC_OPEN | MC_CLOSE | MC_SETPROP | MC_GETPROP)
92d62bc4baSyz147064 
93d62bc4baSyz147064 static mac_callbacks_t softmac_m_callbacks = {
94d62bc4baSyz147064 	SOFTMAC_M_CALLBACK_FLAGS,
95d62bc4baSyz147064 	softmac_m_stat,
96d62bc4baSyz147064 	softmac_m_start,
97d62bc4baSyz147064 	softmac_m_stop,
98d62bc4baSyz147064 	softmac_m_promisc,
99d62bc4baSyz147064 	softmac_m_multicst,
100d62bc4baSyz147064 	softmac_m_unicst,
101d62bc4baSyz147064 	softmac_m_tx,
102d62bc4baSyz147064 	softmac_m_ioctl,
103d62bc4baSyz147064 	softmac_m_getcapab,
104d62bc4baSyz147064 	softmac_m_open,
1055d460eafSCathy Zhou 	softmac_m_close,
1065d460eafSCathy Zhou 	softmac_m_setprop,
1075d460eafSCathy Zhou 	softmac_m_getprop
108d62bc4baSyz147064 };
109d62bc4baSyz147064 
1105d460eafSCathy Zhou /*ARGSUSED*/
1115d460eafSCathy Zhou static int
1125d460eafSCathy Zhou softmac_constructor(void *buf, void *arg, int kmflag)
1135d460eafSCathy Zhou {
1145d460eafSCathy Zhou 	softmac_t	*softmac = buf;
1155d460eafSCathy Zhou 
1165d460eafSCathy Zhou 	bzero(buf, sizeof (softmac_t));
1175d460eafSCathy Zhou 	mutex_init(&softmac->smac_mutex, NULL, MUTEX_DEFAULT, NULL);
1185d460eafSCathy Zhou 	mutex_init(&softmac->smac_active_mutex, NULL, MUTEX_DEFAULT, NULL);
1195d460eafSCathy Zhou 	mutex_init(&softmac->smac_fp_mutex, NULL, MUTEX_DEFAULT, NULL);
1205d460eafSCathy Zhou 	cv_init(&softmac->smac_cv, NULL, CV_DEFAULT, NULL);
1215d460eafSCathy Zhou 	cv_init(&softmac->smac_fp_cv, NULL, CV_DEFAULT, NULL);
1225d460eafSCathy Zhou 	list_create(&softmac->smac_sup_list, sizeof (softmac_upper_t),
1235d460eafSCathy Zhou 	    offsetof(softmac_upper_t, su_list_node));
1245d460eafSCathy Zhou 	return (0);
1255d460eafSCathy Zhou }
1265d460eafSCathy Zhou 
1275d460eafSCathy Zhou /*ARGSUSED*/
1285d460eafSCathy Zhou static void
1295d460eafSCathy Zhou softmac_destructor(void *buf, void *arg)
1305d460eafSCathy Zhou {
1315d460eafSCathy Zhou 	softmac_t	*softmac = buf;
1325d460eafSCathy Zhou 
1335d460eafSCathy Zhou 	ASSERT(softmac->smac_fp_disable_clients == 0);
1345d460eafSCathy Zhou 	ASSERT(!softmac->smac_fastpath_admin_disabled);
1355d460eafSCathy Zhou 
1365d460eafSCathy Zhou 	ASSERT(!(softmac->smac_flags & SOFTMAC_ATTACH_DONE));
1375d460eafSCathy Zhou 	ASSERT(softmac->smac_hold_cnt == 0);
1385d460eafSCathy Zhou 	ASSERT(softmac->smac_attachok_cnt == 0);
1395d460eafSCathy Zhou 	ASSERT(softmac->smac_mh == NULL);
1405d460eafSCathy Zhou 	ASSERT(softmac->smac_softmac[0] == NULL &&
1415d460eafSCathy Zhou 	    softmac->smac_softmac[1] == NULL);
1425d460eafSCathy Zhou 	ASSERT(softmac->smac_state == SOFTMAC_INITIALIZED);
1435d460eafSCathy Zhou 	ASSERT(softmac->smac_lower == NULL);
1445d460eafSCathy Zhou 	ASSERT(softmac->smac_active == B_FALSE);
1455d460eafSCathy Zhou 	ASSERT(softmac->smac_nactive == 0);
1465d460eafSCathy Zhou 	ASSERT(list_is_empty(&softmac->smac_sup_list));
1475d460eafSCathy Zhou 
1485d460eafSCathy Zhou 	list_destroy(&softmac->smac_sup_list);
1495d460eafSCathy Zhou 	mutex_destroy(&softmac->smac_mutex);
1505d460eafSCathy Zhou 	mutex_destroy(&softmac->smac_active_mutex);
1515d460eafSCathy Zhou 	mutex_destroy(&softmac->smac_fp_mutex);
1525d460eafSCathy Zhou 	cv_destroy(&softmac->smac_cv);
1535d460eafSCathy Zhou 	cv_destroy(&softmac->smac_fp_cv);
1545d460eafSCathy Zhou }
1555d460eafSCathy Zhou 
156d62bc4baSyz147064 void
157d62bc4baSyz147064 softmac_init()
158d62bc4baSyz147064 {
159d62bc4baSyz147064 	softmac_hash = mod_hash_create_extended("softmac_hash",
160d62bc4baSyz147064 	    SOFTMAC_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
161d62bc4baSyz147064 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
162d62bc4baSyz147064 
163d62bc4baSyz147064 	rw_init(&softmac_hash_lock, NULL, RW_DEFAULT, NULL);
164da14cebeSEric Cheng 	mutex_init(&smac_global_lock, NULL, MUTEX_DRIVER, NULL);
165da14cebeSEric Cheng 	cv_init(&smac_global_cv, NULL, CV_DRIVER, NULL);
1665d460eafSCathy Zhou 
1675d460eafSCathy Zhou 	softmac_cachep = kmem_cache_create("softmac_cache",
1685d460eafSCathy Zhou 	    sizeof (softmac_t), 0, softmac_constructor,
1695d460eafSCathy Zhou 	    softmac_destructor, NULL, NULL, NULL, 0);
1705d460eafSCathy Zhou 	ASSERT(softmac_cachep != NULL);
1715d460eafSCathy Zhou 	softmac_fp_init();
172d62bc4baSyz147064 }
173d62bc4baSyz147064 
174d62bc4baSyz147064 void
175d62bc4baSyz147064 softmac_fini()
176d62bc4baSyz147064 {
1775d460eafSCathy Zhou 	softmac_fp_fini();
1785d460eafSCathy Zhou 	kmem_cache_destroy(softmac_cachep);
179d62bc4baSyz147064 	rw_destroy(&softmac_hash_lock);
180d62bc4baSyz147064 	mod_hash_destroy_hash(softmac_hash);
181da14cebeSEric Cheng 	mutex_destroy(&smac_global_lock);
182da14cebeSEric Cheng 	cv_destroy(&smac_global_cv);
183d62bc4baSyz147064 }
184d62bc4baSyz147064 
185d62bc4baSyz147064 /* ARGSUSED */
186d62bc4baSyz147064 static uint_t
187d62bc4baSyz147064 softmac_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
188d62bc4baSyz147064 {
189d62bc4baSyz147064 	boolean_t *pexist = arg;
190d62bc4baSyz147064 
191d62bc4baSyz147064 	*pexist = B_TRUE;
192d62bc4baSyz147064 	return (MH_WALK_TERMINATE);
193d62bc4baSyz147064 }
194d62bc4baSyz147064 
195d62bc4baSyz147064 boolean_t
196d62bc4baSyz147064 softmac_busy()
197d62bc4baSyz147064 {
198d62bc4baSyz147064 	boolean_t exist = B_FALSE;
199d62bc4baSyz147064 
200d62bc4baSyz147064 	rw_enter(&softmac_hash_lock, RW_READER);
201d62bc4baSyz147064 	mod_hash_walk(softmac_hash, softmac_exist, &exist);
202d62bc4baSyz147064 	rw_exit(&softmac_hash_lock);
203d62bc4baSyz147064 	return (exist);
204d62bc4baSyz147064 }
205d62bc4baSyz147064 
206d62bc4baSyz147064 /*
207da14cebeSEric Cheng  *
208da14cebeSEric Cheng  * softmac_create() is called for each minor node during the post-attach of
209d62bc4baSyz147064  * each DDI_NT_NET device instance.  Note that it is possible that a device
210d62bc4baSyz147064  * instance has two minor nodes (DLPI style-1 and style-2), so that for that
211d62bc4baSyz147064  * specific device, softmac_create() could be called twice.
212d62bc4baSyz147064  *
213d62bc4baSyz147064  * A softmac_t is used to track each DDI_NT_NET device, and a softmac_dev_t
214d62bc4baSyz147064  * is created to track each minor node.
215d62bc4baSyz147064  *
216d62bc4baSyz147064  * For each minor node of a legacy device, a taskq is started to finish
217d62bc4baSyz147064  * softmac_mac_register(), which will finish the rest of work (see comments
218d62bc4baSyz147064  * above softmac_mac_register()).
219da14cebeSEric Cheng  *
220da14cebeSEric Cheng  *			softmac state machine
221da14cebeSEric Cheng  * --------------------------------------------------------------------------
222da14cebeSEric Cheng  * OLD STATE		EVENT					NEW STATE
223da14cebeSEric Cheng  * --------------------------------------------------------------------------
224da14cebeSEric Cheng  * UNINIT		attach of 1st minor node 		ATTACH_INPROG
225da14cebeSEric Cheng  * okcnt = 0		net_postattach -> softmac_create	okcnt = 1
226da14cebeSEric Cheng  *
227da14cebeSEric Cheng  * ATTACH_INPROG	attach of 2nd minor node (GLDv3)	ATTACH_DONE
228da14cebeSEric Cheng  * okcnt = 1		net_postattach -> softmac_create	okcnt = 2
229da14cebeSEric Cheng  *
230da14cebeSEric Cheng  * ATTACH_INPROG	attach of 2nd minor node (legacy)	ATTACH_INPROG
231da14cebeSEric Cheng  * okcnt = 1		net_postattach -> softmac_create	okcnt = 2
232da14cebeSEric Cheng  *			schedule softmac_mac_register
233da14cebeSEric Cheng  *
234da14cebeSEric Cheng  * ATTACH_INPROG	legacy device node			ATTACH_DONE
235da14cebeSEric Cheng  * okcnt = 2		softmac_mac_register			okcnt = 2
236da14cebeSEric Cheng  *
237da14cebeSEric Cheng  * ATTACH_DONE		detach of 1st minor node		DETACH_INPROG
238da14cebeSEric Cheng  * okcnt = 2		(success)				okcnt = 1
239da14cebeSEric Cheng  *
240da14cebeSEric Cheng  * DETACH_INPROG	detach of 2nd minor node		UNINIT (or free)
241da14cebeSEric Cheng  * okcnt = 1		(success)				okcnt = 0
242da14cebeSEric Cheng  *
243da14cebeSEric Cheng  * ATTACH_DONE		detach failure				state unchanged
244da14cebeSEric Cheng  * DETACH_INPROG						left = okcnt
245da14cebeSEric Cheng  *
246da14cebeSEric Cheng  * DETACH_INPROG	reattach				ATTACH_INPROG
247da14cebeSEric Cheng  * okcnt = 0,1		net_postattach -> softmac_create
248da14cebeSEric Cheng  *
249da14cebeSEric Cheng  * ATTACH_DONE		reattach				ATTACH_DONE
250da14cebeSEric Cheng  * left != 0		net_postattach -> softmac_create	left = 0
251da14cebeSEric Cheng  *
252da14cebeSEric Cheng  * Abbreviation notes:
253da14cebeSEric Cheng  * states have SOFTMAC_ prefix,
254da14cebeSEric Cheng  * okcnt - softmac_attach_okcnt,
255da14cebeSEric Cheng  * left - softmac_attached_left
256d62bc4baSyz147064  */
257da14cebeSEric Cheng 
258da14cebeSEric Cheng #ifdef DEBUG
259da14cebeSEric Cheng void
260da14cebeSEric Cheng softmac_state_verify(softmac_t *softmac)
261da14cebeSEric Cheng {
262da14cebeSEric Cheng 	ASSERT(MUTEX_HELD(&softmac->smac_mutex));
263da14cebeSEric Cheng 
264da14cebeSEric Cheng 	/*
265da14cebeSEric Cheng 	 * There are at most 2 minor nodes, one per DLPI style
266da14cebeSEric Cheng 	 */
267da14cebeSEric Cheng 	ASSERT(softmac->smac_cnt <= 2 && softmac->smac_attachok_cnt <= 2);
268da14cebeSEric Cheng 
269da14cebeSEric Cheng 	/*
270da14cebeSEric Cheng 	 * The smac_attachok_cnt represents the number of attaches i.e. the
271da14cebeSEric Cheng 	 * number of times net_postattach -> softmac_create() has been called
272da14cebeSEric Cheng 	 * for a device instance.
273da14cebeSEric Cheng 	 */
274da14cebeSEric Cheng 	ASSERT(softmac->smac_attachok_cnt == SMAC_NONZERO_NODECNT(softmac));
275da14cebeSEric Cheng 
276da14cebeSEric Cheng 	/*
277da14cebeSEric Cheng 	 * softmac_create (or softmac_mac_register) ->  softmac_create_datalink
278da14cebeSEric Cheng 	 * happens only after all minor nodes have been attached
279da14cebeSEric Cheng 	 */
280da14cebeSEric Cheng 	ASSERT(softmac->smac_state != SOFTMAC_ATTACH_DONE ||
281da14cebeSEric Cheng 	    softmac->smac_attachok_cnt == softmac->smac_cnt);
282da14cebeSEric Cheng 
283da14cebeSEric Cheng 	if (softmac->smac_attachok_cnt == 0) {
284da14cebeSEric Cheng 		ASSERT(softmac->smac_state == SOFTMAC_UNINIT);
285da14cebeSEric Cheng 		ASSERT(softmac->smac_mh == NULL);
286da14cebeSEric Cheng 	} else if (softmac->smac_attachok_cnt < softmac->smac_cnt) {
287da14cebeSEric Cheng 		ASSERT(softmac->smac_state == SOFTMAC_ATTACH_INPROG ||
288da14cebeSEric Cheng 		    softmac->smac_state == SOFTMAC_DETACH_INPROG);
289da14cebeSEric Cheng 		ASSERT(softmac->smac_mh == NULL);
290da14cebeSEric Cheng 	} else {
291da14cebeSEric Cheng 		/*
292da14cebeSEric Cheng 		 * In the stable condition the state whould be
293da14cebeSEric Cheng 		 * SOFTMAC_ATTACH_DONE. But there is a small transient window
294da14cebeSEric Cheng 		 * in softmac_destroy where we change the state to
295da14cebeSEric Cheng 		 * SOFTMAC_DETACH_INPROG and drop the lock before doing
296da14cebeSEric Cheng 		 * the link destroy
297da14cebeSEric Cheng 		 */
298da14cebeSEric Cheng 		ASSERT(softmac->smac_attachok_cnt == softmac->smac_cnt);
299da14cebeSEric Cheng 		ASSERT(softmac->smac_state != SOFTMAC_UNINIT);
300da14cebeSEric Cheng 	}
301da14cebeSEric Cheng 	if (softmac->smac_mh != NULL)
302da14cebeSEric Cheng 		ASSERT(softmac->smac_attachok_cnt == softmac->smac_cnt);
303da14cebeSEric Cheng }
304da14cebeSEric Cheng #endif
305da14cebeSEric Cheng 
306da14cebeSEric Cheng #ifdef DEBUG
307da14cebeSEric Cheng #define	SOFTMAC_STATE_VERIFY(softmac)	softmac_state_verify(softmac)
308da14cebeSEric Cheng #else
309da14cebeSEric Cheng #define	SOFTMAC_STATE_VERIFY(softmac)
310da14cebeSEric Cheng #endif
311da14cebeSEric Cheng 
312d62bc4baSyz147064 int
313d62bc4baSyz147064 softmac_create(dev_info_t *dip, dev_t dev)
314d62bc4baSyz147064 {
315d62bc4baSyz147064 	char		devname[MAXNAMELEN];
316d62bc4baSyz147064 	softmac_t	*softmac;
317d62bc4baSyz147064 	softmac_dev_t	*softmac_dev = NULL;
318d62bc4baSyz147064 	int		index;
319d62bc4baSyz147064 	int		ppa, err = 0;
320d62bc4baSyz147064 
321d62bc4baSyz147064 	/*
322d62bc4baSyz147064 	 * Force the softmac driver to be attached.
323d62bc4baSyz147064 	 */
324d62bc4baSyz147064 	if (i_ddi_attach_pseudo_node(SOFTMAC_DEV_NAME) == NULL) {
325d62bc4baSyz147064 		cmn_err(CE_WARN, "softmac_create:softmac attach fails");
326d62bc4baSyz147064 		return (ENXIO);
327d62bc4baSyz147064 	}
328d62bc4baSyz147064 
32961af1958SGarrett D'Amore 	if (GLDV3_DRV(ddi_driver_major(dip))) {
33061af1958SGarrett D'Amore 		minor_t minor = getminor(dev);
33161af1958SGarrett D'Amore 		/*
33261af1958SGarrett D'Amore 		 * For GLDv3, we don't care about the DLPI style 2
33361af1958SGarrett D'Amore 		 * compatibility node.  (We know that all such devices
33461af1958SGarrett D'Amore 		 * have style 1 nodes.)
33561af1958SGarrett D'Amore 		 */
33661af1958SGarrett D'Amore 		if ((strcmp(ddi_driver_name(dip), "clone") == 0) ||
33761af1958SGarrett D'Amore 		    (getmajor(dev) == ddi_name_to_major("clone")) ||
33861af1958SGarrett D'Amore 		    (minor == 0)) {
33961af1958SGarrett D'Amore 			return (0);
34061af1958SGarrett D'Amore 		}
341d62bc4baSyz147064 
342d62bc4baSyz147064 		/*
34361af1958SGarrett D'Amore 		 * Likewise, we know that the minor number for DLPI style 1
34461af1958SGarrett D'Amore 		 * nodes is constrained to a maximum value.
345d62bc4baSyz147064 		 */
34661af1958SGarrett D'Amore 		if (minor >= DLS_MAX_MINOR) {
347d62bc4baSyz147064 			return (ENOTSUP);
348d62bc4baSyz147064 		}
34961af1958SGarrett D'Amore 		/*
35061af1958SGarrett D'Amore 		 * Otherwise we can decode the instance from the minor number,
35161af1958SGarrett D'Amore 		 * which allows for situations with multiple mac instances
35261af1958SGarrett D'Amore 		 * for a single dev_info_t.
35361af1958SGarrett D'Amore 		 */
35461af1958SGarrett D'Amore 		ppa = DLS_MINOR2INST(minor);
35561af1958SGarrett D'Amore 	} else {
35661af1958SGarrett D'Amore 		/*
35761af1958SGarrett D'Amore 		 * For legacy drivers, we just have to limit them to
35861af1958SGarrett D'Amore 		 * two minor nodes, one style 1 and one style 2, and
35961af1958SGarrett D'Amore 		 * we assume the ddi_get_instance() is the PPA.
36061af1958SGarrett D'Amore 		 * Drivers that need more flexibility should be ported
36161af1958SGarrett D'Amore 		 * to GLDv3.
36261af1958SGarrett D'Amore 		 */
36361af1958SGarrett D'Amore 		ppa = ddi_get_instance(dip);
36461af1958SGarrett D'Amore 		if (i_ddi_minor_node_count(dip, DDI_NT_NET) > 2) {
36561af1958SGarrett D'Amore 			cmn_err(CE_WARN, "%s has more than 2 minor nodes; "
36661af1958SGarrett D'Amore 			    "unsupported", devname);
36761af1958SGarrett D'Amore 			return (ENOTSUP);
36861af1958SGarrett D'Amore 		}
36961af1958SGarrett D'Amore 	}
37061af1958SGarrett D'Amore 
37161af1958SGarrett D'Amore 	(void) snprintf(devname, MAXNAMELEN, "%s%d", ddi_driver_name(dip), ppa);
372d62bc4baSyz147064 
373d62bc4baSyz147064 	/*
374d62bc4baSyz147064 	 * Check whether the softmac for the specified device already exists
375d62bc4baSyz147064 	 */
376d62bc4baSyz147064 	rw_enter(&softmac_hash_lock, RW_WRITER);
3775d460eafSCathy Zhou 	if ((mod_hash_find(softmac_hash, (mod_hash_key_t)devname,
378d62bc4baSyz147064 	    (mod_hash_val_t *)&softmac)) != 0) {
379d62bc4baSyz147064 
3805d460eafSCathy Zhou 		softmac = kmem_cache_alloc(softmac_cachep, KM_SLEEP);
381d62bc4baSyz147064 		(void) strlcpy(softmac->smac_devname, devname, MAXNAMELEN);
3825d460eafSCathy Zhou 
383d62bc4baSyz147064 		err = mod_hash_insert(softmac_hash,
384d62bc4baSyz147064 		    (mod_hash_key_t)softmac->smac_devname,
385d62bc4baSyz147064 		    (mod_hash_val_t)softmac);
386d62bc4baSyz147064 		ASSERT(err == 0);
387da14cebeSEric Cheng 		mutex_enter(&smac_global_lock);
388da14cebeSEric Cheng 		cv_broadcast(&smac_global_cv);
389da14cebeSEric Cheng 		mutex_exit(&smac_global_lock);
390d62bc4baSyz147064 	}
391d62bc4baSyz147064 
392d62bc4baSyz147064 	mutex_enter(&softmac->smac_mutex);
393da14cebeSEric Cheng 	SOFTMAC_STATE_VERIFY(softmac);
394da14cebeSEric Cheng 	if (softmac->smac_state != SOFTMAC_ATTACH_DONE)
395da14cebeSEric Cheng 		softmac->smac_state = SOFTMAC_ATTACH_INPROG;
396d62bc4baSyz147064 	if (softmac->smac_attachok_cnt == 0) {
397d62bc4baSyz147064 		/*
398d62bc4baSyz147064 		 * Initialize the softmac if this is the post-attach of the
399d62bc4baSyz147064 		 * first minor node.
400d62bc4baSyz147064 		 */
401d62bc4baSyz147064 		softmac->smac_flags = 0;
402d62bc4baSyz147064 		softmac->smac_umajor = ddi_driver_major(dip);
403d62bc4baSyz147064 		softmac->smac_uppa = ppa;
404d62bc4baSyz147064 
405d62bc4baSyz147064 		/*
40661af1958SGarrett D'Amore 		 * For GLDv3, we ignore the style 2 node (see the logic
40761af1958SGarrett D'Amore 		 * above on that), and we should have exactly one attach
40861af1958SGarrett D'Amore 		 * per MAC instance (possibly more than one per dev_info_t).
409d62bc4baSyz147064 		 */
410d62bc4baSyz147064 		if (GLDV3_DRV(ddi_driver_major(dip))) {
411d62bc4baSyz147064 			softmac->smac_flags |= SOFTMAC_GLDV3;
41261af1958SGarrett D'Amore 			softmac->smac_cnt = 1;
413d62bc4baSyz147064 		} else {
414d62bc4baSyz147064 			softmac->smac_cnt =
415d62bc4baSyz147064 			    i_ddi_minor_node_count(dip, DDI_NT_NET);
416d62bc4baSyz147064 		}
417d62bc4baSyz147064 	}
418d62bc4baSyz147064 
419d62bc4baSyz147064 	index = (getmajor(dev) == ddi_name_to_major("clone"));
420d62bc4baSyz147064 	if (softmac->smac_softmac[index] != NULL) {
421d62bc4baSyz147064 		/*
422da14cebeSEric Cheng 		 * This is possible if the post_attach() is called after
423da14cebeSEric Cheng 		 * pre_detach() fails. This seems to be a defect of the DACF
424da14cebeSEric Cheng 		 * framework. We work around it by using a smac_attached_left
425da14cebeSEric Cheng 		 * field that tracks this
426d62bc4baSyz147064 		 */
427da14cebeSEric Cheng 		ASSERT(softmac->smac_attached_left != 0);
428d62bc4baSyz147064 		softmac->smac_attached_left--;
429d62bc4baSyz147064 		mutex_exit(&softmac->smac_mutex);
430d62bc4baSyz147064 		rw_exit(&softmac_hash_lock);
431d62bc4baSyz147064 		return (0);
432da14cebeSEric Cheng 
433d62bc4baSyz147064 	}
434d62bc4baSyz147064 	mutex_exit(&softmac->smac_mutex);
435d62bc4baSyz147064 	rw_exit(&softmac_hash_lock);
436d62bc4baSyz147064 
437d62bc4baSyz147064 	softmac_dev = kmem_zalloc(sizeof (softmac_dev_t), KM_SLEEP);
438d62bc4baSyz147064 	softmac_dev->sd_dev = dev;
439d62bc4baSyz147064 
440da14cebeSEric Cheng 	mutex_enter(&softmac->smac_mutex);
441da14cebeSEric Cheng 	softmac->smac_softmac[index] = softmac_dev;
442d62bc4baSyz147064 	/*
443d62bc4baSyz147064 	 * Continue to register the mac and create the datalink only when all
444d62bc4baSyz147064 	 * the minor nodes are attached.
445d62bc4baSyz147064 	 */
446d62bc4baSyz147064 	if (++softmac->smac_attachok_cnt != softmac->smac_cnt) {
447d62bc4baSyz147064 		mutex_exit(&softmac->smac_mutex);
448d62bc4baSyz147064 		return (0);
449d62bc4baSyz147064 	}
450d62bc4baSyz147064 
451d62bc4baSyz147064 	/*
4520dc974a9SCathy Zhou 	 * All of the minor nodes have been attached; start a taskq
453da14cebeSEric Cheng 	 * to do the rest of the work.  We use a taskq instead of
4540dc974a9SCathy Zhou 	 * doing the work here because:
455d62bc4baSyz147064 	 *
456da14cebeSEric Cheng 	 * We could be called as a result of a open() system call
4570dc974a9SCathy Zhou 	 * where spec_open() already SLOCKED the snode. Using a taskq
4580dc974a9SCathy Zhou 	 * sidesteps the risk that our ldi_open_by_dev() call would
4590dc974a9SCathy Zhou 	 * deadlock trying to set SLOCKED on the snode again.
4600dc974a9SCathy Zhou 	 *
461da14cebeSEric Cheng 	 * The devfs design requires that the downcalls don't use any
462da14cebeSEric Cheng 	 * interruptible cv_wait which happens when we do door upcalls.
463da14cebeSEric Cheng 	 * Otherwise the downcalls which may be holding devfs resources
464da14cebeSEric Cheng 	 * may cause a deadlock if the thread is stopped. Also we need to make
465da14cebeSEric Cheng 	 * sure these downcalls into softmac_create or softmac_destroy
466da14cebeSEric Cheng 	 * don't cv_wait on any devfs related condition. Thus softmac_destroy
467da14cebeSEric Cheng 	 * returns EBUSY if the asynchronous threads started in softmac_create
468da14cebeSEric Cheng 	 * haven't finished.
469d62bc4baSyz147064 	 */
47065041820SCathy Zhou 	(void) taskq_dispatch(system_taskq, softmac_create_task,
47165041820SCathy Zhou 	    softmac, TQ_SLEEP);
472d62bc4baSyz147064 	mutex_exit(&softmac->smac_mutex);
473d62bc4baSyz147064 	return (0);
474d62bc4baSyz147064 }
475d62bc4baSyz147064 
476d62bc4baSyz147064 static boolean_t
477d62bc4baSyz147064 softmac_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
478d62bc4baSyz147064 {
479d62bc4baSyz147064 	softmac_t *softmac = arg;
480d62bc4baSyz147064 
481d62bc4baSyz147064 	if (!(softmac->smac_capab_flags & cap))
482d62bc4baSyz147064 		return (B_FALSE);
483d62bc4baSyz147064 
484d62bc4baSyz147064 	switch (cap) {
485d62bc4baSyz147064 	case MAC_CAPAB_HCKSUM: {
486d62bc4baSyz147064 		uint32_t *txflags = cap_data;
487d62bc4baSyz147064 
488d62bc4baSyz147064 		*txflags = softmac->smac_hcksum_txflags;
489d62bc4baSyz147064 		break;
490d62bc4baSyz147064 	}
491d62bc4baSyz147064 	case MAC_CAPAB_LEGACY: {
492d62bc4baSyz147064 		mac_capab_legacy_t *legacy = cap_data;
493d62bc4baSyz147064 
4945d460eafSCathy Zhou 		/*
4955d460eafSCathy Zhou 		 * The caller is not interested in the details.
4965d460eafSCathy Zhou 		 */
4975d460eafSCathy Zhou 		if (legacy == NULL)
4985d460eafSCathy Zhou 			break;
4995d460eafSCathy Zhou 
500d62bc4baSyz147064 		legacy->ml_unsup_note = ~softmac->smac_notifications &
501d62bc4baSyz147064 		    (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_SPEED);
5025d460eafSCathy Zhou 		legacy->ml_active_set = softmac_active_set;
5035d460eafSCathy Zhou 		legacy->ml_active_clear = softmac_active_clear;
5045d460eafSCathy Zhou 		legacy->ml_fastpath_disable = softmac_fastpath_disable;
5055d460eafSCathy Zhou 		legacy->ml_fastpath_enable = softmac_fastpath_enable;
506d62bc4baSyz147064 		legacy->ml_dev = makedevice(softmac->smac_umajor,
507d62bc4baSyz147064 		    softmac->smac_uppa + 1);
508d62bc4baSyz147064 		break;
509d62bc4baSyz147064 	}
510d62bc4baSyz147064 
511d62bc4baSyz147064 	/*
512d62bc4baSyz147064 	 * For the capabilities below, there's nothing for us to fill in;
513d62bc4baSyz147064 	 * simply return B_TRUE if we support it.
514d62bc4baSyz147064 	 */
515d62bc4baSyz147064 	case MAC_CAPAB_NO_ZCOPY:
516d62bc4baSyz147064 	case MAC_CAPAB_NO_NATIVEVLAN:
517d62bc4baSyz147064 	default:
518d62bc4baSyz147064 		break;
519d62bc4baSyz147064 	}
520d62bc4baSyz147064 	return (B_TRUE);
521d62bc4baSyz147064 }
522d62bc4baSyz147064 
523d62bc4baSyz147064 static int
524d62bc4baSyz147064 softmac_update_info(softmac_t *softmac, datalink_id_t *linkidp)
525d62bc4baSyz147064 {
526d62bc4baSyz147064 	datalink_id_t	linkid = DATALINK_INVALID_LINKID;
527d62bc4baSyz147064 	uint32_t	media;
528d62bc4baSyz147064 	int		err;
529d62bc4baSyz147064 
530d62bc4baSyz147064 	if ((err = dls_mgmt_update(softmac->smac_devname, softmac->smac_media,
531d62bc4baSyz147064 	    softmac->smac_flags & SOFTMAC_NOSUPP, &media, &linkid)) == 0) {
532d62bc4baSyz147064 		*linkidp = linkid;
533d62bc4baSyz147064 	}
534d62bc4baSyz147064 
535d62bc4baSyz147064 	if (err == EEXIST) {
536d62bc4baSyz147064 		/*
537d62bc4baSyz147064 		 * There is a link name conflict.  Either:
538d62bc4baSyz147064 		 *
539d62bc4baSyz147064 		 * - An existing link with the same device name with a
540d62bc4baSyz147064 		 *   different media type from of the given type.
541d62bc4baSyz147064 		 *   Mark this link back to persistent only; or
542d62bc4baSyz147064 		 *
543d62bc4baSyz147064 		 * - We cannot assign the "suggested" name because
544d62bc4baSyz147064 		 *   GLDv3 and therefore vanity naming is not supported
545d62bc4baSyz147064 		 *   for this link type. Delete this link's <link name,
546d62bc4baSyz147064 		 *   linkid> mapping.
547d62bc4baSyz147064 		 */
548d62bc4baSyz147064 		if (media != softmac->smac_media) {
549d62bc4baSyz147064 			cmn_err(CE_WARN, "%s device %s conflicts with "
550d62bc4baSyz147064 			    "existing %s device %s.",
551d62bc4baSyz147064 			    dl_mactypestr(softmac->smac_media),
552d62bc4baSyz147064 			    softmac->smac_devname, dl_mactypestr(media),
553d62bc4baSyz147064 			    softmac->smac_devname);
554d62bc4baSyz147064 			(void) dls_mgmt_destroy(linkid, B_FALSE);
555d62bc4baSyz147064 		} else {
556d62bc4baSyz147064 			cmn_err(CE_WARN, "link name %s is already in-use.",
557d62bc4baSyz147064 			    softmac->smac_devname);
558d62bc4baSyz147064 			(void) dls_mgmt_destroy(linkid, B_TRUE);
559d62bc4baSyz147064 		}
560d62bc4baSyz147064 
561d62bc4baSyz147064 		cmn_err(CE_WARN, "%s device might not be available "
562d62bc4baSyz147064 		    "for use.", softmac->smac_devname);
563d62bc4baSyz147064 		cmn_err(CE_WARN, "See dladm(1M) for more information.");
564d62bc4baSyz147064 	}
565d62bc4baSyz147064 
566d62bc4baSyz147064 	return (err);
567d62bc4baSyz147064 }
568d62bc4baSyz147064 
569d62bc4baSyz147064 /*
570d62bc4baSyz147064  * This function:
571d62bc4baSyz147064  * 1. provides the link's media type to dlmgmtd.
572d62bc4baSyz147064  * 2. creates the GLDv3 datalink if the media type is supported by GLDv3.
573d62bc4baSyz147064  */
574d62bc4baSyz147064 static int
575d62bc4baSyz147064 softmac_create_datalink(softmac_t *softmac)
576d62bc4baSyz147064 {
577d62bc4baSyz147064 	datalink_id_t	linkid = DATALINK_INVALID_LINKID;
578d62bc4baSyz147064 	int		err;
579d62bc4baSyz147064 
580d62bc4baSyz147064 	/*
5810dc974a9SCathy Zhou 	 * Inform dlmgmtd of this link so that softmac_hold_device() is able
5820dc974a9SCathy Zhou 	 * to know the existence of this link. If this failed with EBADF,
5830dc974a9SCathy Zhou 	 * it might be because dlmgmtd was not started in time (e.g.,
5840dc974a9SCathy Zhou 	 * diskless boot); ignore the failure and continue to create
5850dc974a9SCathy Zhou 	 * the GLDv3 datalink if needed.
586d62bc4baSyz147064 	 */
5870dc974a9SCathy Zhou 	err = dls_mgmt_create(softmac->smac_devname,
5880dc974a9SCathy Zhou 	    makedevice(softmac->smac_umajor, softmac->smac_uppa + 1),
5890dc974a9SCathy Zhou 	    DATALINK_CLASS_PHYS, DL_OTHER, B_TRUE, &linkid);
5900dc974a9SCathy Zhou 	if (err != 0 && err != EBADF)
5910dc974a9SCathy Zhou 		return (err);
5920dc974a9SCathy Zhou 
5930dc974a9SCathy Zhou 	/*
5940dc974a9SCathy Zhou 	 * Provide the media type of the physical link to dlmgmtd.
5950dc974a9SCathy Zhou 	 */
5960dc974a9SCathy Zhou 	if ((err != EBADF) &&
5970dc974a9SCathy Zhou 	    ((err = softmac_update_info(softmac, &linkid)) != 0)) {
598d62bc4baSyz147064 		return (err);
599d62bc4baSyz147064 	}
600d62bc4baSyz147064 
601d62bc4baSyz147064 	/*
602d62bc4baSyz147064 	 * Create the GLDv3 datalink.
603d62bc4baSyz147064 	 */
6042b24ab6bSSebastien Roy 	if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) {
6052b24ab6bSSebastien Roy 		err = dls_devnet_create(softmac->smac_mh, linkid,
6062b24ab6bSSebastien Roy 		    crgetzoneid(CRED()));
6072b24ab6bSSebastien Roy 		if (err != 0) {
608d62bc4baSyz147064 			cmn_err(CE_WARN, "dls_devnet_create failed for %s",
609d62bc4baSyz147064 			    softmac->smac_devname);
610d62bc4baSyz147064 			return (err);
611d62bc4baSyz147064 		}
6122b24ab6bSSebastien Roy 	}
613d62bc4baSyz147064 
614da14cebeSEric Cheng 	if (linkid == DATALINK_INVALID_LINKID) {
615da14cebeSEric Cheng 		mutex_enter(&softmac->smac_mutex);
616d62bc4baSyz147064 		softmac->smac_flags |= SOFTMAC_NEED_RECREATE;
617da14cebeSEric Cheng 		mutex_exit(&softmac->smac_mutex);
618da14cebeSEric Cheng 	}
619d62bc4baSyz147064 
620d62bc4baSyz147064 	return (0);
621d62bc4baSyz147064 }
622d62bc4baSyz147064 
6230dc974a9SCathy Zhou static void
6240dc974a9SCathy Zhou softmac_create_task(void *arg)
6250dc974a9SCathy Zhou {
6260dc974a9SCathy Zhou 	softmac_t	*softmac = arg;
6270dc974a9SCathy Zhou 	mac_handle_t	mh;
6280dc974a9SCathy Zhou 	int		err;
6290dc974a9SCathy Zhou 
6300dc974a9SCathy Zhou 	if (!GLDV3_DRV(softmac->smac_umajor)) {
6310dc974a9SCathy Zhou 		softmac_mac_register(softmac);
6320dc974a9SCathy Zhou 		return;
6330dc974a9SCathy Zhou 	}
6340dc974a9SCathy Zhou 
6350dc974a9SCathy Zhou 	if ((err = mac_open(softmac->smac_devname, &mh)) != 0)
6360dc974a9SCathy Zhou 		goto done;
6370dc974a9SCathy Zhou 
6380dc974a9SCathy Zhou 	mutex_enter(&softmac->smac_mutex);
6390dc974a9SCathy Zhou 	softmac->smac_media = (mac_info(mh))->mi_nativemedia;
6400dc974a9SCathy Zhou 	softmac->smac_mh = mh;
641da14cebeSEric Cheng 	mutex_exit(&softmac->smac_mutex);
6420dc974a9SCathy Zhou 
6430dc974a9SCathy Zhou 	/*
6440dc974a9SCathy Zhou 	 * We can safely release the reference on the mac because
6450dc974a9SCathy Zhou 	 * this mac will only be unregistered and destroyed when
6460dc974a9SCathy Zhou 	 * the device detaches, and the softmac will be destroyed
6470dc974a9SCathy Zhou 	 * before then (in the pre-detach routine of the device).
6480dc974a9SCathy Zhou 	 */
6490dc974a9SCathy Zhou 	mac_close(mh);
6500dc974a9SCathy Zhou 
6510dc974a9SCathy Zhou 	/*
6520dc974a9SCathy Zhou 	 * Create the GLDv3 datalink for this mac.
6530dc974a9SCathy Zhou 	 */
6540dc974a9SCathy Zhou 	err = softmac_create_datalink(softmac);
6550dc974a9SCathy Zhou 
6560dc974a9SCathy Zhou done:
65765041820SCathy Zhou 	mutex_enter(&softmac->smac_mutex);
65865041820SCathy Zhou 	if (err != 0)
659da14cebeSEric Cheng 		softmac->smac_mh = NULL;
6600dc974a9SCathy Zhou 	softmac->smac_attacherr = err;
661da14cebeSEric Cheng 	softmac->smac_state = SOFTMAC_ATTACH_DONE;
6620dc974a9SCathy Zhou 	cv_broadcast(&softmac->smac_cv);
6630dc974a9SCathy Zhou 	mutex_exit(&softmac->smac_mutex);
6640dc974a9SCathy Zhou }
6650dc974a9SCathy Zhou 
666d62bc4baSyz147064 /*
667d62bc4baSyz147064  * This function is only called for legacy devices. It:
668d62bc4baSyz147064  * 1. registers the MAC for the legacy devices whose media type is supported
669d62bc4baSyz147064  *    by the GLDv3 framework.
670d62bc4baSyz147064  * 2. creates the GLDv3 datalink if the media type is supported by GLDv3.
671d62bc4baSyz147064  */
672d62bc4baSyz147064 static void
6730dc974a9SCathy Zhou softmac_mac_register(softmac_t *softmac)
674d62bc4baSyz147064 {
675d62bc4baSyz147064 	softmac_dev_t	*softmac_dev;
676d62bc4baSyz147064 	dev_t		dev;
677d62bc4baSyz147064 	ldi_handle_t	lh = NULL;
678d62bc4baSyz147064 	ldi_ident_t	li = NULL;
679d62bc4baSyz147064 	int		index;
680d62bc4baSyz147064 	boolean_t	native_vlan = B_FALSE;
681d62bc4baSyz147064 	int		err;
682d62bc4baSyz147064 
683d62bc4baSyz147064 	/*
684d62bc4baSyz147064 	 * Note that we do not need any locks to access this softmac pointer,
685d62bc4baSyz147064 	 * as softmac_destroy() will wait until this function is called.
686d62bc4baSyz147064 	 */
687d62bc4baSyz147064 	ASSERT(softmac != NULL);
688da14cebeSEric Cheng 	ASSERT(softmac->smac_state == SOFTMAC_ATTACH_INPROG &&
689da14cebeSEric Cheng 	    softmac->smac_attachok_cnt == softmac->smac_cnt);
690d62bc4baSyz147064 
691d62bc4baSyz147064 	if ((err = ldi_ident_from_dip(softmac_dip, &li)) != 0) {
692d62bc4baSyz147064 		mutex_enter(&softmac->smac_mutex);
693d62bc4baSyz147064 		goto done;
694d62bc4baSyz147064 	}
695d62bc4baSyz147064 
696d62bc4baSyz147064 	/*
697d62bc4baSyz147064 	 * Determine whether this legacy device support VLANs by opening
698d62bc4baSyz147064 	 * the style-2 device node (if it exists) and attaching to a VLAN
699d62bc4baSyz147064 	 * PPA (1000 + ppa).
700d62bc4baSyz147064 	 */
701d62bc4baSyz147064 	dev = makedevice(ddi_name_to_major("clone"), softmac->smac_umajor);
702d62bc4baSyz147064 	err = ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh, li);
703d62bc4baSyz147064 	if (err == 0) {
704d62bc4baSyz147064 		if (dl_attach(lh, softmac->smac_uppa + 1 * 1000, NULL) == 0)
705d62bc4baSyz147064 			native_vlan = B_TRUE;
706d62bc4baSyz147064 		(void) ldi_close(lh, FREAD|FWRITE, kcred);
707d62bc4baSyz147064 	}
708d62bc4baSyz147064 
709d62bc4baSyz147064 	err = EINVAL;
710d62bc4baSyz147064 	for (index = 0; index < 2; index++) {
711d62bc4baSyz147064 		dl_info_ack_t	dlia;
712d62bc4baSyz147064 		dl_error_ack_t	dlea;
713d62bc4baSyz147064 		uint32_t	notes;
714d62bc4baSyz147064 		struct strioctl	iocb;
715d62bc4baSyz147064 		uint32_t	margin;
716d62bc4baSyz147064 		int		rval;
717d62bc4baSyz147064 
718d62bc4baSyz147064 		if ((softmac_dev = softmac->smac_softmac[index]) == NULL)
719d62bc4baSyz147064 			continue;
720d62bc4baSyz147064 
721d62bc4baSyz147064 		softmac->smac_dev = dev = softmac_dev->sd_dev;
722d62bc4baSyz147064 		if (ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh,
723d62bc4baSyz147064 		    li) != 0) {
724d62bc4baSyz147064 			continue;
725d62bc4baSyz147064 		}
726d62bc4baSyz147064 
727d62bc4baSyz147064 		/*
728d62bc4baSyz147064 		 * Pop all the intermediate modules in order to negotiate
729d62bc4baSyz147064 		 * capabilities correctly.
730d62bc4baSyz147064 		 */
731d62bc4baSyz147064 		while (ldi_ioctl(lh, I_POP, 0, FKIOCTL, kcred, &rval) == 0)
732d62bc4baSyz147064 			;
733d62bc4baSyz147064 
734d62bc4baSyz147064 		/* DLPI style-1 or DLPI style-2? */
735d62bc4baSyz147064 		if ((rval = dl_info(lh, &dlia, NULL, NULL, &dlea)) != 0) {
736d62bc4baSyz147064 			if (rval == ENOTSUP) {
737d62bc4baSyz147064 				cmn_err(CE_NOTE, "softmac: received "
738d62bc4baSyz147064 				    "DL_ERROR_ACK to DL_INFO_ACK; "
739d62bc4baSyz147064 				    "DLPI errno 0x%x, UNIX errno %d",
740d62bc4baSyz147064 				    dlea.dl_errno, dlea.dl_unix_errno);
741d62bc4baSyz147064 			}
742d62bc4baSyz147064 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
743d62bc4baSyz147064 			continue;
744d62bc4baSyz147064 		}
745d62bc4baSyz147064 
746d62bc4baSyz147064 		/*
747d62bc4baSyz147064 		 * Currently only DL_ETHER has GLDv3 mac plugin support.
748d62bc4baSyz147064 		 * For media types that GLDv3 does not support, create a
749d62bc4baSyz147064 		 * link id for it.
750d62bc4baSyz147064 		 */
751d62bc4baSyz147064 		if ((softmac->smac_media = dlia.dl_mac_type) != DL_ETHER) {
752d62bc4baSyz147064 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
753d62bc4baSyz147064 			err = 0;
754d62bc4baSyz147064 			break;
755d62bc4baSyz147064 		}
756d62bc4baSyz147064 
757d62bc4baSyz147064 		if ((dlia.dl_provider_style == DL_STYLE2) &&
758d62bc4baSyz147064 		    (dl_attach(lh, softmac->smac_uppa, NULL) != 0)) {
759d62bc4baSyz147064 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
760d62bc4baSyz147064 			continue;
761d62bc4baSyz147064 		}
762d62bc4baSyz147064 
763d62bc4baSyz147064 		if ((rval = dl_bind(lh, 0, NULL)) != 0) {
764d62bc4baSyz147064 			if (rval == ENOTSUP) {
765d62bc4baSyz147064 				cmn_err(CE_NOTE, "softmac: received "
766d62bc4baSyz147064 				    "DL_ERROR_ACK to DL_BIND_ACK; "
767d62bc4baSyz147064 				    "DLPI errno 0x%x, UNIX errno %d",
768d62bc4baSyz147064 				    dlea.dl_errno, dlea.dl_unix_errno);
769d62bc4baSyz147064 			}
770d62bc4baSyz147064 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
771d62bc4baSyz147064 			continue;
772d62bc4baSyz147064 		}
773d62bc4baSyz147064 
774d62bc4baSyz147064 		/*
775d62bc4baSyz147064 		 * Call dl_info() after dl_bind() because some drivers only
776d62bc4baSyz147064 		 * provide correct information (e.g. MAC address) once bound.
777d62bc4baSyz147064 		 */
778d62bc4baSyz147064 		softmac->smac_addrlen = sizeof (softmac->smac_unicst_addr);
779d62bc4baSyz147064 		if ((rval = dl_info(lh, &dlia, softmac->smac_unicst_addr,
780d62bc4baSyz147064 		    &softmac->smac_addrlen, &dlea)) != 0) {
781d62bc4baSyz147064 			if (rval == ENOTSUP) {
782d62bc4baSyz147064 				cmn_err(CE_NOTE, "softmac: received "
783d62bc4baSyz147064 				    "DL_ERROR_ACK to DL_INFO_ACK; "
784d62bc4baSyz147064 				    "DLPI errno 0x%x, UNIX errno %d",
785d62bc4baSyz147064 				    dlea.dl_errno, dlea.dl_unix_errno);
786d62bc4baSyz147064 			}
787d62bc4baSyz147064 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
788d62bc4baSyz147064 			continue;
789d62bc4baSyz147064 		}
790d62bc4baSyz147064 
791d62bc4baSyz147064 		softmac->smac_style = dlia.dl_provider_style;
792d62bc4baSyz147064 		softmac->smac_saplen = ABS(dlia.dl_sap_length);
793d62bc4baSyz147064 		softmac->smac_min_sdu = dlia.dl_min_sdu;
794d62bc4baSyz147064 		softmac->smac_max_sdu = dlia.dl_max_sdu;
795d62bc4baSyz147064 
796d62bc4baSyz147064 		if ((softmac->smac_saplen != sizeof (uint16_t)) ||
797d62bc4baSyz147064 		    (softmac->smac_addrlen != ETHERADDRL) ||
798d62bc4baSyz147064 		    (dlia.dl_brdcst_addr_length != ETHERADDRL) ||
799d62bc4baSyz147064 		    (dlia.dl_brdcst_addr_offset == 0)) {
800d62bc4baSyz147064 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
801d62bc4baSyz147064 			continue;
802d62bc4baSyz147064 		}
803d62bc4baSyz147064 
804d62bc4baSyz147064 		/*
805d62bc4baSyz147064 		 * Check other DLPI capabilities. Note that this must be after
806d62bc4baSyz147064 		 * dl_bind() because some drivers return DL_ERROR_ACK if the
807d62bc4baSyz147064 		 * stream is not bound. It is also before mac_register(), so
808d62bc4baSyz147064 		 * we don't need any lock protection here.
809d62bc4baSyz147064 		 */
810d62bc4baSyz147064 		softmac->smac_capab_flags =
811da14cebeSEric Cheng 		    (MAC_CAPAB_NO_ZCOPY | MAC_CAPAB_LEGACY);
812d62bc4baSyz147064 
813d62bc4baSyz147064 		softmac->smac_no_capability_req = B_FALSE;
814d62bc4baSyz147064 		if (softmac_fill_capab(lh, softmac) != 0)
815d62bc4baSyz147064 			softmac->smac_no_capability_req = B_TRUE;
816d62bc4baSyz147064 
817d62bc4baSyz147064 		/*
818d62bc4baSyz147064 		 * Check the margin of the underlying driver.
819d62bc4baSyz147064 		 */
820d62bc4baSyz147064 		margin = 0;
821d62bc4baSyz147064 		iocb.ic_cmd = DLIOCMARGININFO;
822d62bc4baSyz147064 		iocb.ic_timout = INFTIM;
823d62bc4baSyz147064 		iocb.ic_len = sizeof (margin);
824d62bc4baSyz147064 		iocb.ic_dp = (char *)&margin;
825d62bc4baSyz147064 		softmac->smac_margin = 0;
826d62bc4baSyz147064 
827d62bc4baSyz147064 		if (ldi_ioctl(lh, I_STR, (intptr_t)&iocb, FKIOCTL, kcred,
828d62bc4baSyz147064 		    &rval) == 0) {
829d62bc4baSyz147064 			softmac->smac_margin = margin;
830d62bc4baSyz147064 		}
831d62bc4baSyz147064 
832d62bc4baSyz147064 		/*
833d62bc4baSyz147064 		 * If the legacy driver doesn't support DLIOCMARGININFO, but
834d62bc4baSyz147064 		 * it can support native VLAN, correct its margin value to 4.
835d62bc4baSyz147064 		 */
836d62bc4baSyz147064 		if (native_vlan) {
837d62bc4baSyz147064 			if (softmac->smac_margin == 0)
838d62bc4baSyz147064 				softmac->smac_margin = VLAN_TAGSZ;
839d62bc4baSyz147064 		} else {
840d62bc4baSyz147064 			softmac->smac_capab_flags |= MAC_CAPAB_NO_NATIVEVLAN;
841d62bc4baSyz147064 		}
842d62bc4baSyz147064 
843d62bc4baSyz147064 		/*
844d62bc4baSyz147064 		 * Not all drivers support DL_NOTIFY_REQ, so ignore ENOTSUP.
845d62bc4baSyz147064 		 */
846d62bc4baSyz147064 		softmac->smac_notifications = 0;
847d62bc4baSyz147064 		notes = DL_NOTE_PHYS_ADDR | DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN;
848d62bc4baSyz147064 		switch (dl_notify(lh, &notes, NULL)) {
849d62bc4baSyz147064 		case 0:
850d62bc4baSyz147064 			softmac->smac_notifications = notes;
851d62bc4baSyz147064 			break;
852d62bc4baSyz147064 		case ENOTSUP:
853d62bc4baSyz147064 			break;
854d62bc4baSyz147064 		default:
855d62bc4baSyz147064 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
856d62bc4baSyz147064 			continue;
857d62bc4baSyz147064 		}
858d62bc4baSyz147064 
859d62bc4baSyz147064 		(void) ldi_close(lh, FREAD|FWRITE, kcred);
860d62bc4baSyz147064 		err = 0;
861d62bc4baSyz147064 		break;
862d62bc4baSyz147064 	}
863d62bc4baSyz147064 	ldi_ident_release(li);
864d62bc4baSyz147064 
865d62bc4baSyz147064 	mutex_enter(&softmac->smac_mutex);
866d62bc4baSyz147064 
867d62bc4baSyz147064 	if (err != 0)
868d62bc4baSyz147064 		goto done;
869d62bc4baSyz147064 
870d62bc4baSyz147064 	if (softmac->smac_media != DL_ETHER)
871d62bc4baSyz147064 		softmac->smac_flags |= SOFTMAC_NOSUPP;
872d62bc4baSyz147064 
873d62bc4baSyz147064 	/*
874d62bc4baSyz147064 	 * Finally, we're ready to register ourselves with the MAC layer
875d62bc4baSyz147064 	 * interface; if this succeeds, we're all ready to start()
876d62bc4baSyz147064 	 */
877d62bc4baSyz147064 	if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) {
878d62bc4baSyz147064 		mac_register_t	*macp;
879d62bc4baSyz147064 
880d62bc4baSyz147064 		if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
881d62bc4baSyz147064 			err = ENOMEM;
882d62bc4baSyz147064 			goto done;
883d62bc4baSyz147064 		}
884d62bc4baSyz147064 
885d62bc4baSyz147064 		macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
886d62bc4baSyz147064 		macp->m_driver = softmac;
887d62bc4baSyz147064 		macp->m_dip = softmac_dip;
888d62bc4baSyz147064 
889d62bc4baSyz147064 		macp->m_margin = softmac->smac_margin;
890d62bc4baSyz147064 		macp->m_src_addr = softmac->smac_unicst_addr;
891d62bc4baSyz147064 		macp->m_min_sdu = softmac->smac_min_sdu;
892d62bc4baSyz147064 		macp->m_max_sdu = softmac->smac_max_sdu;
893d62bc4baSyz147064 		macp->m_callbacks = &softmac_m_callbacks;
894d62bc4baSyz147064 		macp->m_instance = (uint_t)-1;
895d62bc4baSyz147064 
896d62bc4baSyz147064 		err = mac_register(macp, &softmac->smac_mh);
897d62bc4baSyz147064 		mac_free(macp);
898d62bc4baSyz147064 		if (err != 0) {
899d62bc4baSyz147064 			cmn_err(CE_WARN, "mac_register failed for %s",
900d62bc4baSyz147064 			    softmac->smac_devname);
901d62bc4baSyz147064 			goto done;
902d62bc4baSyz147064 		}
903d62bc4baSyz147064 	}
904da14cebeSEric Cheng 	mutex_exit(&softmac->smac_mutex);
905d62bc4baSyz147064 
906d62bc4baSyz147064 	/*
907d62bc4baSyz147064 	 * Try to create the datalink for this softmac.
908d62bc4baSyz147064 	 */
909d62bc4baSyz147064 	if ((err = softmac_create_datalink(softmac)) != 0) {
9105d460eafSCathy Zhou 		if (!(softmac->smac_flags & SOFTMAC_NOSUPP))
911d62bc4baSyz147064 			(void) mac_unregister(softmac->smac_mh);
9125d460eafSCathy Zhou 		mutex_enter(&softmac->smac_mutex);
913d62bc4baSyz147064 		softmac->smac_mh = NULL;
9145d460eafSCathy Zhou 		goto done;
915d62bc4baSyz147064 	}
916da14cebeSEric Cheng 	/*
917da14cebeSEric Cheng 	 * If succeed, create the thread which handles the DL_NOTIFY_IND from
918da14cebeSEric Cheng 	 * the lower stream.
919da14cebeSEric Cheng 	 */
9205d460eafSCathy Zhou 	mutex_enter(&softmac->smac_mutex);
921da14cebeSEric Cheng 	if (softmac->smac_mh != NULL) {
922da14cebeSEric Cheng 		softmac->smac_notify_thread = thread_create(NULL, 0,
923da14cebeSEric Cheng 		    softmac_notify_thread, softmac, 0, &p0,
924da14cebeSEric Cheng 		    TS_RUN, minclsyspri);
925da14cebeSEric Cheng 	}
926d62bc4baSyz147064 
927d62bc4baSyz147064 done:
928da14cebeSEric Cheng 	ASSERT(softmac->smac_state == SOFTMAC_ATTACH_INPROG &&
929da14cebeSEric Cheng 	    softmac->smac_attachok_cnt == softmac->smac_cnt);
930da14cebeSEric Cheng 	softmac->smac_state = SOFTMAC_ATTACH_DONE;
931d62bc4baSyz147064 	softmac->smac_attacherr = err;
932d62bc4baSyz147064 	cv_broadcast(&softmac->smac_cv);
933d62bc4baSyz147064 	mutex_exit(&softmac->smac_mutex);
934d62bc4baSyz147064 }
935d62bc4baSyz147064 
936d62bc4baSyz147064 int
937d62bc4baSyz147064 softmac_destroy(dev_info_t *dip, dev_t dev)
938d62bc4baSyz147064 {
939d62bc4baSyz147064 	char			devname[MAXNAMELEN];
940d62bc4baSyz147064 	softmac_t		*softmac;
941d62bc4baSyz147064 	softmac_dev_t		*softmac_dev;
942d62bc4baSyz147064 	int			index;
943d62bc4baSyz147064 	int			ppa, err;
944d62bc4baSyz147064 	datalink_id_t		linkid;
945da14cebeSEric Cheng 	mac_handle_t		smac_mh;
946da14cebeSEric Cheng 	uint32_t		smac_flags;
947d62bc4baSyz147064 
94861af1958SGarrett D'Amore 	if (GLDV3_DRV(ddi_driver_major(dip))) {
94961af1958SGarrett D'Amore 		minor_t minor = getminor(dev);
95061af1958SGarrett D'Amore 		/*
95161af1958SGarrett D'Amore 		 * For an explanation of this logic, see the
95261af1958SGarrett D'Amore 		 * equivalent code in softmac_create.
95361af1958SGarrett D'Amore 		 */
95461af1958SGarrett D'Amore 		if ((strcmp(ddi_driver_name(dip), "clone") == 0) ||
95561af1958SGarrett D'Amore 		    (getmajor(dev) == ddi_name_to_major("clone")) ||
95661af1958SGarrett D'Amore 		    (minor == 0)) {
95761af1958SGarrett D'Amore 			return (0);
95861af1958SGarrett D'Amore 		}
95961af1958SGarrett D'Amore 		if (minor >= DLS_MAX_MINOR) {
96061af1958SGarrett D'Amore 			return (ENOTSUP);
96161af1958SGarrett D'Amore 		}
96261af1958SGarrett D'Amore 		ppa = DLS_MINOR2INST(minor);
96361af1958SGarrett D'Amore 	} else {
964d62bc4baSyz147064 		ppa = ddi_get_instance(dip);
96561af1958SGarrett D'Amore 	}
96661af1958SGarrett D'Amore 
967d62bc4baSyz147064 	(void) snprintf(devname, MAXNAMELEN, "%s%d", ddi_driver_name(dip), ppa);
968d62bc4baSyz147064 
969da14cebeSEric Cheng 	/*
970da14cebeSEric Cheng 	 * We are called only from the predetach entry point. The DACF
971da14cebeSEric Cheng 	 * framework ensures there can't be a concurrent postattach call
972da14cebeSEric Cheng 	 * for the same softmac. The softmac found out from the modhash
973da14cebeSEric Cheng 	 * below can't vanish beneath us since this is the only place where
974da14cebeSEric Cheng 	 * it is deleted.
975da14cebeSEric Cheng 	 */
976d62bc4baSyz147064 	err = mod_hash_find(softmac_hash, (mod_hash_key_t)devname,
977d62bc4baSyz147064 	    (mod_hash_val_t *)&softmac);
978d62bc4baSyz147064 	ASSERT(err == 0);
979d62bc4baSyz147064 
980d62bc4baSyz147064 	mutex_enter(&softmac->smac_mutex);
981da14cebeSEric Cheng 	SOFTMAC_STATE_VERIFY(softmac);
982d62bc4baSyz147064 
983d62bc4baSyz147064 	/*
984d62bc4baSyz147064 	 * Fail the predetach routine if this softmac is in-use.
985da14cebeSEric Cheng 	 * Make sure these downcalls into softmac_create or softmac_destroy
986da14cebeSEric Cheng 	 * don't cv_wait on any devfs related condition. Thus softmac_destroy
987da14cebeSEric Cheng 	 * returns EBUSY if the asynchronous thread started in softmac_create
988da14cebeSEric Cheng 	 * hasn't finished
989d62bc4baSyz147064 	 */
990da14cebeSEric Cheng 	if ((softmac->smac_hold_cnt != 0) ||
991da14cebeSEric Cheng 	    (softmac->smac_state == SOFTMAC_ATTACH_INPROG)) {
992d62bc4baSyz147064 		softmac->smac_attached_left = softmac->smac_attachok_cnt;
993d62bc4baSyz147064 		mutex_exit(&softmac->smac_mutex);
994d62bc4baSyz147064 		return (EBUSY);
995d62bc4baSyz147064 	}
996d62bc4baSyz147064 
997d62bc4baSyz147064 	/*
998d62bc4baSyz147064 	 * Even if the predetach of one minor node has already failed
999d62bc4baSyz147064 	 * (smac_attached_left is not 0), the DACF framework will continue
1000d62bc4baSyz147064 	 * to call the predetach routines of the other minor nodes,
1001d62bc4baSyz147064 	 * so we fail these calls here.
1002d62bc4baSyz147064 	 */
1003d62bc4baSyz147064 	if (softmac->smac_attached_left != 0) {
1004d62bc4baSyz147064 		mutex_exit(&softmac->smac_mutex);
1005d62bc4baSyz147064 		return (EBUSY);
1006d62bc4baSyz147064 	}
1007d62bc4baSyz147064 
1008da14cebeSEric Cheng 	smac_mh = softmac->smac_mh;
1009da14cebeSEric Cheng 	smac_flags = softmac->smac_flags;
1010da14cebeSEric Cheng 	softmac->smac_state = SOFTMAC_DETACH_INPROG;
1011da14cebeSEric Cheng 	mutex_exit(&softmac->smac_mutex);
1012d62bc4baSyz147064 
1013da14cebeSEric Cheng 	if (smac_mh != NULL) {
1014d62bc4baSyz147064 		/*
1015da14cebeSEric Cheng 		 * This is the first minor node that is being detached for this
1016da14cebeSEric Cheng 		 * softmac.
1017d62bc4baSyz147064 		 */
1018da14cebeSEric Cheng 		ASSERT(softmac->smac_attachok_cnt == softmac->smac_cnt);
1019da14cebeSEric Cheng 		if (!(smac_flags & SOFTMAC_NOSUPP)) {
1020da14cebeSEric Cheng 			if ((err = dls_devnet_destroy(smac_mh, &linkid,
1021da14cebeSEric Cheng 			    B_FALSE)) != 0) {
1022da14cebeSEric Cheng 				goto error;
1023d62bc4baSyz147064 			}
1024d62bc4baSyz147064 		}
1025d62bc4baSyz147064 		/*
1026d62bc4baSyz147064 		 * If softmac_mac_register() succeeds in registering the mac
1027d62bc4baSyz147064 		 * of the legacy device, unregister it.
1028d62bc4baSyz147064 		 */
1029da14cebeSEric Cheng 		if (!(smac_flags & (SOFTMAC_GLDV3 | SOFTMAC_NOSUPP))) {
1030da14cebeSEric Cheng 			if ((err = mac_disable_nowait(smac_mh)) != 0) {
10312b24ab6bSSebastien Roy 				(void) dls_devnet_create(smac_mh, linkid,
10322b24ab6bSSebastien Roy 				    crgetzoneid(CRED()));
1033da14cebeSEric Cheng 				goto error;
1034d62bc4baSyz147064 			}
1035da14cebeSEric Cheng 			/*
1036da14cebeSEric Cheng 			 * Ask softmac_notify_thread to quit, and wait for
1037da14cebeSEric Cheng 			 * that to be done.
1038da14cebeSEric Cheng 			 */
1039da14cebeSEric Cheng 			mutex_enter(&softmac->smac_mutex);
1040da14cebeSEric Cheng 			softmac->smac_flags |= SOFTMAC_NOTIFY_QUIT;
1041da14cebeSEric Cheng 			cv_broadcast(&softmac->smac_cv);
1042da14cebeSEric Cheng 			while (softmac->smac_notify_thread != NULL) {
1043da14cebeSEric Cheng 				cv_wait(&softmac->smac_cv,
1044da14cebeSEric Cheng 				    &softmac->smac_mutex);
1045da14cebeSEric Cheng 			}
1046da14cebeSEric Cheng 			mutex_exit(&softmac->smac_mutex);
1047da14cebeSEric Cheng 			VERIFY(mac_unregister(smac_mh) == 0);
1048d62bc4baSyz147064 		}
1049d62bc4baSyz147064 		softmac->smac_mh = NULL;
1050d62bc4baSyz147064 	}
1051d62bc4baSyz147064 
1052d62bc4baSyz147064 	/*
1053d62bc4baSyz147064 	 * Free softmac_dev
1054d62bc4baSyz147064 	 */
1055da14cebeSEric Cheng 	rw_enter(&softmac_hash_lock, RW_WRITER);
1056da14cebeSEric Cheng 	mutex_enter(&softmac->smac_mutex);
1057da14cebeSEric Cheng 
1058da14cebeSEric Cheng 	ASSERT(softmac->smac_state == SOFTMAC_DETACH_INPROG &&
1059da14cebeSEric Cheng 	    softmac->smac_attachok_cnt != 0);
1060da14cebeSEric Cheng 	softmac->smac_mh = NULL;
1061d62bc4baSyz147064 	index = (getmajor(dev) == ddi_name_to_major("clone"));
1062d62bc4baSyz147064 	softmac_dev = softmac->smac_softmac[index];
1063d62bc4baSyz147064 	ASSERT(softmac_dev != NULL);
1064d62bc4baSyz147064 	softmac->smac_softmac[index] = NULL;
1065d62bc4baSyz147064 	kmem_free(softmac_dev, sizeof (softmac_dev_t));
1066d62bc4baSyz147064 
1067d62bc4baSyz147064 	if (--softmac->smac_attachok_cnt == 0) {
1068d62bc4baSyz147064 		mod_hash_val_t	hashval;
1069d62bc4baSyz147064 
1070da14cebeSEric Cheng 		softmac->smac_state = SOFTMAC_UNINIT;
1071da14cebeSEric Cheng 		if (softmac->smac_hold_cnt != 0) {
1072da14cebeSEric Cheng 			/*
1073da14cebeSEric Cheng 			 * Someone did a softmac_hold_device while we dropped
1074da14cebeSEric Cheng 			 * the locks. Leave the softmac itself intact which
1075da14cebeSEric Cheng 			 * will be reused by the reattach
1076da14cebeSEric Cheng 			 */
1077da14cebeSEric Cheng 			mutex_exit(&softmac->smac_mutex);
1078da14cebeSEric Cheng 			rw_exit(&softmac_hash_lock);
1079da14cebeSEric Cheng 			return (0);
1080da14cebeSEric Cheng 		}
1081d62bc4baSyz147064 		err = mod_hash_remove(softmac_hash,
1082d62bc4baSyz147064 		    (mod_hash_key_t)devname,
1083d62bc4baSyz147064 		    (mod_hash_val_t *)&hashval);
1084d62bc4baSyz147064 		ASSERT(err == 0);
1085d62bc4baSyz147064 
1086d62bc4baSyz147064 		mutex_exit(&softmac->smac_mutex);
1087d62bc4baSyz147064 		rw_exit(&softmac_hash_lock);
10885d460eafSCathy Zhou 		ASSERT(softmac->smac_fp_disable_clients == 0);
10895d460eafSCathy Zhou 		softmac->smac_fastpath_admin_disabled = B_FALSE;
10905d460eafSCathy Zhou 		kmem_cache_free(softmac_cachep, softmac);
1091d62bc4baSyz147064 		return (0);
1092d62bc4baSyz147064 	}
1093d62bc4baSyz147064 	mutex_exit(&softmac->smac_mutex);
1094d62bc4baSyz147064 	rw_exit(&softmac_hash_lock);
1095da14cebeSEric Cheng 	return (0);
1096da14cebeSEric Cheng 
1097da14cebeSEric Cheng error:
1098da14cebeSEric Cheng 	mutex_enter(&softmac->smac_mutex);
1099da14cebeSEric Cheng 	softmac->smac_attached_left = softmac->smac_attachok_cnt;
1100da14cebeSEric Cheng 	softmac->smac_state = SOFTMAC_ATTACH_DONE;
1101da14cebeSEric Cheng 	cv_broadcast(&softmac->smac_cv);
1102da14cebeSEric Cheng 	mutex_exit(&softmac->smac_mutex);
1103d62bc4baSyz147064 	return (err);
1104d62bc4baSyz147064 }
1105d62bc4baSyz147064 
1106d62bc4baSyz147064 /*
1107d62bc4baSyz147064  * This function is called as the result of a newly started dlmgmtd daemon.
1108d62bc4baSyz147064  *
1109d62bc4baSyz147064  * We walk through every softmac that was created but failed to notify
1110d62bc4baSyz147064  * dlmgmtd about it (whose SOFTMAC_NEED_RECREATE flag is set).  This occurs
1111d62bc4baSyz147064  * when softmacs are created before dlmgmtd is ready.  For example, during
1112d62bc4baSyz147064  * diskless boot, a network device is used (and therefore attached) before
1113d62bc4baSyz147064  * the datalink-management service starts dlmgmtd.
1114d62bc4baSyz147064  */
1115d62bc4baSyz147064 /* ARGSUSED */
1116d62bc4baSyz147064 static uint_t
1117d62bc4baSyz147064 softmac_mac_recreate(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1118d62bc4baSyz147064 {
1119d62bc4baSyz147064 	softmac_t	*softmac = (softmac_t *)val;
1120d62bc4baSyz147064 	datalink_id_t	linkid;
1121d62bc4baSyz147064 	int		err;
1122da14cebeSEric Cheng 	softmac_walk_t	*smwp = arg;
1123d62bc4baSyz147064 
1124d62bc4baSyz147064 	/*
1125da14cebeSEric Cheng 	 * The framework itself must not hold any locks across calls to the
1126da14cebeSEric Cheng 	 * mac perimeter. Thus this function does not call any framework
1127da14cebeSEric Cheng 	 * function that needs to grab the mac perimeter.
1128d62bc4baSyz147064 	 */
1129da14cebeSEric Cheng 	ASSERT(RW_READ_HELD(&softmac_hash_lock));
1130d62bc4baSyz147064 
1131da14cebeSEric Cheng 	smwp->smw_retry = B_FALSE;
1132da14cebeSEric Cheng 	mutex_enter(&softmac->smac_mutex);
1133da14cebeSEric Cheng 	SOFTMAC_STATE_VERIFY(softmac);
1134da14cebeSEric Cheng 	if (softmac->smac_state == SOFTMAC_ATTACH_INPROG) {
1135da14cebeSEric Cheng 		/*
1136da14cebeSEric Cheng 		 * Wait till softmac_create or softmac_mac_register finishes
1137da14cebeSEric Cheng 		 * Hold the softmac to ensure it stays around. The wait itself
1138da14cebeSEric Cheng 		 * is done in the caller, since we need to drop all locks
1139da14cebeSEric Cheng 		 * including the mod hash's internal lock before calling
1140da14cebeSEric Cheng 		 * cv_wait.
1141da14cebeSEric Cheng 		 */
1142da14cebeSEric Cheng 		smwp->smw_retry = B_TRUE;
1143da14cebeSEric Cheng 		smwp->smw_softmac = softmac;
1144da14cebeSEric Cheng 		softmac->smac_hold_cnt++;
1145da14cebeSEric Cheng 		return (MH_WALK_TERMINATE);
1146da14cebeSEric Cheng 	}
1147da14cebeSEric Cheng 
1148da14cebeSEric Cheng 	if ((softmac->smac_state != SOFTMAC_ATTACH_DONE) ||
1149d62bc4baSyz147064 	    !(softmac->smac_flags & SOFTMAC_NEED_RECREATE)) {
1150d62bc4baSyz147064 		mutex_exit(&softmac->smac_mutex);
1151d62bc4baSyz147064 		return (MH_WALK_CONTINUE);
1152d62bc4baSyz147064 	}
1153d62bc4baSyz147064 
1154ae6aa22aSVenugopal Iyer 	/*
1155ae6aa22aSVenugopal Iyer 	 * Bumping up the smac_hold_cnt allows us to drop the lock. It also
1156ae6aa22aSVenugopal Iyer 	 * makes softmac_destroy() return failure on an attempted device detach.
1157ae6aa22aSVenugopal Iyer 	 * We don't want to hold the lock across calls to other subsystems
1158ae6aa22aSVenugopal Iyer 	 * like kstats, which will happen in the call to dls_devnet_recreate
1159ae6aa22aSVenugopal Iyer 	 */
1160ae6aa22aSVenugopal Iyer 	softmac->smac_hold_cnt++;
1161ae6aa22aSVenugopal Iyer 	mutex_exit(&softmac->smac_mutex);
1162ae6aa22aSVenugopal Iyer 
1163d62bc4baSyz147064 	if (dls_mgmt_create(softmac->smac_devname,
1164d62bc4baSyz147064 	    makedevice(softmac->smac_umajor, softmac->smac_uppa + 1),
1165d62bc4baSyz147064 	    DATALINK_CLASS_PHYS, softmac->smac_media, B_TRUE, &linkid) != 0) {
1166ae6aa22aSVenugopal Iyer 		softmac_rele_device((dls_dev_handle_t)softmac);
1167d62bc4baSyz147064 		return (MH_WALK_CONTINUE);
1168d62bc4baSyz147064 	}
1169d62bc4baSyz147064 
1170d62bc4baSyz147064 	if ((err = softmac_update_info(softmac, &linkid)) != 0) {
1171d62bc4baSyz147064 		cmn_err(CE_WARN, "softmac: softmac_update_info() for %s "
1172d62bc4baSyz147064 		    "failed (%d)", softmac->smac_devname, err);
1173ae6aa22aSVenugopal Iyer 		softmac_rele_device((dls_dev_handle_t)softmac);
1174d62bc4baSyz147064 		return (MH_WALK_CONTINUE);
1175d62bc4baSyz147064 	}
1176d62bc4baSyz147064 
1177d62bc4baSyz147064 	/*
1178d62bc4baSyz147064 	 * Create a link for this MAC. The link name will be the same
1179d62bc4baSyz147064 	 * as the MAC name.
1180d62bc4baSyz147064 	 */
1181d62bc4baSyz147064 	if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) {
1182d62bc4baSyz147064 		err = dls_devnet_recreate(softmac->smac_mh, linkid);
1183d62bc4baSyz147064 		if (err != 0) {
1184d62bc4baSyz147064 			cmn_err(CE_WARN, "softmac: dls_devnet_recreate() for "
1185d62bc4baSyz147064 			    "%s (linkid %d) failed (%d)",
1186d62bc4baSyz147064 			    softmac->smac_devname, linkid, err);
1187d62bc4baSyz147064 		}
1188d62bc4baSyz147064 	}
1189d62bc4baSyz147064 
1190ae6aa22aSVenugopal Iyer 	mutex_enter(&softmac->smac_mutex);
1191d62bc4baSyz147064 	softmac->smac_flags &= ~SOFTMAC_NEED_RECREATE;
1192ae6aa22aSVenugopal Iyer 	ASSERT(softmac->smac_hold_cnt != 0);
1193ae6aa22aSVenugopal Iyer 	softmac->smac_hold_cnt--;
1194d62bc4baSyz147064 	mutex_exit(&softmac->smac_mutex);
1195d62bc4baSyz147064 
1196d62bc4baSyz147064 	return (MH_WALK_CONTINUE);
1197d62bc4baSyz147064 }
1198d62bc4baSyz147064 
1199d62bc4baSyz147064 /*
1200d62bc4baSyz147064  * See comments above softmac_mac_recreate().
1201d62bc4baSyz147064  */
1202d62bc4baSyz147064 void
1203d62bc4baSyz147064 softmac_recreate()
1204d62bc4baSyz147064 {
1205da14cebeSEric Cheng 	softmac_walk_t	smw;
1206da14cebeSEric Cheng 	softmac_t	*softmac;
1207da14cebeSEric Cheng 
1208d62bc4baSyz147064 	/*
1209d62bc4baSyz147064 	 * Walk through the softmac_hash table. Request to create the
1210d62bc4baSyz147064 	 * [link name, linkid] mapping if we failed to do so.
1211d62bc4baSyz147064 	 */
1212da14cebeSEric Cheng 	do {
1213da14cebeSEric Cheng 		smw.smw_retry = B_FALSE;
1214d62bc4baSyz147064 		rw_enter(&softmac_hash_lock, RW_READER);
1215da14cebeSEric Cheng 		mod_hash_walk(softmac_hash, softmac_mac_recreate, &smw);
1216d62bc4baSyz147064 		rw_exit(&softmac_hash_lock);
1217da14cebeSEric Cheng 		if (smw.smw_retry) {
1218da14cebeSEric Cheng 			/*
1219da14cebeSEric Cheng 			 * softmac_create or softmac_mac_register hasn't yet
1220da14cebeSEric Cheng 			 * finished and the softmac is not yet in the
1221da14cebeSEric Cheng 			 * SOFTMAC_ATTACH_DONE state.
1222da14cebeSEric Cheng 			 */
1223da14cebeSEric Cheng 			softmac = smw.smw_softmac;
1224da14cebeSEric Cheng 			cv_wait(&softmac->smac_cv, &softmac->smac_mutex);
1225da14cebeSEric Cheng 			softmac->smac_hold_cnt--;
1226da14cebeSEric Cheng 			mutex_exit(&softmac->smac_mutex);
1227da14cebeSEric Cheng 		}
1228da14cebeSEric Cheng 	} while (smw.smw_retry);
1229d62bc4baSyz147064 }
1230d62bc4baSyz147064 
1231d62bc4baSyz147064 static int
1232d62bc4baSyz147064 softmac_m_start(void *arg)
1233d62bc4baSyz147064 {
12345d460eafSCathy Zhou 	softmac_t	*softmac = arg;
12355d460eafSCathy Zhou 	softmac_lower_t	*slp = softmac->smac_lower;
12365d460eafSCathy Zhou 	int		err;
12375d460eafSCathy Zhou 
12385d460eafSCathy Zhou 	ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
12395d460eafSCathy Zhou 	/*
12405d460eafSCathy Zhou 	 * Bind to SAP 2 on token ring, 0 on other interface types.
12415d460eafSCathy Zhou 	 * (SAP 0 has special significance on token ring).
12425d460eafSCathy Zhou 	 * Note that the receive-side packets could come anytime after bind.
12435d460eafSCathy Zhou 	 */
12445d460eafSCathy Zhou 	err = softmac_send_bind_req(slp, softmac->smac_media == DL_TPR ? 2 : 0);
12455d460eafSCathy Zhou 	if (err != 0)
12465d460eafSCathy Zhou 		return (err);
12475d460eafSCathy Zhou 
12485d460eafSCathy Zhou 	/*
12495d460eafSCathy Zhou 	 * Put the lower stream to the DL_PROMISC_SAP mode in order to receive
12505d460eafSCathy Zhou 	 * all packets of interest.
12515d460eafSCathy Zhou 	 *
12525d460eafSCathy Zhou 	 * some driver (e.g. the old legacy eri driver) incorrectly passes up
12535d460eafSCathy Zhou 	 * packets to DL_PROMISC_SAP stream when the lower stream is not bound,
12545d460eafSCathy Zhou 	 * so that we send DL_PROMISON_REQ after DL_BIND_REQ.
12555d460eafSCathy Zhou 	 */
12565d460eafSCathy Zhou 	err = softmac_send_promisc_req(slp, DL_PROMISC_SAP, B_TRUE);
12575d460eafSCathy Zhou 	if (err != 0) {
12585d460eafSCathy Zhou 		(void) softmac_send_unbind_req(slp);
12595d460eafSCathy Zhou 		return (err);
12605d460eafSCathy Zhou 	}
12615d460eafSCathy Zhou 
12625d460eafSCathy Zhou 	/*
12635d460eafSCathy Zhou 	 * Enable capabilities the underlying driver claims to support.
12645d460eafSCathy Zhou 	 * Some driver requires this being called after the stream is bound.
12655d460eafSCathy Zhou 	 */
12665d460eafSCathy Zhou 	if ((err = softmac_capab_enable(slp)) != 0) {
12675d460eafSCathy Zhou 		(void) softmac_send_promisc_req(slp, DL_PROMISC_SAP, B_FALSE);
12685d460eafSCathy Zhou 		(void) softmac_send_unbind_req(slp);
12695d460eafSCathy Zhou 	}
12705d460eafSCathy Zhou 
12715d460eafSCathy Zhou 	return (err);
1272d62bc4baSyz147064 }
1273d62bc4baSyz147064 
1274d62bc4baSyz147064 /* ARGSUSED */
1275d62bc4baSyz147064 static void
1276d62bc4baSyz147064 softmac_m_stop(void *arg)
1277d62bc4baSyz147064 {
12785d460eafSCathy Zhou 	softmac_t	*softmac = arg;
12795d460eafSCathy Zhou 	softmac_lower_t	*slp = softmac->smac_lower;
12805d460eafSCathy Zhou 
12815d460eafSCathy Zhou 	ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
12825d460eafSCathy Zhou 
12835d460eafSCathy Zhou 	/*
12845d460eafSCathy Zhou 	 * It is not needed to reset zerocopy, MDT or HCKSUM capabilities.
12855d460eafSCathy Zhou 	 */
12865d460eafSCathy Zhou 	(void) softmac_send_promisc_req(slp, DL_PROMISC_SAP, B_FALSE);
12875d460eafSCathy Zhou 	(void) softmac_send_unbind_req(slp);
1288d62bc4baSyz147064 }
1289d62bc4baSyz147064 
1290d62bc4baSyz147064 /*
12915d460eafSCathy Zhou  * Set up the lower stream above the legacy device. There are two different
12925d460eafSCathy Zhou  * type of lower streams:
12935d460eafSCathy Zhou  *
12945d460eafSCathy Zhou  * - Shared lower-stream
12955d460eafSCathy Zhou  *
12965d460eafSCathy Zhou  * Shared by all GLDv3 MAC clients. Put the lower stream to the DLIOCRAW
12975d460eafSCathy Zhou  * mode to send and receive the raw data. Further, put the lower stream into
1298d62bc4baSyz147064  * DL_PROMISC_SAP mode to receive all packets of interest.
12995d460eafSCathy Zhou  *
13005d460eafSCathy Zhou  * - Dedicated lower-stream
13015d460eafSCathy Zhou  *
13025d460eafSCathy Zhou  * The lower-stream which is dedicated to upper IP/ARP stream. This is used
13035d460eafSCathy Zhou  * as fast-path for IP. In this case, the second argument is the pointer to
13045d460eafSCathy Zhou  * the softmac upper-stream.
1305d62bc4baSyz147064  */
13065d460eafSCathy Zhou int
13075d460eafSCathy Zhou softmac_lower_setup(softmac_t *softmac, softmac_upper_t *sup,
13085d460eafSCathy Zhou     softmac_lower_t **slpp)
1309d62bc4baSyz147064 {
1310d62bc4baSyz147064 	ldi_ident_t		li;
1311d62bc4baSyz147064 	dev_t			dev;
1312d62bc4baSyz147064 	ldi_handle_t		lh = NULL;
1313d62bc4baSyz147064 	softmac_lower_t		*slp = NULL;
1314d62bc4baSyz147064 	smac_ioc_start_t	start_arg;
1315d62bc4baSyz147064 	struct strioctl		strioc;
1316d62bc4baSyz147064 	uint32_t		notifications;
1317d62bc4baSyz147064 	int			err, rval;
1318d62bc4baSyz147064 
1319d62bc4baSyz147064 	if ((err = ldi_ident_from_dip(softmac_dip, &li)) != 0)
1320d62bc4baSyz147064 		return (err);
1321d62bc4baSyz147064 
13225d460eafSCathy Zhou 	/*
13235d460eafSCathy Zhou 	 * The GLDv3 framework makes sure that mac_unregister(), mac_open(),
13245d460eafSCathy Zhou 	 * and mac_close() cannot be called at the same time. So we don't
13255d460eafSCathy Zhou 	 * need any protection to access softmac here.
13265d460eafSCathy Zhou 	 */
1327d62bc4baSyz147064 	dev = softmac->smac_dev;
13285d460eafSCathy Zhou 
1329d62bc4baSyz147064 	err = ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh, li);
1330d62bc4baSyz147064 	ldi_ident_release(li);
1331d62bc4baSyz147064 	if (err != 0)
1332d62bc4baSyz147064 		goto done;
1333d62bc4baSyz147064 
1334d62bc4baSyz147064 	/*
1335d62bc4baSyz147064 	 * Pop all the intermediate modules. The autopushed modules will
1336d62bc4baSyz147064 	 * be pushed when the softmac node is opened.
1337d62bc4baSyz147064 	 */
1338d62bc4baSyz147064 	while (ldi_ioctl(lh, I_POP, 0, FKIOCTL, kcred, &rval) == 0)
1339d62bc4baSyz147064 		;
1340d62bc4baSyz147064 
1341d62bc4baSyz147064 	if ((softmac->smac_style == DL_STYLE2) &&
1342d62bc4baSyz147064 	    ((err = dl_attach(lh, softmac->smac_uppa, NULL)) != 0)) {
1343d62bc4baSyz147064 		goto done;
1344d62bc4baSyz147064 	}
1345d62bc4baSyz147064 
1346d62bc4baSyz147064 	/*
13475d460eafSCathy Zhou 	 * If this is the shared-lower-stream, put the lower stream to
13485d460eafSCathy Zhou 	 * the DLIOCRAW mode to send/receive raw data.
1349d62bc4baSyz147064 	 */
13505d460eafSCathy Zhou 	if ((sup == NULL) && (err = ldi_ioctl(lh, DLIOCRAW, 0, FKIOCTL,
13515d460eafSCathy Zhou 	    kcred, &rval)) != 0) {
1352d62bc4baSyz147064 		goto done;
13535d460eafSCathy Zhou 	}
1354d62bc4baSyz147064 
1355d62bc4baSyz147064 	/*
1356d62bc4baSyz147064 	 * Then push the softmac shim layer atop the lower stream.
1357d62bc4baSyz147064 	 */
1358d62bc4baSyz147064 	if ((err = ldi_ioctl(lh, I_PUSH, (intptr_t)SOFTMAC_DEV_NAME, FKIOCTL,
1359d62bc4baSyz147064 	    kcred, &rval)) != 0) {
1360d62bc4baSyz147064 		goto done;
1361d62bc4baSyz147064 	}
1362d62bc4baSyz147064 
1363d62bc4baSyz147064 	/*
1364d62bc4baSyz147064 	 * Send the ioctl to get the slp pointer.
1365d62bc4baSyz147064 	 */
1366d62bc4baSyz147064 	strioc.ic_cmd = SMAC_IOC_START;
1367d62bc4baSyz147064 	strioc.ic_timout = INFTIM;
1368d62bc4baSyz147064 	strioc.ic_len = sizeof (start_arg);
1369d62bc4baSyz147064 	strioc.ic_dp = (char *)&start_arg;
1370d62bc4baSyz147064 
1371d62bc4baSyz147064 	if ((err = ldi_ioctl(lh, I_STR, (intptr_t)&strioc, FKIOCTL,
1372d62bc4baSyz147064 	    kcred, &rval)) != 0) {
1373d62bc4baSyz147064 		goto done;
1374d62bc4baSyz147064 	}
1375d62bc4baSyz147064 	slp = start_arg.si_slp;
13765d460eafSCathy Zhou 	slp->sl_sup = sup;
1377d62bc4baSyz147064 	slp->sl_lh = lh;
1378d62bc4baSyz147064 	slp->sl_softmac = softmac;
1379d62bc4baSyz147064 	*slpp = slp;
1380d62bc4baSyz147064 
13815d460eafSCathy Zhou 	if (sup != NULL) {
13825d460eafSCathy Zhou 		slp->sl_rxinfo = &sup->su_rxinfo;
13835d460eafSCathy Zhou 	} else {
1384d62bc4baSyz147064 		/*
13855d460eafSCathy Zhou 		 * Send DL_NOTIFY_REQ to enable certain DL_NOTIFY_IND.
1386d62bc4baSyz147064 		 * We don't have to wait for the ack.
1387d62bc4baSyz147064 		 */
1388d62bc4baSyz147064 		notifications = DL_NOTE_PHYS_ADDR | DL_NOTE_LINK_UP |
1389d62bc4baSyz147064 		    DL_NOTE_LINK_DOWN | DL_NOTE_PROMISC_ON_PHYS |
1390d62bc4baSyz147064 		    DL_NOTE_PROMISC_OFF_PHYS;
1391d62bc4baSyz147064 
1392d62bc4baSyz147064 		(void) softmac_send_notify_req(slp,
1393d62bc4baSyz147064 		    (notifications & softmac->smac_notifications));
13945d460eafSCathy Zhou 	}
1395d62bc4baSyz147064 
1396d62bc4baSyz147064 done:
1397d62bc4baSyz147064 	if (err != 0)
1398d62bc4baSyz147064 		(void) ldi_close(lh, FREAD|FWRITE, kcred);
1399d62bc4baSyz147064 	return (err);
1400d62bc4baSyz147064 }
1401d62bc4baSyz147064 
1402d62bc4baSyz147064 static int
1403d62bc4baSyz147064 softmac_m_open(void *arg)
1404d62bc4baSyz147064 {
1405d62bc4baSyz147064 	softmac_t	*softmac = arg;
1406d62bc4baSyz147064 	softmac_lower_t	*slp;
1407d62bc4baSyz147064 	int		err;
1408d62bc4baSyz147064 
1409da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
1410d62bc4baSyz147064 
14115d460eafSCathy Zhou 	if ((err = softmac_lower_setup(softmac, NULL, &slp)) != 0)
1412d62bc4baSyz147064 		return (err);
1413d62bc4baSyz147064 
1414d62bc4baSyz147064 	softmac->smac_lower = slp;
1415d62bc4baSyz147064 	return (0);
1416d62bc4baSyz147064 }
1417d62bc4baSyz147064 
1418d62bc4baSyz147064 static void
1419d62bc4baSyz147064 softmac_m_close(void *arg)
1420d62bc4baSyz147064 {
1421d62bc4baSyz147064 	softmac_t	*softmac = arg;
1422d62bc4baSyz147064 	softmac_lower_t	*slp;
1423d62bc4baSyz147064 
1424da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
1425d62bc4baSyz147064 	slp = softmac->smac_lower;
1426d62bc4baSyz147064 	ASSERT(slp != NULL);
1427d62bc4baSyz147064 
1428d62bc4baSyz147064 	/*
1429d62bc4baSyz147064 	 * Note that slp is destroyed when lh is closed.
1430d62bc4baSyz147064 	 */
1431d62bc4baSyz147064 	(void) ldi_close(slp->sl_lh, FREAD|FWRITE, kcred);
1432d62bc4baSyz147064 	softmac->smac_lower = NULL;
1433d62bc4baSyz147064 }
1434d62bc4baSyz147064 
14355d460eafSCathy Zhou /*
14365d460eafSCathy Zhou  * Softmac supports two priviate link properteis:
14375d460eafSCathy Zhou  *
14385d460eafSCathy Zhou  * - "_fastpath"
14395d460eafSCathy Zhou  *
14405d460eafSCathy Zhou  *    This is a read-only link property which points out the current data-path
14415d460eafSCathy Zhou  *    model of the given legacy link. The possible values are "disabled" and
14425d460eafSCathy Zhou  *    "enabled".
14435d460eafSCathy Zhou  *
14445d460eafSCathy Zhou  * - "_disable_fastpath"
14455d460eafSCathy Zhou  *
14465d460eafSCathy Zhou  *    This is a read-write link property which can be used to disable or enable
14475d460eafSCathy Zhou  *    the fast-path of the given legacy link. The possible values are "true"
14485d460eafSCathy Zhou  *    and "false". Note that even when "_disable_fastpath" is set to be
14495d460eafSCathy Zhou  *    "false", the fast-path may still not be enabled since there may be
14505d460eafSCathy Zhou  *    other mac cleints that request the fast-path to be disabled.
14515d460eafSCathy Zhou  */
14525d460eafSCathy Zhou /* ARGSUSED */
14535d460eafSCathy Zhou static int
14545d460eafSCathy Zhou softmac_m_setprop(void *arg, const char *name, mac_prop_id_t id,
14555d460eafSCathy Zhou     uint_t valsize, const void *val)
14565d460eafSCathy Zhou {
14575d460eafSCathy Zhou 	softmac_t	*softmac = arg;
14585d460eafSCathy Zhou 
14595d460eafSCathy Zhou 	if (id != MAC_PROP_PRIVATE || strcmp(name, "_disable_fastpath") != 0)
14605d460eafSCathy Zhou 		return (ENOTSUP);
14615d460eafSCathy Zhou 
14625d460eafSCathy Zhou 	if (strcmp(val, "true") == 0)
14635d460eafSCathy Zhou 		return (softmac_datapath_switch(softmac, B_TRUE, B_TRUE));
14645d460eafSCathy Zhou 	else if (strcmp(val, "false") == 0)
14655d460eafSCathy Zhou 		return (softmac_datapath_switch(softmac, B_FALSE, B_TRUE));
14665d460eafSCathy Zhou 	else
14675d460eafSCathy Zhou 		return (EINVAL);
14685d460eafSCathy Zhou }
14695d460eafSCathy Zhou 
14705d460eafSCathy Zhou static int
14715d460eafSCathy Zhou softmac_m_getprop(void *arg, const char *name, mac_prop_id_t id, uint_t flags,
14725d460eafSCathy Zhou     uint_t valsize, void *val, uint_t *perm)
14735d460eafSCathy Zhou {
14745d460eafSCathy Zhou 	softmac_t	*softmac = arg;
14755d460eafSCathy Zhou 	char		*fpstr;
14765d460eafSCathy Zhou 
14775d460eafSCathy Zhou 	if (id != MAC_PROP_PRIVATE)
14785d460eafSCathy Zhou 		return (ENOTSUP);
14795d460eafSCathy Zhou 
14805d460eafSCathy Zhou 	if (strcmp(name, "_fastpath") == 0) {
14815d460eafSCathy Zhou 		if ((flags & MAC_PROP_DEFAULT) != 0)
14825d460eafSCathy Zhou 			return (ENOTSUP);
14835d460eafSCathy Zhou 
14845d460eafSCathy Zhou 		*perm = MAC_PROP_PERM_READ;
14855d460eafSCathy Zhou 		mutex_enter(&softmac->smac_fp_mutex);
14865d460eafSCathy Zhou 		fpstr = (DATAPATH_MODE(softmac) == SOFTMAC_SLOWPATH) ?
14875d460eafSCathy Zhou 		    "disabled" : "enabled";
14885d460eafSCathy Zhou 		mutex_exit(&softmac->smac_fp_mutex);
14895d460eafSCathy Zhou 	} else if (strcmp(name, "_disable_fastpath") == 0) {
14905d460eafSCathy Zhou 		*perm = MAC_PROP_PERM_RW;
14915d460eafSCathy Zhou 		fpstr = ((flags & MAC_PROP_DEFAULT) != 0) ? "false" :
14925d460eafSCathy Zhou 		    (softmac->smac_fastpath_admin_disabled ? "true" : "false");
14935d460eafSCathy Zhou 	} else {
14945d460eafSCathy Zhou 		return (ENOTSUP);
14955d460eafSCathy Zhou 	}
14965d460eafSCathy Zhou 
14975d460eafSCathy Zhou 	return (strlcpy(val, fpstr, valsize) >= valsize ? EINVAL : 0);
14985d460eafSCathy Zhou }
14995d460eafSCathy Zhou 
1500d62bc4baSyz147064 int
1501d62bc4baSyz147064 softmac_hold_device(dev_t dev, dls_dev_handle_t *ddhp)
1502d62bc4baSyz147064 {
1503d62bc4baSyz147064 	dev_info_t	*dip;
1504d81b850fSyz147064 	const char	*drvname;
1505d62bc4baSyz147064 	char		devname[MAXNAMELEN];
1506d62bc4baSyz147064 	softmac_t	*softmac;
150761af1958SGarrett D'Amore 	int		ppa, err, inst;
150861af1958SGarrett D'Amore 
150961af1958SGarrett D'Amore 	drvname = ddi_major_to_name(getmajor(dev));
151061af1958SGarrett D'Amore 
151161af1958SGarrett D'Amore 	/*
151261af1958SGarrett D'Amore 	 * We have to lookup the device instance using getinfo(9e).
151361af1958SGarrett D'Amore 	 */
151461af1958SGarrett D'Amore 	inst = dev_to_instance(dev);
151561af1958SGarrett D'Amore 	if (inst < 0)
151661af1958SGarrett D'Amore 		return (ENOENT);
1517d62bc4baSyz147064 
1518*3ade6e84SGarrett D'Amore 	if ((ppa = getminor(dev) - 1) > DLS_MAX_PPA)
1519d62bc4baSyz147064 		return (ENOENT);
1520d62bc4baSyz147064 
1521d62bc4baSyz147064 	/*
1522d62bc4baSyz147064 	 * First try to hold this device instance to force the MAC
1523d62bc4baSyz147064 	 * to be registered.
1524d62bc4baSyz147064 	 */
152561af1958SGarrett D'Amore 	if ((dip = ddi_hold_devi_by_instance(getmajor(dev), inst, 0)) == NULL)
1526d62bc4baSyz147064 		return (ENOENT);
1527d62bc4baSyz147064 
1528d62bc4baSyz147064 	/*
1529*3ade6e84SGarrett D'Amore 	 * Exclude non-physical network device instances, for example, aggr0.
1530*3ade6e84SGarrett D'Amore 	 * Note: this check *must* occur after the dip is held, or else
1531*3ade6e84SGarrett D'Amore 	 * NETWORK_DRV might return false incorrectly.  (Essentially, the
1532*3ade6e84SGarrett D'Amore 	 * driver needs to be loaded to populate the dev_ops structure
1533*3ade6e84SGarrett D'Amore 	 * that NETWORK_DRV checks.)
1534*3ade6e84SGarrett D'Amore 	 */
1535*3ade6e84SGarrett D'Amore 	if (!NETWORK_DRV(getmajor(dev)) || (strcmp(drvname, "aggr") == 0) ||
1536*3ade6e84SGarrett D'Amore 	    (strcmp(drvname, "vnic") == 0)) {
1537*3ade6e84SGarrett D'Amore 		ddi_release_devi(dip);
1538*3ade6e84SGarrett D'Amore 		return (ENOENT);
1539*3ade6e84SGarrett D'Amore 	}
1540*3ade6e84SGarrett D'Amore 
1541*3ade6e84SGarrett D'Amore 	/*
1542d62bc4baSyz147064 	 * This is a network device; wait for its softmac to be registered.
1543d62bc4baSyz147064 	 */
1544d81b850fSyz147064 	(void) snprintf(devname, MAXNAMELEN, "%s%d", drvname, ppa);
1545d62bc4baSyz147064 again:
1546d62bc4baSyz147064 	rw_enter(&softmac_hash_lock, RW_READER);
1547d62bc4baSyz147064 
1548d62bc4baSyz147064 	if (mod_hash_find(softmac_hash, (mod_hash_key_t)devname,
1549d62bc4baSyz147064 	    (mod_hash_val_t *)&softmac) != 0) {
1550d62bc4baSyz147064 		/*
1551d62bc4baSyz147064 		 * This is rare but possible. It could happen when pre-detach
1552d62bc4baSyz147064 		 * routine of the device succeeds. But the softmac will then
1553d62bc4baSyz147064 		 * be recreated when device fails to detach (as this device
1554d62bc4baSyz147064 		 * is held).
1555d62bc4baSyz147064 		 */
1556da14cebeSEric Cheng 		mutex_enter(&smac_global_lock);
1557d62bc4baSyz147064 		rw_exit(&softmac_hash_lock);
1558da14cebeSEric Cheng 		cv_wait(&smac_global_cv, &smac_global_lock);
1559da14cebeSEric Cheng 		mutex_exit(&smac_global_lock);
1560d62bc4baSyz147064 		goto again;
1561d62bc4baSyz147064 	}
1562d62bc4baSyz147064 
1563d62bc4baSyz147064 	/*
1564d62bc4baSyz147064 	 * Bump smac_hold_cnt to prevent device detach.
1565d62bc4baSyz147064 	 */
1566d62bc4baSyz147064 	mutex_enter(&softmac->smac_mutex);
1567d62bc4baSyz147064 	softmac->smac_hold_cnt++;
1568d62bc4baSyz147064 	rw_exit(&softmac_hash_lock);
1569d62bc4baSyz147064 
1570d62bc4baSyz147064 	/*
1571d62bc4baSyz147064 	 * Wait till the device is fully attached.
1572d62bc4baSyz147064 	 */
1573da14cebeSEric Cheng 	while (softmac->smac_state != SOFTMAC_ATTACH_DONE)
1574d62bc4baSyz147064 		cv_wait(&softmac->smac_cv, &softmac->smac_mutex);
1575d62bc4baSyz147064 
1576da14cebeSEric Cheng 	SOFTMAC_STATE_VERIFY(softmac);
1577da14cebeSEric Cheng 
1578050fb016Syz147064 	if ((err = softmac->smac_attacherr) != 0)
1579050fb016Syz147064 		softmac->smac_hold_cnt--;
1580050fb016Syz147064 	else
1581d62bc4baSyz147064 		*ddhp = (dls_dev_handle_t)softmac;
1582d62bc4baSyz147064 	mutex_exit(&softmac->smac_mutex);
1583d62bc4baSyz147064 
1584050fb016Syz147064 	ddi_release_devi(dip);
1585d62bc4baSyz147064 	return (err);
1586d62bc4baSyz147064 }
1587d62bc4baSyz147064 
1588d62bc4baSyz147064 void
1589d62bc4baSyz147064 softmac_rele_device(dls_dev_handle_t ddh)
1590d62bc4baSyz147064 {
15915d460eafSCathy Zhou 	if (ddh != NULL)
15925d460eafSCathy Zhou 		softmac_rele((softmac_t *)ddh);
15935d460eafSCathy Zhou }
15945d460eafSCathy Zhou 
15955d460eafSCathy Zhou int
15965d460eafSCathy Zhou softmac_hold(dev_t dev, softmac_t **softmacp)
15975d460eafSCathy Zhou {
1598d62bc4baSyz147064 	softmac_t	*softmac;
15995d460eafSCathy Zhou 	char		*drv;
16005d460eafSCathy Zhou 	mac_handle_t	mh;
16015d460eafSCathy Zhou 	char		mac[MAXNAMELEN];
16025d460eafSCathy Zhou 	int		err;
1603d62bc4baSyz147064 
16045d460eafSCathy Zhou 	if ((drv = ddi_major_to_name(getmajor(dev))) == NULL)
16055d460eafSCathy Zhou 		return (EINVAL);
1606d62bc4baSyz147064 
16075d460eafSCathy Zhou 	(void) snprintf(mac, MAXNAMELEN, "%s%d", drv, getminor(dev) - 1);
16085d460eafSCathy Zhou 	if ((err = mac_open(mac, &mh)) != 0)
16095d460eafSCathy Zhou 		return (err);
16105d460eafSCathy Zhou 
16115d460eafSCathy Zhou 	softmac = (softmac_t *)mac_driver(mh);
16125d460eafSCathy Zhou 
16135d460eafSCathy Zhou 	mutex_enter(&softmac->smac_mutex);
16145d460eafSCathy Zhou 	softmac->smac_hold_cnt++;
16155d460eafSCathy Zhou 	mutex_exit(&softmac->smac_mutex);
16165d460eafSCathy Zhou 	mac_close(mh);
16175d460eafSCathy Zhou 	*softmacp = softmac;
16185d460eafSCathy Zhou 	return (0);
16195d460eafSCathy Zhou }
16205d460eafSCathy Zhou 
16215d460eafSCathy Zhou void
16225d460eafSCathy Zhou softmac_rele(softmac_t *softmac)
16235d460eafSCathy Zhou {
1624d62bc4baSyz147064 	mutex_enter(&softmac->smac_mutex);
1625050fb016Syz147064 	softmac->smac_hold_cnt--;
1626d62bc4baSyz147064 	mutex_exit(&softmac->smac_mutex);
1627d62bc4baSyz147064 }
1628