diff options
| author | Nikhil P. Rao <nikhil.rao@amd.com> | 2026-07-30 04:25:22 +0000 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-08-05 17:59:29 -0700 |
| commit | b311af86b58f56ce81b85bd789c98e474851de90 (patch) | |
| tree | b42b87a0226098dcfe862019995f149c69004ba9 /drivers/net/ethernet | |
| parent | 5df545b3a5ee485fb366bba544063996d45d64af (diff) | |
| download | linux-next-b311af86b58f56ce81b85bd789c98e474851de90.tar.gz linux-next-b311af86b58f56ce81b85bd789c98e474851de90.zip | |
pds_core: add host backed memory support for firmware
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.
Host memory allocation is best-effort: if some allocations fail, the
driver continues with whatever succeeded. Firmware gracefully degrades
when less memory is available than requested.
Signed-off-by: Vamsi Atluri <Vamsi.Atluri@amd.com>
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
Link: https://patch.msgid.link/20260730-upstream_v8-v12-5-136cd174ee85@amd.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'drivers/net/ethernet')
| -rw-r--r-- | drivers/net/ethernet/amd/pds_core/core.c | 160 | ||||
| -rw-r--r-- | drivers/net/ethernet/amd/pds_core/core.h | 22 | ||||
| -rw-r--r-- | drivers/net/ethernet/amd/pds_core/main.c | 2 |
3 files changed, 184 insertions, 0 deletions
diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c index 51d21328c73d..26ddcbbfc14c 100644 --- a/drivers/net/ethernet/amd/pds_core/core.c +++ b/drivers/net/ethernet/amd/pds_core/core.c @@ -506,6 +506,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); @@ -515,6 +516,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; } @@ -681,3 +683,161 @@ 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; + + 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_warn(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 73356c74bb9f..085f2e988aa0 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,12 @@ #define PDSC_SETUP_RECOVERY false #define PDSC_SETUP_INIT true +/* Use fixed 4MB instead of PAGE_SIZE << MAX_PAGE_ORDER 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 +156,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 +225,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 +305,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); @@ -346,6 +365,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 c15a1e376353..5e1b07850163 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); |
