xref: /illumos-gate/usr/src/uts/common/io/vnic/vnic_dev.c (revision d5d1522f)
1843e1988Sjohnlev /*
2843e1988Sjohnlev  * CDDL HEADER START
3843e1988Sjohnlev  *
4843e1988Sjohnlev  * The contents of this file are subject to the terms of the
5843e1988Sjohnlev  * Common Development and Distribution License (the "License").
6843e1988Sjohnlev  * You may not use this file except in compliance with the License.
7843e1988Sjohnlev  *
8843e1988Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9843e1988Sjohnlev  * or http://www.opensolaris.org/os/licensing.
10843e1988Sjohnlev  * See the License for the specific language governing permissions
11843e1988Sjohnlev  * and limitations under the License.
12843e1988Sjohnlev  *
13843e1988Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
14843e1988Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15843e1988Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
16843e1988Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
17843e1988Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
18843e1988Sjohnlev  *
19843e1988Sjohnlev  * CDDL HEADER END
20843e1988Sjohnlev  */
21843e1988Sjohnlev /*
229056fcebSCathy Zhou  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2384de666eSRyan Zezeski  * Copyright 2018 Joyent, Inc.
24238d8f47SDale Ghent  * Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
25ea720162SAndy Fiddaman  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
26*d5d1522fSGarrett D'Amore  * Copyright 2022 RackTop Systems, Inc.
27843e1988Sjohnlev  */
28843e1988Sjohnlev 
29843e1988Sjohnlev #include <sys/types.h>
302b24ab6bSSebastien Roy #include <sys/cred.h>
31843e1988Sjohnlev #include <sys/sysmacros.h>
32843e1988Sjohnlev #include <sys/conf.h>
33843e1988Sjohnlev #include <sys/cmn_err.h>
34843e1988Sjohnlev #include <sys/list.h>
35843e1988Sjohnlev #include <sys/ksynch.h>
36843e1988Sjohnlev #include <sys/kmem.h>
37843e1988Sjohnlev #include <sys/stream.h>
38843e1988Sjohnlev #include <sys/modctl.h>
39843e1988Sjohnlev #include <sys/ddi.h>
40843e1988Sjohnlev #include <sys/sunddi.h>
41843e1988Sjohnlev #include <sys/atomic.h>
42843e1988Sjohnlev #include <sys/stat.h>
43843e1988Sjohnlev #include <sys/modhash.h>
44843e1988Sjohnlev #include <sys/strsubr.h>
45843e1988Sjohnlev #include <sys/strsun.h>
46843e1988Sjohnlev #include <sys/dlpi.h>
47843e1988Sjohnlev #include <sys/mac.h>
48da14cebeSEric Cheng #include <sys/mac_provider.h>
49da14cebeSEric Cheng #include <sys/mac_client.h>
50da14cebeSEric Cheng #include <sys/mac_client_priv.h>
51843e1988Sjohnlev #include <sys/mac_ether.h>
52d62bc4baSyz #include <sys/dls.h>
53843e1988Sjohnlev #include <sys/pattr.h>
54da14cebeSEric Cheng #include <sys/time.h>
55da14cebeSEric Cheng #include <sys/vlan.h>
56843e1988Sjohnlev #include <sys/vnic.h>
57843e1988Sjohnlev #include <sys/vnic_impl.h>
5810a40492SRobert Mustacchi #include <sys/mac_impl.h>
59da14cebeSEric Cheng #include <sys/mac_flow_impl.h>
60843e1988Sjohnlev #include <inet/ip_impl.h>
61843e1988Sjohnlev 
62da14cebeSEric Cheng /*
63da14cebeSEric Cheng  * Note that for best performance, the VNIC is a passthrough design.
64da14cebeSEric Cheng  * For each VNIC corresponds a MAC client of the underlying MAC (lower MAC).
65da14cebeSEric Cheng  * This MAC client is opened by the VNIC driver at VNIC creation,
66da14cebeSEric Cheng  * and closed when the VNIC is deleted.
67da14cebeSEric Cheng  * When a MAC client of the VNIC itself opens a VNIC, the MAC layer
68da14cebeSEric Cheng  * (upper MAC) detects that the MAC being opened is a VNIC. Instead
69da14cebeSEric Cheng  * of allocating a new MAC client, it asks the VNIC driver to return
70da14cebeSEric Cheng  * the lower MAC client handle associated with the VNIC, and that handle
71da14cebeSEric Cheng  * is returned to the upper MAC client directly. This allows access
72da14cebeSEric Cheng  * by upper MAC clients of the VNIC to have direct access to the lower
73da14cebeSEric Cheng  * MAC client for the control path and data path.
74da14cebeSEric Cheng  *
75da14cebeSEric Cheng  * Due to this passthrough, some of the entry points exported by the
76da14cebeSEric Cheng  * VNIC driver are never directly invoked. These entry points include
77da14cebeSEric Cheng  * vnic_m_start, vnic_m_stop, vnic_m_promisc, vnic_m_multicst, etc.
781a41ca23SJerry Jelinek  *
791a41ca23SJerry Jelinek  * VNICs support multiple upper mac clients to enable support for
801a41ca23SJerry Jelinek  * multiple MAC addresses on the VNIC. When the VNIC is created the
811a41ca23SJerry Jelinek  * initial mac client is the primary upper mac. Any additional mac
821a41ca23SJerry Jelinek  * clients are secondary macs.
83da14cebeSEric Cheng  */
84da14cebeSEric Cheng 
85843e1988Sjohnlev static int vnic_m_start(void *);
86843e1988Sjohnlev static void vnic_m_stop(void *);
87843e1988Sjohnlev static int vnic_m_promisc(void *, boolean_t);
88843e1988Sjohnlev static int vnic_m_multicst(void *, boolean_t, const uint8_t *);
89843e1988Sjohnlev static int vnic_m_unicst(void *, const uint8_t *);
90843e1988Sjohnlev static int vnic_m_stat(void *, uint_t, uint64_t *);
91da14cebeSEric Cheng static void vnic_m_ioctl(void *, queue_t *, mblk_t *);
92ab6f61efSGirish Moodalbail static int vnic_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
93ab6f61efSGirish Moodalbail     const void *);
941a41ca23SJerry Jelinek static int vnic_m_getprop(void *, const char *, mac_prop_id_t, uint_t, void *);
950dc2366fSVenugopal Iyer static void vnic_m_propinfo(void *, const char *, mac_prop_id_t,
960dc2366fSVenugopal Iyer     mac_prop_info_handle_t);
97843e1988Sjohnlev static mblk_t *vnic_m_tx(void *, mblk_t *);
98843e1988Sjohnlev static boolean_t vnic_m_capab_get(void *, mac_capab_t, void *);
99843e1988Sjohnlev static void vnic_notify_cb(void *, mac_notify_type_t);
1001a41ca23SJerry Jelinek static void vnic_cleanup_secondary_macs(vnic_t *, int);
101843e1988Sjohnlev 
102843e1988Sjohnlev static kmem_cache_t	*vnic_cache;
103843e1988Sjohnlev static krwlock_t	vnic_lock;
104843e1988Sjohnlev static uint_t		vnic_count;
105843e1988Sjohnlev 
106f0f2c3a5SGirish Moodalbail #define	ANCHOR_VNIC_MIN_MTU	576
107f0f2c3a5SGirish Moodalbail #define	ANCHOR_VNIC_MAX_MTU	9000
108f0f2c3a5SGirish Moodalbail 
109843e1988Sjohnlev /* hash of VNICs (vnic_t's), keyed by VNIC id */
110843e1988Sjohnlev static mod_hash_t	*vnic_hash;
111843e1988Sjohnlev #define	VNIC_HASHSZ	64
112843e1988Sjohnlev #define	VNIC_HASH_KEY(vnic_id)	((mod_hash_key_t)(uintptr_t)vnic_id)
113843e1988Sjohnlev 
114ab6f61efSGirish Moodalbail #define	VNIC_M_CALLBACK_FLAGS	\
1151a41ca23SJerry Jelinek 	(MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO)
116843e1988Sjohnlev 
117843e1988Sjohnlev static mac_callbacks_t vnic_m_callbacks = {
118843e1988Sjohnlev 	VNIC_M_CALLBACK_FLAGS,
119843e1988Sjohnlev 	vnic_m_stat,
120843e1988Sjohnlev 	vnic_m_start,
121843e1988Sjohnlev 	vnic_m_stop,
122843e1988Sjohnlev 	vnic_m_promisc,
123843e1988Sjohnlev 	vnic_m_multicst,
124843e1988Sjohnlev 	vnic_m_unicst,
125843e1988Sjohnlev 	vnic_m_tx,
1260dc2366fSVenugopal Iyer 	NULL,
127da14cebeSEric Cheng 	vnic_m_ioctl,
128ab6f61efSGirish Moodalbail 	vnic_m_capab_get,
129ab6f61efSGirish Moodalbail 	NULL,
130ab6f61efSGirish Moodalbail 	NULL,
131ab6f61efSGirish Moodalbail 	vnic_m_setprop,
1321a41ca23SJerry Jelinek 	vnic_m_getprop,
1330dc2366fSVenugopal Iyer 	vnic_m_propinfo
134843e1988Sjohnlev };
135843e1988Sjohnlev 
136843e1988Sjohnlev void
vnic_dev_init(void)137843e1988Sjohnlev vnic_dev_init(void)
138843e1988Sjohnlev {
139843e1988Sjohnlev 	vnic_cache = kmem_cache_create("vnic_cache",
140843e1988Sjohnlev 	    sizeof (vnic_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
141843e1988Sjohnlev 
142843e1988Sjohnlev 	vnic_hash = mod_hash_create_idhash("vnic_hash",
143843e1988Sjohnlev 	    VNIC_HASHSZ, mod_hash_null_valdtor);
144843e1988Sjohnlev 
145843e1988Sjohnlev 	rw_init(&vnic_lock, NULL, RW_DEFAULT, NULL);
146843e1988Sjohnlev 
147843e1988Sjohnlev 	vnic_count = 0;
148843e1988Sjohnlev }
149843e1988Sjohnlev 
150843e1988Sjohnlev void
vnic_dev_fini(void)151843e1988Sjohnlev vnic_dev_fini(void)
152843e1988Sjohnlev {
153843e1988Sjohnlev 	ASSERT(vnic_count == 0);
154843e1988Sjohnlev 
155843e1988Sjohnlev 	rw_destroy(&vnic_lock);
156843e1988Sjohnlev 	mod_hash_destroy_idhash(vnic_hash);
157843e1988Sjohnlev 	kmem_cache_destroy(vnic_cache);
158843e1988Sjohnlev }
159843e1988Sjohnlev 
160843e1988Sjohnlev uint_t
vnic_dev_count(void)161843e1988Sjohnlev vnic_dev_count(void)
162843e1988Sjohnlev {
163843e1988Sjohnlev 	return (vnic_count);
164843e1988Sjohnlev }
165843e1988Sjohnlev 
166da14cebeSEric Cheng static vnic_ioc_diag_t
vnic_mac2vnic_diag(mac_diag_t diag)167da14cebeSEric Cheng vnic_mac2vnic_diag(mac_diag_t diag)
168da14cebeSEric Cheng {
169da14cebeSEric Cheng 	switch (diag) {
170da14cebeSEric Cheng 	case MAC_DIAG_MACADDR_NIC:
171da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_MACADDR_NIC);
172da14cebeSEric Cheng 	case MAC_DIAG_MACADDR_INUSE:
173da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_MACADDR_INUSE);
174da14cebeSEric Cheng 	case MAC_DIAG_MACADDR_INVALID:
175da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_MACADDR_INVALID);
176da14cebeSEric Cheng 	case MAC_DIAG_MACADDRLEN_INVALID:
177da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_MACADDRLEN_INVALID);
178da14cebeSEric Cheng 	case MAC_DIAG_MACFACTORYSLOTINVALID:
179da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_MACFACTORYSLOTINVALID);
180da14cebeSEric Cheng 	case MAC_DIAG_MACFACTORYSLOTUSED:
181da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_MACFACTORYSLOTUSED);
182da14cebeSEric Cheng 	case MAC_DIAG_MACFACTORYSLOTALLUSED:
183da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED);
184da14cebeSEric Cheng 	case MAC_DIAG_MACFACTORYNOTSUP:
185da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_MACFACTORYNOTSUP);
186da14cebeSEric Cheng 	case MAC_DIAG_MACPREFIX_INVALID:
187da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_MACPREFIX_INVALID);
188da14cebeSEric Cheng 	case MAC_DIAG_MACPREFIXLEN_INVALID:
189da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_MACPREFIXLEN_INVALID);
190da14cebeSEric Cheng 	case MAC_DIAG_MACNO_HWRINGS:
191da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_NO_HWRINGS);
192da14cebeSEric Cheng 	default:
193da14cebeSEric Cheng 		return (VNIC_IOC_DIAG_NONE);
194843e1988Sjohnlev 	}
195843e1988Sjohnlev }
196843e1988Sjohnlev 
197843e1988Sjohnlev static int
vnic_unicast_add(vnic_t * vnic,vnic_mac_addr_type_t vnic_addr_type,int * addr_slot,uint_t prefix_len,int * addr_len_ptr_arg,uint8_t * mac_addr_arg,uint16_t flags,vnic_ioc_diag_t * diag,uint16_t vid,boolean_t req_hwgrp_flag)198da14cebeSEric Cheng vnic_unicast_add(vnic_t *vnic, vnic_mac_addr_type_t vnic_addr_type,
199da14cebeSEric Cheng     int *addr_slot, uint_t prefix_len, int *addr_len_ptr_arg,
200da14cebeSEric Cheng     uint8_t *mac_addr_arg, uint16_t flags, vnic_ioc_diag_t *diag,
2010dc2366fSVenugopal Iyer     uint16_t vid, boolean_t req_hwgrp_flag)
202843e1988Sjohnlev {
203ea720162SAndy Fiddaman 	mac_diag_t mac_diag = MAC_DIAG_NONE;
204da14cebeSEric Cheng 	uint16_t mac_flags = 0;
205da14cebeSEric Cheng 	int err;
206da14cebeSEric Cheng 	uint_t addr_len;
207843e1988Sjohnlev 
208da14cebeSEric Cheng 	if (flags & VNIC_IOC_CREATE_NODUPCHECK)
209da14cebeSEric Cheng 		mac_flags |= MAC_UNICAST_NODUPCHECK;
210843e1988Sjohnlev 
211da14cebeSEric Cheng 	switch (vnic_addr_type) {
212da14cebeSEric Cheng 	case VNIC_MAC_ADDR_TYPE_FIXED:
2131cb875aeSCathy Zhou 	case VNIC_MAC_ADDR_TYPE_VRID:
214843e1988Sjohnlev 		/*
215da14cebeSEric Cheng 		 * The MAC address value to assign to the VNIC
216da14cebeSEric Cheng 		 * is already provided in mac_addr_arg. addr_len_ptr_arg
217da14cebeSEric Cheng 		 * already contains the MAC address length.
218843e1988Sjohnlev 		 */
219da14cebeSEric Cheng 		break;
220843e1988Sjohnlev 
221da14cebeSEric Cheng 	case VNIC_MAC_ADDR_TYPE_RANDOM:
222843e1988Sjohnlev 		/*
223da14cebeSEric Cheng 		 * Random MAC address. There are two sub-cases:
224da14cebeSEric Cheng 		 *
225da14cebeSEric Cheng 		 * 1 - If mac_len == 0, a new MAC address is generated.
226da14cebeSEric Cheng 		 *	The length of the MAC address to generated depends
227da14cebeSEric Cheng 		 *	on the type of MAC used. The prefix to use for the MAC
228da14cebeSEric Cheng 		 *	address is stored in the most significant bytes
229da14cebeSEric Cheng 		 *	of the mac_addr argument, and its length is specified
230da14cebeSEric Cheng 		 *	by the mac_prefix_len argument. This prefix can
231da14cebeSEric Cheng 		 *	correspond to a IEEE OUI in the case of Ethernet,
232da14cebeSEric Cheng 		 *	for example.
233da14cebeSEric Cheng 		 *
234da14cebeSEric Cheng 		 * 2 - If mac_len > 0, the address was already picked
235da14cebeSEric Cheng 		 *	randomly, and is now passed back during VNIC
236da14cebeSEric Cheng 		 *	re-creation. The mac_addr argument contains the MAC
237da14cebeSEric Cheng 		 *	address that was generated. We distinguish this
238da14cebeSEric Cheng 		 *	case from the fixed MAC address case, since we
239da14cebeSEric Cheng 		 *	want the user consumers to know, when they query
240da14cebeSEric Cheng 		 *	the list of VNICs, that a VNIC was assigned a
241da14cebeSEric Cheng 		 *	random MAC address vs assigned a fixed address
242da14cebeSEric Cheng 		 *	specified by the user.
243843e1988Sjohnlev 		 */
244843e1988Sjohnlev 
245da14cebeSEric Cheng 		/*
246da14cebeSEric Cheng 		 * If it's a pre-generated address, we're done. mac_addr_arg
247da14cebeSEric Cheng 		 * and addr_len_ptr_arg already contain the MAC address
248da14cebeSEric Cheng 		 * value and length.
249da14cebeSEric Cheng 		 */
250da14cebeSEric Cheng 		if (*addr_len_ptr_arg > 0)
251da14cebeSEric Cheng 			break;
252843e1988Sjohnlev 
253da14cebeSEric Cheng 		/* generate a new random MAC address */
254da14cebeSEric Cheng 		if ((err = mac_addr_random(vnic->vn_mch,
255da14cebeSEric Cheng 		    prefix_len, mac_addr_arg, &mac_diag)) != 0) {
256da14cebeSEric Cheng 			*diag = vnic_mac2vnic_diag(mac_diag);
257da14cebeSEric Cheng 			return (err);
258843e1988Sjohnlev 		}
259da14cebeSEric Cheng 		*addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
260da14cebeSEric Cheng 		break;
261843e1988Sjohnlev 
262da14cebeSEric Cheng 	case VNIC_MAC_ADDR_TYPE_FACTORY:
263da14cebeSEric Cheng 		err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
264da14cebeSEric Cheng 		if (err != 0) {
265da14cebeSEric Cheng 			if (err == EINVAL)
266da14cebeSEric Cheng 				*diag = VNIC_IOC_DIAG_MACFACTORYSLOTINVALID;
267da14cebeSEric Cheng 			if (err == EBUSY)
268da14cebeSEric Cheng 				*diag = VNIC_IOC_DIAG_MACFACTORYSLOTUSED;
269da14cebeSEric Cheng 			if (err == ENOSPC)
270da14cebeSEric Cheng 				*diag = VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED;
271da14cebeSEric Cheng 			return (err);
272843e1988Sjohnlev 		}
273843e1988Sjohnlev 
274da14cebeSEric Cheng 		mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
275da14cebeSEric Cheng 		    mac_addr_arg, &addr_len, NULL, NULL);
276da14cebeSEric Cheng 		*addr_len_ptr_arg = addr_len;
277da14cebeSEric Cheng 		break;
278843e1988Sjohnlev 
279da14cebeSEric Cheng 	case VNIC_MAC_ADDR_TYPE_AUTO:
280da14cebeSEric Cheng 		/* first try to allocate a factory MAC address */
281da14cebeSEric Cheng 		err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
282da14cebeSEric Cheng 		if (err == 0) {
283da14cebeSEric Cheng 			mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
284da14cebeSEric Cheng 			    mac_addr_arg, &addr_len, NULL, NULL);
285da14cebeSEric Cheng 			vnic_addr_type = VNIC_MAC_ADDR_TYPE_FACTORY;
286da14cebeSEric Cheng 			*addr_len_ptr_arg = addr_len;
287da14cebeSEric Cheng 			break;
288843e1988Sjohnlev 		}
289843e1988Sjohnlev 
290843e1988Sjohnlev 		/*
291da14cebeSEric Cheng 		 * Allocating a factory MAC address failed, generate a
292da14cebeSEric Cheng 		 * random MAC address instead.
293843e1988Sjohnlev 		 */
294da14cebeSEric Cheng 		if ((err = mac_addr_random(vnic->vn_mch,
295da14cebeSEric Cheng 		    prefix_len, mac_addr_arg, &mac_diag)) != 0) {
296da14cebeSEric Cheng 			*diag = vnic_mac2vnic_diag(mac_diag);
297843e1988Sjohnlev 			return (err);
298843e1988Sjohnlev 		}
299da14cebeSEric Cheng 		*addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
300da14cebeSEric Cheng 		vnic_addr_type = VNIC_MAC_ADDR_TYPE_RANDOM;
301da14cebeSEric Cheng 		break;
302da14cebeSEric Cheng 	case VNIC_MAC_ADDR_TYPE_PRIMARY:
303da14cebeSEric Cheng 		/*
304da14cebeSEric Cheng 		 * We get the address here since we copy it in the
305da14cebeSEric Cheng 		 * vnic's vn_addr.
3060dc2366fSVenugopal Iyer 		 * We can't ask for hardware resources since we
3070dc2366fSVenugopal Iyer 		 * don't currently support hardware classification
3080dc2366fSVenugopal Iyer 		 * for these MAC clients.
309da14cebeSEric Cheng 		 */
3100dc2366fSVenugopal Iyer 		if (req_hwgrp_flag) {
3110dc2366fSVenugopal Iyer 			*diag = VNIC_IOC_DIAG_NO_HWRINGS;
3120dc2366fSVenugopal Iyer 			return (ENOTSUP);
3130dc2366fSVenugopal Iyer 		}
314da14cebeSEric Cheng 		mac_unicast_primary_get(vnic->vn_lower_mh, mac_addr_arg);
315da14cebeSEric Cheng 		*addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
316da14cebeSEric Cheng 		mac_flags |= MAC_UNICAST_VNIC_PRIMARY;
317da14cebeSEric Cheng 		break;
318843e1988Sjohnlev 	}
319843e1988Sjohnlev 
320da14cebeSEric Cheng 	vnic->vn_addr_type = vnic_addr_type;
321843e1988Sjohnlev 
322da14cebeSEric Cheng 	err = mac_unicast_add(vnic->vn_mch, mac_addr_arg, mac_flags,
323da14cebeSEric Cheng 	    &vnic->vn_muh, vid, &mac_diag);
324da14cebeSEric Cheng 	if (err != 0) {
325da14cebeSEric Cheng 		if (vnic_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
326da14cebeSEric Cheng 			/* release factory MAC address */
327da14cebeSEric Cheng 			mac_addr_factory_release(vnic->vn_mch, *addr_slot);
328da14cebeSEric Cheng 		}
329da14cebeSEric Cheng 		*diag = vnic_mac2vnic_diag(mac_diag);
330843e1988Sjohnlev 	}
331843e1988Sjohnlev 
332843e1988Sjohnlev 	return (err);
333843e1988Sjohnlev }
334843e1988Sjohnlev 
335843e1988Sjohnlev /*
336843e1988Sjohnlev  * Create a new VNIC upon request from administrator.
337843e1988Sjohnlev  * Returns 0 on success, an errno on failure.
338843e1988Sjohnlev  */
339da14cebeSEric Cheng /* ARGSUSED */
340843e1988Sjohnlev int
vnic_dev_create(datalink_id_t vnic_id,datalink_id_t linkid,vnic_mac_addr_type_t * vnic_addr_type,int * mac_len,uchar_t * mac_addr,int * mac_slot,uint_t mac_prefix_len,uint16_t vid,vrid_t vrid,int af,mac_resource_props_t * mrp,uint32_t flags,vnic_ioc_diag_t * diag,cred_t * credp)341da14cebeSEric Cheng vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid,
342da14cebeSEric Cheng     vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr,
3431cb875aeSCathy Zhou     int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid,
3441cb875aeSCathy Zhou     int af, mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag,
3452b24ab6bSSebastien Roy     cred_t *credp)
346843e1988Sjohnlev {
347da14cebeSEric Cheng 	vnic_t *vnic;
348843e1988Sjohnlev 	mac_register_t *mac;
349843e1988Sjohnlev 	int err;
350da14cebeSEric Cheng 	boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0);
351da14cebeSEric Cheng 	char vnic_name[MAXNAMELEN];
352da14cebeSEric Cheng 	const mac_info_t *minfop;
3530dc2366fSVenugopal Iyer 	uint32_t req_hwgrp_flag = B_FALSE;
354843e1988Sjohnlev 
355da14cebeSEric Cheng 	*diag = VNIC_IOC_DIAG_NONE;
356843e1988Sjohnlev 
357843e1988Sjohnlev 	rw_enter(&vnic_lock, RW_WRITER);
358843e1988Sjohnlev 
35984de666eSRyan Zezeski 	/* Does a VNIC with the same id already exist? */
360843e1988Sjohnlev 	err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
361843e1988Sjohnlev 	    (mod_hash_val_t *)&vnic);
362843e1988Sjohnlev 	if (err == 0) {
363843e1988Sjohnlev 		rw_exit(&vnic_lock);
364843e1988Sjohnlev 		return (EEXIST);
365843e1988Sjohnlev 	}
366843e1988Sjohnlev 
367843e1988Sjohnlev 	vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP);
368843e1988Sjohnlev 	if (vnic == NULL) {
369843e1988Sjohnlev 		rw_exit(&vnic_lock);
370843e1988Sjohnlev 		return (ENOMEM);
371843e1988Sjohnlev 	}
372843e1988Sjohnlev 
373843e1988Sjohnlev 	bzero(vnic, sizeof (*vnic));
374843e1988Sjohnlev 
375a25df667SRobert Mustacchi 	vnic->vn_ls = LINK_STATE_UNKNOWN;
376da14cebeSEric Cheng 	vnic->vn_id = vnic_id;
377da14cebeSEric Cheng 	vnic->vn_link_id = linkid;
3781cb875aeSCathy Zhou 	vnic->vn_vrid = vrid;
3791cb875aeSCathy Zhou 	vnic->vn_af = af;
380843e1988Sjohnlev 
381da14cebeSEric Cheng 	if (!is_anchor) {
382da14cebeSEric Cheng 		if (linkid == DATALINK_INVALID_LINKID) {
383da14cebeSEric Cheng 			err = EINVAL;
384da14cebeSEric Cheng 			goto bail;
385da14cebeSEric Cheng 		}
386da14cebeSEric Cheng 
387da14cebeSEric Cheng 		/*
388da14cebeSEric Cheng 		 * Open the lower MAC and assign its initial bandwidth and
389da14cebeSEric Cheng 		 * MAC address. We do this here during VNIC creation and
390da14cebeSEric Cheng 		 * do not wait until the upper MAC client open so that we
391da14cebeSEric Cheng 		 * can validate the VNIC creation parameters (bandwidth,
392da14cebeSEric Cheng 		 * MAC address, etc) and reserve a factory MAC address if
393da14cebeSEric Cheng 		 * one was requested.
394da14cebeSEric Cheng 		 */
395da14cebeSEric Cheng 		err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh);
396da14cebeSEric Cheng 		if (err != 0)
397da14cebeSEric Cheng 			goto bail;
398da14cebeSEric Cheng 
399da14cebeSEric Cheng 		/*
400da14cebeSEric Cheng 		 * VNIC(vlan) over VNICs(vlans) is not supported.
401da14cebeSEric Cheng 		 */
402da14cebeSEric Cheng 		if (mac_is_vnic(vnic->vn_lower_mh)) {
403da14cebeSEric Cheng 			err = EINVAL;
404da14cebeSEric Cheng 			goto bail;
405da14cebeSEric Cheng 		}
406da14cebeSEric Cheng 
407da14cebeSEric Cheng 		/* only ethernet support for now */
408da14cebeSEric Cheng 		minfop = mac_info(vnic->vn_lower_mh);
409da14cebeSEric Cheng 		if (minfop->mi_nativemedia != DL_ETHER) {
410da14cebeSEric Cheng 			err = ENOTSUP;
411da14cebeSEric Cheng 			goto bail;
412da14cebeSEric Cheng 		}
413da14cebeSEric Cheng 
414da14cebeSEric Cheng 		(void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL,
415da14cebeSEric Cheng 		    NULL);
416da14cebeSEric Cheng 		err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch,
4170dc2366fSVenugopal Iyer 		    vnic_name, MAC_OPEN_FLAGS_IS_VNIC);
418da14cebeSEric Cheng 		if (err != 0)
419da14cebeSEric Cheng 			goto bail;
420da14cebeSEric Cheng 
421da14cebeSEric Cheng 		/* assign a MAC address to the VNIC */
422da14cebeSEric Cheng 
423da14cebeSEric Cheng 		err = vnic_unicast_add(vnic, *vnic_addr_type, mac_slot,
4240dc2366fSVenugopal Iyer 		    mac_prefix_len, mac_len, mac_addr, flags, diag, vid,
4250dc2366fSVenugopal Iyer 		    req_hwgrp_flag);
426da14cebeSEric Cheng 		if (err != 0) {
427da14cebeSEric Cheng 			vnic->vn_muh = NULL;
4280dc2366fSVenugopal Iyer 			if (diag != NULL && req_hwgrp_flag)
429da14cebeSEric Cheng 				*diag = VNIC_IOC_DIAG_NO_HWRINGS;
430da14cebeSEric Cheng 			goto bail;
431da14cebeSEric Cheng 		}
432da14cebeSEric Cheng 
433da14cebeSEric Cheng 		/* register to receive notification from underlying MAC */
434da14cebeSEric Cheng 		vnic->vn_mnh = mac_notify_add(vnic->vn_lower_mh, vnic_notify_cb,
435da14cebeSEric Cheng 		    vnic);
436da14cebeSEric Cheng 
437da14cebeSEric Cheng 		*vnic_addr_type = vnic->vn_addr_type;
438da14cebeSEric Cheng 		vnic->vn_addr_len = *mac_len;
439da14cebeSEric Cheng 		vnic->vn_vid = vid;
440da14cebeSEric Cheng 
441da14cebeSEric Cheng 		bcopy(mac_addr, vnic->vn_addr, vnic->vn_addr_len);
442da14cebeSEric Cheng 
443da14cebeSEric Cheng 		if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY)
444da14cebeSEric Cheng 			vnic->vn_slot_id = *mac_slot;
445da14cebeSEric Cheng 
4469056fcebSCathy Zhou 		/*
4479056fcebSCathy Zhou 		 * Set the initial VNIC capabilities. If the VNIC is created
4489056fcebSCathy Zhou 		 * over MACs which does not support nactive vlan, disable
4499056fcebSCathy Zhou 		 * VNIC's hardware checksum capability if its VID is not 0,
4509056fcebSCathy Zhou 		 * since the underlying MAC would get the hardware checksum
4519056fcebSCathy Zhou 		 * offset wrong in case of VLAN packets.
4529056fcebSCathy Zhou 		 */
4539056fcebSCathy Zhou 		if (vid == 0 || !mac_capab_get(vnic->vn_lower_mh,
4549056fcebSCathy Zhou 		    MAC_CAPAB_NO_NATIVEVLAN, NULL)) {
4559056fcebSCathy Zhou 			if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_HCKSUM,
4569056fcebSCathy Zhou 			    &vnic->vn_hcksum_txflags))
4579056fcebSCathy Zhou 				vnic->vn_hcksum_txflags = 0;
4589056fcebSCathy Zhou 		} else {
459da14cebeSEric Cheng 			vnic->vn_hcksum_txflags = 0;
4609056fcebSCathy Zhou 		}
461c61a1653SRyan Zezeski 
462c61a1653SRyan Zezeski 		/*
463c61a1653SRyan Zezeski 		 * Check for LSO capabilities. LSO implementations
464c61a1653SRyan Zezeski 		 * depend on hardware checksumming, so the same
465c61a1653SRyan Zezeski 		 * requirement is enforced here.
466c61a1653SRyan Zezeski 		 */
467c61a1653SRyan Zezeski 		if (vnic->vn_hcksum_txflags != 0) {
468c61a1653SRyan Zezeski 			if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_LSO,
469c61a1653SRyan Zezeski 			    &vnic->vn_cap_lso)) {
470c61a1653SRyan Zezeski 				vnic->vn_cap_lso.lso_flags = 0;
471c61a1653SRyan Zezeski 			}
472c61a1653SRyan Zezeski 		} else {
473c61a1653SRyan Zezeski 			vnic->vn_cap_lso.lso_flags = 0;
474c61a1653SRyan Zezeski 		}
475da14cebeSEric Cheng 	}
476843e1988Sjohnlev 
477843e1988Sjohnlev 	/* register with the MAC module */
478843e1988Sjohnlev 	if ((mac = mac_alloc(MAC_VERSION)) == NULL)
479843e1988Sjohnlev 		goto bail;
480843e1988Sjohnlev 
481843e1988Sjohnlev 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
482843e1988Sjohnlev 	mac->m_driver = vnic;
483843e1988Sjohnlev 	mac->m_dip = vnic_get_dip();
484d62bc4baSyz 	mac->m_instance = (uint_t)-1;
485843e1988Sjohnlev 	mac->m_src_addr = vnic->vn_addr;
486843e1988Sjohnlev 	mac->m_callbacks = &vnic_m_callbacks;
487843e1988Sjohnlev 
488da14cebeSEric Cheng 	if (!is_anchor) {
489da14cebeSEric Cheng 		/*
490da14cebeSEric Cheng 		 * If this is a VNIC based VLAN, then we check for the
491da14cebeSEric Cheng 		 * margin unless it has been created with the force
492da14cebeSEric Cheng 		 * flag. If we are configuring a VLAN over an etherstub,
493da14cebeSEric Cheng 		 * we don't check the margin even if force is not set.
494da14cebeSEric Cheng 		 */
495da14cebeSEric Cheng 		if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) {
496da14cebeSEric Cheng 			if (vid != VLAN_ID_NONE)
497da14cebeSEric Cheng 				vnic->vn_force = B_TRUE;
498da14cebeSEric Cheng 			/*
499da14cebeSEric Cheng 			 * As the current margin size of the underlying mac is
500da14cebeSEric Cheng 			 * used to determine the margin size of the VNIC
501da14cebeSEric Cheng 			 * itself, request the underlying mac not to change
502da14cebeSEric Cheng 			 * to a smaller margin size.
503da14cebeSEric Cheng 			 */
504da14cebeSEric Cheng 			err = mac_margin_add(vnic->vn_lower_mh,
505da14cebeSEric Cheng 			    &vnic->vn_margin, B_TRUE);
506da14cebeSEric Cheng 			ASSERT(err == 0);
507da14cebeSEric Cheng 		} else {
508da14cebeSEric Cheng 			vnic->vn_margin = VLAN_TAGSZ;
509da14cebeSEric Cheng 			err = mac_margin_add(vnic->vn_lower_mh,
510da14cebeSEric Cheng 			    &vnic->vn_margin, B_FALSE);
511da14cebeSEric Cheng 			if (err != 0) {
512da14cebeSEric Cheng 				mac_free(mac);
513da14cebeSEric Cheng 				if (diag != NULL)
514da14cebeSEric Cheng 					*diag = VNIC_IOC_DIAG_MACMARGIN_INVALID;
515da14cebeSEric Cheng 				goto bail;
516da14cebeSEric Cheng 			}
517da14cebeSEric Cheng 		}
518da14cebeSEric Cheng 
519da14cebeSEric Cheng 		mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu,
520da14cebeSEric Cheng 		    &mac->m_max_sdu);
521a776d98eSRobert Mustacchi 		err = mac_mtu_add(vnic->vn_lower_mh, &mac->m_max_sdu, B_FALSE);
522a776d98eSRobert Mustacchi 		if (err != 0) {
523a776d98eSRobert Mustacchi 			VERIFY(mac_margin_remove(vnic->vn_lower_mh,
524a776d98eSRobert Mustacchi 			    vnic->vn_margin) == 0);
525a776d98eSRobert Mustacchi 			mac_free(mac);
526a776d98eSRobert Mustacchi 			if (diag != NULL)
527a776d98eSRobert Mustacchi 				*diag = VNIC_IOC_DIAG_MACMTU_INVALID;
528a776d98eSRobert Mustacchi 			goto bail;
529a776d98eSRobert Mustacchi 		}
530a776d98eSRobert Mustacchi 		vnic->vn_mtu = mac->m_max_sdu;
531da14cebeSEric Cheng 	} else {
532da14cebeSEric Cheng 		vnic->vn_margin = VLAN_TAGSZ;
533dcb12fb7SRobert Mustacchi 		mac->m_min_sdu = 1;
534f0f2c3a5SGirish Moodalbail 		mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU;
535a776d98eSRobert Mustacchi 		vnic->vn_mtu = ANCHOR_VNIC_MAX_MTU;
536da14cebeSEric Cheng 	}
537843e1988Sjohnlev 
538d62bc4baSyz 	mac->m_margin = vnic->vn_margin;
539da14cebeSEric Cheng 
540843e1988Sjohnlev 	err = mac_register(mac, &vnic->vn_mh);
541843e1988Sjohnlev 	mac_free(mac);
542d62bc4baSyz 	if (err != 0) {
543a776d98eSRobert Mustacchi 		if (!is_anchor) {
544a776d98eSRobert Mustacchi 			VERIFY(mac_mtu_remove(vnic->vn_lower_mh,
545a776d98eSRobert Mustacchi 			    vnic->vn_mtu) == 0);
546a776d98eSRobert Mustacchi 			VERIFY(mac_margin_remove(vnic->vn_lower_mh,
547a776d98eSRobert Mustacchi 			    vnic->vn_margin) == 0);
548a776d98eSRobert Mustacchi 		}
549843e1988Sjohnlev 		goto bail;
550d62bc4baSyz 	}
551d62bc4baSyz 
552da14cebeSEric Cheng 	/* Set the VNIC's MAC in the client */
55354ba2481SMax Bruning 	if (!is_anchor) {
5540dc2366fSVenugopal Iyer 		mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh, mrp);
555da14cebeSEric Cheng 
55654ba2481SMax Bruning 		if (mrp != NULL) {
55754ba2481SMax Bruning 			if ((mrp->mrp_mask & MRP_RX_RINGS) != 0 ||
55854ba2481SMax Bruning 			    (mrp->mrp_mask & MRP_TX_RINGS) != 0) {
55954ba2481SMax Bruning 				req_hwgrp_flag = B_TRUE;
56054ba2481SMax Bruning 			}
56154ba2481SMax Bruning 			err = mac_client_set_resources(vnic->vn_mch, mrp);
56254ba2481SMax Bruning 			if (err != 0) {
563a776d98eSRobert Mustacchi 				VERIFY(mac_mtu_remove(vnic->vn_lower_mh,
564a776d98eSRobert Mustacchi 				    vnic->vn_mtu) == 0);
565a776d98eSRobert Mustacchi 				VERIFY(mac_margin_remove(vnic->vn_lower_mh,
566a776d98eSRobert Mustacchi 				    vnic->vn_margin) == 0);
56754ba2481SMax Bruning 				(void) mac_unregister(vnic->vn_mh);
56854ba2481SMax Bruning 				goto bail;
56954ba2481SMax Bruning 			}
57054ba2481SMax Bruning 		}
57154ba2481SMax Bruning 	}
57254ba2481SMax Bruning 
5732b24ab6bSSebastien Roy 	err = dls_devnet_create(vnic->vn_mh, vnic->vn_id, crgetzoneid(credp));
5742b24ab6bSSebastien Roy 	if (err != 0) {
575a776d98eSRobert Mustacchi 		if (!is_anchor) {
576a776d98eSRobert Mustacchi 			VERIFY(mac_mtu_remove(vnic->vn_lower_mh,
577a776d98eSRobert Mustacchi 			    vnic->vn_mtu) == 0);
578a776d98eSRobert Mustacchi 			VERIFY(mac_margin_remove(vnic->vn_lower_mh,
579a776d98eSRobert Mustacchi 			    vnic->vn_margin) == 0);
580a776d98eSRobert Mustacchi 		}
581d62bc4baSyz 		(void) mac_unregister(vnic->vn_mh);
582d62bc4baSyz 		goto bail;
583d62bc4baSyz 	}
584843e1988Sjohnlev 
585843e1988Sjohnlev 	/* add new VNIC to hash table */
586843e1988Sjohnlev 	err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id),
587843e1988Sjohnlev 	    (mod_hash_val_t)vnic);
588843e1988Sjohnlev 	ASSERT(err == 0);
589843e1988Sjohnlev 	vnic_count++;
590843e1988Sjohnlev 
591bf773d37SRobert Mustacchi 	/*
592bf773d37SRobert Mustacchi 	 * Now that we've enabled this VNIC, we should go through and update the
593bf773d37SRobert Mustacchi 	 * link state by setting it to our parents.
594bf773d37SRobert Mustacchi 	 */
5952c4ec682SEric Cheng 	vnic->vn_enabled = B_TRUE;
596bf773d37SRobert Mustacchi 
597bf773d37SRobert Mustacchi 	if (is_anchor) {
598a25df667SRobert Mustacchi 		vnic->vn_ls = LINK_STATE_UP;
599bf773d37SRobert Mustacchi 	} else {
600a25df667SRobert Mustacchi 		vnic->vn_ls = mac_client_stat_get(vnic->vn_mch,
601a25df667SRobert Mustacchi 		    MAC_STAT_LINK_STATE);
602bf773d37SRobert Mustacchi 	}
603a25df667SRobert Mustacchi 	mac_link_update(vnic->vn_mh, vnic->vn_ls);
604bf773d37SRobert Mustacchi 
605843e1988Sjohnlev 	rw_exit(&vnic_lock);
606843e1988Sjohnlev 
607843e1988Sjohnlev 	return (0);
608843e1988Sjohnlev 
609843e1988Sjohnlev bail:
610843e1988Sjohnlev 	rw_exit(&vnic_lock);
611da14cebeSEric Cheng 	if (!is_anchor) {
612da14cebeSEric Cheng 		if (vnic->vn_mnh != NULL)
613da14cebeSEric Cheng 			(void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
614da14cebeSEric Cheng 		if (vnic->vn_muh != NULL)
615da14cebeSEric Cheng 			(void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
616da14cebeSEric Cheng 		if (vnic->vn_mch != NULL)
617da14cebeSEric Cheng 			mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
618da14cebeSEric Cheng 		if (vnic->vn_lower_mh != NULL)
619da14cebeSEric Cheng 			mac_close(vnic->vn_lower_mh);
620843e1988Sjohnlev 	}
621843e1988Sjohnlev 
622da14cebeSEric Cheng 	kmem_cache_free(vnic_cache, vnic);
623843e1988Sjohnlev 	return (err);
624843e1988Sjohnlev }
625843e1988Sjohnlev 
626843e1988Sjohnlev /*
627843e1988Sjohnlev  * Modify the properties of an existing VNIC.
628843e1988Sjohnlev  */
629843e1988Sjohnlev /* ARGSUSED */
630843e1988Sjohnlev int
vnic_dev_modify(datalink_id_t vnic_id,uint_t modify_mask,vnic_mac_addr_type_t mac_addr_type,uint_t mac_len,uchar_t * mac_addr,uint_t mac_slot,mac_resource_props_t * mrp)631d62bc4baSyz vnic_dev_modify(datalink_id_t vnic_id, uint_t modify_mask,
632da14cebeSEric Cheng     vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr,
633da14cebeSEric Cheng     uint_t mac_slot, mac_resource_props_t *mrp)
634843e1988Sjohnlev {
635843e1988Sjohnlev 	vnic_t *vnic = NULL;
636843e1988Sjohnlev 
637843e1988Sjohnlev 	rw_enter(&vnic_lock, RW_WRITER);
638843e1988Sjohnlev 
639843e1988Sjohnlev 	if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
640843e1988Sjohnlev 	    (mod_hash_val_t *)&vnic) != 0) {
641843e1988Sjohnlev 		rw_exit(&vnic_lock);
642843e1988Sjohnlev 		return (ENOENT);
643843e1988Sjohnlev 	}
644843e1988Sjohnlev 
645843e1988Sjohnlev 	rw_exit(&vnic_lock);
646843e1988Sjohnlev 
647da14cebeSEric Cheng 	return (0);
648843e1988Sjohnlev }
649843e1988Sjohnlev 
650da14cebeSEric Cheng /* ARGSUSED */
651843e1988Sjohnlev int
vnic_dev_delete(datalink_id_t vnic_id,uint32_t flags,cred_t * credp)6522b24ab6bSSebastien Roy vnic_dev_delete(datalink_id_t vnic_id, uint32_t flags, cred_t *credp)
653843e1988Sjohnlev {
654843e1988Sjohnlev 	vnic_t *vnic = NULL;
655843e1988Sjohnlev 	mod_hash_val_t val;
656d62bc4baSyz 	datalink_id_t tmpid;
657843e1988Sjohnlev 	int rc;
658843e1988Sjohnlev 
659843e1988Sjohnlev 	rw_enter(&vnic_lock, RW_WRITER);
660843e1988Sjohnlev 
661843e1988Sjohnlev 	if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
662843e1988Sjohnlev 	    (mod_hash_val_t *)&vnic) != 0) {
663843e1988Sjohnlev 		rw_exit(&vnic_lock);
664843e1988Sjohnlev 		return (ENOENT);
665843e1988Sjohnlev 	}
666843e1988Sjohnlev 
667da14cebeSEric Cheng 	if ((rc = dls_devnet_destroy(vnic->vn_mh, &tmpid, B_TRUE)) != 0) {
668d62bc4baSyz 		rw_exit(&vnic_lock);
669d62bc4baSyz 		return (rc);
670d62bc4baSyz 	}
671d62bc4baSyz 
672d62bc4baSyz 	ASSERT(vnic_id == tmpid);
673d62bc4baSyz 
674843e1988Sjohnlev 	/*
675843e1988Sjohnlev 	 * We cannot unregister the MAC yet. Unregistering would
676843e1988Sjohnlev 	 * free up mac_impl_t which should not happen at this time.
677da14cebeSEric Cheng 	 * So disable mac_impl_t by calling mac_disable(). This will prevent
678da14cebeSEric Cheng 	 * any new claims on mac_impl_t.
679843e1988Sjohnlev 	 */
680da14cebeSEric Cheng 	if ((rc = mac_disable(vnic->vn_mh)) != 0) {
6812b24ab6bSSebastien Roy 		(void) dls_devnet_create(vnic->vn_mh, vnic_id,
6822b24ab6bSSebastien Roy 		    crgetzoneid(credp));
683843e1988Sjohnlev 		rw_exit(&vnic_lock);
684da14cebeSEric Cheng 		return (rc);
685843e1988Sjohnlev 	}
686843e1988Sjohnlev 
6871a41ca23SJerry Jelinek 	vnic_cleanup_secondary_macs(vnic, vnic->vn_nhandles);
6881a41ca23SJerry Jelinek 
6892c4ec682SEric Cheng 	vnic->vn_enabled = B_FALSE;
690843e1988Sjohnlev 	(void) mod_hash_remove(vnic_hash, VNIC_HASH_KEY(vnic_id), &val);
691843e1988Sjohnlev 	ASSERT(vnic == (vnic_t *)val);
692843e1988Sjohnlev 	vnic_count--;
693843e1988Sjohnlev 	rw_exit(&vnic_lock);
694843e1988Sjohnlev 
695843e1988Sjohnlev 	/*
696da14cebeSEric Cheng 	 * XXX-nicolas shouldn't have a void cast here, if it's
697da14cebeSEric Cheng 	 * expected that the function will never fail, then we should
698da14cebeSEric Cheng 	 * have an ASSERT().
699843e1988Sjohnlev 	 */
700da14cebeSEric Cheng 	(void) mac_unregister(vnic->vn_mh);
701843e1988Sjohnlev 
702da14cebeSEric Cheng 	if (vnic->vn_lower_mh != NULL) {
703da14cebeSEric Cheng 		/*
704da14cebeSEric Cheng 		 * Check if MAC address for the vnic was obtained from the
705da14cebeSEric Cheng 		 * factory MAC addresses. If yes, release it.
706da14cebeSEric Cheng 		 */
707da14cebeSEric Cheng 		if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
708da14cebeSEric Cheng 			(void) mac_addr_factory_release(vnic->vn_mch,
709da14cebeSEric Cheng 			    vnic->vn_slot_id);
710843e1988Sjohnlev 		}
711da14cebeSEric Cheng 		(void) mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin);
712a776d98eSRobert Mustacchi 		(void) mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu);
713da14cebeSEric Cheng 		(void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
714da14cebeSEric Cheng 		(void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
715da14cebeSEric Cheng 		mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
716da14cebeSEric Cheng 		mac_close(vnic->vn_lower_mh);
717843e1988Sjohnlev 	}
718843e1988Sjohnlev 
719da14cebeSEric Cheng 	kmem_cache_free(vnic_cache, vnic);
720da14cebeSEric Cheng 	return (0);
721843e1988Sjohnlev }
722843e1988Sjohnlev 
723da14cebeSEric Cheng /* ARGSUSED */
724843e1988Sjohnlev mblk_t *
vnic_m_tx(void * arg,mblk_t * mp_chain)725843e1988Sjohnlev vnic_m_tx(void *arg, mblk_t *mp_chain)
726843e1988Sjohnlev {
727843e1988Sjohnlev 	/*
728da14cebeSEric Cheng 	 * This function could be invoked for an anchor VNIC when sending
729da14cebeSEric Cheng 	 * broadcast and multicast packets, and unicast packets which did
730da14cebeSEric Cheng 	 * not match any local known destination.
731843e1988Sjohnlev 	 */
732da14cebeSEric Cheng 	freemsgchain(mp_chain);
733da14cebeSEric Cheng 	return (NULL);
734843e1988Sjohnlev }
735843e1988Sjohnlev 
736da14cebeSEric Cheng /*ARGSUSED*/
737843e1988Sjohnlev static void
vnic_m_ioctl(void * arg,queue_t * q,mblk_t * mp)738da14cebeSEric Cheng vnic_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
739843e1988Sjohnlev {
740da14cebeSEric Cheng 	miocnak(q, mp, 0, ENOTSUP);
741843e1988Sjohnlev }
742843e1988Sjohnlev 
743da14cebeSEric Cheng /*
744da14cebeSEric Cheng  * This entry point cannot be passed-through, since it is invoked
745da14cebeSEric Cheng  * for the per-VNIC kstats which must be exported independently
746da14cebeSEric Cheng  * of the existence of VNIC MAC clients.
747da14cebeSEric Cheng  */
748843e1988Sjohnlev static int
vnic_m_stat(void * arg,uint_t stat,uint64_t * val)749843e1988Sjohnlev vnic_m_stat(void *arg, uint_t stat, uint64_t *val)
750843e1988Sjohnlev {
751843e1988Sjohnlev 	vnic_t *vnic = arg;
752843e1988Sjohnlev 	int rval = 0;
753843e1988Sjohnlev 
754da14cebeSEric Cheng 	if (vnic->vn_lower_mh == NULL) {
755da14cebeSEric Cheng 		/*
756da14cebeSEric Cheng 		 * It's an anchor VNIC, which does not have any
757da14cebeSEric Cheng 		 * statistics in itself.
758da14cebeSEric Cheng 		 */
759da14cebeSEric Cheng 		return (ENOTSUP);
760da14cebeSEric Cheng 	}
761da14cebeSEric Cheng 
762da14cebeSEric Cheng 	/*
763da14cebeSEric Cheng 	 * ENOTSUP must be reported for unsupported stats, the VNIC
764da14cebeSEric Cheng 	 * driver reports a subset of the stats that would
765da14cebeSEric Cheng 	 * be returned by a real piece of hardware.
766da14cebeSEric Cheng 	 */
767843e1988Sjohnlev 
768843e1988Sjohnlev 	switch (stat) {
769da14cebeSEric Cheng 	case MAC_STAT_LINK_STATE:
770da14cebeSEric Cheng 	case MAC_STAT_LINK_UP:
771da14cebeSEric Cheng 	case MAC_STAT_PROMISC:
772843e1988Sjohnlev 	case MAC_STAT_IFSPEED:
773843e1988Sjohnlev 	case MAC_STAT_MULTIRCV:
774843e1988Sjohnlev 	case MAC_STAT_MULTIXMT:
775da14cebeSEric Cheng 	case MAC_STAT_BRDCSTRCV:
776843e1988Sjohnlev 	case MAC_STAT_BRDCSTXMT:
777da14cebeSEric Cheng 	case MAC_STAT_OPACKETS:
778da14cebeSEric Cheng 	case MAC_STAT_OBYTES:
779843e1988Sjohnlev 	case MAC_STAT_IERRORS:
780843e1988Sjohnlev 	case MAC_STAT_OERRORS:
781843e1988Sjohnlev 	case MAC_STAT_RBYTES:
782843e1988Sjohnlev 	case MAC_STAT_IPACKETS:
783da14cebeSEric Cheng 		*val = mac_client_stat_get(vnic->vn_mch, stat);
784843e1988Sjohnlev 		break;
785843e1988Sjohnlev 	default:
786843e1988Sjohnlev 		rval = ENOTSUP;
787843e1988Sjohnlev 	}
788843e1988Sjohnlev 
789843e1988Sjohnlev 	return (rval);
790843e1988Sjohnlev }
791843e1988Sjohnlev 
792da14cebeSEric Cheng /*
793da14cebeSEric Cheng  * Invoked by the upper MAC to retrieve the lower MAC client handle
794da14cebeSEric Cheng  * corresponding to a VNIC. A pointer to this function is obtained
795da14cebeSEric Cheng  * by the upper MAC via capability query.
796da14cebeSEric Cheng  *
797da14cebeSEric Cheng  * XXX-nicolas Note: this currently causes all VNIC MAC clients to
798da14cebeSEric Cheng  * receive the same MAC client handle for the same VNIC. This is ok
799da14cebeSEric Cheng  * as long as we have only one VNIC MAC client which sends and
800da14cebeSEric Cheng  * receives data, but we don't currently enforce this at the MAC layer.
801da14cebeSEric Cheng  */
802da14cebeSEric Cheng static void *
vnic_mac_client_handle(void * vnic_arg)803da14cebeSEric Cheng vnic_mac_client_handle(void *vnic_arg)
804da14cebeSEric Cheng {
805da14cebeSEric Cheng 	vnic_t *vnic = vnic_arg;
806da14cebeSEric Cheng 
807da14cebeSEric Cheng 	return (vnic->vn_mch);
808da14cebeSEric Cheng }
809da14cebeSEric Cheng 
8101a41ca23SJerry Jelinek /*
8111a41ca23SJerry Jelinek  * Invoked when updating the primary MAC so that the secondary MACs are
8121a41ca23SJerry Jelinek  * kept in sync.
8131a41ca23SJerry Jelinek  */
8141a41ca23SJerry Jelinek static void
vnic_mac_secondary_update(void * vnic_arg)8151a41ca23SJerry Jelinek vnic_mac_secondary_update(void *vnic_arg)
8161a41ca23SJerry Jelinek {
8171a41ca23SJerry Jelinek 	vnic_t *vn = vnic_arg;
8181a41ca23SJerry Jelinek 	int i;
8191a41ca23SJerry Jelinek 
8201a41ca23SJerry Jelinek 	for (i = 1; i <= vn->vn_nhandles; i++) {
8211a41ca23SJerry Jelinek 		mac_secondary_dup(vn->vn_mc_handles[0], vn->vn_mc_handles[i]);
8221a41ca23SJerry Jelinek 	}
8231a41ca23SJerry Jelinek }
824da14cebeSEric Cheng 
825843e1988Sjohnlev /*
826843e1988Sjohnlev  * Return information about the specified capability.
827843e1988Sjohnlev  */
828843e1988Sjohnlev /* ARGSUSED */
829843e1988Sjohnlev static boolean_t
vnic_m_capab_get(void * arg,mac_capab_t cap,void * cap_data)830843e1988Sjohnlev vnic_m_capab_get(void *arg, mac_capab_t cap, void *cap_data)
831843e1988Sjohnlev {
832843e1988Sjohnlev 	vnic_t *vnic = arg;
833843e1988Sjohnlev 
834843e1988Sjohnlev 	switch (cap) {
835843e1988Sjohnlev 	case MAC_CAPAB_HCKSUM: {
836843e1988Sjohnlev 		uint32_t *hcksum_txflags = cap_data;
837843e1988Sjohnlev 
838843e1988Sjohnlev 		*hcksum_txflags = vnic->vn_hcksum_txflags &
83967ba9ac9SRobert Mustacchi 		    (HCKSUM_INET_FULL_V4 | HCKSUM_INET_FULL_V6 |
84067ba9ac9SRobert Mustacchi 		    HCKSUM_IPHDRCKSUM | HCKSUM_INET_PARTIAL);
841843e1988Sjohnlev 		break;
842843e1988Sjohnlev 	}
843c61a1653SRyan Zezeski 	case MAC_CAPAB_LSO: {
844c61a1653SRyan Zezeski 		mac_capab_lso_t *cap_lso = cap_data;
845c61a1653SRyan Zezeski 
846c61a1653SRyan Zezeski 		if (vnic->vn_cap_lso.lso_flags == 0) {
847c61a1653SRyan Zezeski 			return (B_FALSE);
848c61a1653SRyan Zezeski 		}
849c61a1653SRyan Zezeski 		*cap_lso = vnic->vn_cap_lso;
850c61a1653SRyan Zezeski 		break;
851c61a1653SRyan Zezeski 	}
852da14cebeSEric Cheng 	case MAC_CAPAB_VNIC: {
853da14cebeSEric Cheng 		mac_capab_vnic_t *vnic_capab = cap_data;
854da14cebeSEric Cheng 
855da14cebeSEric Cheng 		if (vnic->vn_lower_mh == NULL) {
856da14cebeSEric Cheng 			/*
857da14cebeSEric Cheng 			 * It's an anchor VNIC, we don't have an underlying
858da14cebeSEric Cheng 			 * NIC and MAC client handle.
859da14cebeSEric Cheng 			 */
860da14cebeSEric Cheng 			return (B_FALSE);
861da14cebeSEric Cheng 		}
862da14cebeSEric Cheng 
863da14cebeSEric Cheng 		if (vnic_capab != NULL) {
864da14cebeSEric Cheng 			vnic_capab->mcv_arg = vnic;
865da14cebeSEric Cheng 			vnic_capab->mcv_mac_client_handle =
866da14cebeSEric Cheng 			    vnic_mac_client_handle;
8671a41ca23SJerry Jelinek 			vnic_capab->mcv_mac_secondary_update =
8681a41ca23SJerry Jelinek 			    vnic_mac_secondary_update;
869da14cebeSEric Cheng 		}
870da14cebeSEric Cheng 		break;
871da14cebeSEric Cheng 	}
872da14cebeSEric Cheng 	case MAC_CAPAB_ANCHOR_VNIC: {
873da14cebeSEric Cheng 		/* since it's an anchor VNIC we don't have lower mac handle */
874da14cebeSEric Cheng 		if (vnic->vn_lower_mh == NULL) {
875da14cebeSEric Cheng 			ASSERT(vnic->vn_link_id == 0);
876da14cebeSEric Cheng 			return (B_TRUE);
877da14cebeSEric Cheng 		}
878da14cebeSEric Cheng 		return (B_FALSE);
879da14cebeSEric Cheng 	}
880da14cebeSEric Cheng 	case MAC_CAPAB_NO_NATIVEVLAN:
8819056fcebSCathy Zhou 		return (B_FALSE);
882da14cebeSEric Cheng 	case MAC_CAPAB_NO_ZCOPY:
883da14cebeSEric Cheng 		return (B_TRUE);
8841cb875aeSCathy Zhou 	case MAC_CAPAB_VRRP: {
8851cb875aeSCathy Zhou 		mac_capab_vrrp_t *vrrp_capab = cap_data;
8861cb875aeSCathy Zhou 
8871cb875aeSCathy Zhou 		if (vnic->vn_vrid != 0) {
8881cb875aeSCathy Zhou 			if (vrrp_capab != NULL)
8891cb875aeSCathy Zhou 				vrrp_capab->mcv_af = vnic->vn_af;
8901cb875aeSCathy Zhou 			return (B_TRUE);
8911cb875aeSCathy Zhou 		}
8921cb875aeSCathy Zhou 		return (B_FALSE);
8931cb875aeSCathy Zhou 	}
894843e1988Sjohnlev 	default:
895843e1988Sjohnlev 		return (B_FALSE);
896843e1988Sjohnlev 	}
897843e1988Sjohnlev 	return (B_TRUE);
898843e1988Sjohnlev }
899843e1988Sjohnlev 
900da14cebeSEric Cheng /* ARGSUSED */
901843e1988Sjohnlev static int
vnic_m_start(void * arg)902843e1988Sjohnlev vnic_m_start(void *arg)
903843e1988Sjohnlev {
904843e1988Sjohnlev 	return (0);
905843e1988Sjohnlev }
906843e1988Sjohnlev 
907da14cebeSEric Cheng /* ARGSUSED */
908843e1988Sjohnlev static void
vnic_m_stop(void * arg)909843e1988Sjohnlev vnic_m_stop(void *arg)
910843e1988Sjohnlev {
911843e1988Sjohnlev }
912843e1988Sjohnlev 
913843e1988Sjohnlev /* ARGSUSED */
914843e1988Sjohnlev static int
vnic_m_promisc(void * arg,boolean_t on)915843e1988Sjohnlev vnic_m_promisc(void *arg, boolean_t on)
916843e1988Sjohnlev {
917da14cebeSEric Cheng 	return (0);
918843e1988Sjohnlev }
919843e1988Sjohnlev 
920da14cebeSEric Cheng /* ARGSUSED */
921843e1988Sjohnlev static int
vnic_m_multicst(void * arg,boolean_t add,const uint8_t * addrp)922843e1988Sjohnlev vnic_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
923843e1988Sjohnlev {
924da14cebeSEric Cheng 	return (0);
925843e1988Sjohnlev }
926843e1988Sjohnlev 
927843e1988Sjohnlev static int
vnic_m_unicst(void * arg,const uint8_t * macaddr)928da14cebeSEric Cheng vnic_m_unicst(void *arg, const uint8_t *macaddr)
929843e1988Sjohnlev {
930843e1988Sjohnlev 	vnic_t *vnic = arg;
931843e1988Sjohnlev 
932da14cebeSEric Cheng 	return (mac_vnic_unicast_set(vnic->vn_mch, macaddr));
933843e1988Sjohnlev }
934843e1988Sjohnlev 
9351a41ca23SJerry Jelinek static void
vnic_cleanup_secondary_macs(vnic_t * vn,int cnt)9361a41ca23SJerry Jelinek vnic_cleanup_secondary_macs(vnic_t *vn, int cnt)
9371a41ca23SJerry Jelinek {
9381a41ca23SJerry Jelinek 	int i;
9391a41ca23SJerry Jelinek 
9401a41ca23SJerry Jelinek 	/* Remove existing secondaries (primary is at 0) */
9411a41ca23SJerry Jelinek 	for (i = 1; i <= cnt; i++) {
9421a41ca23SJerry Jelinek 		mac_rx_clear(vn->vn_mc_handles[i]);
9431a41ca23SJerry Jelinek 
9441a41ca23SJerry Jelinek 		/* unicast handle might not have been set yet */
9451a41ca23SJerry Jelinek 		if (vn->vn_mu_handles[i] != NULL)
9461a41ca23SJerry Jelinek 			(void) mac_unicast_remove(vn->vn_mc_handles[i],
9471a41ca23SJerry Jelinek 			    vn->vn_mu_handles[i]);
9481a41ca23SJerry Jelinek 
9491a41ca23SJerry Jelinek 		mac_secondary_cleanup(vn->vn_mc_handles[i]);
9501a41ca23SJerry Jelinek 
9511a41ca23SJerry Jelinek 		mac_client_close(vn->vn_mc_handles[i], MAC_CLOSE_FLAGS_IS_VNIC);
9521a41ca23SJerry Jelinek 
9531a41ca23SJerry Jelinek 		vn->vn_mu_handles[i] = NULL;
9541a41ca23SJerry Jelinek 		vn->vn_mc_handles[i] = NULL;
9551a41ca23SJerry Jelinek 	}
9561a41ca23SJerry Jelinek 
9571a41ca23SJerry Jelinek 	vn->vn_nhandles = 0;
9581a41ca23SJerry Jelinek }
9591a41ca23SJerry Jelinek 
9601a41ca23SJerry Jelinek /*
9611a41ca23SJerry Jelinek  * Setup secondary MAC addresses on the vnic. Due to limitations in the mac
9621a41ca23SJerry Jelinek  * code, each mac address must be associated with a mac_client (and the
9631a41ca23SJerry Jelinek  * flow that goes along with the client) so we need to create those clients
9641a41ca23SJerry Jelinek  * here.
9651a41ca23SJerry Jelinek  */
9661a41ca23SJerry Jelinek static int
vnic_set_secondary_macs(vnic_t * vn,mac_secondary_addr_t * msa)9671a41ca23SJerry Jelinek vnic_set_secondary_macs(vnic_t *vn, mac_secondary_addr_t *msa)
9681a41ca23SJerry Jelinek {
9691a41ca23SJerry Jelinek 	int i, err;
9701a41ca23SJerry Jelinek 	char primary_name[MAXNAMELEN];
9711a41ca23SJerry Jelinek 
9721a41ca23SJerry Jelinek 	/* First, remove pre-existing secondaries */
9731a41ca23SJerry Jelinek 	ASSERT(vn->vn_nhandles < MPT_MAXMACADDR);
9741a41ca23SJerry Jelinek 	vnic_cleanup_secondary_macs(vn, vn->vn_nhandles);
9751a41ca23SJerry Jelinek 
9761a41ca23SJerry Jelinek 	if (msa->ms_addrcnt == (uint32_t)-1)
9771a41ca23SJerry Jelinek 		msa->ms_addrcnt = 0;
9781a41ca23SJerry Jelinek 
9791a41ca23SJerry Jelinek 	vn->vn_nhandles = msa->ms_addrcnt;
9801a41ca23SJerry Jelinek 
9811a41ca23SJerry Jelinek 	(void) dls_mgmt_get_linkinfo(vn->vn_id, primary_name, NULL, NULL, NULL);
9821a41ca23SJerry Jelinek 
9831a41ca23SJerry Jelinek 	/*
9841a41ca23SJerry Jelinek 	 * Now add the new secondary MACs
9851a41ca23SJerry Jelinek 	 * Recall that the primary MAC address is the first element.
9861a41ca23SJerry Jelinek 	 * The secondary clients are named after the primary with their
9871a41ca23SJerry Jelinek 	 * index to distinguish them.
9881a41ca23SJerry Jelinek 	 */
9891a41ca23SJerry Jelinek 	for (i = 1; i <= vn->vn_nhandles; i++) {
9901a41ca23SJerry Jelinek 		uint8_t *addr;
9911a41ca23SJerry Jelinek 		mac_diag_t mac_diag;
9921a41ca23SJerry Jelinek 		char secondary_name[MAXNAMELEN];
9931a41ca23SJerry Jelinek 
9941a41ca23SJerry Jelinek 		(void) snprintf(secondary_name, sizeof (secondary_name),
9951a41ca23SJerry Jelinek 		    "%s%02d", primary_name, i);
9961a41ca23SJerry Jelinek 
9971a41ca23SJerry Jelinek 		err = mac_client_open(vn->vn_lower_mh, &vn->vn_mc_handles[i],
9981a41ca23SJerry Jelinek 		    secondary_name, MAC_OPEN_FLAGS_IS_VNIC);
9991a41ca23SJerry Jelinek 		if (err != 0) {
10001a41ca23SJerry Jelinek 			/* Remove any that we successfully added */
10011a41ca23SJerry Jelinek 			vnic_cleanup_secondary_macs(vn, --i);
10021a41ca23SJerry Jelinek 			return (err);
10031a41ca23SJerry Jelinek 		}
10041a41ca23SJerry Jelinek 
10051a41ca23SJerry Jelinek 		/*
10061a41ca23SJerry Jelinek 		 * Assign a MAC address to the VNIC
10071a41ca23SJerry Jelinek 		 *
10081a41ca23SJerry Jelinek 		 * Normally this would be done with vnic_unicast_add but since
10091a41ca23SJerry Jelinek 		 * we know these are fixed adddresses, and since we need to
10101a41ca23SJerry Jelinek 		 * save this in the proper array slot, we bypass that function
10111a41ca23SJerry Jelinek 		 * and go direct.
10121a41ca23SJerry Jelinek 		 */
10131a41ca23SJerry Jelinek 		addr = msa->ms_addrs[i - 1];
10141a41ca23SJerry Jelinek 		err = mac_unicast_add(vn->vn_mc_handles[i], addr, 0,
10151a41ca23SJerry Jelinek 		    &vn->vn_mu_handles[i], vn->vn_vid, &mac_diag);
10161a41ca23SJerry Jelinek 		if (err != 0) {
10171a41ca23SJerry Jelinek 			/* Remove any that we successfully added */
10181a41ca23SJerry Jelinek 			vnic_cleanup_secondary_macs(vn, i);
10191a41ca23SJerry Jelinek 			return (err);
10201a41ca23SJerry Jelinek 		}
10211a41ca23SJerry Jelinek 
10221a41ca23SJerry Jelinek 		/*
10231a41ca23SJerry Jelinek 		 * Setup the secondary the same way as the primary (i.e.
10241a41ca23SJerry Jelinek 		 * receiver function/argument (e.g. i_dls_link_rx, mac_pkt_drop,
10251a41ca23SJerry Jelinek 		 * etc.), the promisc list, and the resource controls).
10261a41ca23SJerry Jelinek 		 */
10271a41ca23SJerry Jelinek 		mac_secondary_dup(vn->vn_mc_handles[0], vn->vn_mc_handles[i]);
10281a41ca23SJerry Jelinek 	}
10291a41ca23SJerry Jelinek 
10301a41ca23SJerry Jelinek 	return (0);
10311a41ca23SJerry Jelinek }
10321a41ca23SJerry Jelinek 
10331a41ca23SJerry Jelinek static int
vnic_get_secondary_macs(vnic_t * vn,uint_t pr_valsize,void * pr_val)10341a41ca23SJerry Jelinek vnic_get_secondary_macs(vnic_t *vn, uint_t pr_valsize, void *pr_val)
10351a41ca23SJerry Jelinek {
10361a41ca23SJerry Jelinek 	int i;
10371a41ca23SJerry Jelinek 	mac_secondary_addr_t msa;
10381a41ca23SJerry Jelinek 
10391a41ca23SJerry Jelinek 	if (pr_valsize < sizeof (msa))
10401a41ca23SJerry Jelinek 		return (EINVAL);
10411a41ca23SJerry Jelinek 
10421a41ca23SJerry Jelinek 	/* Get existing addresses (primary is at 0) */
10431a41ca23SJerry Jelinek 	ASSERT(vn->vn_nhandles < MPT_MAXMACADDR);
10441a41ca23SJerry Jelinek 	for (i = 1; i <= vn->vn_nhandles; i++) {
10451a41ca23SJerry Jelinek 		ASSERT(vn->vn_mc_handles[i] != NULL);
10461a41ca23SJerry Jelinek 		mac_unicast_secondary_get(vn->vn_mc_handles[i],
10471a41ca23SJerry Jelinek 		    msa.ms_addrs[i - 1]);
10481a41ca23SJerry Jelinek 	}
10491a41ca23SJerry Jelinek 	msa.ms_addrcnt = vn->vn_nhandles;
10501a41ca23SJerry Jelinek 
10511a41ca23SJerry Jelinek 	bcopy(&msa, pr_val, sizeof (msa));
10521a41ca23SJerry Jelinek 	return (0);
10531a41ca23SJerry Jelinek }
10541a41ca23SJerry Jelinek 
1055ab6f61efSGirish Moodalbail /*
1056ab6f61efSGirish Moodalbail  * Callback functions for set/get of properties
1057ab6f61efSGirish Moodalbail  */
1058ab6f61efSGirish Moodalbail /*ARGSUSED*/
1059ab6f61efSGirish Moodalbail static int
vnic_m_setprop(void * m_driver,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,const void * pr_val)1060ab6f61efSGirish Moodalbail vnic_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
1061ab6f61efSGirish Moodalbail     uint_t pr_valsize, const void *pr_val)
1062ab6f61efSGirish Moodalbail {
106384de666eSRyan Zezeski 	int		err = 0;
1064ab6f61efSGirish Moodalbail 	vnic_t		*vn = m_driver;
1065ab6f61efSGirish Moodalbail 
1066ab6f61efSGirish Moodalbail 	switch (pr_num) {
1067ab6f61efSGirish Moodalbail 	case MAC_PROP_MTU: {
1068ab6f61efSGirish Moodalbail 		uint32_t	mtu;
1069ab6f61efSGirish Moodalbail 
1070ab6f61efSGirish Moodalbail 		if (pr_valsize < sizeof (mtu)) {
1071ab6f61efSGirish Moodalbail 			err = EINVAL;
1072ab6f61efSGirish Moodalbail 			break;
1073ab6f61efSGirish Moodalbail 		}
1074ab6f61efSGirish Moodalbail 		bcopy(pr_val, &mtu, sizeof (mtu));
1075a776d98eSRobert Mustacchi 
1076a776d98eSRobert Mustacchi 		if (vn->vn_link_id == DATALINK_INVALID_LINKID) {
1077a776d98eSRobert Mustacchi 			if (mtu < ANCHOR_VNIC_MIN_MTU ||
1078a776d98eSRobert Mustacchi 			    mtu > ANCHOR_VNIC_MAX_MTU) {
1079a776d98eSRobert Mustacchi 				err = EINVAL;
1080a776d98eSRobert Mustacchi 				break;
1081a776d98eSRobert Mustacchi 			}
1082a776d98eSRobert Mustacchi 		} else {
1083a776d98eSRobert Mustacchi 			err = mac_mtu_add(vn->vn_lower_mh, &mtu, B_FALSE);
1084a776d98eSRobert Mustacchi 			/*
1085a776d98eSRobert Mustacchi 			 * If it's not supported to set a value here, translate
1086a776d98eSRobert Mustacchi 			 * that to EINVAL, so user land gets a better idea of
1087a776d98eSRobert Mustacchi 			 * what went wrong. This realistically means that they
1088a776d98eSRobert Mustacchi 			 * violated the output of prop info.
1089a776d98eSRobert Mustacchi 			 */
1090a776d98eSRobert Mustacchi 			if (err == ENOTSUP)
1091a776d98eSRobert Mustacchi 				err = EINVAL;
1092a776d98eSRobert Mustacchi 			if (err != 0)
1093a776d98eSRobert Mustacchi 				break;
1094a776d98eSRobert Mustacchi 			VERIFY(mac_mtu_remove(vn->vn_lower_mh,
1095a776d98eSRobert Mustacchi 			    vn->vn_mtu) == 0);
1096f0f2c3a5SGirish Moodalbail 		}
1097a776d98eSRobert Mustacchi 		vn->vn_mtu = mtu;
1098ab6f61efSGirish Moodalbail 		err = mac_maxsdu_update(vn->vn_mh, mtu);
1099ab6f61efSGirish Moodalbail 		break;
1100ab6f61efSGirish Moodalbail 	}
110110a40492SRobert Mustacchi 	case MAC_PROP_VN_PROMISC_FILTERED: {
110210a40492SRobert Mustacchi 		boolean_t filtered;
110310a40492SRobert Mustacchi 
110410a40492SRobert Mustacchi 		if (pr_valsize < sizeof (filtered)) {
110510a40492SRobert Mustacchi 			err = EINVAL;
110610a40492SRobert Mustacchi 			break;
110710a40492SRobert Mustacchi 		}
110810a40492SRobert Mustacchi 
110910a40492SRobert Mustacchi 		bcopy(pr_val, &filtered, sizeof (filtered));
111010a40492SRobert Mustacchi 		mac_set_promisc_filtered(vn->vn_mch, filtered);
111110a40492SRobert Mustacchi 		break;
111210a40492SRobert Mustacchi 	}
11131a41ca23SJerry Jelinek 	case MAC_PROP_SECONDARY_ADDRS: {
11141a41ca23SJerry Jelinek 		mac_secondary_addr_t msa;
11151a41ca23SJerry Jelinek 
11161a41ca23SJerry Jelinek 		bcopy(pr_val, &msa, sizeof (msa));
11171a41ca23SJerry Jelinek 		err = vnic_set_secondary_macs(vn, &msa);
11181a41ca23SJerry Jelinek 		break;
11191a41ca23SJerry Jelinek 	}
1120a25df667SRobert Mustacchi 	case MAC_PROP_PRIVATE: {
1121a25df667SRobert Mustacchi 		if (vn->vn_link_id != DATALINK_INVALID_LINKID ||
1122a25df667SRobert Mustacchi 		    strcmp(pr_name, "_linkstate") != 0) {
1123a25df667SRobert Mustacchi 			err = ENOTSUP;
1124a25df667SRobert Mustacchi 			break;
1125a25df667SRobert Mustacchi 		}
1126a25df667SRobert Mustacchi 
11271f78f831SRyan Goodfellow 		if (strcmp(pr_val, "up") == 0) {
11281f78f831SRyan Goodfellow 			vn->vn_ls = LINK_STATE_UP;
11291f78f831SRyan Goodfellow 		} else if (strcmp(pr_val, "down") == 0) {
11301f78f831SRyan Goodfellow 			vn->vn_ls = LINK_STATE_DOWN;
11311f78f831SRyan Goodfellow 		} else if (strcmp(pr_val, "unknown") == 0) {
11321f78f831SRyan Goodfellow 			vn->vn_ls = LINK_STATE_UNKNOWN;
11331f78f831SRyan Goodfellow 		} else {
11341f78f831SRyan Goodfellow 			return (EINVAL);
1135a25df667SRobert Mustacchi 		}
1136a25df667SRobert Mustacchi 		mac_link_update(vn->vn_mh, vn->vn_ls);
1137a25df667SRobert Mustacchi 		break;
1138a25df667SRobert Mustacchi 	}
1139ab6f61efSGirish Moodalbail 	default:
11401a41ca23SJerry Jelinek 		err = ENOTSUP;
1141ab6f61efSGirish Moodalbail 		break;
1142ab6f61efSGirish Moodalbail 	}
1143ab6f61efSGirish Moodalbail 	return (err);
1144ab6f61efSGirish Moodalbail }
1145ab6f61efSGirish Moodalbail 
11461a41ca23SJerry Jelinek /* ARGSUSED */
11471a41ca23SJerry Jelinek static int
vnic_m_getprop(void * arg,const char * pr_name,mac_prop_id_t pr_num,uint_t pr_valsize,void * pr_val)11481a41ca23SJerry Jelinek vnic_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
11491a41ca23SJerry Jelinek     uint_t pr_valsize, void *pr_val)
11501a41ca23SJerry Jelinek {
11511a41ca23SJerry Jelinek 	vnic_t		*vn = arg;
115284de666eSRyan Zezeski 	int		ret = 0;
115310a40492SRobert Mustacchi 	boolean_t	out;
11541a41ca23SJerry Jelinek 
11551a41ca23SJerry Jelinek 	switch (pr_num) {
115610a40492SRobert Mustacchi 	case MAC_PROP_VN_PROMISC_FILTERED:
115710a40492SRobert Mustacchi 		out = mac_get_promisc_filtered(vn->vn_mch);
115810a40492SRobert Mustacchi 		ASSERT(pr_valsize >= sizeof (boolean_t));
115910a40492SRobert Mustacchi 		bcopy(&out, pr_val, sizeof (boolean_t));
116010a40492SRobert Mustacchi 		break;
11611a41ca23SJerry Jelinek 	case MAC_PROP_SECONDARY_ADDRS:
11621a41ca23SJerry Jelinek 		ret = vnic_get_secondary_macs(vn, pr_valsize, pr_val);
11631a41ca23SJerry Jelinek 		break;
1164a25df667SRobert Mustacchi 	case MAC_PROP_PRIVATE:
1165a25df667SRobert Mustacchi 		if (vn->vn_link_id != DATALINK_INVALID_LINKID) {
1166a25df667SRobert Mustacchi 			ret = EINVAL;
1167a25df667SRobert Mustacchi 			break;
1168a25df667SRobert Mustacchi 		}
1169a25df667SRobert Mustacchi 
1170a25df667SRobert Mustacchi 		if (strcmp(pr_name, "_linkstate") != 0) {
1171a25df667SRobert Mustacchi 			ret = EINVAL;
1172a25df667SRobert Mustacchi 			break;
1173a25df667SRobert Mustacchi 		}
11741f78f831SRyan Goodfellow 		if (vn->vn_ls == LINK_STATE_UP) {
11751f78f831SRyan Goodfellow 			(void) sprintf(pr_val, "up");
11761f78f831SRyan Goodfellow 		} else if (vn->vn_ls == LINK_STATE_DOWN) {
11771f78f831SRyan Goodfellow 			(void) sprintf(pr_val, "down");
11781f78f831SRyan Goodfellow 		} else {
11791f78f831SRyan Goodfellow 			(void) sprintf(pr_val, "unknown");
11801f78f831SRyan Goodfellow 		}
1181a25df667SRobert Mustacchi 		break;
11821a41ca23SJerry Jelinek 	default:
1183238d8f47SDale Ghent 		ret = ENOTSUP;
11841a41ca23SJerry Jelinek 		break;
11851a41ca23SJerry Jelinek 	}
11861a41ca23SJerry Jelinek 
11871a41ca23SJerry Jelinek 	return (ret);
11881a41ca23SJerry Jelinek }
11891a41ca23SJerry Jelinek 
11900dc2366fSVenugopal Iyer /* ARGSUSED */
1191a25df667SRobert Mustacchi static void
vnic_m_propinfo(void * m_driver,const char * pr_name,mac_prop_id_t pr_num,mac_prop_info_handle_t prh)1192a25df667SRobert Mustacchi vnic_m_propinfo(void *m_driver, const char *pr_name,
11930dc2366fSVenugopal Iyer     mac_prop_id_t pr_num, mac_prop_info_handle_t prh)
1194ab6f61efSGirish Moodalbail {
11950dc2366fSVenugopal Iyer 	vnic_t		*vn = m_driver;
1196f0f2c3a5SGirish Moodalbail 
1197f0f2c3a5SGirish Moodalbail 	switch (pr_num) {
1198f0f2c3a5SGirish Moodalbail 	case MAC_PROP_MTU:
1199a776d98eSRobert Mustacchi 		if (vn->vn_link_id == DATALINK_INVALID_LINKID) {
1200a776d98eSRobert Mustacchi 			mac_prop_info_set_range_uint32(prh,
1201a776d98eSRobert Mustacchi 			    ANCHOR_VNIC_MIN_MTU, ANCHOR_VNIC_MAX_MTU);
1202a776d98eSRobert Mustacchi 		} else {
1203a776d98eSRobert Mustacchi 			uint32_t		max;
1204a776d98eSRobert Mustacchi 			mac_perim_handle_t	mph;
1205a776d98eSRobert Mustacchi 			mac_propval_range_t	range;
1206a776d98eSRobert Mustacchi 
1207a776d98eSRobert Mustacchi 			/*
1208a776d98eSRobert Mustacchi 			 * The valid range for a VNIC's MTU is the minimum that
1209a776d98eSRobert Mustacchi 			 * the device supports and the current value of the
1210a776d98eSRobert Mustacchi 			 * device. A VNIC cannot increase the current MTU of the
1211a776d98eSRobert Mustacchi 			 * device. Therefore we need to get the range from the
1212a776d98eSRobert Mustacchi 			 * propinfo endpoint and current mtu from the
1213a776d98eSRobert Mustacchi 			 * traditional property endpoint.
1214a776d98eSRobert Mustacchi 			 */
1215a776d98eSRobert Mustacchi 			mac_perim_enter_by_mh(vn->vn_lower_mh, &mph);
1216a776d98eSRobert Mustacchi 			if (mac_get_prop(vn->vn_lower_mh, MAC_PROP_MTU, "mtu",
1217a776d98eSRobert Mustacchi 			    &max, sizeof (uint32_t)) != 0) {
1218a776d98eSRobert Mustacchi 				mac_perim_exit(mph);
1219a776d98eSRobert Mustacchi 				return;
1220a776d98eSRobert Mustacchi 			}
1221a776d98eSRobert Mustacchi 
1222a776d98eSRobert Mustacchi 			range.mpr_count = 1;
1223a776d98eSRobert Mustacchi 			if (mac_prop_info(vn->vn_lower_mh, MAC_PROP_MTU, "mtu",
1224a776d98eSRobert Mustacchi 			    NULL, 0, &range, NULL) != 0) {
1225a776d98eSRobert Mustacchi 				mac_perim_exit(mph);
1226a776d98eSRobert Mustacchi 				return;
1227a776d98eSRobert Mustacchi 			}
1228a776d98eSRobert Mustacchi 
1229a776d98eSRobert Mustacchi 			mac_prop_info_set_default_uint32(prh, max);
1230a776d98eSRobert Mustacchi 			mac_prop_info_set_range_uint32(prh,
1231a776d98eSRobert Mustacchi 			    range.mpr_range_uint32[0].mpur_min, max);
1232a776d98eSRobert Mustacchi 			mac_perim_exit(mph);
1233a776d98eSRobert Mustacchi 		}
1234f0f2c3a5SGirish Moodalbail 		break;
1235a25df667SRobert Mustacchi 	case MAC_PROP_PRIVATE:
1236a25df667SRobert Mustacchi 		if (vn->vn_link_id != DATALINK_INVALID_LINKID)
1237a25df667SRobert Mustacchi 			break;
1238a25df667SRobert Mustacchi 
1239a25df667SRobert Mustacchi 		if (strcmp(pr_name, "_linkstate") == 0) {
1240a25df667SRobert Mustacchi 			char buf[16];
1241a25df667SRobert Mustacchi 
1242a25df667SRobert Mustacchi 			mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
12431f78f831SRyan Goodfellow 			(void) sprintf(buf, "unknown");
1244a25df667SRobert Mustacchi 			mac_prop_info_set_default_str(prh, buf);
1245a25df667SRobert Mustacchi 		}
1246a25df667SRobert Mustacchi 		break;
1247f0f2c3a5SGirish Moodalbail 	}
1248f0f2c3a5SGirish Moodalbail }
1249ab6f61efSGirish Moodalbail 
12500dc2366fSVenugopal Iyer 
1251843e1988Sjohnlev int
vnic_info(vnic_info_t * info,cred_t * credp)12522b24ab6bSSebastien Roy vnic_info(vnic_info_t *info, cred_t *credp)
1253843e1988Sjohnlev {
1254da14cebeSEric Cheng 	vnic_t		*vnic;
1255da14cebeSEric Cheng 	int		err;
1256843e1988Sjohnlev 
12572b24ab6bSSebastien Roy 	/* Make sure that the VNIC link is visible from the caller's zone. */
12582b24ab6bSSebastien Roy 	if (!dls_devnet_islinkvisible(info->vn_vnic_id, crgetzoneid(credp)))
12592b24ab6bSSebastien Roy 		return (ENOENT);
12602b24ab6bSSebastien Roy 
1261da14cebeSEric Cheng 	rw_enter(&vnic_lock, RW_WRITER);
1262843e1988Sjohnlev 
1263da14cebeSEric Cheng 	err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(info->vn_vnic_id),
1264da14cebeSEric Cheng 	    (mod_hash_val_t *)&vnic);
1265da14cebeSEric Cheng 	if (err != 0) {
1266da14cebeSEric Cheng 		rw_exit(&vnic_lock);
1267da14cebeSEric Cheng 		return (ENOENT);
1268843e1988Sjohnlev 	}
1269843e1988Sjohnlev 
1270da14cebeSEric Cheng 	info->vn_link_id = vnic->vn_link_id;
1271da14cebeSEric Cheng 	info->vn_mac_addr_type = vnic->vn_addr_type;
1272da14cebeSEric Cheng 	info->vn_mac_len = vnic->vn_addr_len;
1273da14cebeSEric Cheng 	bcopy(vnic->vn_addr, info->vn_mac_addr, MAXMACADDRLEN);
1274da14cebeSEric Cheng 	info->vn_mac_slot = vnic->vn_slot_id;
1275da14cebeSEric Cheng 	info->vn_mac_prefix_len = 0;
1276da14cebeSEric Cheng 	info->vn_vid = vnic->vn_vid;
1277da14cebeSEric Cheng 	info->vn_force = vnic->vn_force;
12781cb875aeSCathy Zhou 	info->vn_vrid = vnic->vn_vrid;
12791cb875aeSCathy Zhou 	info->vn_af = vnic->vn_af;
1280843e1988Sjohnlev 
1281da14cebeSEric Cheng 	bzero(&info->vn_resource_props, sizeof (mac_resource_props_t));
1282da14cebeSEric Cheng 	if (vnic->vn_mch != NULL)
12831a41ca23SJerry Jelinek 		mac_client_get_resources(vnic->vn_mch,
12841a41ca23SJerry Jelinek 		    &info->vn_resource_props);
1285843e1988Sjohnlev 
1286843e1988Sjohnlev 	rw_exit(&vnic_lock);
1287843e1988Sjohnlev 	return (0);
1288843e1988Sjohnlev }
1289843e1988Sjohnlev 
1290da14cebeSEric Cheng static void
vnic_notify_cb(void * arg,mac_notify_type_t type)1291da14cebeSEric Cheng vnic_notify_cb(void *arg, mac_notify_type_t type)
1292843e1988Sjohnlev {
1293da14cebeSEric Cheng 	vnic_t *vnic = arg;
1294843e1988Sjohnlev 
1295843e1988Sjohnlev 	/*
12962c4ec682SEric Cheng 	 * Do not deliver notifications if the vnic is not fully initialized
12972c4ec682SEric Cheng 	 * or is in process of being torn down.
1298843e1988Sjohnlev 	 */
12992c4ec682SEric Cheng 	if (!vnic->vn_enabled)
1300da14cebeSEric Cheng 		return;
1301da14cebeSEric Cheng 
1302da14cebeSEric Cheng 	switch (type) {
1303da14cebeSEric Cheng 	case MAC_NOTE_UNICST:
13042c4ec682SEric Cheng 		/*
13052c4ec682SEric Cheng 		 * Only the VLAN VNIC needs to be notified with primary MAC
13062c4ec682SEric Cheng 		 * address change.
13072c4ec682SEric Cheng 		 */
13082c4ec682SEric Cheng 		if (vnic->vn_addr_type != VNIC_MAC_ADDR_TYPE_PRIMARY)
13092c4ec682SEric Cheng 			return;
13102c4ec682SEric Cheng 
1311da14cebeSEric Cheng 		/*  the unicast MAC address value */
1312da14cebeSEric Cheng 		mac_unicast_primary_get(vnic->vn_lower_mh, vnic->vn_addr);
1313da14cebeSEric Cheng 
1314da14cebeSEric Cheng 		/* notify its upper layer MAC about MAC address change */
1315da14cebeSEric Cheng 		mac_unicst_update(vnic->vn_mh, (const uint8_t *)vnic->vn_addr);
1316da14cebeSEric Cheng 		break;
13172c4ec682SEric Cheng 
13182c4ec682SEric Cheng 	case MAC_NOTE_LINK:
1319a25df667SRobert Mustacchi 		vnic->vn_ls = mac_client_stat_get(vnic->vn_mch,
1320a25df667SRobert Mustacchi 		    MAC_STAT_LINK_STATE);
1321a25df667SRobert Mustacchi 		mac_link_update(vnic->vn_mh, vnic->vn_ls);
13222c4ec682SEric Cheng 		break;
13232c4ec682SEric Cheng 
1324da14cebeSEric Cheng 	default:
1325da14cebeSEric Cheng 		break;
1326843e1988Sjohnlev 	}
1327843e1988Sjohnlev }
1328