vsw_phys.c revision 11878:ac93462db6d7
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. */
72static void vsw_port_rx_cb(void *, mac_resource_handle_t, mblk_t *,
73    boolean_t);
74static void vsw_if_rx_cb(void *, mac_resource_handle_t, mblk_t *, boolean_t);
75
76/* MAC layer routines */
77static int vsw_set_port_hw_addr(vsw_port_t *port);
78static int vsw_set_if_hw_addr(vsw_t *vswp);
79static	void vsw_unset_hw_addr(vsw_t *, vsw_port_t *, int);
80static int vsw_maccl_open(vsw_t *vswp, vsw_port_t *port, int type);
81static void vsw_maccl_close(vsw_t *vswp, vsw_port_t *port, int type);
82static void vsw_mac_multicast_add_all(vsw_t *vswp, vsw_port_t *portp, int type);
83static void vsw_mac_multicast_remove_all(vsw_t *vswp,
84    vsw_port_t *portp, int type);
85static 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);
87static void vsw_mac_remove_vlans(mac_client_handle_t mch, vsw_vlanid_t *vids,
88    int nvids);
89static	void vsw_mac_set_mtu(vsw_t *vswp, uint32_t mtu);
90static void vsw_maccl_set_bandwidth(vsw_t *vswp, vsw_port_t *port, int type,
91    uint64_t maxbw);
92static int vsw_notify_add(vsw_t *vswp);
93static int vsw_notify_rem(vsw_t *vswp);
94static void vsw_notify_cb(void *arg, mac_notify_type_t type);
95static void vsw_notify_link(vsw_t *vswp);
96
97/* Support functions */
98int vsw_set_hw(vsw_t *, vsw_port_t *, int);
99void vsw_unset_hw(vsw_t *, vsw_port_t *, int);
100void vsw_reconfig_hw(vsw_t *);
101int vsw_mac_open(vsw_t *vswp);
102void vsw_mac_close(vsw_t *vswp);
103int vsw_mac_multicast_add(vsw_t *vswp, vsw_port_t *port, mcst_addr_t *mcst_p,
104    int type);
105void vsw_mac_multicast_remove(vsw_t *vswp, vsw_port_t *port,
106    mcst_addr_t *mcst_p, int type);
107int vsw_mac_client_init(vsw_t *vswp, vsw_port_t *port, int type);
108void vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type);
109void vsw_mac_cleanup_ports(vsw_t *vswp);
110void vsw_unset_addrs(vsw_t *vswp);
111void vsw_set_addrs(vsw_t *vswp);
112mblk_t *vsw_tx_msg(vsw_t *, mblk_t *, int, vsw_port_t *);
113void vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp);
114void 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);
116void vsw_mac_port_reconfig_vlans(vsw_port_t *portp, uint16_t new_pvid,
117    vsw_vlanid_t *new_vids, int new_nvids);
118void vsw_if_mac_reconfig(vsw_t *vswp, boolean_t update_vlans,
119    uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids);
120void 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 */
126extern int vsw_portsend(vsw_port_t *port, mblk_t *mp);
127extern void vsw_hio_stop_port(vsw_port_t *portp);
128extern void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate);
129extern uint32_t vsw_publish_macaddr_count;
130extern uint32_t vsw_vlan_frame_untag(void *arg, int type, mblk_t **np,
131	mblk_t **npt);
132extern void vsw_physlink_state_update(vsw_t *vswp);
133static char mac_mtu_propname[] = "mtu";
134
135/*
136 * Tunables used in this file.
137 */
138extern 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 */
178void
179vsw_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 */
239void
240vsw_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 */
261int
262vsw_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 */
311void
312vsw_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 */
332int
333vsw_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 */
363void
364vsw_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 */
383static void
384vsw_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 */
426static void
427vsw_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
459void
460vsw_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 */
476int
477vsw_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 */
515static int
516vsw_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 */
568void
569vsw_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 */
581static void
582vsw_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 */
598void
599vsw_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 */
623int
624vsw_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 */
656void
657vsw_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 */
681static int
682vsw_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 */
749static int
750vsw_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 */
823static void
824vsw_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 */
885static void
886vsw_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 */
919static void
920vsw_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 */
948mblk_t *
949vsw_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 */
986void
987vsw_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
1033recret:
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 */
1047void
1048vsw_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
1093ifrecret:
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 */
1109void
1110vsw_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 */
1129static void
1130vsw_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 */
1166static void
1167vsw_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 */
1197void
1198vsw_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
1262static void
1263vsw_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 */
1285static int
1286vsw_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 */
1327static int
1328vsw_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 */
1340static void
1341vsw_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 */
1361static void
1362vsw_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 */
1384static void
1385vsw_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