vsw_hio.c revision 6ab6cb20c72ce71fe6022b1c164f36dfe716e425
2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/errno.h>
2N/A#include <sys/debug.h>
2N/A#include <sys/time.h>
2N/A#include <sys/sysmacros.h>
2N/A#include <sys/systm.h>
2N/A#include <sys/user.h>
2N/A#include <sys/stropts.h>
2N/A#include <sys/stream.h>
2N/A#include <sys/strlog.h>
2N/A#include <sys/strsubr.h>
2N/A#include <sys/cmn_err.h>
2N/A#include <sys/cpu.h>
2N/A#include <sys/kmem.h>
2N/A#include <sys/conf.h>
2N/A#include <sys/ddi.h>
2N/A#include <sys/sunddi.h>
2N/A#include <sys/ksynch.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/kstat.h>
2N/A#include <sys/vtrace.h>
2N/A#include <sys/strsun.h>
2N/A#include <sys/dlpi.h>
2N/A#include <sys/ethernet.h>
2N/A#include <net/if.h>
2N/A#include <sys/varargs.h>
2N/A#include <sys/machsystm.h>
2N/A#include <sys/modctl.h>
2N/A#include <sys/modhash.h>
2N/A#include <sys/mac.h>
2N/A#include <sys/mac_ether.h>
2N/A#include <sys/taskq.h>
2N/A#include <sys/note.h>
2N/A#include <sys/mach_descrip.h>
2N/A#include <sys/mac.h>
2N/A#include <sys/mdeg.h>
2N/A#include <sys/ldc.h>
2N/A#include <sys/vsw_fdb.h>
2N/A#include <sys/vsw.h>
2N/A#include <sys/vio_mailbox.h>
2N/A#include <sys/vnet_mailbox.h>
2N/A#include <sys/vnet_common.h>
2N/A#include <sys/vio_util.h>
2N/A#include <sys/sdt.h>
2N/A#include <sys/atomic.h>
2N/A#include <sys/callb.h>
2N/A
2N/A
2N/A#define VSW_DDS_NEXT_REQID(vsharep) (++vsharep->vs_req_id)
2N/A
2N/Aextern boolean_t vsw_hio_enabled; /* HybridIO enabled? */
2N/Aextern int vsw_hio_max_cleanup_retries;
2N/Aextern int vsw_hio_cleanup_delay;
2N/A
2N/A/* Functions imported from other files */
2N/Aextern int vsw_send_msg(vsw_ldc_t *, void *, int, boolean_t);
2N/Aextern int vsw_set_hw(vsw_t *, vsw_port_t *, int);
2N/Aextern int vsw_unset_hw(vsw_t *, vsw_port_t *, int);
2N/Aextern void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate);
2N/A
2N/A/* Functions exported to other files */
2N/Avoid vsw_hio_init(vsw_t *vswp);
2N/Avoid vsw_hio_cleanup(vsw_t *vswp);
2N/Avoid vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp);
2N/Avoid vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp);
2N/Avoid vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg);
2N/Avoid vsw_hio_start_ports(vsw_t *vswp);
2N/Avoid vsw_hio_stop_port(vsw_port_t *portp);
2N/A
2N/A/* Support functions */
2N/Astatic void vsw_hio_free_all_shares(vsw_t *vswp, boolean_t reboot);
2N/Astatic vsw_share_t *vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp);
2N/Astatic void vsw_hio_free_share(vsw_share_t *vsharep);
2N/Astatic vsw_share_t *vsw_hio_find_free_share(vsw_t *vswp);
2N/Astatic vsw_share_t *vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id);
2N/Astatic vsw_share_t *vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp);
2N/Astatic int vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass,
2N/A uint64_t cookie, uint64_t macaddr, uint32_t req_id);
2N/Astatic int vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack);
2N/Astatic int vsw_hio_send_delshare_msg(vsw_share_t *vsharep);
2N/Astatic int vsw_hio_bind_macaddr(vsw_share_t *vsharep);
2N/Astatic void vsw_hio_unbind_macaddr(vsw_share_t *vsharep);
2N/Astatic boolean_t vsw_hio_reboot_callb(void *arg, int code);
2N/Astatic boolean_t vsw_hio_panic_callb(void *arg, int code);
2N/A
2N/Astatic kstat_t *vsw_hio_setup_kstats(char *ks_mod, char *ks_name, vsw_t *vswp);
2N/Astatic void vsw_hio_destroy_kstats(vsw_t *vswp);
2N/Astatic int vsw_hio_kstats_update(kstat_t *ksp, int rw);
2N/A
2N/A/*
2N/A * vsw_hio_init -- Initialize the HybridIO related info.
2N/A * - Query SHARES and RINGS capability. Both capabilities
2N/A * need to be supported by the physical-device.
2N/A */
2N/Avoid
2N/Avsw_hio_init(vsw_t *vswp)
2N/A{
2N/A vsw_hio_t *hiop = &vswp->vhio;
2N/A int i;
2N/A int rv;
2N/A
2N/A D1(vswp, "%s:enter\n", __func__);
2N/A mutex_enter(&vswp->hw_lock);
2N/A if (vsw_hio_enabled == B_FALSE) {
2N/A mutex_exit(&vswp->hw_lock);
2N/A return;
2N/A }
2N/A
2N/A vswp->hio_capable = B_FALSE;
2N/A rv = mac_capab_get(vswp->mh, MAC_CAPAB_SHARES, &hiop->vh_scapab);
2N/A if (rv == B_FALSE) {
2N/A D2(vswp, "%s: %s is not HybridIO capable\n", __func__,
2N/A vswp->physname);
2N/A mutex_exit(&vswp->hw_lock);
2N/A return;
2N/A }
2N/A rv = mac_capab_get(vswp->mh, MAC_CAPAB_RINGS, &hiop->vh_rcapab);
2N/A if (rv == B_FALSE) {
2N/A DWARN(vswp, "%s: %s has no RINGS capability\n", __func__,
2N/A vswp->physname);
2N/A mutex_exit(&vswp->hw_lock);
2N/A return;
2N/A }
2N/A hiop->vh_num_shares = hiop->vh_scapab.ms_snum;
2N/A hiop->vh_shares = kmem_zalloc((sizeof (vsw_share_t) *
2N/A hiop->vh_num_shares), KM_SLEEP);
2N/A for (i = 0; i < hiop->vh_num_shares; i++) {
2N/A hiop->vh_shares[i].vs_state = VSW_SHARE_FREE;
2N/A hiop->vh_shares[i].vs_index = i;
2N/A hiop->vh_shares[i].vs_vswp = vswp;
2N/A }
2N/A vswp->hio_capable = B_TRUE;
2N/A
2N/A /*
2N/A * Register to get reboot and panic events so that
2N/A * we can cleanup HybridIO resources gracefully.
2N/A */
2N/A vswp->hio_reboot_cb_id = callb_add(vsw_hio_reboot_callb,
2N/A (void *)vswp, CB_CL_MDBOOT, "vsw_hio");
2N/A
2N/A vswp->hio_panic_cb_id = callb_add(vsw_hio_panic_callb,
2N/A (void *)vswp, CB_CL_PANIC, "vsw_hio");
2N/A
2N/A /* setup kstats for hybrid resources */
2N/A hiop->vh_ksp = vsw_hio_setup_kstats(DRV_NAME, "hio", vswp);
2N/A if (hiop->vh_ksp == NULL) {
2N/A DERR(vswp, "%s: kstats setup failed", __func__);
2N/A }
2N/A
2N/A D2(vswp, "%s: %s is HybridIO capable num_shares=%d\n", __func__,
2N/A vswp->physname, hiop->vh_num_shares);
2N/A D1(vswp, "%s:exit\n", __func__);
2N/A mutex_exit(&vswp->hw_lock);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_alloc_share -- Allocate and setup the share for a guest domain.
2N/A * - Allocate a free share.
2N/A * - Bind the Guest's MAC address.
2N/A */
2N/Astatic vsw_share_t *
2N/Avsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp)
2N/A{
2N/A vsw_hio_t *hiop = &vswp->vhio;
2N/A mac_capab_share_t *hcapab = &hiop->vh_scapab;
2N/A vsw_share_t *vsharep;
2N/A vsw_port_t *portp = ldcp->ldc_port;
2N/A uint64_t ldc_id = ldcp->ldc_id;
2N/A uint32_t rmin, rmax;
2N/A uint64_t rmap;
2N/A int rv;
2N/A
2N/A D1(vswp, "%s:enter\n", __func__);
2N/A vsharep = vsw_hio_find_free_share(vswp);
2N/A if (vsharep == NULL) {
2N/A /* No free shares available */
2N/A return (NULL);
2N/A }
2N/A /*
2N/A * Allocate a Share - it will come with rings/groups
2N/A * already assigned to it.
2N/A */
2N/A rv = hcapab->ms_salloc(hcapab->ms_handle, ldc_id,
2N/A &vsharep->vs_cookie, &vsharep->vs_shdl);
2N/A if (rv != 0) {
2N/A D2(vswp, "Alloc a share failed for ldc=0x%lx rv=%d",
2N/A ldc_id, rv);
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * Query the RX group number to bind the port's
2N/A * MAC address to it.
2N/A */
2N/A hcapab->ms_squery(vsharep->vs_shdl, MAC_RING_TYPE_RX,
2N/A &rmin, &rmax, &rmap, &vsharep->vs_gnum);
2N/A
2N/A /* Cache some useful info */
2N/A vsharep->vs_ldcid = ldcp->ldc_id;
2N/A vsharep->vs_macaddr = vnet_macaddr_strtoul(
2N/A portp->p_macaddr.ether_addr_octet);
2N/A vsharep->vs_portp = ldcp->ldc_port;
2N/A
2N/A /* Bind the Guest's MAC address */
2N/A rv = vsw_hio_bind_macaddr(vsharep);
2N/A if (rv != 0) {
2N/A /* something went wrong, cleanup */
2N/A hcapab->ms_sfree(vsharep->vs_shdl);
2N/A return (NULL);
2N/A }
2N/A
2N/A vsharep->vs_state |= VSW_SHARE_ASSIGNED;
2N/A
2N/A D1(vswp, "%s:exit\n", __func__);
2N/A return (vsharep);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_bind_macaddr -- Remove the port's MAC address from the
2N/A * physdev and bind it to the Share's RX group.
2N/A */
2N/Astatic int
2N/Avsw_hio_bind_macaddr(vsw_share_t *vsharep)
2N/A{
2N/A vsw_t *vswp = vsharep->vs_vswp;
2N/A vsw_port_t *portp = vsharep->vs_portp;
2N/A mac_capab_rings_t *rcapab = &vswp->vhio.vh_rcapab;
2N/A mac_group_info_t *ginfop = &vsharep->vs_rxginfo;
2N/A int rv;
2N/A
2N/A /* Get the RX groupinfo */
2N/A rcapab->mr_gget(rcapab->mr_handle, MAC_RING_TYPE_RX,
2N/A vsharep->vs_gnum, &vsharep->vs_rxginfo, NULL);
2N/A
2N/A /* Unset the MAC address first */
2N/A if (portp->addr_set != VSW_ADDR_UNSET) {
2N/A (void) vsw_unset_hw(vswp, portp, VSW_VNETPORT);
2N/A }
2N/A
2N/A /* Bind the MAC address to the RX group */
2N/A rv = ginfop->mrg_addmac(ginfop->mrg_driver,
2N/A (uint8_t *)&portp->p_macaddr.ether_addr_octet);
2N/A if (rv != 0) {
2N/A /* Restore the address back as it was */
2N/A (void) vsw_set_hw(vswp, portp, VSW_VNETPORT);
2N/A return (rv);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_unbind_macaddr -- Unbind the port's MAC address and restore
2N/A * it back as it was before.
2N/A */
2N/Astatic void
2N/Avsw_hio_unbind_macaddr(vsw_share_t *vsharep)
2N/A{
2N/A vsw_t *vswp = vsharep->vs_vswp;
2N/A vsw_port_t *portp = vsharep->vs_portp;
2N/A mac_group_info_t *ginfop = &vsharep->vs_rxginfo;
2N/A
2N/A if (portp == NULL) {
2N/A return;
2N/A }
2N/A /* Unbind the MAC address from the RX group */
2N/A (void) ginfop->mrg_remmac(ginfop->mrg_driver,
2N/A (uint8_t *)&portp->p_macaddr.ether_addr_octet);
2N/A
2N/A /* Program the MAC address back */
2N/A (void) vsw_set_hw(vswp, portp, VSW_VNETPORT);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_find_free_share -- Find a free Share.
2N/A */
2N/Astatic vsw_share_t *
2N/Avsw_hio_find_free_share(vsw_t *vswp)
2N/A{
2N/A vsw_hio_t *hiop = &vswp->vhio;
2N/A vsw_share_t *vsharep;
2N/A int i;
2N/A
2N/A D1(vswp, "%s:enter\n", __func__);
2N/A for (i = 0; i < hiop->vh_num_shares; i++) {
2N/A vsharep = &hiop->vh_shares[i];
2N/A if (vsharep->vs_state == VSW_SHARE_FREE) {
2N/A D1(vswp, "%s:Returning free share(%d)\n",
2N/A __func__, vsharep->vs_index);
2N/A return (vsharep);
2N/A }
2N/A }
2N/A D1(vswp, "%s:no free share\n", __func__);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_find_vshare_ldcid -- Given ldc_id, find the corresponding
2N/A * share structure.
2N/A */
2N/Astatic vsw_share_t *
2N/Avsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id)
2N/A{
2N/A vsw_hio_t *hiop = &vswp->vhio;
2N/A vsw_share_t *vsharep;
2N/A int i;
2N/A
2N/A D1(vswp, "%s:enter, ldc=0x%lx", __func__, ldc_id);
2N/A for (i = 0; i < hiop->vh_num_shares; i++) {
2N/A vsharep = &hiop->vh_shares[i];
2N/A if (vsharep->vs_state == VSW_SHARE_FREE) {
2N/A continue;
2N/A }
2N/A if (vsharep->vs_ldcid == ldc_id) {
2N/A D1(vswp, "%s:returning share(%d)",
2N/A __func__, vsharep->vs_index);
2N/A return (vsharep);
2N/A }
2N/A }
2N/A D1(vswp, "%s:returning NULL", __func__);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_find_vshare_port -- Given portp, find the corresponding
2N/A * share structure.
2N/A */
2N/Astatic vsw_share_t *
2N/Avsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp)
2N/A{
2N/A vsw_hio_t *hiop = &vswp->vhio;
2N/A vsw_share_t *vsharep;
2N/A int i;
2N/A
2N/A D1(vswp, "%s:enter, portp=0x%p", __func__, portp);
2N/A for (i = 0; i < hiop->vh_num_shares; i++) {
2N/A vsharep = &hiop->vh_shares[i];
2N/A if (vsharep->vs_state == VSW_SHARE_FREE) {
2N/A continue;
2N/A }
2N/A if (vsharep->vs_portp == portp) {
2N/A D1(vswp, "%s:returning share(%d)",
2N/A __func__, vsharep->vs_index);
2N/A return (vsharep);
2N/A }
2N/A }
2N/A D1(vswp, "%s:returning NULL", __func__);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_free_share -- Unbind the MAC address and free share.
2N/A */
2N/Astatic void
2N/Avsw_hio_free_share(vsw_share_t *vsharep)
2N/A{
2N/A vsw_t *vswp = vsharep->vs_vswp;
2N/A vsw_hio_t *hiop = &vswp->vhio;
2N/A mac_capab_share_t *hcapab = &hiop->vh_scapab;
2N/A
2N/A D1(vswp, "%s:enter\n", __func__);
2N/A
2N/A /* First unbind the MAC address and restore it back */
2N/A vsw_hio_unbind_macaddr(vsharep);
2N/A
2N/A /* free share */
2N/A hcapab->ms_sfree(vsharep->vs_shdl);
2N/A vsharep->vs_state = VSW_SHARE_FREE;
2N/A vsharep->vs_macaddr = 0;
2N/A
2N/A /* DERR only for printing by default */
2N/A DERR(vswp, "Share freed for ldc_id=0x%lx Cookie=0x%lX",
2N/A vsharep->vs_ldcid, vsharep->vs_cookie);
2N/A D1(vswp, "%s:exit\n", __func__);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * vsw_hio_cleanup -- Cleanup the HybridIO. It unregisters the callbs
2N/A * and frees all shares.
2N/A */
2N/Avoid
2N/Avsw_hio_cleanup(vsw_t *vswp)
2N/A{
2N/A D1(vswp, "%s:enter\n", __func__);
2N/A
2N/A /* Unregister reboot and panic callbs. */
2N/A if (vswp->hio_reboot_cb_id) {
2N/A (void) callb_delete(vswp->hio_reboot_cb_id);
2N/A vswp->hio_reboot_cb_id = 0;
2N/A }
2N/A if (vswp->hio_panic_cb_id) {
2N/A (void) callb_delete(vswp->hio_panic_cb_id);
2N/A vswp->hio_panic_cb_id = 0;
2N/A }
2N/A vsw_hio_free_all_shares(vswp, B_FALSE);
2N/A vsw_hio_destroy_kstats(vswp);
2N/A D1(vswp, "%s:exit\n", __func__);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_free_all_shares -- A routine to free all shares gracefully.
2N/A * The following are the steps followed to accomplish this:
2N/A *
2N/A * - First clear 'hio_capable' to avoid further share allocations.
2N/A * - If a share is in accepted(ACKD) state, that means the guest
2N/A * has HybridIO setup etc. If so, send a DEL_SHARE message and
2N/A * give some time(delay) for the guest to ACK.
2N/A * - If the Share is another state, give some time to transition to
2N/A * ACKD state, then try the above.
2N/A * - After max retries, reset the ports to brute force the shares
2N/A * to be freed. Give a little delay for the LDC reset code to
2N/A * free the Share.
2N/A */
2N/Astatic void
2N/Avsw_hio_free_all_shares(vsw_t *vswp, boolean_t reboot)
2N/A{
2N/A vsw_hio_t *hiop = &vswp->vhio;
2N/A vsw_port_list_t *plist = &vswp->plist;
2N/A vsw_share_t *vsharep;
2N/A int free_shares = 0;
2N/A int max_retries = vsw_hio_max_cleanup_retries;
2N/A int i;
2N/A
2N/A D1(vswp, "%s:enter\n", __func__);
2N/A
2N/A /*
2N/A * Acquire plist->lockrw to make the locking a bit easier
2N/A * and keep the ports in a stable state while we are cleaningup
2N/A * HybridIO.
2N/A */
2N/A READ_ENTER(&plist->lockrw);
2N/A mutex_enter(&vswp->hw_lock);
2N/A /*
2N/A * first clear the hio_capable flag so that no more
2N/A * HybridIO operations are initiated.
2N/A */
2N/A vswp->hio_capable = B_FALSE;
2N/A
2N/A do {
2N/A free_shares = 0;
2N/A for (i = 0; i < hiop->vh_num_shares; i++) {
2N/A vsharep = &hiop->vh_shares[i];
2N/A if (vsharep->vs_state == VSW_SHARE_FREE) {
2N/A free_shares++;
2N/A continue;
2N/A }
2N/A /*
2N/A * If the share is in DDS_ACKD state, then
2N/A * send DEL_SHARE message so that guest can
2N/A * release its Hybrid resource.
2N/A */
2N/A if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) {
2N/A int rv;
2N/A
2N/A /* send DDS_DEL_SHARE */
2N/A D1(vswp, "%s:sending DEL_SHARE msg for "
2N/A "share(%d)", __func__, vsharep->vs_index);
2N/A rv = vsw_hio_send_delshare_msg(vsharep);
2N/A if (rv != 0) {
2N/A /*
2N/A * No alternative, reset the port
2N/A * to force the release of Hybrid
2N/A * resources.
2N/A */
2N/A vsw_hio_port_reset(vsharep->vs_portp,
2N/A B_FALSE);
2N/A }
2N/A }
2N/A if (max_retries == 1) {
2N/A /*
2N/A * Last retry, reset the port.
2N/A * If it is reboot case, issue an immediate
2N/A * reset.
2N/A */
2N/A DWARN(vswp, "%s:All retries failed, "
2N/A " cause a reset to trigger cleanup for "
2N/A "share(%d)", __func__, vsharep->vs_index);
2N/A vsw_hio_port_reset(vsharep->vs_portp, reboot);
2N/A }
2N/A }
2N/A if (free_shares == hiop->vh_num_shares) {
2N/A /* Clean up is done */
2N/A break;
2N/A }
2N/A /*
2N/A * Release the lock so that reply for DEL_SHARE
2N/A * messages come and get processed, that is, shares
2N/A * get freed.
2N/A * This delay is also needed for the port reset to
2N/A * release the Hybrid resource.
2N/A */
2N/A mutex_exit(&vswp->hw_lock);
2N/A drv_usecwait(vsw_hio_cleanup_delay);
2N/A mutex_enter(&vswp->hw_lock);
2N/A max_retries--;
2N/A } while ((free_shares < hiop->vh_num_shares) && (max_retries > 0));
2N/A
2N/A /* By now, all shares should be freed */
2N/A if (free_shares != hiop->vh_num_shares) {
2N/A if (reboot == B_FALSE) {
2N/A cmn_err(CE_NOTE, "vsw%d: All physical resources "
2N/A "could not be freed", vswp->instance);
2N/A }
2N/A }
2N/A
2N/A kmem_free(hiop->vh_shares, sizeof (vsw_share_t) * hiop->vh_num_shares);
2N/A hiop->vh_shares = NULL;
2N/A hiop->vh_num_shares = 0;
2N/A mutex_exit(&vswp->hw_lock);
2N/A RW_EXIT(&plist->lockrw);
2N/A D1(vswp, "%s:exit\n", __func__);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_start_ports -- Start HybridIO for ports that have
2N/A * already established connection before HybridIO is intialized.
2N/A */
2N/Avoid
2N/Avsw_hio_start_ports(vsw_t *vswp)
2N/A{
2N/A vsw_port_list_t *plist = &vswp->plist;
2N/A vsw_port_t *portp;
2N/A vsw_share_t *vsharep;
2N/A boolean_t reset;
2N/A
2N/A if (vswp->hio_capable == B_FALSE) {
2N/A return;
2N/A }
2N/A READ_ENTER(&plist->lockrw);
2N/A for (portp = plist->head; portp != NULL; portp = portp->p_next) {
2N/A if ((portp->p_hio_enabled == B_FALSE) ||
2N/A (portp->p_hio_capable == B_FALSE)) {
2N/A continue;
2N/A }
2N/A
2N/A reset = B_FALSE;
2N/A mutex_enter(&vswp->hw_lock);
2N/A vsharep = vsw_hio_find_vshare_port(vswp, portp);
2N/A if (vsharep == NULL) {
2N/A reset = B_TRUE;
2N/A }
2N/A mutex_exit(&vswp->hw_lock);
2N/A
2N/A if (reset == B_TRUE) {
2N/A /* Cause a rest to trigger HybridIO setup */
2N/A vsw_hio_port_reset(portp, B_FALSE);
2N/A }
2N/A }
2N/A RW_EXIT(&plist->lockrw);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_start -- Start HybridIO for a guest(given LDC)
2N/A */
2N/Avoid
2N/Avsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp)
2N/A{
2N/A vsw_share_t *vsharep;
2N/A uint32_t req_id;
2N/A int rv;
2N/A
2N/A D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id);
2N/A mutex_enter(&vswp->hw_lock);
2N/A if (vswp->hio_capable == B_FALSE) {
2N/A mutex_exit(&vswp->hw_lock);
2N/A D2(vswp, "%s:not HIO capable", __func__);
2N/A return;
2N/A }
2N/A
2N/A /* Verify if a share was already allocated */
2N/A vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
2N/A if (vsharep != NULL) {
2N/A mutex_exit(&vswp->hw_lock);
2N/A D2(vswp, "%s:Share already allocated to ldc=0x%lx",
2N/A __func__, ldcp->ldc_id);
2N/A return;
2N/A }
2N/A vsharep = vsw_hio_alloc_share(vswp, ldcp);
2N/A if (vsharep == NULL) {
2N/A mutex_exit(&vswp->hw_lock);
2N/A D2(vswp, "%s: no Share available for ldc=0x%lx",
2N/A __func__, ldcp->ldc_id);
2N/A return;
2N/A }
2N/A req_id = VSW_DDS_NEXT_REQID(vsharep);
2N/A rv = vsw_send_dds_msg(ldcp, DDS_VNET_ADD_SHARE, vsharep->vs_cookie,
2N/A vsharep->vs_macaddr, req_id);
2N/A if (rv != 0) {
2N/A /*
2N/A * Failed to send a DDS message, so cleanup now.
2N/A */
2N/A vsw_hio_free_share(vsharep);
2N/A mutex_exit(&vswp->hw_lock);
2N/A return;
2N/A }
2N/A vsharep->vs_state &= ~VSW_SHARE_DDS_ACKD;
2N/A vsharep->vs_state |= VSW_SHARE_DDS_SENT;
2N/A mutex_exit(&vswp->hw_lock);
2N/A
2N/A /* DERR only to print by default */
2N/A DERR(vswp, "Share allocated for ldc_id=0x%lx Cookie=0x%lX",
2N/A ldcp->ldc_id, vsharep->vs_cookie);
2N/A
2N/A D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_stop -- Stop/clean the HybridIO config for a guest(given ldc).
2N/A */
2N/Avoid
2N/Avsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp)
2N/A{
2N/A vsw_share_t *vsharep;
2N/A
2N/A D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id);
2N/A
2N/A mutex_enter(&vswp->hw_lock);
2N/A vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
2N/A if (vsharep == NULL) {
2N/A D1(vswp, "%s:no share found for ldc=0x%lx",
2N/A __func__, ldcp->ldc_id);
2N/A mutex_exit(&vswp->hw_lock);
2N/A return;
2N/A }
2N/A vsw_hio_free_share(vsharep);
2N/A mutex_exit(&vswp->hw_lock);
2N/A
2N/A D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id);
2N/A}
2N/A
2N/A/*
2N/A * vsw_hio_send_delshare_msg -- Send a DEL_SHARE message to the guest.
2N/A */
2N/Astatic int
2N/Avsw_hio_send_delshare_msg(vsw_share_t *vsharep)
2N/A{
2N/A vsw_t *vswp = vsharep->vs_vswp;
2N/A vsw_port_t *portp;
2N/A vsw_ldc_list_t *ldcl;
2N/A vsw_ldc_t *ldcp;
2N/A uint32_t req_id;
2N/A uint64_t cookie = vsharep->vs_cookie;
2N/A uint64_t macaddr = vsharep->vs_macaddr;
2N/A int rv;
2N/A
2N/A ASSERT(MUTEX_HELD(&vswp->hw_lock));
2N/A mutex_exit(&vswp->hw_lock);
2N/A
2N/A portp = vsharep->vs_portp;
2N/A if (portp == NULL) {
2N/A mutex_enter(&vswp->hw_lock);
2N/A return (0);
2N/A }
2N/A
2N/A ldcl = &portp->p_ldclist;
2N/A READ_ENTER(&ldcl->lockrw);
2N/A ldcp = ldcl->head;
2N/A if ((ldcp == NULL) || (ldcp->ldc_id != vsharep->vs_ldcid)) {
2N/A RW_EXIT(&ldcl->lockrw);
2N/A mutex_enter(&vswp->hw_lock);
2N/A return (0);
2N/A }
2N/A req_id = VSW_DDS_NEXT_REQID(vsharep);
2N/A rv = vsw_send_dds_msg(ldcp, DDS_VNET_DEL_SHARE,
2N/A cookie, macaddr, req_id);
2N/A
2N/A RW_EXIT(&ldcl->lockrw);
2N/A mutex_enter(&vswp->hw_lock);
2N/A if (rv == 0) {
2N/A vsharep->vs_state &= ~VSW_SHARE_DDS_ACKD;
2N/A vsharep->vs_state |= VSW_SHARE_DDS_SENT;
2N/A }
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * vsw_send_dds_msg -- Send a DDS message.
2N/A */
2N/Astatic int
2N/Avsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass, uint64_t
2N/A cookie, uint64_t macaddr, uint32_t req_id)
2N/A{
2N/A vsw_t *vswp = ldcp->ldc_port->p_vswp;
2N/A vio_dds_msg_t vmsg;
2N/A dds_share_msg_t *smsg = &vmsg.msg.share_msg;
2N/A int rv;
2N/A
2N/A D1(vswp, "%s:enter\n", __func__);
2N/A vmsg.tag.vio_msgtype = VIO_TYPE_CTRL;
2N/A vmsg.tag.vio_subtype = VIO_SUBTYPE_INFO;
2N/A vmsg.tag.vio_subtype_env = VIO_DDS_INFO;
2N/A vmsg.tag.vio_sid = ldcp->local_session;
2N/A vmsg.dds_class = DDS_VNET_NIU;
2N/A vmsg.dds_subclass = dds_subclass;
2N/A vmsg.dds_req_id = req_id;
2N/A smsg->macaddr = macaddr;
2N/A smsg->cookie = cookie;
2N/A rv = vsw_send_msg(ldcp, &vmsg, sizeof (vmsg), B_FALSE);
2N/A D1(vswp, "%s:exit rv=%d\n", __func__, rv);
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * vsw_process_dds_msg -- Process a DDS message received from a guest.
2N/A */
2N/Avoid
2N/Avsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg)
2N/A{
2N/A vsw_share_t *vsharep;
2N/A vio_dds_msg_t *dmsg = msg;
2N/A
2N/A D1(vswp, "%s:enter ldc=0x%lx\n", __func__, ldcp->ldc_id);
2N/A if (dmsg->dds_class != DDS_VNET_NIU) {
2N/A /* discard */
2N/A return;
2N/A }
2N/A mutex_enter(&vswp->hw_lock);
2N/A /*
2N/A * We expect to receive DDS messages only from guests that
2N/A * have HybridIO started.
2N/A */
2N/A vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
2N/A if (vsharep == NULL) {
2N/A mutex_exit(&vswp->hw_lock);
2N/A return;
2N/A }
2N/A
2N/A switch (dmsg->dds_subclass) {
2N/A case DDS_VNET_ADD_SHARE:
2N/A /* A response for ADD_SHARE message. */
2N/A D1(vswp, "%s:DDS_VNET_ADD_SHARE\n", __func__);
2N/A if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) {
2N/A DWARN(vswp, "%s: invalid ADD_SHARE response message "
2N/A " share state=0x%X", __func__, vsharep->vs_state);
2N/A break;
2N/A }
2N/A
2N/A if (dmsg->dds_req_id != vsharep->vs_req_id) {
2N/A DWARN(vswp, "%s: invalid req_id in ADD_SHARE response"
2N/A " message req_id=0x%X share's req_id=0x%X",
2N/A __func__, dmsg->dds_req_id, vsharep->vs_req_id);
2N/A break;
2N/A }
2N/A
2N/A if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) {
2N/A DWARN(vswp, "%s: NACK received for ADD_SHARE"
2N/A " message ldcid=0x%lx", __func__, ldcp->ldc_id);
2N/A /* cleanup for NACK */
2N/A vsw_hio_free_share(vsharep);
2N/A } else {
2N/A D2(vswp, "%s: ACK received for ADD_SHARE", __func__);
2N/A vsharep->vs_state &= ~VSW_SHARE_DDS_SENT;
2N/A vsharep->vs_state |= VSW_SHARE_DDS_ACKD;
2N/A }
2N/A break;
2N/A
2N/A case DDS_VNET_DEL_SHARE:
2N/A /* A response for DEL_SHARE message */
2N/A D1(vswp, "%s:DDS_VNET_DEL_SHARE\n", __func__);
2N/A if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) {
2N/A DWARN(vswp, "%s: invalid DEL_SHARE response message "
2N/A " share state=0x%X", __func__, vsharep->vs_state);
2N/A break;
2N/A }
2N/A
2N/A if (dmsg->dds_req_id != vsharep->vs_req_id) {
2N/A DWARN(vswp, "%s: invalid req_id in DEL_SHARE response"
2N/A " message share req_id=0x%X share's req_id=0x%X",
2N/A __func__, dmsg->dds_req_id, vsharep->vs_req_id);
2N/A break;
2N/A }
2N/A if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) {
2N/A DWARN(vswp, "%s: NACK received for DEL_SHARE",
2N/A __func__);
2N/A }
2N/A
2N/A /* There is nothing we can do, free share now */
2N/A vsw_hio_free_share(vsharep);
2N/A break;
2N/A
case DDS_VNET_REL_SHARE:
/* Guest has released Share voluntarily, so free it now */
D1(vswp, "%s:DDS_VNET_REL_SHARE\n", __func__);
/* send ACK */
(void) vsw_send_dds_resp_msg(ldcp, dmsg, B_FALSE);
vsw_hio_free_share(vsharep);
break;
default:
DERR(vswp, "%s: Invalid DDS message type=0x%X",
__func__, dmsg->dds_subclass);
break;
}
mutex_exit(&vswp->hw_lock);
D1(vswp, "%s:exit ldc=0x%lx\n", __func__, ldcp->ldc_id);
}
/*
* vsw_send_dds_resp_msg -- Send a DDS response message.
*/
static int
vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack)
{
vsw_t *vswp = ldcp->ldc_port->p_vswp;
int rv;
D1(vswp, "%s:enter\n", __func__);
if (ack == B_TRUE) {
dmsg->tag.vio_subtype = VIO_SUBTYPE_ACK;
dmsg->msg.share_resp_msg.status = DDS_VNET_SUCCESS;
} else {
dmsg->tag.vio_subtype = VIO_SUBTYPE_NACK;
dmsg->msg.share_resp_msg.status = DDS_VNET_FAIL;
}
rv = vsw_send_msg(ldcp, dmsg, sizeof (vio_dds_msg_t), B_FALSE);
D1(vswp, "%s:exit rv=%d\n", __func__, rv);
return (rv);
}
/*
* vsw_hio_port_update -- update Hybrid mode change for a port.
*/
void
vsw_hio_port_update(vsw_port_t *portp, boolean_t hio_enabled)
{
/* Verify if the mode really changed */
if (portp->p_hio_enabled == hio_enabled) {
return;
}
if (hio_enabled == B_FALSE) {
/* Hybrid Mode is disabled, so stop HybridIO */
vsw_hio_stop_port(portp);
portp->p_hio_enabled = B_FALSE;
} else {
portp->p_hio_enabled = B_TRUE;
/* reset the port to initiate HybridIO setup */
vsw_hio_port_reset(portp, B_FALSE);
}
}
/*
* vsw_hio_stop_port -- Stop HybridIO for a given port. Sequence
* followed is similar to vsw_hio_free_all_shares().
*
*/
void
vsw_hio_stop_port(vsw_port_t *portp)
{
vsw_t *vswp = portp->p_vswp;
vsw_share_t *vsharep;
int max_retries = vsw_hio_max_cleanup_retries;
D1(vswp, "%s:enter\n", __func__);
mutex_enter(&vswp->hw_lock);
if (vswp->hio_capable == B_FALSE) {
mutex_exit(&vswp->hw_lock);
return;
}
vsharep = vsw_hio_find_vshare_port(vswp, portp);
if (vsharep == NULL) {
mutex_exit(&vswp->hw_lock);
return;
}
do {
if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) {
int rv;
/* send DDS_DEL_SHARE */
D1(vswp, "%s:sending DEL_SHARE msg for "
"share(%d)", __func__, vsharep->vs_index);
rv = vsw_hio_send_delshare_msg(vsharep);
if (rv != 0) {
/*
* Cause a port reset to trigger
* cleanup.
*/
vsw_hio_port_reset(vsharep->vs_portp, B_FALSE);
}
}
if (max_retries == 1) {
/* last retry */
DWARN(vswp, "%s:All retries failed, "
" cause a reset to trigger cleanup for "
"share(%d)", __func__, vsharep->vs_index);
vsw_hio_port_reset(vsharep->vs_portp, B_FALSE);
}
/* Check if the share still assigned to this port */
if ((vsharep->vs_portp != portp) ||
(vsharep->vs_state == VSW_SHARE_FREE)) {
break;
}
/*
* Release the lock so that reply for DEL_SHARE
* messages come and get processed, that is, shares
* get freed.
*/
mutex_exit(&vswp->hw_lock);
drv_usecwait(vsw_hio_cleanup_delay);
mutex_enter(&vswp->hw_lock);
/* Check if the share still assigned to this port */
if ((vsharep->vs_portp != portp) ||
(vsharep->vs_state == VSW_SHARE_FREE)) {
break;
}
max_retries--;
} while ((vsharep->vs_state != VSW_SHARE_FREE) && (max_retries > 0));
mutex_exit(&vswp->hw_lock);
D1(vswp, "%s:exit\n", __func__);
}
/*
* vsw_hio_rest_all -- Resets all ports that have shares allocated.
* It is called only in the panic code path, so the LDC channels
* are reset immediately.
*/
static void
vsw_hio_reset_all(vsw_t *vswp)
{
vsw_hio_t *hiop = &vswp->vhio;
vsw_share_t *vsharep;
int i;
D1(vswp, "%s:enter\n", __func__);
if (vswp->hio_capable != B_TRUE)
return;
for (i = 0; i < hiop->vh_num_shares; i++) {
vsharep = &hiop->vh_shares[i];
if (vsharep->vs_state == VSW_SHARE_FREE) {
continue;
}
/*
* Reset the port with immediate flag enabled,
* to cause LDC reset immediately.
*/
vsw_hio_port_reset(vsharep->vs_portp, B_TRUE);
}
D1(vswp, "%s:exit\n", __func__);
}
/*
* vsw_hio_reboot_callb -- Called for reboot event. It tries to
* free all currently allocated shares.
*/
/* ARGSUSED */
static boolean_t
vsw_hio_reboot_callb(void *arg, int code)
{
vsw_t *vswp = arg;
D1(vswp, "%s:enter\n", __func__);
vsw_hio_free_all_shares(vswp, B_TRUE);
D1(vswp, "%s:exit\n", __func__);
return (B_TRUE);
}
/*
* vsw_hio_panic_callb -- Called from panic event. It resets all
* the ports that have shares allocated. This is done to
* trigger the cleanup in the guest ahead of HV reset.
*/
/* ARGSUSED */
static boolean_t
vsw_hio_panic_callb(void *arg, int code)
{
vsw_t *vswp = arg;
D1(vswp, "%s:enter\n", __func__);
vsw_hio_reset_all(vswp);
D1(vswp, "%s:exit\n", __func__);
return (B_TRUE);
}
/*
* Setup kstats for hio statistics.
*/
static kstat_t *
vsw_hio_setup_kstats(char *ks_mod, char *ks_name, vsw_t *vswp)
{
kstat_t *ksp;
vsw_hio_kstats_t *hiokp;
vsw_hio_t *hiop;
char share_assigned_info[MAXNAMELEN];
size_t size;
int i;
hiop = &vswp->vhio;
/*
* vsw_hio_stats_t structure is variable size structure
* having fields defined only for one share. So, we need
* allocate additional space for the rest of the shares.
*/
size = sizeof (vsw_hio_kstats_t) / sizeof (kstat_named_t);
ASSERT(hiop->vh_num_shares >= 1);
size += ((hiop->vh_num_shares - 1) * 2);
ksp = kstat_create(ks_mod, vswp->instance, ks_name, "misc",
KSTAT_TYPE_NAMED, size, KSTAT_FLAG_VIRTUAL);
if (ksp == NULL) {
return (NULL);
}
hiokp = (vsw_hio_kstats_t *)kmem_zalloc(sizeof (kstat_named_t) *
size, KM_SLEEP);
ksp->ks_data = hiokp;
hiop->vh_ksp = ksp;
hiop->vh_kstatsp = hiokp;
hiop->vh_kstat_size = size;
kstat_named_init(&hiokp->hio_capable, "hio_capable", KSTAT_DATA_CHAR);
kstat_named_init(&hiokp->hio_num_shares, "hio_num_shares",
KSTAT_DATA_ULONG);
for (i = 0; i < hiop->vh_num_shares; i++) {
(void) sprintf(share_assigned_info, "%s%d", "hio_share_", i);
kstat_named_init(&(hiokp->share[i].assigned),
share_assigned_info, KSTAT_DATA_ULONG);
(void) sprintf(share_assigned_info, "%s%d%s",
"hio_share_", i, "_state");
kstat_named_init(&(hiokp->share[i].state),
share_assigned_info, KSTAT_DATA_ULONG);
}
ksp->ks_update = vsw_hio_kstats_update;
ksp->ks_private = (void *)vswp;
kstat_install(ksp);
return (ksp);
}
/*
* Destroy hio kstats.
*/
static void
vsw_hio_destroy_kstats(vsw_t *vswp)
{
kstat_t *ksp;
vsw_hio_t *hiop;
ASSERT(vswp != NULL);
ksp = vswp->vhio.vh_ksp;
hiop = &vswp->vhio;
if (ksp != NULL) {
kmem_free(hiop->vh_kstatsp, sizeof (kstat_named_t) *
hiop->vh_kstat_size);
kstat_delete(ksp);
hiop->vh_kstatsp = NULL;
hiop->vh_ksp = NULL;
}
}
/*
* Update hio kstats.
*/
static int
vsw_hio_kstats_update(kstat_t *ksp, int rw)
{
vsw_t *vswp;
vsw_hio_t *hiop;
vsw_hio_kstats_t *hiokp;
int i;
vswp = (vsw_t *)ksp->ks_private;
ASSERT(vswp != NULL);
hiop = &vswp->vhio;
hiokp = hiop->vh_kstatsp;
if (rw == KSTAT_READ) {
if (vswp->hio_capable) {
(void) strcpy(hiokp->hio_capable.value.c, "Yes");
} else {
/* not hio capable, just return */
(void) strcpy(hiokp->hio_capable.value.c, "No");
return (0);
}
mutex_enter(&vswp->hw_lock);
hiokp->hio_num_shares.value.ul = (uint32_t)hiop->vh_num_shares;
for (i = 0; i < hiop->vh_num_shares; i++) {
hiokp->share[i].assigned.value.ul =
hiop->vh_shares[i].vs_macaddr;
hiokp->share[i].state.value.ul =
hiop->vh_shares[i].vs_state;
}
mutex_exit(&vswp->hw_lock);
} else {
return (EACCES);
}
return (0);
}