Merge branch '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next...
authorDavid S. Miller <davem@davemloft.net>
Tue, 4 Oct 2016 03:28:50 +0000 (23:28 -0400)
committerDavid S. Miller <davem@davemloft.net>
Tue, 4 Oct 2016 03:28:50 +0000 (23:28 -0400)
Jeff Kirsher says:

====================
100GbE Intel Wired LAN Driver Updates 2016-10-02

This series contains updates to fm10k only.

Jake fixes an issue where PTP applications requesting software timestamps
may complain that the requested mode is not supported, so add a generic
callback for those drivers that have software transmit timestamp support
enabled.  Then provides a trivial cleanup where a code was not wrapped
properly.  Got make sure that code looks good in a 80 character limit.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
52 files changed:
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/marvell/mvmdio.c
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/qlogic/Kconfig
drivers/net/ethernet/qlogic/qed/Makefile
drivers/net/ethernet/qlogic/qed/qed.h
drivers/net/ethernet/qlogic/qed/qed_cxt.c
drivers/net/ethernet/qlogic/qed/qed_cxt.h
drivers/net/ethernet/qlogic/qed/qed_dev.c
drivers/net/ethernet/qlogic/qed/qed_dev_api.h
drivers/net/ethernet/qlogic/qed/qed_hsi.h
drivers/net/ethernet/qlogic/qed/qed_ll2.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_ll2.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_main.c
drivers/net/ethernet/qlogic/qed/qed_reg_addr.h
drivers/net/ethernet/qlogic/qed/qed_roce.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_roce.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_sp.h
drivers/net/ethernet/qlogic/qed/qed_spq.c
drivers/net/ethernet/qlogic/qed/qed_sriov.c
drivers/net/ethernet/qlogic/qed/qed_vf.c
drivers/net/ethernet/qlogic/qede/Makefile
drivers/net/ethernet/qlogic/qede/qede.h
drivers/net/ethernet/qlogic/qede/qede_main.c
drivers/net/ethernet/qlogic/qede/qede_roce.c [new file with mode: 0644]
drivers/net/ethernet/qualcomm/emac/emac-sgmii.c
drivers/ptp/ptp_clock.c
include/linux/qed/common_hsi.h
include/linux/qed/qed_if.h
include/linux/qed/qed_ll2_if.h [new file with mode: 0644]
include/linux/qed/qed_roce_if.h [new file with mode: 0644]
include/linux/qed/qede_roce.h [new file with mode: 0644]
include/linux/qed/rdma_common.h
include/net/mpls.h
include/trace/events/rxrpc.h
net/core/skbuff.c
net/mpls/internal.h
net/openvswitch/actions.c
net/openvswitch/flow.c
net/rxrpc/ar-internal.h
net/rxrpc/call_event.c
net/rxrpc/call_object.c
net/rxrpc/conn_event.c
net/rxrpc/input.c
net/rxrpc/local_event.c
net/rxrpc/misc.c
net/rxrpc/recvmsg.c
net/rxrpc/rxkad.c
net/rxrpc/sendmsg.c
net/rxrpc/sysctl.c
net/sched/act_vlan.c

index 8176596..ac1faee 100644 (file)
@@ -7641,7 +7641,6 @@ static int i40e_init_msix(struct i40e_pf *pf)
                        vectors_left--;
                } else {
                        pf->num_fdsb_msix = 0;
-                       pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
                }
        }
 
@@ -7661,6 +7660,8 @@ static int i40e_init_msix(struct i40e_pf *pf)
 #endif
        /* can we reserve enough for iWARP? */
        if (pf->flags & I40E_FLAG_IWARP_ENABLED) {
+               iwarp_requested = pf->num_iwarp_msix;
+
                if (!vectors_left)
                        pf->num_iwarp_msix = 0;
                else if (vectors_left < pf->num_iwarp_msix)
@@ -7674,18 +7675,23 @@ static int i40e_init_msix(struct i40e_pf *pf)
                int vmdq_vecs_wanted = pf->num_vmdq_vsis * pf->num_vmdq_qps;
                int vmdq_vecs = min_t(int, vectors_left, vmdq_vecs_wanted);
 
-               /* if we're short on vectors for what's desired, we limit
-                * the queues per vmdq.  If this is still more than are
-                * available, the user will need to change the number of
-                * queues/vectors used by the PF later with the ethtool
-                * channels command
-                */
-               if (vmdq_vecs < vmdq_vecs_wanted)
-                       pf->num_vmdq_qps = 1;
-               pf->num_vmdq_msix = pf->num_vmdq_qps;
+               if (!vectors_left) {
+                       pf->num_vmdq_msix = 0;
+                       pf->num_vmdq_qps = 0;
+               } else {
+                       /* if we're short on vectors for what's desired, we limit
+                        * the queues per vmdq.  If this is still more than are
+                        * available, the user will need to change the number of
+                        * queues/vectors used by the PF later with the ethtool
+                        * channels command
+                        */
+                       if (vmdq_vecs < vmdq_vecs_wanted)
+                               pf->num_vmdq_qps = 1;
+                       pf->num_vmdq_msix = pf->num_vmdq_qps;
 
-               v_budget += vmdq_vecs;
-               vectors_left -= vmdq_vecs;
+                       v_budget += vmdq_vecs;
+                       vectors_left -= vmdq_vecs;
+               }
        }
 
        pf->msix_entries = kcalloc(v_budget, sizeof(struct msix_entry),
@@ -7697,21 +7703,6 @@ static int i40e_init_msix(struct i40e_pf *pf)
                pf->msix_entries[i].entry = i;
        v_actual = i40e_reserve_msix_vectors(pf, v_budget);
 
-       if (v_actual != v_budget) {
-               /* If we have limited resources, we will start with no vectors
-                * for the special features and then allocate vectors to some
-                * of these features based on the policy and at the end disable
-                * the features that did not get any vectors.
-                */
-               iwarp_requested = pf->num_iwarp_msix;
-               pf->num_iwarp_msix = 0;
-#ifdef I40E_FCOE
-               pf->num_fcoe_qps = 0;
-               pf->num_fcoe_msix = 0;
-#endif
-               pf->num_vmdq_msix = 0;
-       }
-
        if (v_actual < I40E_MIN_MSIX) {
                pf->flags &= ~I40E_FLAG_MSIX_ENABLED;
                kfree(pf->msix_entries);
@@ -7725,9 +7716,16 @@ static int i40e_init_msix(struct i40e_pf *pf)
                pf->num_lan_qps = 1;
                pf->num_lan_msix = 1;
 
-       } else if (v_actual != v_budget) {
+       } else if (!vectors_left) {
+               /* If we have limited resources, we will start with no vectors
+                * for the special features and then allocate vectors to some
+                * of these features based on the policy and at the end disable
+                * the features that did not get any vectors.
+                */
                int vec;
 
+               dev_info(&pf->pdev->dev,
+                        "MSI-X vector limit reached, attempting to redistribute vectors\n");
                /* reserve the misc vector */
                vec = v_actual - 1;
 
@@ -7735,7 +7733,10 @@ static int i40e_init_msix(struct i40e_pf *pf)
                pf->num_vmdq_msix = 1;    /* force VMDqs to only one vector */
                pf->num_vmdq_vsis = 1;
                pf->num_vmdq_qps = 1;
-               pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
+#ifdef I40E_FCOE
+               pf->num_fcoe_qps = 0;
+               pf->num_fcoe_msix = 0;
+#endif
 
                /* partition out the remaining vectors */
                switch (vec) {
@@ -7767,9 +7768,14 @@ static int i40e_init_msix(struct i40e_pf *pf)
                                pf->num_vmdq_vsis = min_t(int, (vec / 2),
                                                  I40E_DEFAULT_NUM_VMDQ_VSI);
                        }
+                       if (pf->flags & I40E_FLAG_FD_SB_ENABLED) {
+                               pf->num_fdsb_msix = 1;
+                               vec--;
+                       }
                        pf->num_lan_msix = min_t(int,
                               (vec - (pf->num_iwarp_msix + pf->num_vmdq_vsis)),
                                                              pf->num_lan_msix);
+                       pf->num_lan_qps = pf->num_lan_msix;
 #ifdef I40E_FCOE
                        /* give one vector to FCoE */
                        if (pf->flags & I40E_FLAG_FCOE_ENABLED) {
@@ -7781,6 +7787,11 @@ static int i40e_init_msix(struct i40e_pf *pf)
                }
        }
 
+       if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) &&
+           (pf->num_fdsb_msix == 0)) {
+               dev_info(&pf->pdev->dev, "Sideband Flowdir disabled, not enough MSI-X vectors\n");
+               pf->flags &= ~I40E_FLAG_FD_SB_ENABLED;
+       }
        if ((pf->flags & I40E_FLAG_VMDQ_ENABLED) &&
            (pf->num_vmdq_msix == 0)) {
                dev_info(&pf->pdev->dev, "VMDq disabled, not enough MSI-X vectors\n");
@@ -7799,6 +7810,13 @@ static int i40e_init_msix(struct i40e_pf *pf)
                pf->flags &= ~I40E_FLAG_FCOE_ENABLED;
        }
 #endif
+       i40e_debug(&pf->hw, I40E_DEBUG_INIT,
+                  "MSI-X vector distribution: PF %d, VMDq %d, FDSB %d, iWARP %d\n",
+                  pf->num_lan_msix,
+                  pf->num_vmdq_msix * pf->num_vmdq_vsis,
+                  pf->num_fdsb_msix,
+                  pf->num_iwarp_msix);
+
        return v_actual;
 }
 
@@ -11382,6 +11400,12 @@ static pci_ers_result_t i40e_pci_error_detected(struct pci_dev *pdev,
 
        dev_info(&pdev->dev, "%s: error %d\n", __func__, error);
 
+       if (!pf) {
+               dev_info(&pdev->dev,
+                        "Cannot recover - error happened during device probe\n");
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+
        /* shutdown all operations */
        if (!test_bit(__I40E_SUSPENDED, &pf->state)) {
                rtnl_lock();
index 8982c88..a0d1b08 100644 (file)
@@ -211,8 +211,7 @@ static int orion_mdio_probe(struct platform_device *pdev)
        dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
        if (!dev->regs) {
                dev_err(&pdev->dev, "Unable to remap SMI register\n");
-               ret = -ENODEV;
-               goto out_mdio;
+               return -ENODEV;
        }
 
        init_waitqueue_head(&dev->smi_busy_wait);
index a350b71..ce8c54d 100644 (file)
@@ -451,7 +451,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol,
        struct mlx5e_tc_flow *flow;
        struct mlx5_flow_spec *spec;
        struct mlx5_flow_rule *old = NULL;
-       struct mlx5_esw_flow_attr *old_attr;
+       struct mlx5_esw_flow_attr *old_attr = NULL;
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
 
        if (esw && esw->mode == SRIOV_OFFLOADS)
index 48d50ef..78fc557 100644 (file)
@@ -1640,7 +1640,7 @@ mlxsw_sp_router_fib4_entry_init(struct mlxsw_sp *mlxsw_sp,
                                struct mlxsw_sp_fib_entry *fib_entry)
 {
        struct fib_info *fi = fen_info->fi;
-       struct mlxsw_sp_rif *r;
+       struct mlxsw_sp_rif *r = NULL;
        int nhsel;
        int err;
 
@@ -1664,11 +1664,15 @@ mlxsw_sp_router_fib4_entry_init(struct mlxsw_sp *mlxsw_sp,
                         * to us. Set trap and pass the packets for
                         * this prefix to kernel.
                         */
-                       fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
-                       return 0;
+                       break;
                }
        }
 
+       if (!r) {
+               fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
+               return 0;
+       }
+
        if (fi->fib_scope != RT_SCOPE_UNIVERSE) {
                fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
                fib_entry->rif = r->rif;
index 6ba4840..0df1391 100644 (file)
@@ -88,6 +88,9 @@ config QED
        ---help---
          This enables the support for ...
 
+config QED_LL2
+       bool
+
 config QED_SRIOV
        bool "QLogic QED 25/40/100Gb SR-IOV support"
        depends on QED && PCI_IOV
@@ -104,4 +107,15 @@ config QEDE
        ---help---
          This enables the support for ...
 
+config INFINIBAND_QEDR
+       tristate "QLogic qede RoCE sources [debug]"
+       depends on QEDE && 64BIT
+       select QED_LL2
+       default n
+       ---help---
+         This provides a temporary node that allows the compilation
+         and logical testing of the InfiniBand over Ethernet support
+         for QLogic QED. This would be replaced by the 'real' option
+         once the QEDR driver is added [+relocated].
+
 endif # NET_VENDOR_QLOGIC
index 86a5b4f..cda0af7 100644 (file)
@@ -4,3 +4,5 @@ qed-y := qed_cxt.o qed_dev.o qed_hw.o qed_init_fw_funcs.o qed_init_ops.o \
         qed_int.o qed_main.o qed_mcp.o qed_sp_commands.o qed_spq.o qed_l2.o \
         qed_selftest.o qed_dcbx.o qed_debug.o
 qed-$(CONFIG_QED_SRIOV) += qed_sriov.o qed_vf.o
+qed-$(CONFIG_QED_LL2) += qed_ll2.o
+qed-$(CONFIG_INFINIBAND_QEDR) += qed_roce.o
index 0929582..653bb57 100644 (file)
@@ -35,6 +35,9 @@ extern const struct qed_common_ops qed_common_ops_pass;
 
 #define QED_WFQ_UNIT   100
 
+#define QED_WID_SIZE            (1024)
+#define QED_PF_DEMS_SIZE        (4)
+
 /* cau states */
 enum qed_coalescing_mode {
        QED_COAL_MODE_DISABLE,
@@ -48,6 +51,14 @@ enum qed_mcp_protocol_type;
 
 /* helpers */
 static inline u32 qed_db_addr(u32 cid, u32 DEMS)
+{
+       u32 db_addr = FIELD_VALUE(DB_LEGACY_ADDR_DEMS, DEMS) |
+                     (cid * QED_PF_DEMS_SIZE);
+
+       return db_addr;
+}
+
+static inline u32 qed_db_addr_vf(u32 cid, u32 DEMS)
 {
        u32 db_addr = FIELD_VALUE(DB_LEGACY_ADDR_DEMS, DEMS) |
                      FIELD_VALUE(DB_LEGACY_ADDR_ICID, cid);
@@ -72,6 +83,7 @@ struct qed_sb_info;
 struct qed_sb_attn_info;
 struct qed_cxt_mngr;
 struct qed_sb_sp_info;
+struct qed_ll2_info;
 struct qed_mcp_info;
 
 struct qed_rt_data {
@@ -151,13 +163,17 @@ enum QED_RESOURCES {
        QED_RL,
        QED_MAC,
        QED_VLAN,
+       QED_RDMA_CNQ_RAM,
        QED_ILT,
+       QED_LL2_QUEUE,
+       QED_RDMA_STATS_QUEUE,
        QED_MAX_RESC,
 };
 
 enum QED_FEATURE {
        QED_PF_L2_QUE,
        QED_VF,
+       QED_RDMA_CNQ,
        QED_MAX_FEATURES,
 };
 
@@ -360,6 +376,9 @@ struct qed_hwfn {
        struct qed_sb_attn_info         *p_sb_attn;
 
        /* Protocol related */
+       bool                            using_ll2;
+       struct qed_ll2_info             *p_ll2_info;
+       struct qed_rdma_info            *p_rdma_info;
        struct qed_pf_params            pf_params;
 
        bool b_rdma_enabled_in_prs;
@@ -398,6 +417,17 @@ struct qed_hwfn {
 
        struct dbg_tools_data           dbg_info;
 
+       /* PWM region specific data */
+       u32                             dpi_size;
+       u32                             dpi_count;
+
+       /* This is used to calculate the doorbell address */
+       u32 dpi_start_offset;
+
+       /* If one of the following is set then EDPM shouldn't be used */
+       u8 dcbx_no_edpm;
+       u8 db_bar_no_edpm;
+
        struct qed_simd_fp_handler      simd_proto_handler[64];
 
 #ifdef CONFIG_QED_SRIOV
@@ -407,6 +437,7 @@ struct qed_hwfn {
 #endif
 
        struct z_stream_s               *stream;
+       struct qed_roce_ll2_info        *ll2;
 };
 
 struct pci_params {
@@ -431,6 +462,8 @@ struct qed_int_params {
        bool                    fp_initialized;
        u8                      fp_msix_base;
        u8                      fp_msix_cnt;
+       u8                      rdma_msix_base;
+       u8                      rdma_msix_cnt;
 };
 
 struct qed_dbg_feature {
@@ -537,7 +570,6 @@ struct qed_dev {
 
        bool                            b_is_vf;
        u32                             drv_type;
-
        struct qed_eth_stats            *reset_stats;
        struct qed_fw_data              *fw_data;
 
@@ -564,7 +596,16 @@ struct qed_dev {
 
        struct qed_dbg_params           dbg_params;
 
+#ifdef CONFIG_QED_LL2
+       struct qed_cb_ll2_info          *ll2;
+       u8                              ll2_mac_address[ETH_ALEN];
+#endif
+
        const struct firmware           *firmware;
+
+       u32 rdma_max_sge;
+       u32 rdma_max_inline;
+       u32 rdma_max_srq_sge;
 };
 
 #define NUM_OF_VFS(dev)         MAX_NUM_VFS_BB
index dd579b2..82370a1 100644 (file)
 #define TM_ELEM_SIZE    4
 
 /* ILT constants */
+#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
+/* For RoCE we configure to 64K to cover for RoCE max tasks 256K purpose. */
+#define ILT_DEFAULT_HW_P_SIZE          4
+#else
 #define ILT_DEFAULT_HW_P_SIZE          3
+#endif
+
 #define ILT_PAGE_IN_BYTES(hw_p_size)   (1U << ((hw_p_size) + 12))
 #define ILT_CFG_REG(cli, reg)  PSWRQ2_REG_ ## cli ## _ ## reg ## _RT_OFFSET
 
@@ -1839,6 +1845,8 @@ int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn)
        /* Set the number of required CORE connections */
        u32 core_cids = 1; /* SPQ */
 
+       if (p_hwfn->using_ll2)
+               core_cids += 4;
        qed_cxt_set_proto_cid_count(p_hwfn, PROTOCOLID_CORE, core_cids, 0);
 
        switch (p_hwfn->hw_info.personality) {
index c6f6f2e..2b8bdaa 100644 (file)
@@ -170,6 +170,13 @@ int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
  */
 void qed_cxt_release_cid(struct qed_hwfn *p_hwfn,
                         u32 cid);
+int qed_cxt_dynamic_ilt_alloc(struct qed_hwfn *p_hwfn,
+                             enum qed_cxt_elem_type elem_type, u32 iid);
+u32 qed_cxt_get_proto_tid_count(struct qed_hwfn *p_hwfn,
+                               enum protocol_type type);
+u32 qed_cxt_get_proto_cid_start(struct qed_hwfn *p_hwfn,
+                               enum protocol_type type);
+int qed_cxt_free_proto_ilt(struct qed_hwfn *p_hwfn, enum protocol_type proto);
 
 #define QED_CTX_WORKING_MEM 0
 #define QED_CTX_FL_MEM 1
index 13d8b40..754f6a9 100644 (file)
 #include "qed_hw.h"
 #include "qed_init_ops.h"
 #include "qed_int.h"
+#include "qed_ll2.h"
 #include "qed_mcp.h"
 #include "qed_reg_addr.h"
 #include "qed_sp.h"
 #include "qed_sriov.h"
 #include "qed_vf.h"
+#include "qed_roce.h"
 
 static DEFINE_SPINLOCK(qm_lock);
 
+#define QED_MIN_DPIS            (4)
+#define QED_MIN_PWM_REGION      (QED_WID_SIZE * QED_MIN_DPIS)
+
 /* API common to all protocols */
 enum BAR_ID {
        BAR_ID_0,       /* used for GRC */
@@ -147,6 +152,9 @@ void qed_resc_free(struct qed_dev *cdev)
                qed_eq_free(p_hwfn, p_hwfn->p_eq);
                qed_consq_free(p_hwfn, p_hwfn->p_consq);
                qed_int_free(p_hwfn);
+#ifdef CONFIG_QED_LL2
+               qed_ll2_free(p_hwfn, p_hwfn->p_ll2_info);
+#endif
                qed_iov_free(p_hwfn);
                qed_dmae_info_free(p_hwfn);
                qed_dcbx_info_free(p_hwfn, p_hwfn->p_dcbx_info);
@@ -403,6 +411,9 @@ int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
 
 int qed_resc_alloc(struct qed_dev *cdev)
 {
+#ifdef CONFIG_QED_LL2
+       struct qed_ll2_info *p_ll2_info;
+#endif
        struct qed_consq *p_consq;
        struct qed_eq *p_eq;
        int i, rc = 0;
@@ -513,6 +524,15 @@ int qed_resc_alloc(struct qed_dev *cdev)
                        goto alloc_no_mem;
                p_hwfn->p_consq = p_consq;
 
+#ifdef CONFIG_QED_LL2
+               if (p_hwfn->using_ll2) {
+                       p_ll2_info = qed_ll2_alloc(p_hwfn);
+                       if (!p_ll2_info)
+                               goto alloc_no_mem;
+                       p_hwfn->p_ll2_info = p_ll2_info;
+               }
+#endif
+
                /* DMA info initialization */
                rc = qed_dmae_info_alloc(p_hwfn);
                if (rc)
@@ -561,6 +581,10 @@ void qed_resc_setup(struct qed_dev *cdev)
                qed_int_setup(p_hwfn, p_hwfn->p_main_ptt);
 
                qed_iov_setup(p_hwfn, p_hwfn->p_main_ptt);
+#ifdef CONFIG_QED_LL2
+               if (p_hwfn->using_ll2)
+                       qed_ll2_setup(p_hwfn, p_hwfn->p_ll2_info);
+#endif
        }
 }
 
@@ -767,6 +791,136 @@ static int qed_hw_init_common(struct qed_hwfn *p_hwfn,
        return rc;
 }
 
+static int
+qed_hw_init_dpi_size(struct qed_hwfn *p_hwfn,
+                    struct qed_ptt *p_ptt, u32 pwm_region_size, u32 n_cpus)
+{
+       u32 dpi_page_size_1, dpi_page_size_2, dpi_page_size;
+       u32 dpi_bit_shift, dpi_count;
+       u32 min_dpis;
+
+       /* Calculate DPI size */
+       dpi_page_size_1 = QED_WID_SIZE * n_cpus;
+       dpi_page_size_2 = max_t(u32, QED_WID_SIZE, PAGE_SIZE);
+       dpi_page_size = max_t(u32, dpi_page_size_1, dpi_page_size_2);
+       dpi_page_size = roundup_pow_of_two(dpi_page_size);
+       dpi_bit_shift = ilog2(dpi_page_size / 4096);
+
+       dpi_count = pwm_region_size / dpi_page_size;
+
+       min_dpis = p_hwfn->pf_params.rdma_pf_params.min_dpis;
+       min_dpis = max_t(u32, QED_MIN_DPIS, min_dpis);
+
+       p_hwfn->dpi_size = dpi_page_size;
+       p_hwfn->dpi_count = dpi_count;
+
+       qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_DPI_BIT_SHIFT, dpi_bit_shift);
+
+       if (dpi_count < min_dpis)
+               return -EINVAL;
+
+       return 0;
+}
+
+enum QED_ROCE_EDPM_MODE {
+       QED_ROCE_EDPM_MODE_ENABLE = 0,
+       QED_ROCE_EDPM_MODE_FORCE_ON = 1,
+       QED_ROCE_EDPM_MODE_DISABLE = 2,
+};
+
+static int
+qed_hw_init_pf_doorbell_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
+{
+       u32 pwm_regsize, norm_regsize;
+       u32 non_pwm_conn, min_addr_reg1;
+       u32 db_bar_size, n_cpus;
+       u32 roce_edpm_mode;
+       u32 pf_dems_shift;
+       int rc = 0;
+       u8 cond;
+
+       db_bar_size = qed_hw_bar_size(p_hwfn, BAR_ID_1);
+       if (p_hwfn->cdev->num_hwfns > 1)
+               db_bar_size /= 2;
+
+       /* Calculate doorbell regions */
+       non_pwm_conn = qed_cxt_get_proto_cid_start(p_hwfn, PROTOCOLID_CORE) +
+                      qed_cxt_get_proto_cid_count(p_hwfn, PROTOCOLID_CORE,
+                                                  NULL) +
+                      qed_cxt_get_proto_cid_count(p_hwfn, PROTOCOLID_ETH,
+                                                  NULL);
+       norm_regsize = roundup(QED_PF_DEMS_SIZE * non_pwm_conn, 4096);
+       min_addr_reg1 = norm_regsize / 4096;
+       pwm_regsize = db_bar_size - norm_regsize;
+
+       /* Check that the normal and PWM sizes are valid */
+       if (db_bar_size < norm_regsize) {
+               DP_ERR(p_hwfn->cdev,
+                      "Doorbell BAR size 0x%x is too small (normal region is 0x%0x )\n",
+                      db_bar_size, norm_regsize);
+               return -EINVAL;
+       }
+
+       if (pwm_regsize < QED_MIN_PWM_REGION) {
+               DP_ERR(p_hwfn->cdev,
+                      "PWM region size 0x%0x is too small. Should be at least 0x%0x (Doorbell BAR size is 0x%x and normal region size is 0x%0x)\n",
+                      pwm_regsize,
+                      QED_MIN_PWM_REGION, db_bar_size, norm_regsize);
+               return -EINVAL;
+       }
+
+       /* Calculate number of DPIs */
+       roce_edpm_mode = p_hwfn->pf_params.rdma_pf_params.roce_edpm_mode;
+       if ((roce_edpm_mode == QED_ROCE_EDPM_MODE_ENABLE) ||
+           ((roce_edpm_mode == QED_ROCE_EDPM_MODE_FORCE_ON))) {
+               /* Either EDPM is mandatory, or we are attempting to allocate a
+                * WID per CPU.
+                */
+               n_cpus = num_active_cpus();
+               rc = qed_hw_init_dpi_size(p_hwfn, p_ptt, pwm_regsize, n_cpus);
+       }
+
+       cond = (rc && (roce_edpm_mode == QED_ROCE_EDPM_MODE_ENABLE)) ||
+              (roce_edpm_mode == QED_ROCE_EDPM_MODE_DISABLE);
+       if (cond || p_hwfn->dcbx_no_edpm) {
+               /* Either EDPM is disabled from user configuration, or it is
+                * disabled via DCBx, or it is not mandatory and we failed to
+                * allocated a WID per CPU.
+                */
+               n_cpus = 1;
+               rc = qed_hw_init_dpi_size(p_hwfn, p_ptt, pwm_regsize, n_cpus);
+
+               if (cond)
+                       qed_rdma_dpm_bar(p_hwfn, p_ptt);
+       }
+
+       DP_INFO(p_hwfn,
+               "doorbell bar: normal_region_size=%d, pwm_region_size=%d, dpi_size=%d, dpi_count=%d, roce_edpm=%s\n",
+               norm_regsize,
+               pwm_regsize,
+               p_hwfn->dpi_size,
+               p_hwfn->dpi_count,
+               ((p_hwfn->dcbx_no_edpm) || (p_hwfn->db_bar_no_edpm)) ?
+               "disabled" : "enabled");
+
+       if (rc) {
+               DP_ERR(p_hwfn,
+                      "Failed to allocate enough DPIs. Allocated %d but the current minimum is %d.\n",
+                      p_hwfn->dpi_count,
+                      p_hwfn->pf_params.rdma_pf_params.min_dpis);
+               return -EINVAL;
+       }
+
+       p_hwfn->dpi_start_offset = norm_regsize;
+
+       /* DEMS size is configured log2 of DWORDs, hence the division by 4 */
+       pf_dems_shift = ilog2(QED_PF_DEMS_SIZE / 4);
+       qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_ICID_BIT_SHIFT_NORM, pf_dems_shift);
+       qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_MIN_ADDR_REG1, min_addr_reg1);
+
+       return 0;
+}
+
 static int qed_hw_init_port(struct qed_hwfn *p_hwfn,
                            struct qed_ptt *p_ptt, int hw_mode)
 {
@@ -840,6 +994,10 @@ static int qed_hw_init_pf(struct qed_hwfn *p_hwfn,
        /* Pure runtime initializations - directly to the HW  */
        qed_int_igu_init_pure_rt(p_hwfn, p_ptt, true, true);
 
+       rc = qed_hw_init_pf_doorbell_bar(p_hwfn, p_ptt);
+       if (rc)
+               return rc;
+
        if (b_hw_start) {
                /* enable interrupts */
                qed_int_igu_enable(p_hwfn, p_ptt, int_mode);
@@ -1264,6 +1422,19 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn)
        u32 *feat_num = p_hwfn->hw_info.feat_num;
        int num_features = 1;
 
+#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
+       /* Roce CNQ each requires: 1 status block + 1 CNQ. We divide the
+        * status blocks equally between L2 / RoCE but with consideration as
+        * to how many l2 queues / cnqs we have
+        */
+       if (p_hwfn->hw_info.personality == QED_PCI_ETH_ROCE) {
+               num_features++;
+
+               feat_num[QED_RDMA_CNQ] =
+                       min_t(u32, RESC_NUM(p_hwfn, QED_SB) / num_features,
+                             RESC_NUM(p_hwfn, QED_RDMA_CNQ_RAM));
+       }
+#endif
        feat_num[QED_PF_L2_QUE] = min_t(u32, RESC_NUM(p_hwfn, QED_SB) /
                                                num_features,
                                        RESC_NUM(p_hwfn, QED_L2_QUEUE));
@@ -1304,6 +1475,10 @@ static int qed_hw_get_resc(struct qed_hwfn *p_hwfn)
        resc_num[QED_VLAN] = (ETH_NUM_VLAN_FILTERS - 1 /*For vlan0*/) /
                             num_funcs;
        resc_num[QED_ILT] = PXP_NUM_ILT_RECORDS_BB / num_funcs;
+       resc_num[QED_LL2_QUEUE] = MAX_NUM_LL2_RX_QUEUES / num_funcs;
+       resc_num[QED_RDMA_CNQ_RAM] = NUM_OF_CMDQS_CQS / num_funcs;
+       resc_num[QED_RDMA_STATS_QUEUE] = RDMA_NUM_STATISTIC_COUNTERS_BB /
+                                        num_funcs;
 
        for (i = 0; i < QED_MAX_RESC; i++)
                resc_start[i] = resc_num[i] * enabled_func_idx;
@@ -1327,7 +1502,8 @@ static int qed_hw_get_resc(struct qed_hwfn *p_hwfn)
                   "RL = %d start = %d\n"
                   "MAC = %d start = %d\n"
                   "VLAN = %d start = %d\n"
-                  "ILT = %d start = %d\n",
+                  "ILT = %d start = %d\n"
+                  "LL2_QUEUE = %d start = %d\n",
                   p_hwfn->hw_info.resc_num[QED_SB],
                   p_hwfn->hw_info.resc_start[QED_SB],
                   p_hwfn->hw_info.resc_num[QED_L2_QUEUE],
@@ -1343,7 +1519,9 @@ static int qed_hw_get_resc(struct qed_hwfn *p_hwfn)
                   p_hwfn->hw_info.resc_num[QED_VLAN],
                   p_hwfn->hw_info.resc_start[QED_VLAN],
                   p_hwfn->hw_info.resc_num[QED_ILT],
-                  p_hwfn->hw_info.resc_start[QED_ILT]);
+                  p_hwfn->hw_info.resc_start[QED_ILT],
+                  RESC_NUM(p_hwfn, QED_LL2_QUEUE),
+                  RESC_START(p_hwfn, QED_LL2_QUEUE));
 
        return 0;
 }
@@ -2133,6 +2311,98 @@ int qed_fw_rss_eng(struct qed_hwfn *p_hwfn, u8 src_id, u8 *dst_id)
        return 0;
 }
 
+static void qed_llh_mac_to_filter(u32 *p_high, u32 *p_low,
+                                 u8 *p_filter)
+{
+       *p_high = p_filter[1] | (p_filter[0] << 8);
+       *p_low = p_filter[5] | (p_filter[4] << 8) |
+                (p_filter[3] << 16) | (p_filter[2] << 24);
+}
+
+int qed_llh_add_mac_filter(struct qed_hwfn *p_hwfn,
+                          struct qed_ptt *p_ptt, u8 *p_filter)
+{
+       u32 high = 0, low = 0, en;
+       int i;
+
+       if (!(IS_MF_SI(p_hwfn) || IS_MF_DEFAULT(p_hwfn)))
+               return 0;
+
+       qed_llh_mac_to_filter(&high, &low, p_filter);
+
+       /* Find a free entry and utilize it */
+       for (i = 0; i < NIG_REG_LLH_FUNC_FILTER_EN_SIZE; i++) {
+               en = qed_rd(p_hwfn, p_ptt,
+                           NIG_REG_LLH_FUNC_FILTER_EN + i * sizeof(u32));
+               if (en)
+                       continue;
+               qed_wr(p_hwfn, p_ptt,
+                      NIG_REG_LLH_FUNC_FILTER_VALUE +
+                      2 * i * sizeof(u32), low);
+               qed_wr(p_hwfn, p_ptt,
+                      NIG_REG_LLH_FUNC_FILTER_VALUE +
+                      (2 * i + 1) * sizeof(u32), high);
+               qed_wr(p_hwfn, p_ptt,
+                      NIG_REG_LLH_FUNC_FILTER_MODE + i * sizeof(u32), 0);
+               qed_wr(p_hwfn, p_ptt,
+                      NIG_REG_LLH_FUNC_FILTER_PROTOCOL_TYPE +
+                      i * sizeof(u32), 0);
+               qed_wr(p_hwfn, p_ptt,
+                      NIG_REG_LLH_FUNC_FILTER_EN + i * sizeof(u32), 1);
+               break;
+       }
+       if (i >= NIG_REG_LLH_FUNC_FILTER_EN_SIZE) {
+               DP_NOTICE(p_hwfn,
+                         "Failed to find an empty LLH filter to utilize\n");
+               return -EINVAL;
+       }
+
+       DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
+                  "mac: %pM is added at %d\n",
+                  p_filter, i);
+
+       return 0;
+}
+
+void qed_llh_remove_mac_filter(struct qed_hwfn *p_hwfn,
+                              struct qed_ptt *p_ptt, u8 *p_filter)
+{
+       u32 high = 0, low = 0;
+       int i;
+
+       if (!(IS_MF_SI(p_hwfn) || IS_MF_DEFAULT(p_hwfn)))
+               return;
+
+       qed_llh_mac_to_filter(&high, &low, p_filter);
+
+       /* Find the entry and clean it */
+       for (i = 0; i < NIG_REG_LLH_FUNC_FILTER_EN_SIZE; i++) {
+               if (qed_rd(p_hwfn, p_ptt,
+                          NIG_REG_LLH_FUNC_FILTER_VALUE +
+                          2 * i * sizeof(u32)) != low)
+                       continue;
+               if (qed_rd(p_hwfn, p_ptt,
+                          NIG_REG_LLH_FUNC_FILTER_VALUE +
+                          (2 * i + 1) * sizeof(u32)) != high)
+                       continue;
+
+               qed_wr(p_hwfn, p_ptt,
+                      NIG_REG_LLH_FUNC_FILTER_EN + i * sizeof(u32), 0);
+               qed_wr(p_hwfn, p_ptt,
+                      NIG_REG_LLH_FUNC_FILTER_VALUE + 2 * i * sizeof(u32), 0);
+               qed_wr(p_hwfn, p_ptt,
+                      NIG_REG_LLH_FUNC_FILTER_VALUE +
+                      (2 * i + 1) * sizeof(u32), 0);
+
+               DP_VERBOSE(p_hwfn, NETIF_MSG_HW,
+                          "mac: %pM is removed from %d\n",
+                          p_filter, i);
+               break;
+       }
+       if (i >= NIG_REG_LLH_FUNC_FILTER_EN_SIZE)
+               DP_NOTICE(p_hwfn, "Tried to remove a non-configured filter\n");
+}
+
 static int qed_set_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
                            u32 hw_addr, void *p_eth_qzone,
                            size_t eth_qzone_size, u8 timeset)
index 343bb03..b6711c1 100644 (file)
@@ -309,6 +309,26 @@ int qed_fw_rss_eng(struct qed_hwfn *p_hwfn,
                   u8 src_id,
                   u8 *dst_id);
 
+/**
+ * @brief qed_llh_add_mac_filter - configures a MAC filter in llh
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param p_filter - MAC to add
+ */
+int qed_llh_add_mac_filter(struct qed_hwfn *p_hwfn,
+                          struct qed_ptt *p_ptt, u8 *p_filter);
+
+/**
+ * @brief qed_llh_remove_mac_filter - removes a MAC filter from llh
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param p_filter - MAC to remove
+ */
+void qed_llh_remove_mac_filter(struct qed_hwfn *p_hwfn,
+                              struct qed_ptt *p_ptt, u8 *p_filter);
+
 /**
  * *@brief Cleanup of previous driver remains prior to load
  *
index 2777d5b..72eee29 100644 (file)
@@ -727,6 +727,9 @@ struct core_tx_bd_flags {
 #define CORE_TX_BD_FLAGS_L4_PROTOCOL_SHIFT     6
 #define CORE_TX_BD_FLAGS_L4_PSEUDO_CSUM_MODE_MASK      0x1
 #define CORE_TX_BD_FLAGS_L4_PSEUDO_CSUM_MODE_SHIFT 7
+#define CORE_TX_BD_FLAGS_ROCE_FLAV_MASK                0x1
+#define CORE_TX_BD_FLAGS_ROCE_FLAV_SHIFT       12
+
 };
 
 struct core_tx_bd {
diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c
new file mode 100644 (file)
index 0000000..a6db107
--- /dev/null
@@ -0,0 +1,1792 @@
+/* QLogic qed NIC Driver
+ *
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available 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.
+ */
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/dma-mapping.h>
+#include <linux/if_vlan.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <net/ipv6.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/qed/qed_ll2_if.h>
+#include "qed.h"
+#include "qed_cxt.h"
+#include "qed_dev_api.h"
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_int.h"
+#include "qed_ll2.h"
+#include "qed_mcp.h"
+#include "qed_reg_addr.h"
+#include "qed_sp.h"
+
+#define QED_LL2_RX_REGISTERED(ll2)     ((ll2)->rx_queue.b_cb_registred)
+#define QED_LL2_TX_REGISTERED(ll2)     ((ll2)->tx_queue.b_cb_registred)
+
+#define QED_LL2_TX_SIZE (256)
+#define QED_LL2_RX_SIZE (4096)
+
+struct qed_cb_ll2_info {
+       int rx_cnt;
+       u32 rx_size;
+       u8 handle;
+       bool frags_mapped;
+
+       /* Lock protecting LL2 buffer lists in sleepless context */
+       spinlock_t lock;
+       struct list_head list;
+
+       const struct qed_ll2_cb_ops *cbs;
+       void *cb_cookie;
+};
+
+struct qed_ll2_buffer {
+       struct list_head list;
+       void *data;
+       dma_addr_t phys_addr;
+};
+
+static void qed_ll2b_complete_tx_packet(struct qed_hwfn *p_hwfn,
+                                       u8 connection_handle,
+                                       void *cookie,
+                                       dma_addr_t first_frag_addr,
+                                       bool b_last_fragment,
+                                       bool b_last_packet)
+{
+       struct qed_dev *cdev = p_hwfn->cdev;
+       struct sk_buff *skb = cookie;
+
+       /* All we need to do is release the mapping */
+       dma_unmap_single(&p_hwfn->cdev->pdev->dev, first_frag_addr,
+                        skb_headlen(skb), DMA_TO_DEVICE);
+
+       if (cdev->ll2->cbs && cdev->ll2->cbs->tx_cb)
+               cdev->ll2->cbs->tx_cb(cdev->ll2->cb_cookie, skb,
+                                     b_last_fragment);
+
+       if (cdev->ll2->frags_mapped)
+               /* Case where mapped frags were received, need to
+                * free skb with nr_frags marked as 0
+                */
+               skb_shinfo(skb)->nr_frags = 0;
+
+       dev_kfree_skb_any(skb);
+}
+
+static int qed_ll2_alloc_buffer(struct qed_dev *cdev,
+                               u8 **data, dma_addr_t *phys_addr)
+{
+       *data = kmalloc(cdev->ll2->rx_size, GFP_ATOMIC);
+       if (!(*data)) {
+               DP_INFO(cdev, "Failed to allocate LL2 buffer data\n");
+               return -ENOMEM;
+       }
+
+       *phys_addr = dma_map_single(&cdev->pdev->dev,
+                                   ((*data) + NET_SKB_PAD),
+                                   cdev->ll2->rx_size, DMA_FROM_DEVICE);
+       if (dma_mapping_error(&cdev->pdev->dev, *phys_addr)) {
+               DP_INFO(cdev, "Failed to map LL2 buffer data\n");
+               kfree((*data));
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static int qed_ll2_dealloc_buffer(struct qed_dev *cdev,
+                                struct qed_ll2_buffer *buffer)
+{
+       spin_lock_bh(&cdev->ll2->lock);
+
+       dma_unmap_single(&cdev->pdev->dev, buffer->phys_addr,
+                        cdev->ll2->rx_size, DMA_FROM_DEVICE);
+       kfree(buffer->data);
+       list_del(&buffer->list);
+
+       cdev->ll2->rx_cnt--;
+       if (!cdev->ll2->rx_cnt)
+               DP_INFO(cdev, "All LL2 entries were removed\n");
+
+       spin_unlock_bh(&cdev->ll2->lock);
+
+       return 0;
+}
+
+static void qed_ll2_kill_buffers(struct qed_dev *cdev)
+{
+       struct qed_ll2_buffer *buffer, *tmp_buffer;
+
+       list_for_each_entry_safe(buffer, tmp_buffer, &cdev->ll2->list, list)
+               qed_ll2_dealloc_buffer(cdev, buffer);
+}
+
+void qed_ll2b_complete_rx_packet(struct qed_hwfn *p_hwfn,
+                                u8 connection_handle,
+                                struct qed_ll2_rx_packet *p_pkt,
+                                struct core_rx_fast_path_cqe *p_cqe,
+                                bool b_last_packet)
+{
+       u16 packet_length = le16_to_cpu(p_cqe->packet_length);
+       struct qed_ll2_buffer *buffer = p_pkt->cookie;
+       struct qed_dev *cdev = p_hwfn->cdev;
+       u16 vlan = le16_to_cpu(p_cqe->vlan);
+       u32 opaque_data_0, opaque_data_1;
+       u8 pad = p_cqe->placement_offset;
+       dma_addr_t new_phys_addr;
+       struct sk_buff *skb;
+       bool reuse = false;
+       int rc = -EINVAL;
+       u8 *new_data;
+
+       opaque_data_0 = le32_to_cpu(p_cqe->opaque_data.data[0]);
+       opaque_data_1 = le32_to_cpu(p_cqe->opaque_data.data[1]);
+
+       DP_VERBOSE(p_hwfn,
+                  (NETIF_MSG_RX_STATUS | QED_MSG_STORAGE | NETIF_MSG_PKTDATA),
+                  "Got an LL2 Rx completion: [Buffer at phys 0x%llx, offset 0x%02x] Length 0x%04x Parse_flags 0x%04x vlan 0x%04x Opaque data [0x%08x:0x%08x]\n",
+                  (u64)p_pkt->rx_buf_addr, pad, packet_length,
+                  le16_to_cpu(p_cqe->parse_flags.flags), vlan,
+                  opaque_data_0, opaque_data_1);
+
+       if ((cdev->dp_module & NETIF_MSG_PKTDATA) && buffer->data) {
+               print_hex_dump(KERN_INFO, "",
+                              DUMP_PREFIX_OFFSET, 16, 1,
+                              buffer->data, packet_length, false);
+       }
+
+       /* Determine if data is valid */
+       if (packet_length < ETH_HLEN)
+               reuse = true;
+
+       /* Allocate a replacement for buffer; Reuse upon failure */
+       if (!reuse)
+               rc = qed_ll2_alloc_buffer(p_hwfn->cdev, &new_data,
+                                         &new_phys_addr);
+
+       /* If need to reuse or there's no replacement buffer, repost this */
+       if (rc)
+               goto out_post;
+
+       skb = build_skb(buffer->data, 0);
+       if (!skb) {
+               rc = -ENOMEM;
+               goto out_post;
+       }
+
+       pad += NET_SKB_PAD;
+       skb_reserve(skb, pad);
+       skb_put(skb, packet_length);
+       skb_checksum_none_assert(skb);
+
+       /* Get parital ethernet information instead of eth_type_trans(),
+        * Since we don't have an associated net_device.
+        */
+       skb_reset_mac_header(skb);
+       skb->protocol = eth_hdr(skb)->h_proto;
+
+       /* Pass SKB onward */
+       if (cdev->ll2->cbs && cdev->ll2->cbs->rx_cb) {
+               if (vlan)
+                       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan);
+               cdev->ll2->cbs->rx_cb(cdev->ll2->cb_cookie, skb,
+                                     opaque_data_0, opaque_data_1);
+       }
+
+       /* Update Buffer information and update FW producer */
+       buffer->data = new_data;
+       buffer->phys_addr = new_phys_addr;
+
+out_post:
+       rc = qed_ll2_post_rx_buffer(QED_LEADING_HWFN(cdev), cdev->ll2->handle,
+                                   buffer->phys_addr, 0,  buffer, 1);
+
+       if (rc)
+               qed_ll2_dealloc_buffer(cdev, buffer);
+}
+
+static struct qed_ll2_info *__qed_ll2_handle_sanity(struct qed_hwfn *p_hwfn,
+                                                   u8 connection_handle,
+                                                   bool b_lock,
+                                                   bool b_only_active)
+{
+       struct qed_ll2_info *p_ll2_conn, *p_ret = NULL;
+
+       if (connection_handle >= QED_MAX_NUM_OF_LL2_CONNECTIONS)
+               return NULL;
+
+       if (!p_hwfn->p_ll2_info)
+               return NULL;
+
+       p_ll2_conn = &p_hwfn->p_ll2_info[connection_handle];
+
+       if (b_only_active) {
+               if (b_lock)
+                       mutex_lock(&p_ll2_conn->mutex);
+               if (p_ll2_conn->b_active)
+                       p_ret = p_ll2_conn;
+               if (b_lock)
+                       mutex_unlock(&p_ll2_conn->mutex);
+       } else {
+               p_ret = p_ll2_conn;
+       }
+
+       return p_ret;
+}
+
+static struct qed_ll2_info *qed_ll2_handle_sanity(struct qed_hwfn *p_hwfn,
+                                                 u8 connection_handle)
+{
+       return __qed_ll2_handle_sanity(p_hwfn, connection_handle, false, true);
+}
+
+static struct qed_ll2_info *qed_ll2_handle_sanity_lock(struct qed_hwfn *p_hwfn,
+                                                      u8 connection_handle)
+{
+       return __qed_ll2_handle_sanity(p_hwfn, connection_handle, true, true);
+}
+
+static struct qed_ll2_info *qed_ll2_handle_sanity_inactive(struct qed_hwfn
+                                                          *p_hwfn,
+                                                          u8 connection_handle)
+{
+       return __qed_ll2_handle_sanity(p_hwfn, connection_handle, false, false);
+}
+
+static void qed_ll2_txq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
+{
+       bool b_last_packet = false, b_last_frag = false;
+       struct qed_ll2_tx_packet *p_pkt = NULL;
+       struct qed_ll2_info *p_ll2_conn;
+       struct qed_ll2_tx_queue *p_tx;
+       dma_addr_t tx_frag;
+
+       p_ll2_conn = qed_ll2_handle_sanity_inactive(p_hwfn, connection_handle);
+       if (!p_ll2_conn)
+               return;
+
+       p_tx = &p_ll2_conn->tx_queue;
+
+       while (!list_empty(&p_tx->active_descq)) {
+               p_pkt = list_first_entry(&p_tx->active_descq,
+                                        struct qed_ll2_tx_packet, list_entry);
+               if (!p_pkt)
+                       break;
+
+               list_del(&p_pkt->list_entry);
+               b_last_packet = list_empty(&p_tx->active_descq);
+               list_add_tail(&p_pkt->list_entry, &p_tx->free_descq);
+               p_tx->cur_completing_packet = *p_pkt;
+               p_tx->cur_completing_bd_idx = 1;
+               b_last_frag = p_tx->cur_completing_bd_idx == p_pkt->bd_used;
+               tx_frag = p_pkt->bds_set[0].tx_frag;
+               if (p_ll2_conn->gsi_enable)
+                       qed_ll2b_release_tx_gsi_packet(p_hwfn,
+                                                      p_ll2_conn->my_id,
+                                                      p_pkt->cookie,
+                                                      tx_frag,
+                                                      b_last_frag,
+                                                      b_last_packet);
+               else
+                       qed_ll2b_complete_tx_packet(p_hwfn,
+                                                   p_ll2_conn->my_id,
+                                                   p_pkt->cookie,
+                                                   tx_frag,
+                                                   b_last_frag,
+                                                   b_last_packet);
+
+       }
+}
+
+static int qed_ll2_txq_completion(struct qed_hwfn *p_hwfn, void *p_cookie)
+{
+       struct qed_ll2_info *p_ll2_conn = p_cookie;
+       struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
+       u16 new_idx = 0, num_bds = 0, num_bds_in_packet = 0;
+       struct qed_ll2_tx_packet *p_pkt;
+       bool b_last_frag = false;
+       unsigned long flags;
+       dma_addr_t tx_frag;
+       int rc = -EINVAL;
+
+       spin_lock_irqsave(&p_tx->lock, flags);
+       if (p_tx->b_completing_packet) {
+               rc = -EBUSY;
+               goto out;
+       }
+
+       new_idx = le16_to_cpu(*p_tx->p_fw_cons);
+       num_bds = ((s16)new_idx - (s16)p_tx->bds_idx);
+       while (num_bds) {
+               if (list_empty(&p_tx->active_descq))
+                       goto out;
+
+               p_pkt = list_first_entry(&p_tx->active_descq,
+                                        struct qed_ll2_tx_packet, list_entry);
+               if (!p_pkt)
+                       goto out;
+
+               p_tx->b_completing_packet = true;
+               p_tx->cur_completing_packet = *p_pkt;
+               num_bds_in_packet = p_pkt->bd_used;
+               list_del(&p_pkt->list_entry);
+
+               if (num_bds < num_bds_in_packet) {
+                       DP_NOTICE(p_hwfn,
+                                 "Rest of BDs does not cover whole packet\n");
+                       goto out;
+               }
+
+               num_bds -= num_bds_in_packet;
+               p_tx->bds_idx += num_bds_in_packet;
+               while (num_bds_in_packet--)
+                       qed_chain_consume(&p_tx->txq_chain);
+
+               p_tx->cur_completing_bd_idx = 1;
+               b_last_frag = p_tx->cur_completing_bd_idx == p_pkt->bd_used;
+               list_add_tail(&p_pkt->list_entry, &p_tx->free_descq);
+
+               spin_unlock_irqrestore(&p_tx->lock, flags);
+               tx_frag = p_pkt->bds_set[0].tx_frag;
+               if (p_ll2_conn->gsi_enable)
+                       qed_ll2b_complete_tx_gsi_packet(p_hwfn,
+                                                       p_ll2_conn->my_id,
+                                                       p_pkt->cookie,
+                                                       tx_frag,
+                                                       b_last_frag, !num_bds);
+               else
+                       qed_ll2b_complete_tx_packet(p_hwfn,
+                                                   p_ll2_conn->my_id,
+                                                   p_pkt->cookie,
+                                                   tx_frag,
+                                                   b_last_frag, !num_bds);
+               spin_lock_irqsave(&p_tx->lock, flags);
+       }
+
+       p_tx->b_completing_packet = false;
+       rc = 0;
+out:
+       spin_unlock_irqrestore(&p_tx->lock, flags);
+       return rc;
+}
+
+static int
+qed_ll2_rxq_completion_gsi(struct qed_hwfn *p_hwfn,
+                          struct qed_ll2_info *p_ll2_info,
+                          union core_rx_cqe_union *p_cqe,
+                          unsigned long lock_flags, bool b_last_cqe)
+{
+       struct qed_ll2_rx_queue *p_rx = &p_ll2_info->rx_queue;
+       struct qed_ll2_rx_packet *p_pkt = NULL;
+       u16 packet_length, parse_flags, vlan;
+       u32 src_mac_addrhi;
+       u16 src_mac_addrlo;
+
+       if (!list_empty(&p_rx->active_descq))
+               p_pkt = list_first_entry(&p_rx->active_descq,
+                                        struct qed_ll2_rx_packet, list_entry);
+       if (!p_pkt) {
+               DP_NOTICE(p_hwfn,
+                         "GSI Rx completion but active_descq is empty\n");
+               return -EIO;
+       }
+
+       list_del(&p_pkt->list_entry);
+       parse_flags = le16_to_cpu(p_cqe->rx_cqe_gsi.parse_flags.flags);
+       packet_length = le16_to_cpu(p_cqe->rx_cqe_gsi.data_length);
+       vlan = le16_to_cpu(p_cqe->rx_cqe_gsi.vlan);
+       src_mac_addrhi = le32_to_cpu(p_cqe->rx_cqe_gsi.src_mac_addrhi);
+       src_mac_addrlo = le16_to_cpu(p_cqe->rx_cqe_gsi.src_mac_addrlo);
+       if (qed_chain_consume(&p_rx->rxq_chain) != p_pkt->rxq_bd)
+               DP_NOTICE(p_hwfn,
+                         "Mismatch between active_descq and the LL2 Rx chain\n");
+       list_add_tail(&p_pkt->list_entry, &p_rx->free_descq);
+
+       spin_unlock_irqrestore(&p_rx->lock, lock_flags);
+       qed_ll2b_complete_rx_gsi_packet(p_hwfn,
+                                       p_ll2_info->my_id,
+                                       p_pkt->cookie,
+                                       p_pkt->rx_buf_addr,
+                                       packet_length,
+                                       p_cqe->rx_cqe_gsi.data_length_error,
+                                       parse_flags,
+                                       vlan,
+                                       src_mac_addrhi,
+                                       src_mac_addrlo, b_last_cqe);
+       spin_lock_irqsave(&p_rx->lock, lock_flags);
+
+       return 0;
+}
+
+static int qed_ll2_rxq_completion_reg(struct qed_hwfn *p_hwfn,
+                                     struct qed_ll2_info *p_ll2_conn,
+                                     union core_rx_cqe_union *p_cqe,
+                                     unsigned long lock_flags,
+                                     bool b_last_cqe)
+{
+       struct qed_ll2_rx_queue *p_rx = &p_ll2_conn->rx_queue;
+       struct qed_ll2_rx_packet *p_pkt = NULL;
+
+       if (!list_empty(&p_rx->active_descq))
+               p_pkt = list_first_entry(&p_rx->active_descq,
+                                        struct qed_ll2_rx_packet, list_entry);
+       if (!p_pkt) {
+               DP_NOTICE(p_hwfn,
+                         "LL2 Rx completion but active_descq is empty\n");
+               return -EIO;
+       }
+       list_del(&p_pkt->list_entry);
+
+       if (qed_chain_consume(&p_rx->rxq_chain) != p_pkt->rxq_bd)
+               DP_NOTICE(p_hwfn,
+                         "Mismatch between active_descq and the LL2 Rx chain\n");
+       list_add_tail(&p_pkt->list_entry, &p_rx->free_descq);
+
+       spin_unlock_irqrestore(&p_rx->lock, lock_flags);
+       qed_ll2b_complete_rx_packet(p_hwfn, p_ll2_conn->my_id,
+                                   p_pkt, &p_cqe->rx_cqe_fp, b_last_cqe);
+       spin_lock_irqsave(&p_rx->lock, lock_flags);
+
+       return 0;
+}
+
+static int qed_ll2_rxq_completion(struct qed_hwfn *p_hwfn, void *cookie)
+{
+       struct qed_ll2_info *p_ll2_conn = cookie;
+       struct qed_ll2_rx_queue *p_rx = &p_ll2_conn->rx_queue;
+       union core_rx_cqe_union *cqe = NULL;
+       u16 cq_new_idx = 0, cq_old_idx = 0;
+       unsigned long flags = 0;
+       int rc = 0;
+
+       spin_lock_irqsave(&p_rx->lock, flags);
+       cq_new_idx = le16_to_cpu(*p_rx->p_fw_cons);
+       cq_old_idx = qed_chain_get_cons_idx(&p_rx->rcq_chain);
+
+       while (cq_new_idx != cq_old_idx) {
+               bool b_last_cqe = (cq_new_idx == cq_old_idx);
+
+               cqe = qed_chain_consume(&p_rx->rcq_chain);
+               cq_old_idx = qed_chain_get_cons_idx(&p_rx->rcq_chain);
+
+               DP_VERBOSE(p_hwfn,
+                          QED_MSG_LL2,
+                          "LL2 [sw. cons %04x, fw. at %04x] - Got Packet of type %02x\n",
+                          cq_old_idx, cq_new_idx, cqe->rx_cqe_sp.type);
+
+               switch (cqe->rx_cqe_sp.type) {
+               case CORE_RX_CQE_TYPE_SLOW_PATH:
+                       DP_NOTICE(p_hwfn, "LL2 - unexpected Rx CQE slowpath\n");
+                       rc = -EINVAL;
+                       break;
+               case CORE_RX_CQE_TYPE_GSI_OFFLOAD:
+                       rc = qed_ll2_rxq_completion_gsi(p_hwfn, p_ll2_conn,
+                                                       cqe, flags, b_last_cqe);
+                       break;
+               case CORE_RX_CQE_TYPE_REGULAR:
+                       rc = qed_ll2_rxq_completion_reg(p_hwfn, p_ll2_conn,
+                                                       cqe, flags, b_last_cqe);
+                       break;
+               default:
+                       rc = -EIO;
+               }
+       }
+
+       spin_unlock_irqrestore(&p_rx->lock, flags);
+       return rc;
+}
+
+void qed_ll2_rxq_flush(struct qed_hwfn *p_hwfn, u8 connection_handle)
+{
+       struct qed_ll2_info *p_ll2_conn = NULL;
+       struct qed_ll2_rx_packet *p_pkt = NULL;
+       struct qed_ll2_rx_queue *p_rx;
+
+       p_ll2_conn = qed_ll2_handle_sanity_inactive(p_hwfn, connection_handle);
+       if (!p_ll2_conn)
+               return;
+
+       p_rx = &p_ll2_conn->rx_queue;
+
+       while (!list_empty(&p_rx->active_descq)) {
+               dma_addr_t rx_buf_addr;
+               void *cookie;
+               bool b_last;
+
+               p_pkt = list_first_entry(&p_rx->active_descq,
+                                        struct qed_ll2_rx_packet, list_entry);
+               if (!p_pkt)
+                       break;
+
+               list_del(&p_pkt->list_entry);
+               list_add_tail(&p_pkt->list_entry, &p_rx->free_descq);
+
+               rx_buf_addr = p_pkt->rx_buf_addr;
+               cookie = p_pkt->cookie;
+
+               b_last = list_empty(&p_rx->active_descq);
+       }
+}
+
+static int qed_sp_ll2_rx_queue_start(struct qed_hwfn *p_hwfn,
+                                    struct qed_ll2_info *p_ll2_conn,
+                                    u8 action_on_error)
+{
+       enum qed_ll2_conn_type conn_type = p_ll2_conn->conn_type;
+       struct qed_ll2_rx_queue *p_rx = &p_ll2_conn->rx_queue;
+       struct core_rx_start_ramrod_data *p_ramrod = NULL;
+       struct qed_spq_entry *p_ent = NULL;
+       struct qed_sp_init_data init_data;
+       u16 cqe_pbl_size;
+       int rc = 0;
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = p_ll2_conn->cid;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                CORE_RAMROD_RX_QUEUE_START,
+                                PROTOCOLID_CORE, &init_data);
+       if (rc)
+               return rc;
+
+       p_ramrod = &p_ent->ramrod.core_rx_queue_start;
+
+       p_ramrod->sb_id = cpu_to_le16(qed_int_get_sp_sb_id(p_hwfn));
+       p_ramrod->sb_index = p_rx->rx_sb_index;
+       p_ramrod->complete_event_flg = 1;
+
+       p_ramrod->mtu = cpu_to_le16(p_ll2_conn->mtu);
+       DMA_REGPAIR_LE(p_ramrod->bd_base,
+                      p_rx->rxq_chain.p_phys_addr);
+       cqe_pbl_size = (u16)qed_chain_get_page_cnt(&p_rx->rcq_chain);
+       p_ramrod->num_of_pbl_pages = cpu_to_le16(cqe_pbl_size);
+       DMA_REGPAIR_LE(p_ramrod->cqe_pbl_addr,
+                      qed_chain_get_pbl_phys(&p_rx->rcq_chain));
+
+       p_ramrod->drop_ttl0_flg = p_ll2_conn->rx_drop_ttl0_flg;
+       p_ramrod->inner_vlan_removal_en = p_ll2_conn->rx_vlan_removal_en;
+       p_ramrod->queue_id = p_ll2_conn->queue_id;
+       p_ramrod->main_func_queue = 1;
+
+       if ((IS_MF_DEFAULT(p_hwfn) || IS_MF_SI(p_hwfn)) &&
+           p_ramrod->main_func_queue && (conn_type != QED_LL2_TYPE_ROCE)) {
+               p_ramrod->mf_si_bcast_accept_all = 1;
+               p_ramrod->mf_si_mcast_accept_all = 1;
+       } else {
+               p_ramrod->mf_si_bcast_accept_all = 0;
+               p_ramrod->mf_si_mcast_accept_all = 0;
+       }
+
+       p_ramrod->action_on_error.error_type = action_on_error;
+       p_ramrod->gsi_offload_flag = p_ll2_conn->gsi_enable;
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
+
+static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn,
+                                    struct qed_ll2_info *p_ll2_conn)
+{
+       enum qed_ll2_conn_type conn_type = p_ll2_conn->conn_type;
+       struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
+       struct core_tx_start_ramrod_data *p_ramrod = NULL;
+       struct qed_spq_entry *p_ent = NULL;
+       struct qed_sp_init_data init_data;
+       union qed_qm_pq_params pq_params;
+       u16 pq_id = 0, pbl_size;
+       int rc = -EINVAL;
+
+       if (!QED_LL2_TX_REGISTERED(p_ll2_conn))
+               return 0;
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = p_ll2_conn->cid;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                CORE_RAMROD_TX_QUEUE_START,
+                                PROTOCOLID_CORE, &init_data);
+       if (rc)
+               return rc;
+
+       p_ramrod = &p_ent->ramrod.core_tx_queue_start;
+
+       p_ramrod->sb_id = cpu_to_le16(qed_int_get_sp_sb_id(p_hwfn));
+       p_ramrod->sb_index = p_tx->tx_sb_index;
+       p_ramrod->mtu = cpu_to_le16(p_ll2_conn->mtu);
+       p_ll2_conn->tx_stats_en = 1;
+       p_ramrod->stats_en = p_ll2_conn->tx_stats_en;
+       p_ramrod->stats_id = p_ll2_conn->tx_stats_id;
+
+       DMA_REGPAIR_LE(p_ramrod->pbl_base_addr,
+                      qed_chain_get_pbl_phys(&p_tx->txq_chain));
+       pbl_size = qed_chain_get_page_cnt(&p_tx->txq_chain);
+       p_ramrod->pbl_size = cpu_to_le16(pbl_size);
+
+       memset(&pq_params, 0, sizeof(pq_params));
+       pq_params.core.tc = p_ll2_conn->tx_tc;
+       pq_id = qed_get_qm_pq(p_hwfn, PROTOCOLID_CORE, &pq_params);
+       p_ramrod->qm_pq_id = cpu_to_le16(pq_id);
+
+       switch (conn_type) {
+       case QED_LL2_TYPE_ISCSI:
+       case QED_LL2_TYPE_ISCSI_OOO:
+               p_ramrod->conn_type = PROTOCOLID_ISCSI;
+               break;
+       case QED_LL2_TYPE_ROCE:
+               p_ramrod->conn_type = PROTOCOLID_ROCE;
+               break;
+       default:
+               p_ramrod->conn_type = PROTOCOLID_ETH;
+               DP_NOTICE(p_hwfn, "Unknown connection type: %d\n", conn_type);
+       }
+
+       p_ramrod->gsi_offload_flag = p_ll2_conn->gsi_enable;
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
+
+static int qed_sp_ll2_rx_queue_stop(struct qed_hwfn *p_hwfn,
+                                   struct qed_ll2_info *p_ll2_conn)
+{
+       struct core_rx_stop_ramrod_data *p_ramrod = NULL;
+       struct qed_spq_entry *p_ent = NULL;
+       struct qed_sp_init_data init_data;
+       int rc = -EINVAL;
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = p_ll2_conn->cid;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                CORE_RAMROD_RX_QUEUE_STOP,
+                                PROTOCOLID_CORE, &init_data);
+       if (rc)
+               return rc;
+
+       p_ramrod = &p_ent->ramrod.core_rx_queue_stop;
+
+       p_ramrod->complete_event_flg = 1;
+       p_ramrod->queue_id = p_ll2_conn->queue_id;
+
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
+
+static int qed_sp_ll2_tx_queue_stop(struct qed_hwfn *p_hwfn,
+                                   struct qed_ll2_info *p_ll2_conn)
+{
+       struct qed_spq_entry *p_ent = NULL;
+       struct qed_sp_init_data init_data;
+       int rc = -EINVAL;
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = p_ll2_conn->cid;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                CORE_RAMROD_TX_QUEUE_STOP,
+                                PROTOCOLID_CORE, &init_data);
+       if (rc)
+               return rc;
+
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
+
+static int
+qed_ll2_acquire_connection_rx(struct qed_hwfn *p_hwfn,
+                             struct qed_ll2_info *p_ll2_info, u16 rx_num_desc)
+{
+       struct qed_ll2_rx_packet *p_descq;
+       u32 capacity;
+       int rc = 0;
+
+       if (!rx_num_desc)
+               goto out;
+
+       rc = qed_chain_alloc(p_hwfn->cdev,
+                            QED_CHAIN_USE_TO_CONSUME_PRODUCE,
+                            QED_CHAIN_MODE_NEXT_PTR,
+                            QED_CHAIN_CNT_TYPE_U16,
+                            rx_num_desc,
+                            sizeof(struct core_rx_bd),
+                            &p_ll2_info->rx_queue.rxq_chain);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "Failed to allocate ll2 rxq chain\n");
+               goto out;
+       }
+
+       capacity = qed_chain_get_capacity(&p_ll2_info->rx_queue.rxq_chain);
+       p_descq = kcalloc(capacity, sizeof(struct qed_ll2_rx_packet),
+                         GFP_KERNEL);
+       if (!p_descq) {
+               rc = -ENOMEM;
+               DP_NOTICE(p_hwfn, "Failed to allocate ll2 Rx desc\n");
+               goto out;
+       }
+       p_ll2_info->rx_queue.descq_array = p_descq;
+
+       rc = qed_chain_alloc(p_hwfn->cdev,
+                            QED_CHAIN_USE_TO_CONSUME_PRODUCE,
+                            QED_CHAIN_MODE_PBL,
+                            QED_CHAIN_CNT_TYPE_U16,
+                            rx_num_desc,
+                            sizeof(struct core_rx_fast_path_cqe),
+                            &p_ll2_info->rx_queue.rcq_chain);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "Failed to allocate ll2 rcq chain\n");
+               goto out;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_LL2,
+                  "Allocated LL2 Rxq [Type %08x] with 0x%08x buffers\n",
+                  p_ll2_info->conn_type, rx_num_desc);
+
+out:
+       return rc;
+}
+
+static int qed_ll2_acquire_connection_tx(struct qed_hwfn *p_hwfn,
+                                        struct qed_ll2_info *p_ll2_info,
+                                        u16 tx_num_desc)
+{
+       struct qed_ll2_tx_packet *p_descq;
+       u32 capacity;
+       int rc = 0;
+
+       if (!tx_num_desc)
+               goto out;
+
+       rc = qed_chain_alloc(p_hwfn->cdev,
+                            QED_CHAIN_USE_TO_CONSUME_PRODUCE,
+                            QED_CHAIN_MODE_PBL,
+                            QED_CHAIN_CNT_TYPE_U16,
+                            tx_num_desc,
+                            sizeof(struct core_tx_bd),
+                            &p_ll2_info->tx_queue.txq_chain);
+       if (rc)
+               goto out;
+
+       capacity = qed_chain_get_capacity(&p_ll2_info->tx_queue.txq_chain);
+       p_descq = kcalloc(capacity, sizeof(struct qed_ll2_tx_packet),
+                         GFP_KERNEL);
+       if (!p_descq) {
+               rc = -ENOMEM;
+               goto out;
+       }
+       p_ll2_info->tx_queue.descq_array = p_descq;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_LL2,
+                  "Allocated LL2 Txq [Type %08x] with 0x%08x buffers\n",
+                  p_ll2_info->conn_type, tx_num_desc);
+
+out:
+       if (rc)
+               DP_NOTICE(p_hwfn,
+                         "Can't allocate memory for Tx LL2 with 0x%08x buffers\n",
+                         tx_num_desc);
+       return rc;
+}
+
+int qed_ll2_acquire_connection(struct qed_hwfn *p_hwfn,
+                              struct qed_ll2_info *p_params,
+                              u16 rx_num_desc,
+                              u16 tx_num_desc,
+                              u8 *p_connection_handle)
+{
+       qed_int_comp_cb_t comp_rx_cb, comp_tx_cb;
+       struct qed_ll2_info *p_ll2_info = NULL;
+       int rc;
+       u8 i;
+
+       if (!p_connection_handle || !p_hwfn->p_ll2_info)
+               return -EINVAL;
+
+       /* Find a free connection to be used */
+       for (i = 0; (i < QED_MAX_NUM_OF_LL2_CONNECTIONS); i++) {
+               mutex_lock(&p_hwfn->p_ll2_info[i].mutex);
+               if (p_hwfn->p_ll2_info[i].b_active) {
+                       mutex_unlock(&p_hwfn->p_ll2_info[i].mutex);
+                       continue;
+               }
+
+               p_hwfn->p_ll2_info[i].b_active = true;
+               p_ll2_info = &p_hwfn->p_ll2_info[i];
+               mutex_unlock(&p_hwfn->p_ll2_info[i].mutex);
+               break;
+       }
+       if (!p_ll2_info)
+               return -EBUSY;
+
+       p_ll2_info->conn_type = p_params->conn_type;
+       p_ll2_info->mtu = p_params->mtu;
+       p_ll2_info->rx_drop_ttl0_flg = p_params->rx_drop_ttl0_flg;
+       p_ll2_info->rx_vlan_removal_en = p_params->rx_vlan_removal_en;
+       p_ll2_info->tx_tc = p_params->tx_tc;
+       p_ll2_info->tx_dest = p_params->tx_dest;
+       p_ll2_info->ai_err_packet_too_big = p_params->ai_err_packet_too_big;
+       p_ll2_info->ai_err_no_buf = p_params->ai_err_no_buf;
+       p_ll2_info->gsi_enable = p_params->gsi_enable;
+
+       rc = qed_ll2_acquire_connection_rx(p_hwfn, p_ll2_info, rx_num_desc);
+       if (rc)
+               goto q_allocate_fail;
+
+       rc = qed_ll2_acquire_connection_tx(p_hwfn, p_ll2_info, tx_num_desc);
+       if (rc)
+               goto q_allocate_fail;
+
+       /* Register callbacks for the Rx/Tx queues */
+       comp_rx_cb = qed_ll2_rxq_completion;
+       comp_tx_cb = qed_ll2_txq_completion;
+
+       if (rx_num_desc) {
+               qed_int_register_cb(p_hwfn, comp_rx_cb,
+                                   &p_hwfn->p_ll2_info[i],
+                                   &p_ll2_info->rx_queue.rx_sb_index,
+                                   &p_ll2_info->rx_queue.p_fw_cons);
+               p_ll2_info->rx_queue.b_cb_registred = true;
+       }
+
+       if (tx_num_desc) {
+               qed_int_register_cb(p_hwfn,
+                                   comp_tx_cb,
+                                   &p_hwfn->p_ll2_info[i],
+                                   &p_ll2_info->tx_queue.tx_sb_index,
+                                   &p_ll2_info->tx_queue.p_fw_cons);
+               p_ll2_info->tx_queue.b_cb_registred = true;
+       }
+
+       *p_connection_handle = i;
+       return rc;
+
+q_allocate_fail:
+       qed_ll2_release_connection(p_hwfn, i);
+       return -ENOMEM;
+}
+
+static int qed_ll2_establish_connection_rx(struct qed_hwfn *p_hwfn,
+                                          struct qed_ll2_info *p_ll2_conn)
+{
+       u8 action_on_error = 0;
+
+       if (!QED_LL2_RX_REGISTERED(p_ll2_conn))
+               return 0;
+
+       DIRECT_REG_WR(p_ll2_conn->rx_queue.set_prod_addr, 0x0);
+
+       SET_FIELD(action_on_error,
+                 CORE_RX_ACTION_ON_ERROR_PACKET_TOO_BIG,
+                 p_ll2_conn->ai_err_packet_too_big);
+       SET_FIELD(action_on_error,
+                 CORE_RX_ACTION_ON_ERROR_NO_BUFF, p_ll2_conn->ai_err_no_buf);
+
+       return qed_sp_ll2_rx_queue_start(p_hwfn, p_ll2_conn, action_on_error);
+}
+
+int qed_ll2_establish_connection(struct qed_hwfn *p_hwfn, u8 connection_handle)
+{
+       struct qed_ll2_info *p_ll2_conn;
+       struct qed_ll2_rx_queue *p_rx;
+       struct qed_ll2_tx_queue *p_tx;
+       int rc = -EINVAL;
+       u32 i, capacity;
+       u8 qid;
+
+       p_ll2_conn = qed_ll2_handle_sanity_lock(p_hwfn, connection_handle);
+       if (!p_ll2_conn)
+               return -EINVAL;
+       p_rx = &p_ll2_conn->rx_queue;
+       p_tx = &p_ll2_conn->tx_queue;
+
+       qed_chain_reset(&p_rx->rxq_chain);
+       qed_chain_reset(&p_rx->rcq_chain);
+       INIT_LIST_HEAD(&p_rx->active_descq);
+       INIT_LIST_HEAD(&p_rx->free_descq);
+       INIT_LIST_HEAD(&p_rx->posting_descq);
+       spin_lock_init(&p_rx->lock);
+       capacity = qed_chain_get_capacity(&p_rx->rxq_chain);
+       for (i = 0; i < capacity; i++)
+               list_add_tail(&p_rx->descq_array[i].list_entry,
+                             &p_rx->free_descq);
+       *p_rx->p_fw_cons = 0;
+
+       qed_chain_reset(&p_tx->txq_chain);
+       INIT_LIST_HEAD(&p_tx->active_descq);
+       INIT_LIST_HEAD(&p_tx->free_descq);
+       INIT_LIST_HEAD(&p_tx->sending_descq);
+       spin_lock_init(&p_tx->lock);
+       capacity = qed_chain_get_capacity(&p_tx->txq_chain);
+       for (i = 0; i < capacity; i++)
+               list_add_tail(&p_tx->descq_array[i].list_entry,
+                             &p_tx->free_descq);
+       p_tx->cur_completing_bd_idx = 0;
+       p_tx->bds_idx = 0;
+       p_tx->b_completing_packet = false;
+       p_tx->cur_send_packet = NULL;
+       p_tx->cur_send_frag_num = 0;
+       p_tx->cur_completing_frag_num = 0;
+       *p_tx->p_fw_cons = 0;
+
+       qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_CORE, &p_ll2_conn->cid);
+
+       qid = p_hwfn->hw_info.resc_start[QED_LL2_QUEUE] + connection_handle;
+       p_ll2_conn->queue_id = qid;
+       p_ll2_conn->tx_stats_id = qid;
+       p_rx->set_prod_addr = (u8 __iomem *)p_hwfn->regview +
+                                           GTT_BAR0_MAP_REG_TSDM_RAM +
+                                           TSTORM_LL2_RX_PRODS_OFFSET(qid);
+       p_tx->doorbell_addr = (u8 __iomem *)p_hwfn->doorbells +
+                                           qed_db_addr(p_ll2_conn->cid,
+                                                       DQ_DEMS_LEGACY);
+
+       rc = qed_ll2_establish_connection_rx(p_hwfn, p_ll2_conn);
+       if (rc)
+               return rc;
+
+       rc = qed_sp_ll2_tx_queue_start(p_hwfn, p_ll2_conn);
+       if (rc)
+               return rc;
+
+       if (p_hwfn->hw_info.personality != QED_PCI_ETH_ROCE)
+               qed_wr(p_hwfn, p_hwfn->p_main_ptt, PRS_REG_USE_LIGHT_L2, 1);
+
+       return rc;
+}
+
+static void qed_ll2_post_rx_buffer_notify_fw(struct qed_hwfn *p_hwfn,
+                                            struct qed_ll2_rx_queue *p_rx,
+                                            struct qed_ll2_rx_packet *p_curp)
+{
+       struct qed_ll2_rx_packet *p_posting_packet = NULL;
+       struct core_ll2_rx_prod rx_prod = { 0, 0, 0 };
+       bool b_notify_fw = false;
+       u16 bd_prod, cq_prod;
+
+       /* This handles the flushing of already posted buffers */
+       while (!list_empty(&p_rx->posting_descq)) {
+               p_posting_packet = list_first_entry(&p_rx->posting_descq,
+                                                   struct qed_ll2_rx_packet,
+                                                   list_entry);
+               list_del(&p_posting_packet->list_entry);
+               list_add_tail(&p_posting_packet->list_entry,
+                             &p_rx->active_descq);
+               b_notify_fw = true;
+       }
+
+       /* This handles the supplied packet [if there is one] */
+       if (p_curp) {
+               list_add_tail(&p_curp->list_entry, &p_rx->active_descq);
+               b_notify_fw = true;
+       }
+
+       if (!b_notify_fw)
+               return;
+
+       bd_prod = qed_chain_get_prod_idx(&p_rx->rxq_chain);
+       cq_prod = qed_chain_get_prod_idx(&p_rx->rcq_chain);
+       rx_prod.bd_prod = cpu_to_le16(bd_prod);
+       rx_prod.cqe_prod = cpu_to_le16(cq_prod);
+       DIRECT_REG_WR(p_rx->set_prod_addr, *((u32 *)&rx_prod));
+}
+
+int qed_ll2_post_rx_buffer(struct qed_hwfn *p_hwfn,
+                          u8 connection_handle,
+                          dma_addr_t addr,
+                          u16 buf_len, void *cookie, u8 notify_fw)
+{
+       struct core_rx_bd_with_buff_len *p_curb = NULL;
+       struct qed_ll2_rx_packet *p_curp = NULL;
+       struct qed_ll2_info *p_ll2_conn;
+       struct qed_ll2_rx_queue *p_rx;
+       unsigned long flags;
+       void *p_data;
+       int rc = 0;
+
+       p_ll2_conn = qed_ll2_handle_sanity(p_hwfn, connection_handle);
+       if (!p_ll2_conn)
+               return -EINVAL;
+       p_rx = &p_ll2_conn->rx_queue;
+
+       spin_lock_irqsave(&p_rx->lock, flags);
+       if (!list_empty(&p_rx->free_descq))
+               p_curp = list_first_entry(&p_rx->free_descq,
+                                         struct qed_ll2_rx_packet, list_entry);
+       if (p_curp) {
+               if (qed_chain_get_elem_left(&p_rx->rxq_chain) &&
+                   qed_chain_get_elem_left(&p_rx->rcq_chain)) {
+                       p_data = qed_chain_produce(&p_rx->rxq_chain);
+                       p_curb = (struct core_rx_bd_with_buff_len *)p_data;
+                       qed_chain_produce(&p_rx->rcq_chain);
+               }
+       }
+
+       /* If we're lacking entires, let's try to flush buffers to FW */
+       if (!p_curp || !p_curb) {
+               rc = -EBUSY;
+               p_curp = NULL;
+               goto out_notify;
+       }
+
+       /* We have an Rx packet we can fill */
+       DMA_REGPAIR_LE(p_curb->addr, addr);
+       p_curb->buff_length = cpu_to_le16(buf_len);
+       p_curp->rx_buf_addr = addr;
+       p_curp->cookie = cookie;
+       p_curp->rxq_bd = p_curb;
+       p_curp->buf_length = buf_len;
+       list_del(&p_curp->list_entry);
+
+       /* Check if we only want to enqueue this packet without informing FW */
+       if (!notify_fw) {
+               list_add_tail(&p_curp->list_entry, &p_rx->posting_descq);
+               goto out;
+       }
+
+out_notify:
+       qed_ll2_post_rx_buffer_notify_fw(p_hwfn, p_rx, p_curp);
+out:
+       spin_unlock_irqrestore(&p_rx->lock, flags);
+       return rc;
+}
+
+static void qed_ll2_prepare_tx_packet_set(struct qed_hwfn *p_hwfn,
+                                         struct qed_ll2_tx_queue *p_tx,
+                                         struct qed_ll2_tx_packet *p_curp,
+                                         u8 num_of_bds,
+                                         dma_addr_t first_frag,
+                                         u16 first_frag_len, void *p_cookie,
+                                         u8 notify_fw)
+{
+       list_del(&p_curp->list_entry);
+       p_curp->cookie = p_cookie;
+       p_curp->bd_used = num_of_bds;
+       p_curp->notify_fw = notify_fw;
+       p_tx->cur_send_packet = p_curp;
+       p_tx->cur_send_frag_num = 0;
+
+       p_curp->bds_set[p_tx->cur_send_frag_num].tx_frag = first_frag;
+       p_curp->bds_set[p_tx->cur_send_frag_num].frag_len = first_frag_len;
+       p_tx->cur_send_frag_num++;
+}
+
+static void qed_ll2_prepare_tx_packet_set_bd(struct qed_hwfn *p_hwfn,
+                                            struct qed_ll2_info *p_ll2,
+                                            struct qed_ll2_tx_packet *p_curp,
+                                            u8 num_of_bds,
+                                            enum core_tx_dest tx_dest,
+                                            u16 vlan,
+                                            u8 bd_flags,
+                                            u16 l4_hdr_offset_w,
+                                            enum core_roce_flavor_type type,
+                                            dma_addr_t first_frag,
+                                            u16 first_frag_len)
+{
+       struct qed_chain *p_tx_chain = &p_ll2->tx_queue.txq_chain;
+       u16 prod_idx = qed_chain_get_prod_idx(p_tx_chain);
+       struct core_tx_bd *start_bd = NULL;
+       u16 frag_idx;
+
+       start_bd = (struct core_tx_bd *)qed_chain_produce(p_tx_chain);
+       start_bd->nw_vlan_or_lb_echo = cpu_to_le16(vlan);
+       SET_FIELD(start_bd->bitfield1, CORE_TX_BD_L4_HDR_OFFSET_W,
+                 cpu_to_le16(l4_hdr_offset_w));
+       SET_FIELD(start_bd->bitfield1, CORE_TX_BD_TX_DST, tx_dest);
+       start_bd->bd_flags.as_bitfield = bd_flags;
+       start_bd->bd_flags.as_bitfield |= CORE_TX_BD_FLAGS_START_BD_MASK <<
+           CORE_TX_BD_FLAGS_START_BD_SHIFT;
+       SET_FIELD(start_bd->bitfield0, CORE_TX_BD_NBDS, num_of_bds);
+       DMA_REGPAIR_LE(start_bd->addr, first_frag);
+       start_bd->nbytes = cpu_to_le16(first_frag_len);
+
+       SET_FIELD(start_bd->bd_flags.as_bitfield, CORE_TX_BD_FLAGS_ROCE_FLAV,
+                 type);
+
+       DP_VERBOSE(p_hwfn,
+                  (NETIF_MSG_TX_QUEUED | QED_MSG_LL2),
+                  "LL2 [q 0x%02x cid 0x%08x type 0x%08x] Tx Producer at [0x%04x] - set with a %04x bytes %02x BDs buffer at %08x:%08x\n",
+                  p_ll2->queue_id,
+                  p_ll2->cid,
+                  p_ll2->conn_type,
+                  prod_idx,
+                  first_frag_len,
+                  num_of_bds,
+                  le32_to_cpu(start_bd->addr.hi),
+                  le32_to_cpu(start_bd->addr.lo));
+
+       if (p_ll2->tx_queue.cur_send_frag_num == num_of_bds)
+               return;
+
+       /* Need to provide the packet with additional BDs for frags */
+       for (frag_idx = p_ll2->tx_queue.cur_send_frag_num;
+            frag_idx < num_of_bds; frag_idx++) {
+               struct core_tx_bd **p_bd = &p_curp->bds_set[frag_idx].txq_bd;
+
+               *p_bd = (struct core_tx_bd *)qed_chain_produce(p_tx_chain);
+               (*p_bd)->bd_flags.as_bitfield = 0;
+               (*p_bd)->bitfield1 = 0;
+               (*p_bd)->bitfield0 = 0;
+               p_curp->bds_set[frag_idx].tx_frag = 0;
+               p_curp->bds_set[frag_idx].frag_len = 0;
+       }
+}
+
+/* This should be called while the Txq spinlock is being held */
+static void qed_ll2_tx_packet_notify(struct qed_hwfn *p_hwfn,
+                                    struct qed_ll2_info *p_ll2_conn)
+{
+       bool b_notify = p_ll2_conn->tx_queue.cur_send_packet->notify_fw;
+       struct qed_ll2_tx_queue *p_tx = &p_ll2_conn->tx_queue;
+       struct qed_ll2_tx_packet *p_pkt = NULL;
+       struct core_db_data db_msg = { 0, 0, 0 };
+       u16 bd_prod;
+
+       /* If there are missing BDs, don't do anything now */
+       if (p_ll2_conn->tx_queue.cur_send_frag_num !=
+           p_ll2_conn->tx_queue.cur_send_packet->bd_used)
+               return;
+
+       /* Push the current packet to the list and clean after it */
+       list_add_tail(&p_ll2_conn->tx_queue.cur_send_packet->list_entry,
+                     &p_ll2_conn->tx_queue.sending_descq);
+       p_ll2_conn->tx_queue.cur_send_packet = NULL;
+       p_ll2_conn->tx_queue.cur_send_frag_num = 0;
+
+       /* Notify FW of packet only if requested to */
+       if (!b_notify)
+               return;
+
+       bd_prod = qed_chain_get_prod_idx(&p_ll2_conn->tx_queue.txq_chain);
+
+       while (!list_empty(&p_tx->sending_descq)) {
+               p_pkt = list_first_entry(&p_tx->sending_descq,
+                                        struct qed_ll2_tx_packet, list_entry);
+               if (!p_pkt)
+                       break;
+
+               list_del(&p_pkt->list_entry);
+               list_add_tail(&p_pkt->list_entry, &p_tx->active_descq);
+       }
+
+       SET_FIELD(db_msg.params, CORE_DB_DATA_DEST, DB_DEST_XCM);
+       SET_FIELD(db_msg.params, CORE_DB_DATA_AGG_CMD, DB_AGG_CMD_SET);
+       SET_FIELD(db_msg.params, CORE_DB_DATA_AGG_VAL_SEL,
+                 DQ_XCM_CORE_TX_BD_PROD_CMD);
+       db_msg.agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
+       db_msg.spq_prod = cpu_to_le16(bd_prod);
+
+       /* Make sure the BDs data is updated before ringing the doorbell */
+       wmb();
+
+       DIRECT_REG_WR(p_tx->doorbell_addr, *((u32 *)&db_msg));
+
+       DP_VERBOSE(p_hwfn,
+                  (NETIF_MSG_TX_QUEUED | QED_MSG_LL2),
+                  "LL2 [q 0x%02x cid 0x%08x type 0x%08x] Doorbelled [producer 0x%04x]\n",
+                  p_ll2_conn->queue_id,
+                  p_ll2_conn->cid, p_ll2_conn->conn_type, db_msg.spq_prod);
+}
+
+int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn,
+                             u8 connection_handle,
+                             u8 num_of_bds,
+                             u16 vlan,
+                             u8 bd_flags,
+                             u16 l4_hdr_offset_w,
+                             enum qed_ll2_roce_flavor_type qed_roce_flavor,
+                             dma_addr_t first_frag,
+                             u16 first_frag_len, void *cookie, u8 notify_fw)
+{
+       struct qed_ll2_tx_packet *p_curp = NULL;
+       struct qed_ll2_info *p_ll2_conn = NULL;
+       enum core_roce_flavor_type roce_flavor;
+       struct qed_ll2_tx_queue *p_tx;
+       struct qed_chain *p_tx_chain;
+       unsigned long flags;
+       int rc = 0;
+
+       p_ll2_conn = qed_ll2_handle_sanity(p_hwfn, connection_handle);
+       if (!p_ll2_conn)
+               return -EINVAL;
+       p_tx = &p_ll2_conn->tx_queue;
+       p_tx_chain = &p_tx->txq_chain;
+
+       if (num_of_bds > CORE_LL2_TX_MAX_BDS_PER_PACKET)
+               return -EIO;
+
+       spin_lock_irqsave(&p_tx->lock, flags);
+       if (p_tx->cur_send_packet) {
+               rc = -EEXIST;
+               goto out;
+       }
+
+       /* Get entry, but only if we have tx elements for it */
+       if (!list_empty(&p_tx->free_descq))
+               p_curp = list_first_entry(&p_tx->free_descq,
+                                         struct qed_ll2_tx_packet, list_entry);
+       if (p_curp && qed_chain_get_elem_left(p_tx_chain) < num_of_bds)
+               p_curp = NULL;
+
+       if (!p_curp) {
+               rc = -EBUSY;
+               goto out;
+       }
+
+       if (qed_roce_flavor == QED_LL2_ROCE) {
+               roce_flavor = CORE_ROCE;
+       } else if (qed_roce_flavor == QED_LL2_RROCE) {
+               roce_flavor = CORE_RROCE;
+       } else {
+               rc = -EINVAL;
+               goto out;
+       }
+
+       /* Prepare packet and BD, and perhaps send a doorbell to FW */
+       qed_ll2_prepare_tx_packet_set(p_hwfn, p_tx, p_curp,
+                                     num_of_bds, first_frag,
+                                     first_frag_len, cookie, notify_fw);
+       qed_ll2_prepare_tx_packet_set_bd(p_hwfn, p_ll2_conn, p_curp,
+                                        num_of_bds, CORE_TX_DEST_NW,
+                                        vlan, bd_flags, l4_hdr_offset_w,
+                                        roce_flavor,
+                                        first_frag, first_frag_len);
+
+       qed_ll2_tx_packet_notify(p_hwfn, p_ll2_conn);
+
+out:
+       spin_unlock_irqrestore(&p_tx->lock, flags);
+       return rc;
+}
+
+int qed_ll2_set_fragment_of_tx_packet(struct qed_hwfn *p_hwfn,
+                                     u8 connection_handle,
+                                     dma_addr_t addr, u16 nbytes)
+{
+       struct qed_ll2_tx_packet *p_cur_send_packet = NULL;
+       struct qed_ll2_info *p_ll2_conn = NULL;
+       u16 cur_send_frag_num = 0;
+       struct core_tx_bd *p_bd;
+       unsigned long flags;
+
+       p_ll2_conn = qed_ll2_handle_sanity(p_hwfn, connection_handle);
+       if (!p_ll2_conn)
+               return -EINVAL;
+
+       if (!p_ll2_conn->tx_queue.cur_send_packet)
+               return -EINVAL;
+
+       p_cur_send_packet = p_ll2_conn->tx_queue.cur_send_packet;
+       cur_send_frag_num = p_ll2_conn->tx_queue.cur_send_frag_num;
+
+       if (cur_send_frag_num >= p_cur_send_packet->bd_used)
+               return -EINVAL;
+
+       /* Fill the BD information, and possibly notify FW */
+       p_bd = p_cur_send_packet->bds_set[cur_send_frag_num].txq_bd;
+       DMA_REGPAIR_LE(p_bd->addr, addr);
+       p_bd->nbytes = cpu_to_le16(nbytes);
+       p_cur_send_packet->bds_set[cur_send_frag_num].tx_frag = addr;
+       p_cur_send_packet->bds_set[cur_send_frag_num].frag_len = nbytes;
+
+       p_ll2_conn->tx_queue.cur_send_frag_num++;
+
+       spin_lock_irqsave(&p_ll2_conn->tx_queue.lock, flags);
+       qed_ll2_tx_packet_notify(p_hwfn, p_ll2_conn);
+       spin_unlock_irqrestore(&p_ll2_conn->tx_queue.lock, flags);
+
+       return 0;
+}
+
+int qed_ll2_terminate_connection(struct qed_hwfn *p_hwfn, u8 connection_handle)
+{
+       struct qed_ll2_info *p_ll2_conn = NULL;
+       int rc = -EINVAL;
+
+       p_ll2_conn = qed_ll2_handle_sanity_lock(p_hwfn, connection_handle);
+       if (!p_ll2_conn)
+               return -EINVAL;
+
+       /* Stop Tx & Rx of connection, if needed */
+       if (QED_LL2_TX_REGISTERED(p_ll2_conn)) {
+               rc = qed_sp_ll2_tx_queue_stop(p_hwfn, p_ll2_conn);
+               if (rc)
+                       return rc;
+               qed_ll2_txq_flush(p_hwfn, connection_handle);
+       }
+
+       if (QED_LL2_RX_REGISTERED(p_ll2_conn)) {
+               rc = qed_sp_ll2_rx_queue_stop(p_hwfn, p_ll2_conn);
+               if (rc)
+                       return rc;
+               qed_ll2_rxq_flush(p_hwfn, connection_handle);
+       }
+
+       return rc;
+}
+
+void qed_ll2_release_connection(struct qed_hwfn *p_hwfn, u8 connection_handle)
+{
+       struct qed_ll2_info *p_ll2_conn = NULL;
+
+       p_ll2_conn = qed_ll2_handle_sanity(p_hwfn, connection_handle);
+       if (!p_ll2_conn)
+               return;
+
+       if (QED_LL2_RX_REGISTERED(p_ll2_conn)) {
+               p_ll2_conn->rx_queue.b_cb_registred = false;
+               qed_int_unregister_cb(p_hwfn, p_ll2_conn->rx_queue.rx_sb_index);
+       }
+
+       if (QED_LL2_TX_REGISTERED(p_ll2_conn)) {
+               p_ll2_conn->tx_queue.b_cb_registred = false;
+               qed_int_unregister_cb(p_hwfn, p_ll2_conn->tx_queue.tx_sb_index);
+       }
+
+       kfree(p_ll2_conn->tx_queue.descq_array);
+       qed_chain_free(p_hwfn->cdev, &p_ll2_conn->tx_queue.txq_chain);
+
+       kfree(p_ll2_conn->rx_queue.descq_array);
+       qed_chain_free(p_hwfn->cdev, &p_ll2_conn->rx_queue.rxq_chain);
+       qed_chain_free(p_hwfn->cdev, &p_ll2_conn->rx_queue.rcq_chain);
+
+       qed_cxt_release_cid(p_hwfn, p_ll2_conn->cid);
+
+       mutex_lock(&p_ll2_conn->mutex);
+       p_ll2_conn->b_active = false;
+       mutex_unlock(&p_ll2_conn->mutex);
+}
+
+struct qed_ll2_info *qed_ll2_alloc(struct qed_hwfn *p_hwfn)
+{
+       struct qed_ll2_info *p_ll2_connections;
+       u8 i;
+
+       /* Allocate LL2's set struct */
+       p_ll2_connections = kcalloc(QED_MAX_NUM_OF_LL2_CONNECTIONS,
+                                   sizeof(struct qed_ll2_info), GFP_KERNEL);
+       if (!p_ll2_connections) {
+               DP_NOTICE(p_hwfn, "Failed to allocate `struct qed_ll2'\n");
+               return NULL;
+       }
+
+       for (i = 0; i < QED_MAX_NUM_OF_LL2_CONNECTIONS; i++)
+               p_ll2_connections[i].my_id = i;
+
+       return p_ll2_connections;
+}
+
+void qed_ll2_setup(struct qed_hwfn *p_hwfn,
+                  struct qed_ll2_info *p_ll2_connections)
+{
+       int i;
+
+       for (i = 0; i < QED_MAX_NUM_OF_LL2_CONNECTIONS; i++)
+               mutex_init(&p_ll2_connections[i].mutex);
+}
+
+void qed_ll2_free(struct qed_hwfn *p_hwfn,
+                 struct qed_ll2_info *p_ll2_connections)
+{
+       kfree(p_ll2_connections);
+}
+
+static void _qed_ll2_get_tstats(struct qed_hwfn *p_hwfn,
+                               struct qed_ptt *p_ptt,
+                               struct qed_ll2_info *p_ll2_conn,
+                               struct qed_ll2_stats *p_stats)
+{
+       struct core_ll2_tstorm_per_queue_stat tstats;
+       u8 qid = p_ll2_conn->queue_id;
+       u32 tstats_addr;
+
+       memset(&tstats, 0, sizeof(tstats));
+       tstats_addr = BAR0_MAP_REG_TSDM_RAM +
+                     CORE_LL2_TSTORM_PER_QUEUE_STAT_OFFSET(qid);
+       qed_memcpy_from(p_hwfn, p_ptt, &tstats, tstats_addr, sizeof(tstats));
+
+       p_stats->packet_too_big_discard =
+                       HILO_64_REGPAIR(tstats.packet_too_big_discard);
+       p_stats->no_buff_discard = HILO_64_REGPAIR(tstats.no_buff_discard);
+}
+
+static void _qed_ll2_get_ustats(struct qed_hwfn *p_hwfn,
+                               struct qed_ptt *p_ptt,
+                               struct qed_ll2_info *p_ll2_conn,
+                               struct qed_ll2_stats *p_stats)
+{
+       struct core_ll2_ustorm_per_queue_stat ustats;
+       u8 qid = p_ll2_conn->queue_id;
+       u32 ustats_addr;
+
+       memset(&ustats, 0, sizeof(ustats));
+       ustats_addr = BAR0_MAP_REG_USDM_RAM +
+                     CORE_LL2_USTORM_PER_QUEUE_STAT_OFFSET(qid);
+       qed_memcpy_from(p_hwfn, p_ptt, &ustats, ustats_addr, sizeof(ustats));
+
+       p_stats->rcv_ucast_bytes = HILO_64_REGPAIR(ustats.rcv_ucast_bytes);
+       p_stats->rcv_mcast_bytes = HILO_64_REGPAIR(ustats.rcv_mcast_bytes);
+       p_stats->rcv_bcast_bytes = HILO_64_REGPAIR(ustats.rcv_bcast_bytes);
+       p_stats->rcv_ucast_pkts = HILO_64_REGPAIR(ustats.rcv_ucast_pkts);
+       p_stats->rcv_mcast_pkts = HILO_64_REGPAIR(ustats.rcv_mcast_pkts);
+       p_stats->rcv_bcast_pkts = HILO_64_REGPAIR(ustats.rcv_bcast_pkts);
+}
+
+static void _qed_ll2_get_pstats(struct qed_hwfn *p_hwfn,
+                               struct qed_ptt *p_ptt,
+                               struct qed_ll2_info *p_ll2_conn,
+                               struct qed_ll2_stats *p_stats)
+{
+       struct core_ll2_pstorm_per_queue_stat pstats;
+       u8 stats_id = p_ll2_conn->tx_stats_id;
+       u32 pstats_addr;
+
+       memset(&pstats, 0, sizeof(pstats));
+       pstats_addr = BAR0_MAP_REG_PSDM_RAM +
+                     CORE_LL2_PSTORM_PER_QUEUE_STAT_OFFSET(stats_id);
+       qed_memcpy_from(p_hwfn, p_ptt, &pstats, pstats_addr, sizeof(pstats));
+
+       p_stats->sent_ucast_bytes = HILO_64_REGPAIR(pstats.sent_ucast_bytes);
+       p_stats->sent_mcast_bytes = HILO_64_REGPAIR(pstats.sent_mcast_bytes);
+       p_stats->sent_bcast_bytes = HILO_64_REGPAIR(pstats.sent_bcast_bytes);
+       p_stats->sent_ucast_pkts = HILO_64_REGPAIR(pstats.sent_ucast_pkts);
+       p_stats->sent_mcast_pkts = HILO_64_REGPAIR(pstats.sent_mcast_pkts);
+       p_stats->sent_bcast_pkts = HILO_64_REGPAIR(pstats.sent_bcast_pkts);
+}
+
+int qed_ll2_get_stats(struct qed_hwfn *p_hwfn,
+                     u8 connection_handle, struct qed_ll2_stats *p_stats)
+{
+       struct qed_ll2_info *p_ll2_conn = NULL;
+       struct qed_ptt *p_ptt;
+
+       memset(p_stats, 0, sizeof(*p_stats));
+
+       if ((connection_handle >= QED_MAX_NUM_OF_LL2_CONNECTIONS) ||
+           !p_hwfn->p_ll2_info)
+               return -EINVAL;
+
+       p_ll2_conn = &p_hwfn->p_ll2_info[connection_handle];
+
+       p_ptt = qed_ptt_acquire(p_hwfn);
+       if (!p_ptt) {
+               DP_ERR(p_hwfn, "Failed to acquire ptt\n");
+               return -EINVAL;
+       }
+
+       _qed_ll2_get_tstats(p_hwfn, p_ptt, p_ll2_conn, p_stats);
+       _qed_ll2_get_ustats(p_hwfn, p_ptt, p_ll2_conn, p_stats);
+       if (p_ll2_conn->tx_stats_en)
+               _qed_ll2_get_pstats(p_hwfn, p_ptt, p_ll2_conn, p_stats);
+
+       qed_ptt_release(p_hwfn, p_ptt);
+       return 0;
+}
+
+static void qed_ll2_register_cb_ops(struct qed_dev *cdev,
+                                   const struct qed_ll2_cb_ops *ops,
+                                   void *cookie)
+{
+       cdev->ll2->cbs = ops;
+       cdev->ll2->cb_cookie = cookie;
+}
+
+static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params)
+{
+       struct qed_ll2_info ll2_info;
+       struct qed_ll2_buffer *buffer;
+       enum qed_ll2_conn_type conn_type;
+       struct qed_ptt *p_ptt;
+       int rc, i;
+
+       /* Initialize LL2 locks & lists */
+       INIT_LIST_HEAD(&cdev->ll2->list);
+       spin_lock_init(&cdev->ll2->lock);
+       cdev->ll2->rx_size = NET_SKB_PAD + ETH_HLEN +
+                            L1_CACHE_BYTES + params->mtu;
+       cdev->ll2->frags_mapped = params->frags_mapped;
+
+       /*Allocate memory for LL2 */
+       DP_INFO(cdev, "Allocating LL2 buffers of size %08x bytes\n",
+               cdev->ll2->rx_size);
+       for (i = 0; i < QED_LL2_RX_SIZE; i++) {
+               buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+               if (!buffer) {
+                       DP_INFO(cdev, "Failed to allocate LL2 buffers\n");
+                       goto fail;
+               }
+
+               rc = qed_ll2_alloc_buffer(cdev, (u8 **)&buffer->data,
+                                         &buffer->phys_addr);
+               if (rc) {
+                       kfree(buffer);
+                       goto fail;
+               }
+
+               list_add_tail(&buffer->list, &cdev->ll2->list);
+       }
+
+       switch (QED_LEADING_HWFN(cdev)->hw_info.personality) {
+       case QED_PCI_ISCSI:
+               conn_type = QED_LL2_TYPE_ISCSI;
+               break;
+       case QED_PCI_ETH_ROCE:
+               conn_type = QED_LL2_TYPE_ROCE;
+               break;
+       default:
+               conn_type = QED_LL2_TYPE_TEST;
+       }
+
+       /* Prepare the temporary ll2 information */
+       memset(&ll2_info, 0, sizeof(ll2_info));
+       ll2_info.conn_type = conn_type;
+       ll2_info.mtu = params->mtu;
+       ll2_info.rx_drop_ttl0_flg = params->drop_ttl0_packets;
+       ll2_info.rx_vlan_removal_en = params->rx_vlan_stripping;
+       ll2_info.tx_tc = 0;
+       ll2_info.tx_dest = CORE_TX_DEST_NW;
+       ll2_info.gsi_enable = 1;
+
+       rc = qed_ll2_acquire_connection(QED_LEADING_HWFN(cdev), &ll2_info,
+                                       QED_LL2_RX_SIZE, QED_LL2_TX_SIZE,
+                                       &cdev->ll2->handle);
+       if (rc) {
+               DP_INFO(cdev, "Failed to acquire LL2 connection\n");
+               goto fail;
+       }
+
+       rc = qed_ll2_establish_connection(QED_LEADING_HWFN(cdev),
+                                         cdev->ll2->handle);
+       if (rc) {
+               DP_INFO(cdev, "Failed to establish LL2 connection\n");
+               goto release_fail;
+       }
+
+       /* Post all Rx buffers to FW */
+       spin_lock_bh(&cdev->ll2->lock);
+       list_for_each_entry(buffer, &cdev->ll2->list, list) {
+               rc = qed_ll2_post_rx_buffer(QED_LEADING_HWFN(cdev),
+                                           cdev->ll2->handle,
+                                           buffer->phys_addr, 0, buffer, 1);
+               if (rc) {
+                       DP_INFO(cdev,
+                               "Failed to post an Rx buffer; Deleting it\n");
+                       dma_unmap_single(&cdev->pdev->dev, buffer->phys_addr,
+                                        cdev->ll2->rx_size, DMA_FROM_DEVICE);
+                       kfree(buffer->data);
+                       list_del(&buffer->list);
+                       kfree(buffer);
+               } else {
+                       cdev->ll2->rx_cnt++;
+               }
+       }
+       spin_unlock_bh(&cdev->ll2->lock);
+
+       if (!cdev->ll2->rx_cnt) {
+               DP_INFO(cdev, "Failed passing even a single Rx buffer\n");
+               goto release_terminate;
+       }
+
+       if (!is_valid_ether_addr(params->ll2_mac_address)) {
+               DP_INFO(cdev, "Invalid Ethernet address\n");
+               goto release_terminate;
+       }
+
+       p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev));
+       if (!p_ptt) {
+               DP_INFO(cdev, "Failed to acquire PTT\n");
+               goto release_terminate;
+       }
+
+       rc = qed_llh_add_mac_filter(QED_LEADING_HWFN(cdev), p_ptt,
+                                   params->ll2_mac_address);
+       qed_ptt_release(QED_LEADING_HWFN(cdev), p_ptt);
+       if (rc) {
+               DP_ERR(cdev, "Failed to allocate LLH filter\n");
+               goto release_terminate_all;
+       }
+
+       ether_addr_copy(cdev->ll2_mac_address, params->ll2_mac_address);
+
+       return 0;
+
+release_terminate_all:
+
+release_terminate:
+       qed_ll2_terminate_connection(QED_LEADING_HWFN(cdev), cdev->ll2->handle);
+release_fail:
+       qed_ll2_release_connection(QED_LEADING_HWFN(cdev), cdev->ll2->handle);
+fail:
+       qed_ll2_kill_buffers(cdev);
+       cdev->ll2->handle = QED_LL2_UNUSED_HANDLE;
+       return -EINVAL;
+}
+
+static int qed_ll2_stop(struct qed_dev *cdev)
+{
+       struct qed_ptt *p_ptt;
+       int rc;
+
+       if (cdev->ll2->handle == QED_LL2_UNUSED_HANDLE)
+               return 0;
+
+       p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev));
+       if (!p_ptt) {
+               DP_INFO(cdev, "Failed to acquire PTT\n");
+               goto fail;
+       }
+
+       qed_llh_remove_mac_filter(QED_LEADING_HWFN(cdev), p_ptt,
+                                 cdev->ll2_mac_address);
+       qed_ptt_release(QED_LEADING_HWFN(cdev), p_ptt);
+       eth_zero_addr(cdev->ll2_mac_address);
+
+       rc = qed_ll2_terminate_connection(QED_LEADING_HWFN(cdev),
+                                         cdev->ll2->handle);
+       if (rc)
+               DP_INFO(cdev, "Failed to terminate LL2 connection\n");
+
+       qed_ll2_kill_buffers(cdev);
+
+       qed_ll2_release_connection(QED_LEADING_HWFN(cdev), cdev->ll2->handle);
+       cdev->ll2->handle = QED_LL2_UNUSED_HANDLE;
+
+       return rc;
+fail:
+       return -EINVAL;
+}
+
+static int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb)
+{
+       const skb_frag_t *frag;
+       int rc = -EINVAL, i;
+       dma_addr_t mapping;
+       u16 vlan = 0;
+       u8 flags = 0;
+
+       if (unlikely(skb->ip_summed != CHECKSUM_NONE)) {
+               DP_INFO(cdev, "Cannot transmit a checksumed packet\n");
+               return -EINVAL;
+       }
+
+       if (1 + skb_shinfo(skb)->nr_frags > CORE_LL2_TX_MAX_BDS_PER_PACKET) {
+               DP_ERR(cdev, "Cannot transmit a packet with %d fragments\n",
+                      1 + skb_shinfo(skb)->nr_frags);
+               return -EINVAL;
+       }
+
+       mapping = dma_map_single(&cdev->pdev->dev, skb->data,
+                                skb->len, DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(&cdev->pdev->dev, mapping))) {
+               DP_NOTICE(cdev, "SKB mapping failed\n");
+               return -EINVAL;
+       }
+
+       /* Request HW to calculate IP csum */
+       if (!((vlan_get_protocol(skb) == htons(ETH_P_IPV6)) &&
+             ipv6_hdr(skb)->nexthdr == NEXTHDR_IPV6))
+               flags |= BIT(CORE_TX_BD_FLAGS_IP_CSUM_SHIFT);
+
+       if (skb_vlan_tag_present(skb)) {
+               vlan = skb_vlan_tag_get(skb);
+               flags |= BIT(CORE_TX_BD_FLAGS_VLAN_INSERTION_SHIFT);
+       }
+
+       rc = qed_ll2_prepare_tx_packet(QED_LEADING_HWFN(cdev),
+                                      cdev->ll2->handle,
+                                      1 + skb_shinfo(skb)->nr_frags,
+                                      vlan, flags, 0, 0 /* RoCE FLAVOR */,
+                                      mapping, skb->len, skb, 1);
+       if (rc)
+               goto err;
+
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               frag = &skb_shinfo(skb)->frags[i];
+               if (!cdev->ll2->frags_mapped) {
+                       mapping = skb_frag_dma_map(&cdev->pdev->dev, frag, 0,
+                                                  skb_frag_size(frag),
+                                                  DMA_TO_DEVICE);
+
+                       if (unlikely(dma_mapping_error(&cdev->pdev->dev,
+                                                      mapping))) {
+                               DP_NOTICE(cdev,
+                                         "Unable to map frag - dropping packet\n");
+                               goto err;
+                       }
+               } else {
+                       mapping = page_to_phys(skb_frag_page(frag)) |
+                           frag->page_offset;
+               }
+
+               rc = qed_ll2_set_fragment_of_tx_packet(QED_LEADING_HWFN(cdev),
+                                                      cdev->ll2->handle,
+                                                      mapping,
+                                                      skb_frag_size(frag));
+
+               /* if failed not much to do here, partial packet has been posted
+                * we can't free memory, will need to wait for completion.
+                */
+               if (rc)
+                       goto err2;
+       }
+
+       return 0;
+
+err:
+       dma_unmap_single(&cdev->pdev->dev, mapping, skb->len, DMA_TO_DEVICE);
+
+err2:
+       return rc;
+}
+
+static int qed_ll2_stats(struct qed_dev *cdev, struct qed_ll2_stats *stats)
+{
+       if (!cdev->ll2)
+               return -EINVAL;
+
+       return qed_ll2_get_stats(QED_LEADING_HWFN(cdev),
+                                cdev->ll2->handle, stats);
+}
+
+const struct qed_ll2_ops qed_ll2_ops_pass = {
+       .start = &qed_ll2_start,
+       .stop = &qed_ll2_stop,
+       .start_xmit = &qed_ll2_start_xmit,
+       .register_cb_ops = &qed_ll2_register_cb_ops,
+       .get_stats = &qed_ll2_stats,
+};
+
+int qed_ll2_alloc_if(struct qed_dev *cdev)
+{
+       cdev->ll2 = kzalloc(sizeof(*cdev->ll2), GFP_KERNEL);
+       return cdev->ll2 ? 0 : -ENOMEM;
+}
+
+void qed_ll2_dealloc_if(struct qed_dev *cdev)
+{
+       kfree(cdev->ll2);
+       cdev->ll2 = NULL;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.h b/drivers/net/ethernet/qlogic/qed/qed_ll2.h
new file mode 100644 (file)
index 0000000..80a5dc2
--- /dev/null
@@ -0,0 +1,316 @@
+/* QLogic qed NIC Driver
+ *
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available 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.
+ */
+
+#ifndef _QED_LL2_H
+#define _QED_LL2_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/qed/qed_chain.h>
+#include <linux/qed/qed_ll2_if.h>
+#include "qed.h"
+#include "qed_hsi.h"
+#include "qed_sp.h"
+
+#define QED_MAX_NUM_OF_LL2_CONNECTIONS                    (4)
+
+enum qed_ll2_roce_flavor_type {
+       QED_LL2_ROCE,
+       QED_LL2_RROCE,
+       MAX_QED_LL2_ROCE_FLAVOR_TYPE
+};
+
+enum qed_ll2_conn_type {
+       QED_LL2_TYPE_RESERVED,
+       QED_LL2_TYPE_ISCSI,
+       QED_LL2_TYPE_TEST,
+       QED_LL2_TYPE_ISCSI_OOO,
+       QED_LL2_TYPE_RESERVED2,
+       QED_LL2_TYPE_ROCE,
+       QED_LL2_TYPE_RESERVED3,
+       MAX_QED_LL2_RX_CONN_TYPE
+};
+
+struct qed_ll2_rx_packet {
+       struct list_head list_entry;
+       struct core_rx_bd_with_buff_len *rxq_bd;
+       dma_addr_t rx_buf_addr;
+       u16 buf_length;
+       void *cookie;
+       u8 placement_offset;
+       u16 parse_flags;
+       u16 packet_length;
+       u16 vlan;
+       u32 opaque_data[2];
+};
+
+struct qed_ll2_tx_packet {
+       struct list_head list_entry;
+       u16 bd_used;
+       u16 vlan;
+       u16 l4_hdr_offset_w;
+       u8 bd_flags;
+       bool notify_fw;
+       void *cookie;
+
+       struct {
+               struct core_tx_bd *txq_bd;
+               dma_addr_t tx_frag;
+               u16 frag_len;
+       } bds_set[ETH_TX_MAX_BDS_PER_NON_LSO_PACKET];
+};
+
+struct qed_ll2_rx_queue {
+       /* Lock protecting the Rx queue manipulation */
+       spinlock_t lock;
+       struct qed_chain rxq_chain;
+       struct qed_chain rcq_chain;
+       u8 rx_sb_index;
+       bool b_cb_registred;
+       __le16 *p_fw_cons;
+       struct list_head active_descq;
+       struct list_head free_descq;
+       struct list_head posting_descq;
+       struct qed_ll2_rx_packet *descq_array;
+       void __iomem *set_prod_addr;
+};
+
+struct qed_ll2_tx_queue {
+       /* Lock protecting the Tx queue manipulation */
+       spinlock_t lock;
+       struct qed_chain txq_chain;
+       u8 tx_sb_index;
+       bool b_cb_registred;
+       __le16 *p_fw_cons;
+       struct list_head active_descq;
+       struct list_head free_descq;
+       struct list_head sending_descq;
+       struct qed_ll2_tx_packet *descq_array;
+       struct qed_ll2_tx_packet *cur_send_packet;
+       struct qed_ll2_tx_packet cur_completing_packet;
+       u16 cur_completing_bd_idx;
+       void __iomem *doorbell_addr;
+       u16 bds_idx;
+       u16 cur_send_frag_num;
+       u16 cur_completing_frag_num;
+       bool b_completing_packet;
+};
+
+struct qed_ll2_info {
+       /* Lock protecting the state of LL2 */
+       struct mutex mutex;
+       enum qed_ll2_conn_type conn_type;
+       u32 cid;
+       u8 my_id;
+       u8 queue_id;
+       u8 tx_stats_id;
+       bool b_active;
+       u16 mtu;
+       u8 rx_drop_ttl0_flg;
+       u8 rx_vlan_removal_en;
+       u8 tx_tc;
+       enum core_tx_dest tx_dest;
+       enum core_error_handle ai_err_packet_too_big;
+       enum core_error_handle ai_err_no_buf;
+       u8 tx_stats_en;
+       struct qed_ll2_rx_queue rx_queue;
+       struct qed_ll2_tx_queue tx_queue;
+       u8 gsi_enable;
+};
+
+/**
+ * @brief qed_ll2_acquire_connection - allocate resources,
+ *        starts rx & tx (if relevant) queues pair. Provides
+ *        connecion handler as output parameter.
+ *
+ * @param p_hwfn
+ * @param p_params             Contain various configuration properties
+ * @param rx_num_desc
+ * @param tx_num_desc
+ *
+ * @param p_connection_handle  Output container for LL2 connection's handle
+ *
+ * @return 0 on success, failure otherwise
+ */
+int qed_ll2_acquire_connection(struct qed_hwfn *p_hwfn,
+                              struct qed_ll2_info *p_params,
+                              u16 rx_num_desc,
+                              u16 tx_num_desc,
+                              u8 *p_connection_handle);
+
+/**
+ * @brief qed_ll2_establish_connection - start previously
+ *        allocated LL2 queues pair
+ *
+ * @param p_hwfn
+ * @param p_ptt
+ * @param connection_handle    LL2 connection's handle obtained from
+ *                              qed_ll2_require_connection
+ *
+ * @return 0 on success, failure otherwise
+ */
+int qed_ll2_establish_connection(struct qed_hwfn *p_hwfn, u8 connection_handle);
+
+/**
+ * @brief qed_ll2_post_rx_buffers - submit buffers to LL2 Rx queue.
+ *
+ * @param p_hwfn
+ * @param connection_handle    LL2 connection's handle obtained from
+ *                             qed_ll2_require_connection
+ * @param addr                 rx (physical address) buffers to submit
+ * @param cookie
+ * @param notify_fw            produce corresponding Rx BD immediately
+ *
+ * @return 0 on success, failure otherwise
+ */
+int qed_ll2_post_rx_buffer(struct qed_hwfn *p_hwfn,
+                          u8 connection_handle,
+                          dma_addr_t addr,
+                          u16 buf_len, void *cookie, u8 notify_fw);
+
+/**
+ * @brief qed_ll2_prepare_tx_packet - request for start Tx BD
+ *                                   to prepare Tx packet submission to FW.
+ *
+ * @param p_hwfn
+ * @param connection_handle    LL2 connection's handle obtained from
+ *                             qed_ll2_require_connection
+ * @param num_of_bds           a number of requested BD equals a number of
+ *                             fragments in Tx packet
+ * @param vlan                 VLAN to insert to packet (if insertion set)
+ * @param bd_flags
+ * @param l4_hdr_offset_w      L4 Header Offset from start of packet
+ *                             (in words). This is needed if both l4_csum
+ *                             and ipv6_ext are set
+ * @param first_frag
+ * @param first_frag_len
+ * @param cookie
+ *
+ * @param notify_fw
+ *
+ * @return 0 on success, failure otherwise
+ */
+int qed_ll2_prepare_tx_packet(struct qed_hwfn *p_hwfn,
+                             u8 connection_handle,
+                             u8 num_of_bds,
+                             u16 vlan,
+                             u8 bd_flags,
+                             u16 l4_hdr_offset_w,
+                             enum qed_ll2_roce_flavor_type qed_roce_flavor,
+                             dma_addr_t first_frag,
+                             u16 first_frag_len, void *cookie, u8 notify_fw);
+
+/**
+ * @brief qed_ll2_release_connection - releases resources
+ *                                     allocated for LL2 connection
+ *
+ * @param p_hwfn
+ * @param connection_handle            LL2 connection's handle obtained from
+ *                                     qed_ll2_require_connection
+ */
+void qed_ll2_release_connection(struct qed_hwfn *p_hwfn, u8 connection_handle);
+
+/**
+ * @brief qed_ll2_set_fragment_of_tx_packet -  provides fragments to fill
+ *                                             Tx BD of BDs requested by
+ *                                             qed_ll2_prepare_tx_packet
+ *
+ * @param p_hwfn
+ * @param connection_handle                    LL2 connection's handle
+ *                                             obtained from
+ *                                             qed_ll2_require_connection
+ * @param addr
+ * @param nbytes
+ *
+ * @return 0 on success, failure otherwise
+ */
+int qed_ll2_set_fragment_of_tx_packet(struct qed_hwfn *p_hwfn,
+                                     u8 connection_handle,
+                                     dma_addr_t addr, u16 nbytes);
+
+/**
+ * @brief qed_ll2_terminate_connection -       stops Tx/Rx queues
+ *
+ *
+ * @param p_hwfn
+ * @param connection_handle                    LL2 connection's handle
+ *                                             obtained from
+ *                                             qed_ll2_require_connection
+ *
+ * @return 0 on success, failure otherwise
+ */
+int qed_ll2_terminate_connection(struct qed_hwfn *p_hwfn, u8 connection_handle);
+
+/**
+ * @brief qed_ll2_get_stats -  get LL2 queue's statistics
+ *
+ *
+ * @param p_hwfn
+ * @param connection_handle    LL2 connection's handle obtained from
+ *                             qed_ll2_require_connection
+ * @param p_stats
+ *
+ * @return 0 on success, failure otherwise
+ */
+int qed_ll2_get_stats(struct qed_hwfn *p_hwfn,
+                     u8 connection_handle, struct qed_ll2_stats *p_stats);
+
+/**
+ * @brief qed_ll2_alloc - Allocates LL2 connections set
+ *
+ * @param p_hwfn
+ *
+ * @return pointer to alocated qed_ll2_info or NULL
+ */
+struct qed_ll2_info *qed_ll2_alloc(struct qed_hwfn *p_hwfn);
+
+/**
+ * @brief qed_ll2_setup - Inits LL2 connections set
+ *
+ * @param p_hwfn
+ * @param p_ll2_connections
+ *
+ */
+void qed_ll2_setup(struct qed_hwfn *p_hwfn,
+                  struct qed_ll2_info *p_ll2_connections);
+
+/**
+ * @brief qed_ll2_free - Releases LL2 connections set
+ *
+ * @param p_hwfn
+ * @param p_ll2_connections
+ *
+ */
+void qed_ll2_free(struct qed_hwfn *p_hwfn,
+                 struct qed_ll2_info *p_ll2_connections);
+void qed_ll2b_complete_rx_gsi_packet(struct qed_hwfn *p_hwfn,
+                                    u8 connection_handle,
+                                    void *cookie,
+                                    dma_addr_t rx_buf_addr,
+                                    u16 data_length,
+                                    u8 data_length_error,
+                                    u16 parse_flags,
+                                    u16 vlan,
+                                    u32 src_mac_addr_hi,
+                                    u16 src_mac_addr_lo, bool b_last_packet);
+void qed_ll2b_complete_tx_gsi_packet(struct qed_hwfn *p_hwfn,
+                                    u8 connection_handle,
+                                    void *cookie,
+                                    dma_addr_t first_frag_addr,
+                                    bool b_last_fragment, bool b_last_packet);
+void qed_ll2b_release_tx_gsi_packet(struct qed_hwfn *p_hwfn,
+                                   u8 connection_handle,
+                                   void *cookie,
+                                   dma_addr_t first_frag_addr,
+                                   bool b_last_fragment, bool b_last_packet);
+#endif
index b730a63..4ee3151 100644 (file)
 #include <linux/etherdevice.h>
 #include <linux/vmalloc.h>
 #include <linux/qed/qed_if.h>
+#include <linux/qed/qed_ll2_if.h>
 
 #include "qed.h"
 #include "qed_sriov.h"
 #include "qed_sp.h"
 #include "qed_dev_api.h"
+#include "qed_ll2.h"
 #include "qed_mcp.h"
 #include "qed_hw.h"
 #include "qed_selftest.h"
 
+#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
+#define QED_ROCE_QPS                   (8192)
+#define QED_ROCE_DPIS                  (8)
+#endif
+
 static char version[] =
        "QLogic FastLinQ 4xxxx Core Module qed " DRV_MODULE_VERSION "\n";
 
@@ -204,8 +211,8 @@ int qed_fill_dev_info(struct qed_dev *cdev,
        dev_info->pci_mem_start = cdev->pci_params.mem_start;
        dev_info->pci_mem_end = cdev->pci_params.mem_end;
        dev_info->pci_irq = cdev->pci_params.irq;
-       dev_info->rdma_supported =
-           (cdev->hwfns[0].hw_info.personality == QED_PCI_ETH_ROCE);
+       dev_info->rdma_supported = (cdev->hwfns[0].hw_info.personality ==
+                                   QED_PCI_ETH_ROCE);
        dev_info->is_mf_default = IS_MF_DEFAULT(&cdev->hwfns[0]);
        ether_addr_copy(dev_info->hw_mac, cdev->hwfns[0].hw_info.hw_mac_addr);
 
@@ -608,7 +615,16 @@ static int qed_nic_reset(struct qed_dev *cdev)
 
 static int qed_nic_setup(struct qed_dev *cdev)
 {
-       int rc;
+       int rc, i;
+
+       /* Determine if interface is going to require LL2 */
+       if (QED_LEADING_HWFN(cdev)->hw_info.personality != QED_PCI_ETH) {
+               for (i = 0; i < cdev->num_hwfns; i++) {
+                       struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
+
+                       p_hwfn->using_ll2 = true;
+               }
+       }
 
        rc = qed_resc_alloc(cdev);
        if (rc)
@@ -666,6 +682,9 @@ static int qed_slowpath_setup_int(struct qed_dev *cdev,
                                  enum qed_int_mode int_mode)
 {
        struct qed_sb_cnt_info sb_cnt_info;
+#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
+       int num_l2_queues;
+#endif
        int rc;
        int i;
 
@@ -696,6 +715,31 @@ static int qed_slowpath_setup_int(struct qed_dev *cdev,
        cdev->int_params.fp_msix_cnt = cdev->int_params.out.num_vectors -
                                       cdev->num_hwfns;
 
+#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
+       num_l2_queues = 0;
+       for_each_hwfn(cdev, i)
+               num_l2_queues += FEAT_NUM(&cdev->hwfns[i], QED_PF_L2_QUE);
+
+       DP_VERBOSE(cdev, QED_MSG_RDMA,
+                  "cdev->int_params.fp_msix_cnt=%d num_l2_queues=%d\n",
+                  cdev->int_params.fp_msix_cnt, num_l2_queues);
+
+       if (cdev->int_params.fp_msix_cnt > num_l2_queues) {
+               cdev->int_params.rdma_msix_cnt =
+                       (cdev->int_params.fp_msix_cnt - num_l2_queues)
+                       / cdev->num_hwfns;
+               cdev->int_params.rdma_msix_base =
+                       cdev->int_params.fp_msix_base + num_l2_queues;
+               cdev->int_params.fp_msix_cnt = num_l2_queues;
+       } else {
+               cdev->int_params.rdma_msix_cnt = 0;
+       }
+
+       DP_VERBOSE(cdev, QED_MSG_RDMA, "roce_msix_cnt=%d roce_msix_base=%d\n",
+                  cdev->int_params.rdma_msix_cnt,
+                  cdev->int_params.rdma_msix_base);
+#endif
+
        return 0;
 }
 
@@ -799,6 +843,13 @@ static void qed_update_pf_params(struct qed_dev *cdev,
 {
        int i;
 
+#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
+       params->rdma_pf_params.num_qps = QED_ROCE_QPS;
+       params->rdma_pf_params.min_dpis = QED_ROCE_DPIS;
+       /* divide by 3 the MRs to avoid MF ILT overflow */
+       params->rdma_pf_params.num_mrs = RDMA_MAX_TIDS;
+       params->rdma_pf_params.gl_pi = QED_ROCE_PROTOCOL_INDEX;
+#endif
        for (i = 0; i < cdev->num_hwfns; i++) {
                struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
 
@@ -873,6 +924,12 @@ static int qed_slowpath_start(struct qed_dev *cdev,
        DP_INFO(cdev,
                "HW initialization and function start completed successfully\n");
 
+       /* Allocate LL2 interface if needed */
+       if (QED_LEADING_HWFN(cdev)->using_ll2) {
+               rc = qed_ll2_alloc_if(cdev);
+               if (rc)
+                       goto err3;
+       }
        if (IS_PF(cdev)) {
                hwfn = QED_LEADING_HWFN(cdev);
                drv_version.version = (params->drv_major << 24) |
@@ -893,6 +950,8 @@ static int qed_slowpath_start(struct qed_dev *cdev,
 
        return 0;
 
+err3:
+       qed_hw_stop(cdev);
 err2:
        qed_hw_timers_stop_all(cdev);
        if (IS_PF(cdev))
@@ -915,6 +974,8 @@ static int qed_slowpath_stop(struct qed_dev *cdev)
        if (!cdev)
                return -ENODEV;
 
+       qed_ll2_dealloc_if(cdev);
+
        if (IS_PF(cdev)) {
                qed_free_stream_mem(cdev);
                if (IS_QED_ETH_IF(cdev))
index 759cb04..b414a05 100644 (file)
        0x50196cUL
 #define NIG_REG_LLH_CLS_TYPE_DUALMODE \
        0x501964UL
+#define NIG_REG_LLH_FUNC_FILTER_VALUE \
+       0x501a00UL
+#define NIG_REG_LLH_FUNC_FILTER_VALUE_SIZE \
+       32
+#define NIG_REG_LLH_FUNC_FILTER_EN \
+       0x501a80UL
+#define NIG_REG_LLH_FUNC_FILTER_EN_SIZE        \
+       16
+#define NIG_REG_LLH_FUNC_FILTER_MODE \
+       0x501ac0UL
+#define NIG_REG_LLH_FUNC_FILTER_MODE_SIZE \
+       16
+#define NIG_REG_LLH_FUNC_FILTER_PROTOCOL_TYPE \
+       0x501b00UL
+#define NIG_REG_LLH_FUNC_FILTER_PROTOCOL_TYPE_SIZE \
+       16
+#define NIG_REG_LLH_FUNC_FILTER_HDR_SEL        \
+       0x501b40UL
+#define NIG_REG_LLH_FUNC_FILTER_HDR_SEL_SIZE \
+       16
 #define  NCSI_REG_CONFIG       \
        0x040200UL
 #define  PBF_REG_INIT \
        0x1f0a1cUL
 #define PRS_REG_ROCE_DEST_QP_MAX_PF \
        0x1f0430UL
+#define PRS_REG_USE_LIGHT_L2 \
+       0x1f096cUL
 #define  PSDM_REG_ENABLE_IN1 \
        0xfa0004UL
 #define  PSEM_REG_ENABLE_IN \
        0x620000UL
 #define PHY_PCIE_REG_PHY1 \
        0x624000UL
-
+#define NIG_REG_ROCE_DUPLICATE_TO_HOST 0x5088f0UL
+#define PRS_REG_LIGHT_L2_ETHERTYPE_EN 0x1f0968UL
+#define NIG_REG_LLH_ENG_CLS_ENG_ID_TBL 0x501b90UL
+#define DORQ_REG_PF_DPM_ENABLE 0x100510UL
+#define DORQ_REG_PF_ICID_BIT_SHIFT_NORM        0x100448UL
+#define DORQ_REG_PF_MIN_ADDR_REG1 0x100400UL
+#define DORQ_REG_PF_DPI_BIT_SHIFT 0x100450UL
 #endif
diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c
new file mode 100644 (file)
index 0000000..2343005
--- /dev/null
@@ -0,0 +1,2954 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * 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
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/types.h>
+#include <asm/byteorder.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/io.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/tcp.h>
+#include <linux/bitops.h>
+#include <linux/qed/qed_roce_if.h>
+#include <linux/qed/qed_roce_if.h>
+#include "qed.h"
+#include "qed_cxt.h"
+#include "qed_hsi.h"
+#include "qed_hw.h"
+#include "qed_init_ops.h"
+#include "qed_int.h"
+#include "qed_ll2.h"
+#include "qed_mcp.h"
+#include "qed_reg_addr.h"
+#include "qed_sp.h"
+#include "qed_roce.h"
+#include "qed_ll2.h"
+
+void qed_async_roce_event(struct qed_hwfn *p_hwfn,
+                         struct event_ring_entry *p_eqe)
+{
+       struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info;
+
+       p_rdma_info->events.affiliated_event(p_rdma_info->events.context,
+                                            p_eqe->opcode, &p_eqe->data);
+}
+
+static int qed_rdma_bmap_alloc(struct qed_hwfn *p_hwfn,
+                              struct qed_bmap *bmap, u32 max_count)
+{
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "max_count = %08x\n", max_count);
+
+       bmap->max_count = max_count;
+
+       bmap->bitmap = kzalloc(BITS_TO_LONGS(max_count) * sizeof(long),
+                              GFP_KERNEL);
+       if (!bmap->bitmap) {
+               DP_NOTICE(p_hwfn,
+                         "qed bmap alloc failed: cannot allocate memory (bitmap)\n");
+               return -ENOMEM;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocated bitmap %p\n",
+                  bmap->bitmap);
+       return 0;
+}
+
+static int qed_rdma_bmap_alloc_id(struct qed_hwfn *p_hwfn,
+                                 struct qed_bmap *bmap, u32 *id_num)
+{
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "bmap = %p\n", bmap);
+
+       *id_num = find_first_zero_bit(bmap->bitmap, bmap->max_count);
+
+       if (*id_num >= bmap->max_count) {
+               DP_NOTICE(p_hwfn, "no id available max_count=%d\n",
+                         bmap->max_count);
+               return -EINVAL;
+       }
+
+       __set_bit(*id_num, bmap->bitmap);
+
+       return 0;
+}
+
+static void qed_bmap_release_id(struct qed_hwfn *p_hwfn,
+                               struct qed_bmap *bmap, u32 id_num)
+{
+       bool b_acquired;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "id_num = %08x", id_num);
+       if (id_num >= bmap->max_count)
+               return;
+
+       b_acquired = test_and_clear_bit(id_num, bmap->bitmap);
+       if (!b_acquired) {
+               DP_NOTICE(p_hwfn, "ID %d already released\n", id_num);
+               return;
+       }
+}
+
+u32 qed_rdma_get_sb_id(void *p_hwfn, u32 rel_sb_id)
+{
+       /* First sb id for RoCE is after all the l2 sb */
+       return FEAT_NUM((struct qed_hwfn *)p_hwfn, QED_PF_L2_QUE) + rel_sb_id;
+}
+
+u32 qed_rdma_query_cau_timer_res(void *rdma_cxt)
+{
+       return QED_CAU_DEF_RX_TIMER_RES;
+}
+
+static int qed_rdma_alloc(struct qed_hwfn *p_hwfn,
+                         struct qed_ptt *p_ptt,
+                         struct qed_rdma_start_in_params *params)
+{
+       struct qed_rdma_info *p_rdma_info;
+       u32 num_cons, num_tasks;
+       int rc = -ENOMEM;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocating RDMA\n");
+
+       /* Allocate a struct with current pf rdma info */
+       p_rdma_info = kzalloc(sizeof(*p_rdma_info), GFP_KERNEL);
+       if (!p_rdma_info) {
+               DP_NOTICE(p_hwfn,
+                         "qed rdma alloc failed: cannot allocate memory (rdma info). rc = %d\n",
+                         rc);
+               return rc;
+       }
+
+       p_hwfn->p_rdma_info = p_rdma_info;
+       p_rdma_info->proto = PROTOCOLID_ROCE;
+
+       num_cons = qed_cxt_get_proto_cid_count(p_hwfn, p_rdma_info->proto, 0);
+
+       p_rdma_info->num_qps = num_cons / 2;
+
+       num_tasks = qed_cxt_get_proto_tid_count(p_hwfn, PROTOCOLID_ROCE);
+
+       /* Each MR uses a single task */
+       p_rdma_info->num_mrs = num_tasks;
+
+       /* Queue zone lines are shared between RoCE and L2 in such a way that
+        * they can be used by each without obstructing the other.
+        */
+       p_rdma_info->queue_zone_base = (u16)FEAT_NUM(p_hwfn, QED_L2_QUEUE);
+
+       /* Allocate a struct with device params and fill it */
+       p_rdma_info->dev = kzalloc(sizeof(*p_rdma_info->dev), GFP_KERNEL);
+       if (!p_rdma_info->dev) {
+               DP_NOTICE(p_hwfn,
+                         "qed rdma alloc failed: cannot allocate memory (rdma info dev). rc = %d\n",
+                         rc);
+               goto free_rdma_info;
+       }
+
+       /* Allocate a struct with port params and fill it */
+       p_rdma_info->port = kzalloc(sizeof(*p_rdma_info->port), GFP_KERNEL);
+       if (!p_rdma_info->port) {
+               DP_NOTICE(p_hwfn,
+                         "qed rdma alloc failed: cannot allocate memory (rdma info port). rc = %d\n",
+                         rc);
+               goto free_rdma_dev;
+       }
+
+       /* Allocate bit map for pd's */
+       rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->pd_map, RDMA_MAX_PDS);
+       if (rc) {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                          "Failed to allocate pd_map, rc = %d\n",
+                          rc);
+               goto free_rdma_port;
+       }
+
+       /* Allocate DPI bitmap */
+       rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->dpi_map,
+                                p_hwfn->dpi_count);
+       if (rc) {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                          "Failed to allocate DPI bitmap, rc = %d\n", rc);
+               goto free_pd_map;
+       }
+
+       /* Allocate bitmap for cq's. The maximum number of CQs is bounded to
+        * twice the number of QPs.
+        */
+       rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->cq_map,
+                                p_rdma_info->num_qps * 2);
+       if (rc) {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                          "Failed to allocate cq bitmap, rc = %d\n", rc);
+               goto free_dpi_map;
+       }
+
+       /* Allocate bitmap for toggle bit for cq icids
+        * We toggle the bit every time we create or resize cq for a given icid.
+        * The maximum number of CQs is bounded to  twice the number of QPs.
+        */
+       rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->toggle_bits,
+                                p_rdma_info->num_qps * 2);
+       if (rc) {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                          "Failed to allocate toogle bits, rc = %d\n", rc);
+               goto free_cq_map;
+       }
+
+       /* Allocate bitmap for itids */
+       rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->tid_map,
+                                p_rdma_info->num_mrs);
+       if (rc) {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                          "Failed to allocate itids bitmaps, rc = %d\n", rc);
+               goto free_toggle_map;
+       }
+
+       /* Allocate bitmap for cids used for qps. */
+       rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->cid_map, num_cons);
+       if (rc) {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                          "Failed to allocate cid bitmap, rc = %d\n", rc);
+               goto free_tid_map;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocation successful\n");
+       return 0;
+
+free_tid_map:
+       kfree(p_rdma_info->tid_map.bitmap);
+free_toggle_map:
+       kfree(p_rdma_info->toggle_bits.bitmap);
+free_cq_map:
+       kfree(p_rdma_info->cq_map.bitmap);
+free_dpi_map:
+       kfree(p_rdma_info->dpi_map.bitmap);
+free_pd_map:
+       kfree(p_rdma_info->pd_map.bitmap);
+free_rdma_port:
+       kfree(p_rdma_info->port);
+free_rdma_dev:
+       kfree(p_rdma_info->dev);
+free_rdma_info:
+       kfree(p_rdma_info);
+
+       return rc;
+}
+
+void qed_rdma_resc_free(struct qed_hwfn *p_hwfn)
+{
+       struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info;
+
+       kfree(p_rdma_info->cid_map.bitmap);
+       kfree(p_rdma_info->tid_map.bitmap);
+       kfree(p_rdma_info->toggle_bits.bitmap);
+       kfree(p_rdma_info->cq_map.bitmap);
+       kfree(p_rdma_info->dpi_map.bitmap);
+       kfree(p_rdma_info->pd_map.bitmap);
+
+       kfree(p_rdma_info->port);
+       kfree(p_rdma_info->dev);
+
+       kfree(p_rdma_info);
+}
+
+static void qed_rdma_free(struct qed_hwfn *p_hwfn)
+{
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Freeing RDMA\n");
+
+       qed_rdma_resc_free(p_hwfn);
+}
+
+static void qed_rdma_get_guid(struct qed_hwfn *p_hwfn, u8 *guid)
+{
+       guid[0] = p_hwfn->hw_info.hw_mac_addr[0] ^ 2;
+       guid[1] = p_hwfn->hw_info.hw_mac_addr[1];
+       guid[2] = p_hwfn->hw_info.hw_mac_addr[2];
+       guid[3] = 0xff;
+       guid[4] = 0xfe;
+       guid[5] = p_hwfn->hw_info.hw_mac_addr[3];
+       guid[6] = p_hwfn->hw_info.hw_mac_addr[4];
+       guid[7] = p_hwfn->hw_info.hw_mac_addr[5];
+}
+
+static void qed_rdma_init_events(struct qed_hwfn *p_hwfn,
+                                struct qed_rdma_start_in_params *params)
+{
+       struct qed_rdma_events *events;
+
+       events = &p_hwfn->p_rdma_info->events;
+
+       events->unaffiliated_event = params->events->unaffiliated_event;
+       events->affiliated_event = params->events->affiliated_event;
+       events->context = params->events->context;
+}
+
+static void qed_rdma_init_devinfo(struct qed_hwfn *p_hwfn,
+                                 struct qed_rdma_start_in_params *params)
+{
+       struct qed_rdma_device *dev = p_hwfn->p_rdma_info->dev;
+       struct qed_dev *cdev = p_hwfn->cdev;
+       u32 pci_status_control;
+       u32 num_qps;
+
+       /* Vendor specific information */
+       dev->vendor_id = cdev->vendor_id;
+       dev->vendor_part_id = cdev->device_id;
+       dev->hw_ver = 0;
+       dev->fw_ver = (FW_MAJOR_VERSION << 24) | (FW_MINOR_VERSION << 16) |
+                     (FW_REVISION_VERSION << 8) | (FW_ENGINEERING_VERSION);
+
+       qed_rdma_get_guid(p_hwfn, (u8 *)&dev->sys_image_guid);
+       dev->node_guid = dev->sys_image_guid;
+
+       dev->max_sge = min_t(u32, RDMA_MAX_SGE_PER_SQ_WQE,
+                            RDMA_MAX_SGE_PER_RQ_WQE);
+
+       if (cdev->rdma_max_sge)
+               dev->max_sge = min_t(u32, cdev->rdma_max_sge, dev->max_sge);
+
+       dev->max_inline = ROCE_REQ_MAX_INLINE_DATA_SIZE;
+
+       dev->max_inline = (cdev->rdma_max_inline) ?
+                         min_t(u32, cdev->rdma_max_inline, dev->max_inline) :
+                         dev->max_inline;
+
+       dev->max_wqe = QED_RDMA_MAX_WQE;
+       dev->max_cnq = (u8)FEAT_NUM(p_hwfn, QED_RDMA_CNQ);
+
+       /* The number of QPs may be higher than QED_ROCE_MAX_QPS, because
+        * it is up-aligned to 16 and then to ILT page size within qed cxt.
+        * This is OK in terms of ILT but we don't want to configure the FW
+        * above its abilities
+        */
+       num_qps = ROCE_MAX_QPS;
+       num_qps = min_t(u64, num_qps, p_hwfn->p_rdma_info->num_qps);
+       dev->max_qp = num_qps;
+
+       /* CQs uses the same icids that QPs use hence they are limited by the
+        * number of icids. There are two icids per QP.
+        */
+       dev->max_cq = num_qps * 2;
+
+       /* The number of mrs is smaller by 1 since the first is reserved */
+       dev->max_mr = p_hwfn->p_rdma_info->num_mrs - 1;
+       dev->max_mr_size = QED_RDMA_MAX_MR_SIZE;
+
+       /* The maximum CQE capacity per CQ supported.
+        * max number of cqes will be in two layer pbl,
+        * 8 is the pointer size in bytes
+        * 32 is the size of cq element in bytes
+        */
+       if (params->cq_mode == QED_RDMA_CQ_MODE_32_BITS)
+               dev->max_cqe = QED_RDMA_MAX_CQE_32_BIT;
+       else
+               dev->max_cqe = QED_RDMA_MAX_CQE_16_BIT;
+
+       dev->max_mw = 0;
+       dev->max_fmr = QED_RDMA_MAX_FMR;
+       dev->max_mr_mw_fmr_pbl = (PAGE_SIZE / 8) * (PAGE_SIZE / 8);
+       dev->max_mr_mw_fmr_size = dev->max_mr_mw_fmr_pbl * PAGE_SIZE;
+       dev->max_pkey = QED_RDMA_MAX_P_KEY;
+
+       dev->max_qp_resp_rd_atomic_resc = RDMA_RING_PAGE_SIZE /
+                                         (RDMA_RESP_RD_ATOMIC_ELM_SIZE * 2);
+       dev->max_qp_req_rd_atomic_resc = RDMA_RING_PAGE_SIZE /
+                                        RDMA_REQ_RD_ATOMIC_ELM_SIZE;
+       dev->max_dev_resp_rd_atomic_resc = dev->max_qp_resp_rd_atomic_resc *
+                                          p_hwfn->p_rdma_info->num_qps;
+       dev->page_size_caps = QED_RDMA_PAGE_SIZE_CAPS;
+       dev->dev_ack_delay = QED_RDMA_ACK_DELAY;
+       dev->max_pd = RDMA_MAX_PDS;
+       dev->max_ah = p_hwfn->p_rdma_info->num_qps;
+       dev->max_stats_queues = (u8)RESC_NUM(p_hwfn, QED_RDMA_STATS_QUEUE);
+
+       /* Set capablities */
+       dev->dev_caps = 0;
+       SET_FIELD(dev->dev_caps, QED_RDMA_DEV_CAP_RNR_NAK, 1);
+       SET_FIELD(dev->dev_caps, QED_RDMA_DEV_CAP_PORT_ACTIVE_EVENT, 1);
+       SET_FIELD(dev->dev_caps, QED_RDMA_DEV_CAP_PORT_CHANGE_EVENT, 1);
+       SET_FIELD(dev->dev_caps, QED_RDMA_DEV_CAP_RESIZE_CQ, 1);
+       SET_FIELD(dev->dev_caps, QED_RDMA_DEV_CAP_BASE_MEMORY_EXT, 1);
+       SET_FIELD(dev->dev_caps, QED_RDMA_DEV_CAP_BASE_QUEUE_EXT, 1);
+       SET_FIELD(dev->dev_caps, QED_RDMA_DEV_CAP_ZBVA, 1);
+       SET_FIELD(dev->dev_caps, QED_RDMA_DEV_CAP_LOCAL_INV_FENCE, 1);
+
+       /* Check atomic operations support in PCI configuration space. */
+       pci_read_config_dword(cdev->pdev,
+                             cdev->pdev->pcie_cap + PCI_EXP_DEVCTL2,
+                             &pci_status_control);
+
+       if (pci_status_control & PCI_EXP_DEVCTL2_LTR_EN)
+               SET_FIELD(dev->dev_caps, QED_RDMA_DEV_CAP_ATOMIC_OP, 1);
+}
+
+static void qed_rdma_init_port(struct qed_hwfn *p_hwfn)
+{
+       struct qed_rdma_port *port = p_hwfn->p_rdma_info->port;
+       struct qed_rdma_device *dev = p_hwfn->p_rdma_info->dev;
+
+       port->port_state = p_hwfn->mcp_info->link_output.link_up ?
+                          QED_RDMA_PORT_UP : QED_RDMA_PORT_DOWN;
+
+       port->max_msg_size = min_t(u64,
+                                  (dev->max_mr_mw_fmr_size *
+                                   p_hwfn->cdev->rdma_max_sge),
+                                  BIT(31));
+
+       port->pkey_bad_counter = 0;
+}
+
+static int qed_rdma_init_hw(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
+{
+       u32 ll2_ethertype_en;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Initializing HW\n");
+       p_hwfn->b_rdma_enabled_in_prs = false;
+
+       qed_wr(p_hwfn, p_ptt, PRS_REG_ROCE_DEST_QP_MAX_PF, 0);
+
+       p_hwfn->rdma_prs_search_reg = PRS_REG_SEARCH_ROCE;
+
+       /* We delay writing to this reg until first cid is allocated. See
+        * qed_cxt_dynamic_ilt_alloc function for more details
+        */
+       ll2_ethertype_en = qed_rd(p_hwfn, p_ptt, PRS_REG_LIGHT_L2_ETHERTYPE_EN);
+       qed_wr(p_hwfn, p_ptt, PRS_REG_LIGHT_L2_ETHERTYPE_EN,
+              (ll2_ethertype_en | 0x01));
+
+       if (qed_cxt_get_proto_cid_start(p_hwfn, PROTOCOLID_ROCE) % 2) {
+               DP_NOTICE(p_hwfn, "The first RoCE's cid should be even\n");
+               return -EINVAL;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Initializing HW - Done\n");
+       return 0;
+}
+
+static int qed_rdma_start_fw(struct qed_hwfn *p_hwfn,
+                            struct qed_rdma_start_in_params *params,
+                            struct qed_ptt *p_ptt)
+{
+       struct rdma_init_func_ramrod_data *p_ramrod;
+       struct qed_rdma_cnq_params *p_cnq_pbl_list;
+       struct rdma_init_func_hdr *p_params_header;
+       struct rdma_cnq_params *p_cnq_params;
+       struct qed_sp_init_data init_data;
+       struct qed_spq_entry *p_ent;
+       u32 cnq_id, sb_id;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Starting FW\n");
+
+       /* Save the number of cnqs for the function close ramrod */
+       p_hwfn->p_rdma_info->num_cnqs = params->desired_cnq;
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent, RDMA_RAMROD_FUNC_INIT,
+                                p_hwfn->p_rdma_info->proto, &init_data);
+       if (rc)
+               return rc;
+
+       p_ramrod = &p_ent->ramrod.roce_init_func.rdma;
+
+       p_params_header = &p_ramrod->params_header;
+       p_params_header->cnq_start_offset = (u8)RESC_START(p_hwfn,
+                                                          QED_RDMA_CNQ_RAM);
+       p_params_header->num_cnqs = params->desired_cnq;
+
+       if (params->cq_mode == QED_RDMA_CQ_MODE_16_BITS)
+               p_params_header->cq_ring_mode = 1;
+       else
+               p_params_header->cq_ring_mode = 0;
+
+       for (cnq_id = 0; cnq_id < params->desired_cnq; cnq_id++) {
+               sb_id = qed_rdma_get_sb_id(p_hwfn, cnq_id);
+               p_cnq_params = &p_ramrod->cnq_params[cnq_id];
+               p_cnq_pbl_list = &params->cnq_pbl_list[cnq_id];
+               p_cnq_params->sb_num =
+                       cpu_to_le16(p_hwfn->sbs_info[sb_id]->igu_sb_id);
+
+               p_cnq_params->sb_index = p_hwfn->pf_params.rdma_pf_params.gl_pi;
+               p_cnq_params->num_pbl_pages = p_cnq_pbl_list->num_pbl_pages;
+
+               DMA_REGPAIR_LE(p_cnq_params->pbl_base_addr,
+                              p_cnq_pbl_list->pbl_ptr);
+
+               /* we assume here that cnq_id and qz_offset are the same */
+               p_cnq_params->queue_zone_num =
+                       cpu_to_le16(p_hwfn->p_rdma_info->queue_zone_base +
+                                   cnq_id);
+       }
+
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
+
+static int qed_rdma_reserve_lkey(struct qed_hwfn *p_hwfn)
+{
+       struct qed_rdma_device *dev = p_hwfn->p_rdma_info->dev;
+
+       /* The first DPI is reserved for the Kernel */
+       __set_bit(0, p_hwfn->p_rdma_info->dpi_map.bitmap);
+
+       /* Tid 0 will be used as the key for "reserved MR".
+        * The driver should allocate memory for it so it can be loaded but no
+        * ramrod should be passed on it.
+        */
+       qed_rdma_alloc_tid(p_hwfn, &dev->reserved_lkey);
+       if (dev->reserved_lkey != RDMA_RESERVED_LKEY) {
+               DP_NOTICE(p_hwfn,
+                         "Reserved lkey should be equal to RDMA_RESERVED_LKEY\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int qed_rdma_setup(struct qed_hwfn *p_hwfn,
+                         struct qed_ptt *p_ptt,
+                         struct qed_rdma_start_in_params *params)
+{
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "RDMA setup\n");
+
+       spin_lock_init(&p_hwfn->p_rdma_info->lock);
+
+       qed_rdma_init_devinfo(p_hwfn, params);
+       qed_rdma_init_port(p_hwfn);
+       qed_rdma_init_events(p_hwfn, params);
+
+       rc = qed_rdma_reserve_lkey(p_hwfn);
+       if (rc)
+               return rc;
+
+       rc = qed_rdma_init_hw(p_hwfn, p_ptt);
+       if (rc)
+               return rc;
+
+       return qed_rdma_start_fw(p_hwfn, params, p_ptt);
+}
+
+int qed_rdma_stop(void *rdma_cxt)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       struct rdma_close_func_ramrod_data *p_ramrod;
+       struct qed_sp_init_data init_data;
+       struct qed_spq_entry *p_ent;
+       struct qed_ptt *p_ptt;
+       u32 ll2_ethertype_en;
+       int rc = -EBUSY;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "RDMA stop\n");
+
+       p_ptt = qed_ptt_acquire(p_hwfn);
+       if (!p_ptt) {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Failed to acquire PTT\n");
+               return rc;
+       }
+
+       /* Disable RoCE search */
+       qed_wr(p_hwfn, p_ptt, p_hwfn->rdma_prs_search_reg, 0);
+       p_hwfn->b_rdma_enabled_in_prs = false;
+
+       qed_wr(p_hwfn, p_ptt, PRS_REG_ROCE_DEST_QP_MAX_PF, 0);
+
+       ll2_ethertype_en = qed_rd(p_hwfn, p_ptt, PRS_REG_LIGHT_L2_ETHERTYPE_EN);
+
+       qed_wr(p_hwfn, p_ptt, PRS_REG_LIGHT_L2_ETHERTYPE_EN,
+              (ll2_ethertype_en & 0xFFFE));
+
+       qed_ptt_release(p_hwfn, p_ptt);
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       /* Stop RoCE */
+       rc = qed_sp_init_request(p_hwfn, &p_ent, RDMA_RAMROD_FUNC_CLOSE,
+                                p_hwfn->p_rdma_info->proto, &init_data);
+       if (rc)
+               goto out;
+
+       p_ramrod = &p_ent->ramrod.rdma_close_func;
+
+       p_ramrod->num_cnqs = p_hwfn->p_rdma_info->num_cnqs;
+       p_ramrod->cnq_start_offset = (u8)RESC_START(p_hwfn, QED_RDMA_CNQ_RAM);
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+
+out:
+       qed_rdma_free(p_hwfn);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "RDMA stop done, rc = %d\n", rc);
+       return rc;
+}
+
+int qed_rdma_add_user(void *rdma_cxt,
+                     struct qed_rdma_add_user_out_params *out_params)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       u32 dpi_start_offset;
+       u32 returned_id = 0;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Adding User\n");
+
+       /* Allocate DPI */
+       spin_lock_bh(&p_hwfn->p_rdma_info->lock);
+       rc = qed_rdma_bmap_alloc_id(p_hwfn, &p_hwfn->p_rdma_info->dpi_map,
+                                   &returned_id);
+       spin_unlock_bh(&p_hwfn->p_rdma_info->lock);
+
+       out_params->dpi = (u16)returned_id;
+
+       /* Calculate the corresponding DPI address */
+       dpi_start_offset = p_hwfn->dpi_start_offset;
+
+       out_params->dpi_addr = (u64)((u8 __iomem *)p_hwfn->doorbells +
+                                    dpi_start_offset +
+                                    ((out_params->dpi) * p_hwfn->dpi_size));
+
+       out_params->dpi_phys_addr = p_hwfn->cdev->db_phys_addr +
+                                   dpi_start_offset +
+                                   ((out_params->dpi) * p_hwfn->dpi_size);
+
+       out_params->dpi_size = p_hwfn->dpi_size;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Adding user - done, rc = %d\n", rc);
+       return rc;
+}
+
+struct qed_rdma_port *qed_rdma_query_port(void *rdma_cxt)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       struct qed_rdma_port *p_port = p_hwfn->p_rdma_info->port;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "RDMA Query port\n");
+
+       /* Link may have changed */
+       p_port->port_state = p_hwfn->mcp_info->link_output.link_up ?
+                            QED_RDMA_PORT_UP : QED_RDMA_PORT_DOWN;
+
+       p_port->link_speed = p_hwfn->mcp_info->link_output.speed;
+
+       return p_port;
+}
+
+struct qed_rdma_device *qed_rdma_query_device(void *rdma_cxt)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Query device\n");
+
+       /* Return struct with device parameters */
+       return p_hwfn->p_rdma_info->dev;
+}
+
+void qed_rdma_free_tid(void *rdma_cxt, u32 itid)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "itid = %08x\n", itid);
+
+       spin_lock_bh(&p_hwfn->p_rdma_info->lock);
+       qed_bmap_release_id(p_hwfn, &p_hwfn->p_rdma_info->tid_map, itid);
+       spin_unlock_bh(&p_hwfn->p_rdma_info->lock);
+}
+
+int qed_rdma_alloc_tid(void *rdma_cxt, u32 *itid)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocate TID\n");
+
+       spin_lock_bh(&p_hwfn->p_rdma_info->lock);
+       rc = qed_rdma_bmap_alloc_id(p_hwfn,
+                                   &p_hwfn->p_rdma_info->tid_map, itid);
+       spin_unlock_bh(&p_hwfn->p_rdma_info->lock);
+       if (rc)
+               goto out;
+
+       rc = qed_cxt_dynamic_ilt_alloc(p_hwfn, QED_ELEM_TASK, *itid);
+out:
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocate TID - done, rc = %d\n", rc);
+       return rc;
+}
+
+void qed_rdma_cnq_prod_update(void *rdma_cxt, u8 qz_offset, u16 prod)
+{
+       struct qed_hwfn *p_hwfn;
+       u16 qz_num;
+       u32 addr;
+
+       p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       qz_num = p_hwfn->p_rdma_info->queue_zone_base + qz_offset;
+       addr = GTT_BAR0_MAP_REG_USDM_RAM +
+              USTORM_COMMON_QUEUE_CONS_OFFSET(qz_num);
+
+       REG_WR16(p_hwfn, addr, prod);
+
+       /* keep prod updates ordered */
+       wmb();
+}
+
+static int qed_fill_rdma_dev_info(struct qed_dev *cdev,
+                                 struct qed_dev_rdma_info *info)
+{
+       memset(info, 0, sizeof(*info));
+
+       info->rdma_type = QED_RDMA_TYPE_ROCE;
+
+       qed_fill_dev_info(cdev, &info->common);
+
+       return 0;
+}
+
+static int qed_rdma_get_sb_start(struct qed_dev *cdev)
+{
+       int feat_num;
+
+       if (cdev->num_hwfns > 1)
+               feat_num = FEAT_NUM(QED_LEADING_HWFN(cdev), QED_PF_L2_QUE);
+       else
+               feat_num = FEAT_NUM(QED_LEADING_HWFN(cdev), QED_PF_L2_QUE) *
+                          cdev->num_hwfns;
+
+       return feat_num;
+}
+
+static int qed_rdma_get_min_cnq_msix(struct qed_dev *cdev)
+{
+       int n_cnq = FEAT_NUM(QED_LEADING_HWFN(cdev), QED_RDMA_CNQ);
+       int n_msix = cdev->int_params.rdma_msix_cnt;
+
+       return min_t(int, n_cnq, n_msix);
+}
+
+static int qed_rdma_set_int(struct qed_dev *cdev, u16 cnt)
+{
+       int limit = 0;
+
+       /* Mark the fastpath as free/used */
+       cdev->int_params.fp_initialized = cnt ? true : false;
+
+       if (cdev->int_params.out.int_mode != QED_INT_MODE_MSIX) {
+               DP_ERR(cdev,
+                      "qed roce supports only MSI-X interrupts (detected %d).\n",
+                      cdev->int_params.out.int_mode);
+               return -EINVAL;
+       } else if (cdev->int_params.fp_msix_cnt) {
+               limit = cdev->int_params.rdma_msix_cnt;
+       }
+
+       if (!limit)
+               return -ENOMEM;
+
+       return min_t(int, cnt, limit);
+}
+
+static int qed_rdma_get_int(struct qed_dev *cdev, struct qed_int_info *info)
+{
+       memset(info, 0, sizeof(*info));
+
+       if (!cdev->int_params.fp_initialized) {
+               DP_INFO(cdev,
+                       "Protocol driver requested interrupt information, but its support is not yet configured\n");
+               return -EINVAL;
+       }
+
+       if (cdev->int_params.out.int_mode == QED_INT_MODE_MSIX) {
+               int msix_base = cdev->int_params.rdma_msix_base;
+
+               info->msix_cnt = cdev->int_params.rdma_msix_cnt;
+               info->msix = &cdev->int_params.msix_table[msix_base];
+
+               DP_VERBOSE(cdev, QED_MSG_RDMA, "msix_cnt = %d msix_base=%d\n",
+                          info->msix_cnt, msix_base);
+       }
+
+       return 0;
+}
+
+int qed_rdma_alloc_pd(void *rdma_cxt, u16 *pd)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       u32 returned_id;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Alloc PD\n");
+
+       /* Allocates an unused protection domain */
+       spin_lock_bh(&p_hwfn->p_rdma_info->lock);
+       rc = qed_rdma_bmap_alloc_id(p_hwfn,
+                                   &p_hwfn->p_rdma_info->pd_map, &returned_id);
+       spin_unlock_bh(&p_hwfn->p_rdma_info->lock);
+
+       *pd = (u16)returned_id;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Alloc PD - done, rc = %d\n", rc);
+       return rc;
+}
+
+void qed_rdma_free_pd(void *rdma_cxt, u16 pd)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "pd = %08x\n", pd);
+
+       /* Returns a previously allocated protection domain for reuse */
+       spin_lock_bh(&p_hwfn->p_rdma_info->lock);
+       qed_bmap_release_id(p_hwfn, &p_hwfn->p_rdma_info->pd_map, pd);
+       spin_unlock_bh(&p_hwfn->p_rdma_info->lock);
+}
+
+static enum qed_rdma_toggle_bit
+qed_rdma_toggle_bit_create_resize_cq(struct qed_hwfn *p_hwfn, u16 icid)
+{
+       struct qed_rdma_info *p_info = p_hwfn->p_rdma_info;
+       enum qed_rdma_toggle_bit toggle_bit;
+       u32 bmap_id;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", icid);
+
+       /* the function toggle the bit that is related to a given icid
+        * and returns the new toggle bit's value
+        */
+       bmap_id = icid - qed_cxt_get_proto_cid_start(p_hwfn, p_info->proto);
+
+       spin_lock_bh(&p_info->lock);
+       toggle_bit = !test_and_change_bit(bmap_id,
+                                         p_info->toggle_bits.bitmap);
+       spin_unlock_bh(&p_info->lock);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "QED_RDMA_TOGGLE_BIT_= %d\n",
+                  toggle_bit);
+
+       return toggle_bit;
+}
+
+int qed_rdma_create_cq(void *rdma_cxt,
+                      struct qed_rdma_create_cq_in_params *params, u16 *icid)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       struct qed_rdma_info *p_info = p_hwfn->p_rdma_info;
+       struct rdma_create_cq_ramrod_data *p_ramrod;
+       enum qed_rdma_toggle_bit toggle_bit;
+       struct qed_sp_init_data init_data;
+       struct qed_spq_entry *p_ent;
+       u32 returned_id, start_cid;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "cq_handle = %08x%08x\n",
+                  params->cq_handle_hi, params->cq_handle_lo);
+
+       /* Allocate icid */
+       spin_lock_bh(&p_info->lock);
+       rc = qed_rdma_bmap_alloc_id(p_hwfn,
+                                   &p_info->cq_map, &returned_id);
+       spin_unlock_bh(&p_info->lock);
+
+       if (rc) {
+               DP_NOTICE(p_hwfn, "Can't create CQ, rc = %d\n", rc);
+               return rc;
+       }
+
+       start_cid = qed_cxt_get_proto_cid_start(p_hwfn,
+                                               p_info->proto);
+       *icid = returned_id + start_cid;
+
+       /* Check if icid requires a page allocation */
+       rc = qed_cxt_dynamic_ilt_alloc(p_hwfn, QED_ELEM_CXT, *icid);
+       if (rc)
+               goto err;
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = *icid;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       /* Send create CQ ramrod */
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                RDMA_RAMROD_CREATE_CQ,
+                                p_info->proto, &init_data);
+       if (rc)
+               goto err;
+
+       p_ramrod = &p_ent->ramrod.rdma_create_cq;
+
+       p_ramrod->cq_handle.hi = cpu_to_le32(params->cq_handle_hi);
+       p_ramrod->cq_handle.lo = cpu_to_le32(params->cq_handle_lo);
+       p_ramrod->dpi = cpu_to_le16(params->dpi);
+       p_ramrod->is_two_level_pbl = params->pbl_two_level;
+       p_ramrod->max_cqes = cpu_to_le32(params->cq_size);
+       DMA_REGPAIR_LE(p_ramrod->pbl_addr, params->pbl_ptr);
+       p_ramrod->pbl_num_pages = cpu_to_le16(params->pbl_num_pages);
+       p_ramrod->cnq_id = (u8)RESC_START(p_hwfn, QED_RDMA_CNQ_RAM) +
+                          params->cnq_id;
+       p_ramrod->int_timeout = params->int_timeout;
+
+       /* toggle the bit for every resize or create cq for a given icid */
+       toggle_bit = qed_rdma_toggle_bit_create_resize_cq(p_hwfn, *icid);
+
+       p_ramrod->toggle_bit = toggle_bit;
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+       if (rc) {
+               /* restore toggle bit */
+               qed_rdma_toggle_bit_create_resize_cq(p_hwfn, *icid);
+               goto err;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Created CQ, rc = %d\n", rc);
+       return rc;
+
+err:
+       /* release allocated icid */
+       qed_bmap_release_id(p_hwfn, &p_info->cq_map, returned_id);
+       DP_NOTICE(p_hwfn, "Create CQ failed, rc = %d\n", rc);
+
+       return rc;
+}
+
+int qed_rdma_resize_cq(void *rdma_cxt,
+                      struct qed_rdma_resize_cq_in_params *in_params,
+                      struct qed_rdma_resize_cq_out_params *out_params)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       struct rdma_resize_cq_output_params *p_ramrod_res;
+       struct rdma_resize_cq_ramrod_data *p_ramrod;
+       enum qed_rdma_toggle_bit toggle_bit;
+       struct qed_sp_init_data init_data;
+       struct qed_spq_entry *p_ent;
+       dma_addr_t ramrod_res_phys;
+       u8 fw_return_code;
+       int rc = -ENOMEM;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", in_params->icid);
+
+       p_ramrod_res =
+           (struct rdma_resize_cq_output_params *)
+           dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                              sizeof(struct rdma_resize_cq_output_params),
+                              &ramrod_res_phys, GFP_KERNEL);
+       if (!p_ramrod_res) {
+               DP_NOTICE(p_hwfn,
+                         "qed resize cq failed: cannot allocate memory (ramrod)\n");
+               return rc;
+       }
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = in_params->icid;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                RDMA_RAMROD_RESIZE_CQ,
+                                p_hwfn->p_rdma_info->proto, &init_data);
+       if (rc)
+               goto err;
+
+       p_ramrod = &p_ent->ramrod.rdma_resize_cq;
+
+       p_ramrod->flags = 0;
+
+       /* toggle the bit for every resize or create cq for a given icid */
+       toggle_bit = qed_rdma_toggle_bit_create_resize_cq(p_hwfn,
+                                                         in_params->icid);
+
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_RESIZE_CQ_RAMROD_DATA_TOGGLE_BIT, toggle_bit);
+
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_RESIZE_CQ_RAMROD_DATA_IS_TWO_LEVEL_PBL,
+                 in_params->pbl_two_level);
+
+       p_ramrod->pbl_log_page_size = in_params->pbl_page_size_log - 12;
+       p_ramrod->pbl_num_pages = cpu_to_le16(in_params->pbl_num_pages);
+       p_ramrod->max_cqes = cpu_to_le32(in_params->cq_size);
+       DMA_REGPAIR_LE(p_ramrod->pbl_addr, in_params->pbl_ptr);
+       DMA_REGPAIR_LE(p_ramrod->output_params_addr, ramrod_res_phys);
+
+       rc = qed_spq_post(p_hwfn, p_ent, &fw_return_code);
+       if (rc)
+               goto err;
+
+       if (fw_return_code != RDMA_RETURN_OK) {
+               DP_NOTICE(p_hwfn, "fw_return_code = %d\n", fw_return_code);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       out_params->prod = le32_to_cpu(p_ramrod_res->old_cq_prod);
+       out_params->cons = le32_to_cpu(p_ramrod_res->old_cq_cons);
+
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                         sizeof(struct rdma_resize_cq_output_params),
+                         p_ramrod_res, ramrod_res_phys);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Resized CQ, rc = %d\n", rc);
+
+       return rc;
+
+err:   dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                         sizeof(struct rdma_resize_cq_output_params),
+                         p_ramrod_res, ramrod_res_phys);
+       DP_NOTICE(p_hwfn, "Resized CQ, Failed - rc = %d\n", rc);
+
+       return rc;
+}
+
+int qed_rdma_destroy_cq(void *rdma_cxt,
+                       struct qed_rdma_destroy_cq_in_params *in_params,
+                       struct qed_rdma_destroy_cq_out_params *out_params)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       struct rdma_destroy_cq_output_params *p_ramrod_res;
+       struct rdma_destroy_cq_ramrod_data *p_ramrod;
+       struct qed_sp_init_data init_data;
+       struct qed_spq_entry *p_ent;
+       dma_addr_t ramrod_res_phys;
+       int rc = -ENOMEM;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", in_params->icid);
+
+       p_ramrod_res =
+           (struct rdma_destroy_cq_output_params *)
+           dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                              sizeof(struct rdma_destroy_cq_output_params),
+                              &ramrod_res_phys, GFP_KERNEL);
+       if (!p_ramrod_res) {
+               DP_NOTICE(p_hwfn,
+                         "qed destroy cq failed: cannot allocate memory (ramrod)\n");
+               return rc;
+       }
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = in_params->icid;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       /* Send destroy CQ ramrod */
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                RDMA_RAMROD_DESTROY_CQ,
+                                p_hwfn->p_rdma_info->proto, &init_data);
+       if (rc)
+               goto err;
+
+       p_ramrod = &p_ent->ramrod.rdma_destroy_cq;
+       DMA_REGPAIR_LE(p_ramrod->output_params_addr, ramrod_res_phys);
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+       if (rc)
+               goto err;
+
+       out_params->num_cq_notif = le16_to_cpu(p_ramrod_res->cnq_num);
+
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                         sizeof(struct rdma_destroy_cq_output_params),
+                         p_ramrod_res, ramrod_res_phys);
+
+       /* Free icid */
+       spin_lock_bh(&p_hwfn->p_rdma_info->lock);
+
+       qed_bmap_release_id(p_hwfn,
+                           &p_hwfn->p_rdma_info->cq_map,
+                           (in_params->icid -
+                            qed_cxt_get_proto_cid_start(p_hwfn,
+                                                        p_hwfn->
+                                                        p_rdma_info->proto)));
+
+       spin_unlock_bh(&p_hwfn->p_rdma_info->lock);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Destroyed CQ, rc = %d\n", rc);
+       return rc;
+
+err:   dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                         sizeof(struct rdma_destroy_cq_output_params),
+                         p_ramrod_res, ramrod_res_phys);
+
+       return rc;
+}
+
+static void qed_rdma_set_fw_mac(u16 *p_fw_mac, u8 *p_qed_mac)
+{
+       p_fw_mac[0] = cpu_to_le16((p_qed_mac[0] << 8) + p_qed_mac[1]);
+       p_fw_mac[1] = cpu_to_le16((p_qed_mac[2] << 8) + p_qed_mac[3]);
+       p_fw_mac[2] = cpu_to_le16((p_qed_mac[4] << 8) + p_qed_mac[5]);
+}
+
+static void qed_rdma_copy_gids(struct qed_rdma_qp *qp, __le32 *src_gid,
+                              __le32 *dst_gid)
+{
+       u32 i;
+
+       if (qp->roce_mode == ROCE_V2_IPV4) {
+               /* The IPv4 addresses shall be aligned to the highest word.
+                * The lower words must be zero.
+                */
+               memset(src_gid, 0, sizeof(union qed_gid));
+               memset(dst_gid, 0, sizeof(union qed_gid));
+               src_gid[3] = cpu_to_le32(qp->sgid.ipv4_addr);
+               dst_gid[3] = cpu_to_le32(qp->dgid.ipv4_addr);
+       } else {
+               /* GIDs and IPv6 addresses coincide in location and size */
+               for (i = 0; i < ARRAY_SIZE(qp->sgid.dwords); i++) {
+                       src_gid[i] = cpu_to_le32(qp->sgid.dwords[i]);
+                       dst_gid[i] = cpu_to_le32(qp->dgid.dwords[i]);
+               }
+       }
+}
+
+static enum roce_flavor qed_roce_mode_to_flavor(enum roce_mode roce_mode)
+{
+       enum roce_flavor flavor;
+
+       switch (roce_mode) {
+       case ROCE_V1:
+               flavor = PLAIN_ROCE;
+               break;
+       case ROCE_V2_IPV4:
+               flavor = RROCE_IPV4;
+               break;
+       case ROCE_V2_IPV6:
+               flavor = ROCE_V2_IPV6;
+               break;
+       default:
+               flavor = MAX_ROCE_MODE;
+               break;
+       }
+       return flavor;
+}
+
+int qed_roce_alloc_cid(struct qed_hwfn *p_hwfn, u16 *cid)
+{
+       struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info;
+       u32 responder_icid;
+       u32 requester_icid;
+       int rc;
+
+       spin_lock_bh(&p_hwfn->p_rdma_info->lock);
+       rc = qed_rdma_bmap_alloc_id(p_hwfn, &p_rdma_info->cid_map,
+                                   &responder_icid);
+       if (rc) {
+               spin_unlock_bh(&p_rdma_info->lock);
+               return rc;
+       }
+
+       rc = qed_rdma_bmap_alloc_id(p_hwfn, &p_rdma_info->cid_map,
+                                   &requester_icid);
+
+       spin_unlock_bh(&p_rdma_info->lock);
+       if (rc)
+               goto err;
+
+       /* the two icid's should be adjacent */
+       if ((requester_icid - responder_icid) != 1) {
+               DP_NOTICE(p_hwfn, "Failed to allocate two adjacent qp's'\n");
+               rc = -EINVAL;
+               goto err;
+       }
+
+       responder_icid += qed_cxt_get_proto_cid_start(p_hwfn,
+                                                     p_rdma_info->proto);
+       requester_icid += qed_cxt_get_proto_cid_start(p_hwfn,
+                                                     p_rdma_info->proto);
+
+       /* If these icids require a new ILT line allocate DMA-able context for
+        * an ILT page
+        */
+       rc = qed_cxt_dynamic_ilt_alloc(p_hwfn, QED_ELEM_CXT, responder_icid);
+       if (rc)
+               goto err;
+
+       rc = qed_cxt_dynamic_ilt_alloc(p_hwfn, QED_ELEM_CXT, requester_icid);
+       if (rc)
+               goto err;
+
+       *cid = (u16)responder_icid;
+       return rc;
+
+err:
+       spin_lock_bh(&p_rdma_info->lock);
+       qed_bmap_release_id(p_hwfn, &p_rdma_info->cid_map, responder_icid);
+       qed_bmap_release_id(p_hwfn, &p_rdma_info->cid_map, requester_icid);
+
+       spin_unlock_bh(&p_rdma_info->lock);
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                  "Allocate CID - failed, rc = %d\n", rc);
+       return rc;
+}
+
+static int qed_roce_sp_create_responder(struct qed_hwfn *p_hwfn,
+                                       struct qed_rdma_qp *qp)
+{
+       struct roce_create_qp_resp_ramrod_data *p_ramrod;
+       struct qed_sp_init_data init_data;
+       union qed_qm_pq_params qm_params;
+       enum roce_flavor roce_flavor;
+       struct qed_spq_entry *p_ent;
+       u16 physical_queue0 = 0;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", qp->icid);
+
+       /* Allocate DMA-able memory for IRQ */
+       qp->irq_num_pages = 1;
+       qp->irq = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                    RDMA_RING_PAGE_SIZE,
+                                    &qp->irq_phys_addr, GFP_KERNEL);
+       if (!qp->irq) {
+               rc = -ENOMEM;
+               DP_NOTICE(p_hwfn,
+                         "qed create responder failed: cannot allocate memory (irq). rc = %d\n",
+                         rc);
+               return rc;
+       }
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = qp->icid;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent, ROCE_RAMROD_CREATE_QP,
+                                PROTOCOLID_ROCE, &init_data);
+       if (rc)
+               goto err;
+
+       p_ramrod = &p_ent->ramrod.roce_create_qp_resp;
+
+       p_ramrod->flags = 0;
+
+       roce_flavor = qed_roce_mode_to_flavor(qp->roce_mode);
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_RESP_RAMROD_DATA_ROCE_FLAVOR, roce_flavor);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_RESP_RAMROD_DATA_RDMA_RD_EN,
+                 qp->incoming_rdma_read_en);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_RESP_RAMROD_DATA_RDMA_WR_EN,
+                 qp->incoming_rdma_write_en);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_RESP_RAMROD_DATA_ATOMIC_EN,
+                 qp->incoming_atomic_en);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_RESP_RAMROD_DATA_E2E_FLOW_CONTROL_EN,
+                 qp->e2e_flow_control_en);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_RESP_RAMROD_DATA_SRQ_FLG, qp->use_srq);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_RESP_RAMROD_DATA_RESERVED_KEY_EN,
+                 qp->fmr_and_reserved_lkey);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_RESP_RAMROD_DATA_MIN_RNR_NAK_TIMER,
+                 qp->min_rnr_nak_timer);
+
+       p_ramrod->max_ird = qp->max_rd_atomic_resp;
+       p_ramrod->traffic_class = qp->traffic_class_tos;
+       p_ramrod->hop_limit = qp->hop_limit_ttl;
+       p_ramrod->irq_num_pages = qp->irq_num_pages;
+       p_ramrod->p_key = cpu_to_le16(qp->pkey);
+       p_ramrod->flow_label = cpu_to_le32(qp->flow_label);
+       p_ramrod->dst_qp_id = cpu_to_le32(qp->dest_qp);
+       p_ramrod->mtu = cpu_to_le16(qp->mtu);
+       p_ramrod->initial_psn = cpu_to_le32(qp->rq_psn);
+       p_ramrod->pd = cpu_to_le16(qp->pd);
+       p_ramrod->rq_num_pages = cpu_to_le16(qp->rq_num_pages);
+       DMA_REGPAIR_LE(p_ramrod->rq_pbl_addr, qp->rq_pbl_ptr);
+       DMA_REGPAIR_LE(p_ramrod->irq_pbl_addr, qp->irq_phys_addr);
+       qed_rdma_copy_gids(qp, p_ramrod->src_gid, p_ramrod->dst_gid);
+       p_ramrod->qp_handle_for_async.hi = cpu_to_le32(qp->qp_handle_async.hi);
+       p_ramrod->qp_handle_for_async.lo = cpu_to_le32(qp->qp_handle_async.lo);
+       p_ramrod->qp_handle_for_cqe.hi = cpu_to_le32(qp->qp_handle.hi);
+       p_ramrod->qp_handle_for_cqe.lo = cpu_to_le32(qp->qp_handle.lo);
+       p_ramrod->stats_counter_id = p_hwfn->rel_pf_id;
+       p_ramrod->cq_cid = cpu_to_le32((p_hwfn->hw_info.opaque_fid << 16) |
+                                      qp->rq_cq_id);
+
+       memset(&qm_params, 0, sizeof(qm_params));
+       qm_params.roce.qpid = qp->icid >> 1;
+       physical_queue0 = qed_get_qm_pq(p_hwfn, PROTOCOLID_ROCE, &qm_params);
+
+       p_ramrod->physical_queue0 = cpu_to_le16(physical_queue0);
+       p_ramrod->dpi = cpu_to_le16(qp->dpi);
+
+       qed_rdma_set_fw_mac(p_ramrod->remote_mac_addr, qp->remote_mac_addr);
+       qed_rdma_set_fw_mac(p_ramrod->local_mac_addr, qp->local_mac_addr);
+
+       p_ramrod->udp_src_port = qp->udp_src_port;
+       p_ramrod->vlan_id = cpu_to_le16(qp->vlan_id);
+       p_ramrod->srq_id.srq_idx = cpu_to_le16(qp->srq_id);
+       p_ramrod->srq_id.opaque_fid = cpu_to_le16(p_hwfn->hw_info.opaque_fid);
+
+       p_ramrod->stats_counter_id = RESC_START(p_hwfn, QED_RDMA_STATS_QUEUE) +
+                                    qp->stats_queue;
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "rc = %d physical_queue0 = 0x%x\n",
+                  rc, physical_queue0);
+
+       if (rc)
+               goto err;
+
+       qp->resp_offloaded = true;
+
+       return rc;
+
+err:
+       DP_NOTICE(p_hwfn, "create responder - failed, rc = %d\n", rc);
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                         qp->irq_num_pages * RDMA_RING_PAGE_SIZE,
+                         qp->irq, qp->irq_phys_addr);
+
+       return rc;
+}
+
+static int qed_roce_sp_create_requester(struct qed_hwfn *p_hwfn,
+                                       struct qed_rdma_qp *qp)
+{
+       struct roce_create_qp_req_ramrod_data *p_ramrod;
+       struct qed_sp_init_data init_data;
+       union qed_qm_pq_params qm_params;
+       enum roce_flavor roce_flavor;
+       struct qed_spq_entry *p_ent;
+       u16 physical_queue0 = 0;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", qp->icid);
+
+       /* Allocate DMA-able memory for ORQ */
+       qp->orq_num_pages = 1;
+       qp->orq = dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                    RDMA_RING_PAGE_SIZE,
+                                    &qp->orq_phys_addr, GFP_KERNEL);
+       if (!qp->orq) {
+               rc = -ENOMEM;
+               DP_NOTICE(p_hwfn,
+                         "qed create requester failed: cannot allocate memory (orq). rc = %d\n",
+                         rc);
+               return rc;
+       }
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = qp->icid + 1;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                ROCE_RAMROD_CREATE_QP,
+                                PROTOCOLID_ROCE, &init_data);
+       if (rc)
+               goto err;
+
+       p_ramrod = &p_ent->ramrod.roce_create_qp_req;
+
+       p_ramrod->flags = 0;
+
+       roce_flavor = qed_roce_mode_to_flavor(qp->roce_mode);
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_REQ_RAMROD_DATA_ROCE_FLAVOR, roce_flavor);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_REQ_RAMROD_DATA_FMR_AND_RESERVED_EN,
+                 qp->fmr_and_reserved_lkey);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_REQ_RAMROD_DATA_SIGNALED_COMP, qp->signal_all);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_REQ_RAMROD_DATA_ERR_RETRY_CNT, qp->retry_cnt);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_REQ_RAMROD_DATA_RNR_NAK_CNT,
+                 qp->rnr_retry_cnt);
+
+       p_ramrod->max_ord = qp->max_rd_atomic_req;
+       p_ramrod->traffic_class = qp->traffic_class_tos;
+       p_ramrod->hop_limit = qp->hop_limit_ttl;
+       p_ramrod->orq_num_pages = qp->orq_num_pages;
+       p_ramrod->p_key = cpu_to_le16(qp->pkey);
+       p_ramrod->flow_label = cpu_to_le32(qp->flow_label);
+       p_ramrod->dst_qp_id = cpu_to_le32(qp->dest_qp);
+       p_ramrod->ack_timeout_val = cpu_to_le32(qp->ack_timeout);
+       p_ramrod->mtu = cpu_to_le16(qp->mtu);
+       p_ramrod->initial_psn = cpu_to_le32(qp->sq_psn);
+       p_ramrod->pd = cpu_to_le16(qp->pd);
+       p_ramrod->sq_num_pages = cpu_to_le16(qp->sq_num_pages);
+       DMA_REGPAIR_LE(p_ramrod->sq_pbl_addr, qp->sq_pbl_ptr);
+       DMA_REGPAIR_LE(p_ramrod->orq_pbl_addr, qp->orq_phys_addr);
+       qed_rdma_copy_gids(qp, p_ramrod->src_gid, p_ramrod->dst_gid);
+       p_ramrod->qp_handle_for_async.hi = cpu_to_le32(qp->qp_handle_async.hi);
+       p_ramrod->qp_handle_for_async.lo = cpu_to_le32(qp->qp_handle_async.lo);
+       p_ramrod->qp_handle_for_cqe.hi = cpu_to_le32(qp->qp_handle.hi);
+       p_ramrod->qp_handle_for_cqe.lo = cpu_to_le32(qp->qp_handle.lo);
+       p_ramrod->stats_counter_id = p_hwfn->rel_pf_id;
+       p_ramrod->cq_cid = cpu_to_le32((p_hwfn->hw_info.opaque_fid << 16) |
+                                      qp->sq_cq_id);
+
+       memset(&qm_params, 0, sizeof(qm_params));
+       qm_params.roce.qpid = qp->icid >> 1;
+       physical_queue0 = qed_get_qm_pq(p_hwfn, PROTOCOLID_ROCE, &qm_params);
+
+       p_ramrod->physical_queue0 = cpu_to_le16(physical_queue0);
+       p_ramrod->dpi = cpu_to_le16(qp->dpi);
+
+       qed_rdma_set_fw_mac(p_ramrod->remote_mac_addr, qp->remote_mac_addr);
+       qed_rdma_set_fw_mac(p_ramrod->local_mac_addr, qp->local_mac_addr);
+
+       p_ramrod->udp_src_port = qp->udp_src_port;
+       p_ramrod->vlan_id = cpu_to_le16(qp->vlan_id);
+       p_ramrod->stats_counter_id = RESC_START(p_hwfn, QED_RDMA_STATS_QUEUE) +
+                                    qp->stats_queue;
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "rc = %d\n", rc);
+
+       if (rc)
+               goto err;
+
+       qp->req_offloaded = true;
+
+       return rc;
+
+err:
+       DP_NOTICE(p_hwfn, "Create requested - failed, rc = %d\n", rc);
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                         qp->orq_num_pages * RDMA_RING_PAGE_SIZE,
+                         qp->orq, qp->orq_phys_addr);
+       return rc;
+}
+
+static int qed_roce_sp_modify_responder(struct qed_hwfn *p_hwfn,
+                                       struct qed_rdma_qp *qp,
+                                       bool move_to_err, u32 modify_flags)
+{
+       struct roce_modify_qp_resp_ramrod_data *p_ramrod;
+       struct qed_sp_init_data init_data;
+       struct qed_spq_entry *p_ent;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", qp->icid);
+
+       if (move_to_err && !qp->resp_offloaded)
+               return 0;
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = qp->icid;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                ROCE_EVENT_MODIFY_QP,
+                                PROTOCOLID_ROCE, &init_data);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "rc = %d\n", rc);
+               return rc;
+       }
+
+       p_ramrod = &p_ent->ramrod.roce_modify_qp_resp;
+
+       p_ramrod->flags = 0;
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_RESP_RAMROD_DATA_MOVE_TO_ERR_FLG, move_to_err);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_RESP_RAMROD_DATA_RDMA_RD_EN,
+                 qp->incoming_rdma_read_en);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_RESP_RAMROD_DATA_RDMA_WR_EN,
+                 qp->incoming_rdma_write_en);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_RESP_RAMROD_DATA_ATOMIC_EN,
+                 qp->incoming_atomic_en);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_CREATE_QP_RESP_RAMROD_DATA_E2E_FLOW_CONTROL_EN,
+                 qp->e2e_flow_control_en);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_RESP_RAMROD_DATA_RDMA_OPS_EN_FLG,
+                 GET_FIELD(modify_flags,
+                           QED_RDMA_MODIFY_QP_VALID_RDMA_OPS_EN));
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_RESP_RAMROD_DATA_P_KEY_FLG,
+                 GET_FIELD(modify_flags, QED_ROCE_MODIFY_QP_VALID_PKEY));
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_RESP_RAMROD_DATA_ADDRESS_VECTOR_FLG,
+                 GET_FIELD(modify_flags,
+                           QED_ROCE_MODIFY_QP_VALID_ADDRESS_VECTOR));
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_RESP_RAMROD_DATA_MAX_IRD_FLG,
+                 GET_FIELD(modify_flags,
+                           QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_RESP));
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_RESP_RAMROD_DATA_MIN_RNR_NAK_TIMER_FLG,
+                 GET_FIELD(modify_flags,
+                           QED_ROCE_MODIFY_QP_VALID_MIN_RNR_NAK_TIMER));
+
+       p_ramrod->fields = 0;
+       SET_FIELD(p_ramrod->fields,
+                 ROCE_MODIFY_QP_RESP_RAMROD_DATA_MIN_RNR_NAK_TIMER,
+                 qp->min_rnr_nak_timer);
+
+       p_ramrod->max_ird = qp->max_rd_atomic_resp;
+       p_ramrod->traffic_class = qp->traffic_class_tos;
+       p_ramrod->hop_limit = qp->hop_limit_ttl;
+       p_ramrod->p_key = cpu_to_le16(qp->pkey);
+       p_ramrod->flow_label = cpu_to_le32(qp->flow_label);
+       p_ramrod->mtu = cpu_to_le16(qp->mtu);
+       qed_rdma_copy_gids(qp, p_ramrod->src_gid, p_ramrod->dst_gid);
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Modify responder, rc = %d\n", rc);
+       return rc;
+}
+
+static int qed_roce_sp_modify_requester(struct qed_hwfn *p_hwfn,
+                                       struct qed_rdma_qp *qp,
+                                       bool move_to_sqd,
+                                       bool move_to_err, u32 modify_flags)
+{
+       struct roce_modify_qp_req_ramrod_data *p_ramrod;
+       struct qed_sp_init_data init_data;
+       struct qed_spq_entry *p_ent;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", qp->icid);
+
+       if (move_to_err && !(qp->req_offloaded))
+               return 0;
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = qp->icid + 1;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                ROCE_EVENT_MODIFY_QP,
+                                PROTOCOLID_ROCE, &init_data);
+       if (rc) {
+               DP_NOTICE(p_hwfn, "rc = %d\n", rc);
+               return rc;
+       }
+
+       p_ramrod = &p_ent->ramrod.roce_modify_qp_req;
+
+       p_ramrod->flags = 0;
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_REQ_RAMROD_DATA_MOVE_TO_ERR_FLG, move_to_err);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_REQ_RAMROD_DATA_MOVE_TO_SQD_FLG, move_to_sqd);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_REQ_RAMROD_DATA_EN_SQD_ASYNC_NOTIFY,
+                 qp->sqd_async);
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_REQ_RAMROD_DATA_P_KEY_FLG,
+                 GET_FIELD(modify_flags, QED_ROCE_MODIFY_QP_VALID_PKEY));
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_REQ_RAMROD_DATA_ADDRESS_VECTOR_FLG,
+                 GET_FIELD(modify_flags,
+                           QED_ROCE_MODIFY_QP_VALID_ADDRESS_VECTOR));
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_REQ_RAMROD_DATA_MAX_ORD_FLG,
+                 GET_FIELD(modify_flags,
+                           QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_REQ));
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_REQ_RAMROD_DATA_RNR_NAK_CNT_FLG,
+                 GET_FIELD(modify_flags,
+                           QED_ROCE_MODIFY_QP_VALID_RNR_RETRY_CNT));
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_REQ_RAMROD_DATA_ERR_RETRY_CNT_FLG,
+                 GET_FIELD(modify_flags, QED_ROCE_MODIFY_QP_VALID_RETRY_CNT));
+
+       SET_FIELD(p_ramrod->flags,
+                 ROCE_MODIFY_QP_REQ_RAMROD_DATA_ACK_TIMEOUT_FLG,
+                 GET_FIELD(modify_flags,
+                           QED_ROCE_MODIFY_QP_VALID_ACK_TIMEOUT));
+
+       p_ramrod->fields = 0;
+       SET_FIELD(p_ramrod->fields,
+                 ROCE_MODIFY_QP_REQ_RAMROD_DATA_ERR_RETRY_CNT, qp->retry_cnt);
+
+       SET_FIELD(p_ramrod->fields,
+                 ROCE_MODIFY_QP_REQ_RAMROD_DATA_RNR_NAK_CNT,
+                 qp->rnr_retry_cnt);
+
+       p_ramrod->max_ord = qp->max_rd_atomic_req;
+       p_ramrod->traffic_class = qp->traffic_class_tos;
+       p_ramrod->hop_limit = qp->hop_limit_ttl;
+       p_ramrod->p_key = cpu_to_le16(qp->pkey);
+       p_ramrod->flow_label = cpu_to_le32(qp->flow_label);
+       p_ramrod->ack_timeout_val = cpu_to_le32(qp->ack_timeout);
+       p_ramrod->mtu = cpu_to_le16(qp->mtu);
+       qed_rdma_copy_gids(qp, p_ramrod->src_gid, p_ramrod->dst_gid);
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Modify requester, rc = %d\n", rc);
+       return rc;
+}
+
+static int qed_roce_sp_destroy_qp_responder(struct qed_hwfn *p_hwfn,
+                                           struct qed_rdma_qp *qp,
+                                           u32 *num_invalidated_mw)
+{
+       struct roce_destroy_qp_resp_output_params *p_ramrod_res;
+       struct roce_destroy_qp_resp_ramrod_data *p_ramrod;
+       struct qed_sp_init_data init_data;
+       struct qed_spq_entry *p_ent;
+       dma_addr_t ramrod_res_phys;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", qp->icid);
+
+       if (!qp->resp_offloaded)
+               return 0;
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = qp->icid;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                ROCE_RAMROD_DESTROY_QP,
+                                PROTOCOLID_ROCE, &init_data);
+       if (rc)
+               return rc;
+
+       p_ramrod = &p_ent->ramrod.roce_destroy_qp_resp;
+
+       p_ramrod_res = (struct roce_destroy_qp_resp_output_params *)
+           dma_alloc_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_ramrod_res),
+                              &ramrod_res_phys, GFP_KERNEL);
+
+       if (!p_ramrod_res) {
+               rc = -ENOMEM;
+               DP_NOTICE(p_hwfn,
+                         "qed destroy responder failed: cannot allocate memory (ramrod). rc = %d\n",
+                         rc);
+               return rc;
+       }
+
+       DMA_REGPAIR_LE(p_ramrod->output_params_addr, ramrod_res_phys);
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+       if (rc)
+               goto err;
+
+       *num_invalidated_mw = le32_to_cpu(p_ramrod_res->num_invalidated_mw);
+
+       /* Free IRQ - only if ramrod succeeded, in case FW is still using it */
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                         qp->irq_num_pages * RDMA_RING_PAGE_SIZE,
+                         qp->irq, qp->irq_phys_addr);
+
+       qp->resp_offloaded = false;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Destroy responder, rc = %d\n", rc);
+
+err:
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                         sizeof(struct roce_destroy_qp_resp_output_params),
+                         p_ramrod_res, ramrod_res_phys);
+
+       return rc;
+}
+
+static int qed_roce_sp_destroy_qp_requester(struct qed_hwfn *p_hwfn,
+                                           struct qed_rdma_qp *qp,
+                                           u32 *num_bound_mw)
+{
+       struct roce_destroy_qp_req_output_params *p_ramrod_res;
+       struct roce_destroy_qp_req_ramrod_data *p_ramrod;
+       struct qed_sp_init_data init_data;
+       struct qed_spq_entry *p_ent;
+       dma_addr_t ramrod_res_phys;
+       int rc = -ENOMEM;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", qp->icid);
+
+       if (!qp->req_offloaded)
+               return 0;
+
+       p_ramrod_res = (struct roce_destroy_qp_req_output_params *)
+                      dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                         sizeof(*p_ramrod_res),
+                                         &ramrod_res_phys, GFP_KERNEL);
+       if (!p_ramrod_res) {
+               DP_NOTICE(p_hwfn,
+                         "qed destroy requester failed: cannot allocate memory (ramrod)\n");
+               return rc;
+       }
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = qp->icid + 1;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent, ROCE_RAMROD_DESTROY_QP,
+                                PROTOCOLID_ROCE, &init_data);
+       if (rc)
+               goto err;
+
+       p_ramrod = &p_ent->ramrod.roce_destroy_qp_req;
+       DMA_REGPAIR_LE(p_ramrod->output_params_addr, ramrod_res_phys);
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+       if (rc)
+               goto err;
+
+       *num_bound_mw = le32_to_cpu(p_ramrod_res->num_bound_mw);
+
+       /* Free ORQ - only if ramrod succeeded, in case FW is still using it */
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev,
+                         qp->orq_num_pages * RDMA_RING_PAGE_SIZE,
+                         qp->orq, qp->orq_phys_addr);
+
+       qp->req_offloaded = false;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Destroy requester, rc = %d\n", rc);
+
+err:
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_ramrod_res),
+                         p_ramrod_res, ramrod_res_phys);
+
+       return rc;
+}
+
+int qed_roce_query_qp(struct qed_hwfn *p_hwfn,
+                     struct qed_rdma_qp *qp,
+                     struct qed_rdma_query_qp_out_params *out_params)
+{
+       struct roce_query_qp_resp_output_params *p_resp_ramrod_res;
+       struct roce_query_qp_req_output_params *p_req_ramrod_res;
+       struct roce_query_qp_resp_ramrod_data *p_resp_ramrod;
+       struct roce_query_qp_req_ramrod_data *p_req_ramrod;
+       struct qed_sp_init_data init_data;
+       dma_addr_t resp_ramrod_res_phys;
+       dma_addr_t req_ramrod_res_phys;
+       struct qed_spq_entry *p_ent;
+       bool rq_err_state;
+       bool sq_err_state;
+       bool sq_draining;
+       int rc = -ENOMEM;
+
+       if ((!(qp->resp_offloaded)) && (!(qp->req_offloaded))) {
+               /* We can't send ramrod to the fw since this qp wasn't offloaded
+                * to the fw yet
+                */
+               out_params->draining = false;
+               out_params->rq_psn = qp->rq_psn;
+               out_params->sq_psn = qp->sq_psn;
+               out_params->state = qp->cur_state;
+
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "No QPs as no offload\n");
+               return 0;
+       }
+
+       if (!(qp->resp_offloaded)) {
+               DP_NOTICE(p_hwfn,
+                         "The responder's qp should be offloded before requester's\n");
+               return -EINVAL;
+       }
+
+       /* Send a query responder ramrod to FW to get RQ-PSN and state */
+       p_resp_ramrod_res = (struct roce_query_qp_resp_output_params *)
+           dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                              sizeof(*p_resp_ramrod_res),
+                              &resp_ramrod_res_phys, GFP_KERNEL);
+       if (!p_resp_ramrod_res) {
+               DP_NOTICE(p_hwfn,
+                         "qed query qp failed: cannot allocate memory (ramrod)\n");
+               return rc;
+       }
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = qp->icid;
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+       rc = qed_sp_init_request(p_hwfn, &p_ent, ROCE_RAMROD_QUERY_QP,
+                                PROTOCOLID_ROCE, &init_data);
+       if (rc)
+               goto err_resp;
+
+       p_resp_ramrod = &p_ent->ramrod.roce_query_qp_resp;
+       DMA_REGPAIR_LE(p_resp_ramrod->output_params_addr, resp_ramrod_res_phys);
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+       if (rc)
+               goto err_resp;
+
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_resp_ramrod_res),
+                         p_resp_ramrod_res, resp_ramrod_res_phys);
+
+       out_params->rq_psn = le32_to_cpu(p_resp_ramrod_res->psn);
+       rq_err_state = GET_FIELD(le32_to_cpu(p_resp_ramrod_res->err_flag),
+                                ROCE_QUERY_QP_RESP_OUTPUT_PARAMS_ERROR_FLG);
+
+       if (!(qp->req_offloaded)) {
+               /* Don't send query qp for the requester */
+               out_params->sq_psn = qp->sq_psn;
+               out_params->draining = false;
+
+               if (rq_err_state)
+                       qp->cur_state = QED_ROCE_QP_STATE_ERR;
+
+               out_params->state = qp->cur_state;
+
+               return 0;
+       }
+
+       /* Send a query requester ramrod to FW to get SQ-PSN and state */
+       p_req_ramrod_res = (struct roce_query_qp_req_output_params *)
+                          dma_alloc_coherent(&p_hwfn->cdev->pdev->dev,
+                                             sizeof(*p_req_ramrod_res),
+                                             &req_ramrod_res_phys,
+                                             GFP_KERNEL);
+       if (!p_req_ramrod_res) {
+               rc = -ENOMEM;
+               DP_NOTICE(p_hwfn,
+                         "qed query qp failed: cannot allocate memory (ramrod)\n");
+               return rc;
+       }
+
+       /* Get SPQ entry */
+       init_data.cid = qp->icid + 1;
+       rc = qed_sp_init_request(p_hwfn, &p_ent, ROCE_RAMROD_QUERY_QP,
+                                PROTOCOLID_ROCE, &init_data);
+       if (rc)
+               goto err_req;
+
+       p_req_ramrod = &p_ent->ramrod.roce_query_qp_req;
+       DMA_REGPAIR_LE(p_req_ramrod->output_params_addr, req_ramrod_res_phys);
+
+       rc = qed_spq_post(p_hwfn, p_ent, NULL);
+       if (rc)
+               goto err_req;
+
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_req_ramrod_res),
+                         p_req_ramrod_res, req_ramrod_res_phys);
+
+       out_params->sq_psn = le32_to_cpu(p_req_ramrod_res->psn);
+       sq_err_state = GET_FIELD(le32_to_cpu(p_req_ramrod_res->flags),
+                                ROCE_QUERY_QP_REQ_OUTPUT_PARAMS_ERR_FLG);
+       sq_draining =
+               GET_FIELD(le32_to_cpu(p_req_ramrod_res->flags),
+                         ROCE_QUERY_QP_REQ_OUTPUT_PARAMS_SQ_DRAINING_FLG);
+
+       out_params->draining = false;
+
+       if (rq_err_state)
+               qp->cur_state = QED_ROCE_QP_STATE_ERR;
+       else if (sq_err_state)
+               qp->cur_state = QED_ROCE_QP_STATE_SQE;
+       else if (sq_draining)
+               out_params->draining = true;
+       out_params->state = qp->cur_state;
+
+       return 0;
+
+err_req:
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_req_ramrod_res),
+                         p_req_ramrod_res, req_ramrod_res_phys);
+       return rc;
+err_resp:
+       dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_resp_ramrod_res),
+                         p_resp_ramrod_res, resp_ramrod_res_phys);
+       return rc;
+}
+
+int qed_roce_destroy_qp(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp)
+{
+       u32 num_invalidated_mw = 0;
+       u32 num_bound_mw = 0;
+       u32 start_cid;
+       int rc;
+
+       /* Destroys the specified QP */
+       if ((qp->cur_state != QED_ROCE_QP_STATE_RESET) &&
+           (qp->cur_state != QED_ROCE_QP_STATE_ERR) &&
+           (qp->cur_state != QED_ROCE_QP_STATE_INIT)) {
+               DP_NOTICE(p_hwfn,
+                         "QP must be in error, reset or init state before destroying it\n");
+               return -EINVAL;
+       }
+
+       rc = qed_roce_sp_destroy_qp_responder(p_hwfn, qp, &num_invalidated_mw);
+       if (rc)
+               return rc;
+
+       /* Send destroy requester ramrod */
+       rc = qed_roce_sp_destroy_qp_requester(p_hwfn, qp, &num_bound_mw);
+       if (rc)
+               return rc;
+
+       if (num_invalidated_mw != num_bound_mw) {
+               DP_NOTICE(p_hwfn,
+                         "number of invalidate memory windows is different from bounded ones\n");
+               return -EINVAL;
+       }
+
+       spin_lock_bh(&p_hwfn->p_rdma_info->lock);
+
+       start_cid = qed_cxt_get_proto_cid_start(p_hwfn,
+                                               p_hwfn->p_rdma_info->proto);
+
+       /* Release responder's icid */
+       qed_bmap_release_id(p_hwfn, &p_hwfn->p_rdma_info->cid_map,
+                           qp->icid - start_cid);
+
+       /* Release requester's icid */
+       qed_bmap_release_id(p_hwfn, &p_hwfn->p_rdma_info->cid_map,
+                           qp->icid + 1 - start_cid);
+
+       spin_unlock_bh(&p_hwfn->p_rdma_info->lock);
+
+       return 0;
+}
+
+int qed_rdma_query_qp(void *rdma_cxt,
+                     struct qed_rdma_qp *qp,
+                     struct qed_rdma_query_qp_out_params *out_params)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", qp->icid);
+
+       /* The following fields are filled in from qp and not FW as they can't
+        * be modified by FW
+        */
+       out_params->mtu = qp->mtu;
+       out_params->dest_qp = qp->dest_qp;
+       out_params->incoming_atomic_en = qp->incoming_atomic_en;
+       out_params->e2e_flow_control_en = qp->e2e_flow_control_en;
+       out_params->incoming_rdma_read_en = qp->incoming_rdma_read_en;
+       out_params->incoming_rdma_write_en = qp->incoming_rdma_write_en;
+       out_params->dgid = qp->dgid;
+       out_params->flow_label = qp->flow_label;
+       out_params->hop_limit_ttl = qp->hop_limit_ttl;
+       out_params->traffic_class_tos = qp->traffic_class_tos;
+       out_params->timeout = qp->ack_timeout;
+       out_params->rnr_retry = qp->rnr_retry_cnt;
+       out_params->retry_cnt = qp->retry_cnt;
+       out_params->min_rnr_nak_timer = qp->min_rnr_nak_timer;
+       out_params->pkey_index = 0;
+       out_params->max_rd_atomic = qp->max_rd_atomic_req;
+       out_params->max_dest_rd_atomic = qp->max_rd_atomic_resp;
+       out_params->sqd_async = qp->sqd_async;
+
+       rc = qed_roce_query_qp(p_hwfn, qp, out_params);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Query QP, rc = %d\n", rc);
+       return rc;
+}
+
+int qed_rdma_destroy_qp(void *rdma_cxt, struct qed_rdma_qp *qp)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       int rc = 0;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", qp->icid);
+
+       rc = qed_roce_destroy_qp(p_hwfn, qp);
+
+       /* free qp params struct */
+       kfree(qp);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "QP destroyed\n");
+       return rc;
+}
+
+struct qed_rdma_qp *
+qed_rdma_create_qp(void *rdma_cxt,
+                  struct qed_rdma_create_qp_in_params *in_params,
+                  struct qed_rdma_create_qp_out_params *out_params)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       struct qed_rdma_qp *qp;
+       u8 max_stats_queues;
+       int rc;
+
+       if (!rdma_cxt || !in_params || !out_params || !p_hwfn->p_rdma_info) {
+               DP_ERR(p_hwfn->cdev,
+                      "qed roce create qp failed due to NULL entry (rdma_cxt=%p, in=%p, out=%p, roce_info=?\n",
+                      rdma_cxt, in_params, out_params);
+               return NULL;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                  "qed rdma create qp called with qp_handle = %08x%08x\n",
+                  in_params->qp_handle_hi, in_params->qp_handle_lo);
+
+       /* Some sanity checks... */
+       max_stats_queues = p_hwfn->p_rdma_info->dev->max_stats_queues;
+       if (in_params->stats_queue >= max_stats_queues) {
+               DP_ERR(p_hwfn->cdev,
+                      "qed rdma create qp failed due to invalid statistics queue %d. maximum is %d\n",
+                      in_params->stats_queue, max_stats_queues);
+               return NULL;
+       }
+
+       qp = kzalloc(sizeof(*qp), GFP_KERNEL);
+       if (!qp) {
+               DP_NOTICE(p_hwfn, "Failed to allocate qed_rdma_qp\n");
+               return NULL;
+       }
+
+       rc = qed_roce_alloc_cid(p_hwfn, &qp->icid);
+       qp->qpid = ((0xFF << 16) | qp->icid);
+
+       DP_INFO(p_hwfn, "ROCE qpid=%x\n", qp->qpid);
+
+       if (rc) {
+               kfree(qp);
+               return NULL;
+       }
+
+       qp->cur_state = QED_ROCE_QP_STATE_RESET;
+       qp->qp_handle.hi = cpu_to_le32(in_params->qp_handle_hi);
+       qp->qp_handle.lo = cpu_to_le32(in_params->qp_handle_lo);
+       qp->qp_handle_async.hi = cpu_to_le32(in_params->qp_handle_async_hi);
+       qp->qp_handle_async.lo = cpu_to_le32(in_params->qp_handle_async_lo);
+       qp->use_srq = in_params->use_srq;
+       qp->signal_all = in_params->signal_all;
+       qp->fmr_and_reserved_lkey = in_params->fmr_and_reserved_lkey;
+       qp->pd = in_params->pd;
+       qp->dpi = in_params->dpi;
+       qp->sq_cq_id = in_params->sq_cq_id;
+       qp->sq_num_pages = in_params->sq_num_pages;
+       qp->sq_pbl_ptr = in_params->sq_pbl_ptr;
+       qp->rq_cq_id = in_params->rq_cq_id;
+       qp->rq_num_pages = in_params->rq_num_pages;
+       qp->rq_pbl_ptr = in_params->rq_pbl_ptr;
+       qp->srq_id = in_params->srq_id;
+       qp->req_offloaded = false;
+       qp->resp_offloaded = false;
+       qp->e2e_flow_control_en = qp->use_srq ? false : true;
+       qp->stats_queue = in_params->stats_queue;
+
+       out_params->icid = qp->icid;
+       out_params->qp_id = qp->qpid;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Create QP, rc = %d\n", rc);
+       return qp;
+}
+
+static int qed_roce_modify_qp(struct qed_hwfn *p_hwfn,
+                             struct qed_rdma_qp *qp,
+                             enum qed_roce_qp_state prev_state,
+                             struct qed_rdma_modify_qp_in_params *params)
+{
+       u32 num_invalidated_mw = 0, num_bound_mw = 0;
+       int rc = 0;
+
+       /* Perform additional operations according to the current state and the
+        * next state
+        */
+       if (((prev_state == QED_ROCE_QP_STATE_INIT) ||
+            (prev_state == QED_ROCE_QP_STATE_RESET)) &&
+           (qp->cur_state == QED_ROCE_QP_STATE_RTR)) {
+               /* Init->RTR or Reset->RTR */
+               rc = qed_roce_sp_create_responder(p_hwfn, qp);
+               return rc;
+       } else if ((prev_state == QED_ROCE_QP_STATE_RTR) &&
+                  (qp->cur_state == QED_ROCE_QP_STATE_RTS)) {
+               /* RTR-> RTS */
+               rc = qed_roce_sp_create_requester(p_hwfn, qp);
+               if (rc)
+                       return rc;
+
+               /* Send modify responder ramrod */
+               rc = qed_roce_sp_modify_responder(p_hwfn, qp, false,
+                                                 params->modify_flags);
+               return rc;
+       } else if ((prev_state == QED_ROCE_QP_STATE_RTS) &&
+                  (qp->cur_state == QED_ROCE_QP_STATE_RTS)) {
+               /* RTS->RTS */
+               rc = qed_roce_sp_modify_responder(p_hwfn, qp, false,
+                                                 params->modify_flags);
+               if (rc)
+                       return rc;
+
+               rc = qed_roce_sp_modify_requester(p_hwfn, qp, false, false,
+                                                 params->modify_flags);
+               return rc;
+       } else if ((prev_state == QED_ROCE_QP_STATE_RTS) &&
+                  (qp->cur_state == QED_ROCE_QP_STATE_SQD)) {
+               /* RTS->SQD */
+               rc = qed_roce_sp_modify_requester(p_hwfn, qp, true, false,
+                                                 params->modify_flags);
+               return rc;
+       } else if ((prev_state == QED_ROCE_QP_STATE_SQD) &&
+                  (qp->cur_state == QED_ROCE_QP_STATE_SQD)) {
+               /* SQD->SQD */
+               rc = qed_roce_sp_modify_responder(p_hwfn, qp, false,
+                                                 params->modify_flags);
+               if (rc)
+                       return rc;
+
+               rc = qed_roce_sp_modify_requester(p_hwfn, qp, false, false,
+                                                 params->modify_flags);
+               return rc;
+       } else if ((prev_state == QED_ROCE_QP_STATE_SQD) &&
+                  (qp->cur_state == QED_ROCE_QP_STATE_RTS)) {
+               /* SQD->RTS */
+               rc = qed_roce_sp_modify_responder(p_hwfn, qp, false,
+                                                 params->modify_flags);
+               if (rc)
+                       return rc;
+
+               rc = qed_roce_sp_modify_requester(p_hwfn, qp, false, false,
+                                                 params->modify_flags);
+
+               return rc;
+       } else if (qp->cur_state == QED_ROCE_QP_STATE_ERR ||
+                  qp->cur_state == QED_ROCE_QP_STATE_SQE) {
+               /* ->ERR */
+               rc = qed_roce_sp_modify_responder(p_hwfn, qp, true,
+                                                 params->modify_flags);
+               if (rc)
+                       return rc;
+
+               rc = qed_roce_sp_modify_requester(p_hwfn, qp, false, true,
+                                                 params->modify_flags);
+               return rc;
+       } else if (qp->cur_state == QED_ROCE_QP_STATE_RESET) {
+               /* Any state -> RESET */
+
+               rc = qed_roce_sp_destroy_qp_responder(p_hwfn, qp,
+                                                     &num_invalidated_mw);
+               if (rc)
+                       return rc;
+
+               rc = qed_roce_sp_destroy_qp_requester(p_hwfn, qp,
+                                                     &num_bound_mw);
+
+               if (num_invalidated_mw != num_bound_mw) {
+                       DP_NOTICE(p_hwfn,
+                                 "number of invalidate memory windows is different from bounded ones\n");
+                       return -EINVAL;
+               }
+       } else {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "0\n");
+       }
+
+       return rc;
+}
+
+int qed_rdma_modify_qp(void *rdma_cxt,
+                      struct qed_rdma_qp *qp,
+                      struct qed_rdma_modify_qp_in_params *params)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       enum qed_roce_qp_state prev_state;
+       int rc = 0;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x params->new_state=%d\n",
+                  qp->icid, params->new_state);
+
+       if (rc) {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "rc = %d\n", rc);
+               return rc;
+       }
+
+       if (GET_FIELD(params->modify_flags,
+                     QED_RDMA_MODIFY_QP_VALID_RDMA_OPS_EN)) {
+               qp->incoming_rdma_read_en = params->incoming_rdma_read_en;
+               qp->incoming_rdma_write_en = params->incoming_rdma_write_en;
+               qp->incoming_atomic_en = params->incoming_atomic_en;
+       }
+
+       /* Update QP structure with the updated values */
+       if (GET_FIELD(params->modify_flags, QED_ROCE_MODIFY_QP_VALID_ROCE_MODE))
+               qp->roce_mode = params->roce_mode;
+       if (GET_FIELD(params->modify_flags, QED_ROCE_MODIFY_QP_VALID_PKEY))
+               qp->pkey = params->pkey;
+       if (GET_FIELD(params->modify_flags,
+                     QED_ROCE_MODIFY_QP_VALID_E2E_FLOW_CONTROL_EN))
+               qp->e2e_flow_control_en = params->e2e_flow_control_en;
+       if (GET_FIELD(params->modify_flags, QED_ROCE_MODIFY_QP_VALID_DEST_QP))
+               qp->dest_qp = params->dest_qp;
+       if (GET_FIELD(params->modify_flags,
+                     QED_ROCE_MODIFY_QP_VALID_ADDRESS_VECTOR)) {
+               /* Indicates that the following parameters have changed:
+                * Traffic class, flow label, hop limit, source GID,
+                * destination GID, loopback indicator
+                */
+               qp->traffic_class_tos = params->traffic_class_tos;
+               qp->flow_label = params->flow_label;
+               qp->hop_limit_ttl = params->hop_limit_ttl;
+
+               qp->sgid = params->sgid;
+               qp->dgid = params->dgid;
+               qp->udp_src_port = 0;
+               qp->vlan_id = params->vlan_id;
+               qp->mtu = params->mtu;
+               qp->lb_indication = params->lb_indication;
+               memcpy((u8 *)&qp->remote_mac_addr[0],
+                      (u8 *)&params->remote_mac_addr[0], ETH_ALEN);
+               if (params->use_local_mac) {
+                       memcpy((u8 *)&qp->local_mac_addr[0],
+                              (u8 *)&params->local_mac_addr[0], ETH_ALEN);
+               } else {
+                       memcpy((u8 *)&qp->local_mac_addr[0],
+                              (u8 *)&p_hwfn->hw_info.hw_mac_addr, ETH_ALEN);
+               }
+       }
+       if (GET_FIELD(params->modify_flags, QED_ROCE_MODIFY_QP_VALID_RQ_PSN))
+               qp->rq_psn = params->rq_psn;
+       if (GET_FIELD(params->modify_flags, QED_ROCE_MODIFY_QP_VALID_SQ_PSN))
+               qp->sq_psn = params->sq_psn;
+       if (GET_FIELD(params->modify_flags,
+                     QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_REQ))
+               qp->max_rd_atomic_req = params->max_rd_atomic_req;
+       if (GET_FIELD(params->modify_flags,
+                     QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_RESP))
+               qp->max_rd_atomic_resp = params->max_rd_atomic_resp;
+       if (GET_FIELD(params->modify_flags,
+                     QED_ROCE_MODIFY_QP_VALID_ACK_TIMEOUT))
+               qp->ack_timeout = params->ack_timeout;
+       if (GET_FIELD(params->modify_flags, QED_ROCE_MODIFY_QP_VALID_RETRY_CNT))
+               qp->retry_cnt = params->retry_cnt;
+       if (GET_FIELD(params->modify_flags,
+                     QED_ROCE_MODIFY_QP_VALID_RNR_RETRY_CNT))
+               qp->rnr_retry_cnt = params->rnr_retry_cnt;
+       if (GET_FIELD(params->modify_flags,
+                     QED_ROCE_MODIFY_QP_VALID_MIN_RNR_NAK_TIMER))
+               qp->min_rnr_nak_timer = params->min_rnr_nak_timer;
+
+       qp->sqd_async = params->sqd_async;
+
+       prev_state = qp->cur_state;
+       if (GET_FIELD(params->modify_flags,
+                     QED_RDMA_MODIFY_QP_VALID_NEW_STATE)) {
+               qp->cur_state = params->new_state;
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "qp->cur_state=%d\n",
+                          qp->cur_state);
+       }
+
+       rc = qed_roce_modify_qp(p_hwfn, qp, prev_state, params);
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Modify QP, rc = %d\n", rc);
+       return rc;
+}
+
+int qed_rdma_register_tid(void *rdma_cxt,
+                         struct qed_rdma_register_tid_in_params *params)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       struct rdma_register_tid_ramrod_data *p_ramrod;
+       struct qed_sp_init_data init_data;
+       struct qed_spq_entry *p_ent;
+       enum rdma_tid_type tid_type;
+       u8 fw_return_code;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "itid = %08x\n", params->itid);
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent, RDMA_RAMROD_REGISTER_MR,
+                                p_hwfn->p_rdma_info->proto, &init_data);
+       if (rc) {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "rc = %d\n", rc);
+               return rc;
+       }
+
+       if (p_hwfn->p_rdma_info->last_tid < params->itid)
+               p_hwfn->p_rdma_info->last_tid = params->itid;
+
+       p_ramrod = &p_ent->ramrod.rdma_register_tid;
+
+       p_ramrod->flags = 0;
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_REGISTER_TID_RAMROD_DATA_TWO_LEVEL_PBL,
+                 params->pbl_two_level);
+
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_REGISTER_TID_RAMROD_DATA_ZERO_BASED, params->zbva);
+
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_REGISTER_TID_RAMROD_DATA_PHY_MR, params->phy_mr);
+
+       /* Don't initialize D/C field, as it may override other bits. */
+       if (!(params->tid_type == QED_RDMA_TID_FMR) && !(params->dma_mr))
+               SET_FIELD(p_ramrod->flags,
+                         RDMA_REGISTER_TID_RAMROD_DATA_PAGE_SIZE_LOG,
+                         params->page_size_log - 12);
+
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_REGISTER_TID_RAMROD_DATA_MAX_ID,
+                 p_hwfn->p_rdma_info->last_tid);
+
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_REGISTER_TID_RAMROD_DATA_REMOTE_READ,
+                 params->remote_read);
+
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_REGISTER_TID_RAMROD_DATA_REMOTE_WRITE,
+                 params->remote_write);
+
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_REGISTER_TID_RAMROD_DATA_REMOTE_ATOMIC,
+                 params->remote_atomic);
+
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_REGISTER_TID_RAMROD_DATA_LOCAL_WRITE,
+                 params->local_write);
+
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_REGISTER_TID_RAMROD_DATA_LOCAL_READ, params->local_read);
+
+       SET_FIELD(p_ramrod->flags,
+                 RDMA_REGISTER_TID_RAMROD_DATA_ENABLE_MW_BIND,
+                 params->mw_bind);
+
+       SET_FIELD(p_ramrod->flags1,
+                 RDMA_REGISTER_TID_RAMROD_DATA_PBL_PAGE_SIZE_LOG,
+                 params->pbl_page_size_log - 12);
+
+       SET_FIELD(p_ramrod->flags2,
+                 RDMA_REGISTER_TID_RAMROD_DATA_DMA_MR, params->dma_mr);
+
+       switch (params->tid_type) {
+       case QED_RDMA_TID_REGISTERED_MR:
+               tid_type = RDMA_TID_REGISTERED_MR;
+               break;
+       case QED_RDMA_TID_FMR:
+               tid_type = RDMA_TID_FMR;
+               break;
+       case QED_RDMA_TID_MW_TYPE1:
+               tid_type = RDMA_TID_MW_TYPE1;
+               break;
+       case QED_RDMA_TID_MW_TYPE2A:
+               tid_type = RDMA_TID_MW_TYPE2A;
+               break;
+       default:
+               rc = -EINVAL;
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "rc = %d\n", rc);
+               return rc;
+       }
+       SET_FIELD(p_ramrod->flags1,
+                 RDMA_REGISTER_TID_RAMROD_DATA_TID_TYPE, tid_type);
+
+       p_ramrod->itid = cpu_to_le32(params->itid);
+       p_ramrod->key = params->key;
+       p_ramrod->pd = cpu_to_le16(params->pd);
+       p_ramrod->length_hi = (u8)(params->length >> 32);
+       p_ramrod->length_lo = DMA_LO_LE(params->length);
+       if (params->zbva) {
+               /* Lower 32 bits of the registered MR address.
+                * In case of zero based MR, will hold FBO
+                */
+               p_ramrod->va.hi = 0;
+               p_ramrod->va.lo = cpu_to_le32(params->fbo);
+       } else {
+               DMA_REGPAIR_LE(p_ramrod->va, params->vaddr);
+       }
+       DMA_REGPAIR_LE(p_ramrod->pbl_base, params->pbl_ptr);
+
+       /* DIF */
+       if (params->dif_enabled) {
+               SET_FIELD(p_ramrod->flags2,
+                         RDMA_REGISTER_TID_RAMROD_DATA_DIF_ON_HOST_FLG, 1);
+               DMA_REGPAIR_LE(p_ramrod->dif_error_addr,
+                              params->dif_error_addr);
+               DMA_REGPAIR_LE(p_ramrod->dif_runt_addr, params->dif_runt_addr);
+       }
+
+       rc = qed_spq_post(p_hwfn, p_ent, &fw_return_code);
+
+       if (fw_return_code != RDMA_RETURN_OK) {
+               DP_NOTICE(p_hwfn, "fw_return_code = %d\n", fw_return_code);
+               return -EINVAL;
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Register TID, rc = %d\n", rc);
+       return rc;
+}
+
+int qed_rdma_deregister_tid(void *rdma_cxt, u32 itid)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       struct rdma_deregister_tid_ramrod_data *p_ramrod;
+       struct qed_sp_init_data init_data;
+       struct qed_spq_entry *p_ent;
+       struct qed_ptt *p_ptt;
+       u8 fw_return_code;
+       int rc;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "itid = %08x\n", itid);
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent, RDMA_RAMROD_DEREGISTER_MR,
+                                p_hwfn->p_rdma_info->proto, &init_data);
+       if (rc) {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "rc = %d\n", rc);
+               return rc;
+       }
+
+       p_ramrod = &p_ent->ramrod.rdma_deregister_tid;
+       p_ramrod->itid = cpu_to_le32(itid);
+
+       rc = qed_spq_post(p_hwfn, p_ent, &fw_return_code);
+       if (rc) {
+               DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "rc = %d\n", rc);
+               return rc;
+       }
+
+       if (fw_return_code == RDMA_RETURN_DEREGISTER_MR_BAD_STATE_ERR) {
+               DP_NOTICE(p_hwfn, "fw_return_code = %d\n", fw_return_code);
+               return -EINVAL;
+       } else if (fw_return_code == RDMA_RETURN_NIG_DRAIN_REQ) {
+               /* Bit indicating that the TID is in use and a nig drain is
+                * required before sending the ramrod again
+                */
+               p_ptt = qed_ptt_acquire(p_hwfn);
+               if (!p_ptt) {
+                       rc = -EBUSY;
+                       DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                                  "Failed to acquire PTT\n");
+                       return rc;
+               }
+
+               rc = qed_mcp_drain(p_hwfn, p_ptt);
+               if (rc) {
+                       qed_ptt_release(p_hwfn, p_ptt);
+                       DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                                  "Drain failed\n");
+                       return rc;
+               }
+
+               qed_ptt_release(p_hwfn, p_ptt);
+
+               /* Resend the ramrod */
+               rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                        RDMA_RAMROD_DEREGISTER_MR,
+                                        p_hwfn->p_rdma_info->proto,
+                                        &init_data);
+               if (rc) {
+                       DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                                  "Failed to init sp-element\n");
+                       return rc;
+               }
+
+               rc = qed_spq_post(p_hwfn, p_ent, &fw_return_code);
+               if (rc) {
+                       DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                                  "Ramrod failed\n");
+                       return rc;
+               }
+
+               if (fw_return_code != RDMA_RETURN_OK) {
+                       DP_NOTICE(p_hwfn, "fw_return_code = %d\n",
+                                 fw_return_code);
+                       return rc;
+               }
+       }
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "De-registered TID, rc = %d\n", rc);
+       return rc;
+}
+
+static void *qed_rdma_get_rdma_ctx(struct qed_dev *cdev)
+{
+       return QED_LEADING_HWFN(cdev);
+}
+
+static void qed_rdma_dpm_conf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
+{
+       u32 val;
+
+       val = (p_hwfn->dcbx_no_edpm || p_hwfn->db_bar_no_edpm) ? 0 : 1;
+
+       qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_DPM_ENABLE, val);
+       DP_VERBOSE(p_hwfn, (QED_MSG_DCB | QED_MSG_RDMA),
+                  "Changing DPM_EN state to %d (DCBX=%d, DB_BAR=%d)\n",
+                  val, p_hwfn->dcbx_no_edpm, p_hwfn->db_bar_no_edpm);
+}
+
+void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
+{
+       p_hwfn->db_bar_no_edpm = true;
+
+       qed_rdma_dpm_conf(p_hwfn, p_ptt);
+}
+
+int qed_rdma_start(void *rdma_cxt, struct qed_rdma_start_in_params *params)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+       struct qed_ptt *p_ptt;
+       int rc = -EBUSY;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA,
+                  "desired_cnq = %08x\n", params->desired_cnq);
+
+       p_ptt = qed_ptt_acquire(p_hwfn);
+       if (!p_ptt)
+               goto err;
+
+       rc = qed_rdma_alloc(p_hwfn, p_ptt, params);
+       if (rc)
+               goto err1;
+
+       rc = qed_rdma_setup(p_hwfn, p_ptt, params);
+       if (rc)
+               goto err2;
+
+       qed_ptt_release(p_hwfn, p_ptt);
+
+       return rc;
+
+err2:
+       qed_rdma_free(p_hwfn);
+err1:
+       qed_ptt_release(p_hwfn, p_ptt);
+err:
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "RDMA start - error, rc = %d\n", rc);
+       return rc;
+}
+
+static int qed_rdma_init(struct qed_dev *cdev,
+                        struct qed_rdma_start_in_params *params)
+{
+       return qed_rdma_start(QED_LEADING_HWFN(cdev), params);
+}
+
+void qed_rdma_remove_user(void *rdma_cxt, u16 dpi)
+{
+       struct qed_hwfn *p_hwfn = (struct qed_hwfn *)rdma_cxt;
+
+       DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "dpi = %08x\n", dpi);
+
+       spin_lock_bh(&p_hwfn->p_rdma_info->lock);
+       qed_bmap_release_id(p_hwfn, &p_hwfn->p_rdma_info->dpi_map, dpi);
+       spin_unlock_bh(&p_hwfn->p_rdma_info->lock);
+}
+
+void qed_ll2b_complete_tx_gsi_packet(struct qed_hwfn *p_hwfn,
+                                    u8 connection_handle,
+                                    void *cookie,
+                                    dma_addr_t first_frag_addr,
+                                    bool b_last_fragment, bool b_last_packet)
+{
+       struct qed_roce_ll2_packet *packet = cookie;
+       struct qed_roce_ll2_info *roce_ll2 = p_hwfn->ll2;
+
+       roce_ll2->cbs.tx_cb(roce_ll2->cb_cookie, packet);
+}
+
+void qed_ll2b_release_tx_gsi_packet(struct qed_hwfn *p_hwfn,
+                                   u8 connection_handle,
+                                   void *cookie,
+                                   dma_addr_t first_frag_addr,
+                                   bool b_last_fragment, bool b_last_packet)
+{
+       qed_ll2b_complete_tx_gsi_packet(p_hwfn, connection_handle,
+                                       cookie, first_frag_addr,
+                                       b_last_fragment, b_last_packet);
+}
+
+void qed_ll2b_complete_rx_gsi_packet(struct qed_hwfn *p_hwfn,
+                                    u8 connection_handle,
+                                    void *cookie,
+                                    dma_addr_t rx_buf_addr,
+                                    u16 data_length,
+                                    u8 data_length_error,
+                                    u16 parse_flags,
+                                    u16 vlan,
+                                    u32 src_mac_addr_hi,
+                                    u16 src_mac_addr_lo, bool b_last_packet)
+{
+       struct qed_roce_ll2_info *roce_ll2 = p_hwfn->ll2;
+       struct qed_roce_ll2_rx_params params;
+       struct qed_dev *cdev = p_hwfn->cdev;
+       struct qed_roce_ll2_packet pkt;
+
+       DP_VERBOSE(cdev,
+                  QED_MSG_LL2,
+                  "roce ll2 rx complete: bus_addr=%p, len=%d, data_len_err=%d\n",
+                  (void *)(uintptr_t)rx_buf_addr,
+                  data_length, data_length_error);
+
+       memset(&pkt, 0, sizeof(pkt));
+       pkt.n_seg = 1;
+       pkt.payload[0].baddr = rx_buf_addr;
+       pkt.payload[0].len = data_length;
+
+       memset(&params, 0, sizeof(params));
+       params.vlan_id = vlan;
+       *((u32 *)&params.smac[0]) = ntohl(src_mac_addr_hi);
+       *((u16 *)&params.smac[4]) = ntohs(src_mac_addr_lo);
+
+       if (data_length_error) {
+               DP_ERR(cdev,
+                      "roce ll2 rx complete: data length error %d, length=%d\n",
+                      data_length_error, data_length);
+               params.rc = -EINVAL;
+       }
+
+       roce_ll2->cbs.rx_cb(roce_ll2->cb_cookie, &pkt, &params);
+}
+
+static int qed_roce_ll2_set_mac_filter(struct qed_dev *cdev,
+                                      u8 *old_mac_address,
+                                      u8 *new_mac_address)
+{
+       struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
+       struct qed_ptt *p_ptt;
+       int rc = 0;
+
+       if (!hwfn->ll2 || hwfn->ll2->handle == QED_LL2_UNUSED_HANDLE) {
+               DP_ERR(cdev,
+                      "qed roce mac filter failed - roce_info/ll2 NULL\n");
+               return -EINVAL;
+       }
+
+       p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev));
+       if (!p_ptt) {
+               DP_ERR(cdev,
+                      "qed roce ll2 mac filter set: failed to acquire PTT\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&hwfn->ll2->lock);
+       if (old_mac_address)
+               qed_llh_remove_mac_filter(QED_LEADING_HWFN(cdev), p_ptt,
+                                         old_mac_address);
+       if (new_mac_address)
+               rc = qed_llh_add_mac_filter(QED_LEADING_HWFN(cdev), p_ptt,
+                                           new_mac_address);
+       mutex_unlock(&hwfn->ll2->lock);
+
+       qed_ptt_release(QED_LEADING_HWFN(cdev), p_ptt);
+
+       if (rc)
+               DP_ERR(cdev,
+                      "qed roce ll2 mac filter set: failed to add mac filter\n");
+
+       return rc;
+}
+
+static int qed_roce_ll2_start(struct qed_dev *cdev,
+                             struct qed_roce_ll2_params *params)
+{
+       struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
+       struct qed_roce_ll2_info *roce_ll2;
+       struct qed_ll2_info ll2_params;
+       int rc;
+
+       if (!params) {
+               DP_ERR(cdev, "qed roce ll2 start: failed due to NULL params\n");
+               return -EINVAL;
+       }
+       if (!params->cbs.tx_cb || !params->cbs.rx_cb) {
+               DP_ERR(cdev,
+                      "qed roce ll2 start: failed due to NULL tx/rx. tx_cb=%p, rx_cb=%p\n",
+                      params->cbs.tx_cb, params->cbs.rx_cb);
+               return -EINVAL;
+       }
+       if (!is_valid_ether_addr(params->mac_address)) {
+               DP_ERR(cdev,
+                      "qed roce ll2 start: failed due to invalid Ethernet address %pM\n",
+                      params->mac_address);
+               return -EINVAL;
+       }
+
+       /* Initialize */
+       roce_ll2 = kzalloc(sizeof(*roce_ll2), GFP_ATOMIC);
+       if (!roce_ll2) {
+               DP_ERR(cdev, "qed roce ll2 start: failed memory allocation\n");
+               return -ENOMEM;
+       }
+       memset(roce_ll2, 0, sizeof(*roce_ll2));
+       roce_ll2->handle = QED_LL2_UNUSED_HANDLE;
+       roce_ll2->cbs = params->cbs;
+       roce_ll2->cb_cookie = params->cb_cookie;
+       mutex_init(&roce_ll2->lock);
+
+       memset(&ll2_params, 0, sizeof(ll2_params));
+       ll2_params.conn_type = QED_LL2_TYPE_ROCE;
+       ll2_params.mtu = params->mtu;
+       ll2_params.rx_drop_ttl0_flg = true;
+       ll2_params.rx_vlan_removal_en = false;
+       ll2_params.tx_dest = CORE_TX_DEST_NW;
+       ll2_params.ai_err_packet_too_big = LL2_DROP_PACKET;
+       ll2_params.ai_err_no_buf = LL2_DROP_PACKET;
+       ll2_params.gsi_enable = true;
+
+       rc = qed_ll2_acquire_connection(QED_LEADING_HWFN(cdev), &ll2_params,
+                                       params->max_rx_buffers,
+                                       params->max_tx_buffers,
+                                       &roce_ll2->handle);
+       if (rc) {
+               DP_ERR(cdev,
+                      "qed roce ll2 start: failed to acquire LL2 connection (rc=%d)\n",
+                      rc);
+               goto err;
+       }
+
+       rc = qed_ll2_establish_connection(QED_LEADING_HWFN(cdev),
+                                         roce_ll2->handle);
+       if (rc) {
+               DP_ERR(cdev,
+                      "qed roce ll2 start: failed to establish LL2 connection (rc=%d)\n",
+                      rc);
+               goto err1;
+       }
+
+       hwfn->ll2 = roce_ll2;
+
+       rc = qed_roce_ll2_set_mac_filter(cdev, NULL, params->mac_address);
+       if (rc) {
+               hwfn->ll2 = NULL;
+               goto err2;
+       }
+       ether_addr_copy(roce_ll2->mac_address, params->mac_address);
+
+       return 0;
+
+err2:
+       qed_ll2_terminate_connection(QED_LEADING_HWFN(cdev), roce_ll2->handle);
+err1:
+       qed_ll2_release_connection(QED_LEADING_HWFN(cdev), roce_ll2->handle);
+err:
+       kfree(roce_ll2);
+       return rc;
+}
+
+static int qed_roce_ll2_stop(struct qed_dev *cdev)
+{
+       struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
+       struct qed_roce_ll2_info *roce_ll2 = hwfn->ll2;
+       int rc;
+
+       if (!cdev) {
+               DP_ERR(cdev, "qed roce ll2 stop: invalid cdev\n");
+               return -EINVAL;
+       }
+
+       if (roce_ll2->handle == QED_LL2_UNUSED_HANDLE) {
+               DP_ERR(cdev, "qed roce ll2 stop: cannot stop an unused LL2\n");
+               return -EINVAL;
+       }
+
+       /* remove LL2 MAC address filter */
+       rc = qed_roce_ll2_set_mac_filter(cdev, roce_ll2->mac_address, NULL);
+       eth_zero_addr(roce_ll2->mac_address);
+
+       rc = qed_ll2_terminate_connection(QED_LEADING_HWFN(cdev),
+                                         roce_ll2->handle);
+       if (rc)
+               DP_ERR(cdev,
+                      "qed roce ll2 stop: failed to terminate LL2 connection (rc=%d)\n",
+                      rc);
+
+       qed_ll2_release_connection(QED_LEADING_HWFN(cdev), roce_ll2->handle);
+
+       roce_ll2->handle = QED_LL2_UNUSED_HANDLE;
+
+       kfree(roce_ll2);
+
+       return rc;
+}
+
+static int qed_roce_ll2_tx(struct qed_dev *cdev,
+                          struct qed_roce_ll2_packet *pkt,
+                          struct qed_roce_ll2_tx_params *params)
+{
+       struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
+       struct qed_roce_ll2_info *roce_ll2 = hwfn->ll2;
+       enum qed_ll2_roce_flavor_type qed_roce_flavor;
+       u8 flags = 0;
+       int rc;
+       int i;
+
+       if (!cdev || !pkt || !params) {
+               DP_ERR(cdev,
+                      "roce ll2 tx: failed tx because one of the following is NULL - drv=%p, pkt=%p, params=%p\n",
+                      cdev, pkt, params);
+               return -EINVAL;
+       }
+
+       qed_roce_flavor = (pkt->roce_mode == ROCE_V1) ? QED_LL2_ROCE
+                                                     : QED_LL2_RROCE;
+
+       if (pkt->roce_mode == ROCE_V2_IPV4)
+               flags |= BIT(CORE_TX_BD_FLAGS_IP_CSUM_SHIFT);
+
+       /* Tx header */
+       rc = qed_ll2_prepare_tx_packet(QED_LEADING_HWFN(cdev), roce_ll2->handle,
+                                      1 + pkt->n_seg, 0, flags, 0,
+                                      qed_roce_flavor, pkt->header.baddr,
+                                      pkt->header.len, pkt, 1);
+       if (rc) {
+               DP_ERR(cdev, "roce ll2 tx: header failed (rc=%d)\n", rc);
+               return QED_ROCE_TX_HEAD_FAILURE;
+       }
+
+       /* Tx payload */
+       for (i = 0; i < pkt->n_seg; i++) {
+               rc = qed_ll2_set_fragment_of_tx_packet(QED_LEADING_HWFN(cdev),
+                                                      roce_ll2->handle,
+                                                      pkt->payload[i].baddr,
+                                                      pkt->payload[i].len);
+               if (rc) {
+                       /* If failed not much to do here, partial packet has
+                        * been posted * we can't free memory, will need to wait
+                        * for completion
+                        */
+                       DP_ERR(cdev,
+                              "roce ll2 tx: payload failed (rc=%d)\n", rc);
+                       return QED_ROCE_TX_FRAG_FAILURE;
+               }
+       }
+
+       return 0;
+}
+
+static int qed_roce_ll2_post_rx_buffer(struct qed_dev *cdev,
+                                      struct qed_roce_ll2_buffer *buf,
+                                      u64 cookie, u8 notify_fw)
+{
+       return qed_ll2_post_rx_buffer(QED_LEADING_HWFN(cdev),
+                                     QED_LEADING_HWFN(cdev)->ll2->handle,
+                                     buf->baddr, buf->len,
+                                     (void *)(uintptr_t)cookie, notify_fw);
+}
+
+static int qed_roce_ll2_stats(struct qed_dev *cdev, struct qed_ll2_stats *stats)
+{
+       struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
+       struct qed_roce_ll2_info *roce_ll2 = hwfn->ll2;
+
+       return qed_ll2_get_stats(QED_LEADING_HWFN(cdev),
+                                roce_ll2->handle, stats);
+}
+
+static const struct qed_rdma_ops qed_rdma_ops_pass = {
+       .common = &qed_common_ops_pass,
+       .fill_dev_info = &qed_fill_rdma_dev_info,
+       .rdma_get_rdma_ctx = &qed_rdma_get_rdma_ctx,
+       .rdma_init = &qed_rdma_init,
+       .rdma_add_user = &qed_rdma_add_user,
+       .rdma_remove_user = &qed_rdma_remove_user,
+       .rdma_stop = &qed_rdma_stop,
+       .rdma_query_port = &qed_rdma_query_port,
+       .rdma_query_device = &qed_rdma_query_device,
+       .rdma_get_start_sb = &qed_rdma_get_sb_start,
+       .rdma_get_rdma_int = &qed_rdma_get_int,
+       .rdma_set_rdma_int = &qed_rdma_set_int,
+       .rdma_get_min_cnq_msix = &qed_rdma_get_min_cnq_msix,
+       .rdma_cnq_prod_update = &qed_rdma_cnq_prod_update,
+       .rdma_alloc_pd = &qed_rdma_alloc_pd,
+       .rdma_dealloc_pd = &qed_rdma_free_pd,
+       .rdma_create_cq = &qed_rdma_create_cq,
+       .rdma_destroy_cq = &qed_rdma_destroy_cq,
+       .rdma_create_qp = &qed_rdma_create_qp,
+       .rdma_modify_qp = &qed_rdma_modify_qp,
+       .rdma_query_qp = &qed_rdma_query_qp,
+       .rdma_destroy_qp = &qed_rdma_destroy_qp,
+       .rdma_alloc_tid = &qed_rdma_alloc_tid,
+       .rdma_free_tid = &qed_rdma_free_tid,
+       .rdma_register_tid = &qed_rdma_register_tid,
+       .rdma_deregister_tid = &qed_rdma_deregister_tid,
+       .roce_ll2_start = &qed_roce_ll2_start,
+       .roce_ll2_stop = &qed_roce_ll2_stop,
+       .roce_ll2_tx = &qed_roce_ll2_tx,
+       .roce_ll2_post_rx_buffer = &qed_roce_ll2_post_rx_buffer,
+       .roce_ll2_set_mac_filter = &qed_roce_ll2_set_mac_filter,
+       .roce_ll2_stats = &qed_roce_ll2_stats,
+};
+
+const struct qed_rdma_ops *qed_get_rdma_ops()
+{
+       return &qed_rdma_ops_pass;
+}
+EXPORT_SYMBOL(qed_get_rdma_ops);
diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.h b/drivers/net/ethernet/qlogic/qed/qed_roce.h
new file mode 100644 (file)
index 0000000..2f091e8
--- /dev/null
@@ -0,0 +1,216 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * 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
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _QED_ROCE_H
+#define _QED_ROCE_H
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/qed/qed_if.h>
+#include <linux/qed/qed_roce_if.h>
+#include "qed.h"
+#include "qed_dev_api.h"
+#include "qed_hsi.h"
+#include "qed_ll2.h"
+
+#define QED_RDMA_MAX_FMR                    (RDMA_MAX_TIDS)
+#define QED_RDMA_MAX_P_KEY                  (1)
+#define QED_RDMA_MAX_WQE                    (0x7FFF)
+#define QED_RDMA_MAX_SRQ_WQE_ELEM           (0x7FFF)
+#define QED_RDMA_PAGE_SIZE_CAPS             (0xFFFFF000)
+#define QED_RDMA_ACK_DELAY                  (15)
+#define QED_RDMA_MAX_MR_SIZE                (0x10000000000ULL)
+#define QED_RDMA_MAX_CQS                    (RDMA_MAX_CQS)
+#define QED_RDMA_MAX_MRS                    (RDMA_MAX_TIDS)
+/* Add 1 for header element */
+#define QED_RDMA_MAX_SRQ_ELEM_PER_WQE      (RDMA_MAX_SGE_PER_RQ_WQE + 1)
+#define QED_RDMA_MAX_SGE_PER_SRQ_WQE        (RDMA_MAX_SGE_PER_RQ_WQE)
+#define QED_RDMA_SRQ_WQE_ELEM_SIZE          (16)
+#define QED_RDMA_MAX_SRQS                   (32 * 1024)
+
+#define QED_RDMA_MAX_CQE_32_BIT             (0x7FFFFFFF - 1)
+#define QED_RDMA_MAX_CQE_16_BIT             (0x7FFF - 1)
+
+enum qed_rdma_toggle_bit {
+       QED_RDMA_TOGGLE_BIT_CLEAR = 0,
+       QED_RDMA_TOGGLE_BIT_SET = 1
+};
+
+struct qed_bmap {
+       unsigned long *bitmap;
+       u32 max_count;
+};
+
+struct qed_rdma_info {
+       /* spin lock to protect bitmaps */
+       spinlock_t lock;
+
+       struct qed_bmap cq_map;
+       struct qed_bmap pd_map;
+       struct qed_bmap tid_map;
+       struct qed_bmap qp_map;
+       struct qed_bmap srq_map;
+       struct qed_bmap cid_map;
+       struct qed_bmap dpi_map;
+       struct qed_bmap toggle_bits;
+       struct qed_rdma_events events;
+       struct qed_rdma_device *dev;
+       struct qed_rdma_port *port;
+       u32 last_tid;
+       u8 num_cnqs;
+       u32 num_qps;
+       u32 num_mrs;
+       u16 queue_zone_base;
+       enum protocol_type proto;
+};
+
+struct qed_rdma_resize_cq_in_params {
+       u16 icid;
+       u32 cq_size;
+       bool pbl_two_level;
+       u64 pbl_ptr;
+       u16 pbl_num_pages;
+       u8 pbl_page_size_log;
+};
+
+struct qed_rdma_resize_cq_out_params {
+       u32 prod;
+       u32 cons;
+};
+
+struct qed_rdma_resize_cnq_in_params {
+       u32 cnq_id;
+       u32 pbl_page_size_log;
+       u64 pbl_ptr;
+};
+
+struct qed_rdma_qp {
+       struct regpair qp_handle;
+       struct regpair qp_handle_async;
+       u32 qpid;
+       u16 icid;
+       enum qed_roce_qp_state cur_state;
+       bool use_srq;
+       bool signal_all;
+       bool fmr_and_reserved_lkey;
+
+       bool incoming_rdma_read_en;
+       bool incoming_rdma_write_en;
+       bool incoming_atomic_en;
+       bool e2e_flow_control_en;
+
+       u16 pd;
+       u16 pkey;
+       u32 dest_qp;
+       u16 mtu;
+       u16 srq_id;
+       u8 traffic_class_tos;
+       u8 hop_limit_ttl;
+       u16 dpi;
+       u32 flow_label;
+       bool lb_indication;
+       u16 vlan_id;
+       u32 ack_timeout;
+       u8 retry_cnt;
+       u8 rnr_retry_cnt;
+       u8 min_rnr_nak_timer;
+       bool sqd_async;
+       union qed_gid sgid;
+       union qed_gid dgid;
+       enum roce_mode roce_mode;
+       u16 udp_src_port;
+       u8 stats_queue;
+
+       /* requeseter */
+       u8 max_rd_atomic_req;
+       u32 sq_psn;
+       u16 sq_cq_id;
+       u16 sq_num_pages;
+       dma_addr_t sq_pbl_ptr;
+       void *orq;
+       dma_addr_t orq_phys_addr;
+       u8 orq_num_pages;
+       bool req_offloaded;
+
+       /* responder */
+       u8 max_rd_atomic_resp;
+       u32 rq_psn;
+       u16 rq_cq_id;
+       u16 rq_num_pages;
+       dma_addr_t rq_pbl_ptr;
+       void *irq;
+       dma_addr_t irq_phys_addr;
+       u8 irq_num_pages;
+       bool resp_offloaded;
+
+       u8 remote_mac_addr[6];
+       u8 local_mac_addr[6];
+
+       void *shared_queue;
+       dma_addr_t shared_queue_phys_addr;
+};
+
+int
+qed_rdma_add_user(void *rdma_cxt,
+                 struct qed_rdma_add_user_out_params *out_params);
+int qed_rdma_alloc_pd(void *rdma_cxt, u16 *pd);
+int qed_rdma_alloc_tid(void *rdma_cxt, u32 *tid);
+int qed_rdma_deregister_tid(void *rdma_cxt, u32 tid);
+void qed_rdma_free_tid(void *rdma_cxt, u32 tid);
+struct qed_rdma_device *qed_rdma_query_device(void *rdma_cxt);
+struct qed_rdma_port *qed_rdma_query_port(void *rdma_cxt);
+int
+qed_rdma_register_tid(void *rdma_cxt,
+                     struct qed_rdma_register_tid_in_params *params);
+void qed_rdma_remove_user(void *rdma_cxt, u16 dpi);
+int qed_rdma_start(void *p_hwfn, struct qed_rdma_start_in_params *params);
+int qed_rdma_stop(void *rdma_cxt);
+u32 qed_rdma_get_sb_id(void *p_hwfn, u32 rel_sb_id);
+u32 qed_rdma_query_cau_timer_res(void *p_hwfn);
+void qed_rdma_cnq_prod_update(void *rdma_cxt, u8 cnq_index, u16 prod);
+void qed_rdma_resc_free(struct qed_hwfn *p_hwfn);
+void qed_async_roce_event(struct qed_hwfn *p_hwfn,
+                         struct event_ring_entry *p_eqe);
+int qed_rdma_destroy_qp(void *rdma_cxt, struct qed_rdma_qp *qp);
+int qed_rdma_modify_qp(void *rdma_cxt, struct qed_rdma_qp *qp,
+                      struct qed_rdma_modify_qp_in_params *params);
+int qed_rdma_query_qp(void *rdma_cxt, struct qed_rdma_qp *qp,
+                     struct qed_rdma_query_qp_out_params *out_params);
+
+#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
+void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
+#else
+void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) {}
+#endif
+#endif
index a548504..652c908 100644 (file)
@@ -61,6 +61,10 @@ union ramrod_data {
        struct vport_start_ramrod_data vport_start;
        struct vport_stop_ramrod_data vport_stop;
        struct vport_update_ramrod_data vport_update;
+       struct core_rx_start_ramrod_data core_rx_queue_start;
+       struct core_rx_stop_ramrod_data core_rx_queue_stop;
+       struct core_tx_start_ramrod_data core_tx_queue_start;
+       struct core_tx_stop_ramrod_data core_tx_queue_stop;
        struct vport_filter_update_ramrod_data vport_filter_update;
 
        struct rdma_init_func_ramrod_data rdma_init_func;
@@ -81,6 +85,7 @@ union ramrod_data {
        struct rdma_srq_create_ramrod_data rdma_create_srq;
        struct rdma_srq_destroy_ramrod_data rdma_destroy_srq;
        struct rdma_srq_modify_ramrod_data rdma_modify_srq;
+       struct roce_init_func_ramrod_data roce_init_func;
 
        struct iscsi_slow_path_hdr iscsi_empty;
        struct iscsi_init_ramrod_params iscsi_init;
index 349af18..caff415 100644 (file)
@@ -28,6 +28,9 @@
 #include "qed_reg_addr.h"
 #include "qed_sp.h"
 #include "qed_sriov.h"
+#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
+#include "qed_roce.h"
+#endif
 
 /***************************************************************************
 * Structures & Definitions
@@ -237,6 +240,11 @@ qed_async_event_completion(struct qed_hwfn *p_hwfn,
                           struct event_ring_entry *p_eqe)
 {
        switch (p_eqe->protocol_id) {
+#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
+       case PROTOCOLID_ROCE:
+               qed_async_roce_event(p_hwfn, p_eqe);
+               return 0;
+#endif
        case PROTOCOLID_COMMON:
                return qed_sriov_eqe_event(p_hwfn,
                                           p_eqe->opcode,
index a4a3cea..d2d6621 100644 (file)
@@ -1851,8 +1851,8 @@ static void qed_iov_vf_mbx_start_txq_resp(struct qed_hwfn *p_hwfn,
        if ((status == PFVF_STATUS_SUCCESS) && !b_legacy) {
                u16 qid = mbx->req_virt->start_txq.tx_qid;
 
-               p_tlv->offset = qed_db_addr(p_vf->vf_queues[qid].fw_cid,
-                                           DQ_DEMS_LEGACY);
+               p_tlv->offset = qed_db_addr_vf(p_vf->vf_queues[qid].fw_cid,
+                                              DQ_DEMS_LEGACY);
        }
 
        qed_iov_send_response(p_hwfn, p_ptt, p_vf, length, status);
index 85334ce..abf5bf1 100644 (file)
@@ -544,7 +544,7 @@ int qed_vf_pf_txq_start(struct qed_hwfn *p_hwfn,
                        u8 cid = p_iov->acquire_resp.resc.cid[tx_queue_id];
                        u32 db_addr;
 
-                       db_addr = qed_db_addr(cid, DQ_DEMS_LEGACY);
+                       db_addr = qed_db_addr_vf(cid, DQ_DEMS_LEGACY);
                        *pp_doorbell = (u8 __iomem *)p_hwfn->doorbells +
                                                     db_addr;
                }
index 74a4985..28dc589 100644 (file)
@@ -2,3 +2,4 @@ obj-$(CONFIG_QEDE) := qede.o
 
 qede-y := qede_main.o qede_ethtool.o
 qede-$(CONFIG_DCB) += qede_dcbnl.o
+qede-$(CONFIG_INFINIBAND_QEDR) += qede_roce.o
index e01adce..28c0e9f 100644 (file)
@@ -106,6 +106,13 @@ struct qede_vlan {
        bool configured;
 };
 
+struct qede_rdma_dev {
+       struct qedr_dev *qedr_dev;
+       struct list_head entry;
+       struct list_head roce_event_list;
+       struct workqueue_struct *roce_wq;
+};
+
 struct qede_dev {
        struct qed_dev                  *cdev;
        struct net_device               *ndev;
@@ -185,6 +192,8 @@ struct qede_dev {
        unsigned long                   sp_flags;
        u16                             vxlan_dst_port;
        u16                             geneve_dst_port;
+
+       struct qede_rdma_dev            rdma_info;
 };
 
 enum QEDE_STATE {
index 0e198fe..343038c 100644 (file)
@@ -36,7 +36,7 @@
 #include <linux/random.h>
 #include <net/ip6_checksum.h>
 #include <linux/bitops.h>
-
+#include <linux/qed/qede_roce.h>
 #include "qede.h"
 
 static char version[] =
@@ -193,8 +193,7 @@ static int qede_netdev_event(struct notifier_block *this, unsigned long event,
        struct ethtool_drvinfo drvinfo;
        struct qede_dev *edev;
 
-       /* Currently only support name change */
-       if (event != NETDEV_CHANGENAME)
+       if (event != NETDEV_CHANGENAME && event != NETDEV_CHANGEADDR)
                goto done;
 
        /* Check whether this is a qede device */
@@ -207,11 +206,18 @@ static int qede_netdev_event(struct notifier_block *this, unsigned long event,
                goto done;
        edev = netdev_priv(ndev);
 
-       /* Notify qed of the name change */
-       if (!edev->ops || !edev->ops->common)
-               goto done;
-       edev->ops->common->set_id(edev->cdev, edev->ndev->name,
-                                 "qede");
+       switch (event) {
+       case NETDEV_CHANGENAME:
+               /* Notify qed of the name change */
+               if (!edev->ops || !edev->ops->common)
+                       goto done;
+               edev->ops->common->set_id(edev->cdev, edev->ndev->name, "qede");
+               break;
+       case NETDEV_CHANGEADDR:
+               edev = netdev_priv(ndev);
+               qede_roce_event_changeaddr(edev);
+               break;
+       }
 
 done:
        return NOTIFY_DONE;
@@ -2545,10 +2551,14 @@ static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level,
 
        qede_init_ndev(edev);
 
+       rc = qede_roce_dev_add(edev);
+       if (rc)
+               goto err3;
+
        rc = register_netdev(edev->ndev);
        if (rc) {
                DP_NOTICE(edev, "Cannot register net-device\n");
-               goto err3;
+               goto err4;
        }
 
        edev->ops->common->set_id(cdev, edev->ndev->name, DRV_MODULE_VERSION);
@@ -2568,6 +2578,8 @@ static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level,
 
        return 0;
 
+err4:
+       qede_roce_dev_remove(edev);
 err3:
        free_netdev(edev->ndev);
 err2:
@@ -2614,8 +2626,11 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode)
        DP_INFO(edev, "Starting qede_remove\n");
 
        cancel_delayed_work_sync(&edev->sp_task);
+
        unregister_netdev(ndev);
 
+       qede_roce_dev_remove(edev);
+
        edev->ops->common->set_power_state(cdev, PCI_D0);
 
        pci_set_drvdata(pdev, NULL);
@@ -3512,6 +3527,7 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode)
 
        DP_INFO(edev, "Starting qede unload\n");
 
+       qede_roce_dev_event_close(edev);
        mutex_lock(&edev->qede_lock);
        edev->state = QEDE_STATE_CLOSED;
 
@@ -3612,6 +3628,7 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode)
        /* Query whether link is already-up */
        memset(&link_output, 0, sizeof(link_output));
        edev->ops->common->get_link(edev->cdev, &link_output);
+       qede_roce_dev_event_open(edev);
        qede_link_update(edev, &link_output);
 
        DP_INFO(edev, "Ending successfully qede load\n");
diff --git a/drivers/net/ethernet/qlogic/qede/qede_roce.c b/drivers/net/ethernet/qlogic/qede/qede_roce.c
new file mode 100644 (file)
index 0000000..9867f96
--- /dev/null
@@ -0,0 +1,314 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * 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
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/qed/qede_roce.h>
+#include "qede.h"
+
+static struct qedr_driver *qedr_drv;
+static LIST_HEAD(qedr_dev_list);
+static DEFINE_MUTEX(qedr_dev_list_lock);
+
+bool qede_roce_supported(struct qede_dev *dev)
+{
+       return dev->dev_info.common.rdma_supported;
+}
+
+static void _qede_roce_dev_add(struct qede_dev *edev)
+{
+       if (!qedr_drv)
+               return;
+
+       edev->rdma_info.qedr_dev = qedr_drv->add(edev->cdev, edev->pdev,
+                                                edev->ndev);
+}
+
+static int qede_roce_create_wq(struct qede_dev *edev)
+{
+       INIT_LIST_HEAD(&edev->rdma_info.roce_event_list);
+       edev->rdma_info.roce_wq = create_singlethread_workqueue("roce_wq");
+       if (!edev->rdma_info.roce_wq) {
+               DP_NOTICE(edev, "qedr: Could not create workqueue\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void qede_roce_cleanup_event(struct qede_dev *edev)
+{
+       struct list_head *head = &edev->rdma_info.roce_event_list;
+       struct qede_roce_event_work *event_node;
+
+       flush_workqueue(edev->rdma_info.roce_wq);
+       while (!list_empty(head)) {
+               event_node = list_entry(head->next, struct qede_roce_event_work,
+                                       list);
+               cancel_work_sync(&event_node->work);
+               list_del(&event_node->list);
+               kfree(event_node);
+       }
+}
+
+static void qede_roce_destroy_wq(struct qede_dev *edev)
+{
+       qede_roce_cleanup_event(edev);
+       destroy_workqueue(edev->rdma_info.roce_wq);
+}
+
+int qede_roce_dev_add(struct qede_dev *edev)
+{
+       int rc = 0;
+
+       if (qede_roce_supported(edev)) {
+               rc = qede_roce_create_wq(edev);
+               if (rc)
+                       return rc;
+
+               INIT_LIST_HEAD(&edev->rdma_info.entry);
+               mutex_lock(&qedr_dev_list_lock);
+               list_add_tail(&edev->rdma_info.entry, &qedr_dev_list);
+               _qede_roce_dev_add(edev);
+               mutex_unlock(&qedr_dev_list_lock);
+       }
+
+       return rc;
+}
+
+static void _qede_roce_dev_remove(struct qede_dev *edev)
+{
+       if (qedr_drv && qedr_drv->remove && edev->rdma_info.qedr_dev)
+               qedr_drv->remove(edev->rdma_info.qedr_dev);
+       edev->rdma_info.qedr_dev = NULL;
+}
+
+void qede_roce_dev_remove(struct qede_dev *edev)
+{
+       if (!qede_roce_supported(edev))
+               return;
+
+       qede_roce_destroy_wq(edev);
+       mutex_lock(&qedr_dev_list_lock);
+       _qede_roce_dev_remove(edev);
+       list_del(&edev->rdma_info.entry);
+       mutex_unlock(&qedr_dev_list_lock);
+}
+
+static void _qede_roce_dev_open(struct qede_dev *edev)
+{
+       if (qedr_drv && edev->rdma_info.qedr_dev && qedr_drv->notify)
+               qedr_drv->notify(edev->rdma_info.qedr_dev, QEDE_UP);
+}
+
+static void qede_roce_dev_open(struct qede_dev *edev)
+{
+       if (!qede_roce_supported(edev))
+               return;
+
+       mutex_lock(&qedr_dev_list_lock);
+       _qede_roce_dev_open(edev);
+       mutex_unlock(&qedr_dev_list_lock);
+}
+
+static void _qede_roce_dev_close(struct qede_dev *edev)
+{
+       if (qedr_drv && edev->rdma_info.qedr_dev && qedr_drv->notify)
+               qedr_drv->notify(edev->rdma_info.qedr_dev, QEDE_DOWN);
+}
+
+static void qede_roce_dev_close(struct qede_dev *edev)
+{
+       if (!qede_roce_supported(edev))
+               return;
+
+       mutex_lock(&qedr_dev_list_lock);
+       _qede_roce_dev_close(edev);
+       mutex_unlock(&qedr_dev_list_lock);
+}
+
+static void qede_roce_dev_shutdown(struct qede_dev *edev)
+{
+       if (!qede_roce_supported(edev))
+               return;
+
+       mutex_lock(&qedr_dev_list_lock);
+       if (qedr_drv && edev->rdma_info.qedr_dev && qedr_drv->notify)
+               qedr_drv->notify(edev->rdma_info.qedr_dev, QEDE_CLOSE);
+       mutex_unlock(&qedr_dev_list_lock);
+}
+
+int qede_roce_register_driver(struct qedr_driver *drv)
+{
+       struct qede_dev *edev;
+       u8 qedr_counter = 0;
+
+       mutex_lock(&qedr_dev_list_lock);
+       if (qedr_drv) {
+               mutex_unlock(&qedr_dev_list_lock);
+               return -EINVAL;
+       }
+       qedr_drv = drv;
+
+       list_for_each_entry(edev, &qedr_dev_list, rdma_info.entry) {
+               struct net_device *ndev;
+
+               qedr_counter++;
+               _qede_roce_dev_add(edev);
+               ndev = edev->ndev;
+               if (netif_running(ndev) && netif_oper_up(ndev))
+                       _qede_roce_dev_open(edev);
+       }
+       mutex_unlock(&qedr_dev_list_lock);
+
+       DP_INFO(edev, "qedr: discovered and registered %d RoCE funcs\n",
+               qedr_counter);
+
+       return 0;
+}
+EXPORT_SYMBOL(qede_roce_register_driver);
+
+void qede_roce_unregister_driver(struct qedr_driver *drv)
+{
+       struct qede_dev *edev;
+
+       mutex_lock(&qedr_dev_list_lock);
+       list_for_each_entry(edev, &qedr_dev_list, rdma_info.entry) {
+               if (edev->rdma_info.qedr_dev)
+                       _qede_roce_dev_remove(edev);
+       }
+       qedr_drv = NULL;
+       mutex_unlock(&qedr_dev_list_lock);
+}
+EXPORT_SYMBOL(qede_roce_unregister_driver);
+
+static void qede_roce_changeaddr(struct qede_dev *edev)
+{
+       if (!qede_roce_supported(edev))
+               return;
+
+       if (qedr_drv && edev->rdma_info.qedr_dev && qedr_drv->notify)
+               qedr_drv->notify(edev->rdma_info.qedr_dev, QEDE_CHANGE_ADDR);
+}
+
+struct qede_roce_event_work *qede_roce_get_free_event_node(struct qede_dev
+                                                          *edev)
+{
+       struct qede_roce_event_work *event_node = NULL;
+       struct list_head *list_node = NULL;
+       bool found = false;
+
+       list_for_each(list_node, &edev->rdma_info.roce_event_list) {
+               event_node = list_entry(list_node, struct qede_roce_event_work,
+                                       list);
+               if (!work_pending(&event_node->work)) {
+                       found = true;
+                       break;
+               }
+       }
+
+       if (!found) {
+               event_node = kzalloc(sizeof(*event_node), GFP_KERNEL);
+               if (!event_node) {
+                       DP_NOTICE(edev,
+                                 "qedr: Could not allocate memory for roce work\n");
+                       return NULL;
+               }
+               list_add_tail(&event_node->list,
+                             &edev->rdma_info.roce_event_list);
+       }
+
+       return event_node;
+}
+
+static void qede_roce_handle_event(struct work_struct *work)
+{
+       struct qede_roce_event_work *event_node;
+       enum qede_roce_event event;
+       struct qede_dev *edev;
+
+       event_node = container_of(work, struct qede_roce_event_work, work);
+       event = event_node->event;
+       edev = event_node->ptr;
+
+       switch (event) {
+       case QEDE_UP:
+               qede_roce_dev_open(edev);
+               break;
+       case QEDE_DOWN:
+               qede_roce_dev_close(edev);
+               break;
+       case QEDE_CLOSE:
+               qede_roce_dev_shutdown(edev);
+               break;
+       case QEDE_CHANGE_ADDR:
+               qede_roce_changeaddr(edev);
+               break;
+       default:
+               DP_NOTICE(edev, "Invalid roce event %d", event);
+       }
+}
+
+static void qede_roce_add_event(struct qede_dev *edev,
+                               enum qede_roce_event event)
+{
+       struct qede_roce_event_work *event_node;
+
+       if (!edev->rdma_info.qedr_dev)
+               return;
+
+       event_node = qede_roce_get_free_event_node(edev);
+       if (!event_node)
+               return;
+
+       event_node->event = event;
+       event_node->ptr = edev;
+
+       INIT_WORK(&event_node->work, qede_roce_handle_event);
+       queue_work(edev->rdma_info.roce_wq, &event_node->work);
+}
+
+void qede_roce_dev_event_open(struct qede_dev *edev)
+{
+       qede_roce_add_event(edev, QEDE_UP);
+}
+
+void qede_roce_dev_event_close(struct qede_dev *edev)
+{
+       qede_roce_add_event(edev, QEDE_DOWN);
+}
+
+void qede_roce_event_changeaddr(struct qede_dev *edev)
+{
+       qede_roce_add_event(edev, QEDE_CHANGE_ADDR);
+}
index 3d2c05a..75c1b53 100644 (file)
@@ -740,9 +740,14 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt)
 
        /* Base address is the first address */
        res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               ret = -EINVAL;
+               goto error_put_device;
+       }
+
        phy->base = ioremap(res->start, resource_size(res));
-       if (IS_ERR(phy->base)) {
-               ret = PTR_ERR(phy->base);
+       if (!phy->base) {
+               ret = -ENOMEM;
                goto error_put_device;
        }
 
@@ -750,8 +755,8 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt)
        res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 1);
        if (res) {
                phy->digital = ioremap(res->start, resource_size(res));
-               if (IS_ERR(phy->digital)) {
-                       ret = PTR_ERR(phy->digital);
+               if (!phy->digital) {
+                       ret = -ENOMEM;
                        goto error_unmap_base;
                }
        }
index 2e481b9..86280b7 100644 (file)
@@ -263,6 +263,7 @@ no_sysfs:
 no_device:
        mutex_destroy(&ptp->tsevq_mux);
        mutex_destroy(&ptp->pincfg_mux);
+       ida_simple_remove(&ptp_clocks_map, index);
 no_slot:
        kfree(ptp);
 no_memory:
index 1902763..734deb0 100644 (file)
@@ -674,6 +674,7 @@ union event_ring_data {
        struct iscsi_eqe_data iscsi_info;
        struct malicious_vf_eqe_data malicious_vf;
        struct initial_cleanup_eqe_data vf_init_cleanup;
+       struct regpair roce_handle;
 };
 
 /* Event Ring Entry */
index e4546ab..f9ae903 100644 (file)
@@ -34,6 +34,8 @@ enum dcbx_protocol_type {
        DCBX_MAX_PROTOCOL_TYPE
 };
 
+#define QED_ROCE_PROTOCOL_INDEX (3)
+
 #ifdef CONFIG_DCB
 #define QED_LLDP_CHASSIS_ID_STAT_LEN 4
 #define QED_LLDP_PORT_ID_STAT_LEN 4
@@ -260,15 +262,15 @@ struct qed_dev_info {
        /* MFW version */
        u32             mfw_rev;
 
-       bool rdma_supported;
-
        u32             flash_size;
        u8              mf_mode;
        bool            tx_switching;
+       bool            rdma_supported;
 };
 
 enum qed_sb_type {
        QED_SB_TYPE_L2_QUEUE,
+       QED_SB_TYPE_CNQ,
 };
 
 enum qed_protocol {
@@ -627,8 +629,9 @@ enum DP_MODULE {
        QED_MSG_SP      = 0x100000,
        QED_MSG_STORAGE = 0x200000,
        QED_MSG_CXT     = 0x800000,
+       QED_MSG_LL2     = 0x1000000,
        QED_MSG_ILT     = 0x2000000,
-       QED_MSG_ROCE    = 0x4000000,
+       QED_MSG_RDMA    = 0x4000000,
        QED_MSG_DEBUG   = 0x8000000,
        /* to be added...up to 0x8000000 */
 };
diff --git a/include/linux/qed/qed_ll2_if.h b/include/linux/qed/qed_ll2_if.h
new file mode 100644 (file)
index 0000000..fd75c26
--- /dev/null
@@ -0,0 +1,139 @@
+/* QLogic qed NIC Driver
+ *
+ * Copyright (c) 2015 QLogic Corporation
+ *
+ * This software is available 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.
+ */
+
+#ifndef _QED_LL2_IF_H
+#define _QED_LL2_IF_H
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/qed/qed_if.h>
+
+struct qed_ll2_stats {
+       u64 gsi_invalid_hdr;
+       u64 gsi_invalid_pkt_length;
+       u64 gsi_unsupported_pkt_typ;
+       u64 gsi_crcchksm_error;
+
+       u64 packet_too_big_discard;
+       u64 no_buff_discard;
+
+       u64 rcv_ucast_bytes;
+       u64 rcv_mcast_bytes;
+       u64 rcv_bcast_bytes;
+       u64 rcv_ucast_pkts;
+       u64 rcv_mcast_pkts;
+       u64 rcv_bcast_pkts;
+
+       u64 sent_ucast_bytes;
+       u64 sent_mcast_bytes;
+       u64 sent_bcast_bytes;
+       u64 sent_ucast_pkts;
+       u64 sent_mcast_pkts;
+       u64 sent_bcast_pkts;
+};
+
+#define QED_LL2_UNUSED_HANDLE   (0xff)
+
+struct qed_ll2_cb_ops {
+       int (*rx_cb)(void *, struct sk_buff *, u32, u32);
+       int (*tx_cb)(void *, struct sk_buff *, bool);
+};
+
+struct qed_ll2_params {
+       u16 mtu;
+       bool drop_ttl0_packets;
+       bool rx_vlan_stripping;
+       u8 tx_tc;
+       bool frags_mapped;
+       u8 ll2_mac_address[ETH_ALEN];
+};
+
+struct qed_ll2_ops {
+/**
+ * @brief start - initializes ll2
+ *
+ * @param cdev
+ * @param params - protocol driver configuration for the ll2.
+ *
+ * @return 0 on success, otherwise error value.
+ */
+       int (*start)(struct qed_dev *cdev, struct qed_ll2_params *params);
+
+/**
+ * @brief stop - stops the ll2
+ *
+ * @param cdev
+ *
+ * @return 0 on success, otherwise error value.
+ */
+       int (*stop)(struct qed_dev *cdev);
+
+/**
+ * @brief start_xmit - transmits an skb over the ll2 interface
+ *
+ * @param cdev
+ * @param skb
+ *
+ * @return 0 on success, otherwise error value.
+ */
+       int (*start_xmit)(struct qed_dev *cdev, struct sk_buff *skb);
+
+/**
+ * @brief register_cb_ops - protocol driver register the callback for Rx/Tx
+ * packets. Should be called before `start'.
+ *
+ * @param cdev
+ * @param cookie - to be passed to the callback functions.
+ * @param ops - the callback functions to register for Rx / Tx.
+ *
+ * @return 0 on success, otherwise error value.
+ */
+       void (*register_cb_ops)(struct qed_dev *cdev,
+                               const struct qed_ll2_cb_ops *ops,
+                               void *cookie);
+
+/**
+ * @brief get LL2 related statistics
+ *
+ * @param cdev
+ * @param stats - pointer to struct that would be filled with stats
+ *
+ * @return 0 on success, error otherwise.
+ */
+       int (*get_stats)(struct qed_dev *cdev, struct qed_ll2_stats *stats);
+};
+
+#ifdef CONFIG_QED_LL2
+int qed_ll2_alloc_if(struct qed_dev *);
+void qed_ll2_dealloc_if(struct qed_dev *);
+#else
+static const struct qed_ll2_ops qed_ll2_ops_pass = {
+       .start = NULL,
+       .stop = NULL,
+       .start_xmit = NULL,
+       .register_cb_ops = NULL,
+       .get_stats = NULL,
+};
+
+static inline int qed_ll2_alloc_if(struct qed_dev *cdev)
+{
+       return 0;
+}
+
+static inline void qed_ll2_dealloc_if(struct qed_dev *cdev)
+{
+}
+#endif
+#endif
diff --git a/include/linux/qed/qed_roce_if.h b/include/linux/qed/qed_roce_if.h
new file mode 100644 (file)
index 0000000..53047d3
--- /dev/null
@@ -0,0 +1,604 @@
+/* QLogic qed NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * 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
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef _QED_ROCE_IF_H
+#define _QED_ROCE_IF_H
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/qed/qed_if.h>
+#include <linux/qed/qed_ll2_if.h>
+#include <linux/qed/rdma_common.h>
+
+enum qed_roce_ll2_tx_dest {
+       /* Light L2 TX Destination to the Network */
+       QED_ROCE_LL2_TX_DEST_NW,
+
+       /* Light L2 TX Destination to the Loopback */
+       QED_ROCE_LL2_TX_DEST_LB,
+       QED_ROCE_LL2_TX_DEST_MAX
+};
+
+#define QED_RDMA_MAX_CNQ_SIZE               (0xFFFF)
+
+/* rdma interface */
+
+enum qed_roce_qp_state {
+       QED_ROCE_QP_STATE_RESET,
+       QED_ROCE_QP_STATE_INIT,
+       QED_ROCE_QP_STATE_RTR,
+       QED_ROCE_QP_STATE_RTS,
+       QED_ROCE_QP_STATE_SQD,
+       QED_ROCE_QP_STATE_ERR,
+       QED_ROCE_QP_STATE_SQE
+};
+
+enum qed_rdma_tid_type {
+       QED_RDMA_TID_REGISTERED_MR,
+       QED_RDMA_TID_FMR,
+       QED_RDMA_TID_MW_TYPE1,
+       QED_RDMA_TID_MW_TYPE2A
+};
+
+struct qed_rdma_events {
+       void *context;
+       void (*affiliated_event)(void *context, u8 fw_event_code,
+                                void *fw_handle);
+       void (*unaffiliated_event)(void *context, u8 event_code);
+};
+
+struct qed_rdma_device {
+       u32 vendor_id;
+       u32 vendor_part_id;
+       u32 hw_ver;
+       u64 fw_ver;
+
+       u64 node_guid;
+       u64 sys_image_guid;
+
+       u8 max_cnq;
+       u8 max_sge;
+       u8 max_srq_sge;
+       u16 max_inline;
+       u32 max_wqe;
+       u32 max_srq_wqe;
+       u8 max_qp_resp_rd_atomic_resc;
+       u8 max_qp_req_rd_atomic_resc;
+       u64 max_dev_resp_rd_atomic_resc;
+       u32 max_cq;
+       u32 max_qp;
+       u32 max_srq;
+       u32 max_mr;
+       u64 max_mr_size;
+       u32 max_cqe;
+       u32 max_mw;
+       u32 max_fmr;
+       u32 max_mr_mw_fmr_pbl;
+       u64 max_mr_mw_fmr_size;
+       u32 max_pd;
+       u32 max_ah;
+       u8 max_pkey;
+       u16 max_srq_wr;
+       u8 max_stats_queues;
+       u32 dev_caps;
+
+       /* Abilty to support RNR-NAK generation */
+
+#define QED_RDMA_DEV_CAP_RNR_NAK_MASK                           0x1
+#define QED_RDMA_DEV_CAP_RNR_NAK_SHIFT                  0
+       /* Abilty to support shutdown port */
+#define QED_RDMA_DEV_CAP_SHUTDOWN_PORT_MASK                     0x1
+#define QED_RDMA_DEV_CAP_SHUTDOWN_PORT_SHIFT                    1
+       /* Abilty to support port active event */
+#define QED_RDMA_DEV_CAP_PORT_ACTIVE_EVENT_MASK         0x1
+#define QED_RDMA_DEV_CAP_PORT_ACTIVE_EVENT_SHIFT                2
+       /* Abilty to support port change event */
+#define QED_RDMA_DEV_CAP_PORT_CHANGE_EVENT_MASK         0x1
+#define QED_RDMA_DEV_CAP_PORT_CHANGE_EVENT_SHIFT                3
+       /* Abilty to support system image GUID */
+#define QED_RDMA_DEV_CAP_SYS_IMAGE_MASK                 0x1
+#define QED_RDMA_DEV_CAP_SYS_IMAGE_SHIFT                        4
+       /* Abilty to support bad P_Key counter support */
+#define QED_RDMA_DEV_CAP_BAD_PKEY_CNT_MASK                      0x1
+#define QED_RDMA_DEV_CAP_BAD_PKEY_CNT_SHIFT                     5
+       /* Abilty to support atomic operations */
+#define QED_RDMA_DEV_CAP_ATOMIC_OP_MASK                 0x1
+#define QED_RDMA_DEV_CAP_ATOMIC_OP_SHIFT                        6
+#define QED_RDMA_DEV_CAP_RESIZE_CQ_MASK                 0x1
+#define QED_RDMA_DEV_CAP_RESIZE_CQ_SHIFT                        7
+       /* Abilty to support modifying the maximum number of
+        * outstanding work requests per QP
+        */
+#define QED_RDMA_DEV_CAP_RESIZE_MAX_WR_MASK                     0x1
+#define QED_RDMA_DEV_CAP_RESIZE_MAX_WR_SHIFT                    8
+       /* Abilty to support automatic path migration */
+#define QED_RDMA_DEV_CAP_AUTO_PATH_MIG_MASK                     0x1
+#define QED_RDMA_DEV_CAP_AUTO_PATH_MIG_SHIFT                    9
+       /* Abilty to support the base memory management extensions */
+#define QED_RDMA_DEV_CAP_BASE_MEMORY_EXT_MASK                   0x1
+#define QED_RDMA_DEV_CAP_BASE_MEMORY_EXT_SHIFT          10
+#define QED_RDMA_DEV_CAP_BASE_QUEUE_EXT_MASK                    0x1
+#define QED_RDMA_DEV_CAP_BASE_QUEUE_EXT_SHIFT                   11
+       /* Abilty to support multipile page sizes per memory region */
+#define QED_RDMA_DEV_CAP_MULTI_PAGE_PER_MR_EXT_MASK             0x1
+#define QED_RDMA_DEV_CAP_MULTI_PAGE_PER_MR_EXT_SHIFT            12
+       /* Abilty to support block list physical buffer list */
+#define QED_RDMA_DEV_CAP_BLOCK_MODE_MASK                        0x1
+#define QED_RDMA_DEV_CAP_BLOCK_MODE_SHIFT                       13
+       /* Abilty to support zero based virtual addresses */
+#define QED_RDMA_DEV_CAP_ZBVA_MASK                              0x1
+#define QED_RDMA_DEV_CAP_ZBVA_SHIFT                             14
+       /* Abilty to support local invalidate fencing */
+#define QED_RDMA_DEV_CAP_LOCAL_INV_FENCE_MASK                   0x1
+#define QED_RDMA_DEV_CAP_LOCAL_INV_FENCE_SHIFT          15
+       /* Abilty to support Loopback on QP */
+#define QED_RDMA_DEV_CAP_LB_INDICATOR_MASK                      0x1
+#define QED_RDMA_DEV_CAP_LB_INDICATOR_SHIFT                     16
+       u64 page_size_caps;
+       u8 dev_ack_delay;
+       u32 reserved_lkey;
+       u32 bad_pkey_counter;
+       struct qed_rdma_events events;
+};
+
+enum qed_port_state {
+       QED_RDMA_PORT_UP,
+       QED_RDMA_PORT_DOWN,
+};
+
+enum qed_roce_capability {
+       QED_ROCE_V1 = 1 << 0,
+       QED_ROCE_V2 = 1 << 1,
+};
+
+struct qed_rdma_port {
+       enum qed_port_state port_state;
+       int link_speed;
+       u64 max_msg_size;
+       u8 source_gid_table_len;
+       void *source_gid_table_ptr;
+       u8 pkey_table_len;
+       void *pkey_table_ptr;
+       u32 pkey_bad_counter;
+       enum qed_roce_capability capability;
+};
+
+struct qed_rdma_cnq_params {
+       u8 num_pbl_pages;
+       u64 pbl_ptr;
+};
+
+/* The CQ Mode affects the CQ doorbell transaction size.
+ * 64/32 bit machines should configure to 32/16 bits respectively.
+ */
+enum qed_rdma_cq_mode {
+       QED_RDMA_CQ_MODE_16_BITS,
+       QED_RDMA_CQ_MODE_32_BITS,
+};
+
+struct qed_roce_dcqcn_params {
+       u8 notification_point;
+       u8 reaction_point;
+
+       /* fields for notification point */
+       u32 cnp_send_timeout;
+
+       /* fields for reaction point */
+       u32 rl_bc_rate;
+       u16 rl_max_rate;
+       u16 rl_r_ai;
+       u16 rl_r_hai;
+       u16 dcqcn_g;
+       u32 dcqcn_k_us;
+       u32 dcqcn_timeout_us;
+};
+
+struct qed_rdma_start_in_params {
+       struct qed_rdma_events *events;
+       struct qed_rdma_cnq_params cnq_pbl_list[128];
+       u8 desired_cnq;
+       enum qed_rdma_cq_mode cq_mode;
+       struct qed_roce_dcqcn_params dcqcn_params;
+       u16 max_mtu;
+       u8 mac_addr[ETH_ALEN];
+       u8 iwarp_flags;
+};
+
+struct qed_rdma_add_user_out_params {
+       u16 dpi;
+       u64 dpi_addr;
+       u64 dpi_phys_addr;
+       u32 dpi_size;
+};
+
+enum roce_mode {
+       ROCE_V1,
+       ROCE_V2_IPV4,
+       ROCE_V2_IPV6,
+       MAX_ROCE_MODE
+};
+
+union qed_gid {
+       u8 bytes[16];
+       u16 words[8];
+       u32 dwords[4];
+       u64 qwords[2];
+       u32 ipv4_addr;
+};
+
+struct qed_rdma_register_tid_in_params {
+       u32 itid;
+       enum qed_rdma_tid_type tid_type;
+       u8 key;
+       u16 pd;
+       bool local_read;
+       bool local_write;
+       bool remote_read;
+       bool remote_write;
+       bool remote_atomic;
+       bool mw_bind;
+       u64 pbl_ptr;
+       bool pbl_two_level;
+       u8 pbl_page_size_log;
+       u8 page_size_log;
+       u32 fbo;
+       u64 length;
+       u64 vaddr;
+       bool zbva;
+       bool phy_mr;
+       bool dma_mr;
+
+       bool dif_enabled;
+       u64 dif_error_addr;
+       u64 dif_runt_addr;
+};
+
+struct qed_rdma_create_cq_in_params {
+       u32 cq_handle_lo;
+       u32 cq_handle_hi;
+       u32 cq_size;
+       u16 dpi;
+       bool pbl_two_level;
+       u64 pbl_ptr;
+       u16 pbl_num_pages;
+       u8 pbl_page_size_log;
+       u8 cnq_id;
+       u16 int_timeout;
+};
+
+struct qed_rdma_create_srq_in_params {
+       u64 pbl_base_addr;
+       u64 prod_pair_addr;
+       u16 num_pages;
+       u16 pd_id;
+       u16 page_size;
+};
+
+struct qed_rdma_destroy_cq_in_params {
+       u16 icid;
+};
+
+struct qed_rdma_destroy_cq_out_params {
+       u16 num_cq_notif;
+};
+
+struct qed_rdma_create_qp_in_params {
+       u32 qp_handle_lo;
+       u32 qp_handle_hi;
+       u32 qp_handle_async_lo;
+       u32 qp_handle_async_hi;
+       bool use_srq;
+       bool signal_all;
+       bool fmr_and_reserved_lkey;
+       u16 pd;
+       u16 dpi;
+       u16 sq_cq_id;
+       u16 sq_num_pages;
+       u64 sq_pbl_ptr;
+       u8 max_sq_sges;
+       u16 rq_cq_id;
+       u16 rq_num_pages;
+       u64 rq_pbl_ptr;
+       u16 srq_id;
+       u8 stats_queue;
+};
+
+struct qed_rdma_create_qp_out_params {
+       u32 qp_id;
+       u16 icid;
+       void *rq_pbl_virt;
+       dma_addr_t rq_pbl_phys;
+       void *sq_pbl_virt;
+       dma_addr_t sq_pbl_phys;
+};
+
+struct qed_rdma_modify_qp_in_params {
+       u32 modify_flags;
+#define QED_RDMA_MODIFY_QP_VALID_NEW_STATE_MASK               0x1
+#define QED_RDMA_MODIFY_QP_VALID_NEW_STATE_SHIFT              0
+#define QED_ROCE_MODIFY_QP_VALID_PKEY_MASK                    0x1
+#define QED_ROCE_MODIFY_QP_VALID_PKEY_SHIFT                   1
+#define QED_RDMA_MODIFY_QP_VALID_RDMA_OPS_EN_MASK             0x1
+#define QED_RDMA_MODIFY_QP_VALID_RDMA_OPS_EN_SHIFT            2
+#define QED_ROCE_MODIFY_QP_VALID_DEST_QP_MASK                 0x1
+#define QED_ROCE_MODIFY_QP_VALID_DEST_QP_SHIFT                3
+#define QED_ROCE_MODIFY_QP_VALID_ADDRESS_VECTOR_MASK          0x1
+#define QED_ROCE_MODIFY_QP_VALID_ADDRESS_VECTOR_SHIFT         4
+#define QED_ROCE_MODIFY_QP_VALID_RQ_PSN_MASK                  0x1
+#define QED_ROCE_MODIFY_QP_VALID_RQ_PSN_SHIFT                 5
+#define QED_ROCE_MODIFY_QP_VALID_SQ_PSN_MASK                  0x1
+#define QED_ROCE_MODIFY_QP_VALID_SQ_PSN_SHIFT                 6
+#define QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_REQ_MASK       0x1
+#define QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_REQ_SHIFT      7
+#define QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_RESP_MASK      0x1
+#define QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_RESP_SHIFT     8
+#define QED_ROCE_MODIFY_QP_VALID_ACK_TIMEOUT_MASK             0x1
+#define QED_ROCE_MODIFY_QP_VALID_ACK_TIMEOUT_SHIFT            9
+#define QED_ROCE_MODIFY_QP_VALID_RETRY_CNT_MASK               0x1
+#define QED_ROCE_MODIFY_QP_VALID_RETRY_CNT_SHIFT              10
+#define QED_ROCE_MODIFY_QP_VALID_RNR_RETRY_CNT_MASK           0x1
+#define QED_ROCE_MODIFY_QP_VALID_RNR_RETRY_CNT_SHIFT          11
+#define QED_ROCE_MODIFY_QP_VALID_MIN_RNR_NAK_TIMER_MASK       0x1
+#define QED_ROCE_MODIFY_QP_VALID_MIN_RNR_NAK_TIMER_SHIFT      12
+#define QED_ROCE_MODIFY_QP_VALID_E2E_FLOW_CONTROL_EN_MASK     0x1
+#define QED_ROCE_MODIFY_QP_VALID_E2E_FLOW_CONTROL_EN_SHIFT    13
+#define QED_ROCE_MODIFY_QP_VALID_ROCE_MODE_MASK               0x1
+#define QED_ROCE_MODIFY_QP_VALID_ROCE_MODE_SHIFT              14
+
+       enum qed_roce_qp_state new_state;
+       u16 pkey;
+       bool incoming_rdma_read_en;
+       bool incoming_rdma_write_en;
+       bool incoming_atomic_en;
+       bool e2e_flow_control_en;
+       u32 dest_qp;
+       bool lb_indication;
+       u16 mtu;
+       u8 traffic_class_tos;
+       u8 hop_limit_ttl;
+       u32 flow_label;
+       union qed_gid sgid;
+       union qed_gid dgid;
+       u16 udp_src_port;
+
+       u16 vlan_id;
+
+       u32 rq_psn;
+       u32 sq_psn;
+       u8 max_rd_atomic_resp;
+       u8 max_rd_atomic_req;
+       u32 ack_timeout;
+       u8 retry_cnt;
+       u8 rnr_retry_cnt;
+       u8 min_rnr_nak_timer;
+       bool sqd_async;
+       u8 remote_mac_addr[6];
+       u8 local_mac_addr[6];
+       bool use_local_mac;
+       enum roce_mode roce_mode;
+};
+
+struct qed_rdma_query_qp_out_params {
+       enum qed_roce_qp_state state;
+       u32 rq_psn;
+       u32 sq_psn;
+       bool draining;
+       u16 mtu;
+       u32 dest_qp;
+       bool incoming_rdma_read_en;
+       bool incoming_rdma_write_en;
+       bool incoming_atomic_en;
+       bool e2e_flow_control_en;
+       union qed_gid sgid;
+       union qed_gid dgid;
+       u32 flow_label;
+       u8 hop_limit_ttl;
+       u8 traffic_class_tos;
+       u32 timeout;
+       u8 rnr_retry;
+       u8 retry_cnt;
+       u8 min_rnr_nak_timer;
+       u16 pkey_index;
+       u8 max_rd_atomic;
+       u8 max_dest_rd_atomic;
+       bool sqd_async;
+};
+
+struct qed_rdma_create_srq_out_params {
+       u16 srq_id;
+};
+
+struct qed_rdma_destroy_srq_in_params {
+       u16 srq_id;
+};
+
+struct qed_rdma_modify_srq_in_params {
+       u32 wqe_limit;
+       u16 srq_id;
+};
+
+struct qed_rdma_stats_out_params {
+       u64 sent_bytes;
+       u64 sent_pkts;
+       u64 rcv_bytes;
+       u64 rcv_pkts;
+};
+
+struct qed_rdma_counters_out_params {
+       u64 pd_count;
+       u64 max_pd;
+       u64 dpi_count;
+       u64 max_dpi;
+       u64 cq_count;
+       u64 max_cq;
+       u64 qp_count;
+       u64 max_qp;
+       u64 tid_count;
+       u64 max_tid;
+};
+
+#define QED_ROCE_TX_HEAD_FAILURE        (1)
+#define QED_ROCE_TX_FRAG_FAILURE        (2)
+
+struct qed_roce_ll2_header {
+       void *vaddr;
+       dma_addr_t baddr;
+       size_t len;
+};
+
+struct qed_roce_ll2_buffer {
+       dma_addr_t baddr;
+       size_t len;
+};
+
+struct qed_roce_ll2_packet {
+       struct qed_roce_ll2_header header;
+       int n_seg;
+       struct qed_roce_ll2_buffer payload[RDMA_MAX_SGE_PER_SQ_WQE];
+       int roce_mode;
+       enum qed_roce_ll2_tx_dest tx_dest;
+};
+
+struct qed_roce_ll2_tx_params {
+       int reserved;
+};
+
+struct qed_roce_ll2_rx_params {
+       u16 vlan_id;
+       u8 smac[ETH_ALEN];
+       int rc;
+};
+
+struct qed_roce_ll2_cbs {
+       void (*tx_cb)(void *pdev, struct qed_roce_ll2_packet *pkt);
+
+       void (*rx_cb)(void *pdev, struct qed_roce_ll2_packet *pkt,
+                     struct qed_roce_ll2_rx_params *params);
+};
+
+struct qed_roce_ll2_params {
+       u16 max_rx_buffers;
+       u16 max_tx_buffers;
+       u16 mtu;
+       u8 mac_address[ETH_ALEN];
+       struct qed_roce_ll2_cbs cbs;
+       void *cb_cookie;
+};
+
+struct qed_roce_ll2_info {
+       u8 handle;
+       struct qed_roce_ll2_cbs cbs;
+       u8 mac_address[ETH_ALEN];
+       void *cb_cookie;
+
+       /* Lock to protect ll2 */
+       struct mutex lock;
+};
+
+enum qed_rdma_type {
+       QED_RDMA_TYPE_ROCE,
+};
+
+struct qed_dev_rdma_info {
+       struct qed_dev_info common;
+       enum qed_rdma_type rdma_type;
+};
+
+struct qed_rdma_ops {
+       const struct qed_common_ops *common;
+
+       int (*fill_dev_info)(struct qed_dev *cdev,
+                            struct qed_dev_rdma_info *info);
+       void *(*rdma_get_rdma_ctx)(struct qed_dev *cdev);
+
+       int (*rdma_init)(struct qed_dev *dev,
+                        struct qed_rdma_start_in_params *iparams);
+
+       int (*rdma_add_user)(void *rdma_cxt,
+                            struct qed_rdma_add_user_out_params *oparams);
+
+       void (*rdma_remove_user)(void *rdma_cxt, u16 dpi);
+       int (*rdma_stop)(void *rdma_cxt);
+       struct qed_rdma_device* (*rdma_query_device)(void *rdma_cxt);
+       struct qed_rdma_port* (*rdma_query_port)(void *rdma_cxt);
+       int (*rdma_get_start_sb)(struct qed_dev *cdev);
+       int (*rdma_get_min_cnq_msix)(struct qed_dev *cdev);
+       void (*rdma_cnq_prod_update)(void *rdma_cxt, u8 cnq_index, u16 prod);
+       int (*rdma_get_rdma_int)(struct qed_dev *cdev,
+                                struct qed_int_info *info);
+       int (*rdma_set_rdma_int)(struct qed_dev *cdev, u16 cnt);
+       int (*rdma_alloc_pd)(void *rdma_cxt, u16 *pd);
+       void (*rdma_dealloc_pd)(void *rdma_cxt, u16 pd);
+       int (*rdma_create_cq)(void *rdma_cxt,
+                             struct qed_rdma_create_cq_in_params *params,
+                             u16 *icid);
+       int (*rdma_destroy_cq)(void *rdma_cxt,
+                              struct qed_rdma_destroy_cq_in_params *iparams,
+                              struct qed_rdma_destroy_cq_out_params *oparams);
+       struct qed_rdma_qp *
+       (*rdma_create_qp)(void *rdma_cxt,
+                         struct qed_rdma_create_qp_in_params *iparams,
+                         struct qed_rdma_create_qp_out_params *oparams);
+
+       int (*rdma_modify_qp)(void *roce_cxt, struct qed_rdma_qp *qp,
+                             struct qed_rdma_modify_qp_in_params *iparams);
+
+       int (*rdma_query_qp)(void *rdma_cxt, struct qed_rdma_qp *qp,
+                            struct qed_rdma_query_qp_out_params *oparams);
+       int (*rdma_destroy_qp)(void *rdma_cxt, struct qed_rdma_qp *qp);
+       int
+       (*rdma_register_tid)(void *rdma_cxt,
+                            struct qed_rdma_register_tid_in_params *iparams);
+       int (*rdma_deregister_tid)(void *rdma_cxt, u32 itid);
+       int (*rdma_alloc_tid)(void *rdma_cxt, u32 *itid);
+       void (*rdma_free_tid)(void *rdma_cxt, u32 itid);
+       int (*roce_ll2_start)(struct qed_dev *cdev,
+                             struct qed_roce_ll2_params *params);
+       int (*roce_ll2_stop)(struct qed_dev *cdev);
+       int (*roce_ll2_tx)(struct qed_dev *cdev,
+                          struct qed_roce_ll2_packet *packet,
+                          struct qed_roce_ll2_tx_params *params);
+       int (*roce_ll2_post_rx_buffer)(struct qed_dev *cdev,
+                                      struct qed_roce_ll2_buffer *buf,
+                                      u64 cookie, u8 notify_fw);
+       int (*roce_ll2_set_mac_filter)(struct qed_dev *cdev,
+                                      u8 *old_mac_address,
+                                      u8 *new_mac_address);
+       int (*roce_ll2_stats)(struct qed_dev *cdev,
+                             struct qed_ll2_stats *stats);
+};
+
+const struct qed_rdma_ops *qed_get_rdma_ops(void);
+
+#endif
diff --git a/include/linux/qed/qede_roce.h b/include/linux/qed/qede_roce.h
new file mode 100644 (file)
index 0000000..99fbe6d
--- /dev/null
@@ -0,0 +1,88 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * 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
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef QEDE_ROCE_H
+#define QEDE_ROCE_H
+
+struct qedr_dev;
+struct qed_dev;
+struct qede_dev;
+
+enum qede_roce_event {
+       QEDE_UP,
+       QEDE_DOWN,
+       QEDE_CHANGE_ADDR,
+       QEDE_CLOSE
+};
+
+struct qede_roce_event_work {
+       struct list_head list;
+       struct work_struct work;
+       void *ptr;
+       enum qede_roce_event event;
+};
+
+struct qedr_driver {
+       unsigned char name[32];
+
+       struct qedr_dev* (*add)(struct qed_dev *, struct pci_dev *,
+                               struct net_device *);
+
+       void (*remove)(struct qedr_dev *);
+       void (*notify)(struct qedr_dev *, enum qede_roce_event);
+};
+
+/* APIs for RoCE driver to register callback handlers,
+ * which will be invoked when device is added, removed, ifup, ifdown
+ */
+int qede_roce_register_driver(struct qedr_driver *drv);
+void qede_roce_unregister_driver(struct qedr_driver *drv);
+
+bool qede_roce_supported(struct qede_dev *dev);
+
+#if IS_ENABLED(CONFIG_INFINIBAND_QEDR)
+int qede_roce_dev_add(struct qede_dev *dev);
+void qede_roce_dev_event_open(struct qede_dev *dev);
+void qede_roce_dev_event_close(struct qede_dev *dev);
+void qede_roce_dev_remove(struct qede_dev *dev);
+void qede_roce_event_changeaddr(struct qede_dev *qedr);
+#else
+static inline int qede_roce_dev_add(struct qede_dev *dev)
+{
+       return 0;
+}
+
+static inline void qede_roce_dev_event_open(struct qede_dev *dev) {}
+static inline void qede_roce_dev_event_close(struct qede_dev *dev) {}
+static inline void qede_roce_dev_remove(struct qede_dev *dev) {}
+static inline void qede_roce_event_changeaddr(struct qede_dev *qedr) {}
+#endif
+#endif
index 187991c..7663725 100644 (file)
@@ -28,6 +28,7 @@
 #define RDMA_MAX_PDS                            (64 * 1024)
 
 #define RDMA_NUM_STATISTIC_COUNTERS                     MAX_NUM_VPORTS
+#define RDMA_NUM_STATISTIC_COUNTERS_BB                 MAX_NUM_VPORTS_BB
 
 #define RDMA_TASK_TYPE (PROTOCOLID_ROCE)
 
index 5b3b5ad..1dbc669 100644 (file)
 
 #define MPLS_HLEN 4
 
+struct mpls_shim_hdr {
+       __be32 label_stack_entry;
+};
+
 static inline bool eth_p_mpls(__be16 eth_type)
 {
        return eth_type == htons(ETH_P_MPLS_UC) ||
                eth_type == htons(ETH_P_MPLS_MC);
 }
 
-/*
- * For non-MPLS skbs this will correspond to the network header.
- * For MPLS skbs it will be before the network_header as the MPLS
- * label stack lies between the end of the mac header and the network
- * header. That is, for MPLS skbs the end of the mac header
- * is the top of the MPLS label stack.
- */
-static inline unsigned char *skb_mpls_header(struct sk_buff *skb)
+static inline struct mpls_shim_hdr *mpls_hdr(const struct sk_buff *skb)
 {
-       return skb_mac_header(skb) + skb->mac_len;
+       return (struct mpls_shim_hdr *)skb_network_header(skb);
 }
 #endif
index 8ba8d76..0383e5e 100644 (file)
@@ -280,11 +280,12 @@ TRACE_EVENT(rxrpc_tx_data,
                    __entry->lose = lose;
                           ),
 
-           TP_printk("c=%p DATA %08x q=%08x fl=%02x%s",
+           TP_printk("c=%p DATA %08x q=%08x fl=%02x%s%s",
                      __entry->call,
                      __entry->serial,
                      __entry->seq,
                      __entry->flags,
+                     __entry->retrans ? " *RETRANS*" : "",
                      __entry->lose ? " *LOSE*" : "")
            );
 
@@ -452,17 +453,18 @@ TRACE_EVENT(rxrpc_rtt_rx,
 
 TRACE_EVENT(rxrpc_timer,
            TP_PROTO(struct rxrpc_call *call, enum rxrpc_timer_trace why,
-                    unsigned long now),
+                    ktime_t now, unsigned long now_j),
 
-           TP_ARGS(call, why, now),
+           TP_ARGS(call, why, now, now_j),
 
            TP_STRUCT__entry(
                    __field(struct rxrpc_call *,                call            )
                    __field(enum rxrpc_timer_trace,             why             )
-                   __field(unsigned long,                      now             )
-                   __field(unsigned long,                      expire_at       )
-                   __field(unsigned long,                      ack_at          )
-                   __field(unsigned long,                      resend_at       )
+                   __field_struct(ktime_t,                     now             )
+                   __field_struct(ktime_t,                     expire_at       )
+                   __field_struct(ktime_t,                     ack_at          )
+                   __field_struct(ktime_t,                     resend_at       )
+                   __field(unsigned long,                      now_j           )
                    __field(unsigned long,                      timer           )
                             ),
 
@@ -473,17 +475,17 @@ TRACE_EVENT(rxrpc_timer,
                    __entry->expire_at  = call->expire_at;
                    __entry->ack_at     = call->ack_at;
                    __entry->resend_at  = call->resend_at;
+                   __entry->now_j      = now_j;
                    __entry->timer      = call->timer.expires;
                           ),
 
-           TP_printk("c=%p %s now=%lx x=%ld a=%ld r=%ld t=%ld",
+           TP_printk("c=%p %s x=%lld a=%lld r=%lld t=%ld",
                      __entry->call,
                      rxrpc_timer_traces[__entry->why],
-                     __entry->now,
-                     __entry->expire_at - __entry->now,
-                     __entry->ack_at - __entry->now,
-                     __entry->resend_at - __entry->now,
-                     __entry->timer - __entry->now)
+                     ktime_to_ns(ktime_sub(__entry->expire_at, __entry->now)),
+                     ktime_to_ns(ktime_sub(__entry->ack_at, __entry->now)),
+                     ktime_to_ns(ktime_sub(__entry->resend_at, __entry->now)),
+                     __entry->timer - __entry->now_j)
            );
 
 TRACE_EVENT(rxrpc_rx_lose,
index d36c754..cbd19d2 100644 (file)
@@ -4528,13 +4528,18 @@ EXPORT_SYMBOL(skb_ensure_writable);
 int __skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci)
 {
        struct vlan_hdr *vhdr;
-       unsigned int offset = skb->data - skb_mac_header(skb);
+       int offset = skb->data - skb_mac_header(skb);
        int err;
 
-       __skb_push(skb, offset);
+       if (WARN_ONCE(offset,
+                     "__skb_vlan_pop got skb with skb->data not at mac header (offset %d)\n",
+                     offset)) {
+               return -EINVAL;
+       }
+
        err = skb_ensure_writable(skb, VLAN_ETH_HLEN);
        if (unlikely(err))
-               goto pull;
+               return err;
 
        skb_postpull_rcsum(skb, skb->data + (2 * ETH_ALEN), VLAN_HLEN);
 
@@ -4551,13 +4556,14 @@ int __skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci)
                skb_set_network_header(skb, ETH_HLEN);
 
        skb_reset_mac_len(skb);
-pull:
-       __skb_pull(skb, offset);
 
        return err;
 }
 EXPORT_SYMBOL(__skb_vlan_pop);
 
+/* Pop a vlan tag either from hwaccel or from payload.
+ * Expects skb->data at mac header.
+ */
 int skb_vlan_pop(struct sk_buff *skb)
 {
        u16 vlan_tci;
@@ -4588,29 +4594,30 @@ int skb_vlan_pop(struct sk_buff *skb)
 }
 EXPORT_SYMBOL(skb_vlan_pop);
 
+/* Push a vlan tag either into hwaccel or into payload (if hwaccel tag present).
+ * Expects skb->data at mac header.
+ */
 int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci)
 {
        if (skb_vlan_tag_present(skb)) {
-               unsigned int offset = skb->data - skb_mac_header(skb);
+               int offset = skb->data - skb_mac_header(skb);
                int err;
 
-               /* __vlan_insert_tag expect skb->data pointing to mac header.
-                * So change skb->data before calling it and change back to
-                * original position later
-                */
-               __skb_push(skb, offset);
+               if (WARN_ONCE(offset,
+                             "skb_vlan_push got skb with skb->data not at mac header (offset %d)\n",
+                             offset)) {
+                       return -EINVAL;
+               }
+
                err = __vlan_insert_tag(skb, skb->vlan_proto,
                                        skb_vlan_tag_get(skb));
-               if (err) {
-                       __skb_pull(skb, offset);
+               if (err)
                        return err;
-               }
 
                skb->protocol = skb->vlan_proto;
                skb->mac_len += VLAN_HLEN;
 
                skb_postpush_rcsum(skb, skb->data + (2 * ETH_ALEN), VLAN_HLEN);
-               __skb_pull(skb, offset);
        }
        __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci);
        return 0;
index 732a5c1..bdfef6c 100644 (file)
@@ -1,9 +1,6 @@
 #ifndef MPLS_INTERNAL_H
 #define MPLS_INTERNAL_H
-
-struct mpls_shim_hdr {
-       __be32 label_stack_entry;
-};
+#include <net/mpls.h>
 
 struct mpls_entry_decoded {
        u32 label;
@@ -93,11 +90,6 @@ struct mpls_route { /* next hop label forwarding entry */
 
 #define endfor_nexthops(rt) }
 
-static inline struct mpls_shim_hdr *mpls_hdr(const struct sk_buff *skb)
-{
-       return (struct mpls_shim_hdr *)skb_network_header(skb);
-}
-
 static inline struct mpls_shim_hdr mpls_entry_encode(u32 label, unsigned ttl, unsigned tc, bool bos)
 {
        struct mpls_shim_hdr result;
index 863e992..4e03f64 100644 (file)
@@ -160,7 +160,7 @@ static void update_ethertype(struct sk_buff *skb, struct ethhdr *hdr,
 static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
                     const struct ovs_action_push_mpls *mpls)
 {
-       __be32 *new_mpls_lse;
+       struct mpls_shim_hdr *new_mpls_lse;
 
        /* Networking stack do not allow simultaneous Tunnel and MPLS GSO. */
        if (skb->encapsulation)
@@ -180,8 +180,8 @@ static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
        skb_reset_mac_header(skb);
        skb_set_network_header(skb, skb->mac_len);
 
-       new_mpls_lse = (__be32 *)skb_mpls_header(skb);
-       *new_mpls_lse = mpls->mpls_lse;
+       new_mpls_lse = mpls_hdr(skb);
+       new_mpls_lse->label_stack_entry = mpls->mpls_lse;
 
        skb_postpush_rcsum(skb, new_mpls_lse, MPLS_HLEN);
 
@@ -202,7 +202,7 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
        if (unlikely(err))
                return err;
 
-       skb_postpull_rcsum(skb, skb_mpls_header(skb), MPLS_HLEN);
+       skb_postpull_rcsum(skb, mpls_hdr(skb), MPLS_HLEN);
 
        memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb),
                skb->mac_len);
@@ -211,10 +211,10 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
        skb_reset_mac_header(skb);
        skb_set_network_header(skb, skb->mac_len);
 
-       /* skb_mpls_header() is used to locate the ethertype
-        * field correctly in the presence of VLAN tags.
+       /* mpls_hdr() is used to locate the ethertype field correctly in the
+        * presence of VLAN tags.
         */
-       hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN);
+       hdr = (struct ethhdr *)((void *)mpls_hdr(skb) - ETH_HLEN);
        update_ethertype(skb, hdr, ethertype);
        if (eth_p_mpls(skb->protocol))
                skb->protocol = ethertype;
@@ -226,7 +226,7 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
 static int set_mpls(struct sk_buff *skb, struct sw_flow_key *flow_key,
                    const __be32 *mpls_lse, const __be32 *mask)
 {
-       __be32 *stack;
+       struct mpls_shim_hdr *stack;
        __be32 lse;
        int err;
 
@@ -234,16 +234,16 @@ static int set_mpls(struct sk_buff *skb, struct sw_flow_key *flow_key,
        if (unlikely(err))
                return err;
 
-       stack = (__be32 *)skb_mpls_header(skb);
-       lse = OVS_MASKED(*stack, *mpls_lse, *mask);
+       stack = mpls_hdr(skb);
+       lse = OVS_MASKED(stack->label_stack_entry, *mpls_lse, *mask);
        if (skb->ip_summed == CHECKSUM_COMPLETE) {
-               __be32 diff[] = { ~(*stack), lse };
+               __be32 diff[] = { ~(stack->label_stack_entry), lse };
 
                skb->csum = ~csum_partial((char *)diff, sizeof(diff),
                                          ~skb->csum);
        }
 
-       *stack = lse;
+       stack->label_stack_entry = lse;
        flow_key->mpls.top_lse = lse;
        return 0;
 }
index 634cc10..c8c82e1 100644 (file)
@@ -633,12 +633,7 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
        } else if (eth_p_mpls(key->eth.type)) {
                size_t stack_len = MPLS_HLEN;
 
-               /* In the presence of an MPLS label stack the end of the L2
-                * header and the beginning of the L3 header differ.
-                *
-                * Advance network_header to the beginning of the L3
-                * header. mac_len corresponds to the end of the L2 header.
-                */
+               skb_set_inner_network_header(skb, skb->mac_len);
                while (1) {
                        __be32 lse;
 
@@ -646,12 +641,12 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
                        if (unlikely(error))
                                return 0;
 
-                       memcpy(&lse, skb_network_header(skb), MPLS_HLEN);
+                       memcpy(&lse, skb_inner_network_header(skb), MPLS_HLEN);
 
                        if (stack_len == MPLS_HLEN)
                                memcpy(&key->mpls.top_lse, &lse, MPLS_HLEN);
 
-                       skb_set_network_header(skb, skb->mac_len + stack_len);
+                       skb_set_inner_network_header(skb, skb->mac_len + stack_len);
                        if (lse & htonl(MPLS_LS_S_MASK))
                                break;
 
index 539db54..d38dffd 100644 (file)
@@ -144,9 +144,7 @@ struct rxrpc_skb_priv {
                u8              nr_jumbo;       /* Number of jumbo subpackets */
        };
        union {
-               unsigned int    offset;         /* offset into buffer of next read */
                int             remain;         /* amount of space remaining for next write */
-               u32             error;          /* network error code */
        };
 
        struct rxrpc_host_header hdr;           /* RxRPC packet header from this packet */
@@ -466,9 +464,9 @@ struct rxrpc_call {
        struct rxrpc_connection *conn;          /* connection carrying call */
        struct rxrpc_peer       *peer;          /* Peer record for remote address */
        struct rxrpc_sock __rcu *socket;        /* socket responsible */
-       unsigned long           ack_at;         /* When deferred ACK needs to happen */
-       unsigned long           resend_at;      /* When next resend needs to happen */
-       unsigned long           expire_at;      /* When the call times out */
+       ktime_t                 ack_at;         /* When deferred ACK needs to happen */
+       ktime_t                 resend_at;      /* When next resend needs to happen */
+       ktime_t                 expire_at;      /* When the call times out */
        struct timer_list       timer;          /* Combined event timer */
        struct work_struct      processor;      /* Event processor */
        rxrpc_notify_rx_t       notify_rx;      /* kernel service Rx notification function */
@@ -807,7 +805,7 @@ int rxrpc_reject_call(struct rxrpc_sock *);
 /*
  * call_event.c
  */
-void rxrpc_set_timer(struct rxrpc_call *, enum rxrpc_timer_trace);
+void rxrpc_set_timer(struct rxrpc_call *, enum rxrpc_timer_trace, ktime_t);
 void rxrpc_propose_ACK(struct rxrpc_call *, u8, u16, u32, bool, bool,
                       enum rxrpc_propose_ack_trace);
 void rxrpc_process_call(struct work_struct *);
index 1f6c763..4f00476 100644 (file)
 /*
  * Set the timer
  */
-void rxrpc_set_timer(struct rxrpc_call *call, enum rxrpc_timer_trace why)
+void rxrpc_set_timer(struct rxrpc_call *call, enum rxrpc_timer_trace why,
+                    ktime_t now)
 {
-       unsigned long t, now = jiffies;
+       unsigned long t_j, now_j = jiffies;
+       ktime_t t;
+       bool queue = false;
 
        read_lock_bh(&call->state_lock);
 
        if (call->state < RXRPC_CALL_COMPLETE) {
                t = call->expire_at;
-               if (time_before_eq(t, now))
+               if (!ktime_after(t, now))
                        goto out;
 
-               if (time_after(call->resend_at, now) &&
-                   time_before(call->resend_at, t))
+               if (!ktime_after(call->resend_at, now)) {
+                       call->resend_at = call->expire_at;
+                       if (!test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events))
+                               queue = true;
+               } else if (ktime_before(call->resend_at, t)) {
                        t = call->resend_at;
+               }
 
-               if (time_after(call->ack_at, now) &&
-                   time_before(call->ack_at, t))
+               if (!ktime_after(call->ack_at, now)) {
+                       call->ack_at = call->expire_at;
+                       if (!test_and_set_bit(RXRPC_CALL_EV_ACK, &call->events))
+                               queue = true;
+               } else if (ktime_before(call->ack_at, t)) {
                        t = call->ack_at;
+               }
 
-               if (call->timer.expires != t || !timer_pending(&call->timer)) {
-                       mod_timer(&call->timer, t);
-                       trace_rxrpc_timer(call, why, now);
+               t_j = nsecs_to_jiffies(ktime_to_ns(ktime_sub(t, now)));
+               t_j += jiffies;
+
+               /* We have to make sure that the calculated jiffies value falls
+                * at or after the nsec value, or we may loop ceaselessly
+                * because the timer times out, but we haven't reached the nsec
+                * timeout yet.
+                */
+               t_j++;
+
+               if (call->timer.expires != t_j || !timer_pending(&call->timer)) {
+                       mod_timer(&call->timer, t_j);
+                       trace_rxrpc_timer(call, why, now, now_j);
                }
+
+               if (queue)
+                       rxrpc_queue_call(call);
        }
 
 out:
@@ -62,7 +86,8 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
                                enum rxrpc_propose_ack_trace why)
 {
        enum rxrpc_propose_ack_outcome outcome = rxrpc_propose_ack_use;
-       unsigned long now, ack_at, expiry = rxrpc_soft_ack_delay;
+       unsigned int expiry = rxrpc_soft_ack_delay;
+       ktime_t now, ack_at;
        s8 prior = rxrpc_ack_priority[ack_reason];
 
        /* Update DELAY, IDLE, REQUESTED and PING_RESPONSE ACK serial
@@ -111,7 +136,6 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
                break;
        }
 
-       now = jiffies;
        if (test_bit(RXRPC_CALL_EV_ACK, &call->events)) {
                _debug("already scheduled");
        } else if (immediate || expiry == 0) {
@@ -120,11 +144,11 @@ static void __rxrpc_propose_ACK(struct rxrpc_call *call, u8 ack_reason,
                    background)
                        rxrpc_queue_call(call);
        } else {
-               ack_at = now + expiry;
-               _debug("deferred ACK %ld < %ld", expiry, call->ack_at - now);
-               if (time_before(ack_at, call->ack_at)) {
+               now = ktime_get_real();
+               ack_at = ktime_add_ms(now, expiry);
+               if (ktime_before(ack_at, call->ack_at)) {
                        call->ack_at = ack_at;
-                       rxrpc_set_timer(call, rxrpc_timer_set_for_ack);
+                       rxrpc_set_timer(call, rxrpc_timer_set_for_ack, now);
                }
        }
 
@@ -157,12 +181,12 @@ static void rxrpc_congestion_timeout(struct rxrpc_call *call)
 /*
  * Perform retransmission of NAK'd and unack'd packets.
  */
-static void rxrpc_resend(struct rxrpc_call *call)
+static void rxrpc_resend(struct rxrpc_call *call, ktime_t now)
 {
        struct rxrpc_skb_priv *sp;
        struct sk_buff *skb;
        rxrpc_seq_t cursor, seq, top;
-       ktime_t now = ktime_get_real(), max_age, oldest, resend_at, ack_ts;
+       ktime_t max_age, oldest, ack_ts;
        int ix;
        u8 annotation, anno_type, retrans = 0, unacked = 0;
 
@@ -212,14 +236,7 @@ static void rxrpc_resend(struct rxrpc_call *call)
                                       ktime_to_ns(ktime_sub(skb->tstamp, max_age)));
        }
 
-       resend_at = ktime_add_ms(oldest, rxrpc_resend_timeout);
-       call->resend_at = jiffies +
-               nsecs_to_jiffies(ktime_to_ns(ktime_sub(resend_at, now))) +
-               1; /* We have to make sure that the calculated jiffies value
-                   * falls at or after the nsec value, or we shall loop
-                   * ceaselessly because the timer times out, but we haven't
-                   * reached the nsec timeout yet.
-                   */
+       call->resend_at = ktime_add_ms(oldest, rxrpc_resend_timeout);
 
        if (unacked)
                rxrpc_congestion_timeout(call);
@@ -229,7 +246,7 @@ static void rxrpc_resend(struct rxrpc_call *call)
         * retransmitting data.
         */
        if (!retrans) {
-               rxrpc_set_timer(call, rxrpc_timer_set_for_resend);
+               rxrpc_set_timer(call, rxrpc_timer_set_for_resend, now);
                spin_unlock_bh(&call->lock);
                ack_ts = ktime_sub(now, call->acks_latest_ts);
                if (ktime_to_ns(ack_ts) < call->peer->rtt)
@@ -301,7 +318,7 @@ void rxrpc_process_call(struct work_struct *work)
 {
        struct rxrpc_call *call =
                container_of(work, struct rxrpc_call, processor);
-       unsigned long now;
+       ktime_t now;
 
        rxrpc_see_call(call);
 
@@ -320,15 +337,14 @@ recheck_state:
                goto out_put;
        }
 
-       now = jiffies;
-       if (time_after_eq(now, call->expire_at)) {
+       now = ktime_get_real();
+       if (ktime_before(call->expire_at, now)) {
                rxrpc_abort_call("EXP", call, 0, RX_CALL_TIMEOUT, ETIME);
                set_bit(RXRPC_CALL_EV_ABORT, &call->events);
                goto recheck_state;
        }
 
-       if (test_and_clear_bit(RXRPC_CALL_EV_ACK, &call->events) ||
-           time_after_eq(now, call->ack_at)) {
+       if (test_and_clear_bit(RXRPC_CALL_EV_ACK, &call->events)) {
                call->ack_at = call->expire_at;
                if (call->ackr_reason) {
                        rxrpc_send_call_packet(call, RXRPC_PACKET_TYPE_ACK);
@@ -336,13 +352,12 @@ recheck_state:
                }
        }
 
-       if (test_and_clear_bit(RXRPC_CALL_EV_RESEND, &call->events) ||
-           time_after_eq(now, call->resend_at)) {
-               rxrpc_resend(call);
+       if (test_and_clear_bit(RXRPC_CALL_EV_RESEND, &call->events)) {
+               rxrpc_resend(call, now);
                goto recheck_state;
        }
 
-       rxrpc_set_timer(call, rxrpc_timer_set_for_resend);
+       rxrpc_set_timer(call, rxrpc_timer_set_for_resend, now);
 
        /* other events may have been raised since we started checking */
        if (call->events && call->state < RXRPC_CALL_COMPLETE) {
index d4b3293..364b42d 100644 (file)
 #include <net/af_rxrpc.h>
 #include "ar-internal.h"
 
-/*
- * Maximum lifetime of a call (in jiffies).
- */
-unsigned int rxrpc_max_call_lifetime = 60 * HZ;
-
 const char *const rxrpc_call_states[NR__RXRPC_CALL_STATES] = {
        [RXRPC_CALL_UNINITIALISED]              = "Uninit  ",
        [RXRPC_CALL_CLIENT_AWAIT_CONN]          = "ClWtConn",
@@ -76,10 +71,8 @@ static void rxrpc_call_timer_expired(unsigned long _call)
 
        _enter("%d", call->debug_id);
 
-       if (call->state < RXRPC_CALL_COMPLETE) {
-               trace_rxrpc_timer(call, rxrpc_timer_expired, jiffies);
-               rxrpc_queue_call(call);
-       }
+       if (call->state < RXRPC_CALL_COMPLETE)
+               rxrpc_set_timer(call, rxrpc_timer_expired, ktime_get_real());
 }
 
 /*
@@ -207,14 +200,14 @@ static struct rxrpc_call *rxrpc_alloc_client_call(struct sockaddr_rxrpc *srx,
  */
 static void rxrpc_start_call_timer(struct rxrpc_call *call)
 {
-       unsigned long expire_at;
+       ktime_t now = ktime_get_real(), expire_at;
 
-       expire_at = jiffies + rxrpc_max_call_lifetime;
+       expire_at = ktime_add_ms(now, rxrpc_max_call_lifetime);
        call->expire_at = expire_at;
        call->ack_at = expire_at;
        call->resend_at = expire_at;
-       call->timer.expires = expire_at + 1;
-       rxrpc_set_timer(call, rxrpc_timer_begin);
+       call->timer.expires = jiffies + LONG_MAX / 2;
+       rxrpc_set_timer(call, rxrpc_timer_begin, now);
 }
 
 /*
index 37609ce..3f9d8d7 100644 (file)
@@ -276,7 +276,8 @@ static int rxrpc_process_event(struct rxrpc_connection *conn,
                return 0;
 
        case RXRPC_PACKET_TYPE_ABORT:
-               if (skb_copy_bits(skb, sp->offset, &wtmp, sizeof(wtmp)) < 0)
+               if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
+                                 &wtmp, sizeof(wtmp)) < 0)
                        return -EPROTO;
                abort_code = ntohl(wtmp);
                _proto("Rx ABORT %%%u { ac=%d }", sp->hdr.serial, abort_code);
index 1461d30..3ad9f75 100644 (file)
@@ -57,7 +57,7 @@ static void rxrpc_congestion_management(struct rxrpc_call *call,
                call->cong_ssthresh = max_t(unsigned int,
                                            summary->flight_size / 2, 2);
                cwnd = 1;
-               if (cwnd > call->cong_ssthresh &&
+               if (cwnd >= call->cong_ssthresh &&
                    call->cong_mode == RXRPC_CALL_SLOW_START) {
                        call->cong_mode = RXRPC_CALL_CONGEST_AVOIDANCE;
                        call->cong_tstamp = skb->tstamp;
@@ -82,7 +82,7 @@ static void rxrpc_congestion_management(struct rxrpc_call *call,
                        goto packet_loss_detected;
                if (summary->cumulative_acks > 0)
                        cwnd += 1;
-               if (cwnd > call->cong_ssthresh) {
+               if (cwnd >= call->cong_ssthresh) {
                        call->cong_mode = RXRPC_CALL_CONGEST_AVOIDANCE;
                        call->cong_tstamp = skb->tstamp;
                }
@@ -161,7 +161,7 @@ resume_normality:
        call->cong_dup_acks = 0;
        call->cong_extra = 0;
        call->cong_tstamp = skb->tstamp;
-       if (cwnd <= call->cong_ssthresh)
+       if (cwnd < call->cong_ssthresh)
                call->cong_mode = RXRPC_CALL_SLOW_START;
        else
                call->cong_mode = RXRPC_CALL_CONGEST_AVOIDANCE;
@@ -328,7 +328,8 @@ static bool rxrpc_receiving_reply(struct rxrpc_call *call)
                call->resend_at = call->expire_at;
                call->ack_at = call->expire_at;
                spin_unlock_bh(&call->lock);
-               rxrpc_set_timer(call, rxrpc_timer_init_for_reply);
+               rxrpc_set_timer(call, rxrpc_timer_init_for_reply,
+                               ktime_get_real());
        }
 
        if (!test_bit(RXRPC_CALL_TX_LAST, &call->flags))
@@ -358,7 +359,7 @@ static bool rxrpc_receiving_reply(struct rxrpc_call *call)
 static bool rxrpc_validate_jumbo(struct sk_buff *skb)
 {
        struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
-       unsigned int offset = sp->offset;
+       unsigned int offset = sizeof(struct rxrpc_wire_header);
        unsigned int len = skb->len;
        int nr_jumbo = 1;
        u8 flags = sp->hdr.flags;
@@ -419,7 +420,7 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb,
                             u16 skew)
 {
        struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
-       unsigned int offset = sp->offset;
+       unsigned int offset = sizeof(struct rxrpc_wire_header);
        unsigned int ix;
        rxrpc_serial_t serial = sp->hdr.serial, ack_serial = 0;
        rxrpc_seq_t seq = sp->hdr.seq, hard_ack;
@@ -658,6 +659,8 @@ static void rxrpc_input_ackinfo(struct rxrpc_call *call, struct sk_buff *skb,
        if (rwind > RXRPC_RXTX_BUFF_SIZE - 1)
                rwind = RXRPC_RXTX_BUFF_SIZE - 1;
        call->tx_winsize = rwind;
+       if (call->cong_ssthresh > rwind)
+               call->cong_ssthresh = rwind;
 
        mtu = min(ntohl(ackinfo->rxMTU), ntohl(ackinfo->maxMTU));
 
@@ -744,15 +747,16 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
        } buf;
        rxrpc_serial_t acked_serial;
        rxrpc_seq_t first_soft_ack, hard_ack;
-       int nr_acks, offset;
+       int nr_acks, offset, ioffset;
 
        _enter("");
 
-       if (skb_copy_bits(skb, sp->offset, &buf.ack, sizeof(buf.ack)) < 0) {
+       offset = sizeof(struct rxrpc_wire_header);
+       if (skb_copy_bits(skb, offset, &buf.ack, sizeof(buf.ack)) < 0) {
                _debug("extraction failure");
                return rxrpc_proto_abort("XAK", call, 0);
        }
-       sp->offset += sizeof(buf.ack);
+       offset += sizeof(buf.ack);
 
        acked_serial = ntohl(buf.ack.serial);
        first_soft_ack = ntohl(buf.ack.firstPacket);
@@ -790,9 +794,9 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
                                  rxrpc_propose_ack_respond_to_ack);
        }
 
-       offset = sp->offset + nr_acks + 3;
-       if (skb->len >= offset + sizeof(buf.info)) {
-               if (skb_copy_bits(skb, offset, &buf.info, sizeof(buf.info)) < 0)
+       ioffset = offset + nr_acks + 3;
+       if (skb->len >= ioffset + sizeof(buf.info)) {
+               if (skb_copy_bits(skb, ioffset, &buf.info, sizeof(buf.info)) < 0)
                        return rxrpc_proto_abort("XAI", call, 0);
                rxrpc_input_ackinfo(call, skb, &buf.info);
        }
@@ -830,7 +834,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb,
                rxrpc_rotate_tx_window(call, hard_ack, &summary);
 
        if (nr_acks > 0) {
-               if (skb_copy_bits(skb, sp->offset, buf.acks, nr_acks) < 0)
+               if (skb_copy_bits(skb, offset, buf.acks, nr_acks) < 0)
                        return rxrpc_proto_abort("XSA", call, 0);
                rxrpc_input_soft_acks(call, buf.acks, first_soft_ack, nr_acks,
                                      &summary);
@@ -878,7 +882,8 @@ static void rxrpc_input_abort(struct rxrpc_call *call, struct sk_buff *skb)
        _enter("");
 
        if (skb->len >= 4 &&
-           skb_copy_bits(skb, sp->offset, &wtmp, sizeof(wtmp)) >= 0)
+           skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
+                         &wtmp, sizeof(wtmp)) >= 0)
                abort_code = ntohl(wtmp);
 
        _proto("Rx ABORT %%%u { %x }", sp->hdr.serial, abort_code);
@@ -994,7 +999,6 @@ int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb)
        sp->hdr.securityIndex   = whdr.securityIndex;
        sp->hdr._rsvd           = ntohs(whdr._rsvd);
        sp->hdr.serviceId       = ntohs(whdr.serviceId);
-       sp->offset = sizeof(whdr);
        return 0;
 }
 
index 190f68b..540d395 100644 (file)
@@ -95,7 +95,8 @@ void rxrpc_process_local_events(struct rxrpc_local *local)
 
                switch (sp->hdr.type) {
                case RXRPC_PACKET_TYPE_VERSION:
-                       if (skb_copy_bits(skb, sp->offset, &v, 1) < 0)
+                       if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
+                                         &v, 1) < 0)
                                return;
                        _proto("Rx VERSION { %02x }", v);
                        if (v == 0)
index 47dddac..9d1c721 100644 (file)
  */
 unsigned int rxrpc_max_backlog __read_mostly = 10;
 
+/*
+ * Maximum lifetime of a call (in mx).
+ */
+unsigned int rxrpc_max_call_lifetime = 60 * 1000;
+
 /*
  * How long to wait before scheduling ACK generation after seeing a
- * packet with RXRPC_REQUEST_ACK set (in jiffies).
+ * packet with RXRPC_REQUEST_ACK set (in ms).
  */
 unsigned int rxrpc_requested_ack_delay = 1;
 
 /*
- * How long to wait before scheduling an ACK with subtype DELAY (in jiffies).
+ * How long to wait before scheduling an ACK with subtype DELAY (in ms).
  *
  * We use this when we've received new data packets.  If those packets aren't
  * all consumed within this time we will send a DELAY ACK if an ACK was not
  * requested to let the sender know it doesn't need to resend.
  */
-unsigned int rxrpc_soft_ack_delay = 1 * HZ;
+unsigned int rxrpc_soft_ack_delay = 1 * 1000;
 
 /*
- * How long to wait before scheduling an ACK with subtype IDLE (in jiffies).
+ * How long to wait before scheduling an ACK with subtype IDLE (in ms).
  *
  * We use this when we've consumed some previously soft-ACK'd packets when
  * further packets aren't immediately received to decide when to send an IDLE
  * ACK let the other end know that it can free up its Tx buffer space.
  */
-unsigned int rxrpc_idle_ack_delay = 0.5 * HZ;
+unsigned int rxrpc_idle_ack_delay = 0.5 * 1000;
 
 /*
  * Receive window size in packets.  This indicates the maximum number of
index 038ae62..f05ea0a 100644 (file)
@@ -261,15 +261,13 @@ static int rxrpc_locate_data(struct rxrpc_call *call, struct sk_buff *skb,
                             u8 *_annotation,
                             unsigned int *_offset, unsigned int *_len)
 {
-       struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
-       unsigned int offset = *_offset;
+       unsigned int offset = sizeof(struct rxrpc_wire_header);
        unsigned int len = *_len;
        int ret;
        u8 annotation = *_annotation;
 
        /* Locate the subpacket */
-       offset = sp->offset;
-       len = skb->len - sp->offset;
+       len = skb->len - offset;
        if ((annotation & RXRPC_RX_ANNO_JUMBO) > 0) {
                offset += (((annotation & RXRPC_RX_ANNO_JUMBO) - 1) *
                           RXRPC_JUMBO_SUBPKTLEN);
index 88d080a..627abed 100644 (file)
@@ -771,7 +771,8 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
        }
 
        abort_code = RXKADPACKETSHORT;
-       if (skb_copy_bits(skb, sp->offset, &challenge, sizeof(challenge)) < 0)
+       if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
+                         &challenge, sizeof(challenge)) < 0)
                goto protocol_error;
 
        version = ntohl(challenge.version);
@@ -1028,7 +1029,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
        _enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key));
 
        abort_code = RXKADPACKETSHORT;
-       if (skb_copy_bits(skb, sp->offset, &response, sizeof(response)) < 0)
+       if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
+                         &response, sizeof(response)) < 0)
                goto protocol_error;
        if (!pskb_pull(skb, sizeof(response)))
                BUG();
@@ -1057,7 +1059,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
                return -ENOMEM;
 
        abort_code = RXKADPACKETSHORT;
-       if (skb_copy_bits(skb, sp->offset, ticket, ticket_len) < 0)
+       if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
+                         ticket, ticket_len) < 0)
                goto protocol_error_free;
 
        ret = rxkad_decrypt_ticket(conn, ticket, ticket_len, &session_key,
index d8dfdce..3322543 100644 (file)
@@ -149,13 +149,13 @@ static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb,
                _debug("need instant resend %d", ret);
                rxrpc_instant_resend(call, ix);
        } else {
-               unsigned long resend_at;
+               ktime_t now = ktime_get_real(), resend_at;
 
-               resend_at = jiffies + msecs_to_jiffies(rxrpc_resend_timeout);
+               resend_at = ktime_add_ms(now, rxrpc_resend_timeout);
 
-               if (time_before(resend_at, call->resend_at)) {
+               if (ktime_before(resend_at, call->resend_at)) {
                        call->resend_at = resend_at;
-                       rxrpc_set_timer(call, rxrpc_timer_set_for_send);
+                       rxrpc_set_timer(call, rxrpc_timer_set_for_send, now);
                }
        }
 
index 13d1df0..34c706d 100644 (file)
@@ -35,7 +35,7 @@ static struct ctl_table rxrpc_sysctl_table[] = {
                .data           = &rxrpc_requested_ack_delay,
                .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec_ms_jiffies,
+               .proc_handler   = proc_dointvec,
                .extra1         = (void *)&zero,
        },
        {
@@ -43,7 +43,7 @@ static struct ctl_table rxrpc_sysctl_table[] = {
                .data           = &rxrpc_soft_ack_delay,
                .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec_ms_jiffies,
+               .proc_handler   = proc_dointvec,
                .extra1         = (void *)&one,
        },
        {
@@ -51,7 +51,7 @@ static struct ctl_table rxrpc_sysctl_table[] = {
                .data           = &rxrpc_idle_ack_delay,
                .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec_ms_jiffies,
+               .proc_handler   = proc_dointvec,
                .extra1         = (void *)&one,
        },
        {
@@ -85,7 +85,7 @@ static struct ctl_table rxrpc_sysctl_table[] = {
                .data           = &rxrpc_max_call_lifetime,
                .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec_jiffies,
+               .proc_handler   = proc_dointvec,
                .extra1         = (void *)&one,
        },
 
index a95c00b..b57fcbc 100644 (file)
@@ -37,6 +37,12 @@ static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
        bstats_update(&v->tcf_bstats, skb);
        action = v->tcf_action;
 
+       /* Ensure 'data' points at mac_header prior calling vlan manipulating
+        * functions.
+        */
+       if (skb_at_tc_ingress(skb))
+               skb_push_rcsum(skb, skb->mac_len);
+
        switch (v->tcfv_action) {
        case TCA_VLAN_ACT_POP:
                err = skb_vlan_pop(skb);
@@ -83,6 +89,9 @@ drop:
        action = TC_ACT_SHOT;
        v->tcf_qstats.drops++;
 unlock:
+       if (skb_at_tc_ingress(skb))
+               skb_pull_rcsum(skb, skb->mac_len);
+
        spin_unlock(&v->tcf_lock);
        return action;
 }