xref: /illumos-gate/usr/src/uts/common/io/vnic/vnic_dev.c (revision 7b79d846)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/sysmacros.h>
28 #include <sys/conf.h>
29 #include <sys/cmn_err.h>
30 #include <sys/list.h>
31 #include <sys/ksynch.h>
32 #include <sys/kmem.h>
33 #include <sys/stream.h>
34 #include <sys/modctl.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/atomic.h>
38 #include <sys/stat.h>
39 #include <sys/modhash.h>
40 #include <sys/strsubr.h>
41 #include <sys/strsun.h>
42 #include <sys/dlpi.h>
43 #include <sys/mac.h>
44 #include <sys/mac_provider.h>
45 #include <sys/mac_client.h>
46 #include <sys/mac_client_priv.h>
47 #include <sys/mac_ether.h>
48 #include <sys/dls.h>
49 #include <sys/pattr.h>
50 #include <sys/time.h>
51 #include <sys/vlan.h>
52 #include <sys/vnic.h>
53 #include <sys/vnic_impl.h>
54 #include <sys/mac_flow_impl.h>
55 #include <inet/ip_impl.h>
56 
57 /*
58  * Note that for best performance, the VNIC is a passthrough design.
59  * For each VNIC corresponds a MAC client of the underlying MAC (lower MAC).
60  * This MAC client is opened by the VNIC driver at VNIC creation,
61  * and closed when the VNIC is deleted.
62  * When a MAC client of the VNIC itself opens a VNIC, the MAC layer
63  * (upper MAC) detects that the MAC being opened is a VNIC. Instead
64  * of allocating a new MAC client, it asks the VNIC driver to return
65  * the lower MAC client handle associated with the VNIC, and that handle
66  * is returned to the upper MAC client directly. This allows access
67  * by upper MAC clients of the VNIC to have direct access to the lower
68  * MAC client for the control path and data path.
69  *
70  * Due to this passthrough, some of the entry points exported by the
71  * VNIC driver are never directly invoked. These entry points include
72  * vnic_m_start, vnic_m_stop, vnic_m_promisc, vnic_m_multicst, etc.
73  */
74 
75 static int vnic_m_start(void *);
76 static void vnic_m_stop(void *);
77 static int vnic_m_promisc(void *, boolean_t);
78 static int vnic_m_multicst(void *, boolean_t, const uint8_t *);
79 static int vnic_m_unicst(void *, const uint8_t *);
80 static int vnic_m_stat(void *, uint_t, uint64_t *);
81 static void vnic_m_ioctl(void *, queue_t *, mblk_t *);
82 static int vnic_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
83     const void *);
84 static int vnic_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
85     uint_t, void *, uint_t *);
86 static mblk_t *vnic_m_tx(void *, mblk_t *);
87 static boolean_t vnic_m_capab_get(void *, mac_capab_t, void *);
88 static void vnic_notify_cb(void *, mac_notify_type_t);
89 
90 static kmem_cache_t	*vnic_cache;
91 static krwlock_t	vnic_lock;
92 static uint_t		vnic_count;
93 
94 /* hash of VNICs (vnic_t's), keyed by VNIC id */
95 static mod_hash_t	*vnic_hash;
96 #define	VNIC_HASHSZ	64
97 #define	VNIC_HASH_KEY(vnic_id)	((mod_hash_key_t)(uintptr_t)vnic_id)
98 
99 #define	VNIC_M_CALLBACK_FLAGS	\
100 	(MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP)
101 
102 static mac_callbacks_t vnic_m_callbacks = {
103 	VNIC_M_CALLBACK_FLAGS,
104 	vnic_m_stat,
105 	vnic_m_start,
106 	vnic_m_stop,
107 	vnic_m_promisc,
108 	vnic_m_multicst,
109 	vnic_m_unicst,
110 	vnic_m_tx,
111 	vnic_m_ioctl,
112 	vnic_m_capab_get,
113 	NULL,
114 	NULL,
115 	vnic_m_setprop,
116 	vnic_m_getprop
117 };
118 
119 void
120 vnic_dev_init(void)
121 {
122 	vnic_cache = kmem_cache_create("vnic_cache",
123 	    sizeof (vnic_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
124 
125 	vnic_hash = mod_hash_create_idhash("vnic_hash",
126 	    VNIC_HASHSZ, mod_hash_null_valdtor);
127 
128 	rw_init(&vnic_lock, NULL, RW_DEFAULT, NULL);
129 
130 	vnic_count = 0;
131 }
132 
133 void
134 vnic_dev_fini(void)
135 {
136 	ASSERT(vnic_count == 0);
137 
138 	rw_destroy(&vnic_lock);
139 	mod_hash_destroy_idhash(vnic_hash);
140 	kmem_cache_destroy(vnic_cache);
141 }
142 
143 uint_t
144 vnic_dev_count(void)
145 {
146 	return (vnic_count);
147 }
148 
149 static vnic_ioc_diag_t
150 vnic_mac2vnic_diag(mac_diag_t diag)
151 {
152 	switch (diag) {
153 	case MAC_DIAG_MACADDR_NIC:
154 		return (VNIC_IOC_DIAG_MACADDR_NIC);
155 	case MAC_DIAG_MACADDR_INUSE:
156 		return (VNIC_IOC_DIAG_MACADDR_INUSE);
157 	case MAC_DIAG_MACADDR_INVALID:
158 		return (VNIC_IOC_DIAG_MACADDR_INVALID);
159 	case MAC_DIAG_MACADDRLEN_INVALID:
160 		return (VNIC_IOC_DIAG_MACADDRLEN_INVALID);
161 	case MAC_DIAG_MACFACTORYSLOTINVALID:
162 		return (VNIC_IOC_DIAG_MACFACTORYSLOTINVALID);
163 	case MAC_DIAG_MACFACTORYSLOTUSED:
164 		return (VNIC_IOC_DIAG_MACFACTORYSLOTUSED);
165 	case MAC_DIAG_MACFACTORYSLOTALLUSED:
166 		return (VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED);
167 	case MAC_DIAG_MACFACTORYNOTSUP:
168 		return (VNIC_IOC_DIAG_MACFACTORYNOTSUP);
169 	case MAC_DIAG_MACPREFIX_INVALID:
170 		return (VNIC_IOC_DIAG_MACPREFIX_INVALID);
171 	case MAC_DIAG_MACPREFIXLEN_INVALID:
172 		return (VNIC_IOC_DIAG_MACPREFIXLEN_INVALID);
173 	case MAC_DIAG_MACNO_HWRINGS:
174 		return (VNIC_IOC_DIAG_NO_HWRINGS);
175 	default:
176 		return (VNIC_IOC_DIAG_NONE);
177 	}
178 }
179 
180 static int
181 vnic_unicast_add(vnic_t *vnic, vnic_mac_addr_type_t vnic_addr_type,
182     int *addr_slot, uint_t prefix_len, int *addr_len_ptr_arg,
183     uint8_t *mac_addr_arg, uint16_t flags, vnic_ioc_diag_t *diag,
184     uint16_t vid)
185 {
186 	mac_diag_t mac_diag;
187 	uint16_t mac_flags = 0;
188 	int err;
189 	uint_t addr_len;
190 
191 	if (flags & VNIC_IOC_CREATE_NODUPCHECK)
192 		mac_flags |= MAC_UNICAST_NODUPCHECK;
193 
194 	switch (vnic_addr_type) {
195 	case VNIC_MAC_ADDR_TYPE_FIXED:
196 		/*
197 		 * The MAC address value to assign to the VNIC
198 		 * is already provided in mac_addr_arg. addr_len_ptr_arg
199 		 * already contains the MAC address length.
200 		 */
201 		break;
202 
203 	case VNIC_MAC_ADDR_TYPE_RANDOM:
204 		/*
205 		 * Random MAC address. There are two sub-cases:
206 		 *
207 		 * 1 - If mac_len == 0, a new MAC address is generated.
208 		 *	The length of the MAC address to generated depends
209 		 *	on the type of MAC used. The prefix to use for the MAC
210 		 *	address is stored in the most significant bytes
211 		 *	of the mac_addr argument, and its length is specified
212 		 *	by the mac_prefix_len argument. This prefix can
213 		 *	correspond to a IEEE OUI in the case of Ethernet,
214 		 *	for example.
215 		 *
216 		 * 2 - If mac_len > 0, the address was already picked
217 		 *	randomly, and is now passed back during VNIC
218 		 *	re-creation. The mac_addr argument contains the MAC
219 		 *	address that was generated. We distinguish this
220 		 *	case from the fixed MAC address case, since we
221 		 *	want the user consumers to know, when they query
222 		 *	the list of VNICs, that a VNIC was assigned a
223 		 *	random MAC address vs assigned a fixed address
224 		 *	specified by the user.
225 		 */
226 
227 		/*
228 		 * If it's a pre-generated address, we're done. mac_addr_arg
229 		 * and addr_len_ptr_arg already contain the MAC address
230 		 * value and length.
231 		 */
232 		if (*addr_len_ptr_arg > 0)
233 			break;
234 
235 		/* generate a new random MAC address */
236 		if ((err = mac_addr_random(vnic->vn_mch,
237 		    prefix_len, mac_addr_arg, &mac_diag)) != 0) {
238 			*diag = vnic_mac2vnic_diag(mac_diag);
239 			return (err);
240 		}
241 		*addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
242 		break;
243 
244 	case VNIC_MAC_ADDR_TYPE_FACTORY:
245 		err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
246 		if (err != 0) {
247 			if (err == EINVAL)
248 				*diag = VNIC_IOC_DIAG_MACFACTORYSLOTINVALID;
249 			if (err == EBUSY)
250 				*diag = VNIC_IOC_DIAG_MACFACTORYSLOTUSED;
251 			if (err == ENOSPC)
252 				*diag = VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED;
253 			return (err);
254 		}
255 
256 		mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
257 		    mac_addr_arg, &addr_len, NULL, NULL);
258 		*addr_len_ptr_arg = addr_len;
259 		break;
260 
261 	case VNIC_MAC_ADDR_TYPE_AUTO:
262 		/* first try to allocate a factory MAC address */
263 		err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
264 		if (err == 0) {
265 			mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
266 			    mac_addr_arg, &addr_len, NULL, NULL);
267 			vnic_addr_type = VNIC_MAC_ADDR_TYPE_FACTORY;
268 			*addr_len_ptr_arg = addr_len;
269 			break;
270 		}
271 
272 		/*
273 		 * Allocating a factory MAC address failed, generate a
274 		 * random MAC address instead.
275 		 */
276 		if ((err = mac_addr_random(vnic->vn_mch,
277 		    prefix_len, mac_addr_arg, &mac_diag)) != 0) {
278 			*diag = vnic_mac2vnic_diag(mac_diag);
279 			return (err);
280 		}
281 		*addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
282 		vnic_addr_type = VNIC_MAC_ADDR_TYPE_RANDOM;
283 		break;
284 	case VNIC_MAC_ADDR_TYPE_PRIMARY:
285 		/*
286 		 * We get the address here since we copy it in the
287 		 * vnic's vn_addr.
288 		 */
289 		mac_unicast_primary_get(vnic->vn_lower_mh, mac_addr_arg);
290 		*addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
291 		mac_flags |= MAC_UNICAST_VNIC_PRIMARY;
292 		break;
293 	}
294 
295 	vnic->vn_addr_type = vnic_addr_type;
296 
297 	err = mac_unicast_add(vnic->vn_mch, mac_addr_arg, mac_flags,
298 	    &vnic->vn_muh, vid, &mac_diag);
299 	if (err != 0) {
300 		if (vnic_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
301 			/* release factory MAC address */
302 			mac_addr_factory_release(vnic->vn_mch, *addr_slot);
303 		}
304 		*diag = vnic_mac2vnic_diag(mac_diag);
305 	}
306 
307 	return (err);
308 }
309 
310 /*
311  * Create a new VNIC upon request from administrator.
312  * Returns 0 on success, an errno on failure.
313  */
314 /* ARGSUSED */
315 int
316 vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid,
317     vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr,
318     int *mac_slot, uint_t mac_prefix_len, uint16_t vid,
319     mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag)
320 {
321 	vnic_t *vnic;
322 	mac_register_t *mac;
323 	int err;
324 	boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0);
325 	char vnic_name[MAXNAMELEN];
326 	const mac_info_t *minfop;
327 	uint32_t req_hwgrp_flag = ((flags & VNIC_IOC_CREATE_REQ_HWRINGS) != 0) ?
328 	    MAC_OPEN_FLAGS_REQ_HWRINGS : 0;
329 
330 	*diag = VNIC_IOC_DIAG_NONE;
331 
332 	rw_enter(&vnic_lock, RW_WRITER);
333 
334 	/* does a VNIC with the same id already exist? */
335 	err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
336 	    (mod_hash_val_t *)&vnic);
337 	if (err == 0) {
338 		rw_exit(&vnic_lock);
339 		return (EEXIST);
340 	}
341 
342 	vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP);
343 	if (vnic == NULL) {
344 		rw_exit(&vnic_lock);
345 		return (ENOMEM);
346 	}
347 
348 	bzero(vnic, sizeof (*vnic));
349 
350 	vnic->vn_id = vnic_id;
351 	vnic->vn_link_id = linkid;
352 
353 	if (!is_anchor) {
354 		if (linkid == DATALINK_INVALID_LINKID) {
355 			err = EINVAL;
356 			goto bail;
357 		}
358 
359 		/*
360 		 * Open the lower MAC and assign its initial bandwidth and
361 		 * MAC address. We do this here during VNIC creation and
362 		 * do not wait until the upper MAC client open so that we
363 		 * can validate the VNIC creation parameters (bandwidth,
364 		 * MAC address, etc) and reserve a factory MAC address if
365 		 * one was requested.
366 		 */
367 		err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh);
368 		if (err != 0)
369 			goto bail;
370 
371 		/*
372 		 * VNIC(vlan) over VNICs(vlans) is not supported.
373 		 */
374 		if (mac_is_vnic(vnic->vn_lower_mh)) {
375 			err = EINVAL;
376 			goto bail;
377 		}
378 
379 		/* only ethernet support for now */
380 		minfop = mac_info(vnic->vn_lower_mh);
381 		if (minfop->mi_nativemedia != DL_ETHER) {
382 			err = ENOTSUP;
383 			goto bail;
384 		}
385 
386 		(void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL,
387 		    NULL);
388 		err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch,
389 		    vnic_name, MAC_OPEN_FLAGS_IS_VNIC | req_hwgrp_flag);
390 		if (err != 0)
391 			goto bail;
392 
393 		if (mrp != NULL) {
394 			err = mac_client_set_resources(vnic->vn_mch, mrp);
395 			if (err != 0)
396 				goto bail;
397 		}
398 		/* assign a MAC address to the VNIC */
399 
400 		err = vnic_unicast_add(vnic, *vnic_addr_type, mac_slot,
401 		    mac_prefix_len, mac_len, mac_addr, flags, diag, vid);
402 		if (err != 0) {
403 			vnic->vn_muh = NULL;
404 			if (diag != NULL && req_hwgrp_flag != 0)
405 				*diag = VNIC_IOC_DIAG_NO_HWRINGS;
406 			goto bail;
407 		}
408 
409 		/* register to receive notification from underlying MAC */
410 		vnic->vn_mnh = mac_notify_add(vnic->vn_lower_mh, vnic_notify_cb,
411 		    vnic);
412 
413 		*vnic_addr_type = vnic->vn_addr_type;
414 		vnic->vn_addr_len = *mac_len;
415 		vnic->vn_vid = vid;
416 
417 		bcopy(mac_addr, vnic->vn_addr, vnic->vn_addr_len);
418 
419 		if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY)
420 			vnic->vn_slot_id = *mac_slot;
421 
422 		/* set the initial VNIC capabilities */
423 		if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_HCKSUM,
424 		    &vnic->vn_hcksum_txflags))
425 			vnic->vn_hcksum_txflags = 0;
426 	}
427 
428 	/* register with the MAC module */
429 	if ((mac = mac_alloc(MAC_VERSION)) == NULL)
430 		goto bail;
431 
432 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
433 	mac->m_driver = vnic;
434 	mac->m_dip = vnic_get_dip();
435 	mac->m_instance = (uint_t)-1;
436 	mac->m_src_addr = vnic->vn_addr;
437 	mac->m_callbacks = &vnic_m_callbacks;
438 
439 	if (!is_anchor) {
440 		/*
441 		 * If this is a VNIC based VLAN, then we check for the
442 		 * margin unless it has been created with the force
443 		 * flag. If we are configuring a VLAN over an etherstub,
444 		 * we don't check the margin even if force is not set.
445 		 */
446 		if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) {
447 			if (vid != VLAN_ID_NONE)
448 				vnic->vn_force = B_TRUE;
449 			/*
450 			 * As the current margin size of the underlying mac is
451 			 * used to determine the margin size of the VNIC
452 			 * itself, request the underlying mac not to change
453 			 * to a smaller margin size.
454 			 */
455 			err = mac_margin_add(vnic->vn_lower_mh,
456 			    &vnic->vn_margin, B_TRUE);
457 			ASSERT(err == 0);
458 		} else {
459 			vnic->vn_margin = VLAN_TAGSZ;
460 			err = mac_margin_add(vnic->vn_lower_mh,
461 			    &vnic->vn_margin, B_FALSE);
462 			if (err != 0) {
463 				mac_free(mac);
464 				if (diag != NULL)
465 					*diag = VNIC_IOC_DIAG_MACMARGIN_INVALID;
466 				goto bail;
467 			}
468 		}
469 
470 		mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu,
471 		    &mac->m_max_sdu);
472 	} else {
473 		vnic->vn_margin = VLAN_TAGSZ;
474 		mac->m_min_sdu = 0;
475 		mac->m_max_sdu = 9000;
476 	}
477 
478 	mac->m_margin = vnic->vn_margin;
479 
480 	err = mac_register(mac, &vnic->vn_mh);
481 	mac_free(mac);
482 	if (err != 0) {
483 		VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
484 		    vnic->vn_margin) == 0);
485 		goto bail;
486 	}
487 
488 	/* Set the VNIC's MAC in the client */
489 	if (!is_anchor)
490 		mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh);
491 
492 	if ((err = dls_devnet_create(vnic->vn_mh, vnic->vn_id)) != 0) {
493 		VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
494 		    vnic->vn_margin) == 0);
495 		(void) mac_unregister(vnic->vn_mh);
496 		goto bail;
497 	}
498 
499 	/* add new VNIC to hash table */
500 	err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id),
501 	    (mod_hash_val_t)vnic);
502 	ASSERT(err == 0);
503 	vnic_count++;
504 
505 	vnic->vn_enabled = B_TRUE;
506 	rw_exit(&vnic_lock);
507 
508 	return (0);
509 
510 bail:
511 	rw_exit(&vnic_lock);
512 	if (!is_anchor) {
513 		if (vnic->vn_mnh != NULL)
514 			(void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
515 		if (vnic->vn_muh != NULL)
516 			(void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
517 		if (vnic->vn_mch != NULL)
518 			mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
519 		if (vnic->vn_lower_mh != NULL)
520 			mac_close(vnic->vn_lower_mh);
521 	}
522 
523 	kmem_cache_free(vnic_cache, vnic);
524 	return (err);
525 }
526 
527 /*
528  * Modify the properties of an existing VNIC.
529  */
530 /* ARGSUSED */
531 int
532 vnic_dev_modify(datalink_id_t vnic_id, uint_t modify_mask,
533     vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr,
534     uint_t mac_slot, mac_resource_props_t *mrp)
535 {
536 	vnic_t *vnic = NULL;
537 
538 	rw_enter(&vnic_lock, RW_WRITER);
539 
540 	if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
541 	    (mod_hash_val_t *)&vnic) != 0) {
542 		rw_exit(&vnic_lock);
543 		return (ENOENT);
544 	}
545 
546 	rw_exit(&vnic_lock);
547 
548 	return (0);
549 }
550 
551 /* ARGSUSED */
552 int
553 vnic_dev_delete(datalink_id_t vnic_id, uint32_t flags)
554 {
555 	vnic_t *vnic = NULL;
556 	mod_hash_val_t val;
557 	datalink_id_t tmpid;
558 	int rc;
559 
560 	rw_enter(&vnic_lock, RW_WRITER);
561 
562 	if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
563 	    (mod_hash_val_t *)&vnic) != 0) {
564 		rw_exit(&vnic_lock);
565 		return (ENOENT);
566 	}
567 
568 	if ((rc = dls_devnet_destroy(vnic->vn_mh, &tmpid, B_TRUE)) != 0) {
569 		rw_exit(&vnic_lock);
570 		return (rc);
571 	}
572 
573 	ASSERT(vnic_id == tmpid);
574 
575 	/*
576 	 * We cannot unregister the MAC yet. Unregistering would
577 	 * free up mac_impl_t which should not happen at this time.
578 	 * So disable mac_impl_t by calling mac_disable(). This will prevent
579 	 * any new claims on mac_impl_t.
580 	 */
581 	if ((rc = mac_disable(vnic->vn_mh)) != 0) {
582 		(void) dls_devnet_create(vnic->vn_mh, vnic_id);
583 		rw_exit(&vnic_lock);
584 		return (rc);
585 	}
586 
587 	vnic->vn_enabled = B_FALSE;
588 	(void) mod_hash_remove(vnic_hash, VNIC_HASH_KEY(vnic_id), &val);
589 	ASSERT(vnic == (vnic_t *)val);
590 	vnic_count--;
591 	rw_exit(&vnic_lock);
592 
593 	/*
594 	 * XXX-nicolas shouldn't have a void cast here, if it's
595 	 * expected that the function will never fail, then we should
596 	 * have an ASSERT().
597 	 */
598 	(void) mac_unregister(vnic->vn_mh);
599 
600 	if (vnic->vn_lower_mh != NULL) {
601 		/*
602 		 * Check if MAC address for the vnic was obtained from the
603 		 * factory MAC addresses. If yes, release it.
604 		 */
605 		if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
606 			(void) mac_addr_factory_release(vnic->vn_mch,
607 			    vnic->vn_slot_id);
608 		}
609 		(void) mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin);
610 		(void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
611 		(void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
612 		mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
613 		mac_close(vnic->vn_lower_mh);
614 	}
615 
616 	kmem_cache_free(vnic_cache, vnic);
617 	return (0);
618 }
619 
620 /* ARGSUSED */
621 mblk_t *
622 vnic_m_tx(void *arg, mblk_t *mp_chain)
623 {
624 	/*
625 	 * This function could be invoked for an anchor VNIC when sending
626 	 * broadcast and multicast packets, and unicast packets which did
627 	 * not match any local known destination.
628 	 */
629 	freemsgchain(mp_chain);
630 	return (NULL);
631 }
632 
633 /*ARGSUSED*/
634 static void
635 vnic_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
636 {
637 	miocnak(q, mp, 0, ENOTSUP);
638 }
639 
640 /*
641  * This entry point cannot be passed-through, since it is invoked
642  * for the per-VNIC kstats which must be exported independently
643  * of the existence of VNIC MAC clients.
644  */
645 static int
646 vnic_m_stat(void *arg, uint_t stat, uint64_t *val)
647 {
648 	vnic_t *vnic = arg;
649 	int rval = 0;
650 
651 	if (vnic->vn_lower_mh == NULL) {
652 		/*
653 		 * It's an anchor VNIC, which does not have any
654 		 * statistics in itself.
655 		 */
656 		return (ENOTSUP);
657 	}
658 
659 	/*
660 	 * ENOTSUP must be reported for unsupported stats, the VNIC
661 	 * driver reports a subset of the stats that would
662 	 * be returned by a real piece of hardware.
663 	 */
664 
665 	switch (stat) {
666 	case MAC_STAT_LINK_STATE:
667 	case MAC_STAT_LINK_UP:
668 	case MAC_STAT_PROMISC:
669 	case MAC_STAT_IFSPEED:
670 	case MAC_STAT_MULTIRCV:
671 	case MAC_STAT_MULTIXMT:
672 	case MAC_STAT_BRDCSTRCV:
673 	case MAC_STAT_BRDCSTXMT:
674 	case MAC_STAT_OPACKETS:
675 	case MAC_STAT_OBYTES:
676 	case MAC_STAT_IERRORS:
677 	case MAC_STAT_OERRORS:
678 	case MAC_STAT_RBYTES:
679 	case MAC_STAT_IPACKETS:
680 		*val = mac_client_stat_get(vnic->vn_mch, stat);
681 		break;
682 	default:
683 		rval = ENOTSUP;
684 	}
685 
686 	return (rval);
687 }
688 
689 /*
690  * Invoked by the upper MAC to retrieve the lower MAC client handle
691  * corresponding to a VNIC. A pointer to this function is obtained
692  * by the upper MAC via capability query.
693  *
694  * XXX-nicolas Note: this currently causes all VNIC MAC clients to
695  * receive the same MAC client handle for the same VNIC. This is ok
696  * as long as we have only one VNIC MAC client which sends and
697  * receives data, but we don't currently enforce this at the MAC layer.
698  */
699 static void *
700 vnic_mac_client_handle(void *vnic_arg)
701 {
702 	vnic_t *vnic = vnic_arg;
703 
704 	return (vnic->vn_mch);
705 }
706 
707 
708 /*
709  * Return information about the specified capability.
710  */
711 /* ARGSUSED */
712 static boolean_t
713 vnic_m_capab_get(void *arg, mac_capab_t cap, void *cap_data)
714 {
715 	vnic_t *vnic = arg;
716 
717 	switch (cap) {
718 	case MAC_CAPAB_HCKSUM: {
719 		uint32_t *hcksum_txflags = cap_data;
720 
721 		*hcksum_txflags = vnic->vn_hcksum_txflags &
722 		    (HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM |
723 		    HCKSUM_INET_PARTIAL);
724 		break;
725 	}
726 	case MAC_CAPAB_VNIC: {
727 		mac_capab_vnic_t *vnic_capab = cap_data;
728 
729 		if (vnic->vn_lower_mh == NULL) {
730 			/*
731 			 * It's an anchor VNIC, we don't have an underlying
732 			 * NIC and MAC client handle.
733 			 */
734 			return (B_FALSE);
735 		}
736 
737 		if (vnic_capab != NULL) {
738 			vnic_capab->mcv_arg = vnic;
739 			vnic_capab->mcv_mac_client_handle =
740 			    vnic_mac_client_handle;
741 		}
742 		break;
743 	}
744 	case MAC_CAPAB_ANCHOR_VNIC: {
745 		/* since it's an anchor VNIC we don't have lower mac handle */
746 		if (vnic->vn_lower_mh == NULL) {
747 			ASSERT(vnic->vn_link_id == 0);
748 			return (B_TRUE);
749 		}
750 		return (B_FALSE);
751 	}
752 	case MAC_CAPAB_NO_NATIVEVLAN:
753 	case MAC_CAPAB_NO_ZCOPY:
754 		return (B_TRUE);
755 	default:
756 		return (B_FALSE);
757 	}
758 	return (B_TRUE);
759 }
760 
761 /* ARGSUSED */
762 static int
763 vnic_m_start(void *arg)
764 {
765 	return (0);
766 }
767 
768 /* ARGSUSED */
769 static void
770 vnic_m_stop(void *arg)
771 {
772 }
773 
774 /* ARGSUSED */
775 static int
776 vnic_m_promisc(void *arg, boolean_t on)
777 {
778 	return (0);
779 }
780 
781 /* ARGSUSED */
782 static int
783 vnic_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
784 {
785 	return (0);
786 }
787 
788 static int
789 vnic_m_unicst(void *arg, const uint8_t *macaddr)
790 {
791 	vnic_t *vnic = arg;
792 
793 	return (mac_vnic_unicast_set(vnic->vn_mch, macaddr));
794 }
795 
796 /*
797  * Callback functions for set/get of properties
798  */
799 /*ARGSUSED*/
800 static int
801 vnic_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
802     uint_t pr_valsize, const void *pr_val)
803 {
804 	int 		err = ENOTSUP;
805 	vnic_t		*vn = m_driver;
806 
807 	/* allow setting MTU only on an etherstub */
808 	if (vn->vn_link_id != DATALINK_INVALID_LINKID)
809 		return (err);
810 
811 	switch (pr_num) {
812 	case MAC_PROP_MTU: {
813 		uint32_t	mtu;
814 
815 		if (pr_valsize < sizeof (mtu)) {
816 			err = EINVAL;
817 			break;
818 		}
819 		bcopy(pr_val, &mtu, sizeof (mtu));
820 		err = mac_maxsdu_update(vn->vn_mh, mtu);
821 		break;
822 	}
823 	default:
824 		break;
825 	}
826 	return (err);
827 }
828 
829 /*ARGSUSED*/
830 static int
831 vnic_m_getprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
832     uint_t pr_flags, uint_t pr_valsize, void *pr_val, uint_t *perm)
833 {
834 	return (ENOTSUP);
835 }
836 
837 
838 int
839 vnic_info(vnic_info_t *info)
840 {
841 	vnic_t		*vnic;
842 	int		err;
843 
844 	rw_enter(&vnic_lock, RW_WRITER);
845 
846 	err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(info->vn_vnic_id),
847 	    (mod_hash_val_t *)&vnic);
848 	if (err != 0) {
849 		rw_exit(&vnic_lock);
850 		return (ENOENT);
851 	}
852 
853 	info->vn_link_id = vnic->vn_link_id;
854 	info->vn_mac_addr_type = vnic->vn_addr_type;
855 	info->vn_mac_len = vnic->vn_addr_len;
856 	bcopy(vnic->vn_addr, info->vn_mac_addr, MAXMACADDRLEN);
857 	info->vn_mac_slot = vnic->vn_slot_id;
858 	info->vn_mac_prefix_len = 0;
859 	info->vn_vid = vnic->vn_vid;
860 	info->vn_force = vnic->vn_force;
861 
862 	bzero(&info->vn_resource_props, sizeof (mac_resource_props_t));
863 	if (vnic->vn_mch != NULL)
864 		mac_resource_ctl_get(vnic->vn_mch, &info->vn_resource_props);
865 
866 	rw_exit(&vnic_lock);
867 	return (0);
868 }
869 
870 static void
871 vnic_notify_cb(void *arg, mac_notify_type_t type)
872 {
873 	vnic_t *vnic = arg;
874 
875 	/*
876 	 * Do not deliver notifications if the vnic is not fully initialized
877 	 * or is in process of being torn down.
878 	 */
879 	if (!vnic->vn_enabled)
880 		return;
881 
882 	switch (type) {
883 	case MAC_NOTE_UNICST:
884 		/*
885 		 * Only the VLAN VNIC needs to be notified with primary MAC
886 		 * address change.
887 		 */
888 		if (vnic->vn_addr_type != VNIC_MAC_ADDR_TYPE_PRIMARY)
889 			return;
890 
891 		/*  the unicast MAC address value */
892 		mac_unicast_primary_get(vnic->vn_lower_mh, vnic->vn_addr);
893 
894 		/* notify its upper layer MAC about MAC address change */
895 		mac_unicst_update(vnic->vn_mh, (const uint8_t *)vnic->vn_addr);
896 		break;
897 
898 	case MAC_NOTE_LINK:
899 		mac_link_update(vnic->vn_mh,
900 		    mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE));
901 		break;
902 
903 	default:
904 		break;
905 	}
906 }
907