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