xref: /illumos-gate/usr/src/uts/common/io/softmac/softmac_main.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * The softmac driver is used to "unify" non-GLDv3 drivers to the GLDv3
28  * framework.  It also creates the kernel datalink structure for each
29  * physical network device.
30  *
31  * Specifically, a softmac will be created for each physical network device
32  * (dip) during the device's post-attach process.  When this softmac is
33  * created, the following will also be done:
34  *   - create the device's <link name, linkid> mapping;
35  *   - register the mac if this is a non-GLDv3 device and the media type is
36  *     supported by the GLDv3 framework;
37  *   - create the kernel data-link structure for this physical device;
38  *
39  * This softmac will be destroyed during the device's pre-detach process,
40  * and all the above will be undone.
41  */
42 
43 #include <sys/types.h>
44 #include <sys/file.h>
45 #include <sys/cred.h>
46 #include <sys/dlpi.h>
47 #include <sys/mac_provider.h>
48 #include <sys/disp.h>
49 #include <sys/sunndi.h>
50 #include <sys/modhash.h>
51 #include <sys/stropts.h>
52 #include <sys/sysmacros.h>
53 #include <sys/vlan.h>
54 #include <sys/softmac_impl.h>
55 #include <sys/softmac.h>
56 #include <sys/dls.h>
57 
58 /* Used as a parameter to the mod hash walk of softmac structures */
59 typedef struct {
60 	softmac_t	*smw_softmac;
61 	boolean_t	smw_retry;
62 } softmac_walk_t;
63 
64 /*
65  * Softmac hash table including softmacs for both style-2 and style-1 devices.
66  */
67 static krwlock_t	softmac_hash_lock;
68 static mod_hash_t	*softmac_hash;
69 static kmutex_t		smac_global_lock;
70 static kcondvar_t	smac_global_cv;
71 
72 static kmem_cache_t	*softmac_cachep;
73 
74 #define	SOFTMAC_HASHSZ		64
75 
76 static void softmac_create_task(void *);
77 static void softmac_mac_register(softmac_t *);
78 static int softmac_create_datalink(softmac_t *);
79 static int softmac_m_start(void *);
80 static void softmac_m_stop(void *);
81 static int softmac_m_open(void *);
82 static void softmac_m_close(void *);
83 static boolean_t softmac_m_getcapab(void *, mac_capab_t, void *);
84 static int softmac_m_setprop(void *, const char *, mac_prop_id_t,
85     uint_t, const void *);
86 static int softmac_m_getprop(void *, const char *, mac_prop_id_t,
87     uint_t, uint_t, void *, uint_t *);
88 
89 
90 #define	SOFTMAC_M_CALLBACK_FLAGS	\
91 	(MC_IOCTL | MC_GETCAPAB | MC_OPEN | MC_CLOSE | MC_SETPROP | MC_GETPROP)
92 
93 static mac_callbacks_t softmac_m_callbacks = {
94 	SOFTMAC_M_CALLBACK_FLAGS,
95 	softmac_m_stat,
96 	softmac_m_start,
97 	softmac_m_stop,
98 	softmac_m_promisc,
99 	softmac_m_multicst,
100 	softmac_m_unicst,
101 	softmac_m_tx,
102 	softmac_m_ioctl,
103 	softmac_m_getcapab,
104 	softmac_m_open,
105 	softmac_m_close,
106 	softmac_m_setprop,
107 	softmac_m_getprop
108 };
109 
110 /*ARGSUSED*/
111 static int
112 softmac_constructor(void *buf, void *arg, int kmflag)
113 {
114 	softmac_t	*softmac = buf;
115 
116 	bzero(buf, sizeof (softmac_t));
117 	mutex_init(&softmac->smac_mutex, NULL, MUTEX_DEFAULT, NULL);
118 	mutex_init(&softmac->smac_active_mutex, NULL, MUTEX_DEFAULT, NULL);
119 	mutex_init(&softmac->smac_fp_mutex, NULL, MUTEX_DEFAULT, NULL);
120 	cv_init(&softmac->smac_cv, NULL, CV_DEFAULT, NULL);
121 	cv_init(&softmac->smac_fp_cv, NULL, CV_DEFAULT, NULL);
122 	list_create(&softmac->smac_sup_list, sizeof (softmac_upper_t),
123 	    offsetof(softmac_upper_t, su_list_node));
124 	return (0);
125 }
126 
127 /*ARGSUSED*/
128 static void
129 softmac_destructor(void *buf, void *arg)
130 {
131 	softmac_t	*softmac = buf;
132 
133 	ASSERT(softmac->smac_fp_disable_clients == 0);
134 	ASSERT(!softmac->smac_fastpath_admin_disabled);
135 
136 	ASSERT(!(softmac->smac_flags & SOFTMAC_ATTACH_DONE));
137 	ASSERT(softmac->smac_hold_cnt == 0);
138 	ASSERT(softmac->smac_attachok_cnt == 0);
139 	ASSERT(softmac->smac_mh == NULL);
140 	ASSERT(softmac->smac_softmac[0] == NULL &&
141 	    softmac->smac_softmac[1] == NULL);
142 	ASSERT(softmac->smac_state == SOFTMAC_INITIALIZED);
143 	ASSERT(softmac->smac_lower == NULL);
144 	ASSERT(softmac->smac_active == B_FALSE);
145 	ASSERT(softmac->smac_nactive == 0);
146 	ASSERT(list_is_empty(&softmac->smac_sup_list));
147 
148 	list_destroy(&softmac->smac_sup_list);
149 	mutex_destroy(&softmac->smac_mutex);
150 	mutex_destroy(&softmac->smac_active_mutex);
151 	mutex_destroy(&softmac->smac_fp_mutex);
152 	cv_destroy(&softmac->smac_cv);
153 	cv_destroy(&softmac->smac_fp_cv);
154 }
155 
156 void
157 softmac_init()
158 {
159 	softmac_hash = mod_hash_create_extended("softmac_hash",
160 	    SOFTMAC_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
161 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
162 
163 	rw_init(&softmac_hash_lock, NULL, RW_DEFAULT, NULL);
164 	mutex_init(&smac_global_lock, NULL, MUTEX_DRIVER, NULL);
165 	cv_init(&smac_global_cv, NULL, CV_DRIVER, NULL);
166 
167 	softmac_cachep = kmem_cache_create("softmac_cache",
168 	    sizeof (softmac_t), 0, softmac_constructor,
169 	    softmac_destructor, NULL, NULL, NULL, 0);
170 	ASSERT(softmac_cachep != NULL);
171 	softmac_fp_init();
172 }
173 
174 void
175 softmac_fini()
176 {
177 	softmac_fp_fini();
178 	kmem_cache_destroy(softmac_cachep);
179 	rw_destroy(&softmac_hash_lock);
180 	mod_hash_destroy_hash(softmac_hash);
181 	mutex_destroy(&smac_global_lock);
182 	cv_destroy(&smac_global_cv);
183 }
184 
185 /* ARGSUSED */
186 static uint_t
187 softmac_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
188 {
189 	boolean_t *pexist = arg;
190 
191 	*pexist = B_TRUE;
192 	return (MH_WALK_TERMINATE);
193 }
194 
195 boolean_t
196 softmac_busy()
197 {
198 	boolean_t exist = B_FALSE;
199 
200 	rw_enter(&softmac_hash_lock, RW_READER);
201 	mod_hash_walk(softmac_hash, softmac_exist, &exist);
202 	rw_exit(&softmac_hash_lock);
203 	return (exist);
204 }
205 
206 /*
207  *
208  * softmac_create() is called for each minor node during the post-attach of
209  * each DDI_NT_NET device instance.  Note that it is possible that a device
210  * instance has two minor nodes (DLPI style-1 and style-2), so that for that
211  * specific device, softmac_create() could be called twice.
212  *
213  * A softmac_t is used to track each DDI_NT_NET device, and a softmac_dev_t
214  * is created to track each minor node.
215  *
216  * For each minor node of a legacy device, a taskq is started to finish
217  * softmac_mac_register(), which will finish the rest of work (see comments
218  * above softmac_mac_register()).
219  *
220  *			softmac state machine
221  * --------------------------------------------------------------------------
222  * OLD STATE		EVENT					NEW STATE
223  * --------------------------------------------------------------------------
224  * UNINIT		attach of 1st minor node 		ATTACH_INPROG
225  * okcnt = 0		net_postattach -> softmac_create	okcnt = 1
226  *
227  * ATTACH_INPROG	attach of 2nd minor node (GLDv3)	ATTACH_DONE
228  * okcnt = 1		net_postattach -> softmac_create	okcnt = 2
229  *
230  * ATTACH_INPROG	attach of 2nd minor node (legacy)	ATTACH_INPROG
231  * okcnt = 1		net_postattach -> softmac_create	okcnt = 2
232  *			schedule softmac_mac_register
233  *
234  * ATTACH_INPROG	legacy device node			ATTACH_DONE
235  * okcnt = 2		softmac_mac_register			okcnt = 2
236  *
237  * ATTACH_DONE		detach of 1st minor node		DETACH_INPROG
238  * okcnt = 2		(success)				okcnt = 1
239  *
240  * DETACH_INPROG	detach of 2nd minor node		UNINIT (or free)
241  * okcnt = 1		(success)				okcnt = 0
242  *
243  * ATTACH_DONE		detach failure				state unchanged
244  * DETACH_INPROG						left = okcnt
245  *
246  * DETACH_INPROG	reattach				ATTACH_INPROG
247  * okcnt = 0,1		net_postattach -> softmac_create
248  *
249  * ATTACH_DONE		reattach				ATTACH_DONE
250  * left != 0		net_postattach -> softmac_create	left = 0
251  *
252  * Abbreviation notes:
253  * states have SOFTMAC_ prefix,
254  * okcnt - softmac_attach_okcnt,
255  * left - softmac_attached_left
256  */
257 
258 #ifdef DEBUG
259 void
260 softmac_state_verify(softmac_t *softmac)
261 {
262 	ASSERT(MUTEX_HELD(&softmac->smac_mutex));
263 
264 	/*
265 	 * There are at most 2 minor nodes, one per DLPI style
266 	 */
267 	ASSERT(softmac->smac_cnt <= 2 && softmac->smac_attachok_cnt <= 2);
268 
269 	/*
270 	 * The smac_attachok_cnt represents the number of attaches i.e. the
271 	 * number of times net_postattach -> softmac_create() has been called
272 	 * for a device instance.
273 	 */
274 	ASSERT(softmac->smac_attachok_cnt == SMAC_NONZERO_NODECNT(softmac));
275 
276 	/*
277 	 * softmac_create (or softmac_mac_register) ->  softmac_create_datalink
278 	 * happens only after all minor nodes have been attached
279 	 */
280 	ASSERT(softmac->smac_state != SOFTMAC_ATTACH_DONE ||
281 	    softmac->smac_attachok_cnt == softmac->smac_cnt);
282 
283 	if (softmac->smac_attachok_cnt == 0) {
284 		ASSERT(softmac->smac_state == SOFTMAC_UNINIT);
285 		ASSERT(softmac->smac_mh == NULL);
286 	} else if (softmac->smac_attachok_cnt < softmac->smac_cnt) {
287 		ASSERT(softmac->smac_state == SOFTMAC_ATTACH_INPROG ||
288 		    softmac->smac_state == SOFTMAC_DETACH_INPROG);
289 		ASSERT(softmac->smac_mh == NULL);
290 	} else {
291 		/*
292 		 * In the stable condition the state whould be
293 		 * SOFTMAC_ATTACH_DONE. But there is a small transient window
294 		 * in softmac_destroy where we change the state to
295 		 * SOFTMAC_DETACH_INPROG and drop the lock before doing
296 		 * the link destroy
297 		 */
298 		ASSERT(softmac->smac_attachok_cnt == softmac->smac_cnt);
299 		ASSERT(softmac->smac_state != SOFTMAC_UNINIT);
300 	}
301 	if (softmac->smac_mh != NULL)
302 		ASSERT(softmac->smac_attachok_cnt == softmac->smac_cnt);
303 }
304 #endif
305 
306 #ifdef DEBUG
307 #define	SOFTMAC_STATE_VERIFY(softmac)	softmac_state_verify(softmac)
308 #else
309 #define	SOFTMAC_STATE_VERIFY(softmac)
310 #endif
311 
312 int
313 softmac_create(dev_info_t *dip, dev_t dev)
314 {
315 	char		devname[MAXNAMELEN];
316 	softmac_t	*softmac;
317 	softmac_dev_t	*softmac_dev = NULL;
318 	int		index;
319 	int		ppa, err = 0;
320 
321 	/*
322 	 * Force the softmac driver to be attached.
323 	 */
324 	if (i_ddi_attach_pseudo_node(SOFTMAC_DEV_NAME) == NULL) {
325 		cmn_err(CE_WARN, "softmac_create:softmac attach fails");
326 		return (ENXIO);
327 	}
328 
329 	ppa = ddi_get_instance(dip);
330 	(void) snprintf(devname, MAXNAMELEN, "%s%d", ddi_driver_name(dip), ppa);
331 
332 	/*
333 	 * We expect legacy devices have at most two minor nodes - one style-1
334 	 * and one style-2.
335 	 */
336 	if (!GLDV3_DRV(ddi_driver_major(dip)) &&
337 	    i_ddi_minor_node_count(dip, DDI_NT_NET) > 2) {
338 		cmn_err(CE_WARN, "%s has more than 2 minor nodes; unsupported",
339 		    devname);
340 		return (ENOTSUP);
341 	}
342 
343 	/*
344 	 * Check whether the softmac for the specified device already exists
345 	 */
346 	rw_enter(&softmac_hash_lock, RW_WRITER);
347 	if ((mod_hash_find(softmac_hash, (mod_hash_key_t)devname,
348 	    (mod_hash_val_t *)&softmac)) != 0) {
349 
350 		softmac = kmem_cache_alloc(softmac_cachep, KM_SLEEP);
351 		(void) strlcpy(softmac->smac_devname, devname, MAXNAMELEN);
352 
353 		err = mod_hash_insert(softmac_hash,
354 		    (mod_hash_key_t)softmac->smac_devname,
355 		    (mod_hash_val_t)softmac);
356 		ASSERT(err == 0);
357 		mutex_enter(&smac_global_lock);
358 		cv_broadcast(&smac_global_cv);
359 		mutex_exit(&smac_global_lock);
360 	}
361 
362 	mutex_enter(&softmac->smac_mutex);
363 	SOFTMAC_STATE_VERIFY(softmac);
364 	if (softmac->smac_state != SOFTMAC_ATTACH_DONE)
365 		softmac->smac_state = SOFTMAC_ATTACH_INPROG;
366 	if (softmac->smac_attachok_cnt == 0) {
367 		/*
368 		 * Initialize the softmac if this is the post-attach of the
369 		 * first minor node.
370 		 */
371 		softmac->smac_flags = 0;
372 		softmac->smac_umajor = ddi_driver_major(dip);
373 		softmac->smac_uppa = ppa;
374 
375 		/*
376 		 * Note that for GLDv3 devices, we create devfs minor nodes
377 		 * for VLANs as well. Assume a GLDv3 driver on which only
378 		 * a VLAN is created. During the detachment of this device
379 		 * instance, the following would happen:
380 		 * a. the pre-detach callback softmac_destroy() succeeds.
381 		 *    Because the physical link itself is not in use,
382 		 *    softmac_destroy() succeeds and destroys softmac_t;
383 		 * b. the device detach fails in mac_unregister() because
384 		 *    this MAC is still used by a VLAN.
385 		 * c. the post-attach callback is then called which leads
386 		 *    us here. Note that ddi_minor_node_count() returns 3
387 		 *    (including the minior node of the VLAN). In that case,
388 		 *    we must correct the minor node count to 2 as that is
389 		 *    the count of minor nodes that go through post-attach.
390 		 */
391 		if (GLDV3_DRV(ddi_driver_major(dip))) {
392 			softmac->smac_flags |= SOFTMAC_GLDV3;
393 			softmac->smac_cnt = 2;
394 		} else {
395 			softmac->smac_cnt =
396 			    i_ddi_minor_node_count(dip, DDI_NT_NET);
397 		}
398 	}
399 
400 	index = (getmajor(dev) == ddi_name_to_major("clone"));
401 	if (softmac->smac_softmac[index] != NULL) {
402 		/*
403 		 * This is possible if the post_attach() is called after
404 		 * pre_detach() fails. This seems to be a defect of the DACF
405 		 * framework. We work around it by using a smac_attached_left
406 		 * field that tracks this
407 		 */
408 		ASSERT(softmac->smac_attached_left != 0);
409 		softmac->smac_attached_left--;
410 		mutex_exit(&softmac->smac_mutex);
411 		rw_exit(&softmac_hash_lock);
412 		return (0);
413 
414 	}
415 	mutex_exit(&softmac->smac_mutex);
416 	rw_exit(&softmac_hash_lock);
417 
418 	softmac_dev = kmem_zalloc(sizeof (softmac_dev_t), KM_SLEEP);
419 	softmac_dev->sd_dev = dev;
420 
421 	mutex_enter(&softmac->smac_mutex);
422 	softmac->smac_softmac[index] = softmac_dev;
423 	/*
424 	 * Continue to register the mac and create the datalink only when all
425 	 * the minor nodes are attached.
426 	 */
427 	if (++softmac->smac_attachok_cnt != softmac->smac_cnt) {
428 		mutex_exit(&softmac->smac_mutex);
429 		return (0);
430 	}
431 
432 	/*
433 	 * All of the minor nodes have been attached; start a taskq
434 	 * to do the rest of the work.  We use a taskq instead of
435 	 * doing the work here because:
436 	 *
437 	 * We could be called as a result of a open() system call
438 	 * where spec_open() already SLOCKED the snode. Using a taskq
439 	 * sidesteps the risk that our ldi_open_by_dev() call would
440 	 * deadlock trying to set SLOCKED on the snode again.
441 	 *
442 	 * The devfs design requires that the downcalls don't use any
443 	 * interruptible cv_wait which happens when we do door upcalls.
444 	 * Otherwise the downcalls which may be holding devfs resources
445 	 * may cause a deadlock if the thread is stopped. Also we need to make
446 	 * sure these downcalls into softmac_create or softmac_destroy
447 	 * don't cv_wait on any devfs related condition. Thus softmac_destroy
448 	 * returns EBUSY if the asynchronous threads started in softmac_create
449 	 * haven't finished.
450 	 */
451 	(void) taskq_dispatch(system_taskq, softmac_create_task,
452 	    softmac, TQ_SLEEP);
453 	mutex_exit(&softmac->smac_mutex);
454 	return (0);
455 }
456 
457 static boolean_t
458 softmac_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
459 {
460 	softmac_t *softmac = arg;
461 
462 	if (!(softmac->smac_capab_flags & cap))
463 		return (B_FALSE);
464 
465 	switch (cap) {
466 	case MAC_CAPAB_HCKSUM: {
467 		uint32_t *txflags = cap_data;
468 
469 		*txflags = softmac->smac_hcksum_txflags;
470 		break;
471 	}
472 	case MAC_CAPAB_LEGACY: {
473 		mac_capab_legacy_t *legacy = cap_data;
474 
475 		/*
476 		 * The caller is not interested in the details.
477 		 */
478 		if (legacy == NULL)
479 			break;
480 
481 		legacy->ml_unsup_note = ~softmac->smac_notifications &
482 		    (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_SPEED);
483 		legacy->ml_active_set = softmac_active_set;
484 		legacy->ml_active_clear = softmac_active_clear;
485 		legacy->ml_fastpath_disable = softmac_fastpath_disable;
486 		legacy->ml_fastpath_enable = softmac_fastpath_enable;
487 		legacy->ml_dev = makedevice(softmac->smac_umajor,
488 		    softmac->smac_uppa + 1);
489 		break;
490 	}
491 
492 	/*
493 	 * For the capabilities below, there's nothing for us to fill in;
494 	 * simply return B_TRUE if we support it.
495 	 */
496 	case MAC_CAPAB_NO_ZCOPY:
497 	case MAC_CAPAB_NO_NATIVEVLAN:
498 	default:
499 		break;
500 	}
501 	return (B_TRUE);
502 }
503 
504 static int
505 softmac_update_info(softmac_t *softmac, datalink_id_t *linkidp)
506 {
507 	datalink_id_t	linkid = DATALINK_INVALID_LINKID;
508 	uint32_t	media;
509 	int		err;
510 
511 	if ((err = dls_mgmt_update(softmac->smac_devname, softmac->smac_media,
512 	    softmac->smac_flags & SOFTMAC_NOSUPP, &media, &linkid)) == 0) {
513 		*linkidp = linkid;
514 	}
515 
516 	if (err == EEXIST) {
517 		/*
518 		 * There is a link name conflict.  Either:
519 		 *
520 		 * - An existing link with the same device name with a
521 		 *   different media type from of the given type.
522 		 *   Mark this link back to persistent only; or
523 		 *
524 		 * - We cannot assign the "suggested" name because
525 		 *   GLDv3 and therefore vanity naming is not supported
526 		 *   for this link type. Delete this link's <link name,
527 		 *   linkid> mapping.
528 		 */
529 		if (media != softmac->smac_media) {
530 			cmn_err(CE_WARN, "%s device %s conflicts with "
531 			    "existing %s device %s.",
532 			    dl_mactypestr(softmac->smac_media),
533 			    softmac->smac_devname, dl_mactypestr(media),
534 			    softmac->smac_devname);
535 			(void) dls_mgmt_destroy(linkid, B_FALSE);
536 		} else {
537 			cmn_err(CE_WARN, "link name %s is already in-use.",
538 			    softmac->smac_devname);
539 			(void) dls_mgmt_destroy(linkid, B_TRUE);
540 		}
541 
542 		cmn_err(CE_WARN, "%s device might not be available "
543 		    "for use.", softmac->smac_devname);
544 		cmn_err(CE_WARN, "See dladm(1M) for more information.");
545 	}
546 
547 	return (err);
548 }
549 
550 /*
551  * This function:
552  * 1. provides the link's media type to dlmgmtd.
553  * 2. creates the GLDv3 datalink if the media type is supported by GLDv3.
554  */
555 static int
556 softmac_create_datalink(softmac_t *softmac)
557 {
558 	datalink_id_t	linkid = DATALINK_INVALID_LINKID;
559 	int		err;
560 
561 	/*
562 	 * Inform dlmgmtd of this link so that softmac_hold_device() is able
563 	 * to know the existence of this link. If this failed with EBADF,
564 	 * it might be because dlmgmtd was not started in time (e.g.,
565 	 * diskless boot); ignore the failure and continue to create
566 	 * the GLDv3 datalink if needed.
567 	 */
568 	err = dls_mgmt_create(softmac->smac_devname,
569 	    makedevice(softmac->smac_umajor, softmac->smac_uppa + 1),
570 	    DATALINK_CLASS_PHYS, DL_OTHER, B_TRUE, &linkid);
571 	if (err != 0 && err != EBADF)
572 		return (err);
573 
574 	/*
575 	 * Provide the media type of the physical link to dlmgmtd.
576 	 */
577 	if ((err != EBADF) &&
578 	    ((err = softmac_update_info(softmac, &linkid)) != 0)) {
579 		return (err);
580 	}
581 
582 	/*
583 	 * Create the GLDv3 datalink.
584 	 */
585 	if ((!(softmac->smac_flags & SOFTMAC_NOSUPP)) &&
586 	    ((err = dls_devnet_create(softmac->smac_mh, linkid)) != 0)) {
587 		cmn_err(CE_WARN, "dls_devnet_create failed for %s",
588 		    softmac->smac_devname);
589 		return (err);
590 	}
591 
592 	if (linkid == DATALINK_INVALID_LINKID) {
593 		mutex_enter(&softmac->smac_mutex);
594 		softmac->smac_flags |= SOFTMAC_NEED_RECREATE;
595 		mutex_exit(&softmac->smac_mutex);
596 	}
597 
598 	return (0);
599 }
600 
601 static void
602 softmac_create_task(void *arg)
603 {
604 	softmac_t	*softmac = arg;
605 	mac_handle_t	mh;
606 	int		err;
607 
608 	if (!GLDV3_DRV(softmac->smac_umajor)) {
609 		softmac_mac_register(softmac);
610 		return;
611 	}
612 
613 	if ((err = mac_open(softmac->smac_devname, &mh)) != 0)
614 		goto done;
615 
616 	mutex_enter(&softmac->smac_mutex);
617 	softmac->smac_media = (mac_info(mh))->mi_nativemedia;
618 	softmac->smac_mh = mh;
619 	mutex_exit(&softmac->smac_mutex);
620 
621 	/*
622 	 * We can safely release the reference on the mac because
623 	 * this mac will only be unregistered and destroyed when
624 	 * the device detaches, and the softmac will be destroyed
625 	 * before then (in the pre-detach routine of the device).
626 	 */
627 	mac_close(mh);
628 
629 	/*
630 	 * Create the GLDv3 datalink for this mac.
631 	 */
632 	err = softmac_create_datalink(softmac);
633 
634 done:
635 	mutex_enter(&softmac->smac_mutex);
636 	if (err != 0)
637 		softmac->smac_mh = NULL;
638 	softmac->smac_attacherr = err;
639 	softmac->smac_state = SOFTMAC_ATTACH_DONE;
640 	cv_broadcast(&softmac->smac_cv);
641 	mutex_exit(&softmac->smac_mutex);
642 }
643 
644 /*
645  * This function is only called for legacy devices. It:
646  * 1. registers the MAC for the legacy devices whose media type is supported
647  *    by the GLDv3 framework.
648  * 2. creates the GLDv3 datalink if the media type is supported by GLDv3.
649  */
650 static void
651 softmac_mac_register(softmac_t *softmac)
652 {
653 	softmac_dev_t	*softmac_dev;
654 	dev_t		dev;
655 	ldi_handle_t	lh = NULL;
656 	ldi_ident_t	li = NULL;
657 	int		index;
658 	boolean_t	native_vlan = B_FALSE;
659 	int		err;
660 
661 	/*
662 	 * Note that we do not need any locks to access this softmac pointer,
663 	 * as softmac_destroy() will wait until this function is called.
664 	 */
665 	ASSERT(softmac != NULL);
666 	ASSERT(softmac->smac_state == SOFTMAC_ATTACH_INPROG &&
667 	    softmac->smac_attachok_cnt == softmac->smac_cnt);
668 
669 	if ((err = ldi_ident_from_dip(softmac_dip, &li)) != 0) {
670 		mutex_enter(&softmac->smac_mutex);
671 		goto done;
672 	}
673 
674 	/*
675 	 * Determine whether this legacy device support VLANs by opening
676 	 * the style-2 device node (if it exists) and attaching to a VLAN
677 	 * PPA (1000 + ppa).
678 	 */
679 	dev = makedevice(ddi_name_to_major("clone"), softmac->smac_umajor);
680 	err = ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh, li);
681 	if (err == 0) {
682 		if (dl_attach(lh, softmac->smac_uppa + 1 * 1000, NULL) == 0)
683 			native_vlan = B_TRUE;
684 		(void) ldi_close(lh, FREAD|FWRITE, kcred);
685 	}
686 
687 	err = EINVAL;
688 	for (index = 0; index < 2; index++) {
689 		dl_info_ack_t	dlia;
690 		dl_error_ack_t	dlea;
691 		uint32_t	notes;
692 		struct strioctl	iocb;
693 		uint32_t	margin;
694 		int		rval;
695 
696 		if ((softmac_dev = softmac->smac_softmac[index]) == NULL)
697 			continue;
698 
699 		softmac->smac_dev = dev = softmac_dev->sd_dev;
700 		if (ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh,
701 		    li) != 0) {
702 			continue;
703 		}
704 
705 		/*
706 		 * Pop all the intermediate modules in order to negotiate
707 		 * capabilities correctly.
708 		 */
709 		while (ldi_ioctl(lh, I_POP, 0, FKIOCTL, kcred, &rval) == 0)
710 			;
711 
712 		/* DLPI style-1 or DLPI style-2? */
713 		if ((rval = dl_info(lh, &dlia, NULL, NULL, &dlea)) != 0) {
714 			if (rval == ENOTSUP) {
715 				cmn_err(CE_NOTE, "softmac: received "
716 				    "DL_ERROR_ACK to DL_INFO_ACK; "
717 				    "DLPI errno 0x%x, UNIX errno %d",
718 				    dlea.dl_errno, dlea.dl_unix_errno);
719 			}
720 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
721 			continue;
722 		}
723 
724 		/*
725 		 * Currently only DL_ETHER has GLDv3 mac plugin support.
726 		 * For media types that GLDv3 does not support, create a
727 		 * link id for it.
728 		 */
729 		if ((softmac->smac_media = dlia.dl_mac_type) != DL_ETHER) {
730 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
731 			err = 0;
732 			break;
733 		}
734 
735 		if ((dlia.dl_provider_style == DL_STYLE2) &&
736 		    (dl_attach(lh, softmac->smac_uppa, NULL) != 0)) {
737 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
738 			continue;
739 		}
740 
741 		if ((rval = dl_bind(lh, 0, NULL)) != 0) {
742 			if (rval == ENOTSUP) {
743 				cmn_err(CE_NOTE, "softmac: received "
744 				    "DL_ERROR_ACK to DL_BIND_ACK; "
745 				    "DLPI errno 0x%x, UNIX errno %d",
746 				    dlea.dl_errno, dlea.dl_unix_errno);
747 			}
748 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
749 			continue;
750 		}
751 
752 		/*
753 		 * Call dl_info() after dl_bind() because some drivers only
754 		 * provide correct information (e.g. MAC address) once bound.
755 		 */
756 		softmac->smac_addrlen = sizeof (softmac->smac_unicst_addr);
757 		if ((rval = dl_info(lh, &dlia, softmac->smac_unicst_addr,
758 		    &softmac->smac_addrlen, &dlea)) != 0) {
759 			if (rval == ENOTSUP) {
760 				cmn_err(CE_NOTE, "softmac: received "
761 				    "DL_ERROR_ACK to DL_INFO_ACK; "
762 				    "DLPI errno 0x%x, UNIX errno %d",
763 				    dlea.dl_errno, dlea.dl_unix_errno);
764 			}
765 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
766 			continue;
767 		}
768 
769 		softmac->smac_style = dlia.dl_provider_style;
770 		softmac->smac_saplen = ABS(dlia.dl_sap_length);
771 		softmac->smac_min_sdu = dlia.dl_min_sdu;
772 		softmac->smac_max_sdu = dlia.dl_max_sdu;
773 
774 		if ((softmac->smac_saplen != sizeof (uint16_t)) ||
775 		    (softmac->smac_addrlen != ETHERADDRL) ||
776 		    (dlia.dl_brdcst_addr_length != ETHERADDRL) ||
777 		    (dlia.dl_brdcst_addr_offset == 0)) {
778 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
779 			continue;
780 		}
781 
782 		/*
783 		 * Check other DLPI capabilities. Note that this must be after
784 		 * dl_bind() because some drivers return DL_ERROR_ACK if the
785 		 * stream is not bound. It is also before mac_register(), so
786 		 * we don't need any lock protection here.
787 		 */
788 		softmac->smac_capab_flags =
789 		    (MAC_CAPAB_NO_ZCOPY | MAC_CAPAB_LEGACY);
790 
791 		softmac->smac_no_capability_req = B_FALSE;
792 		if (softmac_fill_capab(lh, softmac) != 0)
793 			softmac->smac_no_capability_req = B_TRUE;
794 
795 		/*
796 		 * Check the margin of the underlying driver.
797 		 */
798 		margin = 0;
799 		iocb.ic_cmd = DLIOCMARGININFO;
800 		iocb.ic_timout = INFTIM;
801 		iocb.ic_len = sizeof (margin);
802 		iocb.ic_dp = (char *)&margin;
803 		softmac->smac_margin = 0;
804 
805 		if (ldi_ioctl(lh, I_STR, (intptr_t)&iocb, FKIOCTL, kcred,
806 		    &rval) == 0) {
807 			softmac->smac_margin = margin;
808 		}
809 
810 		/*
811 		 * If the legacy driver doesn't support DLIOCMARGININFO, but
812 		 * it can support native VLAN, correct its margin value to 4.
813 		 */
814 		if (native_vlan) {
815 			if (softmac->smac_margin == 0)
816 				softmac->smac_margin = VLAN_TAGSZ;
817 		} else {
818 			softmac->smac_capab_flags |= MAC_CAPAB_NO_NATIVEVLAN;
819 		}
820 
821 		/*
822 		 * Not all drivers support DL_NOTIFY_REQ, so ignore ENOTSUP.
823 		 */
824 		softmac->smac_notifications = 0;
825 		notes = DL_NOTE_PHYS_ADDR | DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN;
826 		switch (dl_notify(lh, &notes, NULL)) {
827 		case 0:
828 			softmac->smac_notifications = notes;
829 			break;
830 		case ENOTSUP:
831 			break;
832 		default:
833 			(void) ldi_close(lh, FREAD|FWRITE, kcred);
834 			continue;
835 		}
836 
837 		(void) ldi_close(lh, FREAD|FWRITE, kcred);
838 		err = 0;
839 		break;
840 	}
841 	ldi_ident_release(li);
842 
843 	mutex_enter(&softmac->smac_mutex);
844 
845 	if (err != 0)
846 		goto done;
847 
848 	if (softmac->smac_media != DL_ETHER)
849 		softmac->smac_flags |= SOFTMAC_NOSUPP;
850 
851 	/*
852 	 * Finally, we're ready to register ourselves with the MAC layer
853 	 * interface; if this succeeds, we're all ready to start()
854 	 */
855 	if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) {
856 		mac_register_t	*macp;
857 
858 		if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
859 			err = ENOMEM;
860 			goto done;
861 		}
862 
863 		macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
864 		macp->m_driver = softmac;
865 		macp->m_dip = softmac_dip;
866 
867 		macp->m_margin = softmac->smac_margin;
868 		macp->m_src_addr = softmac->smac_unicst_addr;
869 		macp->m_min_sdu = softmac->smac_min_sdu;
870 		macp->m_max_sdu = softmac->smac_max_sdu;
871 		macp->m_callbacks = &softmac_m_callbacks;
872 		macp->m_instance = (uint_t)-1;
873 
874 		err = mac_register(macp, &softmac->smac_mh);
875 		mac_free(macp);
876 		if (err != 0) {
877 			cmn_err(CE_WARN, "mac_register failed for %s",
878 			    softmac->smac_devname);
879 			goto done;
880 		}
881 	}
882 	mutex_exit(&softmac->smac_mutex);
883 
884 	/*
885 	 * Try to create the datalink for this softmac.
886 	 */
887 	if ((err = softmac_create_datalink(softmac)) != 0) {
888 		if (!(softmac->smac_flags & SOFTMAC_NOSUPP))
889 			(void) mac_unregister(softmac->smac_mh);
890 		mutex_enter(&softmac->smac_mutex);
891 		softmac->smac_mh = NULL;
892 		goto done;
893 	}
894 	/*
895 	 * If succeed, create the thread which handles the DL_NOTIFY_IND from
896 	 * the lower stream.
897 	 */
898 	mutex_enter(&softmac->smac_mutex);
899 	if (softmac->smac_mh != NULL) {
900 		softmac->smac_notify_thread = thread_create(NULL, 0,
901 		    softmac_notify_thread, softmac, 0, &p0,
902 		    TS_RUN, minclsyspri);
903 	}
904 
905 done:
906 	ASSERT(softmac->smac_state == SOFTMAC_ATTACH_INPROG &&
907 	    softmac->smac_attachok_cnt == softmac->smac_cnt);
908 	softmac->smac_state = SOFTMAC_ATTACH_DONE;
909 	softmac->smac_attacherr = err;
910 	cv_broadcast(&softmac->smac_cv);
911 	mutex_exit(&softmac->smac_mutex);
912 }
913 
914 int
915 softmac_destroy(dev_info_t *dip, dev_t dev)
916 {
917 	char			devname[MAXNAMELEN];
918 	softmac_t		*softmac;
919 	softmac_dev_t		*softmac_dev;
920 	int			index;
921 	int			ppa, err;
922 	datalink_id_t		linkid;
923 	mac_handle_t		smac_mh;
924 	uint32_t		smac_flags;
925 
926 	ppa = ddi_get_instance(dip);
927 	(void) snprintf(devname, MAXNAMELEN, "%s%d", ddi_driver_name(dip), ppa);
928 
929 	/*
930 	 * We are called only from the predetach entry point. The DACF
931 	 * framework ensures there can't be a concurrent postattach call
932 	 * for the same softmac. The softmac found out from the modhash
933 	 * below can't vanish beneath us since this is the only place where
934 	 * it is deleted.
935 	 */
936 	err = mod_hash_find(softmac_hash, (mod_hash_key_t)devname,
937 	    (mod_hash_val_t *)&softmac);
938 	ASSERT(err == 0);
939 
940 	mutex_enter(&softmac->smac_mutex);
941 	SOFTMAC_STATE_VERIFY(softmac);
942 
943 	/*
944 	 * Fail the predetach routine if this softmac is in-use.
945 	 * Make sure these downcalls into softmac_create or softmac_destroy
946 	 * don't cv_wait on any devfs related condition. Thus softmac_destroy
947 	 * returns EBUSY if the asynchronous thread started in softmac_create
948 	 * hasn't finished
949 	 */
950 	if ((softmac->smac_hold_cnt != 0) ||
951 	    (softmac->smac_state == SOFTMAC_ATTACH_INPROG)) {
952 		softmac->smac_attached_left = softmac->smac_attachok_cnt;
953 		mutex_exit(&softmac->smac_mutex);
954 		return (EBUSY);
955 	}
956 
957 	/*
958 	 * Even if the predetach of one minor node has already failed
959 	 * (smac_attached_left is not 0), the DACF framework will continue
960 	 * to call the predetach routines of the other minor nodes,
961 	 * so we fail these calls here.
962 	 */
963 	if (softmac->smac_attached_left != 0) {
964 		mutex_exit(&softmac->smac_mutex);
965 		return (EBUSY);
966 	}
967 
968 	smac_mh = softmac->smac_mh;
969 	smac_flags = softmac->smac_flags;
970 	softmac->smac_state = SOFTMAC_DETACH_INPROG;
971 	mutex_exit(&softmac->smac_mutex);
972 
973 	if (smac_mh != NULL) {
974 		/*
975 		 * This is the first minor node that is being detached for this
976 		 * softmac.
977 		 */
978 		ASSERT(softmac->smac_attachok_cnt == softmac->smac_cnt);
979 		if (!(smac_flags & SOFTMAC_NOSUPP)) {
980 			if ((err = dls_devnet_destroy(smac_mh, &linkid,
981 			    B_FALSE)) != 0) {
982 				goto error;
983 			}
984 		}
985 		/*
986 		 * If softmac_mac_register() succeeds in registering the mac
987 		 * of the legacy device, unregister it.
988 		 */
989 		if (!(smac_flags & (SOFTMAC_GLDV3 | SOFTMAC_NOSUPP))) {
990 			if ((err = mac_disable_nowait(smac_mh)) != 0) {
991 				(void) dls_devnet_create(smac_mh, linkid);
992 				goto error;
993 			}
994 			/*
995 			 * Ask softmac_notify_thread to quit, and wait for
996 			 * that to be done.
997 			 */
998 			mutex_enter(&softmac->smac_mutex);
999 			softmac->smac_flags |= SOFTMAC_NOTIFY_QUIT;
1000 			cv_broadcast(&softmac->smac_cv);
1001 			while (softmac->smac_notify_thread != NULL) {
1002 				cv_wait(&softmac->smac_cv,
1003 				    &softmac->smac_mutex);
1004 			}
1005 			mutex_exit(&softmac->smac_mutex);
1006 			VERIFY(mac_unregister(smac_mh) == 0);
1007 		}
1008 		softmac->smac_mh = NULL;
1009 	}
1010 
1011 	/*
1012 	 * Free softmac_dev
1013 	 */
1014 	rw_enter(&softmac_hash_lock, RW_WRITER);
1015 	mutex_enter(&softmac->smac_mutex);
1016 
1017 	ASSERT(softmac->smac_state == SOFTMAC_DETACH_INPROG &&
1018 	    softmac->smac_attachok_cnt != 0);
1019 	softmac->smac_mh = NULL;
1020 	index = (getmajor(dev) == ddi_name_to_major("clone"));
1021 	softmac_dev = softmac->smac_softmac[index];
1022 	ASSERT(softmac_dev != NULL);
1023 	softmac->smac_softmac[index] = NULL;
1024 	kmem_free(softmac_dev, sizeof (softmac_dev_t));
1025 
1026 	if (--softmac->smac_attachok_cnt == 0) {
1027 		mod_hash_val_t	hashval;
1028 
1029 		softmac->smac_state = SOFTMAC_UNINIT;
1030 		if (softmac->smac_hold_cnt != 0) {
1031 			/*
1032 			 * Someone did a softmac_hold_device while we dropped
1033 			 * the locks. Leave the softmac itself intact which
1034 			 * will be reused by the reattach
1035 			 */
1036 			mutex_exit(&softmac->smac_mutex);
1037 			rw_exit(&softmac_hash_lock);
1038 			return (0);
1039 		}
1040 		err = mod_hash_remove(softmac_hash,
1041 		    (mod_hash_key_t)devname,
1042 		    (mod_hash_val_t *)&hashval);
1043 		ASSERT(err == 0);
1044 
1045 		mutex_exit(&softmac->smac_mutex);
1046 		rw_exit(&softmac_hash_lock);
1047 		ASSERT(softmac->smac_fp_disable_clients == 0);
1048 		softmac->smac_fastpath_admin_disabled = B_FALSE;
1049 		kmem_cache_free(softmac_cachep, softmac);
1050 		return (0);
1051 	}
1052 	mutex_exit(&softmac->smac_mutex);
1053 	rw_exit(&softmac_hash_lock);
1054 	return (0);
1055 
1056 error:
1057 	mutex_enter(&softmac->smac_mutex);
1058 	softmac->smac_attached_left = softmac->smac_attachok_cnt;
1059 	softmac->smac_state = SOFTMAC_ATTACH_DONE;
1060 	cv_broadcast(&softmac->smac_cv);
1061 	mutex_exit(&softmac->smac_mutex);
1062 	return (err);
1063 }
1064 
1065 /*
1066  * This function is called as the result of a newly started dlmgmtd daemon.
1067  *
1068  * We walk through every softmac that was created but failed to notify
1069  * dlmgmtd about it (whose SOFTMAC_NEED_RECREATE flag is set).  This occurs
1070  * when softmacs are created before dlmgmtd is ready.  For example, during
1071  * diskless boot, a network device is used (and therefore attached) before
1072  * the datalink-management service starts dlmgmtd.
1073  */
1074 /* ARGSUSED */
1075 static uint_t
1076 softmac_mac_recreate(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1077 {
1078 	softmac_t	*softmac = (softmac_t *)val;
1079 	datalink_id_t	linkid;
1080 	int		err;
1081 	softmac_walk_t	*smwp = arg;
1082 
1083 	/*
1084 	 * The framework itself must not hold any locks across calls to the
1085 	 * mac perimeter. Thus this function does not call any framework
1086 	 * function that needs to grab the mac perimeter.
1087 	 */
1088 	ASSERT(RW_READ_HELD(&softmac_hash_lock));
1089 
1090 	smwp->smw_retry = B_FALSE;
1091 	mutex_enter(&softmac->smac_mutex);
1092 	SOFTMAC_STATE_VERIFY(softmac);
1093 	if (softmac->smac_state == SOFTMAC_ATTACH_INPROG) {
1094 		/*
1095 		 * Wait till softmac_create or softmac_mac_register finishes
1096 		 * Hold the softmac to ensure it stays around. The wait itself
1097 		 * is done in the caller, since we need to drop all locks
1098 		 * including the mod hash's internal lock before calling
1099 		 * cv_wait.
1100 		 */
1101 		smwp->smw_retry = B_TRUE;
1102 		smwp->smw_softmac = softmac;
1103 		softmac->smac_hold_cnt++;
1104 		return (MH_WALK_TERMINATE);
1105 	}
1106 
1107 	if ((softmac->smac_state != SOFTMAC_ATTACH_DONE) ||
1108 	    !(softmac->smac_flags & SOFTMAC_NEED_RECREATE)) {
1109 		mutex_exit(&softmac->smac_mutex);
1110 		return (MH_WALK_CONTINUE);
1111 	}
1112 
1113 	/*
1114 	 * Bumping up the smac_hold_cnt allows us to drop the lock. It also
1115 	 * makes softmac_destroy() return failure on an attempted device detach.
1116 	 * We don't want to hold the lock across calls to other subsystems
1117 	 * like kstats, which will happen in the call to dls_devnet_recreate
1118 	 */
1119 	softmac->smac_hold_cnt++;
1120 	mutex_exit(&softmac->smac_mutex);
1121 
1122 	if (dls_mgmt_create(softmac->smac_devname,
1123 	    makedevice(softmac->smac_umajor, softmac->smac_uppa + 1),
1124 	    DATALINK_CLASS_PHYS, softmac->smac_media, B_TRUE, &linkid) != 0) {
1125 		softmac_rele_device((dls_dev_handle_t)softmac);
1126 		return (MH_WALK_CONTINUE);
1127 	}
1128 
1129 	if ((err = softmac_update_info(softmac, &linkid)) != 0) {
1130 		cmn_err(CE_WARN, "softmac: softmac_update_info() for %s "
1131 		    "failed (%d)", softmac->smac_devname, err);
1132 		softmac_rele_device((dls_dev_handle_t)softmac);
1133 		return (MH_WALK_CONTINUE);
1134 	}
1135 
1136 	/*
1137 	 * Create a link for this MAC. The link name will be the same
1138 	 * as the MAC name.
1139 	 */
1140 	if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) {
1141 		err = dls_devnet_recreate(softmac->smac_mh, linkid);
1142 		if (err != 0) {
1143 			cmn_err(CE_WARN, "softmac: dls_devnet_recreate() for "
1144 			    "%s (linkid %d) failed (%d)",
1145 			    softmac->smac_devname, linkid, err);
1146 		}
1147 	}
1148 
1149 	mutex_enter(&softmac->smac_mutex);
1150 	softmac->smac_flags &= ~SOFTMAC_NEED_RECREATE;
1151 	ASSERT(softmac->smac_hold_cnt != 0);
1152 	softmac->smac_hold_cnt--;
1153 	mutex_exit(&softmac->smac_mutex);
1154 
1155 	return (MH_WALK_CONTINUE);
1156 }
1157 
1158 /*
1159  * See comments above softmac_mac_recreate().
1160  */
1161 void
1162 softmac_recreate()
1163 {
1164 	softmac_walk_t	smw;
1165 	softmac_t	*softmac;
1166 
1167 	/*
1168 	 * Walk through the softmac_hash table. Request to create the
1169 	 * [link name, linkid] mapping if we failed to do so.
1170 	 */
1171 	do {
1172 		smw.smw_retry = B_FALSE;
1173 		rw_enter(&softmac_hash_lock, RW_READER);
1174 		mod_hash_walk(softmac_hash, softmac_mac_recreate, &smw);
1175 		rw_exit(&softmac_hash_lock);
1176 		if (smw.smw_retry) {
1177 			/*
1178 			 * softmac_create or softmac_mac_register hasn't yet
1179 			 * finished and the softmac is not yet in the
1180 			 * SOFTMAC_ATTACH_DONE state.
1181 			 */
1182 			softmac = smw.smw_softmac;
1183 			cv_wait(&softmac->smac_cv, &softmac->smac_mutex);
1184 			softmac->smac_hold_cnt--;
1185 			mutex_exit(&softmac->smac_mutex);
1186 		}
1187 	} while (smw.smw_retry);
1188 }
1189 
1190 static int
1191 softmac_m_start(void *arg)
1192 {
1193 	softmac_t	*softmac = arg;
1194 	softmac_lower_t	*slp = softmac->smac_lower;
1195 	int		err;
1196 
1197 	ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
1198 	/*
1199 	 * Bind to SAP 2 on token ring, 0 on other interface types.
1200 	 * (SAP 0 has special significance on token ring).
1201 	 * Note that the receive-side packets could come anytime after bind.
1202 	 */
1203 	err = softmac_send_bind_req(slp, softmac->smac_media == DL_TPR ? 2 : 0);
1204 	if (err != 0)
1205 		return (err);
1206 
1207 	/*
1208 	 * Put the lower stream to the DL_PROMISC_SAP mode in order to receive
1209 	 * all packets of interest.
1210 	 *
1211 	 * some driver (e.g. the old legacy eri driver) incorrectly passes up
1212 	 * packets to DL_PROMISC_SAP stream when the lower stream is not bound,
1213 	 * so that we send DL_PROMISON_REQ after DL_BIND_REQ.
1214 	 */
1215 	err = softmac_send_promisc_req(slp, DL_PROMISC_SAP, B_TRUE);
1216 	if (err != 0) {
1217 		(void) softmac_send_unbind_req(slp);
1218 		return (err);
1219 	}
1220 
1221 	/*
1222 	 * Enable capabilities the underlying driver claims to support.
1223 	 * Some driver requires this being called after the stream is bound.
1224 	 */
1225 	if ((err = softmac_capab_enable(slp)) != 0) {
1226 		(void) softmac_send_promisc_req(slp, DL_PROMISC_SAP, B_FALSE);
1227 		(void) softmac_send_unbind_req(slp);
1228 	}
1229 
1230 	return (err);
1231 }
1232 
1233 /* ARGSUSED */
1234 static void
1235 softmac_m_stop(void *arg)
1236 {
1237 	softmac_t	*softmac = arg;
1238 	softmac_lower_t	*slp = softmac->smac_lower;
1239 
1240 	ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
1241 
1242 	/*
1243 	 * It is not needed to reset zerocopy, MDT or HCKSUM capabilities.
1244 	 */
1245 	(void) softmac_send_promisc_req(slp, DL_PROMISC_SAP, B_FALSE);
1246 	(void) softmac_send_unbind_req(slp);
1247 }
1248 
1249 /*
1250  * Set up the lower stream above the legacy device. There are two different
1251  * type of lower streams:
1252  *
1253  * - Shared lower-stream
1254  *
1255  * Shared by all GLDv3 MAC clients. Put the lower stream to the DLIOCRAW
1256  * mode to send and receive the raw data. Further, put the lower stream into
1257  * DL_PROMISC_SAP mode to receive all packets of interest.
1258  *
1259  * - Dedicated lower-stream
1260  *
1261  * The lower-stream which is dedicated to upper IP/ARP stream. This is used
1262  * as fast-path for IP. In this case, the second argument is the pointer to
1263  * the softmac upper-stream.
1264  */
1265 int
1266 softmac_lower_setup(softmac_t *softmac, softmac_upper_t *sup,
1267     softmac_lower_t **slpp)
1268 {
1269 	ldi_ident_t		li;
1270 	dev_t			dev;
1271 	ldi_handle_t		lh = NULL;
1272 	softmac_lower_t		*slp = NULL;
1273 	smac_ioc_start_t	start_arg;
1274 	struct strioctl		strioc;
1275 	uint32_t		notifications;
1276 	int			err, rval;
1277 
1278 	if ((err = ldi_ident_from_dip(softmac_dip, &li)) != 0)
1279 		return (err);
1280 
1281 	/*
1282 	 * The GLDv3 framework makes sure that mac_unregister(), mac_open(),
1283 	 * and mac_close() cannot be called at the same time. So we don't
1284 	 * need any protection to access softmac here.
1285 	 */
1286 	dev = softmac->smac_dev;
1287 
1288 	err = ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh, li);
1289 	ldi_ident_release(li);
1290 	if (err != 0)
1291 		goto done;
1292 
1293 	/*
1294 	 * Pop all the intermediate modules. The autopushed modules will
1295 	 * be pushed when the softmac node is opened.
1296 	 */
1297 	while (ldi_ioctl(lh, I_POP, 0, FKIOCTL, kcred, &rval) == 0)
1298 		;
1299 
1300 	if ((softmac->smac_style == DL_STYLE2) &&
1301 	    ((err = dl_attach(lh, softmac->smac_uppa, NULL)) != 0)) {
1302 		goto done;
1303 	}
1304 
1305 	/*
1306 	 * If this is the shared-lower-stream, put the lower stream to
1307 	 * the DLIOCRAW mode to send/receive raw data.
1308 	 */
1309 	if ((sup == NULL) && (err = ldi_ioctl(lh, DLIOCRAW, 0, FKIOCTL,
1310 	    kcred, &rval)) != 0) {
1311 		goto done;
1312 	}
1313 
1314 	/*
1315 	 * Then push the softmac shim layer atop the lower stream.
1316 	 */
1317 	if ((err = ldi_ioctl(lh, I_PUSH, (intptr_t)SOFTMAC_DEV_NAME, FKIOCTL,
1318 	    kcred, &rval)) != 0) {
1319 		goto done;
1320 	}
1321 
1322 	/*
1323 	 * Send the ioctl to get the slp pointer.
1324 	 */
1325 	strioc.ic_cmd = SMAC_IOC_START;
1326 	strioc.ic_timout = INFTIM;
1327 	strioc.ic_len = sizeof (start_arg);
1328 	strioc.ic_dp = (char *)&start_arg;
1329 
1330 	if ((err = ldi_ioctl(lh, I_STR, (intptr_t)&strioc, FKIOCTL,
1331 	    kcred, &rval)) != 0) {
1332 		goto done;
1333 	}
1334 	slp = start_arg.si_slp;
1335 	slp->sl_sup = sup;
1336 	slp->sl_lh = lh;
1337 	slp->sl_softmac = softmac;
1338 	*slpp = slp;
1339 
1340 	if (sup != NULL) {
1341 		slp->sl_rxinfo = &sup->su_rxinfo;
1342 	} else {
1343 		/*
1344 		 * Send DL_NOTIFY_REQ to enable certain DL_NOTIFY_IND.
1345 		 * We don't have to wait for the ack.
1346 		 */
1347 		notifications = DL_NOTE_PHYS_ADDR | DL_NOTE_LINK_UP |
1348 		    DL_NOTE_LINK_DOWN | DL_NOTE_PROMISC_ON_PHYS |
1349 		    DL_NOTE_PROMISC_OFF_PHYS;
1350 
1351 		(void) softmac_send_notify_req(slp,
1352 		    (notifications & softmac->smac_notifications));
1353 	}
1354 
1355 done:
1356 	if (err != 0)
1357 		(void) ldi_close(lh, FREAD|FWRITE, kcred);
1358 	return (err);
1359 }
1360 
1361 static int
1362 softmac_m_open(void *arg)
1363 {
1364 	softmac_t	*softmac = arg;
1365 	softmac_lower_t	*slp;
1366 	int		err;
1367 
1368 	ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
1369 
1370 	if ((err = softmac_lower_setup(softmac, NULL, &slp)) != 0)
1371 		return (err);
1372 
1373 	softmac->smac_lower = slp;
1374 	return (0);
1375 }
1376 
1377 static void
1378 softmac_m_close(void *arg)
1379 {
1380 	softmac_t	*softmac = arg;
1381 	softmac_lower_t	*slp;
1382 
1383 	ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
1384 	slp = softmac->smac_lower;
1385 	ASSERT(slp != NULL);
1386 
1387 	/*
1388 	 * Note that slp is destroyed when lh is closed.
1389 	 */
1390 	(void) ldi_close(slp->sl_lh, FREAD|FWRITE, kcred);
1391 	softmac->smac_lower = NULL;
1392 }
1393 
1394 /*
1395  * Softmac supports two priviate link properteis:
1396  *
1397  * - "_fastpath"
1398  *
1399  *    This is a read-only link property which points out the current data-path
1400  *    model of the given legacy link. The possible values are "disabled" and
1401  *    "enabled".
1402  *
1403  * - "_disable_fastpath"
1404  *
1405  *    This is a read-write link property which can be used to disable or enable
1406  *    the fast-path of the given legacy link. The possible values are "true"
1407  *    and "false". Note that even when "_disable_fastpath" is set to be
1408  *    "false", the fast-path may still not be enabled since there may be
1409  *    other mac cleints that request the fast-path to be disabled.
1410  */
1411 /* ARGSUSED */
1412 static int
1413 softmac_m_setprop(void *arg, const char *name, mac_prop_id_t id,
1414     uint_t valsize, const void *val)
1415 {
1416 	softmac_t	*softmac = arg;
1417 
1418 	if (id != MAC_PROP_PRIVATE || strcmp(name, "_disable_fastpath") != 0)
1419 		return (ENOTSUP);
1420 
1421 	if (strcmp(val, "true") == 0)
1422 		return (softmac_datapath_switch(softmac, B_TRUE, B_TRUE));
1423 	else if (strcmp(val, "false") == 0)
1424 		return (softmac_datapath_switch(softmac, B_FALSE, B_TRUE));
1425 	else
1426 		return (EINVAL);
1427 }
1428 
1429 static int
1430 softmac_m_getprop(void *arg, const char *name, mac_prop_id_t id, uint_t flags,
1431     uint_t valsize, void *val, uint_t *perm)
1432 {
1433 	softmac_t	*softmac = arg;
1434 	char		*fpstr;
1435 
1436 	if (id != MAC_PROP_PRIVATE)
1437 		return (ENOTSUP);
1438 
1439 	if (strcmp(name, "_fastpath") == 0) {
1440 		if ((flags & MAC_PROP_DEFAULT) != 0)
1441 			return (ENOTSUP);
1442 
1443 		*perm = MAC_PROP_PERM_READ;
1444 		mutex_enter(&softmac->smac_fp_mutex);
1445 		fpstr = (DATAPATH_MODE(softmac) == SOFTMAC_SLOWPATH) ?
1446 		    "disabled" : "enabled";
1447 		mutex_exit(&softmac->smac_fp_mutex);
1448 	} else if (strcmp(name, "_disable_fastpath") == 0) {
1449 		*perm = MAC_PROP_PERM_RW;
1450 		fpstr = ((flags & MAC_PROP_DEFAULT) != 0) ? "false" :
1451 		    (softmac->smac_fastpath_admin_disabled ? "true" : "false");
1452 	} else {
1453 		return (ENOTSUP);
1454 	}
1455 
1456 	return (strlcpy(val, fpstr, valsize) >= valsize ? EINVAL : 0);
1457 }
1458 
1459 int
1460 softmac_hold_device(dev_t dev, dls_dev_handle_t *ddhp)
1461 {
1462 	dev_info_t	*dip;
1463 	const char	*drvname;
1464 	char		devname[MAXNAMELEN];
1465 	softmac_t	*softmac;
1466 	int		ppa, err;
1467 
1468 	if ((ppa = getminor(dev) - 1) > 1000)
1469 		return (ENOENT);
1470 
1471 	/*
1472 	 * First try to hold this device instance to force the MAC
1473 	 * to be registered.
1474 	 */
1475 	if ((dip = ddi_hold_devi_by_instance(getmajor(dev), ppa, 0)) == NULL)
1476 		return (ENOENT);
1477 
1478 	drvname = ddi_driver_name(dip);
1479 
1480 	/*
1481 	 * Exclude non-physical network device instances, for example, aggr0.
1482 	 */
1483 	if ((ddi_driver_major(dip) != getmajor(dev)) ||
1484 	    !NETWORK_DRV(getmajor(dev)) || (strcmp(drvname, "aggr") == 0) ||
1485 	    (strcmp(drvname, "vnic") == 0)) {
1486 		ddi_release_devi(dip);
1487 		return (ENOENT);
1488 	}
1489 
1490 	/*
1491 	 * This is a network device; wait for its softmac to be registered.
1492 	 */
1493 	(void) snprintf(devname, MAXNAMELEN, "%s%d", drvname, ppa);
1494 again:
1495 	rw_enter(&softmac_hash_lock, RW_READER);
1496 
1497 	if (mod_hash_find(softmac_hash, (mod_hash_key_t)devname,
1498 	    (mod_hash_val_t *)&softmac) != 0) {
1499 		/*
1500 		 * This is rare but possible. It could happen when pre-detach
1501 		 * routine of the device succeeds. But the softmac will then
1502 		 * be recreated when device fails to detach (as this device
1503 		 * is held).
1504 		 */
1505 		mutex_enter(&smac_global_lock);
1506 		rw_exit(&softmac_hash_lock);
1507 		cv_wait(&smac_global_cv, &smac_global_lock);
1508 		mutex_exit(&smac_global_lock);
1509 		goto again;
1510 	}
1511 
1512 	/*
1513 	 * Bump smac_hold_cnt to prevent device detach.
1514 	 */
1515 	mutex_enter(&softmac->smac_mutex);
1516 	softmac->smac_hold_cnt++;
1517 	rw_exit(&softmac_hash_lock);
1518 
1519 	/*
1520 	 * Wait till the device is fully attached.
1521 	 */
1522 	while (softmac->smac_state != SOFTMAC_ATTACH_DONE)
1523 		cv_wait(&softmac->smac_cv, &softmac->smac_mutex);
1524 
1525 	SOFTMAC_STATE_VERIFY(softmac);
1526 
1527 	if ((err = softmac->smac_attacherr) != 0)
1528 		softmac->smac_hold_cnt--;
1529 	else
1530 		*ddhp = (dls_dev_handle_t)softmac;
1531 	mutex_exit(&softmac->smac_mutex);
1532 
1533 	ddi_release_devi(dip);
1534 	return (err);
1535 }
1536 
1537 void
1538 softmac_rele_device(dls_dev_handle_t ddh)
1539 {
1540 	if (ddh != NULL)
1541 		softmac_rele((softmac_t *)ddh);
1542 }
1543 
1544 int
1545 softmac_hold(dev_t dev, softmac_t **softmacp)
1546 {
1547 	softmac_t	*softmac;
1548 	char		*drv;
1549 	mac_handle_t	mh;
1550 	char		mac[MAXNAMELEN];
1551 	int		err;
1552 
1553 	if ((drv = ddi_major_to_name(getmajor(dev))) == NULL)
1554 		return (EINVAL);
1555 
1556 	(void) snprintf(mac, MAXNAMELEN, "%s%d", drv, getminor(dev) - 1);
1557 	if ((err = mac_open(mac, &mh)) != 0)
1558 		return (err);
1559 
1560 	softmac = (softmac_t *)mac_driver(mh);
1561 
1562 	mutex_enter(&softmac->smac_mutex);
1563 	softmac->smac_hold_cnt++;
1564 	mutex_exit(&softmac->smac_mutex);
1565 	mac_close(mh);
1566 	*softmacp = softmac;
1567 	return (0);
1568 }
1569 
1570 void
1571 softmac_rele(softmac_t *softmac)
1572 {
1573 	mutex_enter(&softmac->smac_mutex);
1574 	softmac->smac_hold_cnt--;
1575 	mutex_exit(&softmac->smac_mutex);
1576 }
1577