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