xref: /titanic_41/usr/src/uts/sun4v/io/vnet.c (revision 53ed03b54d7e8d004eeb46c17e2cfd6b4616945f)
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 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/errno.h>
31 #include <sys/param.h>
32 #include <sys/stream.h>
33 #include <sys/kmem.h>
34 #include <sys/conf.h>
35 #include <sys/devops.h>
36 #include <sys/ksynch.h>
37 #include <sys/stat.h>
38 #include <sys/modctl.h>
39 #include <sys/debug.h>
40 #include <sys/ethernet.h>
41 #include <sys/dlpi.h>
42 #include <net/if.h>
43 #include <sys/mac.h>
44 #include <sys/mac_ether.h>
45 #include <sys/ddi.h>
46 #include <sys/sunddi.h>
47 #include <sys/strsun.h>
48 #include <sys/note.h>
49 #include <sys/vnet.h>
50 
51 /*
52  * Function prototypes.
53  */
54 
55 /* DDI entrypoints */
56 static int vnetdevinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
57 static int vnetattach(dev_info_t *, ddi_attach_cmd_t);
58 static int vnetdetach(dev_info_t *, ddi_detach_cmd_t);
59 
60 /* MAC entrypoints  */
61 static int vnet_m_stat(void *, uint_t, uint64_t *);
62 static int vnet_m_start(void *);
63 static void vnet_m_stop(void *);
64 static int vnet_m_promisc(void *, boolean_t);
65 static int vnet_m_multicst(void *, boolean_t, const uint8_t *);
66 static int vnet_m_unicst(void *, const uint8_t *);
67 mblk_t *vnet_m_tx(void *, mblk_t *);
68 
69 /* vnet internal functions */
70 static int vnet_mac_register(vnet_t *);
71 static int vnet_read_mac_address(vnet_t *vnetp);
72 static void vnet_add_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp);
73 static void vnet_del_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp);
74 static vp_tl_t *vnet_get_vptl(vnet_t *vnetp, const char *devname);
75 static fdb_t *vnet_lookup_fdb(fdb_fanout_t *fdbhp, uint8_t *macaddr);
76 
77 /* exported functions */
78 void vnet_add_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx, void *txarg);
79 void vnet_del_fdb(void *arg, uint8_t *macaddr);
80 void vnet_modify_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx,
81 	void *txarg, boolean_t upgrade);
82 void vnet_add_def_rte(void *arg, mac_tx_t m_tx, void *txarg);
83 void vnet_del_def_rte(void *arg);
84 void vnet_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp);
85 void vnet_tx_update(void *arg);
86 
87 /* externs */
88 extern int vgen_init(void *vnetp, dev_info_t *vnetdip, const uint8_t *macaddr,
89 	mac_register_t **vgenmacp);
90 extern int vgen_uninit(void *arg);
91 
92 static mac_callbacks_t vnet_m_callbacks = {
93 	0,
94 	vnet_m_stat,
95 	vnet_m_start,
96 	vnet_m_stop,
97 	vnet_m_promisc,
98 	vnet_m_multicst,
99 	vnet_m_unicst,
100 	vnet_m_tx,
101 	NULL,
102 	NULL,
103 	NULL
104 };
105 
106 /*
107  * Linked list of "vnet_t" structures - one per instance.
108  */
109 static vnet_t	*vnet_headp = NULL;
110 static krwlock_t vnet_rw;
111 
112 /* Tunables */
113 uint32_t vnet_ntxds = VNET_NTXDS;	/* power of 2 transmit descriptors */
114 uint32_t vnet_ldcwd_interval = VNET_LDCWD_INTERVAL; /* watchdog freq in msec */
115 uint32_t vnet_ldcwd_txtimeout = VNET_LDCWD_TXTIMEOUT;  /* tx timeout in msec */
116 uint32_t vnet_ldc_mtu = VNET_LDC_MTU;		/* ldc mtu */
117 uint32_t vnet_nfdb_hash = VNET_NFDB_HASH;	/* size of fdb hash table */
118 
119 /*
120  * Property names
121  */
122 static char macaddr_propname[] = "local-mac-address";
123 
124 /*
125  * This is the string displayed by modinfo(1m).
126  */
127 static char vnet_ident[] = "vnet driver v%I%";
128 extern struct mod_ops mod_driverops;
129 static struct cb_ops cb_vnetops = {
130 	nulldev,		/* cb_open */
131 	nulldev,		/* cb_close */
132 	nodev,			/* cb_strategy */
133 	nodev,			/* cb_print */
134 	nodev,			/* cb_dump */
135 	nodev,			/* cb_read */
136 	nodev,			/* cb_write */
137 	nodev,			/* cb_ioctl */
138 	nodev,			/* cb_devmap */
139 	nodev,			/* cb_mmap */
140 	nodev,			/* cb_segmap */
141 	nochpoll,		/* cb_chpoll */
142 	ddi_prop_op,		/* cb_prop_op */
143 	NULL,			/* cb_stream */
144 	(int)(D_MP)		/* cb_flag */
145 };
146 
147 static struct dev_ops vnetops = {
148 	DEVO_REV,		/* devo_rev */
149 	0,			/* devo_refcnt */
150 	NULL,			/* devo_getinfo */
151 	nulldev,		/* devo_identify */
152 	nulldev,		/* devo_probe */
153 	vnetattach,		/* devo_attach */
154 	vnetdetach,		/* devo_detach */
155 	nodev,			/* devo_reset */
156 	&cb_vnetops,		/* devo_cb_ops */
157 	(struct bus_ops *)NULL	/* devo_bus_ops */
158 };
159 
160 static struct modldrv modldrv = {
161 	&mod_driverops,		/* Type of module.  This one is a driver */
162 	vnet_ident,		/* ID string */
163 	&vnetops		/* driver specific ops */
164 };
165 
166 static struct modlinkage modlinkage = {
167 	MODREV_1, (void *)&modldrv, NULL
168 };
169 
170 #ifdef DEBUG
171 
172 /*
173  * Print debug messages - set to 0xf to enable all msgs
174  */
175 int vnet_dbglevel = 0x8;
176 
177 static void
178 debug_printf(const char *fname, void *arg, const char *fmt, ...)
179 {
180 	char    buf[512];
181 	va_list ap;
182 	vnet_t *vnetp = (vnet_t *)arg;
183 	char    *bufp = buf;
184 
185 	if (vnetp == NULL) {
186 		(void) sprintf(bufp, "%s: ", fname);
187 		bufp += strlen(bufp);
188 	} else {
189 		(void) sprintf(bufp, "vnet%d:%s: ", vnetp->instance, fname);
190 		bufp += strlen(bufp);
191 	}
192 	va_start(ap, fmt);
193 	(void) vsprintf(bufp, fmt, ap);
194 	va_end(ap);
195 	cmn_err(CE_CONT, "%s\n", buf);
196 }
197 
198 #endif
199 
200 /* _init(9E): initialize the loadable module */
201 int
202 _init(void)
203 {
204 	int status;
205 
206 	DBG1(NULL, "enter\n");
207 
208 	mac_init_ops(&vnetops, "vnet");
209 	status = mod_install(&modlinkage);
210 	if (status != 0) {
211 		mac_fini_ops(&vnetops);
212 	}
213 
214 	DBG1(NULL, "exit(%d)\n", status);
215 	return (status);
216 }
217 
218 /* _fini(9E): prepare the module for unloading. */
219 int
220 _fini(void)
221 {
222 	int status;
223 
224 	DBG1(NULL, "enter\n");
225 
226 	status = mod_remove(&modlinkage);
227 	if (status != 0)
228 		return (status);
229 	mac_fini_ops(&vnetops);
230 
231 	DBG1(NULL, "exit(%d)\n", status);
232 	return (status);
233 }
234 
235 /* _info(9E): return information about the loadable module */
236 int
237 _info(struct modinfo *modinfop)
238 {
239 	return (mod_info(&modlinkage, modinfop));
240 }
241 
242 /*
243  * attach(9E): attach a device to the system.
244  * called once for each instance of the device on the system.
245  */
246 static int
247 vnetattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
248 {
249 	vnet_t		*vnetp;
250 	vp_tl_t		*vp_tlp;
251 	int		instance;
252 	int		status;
253 	mac_register_t	*vgenmacp = NULL;
254 	uint32_t	nfdbh = 0;
255 	enum	{ AST_init = 0x0, AST_vnet_alloc = 0x1,
256 		AST_mac_alloc = 0x2, AST_read_macaddr = 0x4,
257 		AST_vgen_init = 0x8, AST_vptl_alloc = 0x10,
258 		AST_fdbh_alloc = 0x20 } attach_state;
259 
260 	attach_state = AST_init;
261 
262 	switch (cmd) {
263 	case DDI_ATTACH:
264 		break;
265 	case DDI_RESUME:
266 	case DDI_PM_RESUME:
267 	default:
268 		goto vnet_attach_fail;
269 	}
270 
271 	instance = ddi_get_instance(dip);
272 	DBG1(NULL, "instance(%d) enter\n", instance);
273 
274 	/* allocate vnet_t and mac_t structures */
275 	vnetp = kmem_zalloc(sizeof (vnet_t), KM_SLEEP);
276 	attach_state |= AST_vnet_alloc;
277 
278 	/* setup links to vnet_t from both devinfo and mac_t */
279 	ddi_set_driver_private(dip, (caddr_t)vnetp);
280 	vnetp->dip = dip;
281 	vnetp->instance = instance;
282 
283 	/* read the mac address */
284 	status = vnet_read_mac_address(vnetp);
285 	if (status != DDI_SUCCESS) {
286 		goto vnet_attach_fail;
287 	}
288 	attach_state |= AST_read_macaddr;
289 
290 	/*
291 	 * Initialize the generic vnet proxy transport. This is the first
292 	 * and default transport used by vnet. The generic transport
293 	 * is provided by using sun4v LDC (logical domain channel). On success,
294 	 * vgen_init() provides a pointer to mac_t of generic transport.
295 	 * Currently, this generic layer provides network connectivity to other
296 	 * vnets within ldoms and also to remote hosts oustide ldoms through
297 	 * the virtual switch (vsw) device on domain0. In the future, when
298 	 * physical adapters that are able to share their resources (such as
299 	 * dma channels) with guest domains become available, the vnet device
300 	 * will use hardware specific driver to communicate directly over the
301 	 * physical device to reach remote hosts without going through vswitch.
302 	 */
303 	status = vgen_init(vnetp, vnetp->dip, (uint8_t *)vnetp->curr_macaddr,
304 	    &vgenmacp);
305 	if (status != DDI_SUCCESS) {
306 		DERR(vnetp, "vgen_init() failed\n");
307 		goto vnet_attach_fail;
308 	}
309 	attach_state |= AST_vgen_init;
310 
311 	vp_tlp = kmem_zalloc(sizeof (vp_tl_t), KM_SLEEP);
312 	vp_tlp->macp = vgenmacp;
313 	(void) snprintf(vp_tlp->name, MAXNAMELEN, "%s%u", "vgen", instance);
314 	(void) strcpy(vnetp->vgen_name, vp_tlp->name);
315 
316 	/* add generic transport to the list of vnet proxy transports */
317 	vnet_add_vptl(vnetp, vp_tlp);
318 	attach_state |= AST_vptl_alloc;
319 
320 	nfdbh = vnet_nfdb_hash;
321 	if ((nfdbh < VNET_NFDB_HASH) || (nfdbh > VNET_NFDB_HASH_MAX)) {
322 		vnetp->nfdb_hash = VNET_NFDB_HASH;
323 	}
324 	else
325 		vnetp->nfdb_hash = nfdbh;
326 
327 	/* allocate fdb hash table, with an extra slot for default route */
328 	vnetp->fdbhp = kmem_zalloc(sizeof (fdb_fanout_t) *
329 	    (vnetp->nfdb_hash + 1), KM_SLEEP);
330 	attach_state |= AST_fdbh_alloc;
331 
332 	/* register with MAC layer */
333 	status = vnet_mac_register(vnetp);
334 	if (status != DDI_SUCCESS) {
335 		goto vnet_attach_fail;
336 	}
337 
338 	/* add to the list of vnet devices */
339 	WRITE_ENTER(&vnet_rw);
340 	vnetp->nextp = vnet_headp;
341 	vnet_headp = vnetp;
342 	RW_EXIT(&vnet_rw);
343 
344 	DBG1(NULL, "instance(%d) exit\n", instance);
345 	return (DDI_SUCCESS);
346 
347 vnet_attach_fail:
348 	if (attach_state & AST_fdbh_alloc) {
349 		kmem_free(vnetp->fdbhp,
350 		    sizeof (fdb_fanout_t) * (vnetp->nfdb_hash + 1));
351 	}
352 	if (attach_state & AST_vptl_alloc) {
353 		WRITE_ENTER(&vnetp->trwlock);
354 		vnet_del_vptl(vnetp, vp_tlp);
355 		RW_EXIT(&vnetp->trwlock);
356 	}
357 	if (attach_state & AST_vgen_init) {
358 		(void) vgen_uninit(vgenmacp->m_driver);
359 	}
360 	if (attach_state & AST_vnet_alloc) {
361 		KMEM_FREE(vnetp);
362 	}
363 	return (DDI_FAILURE);
364 }
365 
366 /*
367  * detach(9E): detach a device from the system.
368  */
369 static int
370 vnetdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
371 {
372 	vnet_t		*vnetp;
373 	vnet_t		**vnetpp;
374 	vp_tl_t		*vp_tlp;
375 	int		instance;
376 	int		rv;
377 
378 	instance = ddi_get_instance(dip);
379 	DBG1(NULL, "instance(%d) enter\n", instance);
380 
381 	vnetp = ddi_get_driver_private(dip);
382 	if (vnetp == NULL) {
383 		goto vnet_detach_fail;
384 	}
385 
386 	switch (cmd) {
387 	case DDI_DETACH:
388 		break;
389 	case DDI_SUSPEND:
390 	case DDI_PM_SUSPEND:
391 	default:
392 		goto vnet_detach_fail;
393 	}
394 
395 	/* uninit and free vnet proxy transports */
396 	WRITE_ENTER(&vnetp->trwlock);
397 	while ((vp_tlp = vnetp->tlp) != NULL) {
398 		if (strcmp(vnetp->vgen_name, vp_tlp->name) == 0) {
399 			/* uninitialize generic transport */
400 			rv = vgen_uninit(vp_tlp->macp->m_driver);
401 			if (rv != DDI_SUCCESS) {
402 				RW_EXIT(&vnetp->trwlock);
403 				goto vnet_detach_fail;
404 			}
405 		}
406 		vnet_del_vptl(vnetp, vp_tlp);
407 	}
408 	RW_EXIT(&vnetp->trwlock);
409 
410 	/*
411 	 * Unregister from the MAC subsystem.  This can fail, in
412 	 * particular if there are DLPI style-2 streams still open -
413 	 * in which case we just return failure.
414 	 */
415 	if (mac_unregister(vnetp->mh) != 0)
416 		goto vnet_detach_fail;
417 
418 	/* unlink from instance(vnet_t) list */
419 	WRITE_ENTER(&vnet_rw);
420 	for (vnetpp = &vnet_headp; *vnetpp; vnetpp = &(*vnetpp)->nextp) {
421 		if (*vnetpp == vnetp) {
422 			*vnetpp = vnetp->nextp;
423 			break;
424 		}
425 	}
426 	RW_EXIT(&vnet_rw);
427 
428 	kmem_free(vnetp->fdbhp,
429 	    sizeof (fdb_fanout_t) * (vnetp->nfdb_hash + 1));
430 
431 	KMEM_FREE(vnetp);
432 
433 	return (DDI_SUCCESS);
434 
435 vnet_detach_fail:
436 	return (DDI_FAILURE);
437 }
438 
439 /* enable the device for transmit/receive */
440 static int
441 vnet_m_start(void *arg)
442 {
443 	vnet_t		*vnetp = arg;
444 	vp_tl_t		*vp_tlp;
445 	mac_register_t	*vp_macp;
446 	mac_callbacks_t	*cbp;
447 
448 	DBG1(vnetp, "enter\n");
449 
450 	/*
451 	 * NOTE:
452 	 * Currently, we only have generic transport. m_start() invokes
453 	 * vgen_start() which enables ports/channels in vgen and
454 	 * initiates handshake with peer vnets and vsw. In the future when we
455 	 * have support for hardware specific transports, this information
456 	 * needs to be propagted back to vnet from vgen and we need to revisit
457 	 * this code (see comments in vnet_attach()).
458 	 *
459 	 */
460 	WRITE_ENTER(&vnetp->trwlock);
461 	for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) {
462 		vp_macp = vp_tlp->macp;
463 		cbp = vp_macp->m_callbacks;
464 		cbp->mc_start(vp_macp->m_driver);
465 	}
466 	RW_EXIT(&vnetp->trwlock);
467 
468 	DBG1(vnetp, "exit\n");
469 	return (VNET_SUCCESS);
470 
471 }
472 
473 /* stop transmit/receive for the device */
474 static void
475 vnet_m_stop(void *arg)
476 {
477 	vnet_t		*vnetp = arg;
478 	vp_tl_t		*vp_tlp;
479 	mac_register_t	*vp_macp;
480 	mac_callbacks_t	*cbp;
481 
482 	DBG1(vnetp, "enter\n");
483 
484 	WRITE_ENTER(&vnetp->trwlock);
485 	for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) {
486 		vp_macp = vp_tlp->macp;
487 		cbp = vp_macp->m_callbacks;
488 		cbp->mc_stop(vp_macp->m_driver);
489 	}
490 	RW_EXIT(&vnetp->trwlock);
491 
492 	DBG1(vnetp, "exit\n");
493 }
494 
495 /* set the unicast mac address of the device */
496 static int
497 vnet_m_unicst(void *arg, const uint8_t *macaddr)
498 {
499 	_NOTE(ARGUNUSED(macaddr))
500 
501 	vnet_t *vnetp = arg;
502 
503 	DBG1(vnetp, "enter\n");
504 	/*
505 	 * NOTE: setting mac address dynamically is not supported.
506 	 */
507 	DBG1(vnetp, "exit\n");
508 
509 	return (VNET_FAILURE);
510 }
511 
512 /* enable/disable a multicast address */
513 static int
514 vnet_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
515 {
516 	_NOTE(ARGUNUSED(add, mca))
517 
518 	vnet_t *vnetp = arg;
519 	vp_tl_t		*vp_tlp;
520 	mac_register_t	*vp_macp;
521 	mac_callbacks_t	*cbp;
522 	int rv = VNET_SUCCESS;
523 
524 	DBG1(vnetp, "enter\n");
525 	READ_ENTER(&vnetp->trwlock);
526 	for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) {
527 		if (strcmp(vnetp->vgen_name, vp_tlp->name) == 0) {
528 			vp_macp = vp_tlp->macp;
529 			cbp = vp_macp->m_callbacks;
530 			rv = cbp->mc_multicst(vp_macp->m_driver, add, mca);
531 			break;
532 		}
533 	}
534 	RW_EXIT(&vnetp->trwlock);
535 	DBG1(vnetp, "exit(%d)\n", rv);
536 	return (rv);
537 }
538 
539 /* set or clear promiscuous mode on the device */
540 static int
541 vnet_m_promisc(void *arg, boolean_t on)
542 {
543 	_NOTE(ARGUNUSED(on))
544 
545 	vnet_t *vnetp = arg;
546 	DBG1(vnetp, "enter\n");
547 	/*
548 	 * NOTE: setting promiscuous mode is not supported, just return success.
549 	 */
550 	DBG1(vnetp, "exit\n");
551 	return (VNET_SUCCESS);
552 }
553 
554 /*
555  * Transmit a chain of packets. This function provides switching functionality
556  * based on the destination mac address to reach other guests (within ldoms) or
557  * external hosts.
558  */
559 mblk_t *
560 vnet_m_tx(void *arg, mblk_t *mp)
561 {
562 	vnet_t *vnetp;
563 	mblk_t *next;
564 	uint32_t fdbhash;
565 	fdb_t *fdbp;
566 	fdb_fanout_t *fdbhp;
567 	struct ether_header *ehp;
568 	uint8_t *macaddr;
569 	mblk_t *resid_mp;
570 
571 	vnetp = (vnet_t *)arg;
572 	DBG1(vnetp, "enter\n");
573 	ASSERT(mp != NULL);
574 
575 	while (mp != NULL) {
576 		next = mp->b_next;
577 		mp->b_next = NULL;
578 
579 		/* get the destination mac address in the eth header */
580 		ehp = (struct ether_header *)mp->b_rptr;
581 		macaddr = (uint8_t *)&ehp->ether_dhost;
582 
583 		/* Calculate hash value and fdb fanout */
584 		fdbhash = MACHASH(macaddr, vnetp->nfdb_hash);
585 		fdbhp = &(vnetp->fdbhp[fdbhash]);
586 
587 		READ_ENTER(&fdbhp->rwlock);
588 		fdbp = vnet_lookup_fdb(fdbhp, macaddr);
589 		if (fdbp) {
590 			/*
591 			 * If the destination is in FDB, the destination is
592 			 * a vnet device within ldoms and directly reachable,
593 			 * invoke the tx function in the fdb entry.
594 			 */
595 			resid_mp = fdbp->m_tx(fdbp->txarg, mp);
596 			if (resid_mp != NULL) {
597 				/* m_tx failed */
598 				mp->b_next = next;
599 				RW_EXIT(&fdbhp->rwlock);
600 				break;
601 			}
602 			RW_EXIT(&fdbhp->rwlock);
603 		} else {
604 			/* destination is not in FDB */
605 			RW_EXIT(&fdbhp->rwlock);
606 			/*
607 			 * If the destination is broadcast/multicast
608 			 * or an unknown unicast address, forward the
609 			 * packet to vsw, using the last slot in fdb which is
610 			 * reserved for default route.
611 			 */
612 			fdbhp = &(vnetp->fdbhp[vnetp->nfdb_hash]);
613 			READ_ENTER(&fdbhp->rwlock);
614 			fdbp = fdbhp->headp;
615 			if (fdbp) {
616 				resid_mp = fdbp->m_tx(fdbp->txarg, mp);
617 				if (resid_mp != NULL) {
618 					/* m_tx failed */
619 					mp->b_next = next;
620 					RW_EXIT(&fdbhp->rwlock);
621 					break;
622 				}
623 			} else {
624 				/* drop the packet */
625 				freemsg(mp);
626 			}
627 			RW_EXIT(&fdbhp->rwlock);
628 		}
629 
630 		mp = next;
631 	}
632 
633 	DBG1(vnetp, "exit\n");
634 	return (mp);
635 }
636 
637 /* get statistics from the device */
638 int
639 vnet_m_stat(void *arg, uint_t stat, uint64_t *val)
640 {
641 	vnet_t *vnetp = arg;
642 	vp_tl_t	*vp_tlp;
643 	mac_register_t	*vp_macp;
644 	mac_callbacks_t	*cbp;
645 	uint64_t val_total = 0;
646 
647 	DBG1(vnetp, "enter\n");
648 
649 	/*
650 	 * get the specified statistic from each transport and return the
651 	 * aggregate val.  This obviously only works for counters.
652 	 */
653 	if ((IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat)) ||
654 	    (IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat))) {
655 		return (ENOTSUP);
656 	}
657 	READ_ENTER(&vnetp->trwlock);
658 	for (vp_tlp = vnetp->tlp; vp_tlp != NULL; vp_tlp = vp_tlp->nextp) {
659 		vp_macp = vp_tlp->macp;
660 		cbp = vp_macp->m_callbacks;
661 		if (cbp->mc_getstat(vp_macp->m_driver, stat, val) == 0)
662 			val_total += *val;
663 	}
664 	RW_EXIT(&vnetp->trwlock);
665 
666 	*val = val_total;
667 
668 	DBG1(vnetp, "exit\n");
669 	return (0);
670 }
671 
672 /* wrapper function for mac_register() */
673 static int
674 vnet_mac_register(vnet_t *vnetp)
675 {
676 	mac_register_t	*macp;
677 	int		err;
678 
679 	if ((macp = mac_alloc(MAC_VERSION)) == NULL)
680 		return (DDI_FAILURE);
681 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
682 	macp->m_driver = vnetp;
683 	macp->m_dip = vnetp->dip;
684 	macp->m_src_addr = vnetp->curr_macaddr;
685 	macp->m_callbacks = &vnet_m_callbacks;
686 	macp->m_min_sdu = 0;
687 	macp->m_max_sdu = ETHERMTU;
688 
689 	/*
690 	 * Finally, we're ready to register ourselves with the MAC layer
691 	 * interface; if this succeeds, we're all ready to start()
692 	 */
693 	err = mac_register(macp, &vnetp->mh);
694 	mac_free(macp);
695 	return (err == 0 ? DDI_SUCCESS : DDI_FAILURE);
696 }
697 
698 /* add vp_tl to the list */
699 static void
700 vnet_add_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp)
701 {
702 	vp_tl_t *ttlp;
703 
704 	WRITE_ENTER(&vnetp->trwlock);
705 	if (vnetp->tlp == NULL) {
706 		vnetp->tlp = vp_tlp;
707 	} else {
708 		ttlp = vnetp->tlp;
709 		while (ttlp->nextp)
710 			ttlp = ttlp->nextp;
711 		ttlp->nextp = vp_tlp;
712 	}
713 	RW_EXIT(&vnetp->trwlock);
714 }
715 
716 /* remove vp_tl from the list */
717 static void
718 vnet_del_vptl(vnet_t *vnetp, vp_tl_t *vp_tlp)
719 {
720 	vp_tl_t *ttlp, **pretlp;
721 	boolean_t found = B_FALSE;
722 
723 	pretlp = &vnetp->tlp;
724 	ttlp = *pretlp;
725 	while (ttlp) {
726 		if (ttlp == vp_tlp) {
727 			found = B_TRUE;
728 			(*pretlp) = ttlp->nextp;
729 			ttlp->nextp = NULL;
730 			break;
731 		}
732 		pretlp = &(ttlp->nextp);
733 		ttlp = *pretlp;
734 	}
735 
736 	if (found) {
737 		KMEM_FREE(vp_tlp);
738 	}
739 }
740 
741 /* get vp_tl corresponding to the given name */
742 static vp_tl_t *
743 vnet_get_vptl(vnet_t *vnetp, const char *name)
744 {
745 	vp_tl_t *tlp;
746 
747 	tlp = vnetp->tlp;
748 	while (tlp) {
749 		if (strcmp(tlp->name, name) == 0) {
750 			return (tlp);
751 		}
752 		tlp = tlp->nextp;
753 	}
754 	DWARN(vnetp, "can't find vp_tl with name (%s)\n", name);
755 	return (NULL);
756 }
757 
758 /* read the mac address of the device */
759 static int
760 vnet_read_mac_address(vnet_t *vnetp)
761 {
762 	uchar_t 	*macaddr;
763 	uint32_t 	size;
764 	int 		rv;
765 
766 	rv = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, vnetp->dip,
767 	    DDI_PROP_DONTPASS, macaddr_propname, &macaddr, &size);
768 	if ((rv != DDI_PROP_SUCCESS) || (size != ETHERADDRL)) {
769 		DWARN(vnetp, "prop_lookup failed(%s) err(%d)\n",
770 		    macaddr_propname, rv);
771 		return (DDI_FAILURE);
772 	}
773 	bcopy(macaddr, (caddr_t)vnetp->vendor_addr, ETHERADDRL);
774 	bcopy(macaddr, (caddr_t)vnetp->curr_macaddr, ETHERADDRL);
775 	ddi_prop_free(macaddr);
776 
777 	return (DDI_SUCCESS);
778 }
779 
780 
781 /*
782  * Functions below are called only by generic transport to add/remove/modify
783  * entries in forwarding database. See comments in vgen_port_init(vnet_gen.c).
784  */
785 
786 /* add an entry into the forwarding database */
787 void
788 vnet_add_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx, void *txarg)
789 {
790 	vnet_t *vnetp = (vnet_t *)arg;
791 	uint32_t fdbhash;
792 	fdb_t *fdbp;
793 	fdb_fanout_t *fdbhp;
794 
795 	/* Calculate hash value and fdb fanout */
796 	fdbhash = MACHASH(macaddr, vnetp->nfdb_hash);
797 	fdbhp = &(vnetp->fdbhp[fdbhash]);
798 
799 	WRITE_ENTER(&fdbhp->rwlock);
800 
801 	fdbp = kmem_zalloc(sizeof (fdb_t), KM_NOSLEEP);
802 	if (fdbp == NULL) {
803 		RW_EXIT(&fdbhp->rwlock);
804 		return;
805 	}
806 	bcopy(macaddr, (caddr_t)fdbp->macaddr, ETHERADDRL);
807 	fdbp->m_tx = m_tx;
808 	fdbp->txarg = txarg;
809 	fdbp->nextp = fdbhp->headp;
810 	fdbhp->headp = fdbp;
811 
812 	RW_EXIT(&fdbhp->rwlock);
813 }
814 
815 /* delete an entry from the forwarding database */
816 void
817 vnet_del_fdb(void *arg, uint8_t *macaddr)
818 {
819 	vnet_t *vnetp = (vnet_t *)arg;
820 	uint32_t fdbhash;
821 	fdb_t *fdbp;
822 	fdb_t **pfdbp;
823 	fdb_fanout_t *fdbhp;
824 
825 	/* Calculate hash value and fdb fanout */
826 	fdbhash = MACHASH(macaddr, vnetp->nfdb_hash);
827 	fdbhp = &(vnetp->fdbhp[fdbhash]);
828 
829 	WRITE_ENTER(&fdbhp->rwlock);
830 
831 	for (pfdbp = &fdbhp->headp; (fdbp  = *pfdbp) != NULL;
832 	    pfdbp = &fdbp->nextp) {
833 		if (bcmp(fdbp->macaddr, macaddr, ETHERADDRL) == 0) {
834 			/* Unlink it from the list */
835 			*pfdbp = fdbp->nextp;
836 			KMEM_FREE(fdbp);
837 			break;
838 		}
839 	}
840 
841 	RW_EXIT(&fdbhp->rwlock);
842 }
843 
844 /* modify an existing entry in the forwarding database */
845 void
846 vnet_modify_fdb(void *arg, uint8_t *macaddr, mac_tx_t m_tx, void *txarg,
847 	boolean_t upgrade)
848 {
849 	vnet_t *vnetp = (vnet_t *)arg;
850 	uint32_t fdbhash;
851 	fdb_t *fdbp;
852 	fdb_fanout_t *fdbhp;
853 
854 	/* Calculate hash value and fdb fanout */
855 	fdbhash = MACHASH(macaddr, vnetp->nfdb_hash);
856 	fdbhp = &(vnetp->fdbhp[fdbhash]);
857 
858 	if (upgrade == B_TRUE) {
859 		/*
860 		 * Caller already holds the lock as a reader. This can
861 		 * occur if this function is invoked in the context
862 		 * of transmit routine - vnet_m_tx(), where the lock
863 		 * is held as a reader before calling the transmit
864 		 * function of an fdb entry (fdbp->m_tx).
865 		 * See comments in vgen_ldcsend() in vnet_gen.c
866 		 */
867 		if (!rw_tryupgrade(&fdbhp->rwlock)) {
868 			RW_EXIT(&fdbhp->rwlock);
869 			WRITE_ENTER(&fdbhp->rwlock);
870 		}
871 	} else {
872 		/* Caller does not hold the lock */
873 		WRITE_ENTER(&fdbhp->rwlock);
874 	}
875 
876 	for (fdbp = fdbhp->headp; fdbp != NULL; fdbp = fdbp->nextp) {
877 		if (bcmp(fdbp->macaddr, macaddr, ETHERADDRL) == 0) {
878 			/* change the entry to have new tx params */
879 			fdbp->m_tx = m_tx;
880 			fdbp->txarg = txarg;
881 			break;
882 		}
883 	}
884 
885 	if (upgrade == B_TRUE) {
886 		/* restore the caller as a reader */
887 		rw_downgrade(&fdbhp->rwlock);
888 	} else {
889 		RW_EXIT(&fdbhp->rwlock);
890 	}
891 }
892 
893 /* look up an fdb entry based on the mac address, caller holds lock */
894 static fdb_t *
895 vnet_lookup_fdb(fdb_fanout_t *fdbhp, uint8_t *macaddr)
896 {
897 	fdb_t *fdbp = NULL;
898 
899 	for (fdbp = fdbhp->headp; fdbp != NULL; fdbp = fdbp->nextp) {
900 		if (bcmp(fdbp->macaddr, macaddr, ETHERADDRL) == 0) {
901 			break;
902 		}
903 	}
904 
905 	return (fdbp);
906 }
907 
908 /* add default route entry into the forwarding database */
909 void
910 vnet_add_def_rte(void *arg, mac_tx_t m_tx, void *txarg)
911 {
912 	vnet_t *vnetp = (vnet_t *)arg;
913 	fdb_t *fdbp;
914 	fdb_fanout_t *fdbhp;
915 
916 	/*
917 	 * The last hash list is reserved for default route entry,
918 	 * and for now, we have only one entry in this list.
919 	 */
920 	fdbhp = &(vnetp->fdbhp[vnetp->nfdb_hash]);
921 
922 	WRITE_ENTER(&fdbhp->rwlock);
923 
924 	if (fdbhp->headp) {
925 		DWARN(vnetp, "default rte already exists\n");
926 		RW_EXIT(&fdbhp->rwlock);
927 		return;
928 	}
929 	fdbp = kmem_zalloc(sizeof (fdb_t), KM_NOSLEEP);
930 	if (fdbp == NULL) {
931 		RW_EXIT(&fdbhp->rwlock);
932 		return;
933 	}
934 	bzero(fdbp->macaddr, ETHERADDRL);
935 	fdbp->m_tx = m_tx;
936 	fdbp->txarg = txarg;
937 	fdbp->nextp = NULL;
938 	fdbhp->headp = fdbp;
939 
940 	RW_EXIT(&fdbhp->rwlock);
941 }
942 
943 /* delete default route entry from the forwarding database */
944 void
945 vnet_del_def_rte(void *arg)
946 {
947 	vnet_t *vnetp = (vnet_t *)arg;
948 	fdb_t *fdbp;
949 	fdb_fanout_t *fdbhp;
950 
951 	/*
952 	 * The last hash list is reserved for default route entry,
953 	 * and for now, we have only one entry in this list.
954 	 */
955 	fdbhp = &(vnetp->fdbhp[vnetp->nfdb_hash]);
956 
957 	WRITE_ENTER(&fdbhp->rwlock);
958 
959 	if (fdbhp->headp == NULL) {
960 		RW_EXIT(&fdbhp->rwlock);
961 		return;
962 	}
963 	fdbp = fdbhp->headp;
964 	KMEM_FREE(fdbp);
965 	fdbhp->headp = NULL;
966 
967 	RW_EXIT(&fdbhp->rwlock);
968 }
969 
970 void
971 vnet_rx(void *arg, mac_resource_handle_t mrh, mblk_t *mp)
972 {
973 	vnet_t *vnetp = arg;
974 	mac_rx(vnetp->mh, mrh, mp);
975 }
976 
977 void
978 vnet_tx_update(void *arg)
979 {
980 	vnet_t *vnetp = arg;
981 	mac_tx_update(vnetp->mh);
982 }
983