All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eric Joyner <eric.joyner@amd.com>
To: <netdev@vger.kernel.org>, <linux-rdma@vger.kernel.org>
Cc: Brett Creeley <brett.creeley@amd.com>,
	Andrew Lunn <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	"Eric Dumazet" <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Abhijit Gangurde <abhijit.gangurde@amd.com>,
	Allen Hubbe <allen.hubbe@amd.com>, Jason Gunthorpe <jgg@ziepe.ca>,
	Leon Romanovsky <leon@kernel.org>,
	Eric Joyner <eric.joyner@amd.com>
Subject: [PATCH net-next 3/4] RDMA/ionic: Add debugfs support
Date: Tue, 5 May 2026 21:19:34 -0700	[thread overview]
Message-ID: <20260506041935.1061-4-eric.joyner@amd.com> (raw)
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


  parent reply	other threads:[~2026-05-06  4:20 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-06  4:19 [PATCH net-next 0/4] RDMA/net/ionic: Misc updates Eric Joyner
2026-05-06  4:19 ` [PATCH net-next 1/4] RDMA/ionic: Update copyright year to 2026 Eric Joyner
2026-05-06  4:19 ` [PATCH net-next 2/4] net/ionic: Add devlink parameter for RDMA Eric Joyner
2026-05-06  4:19 ` Eric Joyner [this message]
2026-05-13  7:21   ` [PATCH net-next 3/4] RDMA/ionic: Add debugfs support Leon Romanovsky
2026-05-14  0:23     ` Jakub Kicinski
2026-05-14  6:00       ` Leon Romanovsky
2026-05-14  7:04         ` Greg Kroah-Hartman
2026-05-14 16:35           ` Leon Romanovsky
2026-05-06  4:19 ` [PATCH net-next 4/4] RDMA/ionic: Add DCQCN parameter configuration via debugfs Eric Joyner
2026-05-06 22:59 ` [PATCH net-next 0/4] RDMA/net/ionic: Misc updates Jakub Kicinski
2026-05-14 14:33   ` Abhijit Gangurde
2026-05-14 16:40     ` Leon Romanovsky
2026-05-14 17:19       ` Creeley, Brett

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260506041935.1061-4-eric.joyner@amd.com \
    --to=eric.joyner@amd.com \
    --cc=abhijit.gangurde@amd.com \
    --cc=allen.hubbe@amd.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=brett.creeley@amd.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=jgg@ziepe.ca \
    --cc=kuba@kernel.org \
    --cc=leon@kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.