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