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 <sys/varargs.h>
     53 #include <sys/machsystm.h>
     54 #include <sys/modctl.h>
     55 #include <sys/modhash.h>
     56 #include <sys/mac_provider.h>
     57 #include <sys/mac_ether.h>
     58 #include <sys/taskq.h>
     59 #include <sys/note.h>
     60 #include <sys/mach_descrip.h>
     61 #include <sys/mac.h>
     62 #include <sys/mdeg.h>
     63 #include <sys/ldc.h>
     64 #include <sys/vsw_fdb.h>
     65 #include <sys/vsw.h>
     66 #include <sys/vio_mailbox.h>
     67 #include <sys/vnet_mailbox.h>
     68 #include <sys/vnet_common.h>
     69 #include <sys/vio_util.h>
     70 #include <sys/sdt.h>
     71 #include <sys/atomic.h>
     72 #include <sys/callb.h>
     73 
     74 
     75 #define	VSW_DDS_NEXT_REQID(vsharep)	(++vsharep->vs_req_id)
     76 
     77 extern boolean_t vsw_hio_enabled;		/* HybridIO enabled? */
     78 extern int vsw_hio_max_cleanup_retries;
     79 extern int vsw_hio_cleanup_delay;
     80 
     81 /* Functions imported from other files */
     82 extern int vsw_send_msg(vsw_ldc_t *, void *, int, boolean_t);
     83 extern void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate);
     84 extern void vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans,
     85     uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids);
     86 
     87 /* Functions exported to other files */
     88 void vsw_hio_init(vsw_t *vswp);
     89 void vsw_hio_cleanup(vsw_t *vswp);
     90 void vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp);
     91 void vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp);
     92 void vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg);
     93 void vsw_hio_start_ports(vsw_t *vswp);
     94 void vsw_hio_stop_port(vsw_port_t *portp);
     95 
     96 /* Support functions */
     97 static void vsw_hio_free_all_shares(vsw_t *vswp, boolean_t reboot);
     98 static vsw_share_t *vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp);
     99 static void vsw_hio_free_share(vsw_share_t *vsharep);
    100 static vsw_share_t *vsw_hio_find_free_share(vsw_t *vswp);
    101 static vsw_share_t *vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id);
    102 static vsw_share_t *vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp);
    103 static int vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass,
    104     uint64_t cookie, uint64_t macaddr, uint32_t req_id);
    105 static int vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack);
    106 static int vsw_hio_send_delshare_msg(vsw_share_t *vsharep);
    107 static boolean_t vsw_hio_reboot_callb(void *arg, int code);
    108 static boolean_t vsw_hio_panic_callb(void *arg, int code);
    109 
    110 /*
    111  * Locking strategy for HybridIO is followed as below:
    112  *
    113  *	- As the Shares are associated with a network device, the
    114  *	  the global lock('vswp>mac_lock') is used for all Shares
    115  *	  related operations.
    116  *	- The 'port->maccl_rwlock' is used to synchronize only the
    117  *	  the operations that operate on that port's mac client. That
    118  *	  is, the share_bind and unbind operations only.
    119  *
    120  *	- The locking hierarchy follows that the global mac_lock is
    121  *	  acquired first and then the ports mac client lock(maccl_rwlock)
    122  */
    123 
    124 
    125 static kstat_t *vsw_hio_setup_kstats(char *ks_mod, char *ks_name, vsw_t *vswp);
    126 static void vsw_hio_destroy_kstats(vsw_t *vswp);
    127 static int vsw_hio_kstats_update(kstat_t *ksp, int rw);
    128 
    129 /*
    130  * vsw_hio_init -- Initialize the HybridIO related info.
    131  *	- Query SHARES and RINGS capability. Both capabilities
    132  *	  need to be supported by the physical-device.
    133  */
    134 void
    135 vsw_hio_init(vsw_t *vswp)
    136 {
    137 	vsw_hio_t	*hiop = &vswp->vhio;
    138 	int		num_shares;
    139 	int		i;
    140 
    141 	ASSERT(MUTEX_HELD(&vswp->mac_lock));
    142 	D1(vswp, "%s:enter\n", __func__);
    143 	if (vsw_hio_enabled == B_FALSE) {
    144 		return;
    145 	}
    146 
    147 	vswp->hio_capable = B_FALSE;
    148 	num_shares = mac_share_capable(vswp->mh);
    149 	if (num_shares == 0) {
    150 		D2(vswp, "%s: %s is not HybridIO capable\n", __func__,
    151 		    vswp->physname);
    152 		return;
    153 	}
    154 	hiop->vh_num_shares = num_shares;
    155 	hiop->vh_shares = kmem_zalloc((sizeof (vsw_share_t) *
    156 	    hiop->vh_num_shares), KM_SLEEP);
    157 	for (i = 0; i < hiop->vh_num_shares; i++) {
    158 		hiop->vh_shares[i].vs_state = VSW_SHARE_FREE;
    159 		hiop->vh_shares[i].vs_index = i;
    160 		hiop->vh_shares[i].vs_vswp = vswp;
    161 	}
    162 	vswp->hio_capable = B_TRUE;
    163 
    164 	/*
    165 	 * Register to get reboot and panic events so that
    166 	 * we can cleanup HybridIO resources gracefully.
    167 	 */
    168 	vswp->hio_reboot_cb_id = callb_add(vsw_hio_reboot_callb,
    169 	    (void *)vswp, CB_CL_MDBOOT, "vsw_hio");
    170 
    171 	vswp->hio_panic_cb_id = callb_add(vsw_hio_panic_callb,
    172 	    (void *)vswp, CB_CL_PANIC, "vsw_hio");
    173 
    174 	/* setup kstats for hybrid resources */
    175 	hiop->vh_ksp = vsw_hio_setup_kstats(DRV_NAME, "hio", vswp);
    176 	if (hiop->vh_ksp == NULL) {
    177 		DERR(vswp, "%s: kstats setup failed", __func__);
    178 	}
    179 
    180 	D2(vswp, "%s: %s is HybridIO capable num_shares=%d\n", __func__,
    181 	    vswp->physname, hiop->vh_num_shares);
    182 	D1(vswp, "%s:exit\n", __func__);
    183 }
    184 
    185 /*
    186  * vsw_hio_alloc_share -- Allocate and setup the share for a guest domain.
    187  *	- Allocate a free share.
    188  *	- Bind the Guest's MAC address.
    189  */
    190 static vsw_share_t *
    191 vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp)
    192 {
    193 	vsw_share_t	*vsharep;
    194 	vsw_port_t	*portp = ldcp->ldc_port;
    195 	uint64_t	ldc_id = ldcp->ldc_id;
    196 	int		rv;
    197 
    198 	D1(vswp, "%s:enter\n", __func__);
    199 	vsharep = vsw_hio_find_free_share(vswp);
    200 	if (vsharep == NULL) {
    201 		/* No free shares available */
    202 		return (NULL);
    203 	}
    204 
    205 	WRITE_ENTER(&portp->maccl_rwlock);
    206 	rv = mac_share_bind(portp->p_mch, ldc_id, &vsharep->vs_cookie);
    207 	RW_EXIT(&portp->maccl_rwlock);
    208 	if (rv != 0) {
    209 		return (NULL);
    210 	}
    211 
    212 	/* Cache some useful info */
    213 	vsharep->vs_ldcid = ldcp->ldc_id;
    214 	vsharep->vs_macaddr = vnet_macaddr_strtoul(
    215 	    portp->p_macaddr.ether_addr_octet);
    216 	vsharep->vs_portp = ldcp->ldc_port;
    217 	vsharep->vs_state |= VSW_SHARE_ASSIGNED;
    218 
    219 	D1(vswp, "%s:exit\n", __func__);
    220 	return (vsharep);
    221 }
    222 
    223 /*
    224  * vsw_hio_find_free_share -- Find a free Share.
    225  */
    226 static vsw_share_t *
    227 vsw_hio_find_free_share(vsw_t *vswp)
    228 {
    229 	vsw_hio_t *hiop = &vswp->vhio;
    230 	vsw_share_t *vsharep;
    231 	int i;
    232 
    233 	D1(vswp, "%s:enter\n", __func__);
    234 	for (i = 0; i < hiop->vh_num_shares; i++) {
    235 		vsharep = &hiop->vh_shares[i];
    236 		if (vsharep->vs_state == VSW_SHARE_FREE) {
    237 			D1(vswp, "%s:Returning free share(%d)\n",
    238 			    __func__, vsharep->vs_index);
    239 			return (vsharep);
    240 		}
    241 	}
    242 	D1(vswp, "%s:no free share\n", __func__);
    243 	return (NULL);
    244 }
    245 
    246 /*
    247  * vsw_hio_find_vshare_ldcid -- Given ldc_id, find the corresponding
    248  *	share structure.
    249  */
    250 static vsw_share_t *
    251 vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id)
    252 {
    253 	vsw_hio_t *hiop = &vswp->vhio;
    254 	vsw_share_t *vsharep;
    255 	int i;
    256 
    257 	D1(vswp, "%s:enter, ldc=0x%lx", __func__, ldc_id);
    258 	for (i = 0; i < hiop->vh_num_shares; i++) {
    259 		vsharep = &hiop->vh_shares[i];
    260 		if (vsharep->vs_state == VSW_SHARE_FREE) {
    261 			continue;
    262 		}
    263 		if (vsharep->vs_ldcid == ldc_id) {
    264 			D1(vswp, "%s:returning share(%d)",
    265 			    __func__, vsharep->vs_index);
    266 			return (vsharep);
    267 		}
    268 	}
    269 	D1(vswp, "%s:returning NULL", __func__);
    270 	return (NULL);
    271 }
    272 
    273 /*
    274  * vsw_hio_find_vshare_port -- Given portp, find the corresponding
    275  *	share structure.
    276  */
    277 static vsw_share_t *
    278 vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp)
    279 {
    280 	vsw_hio_t *hiop = &vswp->vhio;
    281 	vsw_share_t *vsharep;
    282 	int i;
    283 
    284 	D1(vswp, "%s:enter, portp=0x%p", __func__, portp);
    285 	for (i = 0; i < hiop->vh_num_shares; i++) {
    286 		vsharep = &hiop->vh_shares[i];
    287 		if (vsharep->vs_state == VSW_SHARE_FREE) {
    288 			continue;
    289 		}
    290 		if (vsharep->vs_portp == portp) {
    291 			D1(vswp, "%s:returning share(%d)",
    292 			    __func__, vsharep->vs_index);
    293 			return (vsharep);
    294 		}
    295 	}
    296 	D1(vswp, "%s:returning NULL", __func__);
    297 	return (NULL);
    298 }
    299 
    300 /*
    301  * vsw_hio_free_share -- Unbind the MAC address and free share.
    302  */
    303 static void
    304 vsw_hio_free_share(vsw_share_t *vsharep)
    305 {
    306 	vsw_t		*vswp = vsharep->vs_vswp;
    307 	vsw_port_t	*portp = vsharep->vs_portp;
    308 
    309 	D1(vswp, "%s:enter\n", __func__);
    310 
    311 	WRITE_ENTER(&portp->maccl_rwlock);
    312 	mac_share_unbind(portp->p_mch);
    313 	RW_EXIT(&portp->maccl_rwlock);
    314 	vsharep->vs_state = VSW_SHARE_FREE;
    315 	vsharep->vs_macaddr = 0;
    316 	vsharep->vs_portp = NULL;
    317 
    318 	/* DERR only for printing by default */
    319 	DERR(vswp, "Share freed for ldc_id=0x%lx Cookie=0x%lX",
    320 	    vsharep->vs_ldcid, vsharep->vs_cookie);
    321 	D1(vswp, "%s:exit\n", __func__);
    322 }
    323 
    324 
    325 /*
    326  * vsw_hio_cleanup -- Cleanup the HybridIO. It unregisters the callbs
    327  *	and frees all shares.
    328  */
    329 void
    330 vsw_hio_cleanup(vsw_t *vswp)
    331 {
    332 	D1(vswp, "%s:enter\n", __func__);
    333 
    334 	/* Unregister reboot and panic callbs. */
    335 	if (vswp->hio_reboot_cb_id) {
    336 		(void) callb_delete(vswp->hio_reboot_cb_id);
    337 		vswp->hio_reboot_cb_id = 0;
    338 	}
    339 	if (vswp->hio_panic_cb_id) {
    340 		(void) callb_delete(vswp->hio_panic_cb_id);
    341 		vswp->hio_panic_cb_id = 0;
    342 	}
    343 	vsw_hio_free_all_shares(vswp, B_FALSE);
    344 	vsw_hio_destroy_kstats(vswp);
    345 	D1(vswp, "%s:exit\n", __func__);
    346 }
    347 
    348 /*
    349  * vsw_hio_free_all_shares -- A routine to free all shares gracefully.
    350  *	The following are the steps followed to accomplish this:
    351  *
    352  *	- First clear 'hio_capable' to avoid further share allocations.
    353  *	- If a share is in accepted(ACKD) state, that means the guest
    354  *	  has HybridIO setup etc. If so, send a DEL_SHARE message and
    355  *	  give some time(delay) for the guest to ACK.
    356  *	- If the Share is another state, give some time to transition to
    357  *	  ACKD state, then try the above.
    358  *	- After max retries, reset the ports to brute force the shares
    359  *	  to be freed. Give a little delay for the LDC reset code to
    360  *	  free the Share.
    361  */
    362 static void
    363 vsw_hio_free_all_shares(vsw_t *vswp, boolean_t reboot)
    364 {
    365 	vsw_hio_t	*hiop = &vswp->vhio;
    366 	vsw_port_list_t	*plist = &vswp->plist;
    367 	vsw_share_t	*vsharep;
    368 	int		free_shares = 0;
    369 	int		max_retries = vsw_hio_max_cleanup_retries;
    370 	int		i;
    371 
    372 	D1(vswp, "%s:enter\n", __func__);
    373 
    374 	/*
    375 	 * Acquire plist->lockrw to make the locking a bit easier
    376 	 * and keep the ports in a stable state while we are cleaningup
    377 	 * HybridIO.
    378 	 */
    379 	READ_ENTER(&plist->lockrw);
    380 	mutex_enter(&vswp->mac_lock);
    381 	/*
    382 	 * first clear the hio_capable flag so that no more
    383 	 * HybridIO operations are initiated.
    384 	 */
    385 	vswp->hio_capable = B_FALSE;
    386 
    387 	do {
    388 		free_shares = 0;
    389 		for (i = 0; i < hiop->vh_num_shares; i++) {
    390 			vsharep = &hiop->vh_shares[i];
    391 			if (vsharep->vs_state == VSW_SHARE_FREE) {
    392 				free_shares++;
    393 				continue;
    394 			}
    395 			/*
    396 			 * If the share is in DDS_ACKD state, then
    397 			 * send DEL_SHARE message so that guest can
    398 			 * release its Hybrid resource.
    399 			 */
    400 			if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) {
    401 				int rv;
    402 
    403 				/* send DDS_DEL_SHARE */
    404 				D1(vswp, "%s:sending DEL_SHARE msg for "
    405 				    "share(%d)", __func__, vsharep->vs_index);
    406 				rv = vsw_hio_send_delshare_msg(vsharep);
    407 				if (rv != 0) {
    408 					/*
    409 					 * No alternative, reset the port
    410 					 * to force the release of Hybrid
    411 					 * resources.
    412 					 */
    413 					vsw_hio_port_reset(vsharep->vs_portp,
    414 					    B_FALSE);
    415 				}
    416 			}
    417 			if (max_retries == 1) {
    418 				/*
    419 				 * Last retry,  reset the port.
    420 				 * If it is reboot case, issue an immediate
    421 				 * reset.
    422 				 */
    423 				DWARN(vswp, "%s:All retries failed, "
    424 				    " cause a reset to trigger cleanup for "
    425 				    "share(%d)", __func__, vsharep->vs_index);
    426 				vsw_hio_port_reset(vsharep->vs_portp, reboot);
    427 			}
    428 		}
    429 		if (free_shares == hiop->vh_num_shares) {
    430 			/* Clean up is done */
    431 			break;
    432 		}
    433 		/*
    434 		 * Release the lock so that reply for DEL_SHARE
    435 		 * messages come and get processed, that is, shares
    436 		 * get freed.
    437 		 * This delay is also needed for the port reset to
    438 		 * release the Hybrid resource.
    439 		 */
    440 		mutex_exit(&vswp->mac_lock);
    441 		drv_usecwait(vsw_hio_cleanup_delay);
    442 		mutex_enter(&vswp->mac_lock);
    443 		max_retries--;
    444 	} while ((free_shares < hiop->vh_num_shares) && (max_retries > 0));
    445 
    446 	/* By now, all shares should be freed */
    447 	if (free_shares != hiop->vh_num_shares) {
    448 		if (reboot == B_FALSE) {
    449 			cmn_err(CE_NOTE, "vsw%d: All physical resources "
    450 			    "could not be freed", vswp->instance);
    451 		}
    452 	}
    453 
    454 	kmem_free(hiop->vh_shares, sizeof (vsw_share_t) * hiop->vh_num_shares);
    455 	hiop->vh_shares = NULL;
    456 	hiop->vh_num_shares = 0;
    457 	mutex_exit(&vswp->mac_lock);
    458 	RW_EXIT(&plist->lockrw);
    459 	D1(vswp, "%s:exit\n", __func__);
    460 }
    461 
    462 /*
    463  * vsw_hio_start_ports -- Start HybridIO for ports that have
    464  *	already established connection before HybridIO is intialized.
    465  */
    466 void
    467 vsw_hio_start_ports(vsw_t *vswp)
    468 {
    469 	vsw_port_list_t	*plist = &vswp->plist;
    470 	vsw_port_t	*portp;
    471 	vsw_share_t	*vsharep;
    472 	boolean_t	reset;
    473 
    474 	if (vswp->hio_capable == B_FALSE) {
    475 		return;
    476 	}
    477 	READ_ENTER(&plist->lockrw);
    478 	for (portp = plist->head; portp != NULL; portp = portp->p_next) {
    479 		if ((portp->p_hio_enabled == B_FALSE) ||
    480 		    (portp->p_hio_capable == B_FALSE)) {
    481 			continue;
    482 		}
    483 
    484 		reset = B_FALSE;
    485 		mutex_enter(&vswp->mac_lock);
    486 		vsharep = vsw_hio_find_vshare_port(vswp, portp);
    487 		if (vsharep == NULL) {
    488 			reset = B_TRUE;
    489 		}
    490 		mutex_exit(&vswp->mac_lock);
    491 
    492 		if (reset == B_TRUE) {
    493 			/* Cause a rest to trigger HybridIO setup */
    494 			vsw_hio_port_reset(portp, B_FALSE);
    495 		}
    496 	}
    497 	RW_EXIT(&plist->lockrw);
    498 }
    499 
    500 /*
    501  * vsw_hio_start -- Start HybridIO for a guest(given LDC)
    502  */
    503 void
    504 vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp)
    505 {
    506 	vsw_share_t	*vsharep;
    507 	uint32_t	req_id;
    508 	int		rv;
    509 
    510 	D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id);
    511 	mutex_enter(&vswp->mac_lock);
    512 	if (vswp->hio_capable == B_FALSE) {
    513 		mutex_exit(&vswp->mac_lock);
    514 		D2(vswp, "%s:not HIO capable", __func__);
    515 		return;
    516 	}
    517 
    518 	/* Verify if a share was already allocated */
    519 	vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
    520 	if (vsharep != NULL) {
    521 		mutex_exit(&vswp->mac_lock);
    522 		D2(vswp, "%s:Share already allocated to ldc=0x%lx",
    523 		    __func__, ldcp->ldc_id);
    524 		return;
    525 	}
    526 	vsharep = vsw_hio_alloc_share(vswp, ldcp);
    527 	if (vsharep == NULL) {
    528 		mutex_exit(&vswp->mac_lock);
    529 		D2(vswp, "%s: no Share available for ldc=0x%lx",
    530 		    __func__, ldcp->ldc_id);
    531 		return;
    532 	}
    533 	req_id = VSW_DDS_NEXT_REQID(vsharep);
    534 	rv = vsw_send_dds_msg(ldcp, DDS_VNET_ADD_SHARE, vsharep->vs_cookie,
    535 	    vsharep->vs_macaddr, req_id);
    536 	if (rv != 0) {
    537 		/*
    538 		 * Failed to send a DDS message, so cleanup now.
    539 		 */
    540 		vsw_hio_free_share(vsharep);
    541 		mutex_exit(&vswp->mac_lock);
    542 		return;
    543 	}
    544 	vsharep->vs_state &= ~VSW_SHARE_DDS_ACKD;
    545 	vsharep->vs_state |= VSW_SHARE_DDS_SENT;
    546 	mutex_exit(&vswp->mac_lock);
    547 
    548 	/* DERR only to print by default */
    549 	DERR(vswp, "Share allocated for ldc_id=0x%lx Cookie=0x%lX",
    550 	    ldcp->ldc_id, vsharep->vs_cookie);
    551 
    552 	D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id);
    553 }
    554 
    555 /*
    556  * vsw_hio_stop -- Stop/clean the HybridIO config for a guest(given ldc).
    557  */
    558 void
    559 vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp)
    560 {
    561 	vsw_share_t *vsharep;
    562 
    563 	D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id);
    564 
    565 	mutex_enter(&vswp->mac_lock);
    566 	vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
    567 	if (vsharep == NULL) {
    568 		D1(vswp, "%s:no share found for ldc=0x%lx",
    569 		    __func__, ldcp->ldc_id);
    570 		mutex_exit(&vswp->mac_lock);
    571 		return;
    572 	}
    573 	vsw_hio_free_share(vsharep);
    574 	mutex_exit(&vswp->mac_lock);
    575 
    576 	D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id);
    577 }
    578 
    579 /*
    580  * vsw_hio_send_delshare_msg -- Send a DEL_SHARE message to the	guest.
    581  */
    582 static int
    583 vsw_hio_send_delshare_msg(vsw_share_t *vsharep)
    584 {
    585 	vsw_t *vswp = vsharep->vs_vswp;
    586 	vsw_port_t *portp;
    587 	vsw_ldc_list_t	*ldcl;
    588 	vsw_ldc_t	*ldcp;
    589 	uint32_t	req_id;
    590 	uint64_t	cookie = vsharep->vs_cookie;
    591 	uint64_t	macaddr = vsharep->vs_macaddr;
    592 	int		rv;
    593 
    594 	ASSERT(MUTEX_HELD(&vswp->mac_lock));
    595 	mutex_exit(&vswp->mac_lock);
    596 
    597 	portp = vsharep->vs_portp;
    598 	if (portp == NULL) {
    599 		mutex_enter(&vswp->mac_lock);
    600 		return (0);
    601 	}
    602 
    603 	ldcl = &portp->p_ldclist;
    604 	READ_ENTER(&ldcl->lockrw);
    605 	ldcp = ldcl->head;
    606 	if ((ldcp == NULL) || (ldcp->ldc_id != vsharep->vs_ldcid)) {
    607 		RW_EXIT(&ldcl->lockrw);
    608 		mutex_enter(&vswp->mac_lock);
    609 		return (0);
    610 	}
    611 	req_id = VSW_DDS_NEXT_REQID(vsharep);
    612 	rv = vsw_send_dds_msg(ldcp, DDS_VNET_DEL_SHARE,
    613 	    cookie, macaddr, req_id);
    614 
    615 	RW_EXIT(&ldcl->lockrw);
    616 	mutex_enter(&vswp->mac_lock);
    617 	if (rv == 0) {
    618 		vsharep->vs_state &= ~VSW_SHARE_DDS_ACKD;
    619 		vsharep->vs_state |= VSW_SHARE_DDS_SENT;
    620 	}
    621 	return (rv);
    622 }
    623 
    624 /*
    625  * vsw_send_dds_msg -- Send a DDS message.
    626  */
    627 static int
    628 vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass, uint64_t
    629     cookie, uint64_t macaddr, uint32_t req_id)
    630 {
    631 	vsw_t *vswp = ldcp->ldc_port->p_vswp;
    632 	vio_dds_msg_t	vmsg;
    633 	dds_share_msg_t	*smsg = &vmsg.msg.share_msg;
    634 	int rv;
    635 
    636 	D1(vswp, "%s:enter\n", __func__);
    637 	vmsg.tag.vio_msgtype = VIO_TYPE_CTRL;
    638 	vmsg.tag.vio_subtype = VIO_SUBTYPE_INFO;
    639 	vmsg.tag.vio_subtype_env = VIO_DDS_INFO;
    640 	vmsg.tag.vio_sid = ldcp->local_session;
    641 	vmsg.dds_class = DDS_VNET_NIU;
    642 	vmsg.dds_subclass = dds_subclass;
    643 	vmsg.dds_req_id = req_id;
    644 	smsg->macaddr = macaddr;
    645 	smsg->cookie = cookie;
    646 	rv = vsw_send_msg(ldcp, &vmsg, sizeof (vmsg), B_FALSE);
    647 	D1(vswp, "%s:exit rv=%d\n", __func__, rv);
    648 	return (rv);
    649 }
    650 
    651 /*
    652  * vsw_process_dds_msg -- Process a DDS message received from a guest.
    653  */
    654 void
    655 vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg)
    656 {
    657 	vsw_share_t	*vsharep;
    658 	vio_dds_msg_t	*dmsg = msg;
    659 
    660 	D1(vswp, "%s:enter ldc=0x%lx\n", __func__, ldcp->ldc_id);
    661 	if (dmsg->dds_class != DDS_VNET_NIU) {
    662 		/* discard */
    663 		return;
    664 	}
    665 	mutex_enter(&vswp->mac_lock);
    666 	/*
    667 	 * We expect to receive DDS messages only from guests that
    668 	 * have HybridIO started.
    669 	 */
    670 	vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
    671 	if (vsharep == NULL) {
    672 		mutex_exit(&vswp->mac_lock);
    673 		return;
    674 	}
    675 
    676 	switch (dmsg->dds_subclass) {
    677 	case DDS_VNET_ADD_SHARE:
    678 		/* A response for ADD_SHARE message. */
    679 		D1(vswp, "%s:DDS_VNET_ADD_SHARE\n", __func__);
    680 		if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) {
    681 			DWARN(vswp, "%s: invalid ADD_SHARE response  message "
    682 			    " share state=0x%X", __func__, vsharep->vs_state);
    683 			break;
    684 		}
    685 
    686 		if (dmsg->dds_req_id != vsharep->vs_req_id) {
    687 			DWARN(vswp, "%s: invalid req_id in ADD_SHARE response"
    688 			    " message req_id=0x%X share's req_id=0x%X",
    689 			    __func__, dmsg->dds_req_id, vsharep->vs_req_id);
    690 			break;
    691 		}
    692 
    693 		if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) {
    694 			DWARN(vswp, "%s: NACK received for ADD_SHARE"
    695 			    " message ldcid=0x%lx", __func__, ldcp->ldc_id);
    696 			/* cleanup for NACK */
    697 			vsw_hio_free_share(vsharep);
    698 		} else {
    699 			D2(vswp, "%s: ACK received for ADD_SHARE", __func__);
    700 			vsharep->vs_state &= ~VSW_SHARE_DDS_SENT;
    701 			vsharep->vs_state |= VSW_SHARE_DDS_ACKD;
    702 		}
    703 		break;
    704 
    705 	case DDS_VNET_DEL_SHARE:
    706 		/* A response for DEL_SHARE message */
    707 		D1(vswp, "%s:DDS_VNET_DEL_SHARE\n", __func__);
    708 		if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) {
    709 			DWARN(vswp, "%s: invalid DEL_SHARE response message "
    710 			    " share state=0x%X", __func__, vsharep->vs_state);
    711 			break;
    712 		}
    713 
    714 		if (dmsg->dds_req_id != vsharep->vs_req_id) {
    715 			DWARN(vswp, "%s: invalid req_id in DEL_SHARE response"
    716 			    " message share req_id=0x%X share's req_id=0x%X",
    717 			    __func__, dmsg->dds_req_id, vsharep->vs_req_id);
    718 			break;
    719 		}
    720 		if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) {
    721 			DWARN(vswp, "%s: NACK received for DEL_SHARE",
    722 			    __func__);
    723 		}
    724 
    725 		/* There is nothing we can do, free share now */
    726 		vsw_hio_free_share(vsharep);
    727 		break;
    728 
    729 	case DDS_VNET_REL_SHARE:
    730 		/* Guest has released Share voluntarily, so free it now */
    731 		D1(vswp, "%s:DDS_VNET_REL_SHARE\n", __func__);
    732 		/* send ACK */
    733 		(void) vsw_send_dds_resp_msg(ldcp, dmsg, B_FALSE);
    734 		vsw_hio_free_share(vsharep);
    735 		break;
    736 	default:
    737 		DERR(vswp, "%s: Invalid DDS message type=0x%X",
    738 		    __func__, dmsg->dds_subclass);
    739 		break;
    740 	}
    741 	mutex_exit(&vswp->mac_lock);
    742 	D1(vswp, "%s:exit ldc=0x%lx\n", __func__, ldcp->ldc_id);
    743 }
    744 
    745 /*
    746  * vsw_send_dds_resp_msg -- Send a DDS response message.
    747  */
    748 static int
    749 vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack)
    750 {
    751 	vsw_t	*vswp = ldcp->ldc_port->p_vswp;
    752 	int	rv;
    753 
    754 	D1(vswp, "%s:enter\n", __func__);
    755 	if (ack == B_TRUE) {
    756 		dmsg->tag.vio_subtype = VIO_SUBTYPE_ACK;
    757 		dmsg->msg.share_resp_msg.status = DDS_VNET_SUCCESS;
    758 	} else {
    759 		dmsg->tag.vio_subtype = VIO_SUBTYPE_NACK;
    760 		dmsg->msg.share_resp_msg.status = DDS_VNET_FAIL;
    761 	}
    762 	rv = vsw_send_msg(ldcp, dmsg, sizeof (vio_dds_msg_t), B_FALSE);
    763 	D1(vswp, "%s:exit rv=%d\n", __func__, rv);
    764 	return (rv);
    765 }
    766 
    767 /*
    768  * vsw_hio_port_update -- update Hybrid mode change for a port.
    769  */
    770 void
    771 vsw_hio_port_update(vsw_port_t *portp, boolean_t hio_enabled)
    772 {
    773 	/* Verify if the mode really changed */
    774 	if (portp->p_hio_enabled == hio_enabled) {
    775 		return;
    776 	}
    777 
    778 	if (hio_enabled == B_FALSE) {
    779 		/* Hybrid Mode is disabled, so stop HybridIO */
    780 		vsw_hio_stop_port(portp);
    781 		portp->p_hio_enabled = B_FALSE;
    782 
    783 		vsw_port_mac_reconfig(portp, B_FALSE, 0, NULL, 0);
    784 	} else {
    785 		portp->p_hio_enabled =  B_TRUE;
    786 		vsw_port_mac_reconfig(portp, B_FALSE, 0, NULL, 0);
    787 
    788 		/* reset the port to initiate HybridIO setup */
    789 		vsw_hio_port_reset(portp, B_FALSE);
    790 	}
    791 }
    792 
    793 /*
    794  * vsw_hio_stop_port -- Stop HybridIO for a given port. Sequence
    795  *	followed is similar to vsw_hio_free_all_shares().
    796  *
    797  */
    798 void
    799 vsw_hio_stop_port(vsw_port_t *portp)
    800 {
    801 	vsw_t *vswp = portp->p_vswp;
    802 	vsw_share_t *vsharep;
    803 	int max_retries = vsw_hio_max_cleanup_retries;
    804 
    805 	D1(vswp, "%s:enter\n", __func__);
    806 	mutex_enter(&vswp->mac_lock);
    807 
    808 	if (vswp->hio_capable == B_FALSE) {
    809 		mutex_exit(&vswp->mac_lock);
    810 		return;
    811 	}
    812 
    813 	vsharep = vsw_hio_find_vshare_port(vswp, portp);
    814 	if (vsharep == NULL) {
    815 		mutex_exit(&vswp->mac_lock);
    816 		return;
    817 	}
    818 
    819 	do {
    820 		if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) {
    821 			int rv;
    822 
    823 			/* send DDS_DEL_SHARE */
    824 			D1(vswp, "%s:sending DEL_SHARE msg for "
    825 			    "share(%d)", __func__, vsharep->vs_index);
    826 			rv = vsw_hio_send_delshare_msg(vsharep);
    827 			if (rv != 0) {
    828 				/*
    829 				 * Cause a port reset to trigger
    830 				 * cleanup.
    831 				 */
    832 				vsw_hio_port_reset(vsharep->vs_portp, B_FALSE);
    833 			}
    834 		}
    835 		if (max_retries == 1) {
    836 			/* last retry */
    837 			DWARN(vswp, "%s:All retries failed, "
    838 			    " cause a reset to trigger cleanup for "
    839 			    "share(%d)", __func__, vsharep->vs_index);
    840 			vsw_hio_port_reset(vsharep->vs_portp, B_FALSE);
    841 		}
    842 
    843 		/* Check if the share still assigned to this port */
    844 		if ((vsharep->vs_portp != portp) ||
    845 		    (vsharep->vs_state == VSW_SHARE_FREE)) {
    846 			break;
    847 		}
    848 
    849 		/*
    850 		 * Release the lock so that reply for DEL_SHARE
    851 		 * messages come and get processed, that is, shares
    852 		 * get freed.
    853 		 */
    854 		mutex_exit(&vswp->mac_lock);
    855 		drv_usecwait(vsw_hio_cleanup_delay);
    856 		mutex_enter(&vswp->mac_lock);
    857 
    858 		/* Check if the share still assigned to this port */
    859 		if ((vsharep->vs_portp != portp) ||
    860 		    (vsharep->vs_state == VSW_SHARE_FREE)) {
    861 			break;
    862 		}
    863 		max_retries--;
    864 	} while ((vsharep->vs_state != VSW_SHARE_FREE) && (max_retries > 0));
    865 
    866 	mutex_exit(&vswp->mac_lock);
    867 	D1(vswp, "%s:exit\n", __func__);
    868 }
    869 
    870 /*
    871  * vsw_hio_rest_all -- Resets all ports that have shares allocated.
    872  *	It is called only in the panic code path, so the LDC channels
    873  *	are reset immediately.
    874  */
    875 static void
    876 vsw_hio_reset_all(vsw_t *vswp)
    877 {
    878 	vsw_hio_t	*hiop = &vswp->vhio;
    879 	vsw_share_t	*vsharep;
    880 	int		i;
    881 
    882 	D1(vswp, "%s:enter\n", __func__);
    883 
    884 	if (vswp->hio_capable != B_TRUE)
    885 		return;
    886 
    887 	for (i = 0; i < hiop->vh_num_shares; i++) {
    888 		vsharep = &hiop->vh_shares[i];
    889 		if (vsharep->vs_state == VSW_SHARE_FREE) {
    890 			continue;
    891 		}
    892 		/*
    893 		 * Reset the port with immediate flag enabled,
    894 		 * to cause LDC reset immediately.
    895 		 */
    896 		vsw_hio_port_reset(vsharep->vs_portp, B_TRUE);
    897 	}
    898 	D1(vswp, "%s:exit\n", __func__);
    899 }
    900 
    901 /*
    902  * vsw_hio_reboot_callb -- Called for reboot event. It tries to
    903  *	free all currently allocated shares.
    904  */
    905 /* ARGSUSED */
    906 static boolean_t
    907 vsw_hio_reboot_callb(void *arg, int code)
    908 {
    909 	vsw_t *vswp = arg;
    910 
    911 	D1(vswp, "%s:enter\n", __func__);
    912 	vsw_hio_free_all_shares(vswp, B_TRUE);
    913 	D1(vswp, "%s:exit\n", __func__);
    914 	return (B_TRUE);
    915 }
    916 
    917 /*
    918  * vsw_hio_panic_callb -- Called from panic event. It resets all
    919  *	the ports that have shares allocated. This is done to
    920  *	trigger the cleanup in the guest ahead of HV reset.
    921  */
    922 /* ARGSUSED */
    923 static boolean_t
    924 vsw_hio_panic_callb(void *arg, int code)
    925 {
    926 	vsw_t *vswp = arg;
    927 
    928 	D1(vswp, "%s:enter\n", __func__);
    929 	vsw_hio_reset_all(vswp);
    930 	D1(vswp, "%s:exit\n", __func__);
    931 	return (B_TRUE);
    932 }
    933 
    934 /*
    935  * Setup kstats for hio statistics.
    936  */
    937 static kstat_t *
    938 vsw_hio_setup_kstats(char *ks_mod, char *ks_name, vsw_t *vswp)
    939 {
    940 	kstat_t			*ksp;
    941 	vsw_hio_kstats_t	*hiokp;
    942 	vsw_hio_t		*hiop;
    943 	char			share_assigned_info[MAXNAMELEN];
    944 	size_t			size;
    945 	int			i;
    946 
    947 	hiop = &vswp->vhio;
    948 	/*
    949 	 * vsw_hio_stats_t structure is variable size structure
    950 	 * having fields defined only for one share. So, we need
    951 	 * allocate additional space for the rest of the shares.
    952 	 */
    953 	size = sizeof (vsw_hio_kstats_t) / sizeof (kstat_named_t);
    954 	ASSERT(hiop->vh_num_shares >= 1);
    955 	size += ((hiop->vh_num_shares - 1) * 2);
    956 
    957 	ksp = kstat_create(ks_mod, vswp->instance, ks_name, "misc",
    958 	    KSTAT_TYPE_NAMED, size, KSTAT_FLAG_VIRTUAL);
    959 
    960 	if (ksp == NULL) {
    961 		return (NULL);
    962 	}
    963 	hiokp = (vsw_hio_kstats_t *)kmem_zalloc(sizeof (kstat_named_t) *
    964 	    size, KM_SLEEP);
    965 	ksp->ks_data = hiokp;
    966 
    967 	hiop->vh_ksp = ksp;
    968 	hiop->vh_kstatsp = hiokp;
    969 	hiop->vh_kstat_size =  size;
    970 
    971 	kstat_named_init(&hiokp->hio_capable, "hio_capable", KSTAT_DATA_CHAR);
    972 	kstat_named_init(&hiokp->hio_num_shares, "hio_num_shares",
    973 	    KSTAT_DATA_ULONG);
    974 
    975 	for (i = 0; i < hiop->vh_num_shares; i++) {
    976 		(void) sprintf(share_assigned_info, "%s%d", "hio_share_", i);
    977 		kstat_named_init(&(hiokp->share[i].assigned),
    978 		    share_assigned_info, KSTAT_DATA_ULONG);
    979 
    980 		(void) sprintf(share_assigned_info, "%s%d%s",
    981 		    "hio_share_", i, "_state");
    982 		kstat_named_init(&(hiokp->share[i].state),
    983 		    share_assigned_info, KSTAT_DATA_ULONG);
    984 	}
    985 
    986 	ksp->ks_update = vsw_hio_kstats_update;
    987 	ksp->ks_private = (void *)vswp;
    988 	kstat_install(ksp);
    989 	return (ksp);
    990 }
    991 
    992 /*
    993  * Destroy hio kstats.
    994  */
    995 static void
    996 vsw_hio_destroy_kstats(vsw_t *vswp)
    997 {
    998 	kstat_t			*ksp;
    999 	vsw_hio_t		*hiop;
   1000 
   1001 	ASSERT(vswp != NULL);
   1002 
   1003 	ksp = vswp->vhio.vh_ksp;
   1004 	hiop = &vswp->vhio;
   1005 	if (ksp != NULL) {
   1006 		kmem_free(hiop->vh_kstatsp, sizeof (kstat_named_t) *
   1007 		    hiop->vh_kstat_size);
   1008 		kstat_delete(ksp);
   1009 		hiop->vh_kstatsp = NULL;
   1010 		hiop->vh_ksp = NULL;
   1011 	}
   1012 }
   1013 
   1014 /*
   1015  * Update hio kstats.
   1016  */
   1017 static int
   1018 vsw_hio_kstats_update(kstat_t *ksp, int rw)
   1019 {
   1020 	vsw_t			*vswp;
   1021 	vsw_hio_t		*hiop;
   1022 	vsw_hio_kstats_t	*hiokp;
   1023 	int			i;
   1024 
   1025 	vswp = (vsw_t *)ksp->ks_private;
   1026 	ASSERT(vswp != NULL);
   1027 
   1028 	hiop = &vswp->vhio;
   1029 	hiokp = hiop->vh_kstatsp;
   1030 
   1031 	if (rw == KSTAT_READ) {
   1032 		if (vswp->hio_capable) {
   1033 			(void) strcpy(hiokp->hio_capable.value.c, "Yes");
   1034 		} else {
   1035 			/* not hio capable, just return */
   1036 			(void) strcpy(hiokp->hio_capable.value.c, "No");
   1037 			return (0);
   1038 		}
   1039 
   1040 		mutex_enter(&vswp->mac_lock);
   1041 		hiokp->hio_num_shares.value.ul = (uint32_t)hiop->vh_num_shares;
   1042 		for (i = 0; i < hiop->vh_num_shares; i++) {
   1043 			hiokp->share[i].assigned.value.ul =
   1044 			    hiop->vh_shares[i].vs_macaddr;
   1045 			hiokp->share[i].state.value.ul =
   1046 			    hiop->vh_shares[i].vs_state;
   1047 		}
   1048 		mutex_exit(&vswp->mac_lock);
   1049 	} else {
   1050 		return (EACCES);
   1051 	}
   1052 
   1053 	return (0);
   1054 }
   1055