* [PATCH 00/15] Add BNG_RE control path verbs
@ 2026-09-04 10:43 Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 01/15] bnge: Add infrastructure support for RoCE MPC channels Siva Reddy Kallam
` (14 more replies)
0 siblings, 15 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Siva Reddy Kallam
Hi,
This patch series adds control path verbs support for the bng_re
driver, along with the corresponding bnge driver changes it depends
on.
Fast path verbs depend on a specific HW resource configuration in the
bnge driver, and work on that is in progress. We plan to submit fast path
verbs support in a follow-up series once the dependent changes in bnge
are pushed and available in the rdma repo.
The corresponding rdma-core user space library patches will also be
published shortly.
1) bnge: MPC infrastructure change
2) bnge: Update New HSI
3) bnge: MPC ring reservation change
4) bng_re: Add RoCE MPC infrastructure and xid manager
5) bng_re: Add support verbs
6) bng_re: Add ucontext/mmap verbs
7) bng_re: Add GID verbs
8) bng_re: Add PD verbs
9) bng_re: Add MR verbs
10) bng_re: Add CQ control path verbs
11) bng_re: Add SRQ control path verbs
12) bng_re: Add statistics verbs
13) bng_re: Add AH verbs
14) bng_re: Add QP control path verbs
15) bng_re: Register device operations with ib-core
Thanks,
Siva
Mohammed Faraaz (1):
RDMA/bng_re: Add AH verbs
Raqib Jones (1):
RDMA/bng_re: Add MPC, XID management, doorbell infrastructure
Sachin Holla (2):
bnge: reserve TX/completion rings for the RoCE MPC channel
RDMA/bng_re: Add GID verbs
Saswati Das (2):
bnge: Add infrastructure support for RoCE MPC channels
bnge: Add HSI definitions for 64-bit doorbell and MPC channels
Siva Reddy Kallam (3):
RDMA/bng_re: Add ucontext/mmap verbs
RDMA/bng_re: Add CQ verbs
RDMA/bng_re: Register with ib-core
Suresh Rupanagudi (3):
RDMA/bng_re: Add PD verbs
RDMA/bng_re: Add MR verbs
RDMA/bng_re: Add SRQ verbs
Usman Ansari (2):
RDMA/bng_re: Add support verbs
RDMA/bng_re: Add Stats verbs
Usman S. Ansari (1):
RDMA/bng_re: Add QP verbs
drivers/infiniband/hw/bng_re/Makefile | 16 +-
drivers/infiniband/hw/bng_re/bng_debugfs.c | 308 ++
drivers/infiniband/hw/bng_re/bng_dev.c | 269 +-
drivers/infiniband/hw/bng_re/bng_fp.c | 38 +
drivers/infiniband/hw/bng_re/bng_fp.h | 8 +
drivers/infiniband/hw/bng_re/bng_fw.c | 6 +-
drivers/infiniband/hw/bng_re/bng_fw.h | 27 +-
.../infiniband/hw/bng_re/bng_hw_counters.c | 304 ++
.../infiniband/hw/bng_re/bng_hw_counters.h | 118 +
drivers/infiniband/hw/bng_re/bng_mpc.c | 574 +++
drivers/infiniband/hw/bng_re/bng_re.h | 275 ++
.../infiniband/hw/bng_re/bng_re_mpc_roce.c | 1861 ++++++++++
.../infiniband/hw/bng_re/bng_re_mpc_roce.h | 218 ++
drivers/infiniband/hw/bng_re/bng_res.c | 299 +-
drivers/infiniband/hw/bng_re/bng_res.h | 321 +-
drivers/infiniband/hw/bng_re/bng_roce_hsi.h | 317 ++
drivers/infiniband/hw/bng_re/bng_sp.c | 1965 ++++++++++
drivers/infiniband/hw/bng_re/bng_sp.h | 458 +++
drivers/infiniband/hw/bng_re/bng_verbs.c | 3211 +++++++++++++++++
drivers/infiniband/hw/bng_re/bng_verbs.h | 379 ++
drivers/infiniband/hw/bng_re/bng_xid.c | 110 +
drivers/infiniband/hw/bng_re/bng_xid.h | 26 +
.../infiniband/hw/bng_re/bng_xid_allocator.c | 870 +++++
drivers/infiniband/hw/bng_re/xid_allocator.h | 114 +
.../net/ethernet/broadcom/bnge/bnge_auxr.c | 1 +
.../net/ethernet/broadcom/bnge/bnge_auxr.h | 1 +
.../net/ethernet/broadcom/bnge/bnge_resc.c | 32 +-
include/linux/bnge/hsi.h | 161 +
include/uapi/rdma/bng_re-abi.h | 118 +
include/uapi/rdma/ib_user_ioctl_verbs.h | 1 +
30 files changed, 12356 insertions(+), 50 deletions(-)
create mode 100644 drivers/infiniband/hw/bng_re/bng_fp.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_fp.h
create mode 100644 drivers/infiniband/hw/bng_re/bng_hw_counters.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_hw_counters.h
create mode 100644 drivers/infiniband/hw/bng_re/bng_mpc.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_re_mpc_roce.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_re_mpc_roce.h
create mode 100644 drivers/infiniband/hw/bng_re/bng_verbs.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_verbs.h
create mode 100644 drivers/infiniband/hw/bng_re/bng_xid.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_xid.h
create mode 100644 drivers/infiniband/hw/bng_re/bng_xid_allocator.c
create mode 100644 drivers/infiniband/hw/bng_re/xid_allocator.h
create mode 100644 include/uapi/rdma/bng_re-abi.h
--
2.43.5
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 01/15] bnge: Add infrastructure support for RoCE MPC channels
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 02/15] bnge: Add HSI definitions for 64-bit doorbell and " Siva Reddy Kallam
` (13 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Saswati Das, Siva Reddy Kallam,
Vikas Gupta, Dharmender Garg
From: Saswati Das <saswati.das@broadcom.com>
Add the initial L2 driver plumbing for RoCE MPC (PRIMATE channel)
support:
- Plumb BAR1 through to the RoCE auxiliary device info, alongside the
existing BAR0, so the RoCE driver can reach the doorbell BAR.
This will be used by follow-on patches to issue RoCE commands over the
MPC channels to the firmware.
Signed-off-by: Saswati Das <saswati.das@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
Reviewed-by: Vikas Gupta <vikas.gupta@broadcom.com>
Reviewed-by: Dharmender Garg <dharmender.garg@broadcom.com>
---
drivers/net/ethernet/broadcom/bnge/bnge_auxr.c | 1 +
drivers/net/ethernet/broadcom/bnge/bnge_auxr.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
index 0955b488b6fe..87ae40eab592 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
@@ -181,6 +181,7 @@ static void bnge_set_auxr_dev_info(struct bnge_auxr_dev *auxr_dev,
auxr_dev->pf_port_id = bd->pf.port_id;
auxr_dev->en_state = bd->state;
auxr_dev->bar0 = bd->bar0;
+ auxr_dev->bar1 = bd->bar1;
}
void bnge_rdma_aux_device_add(struct bnge_dev *bd)
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.h b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.h
index 6c5c15ef2b0a..13cb9289f5b8 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.h
@@ -46,6 +46,7 @@ struct bnge_auxr_dev {
struct net_device *net;
struct pci_dev *pdev;
void __iomem *bar0;
+ void __iomem *bar1;
struct bnge_msix_info msix_info[BNGE_MAX_ROCE_MSIX];
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 02/15] bnge: Add HSI definitions for 64-bit doorbell and MPC channels
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 01/15] bnge: Add infrastructure support for RoCE MPC channels Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 03/15] bnge: reserve TX/completion rings for the RoCE MPC channel Siva Reddy Kallam
` (12 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Saswati Das, Siva Reddy Kallam,
Vikas Gupta, Dharmender Garg
From: Saswati Das <saswati.das@broadcom.com>
Add the Hardware-Software Interface (HSI) structure and macro definitions
required to support 64-bit doorbells and Multi-Path Channel (MPC)
firmware commands/completions for next-generation RoCE:
- Add struct dbc_dbc64: Introduce the 64-bit doorbell layout along with
field masks, shift offsets, and type/path enumerations. This includes
queue types (SQ, RQ, SRQ, CQ, NQ, etc.) and paths (ROCE, L2, ENGINE).
- Add MPC Command Macros: Define the main MPC request types (ICA, RCA,
PFVF, EVENT) and their corresponding subtypes used to manage security
associations (SA enable, key rotation, SA stats/init), QP/AH
modification/batching, and PF/VF commands (QP/AH creation,
modification, and destruction).
- Add MPC Completion Macros & Structs: Add completion error codes for ICA
and introduce the 8-byte completion header structure (struct
mpc_cmpl_hdr) used to parse MPC firmware responses.
These definitions will be used by the driver to interface with the
firmware over the MPC/PRIMATE channels.
Signed-off-by: Saswati Das <saswati.das@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
Reviewed-by: Vikas Gupta <vikas.gupta@broadcom.com>
Reviewed-by: Dharmender Garg <dharmender.garg@broadcom.com>
---
include/linux/bnge/hsi.h | 161 +++++++++++++++++++++++++++++++++++++++
1 file changed, 161 insertions(+)
diff --git a/include/linux/bnge/hsi.h b/include/linux/bnge/hsi.h
index 1f7bd96415a5..a3383f2c7381 100644
--- a/include/linux/bnge/hsi.h
+++ b/include/linux/bnge/hsi.h
@@ -12510,6 +12510,43 @@ struct dbc_dbc {
#define DBC_DBC_TYPE_LAST DBC_DBC_TYPE_NULL
};
+/* dbc_dbc64 (size:64b/8B) */
+struct dbc_dbc64 {
+ __le64 dbc;
+ #define DBC_DBC64_INDEX_MASK 0xffffffUL
+ #define DBC_DBC64_INDEX_SFT 0
+ #define DBC_DBC64_EPOCH 0x1000000UL
+ #define DBC_DBC64_TOGGLE_MASK 0x6000000UL
+ #define DBC_DBC64_TOGGLE_SFT 25
+ #define DBC_DBC64_XID_MASK 0xfffff00000000ULL
+ #define DBC_DBC64_XID_SFT 32
+ #define DBC_DBC64_PATH_MASK 0x300000000000000ULL
+ #define DBC_DBC64_PATH_SFT 56
+ #define DBC_DBC64_PATH_ROCE (0x0ULL << 56)
+ #define DBC_DBC64_PATH_L2 (0x1ULL << 56)
+ #define DBC_DBC64_PATH_ENGINE (0x2ULL << 56)
+ #define DBC_DBC64_PATH_LAST DBC_DBC64_PATH_ENGINE
+ #define DBC_DBC64_VALID 0x400000000000000ULL
+ #define DBC_DBC64_DEBUG_TRACE 0x800000000000000ULL
+ #define DBC_DBC64_TYPE_MASK 0xf000000000000000ULL
+ #define DBC_DBC64_TYPE_SFT 60
+ #define DBC_DBC64_TYPE_SQ (0x0ULL << 60)
+ #define DBC_DBC64_TYPE_RQ (0x1ULL << 60)
+ #define DBC_DBC64_TYPE_SRQ (0x2ULL << 60)
+ #define DBC_DBC64_TYPE_SRQ_ARM (0x3ULL << 60)
+ #define DBC_DBC64_TYPE_CQ (0x4ULL << 60)
+ #define DBC_DBC64_TYPE_CQ_ARMSE (0x5ULL << 60)
+ #define DBC_DBC64_TYPE_CQ_ARMALL (0x6ULL << 60)
+ #define DBC_DBC64_TYPE_CQ_ARMENA (0x7ULL << 60)
+ #define DBC_DBC64_TYPE_SRQ_ARMENA (0x8ULL << 60)
+ #define DBC_DBC64_TYPE_CQ_CUTOFF_ACK (0x9ULL << 60)
+ #define DBC_DBC64_TYPE_NQ (0xaULL << 60)
+ #define DBC_DBC64_TYPE_NQ_ARM (0xbULL << 60)
+ #define DBC_DBC64_TYPE_NQ_MASK (0xeULL << 60)
+ #define DBC_DBC64_TYPE_NULL (0xfULL << 60)
+ #define DBC_DBC64_TYPE_LAST DBC_DBC64_TYPE_NULL
+};
+
/* db_push_start (size:64b/8B) */
struct db_push_start {
u64 db;
@@ -12605,4 +12642,128 @@ struct hcomm_status {
#define HCOMM_STATUS_STRUCT_LOC 0x31001F0UL
+/* MPC macros */
+#define MPC_CMD_HDR_REQ_TYPE_ICA 0x1UL
+#define MPC_CMD_HDR_REQ_TYPE_RCA 0x2UL
+#define MPC_CMD_HDR_REQ_TYPE_PFVF 0x3UL
+#define MPC_CMD_HDR_REQ_TYPE_EVENT 0x4UL
+#define MPC_CMD_HDR_REQ_TYPE_LAST MPC_CMD_HDR_REQ_TYPE_EVENT
+
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_SA_ENABLE 0x0UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_SM_PSP_SA_INIT 0x1UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_SM_PSP_NEW_RX_ASSOC 0x2UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_SM_PSP_KEY_ROTATE 0x3UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_CM_PSP_SA_INIT 0x4UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_CM_PSP_KEY_ROTATE 0x5UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_CM_PSP_KEY_SET 0x6UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_SA_INFO_GET 0x7UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_SA_STATS_GET 0x8UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_SA_STATS_CLEAR 0x9UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_SA_RX_MATCH_RULE_SET 0xaUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_ICA_SM_PSP_SA_SET 0xbUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_RCA_QP_MODIFY 0x0UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_RCA_AH_MODIFY 0x1UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_RCA_QP_MODIFY_CMPL 0x2UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_RCA_AH_MODIFY_CMPL 0x3UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_RCA_QP_MODIFY_BATCH 0x4UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_RCA_AH_MODIFY_BATCH 0x5UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_RCA_QP_MODIFY_BATCH_CMPL 0x6UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_RCA_AH_MODIFY_BATCH_CMPL 0x7UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_RCA_MPC_TEST_CMD 0xc8UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_RCA_MPC_TEST_CMPL 0xc9UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_EVENT_RESP_CMPL 0x0UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_CREATE 0x0UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_CREATE 0x1UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_MODIFY 0x2UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_MODIFY 0x3UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_DESTROY 0x4UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_DESTROY 0x5UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_CREATE_CMPL 0xaUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_CREATE_CMPL 0xbUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_MODIFY_CMPL 0xcUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_MODIFY_CMPL 0xdUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_DESTROY_CMPL 0xeUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_DESTROY_CMPL 0xfUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_CREATE_FWD_REQ 0x14UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_CREATE_FWD_REQ 0x15UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_MODIFY_FWD_REQ 0x16UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_MODIFY_FWD_REQ 0x17UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_DESTROY_FWD_REQ 0x18UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_DESTROY_FWD_REQ 0x19UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_CREATE_FWD_RESP 0x1eUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_CREATE_FWD_RESP 0x1fUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_MODIFY_FWD_RESP 0x20UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_MODIFY_FWD_RESP 0x21UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_DESTROY_FWD_RESP 0x22UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_DESTROY_FWD_RESP 0x23UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_ALL_FWD_RESP_CMPL 0x28UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_CREATE_BATCH 0x32UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_CREATE_BATCH 0x33UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_MODIFY_BATCH 0x34UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_MODIFY_BATCH 0x35UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_DESTROY_BATCH 0x36UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_DESTROY_BATCH 0x37UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_CREATE_BATCH_CMPL 0x3cUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_CREATE_BATCH_CMPL 0x3dUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_MODIFY_BATCH_CMPL 0x3eUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_MODIFY_BATCH_CMPL 0x3fUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_DESTROY_BATCH_CMPL 0x40UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_DESTROY_BATCH_CMPL 0x41UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_CREATE_BATCH_FWD_REQ 0x46UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_CREATE_BATCH_FWD_REQ 0x47UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_MODIFY_BATCH_FWD_REQ 0x48UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_MODIFY_BATCH_FWD_REQ 0x49UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_DESTROY_BATCH_FWD_REQ 0x4aUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_DESTROY_BATCH_FWD_REQ 0x4bUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_CREATE_BATCH_FWD_RESP 0x50UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_CREATE_BATCH_FWD_RESP 0x51UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_MODIFY_BATCH_FWD_RESP 0x52UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_MODIFY_BATCH_FWD_RESP 0x53UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_DESTROY_BATCH_FWD_RESP 0x54UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_DESTROY_BATCH_FWD_RESP 0x55UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_ALL_BATCH_FWD_RESP_CMPL 0x5aUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_DRV_RGTR 0x64UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_DRV_RGTR_CMPL 0x65UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_DRV_UNRGTR 0x66UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_DRV_UNRGTR_CMPL 0x67UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_BUF_RGTR 0x68UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_BUF_RGTR_CMPL 0x69UL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_BUF_UNRGTR 0x6aUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_BUF_UNRGTR_CMPL 0x6bUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_MPC_TEST_CMD 0xcaUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_MPC_TEST_CMPL 0xcbUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_MPC_TEST_FWD_CMD 0xccUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_MPC_TEST_FWD_CMPL 0xcdUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_MPC_TEST_FWD_REQ 0xceUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_MPC_TEST_FWD_RESP 0xcfUL
+#define MPC_CMD_HDR_REQ_SUB_TYPE_LAST MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_MPC_TEST_FWD_RESP
+
+#define MPC_CMPL_HDR_ERROR_CODE_ICA_ERR_NONE 0x0UL
+#define MPC_CMPL_HDR_ERROR_CODE_ICA_ERR_BAD_TARGET_ID 0x1UL
+#define MPC_CMPL_HDR_ERROR_CODE_ICA_ERR_SA_NOT_INIT_ENABLED 0x2UL
+#define MPC_CMPL_HDR_ERROR_CODE_ICA_ERR_HW_CMD_FAILED 0x3UL
+#define MPC_CMPL_HDR_ERROR_CODE_ICA_ERR_BAD_PSP_MODE 0x4UL
+#define MPC_CMPL_HDR_ERROR_CODE_ICA_ERR_BAD_SPI 0x5UL
+#define MPC_CMPL_HDR_ERROR_CODE_ICA_ERR_SPI_THRESHOLD_BREACHED 0x6UL
+#define MPC_CMPL_HDR_ERROR_CODE_ICA_ERR_ACTIVE_QPS_EXIST 0x7UL
+#define MPC_CMPL_HDR_ERROR_CODE_LAST MPC_CMPL_HDR_ERROR_CODE_ICA_ERR_ACTIVE_QPS_EXIST
+
+/* mpc_cmpl_hdr (size:64b/8B) */
+struct mpc_cmpl_hdr {
+ u8 cmpl_type_reserved;
+ #define CMPL_TYPE_RESERVED_CMPL_TYPE_MASK 0x3fUL
+ #define CMPL_TYPE_RESERVED_CMPL_TYPE_SFT 0
+ #define CMPL_TYPE_RESERVED_CMPL_TYPE_MPC_CMP_SHORT 0x1eUL
+ #define CMPL_TYPE_RESERVED_CMPL_TYPE_MPC_CMP_LONG 0x1fUL
+ #define CMPL_TYPE_RESERVED_CMPL_TYPE_LAST CMPL_TYPE_RESERVED_CMPL_TYPE_MPC_CMP_LONG
+ u8 error_code_mp_client;
+ #define ERROR_CODE_MP_CLIENT_ERROR_CODE_MASK 0xfUL
+ #define ERROR_CODE_MP_CLIENT_ERROR_CODE_SFT 0
+ #define ERROR_CODE_MP_CLIENT_MP_CLIENT_MASK 0xf0UL
+ #define ERROR_CODE_MP_CLIENT_MP_CLIENT_SFT 4
+ u8 req_type;
+ u8 req_subtype;
+ __le32 opaque;
+};
+
#endif /* _BNGE_HSI_H_ */
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 03/15] bnge: reserve TX/completion rings for the RoCE MPC channel
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 01/15] bnge: Add infrastructure support for RoCE MPC channels Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 02/15] bnge: Add HSI definitions for 64-bit doorbell and " Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 15:52 ` Jakub Kicinski
2026-09-04 10:43 ` [PATCH 04/15] RDMA/bng_re: Add MPC, XID management, doorbell infrastructure Siva Reddy Kallam
` (11 subsequent siblings)
14 siblings, 1 reply; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Sachin Holla, Siva Reddy Kallam,
Vikas Gupta, Dharmender Garg
From: Sachin Holla <sachin.holla@broadcom.com>
When RoCE is enabled, the bng_re driver allocates its own TX/CQ rings
for the MPC control channel out of the same per-function FW ring pool.
Enhanced bnge_reserve_rings() to account for this extra ring count to
avoid pool overflow while allocating netdev rings.
Signed-off-by: Sachin Holla <sachin.holla@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
Reviewed-by: Vikas Gupta <vikas.gupta@broadcom.com>
Reviewed-by: Dharmender Garg <dharmender.garg@broadcom.com>
---
.../net/ethernet/broadcom/bnge/bnge_resc.c | 32 ++++++++++++++++---
1 file changed, 28 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
index 4711dd4945ff..9dfb22b7c7c7 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
@@ -110,9 +110,24 @@ static u16 bnge_nqs_demand(struct bnge_dev *bd)
return bd->nq_nr_rings + bnge_aux_get_msix(bd);
}
+static u16 bnge_tx_rings_demand(struct bnge_dev *bd)
+{
+ u16 tx_rings = bd->tx_nr_rings;
+
+ if (bnge_is_roce_en(bd))
+ tx_rings += 1; /* For MPC TX ring */
+
+ return tx_rings;
+}
+
static u16 bnge_cprs_demand(struct bnge_dev *bd)
{
- return bd->tx_nr_rings + bd->rx_nr_rings;
+ u16 cprs = bd->tx_nr_rings + bd->rx_nr_rings;
+
+ if (bnge_is_roce_en(bd))
+ cprs += 1; /* For MPC CQ ring */
+
+ return cprs;
}
static u16 bnge_get_avail_msix(struct bnge_dev *bd, int num)
@@ -250,7 +265,7 @@ static bool bnge_need_reserve_rings(struct bnge_dev *bd)
u16 nqs = bnge_nqs_demand(bd);
u16 vnic;
- if (hw_resc->resv_tx_rings != bd->tx_nr_rings)
+ if (hw_resc->resv_tx_rings != bnge_tx_rings_demand(bd))
return true;
vnic = bnge_get_total_vnics(bd, rx);
@@ -275,6 +290,7 @@ int bnge_reserve_rings(struct bnge_dev *bd)
u16 nq = bd->nq_nr_rings;
u16 aux_msix = 0;
bool sh = false;
+ u16 tx_demand;
u16 tx_cp;
int rc;
@@ -293,11 +309,12 @@ int bnge_reserve_rings(struct bnge_dev *bd)
hwr.nq = bnge_nqs_demand(bd);
}
- hwr.tx = bd->tx_nr_rings;
+ tx_demand = bnge_tx_rings_demand(bd);
+ hwr.tx = tx_demand;
hwr.rx = bd->rx_nr_rings;
if (bd->flags & BNGE_EN_SHARED_CHNL)
sh = true;
- hwr.cmpl = hwr.rx + hwr.tx;
+ hwr.cmpl = bnge_cprs_demand(bd);
hwr.vnic = bnge_get_total_vnics(bd, hwr.rx);
@@ -327,6 +344,13 @@ int bnge_reserve_rings(struct bnge_dev *bd)
if (hwr.stat > bnge_aux_get_stat_ctxs(bd))
hwr.stat -= bnge_aux_get_stat_ctxs(bd);
hwr.nq = min_t(u16, hwr.nq, hwr.stat);
+ /* Drop MPC TX ring from L2 TX ring count; it is owned by RoCE driver */
+ if (bnge_is_roce_en(bd)) {
+ if (hwr.tx >= tx_demand)
+ hwr.tx -= 1;
+ else
+ return -ENOMEM;
+ }
/* Adjust the rings */
rc = bnge_adjust_rings(bd, &rx_rings, &hwr.tx, hwr.nq, sh);
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 04/15] RDMA/bng_re: Add MPC, XID management, doorbell infrastructure
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (2 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 03/15] bnge: reserve TX/completion rings for the RoCE MPC channel Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 05/15] RDMA/bng_re: Add support verbs Siva Reddy Kallam
` (10 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Raqib Jones, Siva Reddy Kallam
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=all, Size: 158435 bytes --]
From: Raqib Jones <raqib.jones@broadcom.com>
This patch has below changes
New MPC channel introduced for AH/QP operations
xid management support for AH/QP
Doorbell infrastructure changes
Signed-off-by: Raqib Jones <raqib.jones@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/Makefile | 13 +-
drivers/infiniband/hw/bng_re/bng_debugfs.c | 308 +++
drivers/infiniband/hw/bng_re/bng_dev.c | 98 +-
drivers/infiniband/hw/bng_re/bng_fw.c | 1 +
drivers/infiniband/hw/bng_re/bng_fw.h | 8 +-
drivers/infiniband/hw/bng_re/bng_mpc.c | 574 +++++
drivers/infiniband/hw/bng_re/bng_re.h | 174 ++
.../infiniband/hw/bng_re/bng_re_mpc_roce.c | 1863 +++++++++++++++++
.../infiniband/hw/bng_re/bng_re_mpc_roce.h | 218 ++
drivers/infiniband/hw/bng_re/bng_res.c | 114 +-
drivers/infiniband/hw/bng_re/bng_res.h | 78 +-
drivers/infiniband/hw/bng_re/bng_roce_hsi.h | 94 +
drivers/infiniband/hw/bng_re/bng_sp.h | 4 +
drivers/infiniband/hw/bng_re/bng_xid.c | 110 +
drivers/infiniband/hw/bng_re/bng_xid.h | 26 +
.../infiniband/hw/bng_re/bng_xid_allocator.c | 870 ++++++++
drivers/infiniband/hw/bng_re/xid_allocator.h | 114 +
17 files changed, 4631 insertions(+), 36 deletions(-)
create mode 100644 drivers/infiniband/hw/bng_re/bng_mpc.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_re_mpc_roce.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_re_mpc_roce.h
create mode 100644 drivers/infiniband/hw/bng_re/bng_xid.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_xid.h
create mode 100644 drivers/infiniband/hw/bng_re/bng_xid_allocator.c
create mode 100644 drivers/infiniband/hw/bng_re/xid_allocator.h
diff --git a/drivers/infiniband/hw/bng_re/Makefile b/drivers/infiniband/hw/bng_re/Makefile
index 17e9d5871d40..ea0c6951728f 100644
--- a/drivers/infiniband/hw/bng_re/Makefile
+++ b/drivers/infiniband/hw/bng_re/Makefile
@@ -3,6 +3,13 @@ ccflags-y := -I $(srctree)/drivers/net/ethernet/broadcom/bnge
obj-$(CONFIG_INFINIBAND_BNG_RE) += bng_re.o
-bng_re-y := bng_dev.o bng_fw.o \
- bng_res.o bng_sp.o \
- bng_debugfs.o
+bng_re-y := \
+ bng_xid_allocator.o \
+ bng_xid.o \
+ bng_mpc.o \
+ bng_re_mpc_roce.o \
+ bng_res.o \
+ bng_debugfs.o \
+ bng_fw.o \
+ bng_dev.o \
+ bng_sp.o
diff --git a/drivers/infiniband/hw/bng_re/bng_debugfs.c b/drivers/infiniband/hw/bng_re/bng_debugfs.c
index 9ec5a8785250..7f1e9bedc4e7 100644
--- a/drivers/infiniband/hw/bng_re/bng_debugfs.c
+++ b/drivers/infiniband/hw/bng_re/bng_debugfs.c
@@ -2,24 +2,332 @@
// Copyright (c) 2025 Broadcom.
#include <linux/debugfs.h>
#include <linux/pci.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
#include <rdma/ib_verbs.h>
#include "bng_res.h"
+#include "bng_sp.h"
#include "bng_fw.h"
#include "bnge.h"
#include "bnge_auxr.h"
#include "bng_re.h"
#include "bng_debugfs.h"
+#include "bng_re_mpc_roce.h"
static struct dentry *bng_re_debugfs_root;
+/* MPC tuning parameter read/write functions */
+static ssize_t mpc_short_timeout_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct bng_re_dev *rdev = file->private_data;
+ char tmp[16];
+
+ if (!rdev || !rdev->mpc_roce_creq.mpc_tune_params)
+ return -ENODEV;
+
+ snprintf(tmp, sizeof(tmp), "%u\n", rdev->mpc_roce_creq.mpc_tune_params->short_timeout);
+ return simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp));
+}
+
+static ssize_t mpc_short_timeout_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct bng_re_dev *rdev = file->private_data;
+ u32 val;
+ int ret;
+
+ if (!rdev || !rdev->mpc_roce_creq.mpc_tune_params)
+ return -ENODEV;
+
+ ret = kstrtou32_from_user(buf, count, 0, &val);
+ if (ret)
+ return ret;
+
+ rdev->mpc_roce_creq.mpc_tune_params->short_timeout = val;
+ return count;
+}
+
+static const struct file_operations mpc_short_timeout_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = mpc_short_timeout_read,
+ .write = mpc_short_timeout_write,
+};
+
+static ssize_t mpc_long_timeout_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct bng_re_dev *rdev = file->private_data;
+ char tmp[16];
+
+ if (!rdev || !rdev->mpc_roce_creq.mpc_tune_params)
+ return -ENODEV;
+
+ snprintf(tmp, sizeof(tmp), "%u\n", rdev->mpc_roce_creq.mpc_tune_params->long_timeout);
+ return simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp));
+}
+
+static ssize_t mpc_long_timeout_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct bng_re_dev *rdev = file->private_data;
+ u32 val;
+ int ret;
+
+ if (!rdev || !rdev->mpc_roce_creq.mpc_tune_params)
+ return -ENODEV;
+
+ ret = kstrtou32_from_user(buf, count, 0, &val);
+ if (ret)
+ return ret;
+
+ if (!val)
+ return -EINVAL;
+
+ rdev->mpc_roce_creq.mpc_tune_params->long_timeout = val;
+ return count;
+}
+
+static const struct file_operations mpc_long_timeout_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = mpc_long_timeout_read,
+ .write = mpc_long_timeout_write,
+};
+
+static ssize_t mpc_max_retries_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct bng_re_dev *rdev = file->private_data;
+ char tmp[16];
+
+ if (!rdev || !rdev->mpc_roce_creq.mpc_tune_params)
+ return -ENODEV;
+
+ snprintf(tmp, sizeof(tmp), "%u\n", rdev->mpc_roce_creq.mpc_tune_params->max_retries);
+ return simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp));
+}
+
+static ssize_t mpc_max_retries_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct bng_re_dev *rdev = file->private_data;
+ u16 val;
+ int ret;
+
+ if (!rdev || !rdev->mpc_roce_creq.mpc_tune_params)
+ return -ENODEV;
+
+ ret = kstrtou16_from_user(buf, count, 0, &val);
+ if (ret)
+ return ret;
+
+ rdev->mpc_roce_creq.mpc_tune_params->max_retries = val;
+ return count;
+}
+
+static const struct file_operations mpc_max_retries_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = mpc_max_retries_read,
+ .write = mpc_max_retries_write,
+};
+
+static ssize_t mpc_retry_sleep_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct bng_re_dev *rdev = file->private_data;
+ char tmp[16];
+
+ if (!rdev || !rdev->mpc_roce_creq.mpc_tune_params)
+ return -ENODEV;
+
+ snprintf(tmp, sizeof(tmp), "%u\n", rdev->mpc_roce_creq.mpc_tune_params->retry_sleep);
+ return simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp));
+}
+
+static ssize_t mpc_retry_sleep_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct bng_re_dev *rdev = file->private_data;
+ u16 val;
+ int ret;
+
+ if (!rdev || !rdev->mpc_roce_creq.mpc_tune_params)
+ return -ENODEV;
+
+ ret = kstrtou16_from_user(buf, count, 0, &val);
+ if (ret)
+ return ret;
+
+ rdev->mpc_roce_creq.mpc_tune_params->retry_sleep = val;
+ return count;
+}
+
+static const struct file_operations mpc_retry_sleep_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = mpc_retry_sleep_read,
+ .write = mpc_retry_sleep_write,
+};
+
+static void bng_re_add_mpctune_knobs(struct bng_re_dev *rdev)
+{
+ struct dentry *mpctune_dir;
+
+ if (!rdev || !rdev->dbg_root)
+ return;
+
+ /* Create mpctune directory under debug root directory */
+ mpctune_dir = debugfs_create_dir("mpctune", rdev->dbg_root);
+ if (IS_ERR_OR_NULL(mpctune_dir)) {
+ dev_dbg(rdev_to_dev(rdev), "Unable to create mpctune debugfs dir\n");
+ return;
+ }
+
+ /* Create read-write files for each parameter - pass rdev as private_data */
+ debugfs_create_file("short_timeout", 0600, mpctune_dir,
+ rdev, &mpc_short_timeout_fops);
+ debugfs_create_file("long_timeout", 0600, mpctune_dir,
+ rdev, &mpc_long_timeout_fops);
+ debugfs_create_file("max_retries", 0600, mpctune_dir,
+ rdev, &mpc_max_retries_fops);
+ debugfs_create_file("retry_sleep", 0600, mpctune_dir,
+ rdev, &mpc_retry_sleep_fops);
+}
+
+static int bng_re_mpc_irq_info_show(struct seq_file *s, void *unused)
+{
+ struct bng_re_dev *rdev = s->private;
+ struct bng_mpc_ctx *mpc = rdev->mpc;
+
+ if (!mpc)
+ return -ENODEV;
+
+ seq_puts(s, "MPC IRQ info:\n");
+ seq_printf(s, "\t MPC IRQ Requested : %s\n\t irq_name : %s\n\t msix_vec: 0x%x\n\t NQ Ring ID: 0x%x\n",
+ mpc->requested ? "YES" : "NO",
+ mpc->irq_name,
+ mpc->msix_vec,
+ mpc->ring_id);
+
+ seq_puts(s, "MPC IRQ Stats:\n");
+ seq_printf(s, "\t Num IRQ Started: 0x%x\n\t Num IRQ Stopped : 0x%x\n\t Num IRQ Received : 0x%llx\n",
+ mpc->stats.num_irq_started,
+ mpc->stats.num_irq_stopped,
+ mpc->stats.num_irq_received);
+ seq_printf(s, "\t Num NQ Rearmed : 0x%llx\n\t Num tasklet rescheduled : 0x%llx\n",
+ mpc->stats.num_nq_rearm,
+ mpc->stats.num_tasklet_resched);
+ seq_printf(s, "\t Num CQ pending/has_more_work: 0x%llx\n",
+ mpc->stats.num_cq_pending);
+
+ seq_puts(s, "MPC NQ Info:\n");
+ seq_printf(s, "\t Last Consumer ID: 0x%x\n\t Max elements: 0x%x\n",
+ mpc->hwq.cons,
+ mpc->hwq.max_elements);
+ seq_puts(s, "\n");
+ return 0;
+}
+
+static int bng_re_mpc_irq_info_open(struct inode *inode, struct file *file)
+{
+ struct bng_re_dev *rdev = inode->i_private;
+
+ return single_open(file, bng_re_mpc_irq_info_show, rdev);
+}
+
+static const struct file_operations bng_re_mpc_irq_info_ops = {
+ .owner = THIS_MODULE,
+ .open = bng_re_mpc_irq_info_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* MPC performance and statistics display (must precede bng_re_debugfs_add_pdev) */
+static int bng_re_mpc_stats_show(struct seq_file *s, void *unused)
+{
+ struct bng_re_dev *rdev = s->private;
+
+ if (!rdev)
+ return -ENODEV;
+
+ seq_puts(s, "=== MPC RoCE Performance Statistics ===\n");
+ bng_re_mpc_roce_perf_debugfs_show(rdev, s, 1);
+
+ seq_puts(s, "\n=== MPC RoCE Diagnostic Counters ===\n");
+ bng_re_mpc_roce_diag_counters_debugfs_show(rdev, s);
+
+ return 0;
+}
+
+static int bng_re_mpc_stats_open(struct inode *inode, struct file *file)
+{
+ struct bng_re_dev *rdev = inode->i_private;
+
+ return single_open(file, bng_re_mpc_stats_show, rdev);
+}
+
+static const struct file_operations bng_re_mpc_stats_ops = {
+ .owner = THIS_MODULE,
+ .open = bng_re_mpc_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static ssize_t mpc_stats_clear_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct bng_re_dev *rdev = file->private_data;
+
+ if (!rdev)
+ return -ENODEV;
+
+ bng_re_mpc_roce_perf_debugfs_clear(rdev);
+ bng_re_mpc_roce_diag_counters_clear(rdev);
+
+ return count;
+}
+
+static const struct file_operations mpc_stats_clear_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = mpc_stats_clear_write,
+};
+
void bng_re_debugfs_add_pdev(struct bng_re_dev *rdev)
{
struct pci_dev *pdev = rdev->aux_dev->pdev;
rdev->dbg_root =
debugfs_create_dir(dev_name(&pdev->dev), bng_re_debugfs_root);
+
+ if (IS_ERR_OR_NULL(rdev->dbg_root)) {
+ dev_dbg(rdev_to_dev(rdev), "Unable to create debugfs %s",
+ dev_name(&pdev->dev));
+ return;
+ }
+
+ /* Add MPC IRQ info debugfs entry */
+ debugfs_create_file("mpc_irq", 0400, rdev->dbg_root,
+ rdev, &bng_re_mpc_irq_info_ops);
+
+ /* Add MPC statistics display */
+ debugfs_create_file("mpc_stats", 0400, rdev->dbg_root,
+ rdev, &bng_re_mpc_stats_ops);
+
+ /* Add MPC statistics clear functionality */
+ debugfs_create_file("mpc_stats_clear", 0200, rdev->dbg_root,
+ rdev, &mpc_stats_clear_fops);
+
+ /* Add MPC tuning knobs for supported chips */
+ if (rdev->mpc_roce_creq.mpc_tune_params)
+ bng_re_add_mpctune_knobs(rdev);
}
void bng_re_debugfs_rem_pdev(struct bng_re_dev *rdev)
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index 311c8bc93160..ea6528e80ce3 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -15,6 +15,8 @@
#include "bng_re.h"
#include "bnge_hwrm.h"
#include "bng_debugfs.h"
+#include "bng_re_mpc_roce.h"
+#include "bng_xid.h"
MODULE_AUTHOR("Siva Reddy Kallam <siva.kallam@broadcom.com>");
MODULE_DESCRIPTION(BNG_RE_DESC);
@@ -41,7 +43,6 @@ static struct bng_re_dev *bng_re_dev_add(struct auxiliary_device *adev,
return rdev;
}
-
static int bng_re_register_netdev(struct bng_re_dev *rdev)
{
struct bnge_auxr_dev *aux_dev;
@@ -54,6 +55,8 @@ static void bng_re_destroy_chip_ctx(struct bng_re_dev *rdev)
{
struct bng_re_chip_ctx *chip_ctx;
+ bng_res_unmap_db_bar(&rdev->bng_res);
+
kfree(rdev->dev_attr);
rdev->dev_attr = NULL;
@@ -65,6 +68,18 @@ static void bng_re_destroy_chip_ctx(struct bng_re_dev *rdev)
kfree(chip_ctx);
}
+static void bng_re_set_db_offset(struct bng_re_dev *rdev)
+{
+ struct bnge_auxr_dev *aux_dev;
+ struct bng_re_res *res;
+
+ res = &rdev->bng_res;
+ aux_dev = rdev->aux_dev;
+
+ res->dpi_tbl.ucreg.offset = aux_dev->l2_db_offset;
+ res->dpi_tbl.wcreg.offset = aux_dev->l2_db_size;
+}
+
static int bng_re_setup_chip_ctx(struct bng_re_dev *rdev)
{
struct bng_re_chip_ctx *chip_ctx;
@@ -87,23 +102,31 @@ static int bng_re_setup_chip_ctx(struct bng_re_dev *rdev)
goto free_chip_ctx;
rdev->bng_res.dattr = rdev->dev_attr;
+ bng_re_set_db_offset(rdev);
+ rc = bng_res_map_db_bar(&rdev->bng_res);
+ if (rc)
+ goto free_attr;
+
return 0;
+free_attr:
+ kfree(rdev->dev_attr);
+ rdev->dev_attr = NULL;
free_chip_ctx:
kfree(rdev->chip_ctx);
rdev->chip_ctx = NULL;
return rc;
}
-static void bng_re_init_hwrm_hdr(struct input *hdr, u16 opcd)
+void bng_re_init_hwrm_hdr(struct input *hdr, u16 opcd)
{
hdr->req_type = cpu_to_le16(opcd);
hdr->cmpl_ring = cpu_to_le16(-1);
hdr->target_id = cpu_to_le16(-1);
}
-static void bng_re_fill_fw_msg(struct bnge_fw_msg *fw_msg, void *msg,
- int msg_len, void *resp, int resp_max_len,
- int timeout)
+void bng_re_fill_fw_msg(struct bnge_fw_msg *fw_msg, void *msg,
+ int msg_len, void *resp, int resp_max_len,
+ int timeout)
{
fw_msg->msg = msg;
fw_msg->msg_len = msg_len;
@@ -112,8 +135,8 @@ static void bng_re_fill_fw_msg(struct bnge_fw_msg *fw_msg, void *msg,
fw_msg->timeout = timeout;
}
-static int bng_re_net_ring_free(struct bng_re_dev *rdev,
- u32 fw_ring_id, int type)
+int bng_re_net_ring_free(struct bng_re_dev *rdev,
+ u32 fw_ring_id, int type)
{
struct bnge_auxr_dev *aux_dev = rdev->aux_dev;
struct hwrm_ring_free_input req = {};
@@ -125,17 +148,17 @@ static int bng_re_net_ring_free(struct bng_re_dev *rdev,
req.ring_type = type;
req.ring_id = cpu_to_le32(fw_ring_id);
bng_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp,
- sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
+ sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
rc = bnge_send_msg(aux_dev, &fw_msg);
if (rc)
- ibdev_err(&rdev->ibdev, "Failed to free HW ring:%d :%#x",
- req.ring_id, rc);
+ ibdev_err(&rdev->ibdev, "Failed to free HW ring:%u :%#x",
+ fw_ring_id, rc);
return rc;
}
-static int bng_re_net_ring_alloc(struct bng_re_dev *rdev,
- struct bng_re_ring_attr *ring_attr,
- u16 *fw_ring_id)
+int bng_re_net_ring_alloc(struct bng_re_dev *rdev,
+ struct bng_re_ring_attr *ring_attr,
+ u16 *fw_ring_id)
{
struct bnge_auxr_dev *aux_dev = rdev->aux_dev;
struct hwrm_ring_alloc_input req = {};
@@ -224,8 +247,8 @@ static int bng_re_query_hwrm_version(struct bng_re_dev *rdev)
ver_get_req.hwrm_intf_min = HWRM_VERSION_MINOR;
ver_get_req.hwrm_intf_upd = HWRM_VERSION_UPDATE;
bng_re_fill_fw_msg(&fw_msg, (void *)&ver_get_req, sizeof(ver_get_req),
- (void *)&ver_get_resp, sizeof(ver_get_resp),
- BNGE_DFLT_HWRM_CMD_TIMEOUT);
+ (void *)&ver_get_resp, sizeof(ver_get_resp),
+ BNGE_DFLT_HWRM_CMD_TIMEOUT);
rc = bnge_send_msg(aux_dev, &fw_msg);
if (rc) {
ibdev_err(&rdev->ibdev, "Failed to query HW version, rc = 0x%x",
@@ -251,8 +274,15 @@ static int bng_re_query_hwrm_version(struct bng_re_dev *rdev)
static void bng_re_dev_uninit(struct bng_re_dev *rdev)
{
int rc;
+
+ bng_deinit_mpc(rdev);
+ bng_re_free_xid_tables(&rdev->bng_res);
+
bng_re_debugfs_rem_pdev(rdev);
+ if (test_and_clear_bit(BNG_RE_FLAG_TBLS_ALLOC_INITED, &rdev->flags))
+ bng_res_free_tbls(&rdev->bng_res);
+
if (test_and_clear_bit(BNG_RE_FLAG_RCFW_CHANNEL_EN, &rdev->flags)) {
rc = bng_re_deinit_rcfw(&rdev->rcfw);
if (rc)
@@ -262,7 +292,7 @@ static void bng_re_dev_uninit(struct bng_re_dev *rdev)
bng_re_free_stats_ctx_mem(rdev->bng_res.pdev, &rdev->stats_ctx);
bng_re_disable_rcfw_channel(&rdev->rcfw);
bng_re_net_ring_free(rdev, rdev->rcfw.creq.ring_id,
- RING_ALLOC_REQ_RING_TYPE_NQ);
+ RING_ALLOC_REQ_RING_TYPE_NQ);
bng_re_free_rcfw_channel(&rdev->rcfw);
}
@@ -286,7 +316,7 @@ static int bng_re_dev_init(struct bng_re_dev *rdev)
rc = bng_re_register_netdev(rdev);
if (rc) {
ibdev_err(&rdev->ibdev,
- "Failed to register with netedev: %#x\n", rc);
+ "Failed to register with netedev: %#x\n", rc);
goto reg_netdev_fail;
}
@@ -347,7 +377,7 @@ static int bng_re_dev_init(struct bng_re_dev *rdev)
vid = rdev->nqr->msix_entries[BNG_RE_CREQ_NQ_IDX].vector;
rc = bng_re_enable_fw_channel(&rdev->rcfw,
- vid, db_offt);
+ vid, db_offt);
if (rc) {
ibdev_err(&rdev->ibdev, "Failed to enable RCFW channel: %#x\n",
rc);
@@ -358,6 +388,13 @@ static int bng_re_dev_init(struct bng_re_dev *rdev)
if (rc)
goto disable_rcfw;
+ rc = bng_re_init_xid_tables(rdev);
+ if (rc) {
+ ibdev_err(&rdev->ibdev,
+ "Failed to init XID / IQM tables: %#x\n", rc);
+ goto disable_rcfw;
+ }
+
bng_re_debugfs_add_pdev(rdev);
rc = bng_re_alloc_stats_ctx_mem(rdev->bng_res.pdev, rdev->chip_ctx,
&rdev->stats_ctx);
@@ -370,7 +407,7 @@ static int bng_re_dev_init(struct bng_re_dev *rdev)
rc = bng_re_stats_ctx_alloc(rdev);
if (rc) {
ibdev_err(&rdev->ibdev,
- "Failed to allocate QPLIB context: %#x\n", rc);
+ "Failed to allocate QP context: %#x\n", rc);
goto free_stats_ctx;
}
@@ -382,12 +419,32 @@ static int bng_re_dev_init(struct bng_re_dev *rdev)
}
set_bit(BNG_RE_FLAG_RCFW_CHANNEL_EN, &rdev->flags);
+ rc = bng_res_alloc_init_tbls(&rdev->bng_res);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to allocate tbls: %#x\n", rc);
+ goto free_sctx;
+ }
+ set_bit(BNG_RE_FLAG_TBLS_ALLOC_INITED, &rdev->flags);
+
+ rtnl_lock();
+ rc = bng_alloc_init_mpc(rdev);
+ rtnl_unlock();
+ if (rc) {
+ ibdev_err(&rdev->ibdev,
+ "MPC alloc-init failed rc = %#x\n", rc);
+ goto deinit_mpc;
+ }
return 0;
+deinit_mpc:
+ bng_deinit_mpc(rdev);
+ if (test_and_clear_bit(BNG_RE_FLAG_TBLS_ALLOC_INITED, &rdev->flags))
+ bng_res_free_tbls(&rdev->bng_res);
free_sctx:
bng_re_stats_ctx_free(rdev);
free_stats_ctx:
bng_re_free_stats_ctx_mem(rdev->bng_res.pdev, &rdev->stats_ctx);
disable_rcfw:
+ bng_re_free_xid_tables(&rdev->bng_res);
bng_re_disable_rcfw_channel(&rdev->rcfw);
free_ring:
bng_re_net_ring_free(rdev, rdev->rcfw.creq.ring_id, type);
@@ -434,7 +491,6 @@ static int bng_re_add_device(struct auxiliary_device *adev)
return rc;
}
-
static void bng_re_remove_device(struct bng_re_dev *rdev,
struct auxiliary_device *aux_dev)
{
@@ -442,7 +498,6 @@ static void bng_re_remove_device(struct bng_re_dev *rdev,
ib_dealloc_device(&rdev->ibdev);
}
-
static int bng_re_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
@@ -495,7 +550,6 @@ static int __init bng_re_mod_init(void)
{
int rc;
-
bng_re_register_debugfs();
rc = auxiliary_driver_register(&bng_re_driver);
diff --git a/drivers/infiniband/hw/bng_re/bng_fw.c b/drivers/infiniband/hw/bng_re/bng_fw.c
index ab6a2d2e95b5..a69221368ba8 100644
--- a/drivers/infiniband/hw/bng_re/bng_fw.c
+++ b/drivers/infiniband/hw/bng_re/bng_fw.c
@@ -723,6 +723,7 @@ int bng_re_deinit_rcfw(struct bng_re_rcfw *rcfw)
clear_bit(FIRMWARE_INITIALIZED_FLAG, &rcfw->cmdq.flags);
return 0;
}
+
static inline bool _is_hw_retx_supported(u16 dev_cap_flags)
{
return dev_cap_flags &
diff --git a/drivers/infiniband/hw/bng_re/bng_fw.h b/drivers/infiniband/hw/bng_re/bng_fw.h
index c89c926ec2fc..f89e4aeb2469 100644
--- a/drivers/infiniband/hw/bng_re/bng_fw.h
+++ b/drivers/infiniband/hw/bng_re/bng_fw.h
@@ -5,6 +5,8 @@
#define __BNG_FW_H__
#include "bng_tlv.h"
+#include "bnge.h"
+#include "bnge_auxr.h"
/* FW DB related */
#define BNG_FW_CMDQ_TRIG_VAL 1
@@ -43,7 +45,6 @@ struct bng_re_crsbe {
u8 data[1024];
};
-
static inline u32 bng_fw_cmdqe_npages(u32 depth)
{
u32 npages;
@@ -58,6 +59,7 @@ static inline u32 bng_fw_cmdqe_page_size(u32 depth)
{
return (bng_fw_cmdqe_npages(depth) * PAGE_SIZE);
}
+
struct bng_re_cmdq_mbox {
struct bng_re_reg_desc reg;
void __iomem *prod;
@@ -208,4 +210,8 @@ int bng_re_rcfw_send_message(struct bng_re_rcfw *rcfw,
int bng_re_init_rcfw(struct bng_re_rcfw *rcfw,
struct bng_re_stats *stats_ctx);
int bng_re_deinit_rcfw(struct bng_re_rcfw *rcfw);
+
+void bng_re_init_hwrm_hdr(struct input *hdr, u16 opcd);
+void bng_re_fill_fw_msg(struct bnge_fw_msg *fw_msg, void *msg, int msg_len,
+ void *resp, int resp_max_len, int timeout);
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_mpc.c b/drivers/infiniband/hw/bng_re/bng_mpc.c
new file mode 100644
index 000000000000..7bcad0dea632
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_mpc.c
@@ -0,0 +1,574 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Broadcom.
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+
+#include "bng_roce_hsi.h"
+#include "bng_tlv.h"
+#include "bng_res.h"
+#include "bng_sp.h"
+#include "bng_re.h"
+#include "bng_re_mpc_roce.h"
+#include "bng_fw.h"
+
+struct rdma_user_mmap_entry;
+
+#ifndef DBC_GROUP_SQ
+#define DBC_GROUP_SQ 0
+#define DBC_GROUP_CQ 3
+#endif
+
+static irqreturn_t bng_mpc_irq(int irq, void *dev_instance)
+{
+ struct bng_mpc_ctx *mpc = dev_instance;
+
+ tasklet_schedule(&mpc->mpc_tasklet);
+ dev_dbg(rdev_to_dev(mpc->rdev), "%s: MPC IRQ Received (0x%llx)\n",
+ __func__, mpc->stats.num_irq_received);
+ mpc->stats.num_irq_received++;
+
+ return IRQ_HANDLED;
+}
+
+static void bng_service_mpc(struct tasklet_struct *t)
+{
+ struct bng_mpc_ctx *mpc = from_tasklet(mpc, t, mpc_tasklet);
+
+ struct bng_re_res *res = &mpc->rdev->bng_res;
+ struct bng_re_hwq *nq_hwq = &mpc->hwq;
+ struct bng_re_dev *rdev = mpc->rdev;
+ int budget = 16; /*mpc->budget;*/
+ u32 has_more_nq = 1;
+ struct nq_base *nqe;
+ u32 type;
+
+ /*Process pending MPC completions before servicing NQ*/
+ if (mpc->cq_pending) {
+ mpc->cq_pending = bng_re_mpc_roce_service_cne(rdev, NULL,
+ BNG_MPC_MAX_MESSAGES_PER_POLL);
+ if (mpc->cq_pending) {
+ mpc->stats.num_cq_pending++;
+ mpc->stats.num_tasklet_resched++;
+ tasklet_schedule(&mpc->mpc_tasklet);
+ dev_dbg(rdev_to_dev(mpc->rdev),
+ "%s:%d MPC IRQ Rescheduled due to CQ pending\n",
+ __func__, __LINE__);
+ return;
+ }
+ }
+
+ spin_lock_bh(&nq_hwq->lock);
+ do {
+ nqe = bng_re_get_qe(nq_hwq, nq_hwq->cons, NULL);
+ if (!NQE_CMP_VALID(nqe, mpc->nq_db.dbinfo.flags)) {
+ has_more_nq = 0;
+ break;
+ }
+ /* The valid test of the entry must be done first before
+ * reading any further.
+ */
+ dma_rmb();
+ type = le16_to_cpu(nqe->info10_type) & NQ_BASE_TYPE_MASK;
+ switch (type) {
+ case NQ_CN_TYPE_CQ_NOTIFICATION:
+ mpc->cq_pending =
+ bng_re_mpc_roce_service_cne(rdev, nqe,
+ BNG_MPC_MAX_MESSAGES_PER_POLL);
+ if (mpc->cq_pending)
+ mpc->stats.num_cq_pending++;
+
+ break;
+ default:
+ dev_err(rdev_to_dev(mpc->rdev), "%s: Unsupported NQ type(0x%x)\n",
+ __func__, type);
+ }
+ bng_re_hwq_incr_cons(nq_hwq->max_elements, &nq_hwq->cons,
+ 1, &mpc->nq_db.dbinfo.flags);
+ dev_dbg(rdev_to_dev(mpc->rdev),
+ "%s: MPC IRQ NQ Processed Cons(0x%x) flag(0x%x) type(0x%x) max(0x%x)\n",
+ __func__, nq_hwq->cons, mpc->nq_db.dbinfo.flags,
+ type, nq_hwq->max_elements);
+ } while ((budget--) && (!mpc->cq_pending));
+
+ if (mpc->cq_pending) {
+ /* Update the consumer index only and dont enable arm */
+ bng_re_ring_nq_db(&mpc->nq_db.dbinfo, res->cctx, false);
+ mpc->stats.num_tasklet_resched++;
+ tasklet_schedule(&mpc->mpc_tasklet);
+ dev_dbg(rdev_to_dev(mpc->rdev),
+ "%s:%d MPC IRQ NQ Rescheduled due to CQ pending\n",
+ __func__, __LINE__);
+ } else {
+ if (!has_more_nq) {
+ mpc->stats.num_nq_rearm++;
+ bng_re_ring_nq_db(&mpc->nq_db.dbinfo, res->cctx, true);
+ dev_dbg(rdev_to_dev(mpc->rdev),
+ "%s: MPC IRQ NQ Armed flag(0x%x)\n",
+ __func__, mpc->nq_db.dbinfo.flags);
+ } else if (mpc->requested) {
+ /* Update the consumer index only and dont enable arm */
+ bng_re_ring_nq_db(&mpc->nq_db.dbinfo, res->cctx, false);
+ mpc->stats.num_tasklet_resched++;
+ tasklet_schedule(&mpc->mpc_tasklet);
+ dev_dbg(rdev_to_dev(mpc->rdev),
+ "%s: MPC IRQ NQ Rescheduled due to NQ pending\n",
+ __func__);
+ }
+ }
+ spin_unlock_bh(&nq_hwq->lock);
+}
+
+static int bng_mpc_start_irq(struct bng_mpc_ctx *mpc, int msix_vector,
+ bool need_init)
+{
+ struct bng_re_res *res;
+ int rc;
+
+ res = &mpc->rdev->bng_res;
+
+ if (mpc->requested) {
+ dev_info(rdev_to_dev(mpc->rdev), "%s: MPC IRQ - Already Started\n", __func__);
+ return 0;
+ }
+ mpc->msix_vec = msix_vector;
+ if (need_init)
+ tasklet_setup(&mpc->mpc_tasklet,
+ bng_service_mpc);
+ else
+ tasklet_enable(&mpc->mpc_tasklet);
+
+ mpc->irq_name = kasprintf(GFP_KERNEL, "bng_re-mpc@pci:%s",
+ pci_name(res->pdev));
+ if (!mpc->irq_name) {
+ dev_err(rdev_to_dev(mpc->rdev), "%s: Failed to alloc irq_name\n", __func__);
+ return -ENOMEM;
+ }
+ rc = request_irq(mpc->msix_vec, bng_mpc_irq, 0,
+ mpc->irq_name, mpc);
+ if (rc) {
+ kfree(mpc->irq_name);
+ mpc->irq_name = NULL;
+ tasklet_disable(&mpc->mpc_tasklet);
+ dev_err(rdev_to_dev(mpc->rdev), "%s: request_irq failed rc(0x%x)\n", __func__, rc);
+ return rc;
+ }
+ mpc->requested = true;
+ bng_re_ring_nq_db(&mpc->nq_db.dbinfo, res->cctx, true);
+
+ mpc->stats.num_irq_started++;
+
+ return 0;
+}
+
+void bng_mpc_stop_irq(struct bng_mpc_ctx *mpc, bool kill)
+{
+ struct bng_re_res *res;
+
+ dev_dbg(rdev_to_dev(mpc->rdev), "%s: Start(%d)\n",
+ __func__, mpc->requested);
+
+ if (!mpc->requested)
+ return;
+
+ mpc->requested = false;
+ res = &mpc->rdev->bng_res;
+ /* Mask h/w interrupt */
+ bng_re_ring_nq_db(&mpc->nq_db.dbinfo, res->cctx, false);
+ /* Sync with last running IRQ handler */
+ synchronize_irq(mpc->msix_vec);
+ irq_set_affinity_hint(mpc->msix_vec, NULL);
+ free_irq(mpc->msix_vec, mpc);
+ kfree(mpc->irq_name);
+ mpc->irq_name = NULL;
+
+ /* Cleanup Tasklet */
+ if (kill)
+ tasklet_kill(&mpc->mpc_tasklet);
+ else
+ tasklet_disable(&mpc->mpc_tasklet);
+}
+
+static void bng_map_mpc_nq_db(struct bng_mpc_ctx *mpc, u32 reg_offt)
+{
+ struct bng_re_reg_desc *dbreg;
+ struct bng_re_nq_db *nq_db;
+ struct bng_re_res *res;
+
+ nq_db = &mpc->nq_db;
+ res = &mpc->rdev->bng_res;
+ dbreg = &res->dpi_tbl.ucreg;
+
+ nq_db->reg.bar_id = dbreg->bar_id;
+ nq_db->reg.bar_base = dbreg->bar_base;
+ nq_db->reg.bar_reg = dbreg->bar_reg + reg_offt;
+ nq_db->reg.len = sizeof(u64);
+
+ nq_db->dbinfo.db = nq_db->reg.bar_reg;
+ nq_db->dbinfo.hwq = &mpc->hwq;
+ nq_db->dbinfo.xid = mpc->ring_id;
+ nq_db->dbinfo.seed = mpc->ring_id;
+ nq_db->dbinfo.flags = 0;
+ spin_lock_init(&nq_db->dbinfo.lock);
+ nq_db->dbinfo.res = &mpc->rdev->bng_res;
+}
+
+static int bng_alloc_mpc_nq_mem(struct bng_mpc_ctx *mpc)
+{
+ struct bng_re_hwq_attr hwq_attr = {};
+ struct bng_re_sg_info sginfo = {};
+
+ if (!mpc->hwq.max_elements ||
+ mpc->hwq.max_elements > BNG_NQE_MAX_CNT)
+ mpc->hwq.max_elements = BNG_NQE_MAX_CNT;
+
+ sginfo.pgsize = PAGE_SIZE;
+ sginfo.pgshft = PAGE_SHIFT;
+ hwq_attr.res = &mpc->rdev->bng_res;
+ hwq_attr.sginfo = &sginfo;
+ hwq_attr.depth = mpc->hwq.max_elements;
+ hwq_attr.stride = sizeof(struct nq_base);
+ hwq_attr.type = BNG_HWQ_TYPE_QUEUE;
+ if (bng_re_alloc_init_hwq(&mpc->hwq, &hwq_attr)) {
+ dev_err(&mpc->rdev->bng_res.pdev->dev, "QP: MPC NQ allocation failed");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void bng_free_mpc_nq_mem(struct bng_mpc_ctx *mpc)
+{
+ if (mpc->hwq.max_elements) {
+ bng_re_free_hwq(&mpc->rdev->bng_res, &mpc->hwq);
+ mpc->hwq.max_elements = 0;
+ }
+}
+
+static int bng_setup_mpc_nq(struct bng_mpc_ctx *mpc)
+{
+ struct bng_re_dev *rdev = mpc->rdev;
+ struct bng_re_ring_attr rattr = {};
+ int depth;
+ u32 offt;
+ u16 vec;
+ int rc;
+
+ mutex_init(&rdev->nqr->load_lock);
+
+ depth = BNG_NQE_MAX_CNT;
+ vec = rdev->nqr->msix_entries[BNG_RE_MPC_IRQ_IDX].vector;
+ offt = rdev->nqr->msix_entries[BNG_RE_MPC_IRQ_IDX].db_offset;
+ mpc->hwq.max_elements = depth;
+ rc = bng_alloc_mpc_nq_mem(mpc);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev),
+ "Failed to get mem for MPC NQ %d, rc = 0x%x",
+ BNG_RE_MPC_IRQ_IDX, rc);
+ return rc;
+ }
+
+ rattr.dma_arr = mpc->hwq.pbl[BNG_PBL_LVL_0].pg_map_arr;
+ rattr.pages = mpc->hwq.pbl[mpc->hwq.level].pg_count;
+ rattr.type = RING_ALLOC_REQ_RING_TYPE_NQ;
+ rattr.mode = RING_ALLOC_REQ_INT_MODE_MSIX;
+ rattr.depth = mpc->hwq.max_elements - 1;
+ rattr.lrid = rdev->nqr->msix_entries[BNG_RE_MPC_IRQ_IDX].ring_idx;
+
+ rc = bng_re_net_ring_alloc(rdev, &rattr, &mpc->ring_id);
+ if (rc) {
+ mpc->ring_id = 0xffff; /* Invalid ring-id */
+ dev_err(rdev_to_dev(rdev),
+ "%s:pages(0x%x) type(0x%x) mode(0x%x)depth(0x%x) lrid(0x%x) ring_id(0x%x)",
+ __func__, rattr.pages, rattr.type, rattr.mode,
+ rattr.depth, rattr.lrid, mpc->ring_id);
+ dev_err(rdev_to_dev(rdev),
+ "Failed to get fw id for MPC NQ %d, rc = 0x%x",
+ BNG_RE_MPC_IRQ_IDX, rc);
+ goto fail_ring;
+ }
+ dev_dbg(rdev_to_dev(rdev),
+ "%s: pages(0x%x) type(0x%x) mode(0x%x) depth(0x%x) lrid(0x%x) ring_id(0x%x)",
+ __func__, rattr.pages, rattr.type, rattr.mode,
+ rattr.depth, rattr.lrid, mpc->ring_id);
+
+ return 0;
+
+fail_ring:
+ bng_free_mpc_nq_mem(mpc);
+ return rc;
+}
+
+static int bng_enable_mpc_nq(struct bng_mpc_ctx *mpc, int msix_vec, int bar_reg_offset)
+{
+ int rc;
+
+ rc = bng_setup_mpc_nq(mpc);
+ if (rc)
+ return rc;
+
+ bng_map_mpc_nq_db(mpc, bar_reg_offset);
+
+ rc = bng_mpc_start_irq(mpc, msix_vec, true);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int bng_disable_mpc_nq(struct bng_mpc_ctx *mpc)
+{
+ /* Make sure the HW is stopped! */
+ bng_mpc_stop_irq(mpc, true);
+
+ mpc->nq_db.reg.bar_reg = NULL;
+ mpc->nq_db.dbinfo.db = NULL;
+
+ mpc->msix_vec = 0;
+
+ return 0;
+}
+
+static int bng_alloc_mpc_ctx(struct bng_re_dev *rdev)
+{
+ rdev->mpc = kzalloc_obj(*rdev->mpc, GFP_KERNEL);
+ if (!rdev->mpc)
+ return -ENOMEM;
+
+ rdev->mpc->rdev = rdev;
+ return 0;
+}
+
+static void bng_free_mpc_ctx(struct bng_re_dev *rdev)
+{
+ kfree(rdev->mpc);
+ rdev->mpc = NULL;
+}
+
+static int bng_alloc_init_mpc_irq(struct bng_re_dev *rdev)
+{
+ int reg_offset = 0;
+ int msix_vec = 0;
+ int rc = 0;
+
+ rc = bng_alloc_mpc_ctx(rdev);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "%s: MPC Ctx Alloc Failed rc (0x%x)\n", __func__, rc);
+ return rc;
+ }
+
+ msix_vec = rdev->nqr->msix_entries[BNG_RE_MPC_IRQ_IDX].vector;
+ reg_offset = rdev->nqr->msix_entries[BNG_RE_MPC_IRQ_IDX].db_offset;
+
+ rc = bng_enable_mpc_nq(rdev->mpc, msix_vec, reg_offset);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "%s: Enable MPC NQ Failed rc (0x%x)\n", __func__, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+static void bng_mask_mpc_irq(struct bng_re_dev *rdev)
+{
+ if (!rdev->mpc)
+ return;
+
+ bng_disable_mpc_nq(rdev->mpc);
+}
+
+static void bng_deinit_mpc_irq(struct bng_re_dev *rdev)
+{
+ if (!rdev->mpc)
+ return;
+
+ bng_free_mpc_nq_mem(rdev->mpc);
+ bng_re_net_ring_free(rdev, rdev->mpc->ring_id, RING_ALLOC_REQ_RING_TYPE_NQ);
+ bng_free_mpc_ctx(rdev);
+}
+
+/**
+ * bng_mpc_background_thread_fn - Continuously poll the MPC CQ.
+ * @data: struct bng_re_dev * (RoCE device)
+ * Returns: 0
+ */
+static int bng_mpc_background_thread_fn(void *data)
+{
+ struct bng_re_dev *rdev = (struct bng_re_dev *)data;
+
+ while (!kthread_should_stop()) {
+ usleep_range(BNG_MPC_MIN_USLEEP_POLL, BNG_MPC_MAX_USLEEP_POLL);
+ bng_re_mpc_roce_service_creq(rdev, BNG_MPC_MAX_MESSAGES_PER_POLL);
+ }
+
+ dev_dbg(rdev_to_dev(rdev), "%s: terminating\n", __func__);
+ return 0;
+}
+
+/**
+ * bng_mpc_start_background_poll - Start polling MPC CQ in the background.
+ * @rdev: RoCE device
+ * @new_thread: out param to cache the created thread
+ * Returns: 0 for success
+ */
+int bng_mpc_start_background_poll(struct bng_re_dev *rdev,
+ struct task_struct **new_thread)
+{
+ *new_thread = kthread_create(bng_mpc_background_thread_fn,
+ rdev, "mpc poll background thread");
+ if (IS_ERR(*new_thread)) {
+ dev_err(rdev_to_dev(rdev), "%s: kthread_create failed\n", __func__);
+ return -EPERM;
+ }
+ sched_set_fifo(*new_thread);
+ wake_up_process(*new_thread);
+ return 0;
+}
+
+/**
+ * bng_mpc_stop_background_poll - Stop the MPC background poll thread.
+ * @rdev: RoCE device
+ * @thread: thread to stop (set to NULL on return)
+ */
+void bng_mpc_stop_background_poll(struct bng_re_dev *rdev,
+ struct task_struct **thread)
+{
+ int rc = 0;
+
+ if (*thread) {
+ rc = kthread_stop(*thread);
+ *thread = NULL;
+ }
+
+ if (rc)
+ dev_err(rdev_to_dev(rdev), "%s: kthread_stop returned %d\n",
+ __func__, rc);
+}
+
+static int bng_alloc_init_mpc_creq(struct bng_re_dev *rdev)
+{
+ int rc = 0;
+
+ /* init mpc creq channel */
+ rdev->mpc_roce_creq.bm_lr_index = rdev->nqr->msix_entries[BNG_RE_MPC_IRQ_IDX].ring_idx;
+ rdev->mpc_roce_creq.bm_nq_ring_id = rdev->mpc->ring_id;
+ rdev->mpc_roce_creq.bm_stat_index = rdev->stats_ctx.fw_id;
+
+ dev_info(rdev_to_dev(rdev),
+ "[%s:%p:%d] bm_lr_index:%d, nq_ring_id:%d, stat_index:%d\n",
+ __func__, current, __LINE__,
+ rdev->mpc_roce_creq.bm_lr_index,
+ rdev->mpc_roce_creq.bm_nq_ring_id,
+ rdev->mpc_roce_creq.bm_stat_index);
+
+ rc = bng_re_mpc_roce_alloc_init(rdev);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev),
+ "[%s:%p:%d] Error alloc init mpc roce creq (err : %d)\n",
+ __func__, current, __LINE__, rc);
+ return rc;
+ }
+ set_bit(BNG_RE_FLAG_MPC_FW_CHANNEL_EN, &rdev->flags);
+
+ return rc;
+}
+
+static int bng_deinit_mpc_creq(struct bng_re_dev *rdev)
+{
+ rdev->mpc_roce_creq.bm_mpc_uninstall_pending = 1;
+ bng_re_check_mpc_pending_empty(rdev);
+ if (test_and_clear_bit(BNG_RE_FLAG_MPC_FW_CHANNEL_EN, &rdev->flags))
+ /*bnge_deinit_and_free_mpc_roce_creq(rdev->en_dev, true);*/
+ bng_re_mpc_roce_deinit_free(rdev);
+ else
+ dev_dbg(rdev_to_dev(rdev),
+ "Did not free mpc roce creq rings");
+
+ return 0;
+}
+
+int bng_alloc_init_mpc(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ int rc = 0;
+
+ if (creq->mpc_cache && creq->mpc_tune_params && creq->bm_diag_counters)
+ return 0;
+
+ rc = bng_alloc_init_mpc_irq(rdev);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "%s: Init MPC IRQ Failed (0x%x)\n", __func__, rc);
+ return rc;
+ }
+ rc = bng_alloc_init_mpc_creq(rdev);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "%s: Alloc Init MPC CREQ Failed(0x%x)\n", __func__, rc);
+ return rc;
+ }
+ /* to make sure it isn't a ghost setting */
+ rdev->mpc_roce_creq.bm_mpc_uninstall_pending = 0;
+ return rc;
+}
+
+int bng_deinit_mpc(struct bng_re_dev *rdev)
+{
+ int rc = 0;
+
+ /* Mask the MPC IRQ and kill the tasklet before draining/freeing
+ * the CREQ hwqs and cache, so an in-flight completion can't run
+ * against memory that is about to be freed.
+ */
+ bng_mask_mpc_irq(rdev);
+
+ rc = bng_deinit_mpc_creq(rdev);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "%s: DeInit MPC CREQ Failed (0x%x)\n", __func__, rc);
+ return rc;
+ }
+
+ bng_deinit_mpc_irq(rdev);
+ return rc;
+}
+
+int bng_re_alloc_mpc_doorbells(struct bng_re_dev *rdev)
+{
+ /* the hw db recovery functionality is pending */
+ return 0;
+}
+
+void bng_re_unalloc_mpc_doorbells(struct bng_re_dev *rdev)
+{
+ struct bng_re_db_info *tx_db = &rdev->mpc_roce_creq.tx_db_info;
+ struct bng_re_db_info *cq_db = &rdev->mpc_roce_creq.cq_db_info;
+
+ tx_db->dbc = NULL;
+ cq_db->dbc = NULL;
+}
+
+void bng_re_check_mpc_pending_empty(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ int iters = 0;
+ const int max_iters = (CHECK_MPC_PENDING_MAX_SECS * 1000) / 20;
+
+ while (atomic_read(&creq->pending)) {
+ if (iters >= max_iters) {
+ dev_err(rdev_to_dev(rdev),
+ "%s - MPC pending did not drain within %d seconds\n",
+ __func__,
+ CHECK_MPC_PENDING_MAX_SECS);
+ return;
+ }
+ msleep(20);
+ iters++;
+ }
+ dev_dbg(rdev_to_dev(rdev),
+ "%s - MPC pending completed in %d iterations\n",
+ __func__,
+ iters);
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_re.h b/drivers/infiniband/hw/bng_re/bng_re.h
index dae4862621a7..d6fb205d9f12 100644
--- a/drivers/infiniband/hw/bng_re/bng_re.h
+++ b/drivers/infiniband/hw/bng_re/bng_re.h
@@ -4,7 +4,13 @@
#ifndef __BNG_RE_H__
#define __BNG_RE_H__
+#include "bnge.h"
+#include "bnge_auxr.h"
#include "bng_res.h"
+#include "bng_fw.h"
+#include <rdma/ib_verbs.h>
+
+#define BNG_RE_XID_AVOID_REUSE false
#define BNG_RE_ADEV_NAME "bng_en"
@@ -18,12 +24,165 @@
#define BNG_RE_CREQ_NQ_IDX 0
#define BNGE_INVALID_STATS_CTX_ID -1
+
+/* poll interval while polling for mpc roce creq response */
+#define BNG_MPC_MIN_USLEEP_POLL (5)
+#define BNG_MPC_MAX_USLEEP_POLL (15)
+#define BNG_MPC_MAX_MESSAGES_PER_POLL (32)
+
+/* limit how long we wait for mpc to drain during uninit */
+#define CHECK_MPC_PENDING_MAX_SECS (40)
+
+enum {
+ BNG_RE_AEQ_IDX = 0,
+ BNG_RE_MPC_IRQ_IDX,
+ BNG_RE_MAX_RSVD_IRQ,
+};
+
+/* MPC VF buffer specific information */
+#define BNG_MPC_MAX_REQ_SIZE 512
+#define BNG_MPC_MAX_NUM_REQ 1024
+#define BNG_MPC_MAX_VF_CMD_FWD_PAGES 1
+#define BNG_MPC_VF_BUF_PAGE_SLOTS 4
+
+/* RoCE MPC diagnostics and knobs (mirrors bnge for compatibility) */
+#define BNG_RE_MPC_MAX_LATENCY_SEC_SLAB_INDEX 201
+#define BNG_RE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX 3000
+#define BNG_RE_MPC_MAX_STAT_INDEX 0x3FFFF
+
+#define ROCE_MPC_MAX_STAT_INDEX BNG_RE_MPC_MAX_STAT_INDEX
+#define ROCE_MPC_MAX_LATENCY_SEC_SLAB_INDEX BNG_RE_MPC_MAX_LATENCY_SEC_SLAB_INDEX
+#define ROCE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX BNG_RE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX
+
+struct bng_re_mpc_poll_info {
+ u64 bm_last_poll_work_poll_jiffies_start;
+ u64 bm_last_poll_work_poll_jiffies_end;
+ u64 bm_max_poll_work_poll_jiffies;
+ u32 bm_current_cons_indx;
+ u64 bm_start_time_on_current_cons_indx;
+ u64 bm_curr_time_on_current_cons_indx;
+};
+
+struct bng_re_mpc_diag_counters {
+ atomic_t bm_mpc_verb_try;
+ atomic_t bm_mpc_sent_try;
+ atomic_t bm_mpc_sent_started_ok;
+ atomic_t bm_mpc_sent_started_ebusy;
+ atomic_t bm_mpc_sent_started_misc_err;
+ atomic_t bm_mpc_sent_response_ok;
+ atomic_t bm_mpc_sent_response_err;
+ atomic_t bm_mpc_fatal_ebusy;
+ atomic_t bm_mpc_sent_timeout;
+ atomic_t bm_mpc_misc_err;
+ atomic_t bm_mpc_consecutive_sent_err;
+ atomic_t bm_mpc_stalled_err;
+};
+
+struct bng_re_mpc_tune_params {
+ u32 short_timeout;
+ u32 long_timeout;
+ u16 max_retries;
+ u16 retry_sleep;
+};
+
+struct bng_re_mpc_roce_stats_info {
+ bool bms_stats_enabled;
+ u32 bms_qp_modify_stats_id;
+ u32 bms_ah_modify_stats_id;
+ u32 bms_other_stats_id;
+ u64 *bms_qp_modify_stats;
+ u64 *bms_ah_modify_stats;
+ u64 *bms_other_stats;
+ u32 *bms_lat_slab_sec;
+ u32 *bms_lat_slab_msec;
+};
+
+struct bng_re_mpc_roce_creq_info {
+ struct task_struct *bm_poll_thread;
+ u32 bm_lr_index;
+ u16 bm_nq_ring_id;
+ u32 bm_stat_index;
+ struct bng_re_db_info tx_db_info;
+ struct bng_re_db_info cq_db_info;
+ struct rdma_user_mmap_entry *tx_hdbr_mmap;
+ struct rdma_user_mmap_entry *cq_hdbr_mmap;
+
+ /* Transmit and completion rings (roce-style HWQ) */
+ struct bng_re_hwq tx_hwq;
+ struct bng_re_hwq cq_hwq;
+ u32 tx_ring_id;
+ u16 cq_ring_id;
+
+ /* SW ring for tx: maps prod index to (handle, inline_bds, last_cons) */
+ struct {
+ unsigned long handle;
+ u8 inline_bds;
+ unsigned long last_cons; /* BNG_RE_MPC_INV_HDL when completion received */
+ } *tx_sw_ring;
+ u32 tx_prod;
+ u32 tx_cons;
+ u16 tx_napi_idx; /* for opaque */
+ u32 tx_ring_mask; /* tx_hwq.max_elements - 1 */
+ u32 cq_ring_mask; /* cq_hwq.max_elements - 1 */
+ u32 cq_cp_bit; /* for CMP valid toggle */
+
+ /* lock access to the transmit ring */
+ spinlock_t tx_lock;
+ /* lock access to the mpc context */
+ spinlock_t mpc_ctx_lock;
+ /* lock access to receive buffer processing */
+ spinlock_t bm_lock;
+
+ /* Diagnostics and poll info (from bnge) */
+ struct bng_re_mpc_poll_info *bm_poll_info;
+ struct bng_re_mpc_diag_counters *bm_diag_counters;
+ struct bng_re_mpc_tune_params *mpc_tune_params;
+ struct bng_re_mpc_roce_stats_info *bm_stats_info;
+
+ /* Pending/context tracking (from bnge mpc_roce_info) */
+ atomic_t pending;
+ atomic_t max_pending;
+ u32 avail_buffer;
+ u32 min_avail_buffer;
+ struct kmem_cache *mpc_cache;
+ bool bm_mpc_stall;
+ u8 bm_mpc_uninstall_pending;
+};
+
+struct bng_mpc_stats {
+ u32 num_irq_started;
+ u32 num_irq_stopped;
+ u64 num_tasklet_resched;
+ u64 num_nq_rearm;
+ u64 num_irq_received;
+ u64 num_cq_pending;
+};
+
+struct bng_mpc_db {
+ struct bng_re_reg_desc reg;
+ void __iomem *db;
+ struct bng_re_db_info dbinfo;
+};
+
/* NQ specific structures */
struct bng_re_nq_db {
struct bng_re_reg_desc reg;
struct bng_re_db_info dbinfo;
};
+struct bng_mpc_ctx {
+ struct bng_re_dev *rdev;
+ struct bng_re_hwq hwq;
+ struct bng_re_nq_db nq_db;
+ struct bng_mpc_stats stats;
+ struct tasklet_struct mpc_tasklet;
+ char *irq_name;
+ int msix_vec;
+ u16 ring_id;
+ bool requested; /*irq handler installed */
+ bool cq_pending;
+};
+
struct bng_re_nq {
struct pci_dev *pdev;
struct bng_re_res *res;
@@ -68,6 +227,13 @@ struct bng_re_dev {
unsigned long flags;
#define BNG_RE_FLAG_NETDEV_REGISTERED 0
#define BNG_RE_FLAG_RCFW_CHANNEL_EN 1
+#define BNG_RE_FLAG_TBLS_ALLOC_INITED 2
+#define BNG_RE_FLAG_MPC_FW_CHANNEL_EN 31
+#define BNG_RE_FLAG_MPC_FW_THREAD_EN 32
+#define BNG_RE_FLAG_MPC_DB_PAGE_EN 34
+#define BNG_RE_FLAG_ERR_DEVICE_DETACHED 36
+
+#define BNG_RE_STEERING_TO_HOST 0
struct net_device *netdev;
struct auxiliary_device *adev;
struct bnge_auxr_dev *aux_dev;
@@ -80,6 +246,14 @@ struct bng_re_dev {
struct bng_re_dev_attr *dev_attr;
struct dentry *dbg_root;
struct bng_re_stats stats_ctx;
+ struct bng_re_mpc_roce_creq_info mpc_roce_creq;
+ struct bng_mpc_ctx *mpc;
};
+int bng_re_net_ring_alloc(struct bng_re_dev *rdev,
+ struct bng_re_ring_attr *ring_attr,
+ u16 *fw_ring_id);
+int bng_re_net_ring_free(struct bng_re_dev *rdev,
+ u32 fw_ring_id, int type);
+
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_re_mpc_roce.c b/drivers/infiniband/hw/bng_re/bng_re_mpc_roce.c
new file mode 100644
index 000000000000..3ecf5f34180a
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_re_mpc_roce.c
@@ -0,0 +1,1863 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Broadcom.
+
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+
+#include "bng_roce_hsi.h"
+#include "bng_re.h"
+#include "bng_res.h"
+#include "bng_fw.h"
+#include "bnge.h"
+#include "bnge_hwrm.h"
+#include "bng_re_mpc_roce.h"
+#include "bnge_auxr.h"
+
+/* Handle unsolicited MPC completion (e.g. RCA QP XID cleanup complete). */
+
+/**
+ * bng_re_mpc_handle_event_cmpl - core handler for unsolicited MPC event completions
+ * @rdev: RoCE device
+ * @cmpl: pointer to the 32-byte completion payload (mpc_event_resp_cmpl)
+ * @cmpl_len: byte length of the payload
+ *
+ * Called directly from bng_re_mpc_roce_service_creq when handle == 0 and the
+ * completion is an EVENT type, bypassing the old ULP callback mechanism.
+ */
+void bng_re_mpc_handle_event_cmpl(struct bng_re_dev *rdev,
+ void *cmpl, u32 cmpl_len)
+{
+ struct mpc_event_resp_cmpl *hdr = (struct mpc_event_resp_cmpl *)cmpl;
+ int event_data;
+ int event;
+ int xid;
+
+ if (!rdev || !cmpl) {
+ dev_warn(rdev_to_dev(rdev),
+ "%s: Invalid driver state or completion pointer: %lu,%lu\n",
+ __func__, (unsigned long)rdev, (unsigned long)cmpl);
+ return;
+ }
+ if (cmpl_len != sizeof(struct mpc_event_resp_cmpl)) {
+ dev_warn(rdev_to_dev(rdev),
+ "unsolicited mpc cmpl too short: %u < %zu\n",
+ cmpl_len, sizeof(struct mpc_event_resp_cmpl));
+ return;
+ }
+ xid = (int)le32_to_cpu(hdr->xid);
+ event = (int)le32_to_cpu(hdr->event_type);
+ event_data = (int)le32_to_cpu(hdr->event_data);
+ /* sanity check the fields look sane */
+ if (!(event == MPC_EVENT_RESP_CMPL_EVENT_TYPE_EVENT_TYPE_QP_EVENT &&
+ event_data == MPC_EVENT_RESP_CMPL_EVENT_DATA_EVENT_TYPE_FASTQP_MD_COMPLETE))
+ dev_warn(rdev_to_dev(rdev),
+ "unexpected event mpc cmpl: xid=%d event=%d data=%d\n",
+ xid, event, event_data);
+ /* process even if event / event_data isn't as expected, as we don't use them rn */
+ /* bng_re_qp_xid_pending_process_unsolicited_cmpl(rdev, xid); */
+}
+
+/**
+ * bng_re_mpc_process_vf_completion - Handle PF/VF MPC completion on the PF path
+ * @rdev: RoCE device
+ * @cmpl: Completion buffer (starts with &struct mpc_cmpl_hdr)
+ * @cmpl_len: Length of @cmpl in bytes
+ *
+ * Dispatches VF-related QP/AH create, modify, and destroy completions without
+ * registering with the bnge ULP. No-op if the device is detached or not registered.
+ */
+
+/**
+ * bng_re_roce_mpc_cmp - Deliver one solicited MPC completion to a command context
+ * @rdev: RoCE device
+ * @handle: Pointer to struct bng_re_roce_cmd_ctx cast to unsigned long
+ * @cmpl: Completion entry (payload and length)
+ *
+ * Copies the completion into the context, issues a write memory barrier, then
+ * either sets cmpl_available for polling mode or completes the wait queue.
+ */
+void bng_re_roce_mpc_cmp(struct bng_re_dev *rdev, unsigned long handle,
+ struct bng_re_cmpl_entry *cmpl)
+{
+ struct bng_re_roce_cmd_ctx *ctx;
+ struct mpc_cmpl_hdr *cmp;
+ u32 len;
+
+ dev_dbg(rdev_to_dev(rdev), "%s: handele:%lx\n", __func__, handle);
+
+ if (cmpl->len != MPC_ROCE_COMPL_MAX_SIZE) {
+ dev_warn(rdev_to_dev(rdev), "%s: Unexpected cmpl_len:%d\n",
+ __func__, cmpl->len);
+ return;
+ }
+ len = cmpl->len;
+ cmp = cmpl->cmpl;
+ ctx = (void *)handle;
+ if (!ctx) {
+ dev_warn(rdev_to_dev(rdev), "%s: ctx null\n",
+ __func__);
+ return;
+ }
+ memcpy(&ctx->roce_cmp, cmp, len);
+ /* make sure memory is updated */
+ smp_wmb();
+ if (ctx->poll_cmp)
+ WRITE_ONCE(ctx->cmpl_available, 1);
+ else
+ complete(&ctx->cmp);
+}
+
+/**
+ * bng_re_mpc_tx_avail - Free TX ring slots for new MPC posts
+ * @creq: RoCE MPC channel state
+ *
+ * Return: Number of unused BD slots in the MPC TX ring (masked ring arithmetic).
+ */
+static u32 bng_re_mpc_tx_avail(struct bng_re_mpc_roce_creq_info *creq)
+{
+ u32 used = READ_ONCE(creq->tx_prod) - READ_ONCE(creq->tx_cons);
+
+ return (creq->tx_ring_mask + 1) - (used & creq->tx_ring_mask);
+}
+
+/**
+ * bng_re_mpc_adv_tx_cons - Advance MPC TX consumer past completed multi-BD posts
+ * @creq: RoCE MPC channel state
+ *
+ * Walks the software TX ring from tx_cons while each slot's last_cons marks
+ * the post as fully completed, so out-of-order completions can be absorbed
+ * safely. Bounded by MAX_ADV_ITERATIONS to avoid infinite loops on corruption.
+ */
+static void bng_re_mpc_adv_tx_cons(struct bng_re_mpc_roce_creq_info *creq)
+{
+ u32 tx_cons, slot, tx_prod, diff;
+ unsigned int iter = 0;
+
+ if (unlikely(!creq || !creq->tx_sw_ring)) {
+ WARN_ONCE(1, "%s: null creq or tx_sw_ring\n", __func__);
+ return;
+ }
+
+ tx_cons = creq->tx_cons;
+ tx_prod = READ_ONCE(creq->tx_prod);
+ /* Use unsigned subtraction (modular u32) so the check remains correct
+ * after tx_prod wraps past 0xFFFFFFFF. A raw tx_cons > tx_prod
+ * comparison is unsafe at rollover: e.g. tx_prod=2, tx_cons=0xFFFFFFFE
+ * is a valid 4-entry-in-flight state but would incorrectly satisfy
+ * tx_cons > tx_prod.
+ */
+ if (unlikely((tx_prod - tx_cons) > (creq->tx_ring_mask + 1))) {
+ WARN_ONCE(1,
+ "%s: tx_cons (%u) > tx_prod (%u): consumer ahead of producer (double-consume, bad chain, or prod reset?)\n",
+ __func__, tx_cons, tx_prod);
+ return;
+ }
+
+ diff = tx_prod - tx_cons;
+ if (diff <= creq->tx_ring_mask && (diff & creq->tx_ring_mask) == 0)
+ return;
+
+ slot = tx_cons & creq->tx_ring_mask;
+ do {
+ tx_prod = READ_ONCE(creq->tx_prod);
+ diff = tx_prod - tx_cons;
+ if (diff <= creq->tx_ring_mask && (diff & creq->tx_ring_mask) == 0)
+ break;
+ {
+ u32 bds = creq->tx_sw_ring[slot].inline_bds;
+ u32 next = tx_cons + bds;
+
+ /* Use unsigned distance (modular u32) so the bound check
+ * stays correct after either counter wraps past 0xFFFFFFFF.
+ * A raw next > tx_prod comparison gives wrong results when
+ * next has wrapped and tx_prod has not, or vice versa.
+ */
+ if (unlikely((tx_prod - next) > (creq->tx_ring_mask + 1))) {
+ WARN_ONCE(1,
+ "%s: would advance tx_cons %u -> %u past tx_prod %u (bds=%u cons_slot=%u)\n",
+ __func__, tx_cons, next, tx_prod, bds, slot);
+ return;
+ }
+ tx_cons = next;
+ }
+ creq->tx_cons = tx_cons;
+ iter++;
+ /*
+ * Re-read producer after advancing: we may have just caught up
+ * (tx_cons == tx_prod) while the physical slot still shows INV for
+ * the next logical post — must exit before the while re-enters.
+ */
+ tx_prod = READ_ONCE(creq->tx_prod);
+ diff = tx_prod - tx_cons;
+ if (diff <= creq->tx_ring_mask && (diff & creq->tx_ring_mask) == 0)
+ break;
+ if (iter >= MAX_ADV_ITERATIONS)
+ break;
+ slot = tx_cons & creq->tx_ring_mask;
+ } while (creq->tx_sw_ring[slot].last_cons == BNG_RE_MPC_INV_HDL);
+
+ if (iter >= MAX_ADV_ITERATIONS) {
+ tx_prod = READ_ONCE(creq->tx_prod);
+ diff = tx_prod - tx_cons;
+ if (diff > creq->tx_ring_mask || (diff & creq->tx_ring_mask) != 0) {
+ u32 prod_slot = tx_prod & creq->tx_ring_mask;
+
+ slot = tx_cons & creq->tx_ring_mask;
+ pr_err("bng_re: %s: exceeded max iterations (%u) tx_cons=%u tx_prod=%u d=%u cons_slot=%u prod_slot=%u last_cons=0x%lx\n",
+ __func__, iter, tx_cons, tx_prod, diff, slot, prod_slot,
+ creq->tx_sw_ring[slot].last_cons);
+ }
+ }
+}
+
+/**
+ * bng_re_mpc_roce_alloc_hwq - Allocate MPC TX and CQ hardware queues
+ * @rdev: RoCE device
+ *
+ * Ensures CQ page layout fits whole LONG completions, then allocates TX and CQ
+ * HWQs via the qpxxx. Sets ring masks and CQ copy-bit metadata.
+ *
+ * Return: 0 on success, %-ENOMEM on allocation failure (partial cleanup on CQ fail).
+ */
+static int bng_re_mpc_roce_alloc_hwq(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ struct bng_re_res *res = &rdev->bng_res;
+ struct bng_re_hwq_attr hwq_attr = {};
+ struct bng_re_sg_info sginfo = {};
+
+ dev_dbg(rdev_to_dev(rdev), "%s: %p\n", __func__, rdev);
+
+ /*
+ * Each CQ page must hold a whole number of 32-byte LONG completions
+ * (sizeof(mpc_qp_modify_cmpl) == 32 == 2 x BNG_RE_MPC_CQ_STRIDE).
+ * This guarantees that a LONG completion never straddles a page
+ * boundary, making non-contiguous scatter-gather handling unnecessary.
+ */
+ BUILD_BUG_ON(PAGE_SIZE % sizeof(struct mpc_qp_modify_cmpl));
+
+ sginfo.pgsize = PAGE_SIZE;
+ sginfo.pgshft = PAGE_SHIFT;
+ hwq_attr.res = res;
+ hwq_attr.sginfo = &sginfo;
+ hwq_attr.type = BNG_HWQ_TYPE_QUEUE;
+
+ /* TX ring */
+ hwq_attr.depth = BNG_RE_MPC_TX_RING_SIZE;
+ hwq_attr.stride = BNG_RE_MPC_TX_STRIDE;
+ if (bng_re_alloc_init_hwq(&creq->tx_hwq, &hwq_attr)) {
+ dev_err(rdev_to_dev(rdev), "%s: TX HWQ alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ creq->tx_ring_mask = creq->tx_hwq.max_elements - 1;
+
+ memset(&sginfo, 0, sizeof(sginfo));
+ sginfo.pgsize = PAGE_SIZE;
+ sginfo.pgshft = PAGE_SHIFT;
+ /* CQ ring */
+ hwq_attr.depth = BNG_RE_MPC_CQ_RING_SIZE;
+ hwq_attr.stride = BNG_RE_MPC_CQ_STRIDE;
+ hwq_attr.type = BNG_HWQ_TYPE_QUEUE;
+ if (bng_re_alloc_init_hwq(&creq->cq_hwq, &hwq_attr)) {
+ dev_err(rdev_to_dev(rdev), "%s: CQ HWQ alloc failed\n", __func__);
+ bng_re_free_hwq(res, &creq->tx_hwq);
+ return -ENOMEM;
+ }
+
+ creq->cq_ring_mask = creq->cq_hwq.max_elements - 1;
+ creq->cq_cp_bit = creq->cq_hwq.max_elements;
+
+ return 0;
+}
+
+/**
+ * bng_re_mpc_roce_alloc_init - Initialize RoCE MPC channel software state and HWQs
+ * @rdev: RoCE device
+ *
+ * Allocates poll/diag/tune structures, stats, TX/CQ HWQs, software TX ring,
+ * command context kmem cache, firmware rings, and MPC doorbells. Initializes
+ * spinlocks and DB info for L2 doorbell posting.
+ *
+ * Return: 0 on success, negative errno on failure (all partial allocations rolled back).
+ */
+int bng_re_mpc_roce_alloc_init(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ struct bnge_auxr_dev *auxr_dev = rdev->aux_dev;
+ struct bng_re_res *res = &rdev->bng_res;
+ int rc;
+
+ res = &rdev->bng_res;
+
+ dev_dbg(rdev_to_dev(rdev), "%s: %p\n", __func__, rdev);
+
+ creq->tx_ring_id = INVALID_HW_RING_ID_32BIT;
+ creq->cq_ring_id = INVALID_HW_RING_ID;
+
+ spin_lock_init(&creq->tx_lock);
+ spin_lock_init(&creq->mpc_ctx_lock);
+ spin_lock_init(&creq->bm_lock);
+
+ creq->bm_poll_info = kzalloc_obj(*creq->bm_poll_info, GFP_KERNEL);
+ if (!creq->bm_poll_info)
+ return -ENOMEM;
+
+ creq->bm_poll_info->bm_current_cons_indx = -1;
+
+ creq->bm_diag_counters = kzalloc_obj(*creq->bm_diag_counters, GFP_KERNEL);
+ if (!creq->bm_diag_counters) {
+ rc = -ENOMEM;
+ goto err_free_poll;
+ }
+
+ creq->mpc_tune_params = kzalloc_obj(*creq->mpc_tune_params, GFP_KERNEL);
+ if (!creq->mpc_tune_params) {
+ rc = -ENOMEM;
+ goto err_free_diag;
+ }
+ creq->mpc_tune_params->short_timeout = BNG_RE_MPC_ROCE_TMO_MSECS_SHORT;
+ creq->mpc_tune_params->long_timeout = BNG_RE_MPC_ROCE_TMO_MSECS_LONG;
+ creq->mpc_tune_params->max_retries = BNG_RE_MPC_ROCE_MAX_RETRIES;
+ creq->mpc_tune_params->retry_sleep = BNG_RE_MPC_ROCE_RETRY_SLEEP;
+
+ rc = bng_re_mpc_roce_alloc_stats(rdev);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "%s: stats_info alloc failed\n", __func__);
+ rc = -ENOMEM;
+ goto err_free_tune;
+ }
+
+ rc = bng_re_mpc_roce_alloc_hwq(rdev);
+ if (rc)
+ goto err_free_stats;
+
+ creq->tx_sw_ring = kcalloc(creq->tx_ring_mask + 1,
+ sizeof(*creq->tx_sw_ring), GFP_KERNEL);
+ if (!creq->tx_sw_ring) {
+ rc = -ENOMEM;
+ goto err_free_hwq;
+ }
+
+ creq->mpc_cache = kmem_cache_create("bng_re_roce_mpc",
+ sizeof(struct bng_re_roce_cmd_ctx), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!creq->mpc_cache) {
+ dev_err(rdev_to_dev(rdev), "%s: mpc_cache create failed\n", __func__);
+ rc = -ENOMEM;
+ goto err_free_sw_ring;
+ }
+
+ atomic_set(&creq->pending, 0);
+ atomic_set(&creq->max_pending, 0);
+ creq->avail_buffer = ~0U;
+ creq->min_avail_buffer = ~0U;
+ creq->bm_mpc_stall = false;
+
+ rc = bng_re_mpc_rings_alloc(rdev);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "%s: mpc_rings_alloc failed rc=%d\n",
+ __func__, rc);
+ goto err_free_cache;
+ }
+
+ if (!auxr_dev || !auxr_dev->bar1) {
+ dev_err(rdev_to_dev(rdev), "%s: mpc_rings_alloc failed: bad auxr_dev info: auxr_dev=%p, bar1=%p\n",
+ __func__, auxr_dev, auxr_dev ? auxr_dev->bar1 : 0);
+ goto err_free_cache;
+ }
+
+ creq->tx_db_info.db = auxr_dev->bar1 + auxr_dev->l2_db_offset;
+
+ creq->tx_db_info.hwq = &creq->tx_hwq;
+ creq->tx_db_info.xid = creq->tx_ring_id;
+ creq->tx_db_info.seed = creq->tx_ring_id;
+ creq->tx_db_info.flags = 0;
+ spin_lock_init(&creq->tx_db_info.lock);
+ creq->tx_db_info.max_slot = 1;
+ creq->tx_db_info.res = res;
+ creq->tx_db_info.is_l2 = true;
+ creq->cq_db_info.toggle = 0;
+
+ creq->cq_db_info.db = auxr_dev->bar1 + auxr_dev->l2_db_offset;
+ creq->cq_db_info.hwq = &creq->cq_hwq;
+ creq->cq_db_info.xid = creq->cq_ring_id;
+ creq->cq_db_info.seed = creq->cq_ring_id;
+ creq->cq_db_info.flags = 0;
+ spin_lock_init(&creq->cq_db_info.lock);
+ creq->cq_db_info.max_slot = 1;
+ creq->cq_db_info.res = res;
+ /*
+ * The MPC CQ is an L2 completion ring (RING_ALLOC_REQ_RING_TYPE_L2_CMPL),
+ * so its arm doorbell must carry DBC_DBC64_PATH_L2. Without is_l2 the
+ * CQ_ARMALL below goes out on the RoCE path, the CQ is never armed, and
+ * firmware never raises an MPC NQ notification -> IRQ never fires and
+ * MPC commands time out (-110). Matches thor3.
+ */
+ creq->cq_db_info.is_l2 = true;
+
+ creq->tx_prod = 0;
+ creq->tx_cons = 0;
+ creq->tx_napi_idx = 0;
+
+ rc = bng_re_alloc_mpc_doorbells(rdev);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev),
+ "[%s:%lx:%d] Error allocating mpc dbs (err : %d)\n",
+ __func__, (unsigned long)current, __LINE__, rc);
+ goto err_free_cache;
+ }
+
+ set_bit(BNG_RE_FLAG_MPC_DB_PAGE_EN, &rdev->flags);
+ bng_re_ring_db(&creq->cq_db_info, DBC_DBC_TYPE_CQ_ARMALL);
+
+ return 0;
+
+err_free_cache:
+ bng_re_mpc_rings_free(rdev);
+ kmem_cache_destroy(creq->mpc_cache);
+ creq->mpc_cache = NULL;
+ kfree(creq->bm_stats_info);
+ creq->bm_stats_info = NULL;
+err_free_sw_ring:
+ kfree(creq->tx_sw_ring);
+ creq->tx_sw_ring = NULL;
+err_free_hwq:
+ bng_re_free_hwq(res, &creq->cq_hwq);
+ bng_re_free_hwq(res, &creq->tx_hwq);
+err_free_stats:
+ bng_re_mpc_roce_free_stats(rdev);
+err_free_tune:
+ kfree(creq->mpc_tune_params);
+ creq->mpc_tune_params = NULL;
+err_free_diag:
+ kfree(creq->bm_diag_counters);
+ creq->bm_diag_counters = NULL;
+err_free_poll:
+ kfree(creq->bm_poll_info);
+ creq->bm_poll_info = NULL;
+
+ return rc;
+}
+
+/**
+ * bng_re_mpc_roce_deinit_free - Tear down RoCE MPC channel and free resources
+ * @rdev: RoCE device
+ *
+ * Frees firmware rings, HWQs, TX software ring, kmem cache, stats, and auxiliary
+ * allocations created by bng_re_mpc_roce_alloc_init().
+ */
+void bng_re_mpc_roce_deinit_free(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ struct bng_re_res *res = &rdev->bng_res;
+
+ dev_dbg(rdev_to_dev(rdev), "%s: %p\n", __func__, rdev);
+
+ bng_re_mpc_rings_free(rdev);
+ bng_re_free_hwq(res, &creq->cq_hwq);
+ bng_re_free_hwq(res, &creq->tx_hwq);
+ kfree(creq->tx_sw_ring);
+ creq->tx_sw_ring = NULL;
+
+ kmem_cache_destroy(creq->mpc_cache);
+ creq->mpc_cache = NULL;
+
+ bng_re_mpc_roce_free_stats(rdev);
+ kfree(creq->mpc_tune_params);
+ kfree(creq->bm_diag_counters);
+ kfree(creq->bm_poll_info);
+ creq->mpc_tune_params = NULL;
+ creq->bm_diag_counters = NULL;
+ creq->bm_poll_info = NULL;
+}
+
+/**
+ * bng_re_mpc_roce_send - Post one MPC command to the TX ring and ring the doorbell
+ * @rdev: RoCE device
+ * @cmd: MPC command payload
+ * @cmd_len: Length of @cmd in bytes
+ * @handle: Opaque handle stored in the TX software ring (e.g. command context pointer)
+ * @mctx: Optional out; set to the address of the stored handle slot for error cleanup
+ *
+ * Splits the command across header and data BDs, updates HWQ producer with
+ * bng_hwq_incr_prod(), writes a memory barrier, and rings the producer DB.
+ *
+ * Return: 0 on success, %-EBUSY if the ring lacks space, %-EIO on queue indexing failure.
+ */
+static int bng_re_mpc_roce_send(struct bng_re_dev *rdev, void *cmd, uint cmd_len,
+ unsigned long handle, void **mctx)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ struct bng_re_hwq *tx_hwq = &creq->tx_hwq;
+ u32 bds, total_bds, free_slots, prod;
+ unsigned long flags;
+ struct tx_bd *txbd;
+
+ dev_dbg(rdev_to_dev(rdev), "[%s:%lx:%d] enter\n",
+ __func__, (unsigned long)current, __LINE__);
+
+ bds = DIV_ROUND_UP(cmd_len, BNG_RE_MPC_TX_STRIDE);
+ total_bds = bds + 1; /* header BD + data BDs */
+
+ spin_lock_irqsave(&creq->tx_lock, flags);
+ free_slots = bng_re_mpc_tx_avail(creq);
+ if (free_slots < total_bds) {
+ spin_unlock_irqrestore(&creq->tx_lock, flags);
+ dev_dbg(rdev_to_dev(rdev), "%s: no space, need %u have %u\n",
+ __func__, total_bds, free_slots);
+ return -EBUSY;
+ }
+
+ prod = creq->tx_prod;
+
+ txbd = bng_re_get_qe(tx_hwq, prod & creq->tx_ring_mask, NULL);
+
+ dev_dbg(rdev_to_dev(rdev), "[1]%s, page:%d, index:%d, prod:%d,len:%d,bds:%d,total_bds:%d\n",
+ __func__,
+ 0,
+ 0,
+ prod,
+ cmd_len,
+ bds,
+ total_bds
+ );
+ if (!txbd) {
+ spin_unlock_irqrestore(&creq->tx_lock, flags);
+ dev_err(rdev_to_dev(rdev), "%s: get_qe failed at prod %u\n",
+ __func__, prod);
+ return -EIO;
+ }
+
+ creq->tx_sw_ring[prod & creq->tx_ring_mask].handle = handle;
+ creq->tx_sw_ring[prod & creq->tx_ring_mask].inline_bds = total_bds;
+ creq->tx_sw_ring[prod & creq->tx_ring_mask].last_cons = 0; /* pending */
+ if (mctx)
+ *(unsigned long **)mctx = &creq->tx_sw_ring[prod & creq->tx_ring_mask].handle;
+
+ txbd->tx_bd_len_flags_type =
+ cpu_to_le32((cmd_len << TX_BD_LEN_SHIFT) | TX_BD_TYPE_MPC_TX_BD |
+ (total_bds << TX_BD_FLAGS_BD_CNT_SHIFT));
+ txbd->tx_bd_opaque = BNG_RE_MPC_SET_TX_OPAQUE(creq->tx_napi_idx, prod,
+ total_bds, creq->tx_ring_mask);
+ prod = ++creq->tx_prod;
+
+ /* Copy payload into following BD slots */
+ while (bds > 0) {
+ u32 chunk = min_t(u32, cmd_len, (uint)BNG_RE_MPC_TX_STRIDE);
+
+ txbd = bng_re_get_qe(tx_hwq, prod & creq->tx_ring_mask, NULL);
+ if (!txbd) {
+ spin_unlock_irqrestore(&creq->tx_lock, flags);
+ dev_err(rdev_to_dev(rdev), "%s: get_qe data failed\n", __func__);
+ return -EIO;
+ }
+ memcpy(txbd, cmd, chunk);
+ cmd += chunk;
+ cmd_len -= chunk;
+ prod = ++creq->tx_prod;
+ bds--;
+ }
+
+ /*
+ * Advance hwq->prod by the total number of BDs posted.
+ * bng_re_hwq_incr_prod keeps hwq->prod within [0, hwq->depth) and
+ * toggles the epoch bit in tx_db_info.flags on wrap-around. Both are
+ * required for bng_re_ring_prod_db to write a correct doorbell.
+ */
+ bng_hwq_incr_prod(&creq->tx_db_info, tx_hwq, total_bds);
+
+ dev_dbg(rdev_to_dev(rdev),
+ "[2]%s, page:%d, index:%d, prod:%d,len:%d,bds:%d,total_bds:%d\n",
+ __func__,
+ 0,
+ 0,
+ prod,
+ cmd_len,
+ bds,
+ total_bds
+ );
+ /* make sure memory is updated */
+ wmb();
+
+ bng_ring_prod_db(&creq->tx_db_info, DBC_DBC_TYPE_SQ);
+
+ dev_info(rdev_to_dev(rdev),
+ "mpc db: db=%p xid=0x%x prod=%u max_slot=%u flags=0x%x is_l2=%d total_bds=%d\n",
+ creq->tx_db_info.db, creq->tx_db_info.xid, tx_hwq->prod,
+ creq->tx_db_info.max_slot, creq->tx_db_info.flags,
+ creq->tx_db_info.is_l2, total_bds);
+
+ spin_unlock_irqrestore(&creq->tx_lock, flags);
+
+ return 0;
+}
+
+/**
+ * bng_re_mpc_dispatch_event_cmpl - Handle unsolicited MPC LONG completions (no TX ctx)
+ * @rdev: RoCE device
+ * @entry: Completion entry (32-byte LONG payload)
+ *
+ * For EVENT/RESP_CMPL, forwards to bng_re_mpc_handle_event_cmpl(). Other
+ * unsolicited types are logged. Mirrors the null-handle path in bnge MPC completion
+ * processing.
+ */
+static void bng_re_mpc_dispatch_event_cmpl(struct bng_re_dev *rdev,
+ struct bng_re_cmpl_entry *entry)
+{
+ struct mpc_cmpl_hdr *hdr = (struct mpc_cmpl_hdr *)entry->cmpl;
+
+ if (hdr->req_type == MPC_CMD_HDR_REQ_TYPE_EVENT &&
+ hdr->req_subtype == MPC_CMD_HDR_REQ_SUB_TYPE_EVENT_RESP_CMPL)
+ bng_re_mpc_handle_event_cmpl(rdev, entry->cmpl, entry->len);
+ else
+ dev_warn(rdev_to_dev(rdev),
+ "%s: null handle, unsolicited mpc cmpl type:%u subtype:%u\n",
+ __func__, hdr->req_type, hdr->req_subtype);
+}
+
+/**
+ * bng_re_mpc_roce_service_creq - Poll the MPC completion ring
+ * @rdev: RoCE device
+ * @budget: Maximum LONG completions to process this call
+ *
+ * Validates CQ epoch and LONG completion layout (two contiguous slots), handles
+ * PF/VF forward completions, unsolicited events, and solicited completions
+ * (opaque TX index, out-of-order-safe tx_cons advance, then bng_re_roce_mpc_cmp()).
+ *
+ * Return: 1 if the budget was fully consumed (more completions may remain), else 0.
+ */
+int bng_re_mpc_roce_service_creq(struct bng_re_dev *rdev, int budget)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ struct bng_re_hwq *cq_hwq = &creq->cq_hwq;
+ struct bng_re_cmpl_entry cmpl_entry;
+ struct mpc_cmpl_hdr *hdr = NULL;
+ static u64 last_poll_jiffies;
+ unsigned long handle = 0;
+ struct mpc_cmp *mpcmp2;
+ bool mpc_unsol = false;
+ struct mpc_cmp *mpcmp;
+ struct tx_cmp *txcmp;
+ unsigned long flags;
+ u32 iterations = 0;
+ u8 inline_bds = 0;
+ u32 cons_next;
+ u32 cmp_type;
+ u32 opaque;
+ u32 cons;
+ u16 idx;
+
+ last_poll_jiffies = get_jiffies_64();
+
+ spin_lock_irqsave(&creq->bm_lock, flags);
+
+ cons = cq_hwq->cons;
+
+ while (iterations < budget) {
+ /*
+ * Valid polarity alternates each CQ lap (HW toggles when posting
+ * at index 0 again). bng_re_hwq_incr_cons() XORs the same
+ * phase into cq_db_info.flags when cons wraps — not (cons & cp_bit)
+ * with cp_bit == max_elements and wrapped cons (always 0..max-1).
+ */
+ u32 cq_epoch = creq->cq_db_info.flags &
+ BNG_QP_FLAG_EPOCH_CONS_MASK;
+
+ dma_rmb();
+ txcmp = bng_re_get_qe(cq_hwq, cons, NULL);
+ if (!txcmp) {
+ dev_err(rdev_to_dev(rdev), "%s: get_qe failed cons %u\n",
+ __func__, cons);
+ break;
+ }
+ if (!(!!(txcmp->tx_cmp_errors_v & cpu_to_le32(TX_CMP_V)) ==
+ !cq_epoch))
+ break;
+
+ dma_rmb();
+ cmp_type = le32_to_cpu(((struct mpc_cmp *)txcmp)->mpc_cmp_client_subtype_type) &
+ MPC_CMP_TYPE;
+ if (cmp_type != MPC_CMP_TYPE_MID_PATH_LONG) {
+ dev_err(rdev_to_dev(rdev), "%s: unexpected cmp type 0x%x\n",
+ __func__, cmp_type);
+ bng_re_hwq_incr_cons(cq_hwq->max_elements, &cons,
+ 1, &creq->cq_db_info.flags);
+ iterations++;
+ continue;
+ }
+
+ mpcmp = (struct mpc_cmp *)txcmp;
+ /*
+ * Fetch and validate the second 16-byte slot of the
+ * LONG completion before reading any data from it.
+ * HW may write the two slots non-atomically; returning
+ * without advancing cons lets the caller retry.
+ */
+ cons_next = cons + 1;
+ if (cons_next >= cq_hwq->max_elements)
+ cons_next = 0;
+
+ mpcmp2 = bng_re_get_qe(cq_hwq, cons_next, NULL);
+ if (!mpcmp2) {
+ dev_err(rdev_to_dev(rdev),
+ "%s: get_qe slot2 failed at cons %u\n",
+ __func__, cons_next);
+ break;
+ }
+
+ /* Same HW write as slot 1 — same V polarity / consumer epoch */
+ if (!(!!(mpcmp2->mpc_cmp_v & cpu_to_le32(MPC_CMP_V)) ==
+ !cq_epoch)) {
+ dev_dbg(rdev_to_dev(rdev),
+ "%s: slot2 not valid yet at cons %u\n",
+ __func__, cons_next);
+ break;
+ }
+
+ /* Ensure slot2 valid bit is read before its payload */
+ dma_rmb();
+
+ /*
+ * Safety check: the two slots must be contiguous in
+ * virtual memory (same page). PAGE_SIZE is guaranteed
+ * to be a multiple of sizeof(mpc_qp_modify_cmpl) by the
+ * BUILD_BUG_ON in bng_re_mpc_roce_alloc_hwq, so this
+ * path should never be reached in practice.
+ */
+ if (unlikely((char *)mpcmp2 !=
+ (char *)mpcmp + BNG_RE_MPC_CQ_STRIDE)) {
+ dev_err(rdev_to_dev(rdev),
+ "%s: LONG cmpl sl non-contig [%lx,%lx] cons %u/%u - skip\n",
+ __func__,
+ (unsigned long)mpcmp,
+ (unsigned long)mpcmp2,
+ cons,
+ cons_next);
+ bng_re_hwq_incr_cons(cq_hwq->max_elements,
+ &cons, 2,
+ &creq->cq_db_info.flags);
+ cq_hwq->cons = cons;
+ iterations++;
+ continue;
+ }
+
+ /*
+ * IRQs are already disabled by the outer
+ * bm_lock irqsave; use a plain spin_lock here
+ * so we do not overwrite 'flags' and corrupt
+ * the IRQ state restored when bm_lock is
+ * released.
+ */
+ hdr = (struct mpc_cmpl_hdr *)mpcmp;
+ mpc_unsol =
+ (hdr->req_type == MPC_CMD_HDR_REQ_TYPE_EVENT &&
+ hdr->req_subtype ==
+ MPC_CMD_HDR_REQ_SUB_TYPE_EVENT_RESP_CMPL);
+
+ cmpl_entry.cmpl = mpcmp;
+ cmpl_entry.len = sizeof(*mpcmp) * 2;
+
+ if (mpc_unsol) {
+ /*
+ * Unsolicited EVENT from firmware: no TX ring
+ * slot was consumed, so tx_cons must not change.
+ */
+ bng_re_mpc_dispatch_event_cmpl(rdev, &cmpl_entry);
+ } else {
+ opaque = mpcmp->mpc_cmp_opaque;
+ idx = BNG_RE_MPC_TX_OPAQUE_IDX(opaque);
+ /*
+ * Solicited completion. Mark the slot done and
+ * advance tx_cons using the out-of-order-safe
+ * mechanism (mirrors bnge_adv_mpc_cons_re):
+ *
+ * - Only advance from the current consumer position.
+ * If this completion arrived out of order (i.e.
+ * the slot is not the oldest outstanding entry),
+ * mark last_cons and leave tx_cons unchanged; the
+ * consumer will be swept forward when the older
+ * entry's completion arrives.
+ * - inline_bds is NOT cleared here so the advance
+ * function can use it to step past the slot.
+ */
+ spin_lock(&creq->mpc_ctx_lock);
+ if (idx <= creq->tx_ring_mask) {
+ inline_bds = creq->tx_sw_ring[idx].inline_bds;
+ handle = creq->tx_sw_ring[idx].handle;
+ if (handle) {
+ /*
+ * Acquire a reference on the ctx while
+ * still under the lock so that
+ * bng_re_roce_mpc_xmit cannot free it
+ * between here and the cmp call below.
+ * Zero the slot so a concurrent or
+ * future xmit error-path clear sees an
+ * already-consumed handle.
+ */
+ refcount_inc(&((struct bng_re_roce_cmd_ctx *)
+ handle)->refcnt);
+ creq->tx_sw_ring[idx].handle = 0;
+ }
+ creq->tx_sw_ring[idx].last_cons =
+ BNG_RE_MPC_INV_HDL;
+ }
+ if ((creq->tx_cons & creq->tx_ring_mask) == idx)
+ bng_re_mpc_adv_tx_cons(creq);
+ spin_unlock(&creq->mpc_ctx_lock);
+
+ if (!handle) {
+ dev_warn(rdev_to_dev(rdev),
+ "%s: null ctx at idx %u, opaque: %x %s 0x%x.%x\n",
+ __func__, idx, opaque,
+ "skip processing MPC completion, req_type",
+ hdr->req_type,
+ hdr->req_subtype);
+ } else {
+ struct bng_re_roce_cmd_ctx *cmp_ctx =
+ (struct bng_re_roce_cmd_ctx *)handle;
+
+ bng_re_roce_mpc_cmp(rdev, handle, &cmpl_entry);
+ if (refcount_dec_and_test(&cmp_ctx->refcnt))
+ kmem_cache_free(creq->mpc_cache, cmp_ctx);
+ }
+ }
+
+ /* LONG completion always occupies two ring slots */
+ bng_re_hwq_incr_cons(cq_hwq->max_elements, &cons,
+ 2, &creq->cq_db_info.flags);
+ cq_hwq->cons = cons;
+ iterations++;
+ }
+
+ creq->cq_hwq.cons = cons;
+ spin_unlock_irqrestore(&creq->bm_lock, flags);
+
+ return (iterations >= budget) ? 1 : 0;
+}
+
+int bng_re_mpc_roce_service_cne(struct bng_re_dev *rdev, void *nq_cmp, int budget)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ u64 dbr_type = DBC_DBC_TYPE_CQ_ARMALL;
+ struct nq_cn *nqcne = nq_cmp;
+ int has_more_work = 0;
+ u16 type;
+
+ if (nqcne) {
+ type = le16_to_cpu(nqcne->type);
+ if ((type & NQ_CN_TYPE_MASK) == NQ_CN_TYPE_CQ_NOTIFICATION)
+ creq->cq_db_info.toggle = ((type) & NQ_CN_TOGGLE_MASK) >> NQ_CN_TOGGLE_SFT;
+ }
+
+ has_more_work = bng_re_mpc_roce_service_creq(rdev, budget);
+
+ if (has_more_work)
+ dbr_type = DBC_DBC_TYPE_CQ;
+
+ bng_re_ring_db(&creq->cq_db_info, dbr_type);
+
+ return has_more_work;
+}
+
+/**
+ * bng_re_roce_mpc_xmit - Send an MPC command and wait for or poll its completion
+ * @rdev: RoCE device
+ * @cmd: MPC command buffer
+ * @cmd_len: Length of @cmd
+ * @cmpl: Caller buffer for completion payload (copied from internal context)
+ * @cmpl_len: Max bytes to copy into @cmpl
+ * @poll_cmp: If true, busy-poll completion; else use completion / timed wait
+ *
+ * Allocates a per-command context, posts via bng_re_mpc_roce_send() with EBUSY
+ * retries, services the CQ as needed, updates diagnostics and optional stall
+ * detection, and records performance stats.
+ *
+ * Return: 0 on success, negative errno on validation, stall, timeout, or I/O errors.
+ */
+int bng_re_roce_mpc_xmit(struct bng_re_dev *rdev, void *cmd, uint cmd_len,
+ void *cmpl, uint cmpl_len, bool poll_cmp)
+{
+ struct bng_re_mpc_roce_creq_info *creq;
+ unsigned long tx_start_time = jiffies;
+ struct bng_re_mpc_diag_counters *dc;
+ struct bng_re_roce_cmd_ctx *ctx;
+ unsigned long poll_end_time = 0;
+ unsigned long tx_end_time = 0;
+ unsigned int max_pending = 0;
+ unsigned long max_timeout;
+ unsigned long retries = 0;
+ bool sent_started = false;
+ unsigned long tmo_left;
+ unsigned long deadline;
+ unsigned long timeout;
+ unsigned long flags;
+ u16 retry_sleep_ms;
+ void *pctx = NULL;
+ int cnt;
+ int rc;
+
+ dev_dbg(rdev_to_dev(rdev), "%s: enter, poll_cmp:%d\n", __func__, poll_cmp);
+
+ creq = &rdev->mpc_roce_creq;
+ dc = creq->bm_diag_counters;
+
+ if (!cmd || !cmpl) {
+ dev_err(rdev_to_dev(rdev), "%s: NULL cmd or cmpl\n", __func__);
+ return -EINVAL;
+ }
+ if (!creq->mpc_tune_params || !dc || !creq->mpc_cache) {
+ dev_err(rdev_to_dev(rdev), "%s: MPC channel not initialized\n", __func__);
+ return -EINVAL;
+ }
+
+ max_timeout = creq->mpc_tune_params->short_timeout;
+ retry_sleep_ms = creq->mpc_tune_params->retry_sleep;
+
+ atomic_inc(&creq->pending);
+ atomic_inc(&dc->bm_mpc_verb_try);
+
+ if (creq->bm_mpc_stall || test_bit(BNG_RE_FLAG_ERR_DEVICE_DETACHED, &rdev->flags) ||
+ creq->bm_mpc_uninstall_pending) {
+ rc = -EHOSTUNREACH;
+ atomic_inc(&dc->bm_mpc_stalled_err);
+ dev_warn(rdev_to_dev(rdev), "%s QUIT(1) as %s:%d or %s:%d or %s:%d",
+ __func__,
+ "mpc stall detected",
+ creq->bm_mpc_stall,
+ "HW FATAL Cond(stall)",
+ test_bit(BNG_RE_FLAG_ERR_DEVICE_DETACHED, &rdev->flags),
+ "driver uninit in progress",
+ creq->bm_mpc_uninstall_pending);
+ goto exit;
+ }
+ /* make sure memory is updated */
+ smp_mb__after_atomic();
+
+ ctx = kmem_cache_zalloc(creq->mpc_cache, GFP_ATOMIC);
+
+ if (!ctx) {
+ rc = -ENOMEM;
+ dev_err(rdev_to_dev(rdev), "%s: ctx alloc failed\n", __func__);
+ goto exit;
+ }
+ refcount_set(&ctx->refcnt, 1);
+ ctx->poll_cmp = poll_cmp;
+ if (!poll_cmp) {
+ init_completion(&ctx->cmp);
+ might_sleep();
+ retries = creq->mpc_tune_params->max_retries;
+ max_timeout = creq->mpc_tune_params->long_timeout;
+ }
+
+ do {
+ atomic_inc(&dc->bm_mpc_sent_try);
+ rc = bng_re_mpc_roce_send(rdev, cmd, cmd_len, (unsigned long)ctx, &pctx);
+ if (rc == -EBUSY) {
+ atomic_inc(&dc->bm_mpc_sent_started_ebusy);
+ } else if (!rc) {
+ atomic_inc(&dc->bm_mpc_sent_started_ok);
+ sent_started = true;
+ } else {
+ atomic_inc(&dc->bm_mpc_sent_started_misc_err);
+ }
+
+ if (rc != -EBUSY) {
+ dev_dbg(rdev_to_dev(rdev), "[%s,%lx]: msg %x.%x BREAK: sleep retry remaining: %ld. outst msgs:%d,rc:%d",
+ __func__,
+ (unsigned long)current,
+ ((u8 *)cmd)[0],
+ ((u8 *)cmd)[1],
+ retries,
+ atomic_read(&creq->pending),
+ rc);
+ break;
+ }
+ /* -EBUSY: one try per (initial + max_retries); last EBUSY exits here */
+ if (!retries) {
+ dev_dbg(rdev_to_dev(rdev), "[%s,%lx]: msg %x.%x BREAK: no retries left. outst msgs:%d,rc:%d",
+ __func__,
+ (unsigned long)current,
+ ((u8 *)cmd)[0],
+ ((u8 *)cmd)[1],
+ atomic_read(&creq->pending),
+ rc);
+ break;
+ }
+ dev_warn(rdev_to_dev(rdev), "[%s,%lx]: msg %x.%x sleep retry remaining: %ld. outstanding msgs:%d",
+ __func__,
+ (unsigned long)current,
+ ((u8 *)cmd)[0],
+ ((u8 *)cmd)[1],
+ retries,
+ atomic_read(&creq->pending));
+ msleep(retry_sleep_ms);
+ retries--;
+ if (creq->bm_mpc_stall || test_bit(BNG_RE_FLAG_ERR_DEVICE_DETACHED, &rdev->flags) ||
+ creq->bm_mpc_uninstall_pending
+ ) {
+ rc = -EHOSTUNREACH;
+ atomic_inc(&dc->bm_mpc_stalled_err);
+ dev_warn(rdev_to_dev(rdev), "%s QUIT(2) as %s:%d or %s:%d or %s:%d",
+ __func__,
+ "mpc stall detected",
+ creq->bm_mpc_stall,
+ "HW FATAL Cond(stall)",
+ test_bit(BNG_RE_FLAG_ERR_DEVICE_DETACHED, &rdev->flags),
+ "driver uninit in progress",
+ creq->bm_mpc_uninstall_pending);
+ break;
+ }
+ } while (true);
+
+ tx_end_time = jiffies;
+
+ if (rc)
+ goto xmit_done;
+
+ if (poll_cmp) {
+ deadline = jiffies + msecs_to_jiffies(max_timeout);
+ do {
+ bool avail = READ_ONCE(ctx->cmpl_available);
+ /* make sure completion is available */
+ smp_rmb();
+ /* check for completion availability */
+ if (avail)
+ break;
+
+ udelay(9);
+ bng_re_mpc_roce_service_creq(rdev, 32);
+ if (time_after_eq(jiffies, deadline)) {
+ rc = -ETIMEDOUT;
+ goto xmit_done;
+ }
+ if (creq->bm_mpc_stall ||
+ test_bit(BNG_RE_FLAG_ERR_DEVICE_DETACHED, &rdev->flags) ||
+ creq->bm_mpc_uninstall_pending
+ ) {
+ rc = -EHOSTUNREACH;
+ atomic_inc(&dc->bm_mpc_stalled_err);
+ dev_warn(rdev_to_dev(rdev), "%s QUIT(3) as %s:%d or %s:%d or %s:%d",
+ __func__,
+ "mpc stall detected",
+ creq->bm_mpc_stall,
+ "HW FATAL Cond(stall)",
+ test_bit(BNG_RE_FLAG_ERR_DEVICE_DETACHED, &rdev->flags),
+ "driver uninit in progress",
+ creq->bm_mpc_uninstall_pending);
+ goto xmit_done;
+ }
+ } while (true);
+ } else {
+ if (max_timeout < 100) {
+ dev_err(rdev_to_dev(rdev),
+ "%s: long_timeout (%lu ms) < 100; poll_cmp=false wait loop is underspecified\n",
+ __func__, max_timeout);
+ }
+ timeout = max_timeout ?: 1;
+ /*
+ * timeout == max_timeout here, so this is always 1 -- except
+ * when max_timeout is 0, which would otherwise divide by zero.
+ */
+ cnt = max_t(int, 1, (int)(max_timeout / timeout));
+
+ do {
+ tmo_left = wait_for_completion_timeout(&ctx->cmp,
+ msecs_to_jiffies(timeout));
+ if (!tmo_left)
+ break;
+ dev_dbg(rdev_to_dev(rdev), "%s:%lx Wait For Completion: iteration:%d\n",
+ __func__, (unsigned long)current, cnt);
+ cnt--;
+ if (creq->bm_mpc_stall ||
+ test_bit(BNG_RE_FLAG_ERR_DEVICE_DETACHED, &rdev->flags) ||
+ creq->bm_mpc_uninstall_pending) {
+ rc = -EHOSTUNREACH;
+ atomic_inc(&dc->bm_mpc_stalled_err);
+ dev_warn(rdev_to_dev(rdev), "%s QUIT(4) as %s:%d or %s:%d or %s:%d",
+ __func__,
+ "mpc stall detected",
+ creq->bm_mpc_stall,
+ "HW FATAL Cond(stall)",
+ test_bit(BNG_RE_FLAG_ERR_DEVICE_DETACHED, &rdev->flags),
+ "driver uninit in progress",
+ creq->bm_mpc_uninstall_pending);
+ goto xmit_done;
+ }
+ } while (cnt);
+ if (!tmo_left) {
+ rc = -ETIMEDOUT;
+ goto xmit_done;
+ }
+ }
+
+ memcpy(cmpl, &ctx->roce_cmp, min(cmpl_len, (uint)sizeof(ctx->roce_cmp)));
+
+xmit_done:
+ if (rc) {
+ int consecutive_errs;
+
+ if (sent_started)
+ atomic_inc(&dc->bm_mpc_sent_response_err);
+
+ dev_warn(rdev_to_dev(rdev), "RoCE MP cmd %08x failed with error:%d\n"
+ "verb try:%d,sent try:%d,sent s.ok:%d,ebusy:%d,resp ok:%d,reps err:%d,%s:%d,%s:%d",
+ *((u32 *)cmd), rc,
+ atomic_read(&dc->bm_mpc_verb_try),
+ atomic_read(&dc->bm_mpc_sent_try),
+ atomic_read(&dc->bm_mpc_sent_started_ok),
+ atomic_read(&dc->bm_mpc_sent_started_ebusy),
+ atomic_read(&dc->bm_mpc_sent_response_ok),
+ atomic_read(&dc->bm_mpc_sent_response_err),
+ "pending",
+ atomic_read(&creq->pending),
+ "stalled",
+ atomic_read(&dc->bm_mpc_stalled_err));
+ spin_lock_irqsave(&creq->mpc_ctx_lock, flags);
+ if (pctx)
+ *(unsigned long **)pctx = 0;
+ spin_unlock_irqrestore(&creq->mpc_ctx_lock, flags);
+ dump_tx_cmpl_ring_info(rdev);
+
+ if (rc == -EBUSY)
+ atomic_inc(&dc->bm_mpc_fatal_ebusy);
+ else if (rc == -ETIMEDOUT)
+ atomic_inc(&dc->bm_mpc_sent_timeout);
+ else
+ atomic_inc(&dc->bm_mpc_misc_err);
+
+ consecutive_errs = atomic_inc_return(&dc->bm_mpc_consecutive_sent_err);
+
+#ifdef MPC_STALL_DETECTION
+ if (consecutive_errs > BNGE_MPC_ROCE_ERR_THRESHOLD) {
+ dev_warn(rdev_to_dev(rdev),
+ "%s:%d consecutive err;MPC ch marked stalled\n",
+ __func__,
+ consecutive_errs);
+ creq->bm_mpc_stall = true;
+ }
+#endif
+ } else {
+ atomic_inc(&dc->bm_mpc_sent_response_ok);
+ atomic_set(&dc->bm_mpc_consecutive_sent_err, 0);
+ }
+ if (refcount_dec_and_test(&ctx->refcnt))
+ kmem_cache_free(creq->mpc_cache, ctx);
+ exit:
+ max_pending = max(atomic_dec_return(&creq->pending),
+ atomic_read(&creq->max_pending));
+ atomic_set(&creq->max_pending, max_pending);
+
+ poll_end_time = jiffies;
+
+ spin_lock_irqsave(&creq->mpc_ctx_lock, flags);
+ /* Track available buffer space */
+ creq->avail_buffer = bng_re_mpc_tx_avail(creq);
+ creq->min_avail_buffer = min(creq->avail_buffer,
+ creq->min_avail_buffer);
+ bng_re_mpc_roce_add_perf_stats(rdev,
+ ((u8 *)cmd)[0],
+ ((u8 *)cmd)[1],
+ (u32)jiffies_to_msecs(poll_end_time - tx_end_time));
+ spin_unlock_irqrestore(&creq->mpc_ctx_lock, flags);
+ bnge_re_print_mpc_msg(rdev, cmd, cmd_len, tx_start_time,
+ tx_end_time, poll_end_time, rc, retries);
+ return rc;
+}
+
+void bnge_re_print_mpc_msg(struct bng_re_dev *rdev,
+ u8 *cmd,
+ uint cmd_len,
+ unsigned long tx_start_time,
+ unsigned long tx_end_time,
+ unsigned long poll_end_time,
+ int rc,
+ int retries_left)
+{
+ unsigned long poll_duration = jiffies_to_msecs(poll_end_time - tx_end_time);
+ unsigned long tx_duration = jiffies_to_msecs(tx_end_time - tx_start_time);
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ u8 req_subtype = *(cmd + 1);
+ u8 req_type = *cmd;
+
+ dev_dbg(rdev_to_dev(rdev),
+ "MPC_TX mtyp:%x:%x,len:%d,tx st time:%ld,enq dur:%ld,comp dur:%ld,rc %d,outst:%d,max_pend:%d,avail_buf:%u,min_avail_buf:%u,retries_left:%d\n",
+ req_type,
+ req_subtype,
+ cmd_len,
+ tx_start_time,
+ tx_duration,
+ poll_duration,
+ rc,
+ atomic_read(&creq->pending),
+ atomic_read(&creq->max_pending),
+ creq->avail_buffer,
+ creq->min_avail_buffer,
+ retries_left);
+}
+
+/**
+ * bng_re_mpc_rings_alloc - Allocate MPC TX and CQ rings with firmware
+ * @rdev: RoCE device
+ *
+ * Must be called after the MPC TX and CQ HWQs are allocated via
+ * bng_re_alloc_init_hwq(). Issues HWRM_RING_ALLOC for L2 completion then TX
+ * (MPC primate channel), wiring CQ ring id and page tables.
+ *
+ * Return: 0 on success, negative errno from firmware or %-ENODEV if detached.
+ */
+int bng_re_mpc_rings_alloc(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ struct bng_re_chip_ctx *cctx = rdev->chip_ctx;
+ struct hwrm_ring_alloc_input req = {0};
+ struct hwrm_ring_alloc_output resp;
+ struct bnge_fw_msg fw_msg = {};
+ int cq_pages, tx_pages;
+ int rc;
+
+ dev_dbg(rdev_to_dev(rdev), "%s: %p", __func__, rdev);
+
+ if (test_bit(BNG_RE_FLAG_ERR_DEVICE_DETACHED, &rdev->flags)) {
+ dev_err(rdev_to_dev(rdev), "%s: device detached\n", __func__);
+ return -ENODEV;
+ }
+
+ cq_pages = creq->cq_hwq.pbl[creq->cq_hwq.level].pg_count;
+ tx_pages = creq->tx_hwq.pbl[creq->tx_hwq.level].pg_count;
+
+ /* 1. Allocate CQ (completion) ring first */
+ bng_re_init_hwrm_hdr((struct input *)&req, HWRM_RING_ALLOC);
+ req.ring_type = RING_ALLOC_REQ_RING_TYPE_L2_CMPL;
+ req.length = cpu_to_le32(creq->cq_ring_mask + 1);
+ req.logical_id = cpu_to_le16(creq->bm_lr_index);
+ req.page_tbl_addr = cpu_to_le64(creq->cq_hwq.pbl[BNG_PBL_LVL_0].pg_map_arr[0]);
+ if (cq_pages > 1) {
+ req.page_size = BNGE_PAGE_SHIFT;
+ req.page_tbl_depth = 1;
+ } else {
+ req.page_size = 4;
+ req.page_tbl_depth = 0;
+ }
+ req.fbo = 0;
+
+ req.enables = cpu_to_le32(RING_ALLOC_REQ_ENABLES_NQ_RING_ID_VALID);
+
+ req.nq_ring_id = cpu_to_le16(creq->bm_nq_ring_id);
+ if (cctx->modes.st_tag_supported) {
+ req.steering_tag = cpu_to_le16(BNG_RE_STEERING_TO_HOST);
+ req.enables |= cpu_to_le32(RING_ALLOC_REQ_ENABLES_STEERING_TAG_VALID);
+ }
+ req.flags |=
+ cpu_to_le16(RING_ALLOC_REQ_FLAGS_DISABLE_CQ_OVERFLOW_DETECTION);
+
+ bng_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp,
+ sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
+ rc = bnge_send_msg(rdev->aux_dev, &fw_msg);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "%s: CQ ring alloc failed rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ creq->cq_ring_id = (u16)le32_to_cpu(resp.ring_id);
+ dev_dbg(rdev_to_dev(rdev), "%s: CQ ring_id=0x%x\n", __func__, creq->cq_ring_id);
+
+ /* 2. Allocate TX ring (associated with CQ) */
+ memset(&req, 0, sizeof(req));
+ bng_re_init_hwrm_hdr((void *)&req, HWRM_RING_ALLOC);
+ req.ring_type = RING_ALLOC_REQ_RING_TYPE_TX;
+ req.length = cpu_to_le32(creq->tx_ring_mask + 1);
+ req.logical_id = cpu_to_le16(creq->bm_lr_index);
+ req.cmpl_ring_id = cpu_to_le16(creq->cq_ring_id);
+ req.stat_ctx_id = cpu_to_le32(creq->bm_stat_index);
+ req.page_tbl_addr = cpu_to_le64(creq->tx_hwq.pbl[BNG_PBL_LVL_0].pg_map_arr[0]);
+ if (tx_pages > 1) {
+ req.page_size = BNGE_PAGE_SHIFT;
+ req.page_tbl_depth = 1;
+ } else {
+ req.page_size = 4;
+ req.page_tbl_depth = 0;
+ }
+ req.fbo = 0;
+ req.enables = cpu_to_le32(RING_ALLOC_REQ_ENABLES_MPC_CHNLS_TYPE);
+ req.mpc_chnls_type = RING_ALLOC_REQ_MPC_CHNLS_TYPE_PRIMATE;
+ if (cctx->modes.st_tag_supported) {
+ req.steering_tag = cpu_to_le16(BNG_RE_STEERING_TO_HOST);
+ req.enables |= cpu_to_le32(RING_ALLOC_REQ_ENABLES_STEERING_TAG_VALID);
+ }
+
+ bng_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp,
+ sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
+ rc = bnge_send_msg(rdev->aux_dev, &fw_msg);
+
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "%s: TX ring alloc failed rc=%d\n",
+ __func__, rc);
+ bng_re_net_ring_free(rdev, creq->cq_ring_id,
+ RING_FREE_REQ_RING_TYPE_L2_CMPL);
+ creq->cq_ring_id = INVALID_HW_RING_ID;
+ return rc;
+ }
+ creq->tx_ring_id = le32_to_cpu(resp.ring_id);
+ dev_dbg(rdev_to_dev(rdev), "%s: TX ring_id=0x%x\n", __func__, creq->tx_ring_id);
+
+ dev_info(rdev_to_dev(rdev),
+ "mpc rings: cq_ring_id=0x%x tx_ring_id=0x%x lr_index=%u nq_ring_id=%u stat_index=%u\n",
+ creq->cq_ring_id, creq->tx_ring_id, creq->bm_lr_index,
+ creq->bm_nq_ring_id, creq->bm_stat_index);
+
+ return 0;
+}
+
+/**
+ * bng_re_mpc_rings_free - Free MPC TX and CQ rings with firmware
+ * @rdev: RoCE device
+ *
+ * Frees the TX ring first when allocated, then the L2 completion ring.
+ */
+void bng_re_mpc_rings_free(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+
+ dev_dbg(rdev_to_dev(rdev), "%s: %p", __func__, creq);
+
+ if (creq->tx_ring_id != INVALID_HW_RING_ID_32BIT) {
+ bng_re_net_ring_free(rdev, creq->tx_ring_id,
+ RING_FREE_REQ_RING_TYPE_TX);
+ creq->tx_ring_id = INVALID_HW_RING_ID;
+ }
+ if (creq->cq_ring_id != INVALID_HW_RING_ID_32BIT) {
+ bng_re_net_ring_free(rdev, creq->cq_ring_id,
+ RING_FREE_REQ_RING_TYPE_L2_CMPL);
+ creq->cq_ring_id = INVALID_HW_RING_ID;
+ }
+}
+
+/******************************************************
+ * Stats section *
+ *****************************************************/
+
+/**
+ * bng_re_mpc_roce_alloc_stats - Allocate MPC performance statistics structures
+ * @rdev: RoCE device
+ *
+ * Allocates latency histograms and per-verb latency ring buffers under
+ * mpc_roce_creq.bm_stats_info. Enables stats only if every allocation succeeds.
+ *
+ * Return: 0 on success, %-ENOMEM on failure (partial allocations freed).
+ */
+int bng_re_mpc_roce_alloc_stats(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_roce_stats_info *p;
+
+ p = vzalloc(sizeof(*p));
+ if (!p)
+ return -ENOMEM;
+
+ p->bms_stats_enabled = false;
+ p->bms_qp_modify_stats = vzalloc(sizeof(u64) * BNG_RE_MPC_MAX_STAT_INDEX);
+ p->bms_ah_modify_stats = vzalloc(sizeof(u64) * BNG_RE_MPC_MAX_STAT_INDEX);
+ p->bms_other_stats = vzalloc(sizeof(u64) * BNG_RE_MPC_MAX_STAT_INDEX);
+ p->bms_lat_slab_sec = vzalloc(sizeof(u32) *
+ BNG_RE_MPC_MAX_LATENCY_SEC_SLAB_INDEX);
+ p->bms_lat_slab_msec = vzalloc(sizeof(u32) *
+ BNG_RE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX);
+ rdev->mpc_roce_creq.bm_stats_info = p;
+
+ if (p->bms_qp_modify_stats &&
+ p->bms_ah_modify_stats &&
+ p->bms_other_stats &&
+ p->bms_lat_slab_sec &&
+ p->bms_lat_slab_msec) {
+ p->bms_stats_enabled = true;
+ return 0;
+ }
+ bng_re_mpc_roce_free_stats(rdev);
+ return -ENOMEM;
+}
+
+/**
+ * bng_re_mpc_roce_free_stats - Free MPC performance statistics
+ * @rdev: RoCE device
+ */
+void bng_re_mpc_roce_free_stats(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_roce_stats_info *p = rdev->mpc_roce_creq.bm_stats_info;
+
+ if (!p)
+ return;
+
+ if (p->bms_qp_modify_stats)
+ vfree(p->bms_qp_modify_stats);
+ if (p->bms_ah_modify_stats)
+ vfree(p->bms_ah_modify_stats);
+ if (p->bms_other_stats)
+ vfree(p->bms_other_stats);
+ if (p->bms_lat_slab_sec)
+ vfree(p->bms_lat_slab_sec);
+ if (p->bms_lat_slab_msec)
+ vfree(p->bms_lat_slab_msec);
+
+ p->bms_stats_enabled = false;
+ rdev->mpc_roce_creq.bm_stats_info = NULL;
+}
+
+/**
+ * bng_re_mpc_roce_add_perf_stats - Record one MPC round-trip latency sample
+ * @rdev: RoCE device
+ * @req_type: MPC command header req_type
+ * @req_subtype: MPC command header req_subtype
+ * @latency_msec: Elapsed time from post to completion, in milliseconds
+ *
+ * Updates second- and millisecond-resolution histograms and stores
+ * the latency in the rotating sample array for QP modify, AH modify, or other.
+ */
+void bng_re_mpc_roce_add_perf_stats(struct bng_re_dev *rdev,
+ u8 req_type,
+ u8 req_subtype,
+ u32 latency_msec)
+{
+ struct bng_re_mpc_roce_stats_info *p =
+ rdev->mpc_roce_creq.bm_stats_info;
+ u64 *dest_stats_ptr = NULL;
+ u32 dest_stats_id;
+
+ if (!p || !p->bms_stats_enabled)
+ return;
+
+ if (latency_msec / 1000 < BNG_RE_MPC_MAX_LATENCY_SEC_SLAB_INDEX)
+ p->bms_lat_slab_sec[latency_msec / 1000]++;
+
+ if (latency_msec < BNG_RE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX)
+ p->bms_lat_slab_msec[latency_msec]++;
+
+ if (req_type == MPC_CMD_HDR_REQ_TYPE_RCA &&
+ req_subtype == MPC_CMD_HDR_REQ_SUB_TYPE_RCA_QP_MODIFY) {
+ dest_stats_id = p->bms_qp_modify_stats_id++;
+ dest_stats_id = dest_stats_id % BNG_RE_MPC_MAX_STAT_INDEX;
+ dest_stats_ptr = &p->bms_qp_modify_stats[dest_stats_id];
+ /* stopped here */
+ } else if (((req_type == MPC_CMD_HDR_REQ_TYPE_RCA) ||
+ (req_type == MPC_CMD_HDR_REQ_TYPE_PFVF)) &&
+ (req_subtype == MPC_CMD_HDR_REQ_SUB_TYPE_RCA_AH_MODIFY)) {
+ dest_stats_id = p->bms_ah_modify_stats_id++;
+ dest_stats_id = dest_stats_id % BNG_RE_MPC_MAX_STAT_INDEX;
+ dest_stats_ptr = &p->bms_ah_modify_stats[dest_stats_id];
+ } else {
+ dest_stats_id = p->bms_other_stats_id++;
+ dest_stats_id = dest_stats_id % BNG_RE_MPC_MAX_STAT_INDEX;
+ dest_stats_ptr = &p->bms_other_stats[dest_stats_id];
+ }
+ if (dest_stats_ptr)
+ *dest_stats_ptr = latency_msec;
+}
+
+/**
+ * bng_re_mpc_roce_diag_counters_debugfs_show - Print MPC diagnostic counters to seq_file
+ * @rdev: RoCE device
+ * @s: debugfs seq_file output
+ *
+ * Return: 0 on success, %-ENOMEM if diagnostic counters are not allocated.
+ */
+int bng_re_mpc_roce_diag_counters_debugfs_show(struct bng_re_dev *rdev,
+ struct seq_file *s)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ struct bng_re_mpc_diag_counters *mpc_diag_counters =
+ creq->bm_diag_counters;
+
+ if (!mpc_diag_counters)
+ return -ENOMEM;
+
+ seq_printf(s,
+ "%s:\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n",
+ "RoCE MP cmd stats",
+ "verb try",
+ atomic_read(&mpc_diag_counters->bm_mpc_verb_try),
+ "sent try",
+ atomic_read(&mpc_diag_counters->bm_mpc_sent_try),
+ "sent s.ok",
+ atomic_read(&mpc_diag_counters->bm_mpc_sent_started_ok),
+ "ebusy",
+ atomic_read(&mpc_diag_counters->bm_mpc_sent_started_ebusy),
+ "sent s.misc err",
+ atomic_read(&mpc_diag_counters->bm_mpc_sent_started_misc_err),
+ "resp ok",
+ atomic_read(&mpc_diag_counters->bm_mpc_sent_response_ok));
+ seq_printf(s,
+ "%s: %d\n%s: %d\n%s: %d\n%s: %u\n%s: %u\n",
+ "resp err",
+ atomic_read(&mpc_diag_counters->bm_mpc_sent_response_err),
+ "pending",
+ atomic_read(&creq->pending),
+ "max_pending",
+ atomic_read(&creq->max_pending),
+ "avail_buf",
+ creq->avail_buffer,
+ "min_avail_buf",
+ creq->min_avail_buffer);
+ seq_puts(s, ".... MP cmd stats:\n");
+ seq_printf(s, "fatal ebusy: %d\nsent timeout: %d\nmisc err: %d\n",
+ atomic_read(&mpc_diag_counters->bm_mpc_fatal_ebusy),
+ atomic_read(&mpc_diag_counters->bm_mpc_sent_timeout),
+ atomic_read(&mpc_diag_counters->bm_mpc_misc_err));
+ seq_printf(s, "consecutive errs: %d\nchannel stalled: %d\nstalled errs:%d\n",
+ atomic_read(&mpc_diag_counters->bm_mpc_consecutive_sent_err),
+ creq->bm_mpc_stall,
+ atomic_read(&mpc_diag_counters->bm_mpc_stalled_err));
+
+ return 0;
+}
+
+/**
+ * bng_re_mpc_roce_perf_debugfs_show - Print MPC latency / perf stats to seq_file
+ * @rdev: RoCE device
+ * @s: debugfs seq_file output
+ * @level: Verbosity (0 = slabs in seconds only; 1 adds msec slabs; 3 adds per-slot
+ * totals; 10+ dumps each index line for QP/AH/other arrays)
+ *
+ * Return: 0 on success, %-ENOMEM if stats are disabled or missing.
+ */
+int bng_re_mpc_roce_perf_debugfs_show(struct bng_re_dev *rdev,
+ struct seq_file *s,
+ u8 level)
+{
+ struct bng_re_mpc_roce_stats_info *p = rdev->mpc_roce_creq.bm_stats_info;
+ u64 qp_modify_total_msec = 0;
+ u64 ah_modify_total_msec = 0;
+ u64 other_total_msec = 0;
+ int qp_modify_total = 0;
+ int ah_modify_total = 0;
+ bool add_entry = false;
+ int other_total = 0;
+ int i = 0;
+
+ seq_puts(s, "==\n");
+
+ seq_printf(s, "bng_re mpc roce perf stats enabled:%s\n",
+ (p && p->bms_stats_enabled) ? "Enabled" : "Disabled");
+
+ if (!(p && p->bms_stats_enabled))
+ return -ENOMEM;
+
+ for (i = 0; i < BNG_RE_MPC_MAX_LATENCY_SEC_SLAB_INDEX; i++) {
+ if (p->bms_lat_slab_sec[i])
+ seq_printf(s, "\tlatency_slab [%d - %d] sec = %d\n",
+ i, i + 1, p->bms_lat_slab_sec[i]);
+ }
+ if (level < 1)
+ goto exit;
+
+ seq_puts(s, "==\n");
+ for (i = 0; i < BNG_RE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX; i++) {
+ if (p->bms_lat_slab_msec[i])
+ seq_printf(s, "\tlatency_slab [%d - %d] msec = %d\n",
+ i, i + 1, p->bms_lat_slab_msec[i]);
+ }
+
+ if (level < 3)
+ goto exit;
+
+ seq_puts(s, "==\n");
+ for (i = 0; i < BNG_RE_MPC_MAX_STAT_INDEX; i++) {
+ if (p->bms_qp_modify_stats[i] > 0) {
+ qp_modify_total++;
+ qp_modify_total_msec += p->bms_qp_modify_stats[i];
+ add_entry = true;
+ }
+ if (p->bms_ah_modify_stats[i] > 0) {
+ ah_modify_total++;
+ ah_modify_total_msec += p->bms_ah_modify_stats[i];
+ add_entry = true;
+ }
+ if (p->bms_other_stats[i] > 0) {
+ other_total++;
+ other_total_msec += p->bms_other_stats[i];
+ add_entry = true;
+ }
+
+ if (level >= 10) {
+ if (add_entry)
+ seq_printf(s, "<qp_modify> %lld <ah_modify> %lld <other> %lld\n",
+ p->bms_qp_modify_stats[i],
+ p->bms_ah_modify_stats[i],
+ p->bms_other_stats[i]);
+ }
+
+ add_entry = false;
+ }
+
+ seq_puts(s, "==\n");
+ seq_printf(s, "Total qp_modify %d in msec %lld\n",
+ qp_modify_total, qp_modify_total_msec);
+ seq_printf(s, "Total ah_modify %d in msec %lld\n",
+ ah_modify_total, ah_modify_total_msec);
+ seq_printf(s, "Total other %d in msec %lld\n",
+ other_total, other_total_msec);
+exit:
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+/**
+ * bng_re_mpc_roce_perf_debugfs_clear - Zero MPC perf histograms and sample arrays
+ * @rdev: RoCE device
+ */
+void bng_re_mpc_roce_perf_debugfs_clear(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_roce_stats_info *p = rdev->mpc_roce_creq.bm_stats_info;
+ int i;
+
+ if (!(p && p->bms_stats_enabled))
+ return;
+
+ for (i = 0; i < BNG_RE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX; i++)
+ p->bms_lat_slab_msec[i] = 0;
+
+ for (i = 0; i < BNG_RE_MPC_MAX_LATENCY_SEC_SLAB_INDEX; i++)
+ p->bms_lat_slab_sec[i] = 0;
+
+ for (i = 0; i < BNG_RE_MPC_MAX_STAT_INDEX; i++) {
+ p->bms_qp_modify_stats[i] = 0;
+ p->bms_ah_modify_stats[i] = 0;
+ p->bms_other_stats[i] = 0;
+ }
+}
+
+/**
+ * bng_re_mpc_roce_diag_counters_clear - Reset MPC xmit/diagnostic atomics to zero
+ * @rdev: RoCE device
+ */
+void bng_re_mpc_roce_diag_counters_clear(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_diag_counters *mpc_diag_counters =
+ rdev->mpc_roce_creq.bm_diag_counters;
+
+ if (!mpc_diag_counters)
+ return;
+
+ atomic_set(&mpc_diag_counters->bm_mpc_verb_try, 0);
+ atomic_set(&mpc_diag_counters->bm_mpc_sent_try, 0);
+ atomic_set(&mpc_diag_counters->bm_mpc_sent_started_ok, 0);
+ atomic_set(&mpc_diag_counters->bm_mpc_sent_started_ebusy, 0);
+ atomic_set(&mpc_diag_counters->bm_mpc_sent_started_misc_err, 0);
+ atomic_set(&mpc_diag_counters->bm_mpc_sent_response_ok, 0);
+ atomic_set(&mpc_diag_counters->bm_mpc_sent_response_err, 0);
+ atomic_set(&mpc_diag_counters->bm_mpc_fatal_ebusy, 0);
+ atomic_set(&mpc_diag_counters->bm_mpc_sent_timeout, 0);
+ atomic_set(&mpc_diag_counters->bm_mpc_misc_err, 0);
+ atomic_set(&mpc_diag_counters->bm_mpc_consecutive_sent_err, 0);
+ atomic_set(&mpc_diag_counters->bm_mpc_stalled_err, 0);
+}
+
+/**
+ * bng_re_snapdump_mpc_stats - Collect MPC performance and latency stats for coredump.
+ * @rdev: RoCE device
+ * @buf: Dump buffer
+ * @buf_len: Buffer length
+ * Returns: Number of bytes written
+ */
+u32 bng_re_snapdump_mpc_stats(struct bng_re_dev *rdev, void *buf, u32 buf_len)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ struct bng_re_mpc_roce_stats_info *p = creq->bm_stats_info;
+ struct bng_re_mpc_diag_counters *mpc_diag =
+ creq->bm_diag_counters;
+ u32 len = 0;
+ int i;
+
+ dev_dbg(rdev_to_dev(rdev), "%s: buf_len:%d\n", __func__, buf_len);
+
+ /* Collect MPC perf stats if enabled */
+ if (p && p->bms_stats_enabled) {
+ len += snprintf(buf + len, buf_len - len, "==\n");
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len,
+ "bnge mpc roce perf stats enabled:%s\n",
+ p->bms_stats_enabled ? "Enabled" : "Disabled");
+ if (len >= buf_len)
+ return len;
+
+ for (i = 0; i < BNG_RE_MPC_MAX_LATENCY_SEC_SLAB_INDEX; i++) {
+ if (p->bms_lat_slab_sec[i]) {
+ len += snprintf(buf + len, buf_len - len,
+ "\tlatency_slab [%d - %d] sec = %d\n",
+ i, i + 1, p->bms_lat_slab_sec[i]);
+ if (len >= buf_len)
+ return len;
+ }
+ }
+
+ len += snprintf(buf + len, buf_len - len, "==\n");
+ if (len >= buf_len)
+ return len;
+
+ for (i = 0; i < BNG_RE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX; i++) {
+ if (p->bms_lat_slab_msec[i]) {
+ len += snprintf(buf + len, buf_len - len,
+ "\tlatency_slab [%d - %d] msec = %d\n",
+ i, i + 1, p->bms_lat_slab_msec[i]);
+ if (len >= buf_len)
+ return len;
+ }
+ }
+ }
+
+ /* Collect MPC diagnostic counters */
+ if (mpc_diag && creq->mpc_cache) {
+ len += snprintf(buf + len, buf_len - len, "RoCE MP cmd stats:\n");
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "verb try: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_verb_try));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "sent try: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_sent_try));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "sent s.ok: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_sent_started_ok));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "pending: %d\n",
+ atomic_read(&creq->pending));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "max_pending: %d\n",
+ atomic_read(&creq->max_pending));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "avail_buf: %u\n",
+ creq->avail_buffer);
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "min_avail_buf: %u\n",
+ creq->min_avail_buffer);
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "ebusy: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_sent_started_ebusy));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "sent s.misc err: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_sent_started_misc_err));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "resp ok: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_sent_response_ok));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "resp err: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_sent_response_err));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, ".... MP cmd stats:\n");
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "fatal ebusy: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_fatal_ebusy));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "sent timeout: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_sent_timeout));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "misc err: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_misc_err));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "consecutive sent err: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_consecutive_sent_err));
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "channel stalled: %d\n",
+ creq->bm_mpc_stall);
+ if (len >= buf_len)
+ return len;
+
+ len += snprintf(buf + len, buf_len - len, "stalled err: %d\n",
+ atomic_read(&mpc_diag->bm_mpc_stalled_err));
+ if (len >= buf_len)
+ return len;
+ }
+
+ return len;
+}
+
+/**
+ * dump_tx_cmpl_ring_info - Log TX/CQ producer-consumer state and recent CQ slots
+ * @rdev: RoCE device
+ *
+ * Takes tx_lock for ring indices and bm_lock while dumping poll timing and a
+ * short hex decode window around the last stalled consumer index.
+ */
+void dump_tx_cmpl_ring_info(struct bng_re_dev *rdev)
+{
+ struct bng_re_mpc_roce_creq_info *creq = &rdev->mpc_roce_creq;
+ struct bng_re_mpc_poll_info *poll_info = creq->bm_poll_info;
+ u64 curr_jiffies = get_jiffies_64();
+ u32 start_raw_index = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&creq->tx_lock, flags);
+ dev_warn(rdev_to_dev(rdev),
+ "[%s.%lx] curr ms:%d,tx ring prod,cons,cq_cons:%d,%d,%d,tx_ring_size:%d,cmpl_ring_size:%d\n",
+ __func__,
+ (unsigned long)current,
+ jiffies_to_msecs(curr_jiffies),
+ READ_ONCE(creq->tx_prod),
+ READ_ONCE(creq->tx_cons),
+ READ_ONCE(creq->cq_hwq.cons),
+ creq->tx_hwq.max_elements,
+ creq->cq_hwq.max_elements);
+ spin_unlock_irqrestore(&creq->tx_lock, flags);
+
+ spin_lock_irqsave(&creq->bm_lock, flags);
+ dev_warn(rdev_to_dev(rdev), "[%s.%lx] %s:%d,%d;%s:%d,%s:%d,%s:%d,%d",
+ __func__,
+ (unsigned long)current,
+ "last_poll_st/end",
+ jiffies_to_msecs(poll_info->bm_last_poll_work_poll_jiffies_start),
+ jiffies_to_msecs(poll_info->bm_last_poll_work_poll_jiffies_end),
+ "max_ms_b_polls",
+ jiffies_to_msecs(poll_info->bm_max_poll_work_poll_jiffies),
+ "curr cons",
+ poll_info->bm_current_cons_indx,
+ "ms on curr cons[st/diff]",
+ jiffies_to_msecs(poll_info->bm_start_time_on_current_cons_indx),
+ jiffies_to_msecs(poll_info->bm_curr_time_on_current_cons_indx));
+ if (poll_info->bm_current_cons_indx >= 5)
+ start_raw_index = poll_info->bm_current_cons_indx - 5;
+
+ spin_unlock_irqrestore(&creq->bm_lock, flags);
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_re_mpc_roce.h b/drivers/infiniband/hw/bng_re/bng_re_mpc_roce.h
new file mode 100644
index 000000000000..152471462048
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_re_mpc_roce.h
@@ -0,0 +1,218 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2025 Broadcom. */
+
+#ifndef __BNG_RE_MPC_ROCE_H__
+#define __BNG_RE_MPC_ROCE_H__
+
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/refcount.h>
+
+#include "bng_roce_hsi.h"
+#include "bng_re.h"
+#include "bng_res.h"
+
+/* RoCE MPC diagnostics and knobs (mirrors bnge for compatibility) */
+#define BNG_RE_MPC_MAX_LATENCY_SEC_SLAB_INDEX 201
+#define BNG_RE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX 3000
+#define BNG_RE_MPC_MAX_STAT_INDEX 0x3FFFF
+
+#define BNG_RE_MPC_ROCE_TMO_MSECS_SHORT 8000 /* for nonblocking context */
+#ifdef BNGE_FPGA
+#define BNG_RE_MPC_ROCE_TMO_MSECS_LONG 100000 /* for normal context */
+#define BNG_RE_MPC_ROCE_RETRY_SLEEP 2000 /* ms */
+#else
+#define BNG_RE_MPC_ROCE_TMO_MSECS_LONG 40000 /* for normal context */
+#define BNG_RE_MPC_ROCE_RETRY_SLEEP 200 /* ms */
+#endif
+#define BNG_RE_MPC_ROCE_MAX_RETRIES 32
+#define BNG_RE_MPC_ROCE_ERR_THRESHOLD 10
+
+/* bng_re_mpc_poll_info, bng_re_mpc_diag_counters, bng_re_mpc_tune_params,
+ * bng_re_mpc_roce_stats_info are defined in bng_re.h — include that instead.
+ */
+#define MPC_ROCE_COMPL_MAX_SIZE 32
+
+/*
+ * Single work item for all PF→VF forwarded MPC completions.
+ * bng_re_mpc_process_vf_completion() dispatches on req_subtype internally,
+ * so no per-operation struct is needed.
+ */
+struct bng_re_vf_pfvf_cmpl_work {
+ struct work_struct work;
+ struct bng_re_dev *rdev;
+ u8 roce_cmp[MPC_ROCE_COMPL_MAX_SIZE];
+};
+
+struct bng_re_cmpl_entry {
+ void *cmpl;
+ u32 len;
+};
+
+struct mpc_cmp {
+ __le32 mpc_cmp_client_subtype_type;
+ #define MPC_CMP_TYPE (0x3f << 0)
+ #define MPC_CMP_TYPE_MID_PATH_SHORT 0x1e
+ #define MPC_CMP_TYPE_MID_PATH_LONG 0x1f
+ #define MPC_CMP_SUBTYPE 0xf00
+ #define MPC_CMP_SUBTYPE_SFT 8
+ #define MPC_CMP_SUBTYPE_SOLICITED (0x0 << 8)
+ #define MPC_CMP_SUBTYPE_ERR (0x1 << 8)
+ #define MPC_CMP_SUBTYPE_RESYNC (0x2 << 8)
+ #define MPC_CMP_CLIENT (0xf << 12)
+ #define MPC_CMP_CLIENT_SFT 12
+ #define MPC_CMP_CLIENT_TCE (0x0 << 12)
+ #define MPC_CMP_CLIENT_RCE (0x1 << 12)
+ #define MPC_CMP_CLIENT_TE_CFA (0x2 << 12)
+ #define MPC_CMP_CLIENT_RE_CFA (0x3 << 12)
+ #define MPC_CMP_CLIENT_PRIMATE (0x4 << 12)
+ u32 mpc_cmp_opaque;
+ __le32 mpc_cmp_v;
+ #define MPC_CMP_V BIT(0)
+ __le32 mpc_cmp_filler;
+};
+
+struct bng_re_roce_cmd_ctx {
+ struct completion cmp;
+ u8 roce_cmp[MPC_ROCE_COMPL_MAX_SIZE];
+ bool poll_cmp;
+ bool cmpl_available;
+ /*
+ * Shared ownership between bng_re_roce_mpc_xmit (always holds 1 ref)
+ * and bng_re_mpc_roce_service_creq (acquires +1 while processing a
+ * completion for this ctx). Whoever decrements to zero frees the
+ * object. This prevents the use-after-free where a late completion
+ * writes into a ctx that xmit has already freed on a timeout/error
+ * path.
+ */
+ refcount_t refcnt;
+};
+
+/* Same message types as bnge */
+#define BNG_RE_MPC_TX_RING_SIZE 512
+#define BNG_RE_MPC_CQ_RING_SIZE (BNG_RE_MPC_TX_RING_SIZE * 4)
+#define BNG_RE_MPC_TX_STRIDE 16 /* sizeof(struct tx_bd) */
+#define BNG_RE_MPC_CQ_STRIDE 16 /* sizeof(struct tx_cmp) */
+#define BNG_RE_MPC_INV_HDL (-1UL)
+
+#define MAX_ADV_ITERATIONS (1000)
+
+#ifndef TX_OPAQUE_IDX_MASK
+#define TX_OPAQUE_IDX_MASK 0x0000ffff
+#endif
+#ifndef TX_OPAQUE_BDS_SHIFT
+#define TX_OPAQUE_BDS_SHIFT 16
+#endif
+#ifndef TX_OPAQUE_RING_SHIFT
+#define TX_OPAQUE_RING_SHIFT 24
+#endif
+#ifndef TX_BD_TYPE_MPC_TX_BD
+#define TX_BD_TYPE_MPC_TX_BD (0x08 << 0)
+#endif
+#ifndef BNGE_DFLT_HWRM_CMD_TIMEOUT
+#define BNGE_DFLT_HWRM_CMD_TIMEOUT 500
+#endif
+
+#define BNG_RE_MPC_SET_TX_OPAQUE(tx_napi_idx, idx, bds, tx_ring_mask) \
+ (((tx_napi_idx) << TX_OPAQUE_RING_SHIFT) | \
+ ((bds) << TX_OPAQUE_BDS_SHIFT) | ((idx) & (tx_ring_mask)))
+
+#define BNG_RE_MPC_TX_OPAQUE_IDX(opq) ((opq) & TX_OPAQUE_IDX_MASK)
+
+static inline void bng_re_roce_mpc_set_stall(struct bng_re_dev *rdev)
+{
+ dev_warn(rdev_to_dev(rdev), "%s: MPC ch marked stalled\n", __func__);
+ rdev->mpc_roce_creq.bm_mpc_stall = true;
+}
+
+struct bng_re_mpc_roce_creq_info *bng_re_mpc_roce_tx_ring(struct bng_re_dev *rdev);
+
+void bng_re_ulp_vf_qp_req(struct bng_re_dev *rdev, void *cmpl, u32 cmpl_len);
+void bng_re_mpc_process_vf_completion(struct bng_re_dev *rdev, void *cmpl, u32 cmpl_len);
+
+void bng_re_mpc_roce_set_stall(struct bng_re_dev *rdev);
+
+int bng_re_mpc_roce_alloc_init(struct bng_re_dev *rdev);
+
+void bng_re_mpc_roce_deinit_free(struct bng_re_dev *rdev);
+
+int bng_re_mpc_roce_service_creq(struct bng_re_dev *rdev, int budget);
+
+int bng_re_mpc_roce_service_cne(struct bng_re_dev *rdev, void *nq_cmp, int budget);
+
+int bng_re_mpc_roce_xmit(struct bng_re_dev *rdev, void *cmd, uint cmd_len,
+ void *cmpl, uint cmpl_len, bool poll_cmp);
+int bng_re_mpc_roce_diag_counters_debugfs_show(struct bng_re_dev *rdev,
+ struct seq_file *s);
+int bng_re_mpc_roce_perf_debugfs_show(struct bng_re_dev *rdev,
+ struct seq_file *s, u8 level);
+void bng_re_mpc_roce_perf_debugfs_clear(struct bng_re_dev *rdev);
+
+void bng_re_mpc_roce_diag_counters_clear(struct bng_re_dev *rdev);
+
+int bng_re_mpc_rings_alloc(struct bng_re_dev *rdev);
+void bng_re_mpc_rings_free(struct bng_re_dev *rdev);
+
+int bng_re_mpc_roce_alloc_stats(struct bng_re_dev *rdev);
+void bng_re_mpc_roce_free_stats(struct bng_re_dev *rdev);
+
+void bng_re_mpc_roce_add_perf_stats(struct bng_re_dev *rdev,
+ u8 req_type,
+ u8 req_subtype,
+ u32 latency_msec);
+
+int bng_re_mpc_roce_diag_counters_debugfs_show(struct bng_re_dev *rdev,
+ struct seq_file *s);
+
+void bnge_re_print_mpc_msg(struct bng_re_dev *rdev,
+ u8 *cmd,
+ uint cmd_len,
+ unsigned long tx_start_time,
+ unsigned long tx_end_time,
+ unsigned long poll_end_time,
+ int rc,
+ int retries_left);
+
+void dump_tx_cmpl_ring_info(struct bng_re_dev *rdev);
+
+void bng_re_mpc_handle_event_cmpl(struct bng_re_dev *rdev,
+ void *cmpl, u32 cmpl_len);
+
+void bng_re_mpc_rings_free(struct bng_re_dev *rdev);
+u32 bng_re_snapdump_mpc_stats(struct bng_re_dev *rdev, void *buf, u32 buf_len);
+void bng_re_mpc_process_vf_completion(struct bng_re_dev *rdev, void *cmpl, u32 cmpl_len);
+
+void bng_re_ulp_vf_qp_req(struct bng_re_dev *rdev, void *cmpl, u32 cmpl_len);
+
+void bng_re_roce_mpc_cmp(struct bng_re_dev *rdev, unsigned long handle,
+ struct bng_re_cmpl_entry *cmpl);
+int bng_re_roce_mpc_xmit(struct bng_re_dev *rdev, void *cmd, uint cmd_len,
+ void *cmpl, uint cmpl_len, bool poll_cmp);
+int bng_re_roce_mpc_diag_counters_debugfs_show(struct bng_re_dev *rdev,
+ struct seq_file *s);
+int bng_re_roce_mpc_perf_debugfs_show(struct bng_re_dev *rdev,
+ struct seq_file *s, u8 level);
+void bng_re_roce_mpc_perf_debugfs_clear(struct bng_re_dev *rdev);
+void bng_re_roce_mpc_diag_counters_clear(struct bng_re_dev *rdev);
+
+int bng_mpc_start_background_poll(struct bng_re_dev *rdev,
+ struct task_struct **new_thread);
+void bng_mpc_stop_background_poll(struct bng_re_dev *rdev,
+ struct task_struct **thread);
+
+int bng_alloc_init_mpc(struct bng_re_dev *rdev);
+int bng_deinit_mpc(struct bng_re_dev *rdev);
+void bng_mpc_stop_irq(struct bng_mpc_ctx *mpc, bool kill);
+
+int bng_re_alloc_mpc_doorbells(struct bng_re_dev *rdev);
+void bng_re_unalloc_mpc_doorbells(struct bng_re_dev *rdev);
+void bng_re_check_mpc_pending_empty(struct bng_re_dev *rdev);
+
+void bng_re_vf_pfvf_cmpl_wq_task(struct work_struct *work);
+
+#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_res.c b/drivers/infiniband/hw/bng_re/bng_res.c
index f6e3528e7f4c..be6d5099aa35 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.c
+++ b/drivers/infiniband/hw/bng_re/bng_res.c
@@ -8,6 +8,7 @@
#include <linux/bnge/hsi.h>
#include "bng_res.h"
#include "bng_roce_hsi.h"
+#include "bng_sp.h"
/* Stats */
void bng_re_free_stats_ctx_mem(struct pci_dev *pdev,
@@ -50,7 +51,7 @@ static void bng_free_pbl(struct bng_re_res *res, struct bng_re_pbl *pbl)
pbl->pg_map_arr[i]);
else
dev_warn(&pdev->dev,
- "PBL free pg_arr[%d] empty?!\n", i);
+ "PBL free pg_arr[%d] empty?!\n", i);
pbl->pg_arr[i] = NULL;
}
@@ -90,9 +91,9 @@ static int bng_alloc_pbl(struct bng_re_res *res,
for (i = 0; i < pages; i++) {
pbl->pg_arr[i] = dma_alloc_coherent(&pdev->dev,
- pbl->pg_size,
- &pbl->pg_map_arr[i],
- GFP_KERNEL);
+ pbl->pg_size,
+ &pbl->pg_map_arr[i],
+ GFP_KERNEL);
if (!pbl->pg_arr[i])
goto fail;
pbl->pg_count++;
@@ -192,7 +193,7 @@ int bng_re_alloc_init_hwq(struct bng_re_hwq *hwq,
/* Alloc or init PTEs */
rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_2],
- hwq_attr->sginfo);
+ hwq_attr->sginfo);
if (rc)
goto fail;
hwq->level = BNG_PBL_LVL_2;
@@ -231,7 +232,7 @@ int bng_re_alloc_init_hwq(struct bng_re_hwq *hwq,
goto fail;
/* Alloc or init PTEs */
rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_1],
- hwq_attr->sginfo);
+ hwq_attr->sginfo);
if (rc)
goto fail;
hwq->level = BNG_PBL_LVL_1;
@@ -277,3 +278,104 @@ int bng_re_alloc_init_hwq(struct bng_re_hwq *hwq,
bng_re_free_hwq(res, hwq);
return -ENOMEM;
}
+
+void bng_res_unmap_db_bar(struct bng_re_res *res)
+{
+ struct bng_re_reg_desc *reg;
+
+ reg = &res->dpi_tbl.ucreg;
+ if (reg->bar_reg)
+ pci_iounmap(res->pdev, reg->bar_reg);
+ reg->bar_reg = NULL;
+ reg->bar_base = 0;
+ reg->len = 0;
+ reg->bar_id = 0;
+}
+
+int bng_res_map_db_bar(struct bng_re_res *res)
+{
+ struct bng_re_reg_desc *ucreg;
+ struct bng_re_reg_desc *wcreg;
+
+ wcreg = &res->dpi_tbl.wcreg;
+ wcreg->bar_id = RCFW_DBR_PCI_BAR_REGION;
+ wcreg->bar_base = pci_resource_start(res->pdev, wcreg->bar_id);
+
+ ucreg = &res->dpi_tbl.ucreg;
+ ucreg->bar_id = RCFW_DBR_PCI_BAR_REGION;
+ ucreg->bar_base = pci_resource_start(res->pdev, ucreg->bar_id);
+ ucreg->len = ucreg->offset + PAGE_SIZE;
+ if (!ucreg->len) {
+ dev_err(&res->pdev->dev, "invalid dbr length %d",
+ (int)ucreg->len);
+ return -EINVAL;
+ }
+ ucreg->bar_reg = ioremap(ucreg->bar_base, ucreg->len);
+ if (!ucreg->bar_reg) {
+ dev_err(&res->pdev->dev, "privileged dpi map failed!");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void bng_res_free_dpi_tbl(struct bng_re_dpi_tbl *dpit)
+{
+ kfree(dpit->tbl);
+ kfree(dpit->app_tbl);
+ dpit->tbl = NULL;
+ dpit->app_tbl = NULL;
+ dpit->max = 0;
+}
+
+static int bng_res_alloc_dpi_tbl(struct bng_re_res *res,
+ struct bng_re_dev_attr *dev_attr)
+{
+ struct bng_re_dpi_tbl *dpit;
+ struct bng_re_reg_desc *reg;
+ unsigned long bar_len;
+ u32 bytes;
+
+ dpit = &res->dpi_tbl;
+ reg = &dpit->wcreg;
+
+ bar_len = pci_resource_len(res->pdev, reg->bar_id);
+
+ dpit->max = (bar_len - reg->offset) / PAGE_SIZE;
+ if (dev_attr->max_dpi)
+ dpit->max = min_t(u32, dpit->max, dev_attr->max_dpi);
+
+ dpit->app_tbl = kcalloc(dpit->max, sizeof(void *), GFP_KERNEL);
+ if (!dpit->app_tbl)
+ return -ENOMEM;
+
+ bytes = (dpit->max + 7) >> 3;
+ dpit->tbl = kmalloc(bytes, GFP_KERNEL);
+ if (!dpit->tbl) {
+ kfree(dpit->app_tbl);
+ dpit->app_tbl = NULL;
+ dev_err(&res->pdev->dev,
+ "DPI tbl allocation failed for size = %d", bytes);
+ return -ENOMEM;
+ }
+
+ memset((u8 *)dpit->tbl, 0xFF, bytes);
+ dpit->priv_db = dpit->ucreg.bar_reg + dpit->ucreg.offset;
+ return 0;
+}
+
+void bng_res_free_tbls(struct bng_re_res *res)
+{
+ bng_res_free_dpi_tbl(&res->dpi_tbl);
+}
+
+int bng_res_alloc_init_tbls(struct bng_re_res *res)
+{
+ int rc;
+
+ rc = bng_res_alloc_dpi_tbl(res, res->dattr);
+ if (rc)
+ dev_err(&res->pdev->dev, "DPI tbl alloc failed\n");
+
+ return rc;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_res.h b/drivers/infiniband/hw/bng_re/bng_res.h
index 2c4e9191ad1c..9745609190d9 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.h
+++ b/drivers/infiniband/hw/bng_re/bng_res.h
@@ -5,6 +5,10 @@
#define __BNG_RES_H__
#include "bng_roce_hsi.h"
+#include "xid_allocator.h"
+#include <linux/bnge/hsi.h>
+
+#define BNG_NQE_MAX_CNT (128 * 1024)
#define BNG_ROCE_FW_MAX_TIMEOUT 60
@@ -14,10 +18,10 @@
#define PTR_IDX(x) ((x) & PTR_MAX_IDX_PER_PG)
#define HWQ_CMP(idx, hwq) ((idx) & ((hwq)->max_elements - 1))
-#define HWQ_FREE_SLOTS(hwq) (hwq->max_elements - \
- ((HWQ_CMP(hwq->prod, hwq)\
- - HWQ_CMP(hwq->cons, hwq))\
- & (hwq->max_elements - 1)))
+#define HWQ_FREE_SLOTS(hwq) ((hwq)->max_elements - \
+ ((HWQ_CMP((hwq)->prod, hwq)\
+ - HWQ_CMP((hwq)->cons, hwq))\
+ & ((hwq)->max_elements - 1)))
#define MAX_PBL_LVL_0_PGS 1
#define MAX_PBL_LVL_1_PGS 512
@@ -32,6 +36,8 @@
#define BNG_MAX_TQM_ALLOC_REQ 48
+#define RCFW_DBR_PCI_BAR_REGION 2
+
struct bng_re_reg_desc {
u8 bar_id;
resource_size_t bar_base;
@@ -43,11 +49,16 @@ struct bng_re_reg_desc {
struct bng_re_db_info {
void __iomem *db;
void __iomem *priv_db;
+ void *dbc; /* HDBR registration handle (optional) */
struct bng_re_hwq *hwq;
u32 xid;
+ u32 seed;
u32 max_slot;
u32 flags;
u8 toggle;
+ spinlock_t lock; /* protects concurrent doorbell ring updates */
+ struct bng_re_res *res;
+ bool is_l2;
};
enum bng_re_db_info_flags_mask {
@@ -57,16 +68,29 @@ enum bng_re_db_info_flags_mask {
BNG_RE_FLAG_EPOCH_PROD_MASK = 0x2UL,
};
+#define BNG_QP_FLAG_EPOCH_CONS_MASK BNG_RE_FLAG_EPOCH_CONS_MASK
+
enum bng_re_db_epoch_flag_shift {
BNG_RE_DB_EPOCH_CONS_SHIFT = BNG_RE_DBR_EPOCH_SHIFT,
BNG_RE_DB_EPOCH_PROD_SHIFT = (BNG_RE_DBR_EPOCH_SHIFT - 1),
};
+struct bng_re_drv_modes {
+ u8 wqe_mode;
+ bool db_push;
+ bool dbr_pacing;
+ u32 toggle_bits;
+ u8 roce_mirror;
+ u8 dbr_primary_pf;
+ bool st_tag_supported;
+};
+
struct bng_re_chip_ctx {
u16 chip_num;
u16 hw_stats_size;
u64 hwrm_intf_ver;
u16 hwrm_cmd_max_timeout;
+ struct bng_re_drv_modes modes;
};
struct bng_re_pbl {
@@ -132,10 +156,23 @@ struct bng_re_stats {
u32 fw_id;
};
+/* DPI table */
+struct bng_re_dpi_tbl {
+ void **app_tbl;
+ unsigned long *tbl;
+ u16 max;
+ struct bng_re_reg_desc ucreg; /* Hold entire DB bar. */
+ struct bng_re_reg_desc wcreg;
+ void __iomem *priv_db;
+};
+
struct bng_re_res {
struct pci_dev *pdev;
struct bng_re_chip_ctx *cctx;
struct bng_re_dev_attr *dattr;
+ struct bng_re_dpi_tbl dpi_tbl;
+ struct xid_manager *qp_xids;
+ struct xid_manager *ah_xids;
};
static inline void *bng_re_get_qe(struct bng_re_hwq *hwq,
@@ -195,6 +232,34 @@ static inline void bng_re_hwq_incr_cons(u32 max_elements, u32 *cons, u32 cnt,
}
}
+static inline void bng_hwq_incr_prod(struct bng_re_db_info *dbinfo,
+ struct bng_re_hwq *hwq, u32 cnt)
+{
+ /* move prod and update toggle/epoch if wrap around */
+ hwq->prod += cnt;
+ if (hwq->prod >= hwq->depth) {
+ hwq->prod %= hwq->depth;
+ dbinfo->flags ^= 1UL << BNG_RE_FLAG_EPOCH_PROD_SHIFT;
+ }
+}
+
+static inline void bng_ring_prod_db(struct bng_re_db_info *info, u32 type)
+{
+ u64 key = 0;
+ u32 indx;
+
+ indx = (((info->hwq->prod / info->max_slot) & DBC_DBC_INDEX_MASK) |
+ ((info->flags & BNG_RE_FLAG_EPOCH_PROD_MASK) <<
+ BNG_RE_DB_EPOCH_PROD_SHIFT));
+
+ key = BNG_RE_INIT_DBHDR(info->xid, type, indx, 0);
+
+ if (info->is_l2)
+ key |= DBC_DBC64_PATH_L2;
+
+ writeq(key, info->db);
+}
+
static inline bool _is_max_srq_ext_supported(u16 dev_cap_ext_flags_2)
{
return !!(dev_cap_ext_flags_2 & CREQ_QUERY_FUNC_RESP_SB_MAX_SRQ_EXTENDED);
@@ -212,4 +277,9 @@ void bng_re_free_stats_ctx_mem(struct pci_dev *pdev,
int bng_re_alloc_stats_ctx_mem(struct pci_dev *pdev,
struct bng_re_chip_ctx *cctx,
struct bng_re_stats *stats);
+
+void bng_res_unmap_db_bar(struct bng_re_res *res);
+int bng_res_map_db_bar(struct bng_re_res *res);
+void bng_res_free_tbls(struct bng_re_res *res);
+int bng_res_alloc_init_tbls(struct bng_re_res *res);
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_roce_hsi.h b/drivers/infiniband/hw/bng_re/bng_roce_hsi.h
index 5ebd7ba90b7b..6c9d45464ef6 100644
--- a/drivers/infiniband/hw/bng_re/bng_roce_hsi.h
+++ b/drivers/infiniband/hw/bng_re/bng_roce_hsi.h
@@ -6447,4 +6447,98 @@ struct ptu_pde {
#define PTU_PDE_PAGE_SFT 12
};
+/* mpc_qp_modify_cmpl (size:256b/32B) */
+struct mpc_qp_modify_cmpl {
+ u8 cmpl_type_reserved;
+ #define MPC_QP_MODIFY_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_MASK 0x3fUL
+ #define MPC_QP_MODIFY_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_SFT 0
+ #define MPC_QP_MODIFY_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_MPC_CMP_SHORT 0x1eUL
+ #define MPC_QP_MODIFY_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_MPC_CMP_LONG 0x1fUL
+ #define MPC_QP_MODIFY_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_LAST \
+ MPC_QP_MODIFY_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_MPC_CMP_LONG
+ u8 error_code_mp_client;
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_MP_CLIENT_ERROR_CODE_MASK 0xfUL
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_MP_CLIENT_ERROR_CODE_SFT 0
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_MP_CLIENT_MP_CLIENT_MASK 0xf0UL
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_MP_CLIENT_MP_CLIENT_SFT 4
+ u8 req_type;
+ u8 req_subtype;
+ __le32 opaque;
+ u8 v;
+ #define MPC_QP_MODIFY_CMPL_V 0x1UL
+ u8 reserved_0[7];
+ __le32 xid;
+ __le16 error_code;
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_SUCCESS 0x0UL
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_FAIL 0x1UL
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_RESOURCES 0x2UL
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_INVALID_CMD 0x3UL
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_NOT_IMPLEMENTED 0x4UL
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_INVALID_PARAMETER 0x5UL
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_HARDWARE_ERROR 0x6UL
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_INTERNAL_ERROR 0x7UL
+ #define MPC_QP_MODIFY_CMPL_ERROR_CODE_LAST \
+ MPC_QP_MODIFY_CMPL_ERROR_CODE_INTERNAL_ERROR
+ u8 pingpong_push_state_index_enabled;
+ #define MPC_QP_MODIFY_CMPL_PINGPONG_PUSH_ENABLED 0x1UL
+ #define MPC_QP_MODIFY_CMPL_PINGPONG_PUSH_INDEX_MASK 0xeUL
+ #define MPC_QP_MODIFY_CMPL_PINGPONG_PUSH_INDEX_SFT 1
+ #define MPC_QP_MODIFY_CMPL_PINGPONG_PUSH_STATE 0x10UL
+ u8 flags;
+ #define MPC_QP_MODIFY_CMPL_FLAGS_FLAGS_MASK 0xffUL
+ #define MPC_QP_MODIFY_CMPL_FLAGS_FLAGS_SFT 0
+ #define MPC_QP_MODIFY_CMPL_FLAGS_FLAGS_QP_MD_COMPLETE 0x0UL
+ #define MPC_QP_MODIFY_CMPL_FLAGS_FLAGS_QP_MD_PENDING 0x1UL
+ #define MPC_QP_MODIFY_CMPL_FLAGS_FLAGS_LAST \
+ MPC_QP_MODIFY_CMPL_FLAGS_FLAGS_QP_MD_PENDING
+ u8 v2;
+ #define MPC_QP_MODIFY_CMPL_V2 0x1UL
+ u8 reserved_3[3];
+ __le32 lag_src_mac;
+};
+
+/* mpc_event_resp_cmpl (size:256b/32B) */
+struct mpc_event_resp_cmpl {
+ u8 cmpl_type_reserved;
+ #define MPC_EVENT_RESP_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_MASK 0x3fUL
+ #define MPC_EVENT_RESP_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_SFT 0
+ #define MPC_EVENT_RESP_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_MPC_CMP_SHORT 0x1eUL
+ #define MPC_EVENT_RESP_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_MPC_CMP_LONG 0x1fUL
+ #define MPC_EVENT_RESP_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_LAST \
+ MPC_EVENT_RESP_CMPL_CMPL_TYPE_RESERVED_CMPL_TYPE_MPC_CMP_LONG
+ u8 error_code_mp_client;
+ #define MPC_EVENT_RESP_CMPL_ERROR_CODE_MP_CLIENT_ERROR_CODE_MASK 0xfUL
+ #define MPC_EVENT_RESP_CMPL_ERROR_CODE_MP_CLIENT_ERROR_CODE_SFT 0
+ #define MPC_EVENT_RESP_CMPL_ERROR_CODE_MP_CLIENT_MP_CLIENT_MASK 0xf0UL
+ #define MPC_EVENT_RESP_CMPL_ERROR_CODE_MP_CLIENT_MP_CLIENT_SFT 4
+ u8 req_type;
+ u8 req_subtype;
+ __le32 opaque;
+ u8 v;
+ #define MPC_EVENT_RESP_CMPL_V 0x1UL
+ u8 reserved_0[1];
+ __le16 error_code;
+ #define MPC_EVENT_RESP_CMPL_ERROR_CODE_SUCCESS 0x0UL
+ #define MPC_EVENT_RESP_CMPL_ERROR_CODE_FAIL 0x1UL
+ #define MPC_EVENT_RESP_CMPL_ERROR_CODE_LAST MPC_EVENT_RESP_CMPL_ERROR_CODE_FAIL
+ __le32 xid;
+ __le32 event_type;
+ #define MPC_EVENT_RESP_CMPL_EVENT_TYPE_EVENT_TYPE_MASK 0xffffffffUL
+ #define MPC_EVENT_RESP_CMPL_EVENT_TYPE_EVENT_TYPE_SFT 0
+ #define MPC_EVENT_RESP_CMPL_EVENT_TYPE_EVENT_TYPE_QP_EVENT 0x1UL
+ #define MPC_EVENT_RESP_CMPL_EVENT_TYPE_EVENT_TYPE_LAST \
+ MPC_EVENT_RESP_CMPL_EVENT_TYPE_EVENT_TYPE_QP_EVENT
+ __le32 event_data;
+ #define MPC_EVENT_RESP_CMPL_EVENT_DATA_EVENT_TYPE_MASK 0xffffffffUL
+ #define MPC_EVENT_RESP_CMPL_EVENT_DATA_EVENT_TYPE_SFT 0
+ #define MPC_EVENT_RESP_CMPL_EVENT_DATA_EVENT_TYPE_FASTQP_MD_COMPLETE 0x0UL
+ #define MPC_EVENT_RESP_CMPL_EVENT_DATA_EVENT_TYPE_FASTQP_MD_PENDING 0x1UL
+ #define MPC_EVENT_RESP_CMPL_EVENT_DATA_EVENT_TYPE_LAST \
+ MPC_EVENT_RESP_CMPL_EVENT_DATA_EVENT_TYPE_FASTQP_MD_PENDING
+ u8 v2;
+ #define MPC_EVENT_RESP_CMPL_V2 0x1UL
+ u8 reserved_1[3];
+ __le32 resp_data;
+};
+
#endif /* _BNG_RE_HSI_H_ */
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.h b/drivers/infiniband/hw/bng_re/bng_sp.h
index e15190515ed1..91faa5ac7464 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.h
+++ b/drivers/infiniband/hw/bng_re/bng_sp.h
@@ -9,6 +9,10 @@
#define BNG_VAR_MAX_WQE 4352
#define BNG_VAR_MAX_SGE 13
+#define NQE_CMP_VALID(hdr, pass) \
+ (!!(le32_to_cpu((hdr)->info63_v[0]) & NQ_BASE_V) == \
+ !((pass) & BNG_RE_FLAG_EPOCH_CONS_MASK))
+
struct bng_re_dev_attr {
#define FW_VER_ARR_LEN 4
u8 fw_ver[FW_VER_ARR_LEN];
diff --git a/drivers/infiniband/hw/bng_re/bng_xid.c b/drivers/infiniband/hw/bng_re/bng_xid.c
new file mode 100644
index 000000000000..6070183d9120
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_xid.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Broadcom.
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+#include "bng_re.h"
+#include "bng_res.h"
+#include "bng_sp.h"
+#include "bng_xid.h"
+#include "bng_fw.h"
+
+int bng_re_qp_alloc_xid_and_iqm(struct bng_re_res *res, u8 qp_type,
+ int num_xids, u32 max_wqe, int vf_id,
+ void *iqm_res, int *xid)
+{
+ dev_dbg(&res->pdev->dev,
+ "XID+IQM: enter qp_type=%u num_xids=%d max_wqe=%u vf_id=%d qp_xids=%p\n",
+ qp_type, num_xids, max_wqe, vf_id, res->qp_xids);
+
+ if (qp_type == CMDQ_CREATE_QP_TYPE_GSI) {
+ *xid = 1;
+ dev_dbg(&res->pdev->dev, "XID+IQM: GSI fixed xid=%d\n", *xid);
+ return 0;
+ }
+ if (qp_type == CMDQ_CREATE_QP_TYPE_RC) {
+ /* IQM not implemented yet */
+ return -EOPNOTSUPP;
+ }
+ if (!res->qp_xids) {
+ dev_dbg(&res->pdev->dev,
+ "XID+IQM: no driver qp_xids pool -> ENODEV\n");
+ return -ENODEV;
+ }
+ *xid = bng_re_xm_alloc_range(res->qp_xids, num_xids, vf_id);
+ if (*xid < 0) {
+ dev_dbg(&res->pdev->dev,
+ "XID+IQM: xm_alloc_range failed ret=%d num_xids=%d vf_id=%d\n",
+ *xid, num_xids, vf_id);
+ {
+ struct xid_public_stats st;
+
+ bng_re_xm_get_stats(res->qp_xids, &st);
+ dev_warn(&res->pdev->dev,
+ "QP xid allocation failed, curr_active=%d max_active=%d alloc_ids=%llu\n",
+ bng_re_xm_curr_active_get(res->qp_xids),
+ bng_re_xm_max_active_get(res->qp_xids),
+ st.xp_current_alloc_ids);
+ }
+ return -ENOMEM;
+ }
+ dev_dbg(&res->pdev->dev, "XID+IQM: xm_alloc ok xid=%d\n", *xid);
+ return 0;
+}
+
+void bng_re_qp_free_xid_and_iqm(struct bng_re_res *res, u8 qp_type, int xid,
+ void *iqm_res, bool free_qp_xid)
+{
+ if (qp_type == CMDQ_CREATE_QP_TYPE_GSI)
+ return;
+
+ if (free_qp_xid && res->qp_xids)
+ bng_re_xm_free_range(res->qp_xids, xid, true);
+}
+
+int bng_re_init_xid_tables(struct bng_re_dev *rdev)
+{
+ struct bng_re_res *res = &rdev->bng_res;
+ int qp_start = 2;
+ int qp_num_ids;
+ int qp_max;
+ int rc;
+
+ qp_max = (int)rdev->dev_attr->max_qp;
+ if (qp_max < 1)
+ return -EINVAL;
+ qp_num_ids = qp_max + 1024;
+
+ res->qp_xids = bng_re_xm_init(qp_start, qp_num_ids, qp_max, BNG_RE_XID_AVOID_REUSE);
+ if (!res->qp_xids)
+ return -ENOMEM;
+
+ res->ah_xids = bng_re_xm_init(0, (int)rdev->dev_attr->max_ah,
+ (int)rdev->dev_attr->max_ah,
+ BNG_RE_XID_AVOID_REUSE);
+ if (!res->ah_xids) {
+ rc = -ENOMEM;
+ goto free_qp_xm;
+ }
+
+ return 0;
+
+free_qp_xm:
+ bng_re_xm_destroy(res->qp_xids);
+ res->qp_xids = NULL;
+ return rc;
+}
+
+void bng_re_free_xid_tables(struct bng_re_res *res)
+{
+ if (res->ah_xids) {
+ bng_re_xm_destroy(res->ah_xids);
+ res->ah_xids = NULL;
+ }
+ if (res->qp_xids) {
+ bng_re_xm_destroy(res->qp_xids);
+ res->qp_xids = NULL;
+ }
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_xid.h b/drivers/infiniband/hw/bng_re/bng_xid.h
new file mode 100644
index 000000000000..f76babb546cf
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_xid.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2025 Broadcom. */
+
+#ifndef __BNG_XID_H__
+#define __BNG_XID_H__
+
+#include <linux/kernel.h>
+
+#include "bng_roce_hsi.h"
+#include "xid_allocator.h"
+
+struct bng_re_dev;
+struct bng_re_res;
+
+#define ROUND_UP_SIZE_BNG(n, sz) round_up(n, sz)
+
+int bng_re_qp_alloc_xid_and_iqm(struct bng_re_res *res, u8 qp_type,
+ int num_xids, u32 max_wqe, int vf_id,
+ void *iqm_res, int *xid);
+void bng_re_qp_free_xid_and_iqm(struct bng_re_res *res, u8 qp_type, int xid,
+ void *iqm_res, bool free_qp_xid);
+
+int bng_re_init_xid_tables(struct bng_re_dev *rdev);
+void bng_re_free_xid_tables(struct bng_re_res *res);
+
+#endif /* __BNG_XID_H__ */
diff --git a/drivers/infiniband/hw/bng_re/bng_xid_allocator.c b/drivers/infiniband/hw/bng_re/bng_xid_allocator.c
new file mode 100644
index 000000000000..41dfb83a0a92
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_xid_allocator.c
@@ -0,0 +1,870 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Broadcom.
+
+#include "xid_allocator.h"
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/stddef.h>
+/* #include <string.h> - required for standalone compile */
+
+#define DEBUG_XID (g_debug_xid)
+
+int g_debug_xid;
+
+static void bng_re_xm_consolidate_ranges(struct xid_manager *xm,
+ struct xid_node_info *prev,
+ struct rb_root *tree
+);
+
+static int bng_re_xm_trans_range_by_pointer(struct xid_manager *xm, struct xid_node_info *nd);
+
+/**
+ * bng_re_xm_rb_insert - common fn for inserting range into rb tree
+ *
+ * @param root - root of rb tree to insert into
+ * @param data - range to insert
+ * @param fn - function for compare
+ * @param return - true for success, false for an issue (duplicate key)
+ */
+static int bng_re_xm_rb_insert(struct rb_root *root, struct xid_node_info *data,
+ rb_compare_fn_t fn)
+{
+ struct rb_node **new = &root->rb_node, *parent = NULL;
+
+ /* Figure out where to put new node */
+ while (*new) {
+ struct xid_node_info *this = container_of(*new, struct xid_node_info, node);
+ int result = fn(data, this);
+
+ parent = *new;
+ if (result < 0)
+ new = &((*new)->rb_left);
+ else if (result > 0)
+ new = &((*new)->rb_right);
+ else
+ return false;
+ }
+
+ /* Add new node and rebalance tree. */
+ rb_link_node(&data->node, parent, new);
+ rb_insert_color(&data->node, root);
+
+ return true;
+}
+
+static int bng_re_xi_free_compare(void *left, void *right)
+{
+ struct xid_node_info *lp = (struct xid_node_info *)left;
+ struct xid_node_info *rp = (struct xid_node_info *)right;
+
+ if (lp->xi_num_ids != rp->xi_num_ids)
+ return lp->xi_num_ids - rp->xi_num_ids;
+
+ return lp->xi_starting_id - rp->xi_starting_id;
+}
+
+static int bng_re_xi_alloced_compare(void *left, void *right)
+{
+ struct xid_node_info *lp = (struct xid_node_info *)left;
+ struct xid_node_info *rp = (struct xid_node_info *)right;
+
+ return lp->xi_starting_id - rp->xi_starting_id;
+}
+
+/**************************************************
+ * util fns for the id based tree *
+ **************************************************/
+
+/**
+ * bng_re_xm_insert_by_id - insert info into xid based tree. Info is a range
+ *of available ids
+ *
+ * @param root - root of rb tree to insert into
+ * @param data - range to insert
+ * @param return - 0 for success, -1 for error
+ */
+static int bng_re_xm_insert_by_id(struct rb_root *root,
+ struct xid_node_info *data)
+{
+ return bng_re_xm_rb_insert(root, data, bng_re_xi_alloced_compare);
+}
+
+/**
+ * bng_re_xm_search_by_id - search xid based tree.
+ *
+ * @param root - root of rb tree to search
+ * @param xid - xid to search
+ * @param return - range which was found; or NULL
+ */
+static struct xid_node_info *bng_re_xm_search_by_id(struct rb_root *root, int xid)
+{
+ struct rb_node *node = root->rb_node;
+
+ while (node) {
+ struct xid_node_info *data = container_of(node, struct xid_node_info, node);
+ int result;
+
+ result = xid - data->xi_starting_id;
+
+ if (result < 0)
+ node = node->rb_left;
+ else if (result > 0)
+ node = node->rb_right;
+ else
+ return data;
+ }
+ return NULL;
+}
+
+/**************************************************
+ * util fns for the free range based tree *
+ **************************************************/
+
+/**
+ * bng_re_xm_insert_by_free_range - insert info into range based tree.
+ * Info is a range of ids. Order by
+ *range size.
+ *
+ * @param root - root of rb tree to insert into
+ * @param data - range to insert
+ * @return - 0 for success, -1 for error
+ */
+static int bng_re_xm_insert_by_free_range(struct rb_root *root,
+ struct xid_node_info *data)
+{
+ return bng_re_xm_rb_insert(root, data, bng_re_xi_free_compare);
+}
+
+/**
+ * bng_re_xm_search_best_free - look for best range for requested number of
+ * ids.
+ *
+ * @param root - root of rb tree to insert into
+ * @param ids_required - number of ids needed
+ * @return - best matching range
+ */
+static struct xid_node_info *bng_re_xm_search_best_free(struct rb_root *root,
+ int ids_required)
+{
+ struct rb_node *node = root->rb_node;
+ struct xid_node_info *best = NULL;
+
+ while (node) {
+ struct xid_node_info *data = container_of(node, struct xid_node_info, node);
+
+ if (data->xi_num_ids == ids_required) {
+ best = data;
+ break;
+ } else if (data->xi_num_ids > ids_required) {
+ best = data;
+ node = node->rb_left;
+ } else {
+ node = node->rb_right;
+ }
+ }
+ return best;
+}
+
+/**
+ * bng_re_xm_reset_pending_ranges -
+ *
+ * @param root - root of rb tree to insert into
+ */
+static int bng_re_xm_reset_pending_ranges(struct xid_manager *xm)
+{
+ struct rb_root *root = &xm->xm_pending_tree;
+ struct rb_node *node = root->rb_node;
+ struct xid_node_info *data = NULL;
+ int num_resets = 0;
+ /* pending => free tree */
+ while (node) {
+ data = container_of(node, struct xid_node_info, node);
+ num_resets++;
+ bng_re_xm_trans_range_by_pointer(xm, data);
+ node = root->rb_node;
+ }
+ return num_resets;
+}
+
+/**
+ * bng_re_xm_search_best_free_wrapper - look for best range for requested number of
+ *ids.
+ *
+ * @param root - root of rb tree to insert into
+ * @param ids_required - number of ids needed
+ * @param best range or NULL
+ */
+static struct xid_node_info *bng_re_xm_search_best_free_wrapper(struct xid_manager *xm,
+ int ids_required)
+{
+ struct xid_node_info *best = NULL;
+ int num_resets;
+
+ best = bng_re_xm_search_best_free(&xm->xm_free_tree, ids_required);
+ if (!best) {
+ num_resets = bng_re_xm_reset_pending_ranges(xm);
+ if (num_resets > 0)
+ best = bng_re_xm_search_best_free(&xm->xm_free_tree, ids_required);
+ }
+ return best;
+}
+
+/* for all trees - common removal function */
+static void bng_re_xm_del_from_tree(struct rb_root *root,
+ struct xid_node_info *data)
+{
+ if (data)
+ rb_erase(&data->node, root);
+}
+
+/**************************************************
+ * util fns for the xid_node_info *
+ **************************************************/
+
+static struct xid_node_info *bng_re_xm_tree_info_alloc(void)
+{
+ struct xid_node_info *xi = (struct xid_node_info *)CALLOC(1, sizeof(struct xid_node_info));
+ return xi;
+}
+
+static void bng_re_xm_tree_info_del(struct xid_node_info *xi)
+{
+ memset(xi, 0xa5, sizeof(*xi));
+ FREE(xi);
+}
+
+/**
+ * bng_re_xm_init - initialize xid manager structure
+ *
+ * @param start_id - starting id
+ * @param max_ids - size of id pool
+ * @param max_active_ids - cap on active IDs (caller passes e.g. max_ids for AH/IQM)
+ * @param avoid_reuse - whether or not to avoid immediate xid reuse
+ *
+ * @return - xid manager, or NULL on failure (e.g. max_active_ids > max_ids)
+ */
+struct xid_manager *bng_re_xm_init(int start_id, int max_ids, int max_active_ids,
+ bool avoid_reuse)
+{
+ struct xid_manager *p = CALLOC(1, sizeof(struct xid_manager));
+ struct xid_node_info *xi = NULL;
+
+ if (!p)
+ return NULL;
+ if (max_active_ids > max_ids)
+ goto err;
+ spin_lock_init(&p->xm_lock);
+ p->xm_free_tree = RB_ROOT;
+ p->xm_allocated_tree = RB_ROOT;
+ p->xm_pending_tree = RB_ROOT;
+ p->xm_num_ids = max_ids;
+ p->xm_starting_id = start_id;
+ p->xm_max_active_ids = max_active_ids;
+ p->xm_curr_active_ids = 0;
+ /* Add initial range to the free tree */
+ xi = bng_re_xm_tree_info_alloc();
+ xi->xi_starting_id = start_id;
+ xi->xi_num_ids = max_ids;
+ xi->xi_state = X_FREED;
+ bng_re_xm_insert_by_free_range(&p->xm_free_tree, xi);
+ p->xm_head = xi;
+ p->xm_avoid_reuse = avoid_reuse;
+ /* Initialize current counters - start with all IDs in free state */
+ p->xm_stats.xs_current_free_ids = max_ids;
+ p->xm_stats.xs_current_alloc_ids = 0;
+ p->xm_stats.xs_current_pending_free_ids = 0;
+ p->xm_stats.xs_max_alloc_ids = 0;
+ return p;
+err:
+ FREE(p);
+ return NULL;
+}
+
+static struct xid_node_info *bng_re_xm_find_best_free_range(struct xid_manager *xm,
+ int num_ids)
+{
+ struct xid_node_info *best = NULL;
+
+ best = bng_re_xm_search_best_free_wrapper(xm, num_ids);
+
+ return best;
+}
+
+/**
+ * bng_re_xm_alloc_range - Allocate a range from the free pool
+ *
+ * @param xm -
+ * @param num_ids - number of ids required
+ * @param tag - tag for the allocation
+ *
+ * @return - starting id for the range, or negative on failure
+ */
+int bng_re_xm_alloc_range(struct xid_manager *xm, int num_ids, int tag)
+{
+ struct xid_node_info *xi_alloced;
+ struct xid_node_info *xi_free;
+ int starting_id = -1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&xm->xm_lock, flags);
+ if (xm->xm_curr_active_ids >= xm->xm_max_active_ids) {
+ xm->xm_stats.xs_alloc_err_active_limit_ctr++;
+ goto exit;
+ }
+ xi_free = bng_re_xm_find_best_free_range(xm, num_ids);
+
+ if (!xi_free) {
+ /* couldnt find that joint */
+ xm->xm_stats.xs_alloc_err_ctr++;
+ goto exit;
+ }
+
+ /*remove it from the tree because; after we change the num ids
+ * it has to be reinserted into the optimal position
+ */
+ bng_re_xm_del_from_tree(&xm->xm_free_tree, xi_free);
+
+ if (num_ids == xi_free->xi_num_ids) {
+ /* an exact fit. Just move it to the allocated tree */
+ xi_free->xi_state = X_ALLOCED;
+ bng_re_xm_insert_by_id(&xm->xm_allocated_tree, xi_free);
+ starting_id = xi_free->xi_starting_id;
+ xi_free->xi_tag = tag;
+ goto count_exit;
+ }
+ /* not exact match. need a range split */
+ xi_alloced = bng_re_xm_tree_info_alloc();
+ *xi_alloced = *xi_free;
+ xi_alloced->xi_next = xi_free;
+ xi_alloced->xi_num_ids = num_ids;
+ xi_alloced->xi_state = X_ALLOCED;
+ xi_alloced->xi_tag = tag;
+ if (xi_free->xi_prev)
+ xi_free->xi_prev->xi_next = xi_alloced;
+ /* We copied over the other fields already : starting id, prev ptr */
+ bng_re_xm_insert_by_id(&xm->xm_allocated_tree, xi_alloced);
+
+ /* adjust the range of the free entries */
+ xi_free->xi_prev = xi_alloced;
+ xi_free->xi_starting_id += num_ids;
+ xi_free->xi_num_ids -= num_ids;
+ /* Now reinsert into the free tree */
+ bng_re_xm_insert_by_free_range(&xm->xm_free_tree, xi_free);
+ if (xm->xm_head == xi_free) {
+ /*newly alloced entry may be the first one */
+ xm->xm_head = xi_alloced;
+ }
+ starting_id = xi_alloced->xi_starting_id;
+count_exit:
+ /* Update current counters: move IDs to allocated tree */
+ xm->xm_stats.xs_current_free_ids -= num_ids;
+ xm->xm_stats.xs_current_alloc_ids += num_ids;
+ xm->xm_curr_active_ids++;
+ /* Update maximum allocated IDs if we reached a new high */
+ if (xm->xm_stats.xs_current_alloc_ids > xm->xm_stats.xs_max_alloc_ids)
+ xm->xm_stats.xs_max_alloc_ids = xm->xm_stats.xs_current_alloc_ids;
+ xm->xm_stats.xs_alloc_op_ctr++;
+ xm->xm_stats.xs_alloc_count_ctr += num_ids;
+exit:
+ spin_unlock_irqrestore(&xm->xm_lock, flags);
+ return starting_id;
+}
+
+/**
+ * bng_re_xm_free_range - find a range by id and free it, optionally
+ * decrementing the active-id count.
+ *
+ * @param xm - xid manager
+ * @param id - id of range to free, or BNG_RE_XID_DECR_ONLY for decr only
+ * @param decr_active - if true, decrement curr_active_ids; for normal id
+ * also remove from tree. For BNG_RE_XID_DECR_ONLY must be true.
+ * @return - 0 for success, negative on error
+ */
+int bng_re_xm_free_range(struct xid_manager *xm, int id, bool decr_active)
+{
+ struct xid_node_info *xi_alloced;
+ unsigned long flags;
+ int rc = -1;
+ int num_ids;
+
+ spin_lock_irqsave(&xm->xm_lock, flags);
+
+ /* dump_stack(); */
+ if (id == BNG_RE_XID_DECR_ONLY) {
+ if (!decr_active) {
+ /* spec: log error */
+ XID_PRINT("xid free: BNG_RE_XID_DECR_ONLY with decr_active false\n");
+ goto exit;
+ }
+ xm->xm_curr_active_ids--;
+ xm->xm_stats.xs_free_decr_only++;
+ rc = 0;
+ goto exit;
+ }
+
+ xi_alloced = bng_re_xm_search_by_id(&xm->xm_allocated_tree, id);
+ if (!xi_alloced) {
+ xm->xm_stats.xs_free_err_ctr++;
+ goto exit;
+ }
+ num_ids = xi_alloced->xi_num_ids;
+ bng_re_xm_trans_range_by_pointer(xm, xi_alloced);
+ xm->xm_stats.xs_free_op_ctr++;
+ xm->xm_stats.xs_free_count_ctr += num_ids;
+ if (decr_active) {
+ xm->xm_curr_active_ids--;
+ xm->xm_stats.xs_free_decr_and_remove++;
+ } else {
+ xm->xm_stats.xs_free_remove_only++;
+ }
+ rc = 0;
+exit:
+ spin_unlock_irqrestore(&xm->xm_lock, flags);
+ return rc;
+}
+
+#ifdef STANDALONE_BUILD
+struct xid_node_info *bng_re_xm_find_allocated(struct xid_manager *xm, int id)
+{
+ struct xid_node_info *n;
+
+ n = bng_re_xm_search_by_id(&xm->xm_allocated_tree, id);
+ return n;
+}
+#endif
+
+bool bng_re_xm_is_id_allocated(struct xid_manager *xm, int id)
+{
+ struct xid_node_info *n = NULL;
+
+ n = bng_re_xm_search_by_id(&xm->xm_allocated_tree, id);
+
+ return n ? true : false;
+}
+
+/**
+ * bng_re_xm_trans_range_by_pointer - trans range to next tree
+ *
+ * @param xm -
+ * @param struct xid_node_info * - node to free
+ * @param new_state = new state to go to
+ * @return - 0 for success
+ */
+static int bng_re_xm_trans_range_by_pointer(struct xid_manager *xm, struct xid_node_info *nd)
+{
+ struct rb_root *curr_tree;
+ struct rb_root *new_tree;
+ int target_state;
+
+ if (xm->xm_avoid_reuse) {
+ if (nd->xi_state == X_ALLOCED) {
+ curr_tree = &xm->xm_allocated_tree;
+ new_tree = &xm->xm_pending_tree;
+ target_state = X_FREE_PENDING;
+ } else if (nd->xi_state == X_FREE_PENDING) {
+ curr_tree = &xm->xm_pending_tree;
+ new_tree = &xm->xm_free_tree;
+ target_state = X_FREED;
+ } else {
+ return -1;
+ }
+ } else {
+ if (nd->xi_state == X_ALLOCED) {
+ curr_tree = &xm->xm_allocated_tree;
+ new_tree = &xm->xm_free_tree;
+ target_state = X_FREED;
+ } else {
+ return -1;
+ }
+ }
+
+ bng_re_xm_del_from_tree(curr_tree, nd);
+
+ /* Update current counters based on state transition */
+ if (nd->xi_state == X_ALLOCED && target_state == X_FREE_PENDING) {
+ /* ALLOCED -> PENDING: move IDs from allocated to pending */
+ xm->xm_stats.xs_current_alloc_ids -= nd->xi_num_ids;
+ xm->xm_stats.xs_current_pending_free_ids += nd->xi_num_ids;
+ } else if (nd->xi_state == X_FREE_PENDING && target_state == X_FREED) {
+ /* PENDING -> FREE: move IDs from pending to free */
+ xm->xm_stats.xs_current_pending_free_ids -= nd->xi_num_ids;
+ xm->xm_stats.xs_current_free_ids += nd->xi_num_ids;
+ } else if (nd->xi_state == X_ALLOCED && target_state == X_FREED) {
+ /* ALLOCED -> FREE (direct): move IDs from allocated to free */
+ xm->xm_stats.xs_current_alloc_ids -= nd->xi_num_ids;
+ xm->xm_stats.xs_current_free_ids += nd->xi_num_ids;
+ }
+
+ nd->xi_state = target_state;
+ bng_re_xm_insert_by_free_range(new_tree, nd);
+
+ if (nd->xi_next && nd->xi_next->xi_state == target_state)
+ bng_re_xm_consolidate_ranges(xm, nd, new_tree);
+ if (nd->xi_prev && nd->xi_prev->xi_state == target_state)
+ bng_re_xm_consolidate_ranges(xm, nd->xi_prev, new_tree);
+ return 0;
+}
+
+/**
+ * bng_re_xm_consolidate_ranges - combine 2 ranges.
+ *
+ * @param xm - xid manager
+ * @param prev - previous range; which will include
+ * the next block after this call
+ */
+static void bng_re_xm_consolidate_ranges(struct xid_manager *xm,
+ struct xid_node_info *prev,
+ struct rb_root *root)
+{
+ struct xid_node_info *next;
+
+ next = prev->xi_next;
+
+ bng_re_xm_del_from_tree(root, prev);
+ bng_re_xm_del_from_tree(root, next);
+
+ prev->xi_num_ids += next->xi_num_ids;
+ prev->xi_next = next->xi_next;
+ if (next->xi_next)
+ next->xi_next->xi_prev = prev;
+
+ /* note we only consolidate in free or free pending trees */
+ bng_re_xm_insert_by_free_range(root, prev);
+ bng_re_xm_tree_info_del(next);
+}
+
+static char *bng_re_map_xi_state_to_string(int state)
+{
+ switch (state) {
+ case X_FREED:
+ return("FREED");
+ case X_FREE_PENDING:
+ return ("FREE_PENDING");
+ case X_ALLOCED:
+ return("ALLOCED");
+ }
+ return("unknown");
+}
+
+static bool bng_re_xm_print_one_range(void *p)
+{
+ struct xid_node_info *info = (struct xid_node_info *)p;
+ char *state = bng_re_map_xi_state_to_string(info->xi_state);
+
+ if (info && state) {
+ XID_PRINT(" info:[%p](start:%d,num_ids:%d,xi_state:%s,nxt:%p,prev:%p,tag:%d\n ",
+ info, info->xi_starting_id, info->xi_num_ids,
+ state, info->xi_next, info->xi_prev, info->xi_tag);
+ }
+ return false;
+}
+
+/* FIXME: move to xm manager */
+struct xid_node_info *g_searched_node;
+int g_searched_id;
+
+#ifdef STANDALONE_BUILD
+/**
+ * bng_re_xm_search_free_node_for_id - Find a node with a specific id
+ *
+ * @param p - node whose id we're searching for
+ */
+bool bng_re_xm_search_free_node_for_id(void *p)
+{
+ struct xid_node_info *info = (struct xid_node_info *)p;
+
+ if (info->xi_starting_id == g_searched_id) {
+ g_searched_node = info;
+ return true;
+ }
+ return false;
+}
+#endif
+
+/**
+ * bng_re_xm_rb_in_order_tree - traverse rb tree in order and execute a
+ *function
+ *
+ * @param root - root of rb tree to insert into
+ * @param fn - range function to execute
+ */
+static void bng_re_xm_rb_in_order_tree(struct rb_root *root,
+ rb_print_fn_t fn)
+{
+ struct rb_node *next_nd;
+ struct rb_node *nd;
+ bool quit = false;
+
+ nd = rb_first(root);
+
+ while (nd) {
+ next_nd = rb_next(nd);
+ quit = fn(nd);
+ if (quit)
+ break;
+ nd = next_nd;
+ }
+}
+
+/**
+ * bng_re_xm_print_all_ranges - debug fn to print range for one xid manager
+ *
+ * @param xm - xid manager
+ */
+static void bng_re_xm_print_all_ranges(struct xid_manager *xm)
+{
+ /* first have to find 0 elem */
+ struct xid_node_info *p;
+
+ p = xm->xm_head;
+ while (p) {
+ bng_re_xm_print_one_range(p);
+ p = p->xi_next;
+ }
+}
+
+static void bng_re_xm_print_free_tree(struct xid_manager *xm)
+{
+ bng_re_xm_rb_in_order_tree(&xm->xm_free_tree,
+ bng_re_xm_print_one_range);
+}
+
+static void bng_re_xm_print_allocated_tree(struct xid_manager *xm)
+{
+ bng_re_xm_rb_in_order_tree(&xm->xm_allocated_tree,
+ bng_re_xm_print_one_range);
+}
+
+static void bng_re_xm_print_pending_tree(struct xid_manager *xm)
+{
+ bng_re_xm_rb_in_order_tree(&xm->xm_pending_tree,
+ bng_re_xm_print_one_range);
+}
+
+void bng_re_xm_print_all_info(struct xid_manager *xm)
+{
+ struct xid_public_stats stats;
+
+ XID_PRINT(" ====================\n");
+ XID_PRINT(" FREE tree\n");
+ bng_re_xm_print_free_tree(xm);
+ XID_PRINT(" ====================\n");
+ XID_PRINT(" ALLOC tree\n");
+ bng_re_xm_print_allocated_tree(xm);
+ XID_PRINT(" ====================\n");
+ XID_PRINT(" FREE pending tree\n");
+ bng_re_xm_print_pending_tree(xm);
+ XID_PRINT(" ====================\n");
+ XID_PRINT(" MEM list\n");
+ bng_re_xm_print_all_ranges(xm);
+ XID_PRINT(" ====================\n");
+ bng_re_xm_get_stats(xm, &stats);
+ XID_PRINT(" Stats:alloc_op:%lu,allced_ids:%lu,allc_errs:%lu,allc_err_active_limit:%lu\n",
+ stats.xp_alloc_op_ctr,
+ stats.xp_alloc_count_ctr,
+ stats.xp_alloc_err_ctr,
+ stats.xp_alloc_err_active_limit_ctr);
+ XID_PRINT(" Stats: free_op:%lu, freed_ids:%lu, free_errs:%lu\n",
+ stats.xp_free_op_ctr,
+ stats.xp_free_count_ctr,
+ stats.xp_free_err_ctr);
+ XID_PRINT(" Gauge: starting id:%lu, num_ids:%lu, largest block:%lu\n",
+ stats.xp_starting_id_ge,
+ stats.xp_num_ids_ge,
+ stats.xp_largest_free_block_ge);
+ XID_PRINT(" Current: alloc_ids:%lu, pending_ids:%lu, free_ids:%lu\n",
+ stats.xp_current_alloc_ids,
+ stats.xp_current_pending_free_ids,
+ stats.xp_current_free_ids);
+ XID_PRINT(" Peak: max_alloc_ids:%lu (%.1f%% of capacity)\n",
+ stats.xp_max_alloc_ids,
+ stats.xp_num_ids_ge > 0 ?
+ (stats.xp_max_alloc_ids * 100.0) / stats.xp_num_ids_ge : 0.0);
+}
+
+/**
+ * bng_re_xm_free_node_matching_tag- optionally free a node if it matches
+ * a tag. Handle repercussions of node free.
+ *
+ * @param xm - xid manager
+ * @param tag - tag id to free
+ * @param nd - node to (maybe) free
+ *
+ * @return next node
+ */
+static struct xid_node_info *bng_re_xm_free_node_matching_tag(struct xid_manager *xm,
+ int tag,
+ struct xid_node_info *nd)
+{
+ struct xid_node_info *next;
+
+ next = nd->xi_next;
+ if (nd && nd->xi_tag == tag && nd->xi_state == X_ALLOCED) {
+ /* before we free anything- have to calculate the next. */
+ if (next) {
+ /* if the next node is not allocated,
+ * then it may be consolidated and freed. we need to
+ * go to the one after.
+ */
+ if (xm->xm_avoid_reuse) {
+ if (next->xi_state == X_FREE_PENDING)
+ next = next->xi_next;
+ } else {
+ if (next->xi_state == X_FREED)
+ next = next->xi_next;
+ }
+ }
+ bng_re_xm_trans_range_by_pointer(xm, nd);
+ }
+ return next;
+}
+
+/**
+ * bng_re_xm_free_tag - free all entries which match a tag
+ * We do this by finding the first allocated node in alloced tree;
+ * then walking the linked list of ranges to find matches.
+ *
+ * @param xm - xid manager
+ * @param tag - tag id to free
+ */
+void bng_re_xm_free_tag(struct xid_manager *xm, int tag)
+{
+ struct xid_node_info *nd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&xm->xm_lock, flags);
+ nd = (struct xid_node_info *)rb_first(&xm->xm_allocated_tree);
+ while (nd)
+ nd = bng_re_xm_free_node_matching_tag(xm, tag, nd);
+
+ spin_unlock_irqrestore(&xm->xm_lock, flags);
+}
+
+static int bng_re_xm_get_largest_free_block(struct xid_manager *xm)
+{
+ struct xid_node_info *nd;
+ int num_ids = 0;
+
+ nd = (struct xid_node_info *)rb_last(&xm->xm_free_tree);
+ if (nd)
+ num_ids = nd->xi_num_ids;
+ return num_ids;
+}
+
+/**
+ * bng_re_xm_get_stats - get the stats of this xid manager for external consumption
+ *
+ * @param xm - xid manager
+ */
+void bng_re_xm_get_stats(struct xid_manager *xm, struct xid_public_stats *stats)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&xm->xm_lock, flags);
+
+ memset(stats, 0, sizeof(*stats));
+
+ stats->xp_alloc_op_ctr = xm->xm_stats.xs_alloc_op_ctr;
+ stats->xp_alloc_count_ctr = xm->xm_stats.xs_alloc_count_ctr;
+ stats->xp_alloc_err_ctr = xm->xm_stats.xs_alloc_err_ctr;
+ stats->xp_alloc_err_active_limit_ctr = xm->xm_stats.xs_alloc_err_active_limit_ctr;
+ stats->xp_free_op_ctr = xm->xm_stats.xs_free_op_ctr;
+ stats->xp_free_count_ctr = xm->xm_stats.xs_free_count_ctr;
+ stats->xp_free_err_ctr = xm->xm_stats.xs_free_err_ctr;
+ stats->xp_starting_id_ge = xm->xm_starting_id;
+ stats->xp_num_ids_ge = xm->xm_num_ids;
+ stats->xp_largest_free_block_ge = bng_re_xm_get_largest_free_block(xm);
+ stats->xp_current_alloc_ids = xm->xm_stats.xs_current_alloc_ids;
+ stats->xp_current_pending_free_ids = xm->xm_stats.xs_current_pending_free_ids;
+ stats->xp_current_free_ids = xm->xm_stats.xs_current_free_ids;
+ stats->xp_max_alloc_ids = xm->xm_stats.xs_max_alloc_ids;
+ stats->xp_max_active_ids = (u64)xm->xm_max_active_ids;
+ stats->xp_curr_active_ids = (u64)xm->xm_curr_active_ids;
+ stats->xp_free_decr_and_remove = xm->xm_stats.xs_free_decr_and_remove;
+ stats->xp_free_decr_only = xm->xm_stats.xs_free_decr_only;
+ stats->xp_free_remove_only = xm->xm_stats.xs_free_remove_only;
+ stats->xp_current_pending_entries = (u64)(xm->xm_stats.xs_current_alloc_ids -
+ xm->xm_curr_active_ids);
+ if (stats->xp_current_pending_entries > stats->xp_current_alloc_ids)
+ stats->xp_current_pending_entries = 0;
+ spin_unlock_irqrestore(&xm->xm_lock, flags);
+}
+
+void bng_re_xm_curr_active_set(struct xid_manager *xm, int val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&xm->xm_lock, flags);
+ xm->xm_curr_active_ids = val;
+ spin_unlock_irqrestore(&xm->xm_lock, flags);
+}
+
+void bng_re_xm_curr_active_inc(struct xid_manager *xm)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&xm->xm_lock, flags);
+ xm->xm_curr_active_ids++;
+ spin_unlock_irqrestore(&xm->xm_lock, flags);
+}
+
+void bng_re_xm_curr_active_dec(struct xid_manager *xm)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&xm->xm_lock, flags);
+ xm->xm_curr_active_ids--;
+ spin_unlock_irqrestore(&xm->xm_lock, flags);
+}
+
+int bng_re_xm_curr_active_get(struct xid_manager *xm)
+{
+ unsigned long flags;
+ int v;
+
+ spin_lock_irqsave(&xm->xm_lock, flags);
+ v = xm->xm_curr_active_ids;
+ spin_unlock_irqrestore(&xm->xm_lock, flags);
+ return v;
+}
+
+int bng_re_xm_max_active_get(struct xid_manager *xm)
+{
+ unsigned long flags;
+ int v;
+
+ spin_lock_irqsave(&xm->xm_lock, flags);
+ v = xm->xm_max_active_ids;
+ spin_unlock_irqrestore(&xm->xm_lock, flags);
+ return v;
+}
+
+static void bng_re_xm_destroy_tree(struct rb_root *root)
+{
+ struct xid_node_info *kn;
+ struct rb_node *n;
+
+ while ((n = rb_first(root))) {
+ kn = rb_entry(n, struct xid_node_info, node);
+ rb_erase(n, root);
+ bng_re_xm_tree_info_del(kn);
+ }
+}
+
+void bng_re_xm_destroy(struct xid_manager *xm)
+{
+ unsigned long flags;
+
+ if (!xm)
+ return;
+ spin_lock_irqsave(&xm->xm_lock, flags);
+ bng_re_xm_destroy_tree(&xm->xm_free_tree);
+ bng_re_xm_destroy_tree(&xm->xm_allocated_tree);
+ bng_re_xm_destroy_tree(&xm->xm_pending_tree);
+ xm->xm_head = NULL;
+ spin_unlock_irqrestore(&xm->xm_lock, flags);
+ kfree(xm);
+}
+
+/* temp */
diff --git a/drivers/infiniband/hw/bng_re/xid_allocator.h b/drivers/infiniband/hw/bng_re/xid_allocator.h
new file mode 100644
index 000000000000..db9c77f63986
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/xid_allocator.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2026 Broadcom */
+
+#ifndef XID_ALLOCATOR_H
+#define XID_ALLOCATOR_H
+
+#include <linux/rbtree.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+#define CALLOC(NMEMB, SIZE) kcalloc(NMEMB, SIZE, GFP_ATOMIC)
+#define FREE(PTR) kfree(PTR)
+#define XID_PRINT(...)
+#define ASSERT(...) WARN_ON(__VA_ARGS__)
+
+typedef bool (*rb_print_fn_t)(void *);
+typedef int (*rb_compare_fn_t)(void *, void *);
+
+enum xid_range_state {
+ X_FREED = 1,
+ X_FREE_PENDING = 2,
+ X_ALLOCED = 3
+};
+
+/*Info block for an XID range */
+struct xid_node_info {
+ struct rb_node node;
+ int xi_starting_id;
+ int xi_num_ids;
+ enum xid_range_state xi_state;
+ /*
+ * Note the linked list maintains the ranges in order
+ * of id ; whether they are in allocated tree or free tree
+ */
+ struct xid_node_info *xi_next;
+ struct xid_node_info *xi_prev;
+ int xi_tag; /* tag can identify VFs or other distinctions */
+};
+
+/* stats for an xid manager */
+struct xid_private_stats {
+ u64 xs_alloc_op_ctr; /* how many alloc ops were done */
+ u64 xs_alloc_count_ctr; /* how many ids were alloced */
+ u64 xs_alloc_err_ctr; /* how many errs during alloc */
+ u64 xs_alloc_err_active_limit_ctr; /* alloc failures due to curr_active >= max_active */
+ u64 xs_free_op_ctr; /* how many free ops were done */
+ u64 xs_free_count_ctr; /* how many ids were freed */
+ u64 xs_free_err_ctr; /* errs during free */
+ u64 xs_current_alloc_ids; /* current number of allocated IDs */
+ u64 xs_current_pending_free_ids; /* current number of pending IDs */
+ u64 xs_current_free_ids; /* current number of free IDs */
+ u64 xs_max_alloc_ids; /* maximum number of allocated IDs reached */
+ /* Deferred QP XID: free operations by semantics */
+ u64 xs_free_decr_and_remove;
+ u64 xs_free_decr_only;
+ u64 xs_free_remove_only;
+};
+
+struct xid_public_stats {
+ u64 xp_alloc_op_ctr; /* how many alloc ops were done */
+ u64 xp_alloc_count_ctr; /* how many ids were alloced */
+ u64 xp_alloc_err_ctr; /* how many errs during alloc */
+ u64 xp_alloc_err_active_limit_ctr; /* alloc errs due to curr_active >= max_active */
+ u64 xp_free_op_ctr; /* how many free ops were done */
+ u64 xp_free_count_ctr; /* how many ids were freed */
+ u64 xp_free_err_ctr; /* errs during free */
+ u64 xp_starting_id_ge; /* start id */
+ u64 xp_num_ids_ge; /* num ids */
+ u64 xp_largest_free_block_ge; /* largest free block */
+ u64 xp_current_alloc_ids; /* current number of allocated IDs */
+ u64 xp_current_pending_free_ids; /* current number of pending IDs */
+ u64 xp_current_free_ids; /* current number of free IDs */
+ u64 xp_max_alloc_ids; /* maximum number of allocated IDs reached */
+ u64 xp_max_active_ids; /* cap on active IDs */
+ u64 xp_curr_active_ids; /* current active IDs */
+ u64 xp_free_decr_and_remove;
+ u64 xp_free_decr_only;
+ u64 xp_free_remove_only;
+ u64 xp_current_pending_entries; /* current_alloc_ids - curr_active_ids */
+};
+
+/* Special XID value for "decrement active count only" (no tree removal). */
+
+struct xid_manager {
+ struct rb_root xm_free_tree;
+ struct rb_root xm_allocated_tree;
+ struct rb_root xm_pending_tree;
+ struct xid_private_stats xm_stats;
+ int xm_starting_id;
+ int xm_num_ids;
+ spinlock_t xm_lock; /* lock this instance of xm */
+ struct xid_node_info *xm_head;
+ bool xm_avoid_reuse;
+ int xm_max_active_ids;
+ int xm_curr_active_ids;
+};
+
+/* public api */
+struct xid_manager *bng_re_xm_init(int start_id, int max_ids, int max_active_ids,
+ bool avoid_reuse);
+int bng_re_xm_alloc_range(struct xid_manager *xm, int num_ids, int tag);
+int bng_re_xm_free_range(struct xid_manager *xm, int id, bool decr_active);
+void bng_re_xm_print_all_info(struct xid_manager *xm);
+void bng_re_xm_free_tag(struct xid_manager *xm, int tag);
+void bng_re_xm_get_stats(struct xid_manager *xm, struct xid_public_stats *stats);
+bool bng_re_xm_is_id_allocated(struct xid_manager *xm, int id);
+void bng_re_xm_curr_active_set(struct xid_manager *xm, int val);
+void bng_re_xm_curr_active_inc(struct xid_manager *xm);
+void bng_re_xm_curr_active_dec(struct xid_manager *xm);
+int bng_re_xm_curr_active_get(struct xid_manager *xm);
+int bng_re_xm_max_active_get(struct xid_manager *xm);
+void bng_re_xm_destroy(struct xid_manager *xm);
+#define BNG_RE_XID_DECR_ONLY (-1)
+#endif
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 05/15] RDMA/bng_re: Add support verbs
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (3 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 04/15] RDMA/bng_re: Add MPC, XID management, doorbell infrastructure Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 06/15] RDMA/bng_re: Add ucontext/mmap verbs Siva Reddy Kallam
` (9 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Usman Ansari, Siva Reddy Kallam
From: Usman Ansari <usman.ansari@broadcom.com>
This patch adds below verbs.
- bng_re_query_device
- bng_re_modify_device
- bng_re_query_fw_str
- bng_re_query_port
- bng_re_get_link_layer
- bng_re_get_port_immutable
- bng_re_process_mad
- bng_re_query_pkey
Signed-off-by: Usman Ansari <usman.ansari@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/Makefile | 3 +-
drivers/infiniband/hw/bng_re/bng_dev.c | 16 ++
drivers/infiniband/hw/bng_re/bng_re.h | 11 ++
drivers/infiniband/hw/bng_re/bng_sp.h | 1 +
drivers/infiniband/hw/bng_re/bng_verbs.c | 200 +++++++++++++++++++++++
drivers/infiniband/hw/bng_re/bng_verbs.h | 30 ++++
include/uapi/rdma/bng_re-abi.h | 12 ++
include/uapi/rdma/ib_user_ioctl_verbs.h | 1 +
8 files changed, 273 insertions(+), 1 deletion(-)
create mode 100644 drivers/infiniband/hw/bng_re/bng_verbs.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_verbs.h
create mode 100644 include/uapi/rdma/bng_re-abi.h
diff --git a/drivers/infiniband/hw/bng_re/Makefile b/drivers/infiniband/hw/bng_re/Makefile
index ea0c6951728f..1b954a35993b 100644
--- a/drivers/infiniband/hw/bng_re/Makefile
+++ b/drivers/infiniband/hw/bng_re/Makefile
@@ -12,4 +12,5 @@ bng_re-y := \
bng_debugfs.o \
bng_fw.o \
bng_dev.o \
- bng_sp.o
+ bng_sp.o \
+ bng_verbs.o
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index ea6528e80ce3..05665c82b4e1 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -6,6 +6,7 @@
#include <linux/auxiliary_bus.h>
#include <rdma/ib_verbs.h>
+#include <rdma/bng_re-abi.h>
#include "bng_res.h"
#include "bng_sp.h"
@@ -17,11 +18,26 @@
#include "bng_debugfs.h"
#include "bng_re_mpc_roce.h"
#include "bng_xid.h"
+#include "bng_verbs.h"
MODULE_AUTHOR("Siva Reddy Kallam <siva.kallam@broadcom.com>");
MODULE_DESCRIPTION(BNG_RE_DESC);
MODULE_LICENSE("Dual BSD/GPL");
+static const struct ib_device_ops bng_re_dev_ops = {
+ .owner = THIS_MODULE,
+ .driver_id = RDMA_DRIVER_BNG_RE,
+ .uverbs_abi_ver = BNG_RE_ABI_VERSION,
+ .query_device = bng_re_query_device,
+ .modify_device = bng_re_modify_device,
+ .get_dev_fw_str = bng_re_query_fw_str,
+ .query_port = bng_re_query_port,
+ .get_link_layer = bng_re_get_link_layer,
+ .get_port_immutable = bng_re_get_port_immutable,
+ .process_mad = bng_re_process_mad,
+ .query_pkey = bng_re_query_pkey,
+};
+
static struct bng_re_dev *bng_re_dev_add(struct auxiliary_device *adev,
struct bnge_auxr_dev *aux_dev)
{
diff --git a/drivers/infiniband/hw/bng_re/bng_re.h b/drivers/infiniband/hw/bng_re/bng_re.h
index d6fb205d9f12..4fa5db15625f 100644
--- a/drivers/infiniband/hw/bng_re/bng_re.h
+++ b/drivers/infiniband/hw/bng_re/bng_re.h
@@ -21,6 +21,14 @@
#define BNG_RE_MIN_MSIX 2
#define BNG_RE_MAX_MSIX BNGE_MAX_ROCE_MSIX
+#define BNG_RE_PAGE_SHIFT_1G (30)
+#define BNG_RE_MAX_MR_SIZE_LOW BIT_ULL(BNG_RE_PAGE_SHIFT_1G)
+#define BNG_RE_MAX_MR_SIZE_HIGH BIT_ULL(39)
+#define BNG_RE_MAX_MR_SIZE BNG_RE_MAX_MR_SIZE_HIGH
+
+#define BNG_RE_DEFAULT_ACK_DELAY 16
+#define BNG_RE_PAGE_SIZE_SUPPORTED 0x7FFFF000 /* 4kb - 1G */
+
#define BNG_RE_CREQ_NQ_IDX 0
#define BNGE_INVALID_STATS_CTX_ID -1
@@ -250,6 +258,9 @@ struct bng_re_dev {
struct bng_mpc_ctx *mpc;
};
+#define to_bng_re_dev(ptr, member) \
+ container_of((ptr), struct bng_re_dev, member)
+
int bng_re_net_ring_alloc(struct bng_re_dev *rdev,
struct bng_re_ring_attr *ring_attr,
u16 *fw_ring_id);
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.h b/drivers/infiniband/hw/bng_re/bng_sp.h
index 91faa5ac7464..4dac43540f54 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.h
+++ b/drivers/infiniband/hw/bng_re/bng_sp.h
@@ -24,6 +24,7 @@ struct bng_re_dev_attr {
u32 max_qp_rd_atom;
u32 max_qp_init_rd_atom;
u32 max_qp_wqes;
+ u32 max_sq_wqes;
u32 max_qp_sges;
u32 max_cq;
u32 max_cq_wqes;
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.c b/drivers/infiniband/hw/bng_re/bng_verbs.c
new file mode 100644
index 000000000000..25996133c83d
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2026 Broadcom.
+
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <net/addrconf.h>
+#include <rdma/ib_cache.h>
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_umem.h>
+#include <rdma/ib_pack.h>
+#include <rdma/ib_addr.h>
+#include <rdma/ib_mad.h>
+#include <rdma/uverbs_ioctl.h>
+#include <rdma/bng_re-abi.h>
+
+#include "bng_res.h"
+#include "bng_fw.h"
+#include "bnge.h"
+#include "bnge_auxr.h"
+#include "bng_re.h"
+#include "bng_verbs.h"
+
+int bng_re_query_device(struct ib_device *ibdev,
+ struct ib_device_attr *ib_attr,
+ struct ib_udata *udata)
+{
+ struct bng_re_dev *rdev = to_bng_re_dev(ibdev, ibdev);
+ struct bng_re_dev_attr *dev_attr = rdev->dev_attr;
+
+ memset(ib_attr, 0, sizeof(*ib_attr));
+ memcpy(&ib_attr->fw_ver, dev_attr->fw_ver,
+ min(sizeof(dev_attr->fw_ver),
+ sizeof(ib_attr->fw_ver)));
+ addrconf_addr_eui48((u8 *)&ib_attr->sys_image_guid,
+ rdev->netdev->dev_addr);
+ ib_attr->max_mr_size = BNG_RE_MAX_MR_SIZE;
+ ib_attr->page_size_cap = BNG_RE_PAGE_SIZE_SUPPORTED;
+ ib_attr->vendor_id = rdev->aux_dev->pdev->vendor;
+ ib_attr->vendor_part_id = rdev->aux_dev->pdev->device;
+ ib_attr->hw_ver = rdev->aux_dev->pdev->revision;
+ ib_attr->max_qp = dev_attr->max_qp;
+ ib_attr->max_qp_wr = dev_attr->max_sq_wqes;
+ ib_attr->device_cap_flags = IB_DEVICE_CURR_QP_STATE_MOD
+ | IB_DEVICE_RC_RNR_NAK_GEN
+ | IB_DEVICE_SHUTDOWN_PORT
+ | IB_DEVICE_SYS_IMAGE_GUID
+ | IB_DEVICE_RESIZE_MAX_WR
+ | IB_DEVICE_PORT_ACTIVE_EVENT
+ | IB_DEVICE_N_NOTIFY_CQ
+ | IB_DEVICE_MEM_WINDOW
+ | IB_DEVICE_MEM_WINDOW_TYPE_2B
+ | IB_DEVICE_MEM_MGT_EXTENSIONS;
+ ib_attr->kernel_cap_flags = IBK_LOCAL_DMA_LKEY;
+ ib_attr->max_send_sge = dev_attr->max_qp_sges;
+ ib_attr->max_recv_sge = dev_attr->max_qp_sges;
+ ib_attr->max_sge_rd = dev_attr->max_qp_sges;
+ ib_attr->max_cq = dev_attr->max_cq;
+ ib_attr->max_cqe = dev_attr->max_cq_wqes;
+ ib_attr->max_mr = dev_attr->max_mr;
+ ib_attr->max_pd = dev_attr->max_pd;
+ ib_attr->max_qp_rd_atom = dev_attr->max_qp_rd_atom;
+ ib_attr->max_qp_init_rd_atom = dev_attr->max_qp_init_rd_atom;
+ ib_attr->atomic_cap = IB_ATOMIC_NONE;
+ ib_attr->masked_atomic_cap = IB_ATOMIC_NONE;
+ if (dev_attr->is_atomic) {
+ ib_attr->atomic_cap = IB_ATOMIC_GLOB;
+ ib_attr->masked_atomic_cap = IB_ATOMIC_GLOB;
+ }
+ ib_attr->max_ee_rd_atom = 0;
+ ib_attr->max_res_rd_atom = 0;
+ ib_attr->max_ee_init_rd_atom = 0;
+ ib_attr->max_ee = 0;
+ ib_attr->max_rdd = 0;
+ ib_attr->max_mw = dev_attr->max_mw;
+ ib_attr->max_raw_ipv6_qp = 0;
+ ib_attr->max_raw_ethy_qp = dev_attr->max_raw_ethy_qp;
+ ib_attr->max_mcast_grp = 0;
+ ib_attr->max_mcast_qp_attach = 0;
+ ib_attr->max_total_mcast_qp_attach = 0;
+ ib_attr->max_ah = dev_attr->max_ah;
+ ib_attr->max_srq = dev_attr->max_srq;
+ ib_attr->max_srq_wr = dev_attr->max_srq_wqes;
+ ib_attr->max_srq_sge = dev_attr->max_srq_sges;
+ ib_attr->max_fast_reg_page_list_len = MAX_PBL_LVL_1_PGS;
+ ib_attr->max_pkeys = 1;
+ ib_attr->local_ca_ack_delay = BNG_RE_DEFAULT_ACK_DELAY;
+
+ return 0;
+}
+
+int bng_re_modify_device(struct ib_device *ibdev,
+ int device_modify_mask,
+ struct ib_device_modify *device_modify)
+{
+ ibdev_dbg(ibdev, "Modify device with mask 0x%x", device_modify_mask);
+
+ if (device_modify_mask & ~IB_DEVICE_MODIFY_NODE_DESC)
+ return -EOPNOTSUPP;
+
+ if (!(device_modify_mask & IB_DEVICE_MODIFY_NODE_DESC))
+ return 0;
+
+ memcpy(ibdev->node_desc, device_modify->node_desc, IB_DEVICE_NODE_DESC_MAX);
+
+ return 0;
+}
+
+void bng_re_query_fw_str(struct ib_device *ibdev, char *str)
+{
+ struct bng_re_dev *rdev = to_bng_re_dev(ibdev, ibdev);
+
+ sprintf(str, "%d.%d.%d.%d", rdev->dev_attr->fw_ver[0],
+ rdev->dev_attr->fw_ver[1], rdev->dev_attr->fw_ver[2],
+ rdev->dev_attr->fw_ver[3]);
+}
+
+int bng_re_query_port(struct ib_device *ibdev, u32 port_num,
+ struct ib_port_attr *port_attr)
+{
+ struct bng_re_dev *rdev = to_bng_re_dev(ibdev, ibdev);
+ struct bng_re_dev_attr *dev_attr = rdev->dev_attr;
+ int rc;
+
+ memset(port_attr, 0, sizeof(*port_attr));
+
+ if (netif_running(rdev->netdev) && netif_carrier_ok(rdev->netdev)) {
+ port_attr->state = IB_PORT_ACTIVE;
+ port_attr->phys_state = IB_PORT_PHYS_STATE_LINK_UP;
+ } else {
+ port_attr->state = IB_PORT_DOWN;
+ port_attr->phys_state = IB_PORT_PHYS_STATE_DISABLED;
+ }
+ port_attr->max_mtu = IB_MTU_4096;
+ port_attr->active_mtu = iboe_get_mtu(rdev->netdev->mtu);
+ port_attr->gid_tbl_len = dev_attr->max_sgid;
+ port_attr->port_cap_flags = IB_PORT_CM_SUP | IB_PORT_REINIT_SUP |
+ IB_PORT_DEVICE_MGMT_SUP |
+ IB_PORT_VENDOR_CLASS_SUP;
+ port_attr->ip_gids = true;
+
+ port_attr->max_msg_sz = (u32)BNG_RE_MAX_MR_SIZE_LOW;
+ port_attr->bad_pkey_cntr = 0;
+ port_attr->qkey_viol_cntr = 0;
+ port_attr->pkey_tbl_len = dev_attr->max_pkey;
+ port_attr->lid = 0;
+ port_attr->sm_lid = 0;
+ port_attr->lmc = 0;
+ port_attr->max_vl_num = 4;
+ port_attr->sm_sl = 0;
+ port_attr->subnet_timeout = 0;
+ port_attr->init_type_reply = 0;
+ rc = ib_get_eth_speed(&rdev->ibdev, port_num, &port_attr->active_speed,
+ &port_attr->active_width);
+
+ return rc;
+}
+
+enum rdma_link_layer bng_re_get_link_layer(struct ib_device *ibdev, u32 port_num)
+{
+ return IB_LINK_LAYER_ETHERNET;
+}
+
+int bng_re_get_port_immutable(struct ib_device *ibdev, u32 port_num,
+ struct ib_port_immutable *immutable)
+{
+ struct ib_port_attr port_attr;
+
+ if (bng_re_query_port(ibdev, port_num, &port_attr))
+ return -EINVAL;
+
+ immutable->pkey_tbl_len = port_attr.pkey_tbl_len;
+ immutable->gid_tbl_len = port_attr.gid_tbl_len;
+ immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE;
+ immutable->core_cap_flags |= RDMA_CORE_CAP_PROT_ROCE_UDP_ENCAP;
+ immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+ return 0;
+}
+
+int bng_re_query_pkey(struct ib_device *ibdev, u32 port_num,
+ u16 index, u16 *pkey)
+{
+ if (index > 0)
+ return -EINVAL;
+
+ *pkey = IB_DEFAULT_PKEY_FULL;
+
+ return 0;
+}
+
+int bng_re_process_mad(struct ib_device *ibdev, int process_mad_flags,
+ u32 port_num, const struct ib_wc *in_wc,
+ const struct ib_grh *in_grh,
+ const struct ib_mad *in_mad, struct ib_mad *out_mad,
+ size_t *out_mad_size, u16 *out_mad_pkey_index)
+{
+ return IB_MAD_RESULT_FAILURE;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.h b/drivers/infiniband/hw/bng_re/bng_verbs.h
new file mode 100644
index 000000000000..5875ec565ea4
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2026 Broadcom.
+
+#ifndef __BNG_RE_VERBS_H__
+#define __BNG_RE_VERBS_H__
+
+#include <linux/refcount.h>
+#include <rdma/ib_verbs.h>
+
+#include "bng_sp.h"
+#include "bng_re.h"
+
+int bng_re_query_device(struct ib_device *ibdev, struct ib_device_attr *ib_attr,
+ struct ib_udata *udata);
+int bng_re_modify_device(struct ib_device *ibdev, int device_modify_mask,
+ struct ib_device_modify *device_modify);
+void bng_re_query_fw_str(struct ib_device *ibdev, char *str);
+int bng_re_query_port(struct ib_device *ibdev, u32 port_num,
+ struct ib_port_attr *port_attr);
+int bng_re_get_port_immutable(struct ib_device *ibdev, u32 port_num,
+ struct ib_port_immutable *immutable);
+int bng_re_query_pkey(struct ib_device *ibdev, u32 port_num, u16 index, u16 *pkey);
+enum rdma_link_layer bng_re_get_link_layer(struct ib_device *ibdev, u32 port_num);
+int bng_re_process_mad(struct ib_device *ibdev, int mad_flags,
+ u32 port_num, const struct ib_wc *in_wc,
+ const struct ib_grh *in_grh,
+ const struct ib_mad *in_mad, struct ib_mad *out_mad,
+ size_t *out_mad_size, u16 *out_mad_pkey_index);
+#endif /* __BNG_RE_VERBS_H__ */
+
diff --git a/include/uapi/rdma/bng_re-abi.h b/include/uapi/rdma/bng_re-abi.h
new file mode 100644
index 000000000000..cea056dc8610
--- /dev/null
+++ b/include/uapi/rdma/bng_re-abi.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */
+/* Copyright (c) 2025 Broadcom */
+
+#ifndef __BNG_RE_UVERBS_ABI_H__
+#define __BNG_RE_UVERBS_ABI_H__
+
+#include <linux/types.h>
+#include <rdma/ib_user_ioctl_cmds.h>
+
+#define BNG_RE_ABI_VERSION 1
+
+#endif /* __BNG_RE_UVERBS_ABI_H__*/
diff --git a/include/uapi/rdma/ib_user_ioctl_verbs.h b/include/uapi/rdma/ib_user_ioctl_verbs.h
index 21f86cc7bb1f..d8ffeb6fbb4a 100644
--- a/include/uapi/rdma/ib_user_ioctl_verbs.h
+++ b/include/uapi/rdma/ib_user_ioctl_verbs.h
@@ -257,6 +257,7 @@ enum rdma_driver_id {
RDMA_DRIVER_ERDMA,
RDMA_DRIVER_MANA,
RDMA_DRIVER_IONIC,
+ RDMA_DRIVER_BNG_RE,
};
enum ib_uverbs_gid_type {
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 06/15] RDMA/bng_re: Add ucontext/mmap verbs
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (4 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 05/15] RDMA/bng_re: Add support verbs Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 07/15] RDMA/bng_re: Add GID verbs Siva Reddy Kallam
` (8 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Siva Reddy Kallam
This patch adds below verbs.
- bng_re_alloc_ucontext
- bng_re_dealloc_ucontext
- bng_re_disassociate_ucontext
- bng_re_mmap
- bng_re_mmap_free
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/bng_dev.c | 9 +
drivers/infiniband/hw/bng_re/bng_re.h | 32 ++++
drivers/infiniband/hw/bng_re/bng_res.c | 94 ++++++++++
drivers/infiniband/hw/bng_re/bng_res.h | 25 +++
drivers/infiniband/hw/bng_re/bng_verbs.c | 219 +++++++++++++++++++++++
drivers/infiniband/hw/bng_re/bng_verbs.h | 22 +++
include/uapi/rdma/bng_re-abi.h | 31 ++++
7 files changed, 432 insertions(+)
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index 05665c82b4e1..401c7a0ddb85 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -36,6 +36,12 @@ static const struct ib_device_ops bng_re_dev_ops = {
.get_port_immutable = bng_re_get_port_immutable,
.process_mad = bng_re_process_mad,
.query_pkey = bng_re_query_pkey,
+ .alloc_ucontext = bng_re_alloc_ucontext,
+ .dealloc_ucontext = bng_re_dealloc_ucontext,
+ .disassociate_ucontext = bng_re_disassociate_ucontext,
+ .mmap = bng_re_mmap,
+ .mmap_free = bng_re_mmap_free,
+ INIT_RDMA_OBJ_SIZE(ib_ucontext, bng_re_ucontext, ib_uctx),
};
static struct bng_re_dev *bng_re_dev_add(struct auxiliary_device *adev,
@@ -284,6 +290,9 @@ static int bng_re_query_hwrm_version(struct bng_re_dev *rdev)
if (!cctx->hwrm_cmd_max_timeout)
cctx->hwrm_cmd_max_timeout = BNG_ROCE_FW_MAX_TIMEOUT;
+ cctx->chip_rev = ver_get_resp.chip_rev;
+ cctx->chip_metal = ver_get_resp.chip_metal;
+
return 0;
}
diff --git a/drivers/infiniband/hw/bng_re/bng_re.h b/drivers/infiniband/hw/bng_re/bng_re.h
index 4fa5db15625f..19b1e69dfb1e 100644
--- a/drivers/infiniband/hw/bng_re/bng_re.h
+++ b/drivers/infiniband/hw/bng_re/bng_re.h
@@ -172,6 +172,38 @@ struct bng_mpc_db {
struct bng_re_db_info dbinfo;
};
+enum bng_re_mmap_flag {
+ BNG_RE_MMAP_WC_DB,
+ BNG_RE_MMAP_DBR_PAGE,
+ BNG_RE_MMAP_DB_RECOVERY_PAGE,
+ BNG_RE_MMAP_DBR_PACING_BAR,
+ BNG_RE_MMAP_UC_DB,
+ BNG_RE_MMAP_TOGGLE_PAGE,
+ BNG_RE_MMAP_HDBR_BASE,
+};
+
+static inline char *bng_re_mmap_str(enum bng_re_mmap_flag mmap_flag)
+{
+ switch (mmap_flag) {
+ case BNG_RE_MMAP_WC_DB:
+ return "BNG_RE_MMAP_WC_DB";
+ case BNG_RE_MMAP_DBR_PAGE:
+ return "BNG_RE_MMAP_DBR_PAGE";
+ case BNG_RE_MMAP_DB_RECOVERY_PAGE:
+ return "BNG_RE_MMAP_DB_RECOVERY_PAGE";
+ case BNG_RE_MMAP_DBR_PACING_BAR:
+ return "BNG_RE_MMAP_DBR_PACING_BAR";
+ case BNG_RE_MMAP_UC_DB:
+ return "BNG_RE_MMAP_UC_DB";
+ case BNG_RE_MMAP_HDBR_BASE:
+ return "BNG_RE_MMAP_HDBR_BASE";
+ case BNG_RE_MMAP_TOGGLE_PAGE:
+ return "BNG_RE_MMAP_TOGGLE_PAGE";
+ default:
+ return "Unknown";
+ }
+}
+
/* NQ specific structures */
struct bng_re_nq_db {
struct bng_re_reg_desc reg;
diff --git a/drivers/infiniband/hw/bng_re/bng_res.c b/drivers/infiniband/hw/bng_re/bng_res.c
index be6d5099aa35..fe261354c4cb 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.c
+++ b/drivers/infiniband/hw/bng_re/bng_res.c
@@ -360,6 +360,7 @@ static int bng_res_alloc_dpi_tbl(struct bng_re_res *res,
}
memset((u8 *)dpit->tbl, 0xFF, bytes);
+ mutex_init(&res->dpi_tbl_lock);
dpit->priv_db = dpit->ucreg.bar_reg + dpit->ucreg.offset;
return 0;
}
@@ -379,3 +380,96 @@ int bng_res_alloc_init_tbls(struct bng_re_res *res)
return rc;
}
+
+int bng_re_alloc_dpi(struct bng_re_res *res,
+ struct bng_re_dpi *dpi,
+ void *app, enum bng_re_dpi_type type)
+{
+ struct bng_re_dpi_tbl *dpit = &res->dpi_tbl;
+ struct bng_re_reg_desc *reg;
+ u32 bit_num;
+ u64 umaddr;
+ int rc = 0;
+
+ reg = &dpit->wcreg;
+ mutex_lock(&res->dpi_tbl_lock);
+ if (!dpit->tbl) {
+ rc = -ENOMEM;
+ goto unlock;
+ }
+ bit_num = find_first_bit(dpit->tbl, dpit->max);
+ if (bit_num >= dpit->max) {
+ rc = -ENOMEM;
+ goto unlock;
+ }
+ clear_bit(bit_num, dpit->tbl);
+ dpit->app_tbl[bit_num] = app;
+ dpi->bit = bit_num;
+ dpi->dpi = bit_num + (reg->offset - dpit->ucreg.offset) / PAGE_SIZE;
+ umaddr = reg->bar_base + reg->offset + bit_num * PAGE_SIZE;
+ dpi->umdbr = umaddr;
+ switch (type) {
+ case BNG_RE_DPI_TYPE_KERNEL:
+ dpi->umdbr = dpit->ucreg.bar_base +
+ dpit->ucreg.offset + bit_num * PAGE_SIZE;
+ dpi->dbr = dpit->priv_db;
+ dpi->dpi = dpi->bit;
+ break;
+ case BNG_RE_DPI_TYPE_WC:
+ dpi->dbr = ioremap_wc(umaddr, PAGE_SIZE);
+ break;
+ default:
+ dpi->dbr = ioremap(umaddr, PAGE_SIZE);
+ break;
+ }
+
+ if (!dpi->dbr) {
+ dev_err(&res->pdev->dev, "DB remap failed, type = %d\n",
+ type);
+ rc = -ENOMEM;
+ set_bit(bit_num, dpit->tbl);
+ dpit->app_tbl[bit_num] = NULL;
+ goto unlock;
+ }
+ dpi->type = type;
+unlock:
+ mutex_unlock(&res->dpi_tbl_lock);
+ return rc;
+}
+
+int bng_re_dealloc_dpi(struct bng_re_res *res,
+ struct bng_re_dpi *dpi)
+{
+ struct bng_re_dpi_tbl *dpit = &res->dpi_tbl;
+ int rc = 0;
+
+ mutex_lock(&res->dpi_tbl_lock);
+ if (dpi->bit >= dpit->max) {
+ dev_warn(&res->pdev->dev,
+ "Invalid DPI? dpi = %d, bit = %d\n",
+ dpi->dpi, dpi->bit);
+ rc = -EINVAL;
+ goto unlock;
+ }
+ if (dpi->dpi && dpi->type != BNG_RE_DPI_TYPE_KERNEL)
+ pci_iounmap(res->pdev, dpi->dbr);
+ if (test_and_set_bit(dpi->bit, dpit->tbl)) {
+ dev_warn(&res->pdev->dev,
+ "Freeing an unused DPI? dpi = %d, bit = %d\n",
+ dpi->dpi, dpi->bit);
+ dev_warn(&res->pdev->dev,
+ "umdbr = %lld, dbr = %p, type = %d\n",
+ dpi->umdbr, dpi->dbr, dpi->type);
+ dev_warn(&res->pdev->dev,
+ "dpit max = %d, priv_db = %p\n",
+ dpit->max, dpit->priv_db);
+ rc = -EINVAL;
+ goto unlock;
+ }
+ if (dpit->app_tbl)
+ dpit->app_tbl[dpi->bit] = NULL;
+ memset(dpi, 0, sizeof(*dpi));
+unlock:
+ mutex_unlock(&res->dpi_tbl_lock);
+ return rc;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_res.h b/drivers/infiniband/hw/bng_re/bng_res.h
index 9745609190d9..11e453d4db36 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.h
+++ b/drivers/infiniband/hw/bng_re/bng_res.h
@@ -91,6 +91,8 @@ struct bng_re_chip_ctx {
u64 hwrm_intf_ver;
u16 hwrm_cmd_max_timeout;
struct bng_re_drv_modes modes;
+ u8 chip_rev;
+ u8 chip_metal;
};
struct bng_re_pbl {
@@ -156,6 +158,22 @@ struct bng_re_stats {
u32 fw_id;
};
+/* DPI types */
+enum bng_re_dpi_type {
+ BNG_RE_DPI_TYPE_KERNEL = 0,
+ BNG_RE_DPI_TYPE_UC = 1,
+ BNG_RE_DPI_TYPE_WC = 2
+};
+
+/* DPI structure */
+struct bng_re_dpi {
+ u32 dpi;
+ u32 bit;
+ u64 umdbr;
+ void __iomem *dbr;
+ enum bng_re_dpi_type type;
+};
+
/* DPI table */
struct bng_re_dpi_tbl {
void **app_tbl;
@@ -171,6 +189,8 @@ struct bng_re_res {
struct bng_re_chip_ctx *cctx;
struct bng_re_dev_attr *dattr;
struct bng_re_dpi_tbl dpi_tbl;
+ /* Serialize access to DPI table */
+ struct mutex dpi_tbl_lock;
struct xid_manager *qp_xids;
struct xid_manager *ah_xids;
};
@@ -282,4 +302,9 @@ void bng_res_unmap_db_bar(struct bng_re_res *res);
int bng_res_map_db_bar(struct bng_re_res *res);
void bng_res_free_tbls(struct bng_re_res *res);
int bng_res_alloc_init_tbls(struct bng_re_res *res);
+int bng_re_alloc_dpi(struct bng_re_res *res,
+ struct bng_re_dpi *dpi,
+ void *app, enum bng_re_dpi_type type);
+int bng_re_dealloc_dpi(struct bng_re_res *res,
+ struct bng_re_dpi *dpi);
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.c b/drivers/infiniband/hw/bng_re/bng_verbs.c
index 25996133c83d..ef2b5bd65e54 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.c
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.c
@@ -198,3 +198,222 @@ int bng_re_process_mad(struct ib_device *ibdev, int process_mad_flags,
{
return IB_MAD_RESULT_FAILURE;
}
+
+static struct bng_re_user_mmap_entry*
+bng_re_mmap_entry_insert(struct bng_re_ucontext *uctx, u64 cpu_addr,
+ dma_addr_t dma_addr, u8 user_mmap_hint, u64 *offset)
+{
+ struct bng_re_user_mmap_entry *entry;
+ int ret;
+
+ entry = kzalloc_obj(*entry, GFP_KERNEL);
+ if (!entry)
+ return NULL;
+
+ entry->cpu_addr = cpu_addr;
+ entry->dma_addr = dma_addr;
+ entry->uctx = uctx;
+ entry->mmap_flag = user_mmap_hint;
+
+ switch (user_mmap_hint) {
+ case BNG_RE_MMAP_UC_DB:
+ ret = rdma_user_mmap_entry_insert_range(&uctx->ib_uctx,
+ &entry->rdma_entry,
+ PAGE_SIZE, 0xF, U32_MAX);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret) {
+ kfree(entry);
+ return NULL;
+ }
+
+ dev_dbg(rdev_to_dev(uctx->rdev),
+ "%s %d uctx->ib_uctx 0x%lx cpu 0x%lx dma 0x%lx mmap hint %s offset 0x%lx\n",
+ __func__, __LINE__, (unsigned long)&uctx->ib_uctx,
+ (unsigned long)entry->cpu_addr, (unsigned long)entry->dma_addr,
+ bng_re_mmap_str(user_mmap_hint),
+ (unsigned long)rdma_user_mmap_get_offset(&entry->rdma_entry));
+
+ if (offset)
+ *offset = rdma_user_mmap_get_offset(&entry->rdma_entry);
+
+ return entry;
+}
+
+static int bng_re_get_user_dpi(struct bng_re_dev *rdev,
+ struct bng_re_ucontext *cntx)
+{
+ int ret = 0;
+ u8 type;
+
+ type = BNG_RE_DPI_TYPE_UC;
+ ret = bng_re_alloc_dpi(&rdev->bng_res, &cntx->dpi, cntx, type);
+ if (ret)
+ dev_err(rdev_to_dev(rdev), "Alloc doorbell page failed!");
+
+ return ret;
+}
+
+int bng_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata)
+{
+ struct ib_device *ibdev = ctx->device;
+ struct bng_re_ucontext *uctx =
+ container_of(ctx, struct bng_re_ucontext, ib_uctx);
+ struct bng_re_dev *rdev = to_bng_re_dev(ibdev, ibdev);
+ struct bng_re_dev_attr *dev_attr = rdev->dev_attr;
+ struct bng_re_user_mmap_entry *is_entry;
+ struct bng_re_uctx_resp resp = {};
+ struct bng_re_uctx_req ureq = {};
+ u32 chip_met_rev_num = 0;
+ int rc;
+
+ ibdev_dbg(ibdev, "ABI version requested %u", ibdev->ops.uverbs_abi_ver);
+
+ if (ibdev->ops.uverbs_abi_ver != BNG_RE_ABI_VERSION) {
+ ibdev_dbg(ibdev, " is different from the device %d ",
+ BNG_RE_ABI_VERSION);
+ return -EPERM;
+ }
+
+ uctx->rdev = rdev;
+
+ rc = bng_re_get_user_dpi(rdev, uctx);
+ if (rc) {
+ ibdev_dbg(ibdev, "user DPI alloc failed");
+ return rc;
+ }
+
+ resp.dpi = uctx->dpi.dpi;
+ is_entry = bng_re_mmap_entry_insert(uctx,
+ (u64)uctx->dpi.umdbr, 0,
+ BNG_RE_MMAP_UC_DB,
+ &resp.uc_db_mmap_key);
+ if (!is_entry) {
+ rc = -ENOMEM;
+ goto err_entry_insert;
+ }
+ uctx->uc_db_mmap = &is_entry->rdma_entry;
+
+ resp.comp_mask = BNG_RE_UCNTX_CMASK_HAVE_CCTX;
+ chip_met_rev_num = rdev->chip_ctx->chip_num;
+ chip_met_rev_num |= ((u32)rdev->chip_ctx->chip_rev & 0xFF) <<
+ BNG_RE_CHIP_REV_SFT;
+ chip_met_rev_num |= ((u32)rdev->chip_ctx->chip_metal & 0xFF) <<
+ BNG_RE_CHIP_MET_SFT;
+ resp.chip_id = chip_met_rev_num;
+ resp.dev_id = rdev->aux_dev->pdev->devfn;
+ resp.max_qp = rdev->dev_attr->max_qp;
+ resp.cqe_sz = sizeof(struct cq_base);
+ resp.max_cqd = dev_attr->max_cq_wqes;
+
+ if (udata->inlen >= sizeof(ureq)) {
+ rc = ib_copy_from_udata(&ureq, udata, min(udata->inlen, sizeof(ureq)));
+ if (rc)
+ goto copy_udata_fail;
+ if (ureq.comp_mask & BNG_RE_COMP_MASK_REQ_UCNTX_POW2_SUPPORT)
+ resp.comp_mask |= BNG_RE_UCNTX_CMASK_POW2_DISABLED;
+
+ if (ureq.comp_mask & BNG_RE_COMP_MASK_REQ_UCNTX_RSVD_WQE)
+ resp.comp_mask |= BNG_RE_UCNTX_CMASK_RSVD_WQE_DISABLED;
+ }
+
+ uctx->cmask = (uint64_t)resp.comp_mask;
+
+ rc = ib_copy_to_udata(udata, &resp, min(udata->outlen, sizeof(resp)));
+ if (rc) {
+ ibdev_err(ibdev, "Failed to copy user context");
+ rc = -EFAULT;
+ goto copy_udata_fail;
+ }
+
+ return 0;
+
+copy_udata_fail:
+ rdma_user_mmap_entry_remove(uctx->uc_db_mmap);
+ uctx->uc_db_mmap = NULL;
+err_entry_insert:
+ bng_re_dealloc_dpi(&rdev->bng_res, &uctx->dpi);
+ return rc;
+}
+
+void bng_re_disassociate_ucontext(struct ib_ucontext *ibcontext)
+{
+}
+
+void bng_re_dealloc_ucontext(struct ib_ucontext *ib_uctx)
+{
+ struct bng_re_ucontext *uctx = container_of(ib_uctx,
+ struct bng_re_ucontext,
+ ib_uctx);
+ struct bng_re_dev *rdev = uctx->rdev;
+ int rc;
+
+ rdma_user_mmap_entry_remove(uctx->uc_db_mmap);
+ uctx->uc_db_mmap = NULL;
+ rc = bng_re_dealloc_dpi(&rdev->bng_res, &uctx->dpi);
+ if (rc)
+ dev_err(rdev_to_dev(rdev), "Deallocate DPI failed");
+ uctx->dpi.dbr = NULL;
+}
+
+int bng_re_mmap(struct ib_ucontext *ib_uctx, struct vm_area_struct *vma)
+{
+ struct bng_re_user_mmap_entry *bng_entry;
+ struct rdma_user_mmap_entry *rdma_entry;
+ int ret = 0;
+ u64 pfn;
+
+ if (vma->vm_end - vma->vm_start != PAGE_SIZE)
+ return -EINVAL;
+
+ rdma_entry = rdma_user_mmap_entry_get(ib_uctx, vma);
+ if (!rdma_entry)
+ return -EINVAL;
+
+ bng_entry = container_of(rdma_entry, struct bng_re_user_mmap_entry,
+ rdma_entry);
+
+ switch (bng_entry->mmap_flag) {
+ case BNG_RE_MMAP_WC_DB:
+ pfn = bng_entry->cpu_addr >> PAGE_SHIFT;
+ ret = rdma_user_mmap_io(ib_uctx, vma, pfn, PAGE_SIZE,
+ pgprot_writecombine(vma->vm_page_prot),
+ rdma_entry);
+ break;
+ case BNG_RE_MMAP_UC_DB:
+ pfn = bng_entry->cpu_addr >> PAGE_SHIFT;
+ ret = rdma_user_mmap_io(ib_uctx, vma, pfn, PAGE_SIZE,
+ pgprot_noncached(vma->vm_page_prot),
+ rdma_entry);
+ break;
+ case BNG_RE_MMAP_DBR_PAGE:
+ case BNG_RE_MMAP_TOGGLE_PAGE:
+ /* Driver doesn't expect write access for user space */
+ if (vma->vm_flags & VM_WRITE)
+ ret = -EFAULT;
+ else
+ ret = vm_insert_page(vma, vma->vm_start,
+ virt_to_page((void *)bng_entry->cpu_addr));
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ rdma_user_mmap_entry_put(rdma_entry);
+ return ret;
+}
+
+void bng_re_mmap_free(struct rdma_user_mmap_entry *rdma_entry)
+{
+ struct bng_re_user_mmap_entry *bng_entry;
+
+ bng_entry = container_of(rdma_entry, struct bng_re_user_mmap_entry,
+ rdma_entry);
+
+ kfree(bng_entry);
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.h b/drivers/infiniband/hw/bng_re/bng_verbs.h
index 5875ec565ea4..565571b2f1fe 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.h
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.h
@@ -10,6 +10,22 @@
#include "bng_sp.h"
#include "bng_re.h"
+struct bng_re_ucontext {
+ struct ib_ucontext ib_uctx;
+ struct bng_re_dev *rdev;
+ struct bng_re_dpi dpi;
+ struct rdma_user_mmap_entry *uc_db_mmap;
+ u64 cmask;
+};
+
+struct bng_re_user_mmap_entry {
+ struct rdma_user_mmap_entry rdma_entry;
+ struct bng_re_ucontext *uctx;
+ dma_addr_t dma_addr;
+ u64 cpu_addr;
+ u8 mmap_flag;
+};
+
int bng_re_query_device(struct ib_device *ibdev, struct ib_device_attr *ib_attr,
struct ib_udata *udata);
int bng_re_modify_device(struct ib_device *ibdev, int device_modify_mask,
@@ -26,5 +42,11 @@ int bng_re_process_mad(struct ib_device *ibdev, int mad_flags,
const struct ib_grh *in_grh,
const struct ib_mad *in_mad, struct ib_mad *out_mad,
size_t *out_mad_size, u16 *out_mad_pkey_index);
+int bng_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata);
+void bng_re_dealloc_ucontext(struct ib_ucontext *context);
+void bng_re_disassociate_ucontext(struct ib_ucontext *ibcontext);
+int bng_re_mmap(struct ib_ucontext *context, struct vm_area_struct *vma);
+void bng_re_mmap_free(struct rdma_user_mmap_entry *rdma_entry);
+
#endif /* __BNG_RE_VERBS_H__ */
diff --git a/include/uapi/rdma/bng_re-abi.h b/include/uapi/rdma/bng_re-abi.h
index cea056dc8610..14d56a88def4 100644
--- a/include/uapi/rdma/bng_re-abi.h
+++ b/include/uapi/rdma/bng_re-abi.h
@@ -9,4 +9,35 @@
#define BNG_RE_ABI_VERSION 1
+#define BNG_RE_CHIP_NUM_SFT 0x00
+#define BNG_RE_CHIP_REV_SFT 0x10
+#define BNG_RE_CHIP_MET_SFT 0x18
+
+enum {
+ BNG_RE_UCNTX_CMASK_HAVE_CCTX = 0x1ULL,
+ BNG_RE_UCNTX_CMASK_POW2_DISABLED = 0x02ULL,
+ BNG_RE_UCNTX_CMASK_RSVD_WQE_DISABLED = 0x04ULL,
+ BNG_RE_UCNTX_CMASK_INTERNAL_QUEUE_MEMORY = 0x08ULL,
+};
+
+enum {
+ BNG_RE_COMP_MASK_REQ_UCNTX_POW2_SUPPORT = 0x01,
+ BNG_RE_COMP_MASK_REQ_UCNTX_RSVD_WQE = 0x02,
+};
+
+struct bng_re_uctx_req {
+ __aligned_u64 comp_mask;
+};
+
+struct bng_re_uctx_resp {
+ __u32 dev_id;
+ __u32 max_qp;
+ __u32 cqe_sz;
+ __u32 max_cqd;
+ __aligned_u64 comp_mask;
+ __u32 chip_id;
+ __u32 dpi;
+ __u64 uc_db_mmap_key;
+};
+
#endif /* __BNG_RE_UVERBS_ABI_H__*/
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 07/15] RDMA/bng_re: Add GID verbs
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (5 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 06/15] RDMA/bng_re: Add ucontext/mmap verbs Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 08/15] RDMA/bng_re: Add PD verbs Siva Reddy Kallam
` (7 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Sachin Holla, Siva Reddy Kallam
From: Sachin Holla <sachin.holla@broadcom.com>
This patch adds below verbs.
- bng_re_query_gid
- bng_re_add_gid
- bng_re_del_gid
Signed-off-by: Sachin Holla <sachin.holla@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/bng_dev.c | 10 ++
drivers/infiniband/hw/bng_re/bng_re.h | 2 +
drivers/infiniband/hw/bng_re/bng_res.c | 13 +-
drivers/infiniband/hw/bng_re/bng_res.h | 16 ++
drivers/infiniband/hw/bng_re/bng_sp.c | 206 +++++++++++++++++++++++
drivers/infiniband/hw/bng_re/bng_sp.h | 16 ++
drivers/infiniband/hw/bng_re/bng_verbs.c | 109 ++++++++++++
drivers/infiniband/hw/bng_re/bng_verbs.h | 10 +-
8 files changed, 380 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index 401c7a0ddb85..9ce8e1bb420a 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -41,6 +41,9 @@ static const struct ib_device_ops bng_re_dev_ops = {
.disassociate_ucontext = bng_re_disassociate_ucontext,
.mmap = bng_re_mmap,
.mmap_free = bng_re_mmap_free,
+ .query_gid = bng_re_query_gid,
+ .add_gid = bng_re_add_gid,
+ .del_gid = bng_re_del_gid,
INIT_RDMA_OBJ_SIZE(ib_ucontext, bng_re_ucontext, ib_uctx),
};
@@ -296,6 +299,11 @@ static int bng_re_query_hwrm_version(struct bng_re_dev *rdev)
return 0;
}
+static void bng_re_free_res(struct bng_re_dev *rdev)
+{
+ bng_sp_cleanup_sgid_tbl(&rdev->bng_res.sgid_tbl);
+}
+
static void bng_re_dev_uninit(struct bng_re_dev *rdev)
{
int rc;
@@ -305,6 +313,8 @@ static void bng_re_dev_uninit(struct bng_re_dev *rdev)
bng_re_debugfs_rem_pdev(rdev);
+ bng_re_free_res(rdev);
+
if (test_and_clear_bit(BNG_RE_FLAG_TBLS_ALLOC_INITED, &rdev->flags))
bng_res_free_tbls(&rdev->bng_res);
diff --git a/drivers/infiniband/hw/bng_re/bng_re.h b/drivers/infiniband/hw/bng_re/bng_re.h
index 19b1e69dfb1e..0c0e362164d7 100644
--- a/drivers/infiniband/hw/bng_re/bng_re.h
+++ b/drivers/infiniband/hw/bng_re/bng_re.h
@@ -62,6 +62,8 @@ enum {
#define ROCE_MPC_MAX_LATENCY_SEC_SLAB_INDEX BNG_RE_MPC_MAX_LATENCY_SEC_SLAB_INDEX
#define ROCE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX BNG_RE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX
+typedef u32 PORT_NUM;
+
struct bng_re_mpc_poll_info {
u64 bm_last_poll_work_poll_jiffies_start;
u64 bm_last_poll_work_poll_jiffies_end;
diff --git a/drivers/infiniband/hw/bng_re/bng_res.c b/drivers/infiniband/hw/bng_re/bng_res.c
index fe261354c4cb..a169ed2bb16b 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.c
+++ b/drivers/infiniband/hw/bng_re/bng_res.c
@@ -368,6 +368,7 @@ static int bng_res_alloc_dpi_tbl(struct bng_re_res *res,
void bng_res_free_tbls(struct bng_re_res *res)
{
bng_res_free_dpi_tbl(&res->dpi_tbl);
+ bng_sp_free_sgid_tbl(&res->sgid_tbl);
}
int bng_res_alloc_init_tbls(struct bng_re_res *res)
@@ -376,7 +377,17 @@ int bng_res_alloc_init_tbls(struct bng_re_res *res)
rc = bng_res_alloc_dpi_tbl(res, res->dattr);
if (rc)
- dev_err(&res->pdev->dev, "DPI tbl alloc failed\n");
+ return rc;
+
+ rc = bng_sp_alloc_sgid_tbl(&res->sgid_tbl,
+ res->dattr->max_sgid);
+ if (rc)
+ goto free_dpi_tbl;
+
+ return 0;
+
+free_dpi_tbl:
+ bng_res_free_dpi_tbl(&res->dpi_tbl);
return rc;
}
diff --git a/drivers/infiniband/hw/bng_re/bng_res.h b/drivers/infiniband/hw/bng_re/bng_res.h
index 11e453d4db36..58a6012998c1 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.h
+++ b/drivers/infiniband/hw/bng_re/bng_res.h
@@ -38,6 +38,18 @@
#define RCFW_DBR_PCI_BAR_REGION 2
+struct bng_re_gid {
+ u8 data[16];
+};
+
+struct bng_re_sgid_tbl {
+ u16 active;
+ u16 max; /* number of entries */
+ struct bng_re_gid_info *tbl; /* array gid entries */
+ u16 *hw_id; /* array of hw gid indices */
+ void *ctx; /* array of IB context pointers */
+};
+
struct bng_re_reg_desc {
u8 bar_id;
resource_size_t bar_base;
@@ -193,8 +205,12 @@ struct bng_re_res {
struct mutex dpi_tbl_lock;
struct xid_manager *qp_xids;
struct xid_manager *ah_xids;
+ struct bng_re_sgid_tbl sgid_tbl;
+ bool prio;
};
+#define to_bng_re_res(ptr, member) container_of(ptr, struct bng_re_res, member)
+
static inline void *bng_re_get_qe(struct bng_re_hwq *hwq,
u32 indx, u64 *pg)
{
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.c b/drivers/infiniband/hw/bng_re/bng_sp.c
index 83099e05328d..8401d1a7ff28 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.c
+++ b/drivers/infiniband/hw/bng_re/bng_sp.c
@@ -7,6 +7,15 @@
#include "bng_fw.h"
#include "bng_sp.h"
#include "bng_tlv.h"
+#include "bng_re.h"
+
+const struct bng_re_gid bng_re_gid_zero = {{0,}};
+
+static inline bool is_zero_gid(struct bng_re_gid *gid)
+{
+ return !memcmp(gid, &bng_re_gid_zero, sizeof(bng_re_gid_zero));
+}
+
static bool bng_re_is_atomic_cap(struct bng_re_rcfw *rcfw)
{
@@ -129,3 +138,200 @@ int bng_re_get_dev_attr(struct bng_re_rcfw *rcfw)
sbuf.sb, sbuf.dma_addr);
return rc;
}
+
+int bng_sp_alloc_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl, u16 size)
+{
+ u16 i;
+
+ sgid_tbl->tbl = kcalloc(size, sizeof(*sgid_tbl->tbl), GFP_KERNEL);
+ if (!sgid_tbl->tbl)
+ return -ENOMEM;
+
+ sgid_tbl->hw_id = kcalloc(size, sizeof(u16), GFP_KERNEL);
+ if (!sgid_tbl->hw_id)
+ goto free_tbl;
+
+ sgid_tbl->ctx = kcalloc(size, sizeof(void *), GFP_KERNEL);
+ if (!sgid_tbl->ctx)
+ goto free_hwid;
+
+ for (i = 0; i < size; i++)
+ sgid_tbl->tbl[i].vlan_id = 0xFFFF;
+ sgid_tbl->max = size;
+ memset(sgid_tbl->hw_id, -1, sizeof(u16) * sgid_tbl->max);
+
+ return 0;
+
+free_hwid:
+ kfree(sgid_tbl->hw_id);
+ sgid_tbl->hw_id = NULL;
+free_tbl:
+ kfree(sgid_tbl->tbl);
+ sgid_tbl->tbl = NULL;
+ return -ENOMEM;
+}
+
+void bng_sp_free_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl)
+{
+ kfree(sgid_tbl->tbl);
+ kfree(sgid_tbl->hw_id);
+ kfree(sgid_tbl->ctx);
+ memset(sgid_tbl, 0, sizeof(*sgid_tbl));
+}
+
+void bng_sp_cleanup_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl)
+{
+ void **ctx_tbl = sgid_tbl->ctx;
+ u16 i;
+
+ for (i = 0; i < sgid_tbl->max; i++) {
+ if (!is_zero_gid(&sgid_tbl->tbl[i].gid))
+ bng_sp_del_sgid(sgid_tbl, i, true);
+ kfree(ctx_tbl[i]);
+ }
+
+ memset(sgid_tbl->tbl, 0, sizeof(*sgid_tbl->tbl) * sgid_tbl->max);
+ memset(sgid_tbl->hw_id, -1, sizeof(u16) * sgid_tbl->max);
+ memset(sgid_tbl->ctx, 0, sizeof(void *) * sgid_tbl->max);
+ sgid_tbl->active = 0;
+}
+
+int bng_sp_add_sgid(struct bng_re_sgid_tbl *sgid_tbl,
+ struct bng_re_gid *gid, const u8 *smac, u16 vlan_id,
+ bool update, u32 *index, bool is_ugid, u32 stats_ctx_id)
+{
+ struct bng_re_res *res = to_bng_re_res(sgid_tbl, sgid_tbl);
+ struct bng_re_dev *rdev = to_bng_re_dev(res, bng_res);
+ int i, free_idx;
+
+ if (sgid_tbl->active == sgid_tbl->max) {
+ dev_err(&res->pdev->dev, "SGID table is full");
+ return -ENOMEM;
+ }
+
+ free_idx = sgid_tbl->max;
+ for (i = 0; i < sgid_tbl->max; i++) {
+ if (!memcmp(&sgid_tbl->tbl[i].gid, gid, sizeof(*gid)) &&
+ sgid_tbl->tbl[i].vlan_id == vlan_id) {
+ dev_dbg(&res->pdev->dev,
+ "SGID entry already exist in entry %d!",
+ i);
+ *index = i;
+ return -EALREADY;
+ }
+ if (free_idx == sgid_tbl->max &&
+ is_zero_gid(&sgid_tbl->tbl[i].gid))
+ free_idx = i;
+ }
+
+ if (free_idx == sgid_tbl->max) {
+ dev_dbg(&res->pdev->dev,
+ "SGID table is FULL but count is not MAX!");
+ return -ENOMEM;
+ }
+
+ if (update) {
+ struct creq_add_gid_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct cmdq_add_gid req = {};
+ int rc;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_ADD_GID, sizeof(req));
+
+ req.host_gid_index = cpu_to_le16(free_idx);
+ req.gid[0] = cpu_to_be32(((u32 *)gid->data)[3]);
+ req.gid[1] = cpu_to_be32(((u32 *)gid->data)[2]);
+ req.gid[2] = cpu_to_be32(((u32 *)gid->data)[1]);
+ req.gid[3] = cpu_to_be32(((u32 *)gid->data)[0]);
+
+ if (vlan_id != 0xFFFF || res->prio) {
+ if (vlan_id != 0xFFFF)
+ req.vlan = cpu_to_le16(vlan_id &
+ CMDQ_ADD_GID_VLAN_VLAN_ID_MASK);
+ req.vlan |=
+ cpu_to_le16(CMDQ_ADD_GID_VLAN_TPID_TPID_8100 |
+ CMDQ_ADD_GID_VLAN_VLAN_EN);
+ }
+
+ /* MAC in network format */
+ req.src_mac[0] = cpu_to_be16(((u16 *)smac)[0]);
+ req.src_mac[1] = cpu_to_be16(((u16 *)smac)[1]);
+ req.src_mac[2] = cpu_to_be16(((u16 *)smac)[2]);
+
+ if (is_ugid)
+ req.stats_ctx = cpu_to_le16(CMDQ_ADD_GID_STATS_CTX_STATS_CTX_VALID |
+ (u16)stats_ctx_id);
+
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL,
+ sizeof(req), sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(&rdev->rcfw, &msg);
+ if (rc)
+ return rc;
+ sgid_tbl->hw_id[free_idx] = le32_to_cpu(resp.xid);
+ }
+
+ /* Add GID to the sgid_tbl */
+ memcpy(&sgid_tbl->tbl[free_idx].gid, gid, sizeof(*gid));
+ sgid_tbl->tbl[free_idx].vlan_id = vlan_id;
+ sgid_tbl->active++;
+
+ dev_dbg(&res->pdev->dev,
+ "SGID added hw_id[0x%x] = 0x%x active = 0x%x",
+ free_idx, sgid_tbl->hw_id[free_idx], sgid_tbl->active);
+
+ *index = free_idx;
+ return 0;
+}
+
+int bng_sp_del_sgid(struct bng_re_sgid_tbl *sgid_tbl, u32 index, bool update)
+{
+ struct bng_re_res *res = to_bng_re_res(sgid_tbl, sgid_tbl);
+ u16 hw_id;
+
+ if (!sgid_tbl->active) {
+ dev_err(&res->pdev->dev, "SGID table has no active entries");
+ return -EINVAL;
+ }
+ if (index >= sgid_tbl->max) {
+ dev_err(&res->pdev->dev, "Invalid index: %d", index);
+ return -EINVAL;
+ }
+
+ /* Remove GID from the SGID table */
+ hw_id = sgid_tbl->hw_id[index];
+ if (update) {
+ struct bng_re_dev *rdev = to_bng_re_dev(res, bng_res);
+ struct creq_delete_gid_resp resp = {};
+ struct cmdq_delete_gid req = {};
+ struct bng_re_cmdqmsg msg = {};
+ int rc;
+
+ if (hw_id == 0xFFFF) {
+ dev_err(&res->pdev->dev,
+ "GID entry contains an invalid HW id\n");
+ return -EINVAL;
+ }
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_DELETE_GID, sizeof(req));
+ req.gid_index = cpu_to_le16(hw_id);
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL,
+ sizeof(req), sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(&rdev->rcfw, &msg);
+ if (rc)
+ return rc;
+ }
+
+ memcpy(&sgid_tbl->tbl[index].gid, &bng_re_gid_zero,
+ sizeof(bng_re_gid_zero));
+ sgid_tbl->tbl[index].vlan_id = 0xFFFF;
+ sgid_tbl->hw_id[index] = 0xFFFF;
+ sgid_tbl->active--;
+
+ dev_dbg(&res->pdev->dev,
+ "SGID deleted hw_id[0x%x] = 0x%x active = 0x%x\n",
+ index, hw_id, sgid_tbl->active);
+ return 0;
+}
+
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.h b/drivers/infiniband/hw/bng_re/bng_sp.h
index 4dac43540f54..05c2749419d8 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.h
+++ b/drivers/infiniband/hw/bng_re/bng_sp.h
@@ -48,5 +48,21 @@ struct bng_re_dev_attr {
u32 max_dpi;
};
+struct bng_re_gid_info {
+ struct bng_re_gid gid;
+ u16 vlan_id;
+};
+
int bng_re_get_dev_attr(struct bng_re_rcfw *rcfw);
+int bng_sp_alloc_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl, u16 size);
+
+void bng_sp_free_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl);
+
+void bng_sp_cleanup_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl);
+
+int bng_sp_add_sgid(struct bng_re_sgid_tbl *sgid_tbl,
+ struct bng_re_gid *gid, const u8 *smac, u16 vlan_id,
+ bool update, u32 *index, bool is_ugid, u32 stats_ctx_id);
+
+int bng_sp_del_sgid(struct bng_re_sgid_tbl *sgid_tbl, u32 index, bool update);
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.c b/drivers/infiniband/hw/bng_re/bng_verbs.c
index ef2b5bd65e54..59c7d95830f4 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.c
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.c
@@ -417,3 +417,112 @@ void bng_re_mmap_free(struct rdma_user_mmap_entry *rdma_entry)
kfree(bng_entry);
}
+
+static int bng_get_sgid(struct bng_re_res *res,
+ struct bng_re_sgid_tbl *sgid_tbl, int index,
+ struct bng_re_gid *gid)
+{
+ if (index > sgid_tbl->max) {
+ dev_err(&res->pdev->dev,
+ "Index %d exceeded SGID table max (%d)",
+ index, sgid_tbl->max);
+ return -EINVAL;
+ }
+ memcpy(gid, &sgid_tbl->tbl[index].gid, sizeof(*gid));
+ return 0;
+}
+
+int bng_re_query_gid(struct ib_device *ibdev, PORT_NUM port_num,
+ int index, union ib_gid *gid)
+{
+ struct bng_re_dev *rdev = to_bng_re_dev(ibdev, ibdev);
+ int rc = 0;
+
+ memset(gid, 0, sizeof(*gid));
+ rc = bng_get_sgid(&rdev->bng_res,
+ &rdev->bng_res.sgid_tbl, index,
+ (struct bng_re_gid *)gid);
+
+ return rc;
+}
+
+int bng_re_add_gid(const struct ib_gid_attr *attr, void **context)
+{
+ struct bng_re_dev *rdev = to_bng_re_dev(attr->device, ibdev);
+ struct bng_re_sgid_tbl *sgid_tbl = &rdev->bng_res.sgid_tbl;
+ struct bng_re_gid *gid = (struct bng_re_gid *)&attr->gid;
+ struct bng_re_gid_ctx *ctx, **ctx_tbl;
+ u8 mac[ETH_ALEN] = {0};
+ u16 vlan_id = 0xFFFF;
+ u32 tbl_idx = 0;
+ int rc;
+
+ rc = rdma_read_gid_l2_fields(attr, &vlan_id, mac);
+ if (rc)
+ return rc;
+
+ rc = bng_sp_add_sgid(sgid_tbl, gid, mac, vlan_id,
+ true, &tbl_idx, false, 0);
+ if (rc == -EALREADY) {
+ ctx_tbl = sgid_tbl->ctx;
+ ctx_tbl[tbl_idx]->refcnt++;
+ *context = ctx_tbl[tbl_idx];
+ return 0;
+ }
+
+ if (rc < 0) {
+ dev_err(rdev_to_dev(rdev), "Failed to add GID: rc=%#x", rc);
+ return rc;
+ }
+
+ ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx_tbl = sgid_tbl->ctx;
+ ctx->idx = tbl_idx;
+ ctx->refcnt = 1;
+ ctx_tbl[tbl_idx] = ctx;
+ *context = ctx;
+
+ return 0;
+}
+
+int bng_re_del_gid(const struct ib_gid_attr *attr, void **context)
+{
+ struct bng_re_dev *rdev = to_bng_re_dev(attr->device, ibdev);
+ struct bng_re_sgid_tbl *sgid_tbl = &rdev->bng_res.sgid_tbl;
+ struct bng_re_gid_ctx *ctx, **ctx_tbl;
+ int rc = 0;
+
+ ctx_tbl = sgid_tbl->ctx;
+ ctx = *context;
+ if (!ctx) {
+ dev_err(rdev_to_dev(rdev), "GID entry has no ctx");
+ return -EINVAL;
+ }
+ if (!sgid_tbl->active) {
+ dev_err(rdev_to_dev(rdev), "SGID table has no active entries");
+ return -EINVAL;
+ }
+ if (ctx->idx >= sgid_tbl->max || ctx != ctx_tbl[ctx->idx]) {
+ dev_err(rdev_to_dev(rdev), "GID entry is invalid ctx");
+ return -EINVAL;
+ }
+
+ /* Delete the entry from the hardware */
+ ctx->refcnt--;
+ if (!ctx->refcnt) {
+ rc = bng_sp_del_sgid(sgid_tbl, ctx->idx, true);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev),
+ "Failed to remove GID: %#x", rc);
+ } else {
+ ctx_tbl[ctx->idx] = NULL;
+ kfree(ctx);
+ }
+ }
+
+ return rc;
+}
+
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.h b/drivers/infiniband/hw/bng_re/bng_verbs.h
index 565571b2f1fe..eba56cf42672 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.h
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.h
@@ -26,6 +26,11 @@ struct bng_re_user_mmap_entry {
u8 mmap_flag;
};
+struct bng_re_gid_ctx {
+ u32 idx;
+ u32 refcnt;
+};
+
int bng_re_query_device(struct ib_device *ibdev, struct ib_device_attr *ib_attr,
struct ib_udata *udata);
int bng_re_modify_device(struct ib_device *ibdev, int device_modify_mask,
@@ -47,6 +52,9 @@ void bng_re_dealloc_ucontext(struct ib_ucontext *context);
void bng_re_disassociate_ucontext(struct ib_ucontext *ibcontext);
int bng_re_mmap(struct ib_ucontext *context, struct vm_area_struct *vma);
void bng_re_mmap_free(struct rdma_user_mmap_entry *rdma_entry);
-
+int bng_re_query_gid(struct ib_device *ibdev, u32 port_num, int index,
+ union ib_gid *gid);
+int bng_re_add_gid(const struct ib_gid_attr *attr, void **context);
+int bng_re_del_gid(const struct ib_gid_attr *attr, void **context);
#endif /* __BNG_RE_VERBS_H__ */
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 08/15] RDMA/bng_re: Add PD verbs
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (6 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 07/15] RDMA/bng_re: Add GID verbs Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 09/15] RDMA/bng_re: Add MR verbs Siva Reddy Kallam
` (6 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Suresh Rupanagudi,
Siva Reddy Kallam
From: Suresh Rupanagudi <suresh.rupanagudi@broadcom.com>
This patch adds below verbs.
- bng_re_alloc_pd
- bng_re_dealloc_pd
Signed-off-by: Suresh Rupanagudi <suresh.rupanagudi@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/bng_dev.c | 3 ++
drivers/infiniband/hw/bng_re/bng_res.c | 38 +++++++++++++++++++++++-
drivers/infiniband/hw/bng_re/bng_res.h | 9 ++++++
drivers/infiniband/hw/bng_re/bng_sp.c | 36 ++++++++++++++++++++++
drivers/infiniband/hw/bng_re/bng_sp.h | 11 +++++++
drivers/infiniband/hw/bng_re/bng_verbs.c | 34 +++++++++++++++++++++
drivers/infiniband/hw/bng_re/bng_verbs.h | 8 +++++
7 files changed, 138 insertions(+), 1 deletion(-)
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index 9ce8e1bb420a..b8a50c8cd299 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -44,6 +44,9 @@ static const struct ib_device_ops bng_re_dev_ops = {
.query_gid = bng_re_query_gid,
.add_gid = bng_re_add_gid,
.del_gid = bng_re_del_gid,
+ .alloc_pd = bng_re_alloc_pd,
+ .dealloc_pd = bng_re_dealloc_pd,
+ INIT_RDMA_OBJ_SIZE(ib_pd, bng_re_pd, ib_pd),
INIT_RDMA_OBJ_SIZE(ib_ucontext, bng_re_ucontext, ib_uctx),
};
diff --git a/drivers/infiniband/hw/bng_re/bng_res.c b/drivers/infiniband/hw/bng_re/bng_res.c
index a169ed2bb16b..8bf6e8efa182 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.c
+++ b/drivers/infiniband/hw/bng_re/bng_res.c
@@ -365,8 +365,38 @@ static int bng_res_alloc_dpi_tbl(struct bng_re_res *res,
return 0;
}
+static void bng_res_free_pd_tbl(struct bng_re_pd_tbl *pdt)
+{
+ kfree(pdt->tbl);
+ pdt->tbl = NULL;
+ pdt->max = 0;
+}
+
+static int bng_res_alloc_pd_tbl(struct bng_re_res *res,
+ struct bng_re_dev_attr *dev_attr)
+{
+ struct bng_re_pd_tbl *pdt = &res->pd_tbl;
+ u32 max = dev_attr->max_pd;
+ u32 bytes;
+
+ max++; /* one extra slot, reserved so it's never allocated */
+ bytes = BITS_TO_LONGS(max) * sizeof(unsigned long);
+
+ pdt->tbl = kmalloc(bytes, GFP_KERNEL);
+ if (!pdt->tbl)
+ return -ENOMEM;
+
+ pdt->max = max;
+ memset(pdt->tbl, 0xFF, bytes); /* mark every pd id as available */
+ clear_bit(pdt->max - 1, pdt->tbl); /* reserve the last pd id */
+ mutex_init(&res->pd_tbl_lock);
+
+ return 0;
+}
+
void bng_res_free_tbls(struct bng_re_res *res)
{
+ bng_res_free_pd_tbl(&res->pd_tbl);
bng_res_free_dpi_tbl(&res->dpi_tbl);
bng_sp_free_sgid_tbl(&res->sgid_tbl);
}
@@ -375,10 +405,14 @@ int bng_res_alloc_init_tbls(struct bng_re_res *res)
{
int rc;
- rc = bng_res_alloc_dpi_tbl(res, res->dattr);
+ rc = bng_res_alloc_pd_tbl(res, res->dattr);
if (rc)
return rc;
+ rc = bng_res_alloc_dpi_tbl(res, res->dattr);
+ if (rc)
+ goto free_pd_tbl;
+
rc = bng_sp_alloc_sgid_tbl(&res->sgid_tbl,
res->dattr->max_sgid);
if (rc)
@@ -388,6 +422,8 @@ int bng_res_alloc_init_tbls(struct bng_re_res *res)
free_dpi_tbl:
bng_res_free_dpi_tbl(&res->dpi_tbl);
+free_pd_tbl:
+ bng_res_free_pd_tbl(&res->pd_tbl);
return rc;
}
diff --git a/drivers/infiniband/hw/bng_re/bng_res.h b/drivers/infiniband/hw/bng_re/bng_res.h
index 58a6012998c1..72afe51c6bfa 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.h
+++ b/drivers/infiniband/hw/bng_re/bng_res.h
@@ -196,6 +196,12 @@ struct bng_re_dpi_tbl {
void __iomem *priv_db;
};
+/* PD table */
+struct bng_re_pd_tbl {
+ unsigned long *tbl;
+ u32 max;
+};
+
struct bng_re_res {
struct pci_dev *pdev;
struct bng_re_chip_ctx *cctx;
@@ -207,6 +213,9 @@ struct bng_re_res {
struct xid_manager *ah_xids;
struct bng_re_sgid_tbl sgid_tbl;
bool prio;
+ struct bng_re_pd_tbl pd_tbl;
+ /* Serialize access to PD table */
+ struct mutex pd_tbl_lock;
};
#define to_bng_re_res(ptr, member) container_of(ptr, struct bng_re_res, member)
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.c b/drivers/infiniband/hw/bng_re/bng_sp.c
index 8401d1a7ff28..97768a25c0e4 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.c
+++ b/drivers/infiniband/hw/bng_re/bng_sp.c
@@ -335,3 +335,39 @@ int bng_sp_del_sgid(struct bng_re_sgid_tbl *sgid_tbl, u32 index, bool update)
return 0;
}
+/* PD allocation/de-allocation functions from Gerrit */
+int bng_sp_alloc_pd(struct bng_re_res *res, struct bng_sp_pd *pd)
+{
+ struct bng_re_pd_tbl *pdt = &res->pd_tbl;
+ u32 bit_num;
+
+ mutex_lock(&res->pd_tbl_lock);
+ bit_num = find_first_bit(pdt->tbl, pdt->max);
+ if (bit_num >= pdt->max - 1) {
+ mutex_unlock(&res->pd_tbl_lock);
+ return -ENOMEM;
+ }
+
+ /* Found unused PD - mark it as allocated */
+ clear_bit(bit_num, pdt->tbl);
+ pd->id = bit_num;
+
+ mutex_unlock(&res->pd_tbl_lock);
+ return 0;
+}
+
+int bng_sp_dealloc_pd(struct bng_re_res *res,
+ struct bng_re_pd_tbl *pdt,
+ struct bng_sp_pd *pd)
+{
+ mutex_lock(&res->pd_tbl_lock);
+ if (pd->id >= pdt->max - 1 || test_and_set_bit(pd->id, pdt->tbl)) {
+ dev_warn(&res->pdev->dev, "Freeing an unused PD? pdn = %d\n",
+ pd->id);
+ mutex_unlock(&res->pd_tbl_lock);
+ return -EINVAL;
+ }
+ pd->id = pdt->max - 1;
+ mutex_unlock(&res->pd_tbl_lock);
+ return 0;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.h b/drivers/infiniband/hw/bng_re/bng_sp.h
index 05c2749419d8..136703942e1f 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.h
+++ b/drivers/infiniband/hw/bng_re/bng_sp.h
@@ -53,6 +53,10 @@ struct bng_re_gid_info {
u16 vlan_id;
};
+struct bng_sp_pd {
+ u32 id;
+};
+
int bng_re_get_dev_attr(struct bng_re_rcfw *rcfw);
int bng_sp_alloc_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl, u16 size);
@@ -65,4 +69,11 @@ int bng_sp_add_sgid(struct bng_re_sgid_tbl *sgid_tbl,
bool update, u32 *index, bool is_ugid, u32 stats_ctx_id);
int bng_sp_del_sgid(struct bng_re_sgid_tbl *sgid_tbl, u32 index, bool update);
+
+int bng_sp_alloc_pd(struct bng_re_res *res, struct bng_sp_pd *pd);
+
+int bng_sp_dealloc_pd(struct bng_re_res *res,
+ struct bng_re_pd_tbl *pdt,
+ struct bng_sp_pd *pd);
+
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.c b/drivers/infiniband/hw/bng_re/bng_verbs.c
index 59c7d95830f4..140a972a0316 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.c
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.c
@@ -526,3 +526,37 @@ int bng_re_del_gid(const struct ib_gid_attr *attr, void **context)
return rc;
}
+int bng_re_alloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata)
+{
+ struct bng_re_pd *pd = container_of(ib_pd, struct bng_re_pd, ib_pd);
+ struct ib_device *ibdev = ib_pd->device;
+ struct bng_re_dev *rdev = container_of(ibdev, struct bng_re_dev, ibdev);
+ int rc;
+
+ pd->rdev = rdev;
+
+ rc = bng_sp_alloc_pd(&rdev->bng_res, &pd->sp_pd);
+ if (rc) {
+ ibdev_err(&rdev->ibdev,
+ "Failed to allocate HW PD\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int bng_re_dealloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata)
+{
+ struct bng_re_pd *pd = container_of(ib_pd, struct bng_re_pd, ib_pd);
+ struct bng_re_dev *rdev = pd->rdev;
+ int rc;
+
+ rc = bng_sp_dealloc_pd(&rdev->bng_res, &rdev->bng_res.pd_tbl,
+ &pd->sp_pd);
+ if (rc) {
+ ibdev_err(&rdev->ibdev,
+ "Dealloc PD failed: pdid=%u, rc=%d\n",
+ pd->sp_pd.id, rc);
+ }
+ return 0;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.h b/drivers/infiniband/hw/bng_re/bng_verbs.h
index eba56cf42672..ea017f70142e 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.h
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.h
@@ -31,6 +31,12 @@ struct bng_re_gid_ctx {
u32 refcnt;
};
+struct bng_re_pd {
+ struct ib_pd ib_pd;
+ struct bng_re_dev *rdev;
+ struct bng_sp_pd sp_pd;
+};
+
int bng_re_query_device(struct ib_device *ibdev, struct ib_device_attr *ib_attr,
struct ib_udata *udata);
int bng_re_modify_device(struct ib_device *ibdev, int device_modify_mask,
@@ -56,5 +62,7 @@ int bng_re_query_gid(struct ib_device *ibdev, u32 port_num, int index,
union ib_gid *gid);
int bng_re_add_gid(const struct ib_gid_attr *attr, void **context);
int bng_re_del_gid(const struct ib_gid_attr *attr, void **context);
+int bng_re_alloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata);
+int bng_re_dealloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata);
#endif /* __BNG_RE_VERBS_H__ */
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 09/15] RDMA/bng_re: Add MR verbs
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (7 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 08/15] RDMA/bng_re: Add PD verbs Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 10/15] RDMA/bng_re: Add CQ verbs Siva Reddy Kallam
` (5 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Suresh Rupanagudi,
Siva Reddy Kallam
From: Suresh Rupanagudi <suresh.rupanagudi@broadcom.com>
This patch adds below verbs.
- bng_re_get_dma_mr
- bng_re_alloc_mr
- bng_re_map_mr_sg
- bng_re_reg_user_mr
- bng_re_reg_user_mr_dmabuf
- bng_re_dereg_mr
Signed-off-by: Suresh Rupanagudi <suresh.rupanagudi@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/bng_dev.c | 8 +
drivers/infiniband/hw/bng_re/bng_res.h | 15 +-
drivers/infiniband/hw/bng_re/bng_sp.c | 210 ++++++++++++++
drivers/infiniband/hw/bng_re/bng_sp.h | 52 ++++
drivers/infiniband/hw/bng_re/bng_verbs.c | 333 +++++++++++++++++++++++
drivers/infiniband/hw/bng_re/bng_verbs.h | 27 ++
6 files changed, 640 insertions(+), 5 deletions(-)
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index b8a50c8cd299..44c278a93778 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -46,6 +46,12 @@ static const struct ib_device_ops bng_re_dev_ops = {
.del_gid = bng_re_del_gid,
.alloc_pd = bng_re_alloc_pd,
.dealloc_pd = bng_re_dealloc_pd,
+ .get_dma_mr = bng_re_get_dma_mr,
+ .alloc_mr = bng_re_alloc_mr,
+ .map_mr_sg = bng_re_map_mr_sg,
+ .reg_user_mr = bng_re_reg_user_mr,
+ .reg_user_mr_dmabuf = bng_re_reg_user_mr_dmabuf,
+ .dereg_mr = bng_re_dereg_mr,
INIT_RDMA_OBJ_SIZE(ib_pd, bng_re_pd, ib_pd),
INIT_RDMA_OBJ_SIZE(ib_ucontext, bng_re_ucontext, ib_uctx),
};
@@ -91,6 +97,7 @@ static void bng_re_destroy_chip_ctx(struct bng_re_dev *rdev)
chip_ctx = rdev->chip_ctx;
rdev->chip_ctx = NULL;
rdev->rcfw.res = NULL;
+ rdev->bng_res.rcfw = NULL;
rdev->bng_res.cctx = NULL;
rdev->bng_res.pdev = NULL;
kfree(chip_ctx);
@@ -117,6 +124,7 @@ static int bng_re_setup_chip_ctx(struct bng_re_dev *rdev)
aux_dev = rdev->aux_dev;
rdev->bng_res.pdev = aux_dev->pdev;
rdev->rcfw.res = &rdev->bng_res;
+ rdev->bng_res.rcfw = &rdev->rcfw;
chip_ctx = kzalloc_obj(*chip_ctx);
if (!chip_ctx)
return -ENOMEM;
diff --git a/drivers/infiniband/hw/bng_re/bng_res.h b/drivers/infiniband/hw/bng_re/bng_res.h
index 72afe51c6bfa..cb0f302fc06a 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.h
+++ b/drivers/infiniband/hw/bng_re/bng_res.h
@@ -38,6 +38,8 @@
#define RCFW_DBR_PCI_BAR_REGION 2
+#define BNG_RE_FR_PMR 0x80000000
+
struct bng_re_gid {
u8 data[16];
};
@@ -123,14 +125,16 @@ enum bng_re_pbl_lvl {
enum bng_re_hwq_type {
BNG_HWQ_TYPE_CTX,
- BNG_HWQ_TYPE_QUEUE
+ BNG_HWQ_TYPE_QUEUE,
+ BNG_HWQ_TYPE_MR
};
struct bng_re_sg_info {
- u32 npages;
- u32 pgshft;
- u32 pgsize;
- bool nopte;
+ u32 npages;
+ u32 pgshft;
+ u32 pgsize;
+ bool nopte;
+ struct ib_umem *umem;
};
struct bng_re_hwq_attr {
@@ -216,6 +220,7 @@ struct bng_re_res {
struct bng_re_pd_tbl pd_tbl;
/* Serialize access to PD table */
struct mutex pd_tbl_lock;
+ struct bng_re_rcfw *rcfw;
};
#define to_bng_re_res(ptr, member) container_of(ptr, struct bng_re_res, member)
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.c b/drivers/infiniband/hw/bng_re/bng_sp.c
index 97768a25c0e4..cd33ff32845a 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.c
+++ b/drivers/infiniband/hw/bng_re/bng_sp.c
@@ -371,3 +371,213 @@ int bng_sp_dealloc_pd(struct bng_re_res *res,
mutex_unlock(&res->pd_tbl_lock);
return 0;
}
+
+/* MR allocation/de-allocation functions */
+int bng_sp_alloc_mrw(struct bng_re_res *res, struct bng_re_mrw *mrw)
+{
+ struct bng_re_rcfw *rcfw = res->rcfw;
+ struct creq_allocate_mrw_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct cmdq_allocate_mrw req = {};
+ int rc;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_ALLOCATE_MRW,
+ sizeof(req));
+
+ req.pd_id = cpu_to_le32(mrw->pd->id);
+ req.mrw_flags = mrw->type;
+
+ if ((mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_PMR &&
+ mrw->access_flags & BNG_RE_FR_PMR) ||
+ mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2A ||
+ mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2B)
+ req.access = CMDQ_ALLOCATE_MRW_ACCESS_CONSUMER_OWNED_KEY;
+ req.mrw_handle = cpu_to_le64(mrw);
+
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL, sizeof(req),
+ sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ return rc;
+
+ if (mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE1 ||
+ mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2A ||
+ mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2B)
+ mrw->rkey = le32_to_cpu(resp.xid);
+ else
+ mrw->lkey = le32_to_cpu(resp.xid);
+
+ return 0;
+}
+
+int bng_sp_alloc_fast_reg_page_list(struct bng_re_res *res,
+ struct bng_re_frpl *frpl,
+ int max_pg_ptrs)
+{
+ struct bng_re_hwq_attr hwq_attr = {};
+ struct bng_re_sg_info sginfo = {};
+ struct bng_re_hwq *hwq;
+ int pg_ptrs, pages, rc;
+
+ pg_ptrs = roundup_pow_of_two(max_pg_ptrs);
+ pages = pg_ptrs >> MAX_PBL_LVL_1_PGS_SHIFT;
+ if (!pages)
+ pages++;
+
+ if (pages > MAX_PBL_LVL_1_PGS)
+ return -ENOMEM;
+
+ sginfo.pgsize = PAGE_SIZE;
+ sginfo.nopte = true;
+
+ hwq_attr.res = res;
+ hwq_attr.depth = pg_ptrs;
+ hwq_attr.stride = PAGE_SIZE;
+ hwq_attr.sginfo = &sginfo;
+ hwq_attr.type = BNG_HWQ_TYPE_CTX;
+
+ hwq = &frpl->hwq;
+ rc = bng_re_alloc_init_hwq(hwq, &hwq_attr);
+ if (!rc)
+ frpl->max_pg_ptrs = pg_ptrs;
+
+ return rc;
+}
+
+int bng_sp_free_mrw(struct bng_re_res *res,
+ struct bng_re_mrw *mrw)
+{
+ struct creq_deallocate_key_resp resp = {};
+ struct cmdq_deallocate_key req = {};
+ struct bng_re_rcfw *rcfw = res->rcfw;
+ struct bng_re_cmdqmsg msg = {};
+ struct bng_re_hwq *hwq;
+ int rc;
+
+ if (mrw->lkey == BNG_RE_RSVD_LKEY)
+ return 0;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_DEALLOCATE_KEY,
+ sizeof(req));
+
+ req.mrw_flags = mrw->type;
+
+ if (mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE1 ||
+ mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2A ||
+ mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2B)
+ req.key = cpu_to_le32(mrw->rkey);
+ else
+ req.key = cpu_to_le32(mrw->lkey);
+
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL, sizeof(req),
+ sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ return rc;
+
+ hwq = &mrw->hwq;
+ if (hwq->max_elements)
+ bng_re_free_hwq(res, hwq);
+
+ return 0;
+}
+
+int bng_sp_free_fast_reg_page_list(struct bng_re_res *res,
+ struct bng_re_frpl *frpl)
+{
+ struct bng_re_hwq *hwq;
+
+ hwq = &frpl->hwq;
+ bng_re_free_hwq(res, hwq);
+ return 0;
+}
+
+int bng_sp_reg_mr(struct bng_re_res *res, struct bng_re_mrw *mr,
+ struct ib_umem *umem, int num_pbls, u32 buf_pg_size,
+ bool unified_mr)
+{
+ struct bng_re_rcfw *rcfw = res->rcfw;
+ struct bng_re_hwq_attr hwq_attr = {};
+ struct bng_re_sg_info sginfo = {};
+ struct creq_register_mr_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct cmdq_register_mr req = {};
+ int pages, rc;
+ u32 pg_size;
+ u16 level;
+
+ if (num_pbls) {
+ pages = roundup_pow_of_two(num_pbls);
+ /* Allocate memory for the non-leaf pages to store buf ptrs.
+ * Non-leaf pages always uses system PAGE_SIZE
+ */
+ /* Free the hwq if it already exist, must be a rereg */
+ if (mr->hwq.max_elements)
+ bng_re_free_hwq(res, &mr->hwq);
+ hwq_attr.res = res;
+ hwq_attr.depth = pages;
+ hwq_attr.stride = sizeof(dma_addr_t);
+ hwq_attr.type = BNG_HWQ_TYPE_MR;
+ hwq_attr.sginfo = &sginfo;
+ hwq_attr.sginfo->umem = umem;
+ hwq_attr.sginfo->npages = pages;
+ hwq_attr.sginfo->pgsize = buf_pg_size;
+ hwq_attr.sginfo->pgshft = ilog2(buf_pg_size);
+ rc = bng_re_alloc_init_hwq(&mr->hwq, &hwq_attr);
+ if (rc) {
+ dev_err(&res->pdev->dev,
+ "SP: Reg MR memory allocation failed\n");
+ return -ENOMEM;
+ }
+ }
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_REGISTER_MR,
+ sizeof(req));
+
+ /* Configure the request */
+ if (mr->hwq.level == BNG_PBL_LVL_MAX) {
+ /* No PBL provided, just use system PAGE_SIZE */
+ level = 0;
+ req.pbl = 0;
+ pg_size = PAGE_SIZE;
+ } else {
+ level = mr->hwq.level;
+ req.pbl = cpu_to_le64(mr->hwq.pbl[BNG_PBL_LVL_0].pg_map_arr[0]);
+ }
+ pg_size = buf_pg_size ? buf_pg_size : PAGE_SIZE;
+ req.log2_pg_size_lvl = (level << CMDQ_REGISTER_MR_LVL_SFT) |
+ ((ilog2(pg_size) <<
+ CMDQ_REGISTER_MR_LOG2_PG_SIZE_SFT) &
+ CMDQ_REGISTER_MR_LOG2_PG_SIZE_MASK);
+ req.log2_pbl_pg_size = cpu_to_le16(((ilog2(PAGE_SIZE) <<
+ CMDQ_REGISTER_MR_LOG2_PBL_PG_SIZE_SFT) &
+ CMDQ_REGISTER_MR_LOG2_PBL_PG_SIZE_MASK));
+ req.access = (mr->access_flags & BNG_RE_MR_ACCESS_MASK);
+ req.va = cpu_to_le64(mr->va);
+ req.key = cpu_to_le32(mr->lkey);
+ if (unified_mr)
+ req.key = cpu_to_le32(mr->pd->id);
+ req.flags = cpu_to_le16(mr->flags);
+ req.mr_size = cpu_to_le64(mr->total_size);
+
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL, sizeof(req),
+ sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ goto free_hwq;
+
+ if (unified_mr) {
+ mr->lkey = le32_to_cpu(resp.xid);
+ mr->rkey = mr->lkey;
+ }
+
+ return 0;
+
+free_hwq:
+ if (mr->hwq.max_elements)
+ bng_re_free_hwq(res, &mr->hwq);
+ return rc;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.h b/drivers/infiniband/hw/bng_re/bng_sp.h
index 136703942e1f..6dd3ff911548 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.h
+++ b/drivers/infiniband/hw/bng_re/bng_sp.h
@@ -4,6 +4,8 @@
#ifndef __BNG_SP_H__
#define __BNG_SP_H__
+#include <rdma/ib_umem.h>
+
#include "bng_fw.h"
#define BNG_VAR_MAX_WQE 4352
@@ -57,6 +59,45 @@ struct bng_sp_pd {
u32 id;
};
+/* Access flags */
+#define BNG_RE_ACCESS_LOCAL_WRITE BIT(0)
+#define BNG_RE_ACCESS_REMOTE_READ BIT(1)
+#define BNG_RE_ACCESS_REMOTE_WRITE BIT(2)
+#define BNG_RE_ACCESS_REMOTE_ATOMIC BIT(3)
+#define BNG_RE_ACCESS_MW_BIND BIT(4)
+#define BNG_RE_ACCESS_ZERO_BASED BIT(5)
+#define BNG_RE_ACCESS_ON_DEMAND BIT(6)
+
+struct bng_re_mrw {
+ struct bng_sp_pd *pd;
+ int type;
+ u32 access_flags;
+#define BNG_RE_MR_ACCESS_MASK 0xFF
+ u32 lkey;
+ u32 rkey;
+#define BNG_RE_RSVD_LKEY 0xFFFFFFFF
+ u64 va;
+ u64 total_size;
+ u32 npages;
+ u16 flags;
+ u64 mr_handle;
+ struct bng_re_hwq hwq;
+ u16 ctx_size_sb;
+};
+
+struct bng_re_frpl {
+ int max_pg_ptrs;
+ struct bng_re_hwq hwq;
+};
+
+struct bng_sp_mrinfo {
+ struct bng_re_mrw *mrw;
+ struct bng_re_sg_info sg;
+ u64 *ptes;
+ bool is_dma;
+ bool request_relax_order;
+};
+
int bng_re_get_dev_attr(struct bng_re_rcfw *rcfw);
int bng_sp_alloc_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl, u16 size);
@@ -76,4 +117,15 @@ int bng_sp_dealloc_pd(struct bng_re_res *res,
struct bng_re_pd_tbl *pdt,
struct bng_sp_pd *pd);
+int bng_sp_alloc_mrw(struct bng_re_res *res, struct bng_re_mrw *mrw);
+int bng_sp_alloc_fast_reg_page_list(struct bng_re_res *res,
+ struct bng_re_frpl *frpl,
+ int max_pg_ptrs);
+int bng_sp_free_fast_reg_page_list(struct bng_re_res *res,
+ struct bng_re_frpl *frpl);
+int bng_sp_free_mrw(struct bng_re_res *res, struct bng_re_mrw *mrw);
+int bng_sp_reg_mr(struct bng_re_res *res, struct bng_re_mrw *mr,
+ struct ib_umem *umem, int num_pbls, u32 buf_pg_size,
+ bool unified_mr);
+
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.c b/drivers/infiniband/hw/bng_re/bng_verbs.c
index 140a972a0316..fe227d20513a 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.c
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.c
@@ -560,3 +560,336 @@ int bng_re_dealloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata)
}
return 0;
}
+
+/* Helper functions for MR operations */
+static inline bool _is_relaxed_ordering_supported(u16 dev_cap_ext_flags2)
+{
+ return dev_cap_ext_flags2 & CREQ_QUERY_FUNC_RESP_SB_MEMORY_REGION_RO_SUPPORTED;
+}
+
+static void bng_re_check_and_set_relaxed_ordering(struct bng_re_dev *rdev,
+ struct bng_re_mrw *mr)
+{
+ struct bng_re_dev_attr *dev_attr = rdev->dev_attr;
+
+ if (_is_relaxed_ordering_supported(dev_attr->dev_cap_flags2) &&
+ pcie_relaxed_ordering_enabled(rdev->aux_dev->pdev))
+ mr->flags |= CMDQ_REGISTER_MR_FLAGS_ENABLE_RO;
+}
+
+static int __from_ib_access_flags(int iflags)
+{
+ int qflags = 0;
+
+ if (iflags & IB_ACCESS_LOCAL_WRITE)
+ qflags |= BNG_RE_ACCESS_LOCAL_WRITE;
+ if (iflags & IB_ACCESS_REMOTE_READ)
+ qflags |= BNG_RE_ACCESS_REMOTE_READ;
+ if (iflags & IB_ACCESS_REMOTE_WRITE)
+ qflags |= BNG_RE_ACCESS_REMOTE_WRITE;
+ if (iflags & IB_ACCESS_REMOTE_ATOMIC)
+ qflags |= BNG_RE_ACCESS_REMOTE_ATOMIC;
+ if (iflags & IB_ACCESS_MW_BIND)
+ qflags |= BNG_RE_ACCESS_MW_BIND;
+ if (iflags & IB_ZERO_BASED)
+ qflags |= BNG_RE_ACCESS_ZERO_BASED;
+ if (iflags & IB_ACCESS_ON_DEMAND)
+ qflags |= BNG_RE_ACCESS_ON_DEMAND;
+
+ return qflags;
+}
+
+static inline bool _is_alloc_mr_unified(struct bng_re_dev_attr *dattr)
+{
+ return dattr->dev_cap_flags &
+ CREQ_QUERY_FUNC_RESP_SB_MR_REGISTER_ALLOC;
+}
+
+struct ib_mr *bng_re_get_dma_mr(struct ib_pd *ib_pd, int mr_access_flags)
+{
+ struct bng_re_pd *pd = container_of(ib_pd, struct bng_re_pd, ib_pd);
+ struct bng_re_dev *rdev = pd->rdev;
+ struct bng_re_mr *mr;
+ struct bng_re_res *sp_res;
+ int rc;
+
+ mr = kzalloc_obj(*mr, GFP_KERNEL);
+ if (!mr)
+ return ERR_PTR(-ENOMEM);
+
+ mr->rdev = rdev;
+ mr->sp_mr.pd = &pd->sp_pd;
+ mr->sp_mr.access_flags = __from_ib_access_flags(mr_access_flags);
+ mr->sp_mr.type = CMDQ_ALLOCATE_MRW_MRW_FLAGS_PMR;
+
+ if (mr_access_flags & IB_ACCESS_RELAXED_ORDERING)
+ bng_re_check_and_set_relaxed_ordering(rdev, &mr->sp_mr);
+
+ sp_res = &rdev->bng_res;
+ rc = bng_sp_alloc_mrw(sp_res, &mr->sp_mr);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Allocate DMA MR failed: %d\n", rc);
+ goto free_mr;
+ }
+
+ mr->sp_mr.hwq.level = BNG_PBL_LVL_MAX;
+ /* Infinite length for DMA MR */
+ mr->sp_mr.total_size = -1;
+
+ /* Register the MR */
+ rc = bng_sp_reg_mr(sp_res, &mr->sp_mr, NULL, 0, PAGE_SIZE, false);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Register DMA MR failed: %d\n", rc);
+ goto free_mrw;
+ }
+
+ /* Set the keys */
+ mr->ib_mr.lkey = mr->sp_mr.lkey;
+ if (mr_access_flags & (IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_READ |
+ IB_ACCESS_REMOTE_ATOMIC))
+ mr->ib_mr.rkey = mr->ib_mr.lkey;
+
+ return &mr->ib_mr;
+
+free_mrw:
+ bng_sp_free_mrw(sp_res, &mr->sp_mr);
+free_mr:
+ kfree(mr);
+ return ERR_PTR(rc);
+}
+
+struct ib_mr *bng_re_alloc_mr(struct ib_pd *ib_pd, enum ib_mr_type type,
+ u32 max_num_sg)
+{
+ struct bng_re_pd *pd = container_of(ib_pd, struct bng_re_pd, ib_pd);
+ struct bng_re_dev *rdev = pd->rdev;
+ struct bng_re_res *sp_res;
+ struct bng_re_mr *mr = NULL;
+ int rc;
+
+ if (type != IB_MR_TYPE_MEM_REG) {
+ ibdev_dbg(&rdev->ibdev, "MR type 0x%x not supported\n", type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (max_num_sg > MAX_PBL_LVL_1_PGS) {
+ ibdev_dbg(&rdev->ibdev, "Max SG %u exceeds limit %d\n",
+ max_num_sg, MAX_PBL_LVL_1_PGS);
+ return ERR_PTR(-EINVAL);
+ }
+
+ mr = kzalloc_obj(*mr, GFP_KERNEL);
+ if (!mr)
+ return ERR_PTR(-ENOMEM);
+
+ mr->rdev = rdev;
+ mr->sp_mr.pd = &pd->sp_pd;
+ mr->sp_mr.access_flags = BNG_RE_FR_PMR;
+ mr->sp_mr.type = CMDQ_ALLOCATE_MRW_MRW_FLAGS_PMR;
+
+ sp_res = &rdev->bng_res;
+ rc = bng_sp_alloc_mrw(sp_res, &mr->sp_mr);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Allocate MR failed: %d\n", rc);
+ goto free_mr;
+ }
+
+ mr->ib_mr.lkey = mr->sp_mr.lkey;
+ mr->ib_mr.rkey = mr->ib_mr.lkey;
+
+ rc = bng_sp_alloc_fast_reg_page_list(sp_res,
+ &mr->sp_frpl, max_num_sg);
+ if (rc) {
+ ibdev_err(&rdev->ibdev,
+ "Failed to allocate HW FR page list: %d\n", rc);
+ goto free_mrw;
+ }
+
+ mr->pages = kcalloc(mr->sp_frpl.max_pg_ptrs, sizeof(*mr->pages),
+ GFP_KERNEL);
+ if (!mr->pages) {
+ rc = -ENOMEM;
+ goto free_frpl;
+ }
+
+ return &mr->ib_mr;
+
+free_frpl:
+ bng_sp_free_fast_reg_page_list(sp_res, &mr->sp_frpl);
+free_mrw:
+ bng_sp_free_mrw(sp_res, &mr->sp_mr);
+free_mr:
+ kfree(mr);
+ return ERR_PTR(rc);
+}
+
+static int bng_re_set_page(struct ib_mr *ib_mr, u64 addr)
+{
+ struct bng_re_mr *mr = container_of(ib_mr, struct bng_re_mr, ib_mr);
+
+ if (unlikely(mr->npages == mr->sp_frpl.max_pg_ptrs))
+ return -ENOMEM;
+
+ mr->pages[mr->npages++] = addr;
+ return 0;
+}
+
+int bng_re_map_mr_sg(struct ib_mr *ib_mr, struct scatterlist *sg, int sg_nents,
+ unsigned int *sg_offset)
+{
+ struct bng_re_mr *mr = container_of(ib_mr, struct bng_re_mr, ib_mr);
+
+ mr->npages = 0;
+ return ib_sg_to_pages(ib_mr, sg, sg_nents, sg_offset, bng_re_set_page);
+}
+
+static struct ib_mr *__bng_re_user_reg_mr(struct ib_pd *ib_pd, u64 length,
+ u64 virt_addr, int mr_access_flags,
+ struct ib_umem *umem)
+{
+ struct bng_re_pd *pd = container_of(ib_pd, struct bng_re_pd, ib_pd);
+ struct bng_re_dev *rdev = pd->rdev;
+ unsigned long page_size;
+ struct bng_re_mr *mr;
+ int umem_pgs, rc;
+
+ if (length > BNG_RE_MAX_MR_SIZE) {
+ ibdev_err(&rdev->ibdev, "MR Size: %llu > Max supported:%llu\n",
+ (unsigned long long)length,
+ (unsigned long long)BNG_RE_MAX_MR_SIZE);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ page_size = ib_umem_find_best_pgsz(umem, BNG_RE_PAGE_SIZE_SUPPORTED, virt_addr);
+ if (!page_size) {
+ ibdev_err(&rdev->ibdev, "umem page size unsupported!\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ mr = kzalloc_obj(*mr, GFP_KERNEL);
+ if (!mr)
+ return ERR_PTR(-ENOMEM);
+
+ mr->rdev = rdev;
+ mr->sp_mr.pd = &pd->sp_pd;
+ mr->sp_mr.access_flags = __from_ib_access_flags(mr_access_flags);
+ mr->sp_mr.type = CMDQ_ALLOCATE_MRW_MRW_FLAGS_MR;
+
+ if (!_is_alloc_mr_unified(rdev->dev_attr)) {
+ rc = bng_sp_alloc_mrw(&rdev->bng_res, &mr->sp_mr);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to allocate MR rc = %d\n", rc);
+ rc = -EIO;
+ goto free_mr;
+ }
+ /* The fixed portion of the rkey is the same as the lkey */
+ mr->ib_mr.rkey = mr->sp_mr.rkey;
+ } else {
+ mr->sp_mr.flags = CMDQ_REGISTER_MR_FLAGS_ALLOC_MR;
+ }
+ mr->ib_umem = umem;
+ mr->sp_mr.va = virt_addr;
+ mr->sp_mr.total_size = length;
+
+ if (mr_access_flags & IB_ACCESS_RELAXED_ORDERING)
+ bng_re_check_and_set_relaxed_ordering(rdev, &mr->sp_mr);
+
+ umem_pgs = ib_umem_num_dma_blocks(umem, page_size);
+ rc = bng_sp_reg_mr(&rdev->bng_res, &mr->sp_mr, umem,
+ umem_pgs, page_size,
+ _is_alloc_mr_unified(rdev->dev_attr));
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to register user MR - rc = %d\n", rc);
+ rc = -EIO;
+ goto free_mrw;
+ }
+
+ mr->ib_mr.lkey = mr->sp_mr.lkey;
+ mr->ib_mr.rkey = mr->sp_mr.lkey;
+
+ return &mr->ib_mr;
+
+free_mrw:
+ bng_sp_free_mrw(&rdev->bng_res, &mr->sp_mr);
+free_mr:
+ kfree(mr);
+ return ERR_PTR(rc);
+}
+
+struct ib_mr *bng_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length,
+ u64 virt_addr, int mr_access_flags,
+ struct ib_dmah *dmah,
+ struct ib_udata *udata)
+{
+ struct bng_re_pd *pd = container_of(ib_pd, struct bng_re_pd, ib_pd);
+ struct bng_re_dev *rdev = pd->rdev;
+ struct ib_umem *umem;
+ struct ib_mr *ib_mr;
+
+ umem = ib_umem_get_va(&rdev->ibdev, start, length, mr_access_flags);
+ if (IS_ERR(umem))
+ return ERR_CAST(umem);
+
+ ib_mr = __bng_re_user_reg_mr(ib_pd, length, virt_addr, mr_access_flags, umem);
+ if (IS_ERR(ib_mr))
+ ib_umem_release(umem);
+ return ib_mr;
+}
+
+struct ib_mr *bng_re_reg_user_mr_dmabuf(struct ib_pd *ib_pd, u64 start,
+ u64 length, u64 virt_addr, int fd,
+ int mr_access_flags,
+ struct ib_dmah *dmah,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct bng_re_pd *pd = container_of(ib_pd, struct bng_re_pd, ib_pd);
+ struct bng_re_dev *rdev = pd->rdev;
+ struct ib_umem_dmabuf *umem_dmabuf;
+ struct ib_umem *umem;
+ struct ib_mr *ib_mr;
+
+ ibdev_dbg(&rdev->ibdev, "Register user DMA-BUF MR\n");
+
+ umem_dmabuf = ib_umem_dmabuf_get_pinned(&rdev->ibdev, start, length,
+ fd, mr_access_flags);
+ if (IS_ERR(umem_dmabuf))
+ return ERR_CAST(umem_dmabuf);
+
+ umem = &umem_dmabuf->umem;
+
+ ib_mr = __bng_re_user_reg_mr(ib_pd, length, virt_addr, mr_access_flags, umem);
+ if (IS_ERR(ib_mr)) {
+ ib_umem_release(umem);
+ return ib_mr;
+ }
+
+ return ib_mr;
+}
+
+int bng_re_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata)
+{
+ struct bng_re_mr *mr = container_of(ib_mr, struct bng_re_mr, ib_mr);
+ struct bng_re_dev *rdev = mr->rdev;
+ struct bng_re_res *sp_res;
+ int rc;
+
+ sp_res = &rdev->bng_res;
+
+ rc = bng_sp_free_mrw(sp_res, &mr->sp_mr);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Dereg MR failed: %d\n", rc);
+ return rc;
+ }
+
+ /* Free page list if it was allocated for fast registration */
+ if (mr->pages) {
+ bng_sp_free_fast_reg_page_list(sp_res, &mr->sp_frpl);
+ kfree(mr->pages);
+ }
+
+ ib_umem_release(mr->ib_umem);
+
+ kfree(mr);
+
+ return 0;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.h b/drivers/infiniband/hw/bng_re/bng_verbs.h
index ea017f70142e..0ef7a7535e87 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.h
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.h
@@ -37,6 +37,16 @@ struct bng_re_pd {
struct bng_sp_pd sp_pd;
};
+struct bng_re_mr {
+ struct bng_re_dev *rdev;
+ struct ib_mr ib_mr;
+ struct ib_umem *ib_umem;
+ struct bng_re_mrw sp_mr;
+ u32 npages;
+ u64 *pages;
+ struct bng_re_frpl sp_frpl;
+};
+
int bng_re_query_device(struct ib_device *ibdev, struct ib_device_attr *ib_attr,
struct ib_udata *udata);
int bng_re_modify_device(struct ib_device *ibdev, int device_modify_mask,
@@ -64,5 +74,22 @@ int bng_re_add_gid(const struct ib_gid_attr *attr, void **context);
int bng_re_del_gid(const struct ib_gid_attr *attr, void **context);
int bng_re_alloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata);
int bng_re_dealloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata);
+
+struct ib_mr *bng_re_alloc_mr(struct ib_pd *ib_pd, enum ib_mr_type type,
+ u32 max_num_sg);
+int bng_re_map_mr_sg(struct ib_mr *ib_mr, struct scatterlist *sg, int sg_nents,
+ unsigned int *sg_offset);
+int bng_re_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata);
+struct ib_mr *bng_re_get_dma_mr(struct ib_pd *ib_pd, int mr_access_flags);
+struct ib_mr *bng_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length,
+ u64 virt_addr, int mr_access_flags,
+ struct ib_dmah *dmah,
+ struct ib_udata *udata);
+struct ib_mr *bng_re_reg_user_mr_dmabuf(struct ib_pd *ib_pd, u64 start,
+ u64 length, u64 virt_addr,
+ int fd, int mr_access_flags,
+ struct ib_dmah *dmah,
+ struct uverbs_attr_bundle *attrs);
+
#endif /* __BNG_RE_VERBS_H__ */
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 10/15] RDMA/bng_re: Add CQ verbs
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (8 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 09/15] RDMA/bng_re: Add MR verbs Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 11/15] RDMA/bng_re: Add SRQ verbs Siva Reddy Kallam
` (4 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Siva Reddy Kallam
This patch adds below verbs.
- bng_re_create_cq
- bng_re_create_user_cq
- bng_re_resize_cq
- bng_re_req_notify_cq
- bng_re_destroy_cq
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/Makefile | 1 +
drivers/infiniband/hw/bng_re/bng_dev.c | 17 ++
drivers/infiniband/hw/bng_re/bng_fp.c | 38 +++
drivers/infiniband/hw/bng_re/bng_fp.h | 8 +
drivers/infiniband/hw/bng_re/bng_re.h | 6 +
drivers/infiniband/hw/bng_re/bng_res.h | 73 ++++++
drivers/infiniband/hw/bng_re/bng_sp.c | 235 ++++++++++++++++++
drivers/infiniband/hw/bng_re/bng_sp.h | 73 ++++++
drivers/infiniband/hw/bng_re/bng_verbs.c | 303 +++++++++++++++++++++++
drivers/infiniband/hw/bng_re/bng_verbs.h | 32 +++
include/uapi/rdma/bng_re-abi.h | 21 ++
11 files changed, 807 insertions(+)
create mode 100644 drivers/infiniband/hw/bng_re/bng_fp.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_fp.h
diff --git a/drivers/infiniband/hw/bng_re/Makefile b/drivers/infiniband/hw/bng_re/Makefile
index 1b954a35993b..5e650a35b185 100644
--- a/drivers/infiniband/hw/bng_re/Makefile
+++ b/drivers/infiniband/hw/bng_re/Makefile
@@ -13,4 +13,5 @@ bng_re-y := \
bng_fw.o \
bng_dev.o \
bng_sp.o \
+ bng_fp.o \
bng_verbs.o
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index 44c278a93778..9c35c56ae583 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -52,6 +52,12 @@ static const struct ib_device_ops bng_re_dev_ops = {
.reg_user_mr = bng_re_reg_user_mr,
.reg_user_mr_dmabuf = bng_re_reg_user_mr_dmabuf,
.dereg_mr = bng_re_dereg_mr,
+ .create_cq = bng_re_create_cq,
+ .create_user_cq = bng_re_create_user_cq,
+ .resize_user_cq = bng_re_resize_cq,
+ .req_notify_cq = bng_re_req_notify_cq,
+ .destroy_cq = bng_re_destroy_cq,
+ INIT_RDMA_OBJ_SIZE(ib_cq, bng_re_cq, ib_cq),
INIT_RDMA_OBJ_SIZE(ib_pd, bng_re_pd, ib_pd),
INIT_RDMA_OBJ_SIZE(ib_ucontext, bng_re_ucontext, ib_uctx),
};
@@ -319,6 +325,10 @@ static void bng_re_dev_uninit(struct bng_re_dev *rdev)
{
int rc;
+ if (rdev->bng_res.dpi_tbl.max)
+ bng_re_dealloc_dpi(&rdev->bng_res,
+ &rdev->dpi_privileged);
+
bng_deinit_mpc(rdev);
bng_re_free_xid_tables(&rdev->bng_res);
@@ -480,6 +490,13 @@ static int bng_re_dev_init(struct bng_re_dev *rdev)
"MPC alloc-init failed rc = %#x\n", rc);
goto deinit_mpc;
}
+
+ rc = bng_re_alloc_dpi(&rdev->bng_res,
+ &rdev->dpi_privileged,
+ rdev, BNG_RE_DPI_TYPE_KERNEL);
+ if (rc)
+ goto deinit_mpc;
+
return 0;
deinit_mpc:
bng_deinit_mpc(rdev);
diff --git a/drivers/infiniband/hw/bng_re/bng_fp.c b/drivers/infiniband/hw/bng_re/bng_fp.c
new file mode 100644
index 000000000000..287729a83402
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_fp.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2026 Broadcom.
+//
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/io.h>
+
+#include "bng_res.h"
+#include "bng_sp.h"
+#include "bng_fp.h"
+
+/* CQE and NQE validation macros */
+#define CQE_CMP_VALID(hdr, pass) \
+ (!!((hdr)->cqe_type_toggle & CQ_BASE_TOGGLE) == \
+ !((pass) & BNG_RE_FLAG_EPOCH_CONS_MASK))
+
+bool bng_fp_is_cq_empty(struct bng_sp_cq *cq)
+{
+ struct cq_base *hw_cqe;
+ bool rc = true;
+
+ hw_cqe = bng_re_get_qe(&cq->hwq, cq->hwq.cons, NULL);
+
+ /* Check for Valid bit. If the CQE is valid, return false */
+ rc = !CQE_CMP_VALID(hw_cqe, cq->dbinfo.flags);
+ return rc;
+}
+
+void bng_fp_req_notify_cq(struct bng_sp_cq *cq, u32 arm_type)
+{
+ cq->dbinfo.toggle = cq->toggle;
+
+ if (arm_type)
+ bng_re_ring_db(&cq->dbinfo, arm_type);
+
+ /* Using cq->arm_state variable to track whether to issue cq handler */
+ atomic_set(&cq->arm_state, 1);
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_fp.h b/drivers/infiniband/hw/bng_re/bng_fp.h
new file mode 100644
index 000000000000..4604b4f44816
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_fp.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2026 Broadcom. */
+
+#ifndef __BNG_FP_H__
+#define __BNG_FP_H__
+bool bng_fp_is_cq_empty(struct bng_sp_cq *cq);
+void bng_fp_req_notify_cq(struct bng_sp_cq *cq, u32 arm_type);
+#endif /* __BNG_FP_H__ */
diff --git a/drivers/infiniband/hw/bng_re/bng_re.h b/drivers/infiniband/hw/bng_re/bng_re.h
index 0c0e362164d7..47b4037e124e 100644
--- a/drivers/infiniband/hw/bng_re/bng_re.h
+++ b/drivers/infiniband/hw/bng_re/bng_re.h
@@ -8,6 +8,7 @@
#include "bnge_auxr.h"
#include "bng_res.h"
#include "bng_fw.h"
+#include "bng_sp.h"
#include <rdma/ib_verbs.h>
#define BNG_RE_XID_AVOID_REUSE false
@@ -62,6 +63,8 @@ enum {
#define ROCE_MPC_MAX_LATENCY_SEC_SLAB_INDEX BNG_RE_MPC_MAX_LATENCY_SEC_SLAB_INDEX
#define ROCE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX BNG_RE_MPC_MAX_LATENCY_MSEC_SLAB_INDEX
+#define MAX_CQ_HASH_BITS (16)
+
typedef u32 PORT_NUM;
struct bng_re_mpc_poll_info {
@@ -290,6 +293,9 @@ struct bng_re_dev {
struct bng_re_stats stats_ctx;
struct bng_re_mpc_roce_creq_info mpc_roce_creq;
struct bng_mpc_ctx *mpc;
+ struct bng_re_dpi dpi_privileged;
+ struct bng_sp_cq_coal_param cq_coalescing;
+ DECLARE_HASHTABLE(cq_hash, MAX_CQ_HASH_BITS);
};
#define to_bng_re_dev(ptr, member) \
diff --git a/drivers/infiniband/hw/bng_re/bng_res.h b/drivers/infiniband/hw/bng_re/bng_res.h
index cb0f302fc06a..0f832f9bae20 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.h
+++ b/drivers/infiniband/hw/bng_re/bng_res.h
@@ -40,6 +40,11 @@
#define BNG_RE_FR_PMR 0x80000000
+enum bng_re_toggle_modes {
+ BNG_RE_CQ_TOGGLE_BIT = 0x1,
+ BNG_RE_SRQ_TOGGLE_BIT = 0x2,
+};
+
struct bng_re_gid {
u8 data[16];
};
@@ -242,6 +247,18 @@ static inline void *bng_re_get_qe(struct bng_re_hwq *hwq,
(type) | BNG_RE_DBR_VALID) << 32) | (indx) | \
(((u32)(toggle)) << (BNG_RE_DBR_TOGGLE_SHIFT)))
+static inline void bng_re_armen_db(struct bng_re_db_info *info, u32 type)
+{
+ u64 key = 0;
+ u8 toggle = 0;
+
+ if (type == DBC_DBC_TYPE_CQ_ARMENA || type == DBC_DBC_TYPE_SRQ_ARMENA)
+ toggle = info->toggle;
+ /* Index always at 0 */
+ key = BNG_RE_INIT_DBHDR(info->xid, type, 0, toggle);
+ writeq(key, info->priv_db);
+}
+
static inline void bng_re_ring_db(struct bng_re_db_info *info,
u32 type)
{
@@ -315,6 +332,59 @@ static inline bool _is_max_srq_ext_supported(u16 dev_cap_ext_flags_2)
return !!(dev_cap_ext_flags_2 & CREQ_QUERY_FUNC_RESP_SB_MAX_SRQ_EXTENDED);
}
+static inline bool _is_cq_coalescing_supported(u16 dev_cap_ext_flags2)
+{
+ return dev_cap_ext_flags2 & CREQ_QUERY_FUNC_RESP_SB_CQ_COALESCING_SUPPORTED;
+}
+
+#define ROCE_PG_SIZE_4K (4 * 1024)
+#define ROCE_PG_SIZE_8K (8 * 1024)
+#define ROCE_PG_SIZE_64K (64 * 1024)
+#define ROCE_PG_SIZE_2M (2 * 1024 * 1024)
+#define ROCE_PG_SIZE_8M (8 * 1024 * 1024)
+#define ROCE_PG_SIZE_1G (1024 * 1024 * 1024)
+
+enum bng_re_hwrm_pg_size {
+ BNG_RE_HWRM_PG_SIZE_4K = 0,
+ BNG_RE_HWRM_PG_SIZE_8K = 1,
+ BNG_RE_HWRM_PG_SIZE_64K = 2,
+ BNG_RE_HWRM_PG_SIZE_2M = 3,
+ BNG_RE_HWRM_PG_SIZE_8M = 4,
+ BNG_RE_HWRM_PG_SIZE_1G = 5,
+};
+
+static inline u8 bng_re_base_pg_size(struct bng_re_hwq *hwq)
+{
+ u8 pg_size = BNG_RE_HWRM_PG_SIZE_4K;
+ struct bng_re_pbl *pbl;
+
+ pbl = &hwq->pbl[BNG_PBL_LVL_0];
+ switch (pbl->pg_size) {
+ case ROCE_PG_SIZE_4K:
+ pg_size = BNG_RE_HWRM_PG_SIZE_4K;
+ break;
+ case ROCE_PG_SIZE_8K:
+ pg_size = BNG_RE_HWRM_PG_SIZE_8K;
+ break;
+ case ROCE_PG_SIZE_64K:
+ pg_size = BNG_RE_HWRM_PG_SIZE_64K;
+ break;
+ case ROCE_PG_SIZE_2M:
+ pg_size = BNG_RE_HWRM_PG_SIZE_2M;
+ break;
+ case ROCE_PG_SIZE_8M:
+ pg_size = BNG_RE_HWRM_PG_SIZE_8M;
+ break;
+ case ROCE_PG_SIZE_1G:
+ pg_size = BNG_RE_HWRM_PG_SIZE_1G;
+ break;
+ default:
+ break;
+ }
+
+ return pg_size;
+}
+
void bng_re_free_hwq(struct bng_re_res *res,
struct bng_re_hwq *hwq);
@@ -337,4 +407,7 @@ int bng_re_alloc_dpi(struct bng_re_res *res,
void *app, enum bng_re_dpi_type type);
int bng_re_dealloc_dpi(struct bng_re_res *res,
struct bng_re_dpi *dpi);
+void bng_re_alloc_kernel_dpi(struct bng_re_res *res,
+ struct bng_re_dpi *dpi);
+void bng_re_dealloc_kernel_dpi(struct bng_re_dpi *dpi);
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.c b/drivers/infiniband/hw/bng_re/bng_sp.c
index cd33ff32845a..04488db6d016 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.c
+++ b/drivers/infiniband/hw/bng_re/bng_sp.c
@@ -581,3 +581,238 @@ int bng_sp_reg_mr(struct bng_re_res *res, struct bng_re_mrw *mr,
bng_re_free_hwq(res, &mr->hwq);
return rc;
}
+
+int bng_sp_create_cq(struct bng_re_res *res, struct bng_sp_cq *cq)
+{
+ struct bng_re_rcfw *rcfw = res->rcfw;
+ struct bng_re_hwq_attr hwq_attr = {};
+ struct creq_create_cq_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct cmdq_create_cq req = {};
+ struct bng_re_pbl *pbl;
+ u32 coalescing = 0;
+ u32 pg_sz_lvl;
+ int rc;
+
+ if (!cq->dpi) {
+ dev_err(&rcfw->pdev->dev,
+ "FP: CREATE_CQ failed due to NULL DPI\n");
+ return -EINVAL;
+ }
+
+ cq->dbinfo.flags = 0;
+ hwq_attr.res = res;
+ hwq_attr.depth = cq->max_wqe;
+ hwq_attr.stride = sizeof(struct cq_base);
+ hwq_attr.type = BNG_HWQ_TYPE_QUEUE;
+ hwq_attr.sginfo = &cq->sg_info;
+ rc = bng_re_alloc_init_hwq(&cq->hwq, &hwq_attr);
+ if (rc)
+ return rc;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_CREATE_CQ,
+ sizeof(req));
+
+ req.dpi = cpu_to_le32(cq->dpi->dpi);
+ req.cq_handle = cpu_to_le64(cq->cq_handle);
+ req.cq_size = cpu_to_le32(cq->max_wqe);
+
+ if (_is_cq_coalescing_supported(res->dattr->dev_cap_flags2) &&
+ cq->coalescing.enable) {
+ req.flags |= cpu_to_le16(CMDQ_CREATE_CQ_FLAGS_COALESCING_VALID);
+ coalescing |= ((cq->coalescing.buf_maxtime <<
+ CMDQ_CREATE_CQ_BUF_MAXTIME_SFT) &
+ CMDQ_CREATE_CQ_BUF_MAXTIME_MASK);
+ coalescing |= ((cq->coalescing.normal_maxbuf <<
+ CMDQ_CREATE_CQ_NORMAL_MAXBUF_SFT) &
+ CMDQ_CREATE_CQ_NORMAL_MAXBUF_MASK);
+ coalescing |= ((cq->coalescing.during_maxbuf <<
+ CMDQ_CREATE_CQ_DURING_MAXBUF_SFT) &
+ CMDQ_CREATE_CQ_DURING_MAXBUF_MASK);
+ if (cq->coalescing.en_ring_idle_mode)
+ coalescing |= CMDQ_CREATE_CQ_ENABLE_RING_IDLE_MODE;
+ else
+ coalescing &= ~CMDQ_CREATE_CQ_ENABLE_RING_IDLE_MODE;
+ req.coalescing = cpu_to_le32(coalescing);
+ }
+
+ pbl = &cq->hwq.pbl[BNG_PBL_LVL_0];
+ pg_sz_lvl = (bng_re_base_pg_size(&cq->hwq) <<
+ CMDQ_CREATE_CQ_PG_SIZE_SFT);
+ pg_sz_lvl |= (cq->hwq.level & CMDQ_CREATE_CQ_LVL_MASK);
+ req.pg_size_lvl = cpu_to_le32(pg_sz_lvl);
+ req.pbl = cpu_to_le64(pbl->pg_map_arr[0]);
+ req.cq_fco_cnq_id = cpu_to_le32(
+ (cq->cnq_hw_ring_id & CMDQ_CREATE_CQ_CNQ_ID_MASK) <<
+ CMDQ_CREATE_CQ_CNQ_ID_SFT);
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL, sizeof(req),
+ sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ goto fail;
+
+ cq->id = le32_to_cpu(resp.xid);
+ cq->period = BNG_QUEUE_START_PERIOD;
+ init_waitqueue_head(&cq->waitq);
+ INIT_LIST_HEAD(&cq->sqf_head);
+ INIT_LIST_HEAD(&cq->rqf_head);
+ spin_lock_init(&cq->compl_lock);
+ spin_lock_init(&cq->flush_lock);
+
+ cq->dbinfo.hwq = &cq->hwq;
+ cq->dbinfo.xid = cq->id;
+ cq->dbinfo.db = cq->dpi->dbr;
+ cq->dbinfo.priv_db = res->dpi_tbl.priv_db;
+ cq->dbinfo.flags = 0;
+ cq->dbinfo.toggle = 0;
+ cq->dbinfo.seed = 0;
+ spin_lock_init(&cq->dbinfo.lock);
+ cq->dbinfo.res = res;
+ cq->dbinfo.is_l2 = false;
+
+ bng_re_armen_db(&cq->dbinfo, DBC_DBC_TYPE_CQ_ARMENA);
+
+ return 0;
+
+fail:
+ bng_re_free_hwq(res, &cq->hwq);
+ return rc;
+}
+
+void bng_sp_resize_cq_complete(struct bng_re_res *res, struct bng_sp_cq *cq)
+{
+ bng_re_free_hwq(res, &cq->hwq);
+ memcpy(&cq->hwq, &cq->resize_hwq, sizeof(cq->hwq));
+ cq->dbinfo.flags &= ~(1UL << BNG_RE_FLAG_EPOCH_CONS_SHIFT);
+}
+
+int bng_sp_resize_cq(struct bng_re_res *res, struct bng_sp_cq *cq,
+ int new_cqes)
+{
+ struct bng_re_hwq_attr hwq_attr = {};
+ struct bng_re_rcfw *rcfw = res->rcfw;
+ struct creq_resize_cq_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct cmdq_resize_cq req = {};
+ struct bng_re_pbl *pbl;
+ u32 pg_sz, lvl, new_sz;
+ int rc;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_RESIZE_CQ,
+ sizeof(req));
+ hwq_attr.sginfo = &cq->sg_info;
+ hwq_attr.res = res;
+ hwq_attr.depth = new_cqes;
+ hwq_attr.stride = sizeof(struct cq_base);
+ hwq_attr.type = BNG_HWQ_TYPE_QUEUE;
+ rc = bng_re_alloc_init_hwq(&cq->resize_hwq, &hwq_attr);
+ if (rc)
+ return rc;
+
+ req.cq_cid = cpu_to_le32(cq->id);
+ pbl = &cq->resize_hwq.pbl[BNG_PBL_LVL_0];
+ pg_sz = bng_re_base_pg_size(&cq->resize_hwq);
+ lvl = (cq->resize_hwq.level << CMDQ_RESIZE_CQ_LVL_SFT) &
+ CMDQ_RESIZE_CQ_LVL_MASK;
+ new_sz = (new_cqes << CMDQ_RESIZE_CQ_NEW_CQ_SIZE_SFT) &
+ CMDQ_RESIZE_CQ_NEW_CQ_SIZE_MASK;
+ req.new_cq_size_pg_size_lvl = cpu_to_le32(new_sz | pg_sz | lvl);
+ req.new_pbl = cpu_to_le64(pbl->pg_map_arr[0]);
+
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL, sizeof(req),
+ sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ bng_re_free_hwq(res, &cq->resize_hwq);
+ return rc;
+}
+
+static void clean_nq(struct bng_re_nq *nq, struct bng_sp_cq *cq)
+{
+ struct bng_re_hwq *hwq = &nq->hwq;
+ struct nq_base *nqe, **nq_ptr;
+ int budget = nq->budget;
+ uintptr_t q_handle;
+ u16 type;
+
+ spin_lock_bh(&hwq->lock);
+ /* Service the NQ until empty */
+ while (budget--) {
+ nq_ptr = (struct nq_base **)hwq->pbl_ptr;
+ nqe = &nq_ptr[NQE_PG(hwq->cons)][NQE_IDX(hwq->cons)];
+ if (!NQE_CMP_VALID(nqe, nq->nq_db.dbinfo.flags))
+ break;
+
+ /*
+ * The valid test of the entry must be done first before
+ * reading any further.
+ */
+ dma_rmb();
+
+ type = le16_to_cpu(nqe->info10_type) & NQ_BASE_TYPE_MASK;
+ switch (type) {
+ case NQ_BASE_TYPE_CQ_NOTIFICATION:
+ {
+ struct nq_cn *nqcne = (struct nq_cn *)nqe;
+
+ q_handle = le32_to_cpu(nqcne->cq_handle_low);
+ q_handle |= (u64)le32_to_cpu(nqcne->cq_handle_high)
+ << 32;
+ if ((unsigned long)cq == q_handle) {
+ nqcne->cq_handle_low = 0;
+ nqcne->cq_handle_high = 0;
+ cq->cnq_events++;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ bng_re_hwq_incr_cons(hwq->max_elements, &hwq->cons,
+ 1, &nq->nq_db.dbinfo.flags);
+ }
+ spin_unlock_bh(&hwq->lock);
+}
+
+/* Wait for receiving all NQEs for this CQ and clean the NQEs associated with
+ * this CQ.
+ */
+static void __wait_for_all_nqes(struct bng_sp_cq *cq, u16 cnq_events)
+{
+ u32 retry_cnt = 100;
+
+ while (retry_cnt--) {
+ if (cnq_events == cq->cnq_events)
+ return;
+ usleep_range(50, 100);
+ clean_nq(cq->nq, cq);
+ }
+}
+
+int bng_sp_destroy_cq(struct bng_re_res *res, struct bng_sp_cq *cq)
+{
+ struct bng_re_rcfw *rcfw = res->rcfw;
+ struct creq_destroy_cq_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct cmdq_destroy_cq req = {};
+ u16 total_cnq_events;
+ int rc;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_DESTROY_CQ,
+ sizeof(req));
+
+ req.cq_cid = cpu_to_le32(cq->id);
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL, sizeof(req),
+ sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ return rc;
+ total_cnq_events = le16_to_cpu(resp.total_cnq_events);
+ __wait_for_all_nqes(cq, total_cnq_events);
+ bng_re_free_hwq(res, &cq->hwq);
+ return 0;
+}
+
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.h b/drivers/infiniband/hw/bng_re/bng_sp.h
index 6dd3ff911548..d05151123f98 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.h
+++ b/drivers/infiniband/hw/bng_re/bng_sp.h
@@ -15,6 +15,12 @@
(!!(le32_to_cpu((hdr)->info63_v[0]) & NQ_BASE_V) == \
!((pass) & BNG_RE_FLAG_EPOCH_CONS_MASK))
+#define BNG_RE_MAX_NQE_ENTRY_SIZE sizeof(struct nq_base)
+#define NQE_MAX_IDX_PER_PG (NQE_CNT_PER_PG - 1)
+#define NQE_CNT_PER_PG (PAGE_SIZE / BNG_RE_MAX_NQE_ENTRY_SIZE)
+#define NQE_PG(x) (((x) & ~NQE_MAX_IDX_PER_PG) / NQE_CNT_PER_PG)
+#define NQE_IDX(x) ((x) & NQE_MAX_IDX_PER_PG)
+
struct bng_re_dev_attr {
#define FW_VER_ARR_LEN 4
u8 fw_ver[FW_VER_ARR_LEN];
@@ -98,6 +104,68 @@ struct bng_sp_mrinfo {
bool request_relax_order;
};
+struct bng_sp_cq_coal_param {
+ u16 buf_maxtime;
+ u8 normal_maxbuf;
+ u8 during_maxbuf;
+ u8 en_ring_idle_mode;
+ u8 enable;
+};
+
+struct bng_sp_cqe {
+ u8 status;
+ u8 type;
+ u8 opcode;
+ u32 length;
+ u16 cfa_meta;
+ u64 wr_id;
+ union {
+ u32 immdata;
+ u32 invrkey;
+ };
+ u64 qp_handle;
+ u64 mr_handle;
+ u16 flags;
+ u8 smac[6];
+ u32 src_qp;
+ u16 raweth_qp1_flags;
+ u16 raweth_qp1_errors;
+ u16 raweth_qp1_cfa_code;
+ u32 raweth_qp1_flags2;
+ u32 raweth_qp1_metadata;
+ u8 raweth_qp1_payload_offset;
+};
+
+#define BNG_QUEUE_START_PERIOD 0x01
+struct bng_sp_cq {
+ struct bng_re_dpi *dpi;
+ struct bng_re_db_info dbinfo;
+ u32 max_wqe;
+ u32 id;
+ u16 count;
+ u16 period;
+ struct bng_re_hwq hwq;
+ struct bng_re_hwq resize_hwq;
+ u32 cnq_hw_ring_id;
+ struct bng_re_nq *nq;
+ bool resize_in_progress;
+ struct bng_re_sg_info sg_info;
+ u64 cq_handle;
+ u8 toggle;
+ struct bng_re_chip_ctx *cctx;
+
+#define CQ_RESIZE_WAIT_TIME_MS 500
+ unsigned long flags;
+#define CQ_FLAGS_RESIZE_IN_PROG 1
+ wait_queue_head_t waitq;
+ struct bng_sp_cq_coal_param coalescing;
+ struct list_head sqf_head, rqf_head;
+ atomic_t arm_state;
+ spinlock_t compl_lock;
+ spinlock_t flush_lock;
+ u16 cnq_events;
+};
+
int bng_re_get_dev_attr(struct bng_re_rcfw *rcfw);
int bng_sp_alloc_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl, u16 size);
@@ -127,5 +195,10 @@ int bng_sp_free_mrw(struct bng_re_res *res, struct bng_re_mrw *mrw);
int bng_sp_reg_mr(struct bng_re_res *res, struct bng_re_mrw *mr,
struct ib_umem *umem, int num_pbls, u32 buf_pg_size,
bool unified_mr);
+int bng_sp_create_cq(struct bng_re_res *res, struct bng_sp_cq *cq);
+void bng_sp_resize_cq_complete(struct bng_re_res *res, struct bng_sp_cq *cq);
+int bng_sp_resize_cq(struct bng_re_res *res, struct bng_sp_cq *cq,
+ int new_cqes);
+int bng_sp_destroy_cq(struct bng_re_res *res, struct bng_sp_cq *cq);
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.c b/drivers/infiniband/hw/bng_re/bng_verbs.c
index fe227d20513a..b08ac20d788d 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.c
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.c
@@ -21,6 +21,7 @@
#include "bnge_auxr.h"
#include "bng_re.h"
#include "bng_verbs.h"
+#include "bng_fp.h"
int bng_re_query_device(struct ib_device *ibdev,
struct ib_device_attr *ib_attr,
@@ -893,3 +894,305 @@ int bng_re_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata)
return 0;
}
+
+static struct bng_re_nq *bng_re_get_nq(struct bng_re_dev *rdev)
+{
+ int min, indx;
+
+ mutex_lock(&rdev->nqr->load_lock);
+ for (indx = 0, min = 0; indx < (rdev->nqr->num_msix - 1); indx++) {
+ if (rdev->nqr->nq[min].load > rdev->nqr->nq[indx].load)
+ min = indx;
+ }
+ rdev->nqr->nq[min].load++;
+ mutex_unlock(&rdev->nqr->load_lock);
+
+ return &rdev->nqr->nq[min];
+}
+
+int bng_re_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct bng_re_cq *cq = container_of(ibcq, struct bng_re_cq, ib_cq);
+ struct bng_re_dev *rdev = to_bng_re_dev(ibcq->device, ibdev);
+ struct ib_udata *udata = &attrs->driver_udata;
+ struct bng_re_ucontext *uctx =
+ rdma_udata_to_drv_context(udata, struct bng_re_ucontext, ib_uctx);
+ struct bng_re_dev_attr *dev_attr = rdev->dev_attr;
+ struct bng_re_chip_ctx *cctx;
+ struct bng_re_cq_resp resp = {};
+ struct bng_re_cq_req req;
+ int rc, entries;
+
+ if (attr->flags)
+ return -EOPNOTSUPP;
+
+ /* Check for valid CQ entries */
+ if (attr->cqe > dev_attr->max_cq_wqes) {
+ ibdev_err(&rdev->ibdev,
+ "Failed to create CQ, cqe:%d max_cq_wqes:%d",
+ attr->cqe, dev_attr->max_cq_wqes);
+ return -EINVAL;
+ }
+
+ cq->rdev = rdev;
+ cctx = rdev->chip_ctx;
+ cq->sp_cq.cq_handle = (u64)(unsigned long)(&cq->sp_cq);
+
+ entries = bng_re_init_depth(attr->cqe + 1, uctx);
+
+ rc = ib_copy_from_udata(&req, udata, sizeof(req));
+ if (rc)
+ return rc;
+
+ cq->umem = ib_umem_get_va(&rdev->ibdev, req.cq_va,
+ entries * sizeof(struct cq_base),
+ IB_ACCESS_LOCAL_WRITE);
+ if (IS_ERR(cq->umem))
+ return PTR_ERR(cq->umem);
+
+ cq->sp_cq.sg_info.umem = cq->umem;
+ cq->sp_cq.dpi = &uctx->dpi;
+ cq->sp_cq.max_wqe = entries;
+ cq->sp_cq.coalescing = rdev->cq_coalescing;
+ cq->sp_cq.nq = bng_re_get_nq(rdev);
+ cq->sp_cq.cnq_hw_ring_id = cq->sp_cq.nq->ring_id;
+ rc = bng_sp_create_cq(&rdev->bng_res, &cq->sp_cq);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to create HW CQ");
+ goto udata_cq_fail;
+ }
+
+ cq->ib_cq.cqe = entries;
+ cq->cq_period = cq->sp_cq.period;
+
+ spin_lock_init(&cq->cq_lock);
+
+ if (cctx->modes.toggle_bits & BNG_RE_CQ_TOGGLE_BIT) {
+ /* Allocate a page */
+ cq->uctx_cq_page = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!cq->uctx_cq_page) {
+ rc = -ENOMEM;
+ goto uctx_cq_page_fail;
+ }
+ resp.comp_mask |= BNG_RE_CQ_TOGGLE_PAGE_SUPPORT;
+ }
+ resp.cqid = cq->sp_cq.id;
+ resp.tail = cq->sp_cq.hwq.cons;
+ resp.phase = cq->sp_cq.period;
+ resp.rsvd = 0;
+ rc = ib_copy_to_udata(udata, &resp, min(sizeof(resp), udata->outlen));
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to copy CQ udata");
+ goto destroy_cq;
+ }
+
+ return 0;
+
+destroy_cq:
+ if (cq->uctx_cq_page)
+ free_page((unsigned long)cq->uctx_cq_page);
+uctx_cq_page_fail:
+ bng_sp_destroy_cq(&rdev->bng_res, &cq->sp_cq);
+ ib_umem_release(cq->umem);
+udata_cq_fail:
+ kfree(cq->cql);
+ return rc;
+}
+
+int bng_re_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
+ struct uverbs_attr_bundle *attrs)
+{
+ struct bng_re_cq *cq = container_of(ibcq, struct bng_re_cq, ib_cq);
+ struct bng_re_dev *rdev = to_bng_re_dev(ibcq->device, ibdev);
+ struct bng_re_dev_attr *dev_attr = rdev->dev_attr;
+ int rc, entries;
+
+ if (attr->flags)
+ return -EOPNOTSUPP;
+
+ /* Check for valid CQ entries */
+ if (attr->cqe > dev_attr->max_cq_wqes) {
+ ibdev_err(&rdev->ibdev,
+ "Failed to create CQ, cqe:%d max_cq_wqes:%d",
+ attr->cqe, dev_attr->max_cq_wqes);
+ return -EINVAL;
+ }
+
+ cq->rdev = rdev;
+ cq->sp_cq.cq_handle = (u64)(unsigned long)(&cq->sp_cq);
+
+ cq->max_cql = min_t(u32, entries, MAX_CQL_PER_POLL);
+ cq->cql = kcalloc(cq->max_cql, sizeof(struct bng_sp_cqe),
+ GFP_KERNEL);
+ if (!cq->cql)
+ return -ENOMEM;
+
+ cq->sp_cq.sg_info.pgsize = SZ_4K;
+ cq->sp_cq.dpi = &rdev->dpi_privileged;
+ cq->sp_cq.max_wqe = cq->max_cql;
+ cq->sp_cq.coalescing = rdev->cq_coalescing;
+ cq->sp_cq.nq = bng_re_get_nq(rdev);
+ cq->sp_cq.cnq_hw_ring_id = cq->sp_cq.nq->ring_id;
+
+ rc = bng_sp_create_cq(&rdev->bng_res, &cq->sp_cq);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to create HW CQ");
+ goto create_cq_fail;
+ }
+
+ cq->ib_cq.cqe = cq->max_cql;
+ cq->cq_period = cq->sp_cq.period;
+
+ spin_lock_init(&cq->cq_lock);
+
+ return 0;
+
+create_cq_fail:
+ kfree(cq->cql);
+ return rc;
+}
+
+int bng_re_resize_cq(struct ib_cq *ibcq, unsigned int cqe, struct ib_udata *udata)
+{
+ struct bng_re_sg_info sg_info = {};
+ struct bng_re_dpi *orig_dpi = NULL;
+ struct bng_re_dev_attr *dev_attr;
+ struct bng_re_ucontext *uctx = NULL;
+ struct bng_re_resize_cq_req req;
+ struct bng_re_dev *rdev;
+ struct bng_re_cq *cq;
+ int rc, entries;
+
+ cq = container_of(ibcq, struct bng_re_cq, ib_cq);
+ rdev = cq->rdev;
+ dev_attr = rdev->dev_attr;
+ if (!ibcq->uobject) {
+ ibdev_err(&rdev->ibdev, "Kernel CQ Resize not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (cq->resize_umem) {
+ ibdev_err(&rdev->ibdev, "Resize CQ %#x failed - Busy",
+ cq->sp_cq.id);
+ return -EBUSY;
+ }
+
+ /* Check the requested cq depth out of supported depth */
+ if (cqe < 1 || cqe > dev_attr->max_cq_wqes) {
+ ibdev_err(&rdev->ibdev, "Resize CQ %#x failed - out of range cqe %d",
+ cq->sp_cq.id, cqe);
+ return -EINVAL;
+ }
+
+ uctx = rdma_udata_to_drv_context(udata, struct bng_re_ucontext, ib_uctx);
+ entries = bng_re_init_depth(cqe + 1, uctx);
+ if (entries > dev_attr->max_cq_wqes + 1)
+ entries = dev_attr->max_cq_wqes + 1;
+
+ /* uverbs consumer */
+ if (ib_copy_from_udata(&req, udata, sizeof(req))) {
+ rc = -EFAULT;
+ goto fail;
+ }
+
+ cq->resize_umem = ib_umem_get_va(&rdev->ibdev, req.cq_va,
+ entries * sizeof(struct cq_base),
+ IB_ACCESS_LOCAL_WRITE);
+ if (IS_ERR(cq->resize_umem)) {
+ rc = PTR_ERR(cq->resize_umem);
+ ibdev_err(&rdev->ibdev, "%s: ib_umem_get_va failed! rc = %pe\n",
+ __func__, cq->resize_umem);
+ cq->resize_umem = NULL;
+ goto fail;
+ }
+ cq->resize_cqe = entries;
+ memcpy(&sg_info, &cq->sp_cq.sg_info, sizeof(sg_info));
+ orig_dpi = cq->sp_cq.dpi;
+
+ cq->sp_cq.sg_info.umem = cq->resize_umem;
+ cq->sp_cq.sg_info.pgsize = PAGE_SIZE;
+ cq->sp_cq.sg_info.pgshft = PAGE_SHIFT;
+ cq->sp_cq.dpi = &uctx->dpi;
+
+ rc = bng_sp_resize_cq(&rdev->bng_res, &cq->sp_cq, entries);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Resize HW CQ %#x failed!",
+ cq->sp_cq.id);
+ goto fail;
+ }
+
+ cq->ib_cq.cqe = cq->resize_cqe;
+
+ return 0;
+
+fail:
+ if (cq->resize_umem) {
+ ib_umem_release(cq->resize_umem);
+ cq->resize_umem = NULL;
+ cq->resize_cqe = 0;
+ memcpy(&cq->sp_cq.sg_info, &sg_info, sizeof(sg_info));
+ cq->sp_cq.dpi = orig_dpi;
+ }
+ return rc;
+}
+
+int bng_re_req_notify_cq(struct ib_cq *ib_cq,
+ enum ib_cq_notify_flags ib_cqn_flags)
+{
+ struct bng_re_cq *cq = container_of(ib_cq, struct bng_re_cq, ib_cq);
+ int type = 0, rc = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cq->cq_lock, flags);
+ /* Trigger on the very next completion */
+ if (ib_cqn_flags & IB_CQ_NEXT_COMP)
+ type = DBC_DBC_TYPE_CQ_ARMALL;
+ /* Trigger on the next solicited completion */
+ else if (ib_cqn_flags & IB_CQ_SOLICITED)
+ type = DBC_DBC_TYPE_CQ_ARMSE;
+
+ /* Poll to see if there are missed events */
+ if ((ib_cqn_flags & IB_CQ_REPORT_MISSED_EVENTS) &&
+ !(bng_fp_is_cq_empty(&cq->sp_cq))) {
+ rc = 1;
+ goto mis_events;
+ }
+ bng_fp_req_notify_cq(&cq->sp_cq, type);
+
+mis_events:
+ spin_unlock_irqrestore(&cq->cq_lock, flags);
+ return rc;
+}
+
+static void bng_re_put_nq(struct bng_re_dev *rdev, struct bng_re_nq *nq)
+{
+ mutex_lock(&rdev->nqr->load_lock);
+ nq->load--;
+ mutex_unlock(&rdev->nqr->load_lock);
+}
+
+int bng_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata)
+{
+ struct bng_re_chip_ctx *cctx;
+ struct bng_re_nq *nq;
+ struct bng_re_dev *rdev;
+ struct bng_re_cq *cq;
+
+ cq = container_of(ib_cq, struct bng_re_cq, ib_cq);
+ rdev = cq->rdev;
+ nq = cq->sp_cq.nq;
+ cctx = rdev->chip_ctx;
+
+ if (cctx->modes.toggle_bits & BNG_RE_CQ_TOGGLE_BIT) {
+ free_page((unsigned long)cq->uctx_cq_page);
+ hash_del(&cq->hash_entry);
+ }
+ bng_sp_destroy_cq(&rdev->bng_res, &cq->sp_cq);
+
+ bng_re_put_nq(rdev, nq);
+ ib_umem_release(cq->umem);
+
+ kfree(cq->cql);
+ return 0;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.h b/drivers/infiniband/hw/bng_re/bng_verbs.h
index 0ef7a7535e87..839967c73f08 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.h
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.h
@@ -47,6 +47,30 @@ struct bng_re_mr {
struct bng_re_frpl sp_frpl;
};
+struct bng_re_cq {
+ struct ib_cq ib_cq;
+ struct bng_re_dev *rdev;
+ spinlock_t cq_lock;
+ u16 cq_count;
+ u16 cq_period;
+ struct bng_sp_cq sp_cq;
+ struct bng_sp_cqe *cql;
+ #define MAX_CQL_PER_POLL 1024
+ u32 max_cql;
+ struct ib_umem *umem;
+ struct ib_umem *resize_umem;
+ int resize_cqe;
+ void *uctx_cq_page;
+ struct hlist_node hash_entry;
+};
+
+static inline u32 bng_re_init_depth(u32 ent, struct bng_re_ucontext *uctx)
+{
+ /* roundup_pow_of_two(0) is undefined (UBSAN: shift by 64 on 64-bit). */
+ return uctx ? (uctx->cmask & BNG_RE_UCNTX_CMASK_POW2_DISABLED) ?
+ ent : roundup_pow_of_two(ent) : ent;
+}
+
int bng_re_query_device(struct ib_device *ibdev, struct ib_device_attr *ib_attr,
struct ib_udata *udata);
int bng_re_modify_device(struct ib_device *ibdev, int device_modify_mask,
@@ -90,6 +114,14 @@ struct ib_mr *bng_re_reg_user_mr_dmabuf(struct ib_pd *ib_pd, u64 start,
int fd, int mr_access_flags,
struct ib_dmah *dmah,
struct uverbs_attr_bundle *attrs);
+int bng_re_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
+ struct uverbs_attr_bundle *attrs);
+int bng_re_create_user_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
+ struct uverbs_attr_bundle *attrs);
+int bng_re_resize_cq(struct ib_cq *ibcq, unsigned int cqe, struct ib_udata *udata);
+int bng_re_req_notify_cq(struct ib_cq *ib_cq,
+ enum ib_cq_notify_flags ib_cqn_flags);
+int bng_re_destroy_cq(struct ib_cq *cq, struct ib_udata *udata);
#endif /* __BNG_RE_VERBS_H__ */
diff --git a/include/uapi/rdma/bng_re-abi.h b/include/uapi/rdma/bng_re-abi.h
index 14d56a88def4..2a88f36bcacd 100644
--- a/include/uapi/rdma/bng_re-abi.h
+++ b/include/uapi/rdma/bng_re-abi.h
@@ -40,4 +40,25 @@ struct bng_re_uctx_resp {
__u64 uc_db_mmap_key;
};
+struct bng_re_cq_req {
+ __aligned_u64 cq_va;
+ __aligned_u64 cq_handle;
+};
+
+enum bng_re_cq_mask {
+ BNG_RE_CQ_TOGGLE_PAGE_SUPPORT = 0x1,
+};
+
+struct bng_re_cq_resp {
+ __u32 cqid;
+ __u32 tail;
+ __u32 phase;
+ __u32 rsvd;
+ __aligned_u64 comp_mask;
+};
+
+struct bng_re_resize_cq_req {
+ __aligned_u64 cq_va;
+};
+
#endif /* __BNG_RE_UVERBS_ABI_H__*/
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 11/15] RDMA/bng_re: Add SRQ verbs
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (9 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 10/15] RDMA/bng_re: Add CQ verbs Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 12/15] RDMA/bng_re: Add Stats verbs Siva Reddy Kallam
` (3 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Suresh Rupanagudi,
Siva Reddy Kallam
From: Suresh Rupanagudi <suresh.rupanagudi@broadcom.com>
This patch adds below verbs.
- bng_re_create_srq
- bng_re_modify_srq
- bng_re_query_srq
- bng_re_destroy_srq
Signed-off-by: Suresh Rupanagudi <suresh.rupanagudi@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/bng_dev.c | 5 +
drivers/infiniband/hw/bng_re/bng_re.h | 1 +
drivers/infiniband/hw/bng_re/bng_res.c | 1 +
drivers/infiniband/hw/bng_re/bng_res.h | 9 +
drivers/infiniband/hw/bng_re/bng_sp.c | 144 +++++++++++++++
drivers/infiniband/hw/bng_re/bng_sp.h | 28 +++
drivers/infiniband/hw/bng_re/bng_verbs.c | 216 +++++++++++++++++++++++
drivers/infiniband/hw/bng_re/bng_verbs.h | 25 +++
include/uapi/rdma/bng_re-abi.h | 14 ++
9 files changed, 443 insertions(+)
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index 9c35c56ae583..a42e24691cad 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -57,6 +57,11 @@ static const struct ib_device_ops bng_re_dev_ops = {
.resize_user_cq = bng_re_resize_cq,
.req_notify_cq = bng_re_req_notify_cq,
.destroy_cq = bng_re_destroy_cq,
+ .create_srq = bng_re_create_srq,
+ .modify_srq = bng_re_modify_srq,
+ .query_srq = bng_re_query_srq,
+ .destroy_srq = bng_re_destroy_srq,
+ INIT_RDMA_OBJ_SIZE(ib_srq, bng_re_srq, ib_srq),
INIT_RDMA_OBJ_SIZE(ib_cq, bng_re_cq, ib_cq),
INIT_RDMA_OBJ_SIZE(ib_pd, bng_re_pd, ib_pd),
INIT_RDMA_OBJ_SIZE(ib_ucontext, bng_re_ucontext, ib_uctx),
diff --git a/drivers/infiniband/hw/bng_re/bng_re.h b/drivers/infiniband/hw/bng_re/bng_re.h
index 47b4037e124e..70851f1180d0 100644
--- a/drivers/infiniband/hw/bng_re/bng_re.h
+++ b/drivers/infiniband/hw/bng_re/bng_re.h
@@ -296,6 +296,7 @@ struct bng_re_dev {
struct bng_re_dpi dpi_privileged;
struct bng_sp_cq_coal_param cq_coalescing;
DECLARE_HASHTABLE(cq_hash, MAX_CQ_HASH_BITS);
+ DECLARE_HASHTABLE(srq_hash, 8); /* SRQ hash table */
};
#define to_bng_re_dev(ptr, member) \
diff --git a/drivers/infiniband/hw/bng_re/bng_res.c b/drivers/infiniband/hw/bng_re/bng_res.c
index 8bf6e8efa182..024d49f68b09 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.c
+++ b/drivers/infiniband/hw/bng_re/bng_res.c
@@ -265,6 +265,7 @@ int bng_re_alloc_init_hwq(struct bng_re_hwq *hwq,
hwq->max_elements = hwq->depth;
hwq->element_size = stride;
hwq->qe_ppg = pg_size / stride;
+ hwq->is_user = !!hwq_attr->sginfo->umem;
/* For direct access to the elements */
lvl = hwq->level;
if (hwq_attr->sginfo->nopte && hwq->level)
diff --git a/drivers/infiniband/hw/bng_re/bng_res.h b/drivers/infiniband/hw/bng_re/bng_res.h
index 0f832f9bae20..00429b4a7d30 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.h
+++ b/drivers/infiniband/hw/bng_re/bng_res.h
@@ -170,6 +170,7 @@ struct bng_re_hwq {
u32 cons;
/* queue entry per page */
u16 qe_ppg;
+ u8 is_user;
};
struct bng_re_stats {
@@ -288,6 +289,14 @@ static inline void bng_re_ring_nq_db(struct bng_re_db_info *info,
bng_re_ring_db(info, type);
}
+static inline void bng_re_srq_arm_db(struct bng_re_db_info *info, u32 th)
+{
+ u64 key = 0;
+
+ key = BNG_RE_INIT_DBHDR(info->xid, DBC_DBC_TYPE_SRQ_ARM, th, info->toggle);
+ writeq(key, info->priv_db);
+}
+
static inline void bng_re_hwq_incr_cons(u32 max_elements, u32 *cons, u32 cnt,
u32 *dbinfo_flags)
{
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.c b/drivers/infiniband/hw/bng_re/bng_sp.c
index 04488db6d016..78e2bb9ad859 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.c
+++ b/drivers/infiniband/hw/bng_re/bng_sp.c
@@ -816,3 +816,147 @@ int bng_sp_destroy_cq(struct bng_re_res *res, struct bng_sp_cq *cq)
return 0;
}
+int bng_sp_create_srq(struct bng_re_res *res, struct bng_sp_srq *srq)
+{
+ struct bng_re_rcfw *rcfw = res->rcfw;
+ struct bng_re_hwq_attr hwq_attr = {};
+ struct creq_create_srq_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct cmdq_create_srq req = {};
+ struct bng_re_pbl *pbl;
+ u16 pg_sz_lvl;
+ int rc, idx;
+
+ hwq_attr.res = res;
+ hwq_attr.sginfo = &srq->sginfo;
+ hwq_attr.depth = srq->max_wqe;
+ hwq_attr.stride = srq->wqe_size;
+ hwq_attr.type = BNG_HWQ_TYPE_QUEUE;
+ rc = bng_re_alloc_init_hwq(&srq->hwq, &hwq_attr);
+ if (rc)
+ return rc;
+
+ srq->dbinfo.flags = 0;
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_CREATE_SRQ,
+ sizeof(req));
+
+ /* Configure the request */
+ req.dpi = cpu_to_le32(srq->dpi->dpi);
+ req.srq_handle = cpu_to_le64((uintptr_t)srq);
+
+ req.srq_size = cpu_to_le16((u16)srq->hwq.max_elements);
+ pbl = &srq->hwq.pbl[BNG_PBL_LVL_0];
+ pg_sz_lvl = ((u16)bng_re_base_pg_size(&srq->hwq) <<
+ CMDQ_CREATE_SRQ_PG_SIZE_SFT);
+ pg_sz_lvl |= (srq->hwq.level & CMDQ_CREATE_SRQ_LVL_MASK) <<
+ CMDQ_CREATE_SRQ_LVL_SFT;
+ req.pg_size_lvl = cpu_to_le16(pg_sz_lvl);
+ req.pbl = cpu_to_le64(pbl->pg_map_arr[0]);
+ req.pd_id = cpu_to_le32(srq->pd->id);
+ req.eventq_id = cpu_to_le16(srq->eventq_hw_ring_id);
+
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL, sizeof(req), sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ goto fail;
+
+ srq->id = le32_to_cpu(resp.xid);
+ srq->start_idx = 0;
+ srq->last_idx = srq->hwq.max_elements - 1;
+
+ /* Allocate shadow queue for kernel SRQs */
+ if (!srq->hwq.is_user) {
+ srq->swq = kcalloc(srq->hwq.max_elements, sizeof(*srq->swq),
+ GFP_KERNEL);
+ if (!srq->swq) {
+ rc = -ENOMEM;
+ goto fail_swq;
+ }
+ for (idx = 0; idx < srq->hwq.max_elements; idx++)
+ srq->swq[idx].next_idx = idx + 1;
+ srq->swq[srq->last_idx].next_idx = -1;
+ }
+
+ srq->dbinfo.hwq = &srq->hwq;
+ srq->dbinfo.xid = srq->id;
+ srq->dbinfo.db = srq->dpi->dbr;
+ srq->dbinfo.max_slot = 1;
+ srq->dbinfo.priv_db = res->dpi_tbl.priv_db;
+
+ /* Arm the SRQ */
+ bng_re_armen_db(&srq->dbinfo, DBC_DBC_TYPE_SRQ_ARMENA);
+
+ return 0;
+
+fail_swq:
+ bng_sp_destroy_srq(res, srq);
+ return rc;
+
+fail:
+ bng_re_free_hwq(res, &srq->hwq);
+ kfree(srq->swq);
+ srq->swq = NULL;
+
+ return rc;
+}
+
+int bng_sp_query_srq(struct bng_re_res *res, struct bng_sp_srq *srq)
+{
+ struct bng_re_rcfw *rcfw = res->rcfw;
+ struct creq_query_srq_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct bng_re_rcfw_sbuf sbuf;
+ struct creq_query_srq_resp_sb *sb;
+ struct cmdq_query_srq req = {};
+ int rc;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_QUERY_SRQ,
+ sizeof(req));
+
+ /* Configure the request */
+ sbuf.size = ALIGN(sizeof(*sb), BNG_FW_CMDQE_UNITS);
+ sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size,
+ &sbuf.dma_addr, GFP_KERNEL);
+ if (!sbuf.sb)
+ return -ENOMEM;
+ req.resp_size = sbuf.size / BNG_FW_CMDQE_UNITS;
+ req.srq_cid = cpu_to_le32(srq->id);
+ sb = sbuf.sb;
+
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req),
+ sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (!rc)
+ srq->threshold = le16_to_cpu(sb->srq_limit);
+ dma_free_coherent(&rcfw->pdev->dev, sbuf.size,
+ sbuf.sb, sbuf.dma_addr);
+
+ return rc;
+}
+
+int bng_sp_destroy_srq(struct bng_re_res *res, struct bng_sp_srq *srq)
+{
+ struct bng_re_rcfw *rcfw = res->rcfw;
+ struct creq_destroy_srq_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct cmdq_destroy_srq req = {};
+ int rc;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_DESTROY_SRQ,
+ sizeof(req));
+
+ /* Configure the request */
+ req.srq_cid = cpu_to_le32(srq->id);
+
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL, sizeof(req), sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ return rc;
+ bng_re_free_hwq(res, &srq->hwq);
+ kfree(srq->swq);
+
+ return 0;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.h b/drivers/infiniband/hw/bng_re/bng_sp.h
index d05151123f98..da4dceba4f46 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.h
+++ b/drivers/infiniband/hw/bng_re/bng_sp.h
@@ -166,6 +166,31 @@ struct bng_sp_cq {
u16 cnq_events;
};
+/* SRQ shadow work queue entry */
+struct bng_sp_swq {
+ u64 wr_id;
+ int next_idx;
+};
+
+/* SRQ SP layer structure */
+struct bng_sp_srq {
+ struct bng_sp_pd *pd;
+ struct bng_re_dpi *dpi;
+ struct bng_re_hwq hwq;
+ struct bng_re_db_info dbinfo;
+ struct bng_re_sg_info sginfo;
+ struct bng_sp_swq *swq;
+ u32 start_idx;
+ u32 last_idx;
+ u64 srq_handle;
+ u32 id;
+ u16 wqe_size;
+ u32 max_wqe;
+ u32 max_sge;
+ u32 threshold;
+ u16 eventq_hw_ring_id;
+};
+
int bng_re_get_dev_attr(struct bng_re_rcfw *rcfw);
int bng_sp_alloc_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl, u16 size);
@@ -200,5 +225,8 @@ void bng_sp_resize_cq_complete(struct bng_re_res *res, struct bng_sp_cq *cq);
int bng_sp_resize_cq(struct bng_re_res *res, struct bng_sp_cq *cq,
int new_cqes);
int bng_sp_destroy_cq(struct bng_re_res *res, struct bng_sp_cq *cq);
+int bng_sp_create_srq(struct bng_re_res *res, struct bng_sp_srq *srq);
+int bng_sp_query_srq(struct bng_re_res *res, struct bng_sp_srq *srq);
+int bng_sp_destroy_srq(struct bng_re_res *res, struct bng_sp_srq *srq);
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.c b/drivers/infiniband/hw/bng_re/bng_verbs.c
index b08ac20d788d..e48caefa64ee 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.c
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.c
@@ -1196,3 +1196,219 @@ int bng_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata)
kfree(cq->cql);
return 0;
}
+
+/* SRQ helper function */
+static int bng_re_init_user_srq(struct bng_re_dev *rdev,
+ struct bng_re_pd *pd,
+ struct bng_re_srq *srq,
+ struct ib_udata *udata)
+{
+ struct bng_re_ucontext *cntx =
+ rdma_udata_to_drv_context(udata, struct bng_re_ucontext,
+ ib_uctx);
+ struct bng_sp_srq *sp_srq = &srq->sp_srq;
+ struct bng_re_srq_req ureq;
+ struct ib_umem *umem;
+ int bytes = 0;
+
+ if (ib_copy_from_udata(&ureq, udata, sizeof(ureq)))
+ return -EFAULT;
+
+ bytes = (sp_srq->max_wqe * sp_srq->wqe_size);
+ bytes = PAGE_ALIGN(bytes);
+ umem = ib_umem_get_va(&rdev->ibdev, ureq.srqva, bytes,
+ IB_ACCESS_LOCAL_WRITE);
+ if (IS_ERR(umem))
+ return PTR_ERR(umem);
+
+ srq->umem = umem;
+ sp_srq->sginfo.umem = umem;
+ sp_srq->sginfo.pgsize = PAGE_SIZE;
+ sp_srq->sginfo.pgshft = PAGE_SHIFT;
+ sp_srq->srq_handle = ureq.srq_handle;
+ sp_srq->dpi = &cntx->dpi;
+
+ return 0;
+}
+
+/* SRQ operations */
+int bng_re_create_srq(struct ib_srq *ib_srq,
+ struct ib_srq_init_attr *srq_init_attr,
+ struct ib_udata *udata)
+{
+ struct bng_re_dev_attr *dev_attr;
+ struct bng_re_ucontext *uctx;
+ struct bng_re_dev *rdev;
+ struct bng_re_srq *srq;
+ struct bng_re_pd *pd;
+ struct ib_pd *ib_pd;
+ int rc, entries;
+
+ ib_pd = ib_srq->pd;
+ pd = container_of(ib_pd, struct bng_re_pd, ib_pd);
+ rdev = pd->rdev;
+ dev_attr = rdev->dev_attr;
+ srq = container_of(ib_srq, struct bng_re_srq, ib_srq);
+
+ if (srq_init_attr->attr.max_wr > dev_attr->max_srq_wqes) {
+ ibdev_err(&rdev->ibdev, "Create SRQ failed - max exceeded\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ if (srq_init_attr->srq_type != IB_SRQT_BASIC) {
+ rc = -EOPNOTSUPP;
+ goto exit;
+ }
+
+ uctx = rdma_udata_to_drv_context(udata, struct bng_re_ucontext, ib_uctx);
+ srq->rdev = rdev;
+ srq->sp_srq.pd = &pd->sp_pd;
+ srq->sp_srq.dpi = &rdev->dpi_privileged;
+ /* Allocate 1 more than what's provided so posting max doesn't
+ * mean empty
+ */
+ entries = bng_re_init_depth(srq_init_attr->attr.max_wr + 1, uctx);
+ if (entries > dev_attr->max_srq_wqes + 1)
+ entries = dev_attr->max_srq_wqes + 1;
+ srq->sp_srq.max_wqe = entries;
+
+ srq->sp_srq.max_sge = srq_init_attr->attr.max_sge;
+ /* 128 byte wqe size for SRQ. So use max sges */
+ srq->sp_srq.wqe_size = bng_re_get_rwqe_size(dev_attr->max_srq_sges);
+ srq->sp_srq.threshold = srq_init_attr->attr.srq_limit;
+ srq->srq_limit = srq_init_attr->attr.srq_limit;
+ srq->sp_srq.eventq_hw_ring_id = rdev->nqr->nq[0].ring_id;
+ srq->sp_srq.sginfo.pgsize = PAGE_SIZE;
+ srq->sp_srq.sginfo.pgshft = PAGE_SHIFT;
+
+ if (udata) {
+ rc = bng_re_init_user_srq(rdev, pd, srq, udata);
+ if (rc)
+ goto fail;
+ }
+
+ rc = bng_sp_create_srq(&rdev->bng_res, &srq->sp_srq);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Create HW SRQ failed!\n");
+ goto fail;
+ }
+
+ if (udata) {
+ struct bng_re_srq_resp resp = {};
+
+ resp.srqid = srq->sp_srq.id;
+ if (rdev->chip_ctx->modes.toggle_bits & BNG_RE_SRQ_TOGGLE_BIT) {
+ srq->uctx_srq_page = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!srq->uctx_srq_page) {
+ rc = -ENOMEM;
+ bng_sp_destroy_srq(&rdev->bng_res, &srq->sp_srq);
+ goto fail;
+ }
+ hash_add(rdev->srq_hash, &srq->hash_entry, srq->sp_srq.id);
+ resp.comp_mask |= BNG_RE_SRQ_TOGGLE_PAGE_SUPPORT;
+ }
+ rc = ib_copy_to_udata(udata, &resp, sizeof(resp));
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "SRQ copy to udata failed!\n");
+ if (srq->uctx_srq_page) {
+ free_page((unsigned long)srq->uctx_srq_page);
+ hash_del(&srq->hash_entry);
+ }
+ bng_sp_destroy_srq(&rdev->bng_res,
+ &srq->sp_srq);
+ goto fail;
+ }
+ }
+ spin_lock_init(&srq->lock);
+
+ return 0;
+
+fail:
+ ib_umem_release(srq->umem);
+exit:
+ return rc;
+}
+
+int bng_re_modify_srq(struct ib_srq *ib_srq, struct ib_srq_attr *srq_attr,
+ enum ib_srq_attr_mask srq_attr_mask,
+ struct ib_udata *udata)
+{
+ struct bng_re_srq *srq = container_of(ib_srq, struct bng_re_srq, ib_srq);
+ struct bng_re_dev *rdev = srq->rdev;
+
+ switch (srq_attr_mask) {
+ case IB_SRQ_MAX_WR:
+ /* SRQ resize is not supported */
+ return -EINVAL;
+ case IB_SRQ_LIMIT:
+ {
+ unsigned long flags;
+
+ /* Change the SRQ threshold */
+ if (srq_attr->srq_limit > srq->sp_srq.max_wqe)
+ return -EINVAL;
+
+ spin_lock_irqsave(&srq->lock, flags);
+ srq->sp_srq.threshold = srq_attr->srq_limit;
+ bng_re_srq_arm_db(&srq->sp_srq.dbinfo, srq->sp_srq.threshold);
+
+ /* On success, update the shadow */
+ srq->srq_limit = srq_attr->srq_limit;
+ spin_unlock_irqrestore(&srq->lock, flags);
+ return 0;
+ }
+ default:
+ ibdev_err(&rdev->ibdev,
+ "Unsupported srq_attr_mask 0x%x\n", srq_attr_mask);
+ return -EINVAL;
+ }
+}
+
+int bng_re_query_srq(struct ib_srq *ib_srq, struct ib_srq_attr *srq_attr)
+{
+ struct bng_re_srq *srq = container_of(ib_srq, struct bng_re_srq, ib_srq);
+ struct bng_re_dev *rdev = srq->rdev;
+ struct bng_sp_srq sp_srq = {};
+ int rc;
+
+ /* Get live SRQ attr */
+ sp_srq.id = srq->sp_srq.id;
+ rc = bng_sp_query_srq(&rdev->bng_res, &sp_srq);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Query HW SRQ failed!\n");
+ return rc;
+ }
+
+ srq_attr->max_wr = srq->sp_srq.max_wqe;
+ srq_attr->max_sge = srq->sp_srq.max_sge;
+ srq_attr->srq_limit = sp_srq.threshold;
+
+ return 0;
+}
+
+int bng_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata)
+{
+ struct bng_re_dev *rdev;
+ struct bng_re_srq *srq;
+ int rc;
+
+ srq = container_of(ib_srq, struct bng_re_srq, ib_srq);
+ rdev = srq->rdev;
+
+ rc = bng_sp_destroy_srq(&rdev->bng_res, &srq->sp_srq);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Destroy SRQ failed: %d\n", rc);
+ return rc;
+ }
+
+ if (srq->uctx_srq_page) {
+ free_page((unsigned long)srq->uctx_srq_page);
+ srq->uctx_srq_page = NULL;
+ hash_del(&srq->hash_entry);
+ }
+
+ ib_umem_release(srq->umem);
+
+ return 0;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.h b/drivers/infiniband/hw/bng_re/bng_verbs.h
index 839967c73f08..dd9fac0f33c0 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.h
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.h
@@ -64,6 +64,17 @@ struct bng_re_cq {
struct hlist_node hash_entry;
};
+struct bng_re_srq {
+ struct ib_srq ib_srq;
+ struct bng_re_dev *rdev;
+ u32 srq_limit;
+ struct bng_sp_srq sp_srq;
+ struct ib_umem *umem;
+ spinlock_t lock; /* protect srq */
+ void *uctx_srq_page;
+ struct hlist_node hash_entry;
+};
+
static inline u32 bng_re_init_depth(u32 ent, struct bng_re_ucontext *uctx)
{
/* roundup_pow_of_two(0) is undefined (UBSAN: shift by 64 on 64-bit). */
@@ -71,6 +82,12 @@ static inline u32 bng_re_init_depth(u32 ent, struct bng_re_ucontext *uctx)
ent : roundup_pow_of_two(ent) : ent;
}
+static inline u16 bng_re_get_rwqe_size(int nsge)
+{
+ /* RQ WQE header + SGEs */
+ return 16 + (nsge * 16); /* header=16, each sge=16 bytes */
+}
+
int bng_re_query_device(struct ib_device *ibdev, struct ib_device_attr *ib_attr,
struct ib_udata *udata);
int bng_re_modify_device(struct ib_device *ibdev, int device_modify_mask,
@@ -122,6 +139,14 @@ int bng_re_resize_cq(struct ib_cq *ibcq, unsigned int cqe, struct ib_udata *udat
int bng_re_req_notify_cq(struct ib_cq *ib_cq,
enum ib_cq_notify_flags ib_cqn_flags);
int bng_re_destroy_cq(struct ib_cq *cq, struct ib_udata *udata);
+int bng_re_create_srq(struct ib_srq *ib_srq,
+ struct ib_srq_init_attr *srq_init_attr,
+ struct ib_udata *udata);
+int bng_re_modify_srq(struct ib_srq *ib_srq, struct ib_srq_attr *srq_attr,
+ enum ib_srq_attr_mask srq_attr_mask,
+ struct ib_udata *udata);
+int bng_re_query_srq(struct ib_srq *ib_srq, struct ib_srq_attr *srq_attr);
+int bng_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata);
#endif /* __BNG_RE_VERBS_H__ */
diff --git a/include/uapi/rdma/bng_re-abi.h b/include/uapi/rdma/bng_re-abi.h
index 2a88f36bcacd..49931b7c1344 100644
--- a/include/uapi/rdma/bng_re-abi.h
+++ b/include/uapi/rdma/bng_re-abi.h
@@ -61,4 +61,18 @@ struct bng_re_resize_cq_req {
__aligned_u64 cq_va;
};
+struct bng_re_srq_req {
+ __aligned_u64 srqva;
+ __aligned_u64 srq_handle;
+};
+
+enum bng_re_srq_mask {
+ BNG_RE_SRQ_TOGGLE_PAGE_SUPPORT = 0x1,
+};
+
+struct bng_re_srq_resp {
+ __u32 srqid;
+ __aligned_u64 comp_mask;
+};
+
#endif /* __BNG_RE_UVERBS_ABI_H__*/
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 12/15] RDMA/bng_re: Add Stats verbs
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (10 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 11/15] RDMA/bng_re: Add SRQ verbs Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 13/15] RDMA/bng_re: Add AH verbs Siva Reddy Kallam
` (2 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Usman Ansari, Siva Reddy Kallam
From: Usman Ansari <usman.ansari@broadcom.com>
This patch adds below verbs.
- bng_re_alloc_hw_port_stats
- bng_re_ib_get_hw_stats
Signed-off-by: Usman Ansari <usman.ansari@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/Makefile | 1 +
drivers/infiniband/hw/bng_re/bng_dev.c | 3 +
drivers/infiniband/hw/bng_re/bng_fw.h | 12 +
.../infiniband/hw/bng_re/bng_hw_counters.c | 304 ++++++++++++++++++
.../infiniband/hw/bng_re/bng_hw_counters.h | 118 +++++++
drivers/infiniband/hw/bng_re/bng_re.h | 4 +
drivers/infiniband/hw/bng_re/bng_res.h | 21 ++
drivers/infiniband/hw/bng_re/bng_sp.c | 144 +++++++++
drivers/infiniband/hw/bng_re/bng_sp.h | 84 +++++
9 files changed, 691 insertions(+)
create mode 100644 drivers/infiniband/hw/bng_re/bng_hw_counters.c
create mode 100644 drivers/infiniband/hw/bng_re/bng_hw_counters.h
diff --git a/drivers/infiniband/hw/bng_re/Makefile b/drivers/infiniband/hw/bng_re/Makefile
index 5e650a35b185..a9a851cc2fa6 100644
--- a/drivers/infiniband/hw/bng_re/Makefile
+++ b/drivers/infiniband/hw/bng_re/Makefile
@@ -14,4 +14,5 @@ bng_re-y := \
bng_dev.o \
bng_sp.o \
bng_fp.o \
+ bng_hw_counters.o \
bng_verbs.o
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index a42e24691cad..978b7efab03e 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -19,6 +19,7 @@
#include "bng_re_mpc_roce.h"
#include "bng_xid.h"
#include "bng_verbs.h"
+#include "bng_hw_counters.h"
MODULE_AUTHOR("Siva Reddy Kallam <siva.kallam@broadcom.com>");
MODULE_DESCRIPTION(BNG_RE_DESC);
@@ -61,6 +62,8 @@ static const struct ib_device_ops bng_re_dev_ops = {
.modify_srq = bng_re_modify_srq,
.query_srq = bng_re_query_srq,
.destroy_srq = bng_re_destroy_srq,
+ .alloc_hw_port_stats = bng_re_alloc_hw_port_stats,
+ .get_hw_stats = bng_re_ib_get_hw_stats,
INIT_RDMA_OBJ_SIZE(ib_srq, bng_re_srq, ib_srq),
INIT_RDMA_OBJ_SIZE(ib_cq, bng_re_cq, ib_cq),
INIT_RDMA_OBJ_SIZE(ib_pd, bng_re_pd, ib_pd),
diff --git a/drivers/infiniband/hw/bng_re/bng_fw.h b/drivers/infiniband/hw/bng_re/bng_fw.h
index f89e4aeb2469..011dc18592d0 100644
--- a/drivers/infiniband/hw/bng_re/bng_fw.h
+++ b/drivers/infiniband/hw/bng_re/bng_fw.h
@@ -28,11 +28,15 @@
!((pass) & BNG_RE_FLAG_EPOCH_CONS_MASK))
#define BNG_FW_CREQ_ENTRY_POLL_BUDGET 0x100
+#define BNG_RE_OOS_COUNT_MASK 0xFFFFFFFF
+
/* CMDQ */
struct bng_fw_cmdqe {
u8 data[16];
};
+#define BNG_RE_CMDQE_UNITS sizeof(struct bng_fw_cmdqe)
+
#define BNG_FW_CMDQE_MAX_CNT 8192
#define BNG_FW_CMDQE_UNITS sizeof(struct bng_fw_cmdqe)
#define BNG_FW_CMDQE_BYTES(depth) ((depth) * BNG_FW_CMDQE_UNITS)
@@ -40,6 +44,12 @@ struct bng_fw_cmdqe {
#define BNG_FW_MAX_COOKIE_VALUE (BNG_FW_CMDQE_MAX_CNT - 1)
#define BNG_FW_CMD_IS_BLOCKING 0x8000
+struct bng_rcfw_sbuf {
+ void *sb;
+ dma_addr_t dma_addr;
+ u32 size;
+};
+
/* Crsq buf is 1024-Byte */
struct bng_re_crsbe {
u8 data[1024];
@@ -128,6 +138,8 @@ struct bng_re_rcfw {
/* cached from chip cctx for quick reference in slow path */
u16 max_timeout;
atomic_t rcfw_intr_enabled;
+ u64 oos_prev;
+ u32 init_oos_stats;
};
struct bng_re_cmdqmsg {
diff --git a/drivers/infiniband/hw/bng_re/bng_hw_counters.c b/drivers/infiniband/hw/bng_re/bng_hw_counters.c
new file mode 100644
index 000000000000..cb9a00fed621
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_hw_counters.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2026 Broadcom.
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <rdma/ib_mad.h>
+#include <rdma/ib_pma.h>
+#include <rdma/bng_re-abi.h>
+
+#include "bng_res.h"
+#include "bng_sp.h"
+#include "bng_fw.h"
+#include "bnge.h"
+#include "bnge_auxr.h"
+#include "bng_fp.h"
+#include "bng_re.h"
+#include "bng_hw_counters.h"
+
+static const struct rdma_stat_desc bng_re_stat_descs[] = {
+ [BNG_RE_RX_PKTS].name = "rx_pkts",
+ [BNG_RE_RX_BYTES].name = "rx_bytes",
+ [BNG_RE_TX_PKTS].name = "tx_pkts",
+ [BNG_RE_TX_BYTES].name = "tx_bytes",
+ [BNG_RE_RECOVERABLE_ERRORS].name = "recoverable_errors",
+ [BNG_RE_TX_ERRORS].name = "tx_roce_errors",
+ [BNG_RE_TX_DISCARDS].name = "tx_roce_discards",
+ [BNG_RE_RX_ERRORS].name = "rx_roce_errors",
+ [BNG_RE_RX_DISCARDS].name = "rx_roce_discards",
+ [BNG_RE_TO_RETRANSMITS].name = "local_ack_timeout_err",
+ [BNG_RE_SEQ_ERR_NAKS_RCVD].name = "packet_seq_err",
+ [BNG_RE_MAX_RETRY_EXCEEDED].name = "max_retry_exceeded",
+ [BNG_RE_RNR_NAKS_RCVD].name = "rnr_nak_retry_err",
+ [BNG_RE_MISSING_RESP].name = "implied_nak_seq_err",
+ [BNG_RE_UNRECOVERABLE_ERR].name = "unrecoverable_err",
+ [BNG_RE_BAD_RESP_ERR].name = "bad_resp_err",
+ [BNG_RE_LOCAL_QP_OP_ERR].name = "local_qp_op_err",
+ [BNG_RE_LOCAL_PROTECTION_ERR].name = "local_protection_err",
+ [BNG_RE_MEM_MGMT_OP_ERR].name = "mem_mgmt_op_err",
+ [BNG_RE_REMOTE_INVALID_REQ_ERR].name = "req_remote_invalid_request",
+ [BNG_RE_REMOTE_ACCESS_ERR].name = "req_remote_access_errors",
+ [BNG_RE_REMOTE_OP_ERR].name = "remote_op_err",
+ [BNG_RE_DUP_REQ].name = "duplicate_request",
+ [BNG_RE_RES_EXCEED_MAX].name = "res_exceed_max",
+ [BNG_RE_RES_LENGTH_MISMATCH].name = "resp_local_length_error",
+ [BNG_RE_RES_EXCEEDS_WQE].name = "res_exceeds_wqe",
+ [BNG_RE_RES_OPCODE_ERR].name = "res_opcode_err",
+ [BNG_RE_RES_RX_INVALID_RKEY].name = "res_rx_invalid_rkey",
+ [BNG_RE_RES_RX_DOMAIN_ERR].name = "res_rx_domain_err",
+ [BNG_RE_RES_RX_NO_PERM].name = "res_rx_no_perm",
+ [BNG_RE_RES_RX_RANGE_ERR].name = "res_rx_range_err",
+ [BNG_RE_RES_TX_INVALID_RKEY].name = "res_tx_invalid_rkey",
+ [BNG_RE_RES_TX_DOMAIN_ERR].name = "res_tx_domain_err",
+ [BNG_RE_RES_TX_NO_PERM].name = "res_tx_no_perm",
+ [BNG_RE_RES_TX_RANGE_ERR].name = "res_tx_range_err",
+ [BNG_RE_RES_IRRQ_OFLOW].name = "res_irrq_oflow",
+ [BNG_RE_RES_UNSUP_OPCODE].name = "res_unsup_opcode",
+ [BNG_RE_RES_UNALIGNED_ATOMIC].name = "res_unaligned_atomic",
+ [BNG_RE_RES_REM_INV_ERR].name = "res_rem_inv_err",
+ [BNG_RE_RES_MEM_ERROR].name = "res_mem_err",
+ [BNG_RE_RES_SRQ_ERR].name = "res_srq_err",
+ [BNG_RE_RES_CMP_ERR].name = "res_cmp_err",
+ [BNG_RE_RES_INVALID_DUP_RKEY].name = "res_invalid_dup_rkey",
+ [BNG_RE_RES_WQE_FORMAT_ERR].name = "res_wqe_format_err",
+ [BNG_RE_RES_CQ_LOAD_ERR].name = "res_cq_load_err",
+ [BNG_RE_RES_SRQ_LOAD_ERR].name = "res_srq_load_err",
+ [BNG_RE_RES_TX_PCI_ERR].name = "res_tx_pci_err",
+ [BNG_RE_RES_RX_PCI_ERR].name = "res_rx_pci_err",
+ [BNG_RE_OUT_OF_SEQ_ERR].name = "out_of_sequence",
+ [BNG_RE_TX_ATOMIC_REQ].name = "tx_atomic_req",
+ [BNG_RE_TX_READ_REQ].name = "tx_read_req",
+ [BNG_RE_TX_READ_RES].name = "tx_read_resp",
+ [BNG_RE_TX_WRITE_REQ].name = "tx_write_req",
+ [BNG_RE_TX_SEND_REQ].name = "tx_send_req",
+ [BNG_RE_TX_ROCE_PKTS].name = "tx_roce_only_pkts",
+ [BNG_RE_TX_ROCE_BYTES].name = "tx_roce_only_bytes",
+ [BNG_RE_RX_ATOMIC_REQ].name = "rx_atomic_requests",
+ [BNG_RE_RX_READ_REQ].name = "rx_read_requests",
+ [BNG_RE_RX_READ_RESP].name = "rx_read_resp",
+ [BNG_RE_RX_WRITE_REQ].name = "rx_write_requests",
+ [BNG_RE_RX_SEND_REQ].name = "rx_send_req",
+ [BNG_RE_RX_ROCE_PKTS].name = "rx_roce_only_pkts",
+ [BNG_RE_RX_ROCE_BYTES].name = "rx_roce_only_bytes",
+ [BNG_RE_RX_ROCE_GOOD_PKTS].name = "rx_roce_good_pkts",
+ [BNG_RE_RX_ROCE_GOOD_BYTES].name = "rx_roce_good_bytes",
+ [BNG_RE_OOB].name = "out_of_buffer",
+ [BNG_RE_TX_CNP].name = "np_cnp_pkts",
+ [BNG_RE_RX_CNP].name = "rp_cnp_handled",
+ [BNG_RE_RX_ECN].name = "np_ecn_marked_roce_packets",
+ [BNG_RE_REQ_CQE_ERROR].name = "req_cqe_error",
+ [BNG_RE_RESP_CQE_ERROR].name = "resp_cqe_error",
+ [BNG_RE_RESP_REMOTE_ACCESS_ERRS].name = "resp_remote_access_errors",
+};
+
+struct rdma_hw_stats *bng_re_alloc_hw_port_stats(struct ib_device *ibdev,
+ u32 port_num)
+{
+ int num_counters = 0;
+
+ num_counters = BNG_RE_NUM_EXT_COUNTERS;
+
+ return rdma_alloc_hw_stats_struct(bng_re_stat_descs, num_counters,
+ RDMA_HW_STATS_DEFAULT_LIFESPAN);
+}
+
+static void bng_re_copy_err_stats(struct bng_re_dev *rdev,
+ struct rdma_hw_stats *stats,
+ struct bng_roce_stats *err_s)
+{
+ stats->value[BNG_RE_TO_RETRANSMITS] =
+ err_s->to_retransmits;
+ stats->value[BNG_RE_SEQ_ERR_NAKS_RCVD] =
+ err_s->seq_err_naks_rcvd;
+ stats->value[BNG_RE_MAX_RETRY_EXCEEDED] =
+ err_s->max_retry_exceeded;
+ stats->value[BNG_RE_RNR_NAKS_RCVD] =
+ err_s->rnr_naks_rcvd;
+ stats->value[BNG_RE_MISSING_RESP] =
+ err_s->missing_resp;
+ stats->value[BNG_RE_UNRECOVERABLE_ERR] =
+ err_s->unrecoverable_err;
+ stats->value[BNG_RE_BAD_RESP_ERR] =
+ err_s->bad_resp_err;
+ stats->value[BNG_RE_LOCAL_QP_OP_ERR] =
+ err_s->local_qp_op_err;
+ stats->value[BNG_RE_LOCAL_PROTECTION_ERR] =
+ err_s->local_protection_err;
+ stats->value[BNG_RE_MEM_MGMT_OP_ERR] =
+ err_s->mem_mgmt_op_err;
+ stats->value[BNG_RE_REMOTE_INVALID_REQ_ERR] =
+ err_s->remote_invalid_req_err;
+ stats->value[BNG_RE_REMOTE_ACCESS_ERR] =
+ err_s->remote_access_err;
+ stats->value[BNG_RE_REMOTE_OP_ERR] =
+ err_s->remote_op_err;
+ stats->value[BNG_RE_DUP_REQ] =
+ err_s->dup_req;
+ stats->value[BNG_RE_RES_EXCEED_MAX] =
+ err_s->res_exceed_max;
+ stats->value[BNG_RE_RES_LENGTH_MISMATCH] =
+ err_s->res_length_mismatch;
+ stats->value[BNG_RE_RES_EXCEEDS_WQE] =
+ err_s->res_exceeds_wqe;
+ stats->value[BNG_RE_RES_OPCODE_ERR] =
+ err_s->res_opcode_err;
+ stats->value[BNG_RE_RES_RX_INVALID_RKEY] =
+ err_s->res_rx_invalid_rkey;
+ stats->value[BNG_RE_RES_RX_DOMAIN_ERR] =
+ err_s->res_rx_domain_err;
+ stats->value[BNG_RE_RES_RX_NO_PERM] =
+ err_s->res_rx_no_perm;
+ stats->value[BNG_RE_RES_RX_RANGE_ERR] =
+ err_s->res_rx_range_err;
+ stats->value[BNG_RE_RES_TX_INVALID_RKEY] =
+ err_s->res_tx_invalid_rkey;
+ stats->value[BNG_RE_RES_TX_DOMAIN_ERR] =
+ err_s->res_tx_domain_err;
+ stats->value[BNG_RE_RES_TX_NO_PERM] =
+ err_s->res_tx_no_perm;
+ stats->value[BNG_RE_RES_TX_RANGE_ERR] =
+ err_s->res_tx_range_err;
+ stats->value[BNG_RE_RES_IRRQ_OFLOW] =
+ err_s->res_irrq_oflow;
+ stats->value[BNG_RE_RES_UNSUP_OPCODE] =
+ err_s->res_unsup_opcode;
+ stats->value[BNG_RE_RES_UNALIGNED_ATOMIC] =
+ err_s->res_unaligned_atomic;
+ stats->value[BNG_RE_RES_REM_INV_ERR] =
+ err_s->res_rem_inv_err;
+ stats->value[BNG_RE_RES_MEM_ERROR] =
+ err_s->res_mem_error;
+ stats->value[BNG_RE_RES_SRQ_ERR] =
+ err_s->res_srq_err;
+ stats->value[BNG_RE_RES_CMP_ERR] =
+ err_s->res_cmp_err;
+ stats->value[BNG_RE_RES_INVALID_DUP_RKEY] =
+ err_s->res_invalid_dup_rkey;
+ stats->value[BNG_RE_RES_WQE_FORMAT_ERR] =
+ err_s->res_wqe_format_err;
+ stats->value[BNG_RE_RES_CQ_LOAD_ERR] =
+ err_s->res_cq_load_err;
+ stats->value[BNG_RE_RES_SRQ_LOAD_ERR] =
+ err_s->res_srq_load_err;
+ stats->value[BNG_RE_RES_TX_PCI_ERR] =
+ err_s->res_tx_pci_err;
+ stats->value[BNG_RE_RES_RX_PCI_ERR] =
+ err_s->res_rx_pci_err;
+ stats->value[BNG_RE_OUT_OF_SEQ_ERR] =
+ err_s->res_oos_drop_count;
+ stats->value[BNG_RE_REQ_CQE_ERROR] =
+ err_s->bad_resp_err +
+ err_s->local_qp_op_err +
+ err_s->local_protection_err +
+ err_s->mem_mgmt_op_err +
+ err_s->remote_invalid_req_err +
+ err_s->remote_access_err +
+ err_s->remote_op_err;
+ stats->value[BNG_RE_RESP_CQE_ERROR] =
+ err_s->res_cmp_err +
+ err_s->res_cq_load_err;
+ stats->value[BNG_RE_RESP_REMOTE_ACCESS_ERRS] =
+ err_s->res_rx_no_perm +
+ err_s->res_tx_no_perm;
+}
+
+static void bng_re_copy_ext_stats(struct bng_re_dev *rdev,
+ struct rdma_hw_stats *stats,
+ struct bng_re_ext_stat *s)
+{
+ stats->value[BNG_RE_TX_ATOMIC_REQ] = s->tx_atomic_req;
+ stats->value[BNG_RE_TX_READ_REQ] = s->tx_read_req;
+ stats->value[BNG_RE_TX_READ_RES] = s->tx_read_res;
+ stats->value[BNG_RE_TX_WRITE_REQ] = s->tx_write_req;
+ stats->value[BNG_RE_TX_SEND_REQ] = s->tx_send_req;
+ stats->value[BNG_RE_TX_ROCE_PKTS] = s->tx_roce_pkts;
+ stats->value[BNG_RE_TX_ROCE_BYTES] = s->tx_roce_bytes;
+ stats->value[BNG_RE_RX_ATOMIC_REQ] = s->rx_atomic_req;
+ stats->value[BNG_RE_RX_READ_REQ] = s->rx_read_req;
+ stats->value[BNG_RE_RX_READ_RESP] = s->rx_read_res;
+ stats->value[BNG_RE_RX_WRITE_REQ] = s->rx_write_req;
+ stats->value[BNG_RE_RX_SEND_REQ] = s->rx_send_req;
+ stats->value[BNG_RE_RX_ROCE_PKTS] = s->rx_roce_pkts;
+ stats->value[BNG_RE_RX_ROCE_BYTES] = s->rx_roce_bytes;
+ stats->value[BNG_RE_RX_ROCE_GOOD_PKTS] = s->rx_roce_good_pkts;
+ stats->value[BNG_RE_RX_ROCE_GOOD_BYTES] = s->rx_roce_good_bytes;
+ stats->value[BNG_RE_OOB] = s->rx_out_of_buffer;
+ stats->value[BNG_RE_TX_CNP] = s->tx_cnp;
+ stats->value[BNG_RE_RX_CNP] = s->rx_cnp;
+ stats->value[BNG_RE_RX_ECN] = s->rx_ecn_marked;
+ stats->value[BNG_RE_OUT_OF_SEQ_ERR] = s->rx_out_of_sequence;
+}
+
+static int bng_re_get_ext_stat(struct bng_re_dev *rdev,
+ struct rdma_hw_stats *stats)
+{
+ struct bng_re_ext_stat *estat = &rdev->stats.rstat.ext_stat;
+ u32 fid;
+ int rc;
+
+ fid = PCI_FUNC(rdev->aux_dev->pdev->devfn);
+ rc = bng_re_qext_stat(&rdev->rcfw, fid, estat);
+ if (rc)
+ goto done;
+ bng_re_copy_ext_stats(rdev, stats, estat);
+
+done:
+ return rc;
+}
+
+int bng_re_ib_get_hw_stats(struct ib_device *ibdev,
+ struct rdma_hw_stats *stats,
+ u32 port, int index)
+{
+ struct bng_re_dev *rdev = to_bng_re_dev(ibdev, ibdev);
+ struct bng_roce_stats *err_s = NULL;
+ struct ctx_hw_stats *hw_stats = NULL;
+ int rc = 0;
+
+ hw_stats = rdev->ctx.stats.dma;
+ if (!port || !stats)
+ return -EINVAL;
+
+ if (hw_stats) {
+ stats->value[BNG_RE_RECOVERABLE_ERRORS] =
+ le64_to_cpu(hw_stats->tx_bcast_pkts);
+ stats->value[BNG_RE_TX_DISCARDS] =
+ le64_to_cpu(hw_stats->tx_discard_pkts);
+ stats->value[BNG_RE_TX_ERRORS] =
+ le64_to_cpu(hw_stats->tx_error_pkts);
+ stats->value[BNG_RE_RX_ERRORS] =
+ le64_to_cpu(hw_stats->rx_error_pkts);
+ stats->value[BNG_RE_RX_DISCARDS] =
+ le64_to_cpu(hw_stats->rx_discard_pkts);
+ stats->value[BNG_RE_RX_PKTS] =
+ le64_to_cpu(hw_stats->rx_ucast_pkts);
+ stats->value[BNG_RE_RX_BYTES] =
+ le64_to_cpu(hw_stats->rx_ucast_bytes);
+ stats->value[BNG_RE_TX_PKTS] =
+ le64_to_cpu(hw_stats->tx_ucast_pkts);
+ stats->value[BNG_RE_TX_BYTES] =
+ le64_to_cpu(hw_stats->tx_ucast_bytes);
+ }
+ err_s = &rdev->stats.rstat.errs;
+ if (test_bit(BNG_RE_FLAG_ISSUE_ROCE_STATS, &rdev->flags)) {
+ rc = bng_re_get_roce_stats(&rdev->rcfw, err_s);
+ if (rc) {
+ clear_bit(BNG_RE_FLAG_ISSUE_ROCE_STATS,
+ &rdev->flags);
+ goto done;
+ }
+ bng_re_copy_err_stats(rdev, stats, err_s);
+ if (bng_ext_stats_supported(rdev->chip_ctx, rdev->dev_attr->dev_cap_flags,
+ rdev->bng_res.is_vf)) {
+ rc = bng_re_get_ext_stat(rdev, stats);
+ if (rc) {
+ clear_bit(BNG_RE_FLAG_ISSUE_ROCE_STATS,
+ &rdev->flags);
+ goto done;
+ }
+ }
+ }
+
+done:
+ return BNG_RE_NUM_EXT_COUNTERS;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_hw_counters.h b/drivers/infiniband/hw/bng_re/bng_hw_counters.h
new file mode 100644
index 000000000000..792dbb655f7e
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_hw_counters.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2026 Broadcom
+//
+#ifndef __HW_COUNTERS_H__
+#define __HW_COUNTERS_H__
+
+struct bng_roce_stats;
+struct bng_re_ext_stat;
+
+enum bng_re_hw_enum_stats {
+ BNG_RE_RX_PKTS,
+ BNG_RE_RX_BYTES,
+ BNG_RE_TX_PKTS,
+ BNG_RE_TX_BYTES,
+ BNG_RE_RECOVERABLE_ERRORS,
+ BNG_RE_TX_ERRORS,
+ BNG_RE_TX_DISCARDS,
+ BNG_RE_RX_ERRORS,
+ BNG_RE_RX_DISCARDS,
+ BNG_RE_TO_RETRANSMITS,
+ BNG_RE_SEQ_ERR_NAKS_RCVD,
+ BNG_RE_MAX_RETRY_EXCEEDED,
+ BNG_RE_RNR_NAKS_RCVD,
+ BNG_RE_MISSING_RESP,
+ BNG_RE_UNRECOVERABLE_ERR,
+ BNG_RE_BAD_RESP_ERR,
+ BNG_RE_LOCAL_QP_OP_ERR,
+ BNG_RE_LOCAL_PROTECTION_ERR,
+ BNG_RE_MEM_MGMT_OP_ERR,
+ BNG_RE_REMOTE_INVALID_REQ_ERR,
+ BNG_RE_REMOTE_ACCESS_ERR,
+ BNG_RE_REMOTE_OP_ERR,
+ BNG_RE_DUP_REQ,
+ BNG_RE_RES_EXCEED_MAX,
+ BNG_RE_RES_LENGTH_MISMATCH,
+ BNG_RE_RES_EXCEEDS_WQE,
+ BNG_RE_RES_OPCODE_ERR,
+ BNG_RE_RES_RX_INVALID_RKEY,
+ BNG_RE_RES_RX_DOMAIN_ERR,
+ BNG_RE_RES_RX_NO_PERM,
+ BNG_RE_RES_RX_RANGE_ERR,
+ BNG_RE_RES_TX_INVALID_RKEY,
+ BNG_RE_RES_TX_DOMAIN_ERR,
+ BNG_RE_RES_TX_NO_PERM,
+ BNG_RE_RES_TX_RANGE_ERR,
+ BNG_RE_RES_IRRQ_OFLOW,
+ BNG_RE_RES_UNSUP_OPCODE,
+ BNG_RE_RES_UNALIGNED_ATOMIC,
+ BNG_RE_RES_REM_INV_ERR,
+ BNG_RE_RES_MEM_ERROR,
+ BNG_RE_RES_SRQ_ERR,
+ BNG_RE_RES_CMP_ERR,
+ BNG_RE_RES_INVALID_DUP_RKEY,
+ BNG_RE_RES_WQE_FORMAT_ERR,
+ BNG_RE_RES_CQ_LOAD_ERR,
+ BNG_RE_RES_SRQ_LOAD_ERR,
+ BNG_RE_RES_TX_PCI_ERR,
+ BNG_RE_RES_RX_PCI_ERR,
+ BNG_RE_OUT_OF_SEQ_ERR,
+ BNG_RE_TX_ATOMIC_REQ,
+ BNG_RE_TX_READ_REQ,
+ BNG_RE_TX_READ_RES,
+ BNG_RE_TX_WRITE_REQ,
+ BNG_RE_TX_SEND_REQ,
+ BNG_RE_TX_ROCE_PKTS,
+ BNG_RE_TX_ROCE_BYTES,
+ BNG_RE_RX_ATOMIC_REQ,
+ BNG_RE_RX_READ_REQ,
+ BNG_RE_RX_READ_RESP,
+ BNG_RE_RX_WRITE_REQ,
+ BNG_RE_RX_SEND_REQ,
+ BNG_RE_RX_ROCE_PKTS,
+ BNG_RE_RX_ROCE_BYTES,
+ BNG_RE_RX_ROCE_GOOD_PKTS,
+ BNG_RE_RX_ROCE_GOOD_BYTES,
+ BNG_RE_OOB,
+ BNG_RE_TX_CNP,
+ BNG_RE_RX_CNP,
+ BNG_RE_RX_ECN,
+ BNG_RE_REQ_CQE_ERROR,
+ BNG_RE_RESP_CQE_ERROR,
+ BNG_RE_RESP_REMOTE_ACCESS_ERRS,
+ BNG_RE_NUM_EXT_COUNTERS
+};
+
+struct bng_re_res_cntrs {
+ atomic_t qp_count;
+ atomic_t rc_qp_count;
+ atomic_t ud_qp_count;
+ atomic_t cq_count;
+ atomic_t srq_count;
+ atomic_t mr_count;
+ atomic_t mr_dmabuf_count;
+ atomic_t peer_mem_mr_count;
+ atomic_t mw_count;
+ atomic_t ah_count;
+ atomic_t ah_hw_count;
+ atomic_t pd_count;
+ atomic_t resize_count;
+ atomic_t max_qp_count;
+ atomic_t max_rc_qp_count;
+ atomic_t max_ud_qp_count;
+ atomic_t max_cq_count;
+ atomic_t max_srq_count;
+ atomic_t max_mr_count;
+ atomic_t max_mr_dmabuf_count;
+ atomic_t max_peer_mem_mr_count;
+ atomic_t max_mw_count;
+ atomic_t max_ah_count;
+ atomic_t max_ah_hw_count;
+ atomic_t max_pd_count;
+};
+
+struct rdma_hw_stats *bng_re_alloc_hw_port_stats(struct ib_device *ibdev,
+ u32 port_num);
+int bng_re_ib_get_hw_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats,
+ u32 port, int index);
+#endif /* __HW_COUNTERS_H__ */
diff --git a/drivers/infiniband/hw/bng_re/bng_re.h b/drivers/infiniband/hw/bng_re/bng_re.h
index 70851f1180d0..414768585e67 100644
--- a/drivers/infiniband/hw/bng_re/bng_re.h
+++ b/drivers/infiniband/hw/bng_re/bng_re.h
@@ -9,6 +9,7 @@
#include "bng_res.h"
#include "bng_fw.h"
#include "bng_sp.h"
+#include "bng_hw_counters.h"
#include <rdma/ib_verbs.h>
#define BNG_RE_XID_AVOID_REUSE false
@@ -273,6 +274,7 @@ struct bng_re_dev {
#define BNG_RE_FLAG_NETDEV_REGISTERED 0
#define BNG_RE_FLAG_RCFW_CHANNEL_EN 1
#define BNG_RE_FLAG_TBLS_ALLOC_INITED 2
+#define BNG_RE_FLAG_ISSUE_ROCE_STATS 29
#define BNG_RE_FLAG_MPC_FW_CHANNEL_EN 31
#define BNG_RE_FLAG_MPC_FW_THREAD_EN 32
#define BNG_RE_FLAG_MPC_DB_PAGE_EN 34
@@ -297,6 +299,8 @@ struct bng_re_dev {
struct bng_sp_cq_coal_param cq_coalescing;
DECLARE_HASHTABLE(cq_hash, MAX_CQ_HASH_BITS);
DECLARE_HASHTABLE(srq_hash, 8); /* SRQ hash table */
+ struct bng_re_hw_stats stats;
+ struct bng_re_ctx ctx;
};
#define to_bng_re_dev(ptr, member) \
diff --git a/drivers/infiniband/hw/bng_re/bng_res.h b/drivers/infiniband/hw/bng_re/bng_res.h
index 00429b4a7d30..5a6db1786886 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.h
+++ b/drivers/infiniband/hw/bng_re/bng_res.h
@@ -227,6 +227,15 @@ struct bng_re_res {
/* Serialize access to PD table */
struct mutex pd_tbl_lock;
struct bng_re_rcfw *rcfw;
+ bool is_vf;
+};
+
+struct bng_re_ctx {
+ u32 qpc_count;
+ u32 mrw_count;
+ u32 srqc_count;
+ u32 cq_count;
+ struct bng_re_stats stats;
};
#define to_bng_re_res(ptr, member) container_of(ptr, struct bng_re_res, member)
@@ -394,6 +403,18 @@ static inline u8 bng_re_base_pg_size(struct bng_re_hwq *hwq)
return pg_size;
}
+static inline bool _is_ext_stats_supported(u16 dev_cap_flags)
+{
+ return dev_cap_flags & CREQ_QUERY_FUNC_RESP_SB_EXT_STATS;
+}
+
+static inline int bng_ext_stats_supported(struct bng_re_chip_ctx *ctx,
+ u16 flags, bool virtfn)
+{
+ return (_is_ext_stats_supported(flags) && ((virtfn) || (!virtfn)));
+}
+
+
void bng_re_free_hwq(struct bng_re_res *res,
struct bng_re_hwq *hwq);
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.c b/drivers/infiniband/hw/bng_re/bng_sp.c
index 78e2bb9ad859..26435fe4e892 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.c
+++ b/drivers/infiniband/hw/bng_re/bng_sp.c
@@ -960,3 +960,147 @@ int bng_sp_destroy_srq(struct bng_re_res *res, struct bng_sp_srq *srq)
return 0;
}
+
+int bng_re_get_roce_stats(struct bng_re_rcfw *rcfw, struct bng_roce_stats *stats)
+{
+ struct creq_query_roce_stats_resp resp = {};
+ struct creq_query_roce_stats_resp_sb *sb;
+ struct cmdq_query_roce_stats req = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct bng_rcfw_sbuf sbuf;
+ int rc;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_QUERY_ROCE_STATS, sizeof(req));
+
+ sbuf.size = ALIGN(sizeof(*sb), BNG_RE_CMDQE_UNITS);
+ sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size,
+ &sbuf.dma_addr, GFP_KERNEL);
+ if (!sbuf.sb)
+ return -ENOMEM;
+ sb = sbuf.sb;
+ req.resp_size = sbuf.size / BNG_RE_CMDQE_UNITS;
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req),
+ sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ goto bail;
+ stats->to_retransmits = le64_to_cpu(sb->to_retransmits);
+ stats->seq_err_naks_rcvd = le64_to_cpu(sb->seq_err_naks_rcvd);
+ stats->max_retry_exceeded = le64_to_cpu(sb->max_retry_exceeded);
+ stats->rnr_naks_rcvd = le64_to_cpu(sb->rnr_naks_rcvd);
+ stats->missing_resp = le64_to_cpu(sb->missing_resp);
+ stats->unrecoverable_err = le64_to_cpu(sb->unrecoverable_err);
+ stats->bad_resp_err = le64_to_cpu(sb->bad_resp_err);
+ stats->local_qp_op_err = le64_to_cpu(sb->local_qp_op_err);
+ stats->local_protection_err = le64_to_cpu(sb->local_protection_err);
+ stats->mem_mgmt_op_err = le64_to_cpu(sb->mem_mgmt_op_err);
+ stats->remote_invalid_req_err = le64_to_cpu(sb->remote_invalid_req_err);
+ stats->remote_access_err = le64_to_cpu(sb->remote_access_err);
+ stats->remote_op_err = le64_to_cpu(sb->remote_op_err);
+ stats->dup_req = le64_to_cpu(sb->dup_req);
+ stats->res_exceed_max = le64_to_cpu(sb->res_exceed_max);
+ stats->res_length_mismatch = le64_to_cpu(sb->res_length_mismatch);
+ stats->res_exceeds_wqe = le64_to_cpu(sb->res_exceeds_wqe);
+ stats->res_opcode_err = le64_to_cpu(sb->res_opcode_err);
+ stats->res_rx_invalid_rkey = le64_to_cpu(sb->res_rx_invalid_rkey);
+ stats->res_rx_domain_err = le64_to_cpu(sb->res_rx_domain_err);
+ stats->res_rx_no_perm = le64_to_cpu(sb->res_rx_no_perm);
+ stats->res_rx_range_err = le64_to_cpu(sb->res_rx_range_err);
+ stats->res_tx_invalid_rkey = le64_to_cpu(sb->res_tx_invalid_rkey);
+ stats->res_tx_domain_err = le64_to_cpu(sb->res_tx_domain_err);
+ stats->res_tx_no_perm = le64_to_cpu(sb->res_tx_no_perm);
+ stats->res_tx_range_err = le64_to_cpu(sb->res_tx_range_err);
+ stats->res_irrq_oflow = le64_to_cpu(sb->res_irrq_oflow);
+ stats->res_unsup_opcode = le64_to_cpu(sb->res_unsup_opcode);
+ stats->res_unaligned_atomic = le64_to_cpu(sb->res_unaligned_atomic);
+ stats->res_rem_inv_err = le64_to_cpu(sb->res_rem_inv_err);
+ stats->res_mem_error = le64_to_cpu(sb->res_mem_error);
+ stats->res_srq_err = le64_to_cpu(sb->res_srq_err);
+ stats->res_cmp_err = le64_to_cpu(sb->res_cmp_err);
+ stats->res_invalid_dup_rkey = le64_to_cpu(sb->res_invalid_dup_rkey);
+ stats->res_wqe_format_err = le64_to_cpu(sb->res_wqe_format_err);
+ stats->res_cq_load_err = le64_to_cpu(sb->res_cq_load_err);
+ stats->res_srq_load_err = le64_to_cpu(sb->res_srq_load_err);
+ stats->res_tx_pci_err = le64_to_cpu(sb->res_tx_pci_err);
+ stats->res_rx_pci_err = le64_to_cpu(sb->res_rx_pci_err);
+ if (!rcfw->init_oos_stats) {
+ rcfw->oos_prev = le64_to_cpu(sb->res_oos_drop_count);
+ rcfw->init_oos_stats = 1;
+ } else {
+ stats->res_oos_drop_count +=
+ (le64_to_cpu(sb->res_oos_drop_count) -
+ rcfw->oos_prev) & BNG_RE_OOS_COUNT_MASK;
+ rcfw->oos_prev = le64_to_cpu(sb->res_oos_drop_count);
+ }
+
+bail:
+ dma_free_coherent(&rcfw->pdev->dev, sbuf.size,
+ sbuf.sb, sbuf.dma_addr);
+ return rc;
+}
+
+int bng_re_qext_stat(struct bng_re_rcfw *rcfw, u32 fid,
+ struct bng_re_ext_stat *estat)
+{
+ struct creq_query_roce_stats_ext_resp resp = {};
+ struct creq_query_roce_stats_ext_resp_sb *sb;
+ struct cmdq_query_roce_stats_ext req = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct bng_rcfw_sbuf sbuf;
+ int rc;
+
+ sbuf.size = ALIGN(sizeof(*sb), BNG_RE_CMDQE_UNITS);
+ sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size,
+ &sbuf.dma_addr, GFP_KERNEL);
+ if (!sbuf.sb)
+ return -ENOMEM;
+
+ sb = sbuf.sb;
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_QUERY_ROCE_STATS_EXT_OPCODE_QUERY_ROCE_STATS,
+ sizeof(req));
+
+ req.resp_size = sbuf.size / BNG_RE_CMDQE_UNITS;
+ req.resp_addr = cpu_to_le64(sbuf.dma_addr);
+ if (rcfw->res->is_vf)
+ req.function_id =
+ cpu_to_le32(CMDQ_QUERY_ROCE_STATS_EXT_VF_VALID |
+ (fid << CMDQ_QUERY_ROCE_STATS_EXT_VF_NUM_SFT));
+ else
+ req.function_id = cpu_to_le32(fid);
+ req.flags = cpu_to_le16(CMDQ_QUERY_ROCE_STATS_EXT_FLAGS_FUNCTION_ID);
+
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req),
+ sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ goto bail;
+
+ estat->tx_atomic_req = le64_to_cpu(sb->tx_atomic_req_pkts);
+ estat->tx_read_req = le64_to_cpu(sb->tx_read_req_pkts);
+ estat->tx_read_res = le64_to_cpu(sb->tx_read_res_pkts);
+ estat->tx_write_req = le64_to_cpu(sb->tx_write_req_pkts);
+ estat->tx_send_req = le64_to_cpu(sb->tx_send_req_pkts);
+ estat->tx_roce_pkts = le64_to_cpu(sb->tx_roce_pkts);
+ estat->tx_roce_bytes = le64_to_cpu(sb->tx_roce_bytes);
+ estat->rx_atomic_req = le64_to_cpu(sb->rx_atomic_req_pkts);
+ estat->rx_read_req = le64_to_cpu(sb->rx_read_req_pkts);
+ estat->rx_read_res = le64_to_cpu(sb->rx_read_res_pkts);
+ estat->rx_write_req = le64_to_cpu(sb->rx_write_req_pkts);
+ estat->rx_send_req = le64_to_cpu(sb->rx_send_req_pkts);
+ estat->rx_roce_pkts = le64_to_cpu(sb->rx_roce_pkts);
+ estat->rx_roce_bytes = le64_to_cpu(sb->rx_roce_bytes);
+ estat->rx_roce_good_pkts = le64_to_cpu(sb->rx_roce_good_pkts);
+ estat->rx_roce_good_bytes = le64_to_cpu(sb->rx_roce_good_bytes);
+ estat->rx_out_of_buffer = le64_to_cpu(sb->rx_out_of_buffer_pkts);
+ estat->rx_out_of_sequence = le64_to_cpu(sb->rx_out_of_sequence_pkts);
+ estat->tx_cnp = le64_to_cpu(sb->tx_cnp_pkts);
+ estat->rx_cnp = le64_to_cpu(sb->rx_cnp_pkts);
+ estat->rx_ecn_marked = le64_to_cpu(sb->rx_ecn_marked_pkts);
+
+bail:
+ dma_free_coherent(&rcfw->pdev->dev, sbuf.size,
+ sbuf.sb, sbuf.dma_addr);
+ return rc;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.h b/drivers/infiniband/hw/bng_re/bng_sp.h
index da4dceba4f46..d69d97e1940b 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.h
+++ b/drivers/infiniband/hw/bng_re/bng_sp.h
@@ -191,6 +191,86 @@ struct bng_sp_srq {
u16 eventq_hw_ring_id;
};
+struct bng_roce_stats {
+ u64 to_retransmits;
+ u64 seq_err_naks_rcvd;
+ u64 max_retry_exceeded;
+ u64 rnr_naks_rcvd;
+ u64 missing_resp;
+ u64 unrecoverable_err;
+ u64 bad_resp_err;
+ u64 local_qp_op_err;
+ u64 local_protection_err;
+ u64 mem_mgmt_op_err;
+ u64 remote_invalid_req_err;
+ u64 remote_access_err;
+ u64 remote_op_err;
+ u64 dup_req;
+ u64 res_exceed_max;
+ u64 res_length_mismatch;
+ u64 res_exceeds_wqe;
+ u64 res_opcode_err;
+ u64 res_rx_invalid_rkey;
+ u64 res_rx_domain_err;
+ u64 res_rx_no_perm;
+ u64 res_rx_range_err;
+ u64 res_tx_invalid_rkey;
+ u64 res_tx_domain_err;
+ u64 res_tx_no_perm;
+ u64 res_tx_range_err;
+ u64 res_irrq_oflow;
+ u64 res_unsup_opcode;
+ u64 res_unaligned_atomic;
+ u64 res_rem_inv_err;
+ u64 res_mem_error;
+ u64 res_srq_err;
+ u64 res_cmp_err;
+ u64 res_invalid_dup_rkey;
+ u64 res_wqe_format_err;
+ u64 res_cq_load_err;
+ u64 res_srq_load_err;
+ u64 res_tx_pci_err;
+ u64 res_rx_pci_err;
+ u64 res_oos_drop_count;
+ u64 active_qp_count_p0;
+ u64 active_qp_count_p1;
+ u64 active_qp_count_p2;
+ u64 active_qp_count_p3;
+};
+
+struct bng_re_ext_stat {
+ u64 tx_atomic_req;
+ u64 tx_read_req;
+ u64 tx_read_res;
+ u64 tx_write_req;
+ u64 tx_send_req;
+ u64 tx_roce_pkts;
+ u64 tx_roce_bytes;
+ u64 rx_atomic_req;
+ u64 rx_read_req;
+ u64 rx_read_res;
+ u64 rx_write_req;
+ u64 rx_send_req;
+ u64 rx_roce_pkts;
+ u64 rx_roce_bytes;
+ u64 rx_roce_good_pkts;
+ u64 rx_roce_good_bytes;
+ u64 rx_out_of_buffer;
+ u64 rx_out_of_sequence;
+ u64 tx_cnp;
+ u64 rx_cnp;
+ u64 rx_ecn_marked;
+};
+
+struct bng_re_rstat {
+ struct bng_roce_stats errs;
+ struct bng_re_ext_stat ext_stat;
+};
+
+struct bng_re_hw_stats {
+ struct bng_re_rstat rstat;
+};
+
int bng_re_get_dev_attr(struct bng_re_rcfw *rcfw);
int bng_sp_alloc_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl, u16 size);
@@ -228,5 +308,9 @@ int bng_sp_destroy_cq(struct bng_re_res *res, struct bng_sp_cq *cq);
int bng_sp_create_srq(struct bng_re_res *res, struct bng_sp_srq *srq);
int bng_sp_query_srq(struct bng_re_res *res, struct bng_sp_srq *srq);
int bng_sp_destroy_srq(struct bng_re_res *res, struct bng_sp_srq *srq);
+int bng_re_qext_stat(struct bng_re_rcfw *rcfw, u32 fid,
+ struct bng_re_ext_stat *estat);
+int bng_re_get_roce_stats(struct bng_re_rcfw *rcfw,
+ struct bng_roce_stats *stats);
#endif
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 13/15] RDMA/bng_re: Add AH verbs
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (11 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 12/15] RDMA/bng_re: Add Stats verbs Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 14/15] RDMA/bng_re: Add QP verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 15/15] RDMA/bng_re: Register with ib-core Siva Reddy Kallam
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Mohammed Faraaz,
Siva Reddy Kallam
From: Mohammed Faraaz <mohammed.faraaz@broadcom.com>
This patch adds below verbs.
- bng_re_create_ah
- bng_re_destroy_ah
- bng_re_query_ah
Signed-off-by: Mohammed Faraaz <mohammed.faraaz@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/bng_dev.c | 16 ++
drivers/infiniband/hw/bng_re/bng_re.h | 2 +
drivers/infiniband/hw/bng_re/bng_roce_hsi.h | 65 ++++++
drivers/infiniband/hw/bng_re/bng_sp.c | 120 +++++++++++
drivers/infiniband/hw/bng_re/bng_sp.h | 19 ++
drivers/infiniband/hw/bng_re/bng_verbs.c | 215 ++++++++++++++++++++
drivers/infiniband/hw/bng_re/bng_verbs.h | 29 +++
include/uapi/rdma/bng_re-abi.h | 5 +
8 files changed, 471 insertions(+)
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index 978b7efab03e..3ebdd777eca3 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -64,6 +64,11 @@ static const struct ib_device_ops bng_re_dev_ops = {
.destroy_srq = bng_re_destroy_srq,
.alloc_hw_port_stats = bng_re_alloc_hw_port_stats,
.get_hw_stats = bng_re_ib_get_hw_stats,
+ .create_ah = bng_re_create_ah,
+ .create_user_ah = bng_re_create_ah,
+ .destroy_ah = bng_re_destroy_ah,
+ .query_ah = bng_re_query_ah,
+ INIT_RDMA_OBJ_SIZE(ib_ah, bng_re_ah, ib_ah),
INIT_RDMA_OBJ_SIZE(ib_srq, bng_re_srq, ib_srq),
INIT_RDMA_OBJ_SIZE(ib_cq, bng_re_cq, ib_cq),
INIT_RDMA_OBJ_SIZE(ib_pd, bng_re_pd, ib_pd),
@@ -333,6 +338,11 @@ static void bng_re_dev_uninit(struct bng_re_dev *rdev)
{
int rc;
+ if (rdev->dest_ah_wq) {
+ destroy_workqueue(rdev->dest_ah_wq);
+ rdev->dest_ah_wq = NULL;
+ }
+
if (rdev->bng_res.dpi_tbl.max)
bng_re_dealloc_dpi(&rdev->bng_res,
&rdev->dpi_privileged);
@@ -504,6 +514,12 @@ static int bng_re_dev_init(struct bng_re_dev *rdev)
rdev, BNG_RE_DPI_TYPE_KERNEL);
if (rc)
goto deinit_mpc;
+ rdev->dest_ah_wq = alloc_ordered_workqueue("bng_re_dest_ah", 0);
+ if (!rdev->dest_ah_wq) {
+ rc = -ENOMEM;
+ goto deinit_mpc;
+ }
+ refcount_set(&rdev->pos_destah_cnt, 0);
return 0;
deinit_mpc:
diff --git a/drivers/infiniband/hw/bng_re/bng_re.h b/drivers/infiniband/hw/bng_re/bng_re.h
index 414768585e67..217383a2b252 100644
--- a/drivers/infiniband/hw/bng_re/bng_re.h
+++ b/drivers/infiniband/hw/bng_re/bng_re.h
@@ -301,6 +301,8 @@ struct bng_re_dev {
DECLARE_HASHTABLE(srq_hash, 8); /* SRQ hash table */
struct bng_re_hw_stats stats;
struct bng_re_ctx ctx;
+ struct workqueue_struct *dest_ah_wq;
+ refcount_t pos_destah_cnt;
};
#define to_bng_re_dev(ptr, member) \
diff --git a/drivers/infiniband/hw/bng_re/bng_roce_hsi.h b/drivers/infiniband/hw/bng_re/bng_roce_hsi.h
index 6c9d45464ef6..3001b9d4d056 100644
--- a/drivers/infiniband/hw/bng_re/bng_roce_hsi.h
+++ b/drivers/infiniband/hw/bng_re/bng_roce_hsi.h
@@ -6541,4 +6541,69 @@ struct mpc_event_resp_cmpl {
__le32 resp_data;
};
+#define AH_MODIFY_DATA_AH_MODIFY_FLAGS_AH_MODIFY_FLAGS_AH_CREATE 0x1UL
+#define AH_MODIFY_DATA_AH_MODIFY_FLAGS_AH_MODIFY_FLAGS_AH_DESTROY 0x2UL
+#define AH_MODIFY_DATA_AH_MODIFY_FLAGS_AH_MODIFY_FLAGS_XID_FIRST_USE 0x4UL
+
+struct ah_modify_data {
+ __le32 ah_modify_flags;
+ __le32 reserved_0;
+ u8 type;
+ u8 unused_1[3];
+ __le32 pd_id;
+ u8 hop_limit;
+ u8 traffic_class;
+ __le16 sgid_index;
+ __le32 dest_vlan_id_flow_label;
+ __le64 ah_handle;
+ __le32 dgid[4];
+ __le16 dest_mac[3];
+ u8 enable_cc;
+ u8 unused_2[1];
+ __le32 xid;
+ __le32 reserved_1;
+};
+
+struct mpc_ah_modify_cmd {
+ u8 req_type;
+ u8 req_subtype;
+ __le16 cookie;
+ __le16 target_id;
+ u8 resp_size;
+ u8 reserved;
+ __le64 resp_addr;
+ __le32 ah_modify_flags;
+ __le32 reserved_0;
+ u8 type;
+ u8 unused_1[3];
+ __le32 pd_id;
+ u8 hop_limit;
+ u8 traffic_class;
+ __le16 sgid_index;
+ __le32 dest_vlan_id_flow_label;
+ __le64 ah_handle;
+ __le32 dgid[4];
+ __le16 dest_mac[3];
+ u8 enable_cc;
+ u8 unused_2[1];
+ __le32 xid;
+ __le32 reserved_1;
+};
+
+struct mpc_ah_modify_cmpl {
+ u8 cmpl_type_reserved;
+ u8 error_code_mp_client;
+ u8 req_type;
+ u8 req_subtype;
+ __le32 opaque;
+ u8 v;
+ u8 reserved_0[7];
+ __le32 xid;
+ __le16 error_code;
+ u8 reserved_1[2];
+ u8 v2;
+ u8 reserved_2[3];
+ __le32 reserved_3;
+};
+
#endif /* _BNG_RE_HSI_H_ */
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.c b/drivers/infiniband/hw/bng_re/bng_sp.c
index 26435fe4e892..b44af599e675 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.c
+++ b/drivers/infiniband/hw/bng_re/bng_sp.c
@@ -8,6 +8,8 @@
#include "bng_sp.h"
#include "bng_tlv.h"
#include "bng_re.h"
+#include "bng_re_mpc_roce.h"
+#include "bng_fp.h"
const struct bng_re_gid bng_re_gid_zero = {{0,}};
@@ -1104,3 +1106,121 @@ int bng_re_qext_stat(struct bng_re_rcfw *rcfw, u32 fid,
sbuf.sb, sbuf.dma_addr);
return rc;
}
+
+static int bng_sp_fill_mpc_ah_modify_parms(struct bng_re_res *res,
+ struct ah_modify_data *data,
+ struct bng_sp_ah *ah,
+ bool for_destroy)
+{
+ u32 flags = 0;
+
+ if (for_destroy) {
+ flags |= AH_MODIFY_DATA_AH_MODIFY_FLAGS_AH_MODIFY_FLAGS_AH_DESTROY;
+ } else {
+ flags |= AH_MODIFY_DATA_AH_MODIFY_FLAGS_AH_MODIFY_FLAGS_AH_CREATE;
+ flags |= AH_MODIFY_DATA_AH_MODIFY_FLAGS_AH_MODIFY_FLAGS_XID_FIRST_USE;
+ }
+ data->ah_modify_flags = cpu_to_le32(flags);
+ data->ah_handle = cpu_to_le64((u64)ah);
+ data->xid = cpu_to_le32(ah->id);
+
+ if (!for_destroy) {
+ if (ah->pd)
+ data->pd_id = cpu_to_le32(ah->pd->id);
+ data->hop_limit = ah->hop_limit;
+ data->traffic_class = ah->traffic_class;
+ data->sgid_index = cpu_to_le16(ah->sgid_index);
+ data->dest_vlan_id_flow_label = cpu_to_le32(ah->flow_label | (ah->vlan_id << 16));
+ memcpy(data->dgid, ah->dgid.data, sizeof(data->dgid));
+ memcpy(data->dest_mac, ah->dmac, sizeof(ah->dmac));
+ }
+
+ dev_dbg(&res->pdev->dev, "MPC: AH modify params - AH ID=%d, flags=0x%x, for_destroy=%s\n",
+ ah->id, flags, for_destroy ? "true" : "false");
+
+ return 0;
+}
+
+int bng_sp_fill_and_send_mpc_ah_modify(struct bng_re_res *res,
+ struct bng_sp_ah **ahs,
+ int num_ahs, bool for_destroy, bool block)
+{
+ struct bng_re_dev *rdev = to_bng_re_dev(res, bng_res);
+ struct mpc_ah_modify_cmpl resp;
+ struct mpc_ah_modify_cmd cmd;
+ struct ah_modify_data *data;
+ struct bng_sp_ah *ah;
+ int rc = 0;
+
+ /* for 1st iteration, don't allow batch */
+ if (num_ahs != 1)
+ return -EINVAL;
+
+ ah = *ahs;
+
+ if (!for_destroy) {
+ if (res->is_vf)
+ return -EOPNOTSUPP;
+
+ if (res->ah_xids) {
+ int xid;
+
+ xid = bng_re_xm_alloc_range(res->ah_xids, 1, 0);
+ if (xid < 0) {
+ dev_err(&res->pdev->dev,
+ "%s - error allocating ah xid\n", __func__);
+ return -ENOMEM;
+ }
+ ah->id = xid;
+ } else {
+ ah->id = 1;
+ }
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&resp, 0, sizeof(resp));
+ data = (struct ah_modify_data *)&cmd.ah_modify_flags;
+
+ bng_sp_fill_mpc_ah_modify_parms(res, data, ah, for_destroy);
+
+ cmd.resp_size = sizeof(resp) / BNG_RE_MPC_CQ_STRIDE;
+
+ /* Set MPC command type based on VF vs PF context */
+ if (res->is_vf) {
+ cmd.req_type = MPC_CMD_HDR_REQ_TYPE_PFVF;
+ cmd.req_subtype = MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_AH_MODIFY;
+ dev_dbg(&res->pdev->dev, "MPC: [%s] VF path\n", __func__);
+ } else {
+ cmd.req_type = MPC_CMD_HDR_REQ_TYPE_RCA;
+ cmd.req_subtype = MPC_CMD_HDR_REQ_SUB_TYPE_RCA_AH_MODIFY;
+ dev_dbg(&res->pdev->dev, "MPC: [%s] PF path\n", __func__);
+ }
+
+ dev_dbg(&res->pdev->dev, "MPC: %s - sending to mpc: AH ID=%d, block=%s\n",
+ __func__, ah->id, block ? "true" : "false");
+
+ if (!rdev) {
+ dev_err(&res->pdev->dev, "%s: no rdev for MPC xmit\n", __func__);
+ return -ENODEV;
+ }
+
+ rc = bng_re_roce_mpc_xmit(rdev, &cmd, sizeof(cmd), &resp, sizeof(resp), block);
+
+ if (rc < 0) {
+ dev_err(&res->pdev->dev, "%s - error sending mpc:%d,%d",
+ __func__, ah->id, rc);
+
+ /* Cleanup on error */
+ if (!res->is_vf && res->ah_xids)
+ bng_re_xm_free_range(res->ah_xids, ah->id, true);
+ return rc;
+ }
+
+ dev_info(&res->pdev->dev, "MPC: AH %s completed for AH ID=%d\n",
+ for_destroy ? "destroy" : "create", ah->id);
+
+ if (for_destroy && !res->is_vf && res->ah_xids)
+ bng_re_xm_free_range(res->ah_xids, ah->id, true);
+
+ return rc;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.h b/drivers/infiniband/hw/bng_re/bng_sp.h
index d69d97e1940b..a6f688c0af3d 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.h
+++ b/drivers/infiniband/hw/bng_re/bng_sp.h
@@ -271,6 +271,22 @@ struct bng_re_hw_stats {
struct bng_re_rstat rstat;
};
+struct bng_sp_ah {
+ struct bng_re_gid dgid;
+ struct bng_sp_pd *pd;
+ u32 id;
+ u8 sgid_index;
+ /* For Query AH if the hw table and SW table are different */
+ u8 host_sgid_index;
+ u8 traffic_class;
+ u32 flow_label;
+ u8 hop_limit;
+ u8 sl;
+ u8 dmac[6];
+ u16 vlan_id;
+ u8 nw_type;
+};
+
int bng_re_get_dev_attr(struct bng_re_rcfw *rcfw);
int bng_sp_alloc_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl, u16 size);
@@ -312,5 +328,8 @@ int bng_re_qext_stat(struct bng_re_rcfw *rcfw, u32 fid,
struct bng_re_ext_stat *estat);
int bng_re_get_roce_stats(struct bng_re_rcfw *rcfw,
struct bng_roce_stats *stats);
+int bng_sp_fill_and_send_mpc_ah_modify(struct bng_re_res *res,
+ struct bng_sp_ah **ahs,
+ int num_ahs, bool for_destroy, bool block);
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.c b/drivers/infiniband/hw/bng_re/bng_verbs.c
index e48caefa64ee..c40f8686b6f6 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.c
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.c
@@ -1412,3 +1412,218 @@ int bng_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata)
return 0;
}
+
+void bng_re_posted_destroy_ah(struct work_struct *work)
+{
+ struct bng_destroy_ah *ah = container_of(work, struct bng_destroy_ah, dwork.work);
+ struct bng_re_dev *rdev = ah->rdev;
+ struct bng_sp_ah *ahs[1];
+ u32 ah_ref_cnt = 0;
+ int rc = 0;
+
+ ahs[0] = &ah->sp_ah;
+
+ ah_ref_cnt = refcount_read(ah->ah_ref_cnt);
+ if (rdev->dest_ah_wq && ah_ref_cnt && ah->retry) {
+ ah->retry--;
+ queue_delayed_work(rdev->dest_ah_wq, &ah->dwork, BNG_RE_AH_DEST_DELAY);
+ return;
+ }
+
+ if (!ah->retry)
+ dev_dbg(rdev_to_dev(rdev), "Non-zero AH reference %d for AH id %d\n",
+ ah_ref_cnt, ah->sp_ah.id);
+
+ dev_dbg(&rdev->ibdev.dev,
+ "MPC: %s - sending AH modify(destroy) for: res:%p, ah:%p\n",
+ __func__, &rdev->bng_res, &ah->sp_ah);
+ rc = bng_sp_fill_and_send_mpc_ah_modify(&rdev->bng_res,
+ ahs, 1, true, false);
+ if (rc)
+ dev_err_ratelimited(&rdev->ibdev.dev,
+ "%s failed to destroy AH id = %d rc = %d",
+ __func__, ah->sp_ah.id, rc);
+ if (!ah_ref_cnt) {
+ kfree(ah->ah_ref_cnt);
+ ah->ah_ref_cnt = NULL;
+ }
+ kfree(ah);
+ refcount_dec(&rdev->pos_destah_cnt);
+}
+
+static u8 bng_re_stack_to_dev_nw_type(enum rdma_network_type ntype)
+{
+ u8 nw_type;
+
+ switch (ntype) {
+ case RDMA_NETWORK_IPV4:
+ nw_type = CMDQ_CREATE_AH_TYPE_V2IPV4;
+ break;
+ case RDMA_NETWORK_IPV6:
+ nw_type = CMDQ_CREATE_AH_TYPE_V2IPV6;
+ break;
+ default:
+ nw_type = CMDQ_CREATE_AH_TYPE_V1;
+ break;
+ }
+ return nw_type;
+}
+
+int bng_re_create_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *init_attr,
+ struct ib_udata *udata)
+{
+ struct ib_pd *ibpd = ibah->pd;
+ struct rdma_ah_attr *ah_attr = init_attr->ah_attr;
+ const struct ib_global_route *grh = rdma_ah_read_grh(ah_attr);
+ struct bng_re_dev *rdev = to_bng_re_dev(ibpd->device, ibdev);
+ struct bng_re_ah *ah = to_bng_re_ah(ibah);
+ const struct ib_gid_attr *sgid_attr;
+ struct bng_re_gid_ctx *ctx;
+ enum rdma_network_type nw_type;
+ int rc;
+
+ if (!(rdma_ah_get_ah_flags(ah_attr) & IB_AH_GRH)) {
+ ibdev_err(&rdev->ibdev, "Failed to create AH: GRH not set");
+ return -EINVAL;
+ }
+
+ ah->rdev = rdev;
+ if (ibpd) {
+ struct bng_re_pd *pd = container_of(ibpd, struct bng_re_pd, ib_pd);
+
+ ah->sp_ah.pd = &pd->sp_pd;
+ }
+
+ /* Supply the configuration for the HW */
+ memcpy(ah->sp_ah.dgid.data, grh->dgid.raw, sizeof(union ib_gid));
+ sgid_attr = grh->sgid_attr;
+ /* Get the HW context of the GID. The reference
+ * of GID table entry is already taken by the caller.
+ */
+ ctx = rdma_read_gid_hw_context(sgid_attr);
+ if (!ctx) {
+ ibdev_err(&rdev->ibdev, "Failed to read GID HW context");
+ return -EINVAL;
+ }
+ ah->sp_ah.sgid_index = ctx->idx;
+ ah->sp_ah.host_sgid_index = grh->sgid_index;
+ ah->sp_ah.flow_label = grh->flow_label;
+ ah->sp_ah.hop_limit = grh->hop_limit;
+ ah->sp_ah.traffic_class = grh->traffic_class;
+ ah->sp_ah.sl = rdma_ah_get_sl(ah_attr);
+
+ /* Get network type from GID attribute */
+ nw_type = rdma_gid_attr_network_type(sgid_attr);
+ ah->sp_ah.nw_type = bng_re_stack_to_dev_nw_type(nw_type);
+
+ memcpy(ah->sp_ah.dmac, ah_attr->roce.dmac, ETH_ALEN);
+
+ struct bng_sp_ah *ahp[1] = { &ah->sp_ah };
+
+ dev_dbg(&rdev->ibdev.dev, "MPC: %s - sending AH modify(create) for: res:%p, ah:%p\n",
+ __func__, &rdev->bng_res, &ah->sp_ah);
+ rc = bng_sp_fill_and_send_mpc_ah_modify(&rdev->bng_res,
+ ahp, 1, false, false);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to create HW AH");
+ return rc;
+ }
+
+ /* Allocate and initialize reference count for deferred destruction */
+ ah->ref_cnt = kzalloc_obj(*ah->ref_cnt, GFP_KERNEL);
+ if (!ah->ref_cnt) {
+ rc = -ENOMEM;
+ goto fail_destroy;
+ }
+ refcount_set(ah->ref_cnt, 1);
+
+ if (udata) {
+ struct bng_re_ah_resp resp = {};
+
+ resp.ah_id = ah->sp_ah.id;
+ rc = ib_copy_to_udata(udata, &resp,
+ min(udata->outlen, sizeof(resp)));
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to copy AH response");
+ goto fail_ref;
+ }
+ }
+
+ ibdev_dbg(&rdev->ibdev, "AH created with ID: %d", ah->sp_ah.id);
+ return 0;
+
+fail_ref:
+ kfree(ah->ref_cnt);
+ ah->ref_cnt = NULL;
+fail_destroy:
+ struct bng_sp_ah *ahs[1] = { &ah->sp_ah };
+
+ bng_sp_fill_and_send_mpc_ah_modify(&rdev->bng_res,
+ ahs, 1, true, true);
+ return rc;
+}
+
+int bng_re_destroy_ah(struct ib_ah *ib_ah, u32 flags)
+{
+ struct bng_re_ah *ah = to_bng_re_ah(ib_ah);
+ struct bng_re_dev *rdev = ah->rdev;
+ struct bng_destroy_ah *pos_ah;
+ struct bng_sp_ah *ahs[1];
+ bool zero_ref = false;
+ bool block = true;
+ int rc = 0;
+
+ block = !!(flags & RDMA_DESTROY_AH_SLEEPABLE);
+ ahs[0] = &ah->sp_ah;
+
+ zero_ref = refcount_dec_and_test(ah->ref_cnt);
+ if (rdev->dest_ah_wq && (block || !zero_ref)) {
+ pos_ah = kzalloc_obj(*pos_ah, GFP_ATOMIC);
+ if (!pos_ah) {
+ rc = bng_sp_fill_and_send_mpc_ah_modify(&rdev->bng_res,
+ ahs, 1, true, block);
+ if (rc)
+ dev_err_ratelimited(&rdev->ibdev.dev,
+ "%s id = %d dest_ah failed rc = %d",
+ __func__, ah->sp_ah.id, rc);
+ if (zero_ref)
+ kfree(ah->ref_cnt);
+ } else {
+ INIT_DELAYED_WORK(&pos_ah->dwork, bng_re_posted_destroy_ah);
+ pos_ah->rdev = rdev;
+ pos_ah->ah_ref_cnt = ah->ref_cnt;
+ pos_ah->retry = BNG_RE_AH_DEST_RETRY;
+ memcpy(&pos_ah->sp_ah, &ah->sp_ah, sizeof(pos_ah->sp_ah));
+ refcount_inc(&rdev->pos_destah_cnt);
+ queue_delayed_work(rdev->dest_ah_wq, &pos_ah->dwork, 0);
+ /* We queued it, do not free ah->ref_cnt yet! */
+ }
+ } else {
+ rc = bng_sp_fill_and_send_mpc_ah_modify(&rdev->bng_res,
+ ahs, 1, true, block);
+ if (rc)
+ dev_err_ratelimited(&rdev->ibdev.dev,
+ "%s id = %d dest_ah failed rc = %d",
+ __func__, ah->sp_ah.id, rc);
+ if (zero_ref)
+ kfree(ah->ref_cnt);
+ }
+ return 0;
+}
+
+int bng_re_query_ah(struct ib_ah *ib_ah, struct rdma_ah_attr *ah_attr)
+{
+ struct bng_re_ah *ah = to_bng_re_ah(ib_ah);
+
+ ah_attr->type = ib_ah->type;
+ ah_attr->ah_flags = IB_AH_GRH;
+ rdma_ah_set_sl(ah_attr, ah->sp_ah.sl);
+ memcpy(ah_attr->roce.dmac, ah->sp_ah.dmac, ETH_ALEN);
+ rdma_ah_set_grh(ah_attr, NULL, 0,
+ ah->sp_ah.host_sgid_index,
+ 0, ah->sp_ah.traffic_class);
+ rdma_ah_set_dgid_raw(ah_attr, ah->sp_ah.dgid.data);
+ rdma_ah_set_port_num(ah_attr, 1);
+ rdma_ah_set_static_rate(ah_attr, 0);
+ return 0;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.h b/drivers/infiniband/hw/bng_re/bng_verbs.h
index dd9fac0f33c0..c5643ee70885 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.h
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.h
@@ -75,6 +75,30 @@ struct bng_re_srq {
struct hlist_node hash_entry;
};
+/* Deferred AH destruction constants */
+#define BNG_RE_AH_DEST_DELAY msecs_to_jiffies(5000)
+#define BNG_RE_AH_DEST_RETRY 10
+
+struct bng_destroy_ah {
+ struct delayed_work dwork;
+ struct bng_re_dev *rdev;
+ struct bng_sp_ah sp_ah;
+ refcount_t *ah_ref_cnt;
+ u8 retry;
+};
+
+struct bng_re_ah {
+ struct ib_ah ib_ah;
+ struct bng_re_dev *rdev;
+ struct bng_sp_ah sp_ah;
+ refcount_t *ref_cnt;
+};
+
+static inline struct bng_re_ah *to_bng_re_ah(struct ib_ah *ibah)
+{
+ return container_of(ibah, struct bng_re_ah, ib_ah);
+}
+
static inline u32 bng_re_init_depth(u32 ent, struct bng_re_ucontext *uctx)
{
/* roundup_pow_of_two(0) is undefined (UBSAN: shift by 64 on 64-bit). */
@@ -147,6 +171,11 @@ int bng_re_modify_srq(struct ib_srq *ib_srq, struct ib_srq_attr *srq_attr,
struct ib_udata *udata);
int bng_re_query_srq(struct ib_srq *ib_srq, struct ib_srq_attr *srq_attr);
int bng_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata);
+int bng_re_create_ah(struct ib_ah *ib_ah, struct rdma_ah_init_attr *init_attr,
+ struct ib_udata *udata);
+int bng_re_destroy_ah(struct ib_ah *ib_ah, u32 flags);
+int bng_re_query_ah(struct ib_ah *ib_ah, struct rdma_ah_attr *ah_attr);
+void bng_re_posted_destroy_ah(struct work_struct *work);
#endif /* __BNG_RE_VERBS_H__ */
diff --git a/include/uapi/rdma/bng_re-abi.h b/include/uapi/rdma/bng_re-abi.h
index 49931b7c1344..36bc902600be 100644
--- a/include/uapi/rdma/bng_re-abi.h
+++ b/include/uapi/rdma/bng_re-abi.h
@@ -75,4 +75,9 @@ struct bng_re_srq_resp {
__aligned_u64 comp_mask;
};
+struct bng_re_ah_resp {
+ __u32 ah_id;
+ __u64 comp_mask;
+} __attribute__((packed));
+
#endif /* __BNG_RE_UVERBS_ABI_H__*/
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 14/15] RDMA/bng_re: Add QP verbs
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (12 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 13/15] RDMA/bng_re: Add AH verbs Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 15/15] RDMA/bng_re: Register with ib-core Siva Reddy Kallam
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Usman S. Ansari,
Siva Reddy Kallam
From: "Usman S. Ansari" <usman.ansari@broadcom.com>
This patch adds below verbs.
-bng_re_query_qp
-bng_re_create_qp
-bng_re_modify_qp
-bng_re_destroy_qp
Signed-off-by: Usman S. Ansari <usman.ansari@broadcom.com>
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/bng_dev.c | 5 +
drivers/infiniband/hw/bng_re/bng_fw.c | 5 -
drivers/infiniband/hw/bng_re/bng_fw.h | 7 +
drivers/infiniband/hw/bng_re/bng_re.h | 43 +
.../infiniband/hw/bng_re/bng_re_mpc_roce.c | 2 -
drivers/infiniband/hw/bng_re/bng_res.c | 45 +-
drivers/infiniband/hw/bng_re/bng_res.h | 75 +
drivers/infiniband/hw/bng_re/bng_roce_hsi.h | 158 ++
drivers/infiniband/hw/bng_re/bng_sp.c | 870 +++++++++
drivers/infiniband/hw/bng_re/bng_sp.h | 172 +-
drivers/infiniband/hw/bng_re/bng_verbs.c | 1582 +++++++++++++++++
drivers/infiniband/hw/bng_re/bng_verbs.h | 198 +++
include/uapi/rdma/bng_re-abi.h | 35 +
13 files changed, 3188 insertions(+), 9 deletions(-)
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index 3ebdd777eca3..6e9df737c751 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -62,12 +62,17 @@ static const struct ib_device_ops bng_re_dev_ops = {
.modify_srq = bng_re_modify_srq,
.query_srq = bng_re_query_srq,
.destroy_srq = bng_re_destroy_srq,
+ .query_qp = bng_re_query_qp,
+ .create_qp = bng_re_create_qp,
+ .modify_qp = bng_re_modify_qp,
+ .destroy_qp = bng_re_destroy_qp,
.alloc_hw_port_stats = bng_re_alloc_hw_port_stats,
.get_hw_stats = bng_re_ib_get_hw_stats,
.create_ah = bng_re_create_ah,
.create_user_ah = bng_re_create_ah,
.destroy_ah = bng_re_destroy_ah,
.query_ah = bng_re_query_ah,
+ INIT_RDMA_OBJ_SIZE(ib_qp, bng_re_qp, ib_qp),
INIT_RDMA_OBJ_SIZE(ib_ah, bng_re_ah, ib_ah),
INIT_RDMA_OBJ_SIZE(ib_srq, bng_re_srq, ib_srq),
INIT_RDMA_OBJ_SIZE(ib_cq, bng_re_cq, ib_cq),
diff --git a/drivers/infiniband/hw/bng_re/bng_fw.c b/drivers/infiniband/hw/bng_re/bng_fw.c
index a69221368ba8..a6b43866666c 100644
--- a/drivers/infiniband/hw/bng_re/bng_fw.c
+++ b/drivers/infiniband/hw/bng_re/bng_fw.c
@@ -732,11 +732,6 @@ static inline bool _is_hw_retx_supported(u16 dev_cap_flags)
}
#define BNG_RE_HW_RETX(a) _is_hw_retx_supported((a))
-static inline bool _is_optimize_modify_qp_supported(u16 dev_cap_ext_flags2)
-{
- return dev_cap_ext_flags2 &
- CREQ_QUERY_FUNC_RESP_SB_OPTIMIZE_MODIFY_QP_SUPPORTED;
-}
int bng_re_init_rcfw(struct bng_re_rcfw *rcfw,
struct bng_re_stats *stats_ctx)
diff --git a/drivers/infiniband/hw/bng_re/bng_fw.h b/drivers/infiniband/hw/bng_re/bng_fw.h
index 011dc18592d0..c2d9fc94c674 100644
--- a/drivers/infiniband/hw/bng_re/bng_fw.h
+++ b/drivers/infiniband/hw/bng_re/bng_fw.h
@@ -125,6 +125,11 @@ struct bng_re_rcfw_sbuf {
u32 size;
};
+struct bng_re_qp_node {
+ u32 qp_id;
+ void *qp_handle;
+};
+
/* RoCE FW Communication Channels */
struct bng_re_rcfw {
struct pci_dev *pdev;
@@ -140,6 +145,8 @@ struct bng_re_rcfw {
atomic_t rcfw_intr_enabled;
u64 oos_prev;
u32 init_oos_stats;
+ int qp_tbl_size;
+ struct bng_re_qp_node *qp_tbl;
};
struct bng_re_cmdqmsg {
diff --git a/drivers/infiniband/hw/bng_re/bng_re.h b/drivers/infiniband/hw/bng_re/bng_re.h
index 217383a2b252..ef81e09f262b 100644
--- a/drivers/infiniband/hw/bng_re/bng_re.h
+++ b/drivers/infiniband/hw/bng_re/bng_re.h
@@ -244,6 +244,7 @@ struct bng_re_nq {
u32 load;
struct workqueue_struct *cqn_wq;
+ int (*cqn_handler)(struct bng_re_nq *nq, void *handle);
};
struct bng_re_nq_record {
@@ -268,6 +269,37 @@ struct bng_re_ring_attr {
u8 mode;
};
+struct bng_re_gsi_context {
+ struct bng_re_qp *gsi_qp;
+ u8 gsi_qp_mode;
+};
+
+struct bng_re_dscp2pri {
+ u8 dscp;
+ u8 mask;
+ u8 pri;
+};
+
+struct bng_re_tc_rec {
+ u8 cos_id_roce;
+ u8 tc_roce;
+ u8 cos_id_cnp;
+ u8 tc_cnp;
+ u8 tc_def;
+ u8 cos_id_def;
+ u8 max_tc;
+ u8 roce_prio;
+ u8 cnp_prio;
+ u8 roce_dscp;
+ u8 cnp_dscp;
+ u8 prio_valid;
+ u8 dscp_valid;
+ bool ecn_enabled;
+ bool serv_type_enabled;
+ u64 cnp_dscp_bv;
+ u64 roce_dscp_bv;
+};
+
struct bng_re_dev {
struct ib_device ibdev;
unsigned long flags;
@@ -303,6 +335,17 @@ struct bng_re_dev {
struct bng_re_ctx ctx;
struct workqueue_struct *dest_ah_wq;
refcount_t pos_destah_cnt;
+ struct bng_re_gsi_context gsi_ctx;
+ struct mutex qp_lock; /* protect qp list */
+ struct list_head qp_list;
+ u8 d2p_count;
+ struct bng_re_dscp2pri *d2p;
+ u8 p2cos[IEEE_8021QAZ_MAX_TCS];
+ u8 lossless_q_count;
+ u8 *lossless_qid;
+ union ib_gid ugid;
+ u32 min_tx_depth;
+ struct bng_re_tc_rec tc_rec[2];
};
#define to_bng_re_dev(ptr, member) \
diff --git a/drivers/infiniband/hw/bng_re/bng_re_mpc_roce.c b/drivers/infiniband/hw/bng_re/bng_re_mpc_roce.c
index 3ecf5f34180a..91cfdcdc2ff4 100644
--- a/drivers/infiniband/hw/bng_re/bng_re_mpc_roce.c
+++ b/drivers/infiniband/hw/bng_re/bng_re_mpc_roce.c
@@ -63,8 +63,6 @@ void bng_re_mpc_handle_event_cmpl(struct bng_re_dev *rdev,
dev_warn(rdev_to_dev(rdev),
"unexpected event mpc cmpl: xid=%d event=%d data=%d\n",
xid, event, event_data);
- /* process even if event / event_data isn't as expected, as we don't use them rn */
- /* bng_re_qp_xid_pending_process_unsolicited_cmpl(rdev, xid); */
}
/**
diff --git a/drivers/infiniband/hw/bng_re/bng_res.c b/drivers/infiniband/hw/bng_re/bng_res.c
index 024d49f68b09..b40c13cbc3a1 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.c
+++ b/drivers/infiniband/hw/bng_re/bng_res.c
@@ -10,6 +10,11 @@
#include "bng_roce_hsi.h"
#include "bng_sp.h"
+bool bng_re_init_fw_state_rtr_enabled(struct bng_re_chip_ctx *chip_ctx)
+{
+ return chip_ctx->modes.init_fw_state_rtr;
+}
+
/* Stats */
void bng_re_free_stats_ctx_mem(struct pci_dev *pdev,
struct bng_re_stats *stats)
@@ -395,8 +400,40 @@ static int bng_res_alloc_pd_tbl(struct bng_re_res *res,
return 0;
}
+static int bng_res_alloc_reftbl(struct bng_re_reftbl *tbl, u32 max)
+{
+ tbl->max = max;
+ tbl->rec = vzalloc(sizeof(*tbl->rec) * max);
+ if (!tbl->rec)
+ return -ENOMEM;
+ spin_lock_init(&tbl->lock);
+ return 0;
+}
+
+static void bng_res_free_reftbls(struct bng_re_res *res)
+{
+ struct bng_re_reftbl *tbl;
+
+ tbl = &res->reftbl.qpref;
+ vfree(tbl->rec);
+}
+
+static int bng_res_alloc_reftbls(struct bng_re_res *res, struct bng_re_dev_attr *dattr)
+{
+ struct bng_re_reftbl *tbl;
+ int rc;
+
+ tbl = &res->reftbl.qpref;
+ rc = bng_res_alloc_reftbl(tbl, BNG_RE_MAX_QPC_COUNT);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
void bng_res_free_tbls(struct bng_re_res *res)
{
+ bng_res_free_reftbls(res);
bng_res_free_pd_tbl(&res->pd_tbl);
bng_res_free_dpi_tbl(&res->dpi_tbl);
bng_sp_free_sgid_tbl(&res->sgid_tbl);
@@ -406,10 +443,14 @@ int bng_res_alloc_init_tbls(struct bng_re_res *res)
{
int rc;
- rc = bng_res_alloc_pd_tbl(res, res->dattr);
+ rc = bng_res_alloc_reftbls(res, res->dattr);
if (rc)
return rc;
+ rc = bng_res_alloc_pd_tbl(res, res->dattr);
+ if (rc)
+ goto free_reftbl;
+
rc = bng_res_alloc_dpi_tbl(res, res->dattr);
if (rc)
goto free_pd_tbl;
@@ -425,6 +466,8 @@ int bng_res_alloc_init_tbls(struct bng_re_res *res)
bng_res_free_dpi_tbl(&res->dpi_tbl);
free_pd_tbl:
bng_res_free_pd_tbl(&res->pd_tbl);
+free_reftbl:
+ bng_res_free_reftbls(res);
return rc;
}
diff --git a/drivers/infiniband/hw/bng_re/bng_res.h b/drivers/infiniband/hw/bng_re/bng_res.h
index 5a6db1786886..d5a6db80de59 100644
--- a/drivers/infiniband/hw/bng_re/bng_res.h
+++ b/drivers/infiniband/hw/bng_re/bng_res.h
@@ -4,6 +4,7 @@
#ifndef __BNG_RES_H__
#define __BNG_RES_H__
+#include <linux/bnge/hsi.h>
#include "bng_roce_hsi.h"
#include "xid_allocator.h"
#include <linux/bnge/hsi.h>
@@ -39,6 +40,7 @@
#define RCFW_DBR_PCI_BAR_REGION 2
#define BNG_RE_FR_PMR 0x80000000
+#define BNG_RE_MAX_QPC_COUNT (64 * 1024)
enum bng_re_toggle_modes {
BNG_RE_CQ_TOGGLE_BIT = 0x1,
@@ -102,6 +104,9 @@ struct bng_re_drv_modes {
u8 roce_mirror;
u8 dbr_primary_pf;
bool st_tag_supported;
+ u8 driver_alloc_xid_supported;
+ u8 init_fw_state_rtr;
+ u8 te_bypass;
};
struct bng_re_chip_ctx {
@@ -212,9 +217,30 @@ struct bng_re_pd_tbl {
u32 max;
};
+/* Reference record structure with MPC support */
+struct bng_re_refrec {
+ void *handle;
+ u32 xid;
+ u8 initial_mpc_sent; /* indicates xid has been sent at least 1x to fw */
+};
+
+/* Reference table structure */
+struct bng_re_reftbl {
+ struct bng_re_refrec *rec;
+ u32 max;
+ spinlock_t lock; /* reftbl lock */
+};
+
+/* Collection of reference tables */
+struct bng_re_reftbls {
+ struct bng_re_reftbl qpref;
+};
+
struct bng_re_res {
struct pci_dev *pdev;
struct bng_re_chip_ctx *cctx;
+ struct net_device *netdev;
+ struct bng_re_dev *rdev;
struct bng_re_dev_attr *dattr;
struct bng_re_dpi_tbl dpi_tbl;
/* Serialize access to DPI table */
@@ -222,6 +248,7 @@ struct bng_re_res {
struct xid_manager *qp_xids;
struct xid_manager *ah_xids;
struct bng_re_sgid_tbl sgid_tbl;
+ struct bng_re_reftbls reftbl;
bool prio;
struct bng_re_pd_tbl pd_tbl;
/* Serialize access to PD table */
@@ -238,6 +265,28 @@ struct bng_re_ctx {
struct bng_re_stats stats;
};
+struct bng_re_q {
+ struct bng_re_hwq hwq;
+ struct bng_re_swq *swq;
+ struct bng_re_db_info dbinfo;
+ struct bng_re_sg_info sg_info;
+ u32 max_wqe;
+ u32 max_sw_wqe;
+ u16 wqe_size;
+ u16 q_full_delta;
+ u16 max_sge;
+ u32 psn;
+ bool condition;
+ bool single;
+ bool send_phantom;
+ u32 phantom_wqe_cnt;
+ u32 phantom_cqe_cnt;
+ u32 next_cq_cons;
+ bool flushed;
+ u32 swq_start;
+ u32 swq_last;
+};
+
#define to_bng_re_res(ptr, member) container_of(ptr, struct bng_re_res, member)
static inline void *bng_re_get_qe(struct bng_re_hwq *hwq,
@@ -414,6 +463,31 @@ static inline int bng_ext_stats_supported(struct bng_re_chip_ctx *ctx,
return (_is_ext_stats_supported(flags) && ((virtfn) || (!virtfn)));
}
+static inline bool _is_optimize_modify_qp_supported(u16 dev_cap_ext_flags2)
+{
+ return dev_cap_ext_flags2 &
+ CREQ_QUERY_FUNC_RESP_SB_OPTIMIZE_MODIFY_QP_SUPPORTED;
+}
+
+static inline bool _is_min_rnr_in_rtr_rts_mandatory(u16 dev_cap_ext_flags2)
+{
+ return !!(dev_cap_ext_flags2 &
+ CREQ_QUERY_FUNC_RESP_SB_MIN_RNR_RTR_RTS_OPT_SUPPORTED);
+}
+
+#define GET_REFTBL_INDEX(id, tbl) ((id) % (((tbl)->max) - 1))
+static inline u32 map_qp_id_to_reftbl_indx(u32 qid, struct bng_re_reftbl *tbl)
+{
+ return (qid == 1) ? tbl->max : GET_REFTBL_INDEX(qid, tbl);
+}
+
+static inline bool _is_change_udp_src_port_wqe_supported(u16 flags)
+{
+ return !!(flags &
+ CREQ_QUERY_FUNC_RESP_SB_CHANGE_UDP_SRC_PORT_WQE_SUPPORTED);
+}
+
+#define BNG_RE_UDP_SP_WQE(a) _is_change_udp_src_port_wqe_supported((a))
void bng_re_free_hwq(struct bng_re_res *res,
struct bng_re_hwq *hwq);
@@ -440,4 +514,5 @@ int bng_re_dealloc_dpi(struct bng_re_res *res,
void bng_re_alloc_kernel_dpi(struct bng_re_res *res,
struct bng_re_dpi *dpi);
void bng_re_dealloc_kernel_dpi(struct bng_re_dpi *dpi);
+bool bng_re_init_fw_state_rtr_enabled(struct bng_re_chip_ctx *chip_ctx);
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_roce_hsi.h b/drivers/infiniband/hw/bng_re/bng_roce_hsi.h
index 3001b9d4d056..efef9de95361 100644
--- a/drivers/infiniband/hw/bng_re/bng_roce_hsi.h
+++ b/drivers/infiniband/hw/bng_re/bng_roce_hsi.h
@@ -6606,4 +6606,162 @@ struct mpc_ah_modify_cmpl {
__le32 reserved_3;
};
+/* mpc_qp_modify_cmd (size:1792b/224B) */
+struct mpc_qp_modify_cmd {
+ u8 req_type;
+ u8 req_subtype;
+ __le16 cookie;
+ __le16 target_id;
+ u8 resp_size;
+ u8 reserved;
+ __le64 resp_addr;
+ __le32 qp_cid;
+ u8 qp_modify_flags;
+ #define MPC_QP_MODIFY_CMD_QP_MODIFY_DATA_QP_MODIFY_FLAGS_QP_MODIFY_FLAGS_MASK \
+ 0xffUL
+ #define MPC_QP_MODIFY_CMD_QP_MODIFY_DATA_QP_MODIFY_FLAGS_QP_MODIFY_FLAGS_SFT \
+ 0
+ #define MPC_QP_MODIFY_CMD_QP_MODIFY_DATA_QP_MODIFY_FLAGS_QP_MODIFY_FLAGS_QP_FIRST_MODIFY \
+ 0x1UL
+ #define MPC_QP_MODIFY_CMD_QP_MODIFY_DATA_QP_MODIFY_FLAGS_QP_MODIFY_FLAGS_QP_FREE \
+ 0x2UL
+ #define MPC_QP_MODIFY_CMD_QP_MODIFY_DATA_QP_MODIFY_FLAGS_QP_MODIFY_FLAGS_XID_FIRST_USE \
+ 0x4UL
+ #define MPC_QP_MODIFY_CMD_QP_MODIFY_DATA_QP_MODIFY_FLAGS_QP_MODIFY_FLAGS_LAST \
+ MPC_QP_MODIFY_CMD_QP_MODIFY_DATA_QP_MODIFY_FLAGS_QP_MODIFY_FLAGS_XID_FIRST_USE
+ u8 qp_type;
+ __le16 schq_id;
+ __le32 qp_flags;
+ u8 sq_pg_size_sq_lvl;
+ u8 rq_pg_size_rq_lvl;
+ __le16 sq_fwo_sq_sge;
+ __le16 rq_fwo_rq_sge;
+ __le16 sq_max_num_wqes;
+ __le16 flags;
+ u8 type;
+ u8 unused_2;
+ __le32 modify_mask;
+ u8 network_type_en_sqd_async_notify_new_state;
+ u8 access;
+ __le16 pkey;
+ __le32 qkey;
+ __le32 flow_label;
+ __le32 dgid[4];
+ __le16 sgid_index;
+ u8 hop_limit;
+ u8 traffic_class;
+ u8 tos_dscp_tos_ecn;
+ u8 path_mtu_pingpong_push_enable;
+ u8 timeout;
+ u8 retry_cnt;
+ u8 rnr_retry;
+ u8 min_rnr_timer;
+ u8 max_dest_rd_atomic;
+ u8 max_rd_atomic;
+ __le32 rq_psn;
+ __le32 sq_psn;
+ __le32 sq_size;
+ __le32 rq_size;
+ __le16 sq_sge;
+ __le16 rq_sge;
+ __le32 max_inline_data;
+ __le32 dest_qp_id;
+ __le32 pingpong_push_dpi;
+ u8 unused_3[4];
+ __le16 enable_cc;
+ __le16 dest_mac[3];
+ __le16 src_mac[3];
+ __le16 vlan_pcp_vlan_dei_vlan_id;
+ __le32 ext_modify_mask;
+ __le32 ext_stats_ctx_id;
+ __le64 qp_handle;
+ __le32 dpi;
+ __le32 scq_cid;
+ __le32 rcq_cid;
+ __le32 srq_cid;
+ __le32 pd_id;
+ __le32 request_xid;
+ __le64 sq_pbl;
+ __le64 rq_pbl;
+ __le32 msn_iqp;
+ __le32 irrq_iqp;
+ __le32 orrq_iqp;
+ __le32 msn_size;
+ __le32 irrq_size;
+ __le32 orrq_size;
+ __le16 steering_tag;
+ __le16 eroce;
+ __le16 rq_prod_idx;
+ u8 reserved_1[2];
+};
+
+struct qp_modify_data {
+ __le32 qp_cid;
+ u8 qp_modify_flags;
+ u8 qp_type;
+ __le16 schq_id;
+ __le32 qp_flags;
+ u8 sq_pg_size_sq_lvl;
+ u8 rq_pg_size_rq_lvl;
+ __le16 sq_fwo_sq_sge;
+ __le16 rq_fwo_rq_sge;
+ __le16 sq_max_num_wqes;
+ __le16 flags;
+ u8 type;
+ u8 unused_2;
+ __le32 modify_mask;
+ u8 network_type_en_sqd_async_notify_new_state;
+ u8 access;
+ __le16 pkey;
+ __le32 qkey;
+ __le32 flow_label;
+ __le32 dgid[4];
+ __le16 sgid_index;
+ u8 hop_limit;
+ u8 traffic_class;
+ u8 tos_dscp_tos_ecn;
+ u8 path_mtu_pingpong_push_enable;
+ u8 timeout;
+ u8 retry_cnt;
+ u8 rnr_retry;
+ u8 min_rnr_timer;
+ u8 max_dest_rd_atomic;
+ u8 max_rd_atomic;
+ __le32 rq_psn;
+ __le32 sq_psn;
+ __le32 sq_size;
+ __le32 rq_size;
+ __le16 sq_sge;
+ __le16 rq_sge;
+ __le32 max_inline_data;
+ __le32 dest_qp_id;
+ __le32 pingpong_push_dpi;
+ u8 unused_3[4];
+ __le16 enable_cc;
+ __le16 dest_mac[3];
+ __le16 src_mac[3];
+ __le16 vlan_pcp_vlan_dei_vlan_id;
+ __le32 ext_modify_mask;
+ __le32 ext_stats_ctx_id;
+ __le64 qp_handle;
+ __le32 dpi;
+ __le32 scq_cid;
+ __le32 rcq_cid;
+ __le32 srq_cid;
+ __le32 pd_id;
+ __le32 request_xid;
+ __le64 sq_pbl;
+ __le64 rq_pbl;
+ __le32 msn_iqp;
+ __le32 irrq_iqp;
+ __le32 orrq_iqp;
+ __le32 msn_size;
+ __le32 irrq_size;
+ __le32 orrq_size;
+ __le16 steering_tag;
+ __le16 eroce;
+ __le16 rq_prod_idx;
+ u8 reserved_1[2];
+};
+
#endif /* _BNG_RE_HSI_H_ */
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.c b/drivers/infiniband/hw/bng_re/bng_sp.c
index b44af599e675..7e93a1be9bad 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.c
+++ b/drivers/infiniband/hw/bng_re/bng_sp.c
@@ -10,6 +10,11 @@
#include "bng_re.h"
#include "bng_re_mpc_roce.h"
#include "bng_fp.h"
+#include "bng_verbs.h"
+
+#define CQE_CMP_VALID(hdr, pass) \
+ (!!((hdr)->cqe_type_toggle & CQ_BASE_TOGGLE) == \
+ !((pass) & BNG_RE_FLAG_EPOCH_CONS_MASK))
const struct bng_re_gid bng_re_gid_zero = {{0,}};
@@ -1224,3 +1229,868 @@ int bng_sp_fill_and_send_mpc_ah_modify(struct bng_re_res *res,
return rc;
}
+
+void bng_re_flush_cqn_wq(struct bng_sp_qp *qp)
+{
+ /*
+ * Partial create (e.g. attr without send_cq/recv_cq) or teardown ordering
+ * can leave scq/rcq or NQ workqueues unset; flush must not dereference them.
+ */
+ if (qp->scq && qp->scq->nq && qp->scq->nq->cqn_wq)
+ flush_workqueue(qp->scq->nq->cqn_wq);
+ if (qp->rcq && qp->scq != qp->rcq && qp->rcq->nq && qp->rcq->nq->cqn_wq)
+ flush_workqueue(qp->rcq->nq->cqn_wq);
+}
+
+static void bng_re_free_qp_hdr_buf(struct bng_re_res *res,
+ struct bng_sp_qp *qp)
+{
+ struct bng_re_q *rq = &qp->rq;
+ struct bng_re_q *sq = &qp->sq;
+
+ if (qp->rq_hdr_buf)
+ dma_free_coherent(&res->pdev->dev,
+ rq->max_wqe * qp->rq_hdr_buf_size,
+ qp->rq_hdr_buf, qp->rq_hdr_buf_map);
+ if (qp->sq_hdr_buf)
+ dma_free_coherent(&res->pdev->dev,
+ sq->max_wqe * qp->sq_hdr_buf_size,
+ qp->sq_hdr_buf, qp->sq_hdr_buf_map);
+ qp->rq_hdr_buf = NULL;
+ qp->sq_hdr_buf = NULL;
+ qp->rq_hdr_buf_map = 0;
+ qp->sq_hdr_buf_map = 0;
+ qp->sq_hdr_buf_size = 0;
+ qp->rq_hdr_buf_size = 0;
+}
+
+void bng_re_free_qp_res(struct bng_re_res *res,
+ struct bng_sp_qp *qp)
+{
+ bng_re_free_qp_hdr_buf(res, qp);
+ bng_re_free_hwq(res, &qp->sq.hwq);
+ kvfree(qp->sq.swq);
+
+ bng_re_free_hwq(res, &qp->rq.hwq);
+ kvfree(qp->rq.swq);
+
+ if (qp->irrq.max_elements)
+ bng_re_free_hwq(res, &qp->irrq);
+ if (qp->orrq.max_elements)
+ bng_re_free_hwq(res, &qp->orrq);
+}
+
+static void bng_re_acquire_cq_flush_locks(struct bng_sp_qp *qp,
+ unsigned long *flags)
+ __acquires(&qp->scq->flush_lock) __acquires(&qp->rcq->flush_lock)
+{
+ spin_lock_irqsave(&qp->scq->flush_lock, *flags);
+ if (qp->scq == qp->rcq)
+ __acquire(&qp->rcq->flush_lock);
+ else
+ spin_lock(&qp->rcq->flush_lock);
+}
+
+static void bng_re_cancel_phantom_processing(struct bng_sp_qp *qp)
+{
+ qp->sq.condition = false;
+ qp->sq.send_phantom = false;
+ qp->sq.single = false;
+}
+
+static void __bng_re_add_flush_qp(struct bng_sp_qp *qp)
+{
+ struct bng_sp_cq *scq, *rcq;
+
+ scq = qp->scq;
+ rcq = qp->rcq;
+
+ if (!qp->sq.flushed) {
+ dev_dbg(&scq->hwq.pdev->dev,
+ "SP: Adding to SQ Flush list = %p\n", qp);
+ bng_re_cancel_phantom_processing(qp);
+ list_add_tail(&qp->sq_flush, &scq->sqf_head);
+ qp->sq.flushed = true;
+ }
+ if (!qp->srq) {
+ if (!qp->rq.flushed) {
+ dev_dbg(&rcq->hwq.pdev->dev,
+ "SP: Adding to RQ Flush list = %p\n", qp);
+ list_add_tail(&qp->rq_flush, &rcq->rqf_head);
+ qp->rq.flushed = true;
+ }
+ }
+}
+
+static void bng_re_release_cq_flush_locks(struct bng_sp_qp *qp,
+ unsigned long *flags)
+ __releases(&qp->scq->flush_lock) __releases(&qp->rcq->flush_lock)
+{
+ if (qp->scq == qp->rcq)
+ __release(&qp->rcq->flush_lock);
+ else
+ spin_unlock(&qp->rcq->flush_lock);
+ spin_unlock_irqrestore(&qp->scq->flush_lock, *flags);
+}
+
+void bng_re_add_flush_qp(struct bng_sp_qp *qp)
+{
+ unsigned long flags;
+
+ bng_re_acquire_cq_flush_locks(qp, &flags);
+ __bng_re_add_flush_qp(qp);
+ bng_re_release_cq_flush_locks(qp, &flags);
+}
+
+static void __bng_re_del_flush_qp(struct bng_sp_qp *qp)
+{
+ if (qp->sq.flushed) {
+ qp->sq.flushed = false;
+ list_del(&qp->sq_flush);
+ }
+ if (!qp->srq) {
+ if (qp->rq.flushed) {
+ qp->rq.flushed = false;
+ list_del(&qp->rq_flush);
+ }
+ }
+}
+
+static void __clean_cq(struct bng_sp_cq *cq, u64 qp)
+{
+ struct bng_re_hwq *cq_hwq = &cq->hwq;
+ u32 peek_flags, peek_cons;
+ struct cq_base *hw_cqe;
+ int i;
+
+ peek_flags = cq->dbinfo.flags;
+ peek_cons = cq_hwq->cons;
+ for (i = 0; i < cq_hwq->max_elements; i++) {
+ hw_cqe = bng_re_get_qe(cq_hwq, peek_cons, NULL);
+ if (!CQE_CMP_VALID(hw_cqe, peek_flags))
+ continue;
+ /*
+ * The valid test of the entry must be done first before
+ * reading any further.
+ */
+ dma_rmb();
+ switch (hw_cqe->cqe_type_toggle & CQ_BASE_CQE_TYPE_MASK) {
+ case CQ_BASE_CQE_TYPE_REQ:
+ case CQ_BASE_CQE_TYPE_TERMINAL:
+ {
+ struct cq_req *cqe = (struct cq_req *)hw_cqe;
+
+ if (qp == le64_to_cpu(cqe->qp_handle))
+ cqe->qp_handle = 0;
+ break;
+ }
+ case CQ_BASE_CQE_TYPE_RES_RC:
+ case CQ_BASE_CQE_TYPE_RES_UD:
+ case CQ_BASE_CQE_TYPE_RES_RAWETH_QP1:
+ {
+ struct cq_res_rc *cqe = (struct cq_res_rc *)hw_cqe;
+
+ if (qp == le64_to_cpu(cqe->qp_handle))
+ cqe->qp_handle = 0;
+ break;
+ }
+ default:
+ break;
+ }
+ bng_re_hwq_incr_cons(cq_hwq->max_elements, &peek_cons,
+ 1, &peek_flags);
+ }
+}
+
+void bng_re_clean_qp(struct bng_sp_qp *qp)
+{
+ unsigned long flags;
+
+ bng_re_acquire_cq_flush_locks(qp, &flags);
+ __clean_cq(qp->scq, (u64)(unsigned long)qp);
+ qp->sq.hwq.prod = 0;
+ qp->sq.hwq.cons = 0;
+ __clean_cq(qp->rcq, (u64)(unsigned long)qp);
+ qp->rq.hwq.prod = 0;
+ qp->rq.hwq.cons = 0;
+
+ __bng_re_del_flush_qp(qp);
+ bng_re_release_cq_flush_locks(qp, &flags);
+}
+
+int bng_re_alloc_init_swq(struct bng_re_q *que)
+{
+ int indx;
+
+ que->swq = kvcalloc(que->max_sw_wqe, sizeof(*que->swq), GFP_KERNEL);
+ if (!que->swq)
+ return -ENOMEM;
+
+ que->swq_start = 0;
+ que->swq_last = que->max_sw_wqe - 1;
+ for (indx = 0; indx < que->max_sw_wqe; indx++)
+ que->swq[indx].next_idx = indx + 1;
+ que->swq[que->swq_last].next_idx = 0;
+ que->swq_last = 0;
+
+ return 0;
+}
+
+static bool bng_sp_get_initial_created_flag(struct bng_re_res *res,
+ struct bng_sp_qp *qp)
+{
+ struct bng_re_reftbl *tbl;
+ unsigned long flag;
+ bool val = false;
+ u32 qp_idx;
+
+ tbl = &res->reftbl.qpref;
+ qp_idx = map_qp_id_to_reftbl_indx(qp->id, tbl);
+ spin_lock_irqsave(&tbl->lock, flag);
+ /* Make sure we have the right table index */
+ if (tbl->rec[qp_idx].xid == qp->id &&
+ tbl->rec[qp_idx].handle == qp)
+ val = tbl->rec[qp_idx].initial_mpc_sent;
+ spin_unlock_irqrestore(&tbl->lock, flag);
+ return val;
+}
+
+static bool is_optimized_state_transition(struct bng_sp_qp *qp)
+{
+ if ((qp->cur_qp_state == CMDQ_MODIFY_QP_NEW_STATE_INIT &&
+ qp->state == CMDQ_MODIFY_QP_NEW_STATE_RTR) ||
+ (qp->cur_qp_state == CMDQ_MODIFY_QP_NEW_STATE_RTR &&
+ qp->state == CMDQ_MODIFY_QP_NEW_STATE_RTS))
+ return true;
+
+ return false;
+}
+
+static void bng_set_mandatory_attributes(struct bng_re_res *res,
+ struct bng_sp_qp *qp,
+ struct cmdq_modify_qp *req)
+{
+ u32 mandatory_flags = 0;
+
+ if (qp->type == CMDQ_MODIFY_QP_QP_TYPE_RC)
+ mandatory_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_ACCESS;
+
+ if (qp->cur_qp_state == CMDQ_MODIFY_QP_NEW_STATE_INIT &&
+ qp->state == CMDQ_MODIFY_QP_NEW_STATE_RTR) {
+ if (qp->type == CMDQ_MODIFY_QP_QP_TYPE_RC && qp->srq)
+ req->flags = CMDQ_MODIFY_QP_FLAGS_SRQ_USED;
+ mandatory_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_PKEY;
+ }
+
+ if (_is_min_rnr_in_rtr_rts_mandatory(res->dattr->dev_cap_ext_flags2) &&
+ (qp->cur_qp_state == CMDQ_MODIFY_QP_NEW_STATE_RTR &&
+ qp->state == CMDQ_MODIFY_QP_NEW_STATE_RTS)) {
+ if (qp->type == CMDQ_MODIFY_QP_QP_TYPE_RC)
+ mandatory_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_MIN_RNR_TIMER;
+ }
+
+ if (qp->type == CMDQ_MODIFY_QP_QP_TYPE_UD || qp->type == CMDQ_MODIFY_QP_QP_TYPE_GSI)
+ mandatory_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_QKEY;
+
+ qp->modify_flags |= mandatory_flags;
+ req->qp_type = qp->type;
+}
+
+static int bng_sp_get_iqm_xrrq_size(bool xrrq, struct bng_re_res *res, struct bng_re_qp *qp)
+{
+ struct bng_sp_qp *sp = &qp->sp_qp;
+ int xrrq_overflow = 0;
+ int dattr_xrrq_size;
+ int iqm_xrrq_size;
+ int qp_xrrq_size;
+ int xrrq_size;
+
+ iqm_xrrq_size = xrrq ? qp->iqm_res.irrq_size : qp->iqm_res.orrq_size;
+ qp_xrrq_size = xrrq ? sp->max_dest_rd_atomic : sp->max_rd_atomic;
+ qp_xrrq_size = max_t(int, 4, qp_xrrq_size);
+ dattr_xrrq_size = xrrq ? res->dattr->max_qp_init_rd_atom : res->dattr->max_qp_rd_atom;
+ /* To avoid irrq overflow, irrq needs to be greater than orrq */
+ xrrq_overflow = xrrq ? BNG_RE_IQM_IRRQ_OVERFLOW_COMP : 0;
+ qp_xrrq_size = qp_xrrq_size + xrrq_overflow;
+
+ if (!iqm_xrrq_size) {
+ /* Allocate IQM resource for both orrq and irrq (applicable only for PF) */
+ /* Will be used once IQM resource allocation is moved to INIT to RTR */
+ }
+
+ if (xrrq)
+ iqm_xrrq_size = IRRQ_CACHELINE_TO_ENTRIES(iqm_xrrq_size);
+ else
+ iqm_xrrq_size = ORRQ_CACHELINE_TO_ENTRIES(iqm_xrrq_size);
+
+ /* irrq/orrq size will be minimum of device attribute & per qp rd_atomic size */
+ xrrq_size = min_t(int, iqm_xrrq_size, qp_xrrq_size);
+
+ return xrrq_size;
+}
+#define QP_FIRST_MODIFY \
+ MPC_QP_MODIFY_CMD_QP_MODIFY_DATA_QP_MODIFY_FLAGS_QP_MODIFY_FLAGS_QP_FIRST_MODIFY
+#define QP_MODIFY_XID_FIRST_USE \
+ MPC_QP_MODIFY_CMD_QP_MODIFY_DATA_QP_MODIFY_FLAGS_QP_MODIFY_FLAGS_XID_FIRST_USE
+static int bng_sp_fill_mpc_qp_create_parms(struct bng_re_res *res,
+ struct qp_modify_data *d,
+ struct bng_re_qp *qp)
+{
+ struct bng_sp_qp *sp = &qp->sp_qp;
+ struct bng_re_q *sq = &sp->sq;
+ struct bng_re_q *rq = &sp->rq;
+ struct bng_re_pbl *pbl;
+ u32 qp_flags = 0;
+ u8 pg_sz_lvl = 0;
+ u16 nsge;
+
+ d->qp_cid = cpu_to_le32(sp->id);
+ if (!test_bit(BNG_SP_QP_FLAG_MPC_INITIAL_CREATE_SENT, &sp->flags)) {
+ u8 qmf = QP_FIRST_MODIFY;
+
+ if (!bng_sp_get_initial_created_flag(res, sp))
+ qmf |= QP_MODIFY_XID_FIRST_USE;
+
+ d->qp_modify_flags |= qmf;
+ }
+
+ if (sp->type != CMDQ_CREATE_QP_TYPE_GSI &&
+ _is_ext_stats_supported(sp->dev_cap_ext_flags)) {
+ d->ext_stats_ctx_id = cpu_to_le32(sp->roce_stat_ext_xid);
+ qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_EXT_STATS_CTX_VALID;
+ }
+
+ d->qp_type = sp->type;
+ d->type = sp->type;
+ d->schq_id = 0;
+ d->dpi = cpu_to_le32(sp->dpi->dpi);
+ d->qp_handle = cpu_to_le64(sp->qp_handle);
+ d->sq_size = cpu_to_le32(sq->max_sw_wqe);
+ d->scq_cid = cpu_to_le32(sp->scq->id);
+ pbl = &sq->hwq.pbl[BNG_PBL_LVL_0];
+ d->sq_pbl = cpu_to_le64(pbl->pg_map_arr[0]);
+
+ qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_RESERVED_LKEY_ENABLE;
+ qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_FR_PMR_ENABLED;
+ if (sp->sig_type)
+ qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_FORCE_COMPLETION;
+ qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_VARIABLE_SIZED_WQE_ENABLED;
+ if (sp->type == CMDQ_CREATE_QP_TYPE_RC)
+ qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_RDMA_READ_OR_ATOMICS_USED;
+ if (sp->type == CMDQ_CREATE_QP_TYPE_GSI || sp->type == CMDQ_CREATE_QP_TYPE_UD)
+ qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_RESPONDER_UD_CQE_WITH_CFA;
+ if (res->dattr &&
+ bng_ext_stats_supported(res->cctx, res->dattr->dev_cap_flags, res->is_vf))
+ qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_EXT_STATS_ENABLED;
+ if (res->cctx->modes.te_bypass)
+ qp_flags |= CMDQ_CREATE_QP_QP_FLAGS_OPTIMIZED_TRANSMIT_ENABLED;
+
+ pg_sz_lvl = (bng_re_base_pg_size(&sq->hwq) << CMDQ_CREATE_QP_SQ_PG_SIZE_SFT);
+ pg_sz_lvl |= (sq->hwq.level & CMDQ_CREATE_QP_SQ_LVL_MASK) << CMDQ_CREATE_QP_SQ_LVL_SFT;
+ d->sq_pg_size_sq_lvl = pg_sz_lvl;
+ d->sq_fwo_sq_sge =
+ cpu_to_le16(((0 << CMDQ_CREATE_QP_SQ_FWO_SFT) &
+ CMDQ_CREATE_QP_SQ_FWO_MASK) |
+ (sq->max_sge & CMDQ_CREATE_QP_SQ_SGE_MASK));
+
+ d->sq_max_num_wqes = min_t(u16, sq->max_wqe - 1, SQ_MAX_NUM_WQES_DEFAULT);
+
+ if (!sp->srq && rq->max_wqe) {
+ pbl = &rq->hwq.pbl[BNG_PBL_LVL_0];
+ d->rq_pbl = cpu_to_le64(pbl->pg_map_arr[0]);
+ pg_sz_lvl = (bng_re_base_pg_size(&rq->hwq) <<
+ CMDQ_CREATE_QP_RQ_PG_SIZE_SFT);
+ pg_sz_lvl |= (rq->hwq.level & CMDQ_CREATE_QP_RQ_LVL_MASK)
+ << CMDQ_CREATE_QP_RQ_LVL_SFT;
+ d->rq_pg_size_rq_lvl = pg_sz_lvl;
+ nsge = rq->max_sge;
+ d->rq_fwo_rq_sge =
+ cpu_to_le16(((0 << CMDQ_CREATE_QP_RQ_FWO_SFT) &
+ CMDQ_CREATE_QP_RQ_FWO_MASK) |
+ (nsge & CMDQ_CREATE_QP_RQ_SGE_MASK));
+
+ d->rq_size = cpu_to_le32(rq->max_wqe);
+ d->rq_prod_idx = cpu_to_le16((u16)(rq->hwq.prod & 0xffffU));
+ } else {
+ d->rq_size = 0;
+ d->rq_prod_idx = 0;
+ }
+
+ if (sp->srq) {
+ d->qp_flags = cpu_to_le32(le32_to_cpu(d->qp_flags) |
+ CMDQ_CREATE_QP_QP_FLAGS_SRQ_USED);
+ d->srq_cid = cpu_to_le32(sp->srq->id);
+ }
+
+ d->rcq_cid = cpu_to_le32(sp->rcq->id);
+
+ if (sp->type == CMDQ_CREATE_QP_TYPE_RC &&
+ qp->iqm_res.irrq_addr != BNG_RE_IQM_INVALID_IDX) {
+ d->msn_iqp = cpu_to_le32(qp->iqm_res.msn_addr);
+ d->irrq_iqp = cpu_to_le32(qp->iqm_res.irrq_addr);
+ d->orrq_iqp = cpu_to_le32(qp->iqm_res.orrq_addr);
+ d->msn_size = cpu_to_le32(qp->iqm_res.msn_size);
+ d->irrq_size = cpu_to_le32(qp->iqm_res.irrq_size);
+ d->orrq_size = cpu_to_le32(qp->iqm_res.orrq_size);
+ }
+
+ d->qp_flags = cpu_to_le32(qp_flags);
+ d->pd_id = cpu_to_le32(sp->pd->id);
+
+ return 0;
+}
+
+static void __filter_modify_flags(struct bng_sp_qp *qp)
+{
+ switch (qp->cur_qp_state) {
+ case CMDQ_MODIFY_QP_NEW_STATE_RESET:
+ switch (qp->state) {
+ case CMDQ_MODIFY_QP_NEW_STATE_INIT:
+ break;
+ default:
+ break;
+ }
+ break;
+ case CMDQ_MODIFY_QP_NEW_STATE_INIT:
+ switch (qp->state) {
+ case CMDQ_MODIFY_QP_NEW_STATE_RTR:
+ /* INIT->RTR, configure the path_mtu to the default
+ * 2048 if not being requested
+ */
+ if (qp->type != CMDQ_CREATE_QP_TYPE_GSI &&
+ qp->type != CMDQ_CREATE_QP_TYPE_UD &&
+ !(qp->modify_flags &
+ CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU)) {
+ qp->modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU;
+ qp->path_mtu = CMDQ_MODIFY_QP_PATH_MTU_MTU_2048;
+ }
+
+ /* Bono FW requires the max_dest_rd_atomic to be >= 1 */
+ if (qp->max_dest_rd_atomic < 1)
+ qp->max_dest_rd_atomic = 1;
+
+ /* TODO: Bono FW 0.0.12.0+ does not allow SRC_MAC modification */
+ qp->modify_flags &= ~CMDQ_MODIFY_QP_MODIFY_MASK_SRC_MAC;
+ /* Bono FW 20.6.5 requires SGID_INDEX to be configured */
+ if (!(qp->modify_flags & CMDQ_MODIFY_QP_MODIFY_MASK_SGID_INDEX)) {
+ qp->modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_SGID_INDEX;
+ qp->ah.sgid_index = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case CMDQ_MODIFY_QP_NEW_STATE_RTR:
+ switch (qp->state) {
+ case CMDQ_MODIFY_QP_NEW_STATE_RTS:
+ /* Bono FW requires the max_rd_atomic to be >= 1 */
+ if (qp->max_rd_atomic < 1)
+ qp->max_rd_atomic = 1;
+
+ /* TODO: Bono FW 0.0.12.0+ does not allow PKEY_INDEX,
+ * DGID, FLOW_LABEL, SGID_INDEX, HOP_LIMIT,
+ * TRAFFIC_CLASS, DEST_MAC, PATH_MTU, RQ_PSN,
+ * MIN_RNR_TIMER, MAX_DEST_RD_ATOMIC, DEST_QP_ID
+ * modification
+ */
+
+ qp->modify_flags &=
+ ~(CMDQ_MODIFY_QP_MODIFY_MASK_PKEY |
+ CMDQ_MODIFY_QP_MODIFY_MASK_DGID |
+ CMDQ_MODIFY_QP_MODIFY_MASK_FLOW_LABEL |
+ CMDQ_MODIFY_QP_MODIFY_MASK_SGID_INDEX |
+ CMDQ_MODIFY_QP_MODIFY_MASK_HOP_LIMIT |
+ CMDQ_MODIFY_QP_MODIFY_MASK_TRAFFIC_CLASS |
+ CMDQ_MODIFY_QP_MODIFY_MASK_DEST_MAC |
+ CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU |
+ CMDQ_MODIFY_QP_MODIFY_MASK_RQ_PSN |
+ CMDQ_MODIFY_QP_MODIFY_MASK_MIN_RNR_TIMER |
+ CMDQ_MODIFY_QP_MODIFY_MASK_MAX_DEST_RD_ATOMIC |
+ CMDQ_MODIFY_QP_MODIFY_MASK_DEST_QP_ID);
+ break;
+ default:
+ break;
+ }
+ break;
+ case CMDQ_MODIFY_QP_NEW_STATE_RTS:
+ break;
+ case CMDQ_MODIFY_QP_NEW_STATE_SQD:
+ break;
+ case CMDQ_MODIFY_QP_NEW_STATE_SQE:
+ break;
+ case CMDQ_MODIFY_QP_NEW_STATE_ERR:
+ break;
+ default:
+ break;
+ }
+}
+
+static int bng_sp_fill_mpc_qp_modify_parms(struct bng_re_res *res,
+ struct qp_modify_data *d,
+ struct bng_re_qp *qp)
+{
+ struct cmdq_modify_qp temp_req = {};
+ struct bng_sp_qp *sp = &qp->sp_qp;
+ u32 m = (u32)sp->modify_flags;
+ u32 temp32[4];
+
+ /* Check if QP free flag is required */
+ if (test_bit(BNG_SP_QP_FLAG_MPC_QP_FREE, &sp->flags))
+ m |= MPC_QP_MODIFY_CMD_QP_MODIFY_DATA_QP_MODIFY_FLAGS_QP_MODIFY_FLAGS_QP_FREE;
+
+ /* Filter out the qp_attr_mask based on the state->new transition */
+ __filter_modify_flags(sp);
+ m = (u32)sp->modify_flags;
+
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_STATE) {
+ /* Set mandatory attributes for INIT -> RTR and RTR -> RTS
+ * transition
+ */
+ if (_is_optimize_modify_qp_supported(res->dattr->dev_cap_ext_flags2) &&
+ is_optimized_state_transition(sp)) {
+ bng_set_mandatory_attributes(res, sp, &temp_req);
+ temp_req.flags =
+ CMDQ_MODIFY_QP_FLAGS_SRQ_USED;
+ d->flags = temp_req.flags;
+ d->qp_type = temp_req.qp_type;
+ }
+ }
+
+ if (bng_re_init_fw_state_rtr_enabled(res->cctx))
+ d->rq_prod_idx = cpu_to_le16(sp->req_buffer_count);
+
+ if (sp->udcc_exclude)
+ d->flags |= CMDQ_MODIFY_QP_FLAGS_EXCLUDE_QP_UDCC;
+
+ d->modify_mask = cpu_to_le32(m);
+ d->qp_cid = cpu_to_le32(sp->id);
+
+ if (d->modify_mask & CMDQ_MODIFY_QP_MODIFY_MASK_STATE) {
+ d->network_type_en_sqd_async_notify_new_state =
+ (sp->state & CMDQ_MODIFY_QP_NEW_STATE_MASK) |
+ (sp->en_sqd_async_notify
+ ? CMDQ_MODIFY_QP_EN_SQD_ASYNC_NOTIFY : 0);
+ }
+ d->network_type_en_sqd_async_notify_new_state |= sp->nw_type;
+
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_ACCESS)
+ d->access = sp->access;
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_PKEY)
+ d->pkey = cpu_to_le16(IB_DEFAULT_PKEY_FULL);
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_QKEY)
+ d->qkey = cpu_to_le32(sp->qkey);
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_FLOW_LABEL)
+ d->flow_label = cpu_to_le32(sp->ah.flow_label);
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_DGID) {
+ memcpy(temp32, sp->ah.dgid.data, sizeof(struct bng_re_gid));
+ d->dgid[0] = cpu_to_le32(temp32[0]);
+ d->dgid[1] = cpu_to_le32(temp32[1]);
+ d->dgid[2] = cpu_to_le32(temp32[2]);
+ d->dgid[3] = cpu_to_le32(temp32[3]);
+ }
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_SGID_INDEX) {
+ if (sp->is_roce_mirror_qp)
+ d->sgid_index = cpu_to_le16(res->sgid_tbl.hw_id[sp->ugid_index]);
+ else
+ d->sgid_index = cpu_to_le16(res->sgid_tbl.hw_id[sp->ah.sgid_index]);
+ }
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_HOP_LIMIT)
+ d->hop_limit = sp->ah.hop_limit;
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_TRAFFIC_CLASS)
+ d->traffic_class = sp->ah.traffic_class;
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_DEST_MAC)
+ memcpy(d->dest_mac, sp->ah.dmac, 6);
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU)
+ d->path_mtu_pingpong_push_enable = sp->path_mtu;
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_TIMEOUT)
+ d->timeout = sp->timeout;
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_RETRY_CNT)
+ d->retry_cnt = sp->retry_cnt;
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_RNR_RETRY)
+ d->rnr_retry = sp->rnr_retry;
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_MIN_RNR_TIMER)
+ d->min_rnr_timer = sp->min_rnr_timer;
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_RQ_PSN)
+ d->rq_psn = cpu_to_le32(sp->rq.psn);
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_SQ_PSN)
+ d->sq_psn = cpu_to_le32(sp->sq.psn);
+
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_MAX_RD_ATOMIC)
+ d->max_rd_atomic = bng_sp_get_iqm_xrrq_size(0, res, qp);
+
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_MAX_DEST_RD_ATOMIC)
+ d->max_dest_rd_atomic = bng_sp_get_iqm_xrrq_size(1, res, qp);
+
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_SQ_SIZE)
+ d->sq_size = cpu_to_le32(bng_re_set_sq_size(&sp->sq));
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_RQ_SIZE) {
+ if (!sp->srq && sp->rq.max_wqe)
+ d->rq_size = cpu_to_le32(sp->rq.max_wqe);
+ }
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_SQ_SGE)
+ d->sq_sge = cpu_to_le16(sp->sq.max_sge);
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_RQ_SGE && !sp->srq)
+ d->rq_sge = cpu_to_le16(sp->rq.max_sge);
+
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_MAX_INLINE_DATA)
+ d->max_inline_data = cpu_to_le32(sp->max_inline_data);
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_DEST_QP_ID)
+ d->dest_qp_id = cpu_to_le32(sp->dest_qpn);
+
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_ENABLE_CC)
+ d->enable_cc = cpu_to_le16(CMDQ_MODIFY_QP_ENABLE_CC);
+
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_TOS_ECN)
+ d->tos_dscp_tos_ecn =
+ ((sp->tos_ecn << CMDQ_MODIFY_QP_TOS_ECN_SFT) &
+ CMDQ_MODIFY_QP_TOS_ECN_MASK);
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_TOS_DSCP)
+ d->tos_dscp_tos_ecn |=
+ ((sp->tos_dscp << CMDQ_MODIFY_QP_TOS_DSCP_SFT) &
+ CMDQ_MODIFY_QP_TOS_DSCP_MASK);
+ if (m & CMDQ_MODIFY_QP_MODIFY_MASK_VLAN_ID) {
+ d->vlan_pcp_vlan_dei_vlan_id =
+ ((res->sgid_tbl.tbl[sp->ah.sgid_index].vlan_id
+ << CMDQ_MODIFY_QP_VLAN_ID_SFT) &
+ CMDQ_MODIFY_QP_VLAN_ID_MASK);
+ d->vlan_pcp_vlan_dei_vlan_id |=
+ ((sp->ah.sl << CMDQ_MODIFY_QP_VLAN_PCP_SFT) &
+ CMDQ_MODIFY_QP_VLAN_PCP_MASK);
+ d->vlan_pcp_vlan_dei_vlan_id =
+ cpu_to_le16(d->vlan_pcp_vlan_dei_vlan_id);
+ }
+
+ m = sp->ext_modify_flags;
+ d->ext_modify_mask = cpu_to_le32(sp->ext_modify_flags);
+ if (m & CMDQ_MODIFY_QP_EXT_MODIFY_MASK_EXT_STATS_CTX)
+ d->ext_stats_ctx_id = cpu_to_le32(sp->roce_stat_ext_xid);
+
+ return 0;
+}
+
+static int bng_sp_fill_mpc_qp_modify_and_create_parms(struct bng_re_res *res,
+ struct qp_modify_data *d,
+ struct bng_re_qp *qp)
+{
+ if (!test_bit(BNG_SP_QP_FLAG_MPC_INITIAL_CREATE_SENT, &qp->sp_qp.flags) &&
+ !test_bit(BNG_SP_QP_FLAG_MPC_QP_FREE, &qp->sp_qp.flags))
+ bng_sp_fill_mpc_qp_create_parms(res, d, qp);
+
+ bng_sp_fill_mpc_qp_modify_parms(res, d, qp);
+ return 0;
+}
+
+static void bng_sp_set_initial_created_flag(struct bng_re_res *res,
+ struct bng_sp_qp *qp)
+{
+ u32 qp_idx;
+ unsigned long flag;
+ struct bng_re_reftbl *tbl;
+
+ tbl = &res->reftbl.qpref;
+ qp_idx = map_qp_id_to_reftbl_indx(qp->id, tbl);
+ spin_lock_irqsave(&tbl->lock, flag);
+ /* Make sure we have the right table index */
+ if (tbl->rec[qp_idx].xid == qp->id &&
+ tbl->rec[qp_idx].handle == qp &&
+ !tbl->rec[qp_idx].initial_mpc_sent)
+ tbl->rec[qp_idx].initial_mpc_sent = true;
+ spin_unlock_irqrestore(&tbl->lock, flag);
+}
+
+int bng_sp_fill_and_send_mpc_qp_modify(struct bng_re_res *res,
+ struct bng_re_qp **qps,
+ int num_qps,
+ bool *xid_cleanup_handled)
+{
+ struct mpc_qp_modify_cmpl resp;
+ struct mpc_qp_modify_cmd cmd;
+ struct bng_re_qp *qp = *qps;
+ struct qp_modify_data *d;
+ u32 cpu_modify_mask;
+ int rc = 0;
+
+ if (xid_cleanup_handled)
+ *xid_cleanup_handled = false;
+
+ if (num_qps != 1)
+ return -EINVAL;
+
+ if (bng_re_init_fw_state_rtr_enabled(res->cctx)) {
+ if (!test_bit(BNG_SP_QP_FLAG_MPC_QP_FREE, &qp->sp_qp.flags)) {
+ if ((qp->sp_qp.state == CMDQ_MODIFY_QP_NEW_STATE_RESET ||
+ qp->sp_qp.state == CMDQ_MODIFY_QP_NEW_STATE_INIT) &&
+ (qp->sp_qp.cur_qp_state == CMDQ_MODIFY_QP_NEW_STATE_RESET ||
+ qp->sp_qp.cur_qp_state == CMDQ_MODIFY_QP_NEW_STATE_INIT)) {
+ dev_warn(&res->pdev->dev,
+ "MPC-skip modify qp- id: %d State: %d->%d\n",
+ qp->sp_qp.id, qp->sp_qp.cur_qp_state,
+ qp->sp_qp.state);
+ qp->sp_qp.cur_qp_state = qp->sp_qp.state;
+ return 0;
+ }
+ }
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&resp, 0, sizeof(resp));
+
+ d = (struct qp_modify_data *)&cmd.qp_cid;
+ bng_sp_fill_mpc_qp_modify_and_create_parms(res, d, qp);
+
+ cmd.resp_size = sizeof(resp) / BNG_RE_MPC_CQ_STRIDE;
+ if (res->is_vf) {
+ cmd.req_type = MPC_CMD_HDR_REQ_TYPE_PFVF;
+ cmd.req_subtype = MPC_CMD_HDR_REQ_SUB_TYPE_PFVF_QP_MODIFY;
+ } else {
+ cmd.req_type = MPC_CMD_HDR_REQ_TYPE_RCA;
+ cmd.req_subtype = MPC_CMD_HDR_REQ_SUB_TYPE_RCA_QP_MODIFY;
+ }
+
+ dev_err(&res->pdev->dev, "MPC: ROCE QP modify id:%d %d->%d\n",
+ qp->sp_qp.id, qp->sp_qp.cur_qp_state, qp->sp_qp.state);
+
+ cpu_modify_mask = le32_to_cpu(d->modify_mask);
+
+ if ((MPC_QP_MODIFY_CMD_QP_MODIFY_DATA_QP_MODIFY_FLAGS_QP_MODIFY_FLAGS_QP_FIRST_MODIFY &
+ cmd.qp_modify_flags) &&
+ !(cpu_modify_mask & CMDQ_MODIFY_QP_MODIFY_MASK_STATE)) {
+ dev_err(&res->pdev->dev,
+ "%s: bad state trans. cqpst:%d,qpst:%d,xid:%d,mask:%x",
+ __func__,
+ qp->sp_qp.cur_qp_state,
+ qp->sp_qp.state,
+ qp->sp_qp.id,
+ cpu_modify_mask);
+ return -EINVAL;
+ }
+
+ if (!res->rdev) {
+ dev_err(&res->pdev->dev, "%s: no rdev for MPC xmit\n", __func__);
+ return -ENODEV;
+ }
+
+ rc = bng_re_roce_mpc_xmit(res->rdev, &cmd, sizeof(cmd),
+ &resp, sizeof(resp), false);
+
+ if (rc) {
+ dev_err(&res->pdev->dev, "MPC: QP modify failed for QP %d, rc=%d\n",
+ qp->sp_qp.id, rc);
+ return rc;
+ }
+
+ if (le16_to_cpu(resp.error_code) != MPC_QP_MODIFY_CMPL_ERROR_CODE_SUCCESS) {
+ dev_err(&res->pdev->dev,
+ "MPC: QP modify cmpl error=%u qp=%u\n",
+ le16_to_cpu(resp.error_code), qp->sp_qp.id);
+ return -EIO;
+ }
+
+ if (qp->sp_qp.id != 1 &&
+ le32_to_cpu(resp.xid) != qp->sp_qp.id) {
+ dev_err(&res->pdev->dev,
+ "MPC: ROCE QP modify xid mismatch cmd:%d resp:%u\n",
+ qp->sp_qp.id, le32_to_cpu(resp.xid));
+ return -EIO;
+ }
+
+ if (qp->sp_qp.state == CMDQ_MODIFY_QP_NEW_STATE_RTR)
+ qp->sp_qp.lag_src_mac = be32_to_cpu(resp.lag_src_mac);
+
+ if (!test_bit(BNG_SP_QP_FLAG_MPC_INITIAL_CREATE_SENT, &qp->sp_qp.flags)) {
+ set_bit(BNG_SP_QP_FLAG_MPC_INITIAL_CREATE_SENT, &qp->sp_qp.flags);
+
+ /* INITIAL_CREATE_SENT is reset when the QP state is modified to RST
+ * QP_DESTROY has QP_FREE flag to release some stats resource
+ * as part of QP destroy in FW and can be sent in RST state
+ */
+
+ if (!test_bit(BNG_SP_QP_FLAG_MPC_ALLOW_QP_DESTROY, &qp->sp_qp.flags)) {
+ set_bit(BNG_SP_QP_FLAG_MPC_ALLOW_QP_DESTROY, &qp->sp_qp.flags);
+ dev_dbg(&res->pdev->dev, "MPC: QP %d marked as initial create sent\n",
+ qp->sp_qp.id);
+ }
+ }
+
+ qp->sp_qp.cur_qp_state = qp->sp_qp.state;
+
+ /* If the QP is moved back to INIT->RTR then send create params again */
+ if (qp->sp_qp.cur_qp_state == CMDQ_MODIFY_QP_NEW_STATE_RESET) {
+ clear_bit(BNG_SP_QP_FLAG_MPC_INITIAL_CREATE_SENT, &qp->sp_qp.flags);
+ dev_dbg(&res->pdev->dev, "MPC: QP[%d] CLEAR initial create sent flag\n",
+ qp->sp_qp.id);
+ }
+
+ bng_sp_set_initial_created_flag(res, &qp->sp_qp);
+
+ return 0;
+}
+
+int bng_sp_mpc_modify_qp(struct bng_re_res *res, struct bng_sp_qp *sp_qp)
+{
+ struct bng_re_qp *qp = container_of(sp_qp, struct bng_re_qp, sp_qp);
+
+ return bng_sp_fill_and_send_mpc_qp_modify(res, &qp, 1, NULL);
+}
+
+int bng_sp_query_qp(struct bng_re_res *res, struct bng_sp_qp *qp)
+{
+ struct bng_re_rcfw *rcfw = res->rcfw;
+ struct creq_query_qp_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct bng_re_rcfw_sbuf sbuf;
+ struct creq_query_qp_resp_sb *sb;
+ struct cmdq_query_qp req = {};
+ int rc;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_QUERY_QP,
+ sizeof(req));
+
+ sbuf.size = ALIGN(sizeof(*sb), BNG_FW_CMDQE_UNITS);
+ sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size,
+ &sbuf.dma_addr, GFP_KERNEL);
+ if (!sbuf.sb)
+ return -ENOMEM;
+ req.resp_size = sbuf.size / BNG_FW_CMDQE_UNITS;
+ req.qp_cid = cpu_to_le32(qp->id);
+ sb = sbuf.sb;
+
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req),
+ sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (!rc) {
+ qp->state = sb->en_sqd_async_notify_state & CREQ_QUERY_QP_RESP_SB_STATE_MASK;
+ qp->cur_qp_state = qp->state; /* Assume same for now */
+ qp->en_sqd_async_notify =
+ (sb->en_sqd_async_notify_state &
+ CREQ_QUERY_QP_RESP_SB_EN_SQD_ASYNC_NOTIFY) ? 1 : 0;
+ qp->access = sb->access;
+ qp->pkey_index = le16_to_cpu(sb->pkey);
+ qp->qkey = le32_to_cpu(sb->qkey);
+ qp->ah.host_sgid_index = le16_to_cpu(sb->sgid_index);
+ qp->udp_sport = le16_to_cpu(sb->udp_src_port);
+ qp->ah.hop_limit = sb->hop_limit;
+ qp->ah.traffic_class = sb->traffic_class;
+ qp->ah.sl = 0;
+ qp->path_mtu = (le16_to_cpu(sb->path_mtu_dest_vlan_id)
+ & CREQ_QUERY_QP_RESP_SB_PATH_MTU_MASK)
+ >> CREQ_QUERY_QP_RESP_SB_PATH_MTU_SFT;
+ qp->timeout = sb->timeout;
+ qp->retry_cnt = sb->retry_cnt;
+ qp->rnr_retry = sb->rnr_retry;
+ qp->min_rnr_timer = sb->min_rnr_timer;
+ qp->port_id = le16_to_cpu(sb->port_id);
+ qp->rq.psn = le32_to_cpu(sb->rq_psn) & 0xffffff;
+ qp->max_rd_atomic = sb->max_rd_atomic;
+ qp->sq.psn = le32_to_cpu(sb->sq_psn) & 0xffffff;
+ qp->max_dest_rd_atomic = sb->max_dest_rd_atomic;
+ qp->dest_qpn = le32_to_cpu(sb->dest_qp_id);
+ memcpy(qp->ah.dgid.data, sb->dgid, sizeof(qp->ah.dgid.data));
+ }
+ dma_free_coherent(&rcfw->pdev->dev, sbuf.size,
+ sbuf.sb, sbuf.dma_addr);
+
+ return rc;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.h b/drivers/infiniband/hw/bng_re/bng_sp.h
index a6f688c0af3d..60f1cdef145e 100644
--- a/drivers/infiniband/hw/bng_re/bng_sp.h
+++ b/drivers/infiniband/hw/bng_re/bng_sp.h
@@ -21,6 +21,35 @@
#define NQE_PG(x) (((x) & ~NQE_MAX_IDX_PER_PG) / NQE_CNT_PER_PG)
#define NQE_IDX(x) ((x) & NQE_MAX_IDX_PER_PG)
+#define BNG_RE_MAX_SQSZ 0xffffu
+
+#define BNG_VAR_MAX_SLOT_ALIGN 256
+#define IRD_LIMIT_TO_IRRQ_SLOTS(x) (2 * (x) + 2)
+#define ORD_LIMIT_TO_ORRQ_SLOTS(x) ((x) + 1)
+#define SQ_MAX_NUM_WQES_DEFAULT 16
+#define IB_DEFAULT_PKEY_FULL 0xFFFF
+
+#define BNG_RE_IQM_INVALID_IDX (0xFFFFFFFFU)
+#define BNG_RE_IQM_ROUND_UP_BYTE_SIZE 128
+
+#define BNG_RE_MAX_ORRQE_ENTRY_SIZE sizeof(struct xrrq_orrq)
+#define BNG_RE_MAX_IRRQE_ENTRY_SIZE sizeof(struct xrrq_irrq)
+#define BNG_RE_IQM_IRRQ_OVERFLOW_COMP \
+ ((1) * (BNG_RE_IQM_ROUND_UP_BYTE_SIZE / BNG_RE_MAX_IRRQE_ENTRY_SIZE))
+#define IRRQ_CACHELINE_TO_ENTRIES(x) ((x) * (BNG_RE_IQM_ROUND_UP_BYTE_SIZE / \
+ BNG_RE_MAX_IRRQE_ENTRY_SIZE))
+#define ORRQ_CACHELINE_TO_ENTRIES(x) ((x) * (BNG_RE_IQM_ROUND_UP_BYTE_SIZE / \
+ BNG_RE_MAX_ORRQE_ENTRY_SIZE))
+
+struct bng_re_qp;
+
+/* MPC slowpath software bits in sp_qp.flags */
+enum {
+ BNG_SP_QP_FLAG_MPC_INITIAL_CREATE_SENT = 0,
+ BNG_SP_QP_FLAG_MPC_QP_FREE = 1,
+ BNG_SP_QP_FLAG_MPC_ALLOW_QP_DESTROY = 2,
+};
+
struct bng_re_dev_attr {
#define FW_VER_ARR_LEN 4
u8 fw_ver[FW_VER_ARR_LEN];
@@ -33,6 +62,7 @@ struct bng_re_dev_attr {
u32 max_qp_init_rd_atom;
u32 max_qp_wqes;
u32 max_sq_wqes;
+ u32 max_rq_wqes;
u32 max_qp_sges;
u32 max_cq;
u32 max_cq_wqes;
@@ -53,6 +83,7 @@ struct bng_re_dev_attr {
bool is_atomic;
u16 dev_cap_flags;
u16 dev_cap_flags2;
+ u16 dev_cap_ext_flags2;
u32 max_dpi;
};
@@ -287,6 +318,135 @@ struct bng_sp_ah {
u8 nw_type;
};
+struct bng_sp_qp {
+ struct bng_sp_pd *pd;
+ struct bng_re_dpi *dpi;
+ struct bng_re_chip_ctx *cctx;
+ u64 qp_handle;
+ u32 id;
+ u8 is_user;
+ u8 type;
+ u8 sig_type;
+ u8 wqe_mode;
+ u8 state;
+ u8 cur_qp_state;
+ u16 dev_cap_ext_flags2;
+ u64 modify_flags;
+ unsigned long flags;
+ u32 max_inline_data;
+ u32 mtu;
+ u8 path_mtu;
+ bool en_sqd_async_notify;
+ u16 pkey_index;
+ u32 qkey;
+ u32 dest_qp_id;
+ u8 access;
+ u8 timeout;
+ u8 retry_cnt;
+ u8 rnr_retry;
+ u64 wqe_cnt;
+ u32 min_rnr_timer;
+ u32 max_rd_atomic;
+ u32 max_dest_rd_atomic;
+ u32 dest_qpn;
+ u32 ext_modify_flags;
+ u32 roce_stat_ext_xid;
+ u8 smac[6];
+ u16 vlan_id;
+ u16 port_id;
+ u16 udp_sport;
+ u8 nw_type;
+ bool is_roce_mirror_qp;
+ u8 tos_ecn;
+ u8 tos_dscp;
+ struct bng_sp_ah ah;
+
+ struct bng_re_q sq;
+ struct bng_re_q rq;
+ struct bng_sp_srq *srq;
+ struct bng_sp_cq *scq;
+ struct bng_sp_cq *rcq;
+ struct bng_re_hwq irrq;
+ struct bng_re_hwq orrq;
+ int sq_hdr_buf_size;
+ int rq_hdr_buf_size;
+ void *sq_hdr_buf;
+ dma_addr_t sq_hdr_buf_map;
+ void *rq_hdr_buf;
+ dma_addr_t rq_hdr_buf_map;
+ struct list_head sq_flush;
+ struct list_head rq_flush;
+ /* 4-byte scrambled MAC received from FW on RTR transition */
+ u32 lag_src_mac;
+ u32 msn;
+ /* indicates buffers avail for rq; at transition to rtr */
+ u32 req_buffer_count;
+ u8 rtr_transition_pending;
+ u8 dev_cap_ext_flags;
+ bool udcc_exclude;
+ u32 ugid_index;
+#define INVALID_VF_IDX 0xFFFFFFFF
+ u32 vf_idx;
+};
+
+/* SWQ */
+struct bng_re_swq {
+ u64 wr_id;
+ int next_idx;
+ u8 type;
+ u8 flags;
+ u32 slot_idx;
+ u8 slots;
+};
+
+static inline u32 bng_re_get_depth(struct bng_re_q *que, bool is_sq)
+{
+ u32 slots;
+
+ slots = (que->wqe_size * que->max_wqe) / sizeof(struct sq_sge);
+ if (is_sq)
+ slots = ALIGN(slots, BNG_VAR_MAX_SLOT_ALIGN);
+ return slots;
+}
+
+static inline u32 bng_re_set_sq_size(struct bng_re_q *que)
+{
+ return bng_re_get_depth(que, true);
+}
+
+static inline u32 bng_re_set_sq_max_slot(u8 wqe_mode)
+{
+ return 1;
+}
+
+static inline u32 bng_re_set_rq_max_slot(u32 wqe_size)
+{
+ return (wqe_size / sizeof(struct sq_sge));
+}
+
+static inline const char *__to_qp_state_str(u8 state)
+{
+ switch (state) {
+ case CMDQ_MODIFY_QP_NEW_STATE_RESET:
+ return "RESET";
+ case CMDQ_MODIFY_QP_NEW_STATE_INIT:
+ return "INIT";
+ case CMDQ_MODIFY_QP_NEW_STATE_RTR:
+ return "RTR";
+ case CMDQ_MODIFY_QP_NEW_STATE_RTS:
+ return "RTS";
+ case CMDQ_MODIFY_QP_NEW_STATE_SQD:
+ return "SQD";
+ case CMDQ_MODIFY_QP_NEW_STATE_SQE:
+ return "SQE";
+ case CMDQ_MODIFY_QP_NEW_STATE_ERR:
+ return "ERR";
+ default:
+ return "NotSupp";
+ }
+}
+
+int bng_re_alloc_init_swq(struct bng_re_q *que);
int bng_re_get_dev_attr(struct bng_re_rcfw *rcfw);
int bng_sp_alloc_sgid_tbl(struct bng_re_sgid_tbl *sgid_tbl, u16 size);
@@ -322,6 +482,7 @@ int bng_sp_resize_cq(struct bng_re_res *res, struct bng_sp_cq *cq,
int new_cqes);
int bng_sp_destroy_cq(struct bng_re_res *res, struct bng_sp_cq *cq);
int bng_sp_create_srq(struct bng_re_res *res, struct bng_sp_srq *srq);
+int bng_sp_query_qp(struct bng_re_res *res, struct bng_sp_qp *qp);
int bng_sp_query_srq(struct bng_re_res *res, struct bng_sp_srq *srq);
int bng_sp_destroy_srq(struct bng_re_res *res, struct bng_sp_srq *srq);
int bng_re_qext_stat(struct bng_re_rcfw *rcfw, u32 fid,
@@ -331,5 +492,14 @@ int bng_re_get_roce_stats(struct bng_re_rcfw *rcfw,
int bng_sp_fill_and_send_mpc_ah_modify(struct bng_re_res *res,
struct bng_sp_ah **ahs,
int num_ahs, bool for_destroy, bool block);
-
+void bng_re_flush_cqn_wq(struct bng_sp_qp *qp);
+void bng_re_free_qp_res(struct bng_re_res *res, struct bng_sp_qp *qp);
+void bng_re_clean_qp(struct bng_sp_qp *qp);
+int bng_sp_fill_and_send_mpc_qp_modify(struct bng_re_res *res,
+ struct bng_re_qp **qps,
+ int num_qps,
+ bool *xid_cleanup_handled);
+void bng_re_add_flush_qp(struct bng_sp_qp *qp);
+int bng_sp_mpc_create_qp(struct bng_re_res *res, struct bng_sp_qp *qp);
+int bng_sp_mpc_modify_qp(struct bng_re_res *res, struct bng_sp_qp *sp_qp);
#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.c b/drivers/infiniband/hw/bng_re/bng_verbs.c
index c40f8686b6f6..e91fd111d2f1 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.c
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.c
@@ -5,6 +5,7 @@
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/io.h>
+#include <linux/crc16.h>
#include <net/addrconf.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_verbs.h>
@@ -22,6 +23,7 @@
#include "bng_re.h"
#include "bng_verbs.h"
#include "bng_fp.h"
+#include "bng_xid.h"
int bng_re_query_device(struct ib_device *ibdev,
struct ib_device_attr *ib_attr,
@@ -578,6 +580,21 @@ static void bng_re_check_and_set_relaxed_ordering(struct bng_re_dev *rdev,
mr->flags |= CMDQ_REGISTER_MR_FLAGS_ENABLE_RO;
}
+static int __qp_access_flags_to_ib(u32 cctx, u8 access)
+{
+ int qflags = 0;
+
+ if (access & CMDQ_MODIFY_QP_ACCESS_LOCAL_WRITE)
+ qflags |= IB_ACCESS_LOCAL_WRITE;
+ if (access & CMDQ_MODIFY_QP_ACCESS_REMOTE_READ)
+ qflags |= IB_ACCESS_REMOTE_READ;
+ if (access & CMDQ_MODIFY_QP_ACCESS_REMOTE_WRITE)
+ qflags |= IB_ACCESS_REMOTE_WRITE;
+ if (access & CMDQ_MODIFY_QP_ACCESS_REMOTE_ATOMIC)
+ qflags |= IB_ACCESS_REMOTE_ATOMIC;
+ return qflags;
+}
+
static int __from_ib_access_flags(int iflags)
{
int qflags = 0;
@@ -1627,3 +1644,1568 @@ int bng_re_query_ah(struct ib_ah *ib_ah, struct rdma_ah_attr *ah_attr)
rdma_ah_set_static_rate(ah_attr, 0);
return 0;
}
+
+/* Helper functions for QP operations */
+static bool bng_re_init_qp_wqe_mode(struct bng_re_dev *rdev)
+{
+ return rdev->chip_ctx->modes.wqe_mode;
+}
+
+static u8 __from_ib_qp_type(enum ib_qp_type type)
+{
+ switch (type) {
+ case IB_QPT_GSI:
+ return CMDQ_CREATE_QP1_TYPE_GSI;
+ case IB_QPT_RC:
+ return CMDQ_CREATE_QP_TYPE_RC;
+ case IB_QPT_UD:
+ return CMDQ_CREATE_QP_TYPE_UD;
+ case IB_QPT_RAW_PACKET:
+ return CMDQ_CREATE_QP_TYPE_RAW_ETHERTYPE;
+ default:
+ return IB_QPT_MAX;
+ }
+}
+
+static bool bng_re_test_qp_limits(struct bng_re_dev *rdev,
+ struct ib_qp_init_attr *init_attr,
+ struct bng_re_dev_attr *dev_attr)
+{
+ bool rc = true;
+
+ int ilsize = ALIGN(init_attr->cap.max_inline_data, sizeof(struct sq_sge));
+
+ if (init_attr->cap.max_send_wr < 1) {
+ ibdev_err(&rdev->ibdev, "Create QP failed - max_send_wr must be >= 1");
+ rc = false;
+ }
+ if (!init_attr->srq && init_attr->cap.max_recv_wr < 1) {
+ ibdev_err(&rdev->ibdev, "Create QP failed - max_recv_wr must be >= 1 when not using SRQ");
+ rc = false;
+ }
+ if (init_attr->cap.max_send_wr > dev_attr->max_sq_wqes ||
+ init_attr->cap.max_recv_wr > dev_attr->max_rq_wqes ||
+ init_attr->cap.max_send_sge > dev_attr->max_qp_sges ||
+ init_attr->cap.max_recv_sge > dev_attr->max_qp_sges ||
+ ilsize > dev_attr->max_inline_data) {
+ ibdev_err(&rdev->ibdev,
+ "Create QP failed - max exceeded! 0x%x/0x%x 0x%x/0x%x 0x%x/0x%x 0x%x/0x%x 0x%x/0x%x",
+ init_attr->cap.max_send_wr, dev_attr->max_sq_wqes,
+ init_attr->cap.max_recv_wr, dev_attr->max_rq_wqes,
+ init_attr->cap.max_send_sge, dev_attr->max_qp_sges,
+ init_attr->cap.max_recv_sge, dev_attr->max_qp_sges,
+ ilsize,
+ dev_attr->max_inline_data);
+ rc = false;
+ }
+ return rc;
+}
+
+static int bng_re_init_qp_type(struct bng_re_dev *rdev,
+ struct ib_qp_init_attr *init_attr)
+{
+ int qptype;
+
+ qptype = __from_ib_qp_type(init_attr->qp_type);
+ if (qptype == IB_QPT_MAX) {
+ ibdev_err(&rdev->ibdev, "QP type 0x%x not supported", qptype);
+ qptype = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (init_attr->qp_type == IB_QPT_GSI) {
+ qptype = CMDQ_CREATE_QP_TYPE_GSI;
+ rdev->gsi_ctx.gsi_qp_mode = BNG_RE_GSI_MODE_UD;
+ }
+out:
+ return qptype;
+}
+
+static int bng_re_init_rq_attr(struct bng_re_qp *qp,
+ struct ib_qp_init_attr *init_attr,
+ struct bng_re_ucontext *uctx)
+{
+ struct bng_re_dev_attr *dev_attr;
+ struct bng_sp_qp *sp_qp;
+ struct bng_re_dev *rdev;
+ struct bng_re_q *rq;
+ int entries;
+
+ rdev = qp->rdev;
+ sp_qp = &qp->sp_qp;
+ rq = &sp_qp->rq;
+ dev_attr = rdev->dev_attr;
+
+ if (init_attr->srq) {
+ struct bng_re_srq *srq;
+
+ srq = container_of(init_attr->srq, struct bng_re_srq, ib_srq);
+ if (!srq) {
+ dev_err(rdev_to_dev(rdev), "SRQ not found");
+ return -EINVAL;
+ }
+ sp_qp->srq = (struct bng_sp_srq *)&srq->sp_srq;
+ rq->max_wqe = 0;
+ } else {
+ rq->max_sge = init_attr->cap.max_recv_sge;
+ if (rq->max_sge > dev_attr->max_qp_sges)
+ rq->max_sge = dev_attr->max_qp_sges;
+ init_attr->cap.max_recv_sge = rq->max_sge;
+ rq->wqe_size = bng_re_get_rwqe_size(rq->max_sge);
+ /* Allocate 1 more than what's provided so posting max doesn't
+ * mean empty.
+ */
+
+ entries = init_attr->cap.max_recv_wr + 1;
+ entries = bng_re_init_depth(entries, uctx);
+ if (init_attr->qp_type == IB_QPT_GSI)
+ entries++;
+ rq->max_wqe = min_t(u32, entries, dev_attr->max_rq_wqes);
+ rq->max_sw_wqe = rq->max_wqe;
+ rq->q_full_delta = 0;
+ rq->sg_info.pgsize = PAGE_SIZE;
+ rq->sg_info.pgshft = PAGE_SHIFT;
+ }
+
+ return 0;
+}
+
+static u16 bng_re_get_swqe_size(int ilsize, int nsge, int align)
+{
+ u16 wqe_size, calc_ils;
+
+ wqe_size = __get_swqe_size(nsge);
+ if (ilsize) {
+ calc_ils = sizeof(struct sq_atomic_hdr) + ilsize;
+ wqe_size = max_t(int, calc_ils, wqe_size);
+ }
+ wqe_size = ALIGN(wqe_size, align);
+ return wqe_size;
+}
+
+static int bng_re_setup_swqe_size(struct bng_re_qp *qp,
+ u32 *max_inline_data)
+{
+ struct bng_re_dev_attr *dev_attr;
+ struct bng_sp_qp *sp_qp;
+ struct bng_re_dev *rdev;
+ struct bng_re_q *sq;
+ int align, ilsize;
+
+ rdev = qp->rdev;
+ sp_qp = &qp->sp_qp;
+ sq = &sp_qp->sq;
+ dev_attr = rdev->dev_attr;
+
+ align = sizeof(struct sq_sge);
+ ilsize = ALIGN(*max_inline_data, align);
+
+ sq->wqe_size = bng_re_get_swqe_size(ilsize, sq->max_sge, align);
+ if (sq->wqe_size > ALIGN(__get_swqe_size(dev_attr->max_qp_sges), align))
+ return -EINVAL;
+
+ if (*max_inline_data) {
+ sp_qp->max_inline_data = sq->wqe_size -
+ ALIGN(sizeof(struct sq_atomic_hdr), align);
+ *max_inline_data = sp_qp->max_inline_data;
+ }
+
+ return 0;
+}
+
+static int bng_re_init_sq_attr(struct bng_re_qp *qp,
+ struct ib_qp_init_attr *init_attr,
+ struct bng_re_ucontext *uctx)
+{
+ struct bng_re_dev_attr *dev_attr;
+ struct bng_sp_qp *sp_qp;
+ struct bng_re_dev *rdev;
+ struct bng_re_q *sq;
+ int diff = 0;
+ int entries;
+ int rc;
+
+ rdev = qp->rdev;
+ sp_qp = &qp->sp_qp;
+ sq = &sp_qp->sq;
+ dev_attr = rdev->dev_attr;
+
+ entries = init_attr->cap.max_send_wr;
+
+ if (sq->max_sge > dev_attr->max_qp_sges) {
+ sq->max_sge = dev_attr->max_qp_sges;
+ init_attr->cap.max_send_sge = sq->max_sge;
+ }
+
+ rc = bng_re_setup_swqe_size(qp, &init_attr->cap.max_inline_data);
+ if (rc)
+ return rc;
+
+ /*
+ * Change the SQ depth if user has requested minimum using
+ * configfs. Only supported for kernel consumers. Setting
+ * min_tx_depth to 4096 to handle iser SQ full condition
+ * in most of the newer OS distros
+ */
+
+ entries = init_attr->cap.max_send_wr;
+
+ if (!uctx && rdev->min_tx_depth && init_attr->qp_type != IB_QPT_GSI) {
+ /*
+ * If users specify any value greater than 1 use min_tx_depth
+ * provided by user for comparison. Else, compare it with the
+ * BNG_RE_MIN_KERNEL_QP_TX_DEPTH and adjust it accordingly.
+ */
+ if (rdev->min_tx_depth > 1 && entries < rdev->min_tx_depth)
+ entries = rdev->min_tx_depth;
+ else if (entries < BNG_RE_MIN_KERNEL_QP_TX_DEPTH)
+ entries = BNG_RE_MIN_KERNEL_QP_TX_DEPTH;
+ }
+
+ diff = bng_re_get_diff(uctx, rdev->chip_ctx);
+ entries = bng_re_init_depth(entries + diff + 1, uctx);
+ sq->max_wqe = min_t(u32, entries, dev_attr->max_sq_wqes + diff + 1);
+ sq->q_full_delta = diff + 1;
+ /*
+ * Reserving one slot for Phantom WQE. Application can
+ * post one extra entry in this case. But allowing this to avoid
+ * unexpected Queue full condition
+ */
+ sp_qp->sq.q_full_delta -= 1;
+ sp_qp->sq.sg_info.pgsize = PAGE_SIZE;
+ sp_qp->sq.sg_info.pgshft = PAGE_SHIFT;
+
+ return 0;
+}
+
+static int bng_re_init_user_qp(struct bng_re_dev *rdev, struct bng_re_pd *pd,
+ struct bng_re_qp *qp, struct ib_udata *udata)
+{
+ struct bng_re_qp_req ureq = {};
+ struct bng_re_sg_info *sginfo;
+ struct bng_re_ucontext *cntx;
+ struct ib_ucontext *context;
+ struct bng_sp_qp *sp_qp;
+ struct ib_umem *umem;
+ int rc, bytes = 0;
+
+ sp_qp = &qp->sp_qp;
+ context = pd->ib_pd.uobject->context;
+ cntx = to_bng_re(context, struct bng_re_ucontext, ib_uctx);
+ sginfo = &sp_qp->sq.sg_info;
+
+ if (udata) {
+ if (udata->inlen < sizeof(ureq))
+ dev_warn_once(rdev_to_dev(rdev),
+ "Update the library ulen %d klen %d",
+ (unsigned int)udata->inlen,
+ (unsigned int)sizeof(ureq));
+ rc = ib_copy_from_udata(&ureq, udata, min(udata->inlen, sizeof(ureq)));
+ if (rc)
+ return rc;
+ }
+
+ bytes = (sp_qp->sq.max_wqe * sp_qp->sq.wqe_size);
+ bytes = PAGE_ALIGN(bytes);
+
+ umem = ib_umem_get_va(&rdev->ibdev, ureq.qpsva, bytes,
+ IB_ACCESS_LOCAL_WRITE);
+ if (IS_ERR(umem)) {
+ dev_err(rdev_to_dev(rdev), "%s: ib_umem_get_va failed with %ld\n",
+ __func__, PTR_ERR(umem));
+ return PTR_ERR(umem);
+ }
+
+ qp->sumem = umem;
+ sginfo->npages = ib_umem_num_dma_blocks(umem, PAGE_SIZE);
+ sginfo->umem = umem;
+ sp_qp->qp_handle = ureq.qp_handle;
+
+ if (!qp->sp_qp.srq) {
+ sginfo = &sp_qp->rq.sg_info;
+ bytes = (sp_qp->rq.max_wqe * sp_qp->rq.wqe_size);
+ bytes = PAGE_ALIGN(bytes);
+ umem = ib_umem_get_va(&rdev->ibdev, ureq.qprva, bytes,
+ IB_ACCESS_LOCAL_WRITE);
+ if (IS_ERR(umem)) {
+ dev_err(rdev_to_dev(rdev),
+ "%s: ib_umem_get_va failed ret =%ld\n",
+ __func__, PTR_ERR(umem));
+ goto rq_fail;
+ }
+ qp->rumem = umem;
+ sp_qp->rq.sg_info.umem = umem;
+ sginfo->npages = ib_umem_num_dma_blocks(umem, PAGE_SIZE);
+ sginfo->umem = umem;
+ }
+
+ sp_qp->dpi = &cntx->dpi;
+ sp_qp->is_user = true;
+
+ return 0;
+rq_fail:
+ ib_umem_release(qp->sumem);
+ qp->sumem = NULL;
+ sp_qp->sq.sg_info.umem = NULL;
+
+ return PTR_ERR(umem);
+}
+
+static int bng_re_init_qp_attr(struct bng_re_qp *qp, struct bng_re_pd *pd,
+ struct ib_qp_init_attr *init_attr, struct ib_udata *udata)
+{
+ struct bng_re_ucontext *cntx = NULL;
+ struct bng_re_dev_attr *dev_attr;
+ struct ib_ucontext *context;
+ struct bng_sp_qp *sp_qp;
+ struct bng_re_dev *rdev;
+ struct bng_re_cq *cq;
+ int rc = 0, qptype;
+
+ rdev = qp->rdev;
+ sp_qp = &qp->sp_qp;
+ dev_attr = rdev->dev_attr;
+
+ sp_qp->vf_idx = INVALID_VF_IDX;
+
+ if (udata) {
+ context = pd->ib_pd.uobject->context;
+ cntx = to_bng_re(context, struct bng_re_ucontext, ib_uctx);
+ }
+
+ sp_qp->is_user = udata ? true : false;
+ sp_qp->pd = &pd->sp_pd;
+ sp_qp->qp_handle = (u64)sp_qp;
+ sp_qp->max_inline_data = init_attr->cap.max_inline_data;
+ sp_qp->sig_type = init_attr->sq_sig_type == IB_SIGNAL_ALL_WR;
+ qptype = bng_re_init_qp_type(rdev, init_attr);
+ if (qptype < 0) {
+ rc = qptype;
+ goto out;
+ }
+ sp_qp->type = (u8)qptype;
+ sp_qp->wqe_mode = bng_re_init_qp_wqe_mode(rdev);
+ ether_addr_copy(sp_qp->smac, rdev->netdev->dev_addr);
+
+ if (init_attr->qp_type == IB_QPT_RC) {
+ sp_qp->max_rd_atomic = dev_attr->max_qp_rd_atom;
+ sp_qp->max_dest_rd_atomic = dev_attr->max_qp_init_rd_atom;
+ }
+ sp_qp->mtu = ib_mtu_enum_to_int(iboe_get_mtu(rdev->netdev->mtu));
+ sp_qp->dpi = &rdev->dpi_privileged;
+ if (init_attr->create_flags) {
+ ibdev_dbg(&rdev->ibdev,
+ "QP create flags 0x%x not supported",
+ init_attr->create_flags);
+ return -EOPNOTSUPP;
+ }
+
+ /* Setup CQs */
+ if (init_attr->send_cq) {
+ cq = to_bng_re(init_attr->send_cq, struct bng_re_cq, ib_cq);
+ if (!cq) {
+ dev_err(rdev_to_dev(rdev), "Send CQ not found");
+ rc = -EINVAL;
+ goto out;
+ }
+ sp_qp->scq = &cq->sp_cq;
+ qp->scq = cq;
+ }
+
+ if (init_attr->recv_cq) {
+ cq = to_bng_re(init_attr->recv_cq, struct bng_re_cq, ib_cq);
+ if (!cq) {
+ dev_err(rdev_to_dev(rdev), "Send CQ not found");
+ rc = -EINVAL;
+ goto out;
+ }
+ sp_qp->rcq = &cq->sp_cq;
+ qp->rcq = cq;
+ }
+
+ if ((init_attr->qp_type == IB_QPT_RC || init_attr->qp_type == IB_QPT_UC) &&
+ (!sp_qp->scq || !sp_qp->rcq)) {
+ ibdev_err(&rdev->ibdev,
+ "Create QP: RC/UC requires both send_cq and recv_cq\n");
+ return -EINVAL;
+ }
+
+ /* Setup RQ/SRQ */
+ rc = bng_re_init_rq_attr(qp, init_attr, cntx);
+ if (rc)
+ goto out;
+
+ /* Setup SQ */
+ rc = bng_re_init_sq_attr(qp, init_attr, cntx);
+ if (rc)
+ goto out;
+
+ if (udata) /* This will update DPI and qp_handle */
+ rc = bng_re_init_user_qp(rdev, pd, qp, udata);
+out:
+ return rc;
+}
+
+/*
+ * bng_re_create_qp_stage1
+ *
+ * Return: 0 on success, negative errno on failure (all partial allocations unwound).
+ */
+static int bng_re_create_qp_stage1(struct bng_re_res *res, struct bng_re_qp *qp)
+{
+ struct bng_re_rcfw *rcfw;
+ struct bng_re_hwq_attr hwq_attr = {};
+ struct bng_sp_qp *fp = &qp->sp_qp;
+ struct bng_re_q *sq = &fp->sq;
+ struct bng_re_q *rq = &fp->rq;
+ u32 tbl_indx;
+ struct bng_re_reftbl *tbl;
+ u32 sqsz;
+ int vf_id = 0;
+ int xid_out = 0;
+ int rc;
+ u32 qp_idx;
+ unsigned long flag;
+
+ if (!res || !res->pdev || !res->rcfw)
+ return -EINVAL;
+ rcfw = res->rcfw;
+ if (!rcfw->qp_tbl || rcfw->qp_tbl_size < 3) {
+ dev_err(&res->pdev->dev,
+ "Stage1: RCFW qp_tbl missing or too small (size=%d)\n",
+ rcfw->qp_tbl_size);
+ return -ENODEV;
+ }
+ if (!fp->dpi || !fp->dpi->dbr) {
+ dev_err(&res->pdev->dev, "Stage1: QP DPI / doorbell not initialized\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(&res->pdev->dev,
+ "MPC: Stage1 enter type=%u srq=%d wqe_mode=%u rq_max_wqe=%u qp_tbl_sz=%d dpi=%u\n",
+ fp->type, fp->srq ? 1 : 0, fp->wqe_mode,
+ fp->srq ? 0U : rq->max_wqe, rcfw->qp_tbl_size,
+ fp->dpi ? fp->dpi->dpi : 0U);
+
+ fp->cctx = res->cctx;
+ fp->flags = 0;
+ sq->dbinfo.flags = 0;
+
+ hwq_attr.res = res;
+ hwq_attr.sginfo = &sq->sg_info;
+ hwq_attr.stride = sizeof(struct sq_sge);
+ hwq_attr.depth = bng_re_get_depth(sq, true);
+ /* Thor3: no SQ aux ring for retransmission metadata */
+ hwq_attr.aux_stride = 0;
+ hwq_attr.aux_depth = 0;
+ hwq_attr.type = BNG_HWQ_TYPE_QUEUE;
+
+ rc = bng_re_alloc_init_hwq(&sq->hwq, &hwq_attr);
+ if (rc) {
+ dev_dbg(&res->pdev->dev, "MPC: Stage1 SQ hwq alloc failed rc=%d\n", rc);
+ goto exit;
+ }
+
+ sqsz = bng_re_set_sq_size(sq);
+ sq->max_sw_wqe = sqsz;
+ if (!sqsz || sqsz > BNG_RE_MAX_SQSZ) {
+ dev_err(&res->pdev->dev,
+ "bng_re: Stage1 SQ size %u invalid (max %u)\n",
+ sqsz, BNG_RE_MAX_SQSZ);
+ rc = -EINVAL;
+ goto fail_sq;
+ }
+
+ if (!fp->srq) {
+ rq->dbinfo.flags = 0;
+ hwq_attr.res = res;
+ hwq_attr.sginfo = &rq->sg_info;
+ hwq_attr.stride = sizeof(struct sq_sge);
+ hwq_attr.depth = bng_re_get_depth(rq, false);
+ hwq_attr.aux_stride = 0;
+ hwq_attr.aux_depth = 0;
+ hwq_attr.type = BNG_HWQ_TYPE_QUEUE;
+ dev_dbg(&res->pdev->dev,
+ "MPC: Stage1 RQ hwq depth=%u max_wqe=%u wqe_sz=%u\n",
+ hwq_attr.depth, rq->max_wqe, rq->wqe_size);
+ rc = bng_re_alloc_init_hwq(&rq->hwq, &hwq_attr);
+ if (rc) {
+ dev_dbg(&res->pdev->dev,
+ "MPC: Stage1 RQ hwq alloc failed rc=%d\n", rc);
+ goto fail_sq;
+ }
+ dev_dbg(&res->pdev->dev,
+ "MPC: Stage1 RQ hwq ok max_elems=%u is_user=%d\n",
+ rq->hwq.max_elements, rq->hwq.is_user);
+ } else {
+ dev_dbg(&res->pdev->dev, "MPC: Stage1 skip RQ hwq (SRQ)\n");
+ }
+
+ if (!sq->hwq.is_user) {
+ dev_dbg(&res->pdev->dev,
+ "MPC: Stage1 kernel SWQ path max_sw_wqe=%u\n", sq->max_sw_wqe);
+ rc = bng_re_alloc_init_swq(sq);
+ if (rc) {
+ dev_dbg(&res->pdev->dev,
+ "MPC: Stage1 SQ swq alloc failed rc=%d\n", rc);
+ goto swq_sq;
+ }
+ if (!fp->srq) {
+ rc = bng_re_alloc_init_swq(rq);
+ if (rc) {
+ dev_dbg(&res->pdev->dev,
+ "MPC: Stage1 RQ swq alloc failed rc=%d\n", rc);
+ goto swq_rq;
+ }
+ }
+ } else {
+ dev_dbg(&res->pdev->dev, "MPC: Stage1 skip kernel SWQ (user SQ)\n");
+ }
+
+ rc = bng_re_qp_alloc_xid_and_iqm(res, fp->type, 1, sq->max_sw_wqe,
+ vf_id, &qp->iqm_res, &xid_out);
+ if (!rc)
+ fp->id = (u32)xid_out;
+ if (rc) {
+ dev_dbg(&res->pdev->dev,
+ "MPC: Stage1 XID/VF path failed rc=%d (no fp->id assign)\n", rc);
+ goto swq_rq;
+ }
+
+ fp->cur_qp_state = CMDQ_MODIFY_QP_NEW_STATE_RESET;
+ INIT_LIST_HEAD(&fp->sq_flush);
+ INIT_LIST_HEAD(&fp->rq_flush);
+
+ sq->dbinfo.hwq = &sq->hwq;
+ sq->dbinfo.xid = fp->id;
+ sq->dbinfo.db = fp->dpi->dbr;
+ sq->dbinfo.max_slot = bng_re_set_sq_max_slot(fp->wqe_mode);
+ sq->dbinfo.flags = 0;
+ spin_lock_init(&sq->dbinfo.lock);
+ sq->dbinfo.seed = fp->id;
+ sq->dbinfo.res = res;
+ sq->dbinfo.is_l2 = false;
+
+ if (rq->max_wqe) {
+ rq->dbinfo.hwq = &rq->hwq;
+ rq->dbinfo.xid = fp->id;
+ rq->dbinfo.db = fp->dpi->dbr;
+ rq->dbinfo.max_slot = bng_re_set_rq_max_slot(rq->wqe_size);
+ rq->dbinfo.flags = 0;
+ spin_lock_init(&rq->dbinfo.lock);
+ rq->dbinfo.seed = fp->id;
+ rq->dbinfo.res = res;
+ rq->dbinfo.is_l2 = false;
+ }
+
+ tbl = &res->reftbl.qpref;
+ qp_idx = map_qp_id_to_reftbl_indx(fp->id, tbl);
+ spin_lock_irqsave(&tbl->lock, flag);
+ tbl->rec[qp_idx].xid = fp->id;
+ tbl->rec[qp_idx].handle = fp;
+ spin_unlock_irqrestore(&tbl->lock, flag);
+
+ if (fp->type == CMDQ_CREATE_QP_TYPE_RC) {
+ dev_dbg(&res->pdev->dev,
+ "MPC: Stage1 ok id=%u tbl=%u IQM irrq=%#x sz=%u orrq=%#x msn=%#x\n",
+ fp->id, tbl_indx, qp->iqm_res.irrq_addr, qp->iqm_res.irrq_size,
+ qp->iqm_res.orrq_addr, qp->iqm_res.msn_addr);
+ } else {
+ dev_dbg(&res->pdev->dev,
+ "MPC: Stage1 ok id=%u tbl=%u type=%u (no RC IQM dump)\n",
+ fp->id, tbl_indx, fp->type);
+ }
+
+ return 0;
+
+swq_rq:
+ kvfree(sq->swq);
+ sq->swq = NULL;
+ if (!fp->srq) {
+ kvfree(rq->swq);
+ rq->swq = NULL;
+ bng_re_free_hwq(res, &rq->hwq);
+ }
+ goto fail_sq;
+swq_sq:
+ if (!fp->srq)
+ bng_re_free_hwq(res, &rq->hwq);
+fail_sq:
+ bng_re_free_hwq(res, &sq->hwq);
+exit:
+ if (rc)
+ dev_dbg(&res->pdev->dev, "MPC: Stage1 fail exit rc=%d\n", rc);
+ return rc;
+}
+
+static enum ib_qp_state bng_re_cmdq_new_state_to_ib(u8 state)
+{
+ switch (state) {
+ case CMDQ_MODIFY_QP_NEW_STATE_RESET:
+ return IB_QPS_RESET;
+ case CMDQ_MODIFY_QP_NEW_STATE_INIT:
+ return IB_QPS_INIT;
+ case CMDQ_MODIFY_QP_NEW_STATE_RTR:
+ return IB_QPS_RTR;
+ case CMDQ_MODIFY_QP_NEW_STATE_RTS:
+ return IB_QPS_RTS;
+ case CMDQ_MODIFY_QP_NEW_STATE_SQD:
+ return IB_QPS_SQD;
+ case CMDQ_MODIFY_QP_NEW_STATE_SQE:
+ return IB_QPS_SQE;
+ case CMDQ_MODIFY_QP_NEW_STATE_ERR:
+ default:
+ return IB_QPS_ERR;
+ }
+}
+
+int bng_re_query_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr,
+ int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr)
+{
+ struct bng_re_qp *qp = to_bng_re(ib_qp, struct bng_re_qp, ib_qp);
+ struct bng_re_dev *rdev = qp->rdev;
+ struct bng_sp_qp *sp_qp;
+ bool is_alloced = false;
+ int rc = 0;
+
+ if (rdev->bng_res.qp_xids &&
+ (!test_bit(BNG_SP_QP_FLAG_MPC_INITIAL_CREATE_SENT, &qp->sp_qp.flags))) {
+ sp_qp = &qp->sp_qp;
+ } else {
+ sp_qp = kcalloc(1, sizeof(*sp_qp), GFP_KERNEL);
+ if (!sp_qp)
+ return -ENOMEM;
+ is_alloced = true;
+ sp_qp->id = qp->sp_qp.id;
+ sp_qp->ah.host_sgid_index = qp->sp_qp.ah.host_sgid_index;
+
+ rc = bng_sp_query_qp(&rdev->bng_res, sp_qp);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "Query HW QP (0x%x) failed! rc = %d",
+ sp_qp->id, rc);
+ goto free_mem;
+ }
+ }
+ qp_attr->qp_state = bng_re_cmdq_new_state_to_ib(sp_qp->state);
+ qp_attr->cur_qp_state = bng_re_cmdq_new_state_to_ib(sp_qp->cur_qp_state);
+ qp_attr->en_sqd_async_notify = sp_qp->en_sqd_async_notify ? 1 : 0;
+ qp_attr->qp_access_flags = __qp_access_flags_to_ib(0,
+ sp_qp->access);
+ qp_attr->pkey_index = sp_qp->pkey_index;
+ qp_attr->qkey = sp_qp->qkey;
+ qp_attr->ah_attr.type = RDMA_AH_ATTR_TYPE_ROCE;
+ memcpy(qp_attr->ah_attr.grh.dgid.raw, sp_qp->ah.dgid.data,
+ sizeof(sp_qp->ah.dgid.data));
+ qp_attr->ah_attr.grh.flow_label = sp_qp->udp_sport;
+ qp_attr->ah_attr.grh.sgid_index = sp_qp->ah.host_sgid_index;
+ qp_attr->ah_attr.grh.hop_limit = sp_qp->ah.hop_limit;
+ qp_attr->ah_attr.grh.traffic_class = sp_qp->ah.traffic_class;
+ qp_attr->ah_attr.sl = sp_qp->ah.sl;
+ qp_attr->path_mtu = __to_ib_mtu(sp_qp->path_mtu);
+ qp_attr->timeout = sp_qp->timeout;
+ qp_attr->retry_cnt = sp_qp->retry_cnt;
+ qp_attr->rnr_retry = sp_qp->rnr_retry;
+ qp_attr->min_rnr_timer = sp_qp->min_rnr_timer;
+ qp_attr->port_num = __to_ib_port_num(sp_qp->port_id);
+ qp_attr->rq_psn = sp_qp->rq.psn;
+ qp_attr->max_rd_atomic = sp_qp->max_rd_atomic;
+ qp_attr->sq_psn = sp_qp->sq.psn;
+ qp_attr->max_dest_rd_atomic = sp_qp->max_dest_rd_atomic;
+ qp_init_attr->sq_sig_type = sp_qp->sig_type ? IB_SIGNAL_ALL_WR : IB_SIGNAL_REQ_WR;
+ qp_attr->dest_qp_num = sp_qp->dest_qpn;
+
+ qp_attr->cap.max_send_wr = qp->sp_qp.sq.max_wqe;
+ qp_attr->cap.max_send_sge = qp->sp_qp.sq.max_sge;
+ qp_attr->cap.max_recv_wr = qp->sp_qp.rq.max_wqe;
+ qp_attr->cap.max_recv_sge = qp->sp_qp.rq.max_sge;
+ qp_attr->cap.max_inline_data = sp_qp->max_inline_data;
+ qp_init_attr->cap = qp_attr->cap;
+
+free_mem:
+ if (is_alloced)
+ kfree(sp_qp);
+ return rc;
+}
+
+int bng_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr,
+ struct ib_udata *udata)
+{
+ struct bng_re_dev_attr *dev_attr;
+ struct bng_re_qp_resp resp;
+ struct bng_re_dev *rdev;
+ struct bng_re_pd *pd;
+ struct bng_re_qp *qp;
+ struct ib_pd *ib_pd;
+ int rc;
+
+ ib_pd = ib_qp->pd;
+ pd = container_of(ib_pd, struct bng_re_pd, ib_pd);
+ rdev = pd->rdev;
+ dev_attr = rdev->dev_attr;
+ qp = container_of(ib_qp, struct bng_re_qp, ib_qp);
+
+ rc = bng_re_test_qp_limits(rdev, qp_init_attr, dev_attr);
+ if (!rc) {
+ rc = -EINVAL;
+ goto fail;
+ }
+
+ qp->rdev = rdev;
+ rc = bng_re_init_qp_attr(qp, pd, qp_init_attr, udata);
+ if (rc)
+ goto fail;
+
+ rc = bng_re_create_qp_stage1(&rdev->bng_res, qp);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to create HW QP (stage1 / XID prep)");
+ goto free_umem;
+ }
+ if (bng_re_init_fw_state_rtr_enabled(rdev->chip_ctx))
+ qp->sp_qp.rtr_transition_pending = true;
+
+ if (udata) {
+ resp.qpid = qp->sp_qp.id;
+ resp.rsvd = 0;
+ if (udata->outlen < sizeof(resp)) {
+ ibdev_err(&rdev->ibdev,
+ "create_qp: udata outlen %zu < sizeof(bng_re_qp_resp) %zu\n",
+ udata->outlen, sizeof(resp));
+ rc = -EINVAL;
+ goto free_umem;
+ }
+ rc = ib_copy_to_udata(udata, &resp, sizeof(resp));
+ if (rc) {
+ ibdev_err(&rdev->ibdev,
+ "create_qp: ib_copy_to_udata failed rc=%d qpid=%u outlen=%zu\n",
+ rc, resp.qpid, udata->outlen);
+ goto free_umem;
+ }
+ }
+
+ qp->ib_qp.qp_num = qp->sp_qp.id;
+ if (qp_init_attr->qp_type == IB_QPT_GSI)
+ rdev->gsi_ctx.gsi_qp = qp;
+ spin_lock_init(&qp->sq_lock);
+ spin_lock_init(&qp->rq_lock);
+ INIT_LIST_HEAD(&qp->list);
+ mutex_lock(&rdev->qp_lock);
+ list_add_tail(&qp->list, &rdev->qp_list);
+ mutex_unlock(&rdev->qp_lock);
+
+ return 0;
+free_umem:
+ bng_re_free_qp_res(&rdev->bng_res, &qp->sp_qp);
+ ib_umem_release(qp->rumem);
+ ib_umem_release(qp->sumem);
+fail:
+ return rc;
+}
+
+static unsigned long bng_re_lock_cqs(struct bng_re_qp *qp)
+ __acquires(&qp->scq->cq_lock) __acquires(&qp->rcq->cq_lock)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&qp->scq->cq_lock, flags);
+ if (qp->rcq != qp->scq)
+ spin_lock(&qp->rcq->cq_lock);
+ else
+ __acquire(&qp->rcq->cq_lock);
+
+ return flags;
+}
+
+static void bng_re_unlock_cqs(struct bng_re_qp *qp, unsigned long flags)
+ __releases(&qp->scq->cq_lock) __releases(&qp->rcq->cq_lock)
+{
+ if (qp->rcq != qp->scq)
+ spin_unlock(&qp->rcq->cq_lock);
+ else
+ __release(&qp->rcq->cq_lock);
+ spin_unlock_irqrestore(&qp->scq->cq_lock, flags);
+}
+
+static u8 __from_ib_qp_state(enum ib_qp_state state)
+{
+ switch (state) {
+ case IB_QPS_RESET:
+ return CMDQ_MODIFY_QP_NEW_STATE_RESET;
+ case IB_QPS_INIT:
+ return CMDQ_MODIFY_QP_NEW_STATE_INIT;
+ case IB_QPS_RTR:
+ return CMDQ_MODIFY_QP_NEW_STATE_RTR;
+ case IB_QPS_RTS:
+ return CMDQ_MODIFY_QP_NEW_STATE_RTS;
+ case IB_QPS_SQD:
+ return CMDQ_MODIFY_QP_NEW_STATE_SQD;
+ case IB_QPS_SQE:
+ return CMDQ_MODIFY_QP_NEW_STATE_SQE;
+ case IB_QPS_ERR:
+ default:
+ return CMDQ_MODIFY_QP_NEW_STATE_ERR;
+ }
+}
+
+static int bng_re_copy_to_udata(struct bng_re_dev *rdev, void *data,
+ int len, struct ib_udata *udata)
+{
+ int rc;
+
+ rc = ib_copy_to_udata(udata, data, len);
+ if (rc)
+ dev_err(rdev_to_dev(rdev),
+ "ucontext copy failed from %ps rc %d",
+ __builtin_return_address(0), rc);
+
+ return rc;
+}
+
+static u8 __qp_access_flags_from_ib(struct bng_re_chip_ctx *cctx, int iflags)
+{
+ u8 qflags = 0;
+
+ if (iflags & IB_ACCESS_LOCAL_WRITE)
+ qflags |= CMDQ_MODIFY_QP_ACCESS_LOCAL_WRITE;
+ if (iflags & IB_ACCESS_REMOTE_WRITE)
+ qflags |= CMDQ_MODIFY_QP_ACCESS_REMOTE_WRITE;
+ if (iflags & IB_ACCESS_REMOTE_READ)
+ qflags |= CMDQ_MODIFY_QP_ACCESS_REMOTE_READ;
+ if (iflags & IB_ACCESS_REMOTE_ATOMIC)
+ qflags |= CMDQ_MODIFY_QP_ACCESS_REMOTE_ATOMIC;
+
+ return qflags;
+}
+
+static void bng_re_update_qp_addr(struct bng_re_dev *rdev, struct bng_re_qp *qp)
+{
+ /* User-space can extract ip address with sgid_index. */
+ if (ipv6_addr_v4mapped((struct in6_addr *)&qp->sp_qp.ah.dgid)) {
+ qp->qp_info_entry.s_ip.ipv4_addr = ipv4_from_gid(qp->qp_info_entry.sgid.raw);
+ qp->qp_info_entry.d_ip.ipv4_addr = ipv4_from_gid(qp->sp_qp.ah.dgid.data);
+ } else {
+ memcpy(&qp->qp_info_entry.s_ip.ipv6_addr, qp->qp_info_entry.sgid.raw,
+ sizeof(qp->qp_info_entry.s_ip.ipv6_addr));
+ memcpy(&qp->qp_info_entry.d_ip.ipv6_addr, qp->sp_qp.ah.dgid.data,
+ sizeof(qp->qp_info_entry.d_ip.ipv6_addr));
+ }
+}
+
+static u16 get_source_port(struct bng_re_dev *rdev, struct bng_re_qp *qp)
+{
+ u8 ip_off, data[48], smac[ETH_ALEN];
+ u16 crc = 0, buf_len = 0, i;
+ u8 addr_len;
+ u32 qpn;
+
+ if (qp->sp_qp.nw_type == CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV2_IPV6) {
+ addr_len = 16;
+ ip_off = 0;
+ } else {
+ addr_len = 4;
+ ip_off = 12;
+ }
+
+ memcpy(smac, qp->sp_qp.smac, ETH_ALEN);
+
+ memset(data, 0, 48);
+ memcpy(data, qp->sp_qp.ah.dmac, ETH_ALEN);
+ buf_len += ETH_ALEN;
+
+ memcpy(data + buf_len, smac, ETH_ALEN);
+ buf_len += ETH_ALEN;
+
+ memcpy(data + buf_len, qp->sp_qp.ah.dgid.data + ip_off, addr_len);
+ buf_len += addr_len;
+
+ memcpy(data + buf_len, qp->qp_info_entry.sgid.raw + ip_off, addr_len);
+ buf_len += addr_len;
+
+ qpn = htonl(qp->sp_qp.dest_qpn);
+ memcpy(data + buf_len, (u8 *)&qpn + 1, 3);
+ buf_len += 3;
+
+ for (i = 0; i < buf_len; i++)
+ crc = crc16(crc, (data + i), 1);
+
+ crc |= 0xc000;
+
+ return crc;
+}
+
+static void bng_re_update_qp_info(struct bng_re_dev *rdev, struct bng_re_qp *qp)
+{
+ u16 type;
+
+ type = __from_hw_to_ib_qp_type(qp->sp_qp.type);
+ bng_re_update_qp_addr(rdev, qp);
+
+ if ((type == IB_QPT_RC ||
+ (!BNG_RE_UDP_SP_WQE(qp->sp_qp.dev_cap_ext_flags2))) &&
+ (qp->sp_qp.nw_type == CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV2_IPV4 ||
+ qp->sp_qp.nw_type == CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV2_IPV6)) {
+ qp->qp_info_entry.s_port = get_source_port(rdev, qp);
+ }
+ qp->qp_info_entry.d_port = BNG_RE_QP_DEST_PORT;
+}
+
+static int bng_re_update_ah_dscp_sl(struct bng_re_qp *qp, struct ib_qp_attr *qp_attr)
+{
+ struct bng_re_dev *rdev = qp->rdev;
+ bool dscp_valid = false;
+ bool pcp_valid = false;
+ u8 cos, dscp_pri = 0;
+ u8 dscp;
+ u8 i, j;
+ u8 pri;
+
+ /*
+ * The traffic class passed by the applications
+ * contains both dscp and ecn values. DSCP is
+ * upper six bits of the traffic class.
+ */
+
+ dscp = qp_attr->ah_attr.grh.traffic_class >> 2;
+ pri = rdma_ah_get_sl(&qp_attr->ah_attr);
+ qp->sp_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_VLAN_ID;
+
+ if (!dscp && !pri)
+ goto default_queue;
+
+ if (dscp) {
+ for (i = 0; i < rdev->d2p_count; i++) {
+ if (rdev->d2p[i].dscp == dscp) {
+ dscp_pri = rdev->d2p[i].pri;
+ cos = rdev->p2cos[dscp_pri];
+ for (j = 0; j < rdev->lossless_q_count; j++) {
+ if (cos == rdev->lossless_qid[j]) {
+ dscp_valid = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (dscp_valid) {
+ qp->sp_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_TRAFFIC_CLASS;
+ qp->sp_qp.ah.traffic_class = dscp;
+ qp->sp_qp.ah.sl = dscp_pri;
+ return 0;
+ }
+ }
+ if (pri) {
+ cos = rdev->p2cos[pri];
+ for (j = 0; j < rdev->lossless_q_count; j++) {
+ if (cos == rdev->lossless_qid[j]) {
+ pcp_valid = true;
+ break;
+ }
+ }
+ if (pcp_valid) {
+ qp->sp_qp.ah.sl = qp_attr->ah_attr.sl;
+ qp->sp_qp.ah.traffic_class = 0;
+ return 0;
+ }
+ }
+ if (!dscp_valid && !pcp_valid) {
+ dev_warn_ratelimited(rdev_to_dev(qp->rdev),
+ "Given DSCP %d and/or SL %d not mapping to lossless queue",
+ dscp, pri);
+ dev_warn_ratelimited(rdev_to_dev(qp->rdev),
+ "Changing to default roce traffic class DSCP %d and SL %d",
+ rdev->tc_rec[0].roce_dscp, rdev->tc_rec[0].roce_prio);
+ }
+
+default_queue:
+ qp->sp_qp.ah.traffic_class = rdev->tc_rec[0].roce_dscp;
+ qp->sp_qp.ah.sl = rdev->tc_rec[0].roce_prio;
+ return 0;
+}
+
+static void bng_re_handle_cqn(struct bng_sp_cq *cq)
+{
+ struct bng_re_nq *nq;
+
+ if (!(cq && cq->nq))
+ return;
+
+ nq = cq->nq;
+ spin_lock_bh(&cq->compl_lock);
+ if (nq->cqn_handler) {
+ dev_dbg(&nq->res->pdev->dev, "%s:Trigger cq = %p event nq = %p\n",
+ __func__, cq, nq);
+ nq->cqn_handler(nq, cq);
+ }
+ spin_unlock_bh(&cq->compl_lock);
+}
+
+static void bng_re_manage_flush_qp(struct bng_re_qp *qp)
+{
+ struct bng_re_q *rq, *sq;
+ struct bng_re_dev *rdev;
+ unsigned long flags;
+
+ if (qp->sumem)
+ return;
+
+ rdev = qp->rdev;
+
+ if (qp->sp_qp.state == CMDQ_MODIFY_QP_NEW_STATE_ERR) {
+ rq = &qp->sp_qp.rq;
+ sq = &qp->sp_qp.sq;
+
+ dev_dbg(rdev_to_dev(rdev),
+ "Move QP = %p to flush list\n", qp);
+ flags = bng_re_lock_cqs(qp);
+ bng_re_add_flush_qp(&qp->sp_qp);
+ bng_re_unlock_cqs(qp, flags);
+
+ if (sq->hwq.prod != sq->hwq.cons)
+ bng_re_handle_cqn(&qp->scq->sp_cq);
+
+ if (qp->rcq && qp->rcq != qp->scq &&
+ rq->hwq.prod != rq->hwq.cons)
+ bng_re_handle_cqn(&qp->rcq->sp_cq);
+ }
+
+ if (qp->sp_qp.state == CMDQ_MODIFY_QP_NEW_STATE_RESET) {
+ dev_dbg(rdev_to_dev(rdev),
+ "Move QP = %p out of flush list\n", qp);
+ flags = bng_re_lock_cqs(qp);
+ bng_re_clean_qp(&qp->sp_qp);
+ bng_re_unlock_cqs(qp, flags);
+ }
+}
+
+static void bng_post_recv_db(struct bng_sp_qp *qp)
+{
+ struct bng_re_q *rq = &qp->rq;
+
+ if (unlikely(qp->cur_qp_state != CMDQ_MODIFY_QP_NEW_STATE_INIT))
+ bng_ring_prod_db(&rq->dbinfo, DBC_DBC_TYPE_RQ);
+}
+
+static bool bng_re_is_qp_valid(struct bng_re_res *res, struct bng_sp_qp *qp)
+{
+ bool is_valid = false;
+
+ if (qp) {
+ if (!res->is_vf) {
+ /*Chk if the QP ID is valid*/
+ is_valid = bng_re_xm_is_id_allocated(res->qp_xids, qp->id);
+ } else {
+ /*revisit for vf*/
+ is_valid = true;
+ }
+ }
+ return is_valid;
+}
+
+int bng_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr,
+ int qp_attr_mask, struct ib_udata *udata)
+{
+ struct bng_re_qp *qp = container_of(ib_qp, struct bng_re_qp, ib_qp);
+ enum ib_qp_state curr_qp_state, new_qp_state;
+ struct bng_re_modify_qp_ex_resp resp = {};
+ struct bng_re_modify_qp_ex_req ureq = {};
+ struct bng_re_dev *rdev = qp->rdev;
+ struct bng_re_dev_attr *dev_attr;
+ union ib_gid *gid_ptr = NULL;
+ int rc, entries, status;
+ bool update_fw = true;
+ unsigned long flags;
+ u8 nw_type;
+
+ dev_attr = rdev->dev_attr;
+
+ if (qp_attr_mask & ~IB_QP_ATTR_STANDARD_BITS)
+ return -EOPNOTSUPP;
+
+ if (!bng_re_init_fw_state_rtr_enabled(rdev->chip_ctx))
+ qp->sp_qp.modify_flags = 0;
+
+ qp->sp_qp.udcc_exclude = true;
+
+ if (udata && ib_copy_from_udata(&ureq, udata, sizeof(ureq))) {
+ dev_err(rdev_to_dev(rdev), "qp %#x udata copy failed",
+ qp->sp_qp.id);
+ return -EINVAL;
+ }
+
+ if (qp_attr_mask & IB_QP_STATE) {
+ curr_qp_state = bng_re_cmdq_new_state_to_ib(qp->sp_qp.cur_qp_state);
+ new_qp_state = qp_attr->qp_state;
+
+ if (bng_re_init_fw_state_rtr_enabled(rdev->chip_ctx)) {
+ /* don't update fw; as we want to hold
+ * the parameters until we move to rtr.
+ *
+ * Also- only reset the modify flags if we've at least
+ * sent the initial create; as we're accumulating flags until then
+ */
+ if (qp->sp_qp.rtr_transition_pending && new_qp_state != IB_QPS_RTR)
+ update_fw = false;
+ else if (test_bit(BNG_SP_QP_FLAG_MPC_INITIAL_CREATE_SENT, &qp->sp_qp.flags))
+ qp->sp_qp.modify_flags = 0;
+ }
+
+ if (!ib_modify_qp_is_ok(curr_qp_state, new_qp_state,
+ ib_qp->qp_type, qp_attr_mask)) {
+ ibdev_err(&rdev->ibdev,
+ "Invalid attribute mask: %#x specified ",
+ qp_attr_mask);
+ ibdev_err(&rdev->ibdev,
+ "for qpn: %#x type: %#x",
+ ib_qp->qp_num, ib_qp->qp_type);
+ ibdev_err(&rdev->ibdev,
+ "curr_qp_state=0x%x, new_qp_state=0x%x\n",
+ curr_qp_state, new_qp_state);
+ return -EINVAL;
+ }
+
+ if (!bng_re_init_fw_state_rtr_enabled(rdev->chip_ctx)) {
+ if (curr_qp_state == IB_QPS_RESET && new_qp_state == IB_QPS_RESET) {
+ if (!test_bit(BNG_SP_QP_FLAG_MPC_INITIAL_CREATE_SENT,
+ &qp->sp_qp.flags)) {
+ dev_warn(rdev_to_dev(rdev),
+ "%s -%s:%d,%s:0x%x,%s:0x%x,%s:0x%x,%s:0x%x,%s:0x%x\n",
+ "FirstModify Not Sent: NoStateChange",
+ "is_valid:",
+ bng_re_is_qp_valid(&rdev->bng_res, &qp->sp_qp),
+ "attribute mask:",
+ qp_attr_mask,
+ "qpn:",
+ ib_qp->qp_num,
+ "type:",
+ ib_qp->qp_type,
+ "current_qp_st",
+ curr_qp_state,
+ "new_qp_st",
+ new_qp_state);
+ return 0;
+ }
+ }
+ }
+
+ qp->sp_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_STATE;
+ qp->sp_qp.state = __from_ib_qp_state(qp_attr->qp_state);
+
+ /* MTU settings allowed only during INIT -> RTR */
+ if (qp_attr->qp_state == IB_QPS_RTR &&
+ qp->sp_qp.type != CMDQ_CREATE_QP_TYPE_GSI &&
+ qp->sp_qp.type != CMDQ_CREATE_QP_TYPE_UD) {
+ rc = bng_re_init_qpmtu(qp, rdev->netdev->mtu, qp_attr_mask, qp_attr);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "qp %#x invalid mtu %d\n",
+ qp->sp_qp.id, ib_mtu_enum_to_int(qp_attr->path_mtu));
+ return rc;
+ }
+ }
+ /*
+ * if we're moving from init => rtr, then for non user and with posted
+ * rx buffers and not srq, set eq buffer coount
+ */
+ if (bng_re_init_fw_state_rtr_enabled(rdev->chip_ctx)) {
+ u32 indx;
+ struct bng_re_db_info *info;
+
+ if (!qp->sp_qp.is_user && !qp->sp_qp.srq &&
+ new_qp_state == IB_QPS_RTR && qp->sp_qp.rq.hwq.prod) {
+ spin_lock_irqsave(&qp->rq_lock, flags);
+
+ info = &qp->sp_qp.rq.dbinfo;
+ indx = (((info->hwq->prod / info->max_slot) & DBC_DBC_INDEX_MASK) |
+ ((info->flags & BNG_RE_FLAG_EPOCH_PROD_MASK) <<
+ BNG_RE_DB_EPOCH_PROD_SHIFT));
+
+ qp->sp_qp.req_buffer_count = indx;
+ spin_unlock_irqrestore(&qp->rq_lock, flags);
+ }
+ }
+ } else {
+ curr_qp_state = __to_ib_qp_state(qp->sp_qp.cur_qp_state);
+ new_qp_state = qp_attr->qp_state;
+
+ if (bng_re_init_fw_state_rtr_enabled(rdev->chip_ctx)) {
+ /* don't update fw; as we want to hold
+ * the parameters until we move to rtr.
+ * Also- only reset the modify flags if we've at least
+ * sent the initial create; as we're accumulating flags until then
+ */
+ if (qp->sp_qp.rtr_transition_pending && new_qp_state != IB_QPS_RTR)
+ update_fw = false;
+ else if (test_bit(BNG_SP_QP_FLAG_MPC_INITIAL_CREATE_SENT, &qp->sp_qp.flags))
+ qp->sp_qp.modify_flags = 0;
+ } else {
+ /* Fix me: we can't check new qp state if the
+ * state modify bit is not set.
+ */
+ /* Check First modify is sent */
+ if ((!test_bit(BNG_SP_QP_FLAG_MPC_INITIAL_CREATE_SENT, &qp->sp_qp.flags))) {
+ dev_warn(rdev_to_dev(rdev),
+ "%s -%s:%d,%s:0x%x,%s:0x%x,%s:0x%x,%s:0x%x,%s:0x%x\n",
+ "FirstModify Not Sent: NoStateChange",
+ "is_valid:",
+ bng_re_is_qp_valid(&rdev->bng_res, &qp->sp_qp),
+ "attribute mask:",
+ qp_attr_mask,
+ "qpn:",
+ ib_qp->qp_num,
+ "type:",
+ ib_qp->qp_type,
+ "current_qp_st",
+ curr_qp_state,
+ "new_qp_st",
+ new_qp_state);
+ return 0;
+ }
+ }
+ }
+
+ if (qp_attr_mask & IB_QP_EN_SQD_ASYNC_NOTIFY) {
+ qp->sp_qp.modify_flags |=
+ CMDQ_MODIFY_QP_MODIFY_MASK_EN_SQD_ASYNC_NOTIFY;
+ qp->sp_qp.en_sqd_async_notify = true;
+ }
+ if (qp_attr_mask & IB_QP_ACCESS_FLAGS) {
+ qp->sp_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_ACCESS;
+ qp->sp_qp.access =
+ __qp_access_flags_from_ib(qp->sp_qp.cctx,
+ qp_attr->qp_access_flags);
+ /* LOCAL_WRITE access must be set to allow RC receive */
+ qp->sp_qp.access |= CMDQ_MODIFY_QP_ACCESS_LOCAL_WRITE;
+ }
+ if (qp_attr_mask & IB_QP_PKEY_INDEX) {
+ qp->sp_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_PKEY;
+ qp->sp_qp.pkey_index = qp_attr->pkey_index;
+ }
+ if (qp_attr_mask & IB_QP_QKEY) {
+ qp->sp_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_QKEY;
+ qp->sp_qp.qkey = qp_attr->qkey;
+ }
+ if (qp_attr_mask & IB_QP_AV) {
+ const struct ib_global_route *grh =
+ rdma_ah_read_grh(&qp_attr->ah_attr);
+ const struct ib_gid_attr *sgid_attr;
+ struct bng_re_gid_ctx *ctx;
+
+ qp->sp_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_DGID |
+ CMDQ_MODIFY_QP_MODIFY_MASK_FLOW_LABEL |
+ CMDQ_MODIFY_QP_MODIFY_MASK_SGID_INDEX |
+ CMDQ_MODIFY_QP_MODIFY_MASK_HOP_LIMIT |
+ CMDQ_MODIFY_QP_MODIFY_MASK_TRAFFIC_CLASS |
+ CMDQ_MODIFY_QP_MODIFY_MASK_DEST_MAC |
+ CMDQ_MODIFY_QP_MODIFY_MASK_VLAN_ID;
+ memcpy(qp->sp_qp.ah.dgid.data, qp_attr->ah_attr.grh.dgid.raw,
+ sizeof(qp->sp_qp.ah.dgid.data));
+
+ qp->sp_qp.ah.flow_label = grh->flow_label;
+
+ sgid_attr = qp_attr->ah_attr.grh.sgid_attr;
+
+ /* Get the HW context of the GID. The reference
+ * of GID table entry is already taken by the caller.
+ */
+ ctx = rdma_read_gid_hw_context(sgid_attr);
+ qp->sp_qp.ah.sgid_index = ctx->idx;
+ qp->sp_qp.ah.host_sgid_index = grh->sgid_index;
+ qp->sp_qp.ah.hop_limit = grh->hop_limit;
+ qp->sp_qp.ah.traffic_class = grh->traffic_class >> 2;
+
+ status = bng_re_update_ah_dscp_sl(qp, qp_attr);
+ if (status) {
+ dev_err(rdev_to_dev(rdev), "%s: qp %d: error updating ah_dscp",
+ __func__, qp->sp_qp.id);
+ return status;
+ }
+
+ ether_addr_copy(qp->sp_qp.ah.dmac,
+ qp_attr->ah_attr.roce.dmac);
+
+ gid_ptr = (union ib_gid *)&sgid_attr->gid;
+
+ if (!sgid_attr->ndev) {
+ dev_err(rdev_to_dev(rdev), "%s: qp %d: sgid_attr has no netdev",
+ __func__, qp->sp_qp.id);
+ return -ENODEV;
+ }
+
+ memcpy(qp->sp_qp.smac, sgid_attr->ndev->dev_addr, ETH_ALEN);
+
+ nw_type = rdma_gid_attr_network_type(sgid_attr);
+ switch (nw_type) {
+ case RDMA_NETWORK_IPV4:
+ qp->sp_qp.nw_type =
+ CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV2_IPV4;
+ break;
+ case RDMA_NETWORK_IPV6:
+ qp->sp_qp.nw_type =
+ CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV2_IPV6;
+ break;
+ default:
+ qp->sp_qp.nw_type =
+ CMDQ_MODIFY_QP_NETWORK_TYPE_ROCEV1;
+ break;
+ }
+ memcpy(&qp->qp_info_entry.sgid, gid_ptr, sizeof(qp->qp_info_entry.sgid));
+ qp->sp_qp.udcc_exclude = true;
+ }
+
+ if (qp_attr_mask & IB_QP_TIMEOUT) {
+ qp->sp_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_TIMEOUT;
+ qp->sp_qp.timeout = qp_attr->timeout;
+ }
+ if (qp_attr_mask & IB_QP_RETRY_CNT) {
+ qp->sp_qp.modify_flags |=
+ CMDQ_MODIFY_QP_MODIFY_MASK_RETRY_CNT;
+ qp->sp_qp.retry_cnt = qp_attr->retry_cnt;
+ }
+ if (qp_attr_mask & IB_QP_RNR_RETRY) {
+ qp->sp_qp.modify_flags |=
+ CMDQ_MODIFY_QP_MODIFY_MASK_RNR_RETRY;
+ qp->sp_qp.rnr_retry = qp_attr->rnr_retry;
+ }
+ if (qp_attr_mask & IB_QP_MIN_RNR_TIMER) {
+ qp->sp_qp.modify_flags |=
+ CMDQ_MODIFY_QP_MODIFY_MASK_MIN_RNR_TIMER;
+ qp->sp_qp.min_rnr_timer = qp_attr->min_rnr_timer;
+ }
+ if (qp_attr_mask & IB_QP_RQ_PSN) {
+ qp->sp_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_RQ_PSN;
+ qp->sp_qp.rq.psn = qp_attr->rq_psn;
+ }
+ if (qp_attr_mask & IB_QP_MAX_QP_RD_ATOMIC) {
+ qp->sp_qp.modify_flags |=
+ CMDQ_MODIFY_QP_MODIFY_MASK_MAX_RD_ATOMIC;
+ /* Cap the max_rd_atomic to device max */
+ qp->sp_qp.max_rd_atomic = min_t(u32, qp_attr->max_rd_atomic,
+ dev_attr->max_qp_rd_atom);
+ }
+ if (qp_attr_mask & IB_QP_SQ_PSN) {
+ qp->sp_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_SQ_PSN;
+ qp->sp_qp.sq.psn = qp_attr->sq_psn;
+ }
+ if (qp_attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) {
+ if (qp_attr->max_dest_rd_atomic >
+ dev_attr->max_qp_init_rd_atom) {
+ ibdev_err(&rdev->ibdev,
+ "max_dest_rd_atomic requested%d is > dev_max%d",
+ qp_attr->max_dest_rd_atomic,
+ dev_attr->max_qp_init_rd_atom);
+ return -EINVAL;
+ }
+
+ qp->sp_qp.modify_flags |=
+ CMDQ_MODIFY_QP_MODIFY_MASK_MAX_DEST_RD_ATOMIC;
+ qp->sp_qp.max_dest_rd_atomic = qp_attr->max_dest_rd_atomic;
+ }
+ if (qp_attr_mask & IB_QP_CAP) {
+ struct bng_re_ucontext *uctx =
+ rdma_udata_to_drv_context(udata, struct bng_re_ucontext, ib_uctx);
+
+ qp->sp_qp.modify_flags |=
+ CMDQ_MODIFY_QP_MODIFY_MASK_SQ_SIZE |
+ CMDQ_MODIFY_QP_MODIFY_MASK_RQ_SIZE |
+ CMDQ_MODIFY_QP_MODIFY_MASK_SQ_SGE |
+ CMDQ_MODIFY_QP_MODIFY_MASK_RQ_SGE |
+ CMDQ_MODIFY_QP_MODIFY_MASK_MAX_INLINE_DATA;
+ if (qp_attr->cap.max_send_wr >= dev_attr->max_sq_wqes ||
+ qp_attr->cap.max_recv_wr >= dev_attr->max_rq_wqes ||
+ qp_attr->cap.max_send_sge >= dev_attr->max_qp_sges ||
+ qp_attr->cap.max_recv_sge >= dev_attr->max_qp_sges ||
+ qp_attr->cap.max_inline_data >=
+ dev_attr->max_inline_data) {
+ ibdev_err(&rdev->ibdev,
+ "Create QP failed - max exceeded");
+ return -EINVAL;
+ }
+ entries = bng_re_init_depth(qp_attr->cap.max_send_wr, uctx);
+
+ if (entries > dev_attr->max_sq_wqes)
+ entries = dev_attr->max_sq_wqes;
+ entries = min_t(u32, entries, dev_attr->max_sq_wqes);
+ qp->sp_qp.sq.max_wqe = entries;
+ qp->sp_qp.sq.q_full_delta = qp->sp_qp.sq.max_wqe - qp_attr->cap.max_send_wr;
+
+ /*
+ * Reserving one slot for Phantom WQE. Some application can
+ * post one extra entry in this case. Allowing this to avoid
+ * unexpected Queue full condition
+ */
+ qp->sp_qp.sq.q_full_delta -= 1;
+ qp->sp_qp.sq.max_sge = qp_attr->cap.max_send_sge;
+ if (qp->sp_qp.rq.max_wqe) {
+ entries = bng_re_init_depth(qp_attr->cap.max_recv_wr, uctx);
+ if (entries > dev_attr->max_rq_wqes)
+ entries = dev_attr->max_rq_wqes;
+ qp->sp_qp.rq.max_wqe = entries;
+ qp->sp_qp.rq.q_full_delta = qp->sp_qp.rq.max_wqe - qp_attr->cap.max_recv_wr;
+ qp->sp_qp.rq.max_sge = qp_attr->cap.max_recv_sge;
+ } else {
+ /* SRQ was used prior, just ignore the RQ caps */
+ }
+ }
+
+ if (qp_attr_mask & IB_QP_DEST_QPN) {
+ qp->sp_qp.modify_flags |=
+ CMDQ_MODIFY_QP_MODIFY_MASK_DEST_QP_ID;
+ qp->sp_qp.dest_qpn = qp_attr->dest_qp_num;
+ }
+
+ if (udata && (ureq.comp_mask & BNG_RE_COMP_MASK_REQ_BUFF_CNT)) {
+ qp->sp_qp.req_buffer_count = ureq.req_buffer_count;
+ } else {
+ dev_warn(rdev_to_dev(rdev),
+ "QP %d : no req_buffer_count set in user data",
+ qp->sp_qp.id);
+ }
+
+ if (update_fw) {
+ rc = bng_sp_mpc_modify_qp(&rdev->bng_res, &qp->sp_qp);
+ } else {
+ /* Transition the state ourselves if we're skipping the hw update */
+ if (qp_attr_mask & IB_QP_STATE)
+ qp->sp_qp.cur_qp_state = __from_ib_qp_state(new_qp_state);
+ }
+
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to modify HW QP");
+ return rc;
+ }
+
+ if (qp_attr_mask & IB_QP_STATE) {
+ /*
+ * When the QP moves to INIT to RTR in the modify_qp
+ * call, firmware has a workaround to update the context
+ * field with CDUDMA read/write. During the QP INIT
+ * state in the post_receive driver needs to ensure
+ * no doorbell is rung to work this properly. And once
+ * QP moves to RTR ring the doorbell if the producer
+ * index is present.
+ */
+ if (qp_attr->qp_state == IB_QPS_RTR && !qp->sp_qp.srq) {
+ if (qp->sp_qp.rq.hwq.prod) {
+ spin_lock_irqsave(&qp->rq_lock, flags);
+ bng_post_recv_db(&qp->sp_qp);
+ spin_unlock_irqrestore(&qp->rq_lock, flags);
+ }
+ }
+
+ if (bng_re_init_fw_state_rtr_enabled(rdev->chip_ctx)) {
+ /* Turn off rtr pending after we've transitioned to rtr; or,
+ * ; when back to reset.
+ */
+ if (qp_attr->qp_state == IB_QPS_RTR)
+ qp->sp_qp.rtr_transition_pending = false;
+ else if (qp_attr->qp_state == IB_QPS_RESET)
+ qp->sp_qp.rtr_transition_pending = true;
+ }
+
+ bng_re_manage_flush_qp(qp);
+ }
+
+ /*
+ * Update info when qp_info_info
+ */
+ bng_re_update_qp_info(rdev, qp);
+
+ if (udata) {
+ rc = bng_re_copy_to_udata(rdev, &resp,
+ min(udata->outlen, sizeof(resp)),
+ udata);
+ if (rc) {
+ dev_err(rdev_to_dev(rdev), "%s: qp:%d error copying user data:%d\n",
+ __func__, qp->sp_qp.id, rc);
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * qp_destroy_qp_mpc - destroy a QP w/mpc / rca
+ *
+ * @res: resource info
+ * @qp: qp to destroy
+ *
+ * Return: 0 for success, anything else; error
+ */
+static int qp_destroy_qp_mpc(struct bng_re_res *res, struct bng_re_qp *qp)
+{
+ int rc = 0;
+ bool xid_cleanup_handled = false;
+
+ /* Reset modify flags during QP destroy and set only the required mask */
+ qp->sp_qp.modify_flags = 0;
+ qp->sp_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_STATE;
+
+ /* Set state to RESET to take QP back to first state */
+ qp->sp_qp.state = CMDQ_MODIFY_QP_NEW_STATE_RESET;
+
+ if (test_bit(BNG_SP_QP_FLAG_MPC_ALLOW_QP_DESTROY, &qp->sp_qp.flags)) {
+ xid_cleanup_handled = false;
+ /* Set the QP Free flag for actual QP destroy */
+ set_bit(BNG_SP_QP_FLAG_MPC_QP_FREE, &qp->sp_qp.flags);
+ rc = bng_sp_fill_and_send_mpc_qp_modify(res, &qp, 1, &xid_cleanup_handled);
+ } else {
+ dev_warn(&res->pdev->dev,
+ "MPC-skip destroy qp- not yet created to FW. id:%d.State:%d,%d\n",
+ qp->sp_qp.id,
+ qp->sp_qp.cur_qp_state,
+ qp->sp_qp.state);
+ }
+
+ if (!xid_cleanup_handled && qp->sp_qp.id != 1 && !res->is_vf &&
+ res->qp_xids) {
+ dev_warn(&res->pdev->dev,
+ "MPC- pathological xid cleanup. id:%d.State:%d,%d, destroy_flag set:%d\n",
+ qp->sp_qp.id,
+ qp->sp_qp.cur_qp_state,
+ qp->sp_qp.state,
+ test_bit(BNG_SP_QP_FLAG_MPC_ALLOW_QP_DESTROY, &qp->sp_qp.flags));
+
+ bng_re_qp_free_xid_and_iqm(res, qp->sp_qp.type, (int)qp->sp_qp.id,
+ &qp->iqm_res, true);
+ }
+
+ return rc;
+}
+
+int bng_re_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata)
+{
+ struct bng_re_qp *qp = container_of(ib_qp, struct bng_re_qp, ib_qp);
+ struct bng_sp_qp *sp_qp = &qp->sp_qp;
+ struct bng_re_dev *rdev = qp->rdev;
+ unsigned int flags;
+ int rc;
+
+ bng_re_flush_cqn_wq(&qp->sp_qp);
+
+ rc = qp_destroy_qp_mpc(&rdev->bng_res, qp);
+ if (rc)
+ ibdev_err(&rdev->ibdev, "Failed to destroy HW QP");
+
+ if (rdma_is_kernel_res(&qp->ib_qp.res)) {
+ flags = bng_re_lock_cqs(qp);
+ bng_re_clean_qp(&qp->sp_qp);
+ bng_re_unlock_cqs(qp, flags);
+ }
+
+ bng_re_free_qp_res(&rdev->bng_res, &qp->sp_qp);
+
+ mutex_lock(&rdev->qp_lock);
+ list_del(&qp->list);
+ mutex_unlock(&rdev->qp_lock);
+
+ ib_umem_release(qp->rumem);
+ ib_umem_release(qp->sumem);
+
+ /* Same as bng_re_flush_cqn_wq: tolerate partial QP / missing CQ-NQ links. */
+ if (sp_qp->scq && sp_qp->scq->nq && sp_qp->scq->nq->cqn_wq)
+ flush_workqueue(sp_qp->scq->nq->cqn_wq);
+ if (sp_qp->rcq && sp_qp->scq != sp_qp->rcq &&
+ sp_qp->rcq->nq && sp_qp->rcq->nq->cqn_wq)
+ flush_workqueue(sp_qp->rcq->nq->cqn_wq);
+
+ return 0;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_verbs.h b/drivers/infiniband/hw/bng_re/bng_verbs.h
index c5643ee70885..f89db8d33ded 100644
--- a/drivers/infiniband/hw/bng_re/bng_verbs.h
+++ b/drivers/infiniband/hw/bng_re/bng_verbs.h
@@ -6,10 +6,26 @@
#include <linux/refcount.h>
#include <rdma/ib_verbs.h>
+#include <rdma/ib_addr.h>
+#include <rdma/bng_re-abi.h>
#include "bng_sp.h"
#include "bng_re.h"
+#define BNG_RE_RESERVED_QP_WRS 128
+#define BNG_RE_XID_AVOID_REUSE false
+#define BNG_RE_MIN_KERNEL_QP_TX_DEPTH 4096
+
+/* GSI QP mode enum */
+enum bng_re_gsi_mode {
+ BNG_RE_GSI_MODE_INVALID = 0,
+ BNG_RE_GSI_MODE_ALL = 1,
+ BNG_RE_GSI_MODE_ROCE_V1,
+ BNG_RE_GSI_MODE_ROCE_V2_IPV4,
+ BNG_RE_GSI_MODE_ROCE_V2_IPV6,
+ BNG_RE_GSI_MODE_UD
+};
+
struct bng_re_ucontext {
struct ib_ucontext ib_uctx;
struct bng_re_dev *rdev;
@@ -94,6 +110,48 @@ struct bng_re_ah {
refcount_t *ref_cnt;
};
+union ip_addr {
+ u32 ipv4_addr;
+ u8 ipv6_addr[16];
+};
+
+struct bng_re_iqm_res {
+ u32 irrq_addr;
+ u32 irrq_size;
+ u32 orrq_addr;
+ u32 orrq_size;
+ u32 msn_addr;
+ u32 msn_size;
+};
+
+struct bng_re_qp_info_entry {
+ union ib_gid sgid;
+ union ib_gid dgid;
+ union ip_addr s_ip;
+ union ip_addr d_ip;
+ u16 s_port;
+#define BNG_RE_QP_DEST_PORT 4791
+ u16 d_port;
+ u32 rate_limit;
+};
+
+struct bng_re_qp {
+ struct ib_qp ib_qp;
+ struct list_head list;
+ struct bng_re_dev *rdev;
+ spinlock_t sq_lock;
+ spinlock_t rq_lock;
+ struct bng_sp_qp sp_qp;
+ struct bng_re_iqm_res iqm_res;
+ struct ib_umem *sumem;
+ struct ib_umem *rumem;
+ u32 send_psn;
+ struct bng_re_cq *scq;
+ struct bng_re_cq *rcq;
+ struct dentry *dentry;
+ struct bng_re_qp_info_entry qp_info_entry;
+};
+
static inline struct bng_re_ah *to_bng_re_ah(struct ib_ah *ibah)
{
return container_of(ibah, struct bng_re_ah, ib_ah);
@@ -112,6 +170,139 @@ static inline u16 bng_re_get_rwqe_size(int nsge)
return 16 + (nsge * 16); /* header=16, each sge=16 bytes */
}
+static inline u16 __get_swqe_size(int nsge)
+{
+ u16 wqe_size;
+
+ wqe_size = sizeof(struct sq_atomic_hdr) + nsge * sizeof(struct sq_sge);
+ return wqe_size;
+}
+
+static inline u32 bng_re_get_diff(struct bng_re_ucontext *uctx,
+ struct bng_re_chip_ctx *cctx)
+{
+ if (!uctx)
+ return 0;
+ else if (uctx->cmask & BNG_RE_UCNTX_CMASK_RSVD_WQE_DISABLED)
+ return 0;
+
+ /* old lib */
+ return BNG_RE_RESERVED_QP_WRS;
+}
+
+static inline enum ib_qp_state __to_ib_qp_state(u8 state)
+{
+ switch (state) {
+ case CMDQ_MODIFY_QP_NEW_STATE_RESET:
+ return IB_QPS_RESET;
+ case CMDQ_MODIFY_QP_NEW_STATE_INIT:
+ return IB_QPS_INIT;
+ case CMDQ_MODIFY_QP_NEW_STATE_RTR:
+ return IB_QPS_RTR;
+ case CMDQ_MODIFY_QP_NEW_STATE_RTS:
+ return IB_QPS_RTS;
+ case CMDQ_MODIFY_QP_NEW_STATE_SQD:
+ return IB_QPS_SQD;
+ case CMDQ_MODIFY_QP_NEW_STATE_SQE:
+ return IB_QPS_SQE;
+ case CMDQ_MODIFY_QP_NEW_STATE_ERR:
+ default:
+ return IB_QPS_ERR;
+ }
+}
+
+static inline enum ib_mtu __to_ib_mtu(u32 mtu)
+{
+ switch (mtu) {
+ case CMDQ_MODIFY_QP_PATH_MTU_MTU_256:
+ return IB_MTU_256;
+ case CMDQ_MODIFY_QP_PATH_MTU_MTU_512:
+ return IB_MTU_512;
+ case CMDQ_MODIFY_QP_PATH_MTU_MTU_1024:
+ return IB_MTU_1024;
+ case CMDQ_MODIFY_QP_PATH_MTU_MTU_2048:
+ return IB_MTU_2048;
+ case CMDQ_MODIFY_QP_PATH_MTU_MTU_4096:
+ return IB_MTU_4096;
+ default:
+ return IB_MTU_1024;
+ }
+}
+
+static inline u8 __to_ib_port_num(u16 port_id)
+{
+ /* Typically Broadcom driver has a 1-based port number mapped to port_id 0 */
+ return (u8)(port_id + 1);
+}
+
+static u32 __from_ib_mtu(enum ib_mtu mtu)
+{
+ switch (mtu) {
+ case IB_MTU_256:
+ return CMDQ_MODIFY_QP_PATH_MTU_MTU_256;
+ case IB_MTU_512:
+ return CMDQ_MODIFY_QP_PATH_MTU_MTU_512;
+ case IB_MTU_1024:
+ return CMDQ_MODIFY_QP_PATH_MTU_MTU_1024;
+ case IB_MTU_2048:
+ return CMDQ_MODIFY_QP_PATH_MTU_MTU_2048;
+ case IB_MTU_4096:
+ return CMDQ_MODIFY_QP_PATH_MTU_MTU_4096;
+ default:
+ return CMDQ_MODIFY_QP_PATH_MTU_MTU_2048;
+ }
+}
+
+static inline enum ib_qp_type __from_hw_to_ib_qp_type(u8 type)
+{
+ switch (type) {
+ case CMDQ_CREATE_QP1_TYPE_GSI:
+ case CMDQ_CREATE_QP_TYPE_GSI:
+ return IB_QPT_GSI;
+ case CMDQ_CREATE_QP_TYPE_RC:
+ return IB_QPT_RC;
+ case CMDQ_CREATE_QP_TYPE_UD:
+ return IB_QPT_UD;
+ case CMDQ_CREATE_QP_TYPE_RAW_ETHERTYPE:
+ return IB_QPT_RAW_ETHERTYPE;
+ default:
+ return IB_QPT_MAX;
+ }
+}
+
+/* Extract IPv4 from last 4 bytes of IPv4-mapped GID (bytes 12-15, host order). */
+static inline u32 ipv4_from_gid(u8 *gid)
+{
+ return (u32)(gid[15] << 24 | gid[14] << 16 | gid[13] << 8 | gid[12]);
+}
+
+static inline int bng_re_init_qpmtu(struct bng_re_qp *qp, int mtu,
+ int mask, struct ib_qp_attr *qp_attr)
+{
+ int qpmtu, qpmtu_int;
+ int ifmtu, ifmtu_int;
+
+ ifmtu = iboe_get_mtu(mtu);
+ ifmtu_int = ib_mtu_enum_to_int(ifmtu);
+ qpmtu = ifmtu;
+ qpmtu_int = ifmtu_int;
+ if (mask & IB_QP_PATH_MTU) {
+ qpmtu = qp_attr->path_mtu;
+ qpmtu_int = ib_mtu_enum_to_int(qpmtu);
+ if (qpmtu_int > ifmtu_int)
+ return -EINVAL;
+ }
+ qp->sp_qp.path_mtu = __from_ib_mtu(qpmtu);
+ qp->sp_qp.mtu = qpmtu_int;
+ qp->sp_qp.modify_flags |=
+ CMDQ_MODIFY_QP_MODIFY_MASK_PATH_MTU;
+
+ return 0;
+}
+
+#define to_bng_re(ptr, type, member) \
+ container_of(ptr, type, member)
+
int bng_re_query_device(struct ib_device *ibdev, struct ib_device_attr *ib_attr,
struct ib_udata *udata);
int bng_re_modify_device(struct ib_device *ibdev, int device_modify_mask,
@@ -176,6 +367,13 @@ int bng_re_create_ah(struct ib_ah *ib_ah, struct rdma_ah_init_attr *init_attr,
int bng_re_destroy_ah(struct ib_ah *ib_ah, u32 flags);
int bng_re_query_ah(struct ib_ah *ib_ah, struct rdma_ah_attr *ah_attr);
void bng_re_posted_destroy_ah(struct work_struct *work);
+int bng_re_query_qp(struct ib_qp *qp, struct ib_qp_attr *qp_attr,
+ int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr);
+int bng_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr,
+ struct ib_udata *udata);
+int bng_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr,
+ int qp_attr_mask, struct ib_udata *udata);
+int bng_re_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata);
#endif /* __BNG_RE_VERBS_H__ */
diff --git a/include/uapi/rdma/bng_re-abi.h b/include/uapi/rdma/bng_re-abi.h
index 36bc902600be..7cee818f06d1 100644
--- a/include/uapi/rdma/bng_re-abi.h
+++ b/include/uapi/rdma/bng_re-abi.h
@@ -25,6 +25,14 @@ enum {
BNG_RE_COMP_MASK_REQ_UCNTX_RSVD_WQE = 0x02,
};
+enum {
+ BNG_RE_COMP_MASK_MQP_EX_PPP_REQ_EN_MASK = 0x1,
+ BNG_RE_COMP_MASK_MQP_EX_PPP_REQ_EN = 0x1,
+ BNG_RE_COMP_MASK_MQP_EX_PATH_MTU_MASK = 0x2,
+ BNG_RE_COMP_MASK_MQP_EX_OOO_DP_EN_MASK = 0x20,
+ BNG_RE_COMP_MASK_REQ_BUFF_CNT = 0x400UL,
+};
+
struct bng_re_uctx_req {
__aligned_u64 comp_mask;
};
@@ -80,4 +88,31 @@ struct bng_re_ah_resp {
__u64 comp_mask;
} __attribute__((packed));
+struct bng_re_qp_req {
+ __aligned_u64 qpsva;
+ __aligned_u64 qprva;
+ __aligned_u64 qp_handle;
+ __aligned_u64 comp_mask;
+};
+
+struct bng_re_qp_resp {
+ __u32 qpid;
+ __u32 rsvd;
+};
+
+struct bng_re_modify_qp_ex_req {
+ __aligned_u64 comp_mask;
+ __u64 path_ctx_id;
+ __u32 dpi;
+ __u8 no_grp;
+ __u8 rsvd[3];
+ __u32 req_buffer_count;
+} __packed;
+
+struct bng_re_modify_qp_ex_resp {
+ __aligned_u64 comp_mask;
+ __u32 ppp_st_idx;
+ __u32 path_mtu;
+} __packed;
+
#endif /* __BNG_RE_UVERBS_ABI_H__*/
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH 15/15] RDMA/bng_re: Register with ib-core
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
` (13 preceding siblings ...)
2026-09-04 10:43 ` [PATCH 14/15] RDMA/bng_re: Add QP verbs Siva Reddy Kallam
@ 2026-09-04 10:43 ` Siva Reddy Kallam
14 siblings, 0 replies; 17+ messages in thread
From: Siva Reddy Kallam @ 2026-09-04 10:43 UTC (permalink / raw)
To: leonro, jgg, davem, edumazet, kuba, pabeni, andrew+netdev, horms
Cc: netdev, linux-kernel, linux-rdma, Siva Reddy Kallam
This patch registers with ib-core
Signed-off-by: Siva Reddy Kallam <siva.kallam@broadcom.com>
---
drivers/infiniband/hw/bng_re/bng_dev.c | 79 ++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
index 6e9df737c751..2633bb289f09 100644
--- a/drivers/infiniband/hw/bng_re/bng_dev.c
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -5,6 +5,8 @@
#include <linux/pci.h>
#include <linux/auxiliary_bus.h>
+#include <net/addrconf.h>
+
#include <rdma/ib_verbs.h>
#include <rdma/bng_re-abi.h>
@@ -553,6 +555,73 @@ static int bng_re_dev_init(struct bng_re_dev *rdev)
return rc;
}
+static void bng_re_dispatch_event(struct ib_device *ibdev, struct ib_qp *qp,
+ u8 port_num, enum ib_event_type event)
+{
+ struct ib_event ib_event;
+
+ ib_event.device = ibdev;
+ if (qp) {
+ ib_event.element.qp = qp;
+ ib_event.event = event;
+ if (qp->event_handler)
+ qp->event_handler(&ib_event, qp->qp_context);
+
+ } else {
+ ib_event.element.port_num = port_num;
+ ib_event.event = event;
+ ib_dispatch_event(&ib_event);
+ }
+}
+
+static int bng_re_register_ib(struct bng_re_dev *rdev)
+{
+ struct ib_device *ibdev = &rdev->ibdev;
+ int ret;
+
+ /* ib device init */
+ ibdev->node_type = RDMA_NODE_IB_CA;
+ strscpy(ibdev->node_desc, BNG_RE_DESC " HCA");
+ ibdev->phys_port_cnt = 1;
+
+ addrconf_addr_eui48((u8 *)&ibdev->node_guid, rdev->netdev->dev_addr);
+
+ ibdev->num_comp_vectors = rdev->nqr->num_msix - BNG_RE_MAX_RSVD_IRQ;
+ ibdev->dev.parent = &rdev->aux_dev->pdev->dev;
+ ibdev->local_dma_lkey = BNG_RE_RSVD_LKEY;
+
+ ib_set_device_ops(ibdev, &bng_re_dev_ops);
+ ret = ib_device_set_netdev(&rdev->ibdev, rdev->netdev, 1);
+ if (ret)
+ return ret;
+
+ dma_set_max_seg_size(&rdev->aux_dev->pdev->dev, UINT_MAX);
+ ibdev->uverbs_cmd_mask |= BIT_ULL(IB_USER_VERBS_CMD_POLL_CQ);
+ return ib_register_device(ibdev, "bng_re%d", &rdev->aux_dev->pdev->dev);
+}
+
+static int bng_re_ib_init(struct bng_re_dev *rdev)
+{
+ int rc;
+ u32 event;
+
+ /* Register ib dev */
+ rc = bng_re_register_ib(rdev);
+ if (rc) {
+ pr_err("Failed to register with IB: %#x\n", rc);
+ return rc;
+ }
+ dev_info(rdev_to_dev(rdev), "Device registered with IB successfully");
+ set_bit(BNG_RE_FLAG_ISSUE_ROCE_STATS, &rdev->flags);
+
+ event = netif_running(rdev->netdev) && netif_carrier_ok(rdev->netdev) ?
+ IB_EVENT_PORT_ACTIVE : IB_EVENT_PORT_ERR;
+
+ bng_re_dispatch_event(&rdev->ibdev, NULL, 1, event);
+
+ return rc;
+}
+
static int bng_re_add_device(struct auxiliary_device *adev)
{
struct bnge_auxr_priv *auxr_priv =
@@ -575,8 +644,17 @@ static int bng_re_add_device(struct auxiliary_device *adev)
if (rc)
goto re_dev_dealloc;
+ rc = bng_re_ib_init(rdev);
+ if (rc) {
+ pr_err("Failed to register with IB: %s",
+ auxr_priv->aux_dev.name);
+ goto re_dev_uninit;
+ }
+
return 0;
+re_dev_uninit:
+ bng_re_dev_uninit(rdev);
re_dev_dealloc:
ib_dealloc_device(&rdev->ibdev);
exit:
@@ -586,6 +664,7 @@ static int bng_re_add_device(struct auxiliary_device *adev)
static void bng_re_remove_device(struct bng_re_dev *rdev,
struct auxiliary_device *aux_dev)
{
+ ib_unregister_device(&rdev->ibdev);
bng_re_dev_uninit(rdev);
ib_dealloc_device(&rdev->ibdev);
}
--
2.43.5
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH 03/15] bnge: reserve TX/completion rings for the RoCE MPC channel
2026-09-04 10:43 ` [PATCH 03/15] bnge: reserve TX/completion rings for the RoCE MPC channel Siva Reddy Kallam
@ 2026-09-04 15:52 ` Jakub Kicinski
0 siblings, 0 replies; 17+ messages in thread
From: Jakub Kicinski @ 2026-09-04 15:52 UTC (permalink / raw)
To: Siva Reddy Kallam
Cc: leonro, jgg, davem, edumazet, pabeni, andrew+netdev, horms,
netdev, linux-kernel, linux-rdma, Sachin Holla, Vikas Gupta,
Dharmender Garg
On Fri, 4 Sep 2026 03:43:11 -0700 Siva Reddy Kallam wrote:
> @@ -293,11 +309,12 @@ int bnge_reserve_rings(struct bnge_dev *bd)
> hwr.nq = bnge_nqs_demand(bd);
> }
>
> - hwr.tx = bd->tx_nr_rings;
> + tx_demand = bnge_tx_rings_demand(bd);
> + hwr.tx = tx_demand;
> hwr.rx = bd->rx_nr_rings;
> if (bd->flags & BNGE_EN_SHARED_CHNL)
> sh = true;
> - hwr.cmpl = hwr.rx + hwr.tx;
> + hwr.cmpl = bnge_cprs_demand(bd);
This if nothing else has a solid chance of conflicting with netdev.
Please put the first 3 patches in a pull request so that both netdev
and RDMA can pull that. Once it's pulled post the remaining 13 to RDMA.
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-09-04 15:52 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04 10:43 [PATCH 00/15] Add BNG_RE control path verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 01/15] bnge: Add infrastructure support for RoCE MPC channels Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 02/15] bnge: Add HSI definitions for 64-bit doorbell and " Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 03/15] bnge: reserve TX/completion rings for the RoCE MPC channel Siva Reddy Kallam
2026-09-04 15:52 ` Jakub Kicinski
2026-09-04 10:43 ` [PATCH 04/15] RDMA/bng_re: Add MPC, XID management, doorbell infrastructure Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 05/15] RDMA/bng_re: Add support verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 06/15] RDMA/bng_re: Add ucontext/mmap verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 07/15] RDMA/bng_re: Add GID verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 08/15] RDMA/bng_re: Add PD verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 09/15] RDMA/bng_re: Add MR verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 10/15] RDMA/bng_re: Add CQ verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 11/15] RDMA/bng_re: Add SRQ verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 12/15] RDMA/bng_re: Add Stats verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 13/15] RDMA/bng_re: Add AH verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 14/15] RDMA/bng_re: Add QP verbs Siva Reddy Kallam
2026-09-04 10:43 ` [PATCH 15/15] RDMA/bng_re: Register with ib-core Siva Reddy Kallam
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).