From: "Nikhil P. Rao" <nikhil.rao@amd.com>
To: <netdev@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>,
<linux-kernel@vger.kernel.org>, Eric Joyner <eric.joyner@amd.com>,
Vamsi Atluri <Vamsi.Atluri@amd.com>,
"Nikhil P . Rao" <nikhil.rao@amd.com>
Subject: [PATCH v3 5/6] pds_core: add host backed memory support for firmware
Date: Mon, 8 Jun 2026 22:32:55 +0000 [thread overview]
Message-ID: <20260608223256.12357-6-nikhil.rao@amd.com> (raw)
In-Reply-To: <20260608223256.12357-1-nikhil.rao@amd.com>
From: Vamsi Atluri <Vamsi.Atluri@amd.com>
Some newer AMD/Pensando cards have minimal memory and there are cases
where components, specifically in the control plane, need more memory.
This series adds support for host backed DMA memory that can be used
by the firmware for the previously mentioned cases.
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Vamsi Atluri <Vamsi.Atluri@amd.com>
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
---
drivers/net/ethernet/amd/pds_core/core.c | 161 +++++++++++++++++++++++
drivers/net/ethernet/amd/pds_core/core.h | 20 +++
drivers/net/ethernet/amd/pds_core/main.c | 4 +-
include/linux/pds/pds_core_if.h | 64 +++++++++
4 files changed, 248 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c
index 705cab7b0727..249afcd5f5d7 100644
--- a/drivers/net/ethernet/amd/pds_core/core.c
+++ b/drivers/net/ethernet/amd/pds_core/core.c
@@ -487,6 +487,7 @@ void pdsc_teardown(struct pdsc *pdsc, bool removing)
pdsc->viftype_status = NULL;
}
+ pdsc_host_mem_free(pdsc);
pdsc_dev_uninit(pdsc);
set_bit(PDSC_S_FW_DEAD, &pdsc->state);
@@ -496,6 +497,7 @@ int pdsc_start(struct pdsc *pdsc)
{
pds_core_intr_mask(&pdsc->intr_ctrl[pdsc->adminqcq.intx],
PDS_CORE_INTR_MASK_CLEAR);
+ pdsc_host_mem_add(pdsc);
return 0;
}
@@ -658,3 +660,162 @@ void pdsc_health_thread(struct work_struct *work)
out_unlock:
mutex_unlock(&pdsc->config_lock);
}
+
+static void pdsc_host_mem_del_one(struct pdsc *pdsc, u16 tag, u8 reason)
+{
+ union pds_core_dev_comp comp = {};
+ union pds_core_dev_cmd cmd = {
+ .host_mem.opcode = PDS_CORE_CMD_HOST_MEM,
+ .host_mem.oper = PDS_CORE_HOST_MEM_DEL,
+ .host_mem.tag = cpu_to_le16(tag),
+ .host_mem.reason = reason,
+ };
+
+ dev_dbg(pdsc->dev, "Sending devcmd for mem del tag %d\n", tag);
+ pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
+}
+
+static int pdsc_host_mem_add_one(struct pdsc *pdsc, int index)
+{
+ struct pdsc_host_mem *hm = &pdsc->host_mem_reqs[index];
+ union pds_core_dev_comp comp = {};
+ union pds_core_dev_cmd cmd = {};
+ int err;
+
+ memset(hm, 0, sizeof(*hm));
+ cmd.host_mem.opcode = PDS_CORE_CMD_HOST_MEM;
+ cmd.host_mem.oper = PDS_CORE_HOST_MEM_QUERY;
+ cmd.host_mem.index = cpu_to_le16(index);
+ dev_dbg(pdsc->dev, "Sending devcmd for mem query index %d\n", index);
+ err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
+ if (err || comp.status != PDS_RC_SUCCESS) {
+ dev_err(pdsc->dev, "mem query failed err %d status %d\n",
+ err, comp.status);
+ return err ? err : -EIO;
+ }
+ hm->size = le32_to_cpu(comp.host_mem.size);
+ hm->tag = le16_to_cpu(comp.host_mem.tag);
+ dev_dbg(pdsc->dev, "mem query returned size %d tag %d\n",
+ hm->size, hm->tag);
+
+ if (!hm->size || hm->size > PDSC_HOST_MEM_MAX_CONTIG) {
+ dev_err(pdsc->dev, "invalid size %d for tag %d\n",
+ hm->size, hm->tag);
+ err = -EINVAL;
+ goto err_del;
+ }
+
+ hm->order = get_order(hm->size);
+ hm->pg = alloc_pages(GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN, hm->order);
+ if (!hm->pg) {
+ dev_err(pdsc->dev, "alloc order %d failed for tag %d\n",
+ hm->order, hm->tag);
+ err = -ENOMEM;
+ goto err_del;
+ }
+
+ hm->pa = dma_map_page(pdsc->dev, hm->pg, 0, hm->size,
+ DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(pdsc->dev, hm->pa)) {
+ dev_err(pdsc->dev, "dma map failed for tag %d size %d\n",
+ hm->tag, hm->size);
+ __free_pages(hm->pg, hm->order);
+ hm->pg = NULL;
+ err = -EIO;
+ goto err_del;
+ }
+
+ /* Track this allocation so pdsc_host_mem_free() can clean it up */
+ pdsc->num_host_mem_reqs++;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&comp, 0, sizeof(comp));
+ cmd.host_mem.opcode = PDS_CORE_CMD_HOST_MEM;
+ cmd.host_mem.oper = PDS_CORE_HOST_MEM_ADD;
+ cmd.host_mem.tag = cpu_to_le16(hm->tag);
+ cmd.host_mem.size = cpu_to_le32(hm->size);
+ cmd.host_mem.buf_pa = cpu_to_le64(hm->pa);
+
+ dev_dbg(pdsc->dev, "Sending devcmd for mem add tag %d size %d pa %pad\n",
+ hm->tag, hm->size, &hm->pa);
+ err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
+ if (err || comp.status != PDS_RC_SUCCESS) {
+ dev_err(pdsc->dev, "mem add failed err %d status %d for tag %d\n",
+ err, comp.status, hm->tag);
+ err = err ? err : -EIO;
+ goto err_del;
+ }
+ dev_dbg(pdsc->dev, "mem add completed for tag %d\n", hm->tag);
+
+ return 0;
+
+err_del:
+ /* After MEM_QUERY succeeds, firmware expects MEM_ADD or MEM_DEL */
+ pdsc_host_mem_del_one(pdsc, hm->tag, PDS_RC_ENOMEM);
+ return err;
+}
+
+void pdsc_host_mem_add(struct pdsc *pdsc)
+{
+ union pds_core_dev_comp comp = {};
+ union pds_core_dev_cmd cmd = {};
+ u16 count;
+ int err;
+ int i;
+
+ if (!(pdsc->dev_ident.capabilities &
+ cpu_to_le64(PDS_CORE_DEV_CAP_HOST_MEM)))
+ return;
+
+ cmd.host_mem.opcode = PDS_CORE_CMD_HOST_MEM;
+ cmd.host_mem.oper = PDS_CORE_HOST_MEM_GET_COUNT;
+ cmd.host_mem.index = cpu_to_le16(PDSC_HOST_MEM_MAX_COUNT);
+ cmd.host_mem.max_contig = cpu_to_le32(PDSC_HOST_MEM_MAX_CONTIG);
+ dev_dbg(pdsc->dev, "Sending devcmd for mem get count max_contig %u\n",
+ PDSC_HOST_MEM_MAX_CONTIG);
+ err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
+ if (err || comp.status != PDS_RC_SUCCESS) {
+ dev_err(pdsc->dev, "mem get count failed err %d status %d\n",
+ err, comp.status);
+ return;
+ }
+
+ count = min(le16_to_cpu(comp.host_mem.count),
+ PDSC_HOST_MEM_MAX_COUNT);
+ dev_dbg(pdsc->dev, "mem get count returned count %d\n", count);
+ if (count == 0)
+ return;
+
+ pdsc->host_mem_reqs = kzalloc_objs(*pdsc->host_mem_reqs, count,
+ GFP_KERNEL);
+ if (!pdsc->host_mem_reqs) {
+ dev_err(pdsc->dev, "failed to alloc host_mem_reqs array\n");
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ err = pdsc_host_mem_add_one(pdsc, i);
+ if (err)
+ break;
+ }
+}
+
+void pdsc_host_mem_free(struct pdsc *pdsc)
+{
+ int i;
+
+ if (!pdsc->host_mem_reqs)
+ return;
+
+ for (i = 0; i < pdsc->num_host_mem_reqs; i++) {
+ dma_unmap_page(pdsc->dev, pdsc->host_mem_reqs[i].pa,
+ pdsc->host_mem_reqs[i].size,
+ DMA_BIDIRECTIONAL);
+ __free_pages(pdsc->host_mem_reqs[i].pg,
+ pdsc->host_mem_reqs[i].order);
+ }
+
+ kfree(pdsc->host_mem_reqs);
+ pdsc->host_mem_reqs = NULL;
+ pdsc->num_host_mem_reqs = 0;
+}
diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
index c686f0bbbaeb..53e5a6f0af9c 100644
--- a/drivers/net/ethernet/amd/pds_core/core.h
+++ b/drivers/net/ethernet/amd/pds_core/core.h
@@ -5,6 +5,7 @@
#define _PDSC_H_
#include <linux/debugfs.h>
+#include <linux/mmzone.h>
#include <net/devlink.h>
#include <linux/pds/pds_common.h>
@@ -23,6 +24,10 @@
#define PDSC_SETUP_RECOVERY false
#define PDSC_SETUP_INIT true
+/* Use fixed 4MB max to avoid cpu_to_le32() truncation on large-page configs */
+#define PDSC_HOST_MEM_MAX_CONTIG (4 * 1024 * 1024)
+#define PDSC_HOST_MEM_MAX_COUNT 256
+
struct pdsc_deferred_dma {
struct list_head list;
dma_addr_t dma_addr;
@@ -149,6 +154,14 @@ struct pdsc_viftype {
struct pds_auxiliary_dev *padev;
};
+struct pdsc_host_mem {
+ u32 size;
+ u16 tag;
+ u8 order;
+ struct page *pg;
+ dma_addr_t pa;
+};
+
/* No state flags set means we are in a steady running state */
enum pdsc_state_flags {
PDSC_S_FW_DEAD, /* stopped, wait on startup or recovery */
@@ -210,6 +223,9 @@ struct pdsc {
struct pdsc_viftype *viftype_status;
struct work_struct pci_reset_work;
+ struct pdsc_host_mem *host_mem_reqs;
+ u16 num_host_mem_reqs;
+
struct pds_core_component_list_info fw_components;
};
@@ -287,6 +303,7 @@ void pdsc_debugfs_add_viftype(struct pdsc *pdsc);
void pdsc_debugfs_add_irqs(struct pdsc *pdsc);
void pdsc_debugfs_add_qcq(struct pdsc *pdsc, struct pdsc_qcq *qcq);
void pdsc_debugfs_del_qcq(struct pdsc_qcq *qcq);
+void pdsc_debugfs_add_host_mem(struct pdsc *pdsc);
int pdsc_err_to_errno(enum pds_core_status_code code);
bool pdsc_is_fw_running(struct pdsc *pdsc);
@@ -345,6 +362,9 @@ void pdsc_fw_down(struct pdsc *pdsc);
void pdsc_fw_up(struct pdsc *pdsc);
void pdsc_pci_reset_thread(struct work_struct *work);
+void pdsc_host_mem_add(struct pdsc *pdsc);
+void pdsc_host_mem_free(struct pdsc *pdsc);
+
void pdsc_deferred_dma_add(struct pdsc *pdsc, struct pdsc_deferred_dma *entry,
dma_addr_t dma_addr, void *va, size_t size,
enum dma_data_direction dir);
diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c
index 02ff5e51f617..a6f327f1c1d5 100644
--- a/drivers/net/ethernet/amd/pds_core/main.c
+++ b/drivers/net/ethernet/amd/pds_core/main.c
@@ -21,6 +21,8 @@ static const struct pci_device_id pdsc_id_table[] = {
};
MODULE_DEVICE_TABLE(pci, pdsc_id_table);
+static void pdsc_stop_health_thread(struct pdsc *pdsc);
+
static void pdsc_wdtimer_cb(struct timer_list *t)
{
struct pdsc *pdsc = timer_container_of(pdsc, t, wdtimer);
@@ -437,7 +439,7 @@ static void pdsc_remove(struct pci_dev *pdev)
pdsc_sriov_configure(pdev, 0);
pdsc_auxbus_dev_del(pdsc, pdsc, &pdsc->padev);
- timer_shutdown_sync(&pdsc->wdtimer);
+ pdsc_stop_health_thread(pdsc);
if (pdsc->wq)
destroy_workqueue(pdsc->wq);
diff --git a/include/linux/pds/pds_core_if.h b/include/linux/pds/pds_core_if.h
index cc1f180aee55..8a0c962b6472 100644
--- a/include/linux/pds/pds_core_if.h
+++ b/include/linux/pds/pds_core_if.h
@@ -46,6 +46,7 @@ enum pds_core_cmd_opcode {
PDS_CORE_CMD_SEND_COMPONENT = 9,
PDS_CORE_CMD_FINALIZE_UPDATE = 10,
PDS_CORE_CMD_MATCH_RECORD_DESC = 11,
+ PDS_CORE_CMD_HOST_MEM = 12,
/* SR/IOV commands */
PDS_CORE_CMD_VF_GETATTR = 60,
@@ -110,9 +111,11 @@ struct pds_core_drv_identity {
/**
* enum pds_core_dev_capability - Device capabilities
* @PDS_CORE_DEV_CAP_PLDM_FW_UPDATE: Device only supports FW update via PLDM
+ * @PDS_CORE_DEV_CAP_HOST_MEM: Device supports host memory for fw use
*/
enum pds_core_dev_capability {
PDS_CORE_DEV_CAP_PLDM_FW_UPDATE = BIT(0),
+ PDS_CORE_DEV_CAP_HOST_MEM = BIT(1),
};
#define PDS_DEV_TYPE_MAX 16
@@ -838,6 +841,65 @@ struct pds_core_match_record_desc_comp {
u8 rsvd;
};
+/**
+ * enum pds_core_host_mem_oper - HOST_MEM sub-operations
+ * @PDS_CORE_HOST_MEM_GET_COUNT: Query number of memory requests
+ * @PDS_CORE_HOST_MEM_QUERY: Query details of a memory request
+ * @PDS_CORE_HOST_MEM_ADD: Provide allocated memory to firmware
+ * @PDS_CORE_HOST_MEM_DEL: Notify firmware of memory deallocation
+ */
+enum pds_core_host_mem_oper {
+ PDS_CORE_HOST_MEM_GET_COUNT = 0,
+ PDS_CORE_HOST_MEM_QUERY = 1,
+ PDS_CORE_HOST_MEM_ADD = 2,
+ PDS_CORE_HOST_MEM_DEL = 3,
+};
+
+/**
+ * struct pds_core_host_mem_cmd - HOST_MEM command
+ * @opcode: Opcode PDS_CORE_CMD_HOST_MEM
+ * @oper: Operation (enum pds_core_host_mem_oper)
+ * @index: Memory request index (GET_COUNT: max_count, QUERY: index)
+ * @tag: Tag for this memory request (ADD/DEL)
+ * @reason: Reason for deletion (DEL only)
+ * @rsvd: Reserved
+ * @max_contig: Maximum contiguous memory size (GET_COUNT only)
+ * @size: Size of memory in bytes (ADD only)
+ * @buf_pa: DMA address of memory (ADD only)
+ *
+ * Unified command for all host memory operations. Fields are reused
+ * across operations to minimize opcode space usage.
+ */
+struct pds_core_host_mem_cmd {
+ u8 opcode;
+ u8 oper;
+ __le16 index;
+ __le16 tag;
+ u8 reason;
+ u8 rsvd;
+ __le32 max_contig;
+ __le32 size;
+ __le64 buf_pa;
+};
+
+/**
+ * struct pds_core_host_mem_comp - HOST_MEM completion
+ * @status: Status of the command (enum pds_core_status_code)
+ * @oper: Operation that was performed
+ * @count: Number of memory requests (GET_COUNT)
+ * @size: Size of memory request in bytes (QUERY)
+ * @tag: Tag for this memory request (QUERY/DEL)
+ * @rsvd: Reserved
+ */
+struct pds_core_host_mem_comp {
+ u8 status;
+ u8 oper;
+ __le16 count;
+ __le32 size;
+ __le16 tag;
+ u8 rsvd[6];
+};
+
/*
* union pds_core_dev_cmd - Overlay of core device command structures
*/
@@ -861,6 +923,7 @@ union pds_core_dev_cmd {
struct pds_core_send_component_cmd send_component;
struct pds_core_finalize_update_cmd finalize_update;
struct pds_core_match_record_desc_cmd match_record_desc;
+ struct pds_core_host_mem_cmd host_mem;
};
/*
@@ -886,6 +949,7 @@ union pds_core_dev_comp {
struct pds_core_send_component_comp send_component;
struct pds_core_finalize_update_comp finalize_update;
struct pds_core_match_record_desc_comp match_record_desc;
+ struct pds_core_host_mem_comp host_mem;
};
/**
--
2.43.0
next prev parent reply other threads:[~2026-06-08 22:41 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-08 22:32 [PATCH net-next v3 0/6] pds_core: Add PLDM firmware update and host backed memory support Nikhil P. Rao
2026-06-08 22:32 ` [PATCH v3 1/6] pds_core: add support for quiet devcmd failures Nikhil P. Rao
2026-06-08 22:32 ` [PATCH v3 2/6] pds_core: add support for identity version 2 Nikhil P. Rao
2026-06-08 22:32 ` [PATCH v3 3/6] pds_core: add PLDM firmware update support via devlink flash Nikhil P. Rao
2026-06-08 22:53 ` Jacob Keller
2026-06-08 22:32 ` [PATCH v3 4/6] pds_core: add PLDM component info display Nikhil P. Rao
2026-06-08 22:32 ` Nikhil P. Rao [this message]
2026-06-08 22:32 ` [PATCH v3 6/6] pds_core: add debugfs support for host backed memory Nikhil P. Rao
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=20260608223256.12357-6-nikhil.rao@amd.com \
--to=nikhil.rao@amd.com \
--cc=Vamsi.Atluri@amd.com \
--cc=andrew+netdev@lunn.ch \
--cc=brett.creeley@amd.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=eric.joyner@amd.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox