root/usr/src/uts/common/io/ib/clients/eoib/eib_cmn.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ksynch.h>

#include <sys/ib/clients/eoib/eib_impl.h>

/*
 * Definitions private to this file
 */
ib_gid_t eib_reserved_gid;

uint8_t eib_zero_mac[] = {
        0x0, 0x0, 0x0, 0x0, 0x0, 0x0
};

uint8_t eib_broadcast_mac[] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

int eib_setbit_mod67[] = {
        -1,  0,  1, 39,  2, 15, 40, 23,
        3,  12, 16, 59, 41, 19, 24, 54,
        4,  -1, 13, 10, 17, 62, 60, 28,
        42, 30, 20, 51, 25, 44, 55, 47,
        5,  32, -1, 38, 14, 22, 11, 58,
        18, 53, 63,  9, 61, 27, 29, 50,
        43, 46, 31, 37, 21, 57, 52,  8,
        26, 49, 45, 36, 56,  7, 48, 35,
        6,  34, 33
};

char *eib_pvt_props[] = {
        EIB_DLPROP_GW_EPORT_STATE,
        EIB_DLPROP_HCA_GUID,
        EIB_DLPROP_PORT_GUID,
        NULL
};

#define eib_prop_get_and_test(inst, dp, propname, propval)              \
{                                                                       \
        (propval) = ddi_prop_get_int(DDI_DEV_T_ANY, (dp),               \
            DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, (propname), -1);      \
        if ((propval) == -1) {                                          \
                EIB_DPRINTF_WARN((inst), "eib_get_props: "              \
                    "ddi_prop_get_int() could not find "                \
                    "property '%s'", (propname));                       \
                goto get_props_fail;                                    \
        }                                                               \
}

#define eib_prop64_get_and_test(inst, dp, propname, propval)            \
{                                                                       \
        (propval) = ddi_prop_get_int64(DDI_DEV_T_ANY, (dp),             \
            DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, (propname), -1);      \
        if ((propval) == -1) {                                          \
                EIB_DPRINTF_WARN((inst), "eib_get_props: "              \
                    "ddi_prop_get_int64() could not find "              \
                    "property '%s'", (propname));                       \
                goto get_props_fail;                                    \
        }                                                               \
}

#define eib_propstr_get_and_test(inst, dp, propname, propval_p)         \
{                                                                       \
        int rv;                                                         \
                                                                        \
        *(propval_p) = NULL;                                            \
                                                                        \
        rv = ddi_prop_lookup_string(DDI_DEV_T_ANY, (dp),                \
            DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, (propname),           \
            (propval_p));                                               \
        if (rv != DDI_PROP_SUCCESS) {                                   \
                EIB_DPRINTF_WARN((inst), "eib_get_props: "              \
                    "ddi_prop_lookup_string() could not find "          \
                    "property '%s'", (propname));                       \
                goto get_props_fail;                                    \
        }                                                               \
}

/*
 * HW/FW workarounds
 */

/*
 * 1. Verification of descriptor list length in the received packets is
 *    disabled, since experimentation shows that BX does not set the desc
 *    list length correctly. True for EoIB nexus as well.
 */
int eib_wa_no_desc_list_len = 1;

/*
 * 2. LSO/Checksum_Offload for EoIB packets does not seem to be supported
 *    currently, so we'll disable both temporarily.
 */
int eib_wa_no_cksum_offload = 1;
int eib_wa_no_lso = 1;

/*
 * 3. The "multicast entry" types are not clearly defined in the spec
 *    at the moment.  The current BX software/firmware appears to ignore
 *    the type of the context table entries, so we will treat these
 *    addresses just like regular vnic addresses.
 */
int eib_wa_no_mcast_entries = 1;

/*
 * 4. VHUB updates from the gateways provide us with destination LIDs,
 *    and we will hand-create these address vectors.
 */
int eib_wa_no_av_discover = 1;

/*
 * 5. The older BX software does not seem to set the VP flag correctly
 *    in the login acknowledgements even when it successfully allocates
 *    a vlan, so we will ignore it for now.
 */
int eib_wa_no_good_vp_flag = 1;

/*
 * 6. Each vhub table is expected to carry a checksum at the end to
 *    verify the contents of the received vhub table. The current BX
 *    software/firmware does not seem to fill this field with the
 *    correct value (and/or the spec description is ambiguous). We
 *    will ignore the vhub table checksum verification for now.
 */
int eib_wa_no_good_vhub_cksum = 1;

int
eib_get_props(eib_t *ss)
{
        int val;
        int64_t val64;
        char *str;
        clock_t gw_ka_usecs;
        clock_t vnic_ka_usecs;

        ss->ei_gw_props = kmem_zalloc(sizeof (eib_gw_props_t), KM_SLEEP);
        ss->ei_props = kmem_zalloc(sizeof (eib_props_t), KM_SLEEP);

        mutex_init(&ss->ei_gw_props->pp_gw_lock, NULL, MUTEX_DRIVER, NULL);

        /*
         * The interface speed is currently set to 10Gb/s, since we don't
         * have a way yet to figure this virtual-wire specific data from
         * the gateway.  The rest of the properties are handed over to us
         * by the EoIB nexus.
         */
        ss->ei_props->ep_ifspeed = 10000000000;

        eib_prop64_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_HCA_GUID, val64);
        ss->ei_props->ep_hca_guid = (ib_guid_t)val64;

        eib_prop64_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_SYS_GUID, val64);
        ss->ei_gw_props->pp_gw_system_guid = (ib_guid_t)val64;

        eib_prop64_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_GUID, val64);
        ss->ei_gw_props->pp_gw_guid = (ib_guid_t)val64;

        eib_prop64_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_SN_PREFIX, val64);
        ss->ei_gw_props->pp_gw_sn_prefix = (ib_sn_prefix_t)val64;

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_ADV_PERIOD, val);
        ss->ei_gw_props->pp_gw_adv_period = (uint_t)val;

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_KA_PERIOD, val);
        ss->ei_gw_props->pp_gw_ka_period = (uint_t)val;

        gw_ka_usecs = ss->ei_gw_props->pp_gw_ka_period * 1000;
        gw_ka_usecs = ((gw_ka_usecs << 2) + gw_ka_usecs) >> 1;
        ss->ei_gw_props->pp_gw_ka_ticks = drv_usectohz(gw_ka_usecs);

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_VNIC_KA_PERIOD, val);
        ss->ei_gw_props->pp_vnic_ka_period = (uint_t)val;

        vnic_ka_usecs = ss->ei_gw_props->pp_vnic_ka_period * 1000;
        ss->ei_gw_props->pp_vnic_ka_ticks = drv_usectohz(vnic_ka_usecs);

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_CTRL_QPN, val);
        ss->ei_gw_props->pp_gw_ctrl_qpn = (ib_qpn_t)val;

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_LID, val);
        ss->ei_gw_props->pp_gw_lid = (ib_lid_t)val;

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_PORTID, val);
        ss->ei_gw_props->pp_gw_portid = (uint16_t)val;

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_NUM_NET_VNICS, val);
        ss->ei_gw_props->pp_gw_num_net_vnics = (uint16_t)val;

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_AVAILABLE, val);
        ss->ei_gw_props->pp_gw_flag_available = (uint8_t)val;

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_HOST_VNICS, val);
        ss->ei_gw_props->pp_gw_is_host_adm_vnics = (uint8_t)val;

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_SL, val);
        ss->ei_gw_props->pp_gw_sl = (uint8_t)val;

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_N_RSS_QPN, val);
        ss->ei_gw_props->pp_gw_n_rss_qpn = (uint8_t)val;

        eib_prop_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_HCA_PORTNUM, val);
        ss->ei_props->ep_port_num = (uint8_t)val;

        eib_propstr_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_SYS_NAME, &str);
        ss->ei_gw_props->pp_gw_system_name = (uint8_t *)str;

        eib_propstr_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_PORT_NAME, &str);
        ss->ei_gw_props->pp_gw_port_name = (uint8_t *)str;

        eib_propstr_get_and_test(ss->ei_instance, ss->ei_dip,
            EIB_PROP_GW_VENDOR_ID, &str);
        ss->ei_gw_props->pp_gw_vendor_id = (uint8_t *)str;

        return (EIB_E_SUCCESS);

get_props_fail:
        eib_rb_get_props(ss);
        return (EIB_E_FAILURE);
}

void
eib_update_props(eib_t *ss, eib_gw_info_t *new_gw_info)
{
        eib_gw_props_t *gwp = ss->ei_gw_props;
        dev_info_t *dip = ss->ei_dip;
        char *str;

        ASSERT(gwp != NULL && dip != NULL);

        mutex_enter(&gwp->pp_gw_lock);

        gwp->pp_gw_system_guid = new_gw_info->gi_system_guid;
        (void) ddi_prop_update_int64(DDI_DEV_T_NONE, dip, EIB_PROP_GW_SYS_GUID,
            gwp->pp_gw_system_guid);

        gwp->pp_gw_guid = new_gw_info->gi_guid;
        (void) ddi_prop_update_int64(DDI_DEV_T_NONE, dip, EIB_PROP_GW_GUID,
            gwp->pp_gw_guid);

        gwp->pp_gw_sn_prefix = new_gw_info->gi_sn_prefix;
        (void) ddi_prop_update_int64(DDI_DEV_T_NONE, dip, EIB_PROP_GW_SN_PREFIX,
            gwp->pp_gw_sn_prefix);

        gwp->pp_gw_adv_period = new_gw_info->gi_adv_period;
        (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_ADV_PERIOD,
            gwp->pp_gw_adv_period);

        gwp->pp_gw_ka_period = new_gw_info->gi_ka_period;
        (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_KA_PERIOD,
            gwp->pp_gw_ka_period);

        gwp->pp_vnic_ka_period = new_gw_info->gi_vnic_ka_period;
        (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_VNIC_KA_PERIOD,
            gwp->pp_vnic_ka_period);

        gwp->pp_gw_ctrl_qpn = new_gw_info->gi_ctrl_qpn;
        (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_CTRL_QPN,
            gwp->pp_gw_ctrl_qpn);

        gwp->pp_gw_lid = new_gw_info->gi_lid;
        (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_LID,
            gwp->pp_gw_lid);

        gwp->pp_gw_portid = new_gw_info->gi_portid;
        (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_PORTID,
            gwp->pp_gw_portid);

        gwp->pp_gw_num_net_vnics = new_gw_info->gi_num_net_vnics;
        (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip,
            EIB_PROP_GW_NUM_NET_VNICS, gwp->pp_gw_num_net_vnics);

        gwp->pp_gw_flag_available = new_gw_info->gi_flag_available;
        (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_AVAILABLE,
            gwp->pp_gw_flag_available);

        gwp->pp_gw_is_host_adm_vnics = new_gw_info->gi_is_host_adm_vnics;
        (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_HOST_VNICS,
            gwp->pp_gw_is_host_adm_vnics);

        gwp->pp_gw_sl = new_gw_info->gi_sl;
        (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_SL,
            gwp->pp_gw_sl);

        gwp->pp_gw_n_rss_qpn = new_gw_info->gi_n_rss_qpn;
        (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, EIB_PROP_GW_N_RSS_QPN,
            gwp->pp_gw_n_rss_qpn);

        (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
            EIB_PROP_GW_SYS_NAME, (char *)(new_gw_info->gi_system_name));
        (void) ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
            DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, EIB_PROP_GW_SYS_NAME, &str);
        if (gwp->pp_gw_system_name) {
                ddi_prop_free(gwp->pp_gw_system_name);
        }
        gwp->pp_gw_system_name = (uint8_t *)str;

        (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
            EIB_PROP_GW_PORT_NAME, (char *)(new_gw_info->gi_port_name));
        (void) ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
            DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, EIB_PROP_GW_PORT_NAME, &str);
        if (gwp->pp_gw_port_name) {
                ddi_prop_free(gwp->pp_gw_port_name);
        }
        gwp->pp_gw_port_name = (uint8_t *)str;

        (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip,
            EIB_PROP_GW_VENDOR_ID, (char *)(new_gw_info->gi_vendor_id));
        (void) ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
            DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, EIB_PROP_GW_VENDOR_ID, &str);
        if (gwp->pp_gw_vendor_id) {
                ddi_prop_free(gwp->pp_gw_vendor_id);
        }
        gwp->pp_gw_vendor_id = (uint8_t *)str;

        mutex_exit(&gwp->pp_gw_lock);
}

void
eib_rb_get_props(eib_t *ss)
{
        /*
         * Free any allocations
         */
        if (ss->ei_gw_props->pp_gw_vendor_id) {
                ddi_prop_free(ss->ei_gw_props->pp_gw_vendor_id);
                ss->ei_gw_props->pp_gw_vendor_id = NULL;
        }
        if (ss->ei_gw_props->pp_gw_port_name) {
                ddi_prop_free(ss->ei_gw_props->pp_gw_port_name);
                ss->ei_gw_props->pp_gw_port_name = NULL;
        }
        if (ss->ei_gw_props->pp_gw_system_name) {
                ddi_prop_free(ss->ei_gw_props->pp_gw_system_name);
                ss->ei_gw_props->pp_gw_system_name = NULL;
        }

        mutex_destroy(&ss->ei_gw_props->pp_gw_lock);

        /*
         * Free space allocated for holding the props
         */
        kmem_free(ss->ei_props, sizeof (eib_props_t));
        kmem_free(ss->ei_gw_props, sizeof (eib_gw_props_t));

        ss->ei_props = NULL;
        ss->ei_gw_props = NULL;
}