diff options
Diffstat (limited to 'drivers/net/ethernet/broadcom/bnx2x')
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 55 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 44 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c | 264 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h | 35 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c | 78 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h | 23 |
9 files changed, 427 insertions, 94 deletions
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 8206a293e6b4..d777fae86988 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1483,6 +1483,7 @@ struct bnx2x { union pf_vf_bulletin *pf2vf_bulletin; dma_addr_t pf2vf_bulletin_mapping; + union pf_vf_bulletin shadow_bulletin; struct pf_vf_bulletin_content old_bulletin; u16 requested_nr_virtfn; @@ -1508,8 +1509,10 @@ struct bnx2x { /* TCP with Timestamp Option (32) + IPv6 (40) */ #define ETH_MAX_TPA_HEADER_SIZE 72 - /* Max supported alignment is 256 (8 shift) */ -#define BNX2X_RX_ALIGN_SHIFT min(8, L1_CACHE_SHIFT) + /* Max supported alignment is 256 (8 shift) + * minimal alignment shift 6 is optimal for 57xxx HW performance + */ +#define BNX2X_RX_ALIGN_SHIFT max(6, min(8, L1_CACHE_SHIFT)) /* FW uses 2 Cache lines Alignment for start packet and size * @@ -1929,6 +1932,8 @@ struct bnx2x { struct semaphore stats_sema; u8 phys_port_id[ETH_ALEN]; + + struct bnx2x_link_report_data vf_link_vars; }; /* Tx queues may be less or equal to Rx queues */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index c43e7238de21..4e6c82e20224 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -1192,29 +1192,38 @@ u16 bnx2x_get_mf_speed(struct bnx2x *bp) static void bnx2x_fill_report_data(struct bnx2x *bp, struct bnx2x_link_report_data *data) { - u16 line_speed = bnx2x_get_mf_speed(bp); - memset(data, 0, sizeof(*data)); - /* Fill the report data: effective line speed */ - data->line_speed = line_speed; - - /* Link is down */ - if (!bp->link_vars.link_up || (bp->flags & MF_FUNC_DIS)) - __set_bit(BNX2X_LINK_REPORT_LINK_DOWN, - &data->link_report_flags); - - /* Full DUPLEX */ - if (bp->link_vars.duplex == DUPLEX_FULL) - __set_bit(BNX2X_LINK_REPORT_FD, &data->link_report_flags); - - /* Rx Flow Control is ON */ - if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_RX) - __set_bit(BNX2X_LINK_REPORT_RX_FC_ON, &data->link_report_flags); - - /* Tx Flow Control is ON */ - if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX) - __set_bit(BNX2X_LINK_REPORT_TX_FC_ON, &data->link_report_flags); + if (IS_PF(bp)) { + /* Fill the report data: effective line speed */ + data->line_speed = bnx2x_get_mf_speed(bp); + + /* Link is down */ + if (!bp->link_vars.link_up || (bp->flags & MF_FUNC_DIS)) + __set_bit(BNX2X_LINK_REPORT_LINK_DOWN, + &data->link_report_flags); + + if (!BNX2X_NUM_ETH_QUEUES(bp)) + __set_bit(BNX2X_LINK_REPORT_LINK_DOWN, + &data->link_report_flags); + + /* Full DUPLEX */ + if (bp->link_vars.duplex == DUPLEX_FULL) + __set_bit(BNX2X_LINK_REPORT_FD, + &data->link_report_flags); + + /* Rx Flow Control is ON */ + if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_RX) + __set_bit(BNX2X_LINK_REPORT_RX_FC_ON, + &data->link_report_flags); + + /* Tx Flow Control is ON */ + if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX) + __set_bit(BNX2X_LINK_REPORT_TX_FC_ON, + &data->link_report_flags); + } else { /* VF */ + *data = bp->vf_link_vars; + } } /** @@ -1268,6 +1277,10 @@ void __bnx2x_link_report(struct bnx2x *bp) */ memcpy(&bp->last_reported_link, &cur_data, sizeof(cur_data)); + /* propagate status to VFs */ + if (IS_PF(bp)) + bnx2x_iov_link_update(bp); + if (test_bit(BNX2X_LINK_REPORT_LINK_DOWN, &cur_data.link_report_flags)) { netif_carrier_off(bp->dev); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c index 51a952c51cb1..fb26bc4c42a1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c @@ -2303,8 +2303,8 @@ static int bnx2x_set_admin_app_up(struct bnx2x *bp, u8 idtype, u16 idval, u8 up) return 0; } -static u8 bnx2x_dcbnl_set_app_up(struct net_device *netdev, u8 idtype, - u16 idval, u8 up) +static int bnx2x_dcbnl_set_app_up(struct net_device *netdev, u8 idtype, + u16 idval, u8 up) { struct bnx2x *bp = netdev_priv(netdev); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 25eddd90f482..92fee842f954 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -216,6 +216,43 @@ static int bnx2x_get_port_type(struct bnx2x *bp) return port_type; } +static int bnx2x_get_vf_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct bnx2x *bp = netdev_priv(dev); + + if (bp->state == BNX2X_STATE_OPEN) { + if (test_bit(BNX2X_LINK_REPORT_FD, + &bp->vf_link_vars.link_report_flags)) + cmd->duplex = DUPLEX_FULL; + else + cmd->duplex = DUPLEX_HALF; + + ethtool_cmd_speed_set(cmd, bp->vf_link_vars.line_speed); + } else { + cmd->duplex = DUPLEX_UNKNOWN; + ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN); + } + + cmd->port = PORT_OTHER; + cmd->phy_address = 0; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = AUTONEG_DISABLE; + cmd->maxtxpkt = 0; + cmd->maxrxpkt = 0; + + DP(BNX2X_MSG_ETHTOOL, "ethtool_cmd: cmd %d\n" + " supported 0x%x advertising 0x%x speed %u\n" + " duplex %d port %d phy_address %d transceiver %d\n" + " autoneg %d maxtxpkt %d maxrxpkt %d\n", + cmd->cmd, cmd->supported, cmd->advertising, + ethtool_cmd_speed(cmd), + cmd->duplex, cmd->port, cmd->phy_address, cmd->transceiver, + cmd->autoneg, cmd->maxtxpkt, cmd->maxrxpkt); + + return 0; +} + static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct bnx2x *bp = netdev_priv(dev); @@ -1111,6 +1148,10 @@ static u32 bnx2x_get_link(struct net_device *dev) if (bp->flags & MF_FUNC_DIS || (bp->state != BNX2X_STATE_OPEN)) return 0; + if (IS_VF(bp)) + return !test_bit(BNX2X_LINK_REPORT_LINK_DOWN, + &bp->vf_link_vars.link_report_flags); + return bp->link_vars.link_up; } @@ -3485,8 +3526,7 @@ static const struct ethtool_ops bnx2x_ethtool_ops = { }; static const struct ethtool_ops bnx2x_vf_ethtool_ops = { - .get_settings = bnx2x_get_settings, - .set_settings = bnx2x_set_settings, + .get_settings = bnx2x_get_vf_settings, .get_drvinfo = bnx2x_get_drvinfo, .get_msglevel = bnx2x_get_msglevel, .set_msglevel = bnx2x_set_msglevel, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 6a8b1453a1b9..3871ec49cc4d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -2698,6 +2698,14 @@ void bnx2x__link_status_update(struct bnx2x *bp) bp->link_vars.duplex = DUPLEX_FULL; bp->link_vars.flow_ctrl = BNX2X_FLOW_CTRL_NONE; __bnx2x_link_report(bp); + + bnx2x_sample_bulletin(bp); + + /* if bulletin board did not have an update for link status + * __bnx2x_link_report will report current status + * but it will NOT duplicate report in case of already reported + * during sampling bulletin board. + */ bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP); } } @@ -12424,6 +12432,7 @@ static const struct net_device_ops bnx2x_netdev_ops = { .ndo_busy_poll = bnx2x_low_latency_recv, #endif .ndo_get_phys_port_id = bnx2x_get_phys_port_id, + .ndo_set_vf_link_state = bnx2x_set_vf_link_state, }; static int bnx2x_set_coherency_mask(struct bnx2x *bp) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index eda8583f6fc0..662310c5f4e9 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -24,6 +24,11 @@ #include <linux/crc32.h> #include <linux/if_vlan.h> +static int bnx2x_vf_op_prep(struct bnx2x *bp, int vfidx, + struct bnx2x_virtf **vf, + struct pf_vf_bulletin_content **bulletin, + bool test_queue); + /* General service functions */ static void storm_memset_vf_to_pf(struct bnx2x *bp, u16 abs_fid, u16 pf_id) @@ -597,8 +602,7 @@ int bnx2x_vf_mcast(struct bnx2x *bp, struct bnx2x_virtf *vf, rc = bnx2x_config_mcast(bp, &mcast, BNX2X_MCAST_CMD_DEL); if (rc) { BNX2X_ERR("Failed to remove multicasts\n"); - if (mc) - kfree(mc); + kfree(mc); return rc; } @@ -1328,6 +1332,8 @@ int bnx2x_iov_init_one(struct bnx2x *bp, int int_mode_param, /* Prepare the VFs event synchronization mechanism */ mutex_init(&bp->vfdb->event_mutex); + mutex_init(&bp->vfdb->bulletin_mutex); + return 0; failed: DP(BNX2X_MSG_IOV, "Failed err=%d\n", err); @@ -1473,6 +1479,107 @@ static void bnx2x_vfq_init(struct bnx2x *bp, struct bnx2x_virtf *vf, vf->abs_vfid, q->sp_obj.func_id, q->cid); } +static int bnx2x_max_speed_cap(struct bnx2x *bp) +{ + u32 supported = bp->port.supported[bnx2x_get_link_cfg_idx(bp)]; + + if (supported & + (SUPPORTED_20000baseMLD2_Full | SUPPORTED_20000baseKR2_Full)) + return 20000; + + return 10000; /* assume lowest supported speed is 10G */ +} + +int bnx2x_iov_link_update_vf(struct bnx2x *bp, int idx) +{ + struct bnx2x_link_report_data *state = &bp->last_reported_link; + struct pf_vf_bulletin_content *bulletin; + struct bnx2x_virtf *vf; + bool update = true; + int rc = 0; + + /* sanity and init */ + rc = bnx2x_vf_op_prep(bp, idx, &vf, &bulletin, false); + if (rc) + return rc; + + mutex_lock(&bp->vfdb->bulletin_mutex); + + if (vf->link_cfg == IFLA_VF_LINK_STATE_AUTO) { + bulletin->valid_bitmap |= 1 << LINK_VALID; + + bulletin->link_speed = state->line_speed; + bulletin->link_flags = 0; + if (test_bit(BNX2X_LINK_REPORT_LINK_DOWN, + &state->link_report_flags)) + bulletin->link_flags |= VFPF_LINK_REPORT_LINK_DOWN; + if (test_bit(BNX2X_LINK_REPORT_FD, + &state->link_report_flags)) + bulletin->link_flags |= VFPF_LINK_REPORT_FULL_DUPLEX; + if (test_bit(BNX2X_LINK_REPORT_RX_FC_ON, + &state->link_report_flags)) + bulletin->link_flags |= VFPF_LINK_REPORT_RX_FC_ON; + if (test_bit(BNX2X_LINK_REPORT_TX_FC_ON, + &state->link_report_flags)) + bulletin->link_flags |= VFPF_LINK_REPORT_TX_FC_ON; + } else if (vf->link_cfg == IFLA_VF_LINK_STATE_DISABLE && + !(bulletin->link_flags & VFPF_LINK_REPORT_LINK_DOWN)) { + bulletin->valid_bitmap |= 1 << LINK_VALID; + bulletin->link_flags |= VFPF_LINK_REPORT_LINK_DOWN; + } else if (vf->link_cfg == IFLA_VF_LINK_STATE_ENABLE && + (bulletin->link_flags & VFPF_LINK_REPORT_LINK_DOWN)) { + bulletin->valid_bitmap |= 1 << LINK_VALID; + bulletin->link_speed = bnx2x_max_speed_cap(bp); + bulletin->link_flags &= ~VFPF_LINK_REPORT_LINK_DOWN; + } else { + update = false; + } + + if (update) { + DP(NETIF_MSG_LINK | BNX2X_MSG_IOV, + "vf %d mode %u speed %d flags %x\n", idx, + vf->link_cfg, bulletin->link_speed, bulletin->link_flags); + + /* Post update on VF's bulletin board */ + rc = bnx2x_post_vf_bulletin(bp, idx); + if (rc) { + BNX2X_ERR("failed to update VF[%d] bulletin\n", idx); + goto out; + } + } + +out: + mutex_unlock(&bp->vfdb->bulletin_mutex); + return rc; +} + +int bnx2x_set_vf_link_state(struct net_device *dev, int idx, int link_state) +{ + struct bnx2x *bp = netdev_priv(dev); + struct bnx2x_virtf *vf = BP_VF(bp, idx); + + if (!vf) + return -EINVAL; + + if (vf->link_cfg == link_state) + return 0; /* nothing todo */ + + vf->link_cfg = link_state; + + return bnx2x_iov_link_update_vf(bp, idx); +} + +void bnx2x_iov_link_update(struct bnx2x *bp) +{ + int vfid; + + if (!IS_SRIOV(bp)) + return; + + for_each_vf(bp, vfid) + bnx2x_iov_link_update_vf(bp, vfid); +} + /* called by bnx2x_nic_load */ int bnx2x_iov_nic_init(struct bnx2x *bp) { @@ -2510,22 +2617,23 @@ void bnx2x_disable_sriov(struct bnx2x *bp) pci_disable_sriov(bp->pdev); } -static int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx, - struct bnx2x_virtf **vf, - struct pf_vf_bulletin_content **bulletin) +static int bnx2x_vf_op_prep(struct bnx2x *bp, int vfidx, + struct bnx2x_virtf **vf, + struct pf_vf_bulletin_content **bulletin, + bool test_queue) { if (bp->state != BNX2X_STATE_OPEN) { - BNX2X_ERR("vf ndo called though PF is down\n"); + BNX2X_ERR("PF is down - can't utilize iov-related functionality\n"); return -EINVAL; } if (!IS_SRIOV(bp)) { - BNX2X_ERR("vf ndo called though sriov is disabled\n"); + BNX2X_ERR("sriov is disabled - can't utilize iov-realted functionality\n"); return -EINVAL; } if (vfidx >= BNX2X_NR_VIRTFN(bp)) { - BNX2X_ERR("vf ndo called for uninitialized VF. vfidx was %d BNX2X_NR_VIRTFN was %d\n", + BNX2X_ERR("VF is uninitialized - can't utilize iov-related functionality. vfidx was %d BNX2X_NR_VIRTFN was %d\n", vfidx, BNX2X_NR_VIRTFN(bp)); return -EINVAL; } @@ -2535,19 +2643,18 @@ static int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx, *bulletin = BP_VF_BULLETIN(bp, vfidx); if (!*vf) { - BNX2X_ERR("vf ndo called but vf struct is null. vfidx was %d\n", - vfidx); + BNX2X_ERR("Unable to get VF structure for vfidx %d\n", vfidx); return -EINVAL; } - if (!(*vf)->vfqs) { - BNX2X_ERR("vf ndo called but vfqs struct is null. Was ndo invoked before dynamically enabling SR-IOV? vfidx was %d\n", + if (test_queue && !(*vf)->vfqs) { + BNX2X_ERR("vfqs struct is null. Was this invoked before dynamically enabling SR-IOV? vfidx was %d\n", vfidx); return -EINVAL; } if (!*bulletin) { - BNX2X_ERR("vf ndo called but Bulletin Board struct is null. vfidx was %d\n", + BNX2X_ERR("Bulletin Board struct is null for vfidx %d\n", vfidx); return -EINVAL; } @@ -2566,9 +2673,10 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, int rc; /* sanity and init */ - rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); + rc = bnx2x_vf_op_prep(bp, vfidx, &vf, &bulletin, true); if (rc) return rc; + mac_obj = &bnx2x_leading_vfq(vf, mac_obj); vlan_obj = &bnx2x_leading_vfq(vf, vlan_obj); if (!mac_obj || !vlan_obj) { @@ -2591,6 +2699,7 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, VLAN_HLEN); } } else { + mutex_lock(&bp->vfdb->bulletin_mutex); /* mac */ if (bulletin->valid_bitmap & (1 << MAC_ADDR_VALID)) /* mac configured by ndo so its in bulletin board */ @@ -2606,6 +2715,8 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx, else /* function has not been loaded yet. Show vlans as 0s */ memset(&ivi->vlan, 0, VLAN_HLEN); + + mutex_unlock(&bp->vfdb->bulletin_mutex); } return 0; @@ -2635,15 +2746,18 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac) struct bnx2x_virtf *vf = NULL; struct pf_vf_bulletin_content *bulletin = NULL; - /* sanity and init */ - rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); - if (rc) - return rc; if (!is_valid_ether_addr(mac)) { BNX2X_ERR("mac address invalid\n"); return -EINVAL; } + /* sanity and init */ + rc = bnx2x_vf_op_prep(bp, vfidx, &vf, &bulletin, true); + if (rc) + return rc; + + mutex_lock(&bp->vfdb->bulletin_mutex); + /* update PF's copy of the VF's bulletin. Will no longer accept mac * configuration requests from vf unless match this mac */ @@ -2652,6 +2766,10 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac) /* Post update on VF's bulletin board */ rc = bnx2x_post_vf_bulletin(bp, vfidx); + + /* release lock before checking return code */ + mutex_unlock(&bp->vfdb->bulletin_mutex); + if (rc) { BNX2X_ERR("failed to update VF[%d] bulletin\n", vfidx); return rc; @@ -2716,11 +2834,6 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos) unsigned long accept_flags; int rc; - /* sanity and init */ - rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin); - if (rc) - return rc; - if (vlan > 4095) { BNX2X_ERR("illegal vlan value %d\n", vlan); return -EINVAL; @@ -2729,18 +2842,27 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos) DP(BNX2X_MSG_IOV, "configuring VF %d with VLAN %d qos %d\n", vfidx, vlan, 0); + /* sanity and init */ + rc = bnx2x_vf_op_prep(bp, vfidx, &vf, &bulletin, true); + if (rc) + return rc; + /* update PF's copy of the VF's bulletin. No point in posting the vlan * to the VF since it doesn't have anything to do with it. But it useful * to store it here in case the VF is not up yet and we can only * configure the vlan later when it does. Treat vlan id 0 as remove the * Host tag. */ + mutex_lock(&bp->vfdb->bulletin_mutex); + if (vlan > 0) bulletin->valid_bitmap |= 1 << VLAN_VALID; else bulletin->valid_bitmap &= ~(1 << VLAN_VALID); bulletin->vlan = vlan; + mutex_unlock(&bp->vfdb->bulletin_mutex); + /* is vf initialized and queue set up? */ if (vf->state != VF_ENABLED || bnx2x_get_q_logical_state(bp, &bnx2x_leading_vfq(vf, sp_obj)) != @@ -2850,10 +2972,9 @@ out: * entire bulletin board excluding the crc field itself. Use the length field * as the Bulletin Board was posted by a PF with possibly a different version * from the vf which will sample it. Therefore, the length is computed by the - * PF and the used blindly by the VF. + * PF and then used blindly by the VF. */ -u32 bnx2x_crc_vf_bulletin(struct bnx2x *bp, - struct pf_vf_bulletin_content *bulletin) +u32 bnx2x_crc_vf_bulletin(struct pf_vf_bulletin_content *bulletin) { return crc32(BULLETIN_CRC_SEED, ((u8 *)bulletin) + sizeof(bulletin->crc), @@ -2863,47 +2984,74 @@ u32 bnx2x_crc_vf_bulletin(struct bnx2x *bp, /* Check for new posts on the bulletin board */ enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp) { - struct pf_vf_bulletin_content bulletin = bp->pf2vf_bulletin->content; + struct pf_vf_bulletin_content *bulletin; int attempts; - /* bulletin board hasn't changed since last sample */ - if (bp->old_bulletin.version == bulletin.version) - return PFVF_BULLETIN_UNCHANGED; + /* sampling structure in mid post may result with corrupted data + * validate crc to ensure coherency. + */ + for (attempts = 0; attempts < BULLETIN_ATTEMPTS; attempts++) { + u32 crc; - /* validate crc of new bulletin board */ - if (bp->old_bulletin.version != bp->pf2vf_bulletin->content.version) { - /* sampling structure in mid post may result with corrupted data - * validate crc to ensure coherency. - */ - for (attempts = 0; attempts < BULLETIN_ATTEMPTS; attempts++) { - bulletin = bp->pf2vf_bulletin->content; - if (bulletin.crc == bnx2x_crc_vf_bulletin(bp, - &bulletin)) - break; - BNX2X_ERR("bad crc on bulletin board. Contained %x computed %x\n", - bulletin.crc, - bnx2x_crc_vf_bulletin(bp, &bulletin)); - } - if (attempts >= BULLETIN_ATTEMPTS) { - BNX2X_ERR("pf to vf bulletin board crc was wrong %d consecutive times. Aborting\n", - attempts); - return PFVF_BULLETIN_CRC_ERR; - } + /* sample the bulletin board */ + memcpy(&bp->shadow_bulletin, bp->pf2vf_bulletin, + sizeof(union pf_vf_bulletin)); + + crc = bnx2x_crc_vf_bulletin(&bp->shadow_bulletin.content); + + if (bp->shadow_bulletin.content.crc == crc) + break; + + BNX2X_ERR("bad crc on bulletin board. Contained %x computed %x\n", + bp->shadow_bulletin.content.crc, crc); + } + + if (attempts >= BULLETIN_ATTEMPTS) { + BNX2X_ERR("pf to vf bulletin board crc was wrong %d consecutive times. Aborting\n", + attempts); + return PFVF_BULLETIN_CRC_ERR; } + bulletin = &bp->shadow_bulletin.content; + + /* bulletin board hasn't changed since last sample */ + if (bp->old_bulletin.version == bulletin->version) + return PFVF_BULLETIN_UNCHANGED; /* the mac address in bulletin board is valid and is new */ - if (bulletin.valid_bitmap & 1 << MAC_ADDR_VALID && - !ether_addr_equal(bulletin.mac, bp->old_bulletin.mac)) { + if (bulletin->valid_bitmap & 1 << MAC_ADDR_VALID && + !ether_addr_equal(bulletin->mac, bp->old_bulletin.mac)) { /* update new mac to net device */ - memcpy(bp->dev->dev_addr, bulletin.mac, ETH_ALEN); + memcpy(bp->dev->dev_addr, bulletin->mac, ETH_ALEN); + } + + if (bulletin->valid_bitmap & (1 << LINK_VALID)) { + DP(BNX2X_MSG_IOV, "link update speed %d flags %x\n", + bulletin->link_speed, bulletin->link_flags); + + bp->vf_link_vars.line_speed = bulletin->link_speed; + bp->vf_link_vars.link_report_flags = 0; + /* Link is down */ + if (bulletin->link_flags & VFPF_LINK_REPORT_LINK_DOWN) + __set_bit(BNX2X_LINK_REPORT_LINK_DOWN, + &bp->vf_link_vars.link_report_flags); + /* Full DUPLEX */ + if (bulletin->link_flags & VFPF_LINK_REPORT_FULL_DUPLEX) + __set_bit(BNX2X_LINK_REPORT_FD, + &bp->vf_link_vars.link_report_flags); + /* Rx Flow Control is ON */ + if (bulletin->link_flags & VFPF_LINK_REPORT_RX_FC_ON) + __set_bit(BNX2X_LINK_REPORT_RX_FC_ON, + &bp->vf_link_vars.link_report_flags); + /* Tx Flow Control is ON */ + if (bulletin->link_flags & VFPF_LINK_REPORT_TX_FC_ON) + __set_bit(BNX2X_LINK_REPORT_TX_FC_ON, + &bp->vf_link_vars.link_report_flags); + __bnx2x_link_report(bp); } - /* the vlan in bulletin board is valid and is new */ - if (bulletin.valid_bitmap & 1 << VLAN_VALID) - memcpy(&bulletin.vlan, &bp->old_bulletin.vlan, VLAN_HLEN); - /* copy new bulletin board to bp */ - bp->old_bulletin = bulletin; + memcpy(&bp->old_bulletin, bulletin, + sizeof(struct pf_vf_bulletin_content)); return PFVF_BULLETIN_UPDATED; } @@ -2948,6 +3096,8 @@ int bnx2x_vf_pci_alloc(struct bnx2x *bp) if (!bp->pf2vf_bulletin) goto alloc_mem_err; + bnx2x_vf_bulletin_finalize(&bp->pf2vf_bulletin->content, true); + return 0; alloc_mem_err: diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h index 96c575e147a5..ca1055f3d8af 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h @@ -126,7 +126,11 @@ struct bnx2x_virtf { #define VF_CACHE_LINE 0x0010 #define VF_CFG_VLAN 0x0020 #define VF_CFG_STATS_COALESCE 0x0040 - +#define VF_CFG_EXT_BULLETIN 0x0080 + u8 link_cfg; /* IFLA_VF_LINK_STATE_AUTO + * IFLA_VF_LINK_STATE_ENABLE + * IFLA_VF_LINK_STATE_DISABLE + */ u8 state; #define VF_FREE 0 /* VF ready to be acquired holds no resc */ #define VF_ACQUIRED 1 /* VF acquired, but not initialized */ @@ -295,22 +299,22 @@ struct bnx2x_vfdb { #define BP_VFDB(bp) ((bp)->vfdb) /* vf array */ struct bnx2x_virtf *vfs; -#define BP_VF(bp, idx) (&((bp)->vfdb->vfs[(idx)])) -#define bnx2x_vf(bp, idx, var) ((bp)->vfdb->vfs[(idx)].var) +#define BP_VF(bp, idx) (&((bp)->vfdb->vfs[idx])) +#define bnx2x_vf(bp, idx, var) ((bp)->vfdb->vfs[idx].var) /* queue array - for all vfs */ struct bnx2x_vf_queue *vfqs; /* vf HW contexts */ struct hw_dma context[BNX2X_VF_CIDS/ILT_PAGE_CIDS]; -#define BP_VF_CXT_PAGE(bp, i) (&(bp)->vfdb->context[(i)]) +#define BP_VF_CXT_PAGE(bp, i) (&(bp)->vfdb->context[i]) /* SR-IOV information */ struct bnx2x_sriov sriov; struct hw_dma mbx_dma; #define BP_VF_MBX_DMA(bp) (&((bp)->vfdb->mbx_dma)) struct bnx2x_vf_mbx mbxs[BNX2X_MAX_NUM_OF_VFS]; -#define BP_VF_MBX(bp, vfid) (&((bp)->vfdb->mbxs[(vfid)])) +#define BP_VF_MBX(bp, vfid) (&((bp)->vfdb->mbxs[vfid])) struct hw_dma bulletin_dma; #define BP_VF_BULLETIN_DMA(bp) (&((bp)->vfdb->bulletin_dma)) @@ -336,6 +340,9 @@ struct bnx2x_vfdb { /* sp_rtnl synchronization */ struct mutex event_mutex; u64 event_occur; + + /* bulletin board update synchronization */ + struct mutex bulletin_mutex; }; /* queue access */ @@ -467,9 +474,10 @@ void bnx2x_vf_handle_flr_event(struct bnx2x *bp); bool bnx2x_tlv_supported(u16 tlvtype); -u32 bnx2x_crc_vf_bulletin(struct bnx2x *bp, - struct pf_vf_bulletin_content *bulletin); +u32 bnx2x_crc_vf_bulletin(struct pf_vf_bulletin_content *bulletin); int bnx2x_post_vf_bulletin(struct bnx2x *bp, int vf); +void bnx2x_vf_bulletin_finalize(struct pf_vf_bulletin_content *bulletin, + bool support_long); enum sample_bulletin_result bnx2x_sample_bulletin(struct bnx2x *bp); @@ -520,6 +528,11 @@ void bnx2x_iov_task(struct work_struct *work); void bnx2x_schedule_iov_task(struct bnx2x *bp, enum bnx2x_iov_flag flag); +void bnx2x_iov_link_update(struct bnx2x *bp); +int bnx2x_iov_link_update_vf(struct bnx2x *bp, int idx); + +int bnx2x_set_vf_link_state(struct net_device *dev, int vf, int link_state); + #else /* CONFIG_BNX2X_SRIOV */ static inline void bnx2x_iov_set_queue_sp_obj(struct bnx2x *bp, int vf_cid, @@ -579,6 +592,14 @@ static inline void bnx2x_iov_channel_down(struct bnx2x *bp) {} static inline void bnx2x_iov_task(struct work_struct *work) {} static inline void bnx2x_schedule_iov_task(struct bnx2x *bp, enum bnx2x_iov_flag flag) {} +static inline void bnx2x_iov_link_update(struct bnx2x *bp) {} +static inline int bnx2x_iov_link_update_vf(struct bnx2x *bp, int idx) {return 0; } + +static inline int bnx2x_set_vf_link_state(struct net_device *dev, int vf, + int link_state) {return 0; } +struct pf_vf_bulletin_content; +static inline void bnx2x_vf_bulletin_finalize(struct pf_vf_bulletin_content *bulletin, + bool support_long) {} #endif /* CONFIG_BNX2X_SRIOV */ #endif /* bnx2x_sriov.h */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c index d712d0ddd719..54e0427a9ee6 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c @@ -251,6 +251,9 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count) bnx2x_add_tlv(bp, req, req->first_tlv.tl.length, CHANNEL_TLV_PHYS_PORT_ID, sizeof(struct channel_tlv)); + /* Bulletin support for bulletin board with length > legacy length */ + req->vfdev_info.caps |= VF_CAP_SUPPORT_EXT_BULLETIN; + /* add list termination tlv */ bnx2x_add_tlv(bp, req, req->first_tlv.tl.length + sizeof(struct channel_tlv), @@ -1232,6 +1235,41 @@ static void bnx2x_vf_mbx_acquire_resp(struct bnx2x *bp, struct bnx2x_virtf *vf, bnx2x_vf_mbx_resp_send_msg(bp, vf, vfop_status); } +static bool bnx2x_vf_mbx_is_windows_vm(struct bnx2x *bp, + struct vfpf_acquire_tlv *acquire) +{ + /* Windows driver does one of three things: + * 1. Old driver doesn't have bulletin board address set. + * 2. 'Middle' driver sends mc_num == 32. + * 3. New driver sets the OS field. + */ + if (!acquire->bulletin_addr || + acquire->resc_request.num_mc_filters == 32 || + ((acquire->vfdev_info.vf_os & VF_OS_MASK) == + VF_OS_WINDOWS)) + return true; + + return false; +} + +static int bnx2x_vf_mbx_acquire_chk_dorq(struct bnx2x *bp, + struct bnx2x_virtf *vf, + struct bnx2x_vf_mbx *mbx) +{ + /* Linux drivers which correctly set the doorbell size also + * send a physical port request + */ + if (bnx2x_search_tlv_list(bp, &mbx->msg->req, + CHANNEL_TLV_PHYS_PORT_ID)) + return 0; + + /* Issue does not exist in windows VMs */ + if (bnx2x_vf_mbx_is_windows_vm(bp, &mbx->msg->req.acquire)) + return 0; + + return -EOPNOTSUPP; +} + static void bnx2x_vf_mbx_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf, struct bnx2x_vf_mbx *mbx) { @@ -1247,12 +1285,32 @@ static void bnx2x_vf_mbx_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf, acquire->resc_request.num_vlan_filters, acquire->resc_request.num_mc_filters); + /* Prevent VFs with old drivers from loading, since they calculate + * CIDs incorrectly requiring a VF-flr [VM reboot] in order to recover + * while being upgraded. + */ + rc = bnx2x_vf_mbx_acquire_chk_dorq(bp, vf, mbx); + if (rc) { + DP(BNX2X_MSG_IOV, + "VF [%d] - Can't support acquire request due to doorbell mismatch. Please update VM driver\n", + vf->abs_vfid); + goto out; + } + /* acquire the resources */ rc = bnx2x_vf_acquire(bp, vf, &acquire->resc_request); /* store address of vf's bulletin board */ vf->bulletin_map = acquire->bulletin_addr; + if (acquire->vfdev_info.caps & VF_CAP_SUPPORT_EXT_BULLETIN) { + DP(BNX2X_MSG_IOV, "VF[%d] supports long bulletin boards\n", + vf->abs_vfid); + vf->cfg_flags |= VF_CFG_EXT_BULLETIN; + } else { + vf->cfg_flags &= ~VF_CFG_EXT_BULLETIN; + } +out: /* response */ bnx2x_vf_mbx_acquire_resp(bp, vf, mbx, rc); } @@ -1273,6 +1331,10 @@ static void bnx2x_vf_mbx_init_vf(struct bnx2x *bp, struct bnx2x_virtf *vf, if (init->flags & VFPF_INIT_FLG_STATS_COALESCE) vf->cfg_flags |= VF_CFG_STATS_COALESCE; + /* Update VF's view of link state */ + if (vf->cfg_flags & VF_CFG_EXT_BULLETIN) + bnx2x_iov_link_update_vf(bp, vf->index); + /* response */ bnx2x_vf_mbx_resp(bp, vf, rc); } @@ -2007,6 +2069,17 @@ void bnx2x_vf_mbx(struct bnx2x *bp) } } +void bnx2x_vf_bulletin_finalize(struct pf_vf_bulletin_content *bulletin, + bool support_long) +{ + /* Older VFs contain a bug where they can't check CRC for bulletin + * boards of length greater than legacy size. + */ + bulletin->length = support_long ? BULLETIN_CONTENT_SIZE : + BULLETIN_CONTENT_LEGACY_SIZE; + bulletin->crc = bnx2x_crc_vf_bulletin(bulletin); +} + /* propagate local bulletin board to vf */ int bnx2x_post_vf_bulletin(struct bnx2x *bp, int vf) { @@ -2023,8 +2096,9 @@ int bnx2x_post_vf_bulletin(struct bnx2x *bp, int vf) /* increment bulletin board version and compute crc */ bulletin->version++; - bulletin->length = BULLETIN_CONTENT_SIZE; - bulletin->crc = bnx2x_crc_vf_bulletin(bp, bulletin); + bnx2x_vf_bulletin_finalize(bulletin, + (bnx2x_vf(bp, vf, cfg_flags) & + VF_CFG_EXT_BULLETIN) ? true : false); /* propagate bulletin board via dmae to vm memory */ rc = bnx2x_copy32_vf_dmae(bp, false, pf_addr, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h index e21e706762c9..15670c499a20 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h @@ -65,6 +65,7 @@ struct hw_sb_info { #define VFPF_RX_MASK_ACCEPT_ALL_MULTICAST 0x00000008 #define VFPF_RX_MASK_ACCEPT_BROADCAST 0x00000010 #define BULLETIN_CONTENT_SIZE (sizeof(struct pf_vf_bulletin_content)) +#define BULLETIN_CONTENT_LEGACY_SIZE (32) #define BULLETIN_ATTEMPTS 5 /* crc failures before throwing towel */ #define BULLETIN_CRC_SEED 0 @@ -117,7 +118,15 @@ struct vfpf_acquire_tlv { /* the following fields are for debug purposes */ u8 vf_id; /* ME register value */ u8 vf_os; /* e.g. Linux, W2K8 */ - u8 padding[2]; +#define VF_OS_SUBVERSION_MASK (0x1f) +#define VF_OS_MASK (0xe0) +#define VF_OS_SHIFT (5) +#define VF_OS_UNDEFINED (0 << VF_OS_SHIFT) +#define VF_OS_WINDOWS (1 << VF_OS_SHIFT) + + u8 padding; + u8 caps; +#define VF_CAP_SUPPORT_EXT_BULLETIN (1 << 0) } vfdev_info; struct vf_pf_resc_request resc_request; @@ -393,11 +402,23 @@ struct pf_vf_bulletin_content { * to attempt to send messages on the * channel after this bit is set */ +#define LINK_VALID 3 /* alert the VF thet a new link status + * update is available for it + */ u8 mac[ETH_ALEN]; u8 mac_padding[2]; u16 vlan; u8 vlan_padding[6]; + + u16 link_speed; /* Effective line speed */ + u8 link_speed_padding[6]; + u32 link_flags; /* VFPF_LINK_REPORT_XXX flags */ +#define VFPF_LINK_REPORT_LINK_DOWN (1 << 0) +#define VFPF_LINK_REPORT_FULL_DUPLEX (1 << 1) +#define VFPF_LINK_REPORT_RX_FC_ON (1 << 2) +#define VFPF_LINK_REPORT_TX_FC_ON (1 << 3) + u8 link_flags_padding[4]; }; union pf_vf_bulletin { |