Home | History | Annotate | Download | only in io
      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/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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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_NO_HWRINGS -- This flag is used by default
    501  *	for all ports/interface so that they are associated with
    502  *	default group & resources. It will not be used for the
    503  *	ports that have HybridIO is enabled so that the h/w resources
    504  *	assigned to it.
    505  *
    506  *	MAC_OPEN_FLAGS_SHARES_DESIRED -- This flag is used to indicate
    507  *	that a port desires a Share. This will be the case with the
    508  *	the ports that have hybrid mode enabled. This will only cause
    509  *	MAC layer to allocate a share and corresponding resources
    510  *	ahead of time.
    511  *
    512  *	MAC_UNICAST_TAG_DISABLE -- This flag is used for VLAN
    513  *	support. It will cause MAC to not add any tags, but expect
    514  *	vsw to tag the packets.
    515  *
    516  *	MAC_UNICAST_STRIP_DISABLE -- This flag is used for VLAN
    517  *	support. It will case the MAC layer to not strip the tags.
    518  *	Vsw may have to strip the tag for pvid case.
    519  */
    520 static int
    521 vsw_maccl_open(vsw_t *vswp, vsw_port_t *port, int type)
    522 {
    523 	int		rv = 0;
    524 	int		instance;
    525 	char		mac_cl_name[MAXNAMELEN];
    526 	const char	*dev_name;
    527 	mac_client_handle_t *mchp;
    528 	uint64_t flags = MAC_OPEN_FLAGS_NO_HWRINGS;
    529 
    530 	ASSERT(MUTEX_HELD(&vswp->mac_lock));
    531 	if (vswp->mh == NULL) {
    532 		/*
    533 		 * In case net-dev is changed (either set to nothing or
    534 		 * using aggregation device), return success here as the
    535 		 * timeout mechanism will handle it.
    536 		 */
    537 		return (0);
    538 	}
    539 
    540 	mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch;
    541 	if (*mchp != NULL) {
    542 		/* already open */
    543 		return (0);
    544 	}
    545 	dev_name = ddi_driver_name(vswp->dip);
    546 	instance = ddi_get_instance(vswp->dip);
    547 	if (type == VSW_VNETPORT) {
    548 		if (port->p_hio_enabled == B_TRUE) {
    549 			flags &= ~MAC_OPEN_FLAGS_NO_HWRINGS;
    550 			flags |= MAC_OPEN_FLAGS_SHARES_DESIRED;
    551 		}
    552 		(void) snprintf(mac_cl_name, MAXNAMELEN, "%s%d%s%d", dev_name,
    553 		    instance, "_port", port->p_instance);
    554 	} else {
    555 		(void) snprintf(mac_cl_name, MAXNAMELEN, "%s%s%d",
    556 		    dev_name, "_if", instance);
    557 	}
    558 
    559 	rv = mac_client_open(vswp->mh, mchp, mac_cl_name, flags);
    560 	if (rv != 0) {
    561 		cmn_err(CE_NOTE, "!vsw%d:%s mac_client_open() failed\n",
    562 		    vswp->instance, mac_cl_name);
    563 	}
    564 	return (rv);
    565 }
    566 
    567 /*
    568  * Clean up by removing uncast, multicast addresses and
    569  * closing the MAC client for a port or the interface.
    570  */
    571 void
    572 vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type)
    573 {
    574 	WRITE_MACCL_ENTER(vswp, port, type);
    575 	vsw_mac_multicast_remove_all(vswp, port, type);
    576 	vsw_unset_hw(vswp, port, type);
    577 	vsw_maccl_close(vswp, port, type);
    578 	RW_MACCL_EXIT(vswp, port, type);
    579 }
    580 
    581 /*
    582  * Close a MAC client for a port or an interface.
    583  */
    584 static void
    585 vsw_maccl_close(vsw_t *vswp, vsw_port_t *port, int type)
    586 {
    587 	mac_client_handle_t *mchp;
    588 
    589 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
    590 
    591 	mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch;
    592 	if (*mchp != NULL) {
    593 		mac_client_close(*mchp, 0);
    594 		*mchp = NULL;
    595 	}
    596 }
    597 
    598 /*
    599  * Cleanup MAC client related stuff for all ports.
    600  */
    601 void
    602 vsw_mac_cleanup_ports(vsw_t *vswp)
    603 {
    604 	vsw_port_list_t		*plist = &vswp->plist;
    605 	vsw_port_t		*port;
    606 
    607 	READ_ENTER(&plist->lockrw);
    608 	for (port = plist->head; port != NULL; port = port->p_next) {
    609 		vsw_mac_client_cleanup(vswp, port, VSW_VNETPORT);
    610 	}
    611 	RW_EXIT(&plist->lockrw);
    612 }
    613 
    614 /*
    615  * Depending on the mode specified, the capabilites and capacity
    616  * of the underlying device setup the physical device.
    617  *
    618  * If in layer 3 mode, then do nothing.
    619  *
    620  * If in layer 2 mode, open a mac client and program the mac-address
    621  * and vlan-ids. The MAC layer will take care of programming
    622  * the address into h/w or set the h/w into promiscuous mode.
    623  *
    624  * Returns 0 success, 1 on failure.
    625  */
    626 int
    627 vsw_set_hw(vsw_t *vswp, vsw_port_t *port, int type)
    628 {
    629 	int			err = 1;
    630 
    631 	D1(vswp, "%s: enter", __func__);
    632 
    633 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
    634 
    635 	if (vswp->smode == VSW_LAYER3)
    636 		return (0);
    637 
    638 	if (type == VSW_VNETPORT) {
    639 		ASSERT(port != NULL);
    640 		err = vsw_set_port_hw_addr(port);
    641 	} else {
    642 		err = vsw_set_if_hw_addr(vswp);
    643 	}
    644 
    645 	D1(vswp, "%s: exit", __func__);
    646 	return (err);
    647 }
    648 
    649 /*
    650  * If in layer 3 mode do nothing.
    651  *
    652  * If in layer 2 switched mode remove the address from the physical
    653  * device.
    654  *
    655  * If in layer 2 promiscuous mode disable promisc mode.
    656  *
    657  * Returns 0 on success.
    658  */
    659 void
    660 vsw_unset_hw(vsw_t *vswp, vsw_port_t *port, int type)
    661 {
    662 	D1(vswp, "%s: enter", __func__);
    663 
    664 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
    665 
    666 	if (vswp->smode == VSW_LAYER3)
    667 		return;
    668 
    669 	if (type == VSW_VNETPORT) {
    670 		ASSERT(port != NULL);
    671 		vsw_unset_hw_addr(vswp, port, type);
    672 	} else {
    673 		vsw_unset_hw_addr(vswp, NULL, type);
    674 	}
    675 
    676 	D1(vswp, "%s: exit", __func__);
    677 }
    678 
    679 /*
    680  * Program the macaddress and vlans of a port.
    681  *
    682  * Returns 0 on sucess, 1 on failure.
    683  */
    684 static int
    685 vsw_set_port_hw_addr(vsw_port_t *port)
    686 {
    687 	vsw_t			*vswp = port->p_vswp;
    688 	mac_diag_t		diag;
    689 	uint8_t			*macaddr;
    690 	uint16_t		vid = VLAN_ID_NONE;
    691 	int			rv;
    692 	uint16_t		mac_flags = MAC_UNICAST_TAG_DISABLE |
    693 	    MAC_UNICAST_STRIP_DISABLE;
    694 
    695 	D1(vswp, "%s: enter", __func__);
    696 
    697 	ASSERT(RW_WRITE_HELD(&port->maccl_rwlock));
    698 	if (port->p_mch == NULL)
    699 		return (0);
    700 
    701 	/*
    702 	 * If the port has a specific 'pvid', then
    703 	 * register with that vlan-id, otherwise register
    704 	 * with VLAN_ID_NONE.
    705 	 */
    706 	if (port->pvid != vswp->default_vlan_id) {
    707 		vid = port->pvid;
    708 	}
    709 	macaddr = (uint8_t *)port->p_macaddr.ether_addr_octet;
    710 
    711 	if (!(vswp->smode & VSW_LAYER2_PROMISC)) {
    712 		mac_flags |= MAC_UNICAST_HW;
    713 	}
    714 
    715 	if (port->addr_set == B_FALSE) {
    716 		port->p_muh = NULL;
    717 		rv = mac_unicast_add(port->p_mch, macaddr, mac_flags,
    718 		    &port->p_muh, vid, &diag);
    719 
    720 		if (rv != 0) {
    721 			cmn_err(CE_WARN, "vsw%d: Failed to program"
    722 			    "macaddr,vid(%s, %d) err=%d",
    723 			    vswp->instance, ether_sprintf((void *)macaddr),
    724 			    vid, rv);
    725 			return (rv);
    726 		}
    727 		port->addr_set = B_TRUE;
    728 
    729 		D2(vswp, "%s:programmed macaddr(%s) vid(%d) into device %s",
    730 		    __func__, ether_sprintf((void *)macaddr), vid,
    731 		    vswp->physname);
    732 	}
    733 
    734 	/* Add vlans to the MAC layer */
    735 	vsw_mac_add_vlans(vswp, port->p_mch, macaddr,
    736 	    mac_flags, port->vids, port->nvids);
    737 
    738 	/* Configure bandwidth to the MAC layer */
    739 	vsw_maccl_set_bandwidth(NULL, port, VSW_VNETPORT, port->p_bandwidth);
    740 
    741 	mac_rx_set(port->p_mch, vsw_port_rx_cb, (void *)port);
    742 
    743 	D1(vswp, "%s: exit", __func__);
    744 	return (rv);
    745 }
    746 
    747 /*
    748  * Program the macaddress and vlans of a port.
    749  *
    750  * Returns 0 on sucess, 1 on failure.
    751  */
    752 static int
    753 vsw_set_if_hw_addr(vsw_t *vswp)
    754 {
    755 	mac_diag_t		diag;
    756 	uint8_t			*macaddr;
    757 	uint8_t			primary_addr[ETHERADDRL];
    758 	uint16_t		vid = VLAN_ID_NONE;
    759 	int			rv;
    760 	uint16_t		mac_flags = MAC_UNICAST_TAG_DISABLE |
    761 	    MAC_UNICAST_STRIP_DISABLE;
    762 
    763 	D1(vswp, "%s: enter", __func__);
    764 
    765 	ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
    766 	if (vswp->mch == NULL)
    767 		return (0);
    768 
    769 	macaddr = (uint8_t *)vswp->if_addr.ether_addr_octet;
    770 
    771 	/* check if it is the primary macaddr of the card. */
    772 	mac_unicast_primary_get(vswp->mh, primary_addr);
    773 	if (ether_cmp((void *)primary_addr, (void*)macaddr) == 0) {
    774 		mac_flags |= MAC_UNICAST_PRIMARY;
    775 	}
    776 
    777 	/*
    778 	 * If the interface has a specific 'pvid', then
    779 	 * register with that vlan-id, otherwise register
    780 	 * with VLAN_ID_NONE.
    781 	 */
    782 	if (vswp->pvid != vswp->default_vlan_id) {
    783 		vid = vswp->pvid;
    784 	}
    785 
    786 	if (!(vswp->smode & VSW_LAYER2_PROMISC)) {
    787 		mac_flags |= MAC_UNICAST_HW;
    788 	}
    789 
    790 	if (vswp->addr_set == B_FALSE) {
    791 		vswp->muh = NULL;
    792 		rv = mac_unicast_add(vswp->mch, macaddr, mac_flags,
    793 		    &vswp->muh, vid, &diag);
    794 
    795 		if (rv != 0) {
    796 			cmn_err(CE_WARN, "vsw%d: Failed to program"
    797 			    "macaddr,vid(%s, %d) err=%d",
    798 			    vswp->instance, ether_sprintf((void *)macaddr),
    799 			    vid, rv);
    800 			return (rv);
    801 		}
    802 		vswp->addr_set = B_TRUE;
    803 
    804 		D2(vswp, "%s:programmed macaddr(%s) vid(%d) into device %s",
    805 		    __func__, ether_sprintf((void *)macaddr), vid,
    806 		    vswp->physname);
    807 	}
    808 
    809 	vsw_mac_add_vlans(vswp, vswp->mch, macaddr, mac_flags,
    810 	    vswp->vids, vswp->nvids);
    811 
    812 	vsw_maccl_set_bandwidth(vswp, NULL, VSW_LOCALDEV, vswp->bandwidth);
    813 
    814 	mac_rx_set(vswp->mch, vsw_if_rx_cb, (void *)vswp);
    815 
    816 	D1(vswp, "%s: exit", __func__);
    817 	return (rv);
    818 }
    819 
    820 /*
    821  * Remove a unicast mac address which has previously been programmed
    822  * into HW.
    823  *
    824  * Returns 0 on sucess, 1 on failure.
    825  */
    826 static void
    827 vsw_unset_hw_addr(vsw_t *vswp, vsw_port_t *port, int type)
    828 {
    829 	vsw_vlanid_t		*vids;
    830 	int			nvids;
    831 	mac_client_handle_t	mch = NULL;
    832 
    833 	D1(vswp, "%s: enter", __func__);
    834 
    835 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
    836 
    837 	if (type == VSW_VNETPORT) {
    838 		ASSERT(port != NULL);
    839 		ASSERT(RW_WRITE_HELD(&port->maccl_rwlock));
    840 		vids = port->vids;
    841 		nvids = port->nvids;
    842 	} else {
    843 		ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
    844 		vids = vswp->vids;
    845 		nvids = vswp->nvids;
    846 	}
    847 
    848 	/* First clear the callback */
    849 	if (type == VSW_LOCALDEV) {
    850 		mch = vswp->mch;
    851 	} else if (type == VSW_VNETPORT) {
    852 		mch = port->p_mch;
    853 	}
    854 
    855 
    856 	if (mch == NULL) {
    857 		return;
    858 	}
    859 
    860 	mac_rx_clear(mch);
    861 
    862 	/* Remove vlans */
    863 	vsw_mac_remove_vlans(mch, vids, nvids);
    864 
    865 	if ((type == VSW_LOCALDEV) && (vswp->addr_set == B_TRUE)) {
    866 		(void) mac_unicast_remove(vswp->mch, vswp->muh);
    867 		vswp->muh = NULL;
    868 		D2(vswp, "removed vsw interface mac-addr from "
    869 		    "the device %s", vswp->physname);
    870 		vswp->addr_set = B_FALSE;
    871 
    872 	} else if ((type == VSW_VNETPORT) && (port->addr_set == B_TRUE)) {
    873 		(void) mac_unicast_remove(port->p_mch, port->p_muh);
    874 		port->p_muh = NULL;
    875 		D2(vswp, "removed port(0x%p) mac-addr from "
    876 		    "the device %s", port, vswp->physname);
    877 		port->addr_set = B_FALSE;
    878 	}
    879 
    880 	D1(vswp, "%s: exit", __func__);
    881 }
    882 
    883 /*
    884  * receive callback routine for vsw interface. Invoked by MAC layer when there
    885  * are pkts being passed up from physical device for this vsw interface.
    886  */
    887 /* ARGSUSED */
    888 static void
    889 vsw_if_rx_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
    890     boolean_t loopback)
    891 {
    892 	_NOTE(ARGUNUSED(mrh))
    893 
    894 	vsw_t		*vswp = (vsw_t *)arg;
    895 	mblk_t		*mpt;
    896 	int		count;
    897 
    898 	ASSERT(vswp != NULL);
    899 
    900 	D1(vswp, "%s: enter", __func__);
    901 
    902 	READ_ENTER(&vswp->if_lockrw);
    903 	if (vswp->if_state & VSW_IF_UP) {
    904 		RW_EXIT(&vswp->if_lockrw);
    905 		count = vsw_vlan_frame_untag(vswp, VSW_LOCALDEV, &mp, &mpt);
    906 		if (count != 0) {
    907 			mac_rx(vswp->if_mh, NULL, mp);
    908 		}
    909 	} else {
    910 		RW_EXIT(&vswp->if_lockrw);
    911 		freemsgchain(mp);
    912 	}
    913 
    914 	D1(vswp, "%s: exit", __func__);
    915 }
    916 
    917 /*
    918  * receive callback routine for port. Invoked by MAC layer when there
    919  * are pkts being passed up from physical device for this port.
    920  */
    921 /* ARGSUSED */
    922 static void
    923 vsw_port_rx_cb(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
    924     boolean_t loopback)
    925 {
    926 	_NOTE(ARGUNUSED(mrh))
    927 
    928 	vsw_t		*vswp;
    929 	vsw_port_t	*port = arg;
    930 
    931 	ASSERT(port != NULL);
    932 
    933 	vswp = port->p_vswp;
    934 
    935 	D1(vswp, "vsw_port_rx_cb: enter");
    936 
    937 	/*
    938 	 * Send the packets to the peer directly.
    939 	 */
    940 	(void) vsw_portsend(port, mp);
    941 
    942 	D1(vswp, "vsw_port_rx_cb: exit");
    943 }
    944 
    945 /*
    946  * Send a message out over the physical device
    947  * via the MAC layer.
    948  *
    949  * Returns any mblks that it was unable to transmit.
    950  */
    951 mblk_t *
    952 vsw_tx_msg(vsw_t *vswp, mblk_t *mp, int caller, vsw_port_t *port)
    953 {
    954 	mac_client_handle_t	mch;
    955 	mac_unicast_handle_t	muh;
    956 
    957 	READ_MACCL_ENTER(vswp, port, caller);
    958 
    959 	mch = (caller == VSW_LOCALDEV) ? vswp->mch : port->p_mch;
    960 	muh = (caller == VSW_LOCALDEV) ? vswp->muh : port->p_muh;
    961 
    962 	if (mch == NULL || muh == NULL) {
    963 		RW_MACCL_EXIT(vswp, port, caller);
    964 		return (mp);
    965 	}
    966 
    967 	/* packets are sent or dropped */
    968 	(void) mac_tx(mch, mp, 0, MAC_DROP_ON_NO_DESC, NULL);
    969 	RW_MACCL_EXIT(vswp, port, caller);
    970 	return (NULL);
    971 }
    972 
    973 /*
    974  * vsw_port_mac_reconfig -- Cleanup and close the MAC client
    975  * and reopen and re-configure the MAC client with new flags etc.
    976  * This function is useful for two different purposes:
    977  *	1) To update the MAC client with new vlan-ids. This is done
    978  *	   by freeing the existing vlan-ids and reopen with the new
    979  *	   vlan-ids.
    980  *
    981  *	2) If the Hybrid mode status of a port changes, then the
    982  *	   MAC client need to be closed and re-opened, otherwise,
    983  *	   Share related resources may not be freed(hybird mode disabled)
    984  *	   or assigned(hybrid mode enabled). To accomplish this,
    985  *	   this function simply closes and reopens the MAC client.
    986  *	   The reopen will result in using the flags based on the
    987  *	   new hybrid mode of the port.
    988  */
    989 void
    990 vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans,
    991     uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids)
    992 {
    993 	vsw_t *vswp = portp->p_vswp;
    994 	int rv;
    995 
    996 	D1(vswp, "%s: enter", __func__);
    997 	/*
    998 	 * Remove the multi-cast addresses, unicast address
    999 	 * and close the mac-client.
   1000 	 */
   1001 	mutex_enter(&vswp->mac_lock);
   1002 	WRITE_ENTER(&portp->maccl_rwlock);
   1003 	vsw_mac_multicast_remove_all(vswp, portp, VSW_VNETPORT);
   1004 	vsw_unset_hw(vswp, portp, VSW_VNETPORT);
   1005 	vsw_maccl_close(vswp, portp, VSW_VNETPORT);
   1006 
   1007 	if (update_vlans == B_TRUE) {
   1008 		if (portp->nvids != 0) {
   1009 			kmem_free(portp->vids,
   1010 			    sizeof (vsw_vlanid_t) * portp->nvids);
   1011 			portp->vids = NULL;
   1012 			portp->nvids = 0;
   1013 		}
   1014 		portp->vids = new_vids;
   1015 		portp->nvids = new_nvids;
   1016 		portp->pvid = new_pvid;
   1017 	}
   1018 
   1019 	/*
   1020 	 * Now re-open the mac-client and
   1021 	 * configure unicast addr and multicast addrs.
   1022 	 */
   1023 	rv = vsw_maccl_open(vswp, portp, VSW_VNETPORT);
   1024 	if (rv != 0) {
   1025 		goto recret;
   1026 	}
   1027 
   1028 	if (vsw_set_hw(vswp, portp, VSW_VNETPORT)) {
   1029 		cmn_err(CE_NOTE, "!vsw%d: port:%d failed to "
   1030 		    "set unicast address\n", vswp->instance, portp->p_instance);
   1031 		goto recret;
   1032 	}
   1033 
   1034 	vsw_mac_multicast_add_all(vswp, portp, VSW_VNETPORT);
   1035 
   1036 recret:
   1037 	RW_EXIT(&portp->maccl_rwlock);
   1038 	mutex_exit(&vswp->mac_lock);
   1039 	D1(vswp, "%s: exit", __func__);
   1040 }
   1041 
   1042 /*
   1043  * vsw_if_mac_reconfig -- Reconfigure the vsw interfaace's mac-client
   1044  * by closing and re-opening it. This function is used handle the
   1045  * following two cases:
   1046  *
   1047  *	1) Handle the MAC address change for the interface.
   1048  *	2) Handle vlan update.
   1049  */
   1050 void
   1051 vsw_if_mac_reconfig(vsw_t *vswp, boolean_t update_vlans,
   1052     uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids)
   1053 {
   1054 	int rv;
   1055 
   1056 	D1(vswp, "%s: enter", __func__);
   1057 	/*
   1058 	 * Remove the multi-cast addresses, unicast address
   1059 	 * and close the mac-client.
   1060 	 */
   1061 	mutex_enter(&vswp->mac_lock);
   1062 	WRITE_ENTER(&vswp->maccl_rwlock);
   1063 	vsw_mac_multicast_remove_all(vswp, NULL, VSW_LOCALDEV);
   1064 	vsw_unset_hw(vswp, NULL, VSW_LOCALDEV);
   1065 	vsw_maccl_close(vswp, NULL, VSW_LOCALDEV);
   1066 
   1067 	if (update_vlans == B_TRUE) {
   1068 		if (vswp->nvids != 0) {
   1069 			kmem_free(vswp->vids,
   1070 			    sizeof (vsw_vlanid_t) * vswp->nvids);
   1071 			vswp->vids = NULL;
   1072 			vswp->nvids = 0;
   1073 		}
   1074 		vswp->vids = new_vids;
   1075 		vswp->nvids = new_nvids;
   1076 		vswp->pvid = new_pvid;
   1077 	}
   1078 
   1079 	/*
   1080 	 * Now re-open the mac-client and
   1081 	 * configure unicast addr and multicast addrs.
   1082 	 */
   1083 	rv = vsw_maccl_open(vswp, NULL, VSW_LOCALDEV);
   1084 	if (rv != 0) {
   1085 		goto ifrecret;
   1086 	}
   1087 
   1088 	if (vsw_set_hw(vswp, NULL, VSW_LOCALDEV)) {
   1089 		cmn_err(CE_NOTE, "!vsw%d:failed to set unicast address\n",
   1090 		    vswp->instance);
   1091 		goto ifrecret;
   1092 	}
   1093 
   1094 	vsw_mac_multicast_add_all(vswp, NULL, VSW_LOCALDEV);
   1095 
   1096 ifrecret:
   1097 	RW_EXIT(&vswp->maccl_rwlock);
   1098 	mutex_exit(&vswp->mac_lock);
   1099 	D1(vswp, "%s: exit", __func__);
   1100 }
   1101 
   1102 /*
   1103  * vsw_mac_port_reconfig_vlans -- Reconfigure a port to handle
   1104  * vlan configuration update. As the removal of the last unicast-address,vid
   1105  * from the MAC client results in releasing all resources, it expects
   1106  * no Shares to be associated with such MAC client.
   1107  *
   1108  * To handle vlan configuration update for a port that already has
   1109  * a Share bound, then we need to free that share prior to reconfiguration.
   1110  * Initiate the hybrdIO setup again after the completion of reconfiguration.
   1111  */
   1112 void
   1113 vsw_mac_port_reconfig_vlans(vsw_port_t *portp, uint16_t new_pvid,
   1114     vsw_vlanid_t *new_vids, int new_nvids)
   1115 {
   1116 	/*
   1117 	 * As the reconfiguration involves the close of
   1118 	 * mac client, cleanup HybridIO and later restart
   1119 	 * HybridIO setup again.
   1120 	 */
   1121 	if (portp->p_hio_enabled == B_TRUE) {
   1122 		vsw_hio_stop_port(portp);
   1123 	}
   1124 	vsw_port_mac_reconfig(portp, B_TRUE, new_pvid, new_vids, new_nvids);
   1125 	if (portp->p_hio_enabled == B_TRUE) {
   1126 		/* reset to setup the HybridIO again. */
   1127 		vsw_hio_port_reset(portp, B_FALSE);
   1128 	}
   1129 }
   1130 
   1131 /* Add vlans to MAC client */
   1132 static void
   1133 vsw_mac_add_vlans(vsw_t *vswp, mac_client_handle_t mch, uint8_t *macaddr,
   1134     uint16_t flags, vsw_vlanid_t *vids, int nvids)
   1135 {
   1136 	vsw_vlanid_t	*vidp;
   1137 	mac_diag_t	diag;
   1138 	int		rv;
   1139 	int		i;
   1140 
   1141 	flags |= MAC_UNICAST_TAG_DISABLE | MAC_UNICAST_STRIP_DISABLE;
   1142 
   1143 	/* Add vlans to the MAC layer */
   1144 	for (i = 0; i < nvids; i++) {
   1145 		vidp = &vids[i];
   1146 
   1147 		if (vidp->vl_set == B_TRUE) {
   1148 			continue;
   1149 		}
   1150 
   1151 		rv = mac_unicast_add(mch, macaddr, flags,
   1152 		    &vidp->vl_muh, vidp->vl_vid, &diag);
   1153 		if (rv != 0) {
   1154 			cmn_err(CE_WARN, "vsw%d: Failed to program"
   1155 			    "macaddr,vid(%s, %d) err=%d",
   1156 			    vswp->instance, ether_sprintf((void *)macaddr),
   1157 			    vidp->vl_vid, rv);
   1158 		} else {
   1159 			vidp->vl_set = B_TRUE;
   1160 			D2(vswp, "%s:programmed macaddr(%s) vid(%d) "
   1161 			    "into device %s", __func__,
   1162 			    ether_sprintf((void *)macaddr),
   1163 			    vidp->vl_vid, vswp->physname);
   1164 		}
   1165 	}
   1166 }
   1167 
   1168 /* Remove vlans from the MAC client */
   1169 static void
   1170 vsw_mac_remove_vlans(mac_client_handle_t mch, vsw_vlanid_t *vids, int nvids)
   1171 {
   1172 	int i;
   1173 	vsw_vlanid_t *vidp;
   1174 
   1175 	for (i = 0; i < nvids; i++) {
   1176 		vidp = &vids[i];
   1177 		if (vidp->vl_set == B_FALSE) {
   1178 			continue;
   1179 		}
   1180 		mac_unicast_remove(mch, vidp->vl_muh);
   1181 		vidp->vl_set = B_FALSE;
   1182 	}
   1183 }
   1184 
   1185 #define	ARH_FIXED_LEN	8    /* Length of fixed part of ARP header(see arp.h) */
   1186 
   1187 /*
   1188  * Send a gratuitous RARP packet to notify the physical switch to update its
   1189  * Layer2 forwarding table for the given mac address. This is done to allow the
   1190  * switch to quickly learn the macaddr-port association when a guest is live
   1191  * migrated or when vsw's physical device is changed dynamically. Any protocol
   1192  * packet would serve this purpose, but we choose RARP, as it allows us to
   1193  * accomplish this within L2 (ie, no need to specify IP addr etc in the packet)
   1194  * The macaddr of vnet is retained across migration. Hence, we don't need to
   1195  * update the arp cache of other hosts within the broadcast domain. Note that
   1196  * it is harmless to send these RARP packets during normal port attach of a
   1197  * client vnet. This can can be turned off if needed, by setting
   1198  * vsw_publish_macaddr_count to zero in /etc/system.
   1199  */
   1200 void
   1201 vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp)
   1202 {
   1203 	mblk_t			*mp;
   1204 	mblk_t			*bp;
   1205 	struct arphdr		*arh;
   1206 	struct	ether_header 	*ehp;
   1207 	int			count = 0;
   1208 	int			plen = 4;
   1209 	uint8_t			*cp;
   1210 
   1211 	mp = allocb(ETHERMIN, BPRI_MED);
   1212 	if (mp == NULL) {
   1213 		return;
   1214 	}
   1215 
   1216 	/* Initialize eth header */
   1217 	ehp = (struct  ether_header *)mp->b_rptr;
   1218 	bcopy(&etherbroadcastaddr, &ehp->ether_dhost, ETHERADDRL);
   1219 	bcopy(&portp->p_macaddr, &ehp->ether_shost, ETHERADDRL);
   1220 	ehp->ether_type = htons(ETHERTYPE_REVARP);
   1221 
   1222 	/* Initialize arp packet */
   1223 	arh = (struct arphdr *)(mp->b_rptr + sizeof (struct ether_header));
   1224 	cp = (uint8_t *)arh;
   1225 
   1226 	arh->ar_hrd = htons(ARPHRD_ETHER);	/* Hardware type:  ethernet */
   1227 	arh->ar_pro = htons(ETHERTYPE_IP);	/* Protocol type:  IP */
   1228 	arh->ar_hln = ETHERADDRL;	/* Length of hardware address:  6 */
   1229 	arh->ar_pln = plen;		/* Length of protocol address:  4 */
   1230 	arh->ar_op = htons(REVARP_REQUEST);	/* Opcode: REVARP Request */
   1231 
   1232 	cp += ARH_FIXED_LEN;
   1233 
   1234 	/* Sender's hardware address and protocol address */
   1235 	bcopy(&portp->p_macaddr, cp, ETHERADDRL);
   1236 	cp += ETHERADDRL;
   1237 	bzero(cp, plen);	/* INADDR_ANY */
   1238 	cp += plen;
   1239 
   1240 	/* Target hardware address and protocol address */
   1241 	bcopy(&portp->p_macaddr, cp, ETHERADDRL);
   1242 	cp += ETHERADDRL;
   1243 	bzero(cp, plen);	/* INADDR_ANY */
   1244 	cp += plen;
   1245 
   1246 	mp->b_wptr += ETHERMIN;	/* total size is 42; round up to ETHERMIN */
   1247 
   1248 	for (count = 0; count < vsw_publish_macaddr_count; count++) {
   1249 
   1250 		bp = dupmsg(mp);
   1251 		if (bp == NULL) {
   1252 			continue;
   1253 		}
   1254 
   1255 		/* transmit the packet */
   1256 		bp = vsw_tx_msg(vswp, bp, VSW_VNETPORT, portp);
   1257 		if (bp != NULL) {
   1258 			freemsg(bp);
   1259 		}
   1260 	}
   1261 
   1262 	freemsg(mp);
   1263 }
   1264 
   1265 static void
   1266 vsw_mac_set_mtu(vsw_t *vswp, uint32_t mtu)
   1267 {
   1268 	uint_t	mtu_orig;
   1269 	int	rv;
   1270 
   1271 	rv = mac_set_mtu(vswp->mh, mtu, &mtu_orig);
   1272 	if (rv != 0) {
   1273 		cmn_err(CE_NOTE,
   1274 		    "!vsw%d: Unable to set the mtu:%d, in the "
   1275 		    "physical device:%s\n",
   1276 		    vswp->instance, mtu, vswp->physname);
   1277 		return;
   1278 	}
   1279 
   1280 	/* save the original mtu of physdev to reset it back later if needed */
   1281 	vswp->mtu_physdev_orig = mtu_orig;
   1282 }
   1283 
   1284 /*
   1285  * Register a callback with underlying mac layer for notifications.
   1286  * We are currently interested in only link-state events.
   1287  */
   1288 static int
   1289 vsw_notify_add(vsw_t *vswp)
   1290 {
   1291 	mac_notify_handle_t	mnh;
   1292 	uint32_t		note;
   1293 
   1294 	/*
   1295 	 * Check if the underlying MAC supports link update notification.
   1296 	 */
   1297 	note = mac_no_notification(vswp->mh);
   1298 	if ((note & (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN)) != 0) {
   1299 		vswp->phys_no_link_update = B_TRUE;
   1300 	} else {
   1301 		vswp->phys_no_link_update = B_FALSE;
   1302 	}
   1303 
   1304 	/*
   1305 	 * Read the current link state of the device and cache it.
   1306 	 */
   1307 	vswp->phys_link_state = vswp->phys_no_link_update ? LINK_STATE_UP :
   1308 	    mac_stat_get(vswp->mh, MAC_STAT_LINK_STATE);
   1309 
   1310 	/*
   1311 	 * Add notify callback function, if link update is supported.
   1312 	 */
   1313 	if (vswp->phys_no_link_update == B_TRUE) {
   1314 		return (0);
   1315 	}
   1316 
   1317 	mnh = mac_notify_add(vswp->mh, vsw_notify_cb, vswp);
   1318 	if (mnh == 0) {
   1319 		/* failed */
   1320 		return (1);
   1321 	}
   1322 
   1323 	vswp->mnh = mnh;
   1324 	return (0);
   1325 }
   1326 
   1327 /*
   1328  * Remove notify callback.
   1329  */
   1330 static int
   1331 vsw_notify_rem(vsw_t *vswp)
   1332 {
   1333 	int	rv;
   1334 
   1335 	rv = mac_notify_remove(vswp->mnh, B_FALSE);
   1336 	return (rv);
   1337 }
   1338 
   1339 /*
   1340  * Notification callback invoked by the MAC service
   1341  * module. Note that we process only link state updates.
   1342  */
   1343 static void
   1344 vsw_notify_cb(void *arg, mac_notify_type_t type)
   1345 {
   1346 	vsw_t	*vswp = arg;
   1347 
   1348 	switch (type) {
   1349 
   1350 	case MAC_NOTE_LINK:
   1351 		vsw_notify_link(vswp);
   1352 		break;
   1353 
   1354 	default:
   1355 		break;
   1356 
   1357 	}
   1358 }
   1359 
   1360 /*
   1361  * Invoked upon receiving a MAC_NOTE_LINK
   1362  * notification for the underlying physical device.
   1363  */
   1364 static void
   1365 vsw_notify_link(vsw_t *vswp)
   1366 {
   1367 	link_state_t	link_state;
   1368 
   1369 	/* link state change  notification */
   1370 	link_state = mac_stat_get(vswp->mh, MAC_STAT_LINK_STATE);
   1371 
   1372 	if (vswp->phys_link_state != link_state) {
   1373 		D3(vswp, "%s: phys_link_state(%d)\n",
   1374 		    __func__, vswp->phys_link_state);
   1375 
   1376 		vswp->phys_link_state = link_state;
   1377 		vsw_physlink_state_update(vswp);
   1378 	}
   1379 }
   1380 
   1381 /*
   1382  * Configure the bandwidth limit on the vsw or vnet devices via the MAC layer.
   1383  * Note that bandwidth limit is not supported on a HybridIO enabled
   1384  * vnet, as the HybridIO assigns a specific unit of hardware resource
   1385  * that cannot be changed to limit bandwidth.
   1386  */
   1387 static void
   1388 vsw_maccl_set_bandwidth(vsw_t *vswp, vsw_port_t *port, int type, uint64_t maxbw)
   1389 {
   1390 	int			rv = 0;
   1391 	uint64_t		*bw;
   1392 	mac_resource_props_t	mrp;
   1393 	mac_client_handle_t	mch;
   1394 
   1395 	ASSERT((type == VSW_LOCALDEV) || (type == VSW_VNETPORT));
   1396 
   1397 	if (type == VSW_VNETPORT) {
   1398 		ASSERT(RW_WRITE_HELD(&port->maccl_rwlock));
   1399 		mch = port->p_mch;
   1400 		bw = &port->p_bandwidth;
   1401 	} else {
   1402 		ASSERT(RW_WRITE_HELD(&vswp->maccl_rwlock));
   1403 		mch = vswp->mch;
   1404 		bw = &vswp->bandwidth;
   1405 	}
   1406 
   1407 	if (mch == NULL) {
   1408 		return;
   1409 	}
   1410 
   1411 	if (maxbw >= MRP_MAXBW_MINVAL || maxbw == 0) {
   1412 		bzero(&mrp, sizeof (mac_resource_props_t));
   1413 		if (maxbw == 0) {
   1414 			mrp.mrp_maxbw = MRP_MAXBW_RESETVAL;
   1415 		} else {
   1416 			mrp.mrp_maxbw = maxbw;
   1417 		}
   1418 		mrp.mrp_mask |= MRP_MAXBW;
   1419 
   1420 		rv = mac_client_set_resources(mch, &mrp);
   1421 		if (rv != 0) {
   1422 			if (type == VSW_VNETPORT) {
   1423 				cmn_err(CE_NOTE, "!port%d: cannot set "
   1424 				    "bandwidth limit to (%ld), error(%d)\n",
   1425 				    port->p_instance, maxbw, rv);
   1426 			} else {
   1427 				cmn_err(CE_NOTE, "!vsw%d: cannot set "
   1428 				    "bandwidth limit to (%ld), error(%d)\n",
   1429 				    vswp->instance, maxbw, rv);
   1430 			}
   1431 		} else {
   1432 			/*
   1433 			 * update with successfully configured bandwidth.
   1434 			 */
   1435 			*bw = maxbw;
   1436 		}
   1437 	}
   1438 }
   1439