Subject: [PATCH net-next 3/4] RDMA/ionic: Add debugfs support

From: Eric Joyner <[email-masked]>
Date: Tue, 5 May 2026 21:19:34 -0700

From: Allen Hubbe <[email-masked]>

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 <[email-masked]>
Co-developed-by: Eric Joyner <[email-masked]>
Signed-off-by: Eric Joyner <[email-masked]>
---
 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