xref: /titanic_50/usr/src/uts/sun4v/io/vnet.c (revision 982b4ad2dc6b5ed2a2c8c1670e94ecf1fe63fc56)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/errno.h>
29 #include <sys/param.h>
30 #include <sys/stream.h>
31 #include <sys/kmem.h>
32 #include <sys/conf.h>
33 #include <sys/devops.h>
34 #include <sys/ksynch.h>
35 #include <sys/stat.h>
36 #include <sys/modctl.h>
37 #include <sys/modhash.h>
38 #include <sys/debug.h>
39 #include <sys/ethernet.h>
40 #include <sys/dlpi.h>
41 #include <net/if.h>
42 #include <sys/mac_provider.h>
43 #include <sys/mac_ether.h>
44 #include <sys/ddi.h>
45 #include <sys/sunddi.h>
46 #include <sys/strsun.h>
47 #include <sys/note.h>
48 #include <sys/atomic.h>
49 #include <sys/vnet.h>
50 #include <sys/vlan.h>
51 #include <sys/vnet_mailbox.h>
52 #include <sys/vnet_common.h>
53 #include <sys/dds.h>
54 #include <sys/strsubr.h>
55 #include <sys/taskq.h>
56 
57 /*
58  * Function prototypes.
59  */
60 
61 /* DDI entrypoints */
62 static int vnetdevinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
63 static int vnetattach(dev_info_t *, ddi_attach_cmd_t);
64 static int vnetdetach(dev_info_t *, ddi_detach_cmd_t);
65 
66 /* MAC entrypoints  */
67 static int vnet_m_stat(void *, uint_t, uint64_t *);
68 static int vnet_m_start(void *);
69 static void vnet_m_stop(void *);
70 static int vnet_m_promisc(void *, boolean_t);
71 static int vnet_m_multicst(void *, boolean_t, const uint8_t *);
72 static int vnet_m_unicst(void *, const uint8_t *);
73 mblk_t *vnet_m_tx(void *, mblk_t *);
74 static void vnet_m_ioctl(void *arg, queue_t *q, mblk_t *mp);
75 #ifdef	VNET_IOC_DEBUG
76 static void vnet_force_link_state(vnet_t *vnetp, queue_t *q, mblk_t *mp);
77 #endif
78 
79 /* vnet internal functions */
80 static int vnet_unattach(vnet_t *vnetp);
81 static int vnet_mac_register(vnet_t *);
82 static int vnet_read_mac_address(vnet_t *vnetp);
83 
84 /* Forwarding database (FDB) routines */
85 static void vnet_fdb_create(vnet_t *vnetp);
86 static void vnet_fdb_destroy(vnet_t *vnetp);
87 static vnet_res_t *vnet_fdbe_find(vnet_t *vnetp, struct ether_addr *addrp);
88 static void vnet_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val);
89 void vnet_fdbe_add(vnet_t *vnetp, vnet_res_t *vresp);
90 static void vnet_fdbe_del(vnet_t *vnetp, vnet_res_t *vresp);
91 
92 static void vnet_rx_frames_untag(uint16_t pvid, mblk_t **mp);
93 static void vnet_rx(vio_net_handle_t vrh, mblk_t *mp);
94 static void vnet_tx_update(vio_net_handle_t vrh);
95 static void vnet_res_start_task(void *arg);
96 static void vnet_start_resources(vnet_t *vnetp);
97 static void vnet_stop_resources(vnet_t *vnetp);
98 static void vnet_dispatch_res_task(vnet_t *vnetp);
99 static void vnet_res_start_task(void *arg);
100 static void vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err);
101 
102 /* Exported to vnet_gen */
103 int vnet_mtu_update(vnet_t *vnetp, uint32_t mtu);
104 void vnet_link_update(vnet_t *vnetp, link_state_t link_state);
105 void vnet_dds_cleanup_hio(vnet_t *vnetp);
106 
107 static kstat_t *vnet_hio_setup_kstats(char *ks_mod, char *ks_name,
108     vnet_res_t *vresp);
109 static int vnet_hio_update_kstats(kstat_t *ksp, int rw);
110 static void vnet_hio_get_stats(vnet_res_t *vresp, vnet_hio_stats_t *statsp);
111 static void vnet_hio_destroy_kstats(kstat_t *ksp);
112 
113 /* Exported to to vnet_dds */
114 int vnet_send_dds_msg(vnet_t *vnetp, void *dmsg);
115 
116 /* Externs that are imported from vnet_gen */
117 extern int vgen_init(void *vnetp, uint64_t regprop, dev_info_t *vnetdip,
118     const uint8_t *macaddr, void **vgenhdl);
119 extern void vgen_uninit(void *arg);
120 extern int vgen_dds_tx(void *arg, void *dmsg);
121 extern void vgen_mod_init(void);
122 extern int vgen_mod_cleanup(void);
123 extern void vgen_mod_fini(void);
124 
125 /* Externs that are imported from vnet_dds */
126 extern void vdds_mod_init(void);
127 extern void vdds_mod_fini(void);
128 extern int vdds_init(vnet_t *vnetp);
129 extern void vdds_cleanup(vnet_t *vnetp);
130 extern void vdds_process_dds_msg(vnet_t *vnetp, vio_dds_msg_t *dmsg);
131 extern void vdds_cleanup_hybrid_res(void *arg);
132 extern void vdds_cleanup_hio(vnet_t *vnetp);
133 
134 #define	DRV_NAME	"vnet"
135 #define	VNET_FDBE_REFHOLD(p)						\
136 {									\
137 	atomic_inc_32(&(p)->refcnt);					\
138 	ASSERT((p)->refcnt != 0);					\
139 }
140 
141 #define	VNET_FDBE_REFRELE(p)						\
142 {									\
143 	ASSERT((p)->refcnt != 0);					\
144 	atomic_dec_32(&(p)->refcnt);					\
145 }
146 
147 #ifdef	VNET_IOC_DEBUG
148 #define	VNET_M_CALLBACK_FLAGS	(MC_IOCTL)
149 #else
150 #define	VNET_M_CALLBACK_FLAGS	(0)
151 #endif
152 
153 static mac_callbacks_t vnet_m_callbacks = {
154 	VNET_M_CALLBACK_FLAGS,
155 	vnet_m_stat,
156 	vnet_m_start,
157 	vnet_m_stop,
158 	vnet_m_promisc,
159 	vnet_m_multicst,
160 	vnet_m_unicst,
161 	vnet_m_tx,
162 	vnet_m_ioctl,
163 	NULL,
164 	NULL
165 };
166 
167 /*
168  * Linked list of "vnet_t" structures - one per instance.
169  */
170 static vnet_t	*vnet_headp = NULL;
171 static krwlock_t vnet_rw;
172 
173 /* Tunables */
174 uint32_t vnet_ntxds = VNET_NTXDS;	/* power of 2 transmit descriptors */
175 uint32_t vnet_ldcwd_interval = VNET_LDCWD_INTERVAL; /* watchdog freq in msec */
176 uint32_t vnet_ldcwd_txtimeout = VNET_LDCWD_TXTIMEOUT;  /* tx timeout in msec */
177 uint32_t vnet_ldc_mtu = VNET_LDC_MTU;		/* ldc mtu */
178 
179 /*
180  * Set this to non-zero to enable additional internal receive buffer pools
181  * based on the MTU of the device for better performance at the cost of more
182  * memory consumption. This is turned off by default, to use allocb(9F) for
183  * receive buffer allocations of sizes > 2K.
184  */
185 boolean_t vnet_jumbo_rxpools = B_FALSE;
186 
187 /* # of chains in fdb hash table */
188 uint32_t	vnet_fdb_nchains = VNET_NFDB_HASH;
189 
190 /* Internal tunables */
191 uint32_t	vnet_ethermtu = 1500;	/* mtu of the device */
192 
193 /*
194  * Default vlan id. This is only used internally when the "default-vlan-id"
195  * property is not present in the MD device node. Therefore, this should not be
196  * used as a tunable; if this value is changed, the corresponding variable
197  * should be updated to the same value in vsw and also other vnets connected to
198  * the same vsw.
199  */
200 uint16_t	vnet_default_vlan_id = 1;
201 
202 /* delay in usec to wait for all references on a fdb entry to be dropped */
203 uint32_t vnet_fdbe_refcnt_delay = 10;
204 
205 static struct ether_addr etherbroadcastaddr = {
206 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
207 };
208 
209 
210 /*
211  * Property names
212  */
213 static char macaddr_propname[] = "local-mac-address";
214 
215 /*
216  * This is the string displayed by modinfo(1m).
217  */
218 static char vnet_ident[] = "vnet driver";
219 extern struct mod_ops mod_driverops;
220 static struct cb_ops cb_vnetops = {
221 	nulldev,		/* cb_open */
222 	nulldev,		/* cb_close */
223 	nodev,			/* cb_strategy */
224 	nodev,			/* cb_print */
225 	nodev,			/* cb_dump */
226 	nodev,			/* cb_read */
227 	nodev,			/* cb_write */
228 	nodev,			/* cb_ioctl */
229 	nodev,			/* cb_devmap */
230 	nodev,			/* cb_mmap */
231 	nodev,			/* cb_segmap */
232 	nochpoll,		/* cb_chpoll */
233 	ddi_prop_op,		/* cb_prop_op */
234 	NULL,			/* cb_stream */
235 	(int)(D_MP)		/* cb_flag */
236 };
237 
238 static struct dev_ops vnetops = {
239 	DEVO_REV,		/* devo_rev */
240 	0,			/* devo_refcnt */
241 	NULL,			/* devo_getinfo */
242 	nulldev,		/* devo_identify */
243 	nulldev,		/* devo_probe */
244 	vnetattach,		/* devo_attach */
245 	vnetdetach,		/* devo_detach */
246 	nodev,			/* devo_reset */
247 	&cb_vnetops,		/* devo_cb_ops */
248 	(struct bus_ops *)NULL,	/* devo_bus_ops */
249 	NULL,			/* devo_power */
250 	ddi_quiesce_not_supported,	/* devo_quiesce */
251 };
252 
253 static struct modldrv modldrv = {
254 	&mod_driverops,		/* Type of module.  This one is a driver */
255 	vnet_ident,		/* ID string */
256 	&vnetops		/* driver specific ops */
257 };
258 
259 static struct modlinkage modlinkage = {
260 	MODREV_1, (void *)&modldrv, NULL
261 };
262 
263 #ifdef DEBUG
264 
265 /*
266  * Print debug messages - set to 0xf to enable all msgs
267  */
268 int vnet_dbglevel = 0x8;
269 
270 static void
271 debug_printf(const char *fname, void *arg, const char *fmt, ...)
272 {
273 	char    buf[512];
274 	va_list ap;
275 	vnet_t *vnetp = (vnet_t *)arg;
276 	char    *bufp = buf;
277 
278 	if (vnetp == NULL) {
279 		(void) sprintf(bufp, "%s: ", fname);
280 		bufp += strlen(bufp);
281 	} else {
282 		(void) sprintf(bufp, "vnet%d:%s: ", vnetp->instance, fname);
283 		bufp += strlen(bufp);
284 	}
285 	va_start(ap, fmt);
286 	(void) vsprintf(bufp, fmt, ap);
287 	va_end(ap);
288 	cmn_err(CE_CONT, "%s\n", buf);
289 }
290 
291 #endif
292 
293 /* _init(9E): initialize the loadable module */
294 int
295 _init(void)
296 {
297 	int status;
298 
299 	DBG1(NULL, "enter\n");
300 
301 	mac_init_ops(&vnetops, "vnet");
302 	status = mod_install(&modlinkage);
303 	if (status != 0) {
304 		mac_fini_ops(&vnetops);
305 	}
306 	vdds_mod_init();
307 	vgen_mod_init();
308 	DBG1(NULL, "exit(%d)\n", status);
309 	return (status);
310 }
311 
312 /* _fini(9E): prepare the module for unloading. */
313 int
314 _fini(void)
315 {
316 	int		status;
317 
318 	DBG1(NULL, "enter\n");
319 
320 	status = vgen_mod_cleanup();
321 	if (status != 0)
322 		return (status);
323 
324 	status = mod_remove(&modlinkage);
325 	if (status != 0)
326 		return (status);
327 	mac_fini_ops(&vnetops);
328 	vgen_mod_fini();
329 	vdds_mod_fini();
330 
331 	DBG1(NULL, "exit(%d)\n", status);
332 	return (status);
333 }
334 
335 /* _info(9E): return information about the loadable module */
336 int
337 _info(struct modinfo *modinfop)
338 {
339 	return (mod_info(&modlinkage, modinfop));
340 }
341 
342 /*
343  * attach(9E): attach a device to the system.
344  * called once for each instance of the device on the system.
345  */
346 static int
347 vnetattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
348 {
349 	vnet_t			*vnetp;
350 	int			status;
351 	int			instance;
352 	uint64_t		reg;
353 	char			qname[TASKQ_NAMELEN];
354 	vnet_attach_progress_t	attach_progress;
355 
356 	attach_progress = AST_init;
357 
358 	switch (cmd) {
359 	case DDI_ATTACH:
360 		break;
361 	case DDI_RESUME:
362 	case DDI_PM_RESUME:
363 	default:
364 		goto vnet_attach_fail;
365 	}
366 
367 	instance = ddi_get_instance(dip);
368 	DBG1(NULL, "instance(%d) enter\n", instance);
369 
370 	/* allocate vnet_t and mac_t structures */
371 	vnetp = kmem_zalloc(sizeof (vnet_t), KM_SLEEP);
372 	vnetp->dip = dip;
373 	vnetp->instance = instance;
374 	rw_init(&vnetp->vrwlock, NULL, RW_DRIVER, NULL);
375 	rw_init(&vnetp->vsw_fp_rw, NULL, RW_DRIVER, NULL);
376 	attach_progress |= AST_vnet_alloc;
377 
378 	status = vdds_init(vnetp);
379 	if (status != 0) {
380 		goto vnet_attach_fail;
381 	}
382 	attach_progress |= AST_vdds_init;
383 
384 	/* setup links to vnet_t from both devinfo and mac_t */
385 	ddi_set_driver_private(dip, (caddr_t)vnetp);
386 
387 	/* read the mac address */
388 	status = vnet_read_mac_address(vnetp);
389 	if (status != DDI_SUCCESS) {
390 		goto vnet_attach_fail;
391 	}
392 	attach_progress |= AST_read_macaddr;
393 
394 	reg = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
395 	    DDI_PROP_DONTPASS, "reg", -1);
396 	if (reg == -1) {
397 		goto vnet_attach_fail;
398 	}
399 	vnetp->reg = reg;
400 
401 	vnet_fdb_create(vnetp);
402 	attach_progress |= AST_fdbh_alloc;
403 
404 	(void) snprintf(qname, TASKQ_NAMELEN, "vnet_taskq%d", instance);
405 	if ((vnetp->taskqp = ddi_taskq_create(dip, qname, 1,
406 	    TASKQ_DEFAULTPRI, 0)) == NULL) {
407 		cmn_err(CE_WARN, "!vnet%d: Unable to create task queue",
408 		    instance);
409 		goto vnet_attach_fail;
410 	}
411 	attach_progress |= AST_taskq_create;
412 
413 	/* add to the list of vnet devices */
414 	WRITE_ENTER(&vnet_rw);
415 	vnetp->nextp = vnet_headp;
416 	vnet_headp = vnetp;
417 	RW_EXIT(&vnet_rw);
418 
419 	attach_progress |= AST_vnet_list;
420 
421 	/*
422 	 * Initialize the generic vnet plugin which provides
423 	 * communication via sun4v LDC (logical domain channel) based
424 	 * resources. It will register the LDC resources as and when
425 	 * they become available.
426 	 */
427 	status = vgen_init(vnetp, reg, vnetp->dip,
428 	    (uint8_t *)vnetp->curr_macaddr, &vnetp->vgenhdl);
429 	if (status != DDI_SUCCESS) {
430 		DERR(vnetp, "vgen_init() failed\n");
431 		goto vnet_attach_fail;
432 	}
433 	attach_progress |= AST_vgen_init;
434 
435 	/* register with MAC layer */
436 	status = vnet_mac_register(vnetp);
437 	if (status != DDI_SUCCESS) {
438 		goto vnet_attach_fail;
439 	}
440 	vnetp->link_state = LINK_STATE_UNKNOWN;
441 
442 	attach_progress |= AST_macreg;
443 
444 	vnetp->attach_progress = attach_progress;
445 
446 	DBG1(NULL, "instance(%d) exit\n", instance);
447 	return (DDI_SUCCESS);
448 
449 vnet_attach_fail:
450 	vnetp->attach_progress = attach_progress;
451 	status = vnet_unattach(vnetp);
452 	ASSERT(status == 0);
453 	return (DDI_FAILURE);
454 }
455 
456 /*
457  * detach(9E): detach a device from the system.
458  */
459 static int
460 vnetdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
461 {
462 	vnet_t		*vnetp;
463 	int		instance;
464 
465 	instance = ddi_get_instance(dip);
466 	DBG1(NULL, "instance(%d) enter\n", instance);
467 
468 	vnetp = ddi_get_driver_private(dip);
469 	if (vnetp == NULL) {
470 		goto vnet_detach_fail;
471 	}
472 
473 	switch (cmd) {
474 	case DDI_DETACH:
475 		break;
476 	case DDI_SUSPEND:
477 	case DDI_PM_SUSPEND:
478 	default:
479 		goto vnet_detach_fail;
480 	}
481 
482 	if (vnet_unattach(vnetp) != 0) {
483 		goto vnet_detach_fail;
484 	}
485 
486 	return (DDI_SUCCESS);
487 
488 vnet_detach_fail:
489 	return (DDI_FAILURE);
490 }
491 
492 /*
493  * Common routine to handle vnetattach() failure and vnetdetach(). Note that
494  * the only reason this function could fail is if mac_unregister() fails.
495  * Otherwise, this function must ensure that all resources are freed and return
496  * success.
497  */
498 static int
499 vnet_unattach(vnet_t *vnetp)
500 {
501 	vnet_attach_progress_t	attach_progress;
502 
503 	attach_progress = vnetp->attach_progress;
504 
505 	/*
506 	 * Unregister from the gldv3 subsystem. This can fail, in particular
507 	 * if there are still any open references to this mac device; in which
508 	 * case we just return failure without continuing to detach further.
509 	 */
510 	if (attach_progress & AST_macreg) {
511 		if (mac_unregister(vnetp->mh) != 0) {
512 			return (1);
513 		}
514 		attach_progress &= ~AST_macreg;
515 	}
516 
517 	/*
518 	 * Now that we have unregistered from gldv3, we must finish all other
519 	 * steps and successfully return from this function; otherwise we will
520 	 * end up leaving the device in a broken/unusable state.
521 	 *
522 	 * First, release any hybrid resources assigned to this vnet device.
523 	 */
524 	if (attach_progress & AST_vdds_init) {
525 		vdds_cleanup(vnetp);
526 		attach_progress &= ~AST_vdds_init;
527 	}
528 
529 	/*
530 	 * Uninit vgen. This stops further mdeg callbacks to this vnet
531 	 * device and/or its ports; and detaches any existing ports.
532 	 */
533 	if (attach_progress & AST_vgen_init) {
534 		vgen_uninit(vnetp->vgenhdl);
535 		attach_progress &= ~AST_vgen_init;
536 	}
537 
538 	/* Destroy the taskq. */
539 	if (attach_progress & AST_taskq_create) {
540 		ddi_taskq_destroy(vnetp->taskqp);
541 		attach_progress &= ~AST_taskq_create;
542 	}
543 
544 	/* Destroy fdb. */
545 	if (attach_progress & AST_fdbh_alloc) {
546 		vnet_fdb_destroy(vnetp);
547 		attach_progress &= ~AST_fdbh_alloc;
548 	}
549 
550 	/* Remove from the device list */
551 	if (attach_progress & AST_vnet_list) {
552 		vnet_t		**vnetpp;
553 		/* unlink from instance(vnet_t) list */
554 		WRITE_ENTER(&vnet_rw);
555 		for (vnetpp = &vnet_headp; *vnetpp;
556 		    vnetpp = &(*vnetpp)->nextp) {
557 			if (*vnetpp == vnetp) {
558 				*vnetpp = vnetp->nextp;
559 				break;
560 			}
561 		}
562 		RW_EXIT(&vnet_rw);
563 		attach_progress &= ~AST_vnet_list;
564 	}
565 
566 	if (attach_progress & AST_vnet_alloc) {
567 		rw_destroy(&vnetp->vrwlock);
568 		rw_destroy(&vnetp->vsw_fp_rw);
569 		attach_progress &= ~AST_vnet_list;
570 		KMEM_FREE(vnetp);
571 	}
572 
573 	return (0);
574 }
575 
576 /* enable the device for transmit/receive */
577 static int
578 vnet_m_start(void *arg)
579 {
580 	vnet_t		*vnetp = arg;
581 
582 	DBG1(vnetp, "enter\n");
583 
584 	WRITE_ENTER(&vnetp->vrwlock);
585 	vnetp->flags |= VNET_STARTED;
586 	vnet_start_resources(vnetp);
587 	RW_EXIT(&vnetp->vrwlock);
588 
589 	DBG1(vnetp, "exit\n");
590 	return (VNET_SUCCESS);
591 
592 }
593 
594 /* stop transmit/receive for the device */
595 static void
596 vnet_m_stop(void *arg)
597 {
598 	vnet_t		*vnetp = arg;
599 
600 	DBG1(vnetp, "enter\n");
601 
602 	WRITE_ENTER(&vnetp->vrwlock);
603 	if (vnetp->flags & VNET_STARTED) {
604 		vnet_stop_resources(vnetp);
605 		vnetp->flags &= ~VNET_STARTED;
606 	}
607 	RW_EXIT(&vnetp->vrwlock);
608 
609 	DBG1(vnetp, "exit\n");
610 }
611 
612 /* set the unicast mac address of the device */
613 static int
614 vnet_m_unicst(void *arg, const uint8_t *macaddr)
615 {
616 	_NOTE(ARGUNUSED(macaddr))
617 
618 	vnet_t *vnetp = arg;
619 
620 	DBG1(vnetp, "enter\n");
621 	/*
622 	 * NOTE: setting mac address dynamically is not supported.
623 	 */
624 	DBG1(vnetp, "exit\n");
625 
626 	return (VNET_FAILURE);
627 }
628 
629 /* enable/disable a multicast address */
630 static int
631 vnet_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
632 {
633 	_NOTE(ARGUNUSED(add, mca))
634 
635 	vnet_t *vnetp = arg;
636 	vnet_res_t	*vresp;
637 	mac_register_t	*macp;
638 	mac_callbacks_t	*cbp;
639 	int rv = VNET_SUCCESS;
640 
641 	DBG1(vnetp, "enter\n");
642 
643 	READ_ENTER(&vnetp->vrwlock);
644 	for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) {
645 		if (vresp->type == VIO_NET_RES_LDC_SERVICE) {
646 			macp = &vresp->macreg;
647 			cbp = macp->m_callbacks;
648 			rv = cbp->mc_multicst(macp->m_driver, add, mca);
649 		}
650 	}
651 	RW_EXIT(&vnetp->vrwlock);
652 
653 	DBG1(vnetp, "exit(%d)\n", rv);
654 	return (rv);
655 }
656 
657 /* set or clear promiscuous mode on the device */
658 static int
659 vnet_m_promisc(void *arg, boolean_t on)
660 {
661 	_NOTE(ARGUNUSED(on))
662 
663 	vnet_t *vnetp = arg;
664 	DBG1(vnetp, "enter\n");
665 	/*
666 	 * NOTE: setting promiscuous mode is not supported, just return success.
667 	 */
668 	DBG1(vnetp, "exit\n");
669 	return (VNET_SUCCESS);
670 }
671 
672 /*
673  * Transmit a chain of packets. This function provides switching functionality
674  * based on the destination mac address to reach other guests (within ldoms) or
675  * external hosts.
676  */
677 mblk_t *
678 vnet_m_tx(void *arg, mblk_t *mp)
679 {
680 	vnet_t			*vnetp;
681 	vnet_res_t		*vresp;
682 	mblk_t			*next;
683 	mblk_t			*resid_mp;
684 	mac_register_t		*macp;
685 	struct ether_header	*ehp;
686 	boolean_t		is_unicast;
687 	boolean_t		is_pvid;	/* non-default pvid ? */
688 	boolean_t		hres;		/* Hybrid resource ? */
689 
690 	vnetp = (vnet_t *)arg;
691 	DBG1(vnetp, "enter\n");
692 	ASSERT(mp != NULL);
693 
694 	is_pvid = (vnetp->pvid != vnetp->default_vlan_id) ? B_TRUE : B_FALSE;
695 
696 	while (mp != NULL) {
697 
698 		next = mp->b_next;
699 		mp->b_next = NULL;
700 
701 		/*
702 		 * Find fdb entry for the destination
703 		 * and hold a reference to it.
704 		 */
705 		ehp = (struct ether_header *)mp->b_rptr;
706 		vresp = vnet_fdbe_find(vnetp, &ehp->ether_dhost);
707 		if (vresp != NULL) {
708 
709 			/*
710 			 * Destination found in FDB.
711 			 * The destination is a vnet device within ldoms
712 			 * and directly reachable, invoke the tx function
713 			 * in the fdb entry.
714 			 */
715 			macp = &vresp->macreg;
716 			resid_mp = macp->m_callbacks->mc_tx(macp->m_driver, mp);
717 
718 			/* tx done; now release ref on fdb entry */
719 			VNET_FDBE_REFRELE(vresp);
720 
721 			if (resid_mp != NULL) {
722 				/* m_tx failed */
723 				mp->b_next = next;
724 				break;
725 			}
726 		} else {
727 			is_unicast = !(IS_BROADCAST(ehp) ||
728 			    (IS_MULTICAST(ehp)));
729 			/*
730 			 * Destination is not in FDB.
731 			 * If the destination is broadcast or multicast,
732 			 * then forward the packet to vswitch.
733 			 * If a Hybrid resource avilable, then send the
734 			 * unicast packet via hybrid resource, otherwise
735 			 * forward it to vswitch.
736 			 */
737 			READ_ENTER(&vnetp->vsw_fp_rw);
738 
739 			if ((is_unicast) && (vnetp->hio_fp != NULL)) {
740 				vresp = vnetp->hio_fp;
741 				hres = B_TRUE;
742 			} else {
743 				vresp = vnetp->vsw_fp;
744 				hres = B_FALSE;
745 			}
746 			if (vresp == NULL) {
747 				/*
748 				 * no fdb entry to vsw? drop the packet.
749 				 */
750 				RW_EXIT(&vnetp->vsw_fp_rw);
751 				freemsg(mp);
752 				mp = next;
753 				continue;
754 			}
755 
756 			/* ref hold the fdb entry to vsw */
757 			VNET_FDBE_REFHOLD(vresp);
758 
759 			RW_EXIT(&vnetp->vsw_fp_rw);
760 
761 			/*
762 			 * In the case of a hybrid resource we need to insert
763 			 * the tag for the pvid case here; unlike packets that
764 			 * are destined to a vnet/vsw in which case the vgen
765 			 * layer does the tagging before sending it over ldc.
766 			 */
767 			if (hres == B_TRUE) {
768 				/*
769 				 * Determine if the frame being transmitted
770 				 * over the hybrid resource is untagged. If so,
771 				 * insert the tag before transmitting.
772 				 */
773 				if (is_pvid == B_TRUE &&
774 				    ehp->ether_type != htons(ETHERTYPE_VLAN)) {
775 
776 					mp = vnet_vlan_insert_tag(mp,
777 					    vnetp->pvid);
778 					if (mp == NULL) {
779 						VNET_FDBE_REFRELE(vresp);
780 						mp = next;
781 						continue;
782 					}
783 
784 				}
785 			}
786 
787 			macp = &vresp->macreg;
788 			resid_mp = macp->m_callbacks->mc_tx(macp->m_driver, mp);
789 
790 			/* tx done; now release ref on fdb entry */
791 			VNET_FDBE_REFRELE(vresp);
792 
793 			if (resid_mp != NULL) {
794 				/* m_tx failed */
795 				mp->b_next = next;
796 				break;
797 			}
798 		}
799 
800 		mp = next;
801 	}
802 
803 	DBG1(vnetp, "exit\n");
804 	return (mp);
805 }
806 
807 /* get statistics from the device */
808 int
809 vnet_m_stat(void *arg, uint_t stat, uint64_t *val)
810 {
811 	vnet_t *vnetp = arg;
812 	vnet_res_t	*vresp;
813 	mac_register_t	*macp;
814 	mac_callbacks_t	*cbp;
815 	uint64_t val_total = 0;
816 
817 	DBG1(vnetp, "enter\n");
818 
819 	/*
820 	 * get the specified statistic from each transport and return the
821 	 * aggregate val.  This obviously only works for counters.
822 	 */
823 	if ((IS_MAC_STAT(stat) && !MAC_STAT_ISACOUNTER(stat)) ||
824 	    (IS_MACTYPE_STAT(stat) && !ETHER_STAT_ISACOUNTER(stat))) {
825 		return (ENOTSUP);
826 	}
827 
828 	READ_ENTER(&vnetp->vrwlock);
829 	for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) {
830 		macp = &vresp->macreg;
831 		cbp = macp->m_callbacks;
832 		if (cbp->mc_getstat(macp->m_driver, stat, val) == 0)
833 			val_total += *val;
834 	}
835 	RW_EXIT(&vnetp->vrwlock);
836 
837 	*val = val_total;
838 
839 	DBG1(vnetp, "exit\n");
840 	return (0);
841 }
842 
843 /* wrapper function for mac_register() */
844 static int
845 vnet_mac_register(vnet_t *vnetp)
846 {
847 	mac_register_t	*macp;
848 	int		err;
849 
850 	if ((macp = mac_alloc(MAC_VERSION)) == NULL)
851 		return (DDI_FAILURE);
852 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
853 	macp->m_driver = vnetp;
854 	macp->m_dip = vnetp->dip;
855 	macp->m_src_addr = vnetp->curr_macaddr;
856 	macp->m_callbacks = &vnet_m_callbacks;
857 	macp->m_min_sdu = 0;
858 	macp->m_max_sdu = vnetp->mtu;
859 	macp->m_margin = VLAN_TAGSZ;
860 
861 	/*
862 	 * Finally, we're ready to register ourselves with the MAC layer
863 	 * interface; if this succeeds, we're all ready to start()
864 	 */
865 	err = mac_register(macp, &vnetp->mh);
866 	mac_free(macp);
867 	return (err == 0 ? DDI_SUCCESS : DDI_FAILURE);
868 }
869 
870 /* read the mac address of the device */
871 static int
872 vnet_read_mac_address(vnet_t *vnetp)
873 {
874 	uchar_t 	*macaddr;
875 	uint32_t 	size;
876 	int 		rv;
877 
878 	rv = ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, vnetp->dip,
879 	    DDI_PROP_DONTPASS, macaddr_propname, &macaddr, &size);
880 	if ((rv != DDI_PROP_SUCCESS) || (size != ETHERADDRL)) {
881 		DWARN(vnetp, "prop_lookup failed(%s) err(%d)\n",
882 		    macaddr_propname, rv);
883 		return (DDI_FAILURE);
884 	}
885 	bcopy(macaddr, (caddr_t)vnetp->vendor_addr, ETHERADDRL);
886 	bcopy(macaddr, (caddr_t)vnetp->curr_macaddr, ETHERADDRL);
887 	ddi_prop_free(macaddr);
888 
889 	return (DDI_SUCCESS);
890 }
891 
892 static void
893 vnet_fdb_create(vnet_t *vnetp)
894 {
895 	char		hashname[MAXNAMELEN];
896 
897 	(void) snprintf(hashname, MAXNAMELEN, "vnet%d-fdbhash",
898 	    vnetp->instance);
899 	vnetp->fdb_nchains = vnet_fdb_nchains;
900 	vnetp->fdb_hashp = mod_hash_create_ptrhash(hashname, vnetp->fdb_nchains,
901 	    mod_hash_null_valdtor, sizeof (void *));
902 }
903 
904 static void
905 vnet_fdb_destroy(vnet_t *vnetp)
906 {
907 	/* destroy fdb-hash-table */
908 	if (vnetp->fdb_hashp != NULL) {
909 		mod_hash_destroy_hash(vnetp->fdb_hashp);
910 		vnetp->fdb_hashp = NULL;
911 		vnetp->fdb_nchains = 0;
912 	}
913 }
914 
915 /*
916  * Add an entry into the fdb.
917  */
918 void
919 vnet_fdbe_add(vnet_t *vnetp, vnet_res_t *vresp)
920 {
921 	uint64_t	addr = 0;
922 	int		rv;
923 
924 	KEY_HASH(addr, vresp->rem_macaddr);
925 
926 	/*
927 	 * If the entry being added corresponds to LDC_SERVICE resource,
928 	 * that is, vswitch connection, it is added to the hash and also
929 	 * the entry is cached, an additional reference count reflects
930 	 * this. The HYBRID resource is not added to the hash, but only
931 	 * cached, as it is only used for sending out packets for unknown
932 	 * unicast destinations.
933 	 */
934 	(vresp->type == VIO_NET_RES_LDC_SERVICE) ?
935 	    (vresp->refcnt = 1) : (vresp->refcnt = 0);
936 
937 	/*
938 	 * Note: duplicate keys will be rejected by mod_hash.
939 	 */
940 	if (vresp->type != VIO_NET_RES_HYBRID) {
941 		rv = mod_hash_insert(vnetp->fdb_hashp, (mod_hash_key_t)addr,
942 		    (mod_hash_val_t)vresp);
943 		if (rv != 0) {
944 			DWARN(vnetp, "Duplicate macaddr key(%lx)\n", addr);
945 			return;
946 		}
947 	}
948 
949 	if (vresp->type == VIO_NET_RES_LDC_SERVICE) {
950 		/* Cache the fdb entry to vsw-port */
951 		WRITE_ENTER(&vnetp->vsw_fp_rw);
952 		if (vnetp->vsw_fp == NULL)
953 			vnetp->vsw_fp = vresp;
954 		RW_EXIT(&vnetp->vsw_fp_rw);
955 	} else if (vresp->type == VIO_NET_RES_HYBRID) {
956 		/* Cache the fdb entry to hybrid resource */
957 		WRITE_ENTER(&vnetp->vsw_fp_rw);
958 		if (vnetp->hio_fp == NULL)
959 			vnetp->hio_fp = vresp;
960 		RW_EXIT(&vnetp->vsw_fp_rw);
961 	}
962 }
963 
964 /*
965  * Remove an entry from fdb.
966  */
967 static void
968 vnet_fdbe_del(vnet_t *vnetp, vnet_res_t *vresp)
969 {
970 	uint64_t	addr = 0;
971 	int		rv;
972 	uint32_t	refcnt;
973 	vnet_res_t	*tmp;
974 
975 	KEY_HASH(addr, vresp->rem_macaddr);
976 
977 	/*
978 	 * Remove the entry from fdb hash table.
979 	 * This prevents further references to this fdb entry.
980 	 */
981 	if (vresp->type != VIO_NET_RES_HYBRID) {
982 		rv = mod_hash_remove(vnetp->fdb_hashp, (mod_hash_key_t)addr,
983 		    (mod_hash_val_t *)&tmp);
984 		if (rv != 0) {
985 			/*
986 			 * As the resources are added to the hash only
987 			 * after they are started, this can occur if
988 			 * a resource unregisters before it is ever started.
989 			 */
990 			return;
991 		}
992 	}
993 
994 	if (vresp->type == VIO_NET_RES_LDC_SERVICE) {
995 		WRITE_ENTER(&vnetp->vsw_fp_rw);
996 
997 		ASSERT(tmp == vnetp->vsw_fp);
998 		vnetp->vsw_fp = NULL;
999 
1000 		RW_EXIT(&vnetp->vsw_fp_rw);
1001 	} else if (vresp->type == VIO_NET_RES_HYBRID) {
1002 		WRITE_ENTER(&vnetp->vsw_fp_rw);
1003 
1004 		vnetp->hio_fp = NULL;
1005 
1006 		RW_EXIT(&vnetp->vsw_fp_rw);
1007 	}
1008 
1009 	/*
1010 	 * If there are threads already ref holding before the entry was
1011 	 * removed from hash table, then wait for ref count to drop to zero.
1012 	 */
1013 	(vresp->type == VIO_NET_RES_LDC_SERVICE) ?
1014 	    (refcnt = 1) : (refcnt = 0);
1015 	while (vresp->refcnt > refcnt) {
1016 		delay(drv_usectohz(vnet_fdbe_refcnt_delay));
1017 	}
1018 }
1019 
1020 /*
1021  * Search fdb for a given mac address. If an entry is found, hold
1022  * a reference to it and return the entry; else returns NULL.
1023  */
1024 static vnet_res_t *
1025 vnet_fdbe_find(vnet_t *vnetp, struct ether_addr *addrp)
1026 {
1027 	uint64_t	key = 0;
1028 	vnet_res_t	*vresp;
1029 	int		rv;
1030 
1031 	KEY_HASH(key, addrp->ether_addr_octet);
1032 
1033 	rv = mod_hash_find_cb(vnetp->fdb_hashp, (mod_hash_key_t)key,
1034 	    (mod_hash_val_t *)&vresp, vnet_fdbe_find_cb);
1035 
1036 	if (rv != 0)
1037 		return (NULL);
1038 
1039 	return (vresp);
1040 }
1041 
1042 /*
1043  * Callback function provided to mod_hash_find_cb(). After finding the fdb
1044  * entry corresponding to the key (macaddr), this callback will be invoked by
1045  * mod_hash_find_cb() to atomically increment the reference count on the fdb
1046  * entry before returning the found entry.
1047  */
1048 static void
1049 vnet_fdbe_find_cb(mod_hash_key_t key, mod_hash_val_t val)
1050 {
1051 	_NOTE(ARGUNUSED(key))
1052 	VNET_FDBE_REFHOLD((vnet_res_t *)val);
1053 }
1054 
1055 /*
1056  * Frames received that are tagged with the pvid of the vnet device must be
1057  * untagged before sending up the stack. This function walks the chain of rx
1058  * frames, untags any such frames and returns the updated chain.
1059  *
1060  * Arguments:
1061  *    pvid:  pvid of the vnet device for which packets are being received
1062  *    mp:    head of pkt chain to be validated and untagged
1063  *
1064  * Returns:
1065  *    mp:    head of updated chain of packets
1066  */
1067 static void
1068 vnet_rx_frames_untag(uint16_t pvid, mblk_t **mp)
1069 {
1070 	struct ether_vlan_header	*evhp;
1071 	mblk_t				*bp;
1072 	mblk_t				*bpt;
1073 	mblk_t				*bph;
1074 	mblk_t				*bpn;
1075 
1076 	bpn = bph = bpt = NULL;
1077 
1078 	for (bp = *mp; bp != NULL; bp = bpn) {
1079 
1080 		bpn = bp->b_next;
1081 		bp->b_next = bp->b_prev = NULL;
1082 
1083 		evhp = (struct ether_vlan_header *)bp->b_rptr;
1084 
1085 		if (ntohs(evhp->ether_tpid) == ETHERTYPE_VLAN &&
1086 		    VLAN_ID(ntohs(evhp->ether_tci)) == pvid) {
1087 
1088 			bp = vnet_vlan_remove_tag(bp);
1089 			if (bp == NULL) {
1090 				continue;
1091 			}
1092 
1093 		}
1094 
1095 		/* build a chain of processed packets */
1096 		if (bph == NULL) {
1097 			bph = bpt = bp;
1098 		} else {
1099 			bpt->b_next = bp;
1100 			bpt = bp;
1101 		}
1102 
1103 	}
1104 
1105 	*mp = bph;
1106 }
1107 
1108 static void
1109 vnet_rx(vio_net_handle_t vrh, mblk_t *mp)
1110 {
1111 	vnet_res_t	*vresp = (vnet_res_t *)vrh;
1112 	vnet_t		*vnetp = vresp->vnetp;
1113 
1114 	if ((vnetp == NULL) || (vnetp->mh == 0)) {
1115 		freemsgchain(mp);
1116 		return;
1117 	}
1118 
1119 	/*
1120 	 * Packets received over a hybrid resource need additional processing
1121 	 * to remove the tag, for the pvid case. The underlying resource is
1122 	 * not aware of the vnet's pvid and thus packets are received with the
1123 	 * vlan tag in the header; unlike packets that are received over a ldc
1124 	 * channel in which case the peer vnet/vsw would have already removed
1125 	 * the tag.
1126 	 */
1127 	if (vresp->type == VIO_NET_RES_HYBRID &&
1128 	    vnetp->pvid != vnetp->default_vlan_id) {
1129 
1130 		vnet_rx_frames_untag(vnetp->pvid, &mp);
1131 		if (mp == NULL) {
1132 			return;
1133 		}
1134 	}
1135 
1136 	mac_rx(vnetp->mh, NULL, mp);
1137 }
1138 
1139 void
1140 vnet_tx_update(vio_net_handle_t vrh)
1141 {
1142 	vnet_res_t *vresp = (vnet_res_t *)vrh;
1143 	vnet_t *vnetp = vresp->vnetp;
1144 
1145 	if ((vnetp != NULL) && (vnetp->mh != NULL)) {
1146 		mac_tx_update(vnetp->mh);
1147 	}
1148 }
1149 
1150 /*
1151  * Update the new mtu of vnet into the mac layer. First check if the device has
1152  * been plumbed and if so fail the mtu update. Returns 0 on success.
1153  */
1154 int
1155 vnet_mtu_update(vnet_t *vnetp, uint32_t mtu)
1156 {
1157 	int	rv;
1158 
1159 	if (vnetp == NULL || vnetp->mh == NULL) {
1160 		return (EINVAL);
1161 	}
1162 
1163 	WRITE_ENTER(&vnetp->vrwlock);
1164 
1165 	if (vnetp->flags & VNET_STARTED) {
1166 		RW_EXIT(&vnetp->vrwlock);
1167 		cmn_err(CE_NOTE, "!vnet%d: Unable to process mtu "
1168 		    "update as the device is plumbed\n",
1169 		    vnetp->instance);
1170 		return (EBUSY);
1171 	}
1172 
1173 	/* update mtu in the mac layer */
1174 	rv = mac_maxsdu_update(vnetp->mh, mtu);
1175 	if (rv != 0) {
1176 		RW_EXIT(&vnetp->vrwlock);
1177 		cmn_err(CE_NOTE,
1178 		    "!vnet%d: Unable to update mtu with mac layer\n",
1179 		    vnetp->instance);
1180 		return (EIO);
1181 	}
1182 
1183 	vnetp->mtu = mtu;
1184 
1185 	RW_EXIT(&vnetp->vrwlock);
1186 
1187 	return (0);
1188 }
1189 
1190 /*
1191  * Update the link state of vnet to the mac layer.
1192  */
1193 void
1194 vnet_link_update(vnet_t *vnetp, link_state_t link_state)
1195 {
1196 	if (vnetp == NULL || vnetp->mh == NULL) {
1197 		return;
1198 	}
1199 
1200 	WRITE_ENTER(&vnetp->vrwlock);
1201 	if (vnetp->link_state == link_state) {
1202 		RW_EXIT(&vnetp->vrwlock);
1203 		return;
1204 	}
1205 	vnetp->link_state = link_state;
1206 	RW_EXIT(&vnetp->vrwlock);
1207 
1208 	mac_link_update(vnetp->mh, link_state);
1209 }
1210 
1211 /*
1212  * vio_net_resource_reg -- An interface called to register a resource
1213  *	with vnet.
1214  *	macp -- a GLDv3 mac_register that has all the details of
1215  *		a resource and its callbacks etc.
1216  *	type -- resource type.
1217  *	local_macaddr -- resource's MAC address. This is used to
1218  *			 associate a resource with a corresponding vnet.
1219  *	remote_macaddr -- remote side MAC address. This is ignored for
1220  *			  the Hybrid resources.
1221  *	vhp -- A handle returned to the caller.
1222  *	vcb -- A set of callbacks provided to the callers.
1223  */
1224 int vio_net_resource_reg(mac_register_t *macp, vio_net_res_type_t type,
1225     ether_addr_t local_macaddr, ether_addr_t rem_macaddr, vio_net_handle_t *vhp,
1226     vio_net_callbacks_t *vcb)
1227 {
1228 	vnet_t	*vnetp;
1229 	vnet_res_t *vresp;
1230 
1231 	vresp = kmem_zalloc(sizeof (vnet_res_t), KM_SLEEP);
1232 	ether_copy(local_macaddr, vresp->local_macaddr);
1233 	ether_copy(rem_macaddr, vresp->rem_macaddr);
1234 	vresp->type = type;
1235 	bcopy(macp, &vresp->macreg, sizeof (mac_register_t));
1236 
1237 	DBG1(NULL, "Resource Registerig type=0%X\n", type);
1238 
1239 	READ_ENTER(&vnet_rw);
1240 	vnetp = vnet_headp;
1241 	while (vnetp != NULL) {
1242 		if (VNET_MATCH_RES(vresp, vnetp)) {
1243 			vresp->vnetp = vnetp;
1244 
1245 			/* Setup kstats for hio resource */
1246 			if (vresp->type == VIO_NET_RES_HYBRID) {
1247 				vresp->ksp = vnet_hio_setup_kstats(DRV_NAME,
1248 				    "hio", vresp);
1249 				if (vresp->ksp == NULL) {
1250 					cmn_err(CE_NOTE, "!vnet%d: Cannot "
1251 					    "create kstats for hio resource",
1252 					    vnetp->instance);
1253 				}
1254 			}
1255 
1256 			WRITE_ENTER(&vnetp->vrwlock);
1257 			vresp->nextp = vnetp->vres_list;
1258 			vnetp->vres_list = vresp;
1259 			RW_EXIT(&vnetp->vrwlock);
1260 			break;
1261 		}
1262 		vnetp = vnetp->nextp;
1263 	}
1264 	RW_EXIT(&vnet_rw);
1265 	if (vresp->vnetp == NULL) {
1266 		DWARN(NULL, "No vnet instance");
1267 		kmem_free(vresp, sizeof (vnet_res_t));
1268 		return (ENXIO);
1269 	}
1270 
1271 	*vhp = vresp;
1272 	vcb->vio_net_rx_cb = vnet_rx;
1273 	vcb->vio_net_tx_update = vnet_tx_update;
1274 	vcb->vio_net_report_err = vnet_handle_res_err;
1275 
1276 	/* Dispatch a task to start resources */
1277 	vnet_dispatch_res_task(vnetp);
1278 	return (0);
1279 }
1280 
1281 /*
1282  * vio_net_resource_unreg -- An interface to unregister a resource.
1283  */
1284 void
1285 vio_net_resource_unreg(vio_net_handle_t vhp)
1286 {
1287 	vnet_res_t *vresp = (vnet_res_t *)vhp;
1288 	vnet_t *vnetp = vresp->vnetp;
1289 	vnet_res_t *vrp;
1290 	kstat_t *ksp = NULL;
1291 
1292 	DBG1(NULL, "Resource Registerig hdl=0x%p", vhp);
1293 
1294 	ASSERT(vnetp != NULL);
1295 	vnet_fdbe_del(vnetp, vresp);
1296 
1297 	WRITE_ENTER(&vnetp->vrwlock);
1298 	if (vresp == vnetp->vres_list) {
1299 		vnetp->vres_list = vresp->nextp;
1300 	} else {
1301 		vrp = vnetp->vres_list;
1302 		while (vrp->nextp != NULL) {
1303 			if (vrp->nextp == vresp) {
1304 				vrp->nextp = vresp->nextp;
1305 				break;
1306 			}
1307 			vrp = vrp->nextp;
1308 		}
1309 	}
1310 
1311 	ksp = vresp->ksp;
1312 	vresp->ksp = NULL;
1313 
1314 	vresp->vnetp = NULL;
1315 	vresp->nextp = NULL;
1316 	RW_EXIT(&vnetp->vrwlock);
1317 	vnet_hio_destroy_kstats(ksp);
1318 	KMEM_FREE(vresp);
1319 }
1320 
1321 /*
1322  * vnet_dds_rx -- an interface called by vgen to DDS messages.
1323  */
1324 void
1325 vnet_dds_rx(void *arg, void *dmsg)
1326 {
1327 	vnet_t *vnetp = arg;
1328 	vdds_process_dds_msg(vnetp, dmsg);
1329 }
1330 
1331 /*
1332  * vnet_send_dds_msg -- An interface provided to DDS to send
1333  *	DDS messages. This simply sends meessages via vgen.
1334  */
1335 int
1336 vnet_send_dds_msg(vnet_t *vnetp, void *dmsg)
1337 {
1338 	int rv;
1339 
1340 	if (vnetp->vgenhdl != NULL) {
1341 		rv = vgen_dds_tx(vnetp->vgenhdl, dmsg);
1342 	}
1343 	return (rv);
1344 }
1345 
1346 /*
1347  * vnet_cleanup_hio -- an interface called by vgen to cleanup hio resources.
1348  */
1349 void
1350 vnet_dds_cleanup_hio(vnet_t *vnetp)
1351 {
1352 	vdds_cleanup_hio(vnetp);
1353 }
1354 
1355 /*
1356  * vnet_handle_res_err -- A callback function called by a resource
1357  *	to report an error. For example, vgen can call to report
1358  *	an LDC down/reset event. This will trigger cleanup of associated
1359  *	Hybrid resource.
1360  */
1361 /* ARGSUSED */
1362 static void
1363 vnet_handle_res_err(vio_net_handle_t vrh, vio_net_err_val_t err)
1364 {
1365 	vnet_res_t *vresp = (vnet_res_t *)vrh;
1366 	vnet_t *vnetp = vresp->vnetp;
1367 
1368 	if (vnetp == NULL) {
1369 		return;
1370 	}
1371 	if ((vresp->type != VIO_NET_RES_LDC_SERVICE) &&
1372 	    (vresp->type != VIO_NET_RES_HYBRID)) {
1373 		return;
1374 	}
1375 
1376 	vdds_cleanup_hio(vnetp);
1377 }
1378 
1379 /*
1380  * vnet_dispatch_res_task -- A function to dispatch tasks start resources.
1381  */
1382 static void
1383 vnet_dispatch_res_task(vnet_t *vnetp)
1384 {
1385 	int rv;
1386 
1387 	/*
1388 	 * Dispatch the task. It could be the case that vnetp->flags does
1389 	 * not have VNET_STARTED set. This is ok as vnet_rest_start_task()
1390 	 * can abort the task when the task is started.
1391 	 */
1392 	rv = ddi_taskq_dispatch(vnetp->taskqp, vnet_res_start_task,
1393 	    vnetp, DDI_NOSLEEP);
1394 	if (rv != DDI_SUCCESS) {
1395 		cmn_err(CE_WARN,
1396 		    "vnet%d:Can't dispatch start resource task",
1397 		    vnetp->instance);
1398 	}
1399 }
1400 
1401 /*
1402  * vnet_res_start_task -- A taskq callback function that starts a resource.
1403  */
1404 static void
1405 vnet_res_start_task(void *arg)
1406 {
1407 	vnet_t *vnetp = arg;
1408 
1409 	WRITE_ENTER(&vnetp->vrwlock);
1410 	if (vnetp->flags & VNET_STARTED) {
1411 		vnet_start_resources(vnetp);
1412 	}
1413 	RW_EXIT(&vnetp->vrwlock);
1414 }
1415 
1416 /*
1417  * vnet_start_resources -- starts all resources associated with
1418  *	a vnet.
1419  */
1420 static void
1421 vnet_start_resources(vnet_t *vnetp)
1422 {
1423 	mac_register_t	*macp;
1424 	mac_callbacks_t	*cbp;
1425 	vnet_res_t	*vresp;
1426 	int rv;
1427 
1428 	DBG1(vnetp, "enter\n");
1429 
1430 	for (vresp = vnetp->vres_list; vresp != NULL; vresp = vresp->nextp) {
1431 		/* skip if it is already started */
1432 		if (vresp->flags & VNET_STARTED) {
1433 			continue;
1434 		}
1435 		macp = &vresp->macreg;
1436 		cbp = macp->m_callbacks;
1437 		rv = cbp->mc_start(macp->m_driver);
1438 		if (rv == 0) {
1439 			/*
1440 			 * Successfully started the resource, so now
1441 			 * add it to the fdb.
1442 			 */
1443 			vresp->flags |= VNET_STARTED;
1444 			vnet_fdbe_add(vnetp, vresp);
1445 		}
1446 	}
1447 
1448 	DBG1(vnetp, "exit\n");
1449 
1450 }
1451 
1452 /*
1453  * vnet_stop_resources -- stop all resources associated with a vnet.
1454  */
1455 static void
1456 vnet_stop_resources(vnet_t *vnetp)
1457 {
1458 	vnet_res_t	*vresp;
1459 	vnet_res_t	*nvresp;
1460 	mac_register_t	*macp;
1461 	mac_callbacks_t	*cbp;
1462 
1463 	DBG1(vnetp, "enter\n");
1464 
1465 	for (vresp = vnetp->vres_list; vresp != NULL; ) {
1466 		nvresp = vresp->nextp;
1467 		if (vresp->flags & VNET_STARTED) {
1468 			macp = &vresp->macreg;
1469 			cbp = macp->m_callbacks;
1470 			cbp->mc_stop(macp->m_driver);
1471 			vresp->flags &= ~VNET_STARTED;
1472 		}
1473 		vresp = nvresp;
1474 	}
1475 	DBG1(vnetp, "exit\n");
1476 }
1477 
1478 /*
1479  * Setup kstats for the HIO statistics.
1480  * NOTE: the synchronization for the statistics is the
1481  * responsibility of the caller.
1482  */
1483 kstat_t *
1484 vnet_hio_setup_kstats(char *ks_mod, char *ks_name, vnet_res_t *vresp)
1485 {
1486 	kstat_t *ksp;
1487 	vnet_t *vnetp = vresp->vnetp;
1488 	vnet_hio_kstats_t *hiokp;
1489 	size_t size;
1490 
1491 	ASSERT(vnetp != NULL);
1492 	size = sizeof (vnet_hio_kstats_t) / sizeof (kstat_named_t);
1493 	ksp = kstat_create(ks_mod, vnetp->instance, ks_name, "net",
1494 	    KSTAT_TYPE_NAMED, size, 0);
1495 	if (ksp == NULL) {
1496 		return (NULL);
1497 	}
1498 
1499 	hiokp = (vnet_hio_kstats_t *)ksp->ks_data;
1500 	kstat_named_init(&hiokp->ipackets,		"ipackets",
1501 	    KSTAT_DATA_ULONG);
1502 	kstat_named_init(&hiokp->ierrors,		"ierrors",
1503 	    KSTAT_DATA_ULONG);
1504 	kstat_named_init(&hiokp->opackets,		"opackets",
1505 	    KSTAT_DATA_ULONG);
1506 	kstat_named_init(&hiokp->oerrors,		"oerrors",
1507 	    KSTAT_DATA_ULONG);
1508 
1509 
1510 	/* MIB II kstat variables */
1511 	kstat_named_init(&hiokp->rbytes,		"rbytes",
1512 	    KSTAT_DATA_ULONG);
1513 	kstat_named_init(&hiokp->obytes,		"obytes",
1514 	    KSTAT_DATA_ULONG);
1515 	kstat_named_init(&hiokp->multircv,		"multircv",
1516 	    KSTAT_DATA_ULONG);
1517 	kstat_named_init(&hiokp->multixmt,		"multixmt",
1518 	    KSTAT_DATA_ULONG);
1519 	kstat_named_init(&hiokp->brdcstrcv,		"brdcstrcv",
1520 	    KSTAT_DATA_ULONG);
1521 	kstat_named_init(&hiokp->brdcstxmt,		"brdcstxmt",
1522 	    KSTAT_DATA_ULONG);
1523 	kstat_named_init(&hiokp->norcvbuf,		"norcvbuf",
1524 	    KSTAT_DATA_ULONG);
1525 	kstat_named_init(&hiokp->noxmtbuf,		"noxmtbuf",
1526 	    KSTAT_DATA_ULONG);
1527 
1528 	ksp->ks_update = vnet_hio_update_kstats;
1529 	ksp->ks_private = (void *)vresp;
1530 	kstat_install(ksp);
1531 	return (ksp);
1532 }
1533 
1534 /*
1535  * Destroy kstats.
1536  */
1537 static void
1538 vnet_hio_destroy_kstats(kstat_t *ksp)
1539 {
1540 	if (ksp != NULL)
1541 		kstat_delete(ksp);
1542 }
1543 
1544 /*
1545  * Update the kstats.
1546  */
1547 static int
1548 vnet_hio_update_kstats(kstat_t *ksp, int rw)
1549 {
1550 	vnet_t *vnetp;
1551 	vnet_res_t *vresp;
1552 	vnet_hio_stats_t statsp;
1553 	vnet_hio_kstats_t *hiokp;
1554 
1555 	vresp = (vnet_res_t *)ksp->ks_private;
1556 	vnetp = vresp->vnetp;
1557 
1558 	bzero(&statsp, sizeof (vnet_hio_stats_t));
1559 
1560 	READ_ENTER(&vnetp->vsw_fp_rw);
1561 	if (vnetp->hio_fp == NULL) {
1562 		/* not using hio resources, just return */
1563 		RW_EXIT(&vnetp->vsw_fp_rw);
1564 		return (0);
1565 	}
1566 	VNET_FDBE_REFHOLD(vnetp->hio_fp);
1567 	RW_EXIT(&vnetp->vsw_fp_rw);
1568 	vnet_hio_get_stats(vnetp->hio_fp, &statsp);
1569 	VNET_FDBE_REFRELE(vnetp->hio_fp);
1570 
1571 	hiokp = (vnet_hio_kstats_t *)ksp->ks_data;
1572 
1573 	if (rw == KSTAT_READ) {
1574 		/* Link Input/Output stats */
1575 		hiokp->ipackets.value.ul	= (uint32_t)statsp.ipackets;
1576 		hiokp->ipackets64.value.ull	= statsp.ipackets;
1577 		hiokp->ierrors.value.ul		= statsp.ierrors;
1578 		hiokp->opackets.value.ul	= (uint32_t)statsp.opackets;
1579 		hiokp->opackets64.value.ull	= statsp.opackets;
1580 		hiokp->oerrors.value.ul		= statsp.oerrors;
1581 
1582 		/* MIB II kstat variables */
1583 		hiokp->rbytes.value.ul		= (uint32_t)statsp.rbytes;
1584 		hiokp->rbytes64.value.ull	= statsp.rbytes;
1585 		hiokp->obytes.value.ul		= (uint32_t)statsp.obytes;
1586 		hiokp->obytes64.value.ull	= statsp.obytes;
1587 		hiokp->multircv.value.ul	= statsp.multircv;
1588 		hiokp->multixmt.value.ul	= statsp.multixmt;
1589 		hiokp->brdcstrcv.value.ul	= statsp.brdcstrcv;
1590 		hiokp->brdcstxmt.value.ul	= statsp.brdcstxmt;
1591 		hiokp->norcvbuf.value.ul	= statsp.norcvbuf;
1592 		hiokp->noxmtbuf.value.ul	= statsp.noxmtbuf;
1593 	} else {
1594 		return (EACCES);
1595 	}
1596 
1597 	return (0);
1598 }
1599 
1600 static void
1601 vnet_hio_get_stats(vnet_res_t *vresp, vnet_hio_stats_t *statsp)
1602 {
1603 	mac_register_t		*macp;
1604 	mac_callbacks_t		*cbp;
1605 	uint64_t		val;
1606 	int			stat;
1607 
1608 	/*
1609 	 * get the specified statistics from the underlying nxge.
1610 	 */
1611 	macp = &vresp->macreg;
1612 	cbp = macp->m_callbacks;
1613 	for (stat = MAC_STAT_MIN; stat < MAC_STAT_OVERFLOWS; stat++) {
1614 		if (cbp->mc_getstat(macp->m_driver, stat, &val) == 0) {
1615 			switch (stat) {
1616 			case MAC_STAT_IPACKETS:
1617 				statsp->ipackets = val;
1618 				break;
1619 
1620 			case MAC_STAT_IERRORS:
1621 				statsp->ierrors = val;
1622 				break;
1623 
1624 			case MAC_STAT_OPACKETS:
1625 				statsp->opackets = val;
1626 				break;
1627 
1628 			case MAC_STAT_OERRORS:
1629 				statsp->oerrors = val;
1630 				break;
1631 
1632 			case MAC_STAT_RBYTES:
1633 				statsp->rbytes = val;
1634 				break;
1635 
1636 			case MAC_STAT_OBYTES:
1637 				statsp->obytes = val;
1638 				break;
1639 
1640 			case MAC_STAT_MULTIRCV:
1641 				statsp->multircv = val;
1642 				break;
1643 
1644 			case MAC_STAT_MULTIXMT:
1645 				statsp->multixmt = val;
1646 				break;
1647 
1648 			case MAC_STAT_BRDCSTRCV:
1649 				statsp->brdcstrcv = val;
1650 				break;
1651 
1652 			case MAC_STAT_BRDCSTXMT:
1653 				statsp->brdcstxmt = val;
1654 				break;
1655 
1656 			case MAC_STAT_NOXMTBUF:
1657 				statsp->noxmtbuf = val;
1658 				break;
1659 
1660 			case MAC_STAT_NORCVBUF:
1661 				statsp->norcvbuf = val;
1662 				break;
1663 
1664 			default:
1665 				/*
1666 				 * parameters not interested.
1667 				 */
1668 				break;
1669 			}
1670 		}
1671 	}
1672 }
1673 
1674 #ifdef	VNET_IOC_DEBUG
1675 
1676 /*
1677  * The ioctl entry point is used only for debugging for now. The ioctl commands
1678  * can be used to force the link state of the channel connected to vsw.
1679  */
1680 static void
1681 vnet_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
1682 {
1683 	struct iocblk	*iocp;
1684 	vnet_t		*vnetp;
1685 
1686 	iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
1687 	iocp->ioc_error = 0;
1688 	vnetp = (vnet_t *)arg;
1689 
1690 	if (vnetp == NULL) {
1691 		miocnak(q, mp, 0, EINVAL);
1692 		return;
1693 	}
1694 
1695 	switch (iocp->ioc_cmd) {
1696 
1697 	case VNET_FORCE_LINK_DOWN:
1698 	case VNET_FORCE_LINK_UP:
1699 		vnet_force_link_state(vnetp, q, mp);
1700 		break;
1701 
1702 	default:
1703 		iocp->ioc_error = EINVAL;
1704 		miocnak(q, mp, 0, iocp->ioc_error);
1705 		break;
1706 
1707 	}
1708 }
1709 
1710 static void
1711 vnet_force_link_state(vnet_t *vnetp, queue_t *q, mblk_t *mp)
1712 {
1713 	mac_register_t	*macp;
1714 	mac_callbacks_t	*cbp;
1715 	vnet_res_t	*vresp;
1716 
1717 	READ_ENTER(&vnetp->vsw_fp_rw);
1718 
1719 	vresp = vnetp->vsw_fp;
1720 	if (vresp == NULL) {
1721 		RW_EXIT(&vnetp->vsw_fp_rw);
1722 		return;
1723 	}
1724 
1725 	macp = &vresp->macreg;
1726 	cbp = macp->m_callbacks;
1727 	cbp->mc_ioctl(macp->m_driver, q, mp);
1728 
1729 	RW_EXIT(&vnetp->vsw_fp_rw);
1730 }
1731 
1732 #else
1733 
1734 static void
1735 vnet_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
1736 {
1737 	vnet_t		*vnetp;
1738 
1739 	vnetp = (vnet_t *)arg;
1740 
1741 	if (vnetp == NULL) {
1742 		miocnak(q, mp, 0, EINVAL);
1743 		return;
1744 	}
1745 
1746 	/* ioctl support only for debugging */
1747 	miocnak(q, mp, 0, ENOTSUP);
1748 }
1749 
1750 #endif
1751