xref: /illumos-gate/usr/src/uts/sun4v/io/vsw_phys.c (revision e850fb0196638a06a748477ed1b45c68bb830271)
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/debug.h>
30 #include <sys/time.h>
31 #include <sys/sysmacros.h>
32 #include <sys/systm.h>
33 #include <sys/user.h>
34 #include <sys/stropts.h>
35 #include <sys/stream.h>
36 #include <sys/strlog.h>
37 #include <sys/strsubr.h>
38 #include <sys/cmn_err.h>
39 #include <sys/cpu.h>
40 #include <sys/kmem.h>
41 #include <sys/conf.h>
42 #include <sys/ddi.h>
43 #include <sys/sunddi.h>
44 #include <sys/ksynch.h>
45 #include <sys/stat.h>
46 #include <sys/kstat.h>
47 #include <sys/vtrace.h>
48 #include <sys/strsun.h>
49 #include <sys/dlpi.h>
50 #include <sys/ethernet.h>
51 #include <net/if.h>
52 #include <netinet/arp.h>
53 #include <inet/arp.h>
54 #include <sys/varargs.h>
55 #include <sys/machsystm.h>
56 #include <sys/modctl.h>
57 #include <sys/modhash.h>
58 #include <sys/mac_client.h>
59 #include <sys/mac_provider.h>
60 #include <sys/mac_client_priv.h>
61 #include <sys/mac_ether.h>
62 #include <sys/taskq.h>
63 #include <sys/note.h>
64 #include <sys/mach_descrip.h>
65 #include <sys/mac.h>
66 #include <sys/mdeg.h>
67 #include <sys/vsw.h>
68 #include <sys/vlan.h>
69 
70 /* MAC Ring table functions. */
71 static void vsw_port_rx_cb(void *, mac_resource_handle_t, mblk_t *,
72     boolean_t);
73 static void vsw_if_rx_cb(void *, mac_resource_handle_t, mblk_t *, boolean_t);
74 
75 /* MAC layer routines */
76 static int vsw_set_port_hw_addr(vsw_port_t *port);
77 static int vsw_set_if_hw_addr(vsw_t *vswp);
78 static	void vsw_unset_hw_addr(vsw_t *, vsw_port_t *, int);
79 static int vsw_maccl_open(vsw_t *vswp, vsw_port_t *port, int type);
80 static void vsw_maccl_close(vsw_t *vswp, vsw_port_t *port, int type);
81 static void vsw_mac_multicast_add_all(vsw_t *vswp, vsw_port_t *portp, int type);
82 static void vsw_mac_multicast_remove_all(vsw_t *vswp,
83     vsw_port_t *portp, int type);
84 static void vsw_mac_add_vlans(vsw_t *vswp, mac_client_handle_t mch,
85     uint8_t *macaddr, uint16_t flags, vsw_vlanid_t *vids, int nvids);
86 static void vsw_mac_remove_vlans(mac_client_handle_t mch, vsw_vlanid_t *vids,
87     int nvids);
88 static	void vsw_mac_set_mtu(vsw_t *vswp, uint32_t mtu);
89 static int vsw_notify_add(vsw_t *vswp);
90 static int vsw_notify_rem(vsw_t *vswp);
91 static void vsw_notify_cb(void *arg, mac_notify_type_t type);
92 static void vsw_notify_link(vsw_t *vswp);
93 
94 /* Support functions */
95 int vsw_set_hw(vsw_t *, vsw_port_t *, int);
96 void vsw_unset_hw(vsw_t *, vsw_port_t *, int);
97 void vsw_reconfig_hw(vsw_t *);
98 int vsw_mac_open(vsw_t *vswp);
99 void vsw_mac_close(vsw_t *vswp);
100 int vsw_mac_multicast_add(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p,
101     int type);
102 void vsw_mac_multicast_remove(vsw_t *vswp, vsw_port_t *port,
103     mcst_addr_t *mcst_p, int type);
104 int vsw_mac_client_init(vsw_t *vswp, vsw_port_t *port, int type);
105 void vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type);
106 void vsw_mac_cleanup_ports(vsw_t *vswp);
107 void vsw_unset_addrs(vsw_t *vswp);
108 void vsw_set_addrs(vsw_t *vswp);
109 mblk_t *vsw_tx_msg(vsw_t *, mblk_t *, int, vsw_port_t *);
110 void vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp);
111 void vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans,
112     uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids);
113 void vsw_mac_port_reconfig_vlans(vsw_port_t *portp, uint16_t new_pvid,
114     vsw_vlanid_t *new_vids, int new_nvids);
115 void vsw_if_mac_reconfig(vsw_t *vswp, boolean_t update_vlans,
116     uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids);
117 
118 /*
119  * Functions imported from other files.
120  */
121 extern int vsw_portsend(vsw_port_t *port, mblk_t *mp);
122 extern void vsw_hio_stop_port(vsw_port_t *portp);
123 extern void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate);
124 extern uint32_t vsw_publish_macaddr_count;
125 extern uint32_t vsw_vlan_frame_untag(void *arg, int type, mblk_t **np,
126 	mblk_t **npt);
127 extern void vsw_physlink_state_update(vsw_t *vswp);
128 static char mac_mtu_propname[] = "mtu";
129 
130 /*
131  * Tunables used in this file.
132  */
133 extern int vsw_mac_open_retries;
134 
135 
136 #define	WRITE_MACCL_ENTER(vswp, port, type)	\
137 	(type == VSW_LOCALDEV) ?  rw_enter(&vswp->maccl_rwlock, RW_WRITER) :\
138 	rw_enter(&port->maccl_rwlock, RW_WRITER)
139 
140 #define	READ_MACCL_ENTER(vswp, port, type)	\
141 	(type == VSW_LOCALDEV) ?  rw_enter(&vswp->maccl_rwlock, RW_READER) :\
142 	rw_enter(&port->maccl_rwlock, RW_READER)
143 
144 #define	RW_MACCL_EXIT(vswp, port, type)	\
145 	(type == VSW_LOCALDEV) ?  rw_exit(&vswp->maccl_rwlock) :	\
146 	rw_exit(&port->maccl_rwlock)
147 
148 
149 /*
150  * Locking strategy in this file is explained as follows:
151  *	 - A global lock(vswp->mac_lock) is used to protect the
152  *	   MAC calls that deal with entire device. That is, the
153  *	   operations that deal with mac_handle which include
154  *	   mac_open()/close() and mac_client_open().
155  *
156  *	- A per port/interface RW lock(maccl_rwlock) is used protect
157  *	  the operations that deal with the MAC client.
158  *
159  *	When both mac_lock and maccl_rwlock need to be held, the
160  *	mac_lock need be acquired first and then maccl_rwlock. That is,
161  *		mac_lock---->maccl_rwlock
162  *
163  *	The 'mca_lock' that protects the mcast list is also acquired
164  *	within the context of maccl_rwlock. The hierarchy for this
165  *	one is as below:
166  *		maccl_rwlock---->mca_lock
167  */
168 
169 
170 /*
171  * Program unicast and multicast addresses of vsw interface and the ports
172  * into the network device.
173  */
174 void
175 vsw_set_addrs(vsw_t *vswp)
176 {
177 	vsw_port_list_t	*plist = &vswp->plist;
178 	vsw_port_t	*port;
179 	int		rv;
180 
181 	READ_ENTER(&vswp->if_lockrw);
182 
183 	if (vswp->if_state & VSW_IF_UP) {
184 
185 		/* Open a mac client and program addresses */
186 		rv = vsw_mac_client_init(vswp, NULL, VSW_LOCALDEV);
187 		if (rv != 0) {
188 			cmn_err(CE_NOTE,
189 			    "!vsw%d: failed to program interface "
190 			    "unicast address\n", vswp->instance);
191 		}
192 
193 		/*
194 		 * Notify the MAC layer of the changed address.
195 		 */
196 		if (rv == 0) {
197 			mac_unicst_update(vswp->if_mh,
198 			    (uint8_t *)&vswp->if_addr);
199 		}
200 
201 	}
202 
203 	RW_EXIT(&vswp->if_lockrw);
204 
205 	WRITE_ENTER(&plist->lockrw);
206 
207 	/* program unicast address of ports in the network device */
208 	for (port = plist->head; port != NULL; port = port->p_next) {
209 		if (port->addr_set) /* addr already set */
210 			continue;
211 
212 		/* Open a mac client and program addresses */
213 		rv = vsw_mac_client_init(vswp, port, VSW_VNETPORT);
214 		if (rv != 0) {
215 			cmn_err(CE_NOTE,
216 			    "!vsw%d: failed to program port(%d) "
217 			    "unicast address\n", vswp->instance,
218 			    port->p_instance);
219 		}
220 	}
221 	/* announce macaddr of vnets to the physical switch */
222 	if (vsw_publish_macaddr_count != 0) {	/* enabled */
223 		for (port = plist->head; port != NULL; port = port->p_next) {
224 			vsw_publish_macaddr(vswp, port);
225 		}
226 	}
227 
228 	RW_EXIT(&plist->lockrw);
229 }
230 
231 /*
232  * Remove unicast, multicast addresses and close mac clients
233  * for the vsw interface and all ports.
234  */
235 void
236 vsw_unset_addrs(vsw_t *vswp)
237 {
238 	READ_ENTER(&vswp->if_lockrw);
239 	if (vswp->if_state & VSW_IF_UP) {
240 
241 		/* Cleanup and close the mac client for the interface */
242 		vsw_mac_client_cleanup(vswp, NULL, VSW_LOCALDEV);
243 	}
244 	RW_EXIT(&vswp->if_lockrw);
245 
246 	/* Cleanup and close the mac clients for all ports */
247 	vsw_mac_cleanup_ports(vswp);
248 }
249 
250 /*
251  * Open the underlying network device for access in layer2 mode.
252  * Returns:
253  *	0 on success
254  *	EAGAIN if mac_open() fails due to the device being not available yet.
255  *	EIO on any other failures.
256  */
257 int
258 vsw_mac_open(vsw_t *vswp)
259 {
260 	int			rv;
261 
262 	ASSERT(MUTEX_HELD(&vswp->mac_lock));
263 
264 	if (vswp->mh != NULL) {
265 		/* already open */
266 		return (0);
267 	}
268 
269 	if (vswp->mac_open_retries++ >= vsw_mac_open_retries) {
270 		/* exceeded max retries */
271 		return (EIO);
272 	}
273 
274 	if ((rv = mac_open_by_linkname(vswp->physname, &vswp->mh)) != 0) {
275 		/*
276 		 * If mac_open() failed and the error indicates that either
277 		 * the dlmgmtd door or the device is not available yet, we
278 		 * return EAGAIN to indicate that mac_open() needs to be
279 		 * retried. For example, this may happen during boot up, if
280 		 * the required link aggregation groups(devices) have not
281 		 * been created yet.
282 		 */
283 		if (rv == ENOENT || rv == EBADF) {
284 			return (EAGAIN);
285 		} else {
286 			cmn_err(CE_WARN, "!vsw%d: mac_open %s failed rv:%x\n",
287 			    vswp->instance, vswp->physname, rv);
288 			return (EIO);
289 		}
290 	}
291 	vswp->mac_open_retries = 0;
292 
293 	vsw_mac_set_mtu(vswp, vswp->mtu);
294 
295 	rv = vsw_notify_add(vswp);
296 	if (rv != 0) {
297 		cmn_err(CE_CONT, "!vsw%d: mac_notify_add %s failed rv:%x\n",
298 		    vswp->instance, vswp->physname, rv);
299 	}
300 
301 	return (0);
302 }
303 
304 /*
305  * Close the underlying physical device.
306  */
307 void
308 vsw_mac_close(vsw_t *vswp)
309 {
310 	ASSERT(MUTEX_HELD(&vswp->mac_lock));
311 
312 	if (vswp->mh != NULL) {
313 		if (vswp->mnh != 0) {
314 			(void) vsw_notify_rem(vswp);
315 			vswp->mnh = 0;
316 		}
317 		if (vswp->mtu != vswp->mtu_physdev_orig) {
318 			vsw_mac_set_mtu(vswp, vswp->mtu_physdev_orig);
319 		}
320 		mac_close(vswp->mh);
321 		vswp->mh = NULL;
322 	}
323 }
324 
325 /*
326  * Add multicast addr.
327  */
328 int
329 vsw_mac_multicast_add(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p,
330     int type)
331 {
332 	int			ret = 0;
333 	mac_client_handle_t	mch;
334 
335 	WRITE_MACCL_ENTER(vswp, port, type);
336 
337 	mch = (type == VSW_LOCALDEV) ? vswp->mch : port->p_mch;
338 
339 	if (mch != NULL) {
340 		ret = mac_multicast_add(mch, mcst_p->mca.ether_addr_octet);
341 		if (ret != 0) {
342 			cmn_err(CE_WARN, "!vsw%d: unable to "
343 			    "program multicast address(%s) err=%d",
344 			    vswp->instance,
345 			    ether_sprintf((void *)&mcst_p->mca), ret);
346 			RW_MACCL_EXIT(vswp, port, type);
347 			return (ret);
348 		}
349 		mcst_p->mac_added = B_TRUE;
350 	}
351 
352 	RW_MACCL_EXIT(vswp, port, type);
353 	return (ret);
354 }
355 
356 /*
357  * Remove multicast addr.
358  */
359 void
360 vsw_mac_multicast_remove(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p,
361     int type)
362 {
363 	mac_client_handle_t	mch;
364 
365 	WRITE_MACCL_ENTER(vswp, port, type);
366 	mch = (type == VSW_LOCALDEV) ? vswp->mch : port->p_mch;
367 
368 	if (mch != NULL && mcst_p->mac_added) {
369 		mac_multicast_remove(mch, mcst_p->mca.ether_addr_octet);
370 		mcst_p->mac_added = B_FALSE;
371 	}
372 	RW_MACCL_EXIT(vswp, port, type);
373 }
374 
375 
376 /*
377  * Add all multicast addresses of the port.
378  */
379 static void
380 vsw_mac_multicast_add_all(vsw_t *vswp, vsw_port_t *portp, int type)
381 {
382 	mcst_addr_t		*mcap;
383 	mac_client_handle_t	mch;
384 	kmutex_t		*mca_lockp;
385 	int			rv;
386 
387 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
388 	if (type == VSW_LOCALDEV) {
389 		ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
390 		mch = vswp->mch;
391 		mcap = vswp->mcap;
392 		mca_lockp = &vswp->mca_lock;
393 	} else {
394 		ASSERT(RW_WRITE_HELD(&portp->maccl_rwlock));
395 		mch = portp->p_mch;
396 		mcap = portp->mcap;
397 		mca_lockp = &portp->mca_lock;
398 	}
399 
400 	if (mch == NULL)
401 		return;
402 
403 	mutex_enter(mca_lockp);
404 	for (mcap = mcap; mcap != NULL; mcap = mcap->nextp) {
405 		if (mcap->mac_added)
406 			continue;
407 		rv = mac_multicast_add(mch, (uchar_t *)&mcap->mca);
408 		if (rv == 0) {
409 			mcap->mac_added = B_TRUE;
410 		} else {
411 			cmn_err(CE_WARN, "!vsw%d: unable to program "
412 			    "multicast address(%s) err=%d", vswp->instance,
413 			    ether_sprintf((void *)&mcap->mca), rv);
414 		}
415 	}
416 	mutex_exit(mca_lockp);
417 }
418 
419 /*
420  * Remove all multicast addresses of the port.
421  */
422 static void
423 vsw_mac_multicast_remove_all(vsw_t *vswp, vsw_port_t *portp, int type)
424 {
425 	mac_client_handle_t	mch;
426 	mcst_addr_t		*mcap;
427 	kmutex_t		*mca_lockp;
428 
429 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
430 	if (type == VSW_LOCALDEV) {
431 		ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
432 		mch = vswp->mch;
433 		mcap = vswp->mcap;
434 		mca_lockp = &vswp->mca_lock;
435 	} else {
436 		ASSERT(RW_WRITE_HELD(&portp->maccl_rwlock));
437 		mch = portp->p_mch;
438 		mcap = portp->mcap;
439 		mca_lockp = &portp->mca_lock;
440 	}
441 
442 	if (mch == NULL)
443 		return;
444 
445 	mutex_enter(mca_lockp);
446 	for (; mcap != NULL; mcap = mcap->nextp) {
447 		if (!mcap->mac_added)
448 			continue;
449 		(void) mac_multicast_remove(mch, (uchar_t *)&mcap->mca);
450 		mcap->mac_added = B_FALSE;
451 	}
452 	mutex_exit(mca_lockp);
453 }
454 
455 /*
456  * Open a mac client and program uncast and multicast addresses
457  * for a port or the interface.
458  * Returns:
459  *	0 on success
460  *	non-zero for failure.
461  */
462 int
463 vsw_mac_client_init(vsw_t *vswp, vsw_port_t *port, int type)
464 {
465 	int rv;
466 
467 	mutex_enter(&vswp->mac_lock);
468 	WRITE_MACCL_ENTER(vswp, port, type);
469 	rv = vsw_maccl_open(vswp, port, type);
470 
471 	/* Release mac_lock now */
472 	mutex_exit(&vswp->mac_lock);
473 
474 	if (rv == 0) {
475 		(void) vsw_set_hw(vswp, port, type);
476 		vsw_mac_multicast_add_all(vswp, port, type);
477 	}
478 	RW_MACCL_EXIT(vswp, port, type);
479 	return (rv);
480 }
481 
482 /*
483  * Open a MAC client for a port or an interface.
484  * The flags and their purpose as below:
485  *
486  *	MAC_OPEN_FLAGS_NO_HWRINGS -- This flag is used by default
487  *	for all ports/interface so that they are associated with
488  *	default group & resources. It will not be used for the
489  *	ports that have HybridIO is enabled so that the h/w resources
490  *	assigned to it.
491  *
492  *	MAC_OPEN_FLAGS_SHARES_DESIRED -- This flag is used to indicate
493  *	that a port desires a Share. This will be the case with the
494  *	the ports that have hybrid mode enabled. This will only cause
495  *	MAC layer to allocate a share and corresponding resources
496  *	ahead of time.
497  *
498  *	MAC_UNICAST_TAG_DISABLE -- This flag is used for VLAN
499  *	support. It will cause MAC to not add any tags, but expect
500  *	vsw to tag the packets.
501  *
502  *	MAC_UNICAST_STRIP_DISABLE -- This flag is used for VLAN
503  *	support. It will case the MAC layer to not strip the tags.
504  *	Vsw may have to strip the tag for pvid case.
505  */
506 static int
507 vsw_maccl_open(vsw_t *vswp, vsw_port_t *port, int type)
508 {
509 	int		rv = 0;
510 	int		instance;
511 	char		mac_cl_name[MAXNAMELEN];
512 	const char	*dev_name;
513 	mac_client_handle_t *mchp;
514 	uint64_t flags = MAC_OPEN_FLAGS_NO_HWRINGS;
515 
516 	ASSERT(MUTEX_HELD(&vswp->mac_lock));
517 	if (vswp->mh == NULL) {
518 		/*
519 		 * In case net-dev is changed (either set to nothing or
520 		 * using aggregation device), return success here as the
521 		 * timeout mechanism will handle it.
522 		 */
523 		return (0);
524 	}
525 
526 	mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch;
527 	if (*mchp != NULL) {
528 		/* already open */
529 		return (0);
530 	}
531 	dev_name = ddi_driver_name(vswp->dip);
532 	instance = ddi_get_instance(vswp->dip);
533 	if (type == VSW_VNETPORT) {
534 		if (port->p_hio_enabled == B_TRUE) {
535 			flags &= ~MAC_OPEN_FLAGS_NO_HWRINGS;
536 			flags |= MAC_OPEN_FLAGS_SHARES_DESIRED;
537 		}
538 		(void) snprintf(mac_cl_name, MAXNAMELEN, "%s%d%s%d", dev_name,
539 		    instance, "_port", port->p_instance);
540 	} else {
541 		(void) snprintf(mac_cl_name, MAXNAMELEN, "%s%s%d",
542 		    dev_name, "_if", instance);
543 	}
544 
545 	rv = mac_client_open(vswp->mh, mchp, mac_cl_name, flags);
546 	if (rv != 0) {
547 		cmn_err(CE_NOTE, "!vsw%d:%s mac_client_open() failed\n",
548 		    vswp->instance, mac_cl_name);
549 	}
550 	return (rv);
551 }
552 
553 /*
554  * Clean up by removing uncast, multicast addresses and
555  * closing the MAC client for a port or the interface.
556  */
557 void
558 vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type)
559 {
560 	WRITE_MACCL_ENTER(vswp, port, type);
561 	vsw_unset_hw(vswp, port, type);
562 	vsw_maccl_close(vswp, port, type);
563 	vsw_mac_multicast_remove_all(vswp, port, type);
564 	RW_MACCL_EXIT(vswp, port, type);
565 }
566 
567 /*
568  * Close a MAC client for a port or an interface.
569  */
570 static void
571 vsw_maccl_close(vsw_t *vswp, vsw_port_t *port, int type)
572 {
573 	mac_client_handle_t *mchp;
574 
575 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
576 
577 	mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch;
578 	if (*mchp != NULL) {
579 		mac_client_close(*mchp, 0);
580 		*mchp = NULL;
581 	}
582 }
583 
584 /*
585  * Cleanup MAC client related stuff for all ports.
586  */
587 void
588 vsw_mac_cleanup_ports(vsw_t *vswp)
589 {
590 	vsw_port_list_t		*plist = &vswp->plist;
591 	vsw_port_t		*port;
592 
593 	READ_ENTER(&plist->lockrw);
594 	for (port = plist->head; port != NULL; port = port->p_next) {
595 		vsw_mac_client_cleanup(vswp, port, VSW_VNETPORT);
596 	}
597 	RW_EXIT(&plist->lockrw);
598 }
599 
600 /*
601  * Depending on the mode specified, the capabilites and capacity
602  * of the underlying device setup the physical device.
603  *
604  * If in layer 3 mode, then do nothing.
605  *
606  * If in layer 2 mode, open a mac client and program the mac-address
607  * and vlan-ids. The MAC layer will take care of programming
608  * the address into h/w or set the h/w into promiscuous mode.
609  *
610  * Returns 0 success, 1 on failure.
611  */
612 int
613 vsw_set_hw(vsw_t *vswp, vsw_port_t *port, int type)
614 {
615 	int			err = 1;
616 
617 	D1(vswp, "%s: enter", __func__);
618 
619 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
620 
621 	if (vswp->smode == VSW_LAYER3)
622 		return (0);
623 
624 	if (type == VSW_VNETPORT) {
625 		ASSERT(port != NULL);
626 		err = vsw_set_port_hw_addr(port);
627 	} else {
628 		err = vsw_set_if_hw_addr(vswp);
629 	}
630 
631 	D1(vswp, "%s: exit", __func__);
632 	return (err);
633 }
634 
635 /*
636  * If in layer 3 mode do nothing.
637  *
638  * If in layer 2 switched mode remove the address from the physical
639  * device.
640  *
641  * If in layer 2 promiscuous mode disable promisc mode.
642  *
643  * Returns 0 on success.
644  */
645 void
646 vsw_unset_hw(vsw_t *vswp, vsw_port_t *port, int type)
647 {
648 	D1(vswp, "%s: enter", __func__);
649 
650 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
651 
652 	if (vswp->smode == VSW_LAYER3)
653 		return;
654 
655 	if (type == VSW_VNETPORT) {
656 		ASSERT(port != NULL);
657 		vsw_unset_hw_addr(vswp, port, type);
658 	} else {
659 		vsw_unset_hw_addr(vswp, NULL, type);
660 	}
661 
662 	D1(vswp, "%s: exit", __func__);
663 }
664 
665 /*
666  * Program the macaddress and vlans of a port.
667  *
668  * Returns 0 on sucess, 1 on failure.
669  */
670 static int
671 vsw_set_port_hw_addr(vsw_port_t *port)
672 {
673 	vsw_t			*vswp = port->p_vswp;
674 	mac_diag_t		diag;
675 	uint8_t			*macaddr;
676 	uint16_t		vid = VLAN_ID_NONE;
677 	int			rv;
678 	uint16_t		mac_flags = MAC_UNICAST_TAG_DISABLE |
679 	    MAC_UNICAST_STRIP_DISABLE;
680 
681 	D1(vswp, "%s: enter", __func__);
682 
683 	ASSERT(RW_WRITE_HELD(&port->maccl_rwlock));
684 	if (port->p_mch == NULL)
685 		return (0);
686 
687 	/*
688 	 * If the port has a specific 'pvid', then
689 	 * register with that vlan-id, otherwise register
690 	 * with VLAN_ID_NONE.
691 	 */
692 	if (port->pvid != vswp->default_vlan_id) {
693 		vid = port->pvid;
694 	}
695 	macaddr = (uint8_t *)port->p_macaddr.ether_addr_octet;
696 
697 	if (!(vswp->smode & VSW_LAYER2_PROMISC)) {
698 		mac_flags |= MAC_UNICAST_HW;
699 	}
700 
701 	if (port->addr_set == B_FALSE) {
702 		port->p_muh = NULL;
703 		rv = mac_unicast_add(port->p_mch, macaddr, mac_flags,
704 		    &port->p_muh, vid, &diag);
705 
706 		if (rv != 0) {
707 			cmn_err(CE_WARN, "vsw%d: Failed to program"
708 			    "macaddr,vid(%s, %d) err=%d",
709 			    vswp->instance, ether_sprintf((void *)macaddr),
710 			    vid, rv);
711 			return (rv);
712 		}
713 		port->addr_set = B_TRUE;
714 
715 		D2(vswp, "%s:programmed macaddr(%s) vid(%d) into device %s",
716 		    __func__, ether_sprintf((void *)macaddr), vid,
717 		    vswp->physname);
718 	}
719 
720 	/* Add vlans to the MAC layer */
721 	vsw_mac_add_vlans(vswp, port->p_mch, macaddr,
722 	    mac_flags, port->vids, port->nvids);
723 
724 	mac_rx_set(port->p_mch, vsw_port_rx_cb, (void *)port);
725 
726 	D1(vswp, "%s: exit", __func__);
727 	return (rv);
728 }
729 
730 /*
731  * Program the macaddress and vlans of a port.
732  *
733  * Returns 0 on sucess, 1 on failure.
734  */
735 static int
736 vsw_set_if_hw_addr(vsw_t *vswp)
737 {
738 	mac_diag_t		diag;
739 	uint8_t			*macaddr;
740 	uint8_t			primary_addr[ETHERADDRL];
741 	uint16_t		vid = VLAN_ID_NONE;
742 	int			rv;
743 	uint16_t		mac_flags = MAC_UNICAST_TAG_DISABLE |
744 	    MAC_UNICAST_STRIP_DISABLE;
745 
746 	D1(vswp, "%s: enter", __func__);
747 
748 	ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
749 	if (vswp->mch == NULL)
750 		return (0);
751 
752 	macaddr = (uint8_t *)vswp->if_addr.ether_addr_octet;
753 
754 	/* check if it is the primary macaddr of the card. */
755 	mac_unicast_primary_get(vswp->mh, primary_addr);
756 	if (ether_cmp((void *)primary_addr, (void*)macaddr) == 0) {
757 		mac_flags |= MAC_UNICAST_PRIMARY;
758 	}
759 
760 	/*
761 	 * If the interface has a specific 'pvid', then
762 	 * register with that vlan-id, otherwise register
763 	 * with VLAN_ID_NONE.
764 	 */
765 	if (vswp->pvid != vswp->default_vlan_id) {
766 		vid = vswp->pvid;
767 	}
768 
769 	if (!(vswp->smode & VSW_LAYER2_PROMISC)) {
770 		mac_flags |= MAC_UNICAST_HW;
771 	}
772 
773 	if (vswp->addr_set == B_FALSE) {
774 		vswp->muh = NULL;
775 		rv = mac_unicast_add(vswp->mch, macaddr, mac_flags,
776 		    &vswp->muh, vid, &diag);
777 
778 		if (rv != 0) {
779 			cmn_err(CE_WARN, "vsw%d: Failed to program"
780 			    "macaddr,vid(%s, %d) err=%d",
781 			    vswp->instance, ether_sprintf((void *)macaddr),
782 			    vid, rv);
783 			return (rv);
784 		}
785 		vswp->addr_set = B_TRUE;
786 
787 		D2(vswp, "%s:programmed macaddr(%s) vid(%d) into device %s",
788 		    __func__, ether_sprintf((void *)macaddr), vid,
789 		    vswp->physname);
790 	}
791 
792 	vsw_mac_add_vlans(vswp, vswp->mch, macaddr, mac_flags,
793 	    vswp->vids, vswp->nvids);
794 
795 	mac_rx_set(vswp->mch, vsw_if_rx_cb, (void *)vswp);
796 
797 	D1(vswp, "%s: exit", __func__);
798 	return (rv);
799 }
800 
801 /*
802  * Remove a unicast mac address which has previously been programmed
803  * into HW.
804  *
805  * Returns 0 on sucess, 1 on failure.
806  */
807 static void
808 vsw_unset_hw_addr(vsw_t *vswp, vsw_port_t *port, int type)
809 {
810 	vsw_vlanid_t		*vids;
811 	int			nvids;
812 	mac_client_handle_t	mch = NULL;
813 
814 	D1(vswp, "%s: enter", __func__);
815 
816 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
817 
818 	if (type == VSW_VNETPORT) {
819 		ASSERT(port != NULL);
820 		ASSERT(RW_WRITE_HELD(&port->maccl_rwlock));
821 		vids = port->vids;
822 		nvids = port->nvids;
823 	} else {
824 		ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
825 		vids = vswp->vids;
826 		nvids = vswp->nvids;
827 	}
828 
829 	/* First clear the callback */
830 	if (type == VSW_LOCALDEV) {
831 		mch = vswp->mch;
832 	} else if (type == VSW_VNETPORT) {
833 		mch = port->p_mch;
834 	}
835 
836 
837 	if (mch == NULL) {
838 		return;
839 	}
840 
841 	mac_rx_clear(mch);
842 
843 	/* Remove vlans */
844 	vsw_mac_remove_vlans(mch, vids, nvids);
845 
846 	if ((type == VSW_LOCALDEV) && (vswp->addr_set == B_TRUE)) {
847 		(void) mac_unicast_remove(vswp->mch, vswp->muh);
848 		vswp->muh = NULL;
849 		D2(vswp, "removed vsw interface mac-addr from "
850 		    "the device %s", vswp->physname);
851 		vswp->addr_set = B_FALSE;
852 
853 	} else if ((type == VSW_VNETPORT) && (port->addr_set == B_TRUE)) {
854 		(void) mac_unicast_remove(port->p_mch, port->p_muh);
855 		port->p_muh = NULL;
856 		D2(vswp, "removed port(0x%p) mac-addr from "
857 		    "the device %s", port, vswp->physname);
858 		port->addr_set = B_FALSE;
859 	}
860 
861 	D1(vswp, "%s: exit", __func__);
862 }
863 
864 /*
865  * receive callback routine for vsw interface. Invoked by MAC layer when there
866  * are pkts being passed up from physical device for this vsw interface.
867  */
868 /* ARGSUSED */
869 static void
870 vsw_if_rx_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
871     boolean_t loopback)
872 {
873 	_NOTE(ARGUNUSED(mrh))
874 
875 	vsw_t		*vswp = (vsw_t *)arg;
876 	mblk_t		*mpt;
877 	int		count;
878 
879 	ASSERT(vswp != NULL);
880 
881 	D1(vswp, "%s: enter", __func__);
882 
883 	READ_ENTER(&vswp->if_lockrw);
884 	if (vswp->if_state & VSW_IF_UP) {
885 		RW_EXIT(&vswp->if_lockrw);
886 		count = vsw_vlan_frame_untag(vswp, VSW_LOCALDEV, &mp, &mpt);
887 		if (count != 0) {
888 			mac_rx(vswp->if_mh, NULL, mp);
889 		}
890 	} else {
891 		RW_EXIT(&vswp->if_lockrw);
892 		freemsgchain(mp);
893 	}
894 
895 	D1(vswp, "%s: exit", __func__);
896 }
897 
898 /*
899  * receive callback routine for port. Invoked by MAC layer when there
900  * are pkts being passed up from physical device for this port.
901  */
902 /* ARGSUSED */
903 static void
904 vsw_port_rx_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
905     boolean_t loopback)
906 {
907 	_NOTE(ARGUNUSED(mrh))
908 
909 	vsw_t		*vswp;
910 	vsw_port_t	*port = arg;
911 
912 	ASSERT(port != NULL);
913 
914 	vswp = port->p_vswp;
915 
916 	D1(vswp, "vsw_port_rx_cb: enter");
917 
918 	/*
919 	 * Send the packets to the peer directly.
920 	 */
921 	(void) vsw_portsend(port, mp);
922 
923 	D1(vswp, "vsw_port_rx_cb: exit");
924 }
925 
926 /*
927  * Send a message out over the physical device
928  * via the MAC layer.
929  *
930  * Returns any mblks that it was unable to transmit.
931  */
932 mblk_t *
933 vsw_tx_msg(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *port)
934 {
935 	mac_client_handle_t	mch;
936 	mac_unicast_handle_t	muh;
937 
938 	READ_MACCL_ENTER(vswp, port, caller);
939 
940 	mch = (caller == VSW_LOCALDEV) ? vswp->mch : port->p_mch;
941 	muh = (caller == VSW_LOCALDEV) ? vswp->muh : port->p_muh;
942 
943 	if (mch == NULL || muh == NULL) {
944 		RW_MACCL_EXIT(vswp, port, caller);
945 		return (mp);
946 	}
947 
948 	/* packets are sent or dropped */
949 	(void) mac_tx(mch, mp, 0, MAC_DROP_ON_NO_DESC, NULL);
950 	RW_MACCL_EXIT(vswp, port, caller);
951 	return (NULL);
952 }
953 
954 /*
955  * vsw_port_mac_reconfig -- Cleanup and close the MAC client
956  * and reopen and re-configure the MAC client with new flags etc.
957  * This function is useful for two different purposes:
958  *	1) To update the MAC client with new vlan-ids. This is done
959  *	   by freeing the existing vlan-ids and reopen with the new
960  *	   vlan-ids.
961  *
962  *	2) If the Hybrid mode status of a port changes, then the
963  *	   MAC client need to be closed and re-opened, otherwise,
964  *	   Share related resources may not be freed(hybird mode disabled)
965  *	   or assigned(hybrid mode enabled). To accomplish this,
966  *	   this function simply closes and reopens the MAC client.
967  *	   The reopen will result in using the flags based on the
968  *	   new hybrid mode of the port.
969  */
970 void
971 vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans,
972     uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids)
973 {
974 	vsw_t *vswp = portp->p_vswp;
975 	int rv;
976 
977 	D1(vswp, "%s: enter", __func__);
978 	/*
979 	 * Remove the multi-cast addresses, unicast address
980 	 * and close the mac-client.
981 	 */
982 	mutex_enter(&vswp->mac_lock);
983 	WRITE_ENTER(&portp->maccl_rwlock);
984 	vsw_mac_multicast_remove_all(vswp, portp, VSW_VNETPORT);
985 	vsw_unset_hw(vswp, portp, VSW_VNETPORT);
986 	vsw_maccl_close(vswp, portp, VSW_VNETPORT);
987 
988 	if (update_vlans == B_TRUE) {
989 		if (portp->nvids != 0) {
990 			kmem_free(portp->vids,
991 			    sizeof (vsw_vlanid_t) * portp->nvids);
992 			portp->vids = NULL;
993 			portp->nvids = 0;
994 		}
995 		portp->vids = new_vids;
996 		portp->nvids = new_nvids;
997 		portp->pvid = new_pvid;
998 	}
999 
1000 	/*
1001 	 * Now re-open the mac-client and
1002 	 * configure unicast addr and multicast addrs.
1003 	 */
1004 	rv = vsw_maccl_open(vswp, portp, VSW_VNETPORT);
1005 	if (rv != 0) {
1006 		goto recret;
1007 	}
1008 
1009 	if (vsw_set_hw(vswp, portp, VSW_VNETPORT)) {
1010 		cmn_err(CE_NOTE, "!vsw%d: port:%d failed to "
1011 		    "set unicast address\n", vswp->instance, portp->p_instance);
1012 		goto recret;
1013 	}
1014 
1015 	vsw_mac_multicast_add_all(vswp, portp, VSW_VNETPORT);
1016 
1017 recret:
1018 	RW_EXIT(&portp->maccl_rwlock);
1019 	mutex_exit(&vswp->mac_lock);
1020 	D1(vswp, "%s: exit", __func__);
1021 }
1022 
1023 /*
1024  * vsw_if_mac_reconfig -- Reconfigure the vsw interfaace's mac-client
1025  * by closing and re-opening it. This function is used handle the
1026  * following two cases:
1027  *
1028  *	1) Handle the MAC address change for the interface.
1029  *	2) Handle vlan update.
1030  */
1031 void
1032 vsw_if_mac_reconfig(vsw_t *vswp, boolean_t update_vlans,
1033     uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids)
1034 {
1035 	int rv;
1036 
1037 	D1(vswp, "%s: enter", __func__);
1038 	/*
1039 	 * Remove the multi-cast addresses, unicast address
1040 	 * and close the mac-client.
1041 	 */
1042 	mutex_enter(&vswp->mac_lock);
1043 	WRITE_ENTER(&vswp->maccl_rwlock);
1044 	vsw_mac_multicast_remove_all(vswp, NULL, VSW_LOCALDEV);
1045 	vsw_unset_hw(vswp, NULL, VSW_LOCALDEV);
1046 	vsw_maccl_close(vswp, NULL, VSW_LOCALDEV);
1047 
1048 	if (update_vlans == B_TRUE) {
1049 		if (vswp->nvids != 0) {
1050 			kmem_free(vswp->vids,
1051 			    sizeof (vsw_vlanid_t) * vswp->nvids);
1052 			vswp->vids = NULL;
1053 			vswp->nvids = 0;
1054 		}
1055 		vswp->vids = new_vids;
1056 		vswp->nvids = new_nvids;
1057 		vswp->pvid = new_pvid;
1058 	}
1059 
1060 	/*
1061 	 * Now re-open the mac-client and
1062 	 * configure unicast addr and multicast addrs.
1063 	 */
1064 	rv = vsw_maccl_open(vswp, NULL, VSW_LOCALDEV);
1065 	if (rv != 0) {
1066 		goto ifrecret;
1067 	}
1068 
1069 	if (vsw_set_hw(vswp, NULL, VSW_LOCALDEV)) {
1070 		cmn_err(CE_NOTE, "!vsw%d:failed to set unicast address\n",
1071 		    vswp->instance);
1072 		goto ifrecret;
1073 	}
1074 
1075 	vsw_mac_multicast_add_all(vswp, NULL, VSW_LOCALDEV);
1076 
1077 ifrecret:
1078 	RW_EXIT(&vswp->maccl_rwlock);
1079 	mutex_exit(&vswp->mac_lock);
1080 	D1(vswp, "%s: exit", __func__);
1081 }
1082 
1083 /*
1084  * vsw_mac_port_reconfig_vlans -- Reconfigure a port to handle
1085  * vlan configuration update. As the removal of the last unicast-address,vid
1086  * from the MAC client results in releasing all resources, it expects
1087  * no Shares to be associated with such MAC client.
1088  *
1089  * To handle vlan configuration update for a port that already has
1090  * a Share bound, then we need to free that share prior to reconfiguration.
1091  * Initiate the hybrdIO setup again after the completion of reconfiguration.
1092  */
1093 void
1094 vsw_mac_port_reconfig_vlans(vsw_port_t *portp, uint16_t new_pvid,
1095     vsw_vlanid_t *new_vids, int new_nvids)
1096 {
1097 	/*
1098 	 * As the reconfiguration involves the close of
1099 	 * mac client, cleanup HybridIO and later restart
1100 	 * HybridIO setup again.
1101 	 */
1102 	if (portp->p_hio_enabled == B_TRUE) {
1103 		vsw_hio_stop_port(portp);
1104 	}
1105 	vsw_port_mac_reconfig(portp, B_TRUE, new_pvid, new_vids, new_nvids);
1106 	if (portp->p_hio_enabled == B_TRUE) {
1107 		/* reset to setup the HybridIO again. */
1108 		vsw_hio_port_reset(portp, B_FALSE);
1109 	}
1110 }
1111 
1112 /* Add vlans to MAC client */
1113 static void
1114 vsw_mac_add_vlans(vsw_t *vswp, mac_client_handle_t mch, uint8_t *macaddr,
1115     uint16_t flags, vsw_vlanid_t *vids, int nvids)
1116 {
1117 	vsw_vlanid_t	*vidp;
1118 	mac_diag_t	diag;
1119 	int		rv;
1120 	int		i;
1121 
1122 	flags |= MAC_UNICAST_TAG_DISABLE | MAC_UNICAST_STRIP_DISABLE;
1123 
1124 	/* Add vlans to the MAC layer */
1125 	for (i = 0; i < nvids; i++) {
1126 		vidp = &vids[i];
1127 
1128 		if (vidp->vl_set == B_TRUE) {
1129 			continue;
1130 		}
1131 
1132 		rv = mac_unicast_add(mch, macaddr, flags,
1133 		    &vidp->vl_muh, vidp->vl_vid, &diag);
1134 		if (rv != 0) {
1135 			cmn_err(CE_WARN, "vsw%d: Failed to program"
1136 			    "macaddr,vid(%s, %d) err=%d",
1137 			    vswp->instance, ether_sprintf((void *)macaddr),
1138 			    vidp->vl_vid, rv);
1139 		} else {
1140 			vidp->vl_set = B_TRUE;
1141 			D2(vswp, "%s:programmed macaddr(%s) vid(%d) "
1142 			    "into device %s", __func__,
1143 			    ether_sprintf((void *)macaddr),
1144 			    vidp->vl_vid, vswp->physname);
1145 		}
1146 	}
1147 }
1148 
1149 /* Remove vlans from the MAC client */
1150 static void
1151 vsw_mac_remove_vlans(mac_client_handle_t mch, vsw_vlanid_t *vids, int nvids)
1152 {
1153 	int i;
1154 	vsw_vlanid_t *vidp;
1155 
1156 	for (i = 0; i < nvids; i++) {
1157 		vidp = &vids[i];
1158 		if (vidp->vl_set == B_FALSE) {
1159 			continue;
1160 		}
1161 		mac_unicast_remove(mch, vidp->vl_muh);
1162 		vidp->vl_set = B_FALSE;
1163 	}
1164 }
1165 
1166 #define	ARH_FIXED_LEN	8    /* Length of fixed part of ARP header(see arp.h) */
1167 
1168 /*
1169  * Send a gratuitous RARP packet to notify the physical switch to update its
1170  * Layer2 forwarding table for the given mac address. This is done to allow the
1171  * switch to quickly learn the macaddr-port association when a guest is live
1172  * migrated or when vsw's physical device is changed dynamically. Any protocol
1173  * packet would serve this purpose, but we choose RARP, as it allows us to
1174  * accomplish this within L2 (ie, no need to specify IP addr etc in the packet)
1175  * The macaddr of vnet is retained across migration. Hence, we don't need to
1176  * update the arp cache of other hosts within the broadcast domain. Note that
1177  * it is harmless to send these RARP packets during normal port attach of a
1178  * client vnet. This can can be turned off if needed, by setting
1179  * vsw_publish_macaddr_count to zero in /etc/system.
1180  */
1181 void
1182 vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp)
1183 {
1184 	mblk_t			*mp;
1185 	mblk_t			*bp;
1186 	struct arphdr		*arh;
1187 	struct	ether_header 	*ehp;
1188 	int			count = 0;
1189 	int			plen = 4;
1190 	uint8_t			*cp;
1191 
1192 	mp = allocb(ETHERMIN, BPRI_MED);
1193 	if (mp == NULL) {
1194 		return;
1195 	}
1196 
1197 	/* Initialize eth header */
1198 	ehp = (struct  ether_header *)mp->b_rptr;
1199 	bcopy(&etherbroadcastaddr, &ehp->ether_dhost, ETHERADDRL);
1200 	bcopy(&portp->p_macaddr, &ehp->ether_shost, ETHERADDRL);
1201 	ehp->ether_type = htons(ETHERTYPE_REVARP);
1202 
1203 	/* Initialize arp packet */
1204 	arh = (struct arphdr *)(mp->b_rptr + sizeof (struct ether_header));
1205 	cp = (uint8_t *)arh;
1206 
1207 	arh->ar_hrd = htons(ARPHRD_ETHER);	/* Hardware type:  ethernet */
1208 	arh->ar_pro = htons(ETHERTYPE_IP);	/* Protocol type:  IP */
1209 	arh->ar_hln = ETHERADDRL;	/* Length of hardware address:  6 */
1210 	arh->ar_pln = plen;		/* Length of protocol address:  4 */
1211 	arh->ar_op = htons(REVARP_REQUEST);	/* Opcode: REVARP Request */
1212 
1213 	cp += ARH_FIXED_LEN;
1214 
1215 	/* Sender's hardware address and protocol address */
1216 	bcopy(&portp->p_macaddr, cp, ETHERADDRL);
1217 	cp += ETHERADDRL;
1218 	bzero(cp, plen);	/* INADDR_ANY */
1219 	cp += plen;
1220 
1221 	/* Target hardware address and protocol address */
1222 	bcopy(&portp->p_macaddr, cp, ETHERADDRL);
1223 	cp += ETHERADDRL;
1224 	bzero(cp, plen);	/* INADDR_ANY */
1225 	cp += plen;
1226 
1227 	mp->b_wptr += ETHERMIN;	/* total size is 42; round up to ETHERMIN */
1228 
1229 	for (count = 0; count < vsw_publish_macaddr_count; count++) {
1230 
1231 		bp = dupmsg(mp);
1232 		if (bp == NULL) {
1233 			continue;
1234 		}
1235 
1236 		/* transmit the packet */
1237 		bp = vsw_tx_msg(vswp, bp, VSW_VNETPORT, portp);
1238 		if (bp != NULL) {
1239 			freemsg(bp);
1240 		}
1241 	}
1242 
1243 	freemsg(mp);
1244 }
1245 
1246 static void
1247 vsw_mac_set_mtu(vsw_t *vswp, uint32_t mtu)
1248 {
1249 	uint_t	mtu_orig;
1250 	int	rv;
1251 
1252 	rv = mac_set_mtu(vswp->mh, mtu, &mtu_orig);
1253 	if (rv != 0) {
1254 		cmn_err(CE_NOTE,
1255 		    "!vsw%d: Unable to set the mtu:%d, in the "
1256 		    "physical device:%s\n",
1257 		    vswp->instance, mtu, vswp->physname);
1258 		return;
1259 	}
1260 
1261 	/* save the original mtu of physdev to reset it back later if needed */
1262 	vswp->mtu_physdev_orig = mtu_orig;
1263 }
1264 
1265 /*
1266  * Register a callback with underlying mac layer for notifications.
1267  * We are currently interested in only link-state events.
1268  */
1269 static int
1270 vsw_notify_add(vsw_t *vswp)
1271 {
1272 	mac_notify_handle_t	mnh;
1273 	uint32_t		note;
1274 
1275 	/*
1276 	 * Check if the underlying MAC supports link update notification.
1277 	 */
1278 	note = mac_no_notification(vswp->mh);
1279 	if ((note & (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN)) != 0) {
1280 		vswp->phys_no_link_update = B_TRUE;
1281 	} else {
1282 		vswp->phys_no_link_update = B_FALSE;
1283 	}
1284 
1285 	/*
1286 	 * Read the current link state of the device and cache it.
1287 	 */
1288 	vswp->phys_link_state = vswp->phys_no_link_update ? LINK_STATE_UP :
1289 	    mac_stat_get(vswp->mh, MAC_STAT_LINK_STATE);
1290 
1291 	/*
1292 	 * Add notify callback function, if link update is supported.
1293 	 */
1294 	if (vswp->phys_no_link_update == B_TRUE) {
1295 		return (0);
1296 	}
1297 
1298 	mnh = mac_notify_add(vswp->mh, vsw_notify_cb, vswp);
1299 	if (mnh == 0) {
1300 		/* failed */
1301 		return (1);
1302 	}
1303 
1304 	vswp->mnh = mnh;
1305 	return (0);
1306 }
1307 
1308 /*
1309  * Remove notify callback.
1310  */
1311 static int
1312 vsw_notify_rem(vsw_t *vswp)
1313 {
1314 	int	rv;
1315 
1316 	rv = mac_notify_remove(vswp->mnh, B_FALSE);
1317 	return (rv);
1318 }
1319 
1320 /*
1321  * Notification callback invoked by the MAC service
1322  * module. Note that we process only link state updates.
1323  */
1324 static void
1325 vsw_notify_cb(void *arg, mac_notify_type_t type)
1326 {
1327 	vsw_t	*vswp = arg;
1328 
1329 	switch (type) {
1330 
1331 	case MAC_NOTE_LINK:
1332 		vsw_notify_link(vswp);
1333 		break;
1334 
1335 	default:
1336 		break;
1337 
1338 	}
1339 }
1340 
1341 /*
1342  * Invoked upon receiving a MAC_NOTE_LINK
1343  * notification for the underlying physical device.
1344  */
1345 static void
1346 vsw_notify_link(vsw_t *vswp)
1347 {
1348 	link_state_t	link_state;
1349 
1350 	/* link state change  notification */
1351 	link_state = mac_stat_get(vswp->mh, MAC_STAT_LINK_STATE);
1352 
1353 	if (vswp->phys_link_state != link_state) {
1354 		D3(vswp, "%s: phys_link_state(%d)\n",
1355 		    __func__, vswp->phys_link_state);
1356 
1357 		vswp->phys_link_state = link_state;
1358 		vsw_physlink_state_update(vswp);
1359 	}
1360 }
1361