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