--- /dev/null
+From 51b369d812cbf75c413f6013ad0cc21c74a8506a Mon Sep 17 00:00:00 2001
+From: Selvin Xavier <selvin.xavier@broadcom.com>
+Date: Sun, 30 Jul 2017 19:57:18 -0700
+Subject: [PATCH 1/9] RDMA/bnxt_re: Allocate multiple notification queues
+
+Enables multiple Interrupt vectors. Driver is requesting the max
+MSIX vectors based on the number of online cpus and creates upto
+9 MSIx vectors (1 for control path and 8 for data path).
+A tasklet is created for each of these vectors. NQs are assigned
+to CQs in round robin fashion.
+This patch also adds IRQ affinity hint for the MSIX vector of each NQ.
+
+Signed-off-by: Ray Jui <ray.jui@broadcom.com>
+Signed-off-by: Selvin Xavier <selvin.xavier@broadcom.com>
+Signed-off-by: Doug Ledford <dledford@redhat.com>
+---
+ drivers/infiniband/hw/bnxt_re/bnxt_re.h | 5 +-
+ drivers/infiniband/hw/bnxt_re/ib_verbs.c | 17 +++--
+ drivers/infiniband/hw/bnxt_re/main.c | 106 +++++++++++++++++++------------
+ drivers/infiniband/hw/bnxt_re/qplib_fp.c | 21 +++++-
+ drivers/infiniband/hw/bnxt_re/qplib_fp.h | 4 +-
+ 5 files changed, 104 insertions(+), 49 deletions(-)
+
+diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h
+index 805088b..c7095c4 100644
+--- a/drivers/infiniband/hw/bnxt_re/bnxt_re.h
++++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h
+@@ -89,7 +89,7 @@ struct bnxt_re_sqp_entries {
+ };
+
+ #define BNXT_RE_MIN_MSIX 2
+-#define BNXT_RE_MAX_MSIX 16
++#define BNXT_RE_MAX_MSIX 9
+ #define BNXT_RE_AEQ_IDX 0
+ #define BNXT_RE_NQ_IDX 1
+
+@@ -120,7 +120,7 @@ struct bnxt_re_dev {
+ struct bnxt_qplib_rcfw rcfw;
+
+ /* NQ */
+- struct bnxt_qplib_nq nq;
++ struct bnxt_qplib_nq nq[BNXT_RE_MAX_MSIX];
+
+ /* Device Resources */
+ struct bnxt_qplib_dev_attr dev_attr;
+@@ -144,6 +144,7 @@ struct bnxt_re_dev {
+ struct bnxt_re_qp *qp1_sqp;
+ struct bnxt_re_ah *sqp_ah;
+ struct bnxt_re_sqp_entries sqp_tbl[1024];
++ atomic_t nq_alloc_cnt;
+ };
+
+ #define to_bnxt_re_dev(ptr, member) \
+diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+index cc53e6d..97c3343 100644
+--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c
++++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+@@ -2356,6 +2356,7 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq)
+ struct bnxt_re_cq *cq = container_of(ib_cq, struct bnxt_re_cq, ib_cq);
+ struct bnxt_re_dev *rdev = cq->rdev;
+ int rc;
++ struct bnxt_qplib_nq *nq = cq->qplib_cq.nq;
+
+ rc = bnxt_qplib_destroy_cq(&rdev->qplib_res, &cq->qplib_cq);
+ if (rc) {
+@@ -2370,7 +2371,7 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq)
+ kfree(cq);
+ }
+ atomic_dec(&rdev->cq_count);
+- rdev->nq.budget--;
++ nq->budget--;
+ return 0;
+ }
+
+@@ -2384,6 +2385,8 @@ struct ib_cq *bnxt_re_create_cq(struct ib_device *ibdev,
+ struct bnxt_re_cq *cq = NULL;
+ int rc, entries;
+ int cqe = attr->cqe;
++ struct bnxt_qplib_nq *nq = NULL;
++ unsigned int nq_alloc_cnt;
+
+ /* Validate CQ fields */
+ if (cqe < 1 || cqe > dev_attr->max_cq_wqes) {
+@@ -2435,9 +2438,15 @@ struct ib_cq *bnxt_re_create_cq(struct ib_device *ibdev,
+ cq->qplib_cq.sghead = NULL;
+ cq->qplib_cq.nmap = 0;
+ }
++ /*
++ * Allocating the NQ in a round robin fashion. nq_alloc_cnt is a
++ * used for getting the NQ index.
++ */
++ nq_alloc_cnt = atomic_inc_return(&rdev->nq_alloc_cnt);
++ nq = &rdev->nq[nq_alloc_cnt % (rdev->num_msix - 1)];
+ cq->qplib_cq.max_wqe = entries;
+- cq->qplib_cq.cnq_hw_ring_id = rdev->nq.ring_id;
+- cq->qplib_cq.nq = &rdev->nq;
++ cq->qplib_cq.cnq_hw_ring_id = nq->ring_id;
++ cq->qplib_cq.nq = nq;
+
+ rc = bnxt_qplib_create_cq(&rdev->qplib_res, &cq->qplib_cq);
+ if (rc) {
+@@ -2447,7 +2456,7 @@ struct ib_cq *bnxt_re_create_cq(struct ib_device *ibdev,
+
+ cq->ib_cq.cqe = entries;
+ cq->cq_period = cq->qplib_cq.period;
+- rdev->nq.budget++;
++ nq->budget++;
+
+ atomic_inc(&rdev->cq_count);
+
+diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c
+index ea89024..91e584e 100644
+--- a/drivers/infiniband/hw/bnxt_re/main.c
++++ b/drivers/infiniband/hw/bnxt_re/main.c
+@@ -163,7 +163,7 @@ static int bnxt_re_free_msix(struct bnxt_re_dev *rdev, bool lock_wait)
+
+ static int bnxt_re_request_msix(struct bnxt_re_dev *rdev)
+ {
+- int rc = 0, num_msix_want = BNXT_RE_MIN_MSIX, num_msix_got;
++ int rc = 0, num_msix_want = BNXT_RE_MAX_MSIX, num_msix_got;
+ struct bnxt_en_dev *en_dev;
+
+ if (!rdev)
+@@ -171,6 +171,8 @@ static int bnxt_re_request_msix(struct bnxt_re_dev *rdev)
+
+ en_dev = rdev->en_dev;
+
++ num_msix_want = min_t(u32, BNXT_RE_MAX_MSIX, num_online_cpus());
++
+ rtnl_lock();
+ num_msix_got = en_dev->en_ops->bnxt_request_msix(en_dev, BNXT_ROCE_ULP,
+ rdev->msix_entries,
+@@ -654,8 +656,12 @@ static int bnxt_re_cqn_handler(struct bnxt_qplib_nq *nq,
+
+ static void bnxt_re_cleanup_res(struct bnxt_re_dev *rdev)
+ {
+- if (rdev->nq.hwq.max_elements)
+- bnxt_qplib_disable_nq(&rdev->nq);
++ int i;
++
++ if (rdev->nq[0].hwq.max_elements) {
++ for (i = 1; i < rdev->num_msix; i++)
++ bnxt_qplib_disable_nq(&rdev->nq[i - 1]);
++ }
+
+ if (rdev->qplib_res.rcfw)
+ bnxt_qplib_cleanup_res(&rdev->qplib_res);
+@@ -663,31 +669,41 @@ static void bnxt_re_cleanup_res(struct bnxt_re_dev *rdev)
+
+ static int bnxt_re_init_res(struct bnxt_re_dev *rdev)
+ {
+- int rc = 0;
++ int rc = 0, i;
+
+ bnxt_qplib_init_res(&rdev->qplib_res);
+
+- if (rdev->msix_entries[BNXT_RE_NQ_IDX].vector <= 0)
+- return -EINVAL;
++ for (i = 1; i < rdev->num_msix ; i++) {
++ rc = bnxt_qplib_enable_nq(rdev->en_dev->pdev, &rdev->nq[i - 1],
++ i - 1, rdev->msix_entries[i].vector,
++ rdev->msix_entries[i].db_offset,
++ &bnxt_re_cqn_handler, NULL);
+
+- rc = bnxt_qplib_enable_nq(rdev->en_dev->pdev, &rdev->nq,
+- rdev->msix_entries[BNXT_RE_NQ_IDX].vector,
+- rdev->msix_entries[BNXT_RE_NQ_IDX].db_offset,
+- &bnxt_re_cqn_handler,
+- NULL);
++ if (rc) {
++ dev_err(rdev_to_dev(rdev),
++ "Failed to enable NQ with rc = 0x%x", rc);
++ goto fail;
++ }
++ }
++ return 0;
++fail:
++ return rc;
++}
+
+- if (rc)
+- dev_err(rdev_to_dev(rdev), "Failed to enable NQ: %#x", rc);
++static void bnxt_re_free_nq_res(struct bnxt_re_dev *rdev, bool lock_wait)
++{
++ int i;
+
+- return rc;
++ for (i = 0; i < rdev->num_msix - 1; i++) {
++ bnxt_re_net_ring_free(rdev, rdev->nq[i].ring_id, lock_wait);
++ bnxt_qplib_free_nq(&rdev->nq[i]);
++ }
+ }
+
+ static void bnxt_re_free_res(struct bnxt_re_dev *rdev, bool lock_wait)
+ {
+- if (rdev->nq.hwq.max_elements) {
+- bnxt_re_net_ring_free(rdev, rdev->nq.ring_id, lock_wait);
+- bnxt_qplib_free_nq(&rdev->nq);
+- }
++ bnxt_re_free_nq_res(rdev, lock_wait);
++
+ if (rdev->qplib_res.dpi_tbl.max) {
+ bnxt_qplib_dealloc_dpi(&rdev->qplib_res,
+ &rdev->qplib_res.dpi_tbl,
+@@ -701,7 +717,7 @@ static void bnxt_re_free_res(struct bnxt_re_dev *rdev, bool lock_wait)
+
+ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev)
+ {
+- int rc = 0;
++ int rc = 0, i;
+
+ /* Configure and allocate resources for qplib */
+ rdev->qplib_res.rcfw = &rdev->rcfw;
+@@ -718,30 +734,42 @@ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev)
+ &rdev->dpi_privileged,
+ rdev);
+ if (rc)
+- goto fail;
++ goto dealloc_res;
+
+- rdev->nq.hwq.max_elements = BNXT_RE_MAX_CQ_COUNT +
+- BNXT_RE_MAX_SRQC_COUNT + 2;
+- rc = bnxt_qplib_alloc_nq(rdev->en_dev->pdev, &rdev->nq);
+- if (rc) {
+- dev_err(rdev_to_dev(rdev),
+- "Failed to allocate NQ memory: %#x", rc);
+- goto fail;
+- }
+- rc = bnxt_re_net_ring_alloc
+- (rdev, rdev->nq.hwq.pbl[PBL_LVL_0].pg_map_arr,
+- rdev->nq.hwq.pbl[rdev->nq.hwq.level].pg_count,
+- HWRM_RING_ALLOC_CMPL, BNXT_QPLIB_NQE_MAX_CNT - 1,
+- rdev->msix_entries[BNXT_RE_NQ_IDX].ring_idx,
+- &rdev->nq.ring_id);
+- if (rc) {
+- dev_err(rdev_to_dev(rdev),
+- "Failed to allocate NQ ring: %#x", rc);
+- goto free_nq;
++ for (i = 0; i < rdev->num_msix - 1; i++) {
++ rdev->nq[i].hwq.max_elements = BNXT_RE_MAX_CQ_COUNT +
++ BNXT_RE_MAX_SRQC_COUNT + 2;
++ rc = bnxt_qplib_alloc_nq(rdev->en_dev->pdev, &rdev->nq[i]);
++ if (rc) {
++ dev_err(rdev_to_dev(rdev), "Alloc Failed NQ%d rc:%#x",
++ i, rc);
++ goto dealloc_dpi;
++ }
++ rc = bnxt_re_net_ring_alloc
++ (rdev, rdev->nq[i].hwq.pbl[PBL_LVL_0].pg_map_arr,
++ rdev->nq[i].hwq.pbl[rdev->nq[i].hwq.level].pg_count,
++ HWRM_RING_ALLOC_CMPL,
++ BNXT_QPLIB_NQE_MAX_CNT - 1,
++ rdev->msix_entries[i + 1].ring_idx,
++ &rdev->nq[i].ring_id);
++ if (rc) {
++ dev_err(rdev_to_dev(rdev),
++ "Failed to allocate NQ fw id with rc = 0x%x",
++ rc);
++ goto free_nq;
++ }
+ }
+ return 0;
+ free_nq:
+- bnxt_qplib_free_nq(&rdev->nq);
++ for (i = 0; i < rdev->num_msix - 1; i++)
++ bnxt_qplib_free_nq(&rdev->nq[i]);
++dealloc_dpi:
++ bnxt_qplib_dealloc_dpi(&rdev->qplib_res,
++ &rdev->qplib_res.dpi_tbl,
++ &rdev->dpi_privileged);
++dealloc_res:
++ bnxt_qplib_free_res(&rdev->qplib_res);
++
+ fail:
+ rdev->qplib_res.rcfw = NULL;
+ return rc;
+diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c
+index 31e15f3..e8afc47 100644
+--- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c
++++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c
+@@ -365,6 +365,7 @@ void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq)
+ tasklet_kill(&nq->worker);
+
+ if (nq->requested) {
++ irq_set_affinity_hint(nq->vector, NULL);
+ free_irq(nq->vector, nq);
+ nq->requested = false;
+ }
+@@ -378,7 +379,7 @@ void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq)
+ }
+
+ int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq,
+- int msix_vector, int bar_reg_offset,
++ int nq_idx, int msix_vector, int bar_reg_offset,
+ int (*cqn_handler)(struct bnxt_qplib_nq *nq,
+ struct bnxt_qplib_cq *),
+ int (*srqn_handler)(struct bnxt_qplib_nq *nq,
+@@ -402,13 +403,25 @@ int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq,
+ goto fail;
+
+ nq->requested = false;
+- rc = request_irq(nq->vector, bnxt_qplib_nq_irq, 0, "bnxt_qplib_nq", nq);
++ memset(nq->name, 0, 32);
++ sprintf(nq->name, "bnxt_qplib_nq-%d", nq_idx);
++ rc = request_irq(nq->vector, bnxt_qplib_nq_irq, 0, nq->name, nq);
+ if (rc) {
+ dev_err(&nq->pdev->dev,
+ "Failed to request IRQ for NQ: %#x", rc);
+ bnxt_qplib_disable_nq(nq);
+ goto fail;
+ }
++
++ cpumask_clear(&nq->mask);
++ cpumask_set_cpu(nq_idx, &nq->mask);
++ rc = irq_set_affinity_hint(nq->vector, &nq->mask);
++ if (rc) {
++ dev_warn(&nq->pdev->dev,
++ "QPLIB: set affinity failed; vector: %d nq_idx: %d\n",
++ nq->vector, nq_idx);
++ }
++
+ nq->requested = true;
+ nq->bar_reg = NQ_CONS_PCI_BAR_REGION;
+ nq->bar_reg_off = bar_reg_offset;
+@@ -432,8 +445,10 @@ fail:
+
+ void bnxt_qplib_free_nq(struct bnxt_qplib_nq *nq)
+ {
+- if (nq->hwq.max_elements)
++ if (nq->hwq.max_elements) {
+ bnxt_qplib_free_hwq(nq->pdev, &nq->hwq);
++ nq->hwq.max_elements = 0;
++ }
+ }
+
+ int bnxt_qplib_alloc_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq)
+diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h
+index 23a26d5..8ead70c 100644
+--- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h
++++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h
+@@ -407,6 +407,7 @@ struct bnxt_qplib_nq {
+ struct pci_dev *pdev;
+
+ int vector;
++ cpumask_t mask;
+ int budget;
+ bool requested;
+ struct tasklet_struct worker;
+@@ -425,6 +426,7 @@ struct bnxt_qplib_nq {
+ void *srq,
+ u8 event);
+ struct workqueue_struct *cqn_wq;
++ char name[32];
+ };
+
+ struct bnxt_qplib_nq_work {
+@@ -435,7 +437,7 @@ struct bnxt_qplib_nq_work {
+
+ void bnxt_qplib_disable_nq(struct bnxt_qplib_nq *nq);
+ int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq,
+- int msix_vector, int bar_reg_offset,
++ int nq_idx, int msix_vector, int bar_reg_offset,
+ int (*cqn_handler)(struct bnxt_qplib_nq *nq,
+ struct bnxt_qplib_cq *cq),
+ int (*srqn_handler)(struct bnxt_qplib_nq *nq,
+--
+1.8.3.1
+
--- /dev/null
+From 1ff20f41833618b9e67af709c38a00b4dc2188e2 Mon Sep 17 00:00:00 2001
+From: Somnath Kotur <somnath.kotur@broadcom.com>
+Date: Mon, 31 Jul 2017 02:15:30 -0700
+Subject: [PATCH 2/9] RDMA/bnxt_re: Implement the alloc/get_hw_stats callback
+
+Expose HW counters using the get_hw_stats callback
+
+Signed-off-by: Somnath Kotur <somnath.kotur@broadcom.com>
+Signed-off-by: Selvin Xavier <selvin.xavier@broadcom.com>
+Reviewed-by: Leon Romanovsky <leonro@mellanox.com>
+Signed-off-by: Doug Ledford <dledford@redhat.com>
+---
+ drivers/infiniband/hw/bnxt_re/Makefile | 2 +-
+ drivers/infiniband/hw/bnxt_re/hw_counters.c | 114 ++++++++++++++++++++++++++++
+ drivers/infiniband/hw/bnxt_re/hw_counters.h | 62 +++++++++++++++
+ drivers/infiniband/hw/bnxt_re/main.c | 4 +
+ 4 files changed, 181 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/infiniband/hw/bnxt_re/hw_counters.c
+ create mode 100644 drivers/infiniband/hw/bnxt_re/hw_counters.h
+
+diff --git a/drivers/infiniband/hw/bnxt_re/Makefile b/drivers/infiniband/hw/bnxt_re/Makefile
+index b7ff61f..bc61365 100644
+--- a/drivers/infiniband/hw/bnxt_re/Makefile
++++ b/drivers/infiniband/hw/bnxt_re/Makefile
+@@ -3,4 +3,4 @@ ccflags-y := -I$(CWD)/drivers/net/ethernet/broadcom/bnxt
+ obj-$(CONFIG_INFINIBAND_BNXT_RE) += bnxt_re.o
+ bnxt_re-y := main.o ib_verbs.o \
+ qplib_res.o qplib_rcfw.o \
+- qplib_sp.o qplib_fp.o
++ qplib_sp.o qplib_fp.o hw_counters.o
+diff --git a/drivers/infiniband/hw/bnxt_re/hw_counters.c b/drivers/infiniband/hw/bnxt_re/hw_counters.c
+new file mode 100644
+index 0000000..7b28219
+--- /dev/null
++++ b/drivers/infiniband/hw/bnxt_re/hw_counters.c
+@@ -0,0 +1,114 @@
++/*
++ * Broadcom NetXtreme-E RoCE driver.
++ *
++ * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term
++ * Broadcom refers to Broadcom Limited and/or its subsidiaries.
++ *
++ * This software is available to you under a choice of one of two
++ * licenses. You may choose to be licensed under the terms of the GNU
++ * General Public License (GPL) Version 2, available from the file
++ * COPYING in the main directory of this source tree, or the
++ * BSD license below:
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
++ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
++ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
++ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
++ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
++ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ * Description: Statistics
++ *
++ */
++
++#include <linux/interrupt.h>
++#include <linux/types.h>
++#include <linux/spinlock.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/pci.h>
++#include <linux/prefetch.h>
++#include <linux/delay.h>
++
++#include <rdma/ib_addr.h>
++
++#include "bnxt_ulp.h"
++#include "roce_hsi.h"
++#include "qplib_res.h"
++#include "qplib_sp.h"
++#include "qplib_fp.h"
++#include "qplib_rcfw.h"
++#include "bnxt_re.h"
++#include "hw_counters.h"
++
++static const char * const bnxt_re_stat_name[] = {
++ [BNXT_RE_ACTIVE_QP] = "active_qps",
++ [BNXT_RE_ACTIVE_SRQ] = "active_srqs",
++ [BNXT_RE_ACTIVE_CQ] = "active_cqs",
++ [BNXT_RE_ACTIVE_MR] = "active_mrs",
++ [BNXT_RE_ACTIVE_MW] = "active_mws",
++ [BNXT_RE_RX_PKTS] = "rx_pkts",
++ [BNXT_RE_RX_BYTES] = "rx_bytes",
++ [BNXT_RE_TX_PKTS] = "tx_pkts",
++ [BNXT_RE_TX_BYTES] = "tx_bytes",
++ [BNXT_RE_RECOVERABLE_ERRORS] = "recoverable_errors"
++};
++
++int bnxt_re_ib_get_hw_stats(struct ib_device *ibdev,
++ struct rdma_hw_stats *stats,
++ u8 port, int index)
++{
++ struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev);
++ struct ctx_hw_stats *bnxt_re_stats = rdev->qplib_ctx.stats.dma;
++
++ if (!port || !stats)
++ return -EINVAL;
++
++ stats->value[BNXT_RE_ACTIVE_QP] = atomic_read(&rdev->qp_count);
++ stats->value[BNXT_RE_ACTIVE_SRQ] = atomic_read(&rdev->srq_count);
++ stats->value[BNXT_RE_ACTIVE_CQ] = atomic_read(&rdev->cq_count);
++ stats->value[BNXT_RE_ACTIVE_MR] = atomic_read(&rdev->mr_count);
++ stats->value[BNXT_RE_ACTIVE_MW] = atomic_read(&rdev->mw_count);
++ if (bnxt_re_stats) {
++ stats->value[BNXT_RE_RECOVERABLE_ERRORS] =
++ le64_to_cpu(bnxt_re_stats->tx_bcast_pkts);
++ stats->value[BNXT_RE_RX_PKTS] =
++ le64_to_cpu(bnxt_re_stats->rx_ucast_pkts);
++ stats->value[BNXT_RE_RX_BYTES] =
++ le64_to_cpu(bnxt_re_stats->rx_ucast_bytes);
++ stats->value[BNXT_RE_TX_PKTS] =
++ le64_to_cpu(bnxt_re_stats->tx_ucast_pkts);
++ stats->value[BNXT_RE_TX_BYTES] =
++ le64_to_cpu(bnxt_re_stats->tx_ucast_bytes);
++ }
++ return ARRAY_SIZE(bnxt_re_stat_name);
++}
++
++struct rdma_hw_stats *bnxt_re_ib_alloc_hw_stats(struct ib_device *ibdev,
++ u8 port_num)
++{
++ BUILD_BUG_ON(ARRAY_SIZE(bnxt_re_stat_name) != BNXT_RE_NUM_COUNTERS);
++ /* We support only per port stats */
++ if (!port_num)
++ return NULL;
++
++ return rdma_alloc_hw_stats_struct(bnxt_re_stat_name,
++ ARRAY_SIZE(bnxt_re_stat_name),
++ RDMA_HW_STATS_DEFAULT_LIFESPAN);
++}
+diff --git a/drivers/infiniband/hw/bnxt_re/hw_counters.h b/drivers/infiniband/hw/bnxt_re/hw_counters.h
+new file mode 100644
+index 0000000..be0dc00
+--- /dev/null
++++ b/drivers/infiniband/hw/bnxt_re/hw_counters.h
+@@ -0,0 +1,62 @@
++/*
++ * Broadcom NetXtreme-E RoCE driver.
++ *
++ * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term
++ * Broadcom refers to Broadcom Limited and/or its subsidiaries.
++ *
++ * This software is available to you under a choice of one of two
++ * licenses. You may choose to be licensed under the terms of the GNU
++ * General Public License (GPL) Version 2, available from the file
++ * COPYING in the main directory of this source tree, or the
++ * BSD license below:
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ *
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in
++ * the documentation and/or other materials provided with the
++ * distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
++ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
++ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
++ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
++ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
++ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ *
++ * Description: Statistics (header)
++ *
++ */
++
++#ifndef __BNXT_RE_HW_STATS_H__
++#define __BNXT_RE_HW_STATS_H__
++
++enum bnxt_re_hw_stats {
++ BNXT_RE_ACTIVE_QP,
++ BNXT_RE_ACTIVE_SRQ,
++ BNXT_RE_ACTIVE_CQ,
++ BNXT_RE_ACTIVE_MR,
++ BNXT_RE_ACTIVE_MW,
++ BNXT_RE_RX_PKTS,
++ BNXT_RE_RX_BYTES,
++ BNXT_RE_TX_PKTS,
++ BNXT_RE_TX_BYTES,
++ BNXT_RE_RECOVERABLE_ERRORS,
++ BNXT_RE_NUM_COUNTERS
++};
++
++struct rdma_hw_stats *bnxt_re_ib_alloc_hw_stats(struct ib_device *ibdev,
++ u8 port_num);
++int bnxt_re_ib_get_hw_stats(struct ib_device *ibdev,
++ struct rdma_hw_stats *stats,
++ u8 port, int index);
++#endif /* __BNXT_RE_HW_STATS_H__ */
+diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c
+index 91e584e..922565e 100644
+--- a/drivers/infiniband/hw/bnxt_re/main.c
++++ b/drivers/infiniband/hw/bnxt_re/main.c
+@@ -65,6 +65,8 @@
+ #include "ib_verbs.h"
+ #include <rdma/bnxt_re-abi.h>
+ #include "bnxt.h"
++#include "hw_counters.h"
++
+ static char version[] =
+ BNXT_RE_DESC " v" ROCE_DRV_MODULE_VERSION "\n";
+
+@@ -516,6 +518,8 @@ static int bnxt_re_register_ib(struct bnxt_re_dev *rdev)
+ ibdev->alloc_ucontext = bnxt_re_alloc_ucontext;
+ ibdev->dealloc_ucontext = bnxt_re_dealloc_ucontext;
+ ibdev->mmap = bnxt_re_mmap;
++ ibdev->get_hw_stats = bnxt_re_ib_get_hw_stats;
++ ibdev->alloc_hw_stats = bnxt_re_ib_alloc_hw_stats;
+
+ return ib_register_device(ibdev, NULL);
+ }
+--
+1.8.3.1
+
--- /dev/null
+From f957acb58c6baac68afd181810ac69f94a1216d7 Mon Sep 17 00:00:00 2001
+From: Devesh Sharma <devesh.sharma@broadcom.com>
+Date: Thu, 24 Aug 2017 14:21:55 +0530
+Subject: [PATCH 3/9] bnxt_re: Fix update of qplib_qp.mtu when modified
+
+The MTU value in the qplib_qp.mtu should be
+consistent with whatever mtu was set during
+INIT to RTR.The Next PSN and number of packets
+are calculated based on this member in the qplib_qp structure.
+
+Signed-off-by: Narender Reddy <narender.reddy@broadcom.com>
+Signed-off-by: Devesh Sharma <devesh.sharma@broadcom.com>
+Signed-off-by: Somnath Kotur <somnath.kotur@broadcom.com>
+---
+ drivers/infiniband/hw/bnxt_re/ib_verbs.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+index 97c3343..27235f2 100644
+--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c
++++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+@@ -1504,11 +1504,14 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr,
+ qp->qplib_qp.modify_flags |=
+ CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU;
+ qp->qplib_qp.path_mtu = __from_ib_mtu(qp_attr->path_mtu);
++ qp->qplib_qp.mtu = ib_mtu_enum_to_int(qp_attr->path_mtu);
+ } else if (qp_attr->qp_state == IB_QPS_RTR) {
+ qp->qplib_qp.modify_flags |=
+ CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU;
+ qp->qplib_qp.path_mtu =
+ __from_ib_mtu(iboe_get_mtu(rdev->netdev->mtu));
++ qp->qplib_qp.mtu =
++ ib_mtu_enum_to_int(iboe_get_mtu(rdev->netdev->mtu));
+ }
+
+ if (qp_attr_mask & IB_QP_TIMEOUT) {
+--
+1.8.3.1
+
--- /dev/null
+From 976ef742be965ec507694748646beb9c29db2d2d Mon Sep 17 00:00:00 2001
+From: Somnath Kotur <somnath.kotur@broadcom.com>
+Date: Thu, 24 Aug 2017 15:02:13 +0530
+Subject: [PATCH 4/9] bnxt_re: Stop issuing further cmds to FW once a cmd times
+ out
+
+Once a cmd to FW times out(after 20s) it is reasonable to
+assume the FW or atleast the control path is dead.
+No point issuing further cmds to the FW as each subsequent cmd
+with another 20s timeout will cascade resulting in unnecessary
+traces and/or NMI Lockups.
+
+Signed-off-by: Somnath Kotur <somnath.kotur@broadcom.com>
+---
+ drivers/infiniband/hw/bnxt_re/qplib_rcfw.c | 4 ++++
+ drivers/infiniband/hw/bnxt_re/qplib_rcfw.h | 3 ++-
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
+index 391bb70..2bdb156 100644
+--- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
++++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c
+@@ -107,6 +107,9 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, struct cmdq_base *req,
+ return -EINVAL;
+ }
+
++ if (test_bit(FIRMWARE_TIMED_OUT, &rcfw->flags))
++ return -ETIMEDOUT;
++
+ /* Cmdq are in 16-byte units, each request can consume 1 or more
+ * cmdqe
+ */
+@@ -226,6 +229,7 @@ int bnxt_qplib_rcfw_send_message(struct bnxt_qplib_rcfw *rcfw,
+ /* timed out */
+ dev_err(&rcfw->pdev->dev, "QPLIB: cmdq[%#x]=%#x timedout (%d)msec",
+ cookie, opcode, RCFW_CMD_WAIT_TIME_MS);
++ set_bit(FIRMWARE_TIMED_OUT, &rcfw->flags);
+ return rc;
+ }
+
+diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h
+index 0ed312f..85b16da 100644
+--- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h
++++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.h
+@@ -162,8 +162,9 @@ struct bnxt_qplib_rcfw {
+ unsigned long *cmdq_bitmap;
+ u32 bmap_size;
+ unsigned long flags;
+-#define FIRMWARE_INITIALIZED_FLAG 1
++#define FIRMWARE_INITIALIZED_FLAG BIT(0)
+ #define FIRMWARE_FIRST_FLAG BIT(31)
++#define FIRMWARE_TIMED_OUT BIT(3)
+ wait_queue_head_t waitq;
+ int (*aeq_handler)(struct bnxt_qplib_rcfw *,
+ struct creq_func_event *);
+--
+1.8.3.1
+
--- /dev/null
+From fc14e62c1c9264ad8031d48be47c4dcbdacde50b Mon Sep 17 00:00:00 2001
+From: Devesh Sharma <devesh.sharma@broadcom.com>
+Date: Thu, 24 Aug 2017 15:13:01 +0530
+Subject: [PATCH 5/9] bnxt_re: Fix compare and swap atomic operands
+
+Driver must assign the user supplied compare/swap values in
+the wqe to successfully complete the atomic compare and
+swap operation.
+
+Signed-off-by: Devesh Sharma <devesh.sharma@broadcom.com>
+Signed-off-by: Somnath Kotur <somnath.kotur@broadcom.com>
+---
+ drivers/infiniband/hw/bnxt_re/ib_verbs.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+index 27235f2..101b6ec 100644
+--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c
++++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+@@ -1979,6 +1979,7 @@ static int bnxt_re_build_atomic_wqe(struct ib_send_wr *wr,
+ switch (wr->opcode) {
+ case IB_WR_ATOMIC_CMP_AND_SWP:
+ wqe->type = BNXT_QPLIB_SWQE_TYPE_ATOMIC_CMP_AND_SWP;
++ wqe->atomic.cmp_data = atomic_wr(wr)->compare_add;
+ wqe->atomic.swap_data = atomic_wr(wr)->swap;
+ break;
+ case IB_WR_ATOMIC_FETCH_AND_ADD:
+--
+1.8.3.1
+
--- /dev/null
+From 02c7a08645e0b65fc7ae3b4e5e40ef1b1a0846fb Mon Sep 17 00:00:00 2001
+From: Somnath Kotur <somnath.kotur@broadcom.com>
+Date: Wed, 30 Aug 2017 09:33:29 +0530
+Subject: [PATCH 6/9] bnxt_re: Free up devices in module_exit path
+
+Clean up all devices added to the bnxt_re_dev_list in the
+module_exit entry point.
+
+Signed-off-by: Somnath Kotur <somnath.kotur@broadcom.com>
+---
+ drivers/infiniband/hw/bnxt_re/main.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c
+index 922565e..918e2ca 100644
+--- a/drivers/infiniband/hw/bnxt_re/main.c
++++ b/drivers/infiniband/hw/bnxt_re/main.c
+@@ -1378,6 +1378,22 @@ err_netdev:
+
+ static void __exit bnxt_re_mod_exit(void)
+ {
++ struct bnxt_re_dev *rdev;
++ LIST_HEAD(to_be_deleted);
++
++ mutex_lock(&bnxt_re_dev_lock);
++ /* Free all adapter allocated resources */
++ if (!list_empty(&bnxt_re_dev_list))
++ list_splice_init(&bnxt_re_dev_list, &to_be_deleted);
++ mutex_unlock(&bnxt_re_dev_lock);
++
++ list_for_each_entry(rdev, &to_be_deleted, list) {
++ dev_info(rdev_to_dev(rdev), "Unregistering Device");
++ bnxt_re_dev_stop(rdev);
++ bnxt_re_ib_unreg(rdev, true);
++ bnxt_re_remove_one(rdev);
++ bnxt_re_dev_unreg(rdev);
++ }
+ unregister_netdevice_notifier(&bnxt_re_netdev_notifier);
+ if (bnxt_re_wq)
+ destroy_workqueue(bnxt_re_wq);
+--
+1.8.3.1
+
--- /dev/null
+From accf29a7b3bc4de2b91d98c5da7ede6313742018 Mon Sep 17 00:00:00 2001
+From: Somnath Kotur <somnath.kotur@broadcom.com>
+Date: Mon, 28 Aug 2017 13:55:17 +0530
+Subject: [PATCH 7/9] bnxt_re: Fix race between the netdev register and
+ unregister events
+
+Upon receipt of the NETDEV_REGISTER event from the netdev notifier chain,
+the IB stack registration is spawned off to a workqueue since that also
+requires an rtnl lock.
+There could be 2 kinds of races between the NETDEV_REGISTER and the
+NETDEV_UNREGISTER event handling.
+a)The NETDEV_UNREGISTER event is received in rapid succession after
+the NETDEV_REGISTER event even before the work queue got a chance to run.
+b)The NETDEV_UNREGISTER event is received while the workqueue that handles
+registration with the IB stack is still in progress.
+Handle both the races with a bit flag that is set just before the work item
+is queued and cleared in the workqueue after the event is handled just
+before the workqueue item is freed.
+
+Signed-off-by: Somnath Kotur <somnath.kotur@broadcom.com>
+---
+ drivers/infiniband/hw/bnxt_re/bnxt_re.h | 12 +++++++-----
+ drivers/infiniband/hw/bnxt_re/main.c | 8 ++++++++
+ 2 files changed, 15 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h
+index c7095c4..a8e931c 100644
+--- a/drivers/infiniband/hw/bnxt_re/bnxt_re.h
++++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h
+@@ -97,11 +97,13 @@ struct bnxt_re_dev {
+ struct ib_device ibdev;
+ struct list_head list;
+ unsigned long flags;
+-#define BNXT_RE_FLAG_NETDEV_REGISTERED 0
+-#define BNXT_RE_FLAG_IBDEV_REGISTERED 1
+-#define BNXT_RE_FLAG_GOT_MSIX 2
+-#define BNXT_RE_FLAG_RCFW_CHANNEL_EN 8
+-#define BNXT_RE_FLAG_QOS_WORK_REG 16
++#define BNXT_RE_FLAG_NETDEV_REGISTERED 0
++#define BNXT_RE_FLAG_IBDEV_REGISTERED 1
++#define BNXT_RE_FLAG_GOT_MSIX 2
++#define BNXT_RE_FLAG_HAVE_L2_REF 3
++#define BNXT_RE_FLAG_RCFW_CHANNEL_EN 4
++#define BNXT_RE_FLAG_QOS_WORK_REG 5
++#define BNXT_RE_FLAG_TASK_IN_PROG 6
+ struct net_device *netdev;
+ unsigned int version, major, minor;
+ struct bnxt_en_dev *en_dev;
+diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c
+index 918e2ca..76ac061 100644
+--- a/drivers/infiniband/hw/bnxt_re/main.c
++++ b/drivers/infiniband/hw/bnxt_re/main.c
+@@ -1262,6 +1262,8 @@ static void bnxt_re_task(struct work_struct *work)
+ default:
+ break;
+ }
++ smp_mb__before_atomic();
++ clear_bit(BNXT_RE_FLAG_TASK_IN_PROG, &rdev->flags);
+ kfree(re_work);
+ }
+
+@@ -1320,6 +1322,11 @@ static int bnxt_re_netdev_event(struct notifier_block *notifier,
+ break;
+
+ case NETDEV_UNREGISTER:
++ /* netdev notifier will call NETDEV_UNREGISTER again later since
++ * we are still holding the reference to the netdev
++ */
++ if (test_bit(BNXT_RE_FLAG_TASK_IN_PROG, &rdev->flags))
++ goto exit;
+ bnxt_re_ib_unreg(rdev, false);
+ bnxt_re_remove_one(rdev);
+ bnxt_re_dev_unreg(rdev);
+@@ -1338,6 +1345,7 @@ static int bnxt_re_netdev_event(struct notifier_block *notifier,
+ re_work->vlan_dev = (real_dev == netdev ?
+ NULL : netdev);
+ INIT_WORK(&re_work->work, bnxt_re_task);
++ set_bit(BNXT_RE_FLAG_TASK_IN_PROG, &rdev->flags);
+ queue_work(bnxt_re_wq, &re_work->work);
+ }
+ }
+--
+1.8.3.1
+
--- /dev/null
+From 7dca8017cf003bc87e40020aa9324792c9437be8 Mon Sep 17 00:00:00 2001
+From: Selvin Xavier <selvin.xavier@broadcom.com>
+Date: Thu, 24 Aug 2017 15:17:22 +0530
+Subject: [PATCH 8/9] bnxt_re: Fix memory leak in FRMR path
+
+This patch fixes a memory leak issue when alloc_mr is used.
+mr->pages and mr->npages are used only in alloc_mr path. mr->pages
+is allocated when alloc_mr is called or in the case of FRMR, while
+creating the MR. mr->npages is updated only when the MR created
+is used i.e. after invoking map_mr_sg verb, before data transfer.
+In the dereg_mr path, if mr->npages is 0, driver ends up not freeing
+the memory created.
+Removing the npages check from the dereg_mr path for kernel consumers.
+
+Signed-off-by: Selvin Xavier <selvin.xavier@broadcom.com>
+Signed-off-by: Somnath Kotur <somnath.kotur@broadcom.com>
+---
+ drivers/infiniband/hw/bnxt_re/ib_verbs.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+index 101b6ec..5697df2 100644
+--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c
++++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+@@ -3134,7 +3134,7 @@ int bnxt_re_dereg_mr(struct ib_mr *ib_mr)
+ return rc;
+ }
+
+- if (mr->npages && mr->pages) {
++ if (mr->pages) {
+ rc = bnxt_qplib_free_fast_reg_page_list(&rdev->qplib_res,
+ &mr->qplib_frpl);
+ kfree(mr->pages);
+--
+1.8.3.1
+
--- /dev/null
+From 12773a01423842b4c90eec1050c3773121fe5e43 Mon Sep 17 00:00:00 2001
+From: Somnath Kotur <somnath.kotur@broadcom.com>
+Date: Thu, 24 Aug 2017 15:37:23 +0530
+Subject: [PATCH 9/9] bnxt_re: Don't issue cmd to delete GID for QP1 GID entry
+ before the QP is destroyed
+
+FW needs the 0th GID Entry in the Table to be preserved before
+it's corresponding QP1 is deleted, else it will fail the cmd.
+Check for the same and return to prevent error msg being logged for
+cmd failure.
+
+Signed-off-by: Somnath Kotur <somnath.kotur@broadcom.com>
+---
+ drivers/infiniband/hw/bnxt_re/ib_verbs.c | 23 ++++++++++++++++++++---
+ 1 file changed, 20 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+index 5697df2..dcddf19 100644
+--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c
++++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+@@ -388,6 +388,7 @@ int bnxt_re_del_gid(struct ib_device *ibdev, u8 port_num,
+ struct bnxt_re_gid_ctx *ctx, **ctx_tbl;
+ struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev);
+ struct bnxt_qplib_sgid_tbl *sgid_tbl = &rdev->qplib_res.sgid_tbl;
++ struct bnxt_qplib_gid *gid_to_del;
+
+ /* Delete the entry from the hardware */
+ ctx = *context;
+@@ -397,11 +398,25 @@ int bnxt_re_del_gid(struct ib_device *ibdev, u8 port_num,
+ if (sgid_tbl && sgid_tbl->active) {
+ if (ctx->idx >= sgid_tbl->max)
+ return -EINVAL;
++ gid_to_del = &sgid_tbl->tbl[ctx->idx];
++ /* DEL_GID is called in WQ context(netdevice_event_work_handler)
++ * or via the ib_unregister_device path. In the former case QP1
++ * may not be destroyed yet, in which case just return as FW
++ * needs that entry to be present and will fail it's deletion.
++ * We could get invoked again after QP1 is destroyed OR get an
++ * ADD_GID call with a different GID value for the same index
++ * where we issue MODIFY_GID cmd to update the GID entry -- TBD
++ */
++ if (ctx->idx == 0 &&
++ rdma_link_local_addr((struct in6_addr *)gid_to_del) &&
++ ctx->refcnt == 1 && rdev->qp1_sqp) {
++ dev_dbg(rdev_to_dev(rdev),
++ "Trying to delete GID0 while QP1 is alive\n");
++ return -EFAULT;
++ }
+ ctx->refcnt--;
+ if (!ctx->refcnt) {
+- rc = bnxt_qplib_del_sgid(sgid_tbl,
+- &sgid_tbl->tbl[ctx->idx],
+- true);
++ rc = bnxt_qplib_del_sgid(sgid_tbl, gid_to_del, true);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev),
+ "Failed to remove GID: %#x", rc);
+@@ -883,6 +898,8 @@ int bnxt_re_destroy_qp(struct ib_qp *ib_qp)
+
+ kfree(rdev->sqp_ah);
+ kfree(rdev->qp1_sqp);
++ rdev->qp1_sqp = NULL;
++ rdev->sqp_ah = NULL;
+ }
+
+ if (!IS_ERR_OR_NULL(qp->rumem))
+--
+1.8.3.1
+