net/bnxt: fix race between interrupt handler and dev config
[dpdk.git] / drivers / net / bnxt / bnxt_ethdev.c
index 9f8a63a..e7ec99e 100644 (file)
@@ -14,7 +14,6 @@
 #include <rte_alarm.h>
 
 #include "bnxt.h"
-#include "bnxt_cpr.h"
 #include "bnxt_filter.h"
 #include "bnxt_hwrm.h"
 #include "bnxt_irq.h"
 #include "bnxt_vnic.h"
 #include "hsi_struct_def_dpdk.h"
 #include "bnxt_nvm_defs.h"
-#include "bnxt_util.h"
 
 #define DRV_MODULE_NAME                "bnxt"
 static const char bnxt_version[] =
        "Broadcom NetXtreme driver " DRV_MODULE_NAME;
 int bnxt_logtype_driver;
 
-#define PCI_VENDOR_ID_BROADCOM 0x14E4
-
-#define BROADCOM_DEV_ID_STRATUS_NIC_VF1 0x1606
-#define BROADCOM_DEV_ID_STRATUS_NIC_VF2 0x1609
-#define BROADCOM_DEV_ID_STRATUS_NIC 0x1614
-#define BROADCOM_DEV_ID_57414_VF 0x16c1
-#define BROADCOM_DEV_ID_57301 0x16c8
-#define BROADCOM_DEV_ID_57302 0x16c9
-#define BROADCOM_DEV_ID_57304_PF 0x16ca
-#define BROADCOM_DEV_ID_57304_VF 0x16cb
-#define BROADCOM_DEV_ID_57417_MF 0x16cc
-#define BROADCOM_DEV_ID_NS2 0x16cd
-#define BROADCOM_DEV_ID_57311 0x16ce
-#define BROADCOM_DEV_ID_57312 0x16cf
-#define BROADCOM_DEV_ID_57402 0x16d0
-#define BROADCOM_DEV_ID_57404 0x16d1
-#define BROADCOM_DEV_ID_57406_PF 0x16d2
-#define BROADCOM_DEV_ID_57406_VF 0x16d3
-#define BROADCOM_DEV_ID_57402_MF 0x16d4
-#define BROADCOM_DEV_ID_57407_RJ45 0x16d5
-#define BROADCOM_DEV_ID_57412 0x16d6
-#define BROADCOM_DEV_ID_57414 0x16d7
-#define BROADCOM_DEV_ID_57416_RJ45 0x16d8
-#define BROADCOM_DEV_ID_57417_RJ45 0x16d9
-#define BROADCOM_DEV_ID_5741X_VF 0x16dc
-#define BROADCOM_DEV_ID_57412_MF 0x16de
-#define BROADCOM_DEV_ID_57314 0x16df
-#define BROADCOM_DEV_ID_57317_RJ45 0x16e0
-#define BROADCOM_DEV_ID_5731X_VF 0x16e1
-#define BROADCOM_DEV_ID_57417_SFP 0x16e2
-#define BROADCOM_DEV_ID_57416_SFP 0x16e3
-#define BROADCOM_DEV_ID_57317_SFP 0x16e4
-#define BROADCOM_DEV_ID_57404_MF 0x16e7
-#define BROADCOM_DEV_ID_57406_MF 0x16e8
-#define BROADCOM_DEV_ID_57407_SFP 0x16e9
-#define BROADCOM_DEV_ID_57407_MF 0x16ea
-#define BROADCOM_DEV_ID_57414_MF 0x16ec
-#define BROADCOM_DEV_ID_57416_MF 0x16ee
-#define BROADCOM_DEV_ID_57508 0x1750
-#define BROADCOM_DEV_ID_57504 0x1751
-#define BROADCOM_DEV_ID_57502 0x1752
-#define BROADCOM_DEV_ID_57500_VF1 0x1806
-#define BROADCOM_DEV_ID_57500_VF2 0x1807
-#define BROADCOM_DEV_ID_58802 0xd802
-#define BROADCOM_DEV_ID_58804 0xd804
-#define BROADCOM_DEV_ID_58808 0x16f0
-#define BROADCOM_DEV_ID_58802_VF 0xd800
-
+/*
+ * The set of PCI devices this driver supports
+ */
 static const struct rte_pci_id bnxt_pci_id_map[] = {
        { RTE_PCI_DEVICE(PCI_VENDOR_ID_BROADCOM,
                         BROADCOM_DEV_ID_STRATUS_NIC_VF1) },
@@ -163,7 +116,8 @@ static const struct rte_pci_id bnxt_pci_id_map[] = {
                                     DEV_RX_OFFLOAD_JUMBO_FRAME | \
                                     DEV_RX_OFFLOAD_KEEP_CRC | \
                                     DEV_RX_OFFLOAD_VLAN_EXTEND | \
-                                    DEV_RX_OFFLOAD_TCP_LRO)
+                                    DEV_RX_OFFLOAD_TCP_LRO | \
+                                    DEV_RX_OFFLOAD_SCATTER)
 
 static int bnxt_vlan_offload_set_op(struct rte_eth_dev *dev, int mask);
 static void bnxt_print_link_info(struct rte_eth_dev *eth_dev);
@@ -223,6 +177,7 @@ static void bnxt_free_mem(struct bnxt *bp, bool reconfig)
                bnxt_free_rx_rings(bp);
        }
        bnxt_free_async_cp_ring(bp);
+       bnxt_free_rxtx_nq_ring(bp);
 }
 
 static int bnxt_alloc_mem(struct bnxt *bp, bool reconfig)
@@ -253,6 +208,10 @@ static int bnxt_alloc_mem(struct bnxt *bp, bool reconfig)
        if (rc)
                goto alloc_mem_err;
 
+       rc = bnxt_alloc_rxtx_nq_ring(bp);
+       if (rc)
+               goto alloc_mem_err;
+
        return 0;
 
 alloc_mem_err:
@@ -308,6 +267,25 @@ static int bnxt_init_chip(struct bnxt *bp)
                goto err_out;
        }
 
+       if (!(bp->vnic_cap_flags & BNXT_VNIC_CAP_COS_CLASSIFY))
+               goto skip_cosq_cfg;
+
+       for (j = 0, i = 0; i < BNXT_COS_QUEUE_COUNT; i++) {
+               if (bp->rx_cos_queue[i].id != 0xff) {
+                       struct bnxt_vnic_info *vnic = &bp->vnic_info[j++];
+
+                       if (!vnic) {
+                               PMD_DRV_LOG(ERR,
+                                           "Num pools more than FW profile\n");
+                               rc = -EINVAL;
+                               goto err_out;
+                       }
+                       vnic->cos_queue_id = bp->rx_cos_queue[i].id;
+                       bp->rx_cosq_cnt++;
+               }
+       }
+
+skip_cosq_cfg:
        rc = bnxt_mq_rx_configure(bp);
        if (rc) {
                PMD_DRV_LOG(ERR, "MQ mode configure failure rc: %x\n", rc);
@@ -644,11 +622,36 @@ static int bnxt_dev_configure_op(struct rte_eth_dev *eth_dev)
                        return -ENOSPC;
                }
 
+               /* If a resource has already been allocated - in this case
+                * it is the async completion ring, free it. Reallocate it after
+                * resource reservation. This will ensure the resource counts
+                * are calculated correctly.
+                */
+
+               pthread_mutex_lock(&bp->def_cp_lock);
+
+               if (!BNXT_HAS_NQ(bp) && bp->async_cp_ring) {
+                       bnxt_disable_int(bp);
+                       bnxt_free_cp_ring(bp, bp->async_cp_ring);
+               }
+
                rc = bnxt_hwrm_func_reserve_vf_resc(bp, false);
                if (rc) {
                        PMD_DRV_LOG(ERR, "HWRM resource alloc fail:%x\n", rc);
+                       pthread_mutex_unlock(&bp->def_cp_lock);
                        return -ENOSPC;
                }
+
+               if (!BNXT_HAS_NQ(bp) && bp->async_cp_ring) {
+                       rc = bnxt_alloc_async_cp_ring(bp);
+                       if (rc) {
+                               pthread_mutex_unlock(&bp->def_cp_lock);
+                               return rc;
+                       }
+                       bnxt_enable_int(bp);
+               }
+
+               pthread_mutex_unlock(&bp->def_cp_lock);
        } else {
                /* legacy driver needs to get updated values */
                rc = bnxt_hwrm_func_qcaps(bp);
@@ -725,6 +728,9 @@ static int bnxt_scattered_rx(struct rte_eth_dev *eth_dev)
        uint16_t buf_size;
        int i;
 
+       if (eth_dev->data->dev_conf.rxmode.offloads & DEV_RX_OFFLOAD_SCATTER)
+               return 1;
+
        for (i = 0; i < eth_dev->data->nb_rx_queues; i++) {
                struct bnxt_rx_queue *rxq = eth_dev->data->rx_queues[i];
 
@@ -831,7 +837,6 @@ static int bnxt_dev_start_op(struct rte_eth_dev *eth_dev)
                        bp->rx_cp_nr_rings, RTE_ETHDEV_QUEUE_STAT_CNTRS);
        }
 
-       bnxt_enable_int(bp);
        rc = bnxt_hwrm_if_change(bp, 1);
        if (!rc) {
                if (bp->flags & BNXT_FLAG_IF_CHANGE_HOT_FW_RESET_DONE) {
@@ -840,6 +845,7 @@ static int bnxt_dev_start_op(struct rte_eth_dev *eth_dev)
                                return rc;
                }
        }
+       bnxt_enable_int(bp);
 
        rc = bnxt_init_chip(bp);
        if (rc)
@@ -885,7 +891,7 @@ static int bnxt_dev_set_link_up_op(struct rte_eth_dev *eth_dev)
                eth_dev->data->dev_link.link_status = 1;
 
        bnxt_print_link_info(eth_dev);
-       return 0;
+       return rc;
 }
 
 static int bnxt_dev_set_link_down_op(struct rte_eth_dev *eth_dev)
@@ -924,8 +930,12 @@ static void bnxt_dev_stop_op(struct rte_eth_dev *eth_dev)
                eth_dev->data->dev_link.link_status = 0;
        }
        bnxt_dev_set_link_down_op(eth_dev);
-       /* Wait for link to be reset and the async notification to process. */
-       rte_delay_ms(BNXT_LINK_WAIT_INTERVAL * 2);
+
+       /* Wait for link to be reset and the async notification to process.
+        * During reset recovery, there is no need to wait
+        */
+       if (!is_bnxt_in_error(bp))
+               rte_delay_ms(BNXT_LINK_WAIT_INTERVAL * 2);
 
        /* Clean queue intr-vector mapping */
        rte_intr_efd_disable(intr_handle);
@@ -1001,6 +1011,53 @@ static void bnxt_mac_addr_remove_op(struct rte_eth_dev *eth_dev,
        }
 }
 
+static int bnxt_add_mac_filter(struct bnxt *bp, struct bnxt_vnic_info *vnic,
+                              struct rte_ether_addr *mac_addr, uint32_t index)
+{
+       struct bnxt_filter_info *filter;
+       int rc = 0;
+
+       filter = STAILQ_FIRST(&vnic->filter);
+       /* During bnxt_mac_addr_add_op, default MAC is
+        * already programmed, so skip it. But, when
+        * hw-vlan-filter is turned OFF from ON, default
+        * MAC filter should be restored
+        */
+       if (filter->dflt)
+               return 0;
+
+       filter = bnxt_alloc_filter(bp);
+       if (!filter) {
+               PMD_DRV_LOG(ERR, "L2 filter alloc failed\n");
+               return -ENODEV;
+       }
+
+       filter->mac_index = index;
+       /* bnxt_alloc_filter copies default MAC to filter->l2_addr. So,
+        * if the MAC that's been programmed now is a different one, then,
+        * copy that addr to filter->l2_addr
+        */
+       if (mac_addr)
+               memcpy(filter->l2_addr, mac_addr, RTE_ETHER_ADDR_LEN);
+       filter->flags |= HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_OUTERMOST;
+
+       rc = bnxt_hwrm_set_l2_filter(bp, vnic->fw_vnic_id, filter);
+       if (!rc) {
+               if (filter->mac_index == 0) {
+                       filter->dflt = true;
+                       STAILQ_INSERT_HEAD(&vnic->filter, filter, next);
+               } else {
+                       STAILQ_INSERT_TAIL(&vnic->filter, filter, next);
+               }
+       } else {
+               filter->mac_index = INVALID_MAC_INDEX;
+               memset(&filter->l2_addr, 0, RTE_ETHER_ADDR_LEN);
+               bnxt_free_filter(bp, filter);
+       }
+
+       return rc;
+}
+
 static int bnxt_mac_addr_add_op(struct rte_eth_dev *eth_dev,
                                struct rte_ether_addr *mac_addr,
                                uint32_t index, uint32_t pool)
@@ -1031,24 +1088,8 @@ static int bnxt_mac_addr_add_op(struct rte_eth_dev *eth_dev,
                        return 0;
                }
        }
-       filter = bnxt_alloc_filter(bp);
-       if (!filter) {
-               PMD_DRV_LOG(ERR, "L2 filter alloc failed\n");
-               return -ENODEV;
-       }
-
-       filter->mac_index = index;
-       memcpy(filter->l2_addr, mac_addr, RTE_ETHER_ADDR_LEN);
-       filter->flags |= HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_OUTERMOST;
 
-       rc = bnxt_hwrm_set_l2_filter(bp, vnic->fw_vnic_id, filter);
-       if (!rc) {
-               STAILQ_INSERT_TAIL(&vnic->filter, filter, next);
-       } else {
-               filter->mac_index = INVALID_MAC_INDEX;
-               memset(&filter->l2_addr, 0, RTE_ETHER_ADDR_LEN);
-               bnxt_free_filter(bp, filter);
-       }
+       rc = bnxt_add_mac_filter(bp, vnic, mac_addr, index);
 
        return rc;
 }
@@ -1683,9 +1724,10 @@ static int bnxt_del_vlan_filter(struct bnxt *bp, uint16_t vlan_id)
        filter = STAILQ_FIRST(&vnic->filter);
        while (filter) {
                /* Search for this matching MAC+VLAN filter */
-               if (filter->enables & chk && filter->l2_ivlan == vlan_id &&
-                   !memcmp(filter->l2_addr,
-                           bp->mac_addr,
+               if ((filter->enables & chk) &&
+                   (filter->l2_ivlan == vlan_id &&
+                    filter->l2_ivlan_mask != 0) &&
+                   !memcmp(filter->l2_addr, bp->mac_addr,
                            RTE_ETHER_ADDR_LEN)) {
                        /* Delete the filter */
                        rc = bnxt_hwrm_clear_l2_filter(bp, filter);
@@ -1726,8 +1768,11 @@ static int bnxt_add_vlan_filter(struct bnxt *bp, uint16_t vlan_id)
        filter = STAILQ_FIRST(&vnic->filter);
        /* Check if the VLAN has already been added */
        while (filter) {
-               if (filter->enables & chk && filter->l2_ivlan == vlan_id &&
-                   !memcmp(filter->l2_addr, bp->mac_addr, RTE_ETHER_ADDR_LEN))
+               if ((filter->enables & chk) &&
+                   (filter->l2_ivlan == vlan_id &&
+                    filter->l2_ivlan_mask == 0x0FFF) &&
+                    !memcmp(filter->l2_addr, bp->mac_addr,
+                            RTE_ETHER_ADDR_LEN))
                        return -EEXIST;
 
                filter = STAILQ_NEXT(filter, next);
@@ -1743,10 +1788,17 @@ static int bnxt_add_vlan_filter(struct bnxt *bp, uint16_t vlan_id)
                return -ENOMEM;
        }
        /* MAC + VLAN ID filter */
+       /* If l2_ivlan == 0 and l2_ivlan_mask != 0, only
+        * untagged packets are received
+        *
+        * If l2_ivlan != 0 and l2_ivlan_mask != 0, untagged
+        * packets and only the programmed vlan's packets are received
+        */
        filter->l2_ivlan = vlan_id;
        filter->l2_ivlan_mask = 0x0FFF;
        filter->enables |= en;
        filter->flags |= HWRM_CFA_L2_FILTER_ALLOC_INPUT_FLAGS_OUTERMOST;
+
        rc = bnxt_hwrm_set_l2_filter(bp, vnic->fw_vnic_id, filter);
        if (rc) {
                /* Free the newly allocated filter as we were
@@ -1755,10 +1807,16 @@ static int bnxt_add_vlan_filter(struct bnxt *bp, uint16_t vlan_id)
                filter->fw_l2_filter_id = UINT64_MAX;
                STAILQ_INSERT_TAIL(&bp->free_filter_list, filter, next);
                return rc;
+       } else {
+               /* Add this new filter to the list */
+               if (vlan_id == 0) {
+                       filter->dflt = true;
+                       STAILQ_INSERT_HEAD(&vnic->filter, filter, next);
+               } else {
+                       STAILQ_INSERT_TAIL(&vnic->filter, filter, next);
+               }
        }
 
-       /* Add this new filter to the list */
-       STAILQ_INSERT_TAIL(&vnic->filter, filter, next);
        PMD_DRV_LOG(INFO,
                    "Added Vlan filter for %d\n", vlan_id);
        return rc;
@@ -1781,11 +1839,39 @@ static int bnxt_vlan_filter_set_op(struct rte_eth_dev *eth_dev,
                return bnxt_del_vlan_filter(bp, vlan_id);
 }
 
+static int bnxt_del_dflt_mac_filter(struct bnxt *bp,
+                                   struct bnxt_vnic_info *vnic)
+{
+       struct bnxt_filter_info *filter;
+       int rc;
+
+       filter = STAILQ_FIRST(&vnic->filter);
+       while (filter) {
+               if (filter->dflt &&
+                   !memcmp(filter->l2_addr, bp->mac_addr,
+                           RTE_ETHER_ADDR_LEN)) {
+                       rc = bnxt_hwrm_clear_l2_filter(bp, filter);
+                       if (rc)
+                               return rc;
+                       filter->dflt = false;
+                       STAILQ_REMOVE(&vnic->filter, filter,
+                                     bnxt_filter_info, next);
+                       STAILQ_INSERT_TAIL(&bp->free_filter_list,
+                                          filter, next);
+                       filter->fw_l2_filter_id = -1;
+                       break;
+               }
+               filter = STAILQ_NEXT(filter, next);
+       }
+       return 0;
+}
+
 static int
 bnxt_vlan_offload_set_op(struct rte_eth_dev *dev, int mask)
 {
        struct bnxt *bp = dev->data->dev_private;
        uint64_t rx_offloads = dev->data->dev_conf.rxmode.offloads;
+       struct bnxt_vnic_info *vnic;
        unsigned int i;
        int rc;
 
@@ -1793,15 +1879,28 @@ bnxt_vlan_offload_set_op(struct rte_eth_dev *dev, int mask)
        if (rc)
                return rc;
 
-       if (mask & ETH_VLAN_FILTER_MASK) {
-               if (!(rx_offloads & DEV_RX_OFFLOAD_VLAN_FILTER)) {
-                       /* Remove any VLAN filters programmed */
-                       for (i = 0; i < 4095; i++)
-                               bnxt_del_vlan_filter(bp, i);
-               }
-               PMD_DRV_LOG(DEBUG, "VLAN Filtering: %d\n",
-                       !!(rx_offloads & DEV_RX_OFFLOAD_VLAN_FILTER));
+       vnic = BNXT_GET_DEFAULT_VNIC(bp);
+       if (!(rx_offloads & DEV_RX_OFFLOAD_VLAN_FILTER)) {
+               /* Remove any VLAN filters programmed */
+               for (i = 0; i < 4095; i++)
+                       bnxt_del_vlan_filter(bp, i);
+
+               rc = bnxt_add_mac_filter(bp, vnic, NULL, 0);
+               if (rc)
+                       return rc;
+       } else {
+               /* Default filter will allow packets that match the
+                * dest mac. So, it has to be deleted, otherwise, we
+                * will endup receiving vlan packets for which the
+                * filter is not programmed, when hw-vlan-filter
+                * configuration is ON
+                */
+               bnxt_del_dflt_mac_filter(bp, vnic);
+               /* This filter will allow only untagged packets */
+               bnxt_add_vlan_filter(bp, 0);
        }
+       PMD_DRV_LOG(DEBUG, "VLAN Filtering: %d\n",
+                   !!(rx_offloads & DEV_RX_OFFLOAD_VLAN_FILTER));
 
        if (mask & ETH_VLAN_STRIP_MASK) {
                /* Enable or disable VLAN stripping */
@@ -1964,6 +2063,10 @@ bnxt_dev_set_mc_addr_list_op(struct rte_eth_dev *eth_dev,
        }
 
        vnic->mc_addr_cnt = i;
+       if (vnic->mc_addr_cnt)
+               vnic->flags |= BNXT_VNIC_INFO_MCAST;
+       else
+               vnic->flags &= ~BNXT_VNIC_INFO_MCAST;
 
 allmulti:
        return bnxt_hwrm_cfa_l2_set_rx_mask(bp, vnic, 0, NULL);
@@ -2862,7 +2965,6 @@ bnxt_parse_fdir_filter(struct bnxt *bp,
                return -EINVAL;
        }
 
-
        if (fdir_mode == RTE_FDIR_MODE_PERFECT_MAC_VLAN) {
                rte_memcpy(filter->dst_macaddr,
                        fdir->input.flow.mac_vlan_flow.mac_addr.addr_bytes, 6);
@@ -4199,7 +4301,9 @@ int bnxt_alloc_ctx_mem(struct bnxt *bp)
        if (rc)
                return rc;
 
-       entries = ctx->qp_max_l2_entries;
+       entries = ctx->qp_max_l2_entries +
+                 ctx->vnic_max_vnic_entries +
+                 ctx->tqm_min_entries_per_ring;
        entries = bnxt_roundup(entries, ctx->tqm_entries_multiple);
        entries = clamp_t(uint32_t, entries, ctx->tqm_min_entries_per_ring,
                          ctx->tqm_max_entries_per_ring);
@@ -4445,7 +4549,7 @@ static int bnxt_init_fw(struct bnxt *bp)
        if (rc)
                return -EIO;
 
-       rc = bnxt_hwrm_cfa_adv_flow_mgmt_qcaps(bp);
+       rc = bnxt_hwrm_vnic_qcaps(bp);
        if (rc)
                return rc;
 
@@ -4453,7 +4557,10 @@ static int bnxt_init_fw(struct bnxt *bp)
        if (rc)
                return rc;
 
-       /* Get the MAX capabilities for this function */
+       /* Get the MAX capabilities for this function.
+        * This function also allocates context memory for TQM rings and
+        * informs the firmware about this allocated backing store memory.
+        */
        rc = bnxt_hwrm_func_qcaps(bp);
        if (rc)
                return rc;
@@ -4462,6 +4569,10 @@ static int bnxt_init_fw(struct bnxt *bp)
        if (rc)
                return rc;
 
+       rc = bnxt_hwrm_cfa_adv_flow_mgmt_qcaps(bp);
+       if (rc)
+               return rc;
+
        /* Get the adapter error recovery support info */
        rc = bnxt_hwrm_error_recovery_qcfg(bp);
        if (rc)
@@ -4482,8 +4593,14 @@ bnxt_init_locks(struct bnxt *bp)
        int err;
 
        err = pthread_mutex_init(&bp->flow_lock, NULL);
-       if (err)
+       if (err) {
                PMD_DRV_LOG(ERR, "Unable to initialize flow_lock\n");
+               return err;
+       }
+
+       err = pthread_mutex_init(&bp->def_cp_lock, NULL);
+       if (err)
+               PMD_DRV_LOG(ERR, "Unable to initialize def_cp_lock\n");
        return err;
 }
 
@@ -4632,6 +4749,7 @@ static void
 bnxt_uninit_locks(struct bnxt *bp)
 {
        pthread_mutex_destroy(&bp->flow_lock);
+       pthread_mutex_destroy(&bp->def_cp_lock);
 }
 
 static int