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