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