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