Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next v2 3/5] ionic: Update ionic_if.h with new extra port stats structure
From: Eric Joyner @ 2026-05-06  4:35 UTC (permalink / raw)
  To: netdev
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260506043526.64301-1-eric.joyner@amd.com>

A new structure to report additional statistics from the firmware has
been added to struct ionic_port_info. It currently only contains FEC
related statistics, but new statistics collected by the firmware for the
port would go in it.

This structure is located in the same area as the unused
ionic_port_pb_stats structure, so this patch also removes that since it
was never used in this driver.

Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 .../net/ethernet/pensando/ionic/ionic_if.h    | 36 ++++++-------------
 1 file changed, 10 insertions(+), 26 deletions(-)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic_if.h b/drivers/net/ethernet/pensando/ionic/ionic_if.h
index 23d6e2b4791e..01668dd10c0a 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_if.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_if.h
@@ -2855,6 +2855,14 @@ struct ionic_mgmt_port_stats {
 	__le64 frames_tx_pause;
 };
 
+struct ionic_port_extra_stats {
+	__le64 rsfec_correctable_blocks;
+	__le64 rsfec_uncorrectable_blocks;
+	__le64 fec_corrected_bits_total;
+	__le64 rx_bits_phy;
+	__le64 fec_codeword_error_bin[16];
+};
+
 enum ionic_pb_buffer_drop_stats {
 	IONIC_BUFFER_INTRINSIC_DROP = 0,
 	IONIC_BUFFER_DISCARDED,
@@ -2883,28 +2891,6 @@ enum ionic_oflow_drop_stats {
 	IONIC_OFLOW_DROP_MAX,
 };
 
-/* struct ionic_port_pb_stats - packet buffers system stats
- * uses ionic_pb_buffer_drop_stats for drop_counts[]
- */
-struct ionic_port_pb_stats {
-	__le64 sop_count_in;
-	__le64 eop_count_in;
-	__le64 sop_count_out;
-	__le64 eop_count_out;
-	__le64 drop_counts[IONIC_BUFFER_DROP_MAX];
-	__le64 input_queue_buffer_occupancy[IONIC_QOS_TC_MAX];
-	__le64 input_queue_port_monitor[IONIC_QOS_TC_MAX];
-	__le64 output_queue_port_monitor[IONIC_QOS_TC_MAX];
-	__le64 oflow_drop_counts[IONIC_OFLOW_DROP_MAX];
-	__le64 input_queue_good_pkts_in[IONIC_QOS_TC_MAX];
-	__le64 input_queue_good_pkts_out[IONIC_QOS_TC_MAX];
-	__le64 input_queue_err_pkts_in[IONIC_QOS_TC_MAX];
-	__le64 input_queue_fifo_depth[IONIC_QOS_TC_MAX];
-	__le64 input_queue_max_fifo_depth[IONIC_QOS_TC_MAX];
-	__le64 input_queue_peak_occupancy[IONIC_QOS_TC_MAX];
-	__le64 output_queue_buffer_occupancy[IONIC_QOS_TC_MAX];
-};
-
 /**
  * struct ionic_port_identity - port identity structure
  * @version:        identity structure version
@@ -2950,7 +2936,7 @@ union ionic_port_identity {
  * @sprom_page2:     Extended Transceiver sprom, page 2
  * @sprom_page17:    Extended Transceiver sprom, page 17
  * @rsvd:            reserved byte(s)
- * @pb_stats:        uplink pb drop stats
+ * @extra_stats:     Extra port statistics data
  */
 struct ionic_port_info {
 	union ionic_port_config config;
@@ -2968,9 +2954,7 @@ struct ionic_port_info {
 		};
 	};
 	u8     rsvd[376];
-
-	/* pb_stats must start at 2k offset */
-	struct ionic_port_pb_stats  pb_stats;
+	struct ionic_port_extra_stats extra_stats;
 };
 
 /*
-- 
2.17.1


^ permalink raw reply related

* [PATCH net-next v2 4/5] ionic: Report rx_bits_phy stat to ethtool
From: Eric Joyner @ 2026-05-06  4:35 UTC (permalink / raw)
  To: netdev
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260506043526.64301-1-eric.joyner@amd.com>

This stat contains the number of total bits that the PHY has received;
it's useful for BER calculations. Add it to the ethtool stats output.

Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 drivers/net/ethernet/pensando/ionic/ionic_stats.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.c b/drivers/net/ethernet/pensando/ionic/ionic_stats.c
index 0107599a9dd4..c45ed0db0582 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_stats.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.c
@@ -167,6 +167,7 @@ static const struct ionic_stat_desc ionic_rx_stats_desc[] = {
 #define IONIC_NUM_PORT_STATS ARRAY_SIZE(ionic_port_stats_desc)
 #define IONIC_NUM_TX_STATS ARRAY_SIZE(ionic_tx_stats_desc)
 #define IONIC_NUM_RX_STATS ARRAY_SIZE(ionic_rx_stats_desc)
+#define IONIC_NUM_EXTRA_PORT_STATS	1
 
 #define MAX_Q(lif)   ((lif)->netdev->real_num_tx_queues)
 
@@ -243,7 +244,7 @@ static u64 ionic_sw_stats_get_count(struct ionic_lif *lif)
 		rx_queues += 1;
 
 	total += IONIC_NUM_LIF_STATS;
-	total += IONIC_NUM_PORT_STATS;
+	total += IONIC_NUM_PORT_STATS + IONIC_NUM_EXTRA_PORT_STATS;
 
 	total += tx_queues * IONIC_NUM_TX_STATS;
 	total += rx_queues * IONIC_NUM_RX_STATS;
@@ -280,6 +281,8 @@ static void ionic_sw_stats_get_strings(struct ionic_lif *lif, u8 **buf)
 
 	for (i = 0; i < IONIC_NUM_PORT_STATS; i++)
 		ethtool_puts(buf, ionic_port_stats_desc[i].name);
+	/* extra port stats */
+	ethtool_puts(buf, "rx_bits_phy");
 
 	for (q_num = 0; q_num < MAX_Q(lif); q_num++)
 		ionic_sw_stats_get_tx_strings(lif, buf, q_num);
@@ -322,6 +325,15 @@ static void ionic_sw_stats_get_rxq_values(struct ionic_lif *lif, u64 **buf,
 	}
 }
 
+static void ionic_extra_port_stats_get_values(struct ionic_lif *lif, u64 **buf)
+{
+	struct ionic_port_info *port_info = lif->ionic->idev.port_info;
+
+	/* The # of stats added here == IONIC_NUM_EXTRA_PORT_STATS */
+	**buf = le64_to_cpu(port_info->extra_stats.rx_bits_phy);
+	(*buf)++;
+}
+
 static void ionic_sw_stats_get_values(struct ionic_lif *lif, u64 **buf)
 {
 	struct ionic_port_stats *port_stats;
@@ -341,6 +353,7 @@ static void ionic_sw_stats_get_values(struct ionic_lif *lif, u64 **buf)
 					     &ionic_port_stats_desc[i]);
 		(*buf)++;
 	}
+	ionic_extra_port_stats_get_values(lif, buf);
 
 	for (q_num = 0; q_num < MAX_Q(lif); q_num++)
 		ionic_sw_stats_get_txq_values(lif, buf, q_num);
-- 
2.17.1


^ permalink raw reply related

* [PATCH net-next v2 2/5] ionic: Get "link_down_count" ext link stat from firmware
From: Eric Joyner @ 2026-05-06  4:35 UTC (permalink / raw)
  To: netdev
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260506043526.64301-1-eric.joyner@amd.com>

The number of times that link has gone down at the port level is tracked
by the firmware and sent to the driver via regular DMA writes to an
instance of struct ionic_port_status in the driver's memory.

This statistic was never reported in favor of a driver-derived stat, but
doing it in the driver was never necessary since firmware had been
reporting it the whole time. Since it would be more accurate and true to
the description of the statistic to get this count at the PHY level,
replace the driver-calculated statistic with the firmware one and remove
the driver-calculated one entirely.

Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 drivers/net/ethernet/pensando/ionic/ionic_ethtool.c | 3 ++-
 drivers/net/ethernet/pensando/ionic/ionic_lif.c     | 1 -
 drivers/net/ethernet/pensando/ionic/ionic_lif.h     | 1 -
 3 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
index 78a802eb159f..af0c4cc8ad8e 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
@@ -117,7 +117,8 @@ static void ionic_get_link_ext_stats(struct net_device *netdev,
 	struct ionic_lif *lif = netdev_priv(netdev);
 
 	if (lif->ionic->pdev->is_physfn)
-		stats->link_down_events = lif->link_down_count;
+		stats->link_down_events =
+			    lif->ionic->idev.port_info->status.link_down_count;
 }
 
 static int ionic_get_link_ksettings(struct net_device *netdev,
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 637e635bbf03..eb7e552bc12e 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -179,7 +179,6 @@ static void ionic_link_status_check(struct ionic_lif *lif)
 		}
 	} else {
 		if (netif_carrier_ok(netdev)) {
-			lif->link_down_count++;
 			netdev_info(netdev, "Link down\n");
 			netif_carrier_off(netdev);
 		}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 8e10f66dc50e..d34692462036 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -214,7 +214,6 @@ struct ionic_lif {
 	bool registered;
 	bool doorbell_wa;
 	u16 lif_type;
-	unsigned int link_down_count;
 	unsigned int nmcast;
 	unsigned int nucast;
 	unsigned int nvlans;
-- 
2.17.1


^ permalink raw reply related

* [PATCH net-next v2 1/5] ionic: Small improvements in devcmd retry logic
From: Eric Joyner @ 2026-05-06  4:35 UTC (permalink / raw)
  To: netdev
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260506043526.64301-1-eric.joyner@amd.com>

From: Brett Creeley <brett.creeley@amd.com>

If the timeout time is hit when the last attempt returned EAGAIN, the
driver returns -ETIMEDOUT. This causes the -EAGAIN result to be lost.
Fix this by returning -EAGAIN if the timeout time is hit and the
previous result matches. Another commit uses this return value to help
signal that a deferred probe should be performed due to the firmware not
being ready, which is why it would return EAGAIN.

Also, reduce the sleep between the write to done and doorbell registers.
The msleep(1000) was initially added in an arbitrary manner. However,
this long of a sleep is problematic because it reduces the number of
retries when -EAGAIN is returned, which may result in the devmcd giving
up early due to the timeout. Fix this by reducing the sleep to
msleep(50).

Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 drivers/net/ethernet/pensando/ionic/ionic_main.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index 3c5200e2fdb7..92f2ec0bd5af 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -554,6 +554,11 @@ static int __ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds,
 
 	if (!done && !time_before(jiffies, max_wait)) {
 		ionic_dev_cmd_clean(ionic);
+
+		/* allow caller to manage EAGAIN from previous attempt */
+		if (err == IONIC_RC_EAGAIN)
+			return -EAGAIN;
+
 		dev_warn(ionic->dev, "DEVCMD %s (%d) timeout after %ld secs\n",
 			 ionic_opcode_to_str(opcode), opcode, max_seconds);
 		return -ETIMEDOUT;
@@ -568,7 +573,7 @@ static int __ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds,
 				ionic_error_to_str(err), err);
 
 			iowrite32(0, &idev->dev_cmd_regs->done);
-			msleep(1000);
+			msleep(50);
 			iowrite32(1, &idev->dev_cmd_regs->doorbell);
 			goto try_again;
 		}
-- 
2.17.1


^ permalink raw reply related

* [PATCH net-next v2 0/5] Expose more port stats to ethtool
From: Eric Joyner @ 2026-05-06  4:35 UTC (permalink / raw)
  To: netdev
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Eric Joyner

Newer hardware collects a lot more FEC statistics than older hardware;
these include FEC histograms and corrected/uncorrected word and bit
totals. This patchset adds plumbing to pass these through to ethtool
along with another link_down_count stat that is another port-level stat.
That link_down_count was already being sent to the driver by the
firmware; it just wasn't used.

Brett's patch is a small unrelated improvement to devcmd handling that's
still nice to have and will help enable deferred probe functionality.

---
v2:
- Add missing cpu_to_le64() to FEC histogram stat assignment
- Remove unused pb_stats field that's replaced by the new FEC/extra stats
- Replace ethtool ext link stat with firmware stat instead of adding
  the firmware stat to general ethtool statistics; remove old driver
  calculated stat
- Add explanation for what EAGAIN return value could be used for in
  commit message

Brett Creeley (1):
  ionic: Small improvements in devcmd retry logic

Eric Joyner (4):
  ionic: Get "link_down_count" ext link stat from firmware
  ionic: Update ionic_if.h with new extra port stats structure
  ionic: Report rx_bits_phy stat to ethtool
  ionic: Add .get_fec_stats ethtool handler

 .../ethernet/pensando/ionic/ionic_ethtool.c   | 55 ++++++++++++++++++-
 .../net/ethernet/pensando/ionic/ionic_if.h    | 36 ++++--------
 .../net/ethernet/pensando/ionic/ionic_lif.c   |  1 -
 .../net/ethernet/pensando/ionic/ionic_lif.h   |  1 -
 .../net/ethernet/pensando/ionic/ionic_main.c  |  7 ++-
 .../net/ethernet/pensando/ionic/ionic_stats.c | 15 ++++-
 6 files changed, 84 insertions(+), 31 deletions(-)


base-commit: 8c699be3dad7bba87cdda485dc099226cfc2f706
-- 
2.17.1


^ permalink raw reply

* [PATCH net-next 3/4] RDMA/ionic: Add debugfs support
From: Eric Joyner @ 2026-05-06  4:19 UTC (permalink / raw)
  To: netdev, linux-rdma
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Abhijit Gangurde, Allen Hubbe,
	Jason Gunthorpe, Leon Romanovsky, Eric Joyner
In-Reply-To: <20260506041935.1061-1-eric.joyner@amd.com>

From: Allen Hubbe <allen.hubbe@amd.com>

Adds a per-RDMA device debugfs folder under the parent ionic ethernet
device's folder for the LIF. Exports RDMA-specific debug information and
various queue information.

Signed-off-by: Allen Hubbe <allen.hubbe@amd.com>
Co-developed-by: Eric Joyner <eric.joyner@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 drivers/infiniband/hw/ionic/Makefile          |   2 +-
 drivers/infiniband/hw/ionic/ionic_admin.c     |   4 +
 .../infiniband/hw/ionic/ionic_controlpath.c   |  14 +
 drivers/infiniband/hw/ionic/ionic_debugfs.c   | 750 ++++++++++++++++++
 drivers/infiniband/hw/ionic/ionic_ibdev.c     |   3 +
 drivers/infiniband/hw/ionic/ionic_ibdev.h     |  29 +
 drivers/infiniband/hw/ionic/ionic_lif_cfg.c   |   3 +
 drivers/infiniband/hw/ionic/ionic_lif_cfg.h   |   2 +
 8 files changed, 806 insertions(+), 1 deletion(-)
 create mode 100644 drivers/infiniband/hw/ionic/ionic_debugfs.c

diff --git a/drivers/infiniband/hw/ionic/Makefile b/drivers/infiniband/hw/ionic/Makefile
index 957973742820..65bb4eaf0c13 100644
--- a/drivers/infiniband/hw/ionic/Makefile
+++ b/drivers/infiniband/hw/ionic/Makefile
@@ -6,4 +6,4 @@ obj-$(CONFIG_INFINIBAND_IONIC)	+= ionic_rdma.o
 
 ionic_rdma-y :=	\
 	ionic_ibdev.o ionic_lif_cfg.o ionic_queue.o ionic_pgtbl.o ionic_admin.o \
-	ionic_controlpath.o ionic_datapath.o ionic_hw_stats.o
+	ionic_controlpath.o ionic_datapath.o ionic_hw_stats.o ionic_debugfs.o
diff --git a/drivers/infiniband/hw/ionic/ionic_admin.c b/drivers/infiniband/hw/ionic/ionic_admin.c
index 6e3cf87025b6..3ef8bcdf8095 100644
--- a/drivers/infiniband/hw/ionic/ionic_admin.c
+++ b/drivers/infiniband/hw/ionic/ionic_admin.c
@@ -586,6 +586,7 @@ static struct ionic_aq *__ionic_create_rdma_adminq(struct ionic_ibdev *dev,
 
 	INIT_WORK(&aq->work, ionic_admin_work);
 	aq->armed = false;
+	ionic_dbg_add_aq(dev, aq);
 
 	return aq;
 
@@ -600,6 +601,7 @@ static struct ionic_aq *__ionic_create_rdma_adminq(struct ionic_ibdev *dev,
 static void __ionic_destroy_rdma_adminq(struct ionic_ibdev *dev,
 					struct ionic_aq *aq)
 {
+	ionic_dbg_rm_aq(aq);
 	kfree(aq->q_wr);
 	ionic_queue_destroy(&aq->q, dev->lif_cfg.hwdev);
 	kfree(aq);
@@ -1032,6 +1034,7 @@ static struct ionic_eq *ionic_create_eq(struct ionic_ibdev *dev, int eqid)
 		goto err_cmd;
 
 	ionic_intr_mask(dev->lif_cfg.intr_ctrl, eq->intr, IONIC_INTR_MASK_CLEAR);
+	ionic_dbg_add_eq(dev, eq);
 
 	return eq;
 
@@ -1053,6 +1056,7 @@ static void ionic_destroy_eq(struct ionic_eq *eq)
 {
 	struct ionic_ibdev *dev = eq->dev;
 
+	ionic_dbg_rm_eq(eq);
 	eq->enable = false;
 	free_irq(eq->irq, eq);
 	flush_work(&eq->work);
diff --git a/drivers/infiniband/hw/ionic/ionic_controlpath.c b/drivers/infiniband/hw/ionic/ionic_controlpath.c
index 850435ec0072..0ea053369cba 100644
--- a/drivers/infiniband/hw/ionic/ionic_controlpath.c
+++ b/drivers/infiniband/hw/ionic/ionic_controlpath.c
@@ -153,6 +153,8 @@ int ionic_create_cq_common(struct ionic_vcq *vcq,
 		goto err_xa;
 	}
 
+	ionic_dbg_add_cq(dev, cq);
+
 	return 0;
 
 err_xa:
@@ -176,6 +178,7 @@ void ionic_destroy_cq_common(struct ionic_ibdev *dev, struct ionic_cq *cq)
 	if (!cq->vcq)
 		return;
 
+	ionic_dbg_rm_cq(cq);
 	xa_erase_irq(&dev->cq_tbl, cq->cqid);
 
 	kref_put(&cq->cq_kref, ionic_cq_complete);
@@ -918,6 +921,7 @@ struct ib_mr *ionic_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 length,
 		goto err_cmd;
 
 	ionic_pgtbl_unbuf(dev, &mr->buf);
+	ionic_dbg_add_mr(dev, mr);
 
 	return &mr->ibmr;
 
@@ -988,6 +992,7 @@ struct ib_mr *ionic_reg_user_mr_dmabuf(struct ib_pd *ibpd, u64 offset,
 		goto err_cmd;
 
 	ionic_pgtbl_unbuf(dev, &mr->buf);
+	ionic_dbg_add_mr(dev, mr);
 
 	return &mr->ibmr;
 
@@ -1017,6 +1022,7 @@ int ionic_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata)
 			return rc;
 	}
 
+	ionic_dbg_rm_mr(mr);
 	ionic_pgtbl_unbuf(dev, &mr->buf);
 
 	if (mr->umem)
@@ -1064,6 +1070,8 @@ struct ib_mr *ionic_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type type,
 	if (rc)
 		goto err_cmd;
 
+	ionic_dbg_add_mr(dev, mr);
+
 	return &mr->ibmr;
 
 err_cmd:
@@ -1135,6 +1143,8 @@ int ionic_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata)
 	if (rc)
 		goto err_cmd;
 
+	ionic_dbg_add_mr(dev, mr);
+
 	return 0;
 
 err_cmd:
@@ -1152,6 +1162,7 @@ int ionic_dealloc_mw(struct ib_mw *ibmw)
 	if (rc)
 		return rc;
 
+	ionic_dbg_rm_mr(mr);
 	ionic_put_mrid(dev, mr->mrid);
 
 	return 0;
@@ -2364,6 +2375,8 @@ int ionic_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attr,
 		qp->rq_cqid = cq->cqid;
 	}
 
+	ionic_dbg_add_qp(dev, qp);
+
 	return 0;
 
 err_resp:
@@ -2643,6 +2656,7 @@ int ionic_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)
 	if (rc)
 		return rc;
 
+	ionic_dbg_rm_qp(qp);
 	xa_erase_irq(&dev->qp_tbl, qp->qpid);
 
 	kref_put(&qp->qp_kref, ionic_qp_complete);
diff --git a/drivers/infiniband/hw/ionic/ionic_debugfs.c b/drivers/infiniband/hw/ionic/ionic_debugfs.c
new file mode 100644
index 000000000000..bff110f6d553
--- /dev/null
+++ b/drivers/infiniband/hw/ionic/ionic_debugfs.c
@@ -0,0 +1,750 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
+
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+
+#include "ionic_ibdev.h"
+
+static void ionic_umem_show(struct seq_file *s, const char *w,
+			    struct ib_umem *umem, u8 page_size_log2)
+{
+	seq_printf(s, "%sumem.length:\t%#lx\n", w, umem->length);
+	seq_printf(s, "%sumem.address:\t%#lx\n", w, umem->address);
+	seq_printf(s, "%sumem.page_size:\t%llu\n", w, 1ULL << page_size_log2);
+	seq_printf(s, "%sumem.writable:\t%d\n", w, umem->writable);
+	seq_printf(s, "%sumem.nmap:\t%d\n", w, umem->sgt_append.sgt.nents);
+	seq_printf(s, "%sumem.offset():\t%#x\n", w, ib_umem_offset(umem));
+	seq_printf(s, "%sumem.num_pages():\t%lu\n",
+		   w, ib_umem_num_pages(umem));
+}
+
+static void ionic_umem_dump(struct seq_file *s, struct ib_umem *umem,
+			    u8 page_size_log2)
+{
+	int sg_i, page_i, page_count;
+	struct scatterlist *sg;
+	u64 page_dma, pg_sz;
+
+	pg_sz = 1 << page_size_log2;
+	for_each_sgtable_dma_sg(&umem->sgt_append.sgt, sg, sg_i) {
+		page_dma = sg_dma_address(sg);
+		page_count = sg_dma_len(sg) >> page_size_log2;
+
+		for (page_i = 0; page_i < page_count; ++page_i) {
+			seq_printf(s, "%#llx\n", page_dma);
+			page_dma += pg_sz;
+		}
+	}
+}
+
+static void ionic_tbl_buf_show(struct seq_file *s, const char *w,
+			       struct ionic_tbl_buf *buf)
+{
+	seq_printf(s, "%stbl_limit:\t%u\n", w, buf->tbl_limit);
+	seq_printf(s, "%stbl_pages:\t%u\n", w, buf->tbl_pages);
+	seq_printf(s, "%stbl_size:\t%zu\n", w, buf->tbl_size);
+	seq_printf(s, "%stbl_dma:\t%#llx\n", w, buf->tbl_dma);
+	seq_printf(s, "%spage_size_log2:\t%u\n", w, buf->page_size_log2);
+}
+
+static void ionic_tbl_buf_dump(struct seq_file *s, struct ionic_tbl_buf *buf)
+{
+	int page_i;
+
+	if (!buf->tbl_buf)
+		return;
+
+	for (page_i = 0; page_i < buf->tbl_pages; ++page_i)
+		seq_printf(s, "%llx\n", buf->tbl_buf[page_i]);
+}
+
+static void ionic_q_show(struct seq_file *s, const char *w,
+			 struct ionic_queue *q)
+{
+	seq_printf(s, "%ssize:\t%#llx\n", w, (u64)q->size);
+	seq_printf(s, "%sdma:\t%#llx\n", w, (u64)q->dma);
+	seq_printf(s, "%sprod:\t%#06x (%#llx)\n",
+		   w, q->prod, (u64)q->prod << q->stride_log2);
+	seq_printf(s, "%scons:\t%#06x (%#llx)\n",
+		   w, q->cons, (u64)q->cons << q->stride_log2);
+	seq_printf(s, "%smask:\t%#06x\n", w, q->mask);
+	seq_printf(s, "%sdepth_log2:\t%u\n", w, q->depth_log2);
+	seq_printf(s, "%sstride_log2:\t%u\n", w, q->stride_log2);
+	seq_printf(s, "%sdbell:\t%#llx\n", w, q->dbell);
+}
+
+static void ionic_q_dump(struct seq_file *s, struct ionic_queue *q)
+{
+	seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 16, 1, q->ptr, q->size, true);
+}
+
+static int ionic_dev_info_show(struct seq_file *s, void *v)
+{
+	struct ionic_ibdev *dev = s->private;
+	struct ionic_lif_cfg *lif_cfg;
+
+	lif_cfg = &dev->lif_cfg;
+
+	seq_printf(s, "lif_index:\t%d\n", lif_cfg->lif_index);
+	seq_printf(s, "dbid:\t%u\n", lif_cfg->dbid);
+
+	seq_printf(s, "rdma_version:\t%u\n", lif_cfg->rdma_version);
+	seq_printf(s, "rdma_minor_version:\t%u\n", lif_cfg->minor_version);
+	seq_printf(s, "qp_opcodes:\t%u\n", lif_cfg->qp_opcodes);
+	seq_printf(s, "admin_opcodes:\t%u\n", lif_cfg->admin_opcodes);
+	seq_printf(s, "reset_cnt:\t%u\n", dev->reset_cnt);
+
+	seq_printf(s, "aq_base:\t%u\n", lif_cfg->aq_base);
+	seq_printf(s, "cq_base:\t%u\n", lif_cfg->cq_base);
+	seq_printf(s, "eq_base:\t%u\n", lif_cfg->eq_base);
+
+	seq_printf(s, "aq_count:\t%u\n", lif_cfg->aq_count);
+	seq_printf(s, "eq_count:\t%u\n", lif_cfg->eq_count);
+
+	seq_printf(s, "aq_qtype:\t%u\n", lif_cfg->aq_qtype);
+	seq_printf(s, "sq_qtype:\t%u\n", lif_cfg->sq_qtype);
+	seq_printf(s, "rq_qtype:\t%u\n", lif_cfg->rq_qtype);
+	seq_printf(s, "cq_qtype:\t%u\n", lif_cfg->cq_qtype);
+	seq_printf(s, "eq_qtype:\t%u\n", lif_cfg->eq_qtype);
+
+	seq_printf(s, "max_stride:\t%u\n", lif_cfg->max_stride);
+
+	seq_printf(s, "sq_expdb:\t%u\n", lif_cfg->sq_expdb);
+	seq_printf(s, "rq_expdb:\t%u\n", lif_cfg->rq_expdb);
+	seq_printf(s, "expdb_mask:\t%u\n", lif_cfg->expdb_mask);
+
+	seq_printf(s, "udma_count:\t%u\n", lif_cfg->udma_count);
+	seq_printf(s, "udma_qgrp_shift:\t%u\n", lif_cfg->udma_qgrp_shift);
+
+	if (dev->aq_vec[0])
+		seq_printf(s, "AQ[0] admin_state:\t%u\n",
+			   atomic_read(&dev->aq_vec[0]->admin_state));
+
+	seq_printf(s, "size_pdid:\t%u\n", dev->inuse_pdid.inuse_size);
+	seq_printf(s, "size_mrid:\t%u\n", dev->inuse_mrid.inuse_size);
+	seq_printf(s, "next_mrkey:\t%u\n", dev->next_mrkey);
+	seq_printf(s, "size_cqid:\t%u\n", dev->lif_cfg.cq_count);
+	seq_printf(s, "size_qpid:\t%u\n", dev->lif_cfg.qp_count);
+
+	seq_printf(s, "page_size_supported:\t0x%llX\n",
+		   lif_cfg->page_size_supported);
+	seq_printf(s, "stats_type:\t0x%0X\n", lif_cfg->stats_type);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_dev_info);
+
+static int ionic_eq_info_show(struct seq_file *s, void *v)
+{
+	struct ionic_eq *eq = s->private;
+	struct ionic_intr __iomem *intr;
+
+	seq_printf(s, "eqid:\t%u\n", eq->eqid);
+	seq_printf(s, "intr:\t%u\n", eq->intr);
+
+	ionic_q_show(s, "q.",  &eq->q);
+	seq_printf(s, "enable:\t%u\n", eq->enable);
+	seq_printf(s, "armed:\t%u\n", eq->armed);
+	seq_printf(s, "irq:\t%u\n", eq->irq);
+	seq_printf(s, "name:\t%s\n", eq->name);
+
+	/* interrupt control readback */
+	intr = &eq->dev->lif_cfg.intr_ctrl[eq->intr];
+	seq_printf(s, "intr_coalesce_init:\t%#x\n", ioread32(&intr->coal_init));
+	seq_printf(s, "intr_mask:\t%#x\n", ioread32(&intr->mask));
+	seq_printf(s, "intr_credits:\t%#x\n", ioread32(&intr->credits));
+	seq_printf(s, "intr_mask_assert:\t%#x\n", ioread32(&intr->mask_assert));
+	seq_printf(s, "intr_coalesce:\t%#x\n", ioread32(&intr->coal));
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_eq_info);
+
+static int ionic_eq_q_show(struct seq_file *s, void *v)
+{
+	struct ionic_eq *eq = s->private;
+
+	ionic_q_dump(s, &eq->q);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_eq_q);
+
+static int ionic_mr_info_show(struct seq_file *s, void *v)
+{
+	struct ionic_mr *mr = s->private;
+
+	seq_printf(s, "mrid:\t%u\n", mr->mrid);
+
+	ionic_tbl_buf_show(s, "", &mr->buf);
+
+	if (mr->umem)
+		ionic_umem_show(s, "", mr->umem, mr->buf.page_size_log2);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_mr_info);
+
+static int ionic_mr_umem_show(struct seq_file *s, void *v)
+{
+	struct ionic_mr *mr = s->private;
+
+	ionic_umem_dump(s, mr->umem, mr->buf.page_size_log2);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_mr_umem);
+
+static int ionic_mr_tbl_buf_show(struct seq_file *s, void *v)
+{
+	struct ionic_mr *mr = s->private;
+
+	ionic_tbl_buf_dump(s, &mr->buf);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_mr_tbl_buf);
+
+static int ionic_cq_info_show(struct seq_file *s, void *v)
+{
+	struct ionic_cq *cq = s->private;
+
+	seq_printf(s, "cqid:\t%u\n", cq->cqid);
+	seq_printf(s, "eqid:\t%u\n", cq->eqid);
+
+	if (cq->q.ptr) {
+		ionic_q_show(s, "", &cq->q);
+		seq_printf(s, "arm_any_prod:\t%#06x\n", cq->arm_any_prod);
+		seq_printf(s, "arm_sol_prod:\t%#06x\n", cq->arm_sol_prod);
+	}
+
+	if (cq->umem)
+		ionic_umem_show(s, "", cq->umem, PAGE_SHIFT);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_cq_info);
+
+static int ionic_cq_q_show(struct seq_file *s, void *v)
+{
+	struct ionic_cq *cq = s->private;
+
+	ionic_q_dump(s, &cq->q);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_cq_q);
+
+static int ionic_cq_umem_show(struct seq_file *s, void *v)
+{
+	struct ionic_cq *cq = s->private;
+
+	ionic_umem_dump(s, cq->umem, PAGE_SHIFT);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_cq_umem);
+
+static int ionic_aq_info_show(struct seq_file *s, void *v)
+{
+	struct ionic_aq *aq = s->private;
+
+	seq_printf(s, "armed:\t%u\n", aq->armed);
+	seq_printf(s, "aqid:\t%u\n", aq->aqid);
+	seq_printf(s, "cqid:\t%u\n", aq->cqid);
+
+	if (aq->q.ptr)
+		ionic_q_show(s, "", &aq->q);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_aq_info);
+
+static int ionic_aq_q_show(struct seq_file *s, void *v)
+{
+	struct ionic_aq *aq = s->private;
+
+	ionic_q_dump(s, &aq->q);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_aq_q);
+
+static int ionic_aq_wqe_show(struct seq_file *s, void *v)
+{
+	struct ionic_aq *aq = s->private;
+	struct ionic_v1_admin_wqe *wqe;
+
+	wqe = &aq->debug_wr->wqe;
+
+	seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 16, 1, wqe, sizeof(*wqe),
+		     true);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_aq_wqe);
+
+static int ionic_aq_cqe_show(struct seq_file *s, void *v)
+{
+	struct ionic_aq *aq = s->private;
+	struct ionic_v1_cqe *cqe;
+
+	cqe = &aq->debug_wr->cqe;
+
+	seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 16, 1, cqe, sizeof(*cqe),
+		     true);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_aq_cqe);
+
+struct ionic_dbg_admin_wr {
+	struct ionic_aq *aq;
+	struct ionic_admin_wr wr;
+	void *data;
+	dma_addr_t dma;
+};
+
+static int ionic_aq_data_show(struct seq_file *s, void *v)
+{
+	struct ionic_aq *aq = s->private;
+	struct ionic_dbg_admin_wr *wr;
+
+	wr = container_of(aq->debug_wr, struct ionic_dbg_admin_wr, wr);
+
+	dma_sync_single_for_cpu(aq->dev->lif_cfg.hwdev, wr->dma, PAGE_SIZE,
+				DMA_FROM_DEVICE);
+
+	seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 16, 1,
+		     wr->data, PAGE_SIZE, true);
+
+	dma_sync_single_for_device(aq->dev->lif_cfg.hwdev, wr->dma, PAGE_SIZE,
+				   DMA_FROM_DEVICE);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_aq_data);
+
+static int ionic_qp_info_show(struct seq_file *s, void *v)
+{
+	struct ionic_qp *qp = s->private;
+
+	seq_printf(s, "qpid:\t%u\n", qp->qpid);
+	seq_printf(s, "udma_idx:\t%u\n", qp->udma_idx);
+	seq_printf(s, "state:\t%d\n", qp->state);
+
+	if (qp->sq.ptr) {
+		ionic_q_show(s, "sq.", &qp->sq);
+		seq_printf(s, "sq_msn_prod:\t%#06x\n",
+			   qp->sq_msn_prod);
+		seq_printf(s, "sq_msn_cons:\t%#06x\n",
+			   qp->sq_msn_cons);
+	}
+
+	if (qp->sq_umem)
+		ionic_umem_show(s, "sq.", qp->sq_umem, PAGE_SHIFT);
+
+	seq_printf(s, "sq_is_cmb:\t%d\n", !!(qp->sq_cmb & IONIC_CMB_ENABLE));
+	if (qp->sq_cmb & IONIC_CMB_ENABLE) {
+		seq_printf(s, "sq_is_expdb:\t%d\n",
+			   !!(qp->sq_cmb & IONIC_CMB_EXPDB));
+		seq_printf(s, "sq_cmb_order:\t%d\n", qp->sq_cmb_order);
+		seq_printf(s, "sq_cmb_pgid:\t%d\n", qp->sq_cmb_pgid);
+		seq_printf(s, "sq_cmb_addr:\t%#llx\n",
+			   (u64)qp->sq_cmb_addr);
+	}
+
+	seq_printf(s, "sq_flush:\t%d\n", qp->sq_flush);
+	seq_printf(s, "sq_flush_rcvd:\t%d\n", qp->sq_flush_rcvd);
+	seq_printf(s, "sq_spec:\t%d\n", qp->sq_spec);
+	seq_printf(s, "sq_cqid:\t%u\n", qp->sq_cqid);
+
+	if (qp->rq.ptr)
+		ionic_q_show(s, "rq.", &qp->rq);
+
+	if (qp->rq_umem)
+		ionic_umem_show(s, "rq.", qp->rq_umem, PAGE_SHIFT);
+
+	seq_printf(s, "rq_is_cmb:\t%d\n", !!(qp->rq_cmb & IONIC_CMB_ENABLE));
+	if (qp->rq_cmb & IONIC_CMB_ENABLE) {
+		seq_printf(s, "rq_is_expdb:\t%d\n",
+			   !!(qp->rq_cmb & IONIC_CMB_EXPDB));
+		seq_printf(s, "rq_cmb_order:\t%d\n", qp->rq_cmb_order);
+		seq_printf(s, "rq_cmb_pgid:\t%d\n", qp->rq_cmb_pgid);
+		seq_printf(s, "rq_cmb_addr:\t%#llx\n",
+			   (u64)qp->rq_cmb_addr);
+	}
+
+	seq_printf(s, "rq_flush:\t%d\n", qp->rq_flush);
+	seq_printf(s, "rq_spec:\t%d\n", qp->rq_spec);
+	seq_printf(s, "rq_cqid:\t%u\n", qp->rq_cqid);
+
+	if (qp->has_ah) {
+		bool is_ip4 = false, is_ip6 = false;
+		struct ib_ud_header *hdr = qp->hdr;
+
+		seq_printf(s, "hdr_eth_smac:\t%pM\n", hdr->eth.smac_h);
+		seq_printf(s, "hdr_eth_dmac:\t%pM\n", hdr->eth.dmac_h);
+
+		if (hdr->eth.type == cpu_to_be16(ETH_P_8021Q)) {
+			seq_printf(s, "hdr_eth_vlan:\t%u\n",
+				   be16_to_cpu(hdr->vlan.tag));
+			is_ip4 = hdr->vlan.type == cpu_to_be16(ETH_P_IP);
+			is_ip6 = hdr->vlan.type == cpu_to_be16(ETH_P_IPV6);
+		} else {
+			is_ip4 = hdr->eth.type == cpu_to_be16(ETH_P_IP);
+			is_ip6 = hdr->eth.type == cpu_to_be16(ETH_P_IPV6);
+		}
+
+		if (is_ip4) {
+			seq_printf(s, "hdr_ip4_saddr:\t%pI4\n", &hdr->ip4.saddr);
+			seq_printf(s, "hdr_ip4_daddr:\t%pI4\n", &hdr->ip4.daddr);
+			seq_printf(s, "hdr_ip4_ttl:\t%u\n", hdr->ip4.ttl);
+			seq_printf(s, "hdr_ip4_tos:\t%u\n", hdr->ip4.tos);
+		}
+
+		if (is_ip6) {
+			seq_printf(s, "hdr_ip6_saddr:\t%pI6\n",
+				   hdr->grh.source_gid.raw);
+			seq_printf(s, "hdr_ip6_daddr:\t%pI6\n",
+				   hdr->grh.destination_gid.raw);
+			seq_printf(s, "hdr_ip6_flow_label:\t%u\n",
+				   be32_to_cpu(hdr->grh.flow_label));
+			seq_printf(s, "hdr_ip6_hop_limit:\t%u\n",
+				   hdr->grh.hop_limit);
+			seq_printf(s, "hdr_ip6_traffic_class:\t%u\n",
+				   hdr->grh.traffic_class);
+		}
+
+		seq_printf(s, "hdr_udp_sport:\t%u\n", be16_to_cpu(hdr->udp.sport));
+		seq_printf(s, "hdr_udp_dport:\t%u\n", be16_to_cpu(hdr->udp.dport));
+	}
+
+	seq_printf(s, "dcqcn_profile:\t%d\n", qp->dcqcn_profile);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_qp_info);
+
+static int ionic_qp_sq_show(struct seq_file *s, void *v)
+{
+	struct ionic_qp *qp = s->private;
+
+	ionic_q_dump(s, &qp->sq);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_qp_sq);
+
+static int ionic_qp_sq_umem_show(struct seq_file *s, void *v)
+{
+	struct ionic_qp *qp = s->private;
+
+	ionic_umem_dump(s, qp->sq_umem, PAGE_SHIFT);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_qp_sq_umem);
+
+static int ionic_qp_rq_show(struct seq_file *s, void *v)
+{
+	struct ionic_qp *qp = s->private;
+
+	ionic_q_dump(s, &qp->rq);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_qp_rq);
+
+static int ionic_qp_rq_umem_show(struct seq_file *s, void *v)
+{
+	struct ionic_qp *qp = s->private;
+
+	ionic_umem_dump(s, qp->rq_umem, PAGE_SHIFT);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ionic_qp_rq_umem);
+
+void ionic_dbg_add_eq(struct ionic_ibdev *dev, struct ionic_eq *eq)
+{
+	char name[8];
+
+	eq->debug = NULL;
+
+	if (!dev->debug_eq)
+		return;
+
+	snprintf(name, sizeof(name), "%u", eq->eqid);
+
+	eq->debug = debugfs_create_dir(name, dev->debug_eq);
+	if (IS_ERR(eq->debug))
+		eq->debug = NULL;
+	if (!eq->debug)
+		return;
+
+	debugfs_create_file("info", 0440, eq->debug, eq,
+			    &ionic_eq_info_fops);
+
+	debugfs_create_file("q", 0440, eq->debug, eq,
+			    &ionic_eq_q_fops);
+}
+
+void ionic_dbg_rm_eq(struct ionic_eq *eq)
+{
+	debugfs_remove_recursive(eq->debug);
+
+	eq->debug = NULL;
+}
+
+void ionic_dbg_add_cq(struct ionic_ibdev *dev, struct ionic_cq *cq)
+{
+	char name[8];
+
+	cq->debug = NULL;
+
+	if (!dev->debug_cq)
+		return;
+
+	snprintf(name, sizeof(name), "%u", cq->cqid);
+
+	cq->debug = debugfs_create_dir(name, dev->debug_cq);
+	if (IS_ERR(cq->debug))
+		cq->debug = NULL;
+	if (!cq->debug)
+		return;
+
+	debugfs_create_file("info", 0440, cq->debug, cq,
+			    &ionic_cq_info_fops);
+
+	if (cq->q.ptr)
+		debugfs_create_file("q", 0440, cq->debug, cq,
+				    &ionic_cq_q_fops);
+
+	if (cq->umem)
+		debugfs_create_file("umem", 0440, cq->debug, cq,
+				    &ionic_cq_umem_fops);
+}
+
+void ionic_dbg_rm_cq(struct ionic_cq *cq)
+{
+	debugfs_remove_recursive(cq->debug);
+
+	cq->debug = NULL;
+}
+
+void ionic_dbg_add_aq(struct ionic_ibdev *dev, struct ionic_aq *aq)
+{
+	struct ionic_dbg_admin_wr *wr;
+	char name[8];
+
+	mutex_init(&aq->debug_mutex);
+
+	aq->debug = NULL;
+
+	if (!dev->debug_aq)
+		return;
+
+	snprintf(name, sizeof(name), "%u", aq->aqid);
+
+	aq->debug = debugfs_create_dir(name, dev->debug_aq);
+	if (IS_ERR(aq->debug))
+		aq->debug = NULL;
+	if (!aq->debug)
+		return;
+
+	debugfs_create_file("info", 0440, aq->debug, aq,
+			    &ionic_aq_info_fops);
+
+	if (aq->q.ptr)
+		debugfs_create_file("q", 0440, aq->debug, aq,
+				    &ionic_aq_q_fops);
+
+	wr = kzalloc_obj(*wr);
+	if (!wr)
+		goto err_wr;
+
+	wr->data = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!wr->data)
+		goto err_data;
+
+	wr->dma = dma_map_single(dev->lif_cfg.hwdev, wr->data, PAGE_SIZE,
+				 DMA_FROM_DEVICE);
+	if (dma_mapping_error(dev->lif_cfg.hwdev, wr->dma))
+		goto err_dma;
+
+	wr->wr.wqe.op = IONIC_V1_ADMIN_DEBUG;
+	wr->wr.wqe.cmd.stats.dma_addr = cpu_to_le64(wr->dma);
+	wr->wr.wqe.cmd.stats.length = cpu_to_le32(PAGE_SIZE);
+
+	init_completion(&wr->wr.work);
+
+	aq->debug_wr = &wr->wr;
+
+	debugfs_create_file("dbg_wr_wqe", 0440, aq->debug, aq,
+			    &ionic_aq_wqe_fops);
+
+	debugfs_create_file("dbg_wr_cqe", 0440, aq->debug, aq,
+			    &ionic_aq_cqe_fops);
+
+	debugfs_create_file("dbg_wr_data", 0440, aq->debug, aq,
+			    &ionic_aq_data_fops);
+
+	return;
+
+err_dma:
+	kfree(wr->data);
+err_data:
+	kfree(wr);
+err_wr:
+	return;
+}
+
+void ionic_dbg_rm_aq(struct ionic_aq *aq)
+{
+	struct ionic_ibdev *dev = aq->dev;
+	struct ionic_dbg_admin_wr *wr;
+
+	debugfs_remove_recursive(aq->debug);
+
+	aq->debug = NULL;
+
+	mutex_destroy(&aq->debug_mutex);
+
+	if (!aq->debug_wr)
+		return;
+
+	wr = container_of(aq->debug_wr, struct ionic_dbg_admin_wr, wr);
+
+	dma_unmap_single(dev->lif_cfg.hwdev, wr->dma, PAGE_SIZE, DMA_FROM_DEVICE);
+	kfree(wr->data);
+	kfree(wr);
+}
+
+void ionic_dbg_add_qp(struct ionic_ibdev *dev, struct ionic_qp *qp)
+{
+	char name[8];
+
+	qp->debug = NULL;
+
+	if (!dev->debug_qp)
+		return;
+
+	snprintf(name, sizeof(name), "%u", qp->qpid);
+
+	qp->debug = debugfs_create_dir(name, dev->debug_qp);
+	if (IS_ERR(qp->debug))
+		qp->debug = NULL;
+	if (!qp->debug)
+		return;
+
+	debugfs_create_file("info", 0440, qp->debug, qp,
+			    &ionic_qp_info_fops);
+
+	if (qp->sq.ptr)
+		debugfs_create_file("sq", 0440, qp->debug, qp,
+				    &ionic_qp_sq_fops);
+
+	if (qp->sq_umem)
+		debugfs_create_file("sq_umem", 0440, qp->debug, qp,
+				    &ionic_qp_sq_umem_fops);
+
+	if (qp->rq.ptr)
+		debugfs_create_file("rq", 0440, qp->debug, qp,
+				    &ionic_qp_rq_fops);
+
+	if (qp->rq_umem)
+		debugfs_create_file("rq_umem", 0440, qp->debug, qp,
+				    &ionic_qp_rq_umem_fops);
+}
+
+void ionic_dbg_rm_qp(struct ionic_qp *qp)
+{
+	debugfs_remove_recursive(qp->debug);
+
+	qp->debug = NULL;
+}
+
+void ionic_dbg_add_mr(struct ionic_ibdev *dev, struct ionic_mr *mr)
+{
+	char name[8];
+
+	mr->debug = NULL;
+
+	if (!dev->debug_mr)
+		return;
+
+	snprintf(name, sizeof(name), "%u", ionic_mrid_index(mr->mrid));
+
+	mr->debug = debugfs_create_dir(name, dev->debug_mr);
+	if (IS_ERR(mr->debug))
+		mr->debug = NULL;
+	if (!mr->debug)
+		return;
+
+	debugfs_create_file("info", 0440, mr->debug, mr,
+			    &ionic_mr_info_fops);
+
+	if (mr->umem)
+		debugfs_create_file("umem", 0440, mr->debug, mr,
+				    &ionic_mr_umem_fops);
+
+	if (mr->buf.tbl_buf)
+		debugfs_create_file("buf", 0440, mr->debug, mr,
+				    &ionic_mr_tbl_buf_fops);
+}
+
+void ionic_dbg_rm_mr(struct ionic_mr *mr)
+{
+	debugfs_remove_recursive(mr->debug);
+
+	mr->debug = NULL;
+}
+
+void ionic_dbg_add_dev(struct ionic_ibdev *dev, struct dentry *parent)
+{
+	if (IS_ERR_OR_NULL(parent))
+		return;
+
+	dev->debug = debugfs_create_dir("rdma", parent);
+	if (IS_ERR(dev->debug))
+		dev->debug = NULL;
+	if (!dev->debug)
+		return;
+
+	debugfs_create_file("info", 0440, dev->debug, dev,
+			    &ionic_dev_info_fops);
+
+	dev->debug_aq = debugfs_create_dir("aq", dev->debug);
+	if (IS_ERR(dev->debug_aq))
+		dev->debug_aq = NULL;
+
+	dev->debug_cq = debugfs_create_dir("cq", dev->debug);
+	if (IS_ERR(dev->debug_cq))
+		dev->debug_cq = NULL;
+
+	dev->debug_eq = debugfs_create_dir("eq", dev->debug);
+	if (IS_ERR(dev->debug_eq))
+		dev->debug_eq = NULL;
+
+	dev->debug_mr = debugfs_create_dir("mr", dev->debug);
+	if (IS_ERR(dev->debug_mr))
+		dev->debug_mr = NULL;
+
+	dev->debug_qp = debugfs_create_dir("qp", dev->debug);
+	if (IS_ERR(dev->debug_qp))
+		dev->debug_qp = NULL;
+}
+
+void ionic_dbg_rm_dev(struct ionic_ibdev *dev)
+{
+	debugfs_remove_recursive(dev->debug);
+
+	dev->debug = NULL;
+	dev->debug_cq = NULL;
+	dev->debug_eq = NULL;
+	dev->debug_mr = NULL;
+	dev->debug_qp = NULL;
+}
diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.c b/drivers/infiniband/hw/ionic/ionic_ibdev.c
index 356ad9fe150f..69e6164e0f1e 100644
--- a/drivers/infiniband/hw/ionic/ionic_ibdev.c
+++ b/drivers/infiniband/hw/ionic/ionic_ibdev.c
@@ -294,6 +294,7 @@ static void ionic_destroy_ibdev(struct ionic_ibdev *dev)
 	ionic_stats_cleanup(dev);
 	ionic_destroy_rdma_admin(dev);
 	ionic_destroy_resids(dev);
+	ionic_dbg_rm_dev(dev);
 	WARN_ON(!xa_empty(&dev->qp_tbl));
 	xa_destroy(&dev->qp_tbl);
 	WARN_ON(!xa_empty(&dev->cq_tbl));
@@ -318,6 +319,7 @@ static struct ionic_ibdev *ionic_create_ibdev(struct ionic_aux_dev *ionic_adev)
 	xa_init_flags(&dev->cq_tbl, GFP_ATOMIC);
 
 	ionic_init_resids(dev);
+	ionic_dbg_add_dev(dev, dev->lif_cfg.dbg_ctx);
 
 	rc = ionic_rdma_reset_devcmd(dev);
 	if (rc)
@@ -364,6 +366,7 @@ static struct ionic_ibdev *ionic_create_ibdev(struct ionic_aux_dev *ionic_adev)
 	ionic_destroy_rdma_admin(dev);
 err_reset:
 	ionic_destroy_resids(dev);
+	ionic_dbg_rm_dev(dev);
 	xa_destroy(&dev->qp_tbl);
 	xa_destroy(&dev->cq_tbl);
 	ib_dealloc_device(&dev->ibdev);
diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.h b/drivers/infiniband/hw/ionic/ionic_ibdev.h
index 7a8e4b59da1c..300d17882db5 100644
--- a/drivers/infiniband/hw/ionic/ionic_ibdev.h
+++ b/drivers/infiniband/hw/ionic/ionic_ibdev.h
@@ -115,6 +115,13 @@ struct ionic_ibdev {
 	void			*hw_stats_buf;
 	struct rdma_stat_desc	*hw_stats_hdrs;
 	struct ionic_counter_stats *counter_stats;
+	struct dentry		*debug;
+	struct dentry		*debug_aq;
+	struct dentry		*debug_cq;
+	struct dentry		*debug_eq;
+	struct dentry		*debug_mr;
+	struct dentry		*debug_qp;
+
 	int			hw_stats_count;
 };
 
@@ -133,6 +140,7 @@ struct ionic_eq {
 
 	int			irq;
 	char			name[32];
+	struct dentry		*debug;
 };
 
 struct ionic_admin_wr {
@@ -167,6 +175,9 @@ struct ionic_aq {
 	struct ionic_admin_wr_q	*q_wr;
 	struct list_head	wr_prod;
 	struct list_head	wr_post;
+	struct dentry		*debug;
+	struct ionic_admin_wr	*debug_wr;
+	struct mutex		debug_mutex; /* for debug_wr */
 };
 
 struct ionic_ctx {
@@ -215,6 +226,7 @@ struct ionic_cq {
 
 	/* infrequently accessed, keep at end */
 	struct ib_umem		*umem;
+	struct dentry		*debug;
 };
 
 struct ionic_vcq {
@@ -304,6 +316,7 @@ struct ionic_qp {
 	int			dcqcn_profile;
 
 	struct ib_ud_header	*hdr;
+	struct dentry		*debug;
 };
 
 struct ionic_ah {
@@ -323,6 +336,7 @@ struct ionic_mr {
 	int			flags;
 
 	struct ib_umem		*umem;
+	struct dentry		*debug;
 	struct ionic_tbl_buf	buf;
 	bool			created;
 };
@@ -514,4 +528,19 @@ int ionic_pgtbl_init(struct ionic_ibdev *dev,
 		     int limit,
 		     u64 page_size);
 void ionic_pgtbl_unbuf(struct ionic_ibdev *dev, struct ionic_tbl_buf *buf);
+
+/* ionic_debugfs.c */
+void ionic_dbg_add_dev(struct ionic_ibdev *dev, struct dentry *parent);
+void ionic_dbg_rm_dev(struct ionic_ibdev *dev);
+void ionic_dbg_add_eq(struct ionic_ibdev *dev, struct ionic_eq *eq);
+void ionic_dbg_rm_eq(struct ionic_eq *eq);
+void ionic_dbg_add_cq(struct ionic_ibdev *dev, struct ionic_cq *cq);
+void ionic_dbg_rm_cq(struct ionic_cq *cq);
+void ionic_dbg_add_aq(struct ionic_ibdev *dev, struct ionic_aq *aq);
+void ionic_dbg_rm_aq(struct ionic_aq *aq);
+void ionic_dbg_add_mr(struct ionic_ibdev *dev, struct ionic_mr *mr);
+void ionic_dbg_rm_mr(struct ionic_mr *mr);
+void ionic_dbg_add_qp(struct ionic_ibdev *dev, struct ionic_qp *qp);
+void ionic_dbg_rm_qp(struct ionic_qp *qp);
+
 #endif /* _IONIC_IBDEV_H_ */
diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
index 800555eb47ac..53e41b1b3e8d 100644
--- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
+++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
@@ -51,6 +51,7 @@ void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg)
 		cfg->page_size_supported = IONIC_PAGE_SIZE_SUPPORTED;
 
 	cfg->rdma_version = ident->rdma.version;
+	cfg->minor_version = ident->rdma.minor_version;
 	cfg->qp_opcodes = ident->rdma.qp_opcodes;
 	cfg->admin_opcodes = ident->rdma.admin_opcodes;
 
@@ -90,6 +91,8 @@ void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg)
 	    !!(lif->qtype_info[IONIC_QTYPE_TXQ].features & IONIC_QIDENT_F_EXPDB);
 	cfg->rq_expdb =
 	    !!(lif->qtype_info[IONIC_QTYPE_RXQ].features & IONIC_QIDENT_F_EXPDB);
+
+	cfg->dbg_ctx = lif->dentry;
 }
 
 struct net_device *ionic_lif_netdev(struct ionic_lif *lif)
diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.h b/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
index 18e7c7f13579..500925c429f6 100644
--- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
+++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
@@ -14,6 +14,7 @@
 struct ionic_lif_cfg {
 	struct device *hwdev;
 	struct ionic_lif *lif;
+	struct dentry *dbg_ctx;
 
 	int lif_index;
 	int lif_hw_index;
@@ -49,6 +50,7 @@ struct ionic_lif_cfg {
 	u8 udma_qgrp_shift;
 
 	u8 rdma_version;
+	u8 minor_version;
 	u8 qp_opcodes;
 	u8 admin_opcodes;
 
-- 
2.17.1


^ permalink raw reply related

* [PATCH net-next 4/4] RDMA/ionic: Add DCQCN parameter configuration via debugfs
From: Eric Joyner @ 2026-05-06  4:19 UTC (permalink / raw)
  To: netdev, linux-rdma
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Abhijit Gangurde, Allen Hubbe,
	Jason Gunthorpe, Leon Romanovsky, Eric Joyner
In-Reply-To: <20260506041935.1061-1-eric.joyner@amd.com>

From: Allen Hubbe <allen.hubbe@amd.com>

The HCA supports DCQCN with multiple profiles; these are normally
configured through a userspace utility, but expose these profiles and
their parameters for debug purposes via debugfs.

To use these, the firmware must be configured in the mode that expects
DCQCN settings to be programmed by the driver, otherwise the firmware
will silently fail and indicate success without any changes actually
occurring.

Signed-off-by: Allen Hubbe <allen.hubbe@amd.com>
Co-developed-by: Eric Joyner <eric.joyner@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 drivers/infiniband/hw/ionic/Makefile          |   3 +-
 .../infiniband/hw/ionic/ionic_controlpath.c   |   5 +
 drivers/infiniband/hw/ionic/ionic_dcqcn.c     | 629 ++++++++++++++++++
 drivers/infiniband/hw/ionic/ionic_fw.h        |  31 +
 drivers/infiniband/hw/ionic/ionic_ibdev.c     |   4 +
 drivers/infiniband/hw/ionic/ionic_ibdev.h     |  12 +
 drivers/infiniband/hw/ionic/ionic_lif_cfg.c   |   1 +
 drivers/infiniband/hw/ionic/ionic_lif_cfg.h   |   2 +
 drivers/infiniband/hw/ionic/ionic_profiles.h  |  86 +++
 9 files changed, 772 insertions(+), 1 deletion(-)
 create mode 100644 drivers/infiniband/hw/ionic/ionic_dcqcn.c
 create mode 100644 drivers/infiniband/hw/ionic/ionic_profiles.h

diff --git a/drivers/infiniband/hw/ionic/Makefile b/drivers/infiniband/hw/ionic/Makefile
index 65bb4eaf0c13..1ad2c3d12b36 100644
--- a/drivers/infiniband/hw/ionic/Makefile
+++ b/drivers/infiniband/hw/ionic/Makefile
@@ -6,4 +6,5 @@ obj-$(CONFIG_INFINIBAND_IONIC)	+= ionic_rdma.o
 
 ionic_rdma-y :=	\
 	ionic_ibdev.o ionic_lif_cfg.o ionic_queue.o ionic_pgtbl.o ionic_admin.o \
-	ionic_controlpath.o ionic_datapath.o ionic_hw_stats.o ionic_debugfs.o
+	ionic_controlpath.o ionic_datapath.o ionic_hw_stats.o ionic_debugfs.o \
+	ionic_dcqcn.o
diff --git a/drivers/infiniband/hw/ionic/ionic_controlpath.c b/drivers/infiniband/hw/ionic/ionic_controlpath.c
index 0ea053369cba..4daca85c4fb3 100644
--- a/drivers/infiniband/hw/ionic/ionic_controlpath.c
+++ b/drivers/infiniband/hw/ionic/ionic_controlpath.c
@@ -1512,6 +1512,7 @@ static int ionic_modify_qp_cmd(struct ionic_ibdev *dev,
 			cpu_to_le32(qp->ahid | (hdr_len << 24));
 		wr.wqe.cmd.mod_qp.dma_addr = cpu_to_le64(hdr_dma);
 
+		wr.wqe.cmd.mod_qp.dcqcn_profile = qp->dcqcn_profile;
 		wr.wqe.cmd.mod_qp.en_pcp = attr->ah_attr.sl;
 		wr.wqe.cmd.mod_qp.ip_dscp = grh->traffic_class >> 2;
 	}
@@ -2586,6 +2587,10 @@ int ionic_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int mask,
 	if (mask & IB_QP_CAP)
 		return -EINVAL;
 
+	if (mask & IB_QP_AV)
+		qp->dcqcn_profile =
+		    ionic_dcqcn_select_profile(dev, &attr->ah_attr);
+
 	rc = ionic_modify_qp_cmd(dev, pd, qp, attr, mask);
 	if (rc)
 		return rc;
diff --git a/drivers/infiniband/hw/ionic/ionic_dcqcn.c b/drivers/infiniband/hw/ionic/ionic_dcqcn.c
new file mode 100644
index 000000000000..80fa79cfd951
--- /dev/null
+++ b/drivers/infiniband/hw/ionic/ionic_dcqcn.c
@@ -0,0 +1,629 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
+
+#include <linux/debugfs.h>
+
+#include "ionic_ibdev.h"
+#include "ionic_profiles.h"
+
+static const struct ionic_profile_vals dcqcn_defaults[] = {
+	{
+		.v[NP_ICNP_802P_PRIO]			= 6,
+		.v[NP_CNP_DSCP]				= 46,
+		.v[RP_TOKEN_BUCKET_SIZE]		= 800000,
+		.v[RP_INITIAL_ALPHA_VALUE]		= 64,
+		.v[RP_DCE_TCP_G]			= 512,
+		.v[RP_DCE_TCP_RTT]			= 1,
+		.v[RP_RATE_REDUCE_MONITOR_PERIOD]	= 1,
+		.v[RP_MIN_RATE]				= 1,
+		.v[RP_GD]				= 11,
+		.v[RP_MIN_DEC_FAC]			= 50,
+		.v[RP_CLAMP_TGT_RATE_ATI]		= 1,
+		.v[RP_THRESHOLD]			= 1,
+		.v[RP_TIME_RESET]			= 1,
+		.v[RP_QP_RATE]				= 100000,
+		.v[RP_BYTE_RESET]			= 431068,
+		.v[RP_AI_RATE]				= 160,
+		.v[RP_HAI_RATE]				= 300,
+	},
+};
+
+#define DCQCN_INT_ATTR(_min, _max, _name) \
+	{ .min = (_min), .max = (_max), .name = (_name) }
+
+#define DCQCN_BOOL_ATTR(_name) \
+	DCQCN_INT_ATTR(0, 1, _name)
+
+static const struct ionic_dcqcn_param_attr dcqcn_attrs[] = {
+	/* under "roce_np" */
+	DCQCN_INT_ATTR(0, 7, "icnp_802p_prio"),
+	DCQCN_INT_ATTR(0, 63, "cnp_dscp"),
+	/* under "roce_rp" */
+	DCQCN_INT_ATTR(100, 200000000, "token_bucket_size"),
+	DCQCN_INT_ATTR(0, 1023, "initial_alpha_value"),
+	DCQCN_INT_ATTR(0, 1023, "dce_tcp_g"),
+	DCQCN_INT_ATTR(1, 131071, "dce_tcp_rtt"),
+	DCQCN_INT_ATTR(1, INT_MAX, "rate_reduce_monitor_period"),
+	DCQCN_INT_ATTR(1, INT_MAX, "rate_to_set_on_first_cnp"),
+	DCQCN_INT_ATTR(1, INT_MAX, "min_rate"),
+	DCQCN_INT_ATTR(1, 11, "gd"),
+	DCQCN_INT_ATTR(0, 100, "min_dec_fac"),
+	DCQCN_BOOL_ATTR("clamp_tgt_rate"),
+	DCQCN_BOOL_ATTR("clamp_tgt_rate_ati"),
+	DCQCN_INT_ATTR(1, 31, "threshold"),
+	DCQCN_INT_ATTR(1, 32767, "time_reset"),
+	DCQCN_INT_ATTR(1, INT_MAX, "qp_rate"),
+	DCQCN_INT_ATTR(1, INT_MAX, "byte_reset"),
+	DCQCN_INT_ATTR(1, INT_MAX, "ai_rate"),
+	DCQCN_INT_ATTR(1, INT_MAX, "hai_rate"),
+};
+
+static void dcqcn_set_profile(struct ionic_profile *profile)
+{
+	struct ionic_ibdev *dev = profile->dev;
+	struct ionic_admin_wr wr = {
+		.work = COMPLETION_INITIALIZER_ONSTACK(wr.work),
+		.wqe = {
+			.op = IONIC_V1_ADMIN_MODIFY_DCQCN,
+			.len = cpu_to_le16(IONIC_ADMIN_MODIFY_DCQCN_IN_V1_LEN),
+			.cmd.mod_dcqcn = {
+				.id_ver = cpu_to_le32(profile->idx + 1),
+			}
+		}
+	};
+	int rc;
+
+	wr.wqe.cmd.mod_dcqcn.np_incp_802p_prio =
+		profile->vals.v[NP_ICNP_802P_PRIO];
+
+	wr.wqe.cmd.mod_dcqcn.np_cnp_dscp =
+		profile->vals.v[NP_CNP_DSCP];
+
+	wr.wqe.cmd.mod_dcqcn.rp_token_bucket_size =
+		cpu_to_be64(profile->vals.v[RP_TOKEN_BUCKET_SIZE]);
+
+	wr.wqe.cmd.mod_dcqcn.rp_initial_alpha_value =
+		cpu_to_be16(profile->vals.v[RP_INITIAL_ALPHA_VALUE]);
+
+	wr.wqe.cmd.mod_dcqcn.rp_dce_tcp_g =
+		cpu_to_be16(profile->vals.v[RP_DCE_TCP_G]);
+
+	wr.wqe.cmd.mod_dcqcn.rp_dce_tcp_rtt =
+		cpu_to_be32(profile->vals.v[RP_DCE_TCP_RTT]);
+
+	wr.wqe.cmd.mod_dcqcn.rp_rate_reduce_monitor_period =
+		cpu_to_be32(profile->vals.v[RP_RATE_REDUCE_MONITOR_PERIOD]);
+
+	wr.wqe.cmd.mod_dcqcn.rp_rate_to_set_on_first_cnp =
+		cpu_to_be32(profile->vals.v[RP_RATE_TO_SET_ON_FIRST_CNP]);
+
+	wr.wqe.cmd.mod_dcqcn.rp_min_rate =
+		cpu_to_be32(profile->vals.v[RP_MIN_RATE]);
+
+	wr.wqe.cmd.mod_dcqcn.rp_gd =
+		profile->vals.v[RP_GD];
+
+	wr.wqe.cmd.mod_dcqcn.rp_min_dec_fac =
+		profile->vals.v[RP_MIN_DEC_FAC];
+
+	if (profile->vals.v[RP_CLAMP_TGT_RATE])
+		wr.wqe.cmd.mod_dcqcn.rp_clamp_flags |= IONIC_RPF_CLAMP_TGT_RATE;
+
+	if (profile->vals.v[RP_CLAMP_TGT_RATE_ATI])
+		wr.wqe.cmd.mod_dcqcn.rp_clamp_flags |=
+			IONIC_RPF_CLAMP_TGT_RATE_ATI;
+
+	wr.wqe.cmd.mod_dcqcn.rp_threshold =
+		profile->vals.v[RP_THRESHOLD];
+
+	wr.wqe.cmd.mod_dcqcn.rp_time_reset =
+		cpu_to_be32(profile->vals.v[RP_TIME_RESET]);
+
+	wr.wqe.cmd.mod_dcqcn.rp_qp_rate =
+		cpu_to_be32(profile->vals.v[RP_QP_RATE]);
+
+	wr.wqe.cmd.mod_dcqcn.rp_byte_reset =
+		cpu_to_be32(profile->vals.v[RP_BYTE_RESET]);
+
+	wr.wqe.cmd.mod_dcqcn.rp_ai_rate =
+		cpu_to_be32(profile->vals.v[RP_AI_RATE]);
+
+	wr.wqe.cmd.mod_dcqcn.rp_hai_rate =
+		cpu_to_be32(profile->vals.v[RP_HAI_RATE]);
+
+	ionic_admin_post(dev, &wr);
+	rc = ionic_admin_wait(dev, &wr, IONIC_ADMIN_F_INTERRUPT);
+	if (rc)
+		ibdev_warn(&dev->ibdev, "dcqcn profile %d not set, error %d\n",
+			   profile->idx + 1, rc);
+}
+
+static int dcqcn_param_show(struct seq_file *s, void *v)
+{
+	struct ionic_dcqcn_param_entry *entry = s->private;
+	struct ionic_profile *profile = entry->profile;
+	int val = profile->vals.v[entry->var];
+
+	seq_printf(s, "%d\n", val);
+	return 0;
+}
+
+static ssize_t dcqcn_param_write(struct file *fp, const char __user *ubuf,
+				 size_t count, loff_t *ppos)
+{
+	struct seq_file *s = fp->private_data;
+	struct ionic_dcqcn_param_entry *entry;
+	struct ionic_profile *profile;
+	int rc, val;
+	char *buf;
+
+	entry = s->private;
+	profile = entry->profile;
+
+	buf = memdup_user_nul(ubuf, count);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	rc = kstrtoint(buf, 0, &val);
+	if (rc < 0)
+		goto out;
+
+	if (val < dcqcn_attrs[entry->var].min ||
+	    val > dcqcn_attrs[entry->var].max) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	profile->vals.v[entry->var] = val;
+
+	dcqcn_set_profile(profile);
+out:
+	kfree(buf);
+	return rc ?: count;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(dcqcn_param);
+
+static const struct ionic_profile_vals *dcqcn_get_defaults(int prof_i)
+{
+	if (prof_i < 0 || prof_i >= ARRAY_SIZE(dcqcn_defaults))
+		return &dcqcn_defaults[0];
+
+	return &dcqcn_defaults[prof_i];
+}
+
+static int dcqcn_match_default_show(struct seq_file *s, void *v)
+{
+	struct ionic_profile_root *profile_root = s->private;
+	int val = profile_root->profiles_default;
+
+	seq_printf(s, "%d\n", val);
+	return 0;
+}
+
+static ssize_t dcqcn_match_default_write(struct file *fp, const char __user *ubuf,
+					 size_t count, loff_t *ppos)
+{
+	struct ionic_profile_root *profile_root;
+	struct seq_file *s = fp->private_data;
+	int rc, val;
+	char *buf;
+
+	profile_root = s->private;
+
+	buf = memdup_user_nul(ubuf, count);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	rc = kstrtoint(buf, 0, &val);
+	if (rc < 0)
+		goto out;
+
+	if (val < 0 || val > profile_root->profiles_count) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	profile_root->profiles_default = val;
+
+out:
+	kfree(buf);
+	return rc ?: count;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(dcqcn_match_default);
+
+static int dcqcn_match_rules_show(struct seq_file *s, void *v)
+{
+	struct ionic_profile_root *profile_root = s->private;
+	struct ionic_match_rule *rule, *rules;
+	unsigned long irqflags;
+	int i, rules_count;
+
+	spin_lock_irqsave(&profile_root->rules_lock, irqflags);
+
+	rules = profile_root->rules;
+	rules_count = profile_root->rules_count;
+	for (i = 0; i < rules_count; ++i) {
+		rule = &rules[i];
+		seq_printf(s, "%s %d %d\n", rule->name, rule->cond, rule->prof);
+	}
+
+	spin_unlock_irqrestore(&profile_root->rules_lock, irqflags);
+
+	return 0;
+}
+
+static bool ionic_match_prio(struct rdma_ah_attr *attr, int cond)
+{
+	int prio = attr->sl;
+
+	return prio >= 0 && prio < 8 && (cond & BIT(prio));
+}
+
+static bool ionic_match_gid(struct rdma_ah_attr *attr, int cond)
+{
+	int gid = rdma_ah_read_grh(attr)->sgid_index;
+
+	return gid == cond;
+}
+
+static bool ionic_parse_rule_name(const char *name, const char *buf, int count)
+{
+	return !strncmp(name, buf, count) && !name[count];
+}
+
+static int ionic_parse_match_rules(const char *buf, size_t count,
+				   int prof_count, int rules_count,
+				   struct ionic_match_rule *rules)
+{
+	bool (*match)(struct rdma_ah_attr *attr, int cond);
+	struct ionic_match_rule *rule;
+	int cmd, cond, prof, end;
+	int rc, rule_i = 0;
+	const char *name;
+
+	for (;; ++rule_i) {
+		/* skip leading whitespace */
+
+		rc = sscanf(buf, " %n", &end);
+		if (rc != 0)
+			return -EINVAL;
+
+		buf += end;
+		count -= end;
+
+		/* break at end of buffer */
+
+		if (!count)
+			break;
+
+		/* Parse one rule, as:
+		 * <name> <condition> <profile>
+		 *
+		 * Name and condition determine when a rule will be a match.
+		 * If a rule is a match, then use the inidcated DCQCN profile.
+		 *
+		 * If name eq "gid":
+		 * then condition is a gid index.
+		 *
+		 * eg: gid 5 3 -> for gid index 5, use profile 3.
+		 *
+		 * If name eq "prio":
+		 * then condition is a bitmask of 802.1p priorities.
+		 *
+		 * eg: prio 0xc 1 -> for 802.1p priority 2 or 3, use profile 1.
+		 */
+
+		rc = sscanf(buf, "%*s%n%i%i%n", &cmd, &cond, &prof, &end);
+		if (rc != 2)
+			return -EINVAL;
+
+		/* rule name in first `cmd` chars of `buf` */
+
+		if (ionic_parse_rule_name("gid", buf, cmd)) {
+			match = ionic_match_gid;
+			name = "gid";
+		} else if (ionic_parse_rule_name("prio", buf, cmd)) {
+			match = ionic_match_prio;
+			name = "prio";
+		} else {
+			return -EINVAL;
+		}
+
+		if (prof < 0 || prof > prof_count)
+			return -EINVAL;
+
+		if (rule_i < rules_count) {
+			rule = &rules[rule_i];
+			rule->match = match;
+			rule->name = name;
+			rule->cond = cond;
+			rule->prof = prof;
+		}
+
+		buf += end;
+		count -= end;
+	}
+
+	return rule_i;
+}
+
+static ssize_t dcqcn_match_rules_write(struct file *fp,
+				       const char __user *ubuf,
+				       size_t count, loff_t *ppos)
+{
+	struct ionic_profile_root *profile_root;
+	struct seq_file *s = fp->private_data;
+	unsigned long irqflags;
+	int rc, rules_count;
+	char *buf;
+
+	profile_root = s->private;
+
+	buf = memdup_user_nul(ubuf, count);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	/* validate and count rules */
+	rc = ionic_parse_match_rules(buf, count, profile_root->profiles_count,
+				     0, NULL);
+	if (rc < 0)
+		goto out;
+
+	rules_count = rc;
+	rc = 0;
+
+	/* clear previous rules */
+	spin_lock_irqsave(&profile_root->rules_lock, irqflags);
+	profile_root->rules_count = 0;
+	spin_unlock_irqrestore(&profile_root->rules_lock, irqflags);
+
+	kfree(profile_root->rules);
+	profile_root->rules = NULL;
+
+	/* assign new rules */
+	if (rules_count) {
+		profile_root->rules = kzalloc_objs(*profile_root->rules,
+						   rules_count);
+		if (!profile_root->rules) {
+			rc = -ENOMEM;
+			goto out;
+		}
+
+		ionic_parse_match_rules(buf, count, profile_root->profiles_count,
+					rules_count, profile_root->rules);
+
+		spin_lock_irqsave(&profile_root->rules_lock, irqflags);
+		profile_root->rules_count = rules_count;
+		spin_unlock_irqrestore(&profile_root->rules_lock, irqflags);
+	}
+out:
+	kfree(buf);
+	return rc ?: count;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(dcqcn_match_rules);
+
+static ssize_t dcqcn_profile_reset(struct file *fp, const char __user *ubuf,
+				   size_t count, loff_t *ppos)
+{
+	struct ionic_profile *profile = fp->private_data;
+	int rc = 0;
+	char *buf;
+
+	buf = memdup_user_nul(ubuf, 2);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	if (strcmp(buf, "1") && strcmp(buf, "1\n")) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	profile->vals = *dcqcn_get_defaults(profile->idx);
+	dcqcn_set_profile(profile);
+
+out:
+	kfree(buf);
+	return rc ?: count;
+}
+
+static const struct file_operations dcqcn_profile_reset_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.write = dcqcn_profile_reset,
+};
+
+static void ionic_dcqcn_add_profile_params(struct ionic_profile *profile)
+{
+	struct ionic_dcqcn_param_entry *entry;
+	struct dentry *dentry;
+	int i;
+
+	for (i = 0; i < DCQCN_VAR_COUNT; i++) {
+		entry = &profile->entries[i];
+
+		entry->profile = profile;
+		entry->var = i;
+
+		if (i <= NP_CNP_DSCP)
+			dentry = profile->roce_np_debug;
+		else
+			dentry = profile->roce_rp_debug;
+
+		debugfs_create_file(dcqcn_attrs[i].name, 0640, dentry, entry,
+				    &dcqcn_param_fops);
+	}
+}
+
+int ionic_dcqcn_init(struct ionic_ibdev *dev, int prof_count)
+{
+	const enum ionic_profile_type type = IONIC_PROFILE_TYPE_DCQCN;
+	struct ionic_profile_root *profile_root;
+	int rc, i;
+
+	if (!prof_count)
+		return 0;
+
+	dev->profile[type] = kzalloc_obj(*dev->profile[type]);
+	if (!dev->profile[type]) {
+		rc = -ENOMEM;
+		goto err_cb_alloc;
+	}
+
+	profile_root = dev->profile[type];
+	profile_root->dev = dev;
+
+	spin_lock_init(&profile_root->rules_lock);
+
+	profile_root->debug = debugfs_create_dir("dcqcn", dev->debug);
+	if (IS_ERR(profile_root->debug)) {
+		profile_root->debug = NULL;
+		goto err_cb_dentry;
+	}
+
+	debugfs_create_file("match_default", 0640, profile_root->debug,
+			    profile_root, &dcqcn_match_default_fops);
+	debugfs_create_file("match_rules", 0640, profile_root->debug,
+			    profile_root, &dcqcn_match_rules_fops);
+
+	profile_root->profiles_debug = debugfs_create_dir("profiles",
+							  profile_root->debug);
+	if (IS_ERR(profile_root->profiles_debug)) {
+		profile_root->profiles_debug = NULL;
+		goto err_prof_dentry;
+	}
+
+	profile_root->profiles = kzalloc_objs(*profile_root->profiles,
+					      prof_count);
+	if (!profile_root->profiles) {
+		rc = -ENOMEM;
+		goto err_prof_alloc;
+	}
+
+	profile_root->profiles_default = 0;
+
+	for (i = 0; i < prof_count; ++i) {
+		struct ionic_profile *profile = &profile_root->profiles[i];
+		char name[8];
+
+		profile->dev = dev;
+		profile->vals = *dcqcn_get_defaults(i);
+		profile->idx = i;
+
+		dcqcn_set_profile(profile);
+
+		snprintf(name, sizeof(name), "%d", i + 1);
+		profile->debug = debugfs_create_dir(name,
+						 profile_root->profiles_debug);
+		if (IS_ERR(profile->debug)) {
+			profile->debug = NULL;
+			break;
+		}
+
+		debugfs_create_file("reset", 0640, profile->debug, profile,
+				    &dcqcn_profile_reset_fops);
+
+		profile->entries = kzalloc_objs(*profile->entries,
+						DCQCN_VAR_COUNT);
+
+		profile->roce_np_debug = debugfs_create_dir("roce_np",
+							    profile->debug);
+		if (IS_ERR(profile->roce_np_debug)) {
+			profile->roce_np_debug = NULL;
+			break;
+		}
+
+		profile->roce_rp_debug = debugfs_create_dir("roce_rp",
+							    profile->debug);
+		if (IS_ERR(profile->roce_rp_debug)) {
+			profile->roce_rp_debug = NULL;
+			break;
+		}
+
+		ionic_dcqcn_add_profile_params(profile);
+	}
+
+	if (!i)
+		goto err_prof_init;
+
+	profile_root->profiles_count = i;
+	if (i != prof_count) {
+		ibdev_warn(&dev->ibdev,
+			   "dcqcn initialized %d out of %d profiles\n",
+			   i, prof_count);
+	}
+	return 0;
+
+err_prof_init:
+	kfree(profile_root->profiles);
+err_prof_alloc:
+	debugfs_remove_recursive(profile_root->profiles_debug);
+err_prof_dentry:
+	debugfs_remove_recursive(profile_root->debug);
+err_cb_dentry:
+	kfree(dev->profile[type]);
+	dev->profile[type] = NULL;
+err_cb_alloc:
+	ibdev_warn(&dev->ibdev, "dcqcn failed init, error %d\n", rc);
+	return rc;
+}
+
+void ionic_dcqcn_destroy(struct ionic_ibdev *dev)
+{
+	struct ionic_profile_root *profile_root;
+	int i, prof_count;
+
+	profile_root = dev->profile[IONIC_PROFILE_TYPE_DCQCN];
+
+	if (!profile_root)
+		return;
+	prof_count = profile_root->profiles_count;
+	for (i = 0; i < prof_count; ++i) {
+		struct ionic_profile *profile;
+
+		profile = &profile_root->profiles[i];
+
+		kfree(profile->entries);
+		debugfs_remove_recursive(profile->debug);
+	}
+
+	debugfs_remove_recursive(profile_root->profiles_debug);
+	kfree(profile_root->rules);
+	kfree(profile_root->profiles);
+
+	debugfs_remove_recursive(profile_root->debug);
+
+	kfree(profile_root);
+	dev->profile[IONIC_PROFILE_TYPE_DCQCN] = NULL;
+}
+
+int ionic_dcqcn_select_profile(struct ionic_ibdev *dev,
+			       struct rdma_ah_attr *attr)
+{
+	struct ionic_profile_root *profile_root;
+	struct ionic_match_rule *rule, *rules;
+	int i, rules_count, prof;
+	unsigned long irqflags;
+
+	if (!dev->profile[IONIC_PROFILE_TYPE_DCQCN])
+		return 0;
+
+	profile_root = dev->profile[IONIC_PROFILE_TYPE_DCQCN];
+
+	spin_lock_irqsave(&profile_root->rules_lock, irqflags);
+
+	prof = profile_root->profiles_default;
+	rules = profile_root->rules;
+	rules_count = profile_root->rules_count;
+
+	for (i = 0; i < rules_count; ++i) {
+		rule = &rules[i];
+		if (rule->match(attr, rule->cond)) {
+			prof = rule->prof;
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&profile_root->rules_lock, irqflags);
+
+	return prof;
+}
diff --git a/drivers/infiniband/hw/ionic/ionic_fw.h b/drivers/infiniband/hw/ionic/ionic_fw.h
index 0806c148faf2..eb463afe2305 100644
--- a/drivers/infiniband/hw/ionic/ionic_fw.h
+++ b/drivers/infiniband/hw/ionic/ionic_fw.h
@@ -838,6 +838,36 @@ struct ionic_admin_query_qp {
 static_assert(sizeof(struct ionic_admin_query_qp) ==
 	       IONIC_ADMIN_QUERY_QP_IN_V1_LEN);
 
+enum ionic_v1_dcqcn_flags {
+	IONIC_RPF_CLAMP_TGT_RATE	= BIT(0),
+	IONIC_RPF_CLAMP_TGT_RATE_ATI	= BIT(1),
+};
+
+struct ionic_admin_mod_dcqcn {
+	__u8		np_incp_802p_prio;
+	__u8		np_cnp_dscp;
+	__be16		rp_dce_tcp_g;
+	__be32		rp_dce_tcp_rtt;
+	__be32		rp_rate_reduce_monitor_period;
+	__be32		rp_rate_to_set_on_first_cnp;
+	__be32		rp_min_rate;
+	__be16		rp_initial_alpha_value;
+	__u8		rp_gd;
+	__u8		rp_min_dec_fac;
+	__u8		rp_clamp_flags;
+	__u8		rp_threshold;
+	__be16		rp_time_reset;
+	__be32		rp_qp_rate;
+	__be32		rp_byte_reset;
+	__be32		rp_ai_rate;
+	__be32		rp_hai_rate;
+	__le32		id_ver;
+	__be64		rp_token_bucket_size;
+} __packed;
+
+#define IONIC_ADMIN_MODIFY_DCQCN_IN_V1_LEN 56
+static_assert(sizeof(struct ionic_admin_mod_dcqcn) == IONIC_ADMIN_MODIFY_DCQCN_IN_V1_LEN);
+
 #define ADMIN_WQE_STRIDE	64
 #define ADMIN_WQE_HDR_LEN	4
 
@@ -860,6 +890,7 @@ struct ionic_v1_admin_wqe {
 		struct ionic_admin_destroy_qp destroy_qp;
 		struct ionic_admin_mod_qp mod_qp;
 		struct ionic_admin_query_qp query_qp;
+		struct ionic_admin_mod_dcqcn mod_dcqcn;
 	} cmd;
 };
 
diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.c b/drivers/infiniband/hw/ionic/ionic_ibdev.c
index 69e6164e0f1e..2d89a4139d5a 100644
--- a/drivers/infiniband/hw/ionic/ionic_ibdev.c
+++ b/drivers/infiniband/hw/ionic/ionic_ibdev.c
@@ -290,6 +290,8 @@ static void ionic_destroy_resids(struct ionic_ibdev *dev)
 static void ionic_destroy_ibdev(struct ionic_ibdev *dev)
 {
 	ionic_kill_rdma_admin(dev, false);
+
+	ionic_dcqcn_destroy(dev);
 	ib_unregister_device(&dev->ibdev);
 	ionic_stats_cleanup(dev);
 	ionic_destroy_rdma_admin(dev);
@@ -357,6 +359,8 @@ static struct ionic_ibdev *ionic_create_ibdev(struct ionic_aux_dev *ionic_adev)
 	if (rc)
 		goto err_register;
 
+	ionic_dcqcn_init(dev, dev->lif_cfg.dcqcn_profiles);
+
 	return dev;
 
 err_register:
diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.h b/drivers/infiniband/hw/ionic/ionic_ibdev.h
index 300d17882db5..cf909b14395a 100644
--- a/drivers/infiniband/hw/ionic/ionic_ibdev.h
+++ b/drivers/infiniband/hw/ionic/ionic_ibdev.h
@@ -74,6 +74,11 @@ enum ionic_mmap_flag {
 	IONIC_MMAP_WC = BIT(0),
 };
 
+enum ionic_profile_type {
+	IONIC_PROFILE_TYPE_DCQCN,
+	IONIC_PROFILE_TYPE_MAX,
+};
+
 struct ionic_mmap_entry {
 	struct rdma_user_mmap_entry rdma_entry;
 	unsigned long size;
@@ -123,6 +128,7 @@ struct ionic_ibdev {
 	struct dentry		*debug_qp;
 
 	int			hw_stats_count;
+	struct ionic_profile_root	*profile[IONIC_PROFILE_TYPE_MAX];
 };
 
 struct ionic_eq {
@@ -543,4 +549,10 @@ void ionic_dbg_rm_mr(struct ionic_mr *mr);
 void ionic_dbg_add_qp(struct ionic_ibdev *dev, struct ionic_qp *qp);
 void ionic_dbg_rm_qp(struct ionic_qp *qp);
 
+/* ionic_dcqcn.c */
+int ionic_dcqcn_init(struct ionic_ibdev *dev, int prof_count);
+void ionic_dcqcn_destroy(struct ionic_ibdev *dev);
+int ionic_dcqcn_select_profile(struct ionic_ibdev *dev,
+			       struct rdma_ah_attr *attr);
+
 #endif /* _IONIC_IBDEV_H_ */
diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
index 53e41b1b3e8d..71aaf8d83f9c 100644
--- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
+++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
@@ -93,6 +93,7 @@ void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg)
 	    !!(lif->qtype_info[IONIC_QTYPE_RXQ].features & IONIC_QIDENT_F_EXPDB);
 
 	cfg->dbg_ctx = lif->dentry;
+	cfg->dcqcn_profiles = ident->rdma.dcqcn_profiles;
 }
 
 struct net_device *ionic_lif_netdev(struct ionic_lif *lif)
diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.h b/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
index 500925c429f6..1e005400a0fa 100644
--- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
+++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
@@ -58,6 +58,8 @@ struct ionic_lif_cfg {
 	bool sq_expdb;
 	bool rq_expdb;
 	u8 expdb_mask;
+
+	u8 dcqcn_profiles;
 };
 
 void ionic_fill_lif_cfg(struct ionic_lif *lif, struct ionic_lif_cfg *cfg);
diff --git a/drivers/infiniband/hw/ionic/ionic_profiles.h b/drivers/infiniband/hw/ionic/ionic_profiles.h
new file mode 100644
index 000000000000..a6cdd5992460
--- /dev/null
+++ b/drivers/infiniband/hw/ionic/ionic_profiles.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
+
+#ifndef _IONIC_PROFILES_H_
+#define _IONIC_PROFILES_H_
+
+#include "ionic_ibdev.h"
+
+enum ionic_dcqcn_var {
+	/* notification point */
+	NP_ICNP_802P_PRIO,		/* 0..7 (prio) */
+	NP_CNP_DSCP,			/* 0..63 (dscp) */
+
+	RP_TOKEN_BUCKET_SIZE,		/* 100..200000000 (100kb - 200gb) */
+	/* reaction point alpha update */
+	RP_INITIAL_ALPHA_VALUE,		/* 0..1023 */
+	RP_DCE_TCP_G,			/* 0..1023 */
+	RP_DCE_TCP_RTT,			/* 1..131071 (us) */
+
+	/* reaction point rate decrease */
+	RP_RATE_REDUCE_MONITOR_PERIOD,	/* 1.. (us) */
+	RP_RATE_TO_SET_ON_FIRST_CNP,	/* 0 disable, 1.. (Mbps) */
+	RP_MIN_RATE,			/* 1.. (Mbps) */
+	RP_GD,				/* 1..11 */
+	RP_MIN_DEC_FAC,			/* 0..100 (%) */
+
+	/* reaction point rate increase */
+	RP_CLAMP_TGT_RATE,		/* 0..1 (bool) */
+	RP_CLAMP_TGT_RATE_ATI,		/* 0..1 (bool) */
+	RP_THRESHOLD,			/* 1..31 */
+	RP_TIME_RESET,			/* 1..32767 (x RP_DCE_TCP_RTT) */
+	RP_QP_RATE,			/* 1.. (Mbps) */
+	RP_BYTE_RESET,			/* 1..4294967296 (B) */
+	RP_AI_RATE,			/* 1.. (Mbps) */
+	RP_HAI_RATE,			/* 1.. (Mbps) */
+
+	DCQCN_VAR_COUNT
+};
+
+struct ionic_match_rule {
+	bool			(*match)(struct rdma_ah_attr *attr, int cond);
+	const char		*name;
+	int			cond;
+	int			prof;
+};
+
+struct ionic_profile_vals {
+	int			v[DCQCN_VAR_COUNT];
+};
+
+struct ionic_dcqcn_param_attr {
+	char			*name;
+	int			min;
+	int			max;
+};
+
+struct ionic_dcqcn_param_entry {
+	struct ionic_profile	*profile;
+	enum ionic_dcqcn_var	var;
+};
+
+struct ionic_profile {
+	struct ionic_ibdev		*dev;
+	struct ionic_profile_vals	vals;
+	struct ionic_dcqcn_param_entry	*entries;
+	int idx;
+
+	struct dentry			*debug;
+	struct dentry			*roce_np_debug;
+	struct dentry			*roce_rp_debug;
+};
+
+struct ionic_profile_root {
+	struct ionic_ibdev	*dev;
+	int			profiles_default;
+	int			profiles_count;
+	struct ionic_profile	*profiles;
+	spinlock_t		rules_lock;	/* lock to atomic update the rules */
+	int			rules_count;
+	struct ionic_match_rule	*rules;
+
+	struct dentry		*debug;
+	struct dentry		*profiles_debug;
+};
+
+#endif /* _IONIC_PROFILES_H_ */
-- 
2.17.1


^ permalink raw reply related

* [PATCH net-next 1/4] RDMA/ionic: Update copyright year to 2026
From: Eric Joyner @ 2026-05-06  4:19 UTC (permalink / raw)
  To: netdev, linux-rdma
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Abhijit Gangurde, Allen Hubbe,
	Jason Gunthorpe, Leon Romanovsky, Eric Joyner
In-Reply-To: <20260506041935.1061-1-eric.joyner@amd.com>

Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 drivers/infiniband/hw/ionic/Kconfig             | 2 +-
 drivers/infiniband/hw/ionic/ionic_admin.c       | 2 +-
 drivers/infiniband/hw/ionic/ionic_controlpath.c | 2 +-
 drivers/infiniband/hw/ionic/ionic_datapath.c    | 2 +-
 drivers/infiniband/hw/ionic/ionic_fw.h          | 2 +-
 drivers/infiniband/hw/ionic/ionic_hw_stats.c    | 2 +-
 drivers/infiniband/hw/ionic/ionic_ibdev.c       | 2 +-
 drivers/infiniband/hw/ionic/ionic_ibdev.h       | 2 +-
 drivers/infiniband/hw/ionic/ionic_lif_cfg.c     | 2 +-
 drivers/infiniband/hw/ionic/ionic_lif_cfg.h     | 2 +-
 drivers/infiniband/hw/ionic/ionic_pgtbl.c       | 2 +-
 drivers/infiniband/hw/ionic/ionic_queue.c       | 2 +-
 drivers/infiniband/hw/ionic/ionic_queue.h       | 2 +-
 drivers/infiniband/hw/ionic/ionic_res.h         | 2 +-
 14 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/drivers/infiniband/hw/ionic/Kconfig b/drivers/infiniband/hw/ionic/Kconfig
index de6f10e9b6e9..3fda9c46ee73 100644
--- a/drivers/infiniband/hw/ionic/Kconfig
+++ b/drivers/infiniband/hw/ionic/Kconfig
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-# Copyright (C) 2018-2025, Advanced Micro Devices, Inc.
+# Copyright (C) 2018-2026, Advanced Micro Devices, Inc.
 
 config INFINIBAND_IONIC
 	tristate "AMD Pensando DSC RDMA/RoCE Support"
diff --git a/drivers/infiniband/hw/ionic/ionic_admin.c b/drivers/infiniband/hw/ionic/ionic_admin.c
index 37e24450d129..6e3cf87025b6 100644
--- a/drivers/infiniband/hw/ionic/ionic_admin.c
+++ b/drivers/infiniband/hw/ionic/ionic_admin.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #include <linux/interrupt.h>
 #include <linux/module.h>
diff --git a/drivers/infiniband/hw/ionic/ionic_controlpath.c b/drivers/infiniband/hw/ionic/ionic_controlpath.c
index 7051a81cca94..850435ec0072 100644
--- a/drivers/infiniband/hw/ionic/ionic_controlpath.c
+++ b/drivers/infiniband/hw/ionic/ionic_controlpath.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #include <linux/module.h>
 #include <linux/printk.h>
diff --git a/drivers/infiniband/hw/ionic/ionic_datapath.c b/drivers/infiniband/hw/ionic/ionic_datapath.c
index aa2944887f23..4ca4ec2eebd4 100644
--- a/drivers/infiniband/hw/ionic/ionic_datapath.c
+++ b/drivers/infiniband/hw/ionic/ionic_datapath.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #include <linux/module.h>
 #include <linux/printk.h>
diff --git a/drivers/infiniband/hw/ionic/ionic_fw.h b/drivers/infiniband/hw/ionic/ionic_fw.h
index adfbb89d856c..0806c148faf2 100644
--- a/drivers/infiniband/hw/ionic/ionic_fw.h
+++ b/drivers/infiniband/hw/ionic/ionic_fw.h
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #ifndef _IONIC_FW_H_
 #define _IONIC_FW_H_
diff --git a/drivers/infiniband/hw/ionic/ionic_hw_stats.c b/drivers/infiniband/hw/ionic/ionic_hw_stats.c
index f72c9837e135..3c845d8b1be1 100644
--- a/drivers/infiniband/hw/ionic/ionic_hw_stats.c
+++ b/drivers/infiniband/hw/ionic/ionic_hw_stats.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #include <linux/dma-mapping.h>
 
diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.c b/drivers/infiniband/hw/ionic/ionic_ibdev.c
index 0382a64839d2..356ad9fe150f 100644
--- a/drivers/infiniband/hw/ionic/ionic_ibdev.c
+++ b/drivers/infiniband/hw/ionic/ionic_ibdev.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #include <linux/module.h>
 #include <linux/printk.h>
diff --git a/drivers/infiniband/hw/ionic/ionic_ibdev.h b/drivers/infiniband/hw/ionic/ionic_ibdev.h
index 63828240d659..7a8e4b59da1c 100644
--- a/drivers/infiniband/hw/ionic/ionic_ibdev.h
+++ b/drivers/infiniband/hw/ionic/ionic_ibdev.h
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #ifndef _IONIC_IBDEV_H_
 #define _IONIC_IBDEV_H_
diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
index f3cd281c3a2f..800555eb47ac 100644
--- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
+++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #include <linux/kernel.h>
 
diff --git a/drivers/infiniband/hw/ionic/ionic_lif_cfg.h b/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
index 20853429f623..18e7c7f13579 100644
--- a/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
+++ b/drivers/infiniband/hw/ionic/ionic_lif_cfg.h
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #ifndef _IONIC_LIF_CFG_H_
 
diff --git a/drivers/infiniband/hw/ionic/ionic_pgtbl.c b/drivers/infiniband/hw/ionic/ionic_pgtbl.c
index e74db73c9246..40ac3b703862 100644
--- a/drivers/infiniband/hw/ionic/ionic_pgtbl.c
+++ b/drivers/infiniband/hw/ionic/ionic_pgtbl.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #include <linux/mman.h>
 #include <linux/dma-mapping.h>
diff --git a/drivers/infiniband/hw/ionic/ionic_queue.c b/drivers/infiniband/hw/ionic/ionic_queue.c
index aa897ed2a412..c8fea41c0363 100644
--- a/drivers/infiniband/hw/ionic/ionic_queue.c
+++ b/drivers/infiniband/hw/ionic/ionic_queue.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #include <linux/dma-mapping.h>
 
diff --git a/drivers/infiniband/hw/ionic/ionic_queue.h b/drivers/infiniband/hw/ionic/ionic_queue.h
index d18020d4cad5..3db1fc664184 100644
--- a/drivers/infiniband/hw/ionic/ionic_queue.h
+++ b/drivers/infiniband/hw/ionic/ionic_queue.h
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #ifndef _IONIC_QUEUE_H_
 #define _IONIC_QUEUE_H_
diff --git a/drivers/infiniband/hw/ionic/ionic_res.h b/drivers/infiniband/hw/ionic/ionic_res.h
index 46c8c584bd9a..bfb9dcf5851b 100644
--- a/drivers/infiniband/hw/ionic/ionic_res.h
+++ b/drivers/infiniband/hw/ionic/ionic_res.h
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-/* Copyright (C) 2018-2025, Advanced Micro Devices, Inc. */
+/* Copyright (C) 2018-2026, Advanced Micro Devices, Inc. */
 
 #ifndef _IONIC_RES_H_
 #define _IONIC_RES_H_
-- 
2.17.1


^ permalink raw reply related

* [PATCH net-next 2/4] net/ionic: Add devlink parameter for RDMA
From: Eric Joyner @ 2026-05-06  4:19 UTC (permalink / raw)
  To: netdev, linux-rdma
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Abhijit Gangurde, Allen Hubbe,
	Jason Gunthorpe, Leon Romanovsky, Eric Joyner
In-Reply-To: <20260506041935.1061-1-eric.joyner@amd.com>

From: Abhijit Gangurde <abhijit.gangurde@amd.com>

Provides a devlink parameter to the ionic Ethernet driver to
enable/disable the RDMA feature.

Signed-off-by: Abhijit Gangurde <abhijit.gangurde@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 .../net/ethernet/pensando/ionic/ionic_aux.c   |  3 +-
 .../ethernet/pensando/ionic/ionic_devlink.c   | 75 +++++++++++++++++++
 2 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic_aux.c b/drivers/net/ethernet/pensando/ionic/ionic_aux.c
index 4f193d0ee87a..689624f19f45 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_aux.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_aux.c
@@ -23,7 +23,8 @@ int ionic_auxbus_register(struct ionic_lif *lif)
 	struct auxiliary_device *aux_dev;
 	int err, id;
 
-	if (!(le64_to_cpu(lif->ionic->ident.lif.capabilities) & IONIC_LIF_CAP_RDMA))
+	if (!IS_ENABLED(CONFIG_INFINIBAND_IONIC) ||
+	    !(le64_to_cpu(lif->ionic->ident.lif.capabilities) & IONIC_LIF_CAP_RDMA))
 		return 0;
 
 	ionic_adev = kzalloc_obj(*ionic_adev);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
index 4ec66a6be073..ab4f6a37c7f8 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_devlink.c
@@ -8,6 +8,7 @@
 #include "ionic_bus.h"
 #include "ionic_lif.h"
 #include "ionic_devlink.h"
+#include "ionic_aux.h"
 
 static int ionic_dl_flash_update(struct devlink *dl,
 				 struct devlink_flash_update_params *params,
@@ -56,6 +57,72 @@ static const struct devlink_ops ionic_dl_ops = {
 	.flash_update	= ionic_dl_flash_update,
 };
 
+static bool is_aux_enabled(struct ionic *ionic)
+{
+	return !!ionic->lif->ionic_adev;
+}
+
+static int ionic_devlink_enable_rdma_get(struct devlink *dl, u32 id,
+					 struct devlink_param_gset_ctx *ctx,
+					 struct netlink_ext_ack *extack)
+{
+	ctx->val.vbool = is_aux_enabled(devlink_priv(dl));
+	return 0;
+}
+
+static int ionic_devlink_enable_rdma_set(struct devlink *dl, u32 id,
+					 struct devlink_param_gset_ctx *ctx,
+					 struct netlink_ext_ack *extack)
+{
+	struct ionic *ionic = devlink_priv(dl);
+	int err = 0;
+
+	if (ctx->val.vbool == is_aux_enabled(ionic))
+		return err;
+
+	if (ctx->val.vbool)
+		err = ionic_auxbus_register(ionic->lif);
+	else
+		ionic_auxbus_unregister(ionic->lif);
+
+	return err;
+}
+
+static int ionic_devlink_enable_rdma_validate(struct devlink *dl, u32 id,
+					      union devlink_param_value val,
+					      struct netlink_ext_ack *extack)
+{
+	struct ionic *ionic = devlink_priv(dl);
+	bool new_state = val.vbool;
+
+	if (new_state &&
+	    !(le64_to_cpu(ionic->ident.lif.capabilities) & IONIC_LIF_CAP_RDMA))
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static const struct devlink_param ionic_dl_rdma_params[] = {
+	DEVLINK_PARAM_GENERIC(ENABLE_RDMA, BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+			      ionic_devlink_enable_rdma_get, ionic_devlink_enable_rdma_set,
+			      ionic_devlink_enable_rdma_validate),
+};
+
+static int ionic_dl_rdma_params_register(struct devlink *dl)
+{
+	if (!IS_ENABLED(CONFIG_INFINIBAND_IONIC))
+		return 0;
+
+	return devlink_params_register(dl, ionic_dl_rdma_params,
+				       ARRAY_SIZE(ionic_dl_rdma_params));
+}
+
+static void ionic_devlink_rdma_params_unregister(struct devlink *dl)
+{
+	devlink_params_unregister(dl, ionic_dl_rdma_params,
+				  ARRAY_SIZE(ionic_dl_rdma_params));
+}
+
 struct ionic *ionic_devlink_alloc(struct device *dev)
 {
 	struct devlink *dl;
@@ -82,9 +149,17 @@ int ionic_devlink_register(struct ionic *ionic)
 
 	attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
 	devlink_port_attrs_set(&ionic->dl_port, &attrs);
+
+	err = ionic_dl_rdma_params_register(dl);
+	if (err) {
+		dev_err(ionic->dev, "ionic_dl_rdma_params_register failed: %d\n", err);
+		return err;
+	}
+
 	err = devlink_port_register(dl, &ionic->dl_port, 0);
 	if (err) {
 		dev_err(ionic->dev, "devlink_port_register failed: %d\n", err);
+		ionic_devlink_rdma_params_unregister(dl);
 		return err;
 	}
 
-- 
2.17.1


^ permalink raw reply related

* [PATCH net-next 0/4] RDMA/net/ionic: Misc updates
From: Eric Joyner @ 2026-05-06  4:19 UTC (permalink / raw)
  To: netdev, linux-rdma
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Abhijit Gangurde, Allen Hubbe,
	Jason Gunthorpe, Leon Romanovsky, Eric Joyner

The big addition in this set is a dedicated debugfs directory under the
main ionic driver's debugfs tree for RDMA-related entries and querying
queue information; a separate patch on top of that adds the ability to
configure DCQCN parameters using debugfs when the firmware is in a mode
that allows it.

Other smaller additions add a devlink parameter to the ionic ethernet
driver for enabling and disabling RDMA, as well as updating the
copyright years for the ionic RDMA driver all in one go.

Abhijit Gangurde (1):
  net/ionic: Add devlink parameter for RDMA

Allen Hubbe (2):
  RDMA/ionic: Add debugfs support
  RDMA/ionic: Add DCQCN parameter configuration via debugfs

Eric Joyner (1):
  RDMA/ionic: Update copyright year to 2026

 drivers/infiniband/hw/ionic/Kconfig           |   2 +-
 drivers/infiniband/hw/ionic/Makefile          |   3 +-
 drivers/infiniband/hw/ionic/ionic_admin.c     |   6 +-
 .../infiniband/hw/ionic/ionic_controlpath.c   |  21 +-
 drivers/infiniband/hw/ionic/ionic_datapath.c  |   2 +-
 drivers/infiniband/hw/ionic/ionic_dcqcn.c     | 629 +++++++++++++++
 drivers/infiniband/hw/ionic/ionic_debugfs.c   | 750 ++++++++++++++++++
 drivers/infiniband/hw/ionic/ionic_fw.h        |  33 +-
 drivers/infiniband/hw/ionic/ionic_hw_stats.c  |   2 +-
 drivers/infiniband/hw/ionic/ionic_ibdev.c     |   9 +-
 drivers/infiniband/hw/ionic/ionic_ibdev.h     |  43 +-
 drivers/infiniband/hw/ionic/ionic_lif_cfg.c   |   6 +-
 drivers/infiniband/hw/ionic/ionic_lif_cfg.h   |   6 +-
 drivers/infiniband/hw/ionic/ionic_pgtbl.c     |   2 +-
 drivers/infiniband/hw/ionic/ionic_profiles.h  |  86 ++
 drivers/infiniband/hw/ionic/ionic_queue.c     |   2 +-
 drivers/infiniband/hw/ionic/ionic_queue.h     |   2 +-
 drivers/infiniband/hw/ionic/ionic_res.h       |   2 +-
 .../net/ethernet/pensando/ionic/ionic_aux.c   |   3 +-
 .../ethernet/pensando/ionic/ionic_devlink.c   |  75 ++
 20 files changed, 1668 insertions(+), 16 deletions(-)
 create mode 100644 drivers/infiniband/hw/ionic/ionic_dcqcn.c
 create mode 100644 drivers/infiniband/hw/ionic/ionic_debugfs.c
 create mode 100644 drivers/infiniband/hw/ionic/ionic_profiles.h


base-commit: 8c699be3dad7bba87cdda485dc099226cfc2f706
-- 
2.17.1


^ permalink raw reply

* [PATCH v1 net 2/2] tcp: Fix imbalanced icsk_accept_queue count.
From: Kuniyuki Iwashima @ 2026-05-06  3:59 UTC (permalink / raw)
  To: Eric Dumazet, Neal Cardwell, David S . Miller, Jakub Kicinski,
	Paolo Abeni
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev,
	Damiano Melotti
In-Reply-To: <20260506035954.1563147-1-kuniyu@google.com>

When TCP socket migration happens in reqsk_timer_handler(),
@sk_listener will be updated with the new listener.

When we call __inet_csk_reqsk_queue_drop(), the listener must
be the one stored in req->rsk_listener.

The cited commit accidentally replaced oreq->rsk_listener with
sk_listener, leading to imbalanced icsk_accept_queue count.

Let's pass the correct listener to __inet_csk_reqsk_queue_drop().

Fixes: e8c526f2bdf1 ("tcp/dccp: Don't use timer_pending() in reqsk_queue_unlink().")
Reported-by: Damiano Melotti <melotti@google.com>
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 net/ipv4/inet_connection_sock.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 971f9db2c586..dbcd37dfdc15 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -1134,7 +1134,7 @@ static void reqsk_timer_handler(struct timer_list *t)
 	}
 
 drop:
-	__inet_csk_reqsk_queue_drop(sk_listener, oreq, true);
+	__inet_csk_reqsk_queue_drop(oreq->rsk_listener, oreq, true);
 	reqsk_put(oreq);
 }
 
-- 
2.54.0.545.g6539524ca2-goog


^ permalink raw reply related

* [PATCH v1 net 1/2] tcp: Fix potential UAF in reqsk_timer_handler().
From: Kuniyuki Iwashima @ 2026-05-06  3:59 UTC (permalink / raw)
  To: Eric Dumazet, Neal Cardwell, David S . Miller, Jakub Kicinski,
	Paolo Abeni
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev,
	Damiano Melotti
In-Reply-To: <20260506035954.1563147-1-kuniyu@google.com>

When TCP socket migration fails at inet_ehash_insert() in
reqsk_timer_handler(), we jump to the no_ownership: label
and free the new reqsk immediately with __reqsk_free().

Thus, we must stop the new reqsk's timer before jumping to the
label, but the timer might be missed since the cited commit,
resulting in UAF.

As we are in the original reqsk's timer context, we can safely
call timer_delete_sync() for the new reqsk.

Let's pass false to __inet_csk_reqsk_queue_drop() to stop
the new reqsk's timer.

Fixes: 83fccfc3940c ("inet: fix potential deadlock in reqsk_queue_unlink()")
Reported-by: Damiano Melotti <melotti@google.com>
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
In case Sashiko asks

  "What happens if TFO reqsk is migrated in reqsk_timer_handler() ?"

, the answer is

  "TFO does not use reqsk_timer_handler()."
---
 net/ipv4/inet_connection_sock.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 928654c34156..971f9db2c586 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -1108,7 +1108,7 @@ static void reqsk_timer_handler(struct timer_list *t)
 
 		if (!inet_ehash_insert(req_to_sk(nreq), req_to_sk(oreq), NULL)) {
 			/* delete timer */
-			__inet_csk_reqsk_queue_drop(sk_listener, nreq, true);
+			__inet_csk_reqsk_queue_drop(sk_listener, nreq, false);
 			goto no_ownership;
 		}
 
-- 
2.54.0.545.g6539524ca2-goog


^ permalink raw reply related

* [PATCH v1 net 0/2] tcp: Two fixes for socket migration in reqsk_timer_handler().
From: Kuniyuki Iwashima @ 2026-05-06  3:59 UTC (permalink / raw)
  To: Eric Dumazet, Neal Cardwell, David S . Miller, Jakub Kicinski,
	Paolo Abeni
  Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev

The series fixes two bugs in the error path of socket migration
in reqsk_timer_handler().

Patch 1 fixes a potential UAF in reqsk_timer_handler().

Patch 2 fixes imbalanced icsk_accept_queue count.


Kuniyuki Iwashima (2):
  tcp: Fix potential UAF in reqsk_timer_handler().
  tcp: Fix imbalanced icsk_accept_queue count.

 net/ipv4/inet_connection_sock.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

-- 
2.54.0.545.g6539524ca2-goog


^ permalink raw reply

* [PATCH net v2 5/5] ionic: fix completion descriptor access with 2x desc size
From: Eric Joyner @ 2026-05-06  3:57 UTC (permalink / raw)
  To: netdev
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Eric Joyner, Prabu Thayalan
In-Reply-To: <20260506035706.12373-1-eric.joyner@amd.com>

From: Prabu Thayalan <prabu.ponrajthayalan@amd.com>

The old ionic_rx_service() and ionic_tx_service() used array
indexing to access completion descriptors:

    comp = &((struct ionic_rxq_comp *)cq->base)[cq->tail_idx];

This assumes the stride is sizeof(struct ionic_rxq_comp) = 16 bytes.
However, when the IONIC_Q_F_2X_CQ_DESC flag is set, the actual
completion descriptor size is 32 bytes (2 * sizeof(comp)), and the
completion itself is located at the end of that 32-byte slot. Array
indexing with a 16-byte stride would access the wrong offset.

Use pointer arithmetic that accounts for the actual descriptor size
from cq->desc_size:

    comp = cq->base +
           cq->desc_size * cq->tail_idx +
           cq->desc_size - sizeof(*comp);

This correctly calculates the completion location regardless of
descriptor size. For the common case where desc_size equals
sizeof(*comp), use array indexing in a likely() fast path to avoid
performance regression.

Fixes: 0ec9f6669a7d ("ionic: add handling of larger descriptors")
Signed-off-by: Prabu Thayalan <prabu.ponrajthayalan@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 .../net/ethernet/pensando/ionic/ionic_txrx.c  | 27 ++++++++++---------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
index 301ebee2fdc5..27a113d63d28 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
@@ -701,11 +701,7 @@ static void ionic_rx_clean(struct ionic_queue *q,
 		__le64 *cq_desc_hwstamp;
 		u64 hwstamp;
 
-		cq_desc_hwstamp =
-			(void *)comp +
-			qcq->cq.desc_size -
-			sizeof(struct ionic_rxq_comp) -
-			IONIC_HWSTAMP_CQ_NEGOFFSET;
+		cq_desc_hwstamp = (void *)comp - IONIC_HWSTAMP_CQ_NEGOFFSET;
 
 		hwstamp = le64_to_cpu(*cq_desc_hwstamp);
 
@@ -729,7 +725,12 @@ static bool __ionic_rx_service(struct ionic_cq *cq, struct bpf_prog *xdp_prog)
 	struct ionic_queue *q = cq->bound_q;
 	struct ionic_rxq_comp *comp;
 
-	comp = &((struct ionic_rxq_comp *)cq->base)[cq->tail_idx];
+	if (likely(cq->desc_size == sizeof(*comp)))
+		comp = &((struct ionic_rxq_comp *)cq->base)[cq->tail_idx];
+	else
+		comp = cq->base +
+		       cq->desc_size * cq->tail_idx +
+		       cq->desc_size - sizeof(*comp);
 
 	if (!color_match(comp->pkt_type_color, cq->done_color))
 		return false;
@@ -1180,7 +1181,6 @@ static void ionic_tx_clean(struct ionic_queue *q,
 			   bool in_napi)
 {
 	struct ionic_tx_stats *stats = q_to_tx_stats(q);
-	struct ionic_qcq *qcq = q_to_qcq(q);
 	struct sk_buff *skb;
 
 	if (desc_info->xdpf) {
@@ -1205,11 +1205,7 @@ static void ionic_tx_clean(struct ionic_queue *q,
 			__le64 *cq_desc_hwstamp;
 			u64 hwstamp;
 
-			cq_desc_hwstamp =
-				(void *)comp +
-				qcq->cq.desc_size -
-				sizeof(struct ionic_txq_comp) -
-				IONIC_HWSTAMP_CQ_NEGOFFSET;
+			cq_desc_hwstamp = (void *)comp - IONIC_HWSTAMP_CQ_NEGOFFSET;
 
 			hwstamp = le64_to_cpu(*cq_desc_hwstamp);
 
@@ -1244,7 +1240,12 @@ static bool ionic_tx_service(struct ionic_cq *cq,
 	unsigned int pkts = 0;
 	u16 index;
 
-	comp = &((struct ionic_txq_comp *)cq->base)[cq->tail_idx];
+	if (likely(cq->desc_size == sizeof(*comp)))
+		comp = &((struct ionic_txq_comp *)cq->base)[cq->tail_idx];
+	else
+		comp = cq->base +
+		       cq->desc_size * cq->tail_idx +
+		       cq->desc_size - sizeof(*comp);
 
 	if (!color_match(comp->color, cq->done_color))
 		return false;
-- 
2.17.1


^ permalink raw reply related

* [PATCH net v2 4/5] ionic: Fix check in ionic_get_link_ext_stats
From: Eric Joyner @ 2026-05-06  3:57 UTC (permalink / raw)
  To: netdev
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260506035706.12373-1-eric.joyner@amd.com>

From: Brett Creeley <brett.creeley@amd.com>

The current check will fail if SR-IOV is not initialized for the
physical function; this is because is_physfn is 0 if sriov_init() isn't
run or fails. Change the check that prevents getting the link down count
to use is_virtfn instead so that VFs don't get this functionality, which
was the original intent.

Fixes: 132b4ebfa090 ("ionic: add support for ethtool extended stat link_down_count")
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 drivers/net/ethernet/pensando/ionic/ionic_ethtool.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
index 78a802eb159f..296f831a514d 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c
@@ -116,8 +116,10 @@ static void ionic_get_link_ext_stats(struct net_device *netdev,
 {
 	struct ionic_lif *lif = netdev_priv(netdev);
 
-	if (lif->ionic->pdev->is_physfn)
-		stats->link_down_events = lif->link_down_count;
+	if (lif->ionic->pdev->is_virtfn)
+		return;
+
+	stats->link_down_events = lif->link_down_count;
 }
 
 static int ionic_get_link_ksettings(struct net_device *netdev,
-- 
2.17.1


^ permalink raw reply related

* [PATCH net v2 3/5] ionic: Fix unexpected dev_cmd failures
From: Eric Joyner @ 2026-05-06  3:57 UTC (permalink / raw)
  To: netdev
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260506035706.12373-1-eric.joyner@amd.com>

From: Brett Creeley <brett.creeley@amd.com>

When polling for a devcmd completion it's possible for the driver to
timeout the command even if the dev_cmd has completed. This can cause
unexpected failures and device probe to fail. Fix this by reading
the dev_cmd's done bit one last time after breaking out of the poll
loop.

Fixes: fbfb8031533c ("ionic: Add hardware init and device commands")
Suggested-by: Neel Patel <neel.patel@amd.com>
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 drivers/net/ethernet/pensando/ionic/ionic_main.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index 91f89b9ff807..810cef0fec93 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -541,6 +541,10 @@ static int __ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds,
 	}
 	duration = jiffies - start_time;
 
+	/* one final check to prevent unexpected timeout */
+	if (!done)
+		done = ionic_dev_cmd_done(idev);
+
 	dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n",
 		ionic_opcode_to_str(opcode), opcode,
 		done, duration / HZ, duration);
-- 
2.17.1


^ permalink raw reply related

* [PATCH net v2 2/5] ionic: Handle failures from ionic_reset() when relevant
From: Eric Joyner @ 2026-05-06  3:57 UTC (permalink / raw)
  To: netdev
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260506035706.12373-1-eric.joyner@amd.com>

From: Brett Creeley <brett.creeley@amd.com>

If ionic_reset() fails, then the device either wasn't ready to be
communicated with, the firmware is down, and/or the devcmd path is
already torn down. For teardown/remove cases we can ignore the
result of ionic_reset(). However, for any setup cases, we should
take the result seriously.

Note, older firmware always returns success for IONIC_CMD_RESET, so this
change will not break those. However, newer firmware may return failure
if the IONIC_CMD_RESET dev cmd fails.

Fixes: 8097a2f3d21a ("ionic: Reset LIF device while restarting LIF")
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 drivers/net/ethernet/pensando/ionic/ionic_lif.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 637e635bbf03..db4bbeda0b29 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -3473,7 +3473,9 @@ static void ionic_lif_handle_fw_up(struct ionic_lif *lif)
 	 * just need to reanimate it.
 	 */
 	ionic_init_devinfo(ionic);
-	ionic_reset(ionic);
+	err = ionic_reset(ionic);
+	if (err)
+		goto err_out;
 	err = ionic_identify(ionic);
 	if (err)
 		goto err_out;
-- 
2.17.1


^ permalink raw reply related

* [PATCH net v2 1/5] ionic: Allow the first devcmd to trigger deferred probe
From: Eric Joyner @ 2026-05-06  3:57 UTC (permalink / raw)
  To: netdev
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Eric Joyner
In-Reply-To: <20260506035706.12373-1-eric.joyner@amd.com>

From: Brett Creeley <brett.creeley@amd.com>

There's a chance the register signature value is set before the
firmware is ready to respond to the driver. This doesn't mean the
device isn't there, but just means it's not yet ready. If the first
devcmd fails, then return -EPROBE_DEFER so the device can be probed
at a later time. As part of this make sure the reset devcmd, which
is the first devcmd, is not so alarming when it fails by printing
an information message instead of the standard devcmd failure
messages.

Note, that the ionic_reset() function could be reworked a bit to
keep retrying if err is -EAGAIN or -ETIMEDOUT, but for now I chose
not to change this as 5 seconds should be enough for the firmware
to come up after the devcmd registers are initialized.

Fixes: fbfb8031533c ("ionic: Add hardware init and device commands")
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
---
 drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c | 8 ++++++--
 drivers/net/ethernet/pensando/ionic/ionic_main.c    | 8 ++++++--
 2 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
index 05f19489ec5c..59ce35404e53 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c
@@ -260,7 +260,8 @@ static int ionic_setup_one(struct ionic *ionic)
 	/* Configure the device */
 	err = ionic_setup(ionic);
 	if (err) {
-		dev_err(dev, "Cannot setup device: %d, aborting\n", err);
+		if (err != -EPROBE_DEFER)
+			dev_err(dev, "Cannot setup device: %d, aborting\n", err);
 		goto err_out_clear_pci;
 	}
 	pci_set_master(pdev);
@@ -335,8 +336,11 @@ static int ionic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 #endif
 
 	err = ionic_setup_one(ionic);
-	if (err)
+	if (err) {
+		if (err == -EPROBE_DEFER)
+			dev_info(dev, "Device isn't ready, deferring probe\n");
 		goto err_out;
+	}
 
 	/* Allocate and init the LIF */
 	err = ionic_lif_size(ionic);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index 3c5200e2fdb7..91f89b9ff807 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -603,7 +603,11 @@ int ionic_setup(struct ionic *ionic)
 	err = ionic_dev_setup(ionic);
 	if (err)
 		return err;
-	ionic_reset(ionic);
+
+	err = ionic_reset(ionic);
+	/* firmware may not be ready to respond yet */
+	if (err == -EAGAIN || err == -ETIMEDOUT)
+		return -EPROBE_DEFER;
 
 	return 0;
 }
@@ -687,7 +691,7 @@ int ionic_reset(struct ionic *ionic)
 
 	mutex_lock(&ionic->dev_cmd_lock);
 	ionic_dev_cmd_reset(idev);
-	err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
+	err = ionic_dev_cmd_wait_nomsg(ionic, DEVCMD_TIMEOUT);
 	mutex_unlock(&ionic->dev_cmd_lock);
 
 	return err;
-- 
2.17.1


^ permalink raw reply related

* [PATCH net v2 0/5] ionic: Various bugfixes
From: Eric Joyner @ 2026-05-06  3:57 UTC (permalink / raw)
  To: netdev
  Cc: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Eric Joyner

Brett's patches mostly fix issues around communication and configuration
between the firmware and the driver. The one that fixes the check in
ionic_get_link_ext_stats actually makes sure the stat is reported
correctly and is a precursor change to have that link_down_count be
derived from the FW and not the driver. These are all fixes to issues
that we've observed internally.

Prabu's patch is critical for enabling PTP/HW timestamping to work
correctly when the completion queue entries are double-sized; the offset
into the descriptor to read the HW timestamp is incorrect when in that
mode.

---
v2: Drop Admin Queue fix patches from v1 pending further
examination, and add more context to the cover letter

Brett Creeley (4):
  ionic: Allow the first devcmd to trigger deferred probe
  ionic: Handle failures from ionic_reset() when relevant
  ionic: Fix unexpected dev_cmd failures
  ionic: Fix check in ionic_get_link_ext_stats

Prabu Thayalan (1):
  ionic: fix completion descriptor access with 2x desc size

 .../ethernet/pensando/ionic/ionic_bus_pci.c   |  8 ++++--
 .../ethernet/pensando/ionic/ionic_ethtool.c   |  6 +++--
 .../net/ethernet/pensando/ionic/ionic_lif.c   |  4 ++-
 .../net/ethernet/pensando/ionic/ionic_main.c  | 12 +++++++--
 .../net/ethernet/pensando/ionic/ionic_txrx.c  | 27 ++++++++++---------
 5 files changed, 37 insertions(+), 20 deletions(-)


base-commit: e728258debd553c95d2e70f9cd97c9fde27c7130
-- 
2.17.1


^ permalink raw reply

* Re: CL37 autonegotiation not working on amd-xgbe
From: Shyam Sundar S K @ 2026-05-06  3:55 UTC (permalink / raw)
  To: Jakub Kicinski, PrashanthKumar.K.R, Madhu.Banavara
  Cc: Patrick Oppenlander, netdev
In-Reply-To: <20260427162839.350135c8@kernel.org>

+ Prashanth

On 4/28/2026 04:58, Jakub Kicinski wrote:
> On Thu, 23 Apr 2026 10:18:49 +1000 Patrick Oppenlander wrote:
>> A recent change [1] stopped CL37 autonegotiation from working on
>> amd-xgbe hardware.
> 
> Hi Raju! Are you looking into this report?

Dropping Raju as he is no longer with AMD.

Thanks for the bug report. Prashanth will maintain this driver going
forward.

Prashanth, can you address this bug? Also, please update the
MAINTAINERS list by dropping Raju's name.

Thanks,
Shyam


^ permalink raw reply

* [PATCH net-next v3 2/2] selftests: net: add tests for filtered dumps of page pool
From: Jakub Kicinski @ 2026-05-06  3:48 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, Jakub Kicinski,
	shuah, linux-kselftest
In-Reply-To: <20260506034821.1710113-1-kuba@kernel.org>

Add tests for page pool dumps of a specific ifindex.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v2:
 - add CONFIG_PAGE_POOL_STATS=y to the config
v1: https://lore.kernel.org/20260319035649.2396137-2-kuba@kernel.org
---
CC: shuah@kernel.org
CC: linux-kselftest@vger.kernel.org
---
 tools/testing/selftests/net/config       |   1 +
 tools/testing/selftests/net/nl_netdev.py | 119 ++++++++++++++++++++++-
 2 files changed, 118 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config
index 94d722770420..d07c5ac5cab7 100644
--- a/tools/testing/selftests/net/config
+++ b/tools/testing/selftests/net/config
@@ -117,6 +117,7 @@ CONFIG_OPENVSWITCH=m
 CONFIG_OPENVSWITCH_GENEVE=m
 CONFIG_OPENVSWITCH_GRE=m
 CONFIG_OPENVSWITCH_VXLAN=m
+CONFIG_PAGE_POOL_STATS=y
 CONFIG_PROC_SYSCTL=y
 CONFIG_PSAMPLE=m
 CONFIG_RPS=y
diff --git a/tools/testing/selftests/net/nl_netdev.py b/tools/testing/selftests/net/nl_netdev.py
index eff55c64a012..ceb44c8e1fec 100755
--- a/tools/testing/selftests/net/nl_netdev.py
+++ b/tools/testing/selftests/net/nl_netdev.py
@@ -9,7 +9,7 @@ import errno
 from os import system
 from lib.py import ksft_run, ksft_exit
 from lib.py import ksft_eq, ksft_ge, ksft_ne, ksft_raises, ksft_busy_wait
-from lib.py import NetdevFamily, NetdevSimDev, NlError, ip
+from lib.py import NetdevFamily, NetdevSimDev, NlError, defer, ip
 
 
 def empty_check(nf) -> None:
@@ -255,6 +255,117 @@ from lib.py import NetdevFamily, NetdevSimDev, NlError, ip
         nsim.dfs_write("pp_hold", "y")
 
 
+def page_pool_dump_ifindex(nf) -> None:
+    """Test page pool dump filtering by ifindex."""
+    nsimdev1 = NetdevSimDev(queue_count=3)
+    rm_nsim1 = defer(nsimdev1.remove)
+    nsimdev2 = NetdevSimDev(queue_count=5)
+    defer(nsimdev2.remove)
+
+    nsim1 = nsimdev1.nsims[0]
+    nsim2 = nsimdev2.nsims[0]
+
+    ip(f"link set dev {nsim1.ifname} up")
+    ip(f"link set dev {nsim2.ifname} up")
+
+    # Unfiltered dump should have pools from both devices
+    all_pp = nf.page_pool_get({}, dump=True)
+    pp1_all = [pp for pp in all_pp
+               if pp.get("ifindex") == nsim1.ifindex]
+    pp2_all = [pp for pp in all_pp
+               if pp.get("ifindex") == nsim2.ifindex]
+    ksft_ge(len(pp1_all), 1)
+    ksft_ge(len(pp2_all), 1)
+
+    # Filtered dump should only return pools for that device
+    pp1_flt = nf.page_pool_get({'ifindex': nsim1.ifindex}, dump=True)
+    ksft_eq(pp1_flt, pp1_all)
+
+    pp2_flt = nf.page_pool_get({'ifindex': nsim2.ifindex}, dump=True)
+    ksft_eq(pp2_flt, pp2_all)
+
+    # Non-existent ifindex should return empty dump
+    pp_none = nf.page_pool_get({'ifindex': 12345678}, dump=True)
+    ksft_eq(len(pp_none), 0)
+
+    # Device down - no pools for that ifindex
+    ip(f"link set dev {nsim1.ifname} down")
+    pp1_down = nf.page_pool_get({'ifindex': nsim1.ifindex}, dump=True)
+    ksft_eq(len(pp1_down), 0)
+
+    # Remove device, dump by its old ifindex should return empty
+    old_ifindex = nsim1.ifindex
+    rm_nsim1.exec()
+    pp1_gone = nf.page_pool_get({'ifindex': old_ifindex}, dump=True)
+    ksft_eq(len(pp1_gone), 0)
+
+
+def page_pool_ifindex_leak_check(nf) -> None:
+    """Test that zombie page pools don't show up under the original ifindex."""
+    nsimdev = NetdevSimDev()
+    rm_nsim = defer(nsimdev.remove)
+    nsim = nsimdev.nsims[0]
+
+    ip(f"link set dev {nsim.ifname} up")
+    nsim.dfs_write("pp_hold", "y")
+
+    pp_up = nf.page_pool_get({'ifindex': nsim.ifindex}, dump=True)
+    ksft_ge(len(pp_up), 1)
+
+    # Remove device with leaked page - pool becomes zombie (orphaned to lo)
+    old_ifindex = nsim.ifindex
+    rm_nsim.exec()
+
+    # Zombie pool should NOT appear under the original device
+    pp_down = nf.page_pool_get({'ifindex': old_ifindex}, dump=True)
+    ksft_eq(len(pp_down), 0)
+
+    # But it should appear in an unfiltered dump (under loopback)
+    pp_all = nf.page_pool_get({}, dump=True)
+    orphans = [pp for pp in pp_all
+               if "detach-time" in pp and "ifindex" not in pp]
+    ksft_ge(len(orphans), 1)
+
+
+def page_pool_stats_ifindex_check(nf) -> None:
+    """Test page pool stats dump filtering by ifindex."""
+    nsimdev1 = NetdevSimDev(queue_count=3)
+    defer(nsimdev1.remove)
+    nsimdev2 = NetdevSimDev(queue_count=5)
+    defer(nsimdev2.remove)
+
+    nsim1 = nsimdev1.nsims[0]
+    nsim2 = nsimdev2.nsims[0]
+
+    ip(f"link set dev {nsim1.ifname} up")
+    ip(f"link set dev {nsim2.ifname} up")
+
+    # Unfiltered stats dump
+    all_stats = nf.page_pool_stats_get({}, dump=True)
+    s1_all = [s for s in all_stats
+              if s.get("info", {}).get("ifindex") == nsim1.ifindex]
+    s2_all = [s for s in all_stats
+              if s.get("info", {}).get("ifindex") == nsim2.ifindex]
+    ksft_ge(len(s1_all), 1)
+    ksft_ge(len(s2_all), 1)
+
+    # Filtered stats dump
+    s1_flt = nf.page_pool_stats_get({'info': {'ifindex': nsim1.ifindex}},
+                                    dump=True)
+    ksft_eq(s1_flt, s1_all)
+
+    # Non-existent ifindex should return empty
+    s_none = nf.page_pool_stats_get({'info': {'ifindex': 12345678}}, dump=True)
+    ksft_eq(len(s_none), 0)
+
+    # info.id should be rejected for stats dump
+    with ksft_raises(NlError) as cm:
+        nf.page_pool_stats_get({'info': {'id': s1_all[0]['info']['id']}},
+                               dump=True)
+    ksft_eq(cm.exception.nl_msg.error, -errno.EINVAL)
+    ksft_eq(cm.exception.nl_msg.extack['bad-attr'], '.info.id')
+
+
 def main() -> None:
     """ Ksft boiler plate main """
     nf = NetdevFamily()
@@ -265,7 +376,11 @@ from lib.py import NetdevFamily, NetdevSimDev, NlError, ip
               napi_set_threaded,
               dev_set_threaded,
               nsim_rxq_reset_down,
-              page_pool_check],
+              page_pool_check,
+              page_pool_dump_ifindex,
+              page_pool_ifindex_leak_check,
+              page_pool_stats_ifindex_check
+              ],
              args=(nf, ))
     ksft_exit()
 
-- 
2.54.0


^ permalink raw reply related

* [PATCH net-next v3 1/2] net: page_pool: support dumping pps of a specific ifindex via Netlink
From: Jakub Kicinski @ 2026-05-06  3:48 UTC (permalink / raw)
  To: davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, Jakub Kicinski,
	donald.hunter, matttbe, chuck.lever, daniel, skhawaja, sdf, hawk

NIPA tries to make sure that HW tests don't modify system state.
It saves the state of page pools, too. Now that I write this commit
message I realize that this is impractical since page pool IDs and
state will get legitimately changed by the tests. But I already
spent a couple of hours implementing the filtering, so..

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
v3:
 - regen with latest YNL
v2: https://lore.kernel.org/20260504214336.613107-1-kuba@kernel.org
 - adjust ynltool to pass empty req pointer now
v1: https://lore.kernel.org/20260319035649.2396137-1-kuba@kernel.org
---
CC: donald.hunter@gmail.com
CC: matttbe@kernel.org
CC: chuck.lever@oracle.com
CC: daniel@iogearbox.net
CC: skhawaja@google.com
CC: sdf@fomichev.me
CC: hawk@kernel.org
---
 Documentation/netlink/specs/netdev.yaml |  6 ++++
 net/core/netdev-genl-gen.c              | 38 ++++++++++++++------
 net/core/page_pool_user.c               | 47 +++++++++++++++++++++++--
 tools/net/ynl/ynltool/page-pool.c       |  6 ++--
 4 files changed, 82 insertions(+), 15 deletions(-)

diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml
index b93beb247a11..a1f4c5a561e9 100644
--- a/Documentation/netlink/specs/netdev.yaml
+++ b/Documentation/netlink/specs/netdev.yaml
@@ -649,6 +649,9 @@ doc: >-
             - dmabuf
             - io-uring
       dump:
+        request:
+          attributes:
+            - ifindex
         reply: *pp-reply
       config-cond: page-pool
     -
@@ -692,6 +695,9 @@ doc: >-
             - recycle-ring-full
             - recycle-released-refcnt
       dump:
+        request:
+          attributes:
+            - info
         reply: *pp-stats-reply
       config-cond: page-pool-stats
     -
diff --git a/net/core/netdev-genl-gen.c b/net/core/netdev-genl-gen.c
index 81aecb5d3bc5..c7e138bfe345 100644
--- a/net/core/netdev-genl-gen.c
+++ b/net/core/netdev-genl-gen.c
@@ -51,14 +51,28 @@ static const struct nla_policy netdev_dev_get_nl_policy[NETDEV_A_DEV_IFINDEX + 1
 
 /* NETDEV_CMD_PAGE_POOL_GET - do */
 #ifdef CONFIG_PAGE_POOL
-static const struct nla_policy netdev_page_pool_get_nl_policy[NETDEV_A_PAGE_POOL_ID + 1] = {
+static const struct nla_policy netdev_page_pool_get_do_nl_policy[NETDEV_A_PAGE_POOL_ID + 1] = {
 	[NETDEV_A_PAGE_POOL_ID] = NLA_POLICY_FULL_RANGE(NLA_UINT, &netdev_a_page_pool_id_range),
 };
 #endif /* CONFIG_PAGE_POOL */
 
+/* NETDEV_CMD_PAGE_POOL_GET - dump */
+#ifdef CONFIG_PAGE_POOL
+static const struct nla_policy netdev_page_pool_get_dump_nl_policy[NETDEV_A_PAGE_POOL_IFINDEX + 1] = {
+	[NETDEV_A_PAGE_POOL_IFINDEX] = NLA_POLICY_FULL_RANGE(NLA_U32, &netdev_a_page_pool_ifindex_range),
+};
+#endif /* CONFIG_PAGE_POOL */
+
 /* NETDEV_CMD_PAGE_POOL_STATS_GET - do */
 #ifdef CONFIG_PAGE_POOL_STATS
-static const struct nla_policy netdev_page_pool_stats_get_nl_policy[NETDEV_A_PAGE_POOL_STATS_INFO + 1] = {
+static const struct nla_policy netdev_page_pool_stats_get_do_nl_policy[NETDEV_A_PAGE_POOL_STATS_INFO + 1] = {
+	[NETDEV_A_PAGE_POOL_STATS_INFO] = NLA_POLICY_NESTED(netdev_page_pool_info_nl_policy),
+};
+#endif /* CONFIG_PAGE_POOL_STATS */
+
+/* NETDEV_CMD_PAGE_POOL_STATS_GET - dump */
+#ifdef CONFIG_PAGE_POOL_STATS
+static const struct nla_policy netdev_page_pool_stats_get_dump_nl_policy[NETDEV_A_PAGE_POOL_STATS_INFO + 1] = {
 	[NETDEV_A_PAGE_POOL_STATS_INFO] = NLA_POLICY_NESTED(netdev_page_pool_info_nl_policy),
 };
 #endif /* CONFIG_PAGE_POOL_STATS */
@@ -138,28 +152,32 @@ static const struct genl_split_ops netdev_nl_ops[] = {
 	{
 		.cmd		= NETDEV_CMD_PAGE_POOL_GET,
 		.doit		= netdev_nl_page_pool_get_doit,
-		.policy		= netdev_page_pool_get_nl_policy,
+		.policy		= netdev_page_pool_get_do_nl_policy,
 		.maxattr	= NETDEV_A_PAGE_POOL_ID,
 		.flags		= GENL_CMD_CAP_DO,
 	},
 	{
-		.cmd	= NETDEV_CMD_PAGE_POOL_GET,
-		.dumpit	= netdev_nl_page_pool_get_dumpit,
-		.flags	= GENL_CMD_CAP_DUMP,
+		.cmd		= NETDEV_CMD_PAGE_POOL_GET,
+		.dumpit		= netdev_nl_page_pool_get_dumpit,
+		.policy		= netdev_page_pool_get_dump_nl_policy,
+		.maxattr	= NETDEV_A_PAGE_POOL_IFINDEX,
+		.flags		= GENL_CMD_CAP_DUMP,
 	},
 #endif /* CONFIG_PAGE_POOL */
 #ifdef CONFIG_PAGE_POOL_STATS
 	{
 		.cmd		= NETDEV_CMD_PAGE_POOL_STATS_GET,
 		.doit		= netdev_nl_page_pool_stats_get_doit,
-		.policy		= netdev_page_pool_stats_get_nl_policy,
+		.policy		= netdev_page_pool_stats_get_do_nl_policy,
 		.maxattr	= NETDEV_A_PAGE_POOL_STATS_INFO,
 		.flags		= GENL_CMD_CAP_DO,
 	},
 	{
-		.cmd	= NETDEV_CMD_PAGE_POOL_STATS_GET,
-		.dumpit	= netdev_nl_page_pool_stats_get_dumpit,
-		.flags	= GENL_CMD_CAP_DUMP,
+		.cmd		= NETDEV_CMD_PAGE_POOL_STATS_GET,
+		.dumpit		= netdev_nl_page_pool_stats_get_dumpit,
+		.policy		= netdev_page_pool_stats_get_dump_nl_policy,
+		.maxattr	= NETDEV_A_PAGE_POOL_STATS_INFO,
+		.flags		= GENL_CMD_CAP_DUMP,
 	},
 #endif /* CONFIG_PAGE_POOL_STATS */
 	{
diff --git a/net/core/page_pool_user.c b/net/core/page_pool_user.c
index ee5060d8eec0..01509d1b3cba 100644
--- a/net/core/page_pool_user.c
+++ b/net/core/page_pool_user.c
@@ -79,7 +79,7 @@ struct page_pool_dump_cb {
 
 static int
 netdev_nl_page_pool_get_dump(struct sk_buff *skb, struct netlink_callback *cb,
-			     pp_nl_fill_cb fill)
+			     pp_nl_fill_cb fill, struct nlattr *ifindex_attr)
 {
 	struct page_pool_dump_cb *state = (void *)cb->ctx;
 	const struct genl_info *info = genl_info_dump(cb);
@@ -88,9 +88,17 @@ netdev_nl_page_pool_get_dump(struct sk_buff *skb, struct netlink_callback *cb,
 	struct page_pool *pool;
 	int err = 0;
 
+	if (ifindex_attr)
+		state->ifindex = nla_get_u32(ifindex_attr);
+
 	rtnl_lock();
 	mutex_lock(&page_pools_lock);
 	for_each_netdev_dump(net, netdev, state->ifindex) {
+		/* Either the provided ifindex doesn't exist or done dumping */
+		if (ifindex_attr &&
+		    netdev->ifindex != nla_get_u32(ifindex_attr))
+			break;
+
 		hlist_for_each_entry(pool, &netdev->page_pools, user.list) {
 			if (state->pp_id && state->pp_id < pool->user.id)
 				continue;
@@ -206,10 +214,40 @@ int netdev_nl_page_pool_stats_get_doit(struct sk_buff *skb,
 	return netdev_nl_page_pool_get_do(info, id, page_pool_nl_stats_fill);
 }
 
+static const struct netlink_range_validation page_pool_ifindex_range = {
+	.min	= 1ULL,
+	.max	= S32_MAX,
+};
+
+static const struct nla_policy
+page_pool_stat_info_policy[NETDEV_A_PAGE_POOL_IFINDEX + 1] = {
+	[NETDEV_A_PAGE_POOL_IFINDEX] =
+		NLA_POLICY_FULL_RANGE(NLA_U32, &page_pool_ifindex_range),
+};
+
 int netdev_nl_page_pool_stats_get_dumpit(struct sk_buff *skb,
 					 struct netlink_callback *cb)
 {
-	return netdev_nl_page_pool_get_dump(skb, cb, page_pool_nl_stats_fill);
+	struct nlattr *tb[ARRAY_SIZE(page_pool_stat_info_policy)];
+	const struct genl_info *info = genl_info_dump(cb);
+	struct nlattr *ifindex_attr = NULL;
+
+	if (info->attrs[NETDEV_A_PAGE_POOL_STATS_INFO]) {
+		struct nlattr *nest;
+		int err;
+
+		nest = info->attrs[NETDEV_A_PAGE_POOL_STATS_INFO];
+		err = nla_parse_nested(tb, ARRAY_SIZE(tb) - 1, nest,
+				       page_pool_stat_info_policy,
+				       info->extack);
+		if (err)
+			return err;
+
+		ifindex_attr = tb[NETDEV_A_PAGE_POOL_IFINDEX];
+	}
+
+	return netdev_nl_page_pool_get_dump(skb, cb, page_pool_nl_stats_fill,
+					    ifindex_attr);
 }
 
 static int
@@ -305,7 +343,10 @@ int netdev_nl_page_pool_get_doit(struct sk_buff *skb, struct genl_info *info)
 int netdev_nl_page_pool_get_dumpit(struct sk_buff *skb,
 				   struct netlink_callback *cb)
 {
-	return netdev_nl_page_pool_get_dump(skb, cb, page_pool_nl_fill);
+	const struct genl_info *info = genl_info_dump(cb);
+
+	return netdev_nl_page_pool_get_dump(skb, cb, page_pool_nl_fill,
+					    info->attrs[NETDEV_A_PAGE_POOL_IFINDEX]);
 }
 
 int page_pool_list(struct page_pool *pool)
diff --git a/tools/net/ynl/ynltool/page-pool.c b/tools/net/ynl/ynltool/page-pool.c
index 4b24492abab7..9487eda6b3aa 100644
--- a/tools/net/ynl/ynltool/page-pool.c
+++ b/tools/net/ynl/ynltool/page-pool.c
@@ -327,7 +327,9 @@ static void aggregate_device_stats(struct pp_stats_array *a,
 
 static int do_stats(int argc, char **argv)
 {
+	struct netdev_page_pool_stats_get_req_dump pp_stat_req = {};
 	struct netdev_page_pool_stats_get_list *pp_stats;
+	struct netdev_page_pool_get_req_dump pp_req = {};
 	struct netdev_page_pool_get_list *pools;
 	enum {
 		GROUP_BY_DEVICE,
@@ -374,14 +376,14 @@ static int do_stats(int argc, char **argv)
 		return -1;
 	}
 
-	pools = netdev_page_pool_get_dump(ys);
+	pools = netdev_page_pool_get_dump(ys, &pp_req);
 	if (!pools) {
 		p_err("failed to get page pools: %s", ys->err.msg);
 		ret = -1;
 		goto exit_close;
 	}
 
-	pp_stats = netdev_page_pool_stats_get_dump(ys);
+	pp_stats = netdev_page_pool_stats_get_dump(ys, &pp_stat_req);
 	if (!pp_stats) {
 		p_err("failed to get page pool stats: %s", ys->err.msg);
 		ret = -1;
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH net-next 2/5] ionic: Report "link_down_events_phy" in ethtool statistics
From: Eric Joyner @ 2026-05-06  3:41 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
	Paolo Abeni
In-Reply-To: <20260505162135.5fa39358@kernel.org>

On 5/5/2026 4:21 PM, Jakub Kicinski wrote:
> Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
> 
> 
> On Tue, 5 May 2026 12:53:26 -0700 Eric Joyner wrote:
>>> We have a standard stat for this:
>>>
>>> struct ethtool_link_ext_stats {
>>>         /* Custom Linux statistic for PHY level link down events.
>>>          * In a simpler world it should be equal to netdev->carrier_down_count
>>>          * unfortunately netdev also counts local reconfigurations which don't
>>>          * actually take the physical link down, not to mention NC-SI which,
>>>          * if present, keeps the link up regardless of host state.
>>>          * This statistic counts when PHY _actually_ went down, or lost link.
>>>          *
>>>          * Note that we need u64 for ethtool_stats_init() and comparisons
>>>          * to ETHTOOL_STAT_NOT_SET, but only u32 is exposed to the user.
>>>          */
>>>         u64 link_down_events;
>>> };
>>>
>>>
>>> IOW the definition of this stat is - ignoring asymetric link faults
>>> this counter should match between link partners.
>>
>> So, this is a little awkward to talk about -- we are already filling out that
>> field with a stat that's calculated by the driver; there's a task that monitors
>> link transitions and increments it.
>>
>> But, I'm not sure if that method exists because of older firmwares or cards not
>> tracking the link_down_event count for us. So, I didn't want to just overwrite
>> this stat with the value from firmware because then we'd lose the count on cards
>> running firmware that doesn't do that counting for us.
>>
>> So that's one reason why I put the stat in the generic ethtool stats output, and
>> gave it the slightly different name "link_down_events_phy" to distinguish it
>> (this also matches Mellanox's name for consistency) from this stat in the struct
>> you posted. Though reading the doc comment you posted, this new stat really does
>> belong there.
> 
> If the stat is currently filled in with something that does not work as
> documented it should be fine to drop it. The counter is optional and
> relatively recent. I think it's unlikely that any monitoring will have
> a hard dependency on it.

We did some testing internally, and it's safe to replace the link_down_events
stat with the FW generated stat instead of the one the driver calculates. It
gets populated and updated properly by the firmware on all of the hardware we
test on; I'll have that replaced in the v2.

- Eric

^ permalink raw reply

* Re: [PATCH net-next] ppp: consolidate RX skb queueing
From: Qingfang Deng @ 2026-05-06  2:54 UTC (permalink / raw)
  To: Paolo Abeni, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Guillaume Nault, Breno Leitao, Taegu Ha,
	Kees Cook, Sebastian Andrzej Siewior, linux-ppp, netdev,
	linux-kernel
In-Reply-To: <410c814a-399a-4eb9-a39a-d1e5fecd6b33@redhat.com>


On 2026/4/30 16:54, Paolo Abeni wrote:
>
> On 4/28/26 4:44 AM, Qingfang Deng wrote:
>> In ppp_input() and ppp_receive_nonmp_frame(), received skbs are queued
>> for userspace delivery using the same open-coded pattern:
>>
>> 	skb_queue_tail(&pf->rq, skb);
>> 	while (pf->rq.qlen > PPP_MAX_RQLEN &&
>> 	       (skb = skb_dequeue(&pf->rq)))
>> 		kfree_skb(skb);
>> 	wake_up_interruptible(&pf->rwait);
>>
>> This has a potential race: skb_queue_tail() releases the queue lock,
>> then qlen is read locklessly before skb_dequeue() re-acquires it.
>> Another CPU enqueueing concurrently could cause the length check to see
>> stale data. This race is benign, as it only causes extra skbs to be
>> freed in the worst case.
>>
>> Introduce ppp_file_queue_rx_skb() to perform the enqueue, length check,
>> and trim atomically under a single pf->rq.lock critical section. As both
>> callers have softirq disabled, plain spin_lock() can be used instead of
>> _bh()/_irqsave() variants. Since only one skb is enqueued at a time, the
>> queue can exceed PPP_MAX_RQLEN by at most one frame, so replace the
>> while-loop with an if-statement. While at it, use skb_queue_len()
>> instead of open-coding the qlen access.
>>
>> Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev>
>> ---
>>   drivers/net/ppp/ppp_generic.c | 37 ++++++++++++++++++++++-------------
>>   1 file changed, 23 insertions(+), 14 deletions(-)
>>
>> diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
>> index 57c68efa5ff8..6ab5011540a0 100644
>> --- a/drivers/net/ppp/ppp_generic.c
>> +++ b/drivers/net/ppp/ppp_generic.c
>> @@ -2307,6 +2307,27 @@ static bool ppp_channel_bridge_input(struct channel *pch, struct sk_buff *skb)
>>   	return !!pchb;
>>   }
>>   
>> +/* Queue up and deliver a received skb to userspace.
>> + * Must be called in softirq.
>> + */
>> +static void ppp_file_queue_rx_skb(struct ppp_file *pf, struct sk_buff *skb)
>> +{
>> +	spin_lock(&pf->rq.lock);
>> +	__skb_queue_tail(&pf->rq, skb);
>> +	/* limit queue length by dropping old frames */
>> +	if (unlikely(skb_queue_len(&pf->rq) > PPP_MAX_RQLEN)) {
>> +		struct sk_buff *old = __skb_peek(&pf->rq);
>> +
>> +		__skb_unlink(old, &pf->rq);
>> +		spin_unlock(&pf->rq.lock);
>> +		kfree_skb(old);
>> +	} else {
>> +		spin_unlock(&pf->rq.lock);
> Note that after __skb_queue_tail(), skb_queue_len(&pf->rq) could be ==
> PPP_MAX_RQLEN + 2, due to the slightly different check in
> ppp_prepare_tx_skb().

The check in ppp_prepare_tx_skb() is for demand dialing mode. As the 
name and comment suggest, when waiting for traffic a ppp interface is 
not able to receive packets, until we see a tx packet and then do the 
actual dial-up to resume normal operation, so that can't happen.

>
> I think the above it could/should be simplified to:
> 	while (unlikely(skb_queue_len(&pf->rq) > PPP_MAX_RQLEN))
> 		kfree_skb(__skb_dequeue(&pf->rq));
> 	spin_unlock(&pf->rq.lock);
>
> And possibly it would make sense to consolidate the test in
> ppp_prepare_tx_skb(), too for consistency - in that case an `if`
> statement should become enough.
I could consolidate this, but it tail-drops the skb instead of 
head-dropping.

^ permalink raw reply

* Re: [PATCH net v5 0/8] xsk: fix bugs around xsk skb allocation
From: patchwork-bot+netdevbpf @ 2026-05-06  2:40 UTC (permalink / raw)
  To: Jason Xing
  Cc: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
	maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
	john.fastabend, horms, andrew+netdev, bpf, netdev, kernelxing
In-Reply-To: <20260502200722.53960-1-kerneljasonxing@gmail.com>

Hello:

This series was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Sat,  2 May 2026 23:07:14 +0300 you wrote:
> From: Jason Xing <kernelxing@tencent.com>
> 
> There are rare issues around xsk_build_skb(). Some of them
> were founded by Sashiko[1][2].
> 
> [1]: https://lore.kernel.org/all/20260415082654.21026-1-kerneljasonxing@gmail.com/
> [2]: https://lore.kernel.org/all/20260418045644.28612-1-kerneljasonxing@gmail.com/
> 
> [...]

Here is the summary with links:
  - [net,v5,1/8] xsk: reject sw-csum UMEM binding to IFF_TX_SKB_NO_LINEAR devices
    https://git.kernel.org/netdev/net/c/d73a9a63f9f7
  - [net,v5,2/8] xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS
    https://git.kernel.org/netdev/net/c/0bb7a9caf5c1
  - [net,v5,3/8] xsk: handle NULL dereference of the skb without frags issue
    https://git.kernel.org/netdev/net/c/8cd3c1c6e7d9
  - [net,v5,4/8] xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path
    https://git.kernel.org/netdev/net/c/0f3776583d28
  - [net,v5,5/8] xsk: prevent CQ desync when freeing half-built skbs in xsk_build_skb()
    https://git.kernel.org/netdev/net/c/3dec153ae484
  - [net,v5,6/8] xsk: avoid skb leak in XDP_TX_METADATA case
    https://git.kernel.org/netdev/net/c/8c2cff50afdd
  - [net,v5,7/8] xsk: fix xsk_addrs slab leak on multi-buffer error path
    https://git.kernel.org/netdev/net/c/e0f229025a8e
  - [net,v5,8/8] xsk: fix u64 descriptor address truncation on 32-bit architectures
    https://git.kernel.org/netdev/net/c/203cee647f55

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox