xref: /illumos-gate/usr/src/uts/common/io/dls/dls_mgmt.c (revision 676abcb7)
1d62bc4baSyz /*
2d62bc4baSyz  * CDDL HEADER START
3d62bc4baSyz  *
4d62bc4baSyz  * The contents of this file are subject to the terms of the
5d62bc4baSyz  * Common Development and Distribution License (the "License").
6d62bc4baSyz  * You may not use this file except in compliance with the License.
7d62bc4baSyz  *
8d62bc4baSyz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d62bc4baSyz  * or http://www.opensolaris.org/os/licensing.
10d62bc4baSyz  * See the License for the specific language governing permissions
11d62bc4baSyz  * and limitations under the License.
12d62bc4baSyz  *
13d62bc4baSyz  * When distributing Covered Code, include this CDDL HEADER in each
14d62bc4baSyz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d62bc4baSyz  * If applicable, add the following below this CDDL HEADER, with the
16d62bc4baSyz  * fields enclosed by brackets "[]" replaced with your own identifying
17d62bc4baSyz  * information: Portions Copyright [yyyy] [name of copyright owner]
18d62bc4baSyz  *
19d62bc4baSyz  * CDDL HEADER END
20d62bc4baSyz  */
21d62bc4baSyz /*
22ae6aa22aSVenugopal Iyer  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23d62bc4baSyz  * Use is subject to license terms.
2485dff7a0SAndy Fiddaman  * Copyright (c) 2017 Joyent, Inc.
25d62bc4baSyz  */
26fe4627efSSebastien Roy /*
27fe4627efSSebastien Roy  * Copyright (c) 2016 by Delphix. All rights reserved.
28fe4627efSSebastien Roy  */
29d62bc4baSyz 
30d62bc4baSyz /*
31d62bc4baSyz  * Datalink management routines.
32d62bc4baSyz  */
33d62bc4baSyz 
34d62bc4baSyz #include <sys/types.h>
35d62bc4baSyz #include <sys/door.h>
36d62bc4baSyz #include <sys/zone.h>
37d62bc4baSyz #include <sys/modctl.h>
38d62bc4baSyz #include <sys/file.h>
39d62bc4baSyz #include <sys/modhash.h>
40d62bc4baSyz #include <sys/kstat.h>
41d62bc4baSyz #include <sys/vnode.h>
42d62bc4baSyz #include <sys/cmn_err.h>
43d62bc4baSyz #include <sys/softmac.h>
44d62bc4baSyz #include <sys/dls.h>
45d62bc4baSyz #include <sys/dls_impl.h>
462b24ab6bSSebastien Roy #include <sys/stropts.h>
472b24ab6bSSebastien Roy #include <sys/netstack.h>
482b24ab6bSSebastien Roy #include <inet/iptun/iptun_impl.h>
49d62bc4baSyz 
50da14cebeSEric Cheng /*
51da14cebeSEric Cheng  * This vanity name management module is treated as part of the GLD framework
52da14cebeSEric Cheng  * and we don't hold any GLD framework lock across a call to any mac
53da14cebeSEric Cheng  * function that needs to acquire the mac perimeter. The hierarchy is
54da14cebeSEric Cheng  * mac perimeter -> framework locks
55da14cebeSEric Cheng  */
56da14cebeSEric Cheng 
572b24ab6bSSebastien Roy typedef struct dls_stack {
582b24ab6bSSebastien Roy 	zoneid_t	dlss_zoneid;
592b24ab6bSSebastien Roy } dls_stack_t;
602b24ab6bSSebastien Roy 
61d62bc4baSyz static kmem_cache_t	*i_dls_devnet_cachep;
62d62bc4baSyz static kmutex_t		i_dls_mgmt_lock;
63d62bc4baSyz static krwlock_t	i_dls_devnet_lock;
64d62bc4baSyz static mod_hash_t	*i_dls_devnet_id_hash;
65d62bc4baSyz static mod_hash_t	*i_dls_devnet_hash;
66d62bc4baSyz 
67d62bc4baSyz boolean_t		devnet_need_rebuild;
68d62bc4baSyz 
69d62bc4baSyz #define	VLAN_HASHSZ	67	/* prime */
70d62bc4baSyz 
712b24ab6bSSebastien Roy /*
72d501bbfeSSebastien Roy  * The following macros take a link name without the trailing PPA as input.
73d501bbfeSSebastien Roy  * Opening a /dev/net node with one of these names causes a tunnel link to be
74d501bbfeSSebastien Roy  * implicitly created in dls_devnet_hold_by_name() for backward compatibility
75d501bbfeSSebastien Roy  * with Solaris 10 and prior.
762b24ab6bSSebastien Roy  */
77d501bbfeSSebastien Roy #define	IS_IPV4_TUN(name)	(strcmp((name), "ip.tun") == 0)
78d501bbfeSSebastien Roy #define	IS_IPV6_TUN(name)	(strcmp((name), "ip6.tun") == 0)
79d501bbfeSSebastien Roy #define	IS_6TO4_TUN(name)	(strcmp((name), "ip.6to4tun") == 0)
802b24ab6bSSebastien Roy #define	IS_IPTUN_LINK(name)	(					\
812b24ab6bSSebastien Roy     IS_IPV4_TUN(name) || IS_IPV6_TUN(name) || IS_6TO4_TUN(name))
822b24ab6bSSebastien Roy 
83d62bc4baSyz /* Upcall door handle */
84d62bc4baSyz static door_handle_t	dls_mgmt_dh = NULL;
85d62bc4baSyz 
86fe4627efSSebastien Roy /* dls_devnet_t dd_flags */
87ae6aa22aSVenugopal Iyer #define	DD_CONDEMNED		0x1
88fe4627efSSebastien Roy #define	DD_IMPLICIT_IPTUN	0x2 /* Implicitly-created ip*.*tun* tunnel */
8985dff7a0SAndy Fiddaman #define	DD_INITIALIZING		0x4
9085dff7a0SAndy Fiddaman 
9185dff7a0SAndy Fiddaman /*
9285dff7a0SAndy Fiddaman  * If the link is marked as initializing or condemned then it should
9385dff7a0SAndy Fiddaman  * not be visible outside of the DLS framework.
9485dff7a0SAndy Fiddaman  */
9585dff7a0SAndy Fiddaman #define	DD_NOT_VISIBLE(flags)	(					\
9685dff7a0SAndy Fiddaman 	(flags & (DD_CONDEMNED | DD_INITIALIZING)) != 0)
97da14cebeSEric Cheng 
98d62bc4baSyz /*
99da14cebeSEric Cheng  * This structure is used to keep the <linkid, macname> mapping.
100ae6aa22aSVenugopal Iyer  * This structure itself is not protected by the mac perimeter, but is
101ae6aa22aSVenugopal Iyer  * protected by the dd_mutex and i_dls_devnet_lock. Thus most of the
102ae6aa22aSVenugopal Iyer  * functions manipulating this structure such as dls_devnet_set/unset etc.
103ae6aa22aSVenugopal Iyer  * may be called while not holding the mac perimeter.
104d62bc4baSyz  */
105d62bc4baSyz typedef struct dls_devnet_s {
106d62bc4baSyz 	datalink_id_t	dd_linkid;
1072b24ab6bSSebastien Roy 	char		dd_linkname[MAXLINKNAMELEN];
108d62bc4baSyz 	char		dd_mac[MAXNAMELEN];
1092b24ab6bSSebastien Roy 	kstat_t		*dd_ksp;	/* kstat in owner_zid */
1102b24ab6bSSebastien Roy 	kstat_t		*dd_zone_ksp;	/* in dd_zid if != owner_zid */
111d62bc4baSyz 	uint32_t	dd_ref;
112d62bc4baSyz 	kmutex_t	dd_mutex;
113d62bc4baSyz 	kcondvar_t	dd_cv;
114d62bc4baSyz 	uint32_t	dd_tref;
115da14cebeSEric Cheng 	uint_t		dd_flags;
1162b24ab6bSSebastien Roy 	zoneid_t	dd_owner_zid;	/* zone where node was created */
1172b24ab6bSSebastien Roy 	zoneid_t	dd_zid;		/* current zone */
11830890389Sartem 	boolean_t	dd_prop_loaded;
11930890389Sartem 	taskqid_t	dd_prop_taskid;
12085dff7a0SAndy Fiddaman 	boolean_t	dd_transient;	/* link goes away when zone does */
121d62bc4baSyz } dls_devnet_t;
122d62bc4baSyz 
123d501bbfeSSebastien Roy static int i_dls_devnet_create_iptun(const char *, const char *,
124d501bbfeSSebastien Roy     datalink_id_t *);
1252b24ab6bSSebastien Roy static int i_dls_devnet_destroy_iptun(datalink_id_t);
12685dff7a0SAndy Fiddaman static int i_dls_devnet_setzid(dls_devnet_t *, zoneid_t, boolean_t, boolean_t);
12785dff7a0SAndy Fiddaman static int dls_devnet_unset(mac_handle_t, datalink_id_t *, boolean_t);
12830890389Sartem 
129d62bc4baSyz /*ARGSUSED*/
130d62bc4baSyz static int
i_dls_devnet_constructor(void * buf,void * arg,int kmflag)131d62bc4baSyz i_dls_devnet_constructor(void *buf, void *arg, int kmflag)
132d62bc4baSyz {
133d62bc4baSyz 	dls_devnet_t	*ddp = buf;
134d62bc4baSyz 
135d62bc4baSyz 	bzero(buf, sizeof (dls_devnet_t));
136d62bc4baSyz 	mutex_init(&ddp->dd_mutex, NULL, MUTEX_DEFAULT, NULL);
137d62bc4baSyz 	cv_init(&ddp->dd_cv, NULL, CV_DEFAULT, NULL);
138d62bc4baSyz 	return (0);
139d62bc4baSyz }
140d62bc4baSyz 
141d62bc4baSyz /*ARGSUSED*/
142d62bc4baSyz static void
i_dls_devnet_destructor(void * buf,void * arg)143d62bc4baSyz i_dls_devnet_destructor(void *buf, void *arg)
144d62bc4baSyz {
145d62bc4baSyz 	dls_devnet_t	*ddp = buf;
146d62bc4baSyz 
14785dff7a0SAndy Fiddaman 	VERIFY(ddp->dd_ksp == NULL);
14885dff7a0SAndy Fiddaman 	VERIFY(ddp->dd_ref == 0);
14985dff7a0SAndy Fiddaman 	VERIFY(ddp->dd_tref == 0);
150d62bc4baSyz 	mutex_destroy(&ddp->dd_mutex);
151d62bc4baSyz 	cv_destroy(&ddp->dd_cv);
152d62bc4baSyz }
153d62bc4baSyz 
1542b24ab6bSSebastien Roy /* ARGSUSED */
1552b24ab6bSSebastien Roy static int
dls_zone_remove(datalink_id_t linkid,void * arg)1562b24ab6bSSebastien Roy dls_zone_remove(datalink_id_t linkid, void *arg)
1572b24ab6bSSebastien Roy {
1582b24ab6bSSebastien Roy 	dls_devnet_t *ddp;
1592b24ab6bSSebastien Roy 
1602b24ab6bSSebastien Roy 	if (dls_devnet_hold_tmp(linkid, &ddp) == 0) {
16185dff7a0SAndy Fiddaman 		/*
16285dff7a0SAndy Fiddaman 		 * Don't bother moving transient links back to the global zone
16385dff7a0SAndy Fiddaman 		 * since we will simply delete them in dls_devnet_unset.
16485dff7a0SAndy Fiddaman 		 */
16585dff7a0SAndy Fiddaman 		if (!ddp->dd_transient)
16685dff7a0SAndy Fiddaman 			(void) dls_devnet_setzid(ddp, GLOBAL_ZONEID);
1672b24ab6bSSebastien Roy 		dls_devnet_rele_tmp(ddp);
1682b24ab6bSSebastien Roy 	}
1692b24ab6bSSebastien Roy 	return (0);
1702b24ab6bSSebastien Roy }
1712b24ab6bSSebastien Roy 
1722b24ab6bSSebastien Roy /* ARGSUSED */
1732b24ab6bSSebastien Roy static void *
dls_stack_init(netstackid_t stackid,netstack_t * ns)1742b24ab6bSSebastien Roy dls_stack_init(netstackid_t stackid, netstack_t *ns)
1752b24ab6bSSebastien Roy {
1762b24ab6bSSebastien Roy 	dls_stack_t *dlss;
1772b24ab6bSSebastien Roy 
1782b24ab6bSSebastien Roy 	dlss = kmem_zalloc(sizeof (*dlss), KM_SLEEP);
1792b24ab6bSSebastien Roy 	dlss->dlss_zoneid = netstackid_to_zoneid(stackid);
1802b24ab6bSSebastien Roy 	return (dlss);
1812b24ab6bSSebastien Roy }
1822b24ab6bSSebastien Roy 
1832b24ab6bSSebastien Roy /* ARGSUSED */
1842b24ab6bSSebastien Roy static void
dls_stack_shutdown(netstackid_t stackid,void * arg)1852b24ab6bSSebastien Roy dls_stack_shutdown(netstackid_t stackid, void *arg)
1862b24ab6bSSebastien Roy {
1872b24ab6bSSebastien Roy 	dls_stack_t	*dlss = (dls_stack_t *)arg;
1882b24ab6bSSebastien Roy 
1892b24ab6bSSebastien Roy 	/* Move remaining datalinks in this zone back to the global zone. */
1902b24ab6bSSebastien Roy 	(void) zone_datalink_walk(dlss->dlss_zoneid, dls_zone_remove, NULL);
1912b24ab6bSSebastien Roy }
1922b24ab6bSSebastien Roy 
1932b24ab6bSSebastien Roy /* ARGSUSED */
1942b24ab6bSSebastien Roy static void
dls_stack_fini(netstackid_t stackid,void * arg)1952b24ab6bSSebastien Roy dls_stack_fini(netstackid_t stackid, void *arg)
1962b24ab6bSSebastien Roy {
1972b24ab6bSSebastien Roy 	dls_stack_t	*dlss = (dls_stack_t *)arg;
1982b24ab6bSSebastien Roy 
1992b24ab6bSSebastien Roy 	kmem_free(dlss, sizeof (*dlss));
2002b24ab6bSSebastien Roy }
2012b24ab6bSSebastien Roy 
202d62bc4baSyz /*
203d62bc4baSyz  * Module initialization and finalization functions.
204d62bc4baSyz  */
205d62bc4baSyz void
dls_mgmt_init(void)206d62bc4baSyz dls_mgmt_init(void)
207d62bc4baSyz {
208d62bc4baSyz 	mutex_init(&i_dls_mgmt_lock, NULL, MUTEX_DEFAULT, NULL);
209d62bc4baSyz 	rw_init(&i_dls_devnet_lock, NULL, RW_DEFAULT, NULL);
210d62bc4baSyz 
211d62bc4baSyz 	/*
212d62bc4baSyz 	 * Create a kmem_cache of dls_devnet_t structures.
213d62bc4baSyz 	 */
214d62bc4baSyz 	i_dls_devnet_cachep = kmem_cache_create("dls_devnet_cache",
215d62bc4baSyz 	    sizeof (dls_devnet_t), 0, i_dls_devnet_constructor,
216d62bc4baSyz 	    i_dls_devnet_destructor, NULL, NULL, NULL, 0);
217d62bc4baSyz 	ASSERT(i_dls_devnet_cachep != NULL);
218d62bc4baSyz 
219d62bc4baSyz 	/*
220da14cebeSEric Cheng 	 * Create a hash table, keyed by dd_linkid, of dls_devnet_t.
221d62bc4baSyz 	 */
222d62bc4baSyz 	i_dls_devnet_id_hash = mod_hash_create_idhash("dls_devnet_id_hash",
223d62bc4baSyz 	    VLAN_HASHSZ, mod_hash_null_valdtor);
224d62bc4baSyz 
225d62bc4baSyz 	/*
226da14cebeSEric Cheng 	 * Create a hash table, keyed by dd_mac
227d62bc4baSyz 	 */
228d62bc4baSyz 	i_dls_devnet_hash = mod_hash_create_extended("dls_devnet_hash",
229d62bc4baSyz 	    VLAN_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
230d62bc4baSyz 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
231d62bc4baSyz 
232d62bc4baSyz 	devnet_need_rebuild = B_FALSE;
2332b24ab6bSSebastien Roy 
2342b24ab6bSSebastien Roy 	netstack_register(NS_DLS, dls_stack_init, dls_stack_shutdown,
2352b24ab6bSSebastien Roy 	    dls_stack_fini);
236d62bc4baSyz }
237d62bc4baSyz 
238d62bc4baSyz void
dls_mgmt_fini(void)239d62bc4baSyz dls_mgmt_fini(void)
240d62bc4baSyz {
2412b24ab6bSSebastien Roy 	netstack_unregister(NS_DLS);
242d62bc4baSyz 	mod_hash_destroy_hash(i_dls_devnet_hash);
243d62bc4baSyz 	mod_hash_destroy_hash(i_dls_devnet_id_hash);
244d62bc4baSyz 	kmem_cache_destroy(i_dls_devnet_cachep);
245d62bc4baSyz 	rw_destroy(&i_dls_devnet_lock);
246d62bc4baSyz 	mutex_destroy(&i_dls_mgmt_lock);
247d62bc4baSyz }
248d62bc4baSyz 
249d62bc4baSyz int
dls_mgmt_door_set(boolean_t start)250d62bc4baSyz dls_mgmt_door_set(boolean_t start)
251d62bc4baSyz {
252d62bc4baSyz 	int	err;
253d62bc4baSyz 
254d62bc4baSyz 	/* handle daemon restart */
255d62bc4baSyz 	mutex_enter(&i_dls_mgmt_lock);
256d62bc4baSyz 	if (dls_mgmt_dh != NULL) {
257d62bc4baSyz 		door_ki_rele(dls_mgmt_dh);
258d62bc4baSyz 		dls_mgmt_dh = NULL;
259d62bc4baSyz 	}
260d62bc4baSyz 
261d62bc4baSyz 	if (start && ((err = door_ki_open(DLMGMT_DOOR, &dls_mgmt_dh)) != 0)) {
262d62bc4baSyz 		mutex_exit(&i_dls_mgmt_lock);
263d62bc4baSyz 		return (err);
264d62bc4baSyz 	}
265d62bc4baSyz 
266d62bc4baSyz 	mutex_exit(&i_dls_mgmt_lock);
267d62bc4baSyz 
268d62bc4baSyz 	/*
269d62bc4baSyz 	 * Create and associate <link name, linkid> mapping for network devices
270d62bc4baSyz 	 * which are already attached before the daemon is started.
271d62bc4baSyz 	 */
272d62bc4baSyz 	if (start)
273d62bc4baSyz 		softmac_recreate();
274d62bc4baSyz 	return (0);
275d62bc4baSyz }
276d62bc4baSyz 
277d62bc4baSyz static boolean_t
i_dls_mgmt_door_revoked(door_handle_t dh)278d62bc4baSyz i_dls_mgmt_door_revoked(door_handle_t dh)
279d62bc4baSyz {
280d62bc4baSyz 	struct door_info info;
281d62bc4baSyz 	extern int sys_shutdown;
282d62bc4baSyz 
283d62bc4baSyz 	ASSERT(dh != NULL);
284d62bc4baSyz 
285d62bc4baSyz 	if (sys_shutdown) {
286d62bc4baSyz 		cmn_err(CE_NOTE, "dls_mgmt_door: shutdown observed\n");
287d62bc4baSyz 		return (B_TRUE);
288d62bc4baSyz 	}
289d62bc4baSyz 
290d62bc4baSyz 	if (door_ki_info(dh, &info) != 0)
291d62bc4baSyz 		return (B_TRUE);
292d62bc4baSyz 
293d62bc4baSyz 	return ((info.di_attributes & DOOR_REVOKED) != 0);
294d62bc4baSyz }
295d62bc4baSyz 
296d62bc4baSyz /*
297d62bc4baSyz  * Upcall to the datalink management daemon (dlmgmtd).
298d62bc4baSyz  */
299d62bc4baSyz static int
i_dls_mgmt_upcall(void * arg,size_t asize,void * rbuf,size_t rsize)300024b0a25Sseb i_dls_mgmt_upcall(void *arg, size_t asize, void *rbuf, size_t rsize)
301d62bc4baSyz {
302d62bc4baSyz 	door_arg_t			darg, save_arg;
303d62bc4baSyz 	door_handle_t			dh;
304024b0a25Sseb 	int				err;
305d62bc4baSyz 	int				retry = 0;
306d62bc4baSyz 
307d62bc4baSyz #define	MAXRETRYNUM	3
308d62bc4baSyz 
309d62bc4baSyz 	ASSERT(arg);
310d62bc4baSyz 	darg.data_ptr = arg;
311d62bc4baSyz 	darg.data_size = asize;
312d62bc4baSyz 	darg.desc_ptr = NULL;
313d62bc4baSyz 	darg.desc_num = 0;
314d62bc4baSyz 	darg.rbuf = rbuf;
315024b0a25Sseb 	darg.rsize = rsize;
316d62bc4baSyz 	save_arg = darg;
317d62bc4baSyz 
318d62bc4baSyz retry:
319d62bc4baSyz 	mutex_enter(&i_dls_mgmt_lock);
320d62bc4baSyz 	dh = dls_mgmt_dh;
321d62bc4baSyz 	if ((dh == NULL) || i_dls_mgmt_door_revoked(dh)) {
322d62bc4baSyz 		mutex_exit(&i_dls_mgmt_lock);
323d62bc4baSyz 		return (EBADF);
324d62bc4baSyz 	}
325d62bc4baSyz 	door_ki_hold(dh);
326d62bc4baSyz 	mutex_exit(&i_dls_mgmt_lock);
327d62bc4baSyz 
328d62bc4baSyz 	for (;;) {
329d62bc4baSyz 		retry++;
3302b24ab6bSSebastien Roy 		if ((err = door_ki_upcall_limited(dh, &darg, zone_kcred(),
331323a81d9Sjwadams 		    SIZE_MAX, 0)) == 0)
332d62bc4baSyz 			break;
333d62bc4baSyz 
334d62bc4baSyz 		/*
335d62bc4baSyz 		 * handle door call errors
336d62bc4baSyz 		 */
337d62bc4baSyz 		darg = save_arg;
338d62bc4baSyz 		switch (err) {
339d62bc4baSyz 		case EINTR:
340d62bc4baSyz 			/*
341d62bc4baSyz 			 * If the operation which caused this door upcall gets
342d62bc4baSyz 			 * interrupted, return directly.
343d62bc4baSyz 			 */
344d62bc4baSyz 			goto done;
345d62bc4baSyz 		case EAGAIN:
346d62bc4baSyz 			/*
347d62bc4baSyz 			 * Repeat upcall if the maximum attempt limit has not
348d62bc4baSyz 			 * been reached.
349d62bc4baSyz 			 */
350d62bc4baSyz 			if (retry < MAXRETRYNUM) {
351d62bc4baSyz 				delay(2 * hz);
352d62bc4baSyz 				break;
353d62bc4baSyz 			}
354d62bc4baSyz 			cmn_err(CE_WARN, "dls: dlmgmtd fatal error %d\n", err);
355d62bc4baSyz 			goto done;
356d62bc4baSyz 		default:
357d62bc4baSyz 			/* A fatal door error */
358d62bc4baSyz 			if (i_dls_mgmt_door_revoked(dh)) {
359d62bc4baSyz 				cmn_err(CE_NOTE,
360d62bc4baSyz 				    "dls: dlmgmtd door service revoked\n");
361d62bc4baSyz 
362d62bc4baSyz 				if (retry < MAXRETRYNUM) {
363d62bc4baSyz 					door_ki_rele(dh);
364d62bc4baSyz 					goto retry;
365d62bc4baSyz 				}
366d62bc4baSyz 			}
367d62bc4baSyz 			cmn_err(CE_WARN, "dls: dlmgmtd fatal error %d\n", err);
368d62bc4baSyz 			goto done;
369d62bc4baSyz 		}
370d62bc4baSyz 	}
371d62bc4baSyz 
372d62bc4baSyz 	if (darg.rbuf != rbuf) {
373d62bc4baSyz 		/*
374d62bc4baSyz 		 * The size of the input rbuf was not big enough, so the
375d62bc4baSyz 		 * upcall allocated the rbuf itself.  If this happens, assume
376d62bc4baSyz 		 * that this was an invalid door call request.
377d62bc4baSyz 		 */
378d62bc4baSyz 		kmem_free(darg.rbuf, darg.rsize);
379d62bc4baSyz 		err = ENOSPC;
380d62bc4baSyz 		goto done;
381d62bc4baSyz 	}
382d62bc4baSyz 
383024b0a25Sseb 	if (darg.rsize != rsize) {
384d62bc4baSyz 		err = EINVAL;
385d62bc4baSyz 		goto done;
386d62bc4baSyz 	}
387d62bc4baSyz 
388024b0a25Sseb 	err = ((dlmgmt_retval_t *)rbuf)->lr_err;
389d62bc4baSyz 
390d62bc4baSyz done:
391d62bc4baSyz 	door_ki_rele(dh);
392d62bc4baSyz 	return (err);
393d62bc4baSyz }
394d62bc4baSyz 
395d62bc4baSyz /*
396d62bc4baSyz  * Request the datalink management daemon to create a link with the attributes
397d62bc4baSyz  * below.  Upon success, zero is returned and linkidp contains the linkid for
398d62bc4baSyz  * the new link; otherwise, an errno is returned.
399d62bc4baSyz  *
400d62bc4baSyz  *     - dev		physical dev_t.  required for all physical links,
401d62bc4baSyz  *		        including GLDv3 links.  It will be used to force the
402d62bc4baSyz  *		        attachment of a physical device, hence the
403d62bc4baSyz  *		        registration of its mac
404d62bc4baSyz  *     - class		datalink class
405d62bc4baSyz  *     - media type	media type; DL_OTHER means unknown
406d62bc4baSyz  *     - persist	whether to persist the datalink
407d62bc4baSyz  */
408d62bc4baSyz int
dls_mgmt_create(const char * devname,dev_t dev,datalink_class_t class,uint32_t media,boolean_t persist,datalink_id_t * linkidp)409d62bc4baSyz dls_mgmt_create(const char *devname, dev_t dev, datalink_class_t class,
410d62bc4baSyz     uint32_t media, boolean_t persist, datalink_id_t *linkidp)
411d62bc4baSyz {
412d62bc4baSyz 	dlmgmt_upcall_arg_create_t	create;
413d62bc4baSyz 	dlmgmt_create_retval_t		retval;
414d62bc4baSyz 	int				err;
415d62bc4baSyz 
416d62bc4baSyz 	create.ld_cmd = DLMGMT_CMD_DLS_CREATE;
417d62bc4baSyz 	create.ld_class = class;
418d62bc4baSyz 	create.ld_media = media;
419d62bc4baSyz 	create.ld_phymaj = getmajor(dev);
420d62bc4baSyz 	create.ld_phyinst = getminor(dev);
421d62bc4baSyz 	create.ld_persist = persist;
4222b24ab6bSSebastien Roy 	if (strlcpy(create.ld_devname, devname, sizeof (create.ld_devname)) >=
4232b24ab6bSSebastien Roy 	    sizeof (create.ld_devname))
424d62bc4baSyz 		return (EINVAL);
425d62bc4baSyz 
426024b0a25Sseb 	if ((err = i_dls_mgmt_upcall(&create, sizeof (create), &retval,
427024b0a25Sseb 	    sizeof (retval))) == 0) {
428d62bc4baSyz 		*linkidp = retval.lr_linkid;
429024b0a25Sseb 	}
430d62bc4baSyz 	return (err);
431d62bc4baSyz }
432d62bc4baSyz 
433d62bc4baSyz /*
434d62bc4baSyz  * Request the datalink management daemon to destroy the specified link.
435d62bc4baSyz  * Returns zero upon success, or an errno upon failure.
436d62bc4baSyz  */
437d62bc4baSyz int
dls_mgmt_destroy(datalink_id_t linkid,boolean_t persist)438d62bc4baSyz dls_mgmt_destroy(datalink_id_t linkid, boolean_t persist)
439d62bc4baSyz {
440d62bc4baSyz 	dlmgmt_upcall_arg_destroy_t	destroy;
441d62bc4baSyz 	dlmgmt_destroy_retval_t		retval;
442d62bc4baSyz 
443d62bc4baSyz 	destroy.ld_cmd = DLMGMT_CMD_DLS_DESTROY;
444d62bc4baSyz 	destroy.ld_linkid = linkid;
445d62bc4baSyz 	destroy.ld_persist = persist;
446d62bc4baSyz 
447024b0a25Sseb 	return (i_dls_mgmt_upcall(&destroy, sizeof (destroy),
448024b0a25Sseb 	    &retval, sizeof (retval)));
449d62bc4baSyz }
450d62bc4baSyz 
451d62bc4baSyz /*
452d62bc4baSyz  * Request the datalink management daemon to verify/update the information
453d62bc4baSyz  * for a physical link.  Upon success, get its linkid.
454d62bc4baSyz  *
455d62bc4baSyz  *     - media type	media type
456d62bc4baSyz  *     - novanity	whether this physical datalink supports vanity naming.
457d62bc4baSyz  *			physical links that do not use the GLDv3 MAC plugin
458d62bc4baSyz  *			cannot suport vanity naming
459d62bc4baSyz  *
460d62bc4baSyz  * This function could fail with ENOENT or EEXIST.  Two cases return EEXIST:
461d62bc4baSyz  *
462d62bc4baSyz  * 1. A link with devname already exists, but the media type does not match.
463d62bc4baSyz  *    In this case, mediap will bee set to the media type of the existing link.
464d62bc4baSyz  * 2. A link with devname already exists, but its link name does not match
465d62bc4baSyz  *    the device name, although this link does not support vanity naming.
466d62bc4baSyz  */
467d62bc4baSyz int
dls_mgmt_update(const char * devname,uint32_t media,boolean_t novanity,uint32_t * mediap,datalink_id_t * linkidp)468d62bc4baSyz dls_mgmt_update(const char *devname, uint32_t media, boolean_t novanity,
469d62bc4baSyz     uint32_t *mediap, datalink_id_t *linkidp)
470d62bc4baSyz {
471d62bc4baSyz 	dlmgmt_upcall_arg_update_t	update;
472d62bc4baSyz 	dlmgmt_update_retval_t		retval;
473d62bc4baSyz 	int				err;
474d62bc4baSyz 
475d62bc4baSyz 	update.ld_cmd = DLMGMT_CMD_DLS_UPDATE;
476d62bc4baSyz 
4772b24ab6bSSebastien Roy 	if (strlcpy(update.ld_devname, devname, sizeof (update.ld_devname)) >=
4782b24ab6bSSebastien Roy 	    sizeof (update.ld_devname))
479d62bc4baSyz 		return (EINVAL);
480d62bc4baSyz 
481d62bc4baSyz 	update.ld_media = media;
482d62bc4baSyz 	update.ld_novanity = novanity;
483d62bc4baSyz 
484024b0a25Sseb 	if ((err = i_dls_mgmt_upcall(&update, sizeof (update), &retval,
485024b0a25Sseb 	    sizeof (retval))) == EEXIST) {
486d62bc4baSyz 		*linkidp = retval.lr_linkid;
487d62bc4baSyz 		*mediap = retval.lr_media;
488d62bc4baSyz 	} else if (err == 0) {
489d62bc4baSyz 		*linkidp = retval.lr_linkid;
490d62bc4baSyz 	}
491d62bc4baSyz 
492d62bc4baSyz 	return (err);
493d62bc4baSyz }
494d62bc4baSyz 
495d62bc4baSyz /*
496d62bc4baSyz  * Request the datalink management daemon to get the information for a link.
497d62bc4baSyz  * Returns zero upon success, or an errno upon failure.
498d62bc4baSyz  *
499d62bc4baSyz  * Only fills in information for argument pointers that are non-NULL.
500d62bc4baSyz  * Note that the link argument is expected to be MAXLINKNAMELEN bytes.
501d62bc4baSyz  */
502d62bc4baSyz int
dls_mgmt_get_linkinfo(datalink_id_t linkid,char * link,datalink_class_t * classp,uint32_t * mediap,uint32_t * flagsp)503d62bc4baSyz dls_mgmt_get_linkinfo(datalink_id_t linkid, char *link,
504d62bc4baSyz     datalink_class_t *classp, uint32_t *mediap, uint32_t *flagsp)
505d62bc4baSyz {
506d62bc4baSyz 	dlmgmt_door_getname_t	getname;
507d62bc4baSyz 	dlmgmt_getname_retval_t	retval;
508d62bc4baSyz 	int			err, len;
509d62bc4baSyz 
510d62bc4baSyz 	getname.ld_cmd = DLMGMT_CMD_GETNAME;
511d62bc4baSyz 	getname.ld_linkid = linkid;
512d62bc4baSyz 
513024b0a25Sseb 	if ((err = i_dls_mgmt_upcall(&getname, sizeof (getname), &retval,
514024b0a25Sseb 	    sizeof (retval))) != 0) {
515d62bc4baSyz 		return (err);
516024b0a25Sseb 	}
517d62bc4baSyz 
518d62bc4baSyz 	len = strlen(retval.lr_link);
519d62bc4baSyz 	if (len <= 1 || len >= MAXLINKNAMELEN)
520d62bc4baSyz 		return (EINVAL);
521d62bc4baSyz 
522d62bc4baSyz 	if (link != NULL)
523d62bc4baSyz 		(void) strlcpy(link, retval.lr_link, MAXLINKNAMELEN);
524d62bc4baSyz 	if (classp != NULL)
525d62bc4baSyz 		*classp = retval.lr_class;
526d62bc4baSyz 	if (mediap != NULL)
527d62bc4baSyz 		*mediap = retval.lr_media;
528d62bc4baSyz 	if (flagsp != NULL)
529d62bc4baSyz 		*flagsp = retval.lr_flags;
530d62bc4baSyz 	return (0);
531d62bc4baSyz }
532d62bc4baSyz 
533d62bc4baSyz /*
534d62bc4baSyz  * Request the datalink management daemon to get the linkid for a link.
535d62bc4baSyz  * Returns a non-zero error code on failure.  The linkid argument is only
536d62bc4baSyz  * set on success (when zero is returned.)
537d62bc4baSyz  */
538d62bc4baSyz int
dls_mgmt_get_linkid(const char * link,datalink_id_t * linkid)539d62bc4baSyz dls_mgmt_get_linkid(const char *link, datalink_id_t *linkid)
540d62bc4baSyz {
541d62bc4baSyz 	dlmgmt_door_getlinkid_t		getlinkid;
542d62bc4baSyz 	dlmgmt_getlinkid_retval_t	retval;
543d62bc4baSyz 	int				err;
544d62bc4baSyz 
545d62bc4baSyz 	getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID;
546d62bc4baSyz 	(void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN);
547d62bc4baSyz 
548024b0a25Sseb 	if ((err = i_dls_mgmt_upcall(&getlinkid, sizeof (getlinkid), &retval,
549024b0a25Sseb 	    sizeof (retval))) == 0) {
550d62bc4baSyz 		*linkid = retval.lr_linkid;
551024b0a25Sseb 	}
552d62bc4baSyz 	return (err);
553d62bc4baSyz }
554d62bc4baSyz 
555d62bc4baSyz datalink_id_t
dls_mgmt_get_next(datalink_id_t linkid,datalink_class_t class,datalink_media_t dmedia,uint32_t flags)556d62bc4baSyz dls_mgmt_get_next(datalink_id_t linkid, datalink_class_t class,
557d62bc4baSyz     datalink_media_t dmedia, uint32_t flags)
558d62bc4baSyz {
559d62bc4baSyz 	dlmgmt_door_getnext_t	getnext;
560d62bc4baSyz 	dlmgmt_getnext_retval_t	retval;
561d62bc4baSyz 
562d62bc4baSyz 	getnext.ld_cmd = DLMGMT_CMD_GETNEXT;
563d62bc4baSyz 	getnext.ld_class = class;
564d62bc4baSyz 	getnext.ld_dmedia = dmedia;
565d62bc4baSyz 	getnext.ld_flags = flags;
566d62bc4baSyz 	getnext.ld_linkid = linkid;
567d62bc4baSyz 
568024b0a25Sseb 	if (i_dls_mgmt_upcall(&getnext, sizeof (getnext), &retval,
569024b0a25Sseb 	    sizeof (retval)) != 0) {
570d62bc4baSyz 		return (DATALINK_INVALID_LINKID);
571024b0a25Sseb 	}
572d62bc4baSyz 
573d62bc4baSyz 	return (retval.lr_linkid);
574d62bc4baSyz }
575d62bc4baSyz 
576d62bc4baSyz static int
i_dls_mgmt_get_linkattr(const datalink_id_t linkid,const char * attr,void * attrval,size_t * attrszp)577d62bc4baSyz i_dls_mgmt_get_linkattr(const datalink_id_t linkid, const char *attr,
578d62bc4baSyz     void *attrval, size_t *attrszp)
579d62bc4baSyz {
580d62bc4baSyz 	dlmgmt_upcall_arg_getattr_t	getattr;
581024b0a25Sseb 	dlmgmt_getattr_retval_t		retval;
582d62bc4baSyz 	int				err;
583d62bc4baSyz 
584d62bc4baSyz 	getattr.ld_cmd = DLMGMT_CMD_DLS_GETATTR;
585d62bc4baSyz 	getattr.ld_linkid = linkid;
586d62bc4baSyz 	(void) strlcpy(getattr.ld_attr, attr, MAXLINKATTRLEN);
587d62bc4baSyz 
588024b0a25Sseb 	if ((err = i_dls_mgmt_upcall(&getattr, sizeof (getattr), &retval,
589024b0a25Sseb 	    sizeof (retval))) == 0) {
590024b0a25Sseb 		if (*attrszp < retval.lr_attrsz)
591024b0a25Sseb 			return (EINVAL);
592024b0a25Sseb 		*attrszp = retval.lr_attrsz;
593024b0a25Sseb 		bcopy(retval.lr_attrval, attrval, retval.lr_attrsz);
594d62bc4baSyz 	}
595d62bc4baSyz 
596d62bc4baSyz 	return (err);
597d62bc4baSyz }
598d62bc4baSyz 
599d62bc4baSyz /*
600d62bc4baSyz  * Note that this function can only get devp successfully for non-VLAN link.
601d62bc4baSyz  */
602d62bc4baSyz int
dls_mgmt_get_phydev(datalink_id_t linkid,dev_t * devp)603d62bc4baSyz dls_mgmt_get_phydev(datalink_id_t linkid, dev_t *devp)
604d62bc4baSyz {
605d62bc4baSyz 	uint64_t	maj, inst;
606d62bc4baSyz 	size_t		attrsz = sizeof (uint64_t);
607d62bc4baSyz 
608d62bc4baSyz 	if (i_dls_mgmt_get_linkattr(linkid, FPHYMAJ, &maj, &attrsz) != 0 ||
609d62bc4baSyz 	    attrsz != sizeof (uint64_t) ||
610d62bc4baSyz 	    i_dls_mgmt_get_linkattr(linkid, FPHYINST, &inst, &attrsz) != 0 ||
611d62bc4baSyz 	    attrsz != sizeof (uint64_t)) {
612d62bc4baSyz 		return (EINVAL);
613d62bc4baSyz 	}
614d62bc4baSyz 
615d62bc4baSyz 	*devp = makedevice((major_t)maj, (minor_t)inst);
616d62bc4baSyz 	return (0);
617d62bc4baSyz }
618d62bc4baSyz 
61930890389Sartem /*
62030890389Sartem  * Request the datalink management daemon to push in
62130890389Sartem  * all properties associated with the link.
62230890389Sartem  * Returns a non-zero error code on failure.
62330890389Sartem  */
62430890389Sartem int
dls_mgmt_linkprop_init(datalink_id_t linkid)62530890389Sartem dls_mgmt_linkprop_init(datalink_id_t linkid)
62630890389Sartem {
62730890389Sartem 	dlmgmt_door_linkprop_init_t	li;
62830890389Sartem 	dlmgmt_linkprop_init_retval_t	retval;
62930890389Sartem 	int				err;
63030890389Sartem 
63130890389Sartem 	li.ld_cmd = DLMGMT_CMD_LINKPROP_INIT;
63230890389Sartem 	li.ld_linkid = linkid;
63330890389Sartem 
63430890389Sartem 	err = i_dls_mgmt_upcall(&li, sizeof (li), &retval, sizeof (retval));
63530890389Sartem 	return (err);
63630890389Sartem }
63730890389Sartem 
63830890389Sartem static void
dls_devnet_prop_task(void * arg)63930890389Sartem dls_devnet_prop_task(void *arg)
64030890389Sartem {
64130890389Sartem 	dls_devnet_t		*ddp = arg;
64230890389Sartem 
643da14cebeSEric Cheng 	(void) dls_mgmt_linkprop_init(ddp->dd_linkid);
64430890389Sartem 
64530890389Sartem 	mutex_enter(&ddp->dd_mutex);
64630890389Sartem 	ddp->dd_prop_loaded = B_TRUE;
647639c732eSToomas Soome 	ddp->dd_prop_taskid = 0;
64830890389Sartem 	cv_broadcast(&ddp->dd_cv);
64930890389Sartem 	mutex_exit(&ddp->dd_mutex);
65030890389Sartem }
65130890389Sartem 
65230890389Sartem /*
65330890389Sartem  * Ensure property loading task is completed.
65430890389Sartem  */
65530890389Sartem void
dls_devnet_prop_task_wait(dls_dl_handle_t ddp)65630890389Sartem dls_devnet_prop_task_wait(dls_dl_handle_t ddp)
65730890389Sartem {
65830890389Sartem 	mutex_enter(&ddp->dd_mutex);
659639c732eSToomas Soome 	while (ddp->dd_prop_taskid != 0)
66030890389Sartem 		cv_wait(&ddp->dd_cv, &ddp->dd_mutex);
66130890389Sartem 	mutex_exit(&ddp->dd_mutex);
66230890389Sartem }
66330890389Sartem 
664da14cebeSEric Cheng void
dls_devnet_rele_tmp(dls_dl_handle_t dlh)665da14cebeSEric Cheng dls_devnet_rele_tmp(dls_dl_handle_t dlh)
666da14cebeSEric Cheng {
667da14cebeSEric Cheng 	dls_devnet_t		*ddp = dlh;
668da14cebeSEric Cheng 
669da14cebeSEric Cheng 	mutex_enter(&ddp->dd_mutex);
670da14cebeSEric Cheng 	ASSERT(ddp->dd_tref != 0);
671da14cebeSEric Cheng 	if (--ddp->dd_tref == 0)
672da14cebeSEric Cheng 		cv_signal(&ddp->dd_cv);
673da14cebeSEric Cheng 	mutex_exit(&ddp->dd_mutex);
674da14cebeSEric Cheng }
675da14cebeSEric Cheng 
676d62bc4baSyz int
dls_devnet_hold_link(datalink_id_t linkid,dls_dl_handle_t * ddhp,dls_link_t ** dlpp)677da14cebeSEric Cheng dls_devnet_hold_link(datalink_id_t linkid, dls_dl_handle_t *ddhp,
678da14cebeSEric Cheng     dls_link_t **dlpp)
679d62bc4baSyz {
680da14cebeSEric Cheng 	dls_dl_handle_t	dlh;
681da14cebeSEric Cheng 	dls_link_t	*dlp;
682da14cebeSEric Cheng 	int		err;
683d62bc4baSyz 
684da14cebeSEric Cheng 	if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0)
685da14cebeSEric Cheng 		return (err);
686d62bc4baSyz 
687da14cebeSEric Cheng 	if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0) {
688da14cebeSEric Cheng 		dls_devnet_rele_tmp(dlh);
689da14cebeSEric Cheng 		return (err);
690d62bc4baSyz 	}
691d62bc4baSyz 
692da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dlp->dl_mh));
693d62bc4baSyz 
694da14cebeSEric Cheng 	*ddhp = dlh;
695da14cebeSEric Cheng 	*dlpp = dlp;
696d62bc4baSyz 	return (0);
697d62bc4baSyz }
698d62bc4baSyz 
699d62bc4baSyz void
dls_devnet_rele_link(dls_dl_handle_t dlh,dls_link_t * dlp)700da14cebeSEric Cheng dls_devnet_rele_link(dls_dl_handle_t dlh, dls_link_t *dlp)
701d62bc4baSyz {
702da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dlp->dl_mh));
703d62bc4baSyz 
704da14cebeSEric Cheng 	dls_link_rele(dlp);
705da14cebeSEric Cheng 	dls_devnet_rele_tmp(dlh);
706d62bc4baSyz }
707d62bc4baSyz 
708d62bc4baSyz /*
709d62bc4baSyz  * "link" kstats related functions.
710d62bc4baSyz  */
711d62bc4baSyz 
712d62bc4baSyz /*
713d62bc4baSyz  * Query the "link" kstats.
714ae6aa22aSVenugopal Iyer  *
715ae6aa22aSVenugopal Iyer  * We may be called from the kstat subsystem in an arbitrary context.
716ae6aa22aSVenugopal Iyer  * If the caller is the stack, the context could be an upcall data
717ae6aa22aSVenugopal Iyer  * thread. Hence we can't acquire the mac perimeter in this function
718ae6aa22aSVenugopal Iyer  * for fear of deadlock.
719d62bc4baSyz  */
720d62bc4baSyz static int
dls_devnet_stat_update(kstat_t * ksp,int rw)721d62bc4baSyz dls_devnet_stat_update(kstat_t *ksp, int rw)
722d62bc4baSyz {
723fe4627efSSebastien Roy 	datalink_id_t	linkid = (datalink_id_t)(uintptr_t)ksp->ks_private;
724fe4627efSSebastien Roy 	dls_devnet_t	*ddp;
725da14cebeSEric Cheng 	dls_link_t	*dlp;
726d62bc4baSyz 	int		err;
727d62bc4baSyz 
728fe4627efSSebastien Roy 	if ((err = dls_devnet_hold_tmp(linkid, &ddp)) != 0) {
729fe4627efSSebastien Roy 		return (err);
730ae6aa22aSVenugopal Iyer 	}
731d62bc4baSyz 
732ae6aa22aSVenugopal Iyer 	/*
733ae6aa22aSVenugopal Iyer 	 * If a device detach happens at this time, it will block in
734fe4627efSSebastien Roy 	 * dls_devnet_unset since the dd_tref has been bumped in
735fe4627efSSebastien Roy 	 * dls_devnet_hold_tmp(). So the access to 'dlp' is safe even though
736fe4627efSSebastien Roy 	 * we don't hold the mac perimeter.
737ae6aa22aSVenugopal Iyer 	 */
738ae6aa22aSVenugopal Iyer 	if (mod_hash_find(i_dls_link_hash, (mod_hash_key_t)ddp->dd_mac,
739ae6aa22aSVenugopal Iyer 	    (mod_hash_val_t *)&dlp) != 0) {
740ae6aa22aSVenugopal Iyer 		dls_devnet_rele_tmp(ddp);
741ae6aa22aSVenugopal Iyer 		return (ENOENT);
742da14cebeSEric Cheng 	}
743da14cebeSEric Cheng 
744da14cebeSEric Cheng 	err = dls_stat_update(ksp, dlp, rw);
745ae6aa22aSVenugopal Iyer 
746ae6aa22aSVenugopal Iyer 	dls_devnet_rele_tmp(ddp);
747d62bc4baSyz 	return (err);
748d62bc4baSyz }
749d62bc4baSyz 
750d62bc4baSyz /*
751d62bc4baSyz  * Create the "link" kstats.
752d62bc4baSyz  */
753d62bc4baSyz static void
dls_devnet_stat_create(dls_devnet_t * ddp,zoneid_t zoneid)7542b24ab6bSSebastien Roy dls_devnet_stat_create(dls_devnet_t *ddp, zoneid_t zoneid)
755d62bc4baSyz {
756d62bc4baSyz 	kstat_t	*ksp;
757d62bc4baSyz 
7582b24ab6bSSebastien Roy 	if (dls_stat_create("link", 0, ddp->dd_linkname, zoneid,
759fe4627efSSebastien Roy 	    dls_devnet_stat_update, (void *)(uintptr_t)ddp->dd_linkid,
760fe4627efSSebastien Roy 	    &ksp) == 0) {
7612b24ab6bSSebastien Roy 		ASSERT(ksp != NULL);
7622b24ab6bSSebastien Roy 		if (zoneid == ddp->dd_owner_zid) {
7632b24ab6bSSebastien Roy 			ASSERT(ddp->dd_ksp == NULL);
7642b24ab6bSSebastien Roy 			ddp->dd_ksp = ksp;
7652b24ab6bSSebastien Roy 		} else {
7662b24ab6bSSebastien Roy 			ASSERT(ddp->dd_zone_ksp == NULL);
7672b24ab6bSSebastien Roy 			ddp->dd_zone_ksp = ksp;
7682b24ab6bSSebastien Roy 		}
769d62bc4baSyz 	}
770d62bc4baSyz }
771d62bc4baSyz 
772d62bc4baSyz /*
773d62bc4baSyz  * Destroy the "link" kstats.
774d62bc4baSyz  */
775d62bc4baSyz static void
dls_devnet_stat_destroy(dls_devnet_t * ddp,zoneid_t zoneid)7762b24ab6bSSebastien Roy dls_devnet_stat_destroy(dls_devnet_t *ddp, zoneid_t zoneid)
777d62bc4baSyz {
7782b24ab6bSSebastien Roy 	if (zoneid == ddp->dd_owner_zid) {
7792b24ab6bSSebastien Roy 		if (ddp->dd_ksp != NULL) {
7802b24ab6bSSebastien Roy 			kstat_delete(ddp->dd_ksp);
7812b24ab6bSSebastien Roy 			ddp->dd_ksp = NULL;
7822b24ab6bSSebastien Roy 		}
7832b24ab6bSSebastien Roy 	} else {
7842b24ab6bSSebastien Roy 		if (ddp->dd_zone_ksp != NULL) {
7852b24ab6bSSebastien Roy 			kstat_delete(ddp->dd_zone_ksp);
7862b24ab6bSSebastien Roy 			ddp->dd_zone_ksp = NULL;
7872b24ab6bSSebastien Roy 		}
7882b24ab6bSSebastien Roy 	}
789d62bc4baSyz }
790d62bc4baSyz 
791d62bc4baSyz /*
792d62bc4baSyz  * The link has been renamed. Destroy the old non-legacy kstats ("link kstats")
793d62bc4baSyz  * and create the new set using the new name.
794d62bc4baSyz  */
795d62bc4baSyz static void
dls_devnet_stat_rename(dls_devnet_t * ddp)7962b24ab6bSSebastien Roy dls_devnet_stat_rename(dls_devnet_t *ddp)
797d62bc4baSyz {
798d62bc4baSyz 	if (ddp->dd_ksp != NULL) {
799d62bc4baSyz 		kstat_delete(ddp->dd_ksp);
800d62bc4baSyz 		ddp->dd_ksp = NULL;
801d62bc4baSyz 	}
8022b24ab6bSSebastien Roy 	/* We can't rename a link while it's assigned to a non-global zone. */
8032b24ab6bSSebastien Roy 	ASSERT(ddp->dd_zone_ksp == NULL);
8042b24ab6bSSebastien Roy 	dls_devnet_stat_create(ddp, ddp->dd_owner_zid);
805d62bc4baSyz }
806d62bc4baSyz 
807d62bc4baSyz /*
80885dff7a0SAndy Fiddaman  * Associate the linkid with the link identified by macname. If this
80985dff7a0SAndy Fiddaman  * is called on behalf of a physical link then linkid may be
81085dff7a0SAndy Fiddaman  * DATALINK_INVALID_LINKID. Otherwise, if called on behalf of a
81185dff7a0SAndy Fiddaman  * virtual link, linkid must have a value.
812d62bc4baSyz  */
813d62bc4baSyz static int
dls_devnet_set(mac_handle_t mh,datalink_id_t linkid,zoneid_t zoneid,dls_devnet_t ** ddpp)81485dff7a0SAndy Fiddaman dls_devnet_set(mac_handle_t mh, datalink_id_t linkid, zoneid_t zoneid,
8152b24ab6bSSebastien Roy     dls_devnet_t **ddpp)
816d62bc4baSyz {
81785dff7a0SAndy Fiddaman 	const char		*macname = mac_name(mh);
818d62bc4baSyz 	dls_devnet_t		*ddp = NULL;
819d62bc4baSyz 	datalink_class_t	class;
820d62bc4baSyz 	int			err;
821ae6aa22aSVenugopal Iyer 	boolean_t		stat_create = B_FALSE;
8222b24ab6bSSebastien Roy 	char			linkname[MAXLINKNAMELEN];
823d62bc4baSyz 
824d62bc4baSyz 	rw_enter(&i_dls_devnet_lock, RW_WRITER);
8252b24ab6bSSebastien Roy 
8262b24ab6bSSebastien Roy 	/*
8272b24ab6bSSebastien Roy 	 * Don't allow callers to set a link name with a linkid that already
8282b24ab6bSSebastien Roy 	 * has a name association (that's what rename is for).
8292b24ab6bSSebastien Roy 	 */
8302b24ab6bSSebastien Roy 	if (linkid != DATALINK_INVALID_LINKID) {
8312b24ab6bSSebastien Roy 		if (mod_hash_find(i_dls_devnet_id_hash,
8322b24ab6bSSebastien Roy 		    (mod_hash_key_t)(uintptr_t)linkid,
8332b24ab6bSSebastien Roy 		    (mod_hash_val_t *)&ddp) == 0) {
8342b24ab6bSSebastien Roy 			err = EEXIST;
8352b24ab6bSSebastien Roy 			goto done;
8362b24ab6bSSebastien Roy 		}
8372b24ab6bSSebastien Roy 		if ((err = dls_mgmt_get_linkinfo(linkid, linkname, &class,
8382b24ab6bSSebastien Roy 		    NULL, NULL)) != 0)
8392b24ab6bSSebastien Roy 			goto done;
8402b24ab6bSSebastien Roy 	}
8412b24ab6bSSebastien Roy 
842d62bc4baSyz 	if ((err = mod_hash_find(i_dls_devnet_hash,
843da14cebeSEric Cheng 	    (mod_hash_key_t)macname, (mod_hash_val_t *)&ddp)) == 0) {
844da14cebeSEric Cheng 		if (ddp->dd_linkid != DATALINK_INVALID_LINKID) {
845da14cebeSEric Cheng 			err = EEXIST;
846da14cebeSEric Cheng 			goto done;
847d62bc4baSyz 		}
848d62bc4baSyz 
849d62bc4baSyz 		/*
85085dff7a0SAndy Fiddaman 		 * If we arrive here we know we are attempting to set
85185dff7a0SAndy Fiddaman 		 * the linkid on a physical link. A virtual link
85285dff7a0SAndy Fiddaman 		 * should never arrive here because it should never
85385dff7a0SAndy Fiddaman 		 * call this function without a linkid. Virtual links
85485dff7a0SAndy Fiddaman 		 * are created through dlgmtmd and thus we know
85585dff7a0SAndy Fiddaman 		 * dlmgmtd is alive to assign it a linkid (search for
85685dff7a0SAndy Fiddaman 		 * uses of dladm_create_datalink_id() to prove this to
85785dff7a0SAndy Fiddaman 		 * yourself); we don't have the same guarantee for a
85885dff7a0SAndy Fiddaman 		 * physical link which may perform an upcall for a
85985dff7a0SAndy Fiddaman 		 * linkid while dlmgmtd is down but will continue
86085dff7a0SAndy Fiddaman 		 * creating a devnet without the linkid (see
86185dff7a0SAndy Fiddaman 		 * softmac_create_datalink() to see how physical link
86285dff7a0SAndy Fiddaman 		 * creation works). That is why there is no entry in
86385dff7a0SAndy Fiddaman 		 * the id hash but there is one in the macname hash --
86485dff7a0SAndy Fiddaman 		 * softmac couldn't acquire a linkid the first time it
86585dff7a0SAndy Fiddaman 		 * called this function.
86685dff7a0SAndy Fiddaman 		 *
86785dff7a0SAndy Fiddaman 		 * Because of the check above, we also know that
86885dff7a0SAndy Fiddaman 		 * ddp->dd_linkid is not set. Following this, the link
86985dff7a0SAndy Fiddaman 		 * must still be in the DD_INITIALIZING state because
87085dff7a0SAndy Fiddaman 		 * that flag is removed IFF dd_linkid is set. This is
87185dff7a0SAndy Fiddaman 		 * why we can ASSERT the DD_INITIALIZING flag below if
87285dff7a0SAndy Fiddaman 		 * the call to i_dls_devnet_setzid() fails.
873d62bc4baSyz 		 */
8742b24ab6bSSebastien Roy 		if (linkid == DATALINK_INVALID_LINKID ||
8752b24ab6bSSebastien Roy 		    class != DATALINK_CLASS_PHYS) {
876da14cebeSEric Cheng 			err = EINVAL;
877d62bc4baSyz 			goto done;
878d62bc4baSyz 		}
87985dff7a0SAndy Fiddaman 
88085dff7a0SAndy Fiddaman 		ASSERT(ddp->dd_flags & DD_INITIALIZING);
88185dff7a0SAndy Fiddaman 
8822b24ab6bSSebastien Roy 	} else {
8832b24ab6bSSebastien Roy 		ddp = kmem_cache_alloc(i_dls_devnet_cachep, KM_SLEEP);
88485dff7a0SAndy Fiddaman 		ddp->dd_flags = DD_INITIALIZING;
8852b24ab6bSSebastien Roy 		ddp->dd_tref = 0;
8862b24ab6bSSebastien Roy 		ddp->dd_ref++;
8872b24ab6bSSebastien Roy 		ddp->dd_owner_zid = zoneid;
88885dff7a0SAndy Fiddaman 		/*
88985dff7a0SAndy Fiddaman 		 * If we are creating a new devnet which will be owned by a NGZ
89085dff7a0SAndy Fiddaman 		 * then mark it as transient. This link has never been in the
89185dff7a0SAndy Fiddaman 		 * GZ, the GZ will not have a hold on its reference, and we do
89285dff7a0SAndy Fiddaman 		 * not want to return it to the GZ when the zone halts.
89385dff7a0SAndy Fiddaman 		 */
89485dff7a0SAndy Fiddaman 		if (zoneid != GLOBAL_ZONEID)
89585dff7a0SAndy Fiddaman 			ddp->dd_transient = B_TRUE;
8962b24ab6bSSebastien Roy 		(void) strlcpy(ddp->dd_mac, macname, sizeof (ddp->dd_mac));
8972b24ab6bSSebastien Roy 		VERIFY(mod_hash_insert(i_dls_devnet_hash,
8982b24ab6bSSebastien Roy 		    (mod_hash_key_t)ddp->dd_mac, (mod_hash_val_t)ddp) == 0);
899d62bc4baSyz 	}
900d62bc4baSyz 
901da14cebeSEric Cheng 	if (linkid != DATALINK_INVALID_LINKID) {
902d62bc4baSyz 		ddp->dd_linkid = linkid;
9032b24ab6bSSebastien Roy 		(void) strlcpy(ddp->dd_linkname, linkname,
9042b24ab6bSSebastien Roy 		    sizeof (ddp->dd_linkname));
905d62bc4baSyz 		VERIFY(mod_hash_insert(i_dls_devnet_id_hash,
906da14cebeSEric Cheng 		    (mod_hash_key_t)(uintptr_t)linkid,
907d62bc4baSyz 		    (mod_hash_val_t)ddp) == 0);
908d62bc4baSyz 		devnet_need_rebuild = B_TRUE;
909ae6aa22aSVenugopal Iyer 		stat_create = B_TRUE;
910d62bc4baSyz 	}
911d62bc4baSyz 	err = 0;
912d62bc4baSyz done:
913ae6aa22aSVenugopal Iyer 	/*
914ae6aa22aSVenugopal Iyer 	 * It is safe to drop the i_dls_devnet_lock at this point. In the case
915ae6aa22aSVenugopal Iyer 	 * of physical devices, the softmac framework will fail the device
916ae6aa22aSVenugopal Iyer 	 * detach based on the smac_state or smac_hold_cnt. Other cases like
917ae6aa22aSVenugopal Iyer 	 * vnic and aggr use their own scheme to serialize creates and deletes
918ae6aa22aSVenugopal Iyer 	 * and ensure that *ddp is valid.
919ae6aa22aSVenugopal Iyer 	 */
9202b24ab6bSSebastien Roy 	rw_exit(&i_dls_devnet_lock);
92185dff7a0SAndy Fiddaman 
92285dff7a0SAndy Fiddaman 	if (err == 0 && zoneid != GLOBAL_ZONEID) {
92385dff7a0SAndy Fiddaman 		/*
92485dff7a0SAndy Fiddaman 		 * If this link is being created directly within a non-global
92585dff7a0SAndy Fiddaman 		 * zone, then flag it as transient so that it will be cleaned
92685dff7a0SAndy Fiddaman 		 * up when the zone is shut down.
92785dff7a0SAndy Fiddaman 		 */
92885dff7a0SAndy Fiddaman 		err = i_dls_devnet_setzid(ddp, zoneid, B_FALSE, B_TRUE);
92985dff7a0SAndy Fiddaman 		if (err != 0) {
93085dff7a0SAndy Fiddaman 			/*
93185dff7a0SAndy Fiddaman 			 * At this point the link is marked as
93285dff7a0SAndy Fiddaman 			 * DD_INITIALIZING -- there can be no
93385dff7a0SAndy Fiddaman 			 * outstanding temp refs and therefore no need
93485dff7a0SAndy Fiddaman 			 * to wait for them.
93585dff7a0SAndy Fiddaman 			 */
93685dff7a0SAndy Fiddaman 			ASSERT(ddp->dd_flags & DD_INITIALIZING);
93785dff7a0SAndy Fiddaman 			(void) dls_devnet_unset(mh, &linkid, B_FALSE);
93885dff7a0SAndy Fiddaman 			return (err);
93985dff7a0SAndy Fiddaman 		}
94085dff7a0SAndy Fiddaman 	}
94185dff7a0SAndy Fiddaman 
9422b24ab6bSSebastien Roy 	if (err == 0) {
9432b24ab6bSSebastien Roy 		/*
9442b24ab6bSSebastien Roy 		 * The kstat subsystem holds its own locks (rather perimeter)
9452b24ab6bSSebastien Roy 		 * before calling the ks_update (dls_devnet_stat_update) entry
9462b24ab6bSSebastien Roy 		 * point which in turn grabs the i_dls_devnet_lock. So the
9472b24ab6bSSebastien Roy 		 * lock hierarchy is kstat locks -> i_dls_devnet_lock.
9482b24ab6bSSebastien Roy 		 */
9492b24ab6bSSebastien Roy 		if (stat_create)
9502b24ab6bSSebastien Roy 			dls_devnet_stat_create(ddp, zoneid);
9512b24ab6bSSebastien Roy 		if (ddpp != NULL)
9522b24ab6bSSebastien Roy 			*ddpp = ddp;
95385dff7a0SAndy Fiddaman 
95485dff7a0SAndy Fiddaman 		mutex_enter(&ddp->dd_mutex);
95585dff7a0SAndy Fiddaman 		if (linkid != DATALINK_INVALID_LINKID &&
95685dff7a0SAndy Fiddaman 		    !ddp->dd_prop_loaded && ddp->dd_prop_taskid == 0) {
95785dff7a0SAndy Fiddaman 			ddp->dd_prop_taskid = taskq_dispatch(system_taskq,
95885dff7a0SAndy Fiddaman 			    dls_devnet_prop_task, ddp, TQ_SLEEP);
95985dff7a0SAndy Fiddaman 		}
96085dff7a0SAndy Fiddaman 		mutex_exit(&ddp->dd_mutex);
96185dff7a0SAndy Fiddaman 
9622b24ab6bSSebastien Roy 	}
963d62bc4baSyz 	return (err);
964d62bc4baSyz }
965d62bc4baSyz 
966d62bc4baSyz /*
96785dff7a0SAndy Fiddaman  * Disassociate the linkid from the link identified by macname. If
96885dff7a0SAndy Fiddaman  * wait is B_TRUE, wait until all temporary refs are released and the
96985dff7a0SAndy Fiddaman  * prop task is finished.
97085dff7a0SAndy Fiddaman  *
97185dff7a0SAndy Fiddaman  * If waiting then you SHOULD NOT call this from inside the MAC perim
97285dff7a0SAndy Fiddaman  * as deadlock will ensue. Otherwise, this function is safe to call
97385dff7a0SAndy Fiddaman  * from inside or outside the MAC perim.
974d62bc4baSyz  */
975d62bc4baSyz static int
dls_devnet_unset(mac_handle_t mh,datalink_id_t * id,boolean_t wait)97685dff7a0SAndy Fiddaman dls_devnet_unset(mac_handle_t mh, datalink_id_t *id, boolean_t wait)
977d62bc4baSyz {
97885dff7a0SAndy Fiddaman 	const char	*macname = mac_name(mh);
979d62bc4baSyz 	dls_devnet_t	*ddp;
980d62bc4baSyz 	int		err;
981da14cebeSEric Cheng 	mod_hash_val_t	val;
982d62bc4baSyz 
983d62bc4baSyz 	rw_enter(&i_dls_devnet_lock, RW_WRITER);
984d62bc4baSyz 	if ((err = mod_hash_find(i_dls_devnet_hash,
985da14cebeSEric Cheng 	    (mod_hash_key_t)macname, (mod_hash_val_t *)&ddp)) != 0) {
986d62bc4baSyz 		ASSERT(err == MH_ERR_NOTFOUND);
987d62bc4baSyz 		rw_exit(&i_dls_devnet_lock);
988d62bc4baSyz 		return (ENOENT);
989d62bc4baSyz 	}
990d62bc4baSyz 
991da14cebeSEric Cheng 	mutex_enter(&ddp->dd_mutex);
992d62bc4baSyz 
993da14cebeSEric Cheng 	/*
994da14cebeSEric Cheng 	 * Make sure downcalls into softmac_create or softmac_destroy from
995da14cebeSEric Cheng 	 * devfs don't cv_wait on any devfs related condition for fear of
996da14cebeSEric Cheng 	 * deadlock. Return EBUSY if the asynchronous thread started for
997da14cebeSEric Cheng 	 * property loading as part of the post attach hasn't yet completed.
998da14cebeSEric Cheng 	 */
99985dff7a0SAndy Fiddaman 	VERIFY(ddp->dd_ref != 0);
1000da14cebeSEric Cheng 	if ((ddp->dd_ref != 1) || (!wait &&
1001639c732eSToomas Soome 	    (ddp->dd_tref != 0 || ddp->dd_prop_taskid != 0))) {
100285dff7a0SAndy Fiddaman 		int zstatus = 0;
100385dff7a0SAndy Fiddaman 
100485dff7a0SAndy Fiddaman 		/*
100585dff7a0SAndy Fiddaman 		 * There are a couple of alternatives that might be going on
100685dff7a0SAndy Fiddaman 		 * here; a) the zone is shutting down and it has a transient
100785dff7a0SAndy Fiddaman 		 * link assigned, in which case we want to clean it up instead
100885dff7a0SAndy Fiddaman 		 * of moving it back to the global zone, or b) its possible
100985dff7a0SAndy Fiddaman 		 * that we're trying to clean up an orphaned vnic that was
101085dff7a0SAndy Fiddaman 		 * delegated to a zone and which wasn't cleaned up properly
101185dff7a0SAndy Fiddaman 		 * when the zone went away.  Check for either of these cases
101285dff7a0SAndy Fiddaman 		 * before we simply return EBUSY.
101385dff7a0SAndy Fiddaman 		 *
101485dff7a0SAndy Fiddaman 		 * zstatus indicates which situation we are dealing with:
101585dff7a0SAndy Fiddaman 		 *	 0 - means return EBUSY
101685dff7a0SAndy Fiddaman 		 *	 1 - means case (a), cleanup transient link
101785dff7a0SAndy Fiddaman 		 *	-1 - means case (b), orphaned VNIC
101885dff7a0SAndy Fiddaman 		 */
101985dff7a0SAndy Fiddaman 		if (ddp->dd_ref > 1 && ddp->dd_zid != GLOBAL_ZONEID) {
102085dff7a0SAndy Fiddaman 			zone_t	*zp;
102185dff7a0SAndy Fiddaman 
102285dff7a0SAndy Fiddaman 			if ((zp = zone_find_by_id(ddp->dd_zid)) == NULL) {
102385dff7a0SAndy Fiddaman 				zstatus = -1;
102485dff7a0SAndy Fiddaman 			} else {
102585dff7a0SAndy Fiddaman 				if (ddp->dd_transient) {
102685dff7a0SAndy Fiddaman 					zone_status_t s = zone_status_get(zp);
102785dff7a0SAndy Fiddaman 
102885dff7a0SAndy Fiddaman 					if (s >= ZONE_IS_SHUTTING_DOWN)
102985dff7a0SAndy Fiddaman 						zstatus = 1;
103085dff7a0SAndy Fiddaman 				}
103185dff7a0SAndy Fiddaman 				zone_rele(zp);
103285dff7a0SAndy Fiddaman 			}
103385dff7a0SAndy Fiddaman 		}
103485dff7a0SAndy Fiddaman 
103585dff7a0SAndy Fiddaman 		if (zstatus == 0) {
103685dff7a0SAndy Fiddaman 			mutex_exit(&ddp->dd_mutex);
103785dff7a0SAndy Fiddaman 			rw_exit(&i_dls_devnet_lock);
103885dff7a0SAndy Fiddaman 			return (EBUSY);
103985dff7a0SAndy Fiddaman 		}
104085dff7a0SAndy Fiddaman 
104185dff7a0SAndy Fiddaman 		/*
104285dff7a0SAndy Fiddaman 		 * We want to delete the link, reset ref to 1;
104385dff7a0SAndy Fiddaman 		 */
104485dff7a0SAndy Fiddaman 		if (zstatus == -1) {
104585dff7a0SAndy Fiddaman 			/* Log a warning, but continue in this case */
104685dff7a0SAndy Fiddaman 			cmn_err(CE_WARN, "clear orphaned datalink: %s\n",
104785dff7a0SAndy Fiddaman 			    ddp->dd_linkname);
104885dff7a0SAndy Fiddaman 		}
104985dff7a0SAndy Fiddaman 		ddp->dd_ref = 1;
1050d62bc4baSyz 	}
1051d62bc4baSyz 
1052da14cebeSEric Cheng 	ddp->dd_flags |= DD_CONDEMNED;
1053d62bc4baSyz 	ddp->dd_ref--;
1054da14cebeSEric Cheng 	*id = ddp->dd_linkid;
1055d62bc4baSyz 
1056da14cebeSEric Cheng 	/*
1057da14cebeSEric Cheng 	 * Remove this dls_devnet_t from the hash table.
1058da14cebeSEric Cheng 	 */
1059da14cebeSEric Cheng 	VERIFY(mod_hash_remove(i_dls_devnet_hash,
1060da14cebeSEric Cheng 	    (mod_hash_key_t)ddp->dd_mac, &val) == 0);
1061d62bc4baSyz 
1062da14cebeSEric Cheng 	if (ddp->dd_linkid != DATALINK_INVALID_LINKID) {
1063da14cebeSEric Cheng 		VERIFY(mod_hash_remove(i_dls_devnet_id_hash,
1064da14cebeSEric Cheng 		    (mod_hash_key_t)(uintptr_t)ddp->dd_linkid, &val) == 0);
1065da14cebeSEric Cheng 
1066da14cebeSEric Cheng 		devnet_need_rebuild = B_TRUE;
1067da14cebeSEric Cheng 	}
1068d62bc4baSyz 	rw_exit(&i_dls_devnet_lock);
1069da14cebeSEric Cheng 
107085dff7a0SAndy Fiddaman 	/*
107185dff7a0SAndy Fiddaman 	 * It is important to call i_dls_devnet_setzid() WITHOUT the
107285dff7a0SAndy Fiddaman 	 * i_dls_devnet_lock held. The setzid call grabs the MAC
107385dff7a0SAndy Fiddaman 	 * perim; thus causing DLS -> MAC lock ordering if performed
107485dff7a0SAndy Fiddaman 	 * with the i_dls_devnet_lock held. This forces consumers to
107585dff7a0SAndy Fiddaman 	 * grab the MAC perim before calling dls_devnet_unset() (the
107685dff7a0SAndy Fiddaman 	 * locking rules state MAC -> DLS order). By performing the
107785dff7a0SAndy Fiddaman 	 * setzid outside of the i_dls_devnet_lock consumers can
107885dff7a0SAndy Fiddaman 	 * safely call dls_devnet_unset() outside the MAC perim.
107985dff7a0SAndy Fiddaman 	 */
108085dff7a0SAndy Fiddaman 	if (ddp->dd_zid != GLOBAL_ZONEID) {
108185dff7a0SAndy Fiddaman 		/*
108285dff7a0SAndy Fiddaman 		 * We need to release the dd_mutex before we try and destroy the
108385dff7a0SAndy Fiddaman 		 * stat. When we destroy it, we'll need to grab the lock for the
108485dff7a0SAndy Fiddaman 		 * kstat but if there's a concurrent reader of the kstat, we'll
108585dff7a0SAndy Fiddaman 		 * be blocked on it. This will lead to deadlock because these
108685dff7a0SAndy Fiddaman 		 * kstats employ a ks_update function (dls_devnet_stat_update)
108785dff7a0SAndy Fiddaman 		 * which needs the dd_mutex that we currently hold.
108885dff7a0SAndy Fiddaman 		 *
108985dff7a0SAndy Fiddaman 		 * Because we've already flagged the dls_devnet_t as
109085dff7a0SAndy Fiddaman 		 * DD_CONDEMNED and we still have a write lock on
109185dff7a0SAndy Fiddaman 		 * i_dls_devnet_lock, we should be able to release the dd_mutex.
109285dff7a0SAndy Fiddaman 		 */
109385dff7a0SAndy Fiddaman 		mutex_exit(&ddp->dd_mutex);
109485dff7a0SAndy Fiddaman 		dls_devnet_stat_destroy(ddp, ddp->dd_zid);
109585dff7a0SAndy Fiddaman 		mutex_enter(&ddp->dd_mutex);
109685dff7a0SAndy Fiddaman 		(void) i_dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE,
109785dff7a0SAndy Fiddaman 		    B_FALSE);
109885dff7a0SAndy Fiddaman 	}
109985dff7a0SAndy Fiddaman 
1100da14cebeSEric Cheng 	if (wait) {
1101da14cebeSEric Cheng 		/*
1102da14cebeSEric Cheng 		 * Wait until all temporary references are released.
110385dff7a0SAndy Fiddaman 		 * The holders of the tref need the MAC perim to
110485dff7a0SAndy Fiddaman 		 * perform their work and release the tref. To avoid
110585dff7a0SAndy Fiddaman 		 * deadlock, assert that the perim is never held here.
1106da14cebeSEric Cheng 		 */
110785dff7a0SAndy Fiddaman 		ASSERT0(MAC_PERIM_HELD(mh));
1108639c732eSToomas Soome 		while ((ddp->dd_tref != 0) || (ddp->dd_prop_taskid != 0))
1109da14cebeSEric Cheng 			cv_wait(&ddp->dd_cv, &ddp->dd_mutex);
1110da14cebeSEric Cheng 	} else {
111185dff7a0SAndy Fiddaman 		VERIFY(ddp->dd_tref == 0);
111285dff7a0SAndy Fiddaman 		VERIFY(ddp->dd_prop_taskid == 0);
1113da14cebeSEric Cheng 	}
1114da14cebeSEric Cheng 
1115ae6aa22aSVenugopal Iyer 	if (ddp->dd_linkid != DATALINK_INVALID_LINKID)
11162b24ab6bSSebastien Roy 		dls_devnet_stat_destroy(ddp, ddp->dd_owner_zid);
1117ae6aa22aSVenugopal Iyer 
1118da14cebeSEric Cheng 	ddp->dd_prop_loaded = B_FALSE;
1119da14cebeSEric Cheng 	ddp->dd_linkid = DATALINK_INVALID_LINKID;
1120da14cebeSEric Cheng 	ddp->dd_flags = 0;
1121da14cebeSEric Cheng 	mutex_exit(&ddp->dd_mutex);
1122da14cebeSEric Cheng 	kmem_cache_free(i_dls_devnet_cachep, ddp);
1123da14cebeSEric Cheng 
1124d62bc4baSyz 	return (0);
1125d62bc4baSyz }
1126d62bc4baSyz 
1127a25df667SRobert Mustacchi /*
1128a25df667SRobert Mustacchi  * This is a private hold routine used when we already have the dls_link_t, thus
1129a25df667SRobert Mustacchi  * we know that it cannot go away.
1130a25df667SRobert Mustacchi  */
1131a25df667SRobert Mustacchi int
dls_devnet_hold_tmp_by_link(dls_link_t * dlp,dls_dl_handle_t * ddhp)1132a25df667SRobert Mustacchi dls_devnet_hold_tmp_by_link(dls_link_t *dlp, dls_dl_handle_t *ddhp)
1133a25df667SRobert Mustacchi {
1134a25df667SRobert Mustacchi 	int err;
1135a25df667SRobert Mustacchi 	dls_devnet_t *ddp = NULL;
1136a25df667SRobert Mustacchi 
1137a25df667SRobert Mustacchi 	rw_enter(&i_dls_devnet_lock, RW_WRITER);
1138a25df667SRobert Mustacchi 	if ((err = mod_hash_find(i_dls_devnet_hash,
1139a25df667SRobert Mustacchi 	    (mod_hash_key_t)dlp->dl_name, (mod_hash_val_t *)&ddp)) != 0) {
1140a25df667SRobert Mustacchi 		ASSERT(err == MH_ERR_NOTFOUND);
1141a25df667SRobert Mustacchi 		rw_exit(&i_dls_devnet_lock);
1142a25df667SRobert Mustacchi 		return (ENOENT);
1143a25df667SRobert Mustacchi 	}
1144a25df667SRobert Mustacchi 
1145a25df667SRobert Mustacchi 	mutex_enter(&ddp->dd_mutex);
114685dff7a0SAndy Fiddaman 	VERIFY(ddp->dd_ref > 0);
114785dff7a0SAndy Fiddaman 	if (DD_NOT_VISIBLE(ddp->dd_flags)) {
1148a25df667SRobert Mustacchi 		mutex_exit(&ddp->dd_mutex);
1149a25df667SRobert Mustacchi 		rw_exit(&i_dls_devnet_lock);
1150a25df667SRobert Mustacchi 		return (ENOENT);
1151a25df667SRobert Mustacchi 	}
1152a25df667SRobert Mustacchi 	ddp->dd_tref++;
1153a25df667SRobert Mustacchi 	mutex_exit(&ddp->dd_mutex);
1154a25df667SRobert Mustacchi 	rw_exit(&i_dls_devnet_lock);
1155a25df667SRobert Mustacchi 
1156a25df667SRobert Mustacchi 	*ddhp = ddp;
1157a25df667SRobert Mustacchi 	return (0);
1158a25df667SRobert Mustacchi }
1159a25df667SRobert Mustacchi 
1160d62bc4baSyz static int
dls_devnet_hold_common(datalink_id_t linkid,dls_devnet_t ** ddpp,boolean_t tmp_hold)1161da14cebeSEric Cheng dls_devnet_hold_common(datalink_id_t linkid, dls_devnet_t **ddpp,
1162da14cebeSEric Cheng     boolean_t tmp_hold)
1163d62bc4baSyz {
1164d62bc4baSyz 	dls_devnet_t		*ddp;
1165d62bc4baSyz 	int			err;
1166d62bc4baSyz 
1167fe4627efSSebastien Roy 	rw_enter(&i_dls_devnet_lock, RW_READER);
1168d62bc4baSyz 	if ((err = mod_hash_find(i_dls_devnet_id_hash,
1169d62bc4baSyz 	    (mod_hash_key_t)(uintptr_t)linkid, (mod_hash_val_t *)&ddp)) != 0) {
1170d62bc4baSyz 		ASSERT(err == MH_ERR_NOTFOUND);
1171d62bc4baSyz 		rw_exit(&i_dls_devnet_lock);
1172d62bc4baSyz 		return (ENOENT);
1173d62bc4baSyz 	}
1174d62bc4baSyz 
1175da14cebeSEric Cheng 	mutex_enter(&ddp->dd_mutex);
117685dff7a0SAndy Fiddaman 	VERIFY(ddp->dd_ref > 0);
117785dff7a0SAndy Fiddaman 	if (DD_NOT_VISIBLE(ddp->dd_flags)) {
1178da14cebeSEric Cheng 		mutex_exit(&ddp->dd_mutex);
1179da14cebeSEric Cheng 		rw_exit(&i_dls_devnet_lock);
1180da14cebeSEric Cheng 		return (ENOENT);
1181da14cebeSEric Cheng 	}
1182da14cebeSEric Cheng 	if (tmp_hold)
1183da14cebeSEric Cheng 		ddp->dd_tref++;
1184da14cebeSEric Cheng 	else
1185da14cebeSEric Cheng 		ddp->dd_ref++;
1186da14cebeSEric Cheng 	mutex_exit(&ddp->dd_mutex);
1187d62bc4baSyz 	rw_exit(&i_dls_devnet_lock);
1188da14cebeSEric Cheng 
1189d62bc4baSyz 	*ddpp = ddp;
1190d62bc4baSyz 	return (0);
1191d62bc4baSyz }
1192d62bc4baSyz 
1193da14cebeSEric Cheng int
dls_devnet_hold(datalink_id_t linkid,dls_devnet_t ** ddpp)1194da14cebeSEric Cheng dls_devnet_hold(datalink_id_t linkid, dls_devnet_t **ddpp)
1195da14cebeSEric Cheng {
1196da14cebeSEric Cheng 	return (dls_devnet_hold_common(linkid, ddpp, B_FALSE));
1197da14cebeSEric Cheng }
1198da14cebeSEric Cheng 
1199da14cebeSEric Cheng /*
1200da14cebeSEric Cheng  * Hold the vanity naming structure (dls_devnet_t) temporarily.  The request to
1201da14cebeSEric Cheng  * delete the dls_devnet_t will wait until the temporary reference is released.
1202da14cebeSEric Cheng  */
1203da14cebeSEric Cheng int
dls_devnet_hold_tmp(datalink_id_t linkid,dls_devnet_t ** ddpp)1204da14cebeSEric Cheng dls_devnet_hold_tmp(datalink_id_t linkid, dls_devnet_t **ddpp)
1205da14cebeSEric Cheng {
1206da14cebeSEric Cheng 	return (dls_devnet_hold_common(linkid, ddpp, B_TRUE));
1207da14cebeSEric Cheng }
1208da14cebeSEric Cheng 
1209d62bc4baSyz /*
1210d62bc4baSyz  * This funtion is called when a DLS client tries to open a device node.
121168f58420SRyan Zezeski  * This dev_t could be a result of a /dev/net node access (returned by
1212d62bc4baSyz  * devnet_create_rvp->dls_devnet_open()) or a direct /dev node access.
1213da14cebeSEric Cheng  * In both cases, this function bumps up the reference count of the
1214da14cebeSEric Cheng  * dls_devnet_t structure. The reference is held as long as the device node
1215da14cebeSEric Cheng  * is open. In the case of /dev/net while it is true that the initial reference
1216da14cebeSEric Cheng  * is held when the devnet_create_rvp->dls_devnet_open call happens, this
1217da14cebeSEric Cheng  * initial reference is released immediately in devnet_inactive_callback ->
1218da14cebeSEric Cheng  * dls_devnet_close(). (Note that devnet_inactive_callback() is called right
1219da14cebeSEric Cheng  * after dld_open completes, not when the /dev/net node is being closed).
1220da14cebeSEric Cheng  * To undo this function, call dls_devnet_rele()
1221d62bc4baSyz  */
1222d62bc4baSyz int
dls_devnet_hold_by_dev(dev_t dev,dls_dl_handle_t * ddhp)1223da14cebeSEric Cheng dls_devnet_hold_by_dev(dev_t dev, dls_dl_handle_t *ddhp)
1224d62bc4baSyz {
1225da14cebeSEric Cheng 	char			name[MAXNAMELEN];
1226da14cebeSEric Cheng 	char			*drv;
1227d62bc4baSyz 	dls_devnet_t		*ddp;
1228d62bc4baSyz 	int			err;
1229d62bc4baSyz 
1230da14cebeSEric Cheng 	if ((drv = ddi_major_to_name(getmajor(dev))) == NULL)
1231da14cebeSEric Cheng 		return (EINVAL);
1232da14cebeSEric Cheng 
123361af1958SGarrett D'Amore 	(void) snprintf(name, sizeof (name), "%s%d", drv,
123461af1958SGarrett D'Amore 	    DLS_MINOR2INST(getminor(dev)));
1235da14cebeSEric Cheng 
1236fe4627efSSebastien Roy 	rw_enter(&i_dls_devnet_lock, RW_READER);
1237d62bc4baSyz 	if ((err = mod_hash_find(i_dls_devnet_hash,
1238da14cebeSEric Cheng 	    (mod_hash_key_t)name, (mod_hash_val_t *)&ddp)) != 0) {
1239d62bc4baSyz 		ASSERT(err == MH_ERR_NOTFOUND);
1240d62bc4baSyz 		rw_exit(&i_dls_devnet_lock);
1241da14cebeSEric Cheng 		return (ENOENT);
1242d62bc4baSyz 	}
1243da14cebeSEric Cheng 	mutex_enter(&ddp->dd_mutex);
124485dff7a0SAndy Fiddaman 	VERIFY(ddp->dd_ref > 0);
124585dff7a0SAndy Fiddaman 	if (DD_NOT_VISIBLE(ddp->dd_flags)) {
1246da14cebeSEric Cheng 		mutex_exit(&ddp->dd_mutex);
1247da14cebeSEric Cheng 		rw_exit(&i_dls_devnet_lock);
1248da14cebeSEric Cheng 		return (ENOENT);
1249da14cebeSEric Cheng 	}
1250d62bc4baSyz 	ddp->dd_ref++;
1251da14cebeSEric Cheng 	mutex_exit(&ddp->dd_mutex);
1252d62bc4baSyz 	rw_exit(&i_dls_devnet_lock);
1253da14cebeSEric Cheng 
1254d62bc4baSyz 	*ddhp = ddp;
1255d62bc4baSyz 	return (0);
1256d62bc4baSyz }
1257d62bc4baSyz 
1258da14cebeSEric Cheng void
dls_devnet_rele(dls_devnet_t * ddp)1259d62bc4baSyz dls_devnet_rele(dls_devnet_t *ddp)
1260d62bc4baSyz {
1261da14cebeSEric Cheng 	mutex_enter(&ddp->dd_mutex);
126285dff7a0SAndy Fiddaman 	VERIFY(ddp->dd_ref > 1);
1263da14cebeSEric Cheng 	ddp->dd_ref--;
12642b24ab6bSSebastien Roy 	if ((ddp->dd_flags & DD_IMPLICIT_IPTUN) && ddp->dd_ref == 1) {
12652b24ab6bSSebastien Roy 		mutex_exit(&ddp->dd_mutex);
12662b24ab6bSSebastien Roy 		if (i_dls_devnet_destroy_iptun(ddp->dd_linkid) != 0)
12672b24ab6bSSebastien Roy 			ddp->dd_flags |= DD_IMPLICIT_IPTUN;
12682b24ab6bSSebastien Roy 		return;
12692b24ab6bSSebastien Roy 	}
1270da14cebeSEric Cheng 	mutex_exit(&ddp->dd_mutex);
1271d62bc4baSyz }
1272d62bc4baSyz 
1273d62bc4baSyz static int
dls_devnet_hold_by_name(const char * link,dls_devnet_t ** ddpp)1274da14cebeSEric Cheng dls_devnet_hold_by_name(const char *link, dls_devnet_t **ddpp)
1275d62bc4baSyz {
1276d62bc4baSyz 	char			drv[MAXLINKNAMELEN];
1277d62bc4baSyz 	uint_t			ppa;
1278d62bc4baSyz 	major_t			major;
1279d62bc4baSyz 	dev_t			phy_dev, tmp_dev;
1280d62bc4baSyz 	datalink_id_t		linkid;
1281d62bc4baSyz 	dls_dev_handle_t	ddh;
1282d62bc4baSyz 	int			err;
1283d62bc4baSyz 
1284d62bc4baSyz 	if ((err = dls_mgmt_get_linkid(link, &linkid)) == 0)
1285d62bc4baSyz 		return (dls_devnet_hold(linkid, ddpp));
1286d62bc4baSyz 
1287d62bc4baSyz 	/*
1288d62bc4baSyz 	 * If we failed to get the link's linkid because the dlmgmtd daemon
1289d62bc4baSyz 	 * has not been started, return ENOENT so that the application can
1290d62bc4baSyz 	 * fallback to open the /dev node.
1291d62bc4baSyz 	 */
1292d62bc4baSyz 	if (err == EBADF)
1293d62bc4baSyz 		return (ENOENT);
1294d62bc4baSyz 
1295d62bc4baSyz 	if (err != ENOENT)
1296d62bc4baSyz 		return (err);
1297d62bc4baSyz 
129868f58420SRyan Zezeski 	/*
129968f58420SRyan Zezeski 	 * If we reach this point it means dlmgmtd is up but has no
130068f58420SRyan Zezeski 	 * mapping for the link name.
130168f58420SRyan Zezeski 	 */
1302*676abcb7SDan McDonald 	if (ddi_parse_dlen(link, drv, MAXLINKNAMELEN, &ppa) != DDI_SUCCESS)
1303d501bbfeSSebastien Roy 		return (ENOENT);
1304d501bbfeSSebastien Roy 
1305d501bbfeSSebastien Roy 	if (IS_IPTUN_LINK(drv)) {
1306d501bbfeSSebastien Roy 		if ((err = i_dls_devnet_create_iptun(link, drv, &linkid)) != 0)
13072b24ab6bSSebastien Roy 			return (err);
13082b24ab6bSSebastien Roy 		/*
13092b24ab6bSSebastien Roy 		 * At this point, an IP tunnel MAC has registered, which
13102b24ab6bSSebastien Roy 		 * resulted in a link being created.
13112b24ab6bSSebastien Roy 		 */
13122b24ab6bSSebastien Roy 		err = dls_devnet_hold(linkid, ddpp);
13132b24ab6bSSebastien Roy 		if (err != 0) {
13142b24ab6bSSebastien Roy 			VERIFY(i_dls_devnet_destroy_iptun(linkid) == 0);
13152b24ab6bSSebastien Roy 			return (err);
13162b24ab6bSSebastien Roy 		}
13172b24ab6bSSebastien Roy 		/*
13182b24ab6bSSebastien Roy 		 * dls_devnet_rele() will know to destroy the implicit IP
13192b24ab6bSSebastien Roy 		 * tunnel on last reference release if DD_IMPLICIT_IPTUN is
13202b24ab6bSSebastien Roy 		 * set.
13212b24ab6bSSebastien Roy 		 */
13222b24ab6bSSebastien Roy 		(*ddpp)->dd_flags |= DD_IMPLICIT_IPTUN;
13232b24ab6bSSebastien Roy 		return (0);
13242b24ab6bSSebastien Roy 	}
13252b24ab6bSSebastien Roy 
1326d62bc4baSyz 	/*
1327da14cebeSEric Cheng 	 * If this link:
1328d62bc4baSyz 	 * (a) is a physical device, (b) this is the first boot, (c) the MAC
1329d62bc4baSyz 	 * is not registered yet, and (d) we cannot find its linkid, then the
1330d62bc4baSyz 	 * linkname is the same as the devname.
1331d62bc4baSyz 	 *
1332d62bc4baSyz 	 * First filter out invalid names.
1333d62bc4baSyz 	 */
1334d62bc4baSyz 	if ((major = ddi_name_to_major(drv)) == (major_t)-1)
1335d62bc4baSyz 		return (ENOENT);
1336d62bc4baSyz 
133761af1958SGarrett D'Amore 	phy_dev = makedevice(major, DLS_PPA2MINOR(ppa));
1338d62bc4baSyz 	if (softmac_hold_device(phy_dev, &ddh) != 0)
1339d62bc4baSyz 		return (ENOENT);
1340d62bc4baSyz 
1341d62bc4baSyz 	/*
1342d62bc4baSyz 	 * At this time, the MAC should be registered, check its phy_dev using
1343d62bc4baSyz 	 * the given name.
1344d62bc4baSyz 	 */
1345da14cebeSEric Cheng 	if ((err = dls_mgmt_get_linkid(link, &linkid)) != 0 ||
1346d62bc4baSyz 	    (err = dls_mgmt_get_phydev(linkid, &tmp_dev)) != 0) {
1347d62bc4baSyz 		softmac_rele_device(ddh);
1348d62bc4baSyz 		return (err);
1349d62bc4baSyz 	}
1350d62bc4baSyz 	if (tmp_dev != phy_dev) {
1351d62bc4baSyz 		softmac_rele_device(ddh);
1352d62bc4baSyz 		return (ENOENT);
1353d62bc4baSyz 	}
1354d62bc4baSyz 
1355da14cebeSEric Cheng 	err = dls_devnet_hold(linkid, ddpp);
1356d62bc4baSyz 	softmac_rele_device(ddh);
1357d62bc4baSyz 	return (err);
1358d62bc4baSyz }
1359d62bc4baSyz 
1360d62bc4baSyz int
dls_devnet_macname2linkid(const char * macname,datalink_id_t * linkidp)1361da14cebeSEric Cheng dls_devnet_macname2linkid(const char *macname, datalink_id_t *linkidp)
1362d62bc4baSyz {
1363d62bc4baSyz 	dls_devnet_t	*ddp;
1364d62bc4baSyz 
1365d62bc4baSyz 	rw_enter(&i_dls_devnet_lock, RW_READER);
1366da14cebeSEric Cheng 	if (mod_hash_find(i_dls_devnet_hash, (mod_hash_key_t)macname,
1367d62bc4baSyz 	    (mod_hash_val_t *)&ddp) != 0) {
1368d62bc4baSyz 		rw_exit(&i_dls_devnet_lock);
1369d62bc4baSyz 		return (ENOENT);
1370d62bc4baSyz 	}
1371d62bc4baSyz 
1372da14cebeSEric Cheng 	*linkidp = ddp->dd_linkid;
1373d62bc4baSyz 	rw_exit(&i_dls_devnet_lock);
1374d62bc4baSyz 	return (0);
1375d62bc4baSyz }
1376d62bc4baSyz 
1377da14cebeSEric Cheng /*
1378da14cebeSEric Cheng  * Get linkid for the given dev.
1379da14cebeSEric Cheng  */
1380da14cebeSEric Cheng int
dls_devnet_dev2linkid(dev_t dev,datalink_id_t * linkidp)1381da14cebeSEric Cheng dls_devnet_dev2linkid(dev_t dev, datalink_id_t *linkidp)
1382da14cebeSEric Cheng {
1383da14cebeSEric Cheng 	char	macname[MAXNAMELEN];
1384da14cebeSEric Cheng 	char	*drv;
1385da14cebeSEric Cheng 
1386da14cebeSEric Cheng 	if ((drv = ddi_major_to_name(getmajor(dev))) == NULL)
1387da14cebeSEric Cheng 		return (EINVAL);
1388da14cebeSEric Cheng 
13892b24ab6bSSebastien Roy 	(void) snprintf(macname, sizeof (macname), "%s%d", drv,
139061af1958SGarrett D'Amore 	    DLS_MINOR2INST(getminor(dev)));
1391da14cebeSEric Cheng 	return (dls_devnet_macname2linkid(macname, linkidp));
1392da14cebeSEric Cheng }
1393da14cebeSEric Cheng 
1394d62bc4baSyz /*
1395d62bc4baSyz  * Get the link's physical dev_t. It this is a VLAN, get the dev_t of the
1396d62bc4baSyz  * link this VLAN is created on.
1397d62bc4baSyz  */
1398d62bc4baSyz int
dls_devnet_phydev(datalink_id_t vlanid,dev_t * devp)1399d62bc4baSyz dls_devnet_phydev(datalink_id_t vlanid, dev_t *devp)
1400d62bc4baSyz {
1401d62bc4baSyz 	dls_devnet_t	*ddp;
1402d62bc4baSyz 	int		err;
1403d62bc4baSyz 
1404d62bc4baSyz 	if ((err = dls_devnet_hold_tmp(vlanid, &ddp)) != 0)
1405d62bc4baSyz 		return (err);
1406d62bc4baSyz 
1407d62bc4baSyz 	err = dls_mgmt_get_phydev(ddp->dd_linkid, devp);
1408d62bc4baSyz 	dls_devnet_rele_tmp(ddp);
1409d62bc4baSyz 	return (err);
1410d62bc4baSyz }
1411d62bc4baSyz 
1412d62bc4baSyz /*
1413d62bc4baSyz  * Handle the renaming requests.  There are two rename cases:
1414d62bc4baSyz  *
1415d62bc4baSyz  * 1. Request to rename a valid link (id1) to an non-existent link name
1416d62bc4baSyz  *    (id2). In this case id2 is DATALINK_INVALID_LINKID.  Just check whether
1417d62bc4baSyz  *    id1 is held by any applications.
1418d62bc4baSyz  *
1419d62bc4baSyz  *    In this case, the link's kstats need to be updated using the given name.
1420d62bc4baSyz  *
1421d62bc4baSyz  * 2. Request to rename a valid link (id1) to the name of a REMOVED
142230890389Sartem  *    physical link (id2). In this case, check that id1 and its associated
1423d62bc4baSyz  *    mac is not held by any application, and update the link's linkid to id2.
1424d62bc4baSyz  *
1425d62bc4baSyz  *    This case does not change the <link name, linkid> mapping, so the link's
1426d62bc4baSyz  *    kstats need to be updated with using name associated the given id2.
1427d62bc4baSyz  */
1428d62bc4baSyz int
dls_devnet_rename(datalink_id_t id1,datalink_id_t id2,const char * link)1429d62bc4baSyz dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
1430d62bc4baSyz {
1431d62bc4baSyz 	dls_dev_handle_t	ddh = NULL;
1432d62bc4baSyz 	int			err = 0;
1433d62bc4baSyz 	dev_t			phydev = 0;
1434d62bc4baSyz 	dls_devnet_t		*ddp;
1435da14cebeSEric Cheng 	mac_perim_handle_t	mph = NULL;
1436d62bc4baSyz 	mac_handle_t		mh;
1437d62bc4baSyz 	mod_hash_val_t		val;
1438d62bc4baSyz 
1439d62bc4baSyz 	/*
1440d62bc4baSyz 	 * In the second case, id2 must be a REMOVED physical link.
1441d62bc4baSyz 	 */
1442d62bc4baSyz 	if ((id2 != DATALINK_INVALID_LINKID) &&
1443d62bc4baSyz 	    (dls_mgmt_get_phydev(id2, &phydev) == 0) &&
1444d62bc4baSyz 	    softmac_hold_device(phydev, &ddh) == 0) {
1445d62bc4baSyz 		softmac_rele_device(ddh);
1446d62bc4baSyz 		return (EEXIST);
1447d62bc4baSyz 	}
1448d62bc4baSyz 
1449d62bc4baSyz 	/*
1450d62bc4baSyz 	 * Hold id1 to prevent it from being detached (if a physical link).
1451d62bc4baSyz 	 */
1452d62bc4baSyz 	if (dls_mgmt_get_phydev(id1, &phydev) == 0)
1453d62bc4baSyz 		(void) softmac_hold_device(phydev, &ddh);
1454d62bc4baSyz 
1455da14cebeSEric Cheng 	/*
1456da14cebeSEric Cheng 	 * The framework does not hold hold locks across calls to the
1457da14cebeSEric Cheng 	 * mac perimeter, hence enter the perimeter first. This also waits
1458da14cebeSEric Cheng 	 * for the property loading to finish.
1459da14cebeSEric Cheng 	 */
1460ae6aa22aSVenugopal Iyer 	if ((err = mac_perim_enter_by_linkid(id1, &mph)) != 0) {
1461ae6aa22aSVenugopal Iyer 		softmac_rele_device(ddh);
1462ae6aa22aSVenugopal Iyer 		return (err);
1463ae6aa22aSVenugopal Iyer 	}
1464da14cebeSEric Cheng 
1465d62bc4baSyz 	rw_enter(&i_dls_devnet_lock, RW_WRITER);
1466d62bc4baSyz 	if ((err = mod_hash_find(i_dls_devnet_id_hash,
1467d62bc4baSyz 	    (mod_hash_key_t)(uintptr_t)id1, (mod_hash_val_t *)&ddp)) != 0) {
1468d62bc4baSyz 		ASSERT(err == MH_ERR_NOTFOUND);
1469d62bc4baSyz 		err = ENOENT;
1470d62bc4baSyz 		goto done;
1471d62bc4baSyz 	}
1472d62bc4baSyz 
1473ae6aa22aSVenugopal Iyer 	mutex_enter(&ddp->dd_mutex);
1474da14cebeSEric Cheng 	if (ddp->dd_ref > 1) {
1475ae6aa22aSVenugopal Iyer 		mutex_exit(&ddp->dd_mutex);
1476d62bc4baSyz 		err = EBUSY;
1477d62bc4baSyz 		goto done;
1478d62bc4baSyz 	}
1479ae6aa22aSVenugopal Iyer 	mutex_exit(&ddp->dd_mutex);
1480ae6aa22aSVenugopal Iyer 
1481d62bc4baSyz 	if (id2 == DATALINK_INVALID_LINKID) {
14822b24ab6bSSebastien Roy 		(void) strlcpy(ddp->dd_linkname, link,
14832b24ab6bSSebastien Roy 		    sizeof (ddp->dd_linkname));
1484da14cebeSEric Cheng 
1485da14cebeSEric Cheng 		/* rename mac client name and its flow if exists */
1486da14cebeSEric Cheng 		if ((err = mac_open(ddp->dd_mac, &mh)) != 0)
1487da14cebeSEric Cheng 			goto done;
1488da14cebeSEric Cheng 		(void) mac_rename_primary(mh, link);
1489da14cebeSEric Cheng 		mac_close(mh);
1490d62bc4baSyz 		goto done;
1491d62bc4baSyz 	}
1492d62bc4baSyz 
1493d62bc4baSyz 	/*
1494d62bc4baSyz 	 * The second case, check whether the MAC is used by any MAC
1495d62bc4baSyz 	 * user.  This must be a physical link so ddh must not be NULL.
1496d62bc4baSyz 	 */
1497d62bc4baSyz 	if (ddh == NULL) {
1498d62bc4baSyz 		err = EINVAL;
1499d62bc4baSyz 		goto done;
1500d62bc4baSyz 	}
1501d62bc4baSyz 
1502d62bc4baSyz 	if ((err = mac_open(ddp->dd_mac, &mh)) != 0)
1503d62bc4baSyz 		goto done;
1504d62bc4baSyz 
1505d62bc4baSyz 	/*
1506d62bc4baSyz 	 * We release the reference of the MAC which mac_open() is
1507d62bc4baSyz 	 * holding. Note that this mac will not be unregistered
1508da14cebeSEric Cheng 	 * because the physical device is held.
1509d62bc4baSyz 	 */
1510d62bc4baSyz 	mac_close(mh);
1511d62bc4baSyz 
1512d62bc4baSyz 	/*
1513d62bc4baSyz 	 * Check if there is any other MAC clients, if not, hold this mac
1514d62bc4baSyz 	 * exclusively until we are done.
1515d62bc4baSyz 	 */
1516da14cebeSEric Cheng 	if ((err = mac_mark_exclusive(mh)) != 0)
1517d62bc4baSyz 		goto done;
1518d62bc4baSyz 
1519d62bc4baSyz 	/*
1520d62bc4baSyz 	 * Update the link's linkid.
1521d62bc4baSyz 	 */
1522d62bc4baSyz 	if ((err = mod_hash_find(i_dls_devnet_id_hash,
1523d62bc4baSyz 	    (mod_hash_key_t)(uintptr_t)id2, &val)) != MH_ERR_NOTFOUND) {
1524da14cebeSEric Cheng 		mac_unmark_exclusive(mh);
1525d62bc4baSyz 		err = EEXIST;
1526d62bc4baSyz 		goto done;
1527d62bc4baSyz 	}
1528d62bc4baSyz 
15292b24ab6bSSebastien Roy 	err = dls_mgmt_get_linkinfo(id2, ddp->dd_linkname, NULL, NULL, NULL);
1530d62bc4baSyz 	if (err != 0) {
1531da14cebeSEric Cheng 		mac_unmark_exclusive(mh);
1532d62bc4baSyz 		goto done;
1533d62bc4baSyz 	}
1534d62bc4baSyz 
1535d62bc4baSyz 	(void) mod_hash_remove(i_dls_devnet_id_hash,
1536d62bc4baSyz 	    (mod_hash_key_t)(uintptr_t)id1, &val);
1537d62bc4baSyz 
1538da14cebeSEric Cheng 	ddp->dd_linkid = id2;
1539d62bc4baSyz 	(void) mod_hash_insert(i_dls_devnet_id_hash,
1540da14cebeSEric Cheng 	    (mod_hash_key_t)(uintptr_t)ddp->dd_linkid, (mod_hash_val_t)ddp);
1541da14cebeSEric Cheng 
1542da14cebeSEric Cheng 	mac_unmark_exclusive(mh);
1543d62bc4baSyz 
154430890389Sartem 	/* load properties for new id */
154530890389Sartem 	mutex_enter(&ddp->dd_mutex);
154630890389Sartem 	ddp->dd_prop_loaded = B_FALSE;
154730890389Sartem 	ddp->dd_prop_taskid = taskq_dispatch(system_taskq,
154830890389Sartem 	    dls_devnet_prop_task, ddp, TQ_SLEEP);
154930890389Sartem 	mutex_exit(&ddp->dd_mutex);
155030890389Sartem 
1551d62bc4baSyz done:
1552ae6aa22aSVenugopal Iyer 	rw_exit(&i_dls_devnet_lock);
1553fe4627efSSebastien Roy 
1554d62bc4baSyz 	if (err == 0)
15552b24ab6bSSebastien Roy 		dls_devnet_stat_rename(ddp);
1556d62bc4baSyz 
1557da14cebeSEric Cheng 	if (mph != NULL)
1558da14cebeSEric Cheng 		mac_perim_exit(mph);
1559d62bc4baSyz 	softmac_rele_device(ddh);
1560d62bc4baSyz 	return (err);
1561d62bc4baSyz }
1562d62bc4baSyz 
15632b24ab6bSSebastien Roy static int
i_dls_devnet_setzid(dls_devnet_t * ddp,zoneid_t new_zoneid,boolean_t setprop,boolean_t transient)156485dff7a0SAndy Fiddaman i_dls_devnet_setzid(dls_devnet_t *ddp, zoneid_t new_zoneid, boolean_t setprop,
156585dff7a0SAndy Fiddaman     boolean_t transient)
1566d62bc4baSyz {
1567f1956ffeSCathy Zhou 	int			err;
1568da14cebeSEric Cheng 	mac_perim_handle_t	mph;
15692b24ab6bSSebastien Roy 	boolean_t		upcall_done = B_FALSE;
15702b24ab6bSSebastien Roy 	datalink_id_t		linkid = ddp->dd_linkid;
15712b24ab6bSSebastien Roy 	zoneid_t		old_zoneid = ddp->dd_zid;
15722b24ab6bSSebastien Roy 	dlmgmt_door_setzoneid_t	setzid;
15732b24ab6bSSebastien Roy 	dlmgmt_setzoneid_retval_t retval;
1574da14cebeSEric Cheng 
15752b24ab6bSSebastien Roy 	if (old_zoneid == new_zoneid)
15762b24ab6bSSebastien Roy 		return (0);
1577d62bc4baSyz 
15782b24ab6bSSebastien Roy 	if ((err = mac_perim_enter_by_macname(ddp->dd_mac, &mph)) != 0)
1579d62bc4baSyz 		return (err);
1580d62bc4baSyz 
15812b24ab6bSSebastien Roy 	/*
15822b24ab6bSSebastien Roy 	 * When changing the zoneid of an existing link, we need to tell
15832b24ab6bSSebastien Roy 	 * dlmgmtd about it.  dlmgmtd already knows the zoneid associated with
15842b24ab6bSSebastien Roy 	 * newly created links.
15852b24ab6bSSebastien Roy 	 */
15862b24ab6bSSebastien Roy 	if (setprop) {
15872b24ab6bSSebastien Roy 		setzid.ld_cmd = DLMGMT_CMD_SETZONEID;
15882b24ab6bSSebastien Roy 		setzid.ld_linkid = linkid;
15892b24ab6bSSebastien Roy 		setzid.ld_zoneid = new_zoneid;
15902b24ab6bSSebastien Roy 		err = i_dls_mgmt_upcall(&setzid, sizeof (setzid), &retval,
15912b24ab6bSSebastien Roy 		    sizeof (retval));
15922b24ab6bSSebastien Roy 		if (err != 0)
15932b24ab6bSSebastien Roy 			goto done;
159485dff7a0SAndy Fiddaman 
159585dff7a0SAndy Fiddaman 		/*
159685dff7a0SAndy Fiddaman 		 * We set upcall_done only if the upcall is
159785dff7a0SAndy Fiddaman 		 * successful. This way, if dls_link_setzid() fails,
159885dff7a0SAndy Fiddaman 		 * we know another upcall must be done to reset the
159985dff7a0SAndy Fiddaman 		 * dlmgmtd state.
160085dff7a0SAndy Fiddaman 		 */
16012b24ab6bSSebastien Roy 		upcall_done = B_TRUE;
1602d62bc4baSyz 	}
16032b24ab6bSSebastien Roy 	if ((err = dls_link_setzid(ddp->dd_mac, new_zoneid)) == 0) {
16042b24ab6bSSebastien Roy 		ddp->dd_zid = new_zoneid;
160585dff7a0SAndy Fiddaman 		ddp->dd_transient = transient;
16062b24ab6bSSebastien Roy 		devnet_need_rebuild = B_TRUE;
1607d62bc4baSyz 	}
1608d62bc4baSyz 
16092b24ab6bSSebastien Roy done:
16102b24ab6bSSebastien Roy 	if (err != 0 && upcall_done) {
16112b24ab6bSSebastien Roy 		setzid.ld_zoneid = old_zoneid;
16122b24ab6bSSebastien Roy 		(void) i_dls_mgmt_upcall(&setzid, sizeof (setzid), &retval,
16132b24ab6bSSebastien Roy 		    sizeof (retval));
16142b24ab6bSSebastien Roy 	}
1615da14cebeSEric Cheng 	mac_perim_exit(mph);
16162b24ab6bSSebastien Roy 	return (err);
16172b24ab6bSSebastien Roy }
16182b24ab6bSSebastien Roy 
16192b24ab6bSSebastien Roy int
dls_devnet_setzid(dls_dl_handle_t ddh,zoneid_t new_zid)16202b24ab6bSSebastien Roy dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid)
16212b24ab6bSSebastien Roy {
16222b24ab6bSSebastien Roy 	dls_devnet_t	*ddp;
16232b24ab6bSSebastien Roy 	int		err;
16242b24ab6bSSebastien Roy 	zoneid_t	old_zid;
16252b24ab6bSSebastien Roy 	boolean_t	refheld = B_FALSE;
16262b24ab6bSSebastien Roy 
16272b24ab6bSSebastien Roy 	old_zid = ddh->dd_zid;
16282b24ab6bSSebastien Roy 
16292b24ab6bSSebastien Roy 	if (old_zid == new_zid)
16302b24ab6bSSebastien Roy 		return (0);
1631d62bc4baSyz 
1632d62bc4baSyz 	/*
16332b24ab6bSSebastien Roy 	 * Acquire an additional reference to the link if it is being assigned
16342b24ab6bSSebastien Roy 	 * to a non-global zone from the global zone.
1635d62bc4baSyz 	 */
16362b24ab6bSSebastien Roy 	if (old_zid == GLOBAL_ZONEID && new_zid != GLOBAL_ZONEID) {
16372b24ab6bSSebastien Roy 		if ((err = dls_devnet_hold(ddh->dd_linkid, &ddp)) != 0)
16382b24ab6bSSebastien Roy 			return (err);
16392b24ab6bSSebastien Roy 		refheld = B_TRUE;
16402b24ab6bSSebastien Roy 	}
16412b24ab6bSSebastien Roy 
164285dff7a0SAndy Fiddaman 	if ((err = i_dls_devnet_setzid(ddh, new_zid, B_TRUE, B_FALSE)) != 0) {
16432b24ab6bSSebastien Roy 		if (refheld)
16442b24ab6bSSebastien Roy 			dls_devnet_rele(ddp);
16452b24ab6bSSebastien Roy 		return (err);
16462b24ab6bSSebastien Roy 	}
1647d62bc4baSyz 
1648d62bc4baSyz 	/*
16492b24ab6bSSebastien Roy 	 * Release the additional reference if the link is returning to the
16502b24ab6bSSebastien Roy 	 * global zone from a non-global zone.
1651d62bc4baSyz 	 */
16522b24ab6bSSebastien Roy 	if (old_zid != GLOBAL_ZONEID && new_zid == GLOBAL_ZONEID)
16532b24ab6bSSebastien Roy 		dls_devnet_rele(ddh);
16542b24ab6bSSebastien Roy 
16552b24ab6bSSebastien Roy 	/* Re-create kstats in the appropriate zones. */
16562b24ab6bSSebastien Roy 	if (old_zid != GLOBAL_ZONEID)
16572b24ab6bSSebastien Roy 		dls_devnet_stat_destroy(ddh, old_zid);
16582b24ab6bSSebastien Roy 	if (new_zid != GLOBAL_ZONEID)
16592b24ab6bSSebastien Roy 		dls_devnet_stat_create(ddh, new_zid);
1660d62bc4baSyz 
1661d62bc4baSyz 	return (0);
1662d62bc4baSyz }
1663d62bc4baSyz 
16642b24ab6bSSebastien Roy zoneid_t
dls_devnet_getzid(dls_dl_handle_t ddh)16652b24ab6bSSebastien Roy dls_devnet_getzid(dls_dl_handle_t ddh)
1666d62bc4baSyz {
16672b24ab6bSSebastien Roy 	return (((dls_devnet_t *)ddh)->dd_zid);
16682b24ab6bSSebastien Roy }
1669d62bc4baSyz 
16702b24ab6bSSebastien Roy zoneid_t
dls_devnet_getownerzid(dls_dl_handle_t ddh)16712b24ab6bSSebastien Roy dls_devnet_getownerzid(dls_dl_handle_t ddh)
16722b24ab6bSSebastien Roy {
16732b24ab6bSSebastien Roy 	return (((dls_devnet_t *)ddh)->dd_owner_zid);
16742b24ab6bSSebastien Roy }
1675d62bc4baSyz 
16762b24ab6bSSebastien Roy /*
16772b24ab6bSSebastien Roy  * Is linkid visible from zoneid?  A link is visible if it was created in the
16782b24ab6bSSebastien Roy  * zone, or if it is currently assigned to the zone.
16792b24ab6bSSebastien Roy  */
16802b24ab6bSSebastien Roy boolean_t
dls_devnet_islinkvisible(datalink_id_t linkid,zoneid_t zoneid)16812b24ab6bSSebastien Roy dls_devnet_islinkvisible(datalink_id_t linkid, zoneid_t zoneid)
16822b24ab6bSSebastien Roy {
16832b24ab6bSSebastien Roy 	dls_devnet_t	*ddp;
16842b24ab6bSSebastien Roy 	boolean_t	result;
1685d62bc4baSyz 
16862b24ab6bSSebastien Roy 	if (dls_devnet_hold_tmp(linkid, &ddp) != 0)
16872b24ab6bSSebastien Roy 		return (B_FALSE);
16882b24ab6bSSebastien Roy 	result = (ddp->dd_owner_zid == zoneid || ddp->dd_zid == zoneid);
1689d62bc4baSyz 	dls_devnet_rele_tmp(ddp);
16902b24ab6bSSebastien Roy 	return (result);
1691d62bc4baSyz }
1692d62bc4baSyz 
1693d62bc4baSyz /*
1694d62bc4baSyz  * Access a vanity naming node.
1695d62bc4baSyz  */
1696d62bc4baSyz int
dls_devnet_open(const char * link,dls_dl_handle_t * dhp,dev_t * devp)1697d62bc4baSyz dls_devnet_open(const char *link, dls_dl_handle_t *dhp, dev_t *devp)
1698d62bc4baSyz {
1699d62bc4baSyz 	dls_devnet_t	*ddp;
1700da14cebeSEric Cheng 	dls_link_t	*dlp;
1701d62bc4baSyz 	zoneid_t	zid = getzoneid();
1702d62bc4baSyz 	int		err;
1703da14cebeSEric Cheng 	mac_perim_handle_t	mph;
1704d62bc4baSyz 
1705da14cebeSEric Cheng 	if ((err = dls_devnet_hold_by_name(link, &ddp)) != 0)
1706d62bc4baSyz 		return (err);
1707d62bc4baSyz 
1708da14cebeSEric Cheng 	dls_devnet_prop_task_wait(ddp);
1709da14cebeSEric Cheng 
1710d62bc4baSyz 	/*
1711d62bc4baSyz 	 * Opening a link that does not belong to the current non-global zone
1712d62bc4baSyz 	 * is not allowed.
1713d62bc4baSyz 	 */
1714d62bc4baSyz 	if (zid != GLOBAL_ZONEID && ddp->dd_zid != zid) {
1715d62bc4baSyz 		dls_devnet_rele(ddp);
1716d62bc4baSyz 		return (ENOENT);
1717d62bc4baSyz 	}
1718d62bc4baSyz 
1719da14cebeSEric Cheng 	err = mac_perim_enter_by_macname(ddp->dd_mac, &mph);
1720d62bc4baSyz 	if (err != 0) {
1721d62bc4baSyz 		dls_devnet_rele(ddp);
1722d62bc4baSyz 		return (err);
1723d62bc4baSyz 	}
1724d62bc4baSyz 
1725da14cebeSEric Cheng 	err = dls_link_hold_create(ddp->dd_mac, &dlp);
1726da14cebeSEric Cheng 	mac_perim_exit(mph);
1727da14cebeSEric Cheng 
1728da14cebeSEric Cheng 	if (err != 0) {
1729da14cebeSEric Cheng 		dls_devnet_rele(ddp);
1730da14cebeSEric Cheng 		return (err);
1731da14cebeSEric Cheng 	}
173230890389Sartem 
1733d62bc4baSyz 	*dhp = ddp;
1734da14cebeSEric Cheng 	*devp = dls_link_dev(dlp);
1735d62bc4baSyz 	return (0);
1736d62bc4baSyz }
1737d62bc4baSyz 
1738d62bc4baSyz /*
1739d62bc4baSyz  * Close access to a vanity naming node.
1740d62bc4baSyz  */
1741d62bc4baSyz void
dls_devnet_close(dls_dl_handle_t dlh)1742d62bc4baSyz dls_devnet_close(dls_dl_handle_t dlh)
1743d62bc4baSyz {
1744d62bc4baSyz 	dls_devnet_t	*ddp = dlh;
1745da14cebeSEric Cheng 	dls_link_t	*dlp;
1746da14cebeSEric Cheng 	mac_perim_handle_t	mph;
1747da14cebeSEric Cheng 
1748da14cebeSEric Cheng 	VERIFY(mac_perim_enter_by_macname(ddp->dd_mac, &mph) == 0);
1749da14cebeSEric Cheng 	VERIFY(dls_link_hold(ddp->dd_mac, &dlp) == 0);
1750d62bc4baSyz 
1751d62bc4baSyz 	/*
1752da14cebeSEric Cheng 	 * One rele for the hold placed in dls_devnet_open, another for
1753da14cebeSEric Cheng 	 * the hold done just above
1754d62bc4baSyz 	 */
1755da14cebeSEric Cheng 	dls_link_rele(dlp);
1756da14cebeSEric Cheng 	dls_link_rele(dlp);
1757da14cebeSEric Cheng 	mac_perim_exit(mph);
1758da14cebeSEric Cheng 
1759d62bc4baSyz 	dls_devnet_rele(ddp);
1760d62bc4baSyz }
1761d62bc4baSyz 
1762d62bc4baSyz /*
1763d62bc4baSyz  * This is used by /dev/net to rebuild the nodes for readdir().  It is not
1764d62bc4baSyz  * critical and no protection is needed.
1765d62bc4baSyz  */
1766d62bc4baSyz boolean_t
dls_devnet_rebuild()1767d62bc4baSyz dls_devnet_rebuild()
1768d62bc4baSyz {
1769d62bc4baSyz 	boolean_t updated = devnet_need_rebuild;
1770d62bc4baSyz 
1771d62bc4baSyz 	devnet_need_rebuild = B_FALSE;
1772d62bc4baSyz 	return (updated);
1773d62bc4baSyz }
1774d62bc4baSyz 
1775d62bc4baSyz int
dls_devnet_create(mac_handle_t mh,datalink_id_t linkid,zoneid_t zoneid)17762b24ab6bSSebastien Roy dls_devnet_create(mac_handle_t mh, datalink_id_t linkid, zoneid_t zoneid)
1777d62bc4baSyz {
1778da14cebeSEric Cheng 	dls_link_t	*dlp;
17792b24ab6bSSebastien Roy 	dls_devnet_t	*ddp;
1780d62bc4baSyz 	int		err;
1781da14cebeSEric Cheng 	mac_perim_handle_t mph;
1782d62bc4baSyz 
1783ae6aa22aSVenugopal Iyer 	/*
1784ae6aa22aSVenugopal Iyer 	 * Holding the mac perimeter ensures that the downcall from the
1785ae6aa22aSVenugopal Iyer 	 * dlmgmt daemon which does the property loading does not proceed
1786ae6aa22aSVenugopal Iyer 	 * until we relinquish the perimeter.
1787ae6aa22aSVenugopal Iyer 	 */
1788da14cebeSEric Cheng 	mac_perim_enter_by_mh(mh, &mph);
1789da14cebeSEric Cheng 	/*
1790da14cebeSEric Cheng 	 * Make this association before we call dls_link_hold_create as
1791da14cebeSEric Cheng 	 * we need to use the linkid to get the user name for the link
1792da14cebeSEric Cheng 	 * when we create the MAC client.
1793da14cebeSEric Cheng 	 */
179485dff7a0SAndy Fiddaman 	if ((err = dls_devnet_set(mh, linkid, zoneid, &ddp)) == 0) {
17952b24ab6bSSebastien Roy 		if ((err = dls_link_hold_create(mac_name(mh), &dlp)) != 0) {
17962b24ab6bSSebastien Roy 			mac_perim_exit(mph);
179785dff7a0SAndy Fiddaman 			(void) dls_devnet_unset(mh, &linkid, B_FALSE);
17982b24ab6bSSebastien Roy 			return (err);
17992b24ab6bSSebastien Roy 		}
180085dff7a0SAndy Fiddaman 
180185dff7a0SAndy Fiddaman 		/*
180285dff7a0SAndy Fiddaman 		 * If dd_linkid is set then the link was successfully
180385dff7a0SAndy Fiddaman 		 * initialized. In this case we can remove the
180485dff7a0SAndy Fiddaman 		 * initializing flag and make the link visible to the
180585dff7a0SAndy Fiddaman 		 * rest of the system.
180685dff7a0SAndy Fiddaman 		 *
180785dff7a0SAndy Fiddaman 		 * If not set then we were called by softmac and it
180885dff7a0SAndy Fiddaman 		 * was unable to obtain a linkid for the physical link
180985dff7a0SAndy Fiddaman 		 * because dlmgmtd is down. In that case softmac will
181085dff7a0SAndy Fiddaman 		 * eventually obtain a linkid and call
181185dff7a0SAndy Fiddaman 		 * dls_devnet_recreate() to complete initialization.
181285dff7a0SAndy Fiddaman 		 */
181385dff7a0SAndy Fiddaman 		mutex_enter(&ddp->dd_mutex);
181485dff7a0SAndy Fiddaman 		if (ddp->dd_linkid != DATALINK_INVALID_LINKID)
181585dff7a0SAndy Fiddaman 			ddp->dd_flags &= ~DD_INITIALIZING;
181685dff7a0SAndy Fiddaman 		mutex_exit(&ddp->dd_mutex);
181785dff7a0SAndy Fiddaman 
1818da14cebeSEric Cheng 	}
181985dff7a0SAndy Fiddaman 
1820da14cebeSEric Cheng 	mac_perim_exit(mph);
1821d62bc4baSyz 	return (err);
1822d62bc4baSyz }
1823d62bc4baSyz 
1824d62bc4baSyz /*
1825d62bc4baSyz  * Set the linkid of the dls_devnet_t and add it into the i_dls_devnet_id_hash.
1826d62bc4baSyz  * This is called in the case that the dlmgmtd daemon is started later than
1827d62bc4baSyz  * the physical devices get attached, and the linkid is only known after the
1828d62bc4baSyz  * daemon starts.
1829d62bc4baSyz  */
1830d62bc4baSyz int
dls_devnet_recreate(mac_handle_t mh,datalink_id_t linkid)1831d62bc4baSyz dls_devnet_recreate(mac_handle_t mh, datalink_id_t linkid)
1832d62bc4baSyz {
183385dff7a0SAndy Fiddaman 	dls_devnet_t	*ddp;
183485dff7a0SAndy Fiddaman 	int		err;
183585dff7a0SAndy Fiddaman 
183685dff7a0SAndy Fiddaman 	VERIFY(linkid != DATALINK_INVALID_LINKID);
183785dff7a0SAndy Fiddaman 	if ((err = dls_devnet_set(mh, linkid, GLOBAL_ZONEID, &ddp)) == 0) {
183885dff7a0SAndy Fiddaman 		mutex_enter(&ddp->dd_mutex);
183985dff7a0SAndy Fiddaman 		if (ddp->dd_linkid != DATALINK_INVALID_LINKID)
184085dff7a0SAndy Fiddaman 			ddp->dd_flags &= ~DD_INITIALIZING;
184185dff7a0SAndy Fiddaman 		mutex_exit(&ddp->dd_mutex);
184285dff7a0SAndy Fiddaman 	}
184385dff7a0SAndy Fiddaman 
184485dff7a0SAndy Fiddaman 	return (err);
184585dff7a0SAndy Fiddaman 
1846d62bc4baSyz }
1847d62bc4baSyz 
1848d62bc4baSyz int
dls_devnet_destroy(mac_handle_t mh,datalink_id_t * idp,boolean_t wait)1849da14cebeSEric Cheng dls_devnet_destroy(mac_handle_t mh, datalink_id_t *idp, boolean_t wait)
1850d62bc4baSyz {
1851da14cebeSEric Cheng 	int			err;
1852da14cebeSEric Cheng 	mac_perim_handle_t	mph;
1853d62bc4baSyz 
1854d62bc4baSyz 	*idp = DATALINK_INVALID_LINKID;
185585dff7a0SAndy Fiddaman 	err = dls_devnet_unset(mh, idp, wait);
185685dff7a0SAndy Fiddaman 
185785dff7a0SAndy Fiddaman 	/*
185885dff7a0SAndy Fiddaman 	 * We continue on in the face of ENOENT because the devnet
185985dff7a0SAndy Fiddaman 	 * unset and DLS link release are not atomic and we may have a
186085dff7a0SAndy Fiddaman 	 * scenario where there is no entry in i_dls_devnet_hash for
186185dff7a0SAndy Fiddaman 	 * the MAC name but there is an entry in i_dls_link_hash. For
186285dff7a0SAndy Fiddaman 	 * example, if the following occurred:
186385dff7a0SAndy Fiddaman 	 *
186485dff7a0SAndy Fiddaman 	 * 1. dls_devnet_unset() returns success, and
186585dff7a0SAndy Fiddaman 	 *
186685dff7a0SAndy Fiddaman 	 * 2. dls_link_rele_by_name() fails with ENOTEMPTY because
186785dff7a0SAndy Fiddaman 	 *    flows still exist, and
186885dff7a0SAndy Fiddaman 	 *
186985dff7a0SAndy Fiddaman 	 * 3. dls_devnet_set() fails to set the zone id and calls
187085dff7a0SAndy Fiddaman 	 *    dls_devnet_unset() -- leaving an entry in
187185dff7a0SAndy Fiddaman 	 *    i_dls_link_hash but no corresponding entry in
187285dff7a0SAndy Fiddaman 	 *    i_dls_devnet_hash.
187385dff7a0SAndy Fiddaman 	 *
187485dff7a0SAndy Fiddaman 	 * Even if #3 wasn't true the dls_devnet_set() may fail for
187585dff7a0SAndy Fiddaman 	 * different reasons in the future; the point is that it _can_
187685dff7a0SAndy Fiddaman 	 * fail as part of its contract. We can't rely on it working
187785dff7a0SAndy Fiddaman 	 * so we must assume that these two pieces of state (devnet
187885dff7a0SAndy Fiddaman 	 * and link hashes), which should always be in sync, can get
187985dff7a0SAndy Fiddaman 	 * out of sync and thus even if we get ENOENT from the devnet
188085dff7a0SAndy Fiddaman 	 * hash we should still try to delete from the link hash just
188185dff7a0SAndy Fiddaman 	 * in case.
188285dff7a0SAndy Fiddaman 	 *
188385dff7a0SAndy Fiddaman 	 * We could prevent the ENOTEMPTY from dls_link_rele_by_name()
188485dff7a0SAndy Fiddaman 	 * by calling mac_disable() before calling
188585dff7a0SAndy Fiddaman 	 * dls_devnet_destroy() but that's not currently possible due
188685dff7a0SAndy Fiddaman 	 * to a long-standing bug. OpenSolaris 6791335: The semantics
188785dff7a0SAndy Fiddaman 	 * of mac_disable() were modified by Crossbow such that
188885dff7a0SAndy Fiddaman 	 * dls_devnet_destroy() needs to be called before
188985dff7a0SAndy Fiddaman 	 * mac_disable() can succeed. This is because of the implicit
189085dff7a0SAndy Fiddaman 	 * reference that dls has on the mac_impl_t.
189185dff7a0SAndy Fiddaman 	 */
1892d62bc4baSyz 	if (err != 0 && err != ENOENT)
1893d62bc4baSyz 		return (err);
1894d62bc4baSyz 
1895da14cebeSEric Cheng 	mac_perim_enter_by_mh(mh, &mph);
1896da14cebeSEric Cheng 	err = dls_link_rele_by_name(mac_name(mh));
1897da14cebeSEric Cheng 	mac_perim_exit(mph);
1898d62bc4baSyz 
18992b24ab6bSSebastien Roy 	if (err != 0) {
190085dff7a0SAndy Fiddaman 		dls_devnet_t	*ddp;
190185dff7a0SAndy Fiddaman 
19022b24ab6bSSebastien Roy 		/*
19032b24ab6bSSebastien Roy 		 * XXX It is a general GLDv3 bug that dls_devnet_set() has to
19042b24ab6bSSebastien Roy 		 * be called to re-set the link when destroy fails.  The
19052b24ab6bSSebastien Roy 		 * zoneid below will be incorrect if this function is ever
19062b24ab6bSSebastien Roy 		 * called from kernel context or from a zone other than that
19072b24ab6bSSebastien Roy 		 * which initially created the link.
19082b24ab6bSSebastien Roy 		 */
190985dff7a0SAndy Fiddaman 		(void) dls_devnet_set(mh, *idp, crgetzoneid(CRED()), &ddp);
191085dff7a0SAndy Fiddaman 
191185dff7a0SAndy Fiddaman 		/*
191285dff7a0SAndy Fiddaman 		 * You might think dd_linkid should always be set
191385dff7a0SAndy Fiddaman 		 * here, but in the case where dls_devnet_unset()
191485dff7a0SAndy Fiddaman 		 * returns ENOENT it will be DATALINK_INVALID_LINKID.
191585dff7a0SAndy Fiddaman 		 * Stay consistent with the rest of DLS and only
191685dff7a0SAndy Fiddaman 		 * remove the initializing flag if linkid is set.
191785dff7a0SAndy Fiddaman 		 */
191885dff7a0SAndy Fiddaman 		mutex_enter(&ddp->dd_mutex);
191985dff7a0SAndy Fiddaman 		if (ddp->dd_linkid != DATALINK_INVALID_LINKID)
192085dff7a0SAndy Fiddaman 			ddp->dd_flags &= ~DD_INITIALIZING;
192185dff7a0SAndy Fiddaman 		mutex_exit(&ddp->dd_mutex);
19222b24ab6bSSebastien Roy 	}
19232b24ab6bSSebastien Roy 	return (err);
19242b24ab6bSSebastien Roy }
1925d62bc4baSyz 
19262b24ab6bSSebastien Roy /*
19272b24ab6bSSebastien Roy  * Implicitly create an IP tunnel link.
19282b24ab6bSSebastien Roy  */
19292b24ab6bSSebastien Roy static int
i_dls_devnet_create_iptun(const char * linkname,const char * drvname,datalink_id_t * linkid)1930d501bbfeSSebastien Roy i_dls_devnet_create_iptun(const char *linkname, const char *drvname,
1931d501bbfeSSebastien Roy     datalink_id_t *linkid)
19322b24ab6bSSebastien Roy {
19332b24ab6bSSebastien Roy 	int		err;
19342b24ab6bSSebastien Roy 	iptun_kparams_t	ik;
19352b24ab6bSSebastien Roy 	uint32_t	media;
19362b24ab6bSSebastien Roy 	netstack_t	*ns;
19372b24ab6bSSebastien Roy 	major_t		iptun_major;
19382b24ab6bSSebastien Roy 	dev_info_t	*iptun_dip;
19392b24ab6bSSebastien Roy 
19402b24ab6bSSebastien Roy 	/* First ensure that the iptun device is attached. */
19412b24ab6bSSebastien Roy 	if ((iptun_major = ddi_name_to_major(IPTUN_DRIVER_NAME)) == (major_t)-1)
19422b24ab6bSSebastien Roy 		return (EINVAL);
19432b24ab6bSSebastien Roy 	if ((iptun_dip = ddi_hold_devi_by_instance(iptun_major, 0, 0)) == NULL)
19442b24ab6bSSebastien Roy 		return (EINVAL);
19452b24ab6bSSebastien Roy 
1946d501bbfeSSebastien Roy 	if (IS_IPV4_TUN(drvname)) {
19472b24ab6bSSebastien Roy 		ik.iptun_kparam_type = IPTUN_TYPE_IPV4;
19482b24ab6bSSebastien Roy 		media = DL_IPV4;
1949d501bbfeSSebastien Roy 	} else if (IS_6TO4_TUN(drvname)) {
19502b24ab6bSSebastien Roy 		ik.iptun_kparam_type = IPTUN_TYPE_6TO4;
19512b24ab6bSSebastien Roy 		media = DL_6TO4;
1952d501bbfeSSebastien Roy 	} else if (IS_IPV6_TUN(drvname)) {
19532b24ab6bSSebastien Roy 		ik.iptun_kparam_type = IPTUN_TYPE_IPV6;
19542b24ab6bSSebastien Roy 		media = DL_IPV6;
19552b24ab6bSSebastien Roy 	}
19562b24ab6bSSebastien Roy 	ik.iptun_kparam_flags = (IPTUN_KPARAM_TYPE | IPTUN_KPARAM_IMPLICIT);
19572b24ab6bSSebastien Roy 
19582b24ab6bSSebastien Roy 	/* Obtain a datalink id for this tunnel. */
1959d501bbfeSSebastien Roy 	err = dls_mgmt_create((char *)linkname, 0, DATALINK_CLASS_IPTUN, media,
19602b24ab6bSSebastien Roy 	    B_FALSE, &ik.iptun_kparam_linkid);
19612b24ab6bSSebastien Roy 	if (err != 0) {
19622b24ab6bSSebastien Roy 		ddi_release_devi(iptun_dip);
19632b24ab6bSSebastien Roy 		return (err);
19642b24ab6bSSebastien Roy 	}
19652b24ab6bSSebastien Roy 
19662b24ab6bSSebastien Roy 	ns = netstack_get_current();
19672b24ab6bSSebastien Roy 	err = iptun_create(&ik, CRED());
19682b24ab6bSSebastien Roy 	netstack_rele(ns);
19692b24ab6bSSebastien Roy 
19702b24ab6bSSebastien Roy 	if (err != 0)
19712b24ab6bSSebastien Roy 		VERIFY(dls_mgmt_destroy(ik.iptun_kparam_linkid, B_FALSE) == 0);
19722b24ab6bSSebastien Roy 	else
19732b24ab6bSSebastien Roy 		*linkid = ik.iptun_kparam_linkid;
19742b24ab6bSSebastien Roy 
19752b24ab6bSSebastien Roy 	ddi_release_devi(iptun_dip);
19762b24ab6bSSebastien Roy 	return (err);
19772b24ab6bSSebastien Roy }
19782b24ab6bSSebastien Roy 
19792b24ab6bSSebastien Roy static int
i_dls_devnet_destroy_iptun(datalink_id_t linkid)19802b24ab6bSSebastien Roy i_dls_devnet_destroy_iptun(datalink_id_t linkid)
19812b24ab6bSSebastien Roy {
19822b24ab6bSSebastien Roy 	int err;
19832b24ab6bSSebastien Roy 
19842b24ab6bSSebastien Roy 	/*
19852b24ab6bSSebastien Roy 	 * Note the use of zone_kcred() here as opposed to CRED().  This is
19862b24ab6bSSebastien Roy 	 * because the process that does the last close of this /dev/net node
19872b24ab6bSSebastien Roy 	 * may not have necessary privileges to delete this IP tunnel, but the
19882b24ab6bSSebastien Roy 	 * tunnel must always be implicitly deleted on last close.
19892b24ab6bSSebastien Roy 	 */
19902b24ab6bSSebastien Roy 	if ((err = iptun_delete(linkid, zone_kcred())) == 0)
19912b24ab6bSSebastien Roy 		(void) dls_mgmt_destroy(linkid, B_FALSE);
1992d62bc4baSyz 	return (err);
1993d62bc4baSyz }
1994d62bc4baSyz 
1995a25df667SRobert Mustacchi const char *
dls_devnet_link(dls_dl_handle_t ddh)1996a25df667SRobert Mustacchi dls_devnet_link(dls_dl_handle_t ddh)
1997a25df667SRobert Mustacchi {
1998a25df667SRobert Mustacchi 	return (ddh->dd_linkname);
1999a25df667SRobert Mustacchi }
2000a25df667SRobert Mustacchi 
2001d62bc4baSyz const char *
dls_devnet_mac(dls_dl_handle_t ddh)2002d62bc4baSyz dls_devnet_mac(dls_dl_handle_t ddh)
2003d62bc4baSyz {
2004d62bc4baSyz 	return (ddh->dd_mac);
2005d62bc4baSyz }
2006d62bc4baSyz 
2007d62bc4baSyz datalink_id_t
dls_devnet_linkid(dls_dl_handle_t ddh)2008d62bc4baSyz dls_devnet_linkid(dls_dl_handle_t ddh)
2009d62bc4baSyz {
2010d62bc4baSyz 	return (ddh->dd_linkid);
2011d62bc4baSyz }
2012