bnx2x: VF can report link speed
authorDmitry Kravkov <Dmitry.Kravkov@qlogic.com>
Thu, 26 Jun 2014 11:31:04 +0000 (14:31 +0300)
committerDavid S. Miller <davem@davemloft.net>
Tue, 1 Jul 2014 22:52:29 +0000 (15:52 -0700)
Until now VFs were oblvious to the actual configured link parameters.
This patch does 2 things:

  1. It enables a PF to inform its VF using the bulletin board of the link
     configured, and allows the VF to present that information.

  2. It adds support of `ndo_set_vf_link_state', allowing the hypervisor
     to set the VF link state.

Signed-off-by: Dmitry Kravkov <Dmitry.Kravkov@qlogic.com>
Signed-off-by: Yuval Mintz <Yuval.Mintz@qlogic.com>
Signed-off-by: Ariel Elior <Ariel.Elior@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h

index 4cab09d3f80729a2bd843cf6b0b6490f0a8e95ad..faf262a0d6c46391071454b88ee97a4e0e63db12 100644 (file)
@@ -1482,6 +1482,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;
@@ -1928,6 +1929,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 */
index 47c5814114e1764145f18c9c2088e8395214602e..cb15e3ac03c4bd859caa2a303684c5f714a6a60f 100644 (file)
@@ -1185,29 +1185,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;
+       }
 }
 
 /**
@@ -1261,6 +1270,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);
index bd0600cf72660f3fbc5ac9b2ad37b481fa0969a0..08ea91cab738a02c2c16d87e1eb1d37a054b881c 100644 (file)
@@ -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);
@@ -1110,6 +1147,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;
 }
 
@@ -3484,8 +3525,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,
index 2887034523e065a362dded7cf025742de29539b3..6af9e3c046a0fda9d8f661f591f6d110df5fc767 100644 (file)
@@ -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)
index c9988e3bfe2bacbfb330ae39f001ccf8777f2202..662310c5f4e98c4f7d7cdda3d0062969f13ddd5b 100644 (file)
 #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)
@@ -1327,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);
@@ -1472,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)
 {
@@ -2509,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;
        }
@@ -2534,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;
        }
@@ -2565,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) {
@@ -2590,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 */
@@ -2605,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;
@@ -2634,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
         */
@@ -2651,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;
@@ -2715,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;
@@ -2728,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)) !=
@@ -2849,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),
@@ -2862,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;
 }
@@ -2947,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:
index 96c575e147a5b14da2b67e9ea25a044a4a882d07..ca1055f3d8afda852f10412d2f551cc05b257848 100644 (file)
@@ -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 */
index d712d0ddd719bd4dd3a37b25736dfdccf156084c..f35077366340bda4c49035f3257e596f7ef939f3 100644 (file)
@@ -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),
@@ -1252,6 +1255,13 @@ static void bnx2x_vf_mbx_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf,
 
        /* 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;
+       }
 
        /* response */
        bnx2x_vf_mbx_acquire_resp(bp, vf, mbx, rc);
@@ -1273,6 +1283,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 +2021,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 +2048,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,
index e21e706762c9964ad917e69b7149700a458f74fc..ace4d7b0529f035cb83c76a70589dbeaf1a433d5 100644 (file)
@@ -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,9 @@ 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];
+               u8 padding;
+               u8 caps;
+#define VF_CAP_SUPPORT_EXT_BULLETIN    (1 << 0)
        } vfdev_info;
 
        struct vf_pf_resc_request resc_request;
@@ -393,11 +396,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 {