xref: /titanic_51/usr/src/uts/common/io/dls/dls_mgmt.c (revision ccdeb6b6d71f3c9aa7e78b688f7b34fff109a817)
1d62bc4baSyz147064 /*
2d62bc4baSyz147064  * CDDL HEADER START
3d62bc4baSyz147064  *
4d62bc4baSyz147064  * The contents of this file are subject to the terms of the
5d62bc4baSyz147064  * Common Development and Distribution License (the "License").
6d62bc4baSyz147064  * You may not use this file except in compliance with the License.
7d62bc4baSyz147064  *
8d62bc4baSyz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d62bc4baSyz147064  * or http://www.opensolaris.org/os/licensing.
10d62bc4baSyz147064  * See the License for the specific language governing permissions
11d62bc4baSyz147064  * and limitations under the License.
12d62bc4baSyz147064  *
13d62bc4baSyz147064  * When distributing Covered Code, include this CDDL HEADER in each
14d62bc4baSyz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d62bc4baSyz147064  * If applicable, add the following below this CDDL HEADER, with the
16d62bc4baSyz147064  * fields enclosed by brackets "[]" replaced with your own identifying
17d62bc4baSyz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
18d62bc4baSyz147064  *
19d62bc4baSyz147064  * CDDL HEADER END
20d62bc4baSyz147064  */
21d62bc4baSyz147064 /*
22ae6aa22aSVenugopal Iyer  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23d62bc4baSyz147064  * Use is subject to license terms.
24d62bc4baSyz147064  */
25d62bc4baSyz147064 
26d62bc4baSyz147064 /*
27d62bc4baSyz147064  * Datalink management routines.
28d62bc4baSyz147064  */
29d62bc4baSyz147064 
30d62bc4baSyz147064 #include <sys/types.h>
31d62bc4baSyz147064 #include <sys/door.h>
32d62bc4baSyz147064 #include <sys/zone.h>
33d62bc4baSyz147064 #include <sys/modctl.h>
34d62bc4baSyz147064 #include <sys/file.h>
35d62bc4baSyz147064 #include <sys/modhash.h>
36d62bc4baSyz147064 #include <sys/kstat.h>
37d62bc4baSyz147064 #include <sys/vnode.h>
38d62bc4baSyz147064 #include <sys/cmn_err.h>
39d62bc4baSyz147064 #include <sys/softmac.h>
40d62bc4baSyz147064 #include <sys/dls.h>
41d62bc4baSyz147064 #include <sys/dls_impl.h>
422b24ab6bSSebastien Roy #include <sys/stropts.h>
432b24ab6bSSebastien Roy #include <sys/netstack.h>
442b24ab6bSSebastien Roy #include <inet/iptun/iptun_impl.h>
45d62bc4baSyz147064 
46da14cebeSEric Cheng /*
47da14cebeSEric Cheng  * This vanity name management module is treated as part of the GLD framework
48da14cebeSEric Cheng  * and we don't hold any GLD framework lock across a call to any mac
49da14cebeSEric Cheng  * function that needs to acquire the mac perimeter. The hierarchy is
50da14cebeSEric Cheng  * mac perimeter -> framework locks
51da14cebeSEric Cheng  */
52da14cebeSEric Cheng 
532b24ab6bSSebastien Roy typedef struct dls_stack {
542b24ab6bSSebastien Roy 	zoneid_t	dlss_zoneid;
552b24ab6bSSebastien Roy } dls_stack_t;
562b24ab6bSSebastien Roy 
57d62bc4baSyz147064 static kmem_cache_t	*i_dls_devnet_cachep;
58d62bc4baSyz147064 static kmutex_t		i_dls_mgmt_lock;
59d62bc4baSyz147064 static krwlock_t	i_dls_devnet_lock;
60d62bc4baSyz147064 static mod_hash_t	*i_dls_devnet_id_hash;
61d62bc4baSyz147064 static mod_hash_t	*i_dls_devnet_hash;
62d62bc4baSyz147064 
63d62bc4baSyz147064 boolean_t		devnet_need_rebuild;
64d62bc4baSyz147064 
65d62bc4baSyz147064 #define	VLAN_HASHSZ	67	/* prime */
66d62bc4baSyz147064 
672b24ab6bSSebastien Roy /*
68d501bbfeSSebastien Roy  * The following macros take a link name without the trailing PPA as input.
69d501bbfeSSebastien Roy  * Opening a /dev/net node with one of these names causes a tunnel link to be
70d501bbfeSSebastien Roy  * implicitly created in dls_devnet_hold_by_name() for backward compatibility
71d501bbfeSSebastien Roy  * with Solaris 10 and prior.
722b24ab6bSSebastien Roy  */
73d501bbfeSSebastien Roy #define	IS_IPV4_TUN(name)	(strcmp((name), "ip.tun") == 0)
74d501bbfeSSebastien Roy #define	IS_IPV6_TUN(name)	(strcmp((name), "ip6.tun") == 0)
75d501bbfeSSebastien Roy #define	IS_6TO4_TUN(name)	(strcmp((name), "ip.6to4tun") == 0)
762b24ab6bSSebastien Roy #define	IS_IPTUN_LINK(name)	(					\
772b24ab6bSSebastien Roy     IS_IPV4_TUN(name) || IS_IPV6_TUN(name) || IS_6TO4_TUN(name))
782b24ab6bSSebastien Roy 
79d62bc4baSyz147064 /* Upcall door handle */
80d62bc4baSyz147064 static door_handle_t	dls_mgmt_dh = NULL;
81d62bc4baSyz147064 
82da14cebeSEric Cheng #define	DD_CONDEMNED		0x1
83ae6aa22aSVenugopal Iyer #define	DD_KSTAT_CHANGING	0x2
842b24ab6bSSebastien Roy #define	DD_IMPLICIT_IPTUN	0x4 /* Implicitly-created ip*.*tun* tunnel */
85da14cebeSEric Cheng 
86d62bc4baSyz147064 /*
87da14cebeSEric Cheng  * This structure is used to keep the <linkid, macname> mapping.
88ae6aa22aSVenugopal Iyer  * This structure itself is not protected by the mac perimeter, but is
89ae6aa22aSVenugopal Iyer  * protected by the dd_mutex and i_dls_devnet_lock. Thus most of the
90ae6aa22aSVenugopal Iyer  * functions manipulating this structure such as dls_devnet_set/unset etc.
91ae6aa22aSVenugopal Iyer  * may be called while not holding the mac perimeter.
92d62bc4baSyz147064  */
93d62bc4baSyz147064 typedef struct dls_devnet_s {
94d62bc4baSyz147064 	datalink_id_t	dd_linkid;
952b24ab6bSSebastien Roy 	char		dd_linkname[MAXLINKNAMELEN];
96d62bc4baSyz147064 	char		dd_mac[MAXNAMELEN];
972b24ab6bSSebastien Roy 	kstat_t		*dd_ksp;	/* kstat in owner_zid */
982b24ab6bSSebastien Roy 	kstat_t		*dd_zone_ksp;	/* in dd_zid if != owner_zid */
99d62bc4baSyz147064 	uint32_t	dd_ref;
100d62bc4baSyz147064 	kmutex_t	dd_mutex;
101d62bc4baSyz147064 	kcondvar_t	dd_cv;
102d62bc4baSyz147064 	uint32_t	dd_tref;
103da14cebeSEric Cheng 	uint_t		dd_flags;
1042b24ab6bSSebastien Roy 	zoneid_t	dd_owner_zid;	/* zone where node was created */
1052b24ab6bSSebastien Roy 	zoneid_t	dd_zid;		/* current zone */
10630890389Sartem 	boolean_t	dd_prop_loaded;
10730890389Sartem 	taskqid_t	dd_prop_taskid;
108d62bc4baSyz147064 } dls_devnet_t;
109d62bc4baSyz147064 
110d501bbfeSSebastien Roy static int i_dls_devnet_create_iptun(const char *, const char *,
111d501bbfeSSebastien Roy     datalink_id_t *);
1122b24ab6bSSebastien Roy static int i_dls_devnet_destroy_iptun(datalink_id_t);
1132b24ab6bSSebastien Roy static int i_dls_devnet_setzid(dls_devnet_t *, zoneid_t, boolean_t);
1142b24ab6bSSebastien Roy static int dls_devnet_unset(const char *, datalink_id_t *, boolean_t);
11530890389Sartem 
116d62bc4baSyz147064 /*ARGSUSED*/
117d62bc4baSyz147064 static int
118d62bc4baSyz147064 i_dls_devnet_constructor(void *buf, void *arg, int kmflag)
119d62bc4baSyz147064 {
120d62bc4baSyz147064 	dls_devnet_t	*ddp = buf;
121d62bc4baSyz147064 
122d62bc4baSyz147064 	bzero(buf, sizeof (dls_devnet_t));
123d62bc4baSyz147064 	mutex_init(&ddp->dd_mutex, NULL, MUTEX_DEFAULT, NULL);
124d62bc4baSyz147064 	cv_init(&ddp->dd_cv, NULL, CV_DEFAULT, NULL);
125d62bc4baSyz147064 	return (0);
126d62bc4baSyz147064 }
127d62bc4baSyz147064 
128d62bc4baSyz147064 /*ARGSUSED*/
129d62bc4baSyz147064 static void
130d62bc4baSyz147064 i_dls_devnet_destructor(void *buf, void *arg)
131d62bc4baSyz147064 {
132d62bc4baSyz147064 	dls_devnet_t	*ddp = buf;
133d62bc4baSyz147064 
134d62bc4baSyz147064 	ASSERT(ddp->dd_ksp == NULL);
135d62bc4baSyz147064 	ASSERT(ddp->dd_ref == 0);
136d62bc4baSyz147064 	ASSERT(ddp->dd_tref == 0);
137d62bc4baSyz147064 	mutex_destroy(&ddp->dd_mutex);
138d62bc4baSyz147064 	cv_destroy(&ddp->dd_cv);
139d62bc4baSyz147064 }
140d62bc4baSyz147064 
1412b24ab6bSSebastien Roy /* ARGSUSED */
1422b24ab6bSSebastien Roy static int
1432b24ab6bSSebastien Roy dls_zone_remove(datalink_id_t linkid, void *arg)
1442b24ab6bSSebastien Roy {
1452b24ab6bSSebastien Roy 	dls_devnet_t *ddp;
1462b24ab6bSSebastien Roy 
1472b24ab6bSSebastien Roy 	if (dls_devnet_hold_tmp(linkid, &ddp) == 0) {
1482b24ab6bSSebastien Roy 		(void) dls_devnet_setzid(ddp, GLOBAL_ZONEID);
1492b24ab6bSSebastien Roy 		dls_devnet_rele_tmp(ddp);
1502b24ab6bSSebastien Roy 	}
1512b24ab6bSSebastien Roy 	return (0);
1522b24ab6bSSebastien Roy }
1532b24ab6bSSebastien Roy 
1542b24ab6bSSebastien Roy /* ARGSUSED */
1552b24ab6bSSebastien Roy static void *
1562b24ab6bSSebastien Roy dls_stack_init(netstackid_t stackid, netstack_t *ns)
1572b24ab6bSSebastien Roy {
1582b24ab6bSSebastien Roy 	dls_stack_t *dlss;
1592b24ab6bSSebastien Roy 
1602b24ab6bSSebastien Roy 	dlss = kmem_zalloc(sizeof (*dlss), KM_SLEEP);
1612b24ab6bSSebastien Roy 	dlss->dlss_zoneid = netstackid_to_zoneid(stackid);
1622b24ab6bSSebastien Roy 	return (dlss);
1632b24ab6bSSebastien Roy }
1642b24ab6bSSebastien Roy 
1652b24ab6bSSebastien Roy /* ARGSUSED */
1662b24ab6bSSebastien Roy static void
1672b24ab6bSSebastien Roy dls_stack_shutdown(netstackid_t stackid, void *arg)
1682b24ab6bSSebastien Roy {
1692b24ab6bSSebastien Roy 	dls_stack_t	*dlss = (dls_stack_t *)arg;
1702b24ab6bSSebastien Roy 
1712b24ab6bSSebastien Roy 	/* Move remaining datalinks in this zone back to the global zone. */
1722b24ab6bSSebastien Roy 	(void) zone_datalink_walk(dlss->dlss_zoneid, dls_zone_remove, NULL);
1732b24ab6bSSebastien Roy }
1742b24ab6bSSebastien Roy 
1752b24ab6bSSebastien Roy /* ARGSUSED */
1762b24ab6bSSebastien Roy static void
1772b24ab6bSSebastien Roy dls_stack_fini(netstackid_t stackid, void *arg)
1782b24ab6bSSebastien Roy {
1792b24ab6bSSebastien Roy 	dls_stack_t	*dlss = (dls_stack_t *)arg;
1802b24ab6bSSebastien Roy 
1812b24ab6bSSebastien Roy 	kmem_free(dlss, sizeof (*dlss));
1822b24ab6bSSebastien Roy }
1832b24ab6bSSebastien Roy 
184d62bc4baSyz147064 /*
185d62bc4baSyz147064  * Module initialization and finalization functions.
186d62bc4baSyz147064  */
187d62bc4baSyz147064 void
188d62bc4baSyz147064 dls_mgmt_init(void)
189d62bc4baSyz147064 {
190d62bc4baSyz147064 	mutex_init(&i_dls_mgmt_lock, NULL, MUTEX_DEFAULT, NULL);
191d62bc4baSyz147064 	rw_init(&i_dls_devnet_lock, NULL, RW_DEFAULT, NULL);
192d62bc4baSyz147064 
193d62bc4baSyz147064 	/*
194d62bc4baSyz147064 	 * Create a kmem_cache of dls_devnet_t structures.
195d62bc4baSyz147064 	 */
196d62bc4baSyz147064 	i_dls_devnet_cachep = kmem_cache_create("dls_devnet_cache",
197d62bc4baSyz147064 	    sizeof (dls_devnet_t), 0, i_dls_devnet_constructor,
198d62bc4baSyz147064 	    i_dls_devnet_destructor, NULL, NULL, NULL, 0);
199d62bc4baSyz147064 	ASSERT(i_dls_devnet_cachep != NULL);
200d62bc4baSyz147064 
201d62bc4baSyz147064 	/*
202da14cebeSEric Cheng 	 * Create a hash table, keyed by dd_linkid, of dls_devnet_t.
203d62bc4baSyz147064 	 */
204d62bc4baSyz147064 	i_dls_devnet_id_hash = mod_hash_create_idhash("dls_devnet_id_hash",
205d62bc4baSyz147064 	    VLAN_HASHSZ, mod_hash_null_valdtor);
206d62bc4baSyz147064 
207d62bc4baSyz147064 	/*
208da14cebeSEric Cheng 	 * Create a hash table, keyed by dd_mac
209d62bc4baSyz147064 	 */
210d62bc4baSyz147064 	i_dls_devnet_hash = mod_hash_create_extended("dls_devnet_hash",
211d62bc4baSyz147064 	    VLAN_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
212d62bc4baSyz147064 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
213d62bc4baSyz147064 
214d62bc4baSyz147064 	devnet_need_rebuild = B_FALSE;
2152b24ab6bSSebastien Roy 
2162b24ab6bSSebastien Roy 	netstack_register(NS_DLS, dls_stack_init, dls_stack_shutdown,
2172b24ab6bSSebastien Roy 	    dls_stack_fini);
218d62bc4baSyz147064 }
219d62bc4baSyz147064 
220d62bc4baSyz147064 void
221d62bc4baSyz147064 dls_mgmt_fini(void)
222d62bc4baSyz147064 {
2232b24ab6bSSebastien Roy 	netstack_unregister(NS_DLS);
224d62bc4baSyz147064 	mod_hash_destroy_hash(i_dls_devnet_hash);
225d62bc4baSyz147064 	mod_hash_destroy_hash(i_dls_devnet_id_hash);
226d62bc4baSyz147064 	kmem_cache_destroy(i_dls_devnet_cachep);
227d62bc4baSyz147064 	rw_destroy(&i_dls_devnet_lock);
228d62bc4baSyz147064 	mutex_destroy(&i_dls_mgmt_lock);
229d62bc4baSyz147064 }
230d62bc4baSyz147064 
231d62bc4baSyz147064 int
232d62bc4baSyz147064 dls_mgmt_door_set(boolean_t start)
233d62bc4baSyz147064 {
234d62bc4baSyz147064 	int	err;
235d62bc4baSyz147064 
236d62bc4baSyz147064 	/* handle daemon restart */
237d62bc4baSyz147064 	mutex_enter(&i_dls_mgmt_lock);
238d62bc4baSyz147064 	if (dls_mgmt_dh != NULL) {
239d62bc4baSyz147064 		door_ki_rele(dls_mgmt_dh);
240d62bc4baSyz147064 		dls_mgmt_dh = NULL;
241d62bc4baSyz147064 	}
242d62bc4baSyz147064 
243d62bc4baSyz147064 	if (start && ((err = door_ki_open(DLMGMT_DOOR, &dls_mgmt_dh)) != 0)) {
244d62bc4baSyz147064 		mutex_exit(&i_dls_mgmt_lock);
245d62bc4baSyz147064 		return (err);
246d62bc4baSyz147064 	}
247d62bc4baSyz147064 
248d62bc4baSyz147064 	mutex_exit(&i_dls_mgmt_lock);
249d62bc4baSyz147064 
250d62bc4baSyz147064 	/*
251d62bc4baSyz147064 	 * Create and associate <link name, linkid> mapping for network devices
252d62bc4baSyz147064 	 * which are already attached before the daemon is started.
253d62bc4baSyz147064 	 */
254d62bc4baSyz147064 	if (start)
255d62bc4baSyz147064 		softmac_recreate();
256d62bc4baSyz147064 	return (0);
257d62bc4baSyz147064 }
258d62bc4baSyz147064 
259d62bc4baSyz147064 static boolean_t
260d62bc4baSyz147064 i_dls_mgmt_door_revoked(door_handle_t dh)
261d62bc4baSyz147064 {
262d62bc4baSyz147064 	struct door_info info;
263d62bc4baSyz147064 	extern int sys_shutdown;
264d62bc4baSyz147064 
265d62bc4baSyz147064 	ASSERT(dh != NULL);
266d62bc4baSyz147064 
267d62bc4baSyz147064 	if (sys_shutdown) {
268d62bc4baSyz147064 		cmn_err(CE_NOTE, "dls_mgmt_door: shutdown observed\n");
269d62bc4baSyz147064 		return (B_TRUE);
270d62bc4baSyz147064 	}
271d62bc4baSyz147064 
272d62bc4baSyz147064 	if (door_ki_info(dh, &info) != 0)
273d62bc4baSyz147064 		return (B_TRUE);
274d62bc4baSyz147064 
275d62bc4baSyz147064 	return ((info.di_attributes & DOOR_REVOKED) != 0);
276d62bc4baSyz147064 }
277d62bc4baSyz147064 
278d62bc4baSyz147064 /*
279d62bc4baSyz147064  * Upcall to the datalink management daemon (dlmgmtd).
280d62bc4baSyz147064  */
281d62bc4baSyz147064 static int
282024b0a25Sseb i_dls_mgmt_upcall(void *arg, size_t asize, void *rbuf, size_t rsize)
283d62bc4baSyz147064 {
284d62bc4baSyz147064 	door_arg_t			darg, save_arg;
285d62bc4baSyz147064 	door_handle_t			dh;
286024b0a25Sseb 	int				err;
287d62bc4baSyz147064 	int				retry = 0;
288d62bc4baSyz147064 
289d62bc4baSyz147064 #define	MAXRETRYNUM	3
290d62bc4baSyz147064 
291d62bc4baSyz147064 	ASSERT(arg);
292d62bc4baSyz147064 	darg.data_ptr = arg;
293d62bc4baSyz147064 	darg.data_size = asize;
294d62bc4baSyz147064 	darg.desc_ptr = NULL;
295d62bc4baSyz147064 	darg.desc_num = 0;
296d62bc4baSyz147064 	darg.rbuf = rbuf;
297024b0a25Sseb 	darg.rsize = rsize;
298d62bc4baSyz147064 	save_arg = darg;
299d62bc4baSyz147064 
300d62bc4baSyz147064 retry:
301d62bc4baSyz147064 	mutex_enter(&i_dls_mgmt_lock);
302d62bc4baSyz147064 	dh = dls_mgmt_dh;
303d62bc4baSyz147064 	if ((dh == NULL) || i_dls_mgmt_door_revoked(dh)) {
304d62bc4baSyz147064 		mutex_exit(&i_dls_mgmt_lock);
305d62bc4baSyz147064 		return (EBADF);
306d62bc4baSyz147064 	}
307d62bc4baSyz147064 	door_ki_hold(dh);
308d62bc4baSyz147064 	mutex_exit(&i_dls_mgmt_lock);
309d62bc4baSyz147064 
310d62bc4baSyz147064 	for (;;) {
311d62bc4baSyz147064 		retry++;
3122b24ab6bSSebastien Roy 		if ((err = door_ki_upcall_limited(dh, &darg, zone_kcred(),
313323a81d9Sjwadams 		    SIZE_MAX, 0)) == 0)
314d62bc4baSyz147064 			break;
315d62bc4baSyz147064 
316d62bc4baSyz147064 		/*
317d62bc4baSyz147064 		 * handle door call errors
318d62bc4baSyz147064 		 */
319d62bc4baSyz147064 		darg = save_arg;
320d62bc4baSyz147064 		switch (err) {
321d62bc4baSyz147064 		case EINTR:
322d62bc4baSyz147064 			/*
323d62bc4baSyz147064 			 * If the operation which caused this door upcall gets
324d62bc4baSyz147064 			 * interrupted, return directly.
325d62bc4baSyz147064 			 */
326d62bc4baSyz147064 			goto done;
327d62bc4baSyz147064 		case EAGAIN:
328d62bc4baSyz147064 			/*
329d62bc4baSyz147064 			 * Repeat upcall if the maximum attempt limit has not
330d62bc4baSyz147064 			 * been reached.
331d62bc4baSyz147064 			 */
332d62bc4baSyz147064 			if (retry < MAXRETRYNUM) {
333d62bc4baSyz147064 				delay(2 * hz);
334d62bc4baSyz147064 				break;
335d62bc4baSyz147064 			}
336d62bc4baSyz147064 			cmn_err(CE_WARN, "dls: dlmgmtd fatal error %d\n", err);
337d62bc4baSyz147064 			goto done;
338d62bc4baSyz147064 		default:
339d62bc4baSyz147064 			/* A fatal door error */
340d62bc4baSyz147064 			if (i_dls_mgmt_door_revoked(dh)) {
341d62bc4baSyz147064 				cmn_err(CE_NOTE,
342d62bc4baSyz147064 				    "dls: dlmgmtd door service revoked\n");
343d62bc4baSyz147064 
344d62bc4baSyz147064 				if (retry < MAXRETRYNUM) {
345d62bc4baSyz147064 					door_ki_rele(dh);
346d62bc4baSyz147064 					goto retry;
347d62bc4baSyz147064 				}
348d62bc4baSyz147064 			}
349d62bc4baSyz147064 			cmn_err(CE_WARN, "dls: dlmgmtd fatal error %d\n", err);
350d62bc4baSyz147064 			goto done;
351d62bc4baSyz147064 		}
352d62bc4baSyz147064 	}
353d62bc4baSyz147064 
354d62bc4baSyz147064 	if (darg.rbuf != rbuf) {
355d62bc4baSyz147064 		/*
356d62bc4baSyz147064 		 * The size of the input rbuf was not big enough, so the
357d62bc4baSyz147064 		 * upcall allocated the rbuf itself.  If this happens, assume
358d62bc4baSyz147064 		 * that this was an invalid door call request.
359d62bc4baSyz147064 		 */
360d62bc4baSyz147064 		kmem_free(darg.rbuf, darg.rsize);
361d62bc4baSyz147064 		err = ENOSPC;
362d62bc4baSyz147064 		goto done;
363d62bc4baSyz147064 	}
364d62bc4baSyz147064 
365024b0a25Sseb 	if (darg.rsize != rsize) {
366d62bc4baSyz147064 		err = EINVAL;
367d62bc4baSyz147064 		goto done;
368d62bc4baSyz147064 	}
369d62bc4baSyz147064 
370024b0a25Sseb 	err = ((dlmgmt_retval_t *)rbuf)->lr_err;
371d62bc4baSyz147064 
372d62bc4baSyz147064 done:
373d62bc4baSyz147064 	door_ki_rele(dh);
374d62bc4baSyz147064 	return (err);
375d62bc4baSyz147064 }
376d62bc4baSyz147064 
377d62bc4baSyz147064 /*
378d62bc4baSyz147064  * Request the datalink management daemon to create a link with the attributes
379d62bc4baSyz147064  * below.  Upon success, zero is returned and linkidp contains the linkid for
380d62bc4baSyz147064  * the new link; otherwise, an errno is returned.
381d62bc4baSyz147064  *
382d62bc4baSyz147064  *     - dev		physical dev_t.  required for all physical links,
383d62bc4baSyz147064  *		        including GLDv3 links.  It will be used to force the
384d62bc4baSyz147064  *		        attachment of a physical device, hence the
385d62bc4baSyz147064  *		        registration of its mac
386d62bc4baSyz147064  *     - class		datalink class
387d62bc4baSyz147064  *     - media type	media type; DL_OTHER means unknown
388d62bc4baSyz147064  *     - persist	whether to persist the datalink
389d62bc4baSyz147064  */
390d62bc4baSyz147064 int
391d62bc4baSyz147064 dls_mgmt_create(const char *devname, dev_t dev, datalink_class_t class,
392d62bc4baSyz147064     uint32_t media, boolean_t persist, datalink_id_t *linkidp)
393d62bc4baSyz147064 {
394d62bc4baSyz147064 	dlmgmt_upcall_arg_create_t	create;
395d62bc4baSyz147064 	dlmgmt_create_retval_t		retval;
396d62bc4baSyz147064 	int				err;
397d62bc4baSyz147064 
398d62bc4baSyz147064 	create.ld_cmd = DLMGMT_CMD_DLS_CREATE;
399d62bc4baSyz147064 	create.ld_class = class;
400d62bc4baSyz147064 	create.ld_media = media;
401d62bc4baSyz147064 	create.ld_phymaj = getmajor(dev);
402d62bc4baSyz147064 	create.ld_phyinst = getminor(dev);
403d62bc4baSyz147064 	create.ld_persist = persist;
4042b24ab6bSSebastien Roy 	if (strlcpy(create.ld_devname, devname, sizeof (create.ld_devname)) >=
4052b24ab6bSSebastien Roy 	    sizeof (create.ld_devname))
406d62bc4baSyz147064 		return (EINVAL);
407d62bc4baSyz147064 
408024b0a25Sseb 	if ((err = i_dls_mgmt_upcall(&create, sizeof (create), &retval,
409024b0a25Sseb 	    sizeof (retval))) == 0) {
410d62bc4baSyz147064 		*linkidp = retval.lr_linkid;
411024b0a25Sseb 	}
412d62bc4baSyz147064 	return (err);
413d62bc4baSyz147064 }
414d62bc4baSyz147064 
415d62bc4baSyz147064 /*
416d62bc4baSyz147064  * Request the datalink management daemon to destroy the specified link.
417d62bc4baSyz147064  * Returns zero upon success, or an errno upon failure.
418d62bc4baSyz147064  */
419d62bc4baSyz147064 int
420d62bc4baSyz147064 dls_mgmt_destroy(datalink_id_t linkid, boolean_t persist)
421d62bc4baSyz147064 {
422d62bc4baSyz147064 	dlmgmt_upcall_arg_destroy_t	destroy;
423d62bc4baSyz147064 	dlmgmt_destroy_retval_t		retval;
424d62bc4baSyz147064 
425d62bc4baSyz147064 	destroy.ld_cmd = DLMGMT_CMD_DLS_DESTROY;
426d62bc4baSyz147064 	destroy.ld_linkid = linkid;
427d62bc4baSyz147064 	destroy.ld_persist = persist;
428d62bc4baSyz147064 
429024b0a25Sseb 	return (i_dls_mgmt_upcall(&destroy, sizeof (destroy),
430024b0a25Sseb 	    &retval, sizeof (retval)));
431d62bc4baSyz147064 }
432d62bc4baSyz147064 
433d62bc4baSyz147064 /*
434d62bc4baSyz147064  * Request the datalink management daemon to verify/update the information
435d62bc4baSyz147064  * for a physical link.  Upon success, get its linkid.
436d62bc4baSyz147064  *
437d62bc4baSyz147064  *     - media type	media type
438d62bc4baSyz147064  *     - novanity	whether this physical datalink supports vanity naming.
439d62bc4baSyz147064  *			physical links that do not use the GLDv3 MAC plugin
440d62bc4baSyz147064  *			cannot suport vanity naming
441d62bc4baSyz147064  *
442d62bc4baSyz147064  * This function could fail with ENOENT or EEXIST.  Two cases return EEXIST:
443d62bc4baSyz147064  *
444d62bc4baSyz147064  * 1. A link with devname already exists, but the media type does not match.
445d62bc4baSyz147064  *    In this case, mediap will bee set to the media type of the existing link.
446d62bc4baSyz147064  * 2. A link with devname already exists, but its link name does not match
447d62bc4baSyz147064  *    the device name, although this link does not support vanity naming.
448d62bc4baSyz147064  */
449d62bc4baSyz147064 int
450d62bc4baSyz147064 dls_mgmt_update(const char *devname, uint32_t media, boolean_t novanity,
451d62bc4baSyz147064     uint32_t *mediap, datalink_id_t *linkidp)
452d62bc4baSyz147064 {
453d62bc4baSyz147064 	dlmgmt_upcall_arg_update_t	update;
454d62bc4baSyz147064 	dlmgmt_update_retval_t		retval;
455d62bc4baSyz147064 	int				err;
456d62bc4baSyz147064 
457d62bc4baSyz147064 	update.ld_cmd = DLMGMT_CMD_DLS_UPDATE;
458d62bc4baSyz147064 
4592b24ab6bSSebastien Roy 	if (strlcpy(update.ld_devname, devname, sizeof (update.ld_devname)) >=
4602b24ab6bSSebastien Roy 	    sizeof (update.ld_devname))
461d62bc4baSyz147064 		return (EINVAL);
462d62bc4baSyz147064 
463d62bc4baSyz147064 	update.ld_media = media;
464d62bc4baSyz147064 	update.ld_novanity = novanity;
465d62bc4baSyz147064 
466024b0a25Sseb 	if ((err = i_dls_mgmt_upcall(&update, sizeof (update), &retval,
467024b0a25Sseb 	    sizeof (retval))) == EEXIST) {
468d62bc4baSyz147064 		*linkidp = retval.lr_linkid;
469d62bc4baSyz147064 		*mediap = retval.lr_media;
470d62bc4baSyz147064 	} else if (err == 0) {
471d62bc4baSyz147064 		*linkidp = retval.lr_linkid;
472d62bc4baSyz147064 	}
473d62bc4baSyz147064 
474d62bc4baSyz147064 	return (err);
475d62bc4baSyz147064 }
476d62bc4baSyz147064 
477d62bc4baSyz147064 /*
478d62bc4baSyz147064  * Request the datalink management daemon to get the information for a link.
479d62bc4baSyz147064  * Returns zero upon success, or an errno upon failure.
480d62bc4baSyz147064  *
481d62bc4baSyz147064  * Only fills in information for argument pointers that are non-NULL.
482d62bc4baSyz147064  * Note that the link argument is expected to be MAXLINKNAMELEN bytes.
483d62bc4baSyz147064  */
484d62bc4baSyz147064 int
485d62bc4baSyz147064 dls_mgmt_get_linkinfo(datalink_id_t linkid, char *link,
486d62bc4baSyz147064     datalink_class_t *classp, uint32_t *mediap, uint32_t *flagsp)
487d62bc4baSyz147064 {
488d62bc4baSyz147064 	dlmgmt_door_getname_t	getname;
489d62bc4baSyz147064 	dlmgmt_getname_retval_t	retval;
490d62bc4baSyz147064 	int			err, len;
491d62bc4baSyz147064 
492d62bc4baSyz147064 	getname.ld_cmd = DLMGMT_CMD_GETNAME;
493d62bc4baSyz147064 	getname.ld_linkid = linkid;
494d62bc4baSyz147064 
495024b0a25Sseb 	if ((err = i_dls_mgmt_upcall(&getname, sizeof (getname), &retval,
496024b0a25Sseb 	    sizeof (retval))) != 0) {
497d62bc4baSyz147064 		return (err);
498024b0a25Sseb 	}
499d62bc4baSyz147064 
500d62bc4baSyz147064 	len = strlen(retval.lr_link);
501d62bc4baSyz147064 	if (len <= 1 || len >= MAXLINKNAMELEN)
502d62bc4baSyz147064 		return (EINVAL);
503d62bc4baSyz147064 
504d62bc4baSyz147064 	if (link != NULL)
505d62bc4baSyz147064 		(void) strlcpy(link, retval.lr_link, MAXLINKNAMELEN);
506d62bc4baSyz147064 	if (classp != NULL)
507d62bc4baSyz147064 		*classp = retval.lr_class;
508d62bc4baSyz147064 	if (mediap != NULL)
509d62bc4baSyz147064 		*mediap = retval.lr_media;
510d62bc4baSyz147064 	if (flagsp != NULL)
511d62bc4baSyz147064 		*flagsp = retval.lr_flags;
512d62bc4baSyz147064 	return (0);
513d62bc4baSyz147064 }
514d62bc4baSyz147064 
515d62bc4baSyz147064 /*
516d62bc4baSyz147064  * Request the datalink management daemon to get the linkid for a link.
517d62bc4baSyz147064  * Returns a non-zero error code on failure.  The linkid argument is only
518d62bc4baSyz147064  * set on success (when zero is returned.)
519d62bc4baSyz147064  */
520d62bc4baSyz147064 int
521d62bc4baSyz147064 dls_mgmt_get_linkid(const char *link, datalink_id_t *linkid)
522d62bc4baSyz147064 {
523d62bc4baSyz147064 	dlmgmt_door_getlinkid_t		getlinkid;
524d62bc4baSyz147064 	dlmgmt_getlinkid_retval_t	retval;
525d62bc4baSyz147064 	int				err;
526d62bc4baSyz147064 
527d62bc4baSyz147064 	getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID;
528d62bc4baSyz147064 	(void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN);
529d62bc4baSyz147064 
530024b0a25Sseb 	if ((err = i_dls_mgmt_upcall(&getlinkid, sizeof (getlinkid), &retval,
531024b0a25Sseb 	    sizeof (retval))) == 0) {
532d62bc4baSyz147064 		*linkid = retval.lr_linkid;
533024b0a25Sseb 	}
534d62bc4baSyz147064 	return (err);
535d62bc4baSyz147064 }
536d62bc4baSyz147064 
537d62bc4baSyz147064 datalink_id_t
538d62bc4baSyz147064 dls_mgmt_get_next(datalink_id_t linkid, datalink_class_t class,
539d62bc4baSyz147064     datalink_media_t dmedia, uint32_t flags)
540d62bc4baSyz147064 {
541d62bc4baSyz147064 	dlmgmt_door_getnext_t	getnext;
542d62bc4baSyz147064 	dlmgmt_getnext_retval_t	retval;
543d62bc4baSyz147064 
544d62bc4baSyz147064 	getnext.ld_cmd = DLMGMT_CMD_GETNEXT;
545d62bc4baSyz147064 	getnext.ld_class = class;
546d62bc4baSyz147064 	getnext.ld_dmedia = dmedia;
547d62bc4baSyz147064 	getnext.ld_flags = flags;
548d62bc4baSyz147064 	getnext.ld_linkid = linkid;
549d62bc4baSyz147064 
550024b0a25Sseb 	if (i_dls_mgmt_upcall(&getnext, sizeof (getnext), &retval,
551024b0a25Sseb 	    sizeof (retval)) != 0) {
552d62bc4baSyz147064 		return (DATALINK_INVALID_LINKID);
553024b0a25Sseb 	}
554d62bc4baSyz147064 
555d62bc4baSyz147064 	return (retval.lr_linkid);
556d62bc4baSyz147064 }
557d62bc4baSyz147064 
558d62bc4baSyz147064 static int
559d62bc4baSyz147064 i_dls_mgmt_get_linkattr(const datalink_id_t linkid, const char *attr,
560d62bc4baSyz147064     void *attrval, size_t *attrszp)
561d62bc4baSyz147064 {
562d62bc4baSyz147064 	dlmgmt_upcall_arg_getattr_t	getattr;
563024b0a25Sseb 	dlmgmt_getattr_retval_t		retval;
564d62bc4baSyz147064 	int				err;
565d62bc4baSyz147064 
566d62bc4baSyz147064 	getattr.ld_cmd = DLMGMT_CMD_DLS_GETATTR;
567d62bc4baSyz147064 	getattr.ld_linkid = linkid;
568d62bc4baSyz147064 	(void) strlcpy(getattr.ld_attr, attr, MAXLINKATTRLEN);
569d62bc4baSyz147064 
570024b0a25Sseb 	if ((err = i_dls_mgmt_upcall(&getattr, sizeof (getattr), &retval,
571024b0a25Sseb 	    sizeof (retval))) == 0) {
572024b0a25Sseb 		if (*attrszp < retval.lr_attrsz)
573024b0a25Sseb 			return (EINVAL);
574024b0a25Sseb 		*attrszp = retval.lr_attrsz;
575024b0a25Sseb 		bcopy(retval.lr_attrval, attrval, retval.lr_attrsz);
576d62bc4baSyz147064 	}
577d62bc4baSyz147064 
578d62bc4baSyz147064 	return (err);
579d62bc4baSyz147064 }
580d62bc4baSyz147064 
581d62bc4baSyz147064 /*
582d62bc4baSyz147064  * Note that this function can only get devp successfully for non-VLAN link.
583d62bc4baSyz147064  */
584d62bc4baSyz147064 int
585d62bc4baSyz147064 dls_mgmt_get_phydev(datalink_id_t linkid, dev_t *devp)
586d62bc4baSyz147064 {
587d62bc4baSyz147064 	uint64_t	maj, inst;
588d62bc4baSyz147064 	size_t		attrsz = sizeof (uint64_t);
589d62bc4baSyz147064 
590d62bc4baSyz147064 	if (i_dls_mgmt_get_linkattr(linkid, FPHYMAJ, &maj, &attrsz) != 0 ||
591d62bc4baSyz147064 	    attrsz != sizeof (uint64_t) ||
592d62bc4baSyz147064 	    i_dls_mgmt_get_linkattr(linkid, FPHYINST, &inst, &attrsz) != 0 ||
593d62bc4baSyz147064 	    attrsz != sizeof (uint64_t)) {
594d62bc4baSyz147064 		return (EINVAL);
595d62bc4baSyz147064 	}
596d62bc4baSyz147064 
597d62bc4baSyz147064 	*devp = makedevice((major_t)maj, (minor_t)inst);
598d62bc4baSyz147064 	return (0);
599d62bc4baSyz147064 }
600d62bc4baSyz147064 
601d62bc4baSyz147064 /*
60230890389Sartem  * Request the datalink management daemon to push in
60330890389Sartem  * all properties associated with the link.
60430890389Sartem  * Returns a non-zero error code on failure.
60530890389Sartem  */
60630890389Sartem int
60730890389Sartem dls_mgmt_linkprop_init(datalink_id_t linkid)
60830890389Sartem {
60930890389Sartem 	dlmgmt_door_linkprop_init_t	li;
61030890389Sartem 	dlmgmt_linkprop_init_retval_t	retval;
61130890389Sartem 	int				err;
61230890389Sartem 
61330890389Sartem 	li.ld_cmd = DLMGMT_CMD_LINKPROP_INIT;
61430890389Sartem 	li.ld_linkid = linkid;
61530890389Sartem 
61630890389Sartem 	err = i_dls_mgmt_upcall(&li, sizeof (li), &retval, sizeof (retval));
61730890389Sartem 	return (err);
61830890389Sartem }
61930890389Sartem 
62030890389Sartem static void
62130890389Sartem dls_devnet_prop_task(void *arg)
62230890389Sartem {
62330890389Sartem 	dls_devnet_t		*ddp = arg;
62430890389Sartem 
625da14cebeSEric Cheng 	(void) dls_mgmt_linkprop_init(ddp->dd_linkid);
62630890389Sartem 
62730890389Sartem 	mutex_enter(&ddp->dd_mutex);
62830890389Sartem 	ddp->dd_prop_loaded = B_TRUE;
62930890389Sartem 	ddp->dd_prop_taskid = NULL;
63030890389Sartem 	cv_broadcast(&ddp->dd_cv);
63130890389Sartem 	mutex_exit(&ddp->dd_mutex);
63230890389Sartem }
63330890389Sartem 
63430890389Sartem /*
63530890389Sartem  * Ensure property loading task is completed.
63630890389Sartem  */
63730890389Sartem void
63830890389Sartem dls_devnet_prop_task_wait(dls_dl_handle_t ddp)
63930890389Sartem {
64030890389Sartem 	mutex_enter(&ddp->dd_mutex);
64130890389Sartem 	while (ddp->dd_prop_taskid != NULL)
64230890389Sartem 		cv_wait(&ddp->dd_cv, &ddp->dd_mutex);
64330890389Sartem 	mutex_exit(&ddp->dd_mutex);
64430890389Sartem }
64530890389Sartem 
646d62bc4baSyz147064 void
647d62bc4baSyz147064 dls_devnet_rele_tmp(dls_dl_handle_t dlh)
648d62bc4baSyz147064 {
649d62bc4baSyz147064 	dls_devnet_t		*ddp = dlh;
650d62bc4baSyz147064 
651d62bc4baSyz147064 	mutex_enter(&ddp->dd_mutex);
652d62bc4baSyz147064 	ASSERT(ddp->dd_tref != 0);
653d62bc4baSyz147064 	if (--ddp->dd_tref == 0)
654d62bc4baSyz147064 		cv_signal(&ddp->dd_cv);
655d62bc4baSyz147064 	mutex_exit(&ddp->dd_mutex);
656d62bc4baSyz147064 }
657d62bc4baSyz147064 
658da14cebeSEric Cheng int
659da14cebeSEric Cheng dls_devnet_hold_link(datalink_id_t linkid, dls_dl_handle_t *ddhp,
660da14cebeSEric Cheng     dls_link_t **dlpp)
661da14cebeSEric Cheng {
662da14cebeSEric Cheng 	dls_dl_handle_t	dlh;
663da14cebeSEric Cheng 	dls_link_t	*dlp;
664da14cebeSEric Cheng 	int		err;
665da14cebeSEric Cheng 
666da14cebeSEric Cheng 	if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0)
667da14cebeSEric Cheng 		return (err);
668da14cebeSEric Cheng 
669da14cebeSEric Cheng 	if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0) {
670da14cebeSEric Cheng 		dls_devnet_rele_tmp(dlh);
671da14cebeSEric Cheng 		return (err);
672da14cebeSEric Cheng 	}
673da14cebeSEric Cheng 
674da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dlp->dl_mh));
675da14cebeSEric Cheng 
676da14cebeSEric Cheng 	*ddhp = dlh;
677da14cebeSEric Cheng 	*dlpp = dlp;
678da14cebeSEric Cheng 	return (0);
679da14cebeSEric Cheng }
680da14cebeSEric Cheng 
681da14cebeSEric Cheng void
682da14cebeSEric Cheng dls_devnet_rele_link(dls_dl_handle_t dlh, dls_link_t *dlp)
683da14cebeSEric Cheng {
684da14cebeSEric Cheng 	ASSERT(MAC_PERIM_HELD(dlp->dl_mh));
685da14cebeSEric Cheng 
686da14cebeSEric Cheng 	dls_link_rele(dlp);
687da14cebeSEric Cheng 	dls_devnet_rele_tmp(dlh);
688da14cebeSEric Cheng }
689da14cebeSEric Cheng 
690d62bc4baSyz147064 /*
691d62bc4baSyz147064  * "link" kstats related functions.
692d62bc4baSyz147064  */
693d62bc4baSyz147064 
694d62bc4baSyz147064 /*
695d62bc4baSyz147064  * Query the "link" kstats.
696ae6aa22aSVenugopal Iyer  *
697ae6aa22aSVenugopal Iyer  * We may be called from the kstat subsystem in an arbitrary context.
698ae6aa22aSVenugopal Iyer  * If the caller is the stack, the context could be an upcall data
699ae6aa22aSVenugopal Iyer  * thread. Hence we can't acquire the mac perimeter in this function
700ae6aa22aSVenugopal Iyer  * for fear of deadlock.
701d62bc4baSyz147064  */
702d62bc4baSyz147064 static int
703d62bc4baSyz147064 dls_devnet_stat_update(kstat_t *ksp, int rw)
704d62bc4baSyz147064 {
705d62bc4baSyz147064 	dls_devnet_t	*ddp = ksp->ks_private;
706da14cebeSEric Cheng 	dls_link_t	*dlp;
707d62bc4baSyz147064 	int		err;
708d62bc4baSyz147064 
709ae6aa22aSVenugopal Iyer 	/*
710ae6aa22aSVenugopal Iyer 	 * Check the link is being renamed or if the link is going away
711ae6aa22aSVenugopal Iyer 	 * before incrementing dd_tref which in turn prevents the link
712ae6aa22aSVenugopal Iyer 	 * from being renamed or deleted until we finish.
713ae6aa22aSVenugopal Iyer 	 */
714ae6aa22aSVenugopal Iyer 	mutex_enter(&ddp->dd_mutex);
715ae6aa22aSVenugopal Iyer 	if (ddp->dd_flags & (DD_CONDEMNED | DD_KSTAT_CHANGING)) {
716ae6aa22aSVenugopal Iyer 		mutex_exit(&ddp->dd_mutex);
717ae6aa22aSVenugopal Iyer 		return (ENOENT);
718ae6aa22aSVenugopal Iyer 	}
719ae6aa22aSVenugopal Iyer 	ddp->dd_tref++;
720ae6aa22aSVenugopal Iyer 	mutex_exit(&ddp->dd_mutex);
721d62bc4baSyz147064 
722ae6aa22aSVenugopal Iyer 	/*
723ae6aa22aSVenugopal Iyer 	 * If a device detach happens at this time, it will block in
724ae6aa22aSVenugopal Iyer 	 * dls_devnet_unset since the dd_tref has been bumped up above. So the
725ae6aa22aSVenugopal Iyer 	 * access to 'dlp' is safe even though we don't hold the mac perimeter.
726ae6aa22aSVenugopal Iyer 	 */
727ae6aa22aSVenugopal Iyer 	if (mod_hash_find(i_dls_link_hash, (mod_hash_key_t)ddp->dd_mac,
728ae6aa22aSVenugopal Iyer 	    (mod_hash_val_t *)&dlp) != 0) {
729ae6aa22aSVenugopal Iyer 		dls_devnet_rele_tmp(ddp);
730ae6aa22aSVenugopal Iyer 		return (ENOENT);
731da14cebeSEric Cheng 	}
732da14cebeSEric Cheng 
733da14cebeSEric Cheng 	err = dls_stat_update(ksp, dlp, rw);
734ae6aa22aSVenugopal Iyer 
735ae6aa22aSVenugopal Iyer 	dls_devnet_rele_tmp(ddp);
736d62bc4baSyz147064 	return (err);
737d62bc4baSyz147064 }
738d62bc4baSyz147064 
739d62bc4baSyz147064 /*
740d62bc4baSyz147064  * Create the "link" kstats.
741d62bc4baSyz147064  */
742d62bc4baSyz147064 static void
7432b24ab6bSSebastien Roy dls_devnet_stat_create(dls_devnet_t *ddp, zoneid_t zoneid)
744d62bc4baSyz147064 {
745d62bc4baSyz147064 	kstat_t	*ksp;
746d62bc4baSyz147064 
7472b24ab6bSSebastien Roy 	if (dls_stat_create("link", 0, ddp->dd_linkname, zoneid,
7482b24ab6bSSebastien Roy 	    dls_devnet_stat_update, ddp, &ksp) == 0) {
749d62bc4baSyz147064 		ASSERT(ksp != NULL);
7502b24ab6bSSebastien Roy 		if (zoneid == ddp->dd_owner_zid) {
7512b24ab6bSSebastien Roy 			ASSERT(ddp->dd_ksp == NULL);
752d62bc4baSyz147064 			ddp->dd_ksp = ksp;
7532b24ab6bSSebastien Roy 		} else {
7542b24ab6bSSebastien Roy 			ASSERT(ddp->dd_zone_ksp == NULL);
7552b24ab6bSSebastien Roy 			ddp->dd_zone_ksp = ksp;
7562b24ab6bSSebastien Roy 		}
7572b24ab6bSSebastien Roy 	}
758d62bc4baSyz147064 }
759d62bc4baSyz147064 
760d62bc4baSyz147064 /*
761d62bc4baSyz147064  * Destroy the "link" kstats.
762d62bc4baSyz147064  */
763d62bc4baSyz147064 static void
7642b24ab6bSSebastien Roy dls_devnet_stat_destroy(dls_devnet_t *ddp, zoneid_t zoneid)
765d62bc4baSyz147064 {
7662b24ab6bSSebastien Roy 	if (zoneid == ddp->dd_owner_zid) {
7672b24ab6bSSebastien Roy 		if (ddp->dd_ksp != NULL) {
768d62bc4baSyz147064 			kstat_delete(ddp->dd_ksp);
769d62bc4baSyz147064 			ddp->dd_ksp = NULL;
770d62bc4baSyz147064 		}
7712b24ab6bSSebastien Roy 	} else {
7722b24ab6bSSebastien Roy 		if (ddp->dd_zone_ksp != NULL) {
7732b24ab6bSSebastien Roy 			kstat_delete(ddp->dd_zone_ksp);
7742b24ab6bSSebastien Roy 			ddp->dd_zone_ksp = NULL;
7752b24ab6bSSebastien Roy 		}
7762b24ab6bSSebastien Roy 	}
7772b24ab6bSSebastien Roy }
778d62bc4baSyz147064 
779d62bc4baSyz147064 /*
780d62bc4baSyz147064  * The link has been renamed. Destroy the old non-legacy kstats ("link kstats")
781d62bc4baSyz147064  * and create the new set using the new name.
782d62bc4baSyz147064  */
783d62bc4baSyz147064 static void
7842b24ab6bSSebastien Roy dls_devnet_stat_rename(dls_devnet_t *ddp)
785d62bc4baSyz147064 {
786d62bc4baSyz147064 	if (ddp->dd_ksp != NULL) {
787d62bc4baSyz147064 		kstat_delete(ddp->dd_ksp);
788d62bc4baSyz147064 		ddp->dd_ksp = NULL;
789d62bc4baSyz147064 	}
7902b24ab6bSSebastien Roy 	/* We can't rename a link while it's assigned to a non-global zone. */
7912b24ab6bSSebastien Roy 	ASSERT(ddp->dd_zone_ksp == NULL);
7922b24ab6bSSebastien Roy 	dls_devnet_stat_create(ddp, ddp->dd_owner_zid);
793d62bc4baSyz147064 }
794d62bc4baSyz147064 
795d62bc4baSyz147064 /*
796da14cebeSEric Cheng  * Associate a linkid with a given link (identified by macname)
797d62bc4baSyz147064  */
798d62bc4baSyz147064 static int
7992b24ab6bSSebastien Roy dls_devnet_set(const char *macname, datalink_id_t linkid, zoneid_t zoneid,
8002b24ab6bSSebastien Roy     dls_devnet_t **ddpp)
801d62bc4baSyz147064 {
802d62bc4baSyz147064 	dls_devnet_t		*ddp = NULL;
803d62bc4baSyz147064 	datalink_class_t	class;
804d62bc4baSyz147064 	int			err;
805ae6aa22aSVenugopal Iyer 	boolean_t		stat_create = B_FALSE;
8062b24ab6bSSebastien Roy 	char			linkname[MAXLINKNAMELEN];
807d62bc4baSyz147064 
808d62bc4baSyz147064 	rw_enter(&i_dls_devnet_lock, RW_WRITER);
8092b24ab6bSSebastien Roy 
8102b24ab6bSSebastien Roy 	/*
8112b24ab6bSSebastien Roy 	 * Don't allow callers to set a link name with a linkid that already
8122b24ab6bSSebastien Roy 	 * has a name association (that's what rename is for).
8132b24ab6bSSebastien Roy 	 */
8142b24ab6bSSebastien Roy 	if (linkid != DATALINK_INVALID_LINKID) {
8152b24ab6bSSebastien Roy 		if (mod_hash_find(i_dls_devnet_id_hash,
8162b24ab6bSSebastien Roy 		    (mod_hash_key_t)(uintptr_t)linkid,
8172b24ab6bSSebastien Roy 		    (mod_hash_val_t *)&ddp) == 0) {
8182b24ab6bSSebastien Roy 			err = EEXIST;
8192b24ab6bSSebastien Roy 			goto done;
8202b24ab6bSSebastien Roy 		}
8212b24ab6bSSebastien Roy 		if ((err = dls_mgmt_get_linkinfo(linkid, linkname, &class,
8222b24ab6bSSebastien Roy 		    NULL, NULL)) != 0)
8232b24ab6bSSebastien Roy 			goto done;
8242b24ab6bSSebastien Roy 	}
8252b24ab6bSSebastien Roy 
826d62bc4baSyz147064 	if ((err = mod_hash_find(i_dls_devnet_hash,
827da14cebeSEric Cheng 	    (mod_hash_key_t)macname, (mod_hash_val_t *)&ddp)) == 0) {
828da14cebeSEric Cheng 		if (ddp->dd_linkid != DATALINK_INVALID_LINKID) {
829d62bc4baSyz147064 			err = EEXIST;
830d62bc4baSyz147064 			goto done;
831d62bc4baSyz147064 		}
832d62bc4baSyz147064 
833d62bc4baSyz147064 		/*
834d62bc4baSyz147064 		 * This might be a physical link that has already
835da14cebeSEric Cheng 		 * been created, but which does not have a linkid
836d62bc4baSyz147064 		 * because dlmgmtd was not running when it was created.
837d62bc4baSyz147064 		 */
8382b24ab6bSSebastien Roy 		if (linkid == DATALINK_INVALID_LINKID ||
8392b24ab6bSSebastien Roy 		    class != DATALINK_CLASS_PHYS) {
840d62bc4baSyz147064 			err = EINVAL;
841d62bc4baSyz147064 			goto done;
842d62bc4baSyz147064 		}
8432b24ab6bSSebastien Roy 	} else {
844d62bc4baSyz147064 		ddp = kmem_cache_alloc(i_dls_devnet_cachep, KM_SLEEP);
845d62bc4baSyz147064 		ddp->dd_tref = 0;
846d62bc4baSyz147064 		ddp->dd_ref++;
8472b24ab6bSSebastien Roy 		ddp->dd_owner_zid = zoneid;
8482b24ab6bSSebastien Roy 		(void) strlcpy(ddp->dd_mac, macname, sizeof (ddp->dd_mac));
849d62bc4baSyz147064 		VERIFY(mod_hash_insert(i_dls_devnet_hash,
850da14cebeSEric Cheng 		    (mod_hash_key_t)ddp->dd_mac, (mod_hash_val_t)ddp) == 0);
8512b24ab6bSSebastien Roy 	}
852d62bc4baSyz147064 
853da14cebeSEric Cheng 	if (linkid != DATALINK_INVALID_LINKID) {
854d62bc4baSyz147064 		ddp->dd_linkid = linkid;
8552b24ab6bSSebastien Roy 		(void) strlcpy(ddp->dd_linkname, linkname,
8562b24ab6bSSebastien Roy 		    sizeof (ddp->dd_linkname));
857d62bc4baSyz147064 		VERIFY(mod_hash_insert(i_dls_devnet_id_hash,
858da14cebeSEric Cheng 		    (mod_hash_key_t)(uintptr_t)linkid,
859d62bc4baSyz147064 		    (mod_hash_val_t)ddp) == 0);
860d62bc4baSyz147064 		devnet_need_rebuild = B_TRUE;
861ae6aa22aSVenugopal Iyer 		stat_create = B_TRUE;
86230890389Sartem 		mutex_enter(&ddp->dd_mutex);
86330890389Sartem 		if (!ddp->dd_prop_loaded && (ddp->dd_prop_taskid == NULL)) {
86430890389Sartem 			ddp->dd_prop_taskid = taskq_dispatch(system_taskq,
86530890389Sartem 			    dls_devnet_prop_task, ddp, TQ_SLEEP);
866d62bc4baSyz147064 		}
86730890389Sartem 		mutex_exit(&ddp->dd_mutex);
86830890389Sartem 	}
869d62bc4baSyz147064 	err = 0;
870d62bc4baSyz147064 done:
871ae6aa22aSVenugopal Iyer 	/*
872ae6aa22aSVenugopal Iyer 	 * It is safe to drop the i_dls_devnet_lock at this point. In the case
873ae6aa22aSVenugopal Iyer 	 * of physical devices, the softmac framework will fail the device
874ae6aa22aSVenugopal Iyer 	 * detach based on the smac_state or smac_hold_cnt. Other cases like
875ae6aa22aSVenugopal Iyer 	 * vnic and aggr use their own scheme to serialize creates and deletes
876ae6aa22aSVenugopal Iyer 	 * and ensure that *ddp is valid.
8772b24ab6bSSebastien Roy 	 */
8782b24ab6bSSebastien Roy 	rw_exit(&i_dls_devnet_lock);
8792b24ab6bSSebastien Roy 	if (err == 0) {
8802b24ab6bSSebastien Roy 		if (zoneid != GLOBAL_ZONEID &&
8812b24ab6bSSebastien Roy 		    (err = i_dls_devnet_setzid(ddp, zoneid, B_FALSE)) != 0)
8822b24ab6bSSebastien Roy 			(void) dls_devnet_unset(macname, &linkid, B_TRUE);
8832b24ab6bSSebastien Roy 		/*
8842b24ab6bSSebastien Roy 		 * The kstat subsystem holds its own locks (rather perimeter)
8852b24ab6bSSebastien Roy 		 * before calling the ks_update (dls_devnet_stat_update) entry
8862b24ab6bSSebastien Roy 		 * point which in turn grabs the i_dls_devnet_lock. So the
8872b24ab6bSSebastien Roy 		 * lock hierarchy is kstat locks -> i_dls_devnet_lock.
888ae6aa22aSVenugopal Iyer 		 */
889ae6aa22aSVenugopal Iyer 		if (stat_create)
8902b24ab6bSSebastien Roy 			dls_devnet_stat_create(ddp, zoneid);
8912b24ab6bSSebastien Roy 		if (ddpp != NULL)
892d62bc4baSyz147064 			*ddpp = ddp;
8932b24ab6bSSebastien Roy 	}
894d62bc4baSyz147064 	return (err);
895d62bc4baSyz147064 }
896d62bc4baSyz147064 
897d62bc4baSyz147064 /*
898da14cebeSEric Cheng  * Disassociate a linkid with a given link (identified by macname)
899da14cebeSEric Cheng  * This waits until temporary references to the dls_devnet_t are gone.
900d62bc4baSyz147064  */
901d62bc4baSyz147064 static int
902da14cebeSEric Cheng dls_devnet_unset(const char *macname, datalink_id_t *id, boolean_t wait)
903d62bc4baSyz147064 {
904d62bc4baSyz147064 	dls_devnet_t	*ddp;
905d62bc4baSyz147064 	int		err;
906da14cebeSEric Cheng 	mod_hash_val_t	val;
907d62bc4baSyz147064 
908d62bc4baSyz147064 	rw_enter(&i_dls_devnet_lock, RW_WRITER);
909d62bc4baSyz147064 	if ((err = mod_hash_find(i_dls_devnet_hash,
910da14cebeSEric Cheng 	    (mod_hash_key_t)macname, (mod_hash_val_t *)&ddp)) != 0) {
911d62bc4baSyz147064 		ASSERT(err == MH_ERR_NOTFOUND);
912d62bc4baSyz147064 		rw_exit(&i_dls_devnet_lock);
913d62bc4baSyz147064 		return (ENOENT);
914d62bc4baSyz147064 	}
915d62bc4baSyz147064 
916da14cebeSEric Cheng 	mutex_enter(&ddp->dd_mutex);
917d62bc4baSyz147064 
918da14cebeSEric Cheng 	/*
919da14cebeSEric Cheng 	 * Make sure downcalls into softmac_create or softmac_destroy from
920da14cebeSEric Cheng 	 * devfs don't cv_wait on any devfs related condition for fear of
921da14cebeSEric Cheng 	 * deadlock. Return EBUSY if the asynchronous thread started for
922da14cebeSEric Cheng 	 * property loading as part of the post attach hasn't yet completed.
923da14cebeSEric Cheng 	 */
924da14cebeSEric Cheng 	ASSERT(ddp->dd_ref != 0);
925da14cebeSEric Cheng 	if ((ddp->dd_ref != 1) || (!wait &&
926da14cebeSEric Cheng 	    (ddp->dd_tref != 0 || ddp->dd_prop_taskid != NULL))) {
927da14cebeSEric Cheng 		mutex_exit(&ddp->dd_mutex);
928d62bc4baSyz147064 		rw_exit(&i_dls_devnet_lock);
929d62bc4baSyz147064 		return (EBUSY);
930d62bc4baSyz147064 	}
931d62bc4baSyz147064 
932da14cebeSEric Cheng 	ddp->dd_flags |= DD_CONDEMNED;
933d62bc4baSyz147064 	ddp->dd_ref--;
934da14cebeSEric Cheng 	*id = ddp->dd_linkid;
935d62bc4baSyz147064 
9362b24ab6bSSebastien Roy 	if (ddp->dd_zid != GLOBAL_ZONEID)
9372b24ab6bSSebastien Roy 		(void) i_dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE);
9382b24ab6bSSebastien Roy 
939da14cebeSEric Cheng 	/*
940da14cebeSEric Cheng 	 * Remove this dls_devnet_t from the hash table.
941da14cebeSEric Cheng 	 */
942da14cebeSEric Cheng 	VERIFY(mod_hash_remove(i_dls_devnet_hash,
943da14cebeSEric Cheng 	    (mod_hash_key_t)ddp->dd_mac, &val) == 0);
944d62bc4baSyz147064 
945da14cebeSEric Cheng 	if (ddp->dd_linkid != DATALINK_INVALID_LINKID) {
946da14cebeSEric Cheng 		VERIFY(mod_hash_remove(i_dls_devnet_id_hash,
947da14cebeSEric Cheng 		    (mod_hash_key_t)(uintptr_t)ddp->dd_linkid, &val) == 0);
948da14cebeSEric Cheng 
949da14cebeSEric Cheng 		devnet_need_rebuild = B_TRUE;
950da14cebeSEric Cheng 	}
951d62bc4baSyz147064 	rw_exit(&i_dls_devnet_lock);
952da14cebeSEric Cheng 
953da14cebeSEric Cheng 	if (wait) {
954da14cebeSEric Cheng 		/*
955da14cebeSEric Cheng 		 * Wait until all temporary references are released.
956da14cebeSEric Cheng 		 */
957da14cebeSEric Cheng 		while ((ddp->dd_tref != 0) || (ddp->dd_prop_taskid != NULL))
958da14cebeSEric Cheng 			cv_wait(&ddp->dd_cv, &ddp->dd_mutex);
959da14cebeSEric Cheng 	} else {
960da14cebeSEric Cheng 		ASSERT(ddp->dd_tref == 0 && ddp->dd_prop_taskid == NULL);
961da14cebeSEric Cheng 	}
962da14cebeSEric Cheng 
963ae6aa22aSVenugopal Iyer 	if (ddp->dd_linkid != DATALINK_INVALID_LINKID)
9642b24ab6bSSebastien Roy 		dls_devnet_stat_destroy(ddp, ddp->dd_owner_zid);
965ae6aa22aSVenugopal Iyer 
966da14cebeSEric Cheng 	ddp->dd_prop_loaded = B_FALSE;
967da14cebeSEric Cheng 	ddp->dd_linkid = DATALINK_INVALID_LINKID;
968da14cebeSEric Cheng 	ddp->dd_flags = 0;
969da14cebeSEric Cheng 	mutex_exit(&ddp->dd_mutex);
970da14cebeSEric Cheng 	kmem_cache_free(i_dls_devnet_cachep, ddp);
971da14cebeSEric Cheng 
972d62bc4baSyz147064 	return (0);
973d62bc4baSyz147064 }
974d62bc4baSyz147064 
975d62bc4baSyz147064 static int
976da14cebeSEric Cheng dls_devnet_hold_common(datalink_id_t linkid, dls_devnet_t **ddpp,
977da14cebeSEric Cheng     boolean_t tmp_hold)
978d62bc4baSyz147064 {
979d62bc4baSyz147064 	dls_devnet_t		*ddp;
980d62bc4baSyz147064 	dev_t			phydev = 0;
981d62bc4baSyz147064 	dls_dev_handle_t	ddh = NULL;
982d62bc4baSyz147064 	int			err;
983d62bc4baSyz147064 
984d62bc4baSyz147064 	/*
985d62bc4baSyz147064 	 * Hold this link to prevent it being detached in case of a
986d62bc4baSyz147064 	 * physical link.
987d62bc4baSyz147064 	 */
988d62bc4baSyz147064 	if (dls_mgmt_get_phydev(linkid, &phydev) == 0)
989d62bc4baSyz147064 		(void) softmac_hold_device(phydev, &ddh);
990d62bc4baSyz147064 
991d62bc4baSyz147064 	rw_enter(&i_dls_devnet_lock, RW_WRITER);
992d62bc4baSyz147064 	if ((err = mod_hash_find(i_dls_devnet_id_hash,
993d62bc4baSyz147064 	    (mod_hash_key_t)(uintptr_t)linkid, (mod_hash_val_t *)&ddp)) != 0) {
994d62bc4baSyz147064 		ASSERT(err == MH_ERR_NOTFOUND);
995d62bc4baSyz147064 		rw_exit(&i_dls_devnet_lock);
996d62bc4baSyz147064 		softmac_rele_device(ddh);
997d62bc4baSyz147064 		return (ENOENT);
998d62bc4baSyz147064 	}
999d62bc4baSyz147064 
1000da14cebeSEric Cheng 	mutex_enter(&ddp->dd_mutex);
1001d62bc4baSyz147064 	ASSERT(ddp->dd_ref > 0);
1002da14cebeSEric Cheng 	if (ddp->dd_flags & DD_CONDEMNED) {
1003da14cebeSEric Cheng 		mutex_exit(&ddp->dd_mutex);
1004d62bc4baSyz147064 		rw_exit(&i_dls_devnet_lock);
1005d62bc4baSyz147064 		softmac_rele_device(ddh);
1006da14cebeSEric Cheng 		return (ENOENT);
1007da14cebeSEric Cheng 	}
1008da14cebeSEric Cheng 	if (tmp_hold)
1009da14cebeSEric Cheng 		ddp->dd_tref++;
1010da14cebeSEric Cheng 	else
1011da14cebeSEric Cheng 		ddp->dd_ref++;
1012da14cebeSEric Cheng 	mutex_exit(&ddp->dd_mutex);
1013da14cebeSEric Cheng 	rw_exit(&i_dls_devnet_lock);
1014d62bc4baSyz147064 
1015da14cebeSEric Cheng 	softmac_rele_device(ddh);
1016da14cebeSEric Cheng 
1017d62bc4baSyz147064 	*ddpp = ddp;
1018d62bc4baSyz147064 	return (0);
1019d62bc4baSyz147064 }
1020d62bc4baSyz147064 
1021da14cebeSEric Cheng int
1022da14cebeSEric Cheng dls_devnet_hold(datalink_id_t linkid, dls_devnet_t **ddpp)
1023da14cebeSEric Cheng {
1024da14cebeSEric Cheng 	return (dls_devnet_hold_common(linkid, ddpp, B_FALSE));
1025da14cebeSEric Cheng }
1026da14cebeSEric Cheng 
1027da14cebeSEric Cheng /*
1028da14cebeSEric Cheng  * Hold the vanity naming structure (dls_devnet_t) temporarily.  The request to
1029da14cebeSEric Cheng  * delete the dls_devnet_t will wait until the temporary reference is released.
1030da14cebeSEric Cheng  */
1031da14cebeSEric Cheng int
1032da14cebeSEric Cheng dls_devnet_hold_tmp(datalink_id_t linkid, dls_devnet_t **ddpp)
1033da14cebeSEric Cheng {
1034da14cebeSEric Cheng 	return (dls_devnet_hold_common(linkid, ddpp, B_TRUE));
1035da14cebeSEric Cheng }
1036da14cebeSEric Cheng 
1037d62bc4baSyz147064 /*
1038d62bc4baSyz147064  * This funtion is called when a DLS client tries to open a device node.
1039d62bc4baSyz147064  * This dev_t could a result of a /dev/net node access (returned by
1040d62bc4baSyz147064  * devnet_create_rvp->dls_devnet_open()) or a direct /dev node access.
1041da14cebeSEric Cheng  * In both cases, this function bumps up the reference count of the
1042da14cebeSEric Cheng  * dls_devnet_t structure. The reference is held as long as the device node
1043da14cebeSEric Cheng  * is open. In the case of /dev/net while it is true that the initial reference
1044da14cebeSEric Cheng  * is held when the devnet_create_rvp->dls_devnet_open call happens, this
1045da14cebeSEric Cheng  * initial reference is released immediately in devnet_inactive_callback ->
1046da14cebeSEric Cheng  * dls_devnet_close(). (Note that devnet_inactive_callback() is called right
1047da14cebeSEric Cheng  * after dld_open completes, not when the /dev/net node is being closed).
1048da14cebeSEric Cheng  * To undo this function, call dls_devnet_rele()
1049d62bc4baSyz147064  */
1050d62bc4baSyz147064 int
1051da14cebeSEric Cheng dls_devnet_hold_by_dev(dev_t dev, dls_dl_handle_t *ddhp)
1052d62bc4baSyz147064 {
1053da14cebeSEric Cheng 	char			name[MAXNAMELEN];
1054da14cebeSEric Cheng 	char			*drv;
1055d62bc4baSyz147064 	dls_dev_handle_t	ddh = NULL;
1056d62bc4baSyz147064 	dls_devnet_t		*ddp;
1057d62bc4baSyz147064 	int			err;
1058d62bc4baSyz147064 
1059da14cebeSEric Cheng 	if ((drv = ddi_major_to_name(getmajor(dev))) == NULL)
1060da14cebeSEric Cheng 		return (EINVAL);
1061da14cebeSEric Cheng 
106261af1958SGarrett D'Amore 	(void) snprintf(name, sizeof (name), "%s%d", drv,
106361af1958SGarrett D'Amore 	    DLS_MINOR2INST(getminor(dev)));
1064da14cebeSEric Cheng 
1065d62bc4baSyz147064 	/*
1066d62bc4baSyz147064 	 * Hold this link to prevent it being detached in case of a
1067d62bc4baSyz147064 	 * GLDv3 physical link.
1068d62bc4baSyz147064 	 */
10693ade6e84SGarrett D'Amore 	if (DLS_MINOR2INST(getminor(dev)) <= DLS_MAX_PPA)
1070d62bc4baSyz147064 		(void) softmac_hold_device(dev, &ddh);
1071d62bc4baSyz147064 
1072d62bc4baSyz147064 	rw_enter(&i_dls_devnet_lock, RW_WRITER);
1073d62bc4baSyz147064 	if ((err = mod_hash_find(i_dls_devnet_hash,
1074da14cebeSEric Cheng 	    (mod_hash_key_t)name, (mod_hash_val_t *)&ddp)) != 0) {
1075d62bc4baSyz147064 		ASSERT(err == MH_ERR_NOTFOUND);
1076d62bc4baSyz147064 		rw_exit(&i_dls_devnet_lock);
1077da14cebeSEric Cheng 		softmac_rele_device(ddh);
1078da14cebeSEric Cheng 		return (ENOENT);
1079d62bc4baSyz147064 	}
1080da14cebeSEric Cheng 	mutex_enter(&ddp->dd_mutex);
1081d62bc4baSyz147064 	ASSERT(ddp->dd_ref > 0);
1082da14cebeSEric Cheng 	if (ddp->dd_flags & DD_CONDEMNED) {
1083da14cebeSEric Cheng 		mutex_exit(&ddp->dd_mutex);
1084d62bc4baSyz147064 		rw_exit(&i_dls_devnet_lock);
1085da14cebeSEric Cheng 		softmac_rele_device(ddh);
1086da14cebeSEric Cheng 		return (ENOENT);
1087da14cebeSEric Cheng 	}
1088da14cebeSEric Cheng 	ddp->dd_ref++;
1089da14cebeSEric Cheng 	mutex_exit(&ddp->dd_mutex);
1090da14cebeSEric Cheng 	rw_exit(&i_dls_devnet_lock);
1091da14cebeSEric Cheng 
1092da14cebeSEric Cheng 	softmac_rele_device(ddh);
1093da14cebeSEric Cheng 
1094d62bc4baSyz147064 	*ddhp = ddp;
1095d62bc4baSyz147064 	return (0);
1096d62bc4baSyz147064 }
1097d62bc4baSyz147064 
1098da14cebeSEric Cheng void
1099d62bc4baSyz147064 dls_devnet_rele(dls_devnet_t *ddp)
1100d62bc4baSyz147064 {
1101da14cebeSEric Cheng 	mutex_enter(&ddp->dd_mutex);
1102da14cebeSEric Cheng 	ASSERT(ddp->dd_ref > 1);
1103da14cebeSEric Cheng 	ddp->dd_ref--;
11042b24ab6bSSebastien Roy 	if ((ddp->dd_flags & DD_IMPLICIT_IPTUN) && ddp->dd_ref == 1) {
11052b24ab6bSSebastien Roy 		mutex_exit(&ddp->dd_mutex);
11062b24ab6bSSebastien Roy 		if (i_dls_devnet_destroy_iptun(ddp->dd_linkid) != 0)
11072b24ab6bSSebastien Roy 			ddp->dd_flags |= DD_IMPLICIT_IPTUN;
11082b24ab6bSSebastien Roy 		return;
11092b24ab6bSSebastien Roy 	}
1110da14cebeSEric Cheng 	mutex_exit(&ddp->dd_mutex);
1111d62bc4baSyz147064 }
1112d62bc4baSyz147064 
1113d62bc4baSyz147064 static int
1114da14cebeSEric Cheng dls_devnet_hold_by_name(const char *link, dls_devnet_t **ddpp)
1115d62bc4baSyz147064 {
1116d62bc4baSyz147064 	char			drv[MAXLINKNAMELEN];
1117d62bc4baSyz147064 	uint_t			ppa;
1118d62bc4baSyz147064 	major_t			major;
1119d62bc4baSyz147064 	dev_t			phy_dev, tmp_dev;
1120d62bc4baSyz147064 	datalink_id_t		linkid;
1121d62bc4baSyz147064 	dls_dev_handle_t	ddh;
1122d62bc4baSyz147064 	int			err;
1123d62bc4baSyz147064 
1124d62bc4baSyz147064 	if ((err = dls_mgmt_get_linkid(link, &linkid)) == 0)
1125d62bc4baSyz147064 		return (dls_devnet_hold(linkid, ddpp));
1126d62bc4baSyz147064 
1127d62bc4baSyz147064 	/*
1128d62bc4baSyz147064 	 * If we failed to get the link's linkid because the dlmgmtd daemon
1129d62bc4baSyz147064 	 * has not been started, return ENOENT so that the application can
1130d62bc4baSyz147064 	 * fallback to open the /dev node.
1131d62bc4baSyz147064 	 */
1132d62bc4baSyz147064 	if (err == EBADF)
1133d62bc4baSyz147064 		return (ENOENT);
1134d62bc4baSyz147064 
1135d62bc4baSyz147064 	if (err != ENOENT)
1136d62bc4baSyz147064 		return (err);
1137d62bc4baSyz147064 
1138*ccdeb6b6SDan McDonald 	if (ddi_parse_dlen(link, drv, MAXLINKNAMELEN, &ppa) != DDI_SUCCESS)
1139d501bbfeSSebastien Roy 		return (ENOENT);
1140d501bbfeSSebastien Roy 
1141d501bbfeSSebastien Roy 	if (IS_IPTUN_LINK(drv)) {
1142d501bbfeSSebastien Roy 		if ((err = i_dls_devnet_create_iptun(link, drv, &linkid)) != 0)
11432b24ab6bSSebastien Roy 			return (err);
11442b24ab6bSSebastien Roy 		/*
11452b24ab6bSSebastien Roy 		 * At this point, an IP tunnel MAC has registered, which
11462b24ab6bSSebastien Roy 		 * resulted in a link being created.
11472b24ab6bSSebastien Roy 		 */
11482b24ab6bSSebastien Roy 		err = dls_devnet_hold(linkid, ddpp);
11492b24ab6bSSebastien Roy 		ASSERT(err == 0);
11502b24ab6bSSebastien Roy 		if (err != 0) {
11512b24ab6bSSebastien Roy 			VERIFY(i_dls_devnet_destroy_iptun(linkid) == 0);
11522b24ab6bSSebastien Roy 			return (err);
11532b24ab6bSSebastien Roy 		}
11542b24ab6bSSebastien Roy 		/*
11552b24ab6bSSebastien Roy 		 * dls_devnet_rele() will know to destroy the implicit IP
11562b24ab6bSSebastien Roy 		 * tunnel on last reference release if DD_IMPLICIT_IPTUN is
11572b24ab6bSSebastien Roy 		 * set.
11582b24ab6bSSebastien Roy 		 */
11592b24ab6bSSebastien Roy 		(*ddpp)->dd_flags |= DD_IMPLICIT_IPTUN;
11602b24ab6bSSebastien Roy 		return (0);
11612b24ab6bSSebastien Roy 	}
11622b24ab6bSSebastien Roy 
1163d62bc4baSyz147064 	/*
1164da14cebeSEric Cheng 	 * If this link:
1165d62bc4baSyz147064 	 * (a) is a physical device, (b) this is the first boot, (c) the MAC
1166d62bc4baSyz147064 	 * is not registered yet, and (d) we cannot find its linkid, then the
1167d62bc4baSyz147064 	 * linkname is the same as the devname.
1168d62bc4baSyz147064 	 *
1169d62bc4baSyz147064 	 * First filter out invalid names.
1170d62bc4baSyz147064 	 */
1171d62bc4baSyz147064 	if ((major = ddi_name_to_major(drv)) == (major_t)-1)
1172d62bc4baSyz147064 		return (ENOENT);
1173d62bc4baSyz147064 
117461af1958SGarrett D'Amore 	phy_dev = makedevice(major, DLS_PPA2MINOR(ppa));
1175d62bc4baSyz147064 	if (softmac_hold_device(phy_dev, &ddh) != 0)
1176d62bc4baSyz147064 		return (ENOENT);
1177d62bc4baSyz147064 
1178d62bc4baSyz147064 	/*
1179d62bc4baSyz147064 	 * At this time, the MAC should be registered, check its phy_dev using
1180d62bc4baSyz147064 	 * the given name.
1181d62bc4baSyz147064 	 */
1182da14cebeSEric Cheng 	if ((err = dls_mgmt_get_linkid(link, &linkid)) != 0 ||
1183d62bc4baSyz147064 	    (err = dls_mgmt_get_phydev(linkid, &tmp_dev)) != 0) {
1184d62bc4baSyz147064 		softmac_rele_device(ddh);
1185d62bc4baSyz147064 		return (err);
1186d62bc4baSyz147064 	}
1187d62bc4baSyz147064 	if (tmp_dev != phy_dev) {
1188d62bc4baSyz147064 		softmac_rele_device(ddh);
1189d62bc4baSyz147064 		return (ENOENT);
1190d62bc4baSyz147064 	}
1191d62bc4baSyz147064 
1192d62bc4baSyz147064 	err = dls_devnet_hold(linkid, ddpp);
1193d62bc4baSyz147064 	softmac_rele_device(ddh);
1194d62bc4baSyz147064 	return (err);
1195d62bc4baSyz147064 }
1196d62bc4baSyz147064 
1197da14cebeSEric Cheng int
1198da14cebeSEric Cheng dls_devnet_macname2linkid(const char *macname, datalink_id_t *linkidp)
1199da14cebeSEric Cheng {
1200da14cebeSEric Cheng 	dls_devnet_t	*ddp;
1201d62bc4baSyz147064 
1202da14cebeSEric Cheng 	rw_enter(&i_dls_devnet_lock, RW_READER);
1203da14cebeSEric Cheng 	if (mod_hash_find(i_dls_devnet_hash, (mod_hash_key_t)macname,
1204da14cebeSEric Cheng 	    (mod_hash_val_t *)&ddp) != 0) {
1205da14cebeSEric Cheng 		rw_exit(&i_dls_devnet_lock);
1206da14cebeSEric Cheng 		return (ENOENT);
1207d62bc4baSyz147064 	}
1208d62bc4baSyz147064 
1209da14cebeSEric Cheng 	*linkidp = ddp->dd_linkid;
1210da14cebeSEric Cheng 	rw_exit(&i_dls_devnet_lock);
1211da14cebeSEric Cheng 	return (0);
1212da14cebeSEric Cheng }
1213da14cebeSEric Cheng 
1214d62bc4baSyz147064 /*
1215d62bc4baSyz147064  * Get linkid for the given dev.
1216d62bc4baSyz147064  */
1217d62bc4baSyz147064 int
1218d62bc4baSyz147064 dls_devnet_dev2linkid(dev_t dev, datalink_id_t *linkidp)
1219d62bc4baSyz147064 {
1220da14cebeSEric Cheng 	char	macname[MAXNAMELEN];
1221da14cebeSEric Cheng 	char	*drv;
1222d62bc4baSyz147064 
1223da14cebeSEric Cheng 	if ((drv = ddi_major_to_name(getmajor(dev))) == NULL)
1224da14cebeSEric Cheng 		return (EINVAL);
1225d62bc4baSyz147064 
12262b24ab6bSSebastien Roy 	(void) snprintf(macname, sizeof (macname), "%s%d", drv,
122761af1958SGarrett D'Amore 	    DLS_MINOR2INST(getminor(dev)));
1228da14cebeSEric Cheng 	return (dls_devnet_macname2linkid(macname, linkidp));
1229d62bc4baSyz147064 }
1230d62bc4baSyz147064 
1231d62bc4baSyz147064 /*
1232d62bc4baSyz147064  * Get the link's physical dev_t. It this is a VLAN, get the dev_t of the
1233d62bc4baSyz147064  * link this VLAN is created on.
1234d62bc4baSyz147064  */
1235d62bc4baSyz147064 int
1236d62bc4baSyz147064 dls_devnet_phydev(datalink_id_t vlanid, dev_t *devp)
1237d62bc4baSyz147064 {
1238d62bc4baSyz147064 	dls_devnet_t	*ddp;
1239d62bc4baSyz147064 	int		err;
1240d62bc4baSyz147064 
1241d62bc4baSyz147064 	if ((err = dls_devnet_hold_tmp(vlanid, &ddp)) != 0)
1242d62bc4baSyz147064 		return (err);
1243d62bc4baSyz147064 
1244d62bc4baSyz147064 	err = dls_mgmt_get_phydev(ddp->dd_linkid, devp);
1245d62bc4baSyz147064 	dls_devnet_rele_tmp(ddp);
1246d62bc4baSyz147064 	return (err);
1247d62bc4baSyz147064 }
1248d62bc4baSyz147064 
1249d62bc4baSyz147064 /*
1250d62bc4baSyz147064  * Handle the renaming requests.  There are two rename cases:
1251d62bc4baSyz147064  *
1252d62bc4baSyz147064  * 1. Request to rename a valid link (id1) to an non-existent link name
1253d62bc4baSyz147064  *    (id2). In this case id2 is DATALINK_INVALID_LINKID.  Just check whether
1254d62bc4baSyz147064  *    id1 is held by any applications.
1255d62bc4baSyz147064  *
1256d62bc4baSyz147064  *    In this case, the link's kstats need to be updated using the given name.
1257d62bc4baSyz147064  *
1258d62bc4baSyz147064  * 2. Request to rename a valid link (id1) to the name of a REMOVED
125930890389Sartem  *    physical link (id2). In this case, check that id1 and its associated
1260d62bc4baSyz147064  *    mac is not held by any application, and update the link's linkid to id2.
1261d62bc4baSyz147064  *
1262d62bc4baSyz147064  *    This case does not change the <link name, linkid> mapping, so the link's
1263d62bc4baSyz147064  *    kstats need to be updated with using name associated the given id2.
1264d62bc4baSyz147064  */
1265d62bc4baSyz147064 int
1266d62bc4baSyz147064 dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
1267d62bc4baSyz147064 {
1268d62bc4baSyz147064 	dls_dev_handle_t	ddh = NULL;
1269d62bc4baSyz147064 	int			err = 0;
1270d62bc4baSyz147064 	dev_t			phydev = 0;
1271d62bc4baSyz147064 	dls_devnet_t		*ddp;
1272da14cebeSEric Cheng 	mac_perim_handle_t	mph = NULL;
1273d62bc4baSyz147064 	mac_handle_t		mh;
1274d62bc4baSyz147064 	mod_hash_val_t		val;
1275ae6aa22aSVenugopal Iyer 	boolean_t		clear_dd_flag = B_FALSE;
1276d62bc4baSyz147064 
1277d62bc4baSyz147064 	/*
1278d62bc4baSyz147064 	 * In the second case, id2 must be a REMOVED physical link.
1279d62bc4baSyz147064 	 */
1280d62bc4baSyz147064 	if ((id2 != DATALINK_INVALID_LINKID) &&
1281d62bc4baSyz147064 	    (dls_mgmt_get_phydev(id2, &phydev) == 0) &&
1282d62bc4baSyz147064 	    softmac_hold_device(phydev, &ddh) == 0) {
1283d62bc4baSyz147064 		softmac_rele_device(ddh);
1284d62bc4baSyz147064 		return (EEXIST);
1285d62bc4baSyz147064 	}
1286d62bc4baSyz147064 
1287d62bc4baSyz147064 	/*
1288d62bc4baSyz147064 	 * Hold id1 to prevent it from being detached (if a physical link).
1289d62bc4baSyz147064 	 */
1290d62bc4baSyz147064 	if (dls_mgmt_get_phydev(id1, &phydev) == 0)
1291d62bc4baSyz147064 		(void) softmac_hold_device(phydev, &ddh);
1292d62bc4baSyz147064 
1293da14cebeSEric Cheng 	/*
1294da14cebeSEric Cheng 	 * The framework does not hold hold locks across calls to the
1295da14cebeSEric Cheng 	 * mac perimeter, hence enter the perimeter first. This also waits
1296da14cebeSEric Cheng 	 * for the property loading to finish.
1297da14cebeSEric Cheng 	 */
1298ae6aa22aSVenugopal Iyer 	if ((err = mac_perim_enter_by_linkid(id1, &mph)) != 0) {
1299ae6aa22aSVenugopal Iyer 		softmac_rele_device(ddh);
1300ae6aa22aSVenugopal Iyer 		return (err);
1301ae6aa22aSVenugopal Iyer 	}
1302da14cebeSEric Cheng 
1303d62bc4baSyz147064 	rw_enter(&i_dls_devnet_lock, RW_WRITER);
1304d62bc4baSyz147064 	if ((err = mod_hash_find(i_dls_devnet_id_hash,
1305d62bc4baSyz147064 	    (mod_hash_key_t)(uintptr_t)id1, (mod_hash_val_t *)&ddp)) != 0) {
1306d62bc4baSyz147064 		ASSERT(err == MH_ERR_NOTFOUND);
1307d62bc4baSyz147064 		err = ENOENT;
1308d62bc4baSyz147064 		goto done;
1309d62bc4baSyz147064 	}
1310d62bc4baSyz147064 
1311d62bc4baSyz147064 	/*
13122b24ab6bSSebastien Roy 	 * Return EBUSY if any applications have this link open, if any thread
13132b24ab6bSSebastien Roy 	 * is currently accessing the link kstats, or if the link is on-loan
13142b24ab6bSSebastien Roy 	 * to a non-global zone. Then set the DD_KSTAT_CHANGING flag to
13152b24ab6bSSebastien Roy 	 * prevent any access to the kstats while we delete and recreate
13162b24ab6bSSebastien Roy 	 * kstats below.
1317d62bc4baSyz147064 	 */
1318ae6aa22aSVenugopal Iyer 	mutex_enter(&ddp->dd_mutex);
1319da14cebeSEric Cheng 	if (ddp->dd_ref > 1) {
1320ae6aa22aSVenugopal Iyer 		mutex_exit(&ddp->dd_mutex);
1321d62bc4baSyz147064 		err = EBUSY;
1322d62bc4baSyz147064 		goto done;
1323d62bc4baSyz147064 	}
1324d62bc4baSyz147064 
1325ae6aa22aSVenugopal Iyer 	ddp->dd_flags |= DD_KSTAT_CHANGING;
1326ae6aa22aSVenugopal Iyer 	clear_dd_flag = B_TRUE;
1327ae6aa22aSVenugopal Iyer 	mutex_exit(&ddp->dd_mutex);
1328ae6aa22aSVenugopal Iyer 
1329d62bc4baSyz147064 	if (id2 == DATALINK_INVALID_LINKID) {
13302b24ab6bSSebastien Roy 		(void) strlcpy(ddp->dd_linkname, link,
13312b24ab6bSSebastien Roy 		    sizeof (ddp->dd_linkname));
1332da14cebeSEric Cheng 
1333da14cebeSEric Cheng 		/* rename mac client name and its flow if exists */
1334da14cebeSEric Cheng 		if ((err = mac_open(ddp->dd_mac, &mh)) != 0)
1335da14cebeSEric Cheng 			goto done;
1336da14cebeSEric Cheng 		(void) mac_rename_primary(mh, link);
1337da14cebeSEric Cheng 		mac_close(mh);
1338d62bc4baSyz147064 		goto done;
1339d62bc4baSyz147064 	}
1340d62bc4baSyz147064 
1341d62bc4baSyz147064 	/*
1342d62bc4baSyz147064 	 * The second case, check whether the MAC is used by any MAC
1343d62bc4baSyz147064 	 * user.  This must be a physical link so ddh must not be NULL.
1344d62bc4baSyz147064 	 */
1345d62bc4baSyz147064 	if (ddh == NULL) {
1346d62bc4baSyz147064 		err = EINVAL;
1347d62bc4baSyz147064 		goto done;
1348d62bc4baSyz147064 	}
1349d62bc4baSyz147064 
1350d62bc4baSyz147064 	if ((err = mac_open(ddp->dd_mac, &mh)) != 0)
1351d62bc4baSyz147064 		goto done;
1352d62bc4baSyz147064 
1353d62bc4baSyz147064 	/*
1354d62bc4baSyz147064 	 * We release the reference of the MAC which mac_open() is
1355d62bc4baSyz147064 	 * holding. Note that this mac will not be unregistered
1356da14cebeSEric Cheng 	 * because the physical device is held.
1357d62bc4baSyz147064 	 */
1358d62bc4baSyz147064 	mac_close(mh);
1359d62bc4baSyz147064 
1360d62bc4baSyz147064 	/*
1361d62bc4baSyz147064 	 * Check if there is any other MAC clients, if not, hold this mac
1362d62bc4baSyz147064 	 * exclusively until we are done.
1363d62bc4baSyz147064 	 */
1364da14cebeSEric Cheng 	if ((err = mac_mark_exclusive(mh)) != 0)
1365d62bc4baSyz147064 		goto done;
1366d62bc4baSyz147064 
1367d62bc4baSyz147064 	/*
1368d62bc4baSyz147064 	 * Update the link's linkid.
1369d62bc4baSyz147064 	 */
1370d62bc4baSyz147064 	if ((err = mod_hash_find(i_dls_devnet_id_hash,
1371d62bc4baSyz147064 	    (mod_hash_key_t)(uintptr_t)id2, &val)) != MH_ERR_NOTFOUND) {
1372da14cebeSEric Cheng 		mac_unmark_exclusive(mh);
1373d62bc4baSyz147064 		err = EEXIST;
1374d62bc4baSyz147064 		goto done;
1375d62bc4baSyz147064 	}
1376d62bc4baSyz147064 
13772b24ab6bSSebastien Roy 	err = dls_mgmt_get_linkinfo(id2, ddp->dd_linkname, NULL, NULL, NULL);
1378d62bc4baSyz147064 	if (err != 0) {
1379da14cebeSEric Cheng 		mac_unmark_exclusive(mh);
1380d62bc4baSyz147064 		goto done;
1381d62bc4baSyz147064 	}
1382d62bc4baSyz147064 
1383d62bc4baSyz147064 	(void) mod_hash_remove(i_dls_devnet_id_hash,
1384d62bc4baSyz147064 	    (mod_hash_key_t)(uintptr_t)id1, &val);
1385d62bc4baSyz147064 
1386da14cebeSEric Cheng 	ddp->dd_linkid = id2;
1387d62bc4baSyz147064 	(void) mod_hash_insert(i_dls_devnet_id_hash,
1388da14cebeSEric Cheng 	    (mod_hash_key_t)(uintptr_t)ddp->dd_linkid, (mod_hash_val_t)ddp);
1389da14cebeSEric Cheng 
1390da14cebeSEric Cheng 	mac_unmark_exclusive(mh);
1391d62bc4baSyz147064 
139230890389Sartem 	/* load properties for new id */
139330890389Sartem 	mutex_enter(&ddp->dd_mutex);
139430890389Sartem 	ddp->dd_prop_loaded = B_FALSE;
139530890389Sartem 	ddp->dd_prop_taskid = taskq_dispatch(system_taskq,
139630890389Sartem 	    dls_devnet_prop_task, ddp, TQ_SLEEP);
139730890389Sartem 	mutex_exit(&ddp->dd_mutex);
139830890389Sartem 
1399d62bc4baSyz147064 done:
1400d62bc4baSyz147064 	/*
1401d62bc4baSyz147064 	 * Change the name of the kstat based on the new link name.
1402ae6aa22aSVenugopal Iyer 	 * We can't hold the i_dls_devnet_lock across calls to the kstat
1403ae6aa22aSVenugopal Iyer 	 * subsystem. Instead the DD_KSTAT_CHANGING flag set above in this
1404ae6aa22aSVenugopal Iyer 	 * function prevents any access to the dd_ksp while we delete and
1405ae6aa22aSVenugopal Iyer 	 * recreate it below.
1406d62bc4baSyz147064 	 */
1407ae6aa22aSVenugopal Iyer 	rw_exit(&i_dls_devnet_lock);
1408d62bc4baSyz147064 	if (err == 0)
14092b24ab6bSSebastien Roy 		dls_devnet_stat_rename(ddp);
1410d62bc4baSyz147064 
1411ae6aa22aSVenugopal Iyer 	if (clear_dd_flag) {
1412ae6aa22aSVenugopal Iyer 		mutex_enter(&ddp->dd_mutex);
1413ae6aa22aSVenugopal Iyer 		ddp->dd_flags &= ~DD_KSTAT_CHANGING;
1414ae6aa22aSVenugopal Iyer 		mutex_exit(&ddp->dd_mutex);
1415ae6aa22aSVenugopal Iyer 	}
1416ae6aa22aSVenugopal Iyer 
1417da14cebeSEric Cheng 	if (mph != NULL)
1418da14cebeSEric Cheng 		mac_perim_exit(mph);
1419d62bc4baSyz147064 	softmac_rele_device(ddh);
1420d62bc4baSyz147064 	return (err);
1421d62bc4baSyz147064 }
1422d62bc4baSyz147064 
14232b24ab6bSSebastien Roy static int
14242b24ab6bSSebastien Roy i_dls_devnet_setzid(dls_devnet_t *ddp, zoneid_t new_zoneid, boolean_t setprop)
14252b24ab6bSSebastien Roy {
14262b24ab6bSSebastien Roy 	int			err;
14272b24ab6bSSebastien Roy 	mac_perim_handle_t	mph;
14282b24ab6bSSebastien Roy 	boolean_t		upcall_done = B_FALSE;
14292b24ab6bSSebastien Roy 	datalink_id_t		linkid = ddp->dd_linkid;
14302b24ab6bSSebastien Roy 	zoneid_t		old_zoneid = ddp->dd_zid;
14312b24ab6bSSebastien Roy 	dlmgmt_door_setzoneid_t	setzid;
14322b24ab6bSSebastien Roy 	dlmgmt_setzoneid_retval_t retval;
14332b24ab6bSSebastien Roy 
14342b24ab6bSSebastien Roy 	if (old_zoneid == new_zoneid)
14352b24ab6bSSebastien Roy 		return (0);
14362b24ab6bSSebastien Roy 
14372b24ab6bSSebastien Roy 	if ((err = mac_perim_enter_by_macname(ddp->dd_mac, &mph)) != 0)
14382b24ab6bSSebastien Roy 		return (err);
14392b24ab6bSSebastien Roy 
14402b24ab6bSSebastien Roy 	/*
14412b24ab6bSSebastien Roy 	 * When changing the zoneid of an existing link, we need to tell
14422b24ab6bSSebastien Roy 	 * dlmgmtd about it.  dlmgmtd already knows the zoneid associated with
14432b24ab6bSSebastien Roy 	 * newly created links.
14442b24ab6bSSebastien Roy 	 */
14452b24ab6bSSebastien Roy 	if (setprop) {
14462b24ab6bSSebastien Roy 		setzid.ld_cmd = DLMGMT_CMD_SETZONEID;
14472b24ab6bSSebastien Roy 		setzid.ld_linkid = linkid;
14482b24ab6bSSebastien Roy 		setzid.ld_zoneid = new_zoneid;
14492b24ab6bSSebastien Roy 		err = i_dls_mgmt_upcall(&setzid, sizeof (setzid), &retval,
14502b24ab6bSSebastien Roy 		    sizeof (retval));
14512b24ab6bSSebastien Roy 		if (err != 0)
14522b24ab6bSSebastien Roy 			goto done;
14532b24ab6bSSebastien Roy 		upcall_done = B_TRUE;
14542b24ab6bSSebastien Roy 	}
14552b24ab6bSSebastien Roy 	if ((err = dls_link_setzid(ddp->dd_mac, new_zoneid)) == 0) {
14562b24ab6bSSebastien Roy 		ddp->dd_zid = new_zoneid;
14572b24ab6bSSebastien Roy 		devnet_need_rebuild = B_TRUE;
14582b24ab6bSSebastien Roy 	}
14592b24ab6bSSebastien Roy 
14602b24ab6bSSebastien Roy done:
14612b24ab6bSSebastien Roy 	if (err != 0 && upcall_done) {
14622b24ab6bSSebastien Roy 		setzid.ld_zoneid = old_zoneid;
14632b24ab6bSSebastien Roy 		(void) i_dls_mgmt_upcall(&setzid, sizeof (setzid), &retval,
14642b24ab6bSSebastien Roy 		    sizeof (retval));
14652b24ab6bSSebastien Roy 	}
14662b24ab6bSSebastien Roy 	mac_perim_exit(mph);
14672b24ab6bSSebastien Roy 	return (err);
14682b24ab6bSSebastien Roy }
14692b24ab6bSSebastien Roy 
1470d62bc4baSyz147064 int
14712b24ab6bSSebastien Roy dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid)
1472d62bc4baSyz147064 {
1473d62bc4baSyz147064 	dls_devnet_t	*ddp;
1474d62bc4baSyz147064 	int		err;
1475d62bc4baSyz147064 	zoneid_t	old_zid;
14762b24ab6bSSebastien Roy 	boolean_t	refheld = B_FALSE;
1477d62bc4baSyz147064 
14782b24ab6bSSebastien Roy 	old_zid = ddh->dd_zid;
1479d62bc4baSyz147064 
14802b24ab6bSSebastien Roy 	if (old_zid == new_zid)
1481d62bc4baSyz147064 		return (0);
14822b24ab6bSSebastien Roy 
14832b24ab6bSSebastien Roy 	/*
14842b24ab6bSSebastien Roy 	 * Acquire an additional reference to the link if it is being assigned
14852b24ab6bSSebastien Roy 	 * to a non-global zone from the global zone.
14862b24ab6bSSebastien Roy 	 */
14872b24ab6bSSebastien Roy 	if (old_zid == GLOBAL_ZONEID && new_zid != GLOBAL_ZONEID) {
14882b24ab6bSSebastien Roy 		if ((err = dls_devnet_hold(ddh->dd_linkid, &ddp)) != 0)
14892b24ab6bSSebastien Roy 			return (err);
14902b24ab6bSSebastien Roy 		refheld = B_TRUE;
1491d62bc4baSyz147064 	}
1492d62bc4baSyz147064 
14932b24ab6bSSebastien Roy 	if ((err = i_dls_devnet_setzid(ddh, new_zid, B_TRUE)) != 0) {
14942b24ab6bSSebastien Roy 		if (refheld)
1495d62bc4baSyz147064 			dls_devnet_rele(ddp);
1496d62bc4baSyz147064 		return (err);
1497d62bc4baSyz147064 	}
1498d62bc4baSyz147064 
1499d62bc4baSyz147064 	/*
15002b24ab6bSSebastien Roy 	 * Release the additional reference if the link is returning to the
15012b24ab6bSSebastien Roy 	 * global zone from a non-global zone.
1502d62bc4baSyz147064 	 */
15032b24ab6bSSebastien Roy 	if (old_zid != GLOBAL_ZONEID && new_zid == GLOBAL_ZONEID)
15042b24ab6bSSebastien Roy 		dls_devnet_rele(ddh);
1505d62bc4baSyz147064 
15062b24ab6bSSebastien Roy 	/* Re-create kstats in the appropriate zones. */
15072b24ab6bSSebastien Roy 	if (old_zid != GLOBAL_ZONEID)
15082b24ab6bSSebastien Roy 		dls_devnet_stat_destroy(ddh, old_zid);
15092b24ab6bSSebastien Roy 	if (new_zid != GLOBAL_ZONEID)
15102b24ab6bSSebastien Roy 		dls_devnet_stat_create(ddh, new_zid);
1511d62bc4baSyz147064 
1512d62bc4baSyz147064 	return (0);
1513d62bc4baSyz147064 }
1514d62bc4baSyz147064 
15152b24ab6bSSebastien Roy zoneid_t
15162b24ab6bSSebastien Roy dls_devnet_getzid(dls_dl_handle_t ddh)
15172b24ab6bSSebastien Roy {
15182b24ab6bSSebastien Roy 	return (((dls_devnet_t *)ddh)->dd_zid);
15192b24ab6bSSebastien Roy }
15202b24ab6bSSebastien Roy 
15212b24ab6bSSebastien Roy zoneid_t
15222b24ab6bSSebastien Roy dls_devnet_getownerzid(dls_dl_handle_t ddh)
15232b24ab6bSSebastien Roy {
15242b24ab6bSSebastien Roy 	return (((dls_devnet_t *)ddh)->dd_owner_zid);
15252b24ab6bSSebastien Roy }
15262b24ab6bSSebastien Roy 
15272b24ab6bSSebastien Roy /*
15282b24ab6bSSebastien Roy  * Is linkid visible from zoneid?  A link is visible if it was created in the
15292b24ab6bSSebastien Roy  * zone, or if it is currently assigned to the zone.
15302b24ab6bSSebastien Roy  */
15312b24ab6bSSebastien Roy boolean_t
15322b24ab6bSSebastien Roy dls_devnet_islinkvisible(datalink_id_t linkid, zoneid_t zoneid)
1533d62bc4baSyz147064 {
1534d62bc4baSyz147064 	dls_devnet_t	*ddp;
15352b24ab6bSSebastien Roy 	boolean_t	result;
1536d62bc4baSyz147064 
15372b24ab6bSSebastien Roy 	if (dls_devnet_hold_tmp(linkid, &ddp) != 0)
15382b24ab6bSSebastien Roy 		return (B_FALSE);
15392b24ab6bSSebastien Roy 	result = (ddp->dd_owner_zid == zoneid || ddp->dd_zid == zoneid);
1540d62bc4baSyz147064 	dls_devnet_rele_tmp(ddp);
15412b24ab6bSSebastien Roy 	return (result);
1542d62bc4baSyz147064 }
1543d62bc4baSyz147064 
1544d62bc4baSyz147064 /*
1545d62bc4baSyz147064  * Access a vanity naming node.
1546d62bc4baSyz147064  */
1547d62bc4baSyz147064 int
1548d62bc4baSyz147064 dls_devnet_open(const char *link, dls_dl_handle_t *dhp, dev_t *devp)
1549d62bc4baSyz147064 {
1550d62bc4baSyz147064 	dls_devnet_t	*ddp;
1551da14cebeSEric Cheng 	dls_link_t	*dlp;
1552d62bc4baSyz147064 	zoneid_t	zid = getzoneid();
1553d62bc4baSyz147064 	int		err;
1554da14cebeSEric Cheng 	mac_perim_handle_t	mph;
1555d62bc4baSyz147064 
1556da14cebeSEric Cheng 	if ((err = dls_devnet_hold_by_name(link, &ddp)) != 0)
1557d62bc4baSyz147064 		return (err);
1558d62bc4baSyz147064 
1559da14cebeSEric Cheng 	dls_devnet_prop_task_wait(ddp);
1560da14cebeSEric Cheng 
1561d62bc4baSyz147064 	/*
1562d62bc4baSyz147064 	 * Opening a link that does not belong to the current non-global zone
1563d62bc4baSyz147064 	 * is not allowed.
1564d62bc4baSyz147064 	 */
1565d62bc4baSyz147064 	if (zid != GLOBAL_ZONEID && ddp->dd_zid != zid) {
1566d62bc4baSyz147064 		dls_devnet_rele(ddp);
1567d62bc4baSyz147064 		return (ENOENT);
1568d62bc4baSyz147064 	}
1569d62bc4baSyz147064 
1570da14cebeSEric Cheng 	err = mac_perim_enter_by_macname(ddp->dd_mac, &mph);
1571d62bc4baSyz147064 	if (err != 0) {
1572d62bc4baSyz147064 		dls_devnet_rele(ddp);
1573d62bc4baSyz147064 		return (err);
1574d62bc4baSyz147064 	}
1575d62bc4baSyz147064 
1576da14cebeSEric Cheng 	err = dls_link_hold_create(ddp->dd_mac, &dlp);
1577da14cebeSEric Cheng 	mac_perim_exit(mph);
1578da14cebeSEric Cheng 
1579da14cebeSEric Cheng 	if (err != 0) {
1580da14cebeSEric Cheng 		dls_devnet_rele(ddp);
1581da14cebeSEric Cheng 		return (err);
1582da14cebeSEric Cheng 	}
158330890389Sartem 
1584d62bc4baSyz147064 	*dhp = ddp;
1585da14cebeSEric Cheng 	*devp = dls_link_dev(dlp);
1586d62bc4baSyz147064 	return (0);
1587d62bc4baSyz147064 }
1588d62bc4baSyz147064 
1589d62bc4baSyz147064 /*
1590d62bc4baSyz147064  * Close access to a vanity naming node.
1591d62bc4baSyz147064  */
1592d62bc4baSyz147064 void
1593d62bc4baSyz147064 dls_devnet_close(dls_dl_handle_t dlh)
1594d62bc4baSyz147064 {
1595d62bc4baSyz147064 	dls_devnet_t	*ddp = dlh;
1596da14cebeSEric Cheng 	dls_link_t	*dlp;
1597da14cebeSEric Cheng 	mac_perim_handle_t	mph;
1598da14cebeSEric Cheng 
1599da14cebeSEric Cheng 	VERIFY(mac_perim_enter_by_macname(ddp->dd_mac, &mph) == 0);
1600da14cebeSEric Cheng 	VERIFY(dls_link_hold(ddp->dd_mac, &dlp) == 0);
1601d62bc4baSyz147064 
1602d62bc4baSyz147064 	/*
1603da14cebeSEric Cheng 	 * One rele for the hold placed in dls_devnet_open, another for
1604da14cebeSEric Cheng 	 * the hold done just above
1605d62bc4baSyz147064 	 */
1606da14cebeSEric Cheng 	dls_link_rele(dlp);
1607da14cebeSEric Cheng 	dls_link_rele(dlp);
1608da14cebeSEric Cheng 	mac_perim_exit(mph);
1609da14cebeSEric Cheng 
1610d62bc4baSyz147064 	dls_devnet_rele(ddp);
1611d62bc4baSyz147064 }
1612d62bc4baSyz147064 
1613d62bc4baSyz147064 /*
1614d62bc4baSyz147064  * This is used by /dev/net to rebuild the nodes for readdir().  It is not
1615d62bc4baSyz147064  * critical and no protection is needed.
1616d62bc4baSyz147064  */
1617d62bc4baSyz147064 boolean_t
1618d62bc4baSyz147064 dls_devnet_rebuild()
1619d62bc4baSyz147064 {
1620d62bc4baSyz147064 	boolean_t updated = devnet_need_rebuild;
1621d62bc4baSyz147064 
1622d62bc4baSyz147064 	devnet_need_rebuild = B_FALSE;
1623d62bc4baSyz147064 	return (updated);
1624d62bc4baSyz147064 }
1625d62bc4baSyz147064 
1626d62bc4baSyz147064 int
16272b24ab6bSSebastien Roy dls_devnet_create(mac_handle_t mh, datalink_id_t linkid, zoneid_t zoneid)
1628d62bc4baSyz147064 {
1629da14cebeSEric Cheng 	dls_link_t	*dlp;
16302b24ab6bSSebastien Roy 	dls_devnet_t	*ddp;
1631d62bc4baSyz147064 	int		err;
1632da14cebeSEric Cheng 	mac_perim_handle_t mph;
1633d62bc4baSyz147064 
1634ae6aa22aSVenugopal Iyer 	/*
1635ae6aa22aSVenugopal Iyer 	 * Holding the mac perimeter ensures that the downcall from the
1636ae6aa22aSVenugopal Iyer 	 * dlmgmt daemon which does the property loading does not proceed
1637ae6aa22aSVenugopal Iyer 	 * until we relinquish the perimeter.
1638ae6aa22aSVenugopal Iyer 	 */
1639da14cebeSEric Cheng 	mac_perim_enter_by_mh(mh, &mph);
1640da14cebeSEric Cheng 	/*
1641da14cebeSEric Cheng 	 * Make this association before we call dls_link_hold_create as
1642da14cebeSEric Cheng 	 * we need to use the linkid to get the user name for the link
1643da14cebeSEric Cheng 	 * when we create the MAC client.
1644da14cebeSEric Cheng 	 */
16452b24ab6bSSebastien Roy 	if ((err = dls_devnet_set(mac_name(mh), linkid, zoneid, &ddp)) == 0) {
1646da14cebeSEric Cheng 		if ((err = dls_link_hold_create(mac_name(mh), &dlp)) != 0) {
1647da14cebeSEric Cheng 			mac_perim_exit(mph);
1648ae6aa22aSVenugopal Iyer 			(void) dls_devnet_unset(mac_name(mh), &linkid, B_TRUE);
1649da14cebeSEric Cheng 			return (err);
1650da14cebeSEric Cheng 		}
16512b24ab6bSSebastien Roy 	}
1652da14cebeSEric Cheng 	mac_perim_exit(mph);
1653d62bc4baSyz147064 	return (err);
1654d62bc4baSyz147064 }
1655d62bc4baSyz147064 
1656d62bc4baSyz147064 /*
1657d62bc4baSyz147064  * Set the linkid of the dls_devnet_t and add it into the i_dls_devnet_id_hash.
1658d62bc4baSyz147064  * This is called in the case that the dlmgmtd daemon is started later than
1659d62bc4baSyz147064  * the physical devices get attached, and the linkid is only known after the
1660d62bc4baSyz147064  * daemon starts.
1661d62bc4baSyz147064  */
1662d62bc4baSyz147064 int
1663d62bc4baSyz147064 dls_devnet_recreate(mac_handle_t mh, datalink_id_t linkid)
1664d62bc4baSyz147064 {
1665d62bc4baSyz147064 	ASSERT(linkid != DATALINK_INVALID_LINKID);
16662b24ab6bSSebastien Roy 	return (dls_devnet_set(mac_name(mh), linkid, GLOBAL_ZONEID, NULL));
1667d62bc4baSyz147064 }
1668d62bc4baSyz147064 
1669d62bc4baSyz147064 int
1670da14cebeSEric Cheng dls_devnet_destroy(mac_handle_t mh, datalink_id_t *idp, boolean_t wait)
1671d62bc4baSyz147064 {
1672d62bc4baSyz147064 	int			err;
1673da14cebeSEric Cheng 	mac_perim_handle_t	mph;
1674d62bc4baSyz147064 
1675d62bc4baSyz147064 	*idp = DATALINK_INVALID_LINKID;
1676da14cebeSEric Cheng 	err = dls_devnet_unset(mac_name(mh), idp, wait);
1677d62bc4baSyz147064 	if (err != 0 && err != ENOENT)
1678d62bc4baSyz147064 		return (err);
1679d62bc4baSyz147064 
1680da14cebeSEric Cheng 	mac_perim_enter_by_mh(mh, &mph);
1681da14cebeSEric Cheng 	err = dls_link_rele_by_name(mac_name(mh));
1682da14cebeSEric Cheng 	mac_perim_exit(mph);
1683da14cebeSEric Cheng 
16842b24ab6bSSebastien Roy 	if (err != 0) {
16852b24ab6bSSebastien Roy 		/*
16862b24ab6bSSebastien Roy 		 * XXX It is a general GLDv3 bug that dls_devnet_set() has to
16872b24ab6bSSebastien Roy 		 * be called to re-set the link when destroy fails.  The
16882b24ab6bSSebastien Roy 		 * zoneid below will be incorrect if this function is ever
16892b24ab6bSSebastien Roy 		 * called from kernel context or from a zone other than that
16902b24ab6bSSebastien Roy 		 * which initially created the link.
16912b24ab6bSSebastien Roy 		 */
16922b24ab6bSSebastien Roy 		(void) dls_devnet_set(mac_name(mh), *idp, crgetzoneid(CRED()),
16932b24ab6bSSebastien Roy 		    NULL);
16942b24ab6bSSebastien Roy 	}
16952b24ab6bSSebastien Roy 	return (err);
16962b24ab6bSSebastien Roy }
1697d62bc4baSyz147064 
16982b24ab6bSSebastien Roy /*
16992b24ab6bSSebastien Roy  * Implicitly create an IP tunnel link.
17002b24ab6bSSebastien Roy  */
17012b24ab6bSSebastien Roy static int
1702d501bbfeSSebastien Roy i_dls_devnet_create_iptun(const char *linkname, const char *drvname,
1703d501bbfeSSebastien Roy     datalink_id_t *linkid)
17042b24ab6bSSebastien Roy {
17052b24ab6bSSebastien Roy 	int		err;
17062b24ab6bSSebastien Roy 	iptun_kparams_t	ik;
17072b24ab6bSSebastien Roy 	uint32_t	media;
17082b24ab6bSSebastien Roy 	netstack_t	*ns;
17092b24ab6bSSebastien Roy 	major_t		iptun_major;
17102b24ab6bSSebastien Roy 	dev_info_t	*iptun_dip;
17112b24ab6bSSebastien Roy 
17122b24ab6bSSebastien Roy 	/* First ensure that the iptun device is attached. */
17132b24ab6bSSebastien Roy 	if ((iptun_major = ddi_name_to_major(IPTUN_DRIVER_NAME)) == (major_t)-1)
17142b24ab6bSSebastien Roy 		return (EINVAL);
17152b24ab6bSSebastien Roy 	if ((iptun_dip = ddi_hold_devi_by_instance(iptun_major, 0, 0)) == NULL)
17162b24ab6bSSebastien Roy 		return (EINVAL);
17172b24ab6bSSebastien Roy 
1718d501bbfeSSebastien Roy 	if (IS_IPV4_TUN(drvname)) {
17192b24ab6bSSebastien Roy 		ik.iptun_kparam_type = IPTUN_TYPE_IPV4;
17202b24ab6bSSebastien Roy 		media = DL_IPV4;
1721d501bbfeSSebastien Roy 	} else if (IS_6TO4_TUN(drvname)) {
17222b24ab6bSSebastien Roy 		ik.iptun_kparam_type = IPTUN_TYPE_6TO4;
17232b24ab6bSSebastien Roy 		media = DL_6TO4;
1724d501bbfeSSebastien Roy 	} else if (IS_IPV6_TUN(drvname)) {
17252b24ab6bSSebastien Roy 		ik.iptun_kparam_type = IPTUN_TYPE_IPV6;
17262b24ab6bSSebastien Roy 		media = DL_IPV6;
17272b24ab6bSSebastien Roy 	}
17282b24ab6bSSebastien Roy 	ik.iptun_kparam_flags = (IPTUN_KPARAM_TYPE | IPTUN_KPARAM_IMPLICIT);
17292b24ab6bSSebastien Roy 
17302b24ab6bSSebastien Roy 	/* Obtain a datalink id for this tunnel. */
1731d501bbfeSSebastien Roy 	err = dls_mgmt_create((char *)linkname, 0, DATALINK_CLASS_IPTUN, media,
17322b24ab6bSSebastien Roy 	    B_FALSE, &ik.iptun_kparam_linkid);
17332b24ab6bSSebastien Roy 	if (err != 0) {
17342b24ab6bSSebastien Roy 		ddi_release_devi(iptun_dip);
17352b24ab6bSSebastien Roy 		return (err);
17362b24ab6bSSebastien Roy 	}
17372b24ab6bSSebastien Roy 
17382b24ab6bSSebastien Roy 	ns = netstack_get_current();
17392b24ab6bSSebastien Roy 	err = iptun_create(&ik, CRED());
17402b24ab6bSSebastien Roy 	netstack_rele(ns);
17412b24ab6bSSebastien Roy 
17422b24ab6bSSebastien Roy 	if (err != 0)
17432b24ab6bSSebastien Roy 		VERIFY(dls_mgmt_destroy(ik.iptun_kparam_linkid, B_FALSE) == 0);
17442b24ab6bSSebastien Roy 	else
17452b24ab6bSSebastien Roy 		*linkid = ik.iptun_kparam_linkid;
17462b24ab6bSSebastien Roy 
17472b24ab6bSSebastien Roy 	ddi_release_devi(iptun_dip);
17482b24ab6bSSebastien Roy 	return (err);
17492b24ab6bSSebastien Roy }
17502b24ab6bSSebastien Roy 
17512b24ab6bSSebastien Roy static int
17522b24ab6bSSebastien Roy i_dls_devnet_destroy_iptun(datalink_id_t linkid)
17532b24ab6bSSebastien Roy {
17542b24ab6bSSebastien Roy 	int err;
17552b24ab6bSSebastien Roy 
17562b24ab6bSSebastien Roy 	/*
17572b24ab6bSSebastien Roy 	 * Note the use of zone_kcred() here as opposed to CRED().  This is
17582b24ab6bSSebastien Roy 	 * because the process that does the last close of this /dev/net node
17592b24ab6bSSebastien Roy 	 * may not have necessary privileges to delete this IP tunnel, but the
17602b24ab6bSSebastien Roy 	 * tunnel must always be implicitly deleted on last close.
17612b24ab6bSSebastien Roy 	 */
17622b24ab6bSSebastien Roy 	if ((err = iptun_delete(linkid, zone_kcred())) == 0)
17632b24ab6bSSebastien Roy 		(void) dls_mgmt_destroy(linkid, B_FALSE);
1764d62bc4baSyz147064 	return (err);
1765d62bc4baSyz147064 }
1766d62bc4baSyz147064 
1767d62bc4baSyz147064 const char *
1768d62bc4baSyz147064 dls_devnet_mac(dls_dl_handle_t ddh)
1769d62bc4baSyz147064 {
1770d62bc4baSyz147064 	return (ddh->dd_mac);
1771d62bc4baSyz147064 }
1772d62bc4baSyz147064 
1773d62bc4baSyz147064 datalink_id_t
1774d62bc4baSyz147064 dls_devnet_linkid(dls_dl_handle_t ddh)
1775d62bc4baSyz147064 {
1776d62bc4baSyz147064 	return (ddh->dd_linkid);
1777d62bc4baSyz147064 }
1778