* [PATCH net-next 3/6] pds_core: add PLDM firmware update support via devlink flash
From: Nikhil P. Rao @ 2026-04-29 8:28 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner
In-Reply-To: <20260429-b4-pldm-b4-v1-0-394fafba526f@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
Implement PLDM FW Update in the pds_core driver using the upstream
pldmfw API. This allows an entire PLDM FW package to be updated
and/or specific components if they aren't fixed.
Flash the entire image:
devlink dev flash pci/0000:b5:00.0 file firmware.pldmfw
Flash individual components from the PLDM FW package:
devlink dev flash pci/0000:b5:00.0 \
file firmware.pldmfw component fw.mainfwa
devlink dev flash pci/0000:b5:00.0 \
file firmware.pldmfw component fw.mainfwb
devlink dev flash pci/0000:b5:00.0 \
file firmware.pldmfw component fw.goldfw
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
---
drivers/net/ethernet/amd/Kconfig | 1 +
drivers/net/ethernet/amd/pds_core/core.h | 14 +-
drivers/net/ethernet/amd/pds_core/dev.c | 42 +-
drivers/net/ethernet/amd/pds_core/devlink.c | 2 +-
drivers/net/ethernet/amd/pds_core/fw.c | 699 +++++++++++++++++++++++++++-
drivers/net/ethernet/amd/pds_core/main.c | 4 +-
include/linux/pds/pds_core_if.h | 375 +++++++++++++++
7 files changed, 1130 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index 45e8d698781c..e7346837dad6 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -192,6 +192,7 @@ config PDS_CORE
depends on 64BIT && PCI
select AUXILIARY_BUS
select NET_DEVLINK
+ select PLDMFW
help
This enables the support for the AMD/Pensando Core device family of
adapters. More specific information on this driver can be
diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
index 4a6b35c84dab..c9ba63878927 100644
--- a/drivers/net/ethernet/amd/pds_core/core.h
+++ b/drivers/net/ethernet/amd/pds_core/core.h
@@ -199,6 +199,8 @@ struct pdsc {
u64 last_eid;
struct pdsc_viftype *viftype_status;
struct work_struct pci_reset_work;
+
+ struct pds_core_component_list_info fw_components;
};
/** enum pds_core_dbell_bits - bitwise composition of dbell values.
@@ -281,8 +283,16 @@ bool pdsc_is_fw_running(struct pdsc *pdsc);
bool pdsc_is_fw_good(struct pdsc *pdsc);
int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds);
+int pdsc_devcmd_with_data(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ const void *data, size_t data_len,
+ union pds_core_dev_comp *comp, int max_seconds);
+int pdsc_devcmd_with_data_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ const void *data, size_t data_len,
+ union pds_core_dev_comp *comp, int max_seconds);
int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds);
+int pdsc_devcmd_locked_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ union pds_core_dev_comp *comp, int max_seconds);
int pdsc_devcmd_init(struct pdsc *pdsc);
int pdsc_devcmd_reset(struct pdsc *pdsc);
int pdsc_dev_init(struct pdsc *pdsc);
@@ -315,8 +325,10 @@ void pdsc_process_adminq(struct pdsc_qcq *qcq);
void pdsc_work_thread(struct work_struct *work);
irqreturn_t pdsc_adminq_isr(int irq, void *data);
-int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
+int pdsc_firmware_update(struct pdsc *pdsc,
+ struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack);
+int pdsc_get_component_info(struct pdsc *pdsc);
void pdsc_fw_down(struct pdsc *pdsc);
void pdsc_fw_up(struct pdsc *pdsc);
diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
index f77bd5e48b92..4bbf299a88dc 100644
--- a/drivers/net/ethernet/amd/pds_core/dev.c
+++ b/drivers/net/ethernet/amd/pds_core/dev.c
@@ -127,7 +127,7 @@ static const char *pdsc_devcmd_str(int opcode)
}
static int __pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds,
- const bool do_msg)
+ bool do_msg)
{
struct device *dev = pdsc->dev;
unsigned long start_time;
@@ -208,6 +208,12 @@ int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
return __pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds, true);
}
+int pdsc_devcmd_locked_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ union pds_core_dev_comp *comp, int max_seconds)
+{
+ return __pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds, false);
+}
+
int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds)
{
@@ -220,6 +226,40 @@ int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
return err;
}
+int pdsc_devcmd_with_data(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ const void *data, size_t data_len,
+ union pds_core_dev_comp *comp, int max_seconds)
+{
+ int err;
+
+ if (data_len > sizeof(pdsc->cmd_regs->data))
+ return -ENOSPC;
+
+ mutex_lock(&pdsc->devcmd_lock);
+ memcpy_toio(&pdsc->cmd_regs->data, data, data_len);
+ err = pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds);
+ mutex_unlock(&pdsc->devcmd_lock);
+
+ return err;
+}
+
+int pdsc_devcmd_with_data_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ const void *data, size_t data_len,
+ union pds_core_dev_comp *comp, int max_seconds)
+{
+ int err;
+
+ if (data_len > sizeof(pdsc->cmd_regs->data))
+ return -ENOSPC;
+
+ mutex_lock(&pdsc->devcmd_lock);
+ memcpy_toio(&pdsc->cmd_regs->data, data, data_len);
+ err = pdsc_devcmd_locked_nomsg(pdsc, cmd, comp, max_seconds);
+ mutex_unlock(&pdsc->devcmd_lock);
+
+ return err;
+}
+
int pdsc_devcmd_init(struct pdsc *pdsc)
{
union pds_core_dev_comp comp = {};
diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c
index b576be626a29..7f44e1a8d4fd 100644
--- a/drivers/net/ethernet/amd/pds_core/devlink.c
+++ b/drivers/net/ethernet/amd/pds_core/devlink.c
@@ -90,7 +90,7 @@ int pdsc_dl_flash_update(struct devlink *dl,
{
struct pdsc *pdsc = devlink_priv(dl);
- return pdsc_firmware_update(pdsc, params->fw, extack);
+ return pdsc_firmware_update(pdsc, params, extack);
}
static char *fw_slotnames[] = {
diff --git a/drivers/net/ethernet/amd/pds_core/fw.c b/drivers/net/ethernet/amd/pds_core/fw.c
index fa626719e68d..4ccf90f25f75 100644
--- a/drivers/net/ethernet/amd/pds_core/fw.c
+++ b/drivers/net/ethernet/amd/pds_core/fw.c
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2023 Advanced Micro Devices, Inc */
+#include <linux/pldmfw.h>
+#include <linux/vmalloc.h>
+
#include "core.h"
/* The worst case wait for the install activity is about 25 minutes when
@@ -14,6 +17,10 @@
/* Number of periodic log updates during fw file download */
#define PDSC_FW_INTERVAL_FRACTION 32
+#define PDSC_FW_COMPONENT_PREFIX "fw."
+#define PDSC_FW_COMPONENT_FULL_NAME_BUFLEN \
+ (sizeof(PDSC_FW_COMPONENT_PREFIX) + PDS_CORE_FW_COMPONENT_NAME_BUFLEN)
+
static int pdsc_devcmd_fw_download_locked(struct pdsc *pdsc, u64 addr,
u32 offset, u32 length)
{
@@ -23,7 +30,7 @@ static int pdsc_devcmd_fw_download_locked(struct pdsc *pdsc, u64 addr,
.fw_download.addr = cpu_to_le64(addr),
.fw_download.length = cpu_to_le32(length),
};
- union pds_core_dev_comp comp;
+ union pds_core_dev_comp comp = {};
return pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
}
@@ -95,8 +102,9 @@ static int pdsc_fw_status_long_wait(struct pdsc *pdsc,
return err;
}
-int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
- struct netlink_ext_ack *extack)
+static int pdsc_legacy_firmware_update(struct pdsc *pdsc,
+ const struct firmware *fw,
+ struct netlink_ext_ack *extack)
{
u32 buf_sz, copy_sz, offset;
struct devlink *dl;
@@ -195,3 +203,688 @@ int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
NULL, 0, 0);
return err;
}
+
+struct pdsc_component_priv {
+ char component_name[PDSC_FW_COMPONENT_FULL_NAME_BUFLEN];
+ u16 component_id;
+ bool skip;
+ struct list_head list_entry;
+};
+
+struct pds_core_fwu_priv {
+ struct pldmfw context;
+ struct devlink_flash_update_params *params;
+ struct netlink_ext_ack *extack;
+ struct pdsc *pdsc;
+ struct list_head components;
+};
+
+static void pdsc_free_fwu_priv(struct pds_core_fwu_priv *priv)
+{
+ struct pdsc_component_priv *component_priv, *tmp;
+
+ list_for_each_entry_safe(component_priv, tmp, &priv->components,
+ list_entry) {
+ list_del(&component_priv->list_entry);
+ kfree(component_priv);
+ }
+}
+
+static int pdsc_devcmd_match_record_desc(struct pdsc *pdsc, u16 desc_type,
+ u16 desc_size, const u8 *desc_data,
+ u8 *match)
+{
+ union pds_core_dev_cmd cmd = {
+ .match_record_desc.opcode = PDS_CORE_CMD_MATCH_RECORD_DESC,
+ .match_record_desc.ver = 1,
+ .match_record_desc.type = cpu_to_le16(desc_type),
+ .match_record_desc.size = cpu_to_le16(desc_size),
+ };
+ union pds_core_dev_comp comp = {};
+ int err;
+
+ err = pdsc_devcmd_with_data(pdsc, &cmd, desc_data, desc_size,
+ &comp, pdsc->devcmd_timeout);
+ *match = comp.match_record_desc.match;
+
+ return err;
+}
+
+static bool pdsc_match_record_descs(struct pldmfw *context,
+ struct pldmfw_record *record)
+{
+ struct pds_core_fwu_priv *priv =
+ container_of(context, struct pds_core_fwu_priv, context);
+ struct pdsc *pdsc = priv->pdsc;
+ struct pldmfw_desc_tlv *desc;
+
+ if (!pldmfw_op_pci_match_record(context, record))
+ return false;
+
+ list_for_each_entry(desc, &record->descs, entry) {
+ u8 match;
+ int err;
+
+ switch (desc->type) {
+ /* skip types checked in pldmfw_op_pci_match_record */
+ case PLDM_DESC_ID_PCI_VENDOR_ID:
+ case PLDM_DESC_ID_PCI_DEVICE_ID:
+ case PLDM_DESC_ID_PCI_SUBVENDOR_ID:
+ case PLDM_DESC_ID_PCI_SUBDEV_ID:
+ continue;
+ }
+
+ if (!desc->size)
+ return false;
+
+ err = pdsc_devcmd_match_record_desc(pdsc, desc->type,
+ desc->size, desc->data,
+ &match);
+ if (err) {
+ dev_err(pdsc->dev, "match_record_desc failed type: 0x%04x size: %u, err %d\n",
+ desc->type, desc->size, err);
+ return false;
+ }
+ /* all record descriptors must match */
+ if (!match)
+ return false;
+ }
+
+ return true;
+}
+
+static int pdsc_devcmd_send_package_data(struct pdsc *pdsc, u64 addr,
+ u16 length, u16 offset, u16 total_len)
+{
+ union pds_core_dev_cmd cmd = {
+ .send_pkg_data.opcode = PDS_CORE_CMD_SEND_PKG_DATA,
+ .send_pkg_data.ver = 1,
+ .send_pkg_data.data_pa = cpu_to_le64(addr),
+ .send_pkg_data.data_len = cpu_to_le16(length),
+ .send_pkg_data.offset = cpu_to_le16(offset),
+ .send_pkg_data.total_len = cpu_to_le16(total_len),
+ };
+ union pds_core_dev_comp comp = {};
+
+ return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
+}
+
+static int pdsc_send_package_data(struct pldmfw *context, const u8 *data, u16 length)
+{
+ struct pds_core_fwu_priv *priv =
+ container_of(context, struct pds_core_fwu_priv, context);
+ struct device *dev = context->dev;
+ struct pdsc *pdsc = priv->pdsc;
+ u8 *package_data;
+ u32 offset;
+ int err;
+
+ if (!length)
+ return 0;
+
+ package_data = kmemdup(data, length, GFP_KERNEL);
+ if (!package_data)
+ return -ENOMEM;
+
+ offset = 0;
+ while (offset < length) {
+ dma_addr_t dma_addr;
+ u32 copy_sz;
+
+ copy_sz = min_t(unsigned int, PDS_PAGE_SIZE, length - offset);
+ dma_addr = dma_map_single(dev, package_data + offset, copy_sz,
+ DMA_TO_DEVICE);
+ err = dma_mapping_error(dev, dma_addr);
+ if (err) {
+ dev_err(dev, "Failed to dma_map package_data at offset 0x%x copy_sz 0x%x: %pe\n",
+ offset, copy_sz, ERR_PTR(err));
+ goto out;
+ }
+
+ err = pdsc_devcmd_send_package_data(pdsc, dma_addr, copy_sz, offset,
+ length);
+ if (err)
+ dev_err(dev, "send_package_data failed offset 0x%x addr 0x%llx len 0x%x: %pe\n",
+ offset, dma_addr, copy_sz, ERR_PTR(err));
+
+ dma_unmap_single(dev, dma_addr, copy_sz, DMA_TO_DEVICE);
+ if (err)
+ goto out;
+
+ offset += copy_sz;
+ }
+
+out:
+ kfree(package_data);
+ return err;
+}
+
+static void pdsc_set_component_name(struct pdsc *pdsc, u16 component_id,
+ u8 slot_id, char *component_name)
+{
+ int i;
+
+ for (i = 0; i < pdsc->fw_components.num_components; i++) {
+ struct pds_core_fw_component_info *info =
+ &pdsc->fw_components.info[i];
+
+ if (component_id == info->identifier &&
+ slot_id == info->slot_id) {
+ snprintf(component_name,
+ PDSC_FW_COMPONENT_FULL_NAME_BUFLEN,
+ "fw.%s", info->name);
+ return;
+ }
+ }
+}
+
+static const char *pdsc_get_component_priv_name(struct pds_core_fwu_priv *priv,
+ u16 component_id)
+{
+ struct pdsc_component_priv *component_priv;
+
+ list_for_each_entry(component_priv, &priv->components, list_entry) {
+ if (component_priv->component_id != component_id)
+ continue;
+
+ return component_priv->component_name;
+ }
+
+ return NULL;
+}
+
+static struct pds_core_fw_component_info *
+pdsc_find_component_by_name(struct pdsc *pdsc, const char *component_name)
+{
+ struct pds_core_fw_component_info *info;
+ size_t prefix_len;
+ int i;
+
+ prefix_len = str_has_prefix(component_name, PDSC_FW_COMPONENT_PREFIX);
+ if (!prefix_len)
+ return NULL;
+
+ component_name += prefix_len; /* Skip "fw." prefix */
+
+ for (i = 0; i < pdsc->fw_components.num_components; i++) {
+ info = &pdsc->fw_components.info[i];
+
+ if (!strncmp(component_name, info->name,
+ PDS_CORE_FW_COMPONENT_NAME_BUFLEN))
+ return info;
+ }
+
+ return NULL;
+}
+
+static u8 pdsc_get_slot_id(struct pdsc *pdsc, const char *component_name)
+{
+ struct pds_core_fw_component_info *info;
+
+ info = pdsc_find_component_by_name(pdsc, component_name);
+ return info ? info->slot_id : PDS_CORE_FW_SLOT_MAX;
+}
+
+static bool pdsc_skip_component(struct pds_core_fwu_priv *priv,
+ u16 component_id, const char *component_name)
+{
+ struct pdsc_component_priv *component_priv;
+
+ list_for_each_entry(component_priv, &priv->components, list_entry) {
+ if (component_priv->component_id != component_id)
+ continue;
+
+ if (component_priv->skip)
+ return true;
+
+ if (component_name &&
+ strncmp(component_priv->component_name, component_name,
+ PDSC_FW_COMPONENT_FULL_NAME_BUFLEN))
+ return true;
+ }
+
+ return false;
+}
+
+static bool pdsc_match_component_name_to_ids(struct pdsc *pdsc,
+ const char *component_name,
+ u8 component_id,
+ u8 slot_id)
+{
+ struct pds_core_fw_component_info *info;
+
+ info = pdsc_find_component_by_name(pdsc, component_name);
+ if (!info)
+ return false;
+
+ return slot_id == info->slot_id && component_id == info->identifier;
+}
+
+static int pdsc_send_component_table(struct pldmfw *context,
+ struct pldmfw_component *component,
+ u8 transfer_flag)
+{
+ struct pds_core_fwu_priv *priv =
+ container_of(context, struct pds_core_fwu_priv, context);
+ struct pds_core_component_tbl *component_tbl;
+ struct pdsc_component_priv *component_priv;
+ struct device *dev = context->dev;
+ union pds_core_dev_comp comp = {};
+ union pds_core_dev_cmd cmd = {};
+ struct pdsc *pdsc = priv->pdsc;
+ bool skip_component = false;
+ u16 buf_sz, tbl_sz;
+ int err = 0;
+ u8 slot_id;
+
+ dev_dbg(dev, "component name %s classification %u id %u activation_method %u ver_len %d ver_str %.*s index %u size %u transfer_flag 0x%02x\n",
+ priv->params->component, component->classification,
+ component->identifier, component->activation_method,
+ component->version_len, component->version_len,
+ component->version_string, component->index,
+ component->component_size, transfer_flag);
+
+ component_priv = kzalloc_obj(*component_priv, GFP_KERNEL);
+ if (!component_priv)
+ return -ENOMEM;
+
+ if (priv->params->component) {
+ slot_id = pdsc_get_slot_id(pdsc, priv->params->component);
+ if (slot_id == PDS_CORE_FW_SLOT_MAX)
+ return -ENOENT;
+
+ if (!pdsc_match_component_name_to_ids(pdsc,
+ priv->params->component,
+ component->identifier,
+ slot_id)) {
+ skip_component = true;
+ goto add_component_priv;
+ }
+ } else {
+ slot_id = PDS_CORE_FW_SLOT_INVALID;
+ }
+
+ buf_sz = sizeof(pdsc->cmd_regs->data);
+ tbl_sz = struct_size(component_tbl, version_str, component->version_len);
+ if (tbl_sz > buf_sz) {
+ dev_err(dev, "component_tbl size %d too big, max size: %d\n",
+ tbl_sz, buf_sz);
+ err = -ENOSPC;
+ goto free_component_priv;
+ }
+ component_tbl = kzalloc(tbl_sz, GFP_KERNEL);
+ if (!component_tbl) {
+ err = -ENOMEM;
+ goto free_component_priv;
+ }
+
+ component_tbl->comparison_stamp = cpu_to_le32(component->comparison_stamp);
+ component_tbl->classification = cpu_to_le16(component->classification);
+ component_tbl->identifier = cpu_to_le16(component->identifier);
+ component_tbl->transfer_flag = transfer_flag;
+ component_tbl->version_str_type = component->version_type;
+ component_tbl->version_str_len = component->version_len;
+ memcpy(component_tbl->version_str, component->version_string,
+ component->version_len);
+
+ cmd.send_component_tbl.opcode = PDS_CORE_CMD_SEND_COMPONENT_TBL;
+ cmd.send_component_tbl.ver = 1;
+ cmd.send_component_tbl.slot_id = slot_id;
+
+ err = pdsc_devcmd_with_data(pdsc, &cmd, component_tbl, tbl_sz,
+ &comp, pdsc->devcmd_timeout);
+ if (err)
+ dev_err(dev, "Failed sending component table: %pe\n",
+ ERR_PTR(err));
+ kfree(component_tbl);
+ if (err)
+ goto free_component_priv;
+
+ if (comp.send_component_tbl.response == 1 &&
+ comp.send_component_tbl.response_code == PDS_CORE_COMPONENT_PREREQS_NOT_MET)
+ skip_component = true;
+ else
+ pdsc_set_component_name(pdsc, component->identifier,
+ comp.send_component_tbl.slot_id,
+ component_priv->component_name);
+
+add_component_priv:
+ component_priv->skip = skip_component;
+ component_priv->component_id = component->identifier;
+ list_add(&component_priv->list_entry, &priv->components);
+
+ return 0;
+
+free_component_priv:
+ kfree(component_priv);
+ return err;
+}
+
+int pdsc_get_component_info(struct pdsc *pdsc)
+{
+ union pds_core_dev_cmd cmd = {
+ .get_component_info.opcode = PDS_CORE_CMD_GET_COMPONENT_INFO,
+ .get_component_info.ver = 1,
+ };
+ struct pds_core_component_list_info *list_info;
+ union pds_core_dev_comp comp = {};
+ dma_addr_t dma_addr;
+ u8 num_components;
+ int err, i;
+
+ list_info = kzalloc(PDS_PAGE_SIZE, GFP_KERNEL);
+ if (!list_info)
+ return -ENOMEM;
+
+ dma_addr = dma_map_single(pdsc->dev, list_info, PDS_PAGE_SIZE, DMA_FROM_DEVICE);
+ err = dma_mapping_error(pdsc->dev, dma_addr);
+ if (err) {
+ dev_err(pdsc->dev, "Failed to dma_map component_list_info length %d: %pe\n",
+ PDS_PAGE_SIZE, ERR_PTR(err));
+ goto out;
+ }
+
+ cmd.get_component_info.data_len = cpu_to_le16(PDS_PAGE_SIZE);
+ cmd.get_component_info.data_pa = cpu_to_le64(dma_addr);
+
+ err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout * 2);
+ dma_unmap_single(pdsc->dev, dma_addr, PDS_PAGE_SIZE, DMA_FROM_DEVICE);
+ if (err)
+ goto out;
+
+ if (comp.get_component_info.ver == 0) {
+ /* Don't support backward compatibility as version 0 has
+ * alignment issues, so give a hint to users to update
+ * their firmware
+ */
+ dev_warn(pdsc->dev, "Incompatible get_component_info version %u reported by firmware\n",
+ comp.get_component_info.ver);
+ err = 0;
+ goto out;
+ }
+
+ num_components = list_info->num_components;
+ if (num_components > PDS_CORE_FW_COMPONENT_LIST_LEN) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ pdsc->fw_components.num_components = num_components;
+ for (i = 0; i < num_components; i++) {
+ struct pds_core_fw_component_info *info =
+ &pdsc->fw_components.info[i];
+
+ memcpy(info, &list_info->info[i], sizeof(*info));
+ info->version[PDS_CORE_FW_COMPONENT_VER_BUFLEN - 1] = 0;
+ info->name[PDS_CORE_FW_COMPONENT_NAME_BUFLEN - 1] = 0;
+ }
+
+out:
+ kfree(list_info);
+ return err;
+}
+
+static int pdsc_devcmd_send_component(struct pdsc *pdsc,
+ struct pds_core_flash_component *info,
+ u16 info_sz, dma_addr_t addr, u32 length,
+ u32 offset, u16 slot_id,
+ union pds_core_dev_comp *comp)
+{
+ union pds_core_dev_cmd cmd = {
+ .send_component.opcode = PDS_CORE_CMD_SEND_COMPONENT,
+ .send_component.ver = 1,
+ .send_component.operation = PDS_CORE_SEND_COMPONENT_START,
+ .send_component.data_pa = cpu_to_le64(addr),
+ .send_component.data_len = cpu_to_le32(length),
+ .send_component.offset = cpu_to_le32(offset),
+ .send_component.slot_id = slot_id,
+ };
+ unsigned long timeout = 300 * HZ;
+ unsigned long start_time;
+ unsigned long end_time;
+ int err;
+
+ start_time = jiffies;
+ end_time = start_time + timeout;
+ do {
+ /* prevent noisy/benign devcmd failures */
+ err = pdsc_devcmd_with_data_nomsg(pdsc, &cmd, info, info_sz,
+ comp, 60);
+ if (err != -EAGAIN)
+ break;
+
+ /* if required, subsequent commands check status of
+ * PDS_CORE_CMD_SEND_COMPONENT command, which returns
+ * EAGAIN/ETIMEDOUT while the command is still running,
+ * else we get the final command status.
+ */
+ cmd.send_component.operation = PDS_CORE_SEND_COMPONENT_STATUS;
+ msleep(20);
+ } while (time_before(jiffies, end_time));
+
+ if (err == -EAGAIN)
+ dev_err(pdsc->dev, "PDS_CORE_CMD_SEND_COMPONENT timed out\n");
+
+ return err;
+}
+
+static int pdsc_flash_component(struct pldmfw *context,
+ struct pldmfw_component *component)
+{
+ struct pds_core_fwu_priv *priv =
+ container_of(context, struct pds_core_fwu_priv, context);
+ const char *component_name = priv->params->component;
+ struct pds_core_flash_component *component_info;
+ struct device *dev = context->dev;
+ struct pdsc *pdsc = priv->pdsc;
+ u16 buf_sz, info_sz;
+ struct devlink *dl;
+ u32 total_len;
+ u32 offset;
+ u8 slot_id;
+ int err;
+
+ if (pdsc_skip_component(priv, component->identifier, component_name))
+ return 0;
+
+ if (component_name) {
+ slot_id = pdsc_get_slot_id(pdsc, component_name);
+ if (slot_id == PDS_CORE_FW_SLOT_MAX)
+ return 0;
+ } else {
+ component_name = pdsc_get_component_priv_name(priv, component->identifier);
+ slot_id = PDS_CORE_FW_SLOT_INVALID;
+ }
+
+ total_len = component->component_size;
+ dev_dbg(dev, "component name %s class %u id %u act_meth %u ver_str %.*s index %u size %u\n",
+ component_name, component->classification,
+ component->identifier, component->activation_method,
+ component->version_len, component->version_string,
+ component->index, component->component_size);
+
+ buf_sz = sizeof(pdsc->cmd_regs->data);
+ info_sz = struct_size(component_info, version_str, component->version_len);
+ if (info_sz > buf_sz) {
+ dev_err(dev, "component_info size %d too big, max size: %d\n",
+ info_sz, buf_sz);
+ return -ENOSPC;
+ }
+ component_info = vzalloc(info_sz);
+ if (!component_info)
+ return -ENOMEM;
+
+ component_info->comparison_stamp = cpu_to_le32(component->comparison_stamp);
+ component_info->image_size = cpu_to_le32(total_len);
+ component_info->classification = cpu_to_le16(component->classification);
+ component_info->identifier = cpu_to_le16(component->identifier);
+ component_info->options = cpu_to_le16(component->options);
+ component_info->version_str_type = component->version_type;
+ component_info->version_str_len = component->version_len;
+ memcpy(component_info->version_str, component->version_string,
+ component->version_len);
+
+ dl = priv_to_devlink(pdsc);
+
+ offset = 0;
+ while (offset < total_len) {
+ union pds_core_dev_comp comp = {};
+ dma_addr_t dma_addr;
+ u8 *component_data;
+ u16 copy_sz;
+
+ copy_sz = min_t(unsigned int, PDS_PAGE_SIZE, total_len - offset);
+ component_data = kmemdup(component->component_data + offset,
+ copy_sz, GFP_KERNEL);
+ if (!component_data) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ dma_addr = dma_map_single(dev, component_data, copy_sz,
+ DMA_TO_DEVICE);
+ err = dma_mapping_error(pdsc->dev, dma_addr);
+ if (err) {
+ dev_err(dev, "Failed to dma_map component_data at offset 0x%x copy_sz 0x%x: %pe\n",
+ offset, copy_sz, ERR_PTR(err));
+ kfree(component_data);
+ goto err_out;
+ }
+
+ err = pdsc_devcmd_send_component(pdsc, component_info, info_sz,
+ dma_addr, copy_sz, offset,
+ slot_id, &comp);
+ dma_unmap_single(dev, dma_addr, copy_sz, DMA_TO_DEVICE);
+ kfree(component_data);
+ if (err && err != -EAGAIN &&
+ comp.send_component.compat_response &&
+ (comp.send_component.compat_response_code ==
+ PDS_CORE_COMPONENT_STAMP_IDENTICAL ||
+ comp.send_component.compat_response_code ==
+ PDS_CORE_COMPONENT_STAMP_LOWER)) {
+ err = 0;
+ devlink_flash_update_status_notify(dl, "Skipped",
+ component_name, 0, 0);
+ goto skip_component;
+ }
+
+ if (err) {
+ dev_err(dev,
+ "send_component failed offset 0x%x addr 0x%llx len 0x%x: %pe\n",
+ offset, dma_addr, copy_sz, ERR_PTR(err));
+ goto err_out;
+ }
+
+ offset += copy_sz;
+ devlink_flash_update_status_notify(dl,
+ "Erasing/Flashing",
+ component_name, offset,
+ total_len);
+ }
+
+ return 0;
+
+err_out:
+ devlink_flash_update_status_notify(dl, "Erasing/Flashing Component Failed",
+ component_name, 0, 0);
+skip_component:
+ vfree(component_info);
+ return err;
+}
+
+static int pdsc_devcmd_finalize_update(struct pdsc *pdsc)
+{
+ union pds_core_dev_cmd cmd = {
+ .finalize_update.opcode = PDS_CORE_CMD_FINALIZE_UPDATE,
+ .finalize_update.ver = 1,
+ };
+ union pds_core_dev_comp comp = {};
+
+ return pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
+}
+
+static int pdsc_finalize_update(struct pldmfw *context)
+{
+ struct pds_core_fwu_priv *priv =
+ container_of(context, struct pds_core_fwu_priv, context);
+ const char *component_name = priv->params->component;
+ unsigned long start_time, end_time;
+ struct device *dev = context->dev;
+ struct pdsc *pdsc = priv->pdsc;
+ struct devlink *dl;
+ int err;
+
+ dl = priv_to_devlink(pdsc);
+
+ start_time = jiffies;
+ end_time = start_time + (PDSC_FW_INSTALL_TIMEOUT * HZ);
+ do {
+ err = pdsc_devcmd_finalize_update(pdsc);
+ if (!err || err != -EAGAIN)
+ break;
+
+ dev_dbg(dev, "retrying finalize_update: %pe\n", ERR_PTR(err));
+ msleep(20);
+ } while (time_before(jiffies, end_time) && err == -EAGAIN);
+
+ if (err) {
+ devlink_flash_update_status_notify(dl, "Finalize Update Failed",
+ component_name, 0, 0);
+ dev_err(dev, "finalize_update failed: %pe\n", ERR_PTR(err));
+ return err;
+ }
+
+ devlink_flash_update_status_notify(dl, "Finalized Update",
+ component_name, 0, 0);
+ return 0;
+}
+
+static const struct pldmfw_ops pdsc_pldmfw_ops = {
+ .match_record = pdsc_match_record_descs,
+ .send_package_data = pdsc_send_package_data,
+ .send_component_table = pdsc_send_component_table,
+ .flash_component = pdsc_flash_component,
+ .finalize_update = pdsc_finalize_update
+};
+
+static int pdsc_pldm_firmware_update(struct pdsc *pdsc,
+ struct devlink_flash_update_params *params,
+ struct netlink_ext_ack *extack,
+ const struct firmware *fw)
+{
+ struct pds_core_fwu_priv priv = {};
+ int err;
+
+ /* If no component filter specified, devlink core didn't refresh cache,
+ * so we must refresh to handle stale cache from previous updates.
+ */
+ if (!params->component) {
+ err = pdsc_get_component_info(pdsc);
+ if (err) {
+ dev_err(pdsc->dev, "Failed to get component info: %pe\n", ERR_PTR(err));
+ return err;
+ }
+ }
+
+ INIT_LIST_HEAD(&priv.components);
+ priv.context.ops = &pdsc_pldmfw_ops;
+ priv.context.dev = pdsc->dev;
+ priv.params = params;
+ priv.pdsc = pdsc;
+
+ err = pldmfw_flash_image(&priv.context, fw);
+ pdsc_free_fwu_priv(&priv);
+
+ return err;
+}
+
+int pdsc_firmware_update(struct pdsc *pdsc,
+ struct devlink_flash_update_params *params,
+ struct netlink_ext_ack *extack)
+{
+ if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2 &&
+ pdsc->dev_ident.capabilities & cpu_to_le64(PDS_CORE_DEV_CAP_PLDM_FW_UPDATE))
+ return pdsc_pldm_firmware_update(pdsc, params, extack, params->fw);
+
+ return pdsc_legacy_firmware_update(pdsc, params->fw, extack);
+}
diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c
index 22db78343eb0..f0d0993f9d91 100644
--- a/drivers/net/ethernet/amd/pds_core/main.c
+++ b/drivers/net/ethernet/amd/pds_core/main.c
@@ -340,7 +340,9 @@ static int pdsc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
is_pf = !pdev->is_virtfn;
ops = is_pf ? &pdsc_dl_ops : &pdsc_dl_vf_ops;
- dl = devlink_alloc(ops, sizeof(struct pdsc), dev);
+ dl = devlink_alloc(ops, sizeof(struct pdsc) +
+ PDS_CORE_FW_COMPONENT_LIST_LEN *
+ sizeof(struct pds_core_fw_component_info), dev);
if (!dl)
return -ENOMEM;
pdsc = devlink_priv(dl);
diff --git a/include/linux/pds/pds_core_if.h b/include/linux/pds/pds_core_if.h
index 619186f26b5b..b8052985dddf 100644
--- a/include/linux/pds/pds_core_if.h
+++ b/include/linux/pds/pds_core_if.h
@@ -40,6 +40,13 @@ enum pds_core_cmd_opcode {
PDS_CORE_CMD_FW_DOWNLOAD = 4,
PDS_CORE_CMD_FW_CONTROL = 5,
+ PDS_CORE_CMD_GET_COMPONENT_INFO = 6,
+ PDS_CORE_CMD_SEND_PKG_DATA = 7,
+ PDS_CORE_CMD_SEND_COMPONENT_TBL = 8,
+ PDS_CORE_CMD_SEND_COMPONENT = 9,
+ PDS_CORE_CMD_FINALIZE_UPDATE = 10,
+ PDS_CORE_CMD_MATCH_RECORD_DESC = 11,
+
/* SR/IOV commands */
PDS_CORE_CMD_VF_GETATTR = 60,
PDS_CORE_CMD_VF_SETATTR = 61,
@@ -100,6 +107,14 @@ struct pds_core_drv_identity {
char driver_ver_str[32];
};
+/**
+ * enum pds_core_dev_capability - Device capabilities
+ * @PDS_CORE_DEV_CAP_PLDM_FW_UPDATE: Device only supports FW update via PLDM
+ */
+enum pds_core_dev_capability {
+ PDS_CORE_DEV_CAP_PLDM_FW_UPDATE = BIT(0),
+};
+
#define PDS_DEV_TYPE_MAX 16
/**
* struct pds_core_dev_identity - Device identity information
@@ -119,6 +134,8 @@ struct pds_core_drv_identity {
* value in usecs to device units using:
* device units = usecs * mult / div
* @vif_types: How many of each VIF device type is supported
+ * @max_fw_slots: Maximum number of fw slots/components
+ * only supported on version >= PDS_CORE_IDENTITY_VERSION_2
* @capabilities: Device capabilities
* only supported on version >= PDS_CORE_IDENTITY_VERSION_2
*/
@@ -133,6 +150,7 @@ struct pds_core_dev_identity {
__le32 intr_coal_mult;
__le32 intr_coal_div;
__le16 vif_types[PDS_DEV_TYPE_MAX];
+ __le16 max_fw_slots;
__le64 capabilities;
};
@@ -284,6 +302,7 @@ enum pds_core_fw_slot {
PDS_CORE_FW_SLOT_A = 1,
PDS_CORE_FW_SLOT_B = 2,
PDS_CORE_FW_SLOT_GOLD = 3,
+ PDS_CORE_FW_SLOT_MAX = 0xff,
};
/**
@@ -450,6 +469,348 @@ struct pds_core_vf_ctrl_comp {
u8 status;
};
+/**
+ * struct pds_core_send_pkg_data_cmd
+ * @opcode: Opcode PDS_CORE_CMD_SEND_PKG_DATA
+ * @ver: Driver's max support version of this command
+ * @total_len: Total length of the package data
+ * @offset: Offset in the package data, non-zero if multiple commands are
+ * needed for sending the package data
+ * @data_len: Length of data stored at data_pa
+ * @data_pa: Data physical address for DMA to device
+ *
+ * The package data may be too large to store in a single buffer, so multiple
+ * PDS_CORE_CMD_SEND_PKG_DATA devcmds may be needed.
+ */
+struct pds_core_send_pkg_data_cmd {
+ u8 opcode;
+ u8 ver;
+ __le16 total_len;
+ __le16 offset;
+ __le16 data_len;
+ __le64 data_pa;
+};
+
+/**
+ * struct pds_core_send_pkg_data_comp - Send package data completion
+ * @status: Status of the command (enum pds_core_status_code)
+ * @ver: Device's max supported version of this command
+ * @rsvd: Word boundary padding
+ */
+struct pds_core_send_pkg_data_comp {
+ u8 status;
+ u8 ver;
+ u8 rsvd[2];
+};
+
+/**
+ * struct pds_core_component_tbl - Component table details
+ * @comparison_stamp: Comparison stamp used for component version checks
+ * @classification: Vendor specific classification info
+ * @identifier: Component's ID
+ * @transfer_flag: Part of the component table this request represents
+ * @version_str_type: The types of strings used
+ * @version_str_len: Length of @version_str
+ * @version_str: Component version information
+ */
+struct pds_core_component_tbl {
+ __le32 comparison_stamp;
+ __le16 classification;
+ __le16 identifier;
+ u8 transfer_flag;
+ u8 version_str_type;
+ u8 version_str_len;
+ u8 version_str[];
+};
+
+/**
+ * struct pds_core_send_component_tbl_cmd
+ * @opcode: Opcode PDS_CORE_CMD_SEND_COMPONENT_TBL
+ * @ver: Driver's max support version of this command
+ * @slot_id: enum pds_core_fw_slot
+ * @rsvd: Word boundary padding
+ *
+ * Expects to find component table info (struct pds_core_component_tbl)
+ * in cmd_regs->data. Driver should keep the devcmd interface locked
+ * while preparing the component table info.
+ */
+struct pds_core_send_component_tbl_cmd {
+ u8 opcode;
+ u8 ver;
+ u8 slot_id;
+ u8 rsvd;
+};
+
+enum pds_core_component_resp_code {
+ PDS_CORE_COMPONENT_VALID = 0x0,
+ PDS_CORE_COMPONENT_STAMP_IDENTICAL = 0x1,
+ PDS_CORE_COMPONENT_STAMP_LOWER = 0x2,
+ PDS_CORE_COMPONENT_STAMP_OR_VERSION_INVALID = 0x3,
+ PDS_CORE_COMPONENT_CONFLICT = 0x4,
+ PDS_CORE_COMPONENT_PREREQS_NOT_MET = 0x5,
+ PDS_CORE_COMPONENT_NOT_SUPPORTED = 0x6,
+ PDS_CORE_COMPONENT_FW_TYPE_INVALID = 0xd0,
+};
+
+/**
+ * struct pds_core_send_component_tbl_comp
+ * @status: Status of the command (enum pds_core_status_code)
+ * @ver: Device's max supported version of this command
+ * @completion_code: Component completion code
+ * @response: Component response
+ * @response_code: Component response code
+ * @slot_id: Actual slot_id of the component (enum pds_core_fw_slot)
+ *
+ * When alternate firmware is requested via PDS_CORE_FW_SLOT_INVALID, the
+ * completion's slot_id will match the actual slot_id that will be flashed
+ * on success. When specific components are flashed, then the completion's
+ * slot_id will match the command's slot_id.
+ *
+ * On failure the slot_id will be set to PDS_CORE_FW_SLOT_MAX.
+ * On success the slot_id will be PDS_CORE_FW_SLOT_A, PDS_CORE_FW_SLOT_B, or
+ * PDS_CORE_FW_SLOT_GOLD.
+ *
+ * @rsvd: Word boundary padding
+ */
+struct pds_core_send_component_tbl_comp {
+ u8 status;
+ u8 ver;
+ u8 completion_code;
+ u8 response;
+ u8 response_code;
+ u8 slot_id;
+ u8 rsvd[2];
+};
+
+/**
+ * enum pds_core_send_component_op - PDS_CORE_CMD_SEND_COMPONENT operation
+ * @PDS_CORE_SEND_COMPONENT_START: Initial operation to start transfer
+ * @PDS_CORE_SEND_COMPONENT_STATUS: Subsequent calls to check on status
+ * PDS_CORE_CMD_SEND_COMPONENT
+ */
+enum pds_core_send_component_op {
+ PDS_CORE_SEND_COMPONENT_START = 0,
+ PDS_CORE_SEND_COMPONENT_STATUS = 1,
+};
+
+#define PDS_CORE_FW_COMPONENT_ID_INVALID 0xFFFF
+/**
+ * struct pds_core_flash_component - Component details
+ * @comparison_stamp: Comparison stamp used for component version checks
+ * @image_size: Component image size
+ * @classification: Vendor specific classification info
+ * @identifier: Component's ID
+ * @options: Component options
+ * @rsvd: Word boundary padding
+ * @version_str_type: The types of strings used
+ * @version_str_len: Length of @version_str
+ * @version_str: Component version information
+ */
+struct pds_core_flash_component {
+ __le32 comparison_stamp;
+ __le32 image_size;
+ __le16 classification;
+ __le16 identifier;
+ __le16 options;
+ u8 rsvd[3];
+ u8 version_str_type;
+ u8 version_str_len;
+ u8 version_str[];
+};
+
+/**
+ * struct pds_core_send_component_cmd
+ * @opcode: Opcode PDS_CORE_CMD_SEND_COMPONENT
+ * @ver: Driver's max supported version of this command
+ * @slot_id: enum pds_core_fw_slot
+ * @operation: enum pds_core_send_component_op
+ * @offset: Offset into the component, non-zero if multiple commands
+ * are needed for a single component
+ * @data_len: Length of this part of the component stored at @data_pa
+ * @rsvd: Word boundary padding
+ * @data_pa: DMA address of the component
+ *
+ * A component may be too large to store in a single buffer, so multiple
+ * PDS_CORE_CMD_SEND_COMPONENT devcmds may be needed.
+ *
+ * Expects to find flash component info (struct pds_core_flash_component)
+ * in cmd_regs->data. Driver should keep the devcmd interface locked
+ * while preparing and sending the flash component info.
+ */
+struct pds_core_send_component_cmd {
+ u8 opcode;
+ u8 ver;
+ u8 slot_id;
+ u8 operation;
+ __le32 offset;
+ __le32 data_len;
+ u8 rsvd[4];
+ __le64 data_pa;
+};
+
+/**
+ * struct pds_core_send_component_comp
+ * @status: Status of the command (enum pds_core_status_code)
+ * @ver: Device's max supported version of this command
+ * @completion_code: Completion code
+ * @compat_response: Compatibility response (0 = Component can be updated)
+ * @compat_response_code: Compatibility response code
+ * @rsvd: Word boundary padding
+ */
+struct pds_core_send_component_comp {
+ u8 status;
+ u8 ver;
+ u8 completion_code;
+ u8 compat_response;
+ u8 compat_response_code;
+ u8 rsvd[3];
+};
+
+/**
+ * enum pds_core_component_info_flags
+ * @PDS_CORE_FW_COMPONENT_INFO_F_RUNNING: Component is currently running
+ * @PDS_CORE_FW_COMPONENT_INFO_F_STARTUP: Component version on next FW boot
+ * @PDS_CORE_FW_COMPONENT_INFO_F_FIXED: Component is fixed and cannot be updated
+ * @PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME: Component can be updated by name
+ */
+enum pds_core_component_info_flags {
+ PDS_CORE_FW_COMPONENT_INFO_F_RUNNING = BIT(0),
+ PDS_CORE_FW_COMPONENT_INFO_F_STARTUP = BIT(1),
+ PDS_CORE_FW_COMPONENT_INFO_F_FIXED = BIT(2),
+ PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME = BIT(3),
+};
+
+/**
+ * struct pds_core_fw_component_info - GET_COMPONENT_INFO entry
+ * @name: Component's name
+ * @rsvd: Word boundary padding
+ * @flags: enum pds_core_component_info_flags
+ * @identifier: Component's identifier
+ * @slot_id: Component's slot identifier
+ * @version: Component's version
+ */
+struct pds_core_fw_component_info {
+#define PDS_CORE_FW_COMPONENT_NAME_BUFLEN 24
+ char name[PDS_CORE_FW_COMPONENT_NAME_BUFLEN];
+ u8 rsvd[4];
+ __le16 flags;
+ u8 identifier;
+ u8 slot_id;
+#define PDS_CORE_FW_COMPONENT_VER_BUFLEN 32
+ char version[PDS_CORE_FW_COMPONENT_VER_BUFLEN];
+};
+
+#define PDS_CORE_FW_COMPONENT_LIST_LEN ((PDS_PAGE_SIZE - \
+ sizeof(struct pds_core_component_list_info)) / \
+ sizeof(struct pds_core_fw_component_info))
+
+#if defined(__has_attribute) && !__has_attribute(__counted_by__)
+#define __counted_by(member)
+#endif
+
+/**
+ * struct pds_core_component_list_info - GET_COMPONENT_INFO completion data
+ * @num_components: Number of valid components
+ * @info: List of valid components
+ */
+struct pds_core_component_list_info {
+ u8 num_components;
+ struct pds_core_fw_component_info info[] __counted_by(num_components);
+} __packed;
+
+/**
+ * struct pds_core_get_component_info_cmd - GET_COMPONENT_INFO command
+ * @opcode: PDS_CORE_CMD_GET_COMPONENT_INFO
+ * @ver: Driver's max supported version of this command
+ * @data_len: Length of data at data_pa
+ * @rsvd: Word boundary padding
+ * @data_pa: DMA address of data
+ *
+ * FW populates struct pds_core_component_list_info pointed to by @data_pa
+ */
+struct pds_core_get_component_info_cmd {
+ u8 opcode;
+ u8 ver;
+ __le16 data_len;
+ u8 rsvd[4];
+ __le64 data_pa;
+};
+
+/**
+ * struct pds_core_get_component_info_comp - GET_COMPONENT_INFO completion
+ * @status: enum pds_core_status_code
+ * @ver: Device's max supported version of this command
+ * @rsvd: Word boundary padding
+ */
+struct pds_core_get_component_info_comp {
+ u8 status;
+ u8 ver;
+ u8 rsvd[2];
+};
+
+/**
+ * struct pds_core_finalize_update_cmd - FINALIZE_UPDATE command
+ * @opcode: PDS_CORE_CMD_FINALIZE_UPDATE
+ * @ver: Driver's max support version of this command
+ * @rsvd: Word boundary padding
+ *
+ * Driver sends at the end of updating all components to finalize the update
+ */
+struct pds_core_finalize_update_cmd {
+ u8 opcode;
+ u8 ver;
+ u8 rsvd[2];
+};
+
+/**
+ * struct pds_core_finalize_update_comp - FINALIZE_UPDATE completion
+ * @status: enum pds_core_status_code
+ * @ver: Device's max supported version of this command
+ * @rsvd: Word boundary padding
+ */
+struct pds_core_finalize_update_comp {
+ u8 status;
+ u8 ver;
+ u8 rsvd[2];
+};
+
+/**
+ * struct pds_core_match_record_desc_cmd - MATCH_RECORD_DESC command
+ * @opcode: PDS_CORE_CMD_MATCH_RECORD_DESC
+ * @ver: Driver's max supported version of this command
+ * @type: PLDM Descriptor Identifier Type
+ * @size: Length of the Descriptor Identifier Value
+ * @rsvd: Word boundary padding
+ *
+ * Expects to find the Descriptor Identifier Data in cmd_regs->data. Driver
+ * should keep the devcmd interface locked while preparing and sending this
+ * command.
+ */
+struct pds_core_match_record_desc_cmd {
+ u8 opcode;
+ u8 ver;
+ __le16 type;
+ __le16 size;
+ u8 rsvd[2];
+};
+
+/**
+ * struct pds_core_match_record_desc_comp - MATCH_RECORD_DESC completion
+ * @status: enum pds_core_status_code
+ * @ver: Device's max supported version of this command
+ * @match: Whether or not the Record Descriptor matches the device
+ * @rsvd: Word boundary padding
+ *
+ * When status is PDS_RC_SUCCESS, then @match is valid, otherwise it's
+ * undefined.
+ */
+struct pds_core_match_record_desc_comp {
+ u8 status;
+ u8 ver;
+ u8 match;
+ u8 rsvd;
+};
+
/*
* union pds_core_dev_cmd - Overlay of core device command structures
*/
@@ -466,6 +827,13 @@ union pds_core_dev_cmd {
struct pds_core_vf_setattr_cmd vf_setattr;
struct pds_core_vf_getattr_cmd vf_getattr;
struct pds_core_vf_ctrl_cmd vf_ctrl;
+
+ struct pds_core_get_component_info_cmd get_component_info;
+ struct pds_core_send_pkg_data_cmd send_pkg_data;
+ struct pds_core_send_component_tbl_cmd send_component_tbl;
+ 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;
};
/*
@@ -484,6 +852,13 @@ union pds_core_dev_comp {
struct pds_core_vf_setattr_comp vf_setattr;
struct pds_core_vf_getattr_comp vf_getattr;
struct pds_core_vf_ctrl_comp vf_ctrl;
+
+ struct pds_core_get_component_info_comp get_component_info;
+ struct pds_core_send_pkg_data_comp send_pkg_data;
+ struct pds_core_send_component_tbl_comp send_component_tbl;
+ 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;
};
/**
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 6/6] pds_core: add debugfs support for host backed memory
From: Nikhil P. Rao @ 2026-04-29 8:28 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner,
Vamsi Atluri
In-Reply-To: <20260429-b4-pldm-b4-v1-0-394fafba526f@amd.com>
From: Vamsi Atluri <Vamsi.Atluri@amd.com>
Add debugfs file to display host memory allocations including tag,
size, order, and physical address for each memory request.
Signed-off-by: Vamsi Atluri <Vamsi.Atluri@amd.com>
---
drivers/net/ethernet/amd/pds_core/debugfs.c | 43 +++++++++++++++++++++++++++++
drivers/net/ethernet/amd/pds_core/main.c | 2 ++
2 files changed, 45 insertions(+)
diff --git a/drivers/net/ethernet/amd/pds_core/debugfs.c b/drivers/net/ethernet/amd/pds_core/debugfs.c
index 04c5e3abd8d7..79312107f721 100644
--- a/drivers/net/ethernet/amd/pds_core/debugfs.c
+++ b/drivers/net/ethernet/amd/pds_core/debugfs.c
@@ -173,3 +173,46 @@ void pdsc_debugfs_del_qcq(struct pdsc_qcq *qcq)
debugfs_remove_recursive(qcq->dentry);
qcq->dentry = NULL;
}
+
+static int host_mem_show(struct seq_file *seq, void *v)
+{
+ struct pdsc *pdsc = seq->private;
+ struct pdsc_host_mem *hm;
+ int i;
+
+ if (!pdsc->host_mem_reqs || pdsc->num_host_mem_reqs == 0) {
+ seq_puts(seq, "No host memory allocated\n");
+ return 0;
+ }
+
+ seq_printf(seq, "Host memory requests: %d\n\n", pdsc->num_host_mem_reqs);
+ seq_puts(seq, "Tag Size Order PA\n");
+ seq_puts(seq, "--- ---- ----- --\n");
+
+ for (i = 0; i < pdsc->num_host_mem_reqs; i++) {
+ hm = &pdsc->host_mem_reqs[i];
+
+ if (!hm->pg)
+ continue;
+
+ seq_printf(seq, "%-6d %-12u %-6d 0x%llx\n",
+ hm->tag, hm->size, hm->order,
+ (unsigned long long)hm->pa);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(host_mem);
+
+void pdsc_debugfs_add_host_mem(struct pdsc *pdsc)
+{
+ if (!(pdsc->dev_ident.capabilities & cpu_to_le64(PDS_CORE_DEV_CAP_HOST_MEM)))
+ return;
+
+ /* This file will already exist in the reset flow */
+ if (debugfs_lookup("host_mem", pdsc->dentry))
+ return;
+
+ debugfs_create_file("host_mem", 0400, pdsc->dentry,
+ pdsc, &host_mem_fops);
+}
diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c
index 0a0542bf7cbb..4c14198eaafe 100644
--- a/drivers/net/ethernet/amd/pds_core/main.c
+++ b/drivers/net/ethernet/amd/pds_core/main.c
@@ -264,6 +264,8 @@ static int pdsc_init_pf(struct pdsc *pdsc)
mutex_unlock(&pdsc->config_lock);
+ pdsc_debugfs_add_host_mem(pdsc);
+
err = pdsc_auxbus_dev_add(pdsc, pdsc, PDS_DEV_TYPE_FWCTL, &pdsc->padev);
if (err)
goto err_out_stop;
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 2/6] pds_core: add support for identity version 2
From: Nikhil P. Rao @ 2026-04-29 8:28 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner
In-Reply-To: <20260429-b4-pldm-b4-v1-0-394fafba526f@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
Add a new capabilities field in struct pds_core_drv_identity,
which requires bumping the identity version to 2, i.e.
PDS_CORE_IDENTITY_VERSION_2. If version 2 negotiation fails,
then quietly fall back to version 1. If version 1 negotiation
fails, then driver load will fail.
Another patch in the series will make use of the capabilities
field.
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
---
drivers/net/ethernet/amd/pds_core/dev.c | 28 +++++++++++++++++++++++-----
include/linux/pds/pds_core_if.h | 4 ++++
2 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
index 5b86d6cd0ac3..f77bd5e48b92 100644
--- a/drivers/net/ethernet/amd/pds_core/dev.c
+++ b/drivers/net/ethernet/amd/pds_core/dev.c
@@ -243,15 +243,17 @@ int pdsc_devcmd_reset(struct pdsc *pdsc)
return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
}
-static int pdsc_devcmd_identify_locked(struct pdsc *pdsc)
+static int pdsc_devcmd_identify_locked(struct pdsc *pdsc, u8 drv_ident_ver,
+ bool do_msg)
{
union pds_core_dev_comp comp = {};
union pds_core_dev_cmd cmd = {
.identify.opcode = PDS_CORE_CMD_IDENTIFY,
- .identify.ver = PDS_CORE_IDENTITY_VERSION_1,
+ .identify.ver = drv_ident_ver,
};
- return pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
+ return __pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout,
+ do_msg);
}
static void pdsc_init_devinfo(struct pdsc *pdsc)
@@ -274,8 +276,9 @@ static void pdsc_init_devinfo(struct pdsc *pdsc)
dev_dbg(pdsc->dev, "fw_version %s\n", pdsc->dev_info.fw_version);
}
-static int pdsc_identify(struct pdsc *pdsc)
+static int pdsc_identify_ver(struct pdsc *pdsc, u8 drv_ident_ver)
{
+ bool do_msg = drv_ident_ver == PDS_CORE_IDENTITY_VERSION_1;
struct pds_core_drv_identity drv = {};
size_t sz;
int err;
@@ -298,7 +301,7 @@ static int pdsc_identify(struct pdsc *pdsc)
sz = min_t(size_t, sizeof(drv), sizeof(pdsc->cmd_regs->data));
memcpy_toio(&pdsc->cmd_regs->data, &drv, sz);
- err = pdsc_devcmd_identify_locked(pdsc);
+ err = pdsc_devcmd_identify_locked(pdsc, drv_ident_ver, do_msg);
if (!err) {
sz = min_t(size_t, sizeof(pdsc->dev_ident),
sizeof(pdsc->cmd_regs->data));
@@ -327,6 +330,21 @@ static int pdsc_identify(struct pdsc *pdsc)
return 0;
}
+static int pdsc_identify(struct pdsc *pdsc)
+{
+ int err;
+
+ /* Older firmware rejects anything but PDS_CORE_IDENTIFY_VERSION_1
+ * instead of returning the max supported identify version, so retry if
+ * firmware doesn't support PDS_CORE_IDENTIFY_VERSION_2
+ */
+ err = pdsc_identify_ver(pdsc, PDS_CORE_IDENTITY_VERSION_2);
+ if (err)
+ err = pdsc_identify_ver(pdsc, PDS_CORE_IDENTITY_VERSION_1);
+
+ return err;
+}
+
void pdsc_dev_uninit(struct pdsc *pdsc)
{
if (pdsc->intr_info) {
diff --git a/include/linux/pds/pds_core_if.h b/include/linux/pds/pds_core_if.h
index 17a87c1a55d7..619186f26b5b 100644
--- a/include/linux/pds/pds_core_if.h
+++ b/include/linux/pds/pds_core_if.h
@@ -119,6 +119,8 @@ struct pds_core_drv_identity {
* value in usecs to device units using:
* device units = usecs * mult / div
* @vif_types: How many of each VIF device type is supported
+ * @capabilities: Device capabilities
+ * only supported on version >= PDS_CORE_IDENTITY_VERSION_2
*/
struct pds_core_dev_identity {
u8 version;
@@ -131,9 +133,11 @@ struct pds_core_dev_identity {
__le32 intr_coal_mult;
__le32 intr_coal_div;
__le16 vif_types[PDS_DEV_TYPE_MAX];
+ __le64 capabilities;
};
#define PDS_CORE_IDENTITY_VERSION_1 1
+#define PDS_CORE_IDENTITY_VERSION_2 2
/**
* struct pds_core_dev_identify_cmd - Driver/device identify command
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 1/6] pds_core: add support for quiet devcmd failures
From: Nikhil P. Rao @ 2026-04-29 8:28 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner
In-Reply-To: <20260429-b4-pldm-b4-v1-0-394fafba526f@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
Currently there aren't any use-cases that require special handling
on whether or not to print devcmd failures. Specifically
non-generic failures, i.e. not supported failures. Add support to
allow these messages to be suppressed. This will be used when
adding support to negotiate PDS_CORE_IDENTITY_VERSION_2.
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
---
drivers/net/ethernet/amd/pds_core/dev.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
index 2e1d0d01d03a..5b86d6cd0ac3 100644
--- a/drivers/net/ethernet/amd/pds_core/dev.c
+++ b/drivers/net/ethernet/amd/pds_core/dev.c
@@ -126,7 +126,8 @@ static const char *pdsc_devcmd_str(int opcode)
}
}
-static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)
+static int __pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds,
+ const bool do_msg)
{
struct device *dev = pdsc->dev;
unsigned long start_time;
@@ -172,7 +173,7 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)
status = pdsc_devcmd_status(pdsc);
err = pdsc_err_to_errno(status);
- if (err && err != -EAGAIN)
+ if (do_msg && err && err != -EAGAIN)
dev_err(dev, "DEVCMD %d %s failed, status=%d err %d %pe\n",
opcode, pdsc_devcmd_str(opcode), status, err,
ERR_PTR(err));
@@ -180,8 +181,9 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)
return err;
}
-int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
- union pds_core_dev_comp *comp, int max_seconds)
+static int __pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ union pds_core_dev_comp *comp, int max_seconds,
+ const bool do_msg)
{
int err;
@@ -190,7 +192,7 @@ int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
memcpy_toio(&pdsc->cmd_regs->cmd, cmd, sizeof(*cmd));
pdsc_devcmd_dbell(pdsc);
- err = pdsc_devcmd_wait(pdsc, cmd->opcode, max_seconds);
+ err = __pdsc_devcmd_wait(pdsc, cmd->opcode, max_seconds, do_msg);
if ((err == -ENXIO || err == -ETIMEDOUT) && pdsc->wq)
queue_work(pdsc->wq, &pdsc->health_work);
@@ -200,6 +202,12 @@ int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
return err;
}
+int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ union pds_core_dev_comp *comp, int max_seconds)
+{
+ return __pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds, true);
+}
+
int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds)
{
--
2.43.0
^ permalink raw reply related
* [PATCH net-next 0/6] pds_core: Add PLDM firmware update and host backed memory support
From: Nikhil P. Rao @ 2026-04-29 8:28 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner,
Vamsi Atluri
This series adds PLDM-based firmware update support to the pds_core
driver. PLDM (Platform Level Data Model) is a DMTF standard for firmware
management that provides a vendor-neutral interface for firmware updates.
The implementation uses the kernel's pldmfw library for package parsing
and component matching. Users can update entire firmware packages or
individual components via devlink flash. Component information is
displayed via devlink info, showing firmware versions and update status
for each component.
The series also adds host backed memory support, allowing firmware to
request memory pages from the host for its operations.
Note: Resending with net-next prefix. No code changes from initial submission.
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
---
Brett Creeley (4):
pds_core: add support for quiet devcmd failures
pds_core: add support for identity version 2
pds_core: add PLDM firmware update support via devlink flash
pds_core: add PLDM component info display
Vamsi Atluri (2):
pds_core: add host backed memory support for firmware
pds_core: add debugfs support for host backed memory
drivers/net/ethernet/amd/Kconfig | 1 +
drivers/net/ethernet/amd/pds_core/core.c | 166 +++++++
drivers/net/ethernet/amd/pds_core/core.h | 33 +-
drivers/net/ethernet/amd/pds_core/debugfs.c | 43 ++
drivers/net/ethernet/amd/pds_core/dev.c | 86 +++-
drivers/net/ethernet/amd/pds_core/devlink.c | 77 ++-
drivers/net/ethernet/amd/pds_core/fw.c | 699 +++++++++++++++++++++++++++-
drivers/net/ethernet/amd/pds_core/main.c | 7 +-
include/linux/pds/pds_adminq.h | 132 ++++++
include/linux/pds/pds_core_if.h | 381 +++++++++++++++
10 files changed, 1603 insertions(+), 22 deletions(-)
---
base-commit: 1f5ffc672165ff851063a5fd044b727ab2517ae3
change-id: 20260429-b4-pldm-b4-b36169e986e6
Best regards,
--
Nikhil P. Rao <nikhil.rao@amd.com>
^ permalink raw reply
* Re: [PATCH v2 2/2] MAINTAINERS: update PTP maintainer entries after directory split
From: Wen Gu @ 2026-04-29 8:28 UTC (permalink / raw)
To: Jakub Kicinski, David Woodhouse
Cc: tglx, richardcochran, andrew+netdev, davem, edumazet, pabeni,
linux-kernel, netdev, jstultz, anna-maria, frederic,
daniel.lezcano, sboyd, vladimir.oltean, wei.fang, xiaoning.wang,
jonathan.lemon, vadim.fedorenko, yangbo.lu, svens, nick.shi,
ajay.kaher, alexey.makhalov, bcm-kernel-feedback-list, linux-fpga,
imx, linux-s390, dust.li, xuanzhuo, mani, imran.shaik, taniya.das
In-Reply-To: <ebf19246-91af-4887-b2aa-d9007921f7b2@linux.alibaba.com>
On 2026/4/13 17:00, Wen Gu wrote:
>
>
> On 2026/4/13 00:53, Jakub Kicinski wrote:
>> On Sun, 12 Apr 2026 17:32:22 +0100 David Woodhouse wrote:
>>> On 12 April 2026 16:47:04 BST, Jakub Kicinski <kuba@kernel.org> wrote:
>>>> On Tue, 7 Apr 2026 18:48:02 +0800 Wen Gu wrote:
>>>>> +PTP EMULATED CLOCK SUPPORT
>>>>> +M: David Woodhouse <dwmw2@infradead.org>
>>>>> +M: Wen Gu <guwen@linux.alibaba.com>
>>>>> +M: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
>>>>> +L: linux-kernel@vger.kernel.org
>>>>> +S: Maintained
>>>>> +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/core
>>>>
>>>> Hi David,
>>>>
>>>> Do you have a tree to route the patches thru? Or do you really have
>>>> access to the tip tree?
>>>
>>> I do not have access to the tip tree. I can make a shared tree on
>>> git.infradead.org if the other two maintainers would like to send me
>>> a SSH pubkey and preferred username...
>>
>> Honestly I'd love for you to be the only M here, and the other two
>> to be reviewers. Xuan Zhuo is currently at v40 trying to upstream
>> an Ethernet driver. Some growth needed there to become a subsystem
>> maintainer IMO.
>
> Hi Jakub, David,
>
> That works for us. We can act as reviewers.
>
> If David sets up a new tree, I will update the MAINTAINERS entry
> accordingly in v3.
Hi David,
Just checking if there is any update on the maintainer tree
for the emulated PTP drivers.
Thanks.
>
> Thanks.
^ permalink raw reply
* [PATCH net-next] net: mctp: test: remove skb dumps from test output
From: Jeremy Kerr @ 2026-04-29 8:27 UTC (permalink / raw)
To: Matt Johnston, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
Cc: netdev
We're currently dumping skb info in our fragment input test, which makes
interpreting the TAP test output a bit awkward.
Remove the skb dumps.
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
---
net/mctp/test/route-test.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/net/mctp/test/route-test.c b/net/mctp/test/route-test.c
index e1033643fab0..fbd6d4e08aef 100644
--- a/net/mctp/test/route-test.c
+++ b/net/mctp/test/route-test.c
@@ -859,12 +859,6 @@ static void mctp_test_route_input_cloned_frag(struct kunit *test)
skb[0]->data[4] = 0;
skb[3]->data[4] = 0;
- skb_dump("pkt1 ", skb[0], false);
- skb_dump("pkt2 ", skb[1], false);
- skb_dump("pkt3 ", skb[2], false);
- skb_dump("pkt4 ", skb[3], false);
- skb_dump("pkt5 ", skb[4], false);
-
for (int i = 0; i < 5; i++) {
KUNIT_EXPECT_EQ(test, refcount_read(&skb[i]->users), 1);
/* Take a reference so we can check refcounts at the end */
---
base-commit: dca922e019dd758b4c1b4bec8f1d509efddeaab4
change-id: 20260429-dev-mctp-test-skb-dump-cc5b95f8e2ad
Best regards,
--
Jeremy Kerr <jk@codeconstruct.com.au>
^ permalink raw reply related
* [PATCH net 2/2] net: mctp: test: Use dev_direct_xmit for TX to our test device
From: Jeremy Kerr @ 2026-04-29 8:21 UTC (permalink / raw)
To: Matt Johnston, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
Cc: netdev, kernel test robot
In-Reply-To: <20260429-dev-mctp-test-fixes-v1-0-1127b7425809@codeconstruct.com.au>
In our test cases, we typically feed a packet sequence into the routing
code, then inspect the device's TXed skbs to assert specific behaviours.
Using dev_queue_xmit() for our TX path introduces a fair bit of
complexity between the test packet sequence and the test device's
ndo_start_xmit callback; which may mean that the skbs have not hit the
device at the point we're inspecting the TXed skb list.
Use dev_direct_xmit instead, as we want a direct a path as possible
here, and the test dev does not need any queueing, scheduling or flow
control.
Fixes: 6ab578739a4c ("net: mctp: test: move TX packetqueue from dst to dev")
Reported-by: kernel test robot <oliver.sang@intel.com>
Closes: https://lore.kernel.org/oe-lkp/202604281320.525eee17-lkp@intel.com
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
---
net/mctp/test/utils.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/mctp/test/utils.c b/net/mctp/test/utils.c
index c3987d5ade7a..6eef8d485c25 100644
--- a/net/mctp/test/utils.c
+++ b/net/mctp/test/utils.c
@@ -116,7 +116,7 @@ void mctp_test_destroy_dev(struct mctp_test_dev *dev)
static int mctp_test_dst_output(struct mctp_dst *dst, struct sk_buff *skb)
{
skb->dev = dst->dev->dev;
- dev_queue_xmit(skb);
+ dev_direct_xmit(skb, 0);
return 0;
}
--
2.39.5
^ permalink raw reply related
* [PATCH net 1/2] net: mctp: test: use a zeroed struct sockaddr_mctp
From: Jeremy Kerr @ 2026-04-29 8:21 UTC (permalink / raw)
To: Matt Johnston, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
Cc: netdev
In-Reply-To: <20260429-dev-mctp-test-fixes-v1-0-1127b7425809@codeconstruct.com.au>
Invalid sockaddr padding will cause bind() to fail; ensure we have a
zeroed address in the testcase.
Fixes: 0d8647bc74cb ("net: mctp: don't require a route for null-EID ingress")
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
---
net/mctp/test/route-test.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/mctp/test/route-test.c b/net/mctp/test/route-test.c
index e1033643fab0..e4b230ef6099 100644
--- a/net/mctp/test/route-test.c
+++ b/net/mctp/test/route-test.c
@@ -920,9 +920,9 @@ static void mctp_test_route_input_cloned_frag(struct kunit *test)
static void mctp_test_route_input_null_eid(struct kunit *test)
{
struct mctp_hdr hdr = RX_HDR(1, 10, 0, FL_S | FL_E | FL_TO);
+ struct sockaddr_mctp addr = { 0 };
struct sk_buff *skb_pkt, *skb_sk;
struct mctp_test_dev *dev;
- struct sockaddr_mctp addr;
struct socket *sock;
u8 type = 0;
int rc;
--
2.39.5
^ permalink raw reply related
* [PATCH net 0/2] net: mctp: test: minor kunit test fixes
From: Jeremy Kerr @ 2026-04-29 8:21 UTC (permalink / raw)
To: Matt Johnston, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
Cc: netdev, kernel test robot
This series provides two fixes in the MCTP kunit tests - one exposed by
ktr, and one found while debugging the former on different VM configs.
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
---
Jeremy Kerr (2):
net: mctp: test: use a zeroed struct sockaddr_mctp
net: mctp: test: Use dev_direct_xmit for TX to our test device
net/mctp/test/route-test.c | 2 +-
net/mctp/test/utils.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
---
base-commit: dca922e019dd758b4c1b4bec8f1d509efddeaab4
change-id: 20260429-dev-mctp-test-fixes-de1505add39e
Best regards,
--
Jeremy Kerr <jk@codeconstruct.com.au>
^ permalink raw reply
* Re: [PATCH net-next v3 0/4] net: dsa: mt7628 embedded switch initial support
From: Joris Vaisvila @ 2026-04-29 8:21 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: netdev, horms, pabeni, kuba, edumazet, davem, olteanv,
Andrew Lunn, devicetree, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
In-Reply-To: <20260429-impossible-archetypal-bear-607a7b@quoll>
On Wed, Apr 29, 2026 at 09:09:44AM +0200, Krzysztof Kozlowski wrote:
> On Tue, Apr 28, 2026 at 09:55:06PM +0300, Joris Vaisvila wrote:
> > Hello,
> >
> > This patch series adds initial support for the MediaTek MT7628 Embedded
> > Switch.
> >
> > The driver implements the basic functionality required to operate the
> > switch using DSA. The hardware provides five internal Fast Ethernet user
> > ports and one Gigabit port connected internally to the CPU MAC.
> >
> > Bridge offloading is not yet supported.
> >
> > Tested on an MT7628NN-based board.
> >
> > changes since v2:
> > - fix binding issues found in review
>
> Which issues exactly?
>
> This has to be specific.
>
> Best regards,
> Krzysztof
>
Hi Krzysztof,
My bad. These are the binding changes since v2:
- Removed description from reg property
- Clarify reset descriptions
- Added ethernet-ports to required
- Fix reg coming before compatible in the example
- Replaced 'ports' and 'port' with 'ethernet-ports' and 'ethernet-port'
respectively in the example
^ permalink raw reply
* [PATCH v3 net] net: enetc: fix VSI mailbox timeout handling and DMA lifecycle
From: Wei Fang @ 2026-04-29 8:19 UTC (permalink / raw)
To: claudiu.manoil, vladimir.oltean, xiaoning.wang, andrew+netdev,
davem, edumazet, kuba, pabeni
Cc: netdev, linux-kernel, imx
In the current VSI mailbox implementation, the VSI allocates a DMA buffer
to store the message sent to the PSI. When the PSI receives the message
request from the VSI, the hardware copies the message data from this DMA
buffer to PSI's DMA buffer for processing.
When enetc_msg_vsi_send() times out, two scenarios can occur:
1) Use-after-free: If the hardware hasn't completed message copying when
the VSI frees the buffer, the hardware may subsequently copy the data
from freed memory to PSI's DMA buffer.
2) Message race: If PSI hasn't processed the previous message when the
next message is sent, the VSI may receive the previous message's
reply, leading to incorrect handling.
To address these issues, implement the following changes:
- Check the mailbox busy status before sending a new message. If the
mailbox is in busy state, it indicates the previous message is still
being processed, so return an error immediately.
- Add the 'msg' field to struct enetc_si to preserve the DMA buffer
information. The caller of enetc_msg_vsi_send() no longer frees the
DMA buffer. Instead, defer freeing until it is safe to do so (when
mailbox is not busy on next send).
- Add cleanup in enetc_vf_remove() to free the last message buffer.
This ensures the DMA buffer remains valid during message copying and
prevents message reply mismatches.
Fixes: beb74ac878c8 ("enetc: Add vf to pf messaging support")
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
v3:
1. Save si->msg after unregister_netdev() to avoid a race between the
local copy and a concurrent ndo callback.
v2:
1. Update commit message
2. Return -EIO instead of -EBUSY when VSIMSGSR_MB bit check fails
3. Move enetc_msg_dma_free() after enetc_pci_remove()
---
drivers/net/ethernet/freescale/enetc/enetc.h | 1 +
.../net/ethernet/freescale/enetc/enetc_vf.c | 42 +++++++++++++++----
2 files changed, 35 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index e663bb5e614e..e691144e8756 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -330,6 +330,7 @@ struct enetc_si {
struct workqueue_struct *workqueue;
struct work_struct rx_mode_task;
struct dentry *debugfs_root;
+ struct enetc_msg_swbd msg; /* Only valid for VSI */
};
#define ENETC_SI_ALIGN 32
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
index 6c4b374bcb0e..df8e95cc47d0 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
@@ -17,11 +17,36 @@ static void enetc_msg_vsi_write_msg(struct enetc_hw *hw,
enetc_wr(hw, ENETC_VSIMSGSNDAR0, val);
}
+static void enetc_msg_dma_free(struct device *dev, struct enetc_msg_swbd *msg)
+{
+ if (msg->vaddr) {
+ dma_free_coherent(dev, msg->size, msg->vaddr, msg->dma);
+ msg->vaddr = NULL;
+ }
+}
+
static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg)
{
+ struct device *dev = &si->pdev->dev;
int timeout = 100;
u32 vsimsgsr;
+ /* The VSI mailbox may be busy if last message was not yet processed
+ * by PSI. So need to check the mailbox status before sending.
+ */
+ vsimsgsr = enetc_rd(&si->hw, ENETC_VSIMSGSR);
+ if (vsimsgsr & ENETC_VSIMSGSR_MB) {
+ /* It is safe to free the DMA buffer here, the caller does
+ * not access the DMA buffer if enetc_msg_vsi_send() fails.
+ */
+ enetc_msg_dma_free(dev, msg);
+ dev_err(dev, "VSI mailbox is busy\n");
+ return -EIO;
+ }
+
+ /* Free the DMA buffer of the last message */
+ enetc_msg_dma_free(dev, &si->msg);
+ si->msg = *msg;
enetc_msg_vsi_write_msg(&si->hw, msg);
do {
@@ -32,12 +57,15 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg)
usleep_range(1000, 2000);
} while (--timeout);
- if (!timeout)
+ if (!timeout) {
+ dev_err(dev, "VSI mailbox timeout\n");
+
return -ETIMEDOUT;
+ }
/* check for message delivery error */
if (vsimsgsr & ENETC_VSIMSGSR_MS) {
- dev_err(&si->pdev->dev, "VSI command execute error: %d\n",
+ dev_err(dev, "VSI command execute error: %d\n",
ENETC_SIMSGSR_GET_MC(vsimsgsr));
return -EIO;
}
@@ -50,7 +78,6 @@ static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv,
{
struct enetc_msg_cmd_set_primary_mac *cmd;
struct enetc_msg_swbd msg;
- int err;
msg.size = ALIGN(sizeof(struct enetc_msg_cmd_set_primary_mac), 64);
msg.vaddr = dma_alloc_coherent(priv->dev, msg.size, &msg.dma,
@@ -67,11 +94,7 @@ static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv,
memcpy(&cmd->mac, saddr, sizeof(struct sockaddr));
/* send the command and wait */
- err = enetc_msg_vsi_send(priv->si, &msg);
-
- dma_free_coherent(priv->dev, msg.size, msg.vaddr, msg.dma);
-
- return err;
+ return enetc_msg_vsi_send(priv->si, &msg);
}
static int enetc_vf_set_mac_addr(struct net_device *ndev, void *addr)
@@ -259,6 +282,7 @@ static void enetc_vf_remove(struct pci_dev *pdev)
{
struct enetc_si *si = pci_get_drvdata(pdev);
struct enetc_ndev_priv *priv;
+ struct enetc_msg_swbd msg;
priv = netdev_priv(si->ndev);
unregister_netdev(si->ndev);
@@ -270,7 +294,9 @@ static void enetc_vf_remove(struct pci_dev *pdev)
free_netdev(si->ndev);
+ msg = si->msg;
enetc_pci_remove(pdev);
+ enetc_msg_dma_free(&pdev->dev, &msg);
}
static const struct pci_device_id enetc_vf_id_table[] = {
--
2.34.1
^ permalink raw reply related
* Re: [RFC PATCH net-next 1/2] net: napi: Fix interrupts permanently disabled during busy poll
From: Dragos Tatulea @ 2026-04-29 8:13 UTC (permalink / raw)
To: Jakub Kicinski, Martin Karsten
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Daniel Borkmann, Björn Töpel, Gal Pressman,
Tariq Toukan, Joe Damato, Frederik Deweerdt, netdev, linux-kernel
In-Reply-To: <20260428173154.7b6864ef@kernel.org>
On Tue, Apr 28, 2026 at 05:31:54PM -0700, Jakub Kicinski wrote:
> On Tue, 28 Apr 2026 20:04:13 -0400 Martin Karsten wrote:
> > On 2026-04-28 19:40, Jakub Kicinski wrote:
> > > On Tue, 28 Apr 2026 17:51:30 +0000 Dragos Tatulea wrote:
> > >> Under certain conditions a queue can be left out with interrupts
> > >> disabled and with the napi re-scheduling timer permanently stopped.
> > >> This behaviour is triggered by the napi busy poll path when
> > >> gro-flush-timeout and defer-hard-irq are set. Here's a sequence of
> > >> operations:
> > >>
> > >> 1. Busy poll starts, NAPI_STATE_SCHED is set to avoid rescheduling napi
> > >> from the timer.
> > >>
> > >> 2. During napi poll, driver disables interrupts due to being in poll
> > >> mode (napi_complete_done() returns false because napi->state has
> > >> NAPIF_STATE_IN_BUSY_POLL set).
> > >
> > > Why does the driver have IRQs disabled in busy poll?
> >
> > The problems occurs in irq deferral mode when both gro-flush-timeout and
> > defer-hard-irqs are nonzero and NIC interrupts are disabled.
>
> Okay.
>
> > >> 3. At the end of the busy poll (busy_poll_stop()):
> > >> 3.1 napi timer is scheduled and skip_schedule is set (due to config)
> > >> 3.2 napi->poll() is called:
> > >> - driver poll() processes exactly budget packets
> > >> and exits early => napi not scheduled.
> > >> (interrupts are still disabled at this point)
> > >> 3.3 Since napi poll processed budget packets, __busy_poll_stop()
> > >> is called with skip_schedule set => napi is not scheduled here
> > >> either.
> > >
> > > with skip_schedule it calls:
> > >
> > > clear_bit(NAPI_STATE_SCHED, &napi->state);
> > >
> > >> 4. If the napi timer from 3.1 gets to be triggered due to slow napi poll
> > >> or some other reason, the timer will run with no effect (due to
> > >> NAPI_STATE_SCHED being set).
> > >
> > > And here you claim STATE_SCHED is still set?
> >
> > Labelling this with number 4. might be misleading, sorry! The concern is
> > that a short enough timer (compared to the duration of the driver poll)
> > can be triggered before the NAPI_STATE_SCHED bit is cleared at the end
> > of Step 3.3.
>
> Ah. Just say that :D Two pages of buggy text, y'all would have been
> better off using this one paragraph as the commit message.
> Please don't use AI for generating commit messages if that's the cause.
> It really is spectacularly shit at it.
I take the blame for this. Funnily enough, the text was written mostly
without AI... Just wanted to present the interactions in a more explanatory
way.
Do you prefer the short version from Martin or an improved version of
the long explanation?
Thanks,
Dragos
^ permalink raw reply
* Re: [PATCH v3 3/3] p54spi: convert to devicetree
From: Johannes Berg @ 2026-04-29 8:09 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Arnd Bergmann, Aaro Koskinen, Andreas Kemnade,
Bartosz Golaszewski, Benoît Cousson, David S. Miller,
Dmitry Torokhov, Eric Dumazet, Felipe Balbi, Jakub Kicinski,
Kevin Hilman, Krzysztof Kozlowski, Linus Walleij, Paolo Abeni,
Rob Herring, Roger Quadros, Tony Lindgren, linux-wireless, netdev,
devicetree, linux-kernel, linux-arm-kernel, linux-gpio,
linux-omap, Christian Lamparter
In-Reply-To: <20260427142355.2532714-4-arnd@kernel.org>
Since you got comments anyway...
> }
>
> - irq_set_irq_type(gpio_to_irq(p54spi_gpio_irq), IRQ_TYPE_EDGE_RISING);
>
> INIT_WORK(&priv->work, p54spi_work);
This leaves two adjacent blank lines.
johannes
^ permalink raw reply
* Re: [PATCH v3 3/3] p54spi: convert to devicetree
From: Krzysztof Kozlowski @ 2026-04-29 8:07 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Arnd Bergmann, Aaro Koskinen, Andreas Kemnade,
Bartosz Golaszewski, Benoît Cousson, David S. Miller,
Dmitry Torokhov, Eric Dumazet, Felipe Balbi, Jakub Kicinski,
Johannes Berg, Kevin Hilman, Krzysztof Kozlowski, Linus Walleij,
Paolo Abeni, Rob Herring, Roger Quadros, Tony Lindgren,
linux-wireless, netdev, devicetree, linux-kernel,
linux-arm-kernel, linux-gpio, linux-omap, Christian Lamparter
In-Reply-To: <20260427142355.2532714-4-arnd@kernel.org>
On 27/04/2026 16:23, Arnd Bergmann wrote:
>
> - ret = gpio_request(p54spi_gpio_power, "p54spi power");
> - if (ret < 0) {
> - dev_err(&priv->spi->dev, "power GPIO request failed: %d", ret);
> + priv->gpio_powerdown = gpiod_get(&spi->dev, "powerdown", GPIOD_OUT_HIGH);
> + if (IS_ERR(priv->gpio_powerdown)) {
> + ret = PTR_ERR(priv->gpio_powerdown);
> + dev_err(&priv->spi->dev, "powerdown GPIO request failed: %d", ret);
Binding said it is optional, so this cannot be a failure.
Also, please use ret = dev_err_probe syntax.
> goto err_free;
> }
>
> - ret = gpio_request(p54spi_gpio_irq, "p54spi irq");
> - if (ret < 0) {
> - dev_err(&priv->spi->dev, "irq GPIO request failed: %d", ret);
> - goto err_free_gpio_power;
> - }
> -
> - gpio_direction_output(p54spi_gpio_power, 0);
> - gpio_direction_input(p54spi_gpio_irq);
> -
> - ret = request_irq(gpio_to_irq(p54spi_gpio_irq),
> - p54spi_interrupt, IRQF_NO_AUTOEN, "p54spi",
> - priv->spi);
> + ret = request_irq(spi->irq, p54spi_interrupt, IRQF_NO_AUTOEN, "p54spi", priv->spi);
> if (ret < 0) {
> dev_err(&priv->spi->dev, "request_irq() failed");
> - goto err_free_gpio_irq;
> + goto err_free_gpio_power;
> }
>
> - irq_set_irq_type(gpio_to_irq(p54spi_gpio_irq), IRQ_TYPE_EDGE_RISING);
>
> INIT_WORK(&priv->work, p54spi_work);
> init_completion(&priv->fw_comp);
> @@ -659,11 +636,9 @@ static int p54spi_probe(struct spi_device *spi)
>
> err_free_common:
> release_firmware(priv->firmware);
> - free_irq(gpio_to_irq(p54spi_gpio_irq), spi);
> -err_free_gpio_irq:
> - gpio_free(p54spi_gpio_irq);
> + free_irq(priv->irq, spi);
> err_free_gpio_power:
> - gpio_free(p54spi_gpio_power);
> + gpiod_put(priv->gpio_powerdown);
> err_free:
> p54_free_common(priv->hw);
> return ret;
> @@ -675,10 +650,8 @@ static void p54spi_remove(struct spi_device *spi)
>
> p54_unregister_common(priv->hw);
>
> - free_irq(gpio_to_irq(p54spi_gpio_irq), spi);
> -
> - gpio_free(p54spi_gpio_power);
> - gpio_free(p54spi_gpio_irq);
> + free_irq(priv->irq, spi);
> + gpiod_put(priv->gpio_powerdown);
> release_firmware(priv->firmware);
>
> mutex_destroy(&priv->mutex);
> @@ -686,10 +659,19 @@ static void p54spi_remove(struct spi_device *spi)
> p54_free_common(priv->hw);
> }
>
> +struct of_device_id p54spi_of_ids[] = {
static const
> + { .compatible = "cnxt,3110x", },
> + { .compatible = "isil,p54spi", },
> + { .compatible = "st,stlc4550", },
> + { .compatible = "st,stlc4560", },
At least last two devices are then compatible, so this should be
expressed in the binding with fallback and drop stlc4560 here. Maybe all
of them are compatible.
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, p54spi_of_ids);
>
> static struct spi_driver p54spi_driver = {
> .driver = {
> .name = "p54spi",
> + .of_match_table = p54spi_of_ids,
> },
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH net-next] selftests/net: packetdrill: add tcp_syncookies_ip6_9k
From: Eric Dumazet @ 2026-04-29 8:00 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Neal Cardwell, Kuniyuki Iwashima, netdev,
eric.dumazet, Eric Dumazet
This test checks syncookie mode is able to reconstruct some
client options when TCP TS are used:
- wscale option.
- sackOK.
- MSS (in a limited way).
- ECN (not tested, because of limited value).
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
.../net/packetdrill/tcp_syncookies_ip6_9k.pkt | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_syncookies_ip6_9k.pkt
diff --git a/tools/testing/selftests/net/packetdrill/tcp_syncookies_ip6_9k.pkt b/tools/testing/selftests/net/packetdrill/tcp_syncookies_ip6_9k.pkt
new file mode 100644
index 0000000000000000000000000000000000000000..15d03992a99140ecc4241edae58693dc5098fece
--- /dev/null
+++ b/tools/testing/selftests/net/packetdrill/tcp_syncookies_ip6_9k.pkt
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Check syncookies.
+//
+// Check we are able to rebuild client sack, wscale and mss options.
+// IPv6 msstab[4] = { 1280 - 60, 1480 - 60, 1500 - 60, 9000 - 60 }
+
+--ip_version=ipv6
+
+`./defaults.sh
+sysctl -q net.ipv4.tcp_syncookies=2
+ip link set dev tun0 mtu 9000
+`
+
+ 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+ +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+ +0 bind(3, ..., ...) = 0
+ +0 listen(3, 10) = 0
+
+ +0 < S 0:0(0) win 32792 <mss 8940,sackOK,TS val 100 ecr 0,nop,wscale 10>
+ +0 > S. 0:0(0) ack 1 <mss 8940,sackOK,TS val 4000 ecr 100,nop,wscale 8>
+ +.01 < . 1:1(0) ack 1 win 1024 <nop,nop,TS val 110 ecr 4000>
+
+ +0 accept(3, ..., ...) = 4
+
+// Check we properly infer from the final packet the other peer wanted 8940 mss, wscale 10 and sackOK
+ +0 %{ assert tcpi_snd_mss == 8928, tcpi_snd_mss }%
+ +0 %{ assert tcpi_snd_wscale = 10, tcpi_snd_wscale }%
+ +0 %{ assert (tcpi_options & TCPI_OPT_WSCALE) != 0, tcpi_options }%
+
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH 5/6] pds_core: add host backed memory support for firmware
From: Nikhil P. Rao @ 2026-04-29 7:58 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner,
Vamsi Atluri
In-Reply-To: <20260429-b4-pldm-b4-v1-0-e43b6c92e46c@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 | 166 +++++++++++++++++++++++++++++++
drivers/net/ethernet/amd/pds_core/core.h | 19 ++++
drivers/net/ethernet/amd/pds_core/main.c | 1 +
include/linux/pds/pds_adminq.h | 132 ++++++++++++++++++++++++
include/linux/pds/pds_core_if.h | 2 +
5 files changed, 320 insertions(+)
diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c
index 705cab7b0727..e94fea06c6cc 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,167 @@ 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_adminq_comp comp = {};
+ union pds_core_adminq_cmd cmd = {
+ .mem_del.opcode = PDS_AQ_CMD_MEM_DEL,
+ .mem_del.tag = cpu_to_le16(tag),
+ .mem_del.reason = reason,
+ };
+
+ dev_dbg(pdsc->dev, "Sending aq cmd for mem del tag %d\n", tag);
+ pdsc_adminq_post(pdsc, &cmd, &comp, false);
+}
+
+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_adminq_comp comp = {};
+ union pds_core_adminq_cmd cmd = {};
+ int err;
+
+ memset(hm, 0, sizeof(*hm));
+ cmd.mem_query.opcode = PDS_AQ_CMD_MEM_QUERY;
+ dev_dbg(pdsc->dev, "Sending aq cmd for mem query index %d\n", index);
+ err = pdsc_adminq_post(pdsc, &cmd, &comp, false);
+ 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.mem_query.size);
+ hm->tag = le16_to_cpu(comp.mem_query.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 = max(ilog2(hm->size), PAGE_SHIFT) - PAGE_SHIFT;
+ hm->pg = alloc_pages(GFP_KERNEL, 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);
+ err = -EIO;
+ goto err_del;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&comp, 0, sizeof(comp));
+ cmd.mem_add.opcode = PDS_AQ_CMD_MEM_ADD;
+ cmd.mem_add.tag = cpu_to_le16(hm->tag);
+ cmd.mem_add.size = cpu_to_le32(hm->size);
+ cmd.mem_add.buf_pa = cpu_to_le64(hm->pa);
+
+ dev_dbg(pdsc->dev, "Sending aq cmd for mem add tag %d size %d pa 0x%llx\n",
+ hm->tag, hm->size, hm->pa);
+ err = pdsc_adminq_post(pdsc, &cmd, &comp, false);
+ 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);
+ if (hm->pg) {
+ if (!dma_mapping_error(pdsc->dev, hm->pa))
+ dma_unmap_page(pdsc->dev, hm->pa, hm->size, DMA_BIDIRECTIONAL);
+ __free_pages(hm->pg, hm->order);
+ hm->pg = NULL;
+ }
+ return err;
+}
+
+void pdsc_host_mem_add(struct pdsc *pdsc)
+{
+ union pds_core_adminq_comp comp = {};
+ union pds_core_adminq_cmd cmd = {};
+ u16 count;
+ int err;
+ int i;
+
+ if (!(pdsc->dev_ident.capabilities & cpu_to_le64(PDS_CORE_DEV_CAP_HOST_MEM)))
+ return;
+
+ cmd.mem_get_count.opcode = PDS_AQ_CMD_MEM_GET_COUNT;
+ cmd.mem_get_count.max_contig = cpu_to_le32(PDSC_HOST_MEM_MAX_CONTIG);
+ dev_dbg(pdsc->dev, "Sending aq cmd for mem get count max_contig %lu\n",
+ PDSC_HOST_MEM_MAX_CONTIG);
+ err = pdsc_adminq_post(pdsc, &cmd, &comp, false);
+ 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 = le16_to_cpu(comp.mem_get_count.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;
+ }
+
+ pdsc->num_host_mem_reqs = i;
+}
+
+void pdsc_host_mem_del(struct pdsc *pdsc)
+{
+ int i;
+
+ if (!pdsc->host_mem_reqs)
+ return;
+
+ for (i = 0; i < pdsc->num_host_mem_reqs; i++)
+ pdsc_host_mem_del_one(pdsc, pdsc->host_mem_reqs[i].tag,
+ PDS_RC_SUCCESS);
+}
+
+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 c9ba63878927..e53edf72a5d5 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,8 @@
#define PDSC_SETUP_RECOVERY false
#define PDSC_SETUP_INIT true
+#define PDSC_HOST_MEM_MAX_CONTIG ((PAGE_SIZE) << (MAX_PAGE_ORDER))
+
struct pdsc_dev_bar {
void __iomem *vaddr;
phys_addr_t bus_addr;
@@ -141,6 +144,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 */
@@ -200,6 +211,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;
};
@@ -277,6 +291,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);
@@ -334,4 +349,8 @@ 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_del(struct pdsc *pdsc);
+void pdsc_host_mem_free(struct pdsc *pdsc);
+
#endif /* _PDSC_H_ */
diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c
index f0d0993f9d91..0a0542bf7cbb 100644
--- a/drivers/net/ethernet/amd/pds_core/main.c
+++ b/drivers/net/ethernet/amd/pds_core/main.c
@@ -437,6 +437,7 @@ static void pdsc_remove(struct pci_dev *pdev)
pdsc_auxbus_dev_del(pdsc, pdsc, &pdsc->padev);
timer_shutdown_sync(&pdsc->wdtimer);
+ pdsc_host_mem_del(pdsc);
if (pdsc->wq)
destroy_workqueue(pdsc->wq);
diff --git a/include/linux/pds/pds_adminq.h b/include/linux/pds/pds_adminq.h
index 40ff0ec2b879..ef46415ab9fd 100644
--- a/include/linux/pds/pds_adminq.h
+++ b/include/linux/pds/pds_adminq.h
@@ -34,6 +34,12 @@ enum pds_core_adminq_opcode {
PDS_AQ_CMD_RX_FILTER_ADD = 31,
PDS_AQ_CMD_RX_FILTER_DEL = 32,
+ /* MEM commands */
+ PDS_AQ_CMD_MEM_GET_COUNT = 10,
+ PDS_AQ_CMD_MEM_QUERY = 11,
+ PDS_AQ_CMD_MEM_ADD = 12,
+ PDS_AQ_CMD_MEM_DEL = 13,
+
/* Queue commands */
PDS_AQ_CMD_Q_IDENTIFY = 39,
PDS_AQ_CMD_Q_INIT = 40,
@@ -207,6 +213,122 @@ struct pds_core_client_request_cmd {
u8 client_cmd[60];
};
+/**
+ * struct pds_core_mem_get_count_cmd - MEM_GET_COUNT command
+ * @opcode: opcode PDS_AQ_CMD_MEM_GET_COUNT
+ * @rsvd: Word boundary padding
+ * @max_contig: Maximum contiguous memory size in bytes
+ *
+ * Query the number of host memory requests needed by firmware.
+ */
+struct pds_core_mem_get_count_cmd {
+ u8 opcode;
+ u8 rsvd[3];
+ __le32 max_contig;
+} __packed;
+
+/**
+ * struct pds_core_mem_get_count_comp - MEM_GET_COUNT completion
+ * @status: Status of the command (enum pds_core_status_code)
+ * @rsvd: Word boundary padding
+ * @comp_index: Index in the descriptor ring for which this is the completion
+ * @count: Number of host memory requests
+ * @rsvd2: Word boundary padding
+ * @color: Color bit
+ */
+struct pds_core_mem_get_count_comp {
+ u8 status;
+ u8 rsvd;
+ __le16 comp_index;
+ __le16 count;
+ u8 rsvd2[9];
+ u8 color;
+} __packed;
+
+/**
+ * struct pds_core_mem_query_cmd - MEM_QUERY command
+ * @opcode: opcode PDS_AQ_CMD_MEM_QUERY
+ * @rsvd: Word boundary padding
+ * @index: Memory request index
+ */
+struct pds_core_mem_query_cmd {
+ u8 opcode;
+ u8 rsvd;
+ __le16 index;
+} __packed;
+
+/**
+ * struct pds_core_mem_query_comp - MEM_QUERY completion
+ * @status: Status of the command (enum pds_core_status_code)
+ * @rsvd: Word boundary padding
+ * @comp_index: Index in the descriptor ring for which this is the completion
+ * @size: Size of memory request in bytes
+ * @tag: Tag for this memory request
+ */
+struct pds_core_mem_query_comp {
+ u8 status;
+ u8 rsvd;
+ __le16 comp_index;
+ __le32 size;
+ __le16 tag;
+} __packed;
+
+/**
+ * struct pds_core_mem_add_cmd - MEM_ADD command
+ * @opcode: opcode PDS_AQ_CMD_MEM_ADD
+ * @rsvd: Word boundary padding
+ * @tag: Tag for this memory request
+ * @size: Size of memory in bytes
+ * @buf_pa: DMA address of memory
+ */
+struct pds_core_mem_add_cmd {
+ u8 opcode;
+ u8 rsvd;
+ __le16 tag;
+ __le32 size;
+ __le64 buf_pa;
+} __packed;
+
+/**
+ * struct pds_core_mem_add_comp - MEM_ADD command completion
+ * @status: Status of the command (enum pds_core_status_code)
+ * @rsvd: padding for natural alignment
+ * @comp_index: Index in the desc ring for which this is the completion
+ */
+struct pds_core_mem_add_comp {
+ u8 status;
+ u8 rsvd;
+ __le16 comp_index;
+} __packed;
+
+/**
+ * struct pds_core_mem_del_cmd - MEM_DEL command
+ * @opcode: opcode PDS_AQ_CMD_MEM_DEL
+ * @rsvd: Word boundary padding
+ * @tag: Tag for this memory request
+ * @reason: Reason for deletion
+ */
+struct pds_core_mem_del_cmd {
+ u8 opcode;
+ u8 rsvd;
+ __le16 tag;
+ u8 reason;
+} __packed;
+
+/**
+ * struct pds_core_mem_del_comp - MEM_DEL command completion
+ * @status: Status of the command (enum pds_core_status_code)
+ * @rsvd: Word boundary padding
+ * @comp_index: Index in the desc ring for which this is the completion
+ * @tag: Tag for the memory request
+ */
+struct pds_core_mem_del_comp {
+ u8 status;
+ u8 rsvd;
+ __le16 comp_index;
+ __le16 tag;
+} __packed;
+
#define PDS_CORE_MAX_FRAGS 16
#define PDS_CORE_QCQ_F_INITED BIT(0)
@@ -1454,6 +1576,11 @@ union pds_core_adminq_cmd {
struct pds_core_client_unreg_cmd client_unreg;
struct pds_core_client_request_cmd client_request;
+ struct pds_core_mem_get_count_cmd mem_get_count;
+ struct pds_core_mem_query_cmd mem_query;
+ struct pds_core_mem_add_cmd mem_add;
+ struct pds_core_mem_del_cmd mem_del;
+
struct pds_core_lif_identify_cmd lif_ident;
struct pds_core_lif_init_cmd lif_init;
struct pds_core_lif_reset_cmd lif_reset;
@@ -1502,6 +1629,11 @@ union pds_core_adminq_comp {
struct pds_core_client_reg_comp client_reg;
+ struct pds_core_mem_get_count_comp mem_get_count;
+ struct pds_core_mem_query_comp mem_query;
+ struct pds_core_mem_add_comp mem_add;
+ struct pds_core_mem_del_comp mem_del;
+
struct pds_core_lif_identify_comp lif_ident;
struct pds_core_lif_init_comp lif_init;
struct pds_core_lif_setattr_comp lif_setattr;
diff --git a/include/linux/pds/pds_core_if.h b/include/linux/pds/pds_core_if.h
index b8052985dddf..fb489e8d54ef 100644
--- a/include/linux/pds/pds_core_if.h
+++ b/include/linux/pds/pds_core_if.h
@@ -110,9 +110,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
--
2.43.0
^ permalink raw reply related
* [PATCH 1/6] pds_core: add support for quiet devcmd failures
From: Nikhil P. Rao @ 2026-04-29 7:58 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner
In-Reply-To: <20260429-b4-pldm-b4-v1-0-e43b6c92e46c@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
Currently there aren't any use-cases that require special handling
on whether or not to print devcmd failures. Specifically
non-generic failures, i.e. not supported failures. Add support to
allow these messages to be suppressed. This will be used when
adding support to negotiate PDS_CORE_IDENTITY_VERSION_2.
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
---
drivers/net/ethernet/amd/pds_core/dev.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
index 2e1d0d01d03a..5b86d6cd0ac3 100644
--- a/drivers/net/ethernet/amd/pds_core/dev.c
+++ b/drivers/net/ethernet/amd/pds_core/dev.c
@@ -126,7 +126,8 @@ static const char *pdsc_devcmd_str(int opcode)
}
}
-static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)
+static int __pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds,
+ const bool do_msg)
{
struct device *dev = pdsc->dev;
unsigned long start_time;
@@ -172,7 +173,7 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)
status = pdsc_devcmd_status(pdsc);
err = pdsc_err_to_errno(status);
- if (err && err != -EAGAIN)
+ if (do_msg && err && err != -EAGAIN)
dev_err(dev, "DEVCMD %d %s failed, status=%d err %d %pe\n",
opcode, pdsc_devcmd_str(opcode), status, err,
ERR_PTR(err));
@@ -180,8 +181,9 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)
return err;
}
-int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
- union pds_core_dev_comp *comp, int max_seconds)
+static int __pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ union pds_core_dev_comp *comp, int max_seconds,
+ const bool do_msg)
{
int err;
@@ -190,7 +192,7 @@ int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
memcpy_toio(&pdsc->cmd_regs->cmd, cmd, sizeof(*cmd));
pdsc_devcmd_dbell(pdsc);
- err = pdsc_devcmd_wait(pdsc, cmd->opcode, max_seconds);
+ err = __pdsc_devcmd_wait(pdsc, cmd->opcode, max_seconds, do_msg);
if ((err == -ENXIO || err == -ETIMEDOUT) && pdsc->wq)
queue_work(pdsc->wq, &pdsc->health_work);
@@ -200,6 +202,12 @@ int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
return err;
}
+int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ union pds_core_dev_comp *comp, int max_seconds)
+{
+ return __pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds, true);
+}
+
int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds)
{
--
2.43.0
^ permalink raw reply related
* [PATCH 6/6] pds_core: add debugfs support for host backed memory
From: Nikhil P. Rao @ 2026-04-29 7:58 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner,
Vamsi Atluri
In-Reply-To: <20260429-b4-pldm-b4-v1-0-e43b6c92e46c@amd.com>
From: Vamsi Atluri <Vamsi.Atluri@amd.com>
Add debugfs file to display host memory allocations including tag,
size, order, and physical address for each memory request.
Signed-off-by: Vamsi Atluri <Vamsi.Atluri@amd.com>
---
drivers/net/ethernet/amd/pds_core/debugfs.c | 43 +++++++++++++++++++++++++++++
drivers/net/ethernet/amd/pds_core/main.c | 2 ++
2 files changed, 45 insertions(+)
diff --git a/drivers/net/ethernet/amd/pds_core/debugfs.c b/drivers/net/ethernet/amd/pds_core/debugfs.c
index 04c5e3abd8d7..79312107f721 100644
--- a/drivers/net/ethernet/amd/pds_core/debugfs.c
+++ b/drivers/net/ethernet/amd/pds_core/debugfs.c
@@ -173,3 +173,46 @@ void pdsc_debugfs_del_qcq(struct pdsc_qcq *qcq)
debugfs_remove_recursive(qcq->dentry);
qcq->dentry = NULL;
}
+
+static int host_mem_show(struct seq_file *seq, void *v)
+{
+ struct pdsc *pdsc = seq->private;
+ struct pdsc_host_mem *hm;
+ int i;
+
+ if (!pdsc->host_mem_reqs || pdsc->num_host_mem_reqs == 0) {
+ seq_puts(seq, "No host memory allocated\n");
+ return 0;
+ }
+
+ seq_printf(seq, "Host memory requests: %d\n\n", pdsc->num_host_mem_reqs);
+ seq_puts(seq, "Tag Size Order PA\n");
+ seq_puts(seq, "--- ---- ----- --\n");
+
+ for (i = 0; i < pdsc->num_host_mem_reqs; i++) {
+ hm = &pdsc->host_mem_reqs[i];
+
+ if (!hm->pg)
+ continue;
+
+ seq_printf(seq, "%-6d %-12u %-6d 0x%llx\n",
+ hm->tag, hm->size, hm->order,
+ (unsigned long long)hm->pa);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(host_mem);
+
+void pdsc_debugfs_add_host_mem(struct pdsc *pdsc)
+{
+ if (!(pdsc->dev_ident.capabilities & cpu_to_le64(PDS_CORE_DEV_CAP_HOST_MEM)))
+ return;
+
+ /* This file will already exist in the reset flow */
+ if (debugfs_lookup("host_mem", pdsc->dentry))
+ return;
+
+ debugfs_create_file("host_mem", 0400, pdsc->dentry,
+ pdsc, &host_mem_fops);
+}
diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c
index 0a0542bf7cbb..4c14198eaafe 100644
--- a/drivers/net/ethernet/amd/pds_core/main.c
+++ b/drivers/net/ethernet/amd/pds_core/main.c
@@ -264,6 +264,8 @@ static int pdsc_init_pf(struct pdsc *pdsc)
mutex_unlock(&pdsc->config_lock);
+ pdsc_debugfs_add_host_mem(pdsc);
+
err = pdsc_auxbus_dev_add(pdsc, pdsc, PDS_DEV_TYPE_FWCTL, &pdsc->padev);
if (err)
goto err_out_stop;
--
2.43.0
^ permalink raw reply related
* [PATCH 3/6] pds_core: add PLDM firmware update support via devlink flash
From: Nikhil P. Rao @ 2026-04-29 7:58 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner
In-Reply-To: <20260429-b4-pldm-b4-v1-0-e43b6c92e46c@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
Implement PLDM FW Update in the pds_core driver using the upstream
pldmfw API. This allows an entire PLDM FW package to be updated
and/or specific components if they aren't fixed.
Flash the entire image:
devlink dev flash pci/0000:b5:00.0 file firmware.pldmfw
Flash individual components from the PLDM FW package:
devlink dev flash pci/0000:b5:00.0 \
file firmware.pldmfw component fw.mainfwa
devlink dev flash pci/0000:b5:00.0 \
file firmware.pldmfw component fw.mainfwb
devlink dev flash pci/0000:b5:00.0 \
file firmware.pldmfw component fw.goldfw
Assisted-by: Claude:claude-opus-4.6
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
---
drivers/net/ethernet/amd/Kconfig | 1 +
drivers/net/ethernet/amd/pds_core/core.h | 14 +-
drivers/net/ethernet/amd/pds_core/dev.c | 42 +-
drivers/net/ethernet/amd/pds_core/devlink.c | 2 +-
drivers/net/ethernet/amd/pds_core/fw.c | 699 +++++++++++++++++++++++++++-
drivers/net/ethernet/amd/pds_core/main.c | 4 +-
include/linux/pds/pds_core_if.h | 375 +++++++++++++++
7 files changed, 1130 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index 45e8d698781c..e7346837dad6 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -192,6 +192,7 @@ config PDS_CORE
depends on 64BIT && PCI
select AUXILIARY_BUS
select NET_DEVLINK
+ select PLDMFW
help
This enables the support for the AMD/Pensando Core device family of
adapters. More specific information on this driver can be
diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
index 4a6b35c84dab..c9ba63878927 100644
--- a/drivers/net/ethernet/amd/pds_core/core.h
+++ b/drivers/net/ethernet/amd/pds_core/core.h
@@ -199,6 +199,8 @@ struct pdsc {
u64 last_eid;
struct pdsc_viftype *viftype_status;
struct work_struct pci_reset_work;
+
+ struct pds_core_component_list_info fw_components;
};
/** enum pds_core_dbell_bits - bitwise composition of dbell values.
@@ -281,8 +283,16 @@ bool pdsc_is_fw_running(struct pdsc *pdsc);
bool pdsc_is_fw_good(struct pdsc *pdsc);
int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds);
+int pdsc_devcmd_with_data(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ const void *data, size_t data_len,
+ union pds_core_dev_comp *comp, int max_seconds);
+int pdsc_devcmd_with_data_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ const void *data, size_t data_len,
+ union pds_core_dev_comp *comp, int max_seconds);
int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds);
+int pdsc_devcmd_locked_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ union pds_core_dev_comp *comp, int max_seconds);
int pdsc_devcmd_init(struct pdsc *pdsc);
int pdsc_devcmd_reset(struct pdsc *pdsc);
int pdsc_dev_init(struct pdsc *pdsc);
@@ -315,8 +325,10 @@ void pdsc_process_adminq(struct pdsc_qcq *qcq);
void pdsc_work_thread(struct work_struct *work);
irqreturn_t pdsc_adminq_isr(int irq, void *data);
-int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
+int pdsc_firmware_update(struct pdsc *pdsc,
+ struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack);
+int pdsc_get_component_info(struct pdsc *pdsc);
void pdsc_fw_down(struct pdsc *pdsc);
void pdsc_fw_up(struct pdsc *pdsc);
diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
index f77bd5e48b92..4bbf299a88dc 100644
--- a/drivers/net/ethernet/amd/pds_core/dev.c
+++ b/drivers/net/ethernet/amd/pds_core/dev.c
@@ -127,7 +127,7 @@ static const char *pdsc_devcmd_str(int opcode)
}
static int __pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds,
- const bool do_msg)
+ bool do_msg)
{
struct device *dev = pdsc->dev;
unsigned long start_time;
@@ -208,6 +208,12 @@ int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
return __pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds, true);
}
+int pdsc_devcmd_locked_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ union pds_core_dev_comp *comp, int max_seconds)
+{
+ return __pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds, false);
+}
+
int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
union pds_core_dev_comp *comp, int max_seconds)
{
@@ -220,6 +226,40 @@ int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
return err;
}
+int pdsc_devcmd_with_data(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ const void *data, size_t data_len,
+ union pds_core_dev_comp *comp, int max_seconds)
+{
+ int err;
+
+ if (data_len > sizeof(pdsc->cmd_regs->data))
+ return -ENOSPC;
+
+ mutex_lock(&pdsc->devcmd_lock);
+ memcpy_toio(&pdsc->cmd_regs->data, data, data_len);
+ err = pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds);
+ mutex_unlock(&pdsc->devcmd_lock);
+
+ return err;
+}
+
+int pdsc_devcmd_with_data_nomsg(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
+ const void *data, size_t data_len,
+ union pds_core_dev_comp *comp, int max_seconds)
+{
+ int err;
+
+ if (data_len > sizeof(pdsc->cmd_regs->data))
+ return -ENOSPC;
+
+ mutex_lock(&pdsc->devcmd_lock);
+ memcpy_toio(&pdsc->cmd_regs->data, data, data_len);
+ err = pdsc_devcmd_locked_nomsg(pdsc, cmd, comp, max_seconds);
+ mutex_unlock(&pdsc->devcmd_lock);
+
+ return err;
+}
+
int pdsc_devcmd_init(struct pdsc *pdsc)
{
union pds_core_dev_comp comp = {};
diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c
index b576be626a29..7f44e1a8d4fd 100644
--- a/drivers/net/ethernet/amd/pds_core/devlink.c
+++ b/drivers/net/ethernet/amd/pds_core/devlink.c
@@ -90,7 +90,7 @@ int pdsc_dl_flash_update(struct devlink *dl,
{
struct pdsc *pdsc = devlink_priv(dl);
- return pdsc_firmware_update(pdsc, params->fw, extack);
+ return pdsc_firmware_update(pdsc, params, extack);
}
static char *fw_slotnames[] = {
diff --git a/drivers/net/ethernet/amd/pds_core/fw.c b/drivers/net/ethernet/amd/pds_core/fw.c
index fa626719e68d..4ccf90f25f75 100644
--- a/drivers/net/ethernet/amd/pds_core/fw.c
+++ b/drivers/net/ethernet/amd/pds_core/fw.c
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2023 Advanced Micro Devices, Inc */
+#include <linux/pldmfw.h>
+#include <linux/vmalloc.h>
+
#include "core.h"
/* The worst case wait for the install activity is about 25 minutes when
@@ -14,6 +17,10 @@
/* Number of periodic log updates during fw file download */
#define PDSC_FW_INTERVAL_FRACTION 32
+#define PDSC_FW_COMPONENT_PREFIX "fw."
+#define PDSC_FW_COMPONENT_FULL_NAME_BUFLEN \
+ (sizeof(PDSC_FW_COMPONENT_PREFIX) + PDS_CORE_FW_COMPONENT_NAME_BUFLEN)
+
static int pdsc_devcmd_fw_download_locked(struct pdsc *pdsc, u64 addr,
u32 offset, u32 length)
{
@@ -23,7 +30,7 @@ static int pdsc_devcmd_fw_download_locked(struct pdsc *pdsc, u64 addr,
.fw_download.addr = cpu_to_le64(addr),
.fw_download.length = cpu_to_le32(length),
};
- union pds_core_dev_comp comp;
+ union pds_core_dev_comp comp = {};
return pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
}
@@ -95,8 +102,9 @@ static int pdsc_fw_status_long_wait(struct pdsc *pdsc,
return err;
}
-int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
- struct netlink_ext_ack *extack)
+static int pdsc_legacy_firmware_update(struct pdsc *pdsc,
+ const struct firmware *fw,
+ struct netlink_ext_ack *extack)
{
u32 buf_sz, copy_sz, offset;
struct devlink *dl;
@@ -195,3 +203,688 @@ int pdsc_firmware_update(struct pdsc *pdsc, const struct firmware *fw,
NULL, 0, 0);
return err;
}
+
+struct pdsc_component_priv {
+ char component_name[PDSC_FW_COMPONENT_FULL_NAME_BUFLEN];
+ u16 component_id;
+ bool skip;
+ struct list_head list_entry;
+};
+
+struct pds_core_fwu_priv {
+ struct pldmfw context;
+ struct devlink_flash_update_params *params;
+ struct netlink_ext_ack *extack;
+ struct pdsc *pdsc;
+ struct list_head components;
+};
+
+static void pdsc_free_fwu_priv(struct pds_core_fwu_priv *priv)
+{
+ struct pdsc_component_priv *component_priv, *tmp;
+
+ list_for_each_entry_safe(component_priv, tmp, &priv->components,
+ list_entry) {
+ list_del(&component_priv->list_entry);
+ kfree(component_priv);
+ }
+}
+
+static int pdsc_devcmd_match_record_desc(struct pdsc *pdsc, u16 desc_type,
+ u16 desc_size, const u8 *desc_data,
+ u8 *match)
+{
+ union pds_core_dev_cmd cmd = {
+ .match_record_desc.opcode = PDS_CORE_CMD_MATCH_RECORD_DESC,
+ .match_record_desc.ver = 1,
+ .match_record_desc.type = cpu_to_le16(desc_type),
+ .match_record_desc.size = cpu_to_le16(desc_size),
+ };
+ union pds_core_dev_comp comp = {};
+ int err;
+
+ err = pdsc_devcmd_with_data(pdsc, &cmd, desc_data, desc_size,
+ &comp, pdsc->devcmd_timeout);
+ *match = comp.match_record_desc.match;
+
+ return err;
+}
+
+static bool pdsc_match_record_descs(struct pldmfw *context,
+ struct pldmfw_record *record)
+{
+ struct pds_core_fwu_priv *priv =
+ container_of(context, struct pds_core_fwu_priv, context);
+ struct pdsc *pdsc = priv->pdsc;
+ struct pldmfw_desc_tlv *desc;
+
+ if (!pldmfw_op_pci_match_record(context, record))
+ return false;
+
+ list_for_each_entry(desc, &record->descs, entry) {
+ u8 match;
+ int err;
+
+ switch (desc->type) {
+ /* skip types checked in pldmfw_op_pci_match_record */
+ case PLDM_DESC_ID_PCI_VENDOR_ID:
+ case PLDM_DESC_ID_PCI_DEVICE_ID:
+ case PLDM_DESC_ID_PCI_SUBVENDOR_ID:
+ case PLDM_DESC_ID_PCI_SUBDEV_ID:
+ continue;
+ }
+
+ if (!desc->size)
+ return false;
+
+ err = pdsc_devcmd_match_record_desc(pdsc, desc->type,
+ desc->size, desc->data,
+ &match);
+ if (err) {
+ dev_err(pdsc->dev, "match_record_desc failed type: 0x%04x size: %u, err %d\n",
+ desc->type, desc->size, err);
+ return false;
+ }
+ /* all record descriptors must match */
+ if (!match)
+ return false;
+ }
+
+ return true;
+}
+
+static int pdsc_devcmd_send_package_data(struct pdsc *pdsc, u64 addr,
+ u16 length, u16 offset, u16 total_len)
+{
+ union pds_core_dev_cmd cmd = {
+ .send_pkg_data.opcode = PDS_CORE_CMD_SEND_PKG_DATA,
+ .send_pkg_data.ver = 1,
+ .send_pkg_data.data_pa = cpu_to_le64(addr),
+ .send_pkg_data.data_len = cpu_to_le16(length),
+ .send_pkg_data.offset = cpu_to_le16(offset),
+ .send_pkg_data.total_len = cpu_to_le16(total_len),
+ };
+ union pds_core_dev_comp comp = {};
+
+ return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
+}
+
+static int pdsc_send_package_data(struct pldmfw *context, const u8 *data, u16 length)
+{
+ struct pds_core_fwu_priv *priv =
+ container_of(context, struct pds_core_fwu_priv, context);
+ struct device *dev = context->dev;
+ struct pdsc *pdsc = priv->pdsc;
+ u8 *package_data;
+ u32 offset;
+ int err;
+
+ if (!length)
+ return 0;
+
+ package_data = kmemdup(data, length, GFP_KERNEL);
+ if (!package_data)
+ return -ENOMEM;
+
+ offset = 0;
+ while (offset < length) {
+ dma_addr_t dma_addr;
+ u32 copy_sz;
+
+ copy_sz = min_t(unsigned int, PDS_PAGE_SIZE, length - offset);
+ dma_addr = dma_map_single(dev, package_data + offset, copy_sz,
+ DMA_TO_DEVICE);
+ err = dma_mapping_error(dev, dma_addr);
+ if (err) {
+ dev_err(dev, "Failed to dma_map package_data at offset 0x%x copy_sz 0x%x: %pe\n",
+ offset, copy_sz, ERR_PTR(err));
+ goto out;
+ }
+
+ err = pdsc_devcmd_send_package_data(pdsc, dma_addr, copy_sz, offset,
+ length);
+ if (err)
+ dev_err(dev, "send_package_data failed offset 0x%x addr 0x%llx len 0x%x: %pe\n",
+ offset, dma_addr, copy_sz, ERR_PTR(err));
+
+ dma_unmap_single(dev, dma_addr, copy_sz, DMA_TO_DEVICE);
+ if (err)
+ goto out;
+
+ offset += copy_sz;
+ }
+
+out:
+ kfree(package_data);
+ return err;
+}
+
+static void pdsc_set_component_name(struct pdsc *pdsc, u16 component_id,
+ u8 slot_id, char *component_name)
+{
+ int i;
+
+ for (i = 0; i < pdsc->fw_components.num_components; i++) {
+ struct pds_core_fw_component_info *info =
+ &pdsc->fw_components.info[i];
+
+ if (component_id == info->identifier &&
+ slot_id == info->slot_id) {
+ snprintf(component_name,
+ PDSC_FW_COMPONENT_FULL_NAME_BUFLEN,
+ "fw.%s", info->name);
+ return;
+ }
+ }
+}
+
+static const char *pdsc_get_component_priv_name(struct pds_core_fwu_priv *priv,
+ u16 component_id)
+{
+ struct pdsc_component_priv *component_priv;
+
+ list_for_each_entry(component_priv, &priv->components, list_entry) {
+ if (component_priv->component_id != component_id)
+ continue;
+
+ return component_priv->component_name;
+ }
+
+ return NULL;
+}
+
+static struct pds_core_fw_component_info *
+pdsc_find_component_by_name(struct pdsc *pdsc, const char *component_name)
+{
+ struct pds_core_fw_component_info *info;
+ size_t prefix_len;
+ int i;
+
+ prefix_len = str_has_prefix(component_name, PDSC_FW_COMPONENT_PREFIX);
+ if (!prefix_len)
+ return NULL;
+
+ component_name += prefix_len; /* Skip "fw." prefix */
+
+ for (i = 0; i < pdsc->fw_components.num_components; i++) {
+ info = &pdsc->fw_components.info[i];
+
+ if (!strncmp(component_name, info->name,
+ PDS_CORE_FW_COMPONENT_NAME_BUFLEN))
+ return info;
+ }
+
+ return NULL;
+}
+
+static u8 pdsc_get_slot_id(struct pdsc *pdsc, const char *component_name)
+{
+ struct pds_core_fw_component_info *info;
+
+ info = pdsc_find_component_by_name(pdsc, component_name);
+ return info ? info->slot_id : PDS_CORE_FW_SLOT_MAX;
+}
+
+static bool pdsc_skip_component(struct pds_core_fwu_priv *priv,
+ u16 component_id, const char *component_name)
+{
+ struct pdsc_component_priv *component_priv;
+
+ list_for_each_entry(component_priv, &priv->components, list_entry) {
+ if (component_priv->component_id != component_id)
+ continue;
+
+ if (component_priv->skip)
+ return true;
+
+ if (component_name &&
+ strncmp(component_priv->component_name, component_name,
+ PDSC_FW_COMPONENT_FULL_NAME_BUFLEN))
+ return true;
+ }
+
+ return false;
+}
+
+static bool pdsc_match_component_name_to_ids(struct pdsc *pdsc,
+ const char *component_name,
+ u8 component_id,
+ u8 slot_id)
+{
+ struct pds_core_fw_component_info *info;
+
+ info = pdsc_find_component_by_name(pdsc, component_name);
+ if (!info)
+ return false;
+
+ return slot_id == info->slot_id && component_id == info->identifier;
+}
+
+static int pdsc_send_component_table(struct pldmfw *context,
+ struct pldmfw_component *component,
+ u8 transfer_flag)
+{
+ struct pds_core_fwu_priv *priv =
+ container_of(context, struct pds_core_fwu_priv, context);
+ struct pds_core_component_tbl *component_tbl;
+ struct pdsc_component_priv *component_priv;
+ struct device *dev = context->dev;
+ union pds_core_dev_comp comp = {};
+ union pds_core_dev_cmd cmd = {};
+ struct pdsc *pdsc = priv->pdsc;
+ bool skip_component = false;
+ u16 buf_sz, tbl_sz;
+ int err = 0;
+ u8 slot_id;
+
+ dev_dbg(dev, "component name %s classification %u id %u activation_method %u ver_len %d ver_str %.*s index %u size %u transfer_flag 0x%02x\n",
+ priv->params->component, component->classification,
+ component->identifier, component->activation_method,
+ component->version_len, component->version_len,
+ component->version_string, component->index,
+ component->component_size, transfer_flag);
+
+ component_priv = kzalloc_obj(*component_priv, GFP_KERNEL);
+ if (!component_priv)
+ return -ENOMEM;
+
+ if (priv->params->component) {
+ slot_id = pdsc_get_slot_id(pdsc, priv->params->component);
+ if (slot_id == PDS_CORE_FW_SLOT_MAX)
+ return -ENOENT;
+
+ if (!pdsc_match_component_name_to_ids(pdsc,
+ priv->params->component,
+ component->identifier,
+ slot_id)) {
+ skip_component = true;
+ goto add_component_priv;
+ }
+ } else {
+ slot_id = PDS_CORE_FW_SLOT_INVALID;
+ }
+
+ buf_sz = sizeof(pdsc->cmd_regs->data);
+ tbl_sz = struct_size(component_tbl, version_str, component->version_len);
+ if (tbl_sz > buf_sz) {
+ dev_err(dev, "component_tbl size %d too big, max size: %d\n",
+ tbl_sz, buf_sz);
+ err = -ENOSPC;
+ goto free_component_priv;
+ }
+ component_tbl = kzalloc(tbl_sz, GFP_KERNEL);
+ if (!component_tbl) {
+ err = -ENOMEM;
+ goto free_component_priv;
+ }
+
+ component_tbl->comparison_stamp = cpu_to_le32(component->comparison_stamp);
+ component_tbl->classification = cpu_to_le16(component->classification);
+ component_tbl->identifier = cpu_to_le16(component->identifier);
+ component_tbl->transfer_flag = transfer_flag;
+ component_tbl->version_str_type = component->version_type;
+ component_tbl->version_str_len = component->version_len;
+ memcpy(component_tbl->version_str, component->version_string,
+ component->version_len);
+
+ cmd.send_component_tbl.opcode = PDS_CORE_CMD_SEND_COMPONENT_TBL;
+ cmd.send_component_tbl.ver = 1;
+ cmd.send_component_tbl.slot_id = slot_id;
+
+ err = pdsc_devcmd_with_data(pdsc, &cmd, component_tbl, tbl_sz,
+ &comp, pdsc->devcmd_timeout);
+ if (err)
+ dev_err(dev, "Failed sending component table: %pe\n",
+ ERR_PTR(err));
+ kfree(component_tbl);
+ if (err)
+ goto free_component_priv;
+
+ if (comp.send_component_tbl.response == 1 &&
+ comp.send_component_tbl.response_code == PDS_CORE_COMPONENT_PREREQS_NOT_MET)
+ skip_component = true;
+ else
+ pdsc_set_component_name(pdsc, component->identifier,
+ comp.send_component_tbl.slot_id,
+ component_priv->component_name);
+
+add_component_priv:
+ component_priv->skip = skip_component;
+ component_priv->component_id = component->identifier;
+ list_add(&component_priv->list_entry, &priv->components);
+
+ return 0;
+
+free_component_priv:
+ kfree(component_priv);
+ return err;
+}
+
+int pdsc_get_component_info(struct pdsc *pdsc)
+{
+ union pds_core_dev_cmd cmd = {
+ .get_component_info.opcode = PDS_CORE_CMD_GET_COMPONENT_INFO,
+ .get_component_info.ver = 1,
+ };
+ struct pds_core_component_list_info *list_info;
+ union pds_core_dev_comp comp = {};
+ dma_addr_t dma_addr;
+ u8 num_components;
+ int err, i;
+
+ list_info = kzalloc(PDS_PAGE_SIZE, GFP_KERNEL);
+ if (!list_info)
+ return -ENOMEM;
+
+ dma_addr = dma_map_single(pdsc->dev, list_info, PDS_PAGE_SIZE, DMA_FROM_DEVICE);
+ err = dma_mapping_error(pdsc->dev, dma_addr);
+ if (err) {
+ dev_err(pdsc->dev, "Failed to dma_map component_list_info length %d: %pe\n",
+ PDS_PAGE_SIZE, ERR_PTR(err));
+ goto out;
+ }
+
+ cmd.get_component_info.data_len = cpu_to_le16(PDS_PAGE_SIZE);
+ cmd.get_component_info.data_pa = cpu_to_le64(dma_addr);
+
+ err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout * 2);
+ dma_unmap_single(pdsc->dev, dma_addr, PDS_PAGE_SIZE, DMA_FROM_DEVICE);
+ if (err)
+ goto out;
+
+ if (comp.get_component_info.ver == 0) {
+ /* Don't support backward compatibility as version 0 has
+ * alignment issues, so give a hint to users to update
+ * their firmware
+ */
+ dev_warn(pdsc->dev, "Incompatible get_component_info version %u reported by firmware\n",
+ comp.get_component_info.ver);
+ err = 0;
+ goto out;
+ }
+
+ num_components = list_info->num_components;
+ if (num_components > PDS_CORE_FW_COMPONENT_LIST_LEN) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ pdsc->fw_components.num_components = num_components;
+ for (i = 0; i < num_components; i++) {
+ struct pds_core_fw_component_info *info =
+ &pdsc->fw_components.info[i];
+
+ memcpy(info, &list_info->info[i], sizeof(*info));
+ info->version[PDS_CORE_FW_COMPONENT_VER_BUFLEN - 1] = 0;
+ info->name[PDS_CORE_FW_COMPONENT_NAME_BUFLEN - 1] = 0;
+ }
+
+out:
+ kfree(list_info);
+ return err;
+}
+
+static int pdsc_devcmd_send_component(struct pdsc *pdsc,
+ struct pds_core_flash_component *info,
+ u16 info_sz, dma_addr_t addr, u32 length,
+ u32 offset, u16 slot_id,
+ union pds_core_dev_comp *comp)
+{
+ union pds_core_dev_cmd cmd = {
+ .send_component.opcode = PDS_CORE_CMD_SEND_COMPONENT,
+ .send_component.ver = 1,
+ .send_component.operation = PDS_CORE_SEND_COMPONENT_START,
+ .send_component.data_pa = cpu_to_le64(addr),
+ .send_component.data_len = cpu_to_le32(length),
+ .send_component.offset = cpu_to_le32(offset),
+ .send_component.slot_id = slot_id,
+ };
+ unsigned long timeout = 300 * HZ;
+ unsigned long start_time;
+ unsigned long end_time;
+ int err;
+
+ start_time = jiffies;
+ end_time = start_time + timeout;
+ do {
+ /* prevent noisy/benign devcmd failures */
+ err = pdsc_devcmd_with_data_nomsg(pdsc, &cmd, info, info_sz,
+ comp, 60);
+ if (err != -EAGAIN)
+ break;
+
+ /* if required, subsequent commands check status of
+ * PDS_CORE_CMD_SEND_COMPONENT command, which returns
+ * EAGAIN/ETIMEDOUT while the command is still running,
+ * else we get the final command status.
+ */
+ cmd.send_component.operation = PDS_CORE_SEND_COMPONENT_STATUS;
+ msleep(20);
+ } while (time_before(jiffies, end_time));
+
+ if (err == -EAGAIN)
+ dev_err(pdsc->dev, "PDS_CORE_CMD_SEND_COMPONENT timed out\n");
+
+ return err;
+}
+
+static int pdsc_flash_component(struct pldmfw *context,
+ struct pldmfw_component *component)
+{
+ struct pds_core_fwu_priv *priv =
+ container_of(context, struct pds_core_fwu_priv, context);
+ const char *component_name = priv->params->component;
+ struct pds_core_flash_component *component_info;
+ struct device *dev = context->dev;
+ struct pdsc *pdsc = priv->pdsc;
+ u16 buf_sz, info_sz;
+ struct devlink *dl;
+ u32 total_len;
+ u32 offset;
+ u8 slot_id;
+ int err;
+
+ if (pdsc_skip_component(priv, component->identifier, component_name))
+ return 0;
+
+ if (component_name) {
+ slot_id = pdsc_get_slot_id(pdsc, component_name);
+ if (slot_id == PDS_CORE_FW_SLOT_MAX)
+ return 0;
+ } else {
+ component_name = pdsc_get_component_priv_name(priv, component->identifier);
+ slot_id = PDS_CORE_FW_SLOT_INVALID;
+ }
+
+ total_len = component->component_size;
+ dev_dbg(dev, "component name %s class %u id %u act_meth %u ver_str %.*s index %u size %u\n",
+ component_name, component->classification,
+ component->identifier, component->activation_method,
+ component->version_len, component->version_string,
+ component->index, component->component_size);
+
+ buf_sz = sizeof(pdsc->cmd_regs->data);
+ info_sz = struct_size(component_info, version_str, component->version_len);
+ if (info_sz > buf_sz) {
+ dev_err(dev, "component_info size %d too big, max size: %d\n",
+ info_sz, buf_sz);
+ return -ENOSPC;
+ }
+ component_info = vzalloc(info_sz);
+ if (!component_info)
+ return -ENOMEM;
+
+ component_info->comparison_stamp = cpu_to_le32(component->comparison_stamp);
+ component_info->image_size = cpu_to_le32(total_len);
+ component_info->classification = cpu_to_le16(component->classification);
+ component_info->identifier = cpu_to_le16(component->identifier);
+ component_info->options = cpu_to_le16(component->options);
+ component_info->version_str_type = component->version_type;
+ component_info->version_str_len = component->version_len;
+ memcpy(component_info->version_str, component->version_string,
+ component->version_len);
+
+ dl = priv_to_devlink(pdsc);
+
+ offset = 0;
+ while (offset < total_len) {
+ union pds_core_dev_comp comp = {};
+ dma_addr_t dma_addr;
+ u8 *component_data;
+ u16 copy_sz;
+
+ copy_sz = min_t(unsigned int, PDS_PAGE_SIZE, total_len - offset);
+ component_data = kmemdup(component->component_data + offset,
+ copy_sz, GFP_KERNEL);
+ if (!component_data) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ dma_addr = dma_map_single(dev, component_data, copy_sz,
+ DMA_TO_DEVICE);
+ err = dma_mapping_error(pdsc->dev, dma_addr);
+ if (err) {
+ dev_err(dev, "Failed to dma_map component_data at offset 0x%x copy_sz 0x%x: %pe\n",
+ offset, copy_sz, ERR_PTR(err));
+ kfree(component_data);
+ goto err_out;
+ }
+
+ err = pdsc_devcmd_send_component(pdsc, component_info, info_sz,
+ dma_addr, copy_sz, offset,
+ slot_id, &comp);
+ dma_unmap_single(dev, dma_addr, copy_sz, DMA_TO_DEVICE);
+ kfree(component_data);
+ if (err && err != -EAGAIN &&
+ comp.send_component.compat_response &&
+ (comp.send_component.compat_response_code ==
+ PDS_CORE_COMPONENT_STAMP_IDENTICAL ||
+ comp.send_component.compat_response_code ==
+ PDS_CORE_COMPONENT_STAMP_LOWER)) {
+ err = 0;
+ devlink_flash_update_status_notify(dl, "Skipped",
+ component_name, 0, 0);
+ goto skip_component;
+ }
+
+ if (err) {
+ dev_err(dev,
+ "send_component failed offset 0x%x addr 0x%llx len 0x%x: %pe\n",
+ offset, dma_addr, copy_sz, ERR_PTR(err));
+ goto err_out;
+ }
+
+ offset += copy_sz;
+ devlink_flash_update_status_notify(dl,
+ "Erasing/Flashing",
+ component_name, offset,
+ total_len);
+ }
+
+ return 0;
+
+err_out:
+ devlink_flash_update_status_notify(dl, "Erasing/Flashing Component Failed",
+ component_name, 0, 0);
+skip_component:
+ vfree(component_info);
+ return err;
+}
+
+static int pdsc_devcmd_finalize_update(struct pdsc *pdsc)
+{
+ union pds_core_dev_cmd cmd = {
+ .finalize_update.opcode = PDS_CORE_CMD_FINALIZE_UPDATE,
+ .finalize_update.ver = 1,
+ };
+ union pds_core_dev_comp comp = {};
+
+ return pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
+}
+
+static int pdsc_finalize_update(struct pldmfw *context)
+{
+ struct pds_core_fwu_priv *priv =
+ container_of(context, struct pds_core_fwu_priv, context);
+ const char *component_name = priv->params->component;
+ unsigned long start_time, end_time;
+ struct device *dev = context->dev;
+ struct pdsc *pdsc = priv->pdsc;
+ struct devlink *dl;
+ int err;
+
+ dl = priv_to_devlink(pdsc);
+
+ start_time = jiffies;
+ end_time = start_time + (PDSC_FW_INSTALL_TIMEOUT * HZ);
+ do {
+ err = pdsc_devcmd_finalize_update(pdsc);
+ if (!err || err != -EAGAIN)
+ break;
+
+ dev_dbg(dev, "retrying finalize_update: %pe\n", ERR_PTR(err));
+ msleep(20);
+ } while (time_before(jiffies, end_time) && err == -EAGAIN);
+
+ if (err) {
+ devlink_flash_update_status_notify(dl, "Finalize Update Failed",
+ component_name, 0, 0);
+ dev_err(dev, "finalize_update failed: %pe\n", ERR_PTR(err));
+ return err;
+ }
+
+ devlink_flash_update_status_notify(dl, "Finalized Update",
+ component_name, 0, 0);
+ return 0;
+}
+
+static const struct pldmfw_ops pdsc_pldmfw_ops = {
+ .match_record = pdsc_match_record_descs,
+ .send_package_data = pdsc_send_package_data,
+ .send_component_table = pdsc_send_component_table,
+ .flash_component = pdsc_flash_component,
+ .finalize_update = pdsc_finalize_update
+};
+
+static int pdsc_pldm_firmware_update(struct pdsc *pdsc,
+ struct devlink_flash_update_params *params,
+ struct netlink_ext_ack *extack,
+ const struct firmware *fw)
+{
+ struct pds_core_fwu_priv priv = {};
+ int err;
+
+ /* If no component filter specified, devlink core didn't refresh cache,
+ * so we must refresh to handle stale cache from previous updates.
+ */
+ if (!params->component) {
+ err = pdsc_get_component_info(pdsc);
+ if (err) {
+ dev_err(pdsc->dev, "Failed to get component info: %pe\n", ERR_PTR(err));
+ return err;
+ }
+ }
+
+ INIT_LIST_HEAD(&priv.components);
+ priv.context.ops = &pdsc_pldmfw_ops;
+ priv.context.dev = pdsc->dev;
+ priv.params = params;
+ priv.pdsc = pdsc;
+
+ err = pldmfw_flash_image(&priv.context, fw);
+ pdsc_free_fwu_priv(&priv);
+
+ return err;
+}
+
+int pdsc_firmware_update(struct pdsc *pdsc,
+ struct devlink_flash_update_params *params,
+ struct netlink_ext_ack *extack)
+{
+ if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2 &&
+ pdsc->dev_ident.capabilities & cpu_to_le64(PDS_CORE_DEV_CAP_PLDM_FW_UPDATE))
+ return pdsc_pldm_firmware_update(pdsc, params, extack, params->fw);
+
+ return pdsc_legacy_firmware_update(pdsc, params->fw, extack);
+}
diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c
index 22db78343eb0..f0d0993f9d91 100644
--- a/drivers/net/ethernet/amd/pds_core/main.c
+++ b/drivers/net/ethernet/amd/pds_core/main.c
@@ -340,7 +340,9 @@ static int pdsc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
is_pf = !pdev->is_virtfn;
ops = is_pf ? &pdsc_dl_ops : &pdsc_dl_vf_ops;
- dl = devlink_alloc(ops, sizeof(struct pdsc), dev);
+ dl = devlink_alloc(ops, sizeof(struct pdsc) +
+ PDS_CORE_FW_COMPONENT_LIST_LEN *
+ sizeof(struct pds_core_fw_component_info), dev);
if (!dl)
return -ENOMEM;
pdsc = devlink_priv(dl);
diff --git a/include/linux/pds/pds_core_if.h b/include/linux/pds/pds_core_if.h
index 619186f26b5b..b8052985dddf 100644
--- a/include/linux/pds/pds_core_if.h
+++ b/include/linux/pds/pds_core_if.h
@@ -40,6 +40,13 @@ enum pds_core_cmd_opcode {
PDS_CORE_CMD_FW_DOWNLOAD = 4,
PDS_CORE_CMD_FW_CONTROL = 5,
+ PDS_CORE_CMD_GET_COMPONENT_INFO = 6,
+ PDS_CORE_CMD_SEND_PKG_DATA = 7,
+ PDS_CORE_CMD_SEND_COMPONENT_TBL = 8,
+ PDS_CORE_CMD_SEND_COMPONENT = 9,
+ PDS_CORE_CMD_FINALIZE_UPDATE = 10,
+ PDS_CORE_CMD_MATCH_RECORD_DESC = 11,
+
/* SR/IOV commands */
PDS_CORE_CMD_VF_GETATTR = 60,
PDS_CORE_CMD_VF_SETATTR = 61,
@@ -100,6 +107,14 @@ struct pds_core_drv_identity {
char driver_ver_str[32];
};
+/**
+ * enum pds_core_dev_capability - Device capabilities
+ * @PDS_CORE_DEV_CAP_PLDM_FW_UPDATE: Device only supports FW update via PLDM
+ */
+enum pds_core_dev_capability {
+ PDS_CORE_DEV_CAP_PLDM_FW_UPDATE = BIT(0),
+};
+
#define PDS_DEV_TYPE_MAX 16
/**
* struct pds_core_dev_identity - Device identity information
@@ -119,6 +134,8 @@ struct pds_core_drv_identity {
* value in usecs to device units using:
* device units = usecs * mult / div
* @vif_types: How many of each VIF device type is supported
+ * @max_fw_slots: Maximum number of fw slots/components
+ * only supported on version >= PDS_CORE_IDENTITY_VERSION_2
* @capabilities: Device capabilities
* only supported on version >= PDS_CORE_IDENTITY_VERSION_2
*/
@@ -133,6 +150,7 @@ struct pds_core_dev_identity {
__le32 intr_coal_mult;
__le32 intr_coal_div;
__le16 vif_types[PDS_DEV_TYPE_MAX];
+ __le16 max_fw_slots;
__le64 capabilities;
};
@@ -284,6 +302,7 @@ enum pds_core_fw_slot {
PDS_CORE_FW_SLOT_A = 1,
PDS_CORE_FW_SLOT_B = 2,
PDS_CORE_FW_SLOT_GOLD = 3,
+ PDS_CORE_FW_SLOT_MAX = 0xff,
};
/**
@@ -450,6 +469,348 @@ struct pds_core_vf_ctrl_comp {
u8 status;
};
+/**
+ * struct pds_core_send_pkg_data_cmd
+ * @opcode: Opcode PDS_CORE_CMD_SEND_PKG_DATA
+ * @ver: Driver's max support version of this command
+ * @total_len: Total length of the package data
+ * @offset: Offset in the package data, non-zero if multiple commands are
+ * needed for sending the package data
+ * @data_len: Length of data stored at data_pa
+ * @data_pa: Data physical address for DMA to device
+ *
+ * The package data may be too large to store in a single buffer, so multiple
+ * PDS_CORE_CMD_SEND_PKG_DATA devcmds may be needed.
+ */
+struct pds_core_send_pkg_data_cmd {
+ u8 opcode;
+ u8 ver;
+ __le16 total_len;
+ __le16 offset;
+ __le16 data_len;
+ __le64 data_pa;
+};
+
+/**
+ * struct pds_core_send_pkg_data_comp - Send package data completion
+ * @status: Status of the command (enum pds_core_status_code)
+ * @ver: Device's max supported version of this command
+ * @rsvd: Word boundary padding
+ */
+struct pds_core_send_pkg_data_comp {
+ u8 status;
+ u8 ver;
+ u8 rsvd[2];
+};
+
+/**
+ * struct pds_core_component_tbl - Component table details
+ * @comparison_stamp: Comparison stamp used for component version checks
+ * @classification: Vendor specific classification info
+ * @identifier: Component's ID
+ * @transfer_flag: Part of the component table this request represents
+ * @version_str_type: The types of strings used
+ * @version_str_len: Length of @version_str
+ * @version_str: Component version information
+ */
+struct pds_core_component_tbl {
+ __le32 comparison_stamp;
+ __le16 classification;
+ __le16 identifier;
+ u8 transfer_flag;
+ u8 version_str_type;
+ u8 version_str_len;
+ u8 version_str[];
+};
+
+/**
+ * struct pds_core_send_component_tbl_cmd
+ * @opcode: Opcode PDS_CORE_CMD_SEND_COMPONENT_TBL
+ * @ver: Driver's max support version of this command
+ * @slot_id: enum pds_core_fw_slot
+ * @rsvd: Word boundary padding
+ *
+ * Expects to find component table info (struct pds_core_component_tbl)
+ * in cmd_regs->data. Driver should keep the devcmd interface locked
+ * while preparing the component table info.
+ */
+struct pds_core_send_component_tbl_cmd {
+ u8 opcode;
+ u8 ver;
+ u8 slot_id;
+ u8 rsvd;
+};
+
+enum pds_core_component_resp_code {
+ PDS_CORE_COMPONENT_VALID = 0x0,
+ PDS_CORE_COMPONENT_STAMP_IDENTICAL = 0x1,
+ PDS_CORE_COMPONENT_STAMP_LOWER = 0x2,
+ PDS_CORE_COMPONENT_STAMP_OR_VERSION_INVALID = 0x3,
+ PDS_CORE_COMPONENT_CONFLICT = 0x4,
+ PDS_CORE_COMPONENT_PREREQS_NOT_MET = 0x5,
+ PDS_CORE_COMPONENT_NOT_SUPPORTED = 0x6,
+ PDS_CORE_COMPONENT_FW_TYPE_INVALID = 0xd0,
+};
+
+/**
+ * struct pds_core_send_component_tbl_comp
+ * @status: Status of the command (enum pds_core_status_code)
+ * @ver: Device's max supported version of this command
+ * @completion_code: Component completion code
+ * @response: Component response
+ * @response_code: Component response code
+ * @slot_id: Actual slot_id of the component (enum pds_core_fw_slot)
+ *
+ * When alternate firmware is requested via PDS_CORE_FW_SLOT_INVALID, the
+ * completion's slot_id will match the actual slot_id that will be flashed
+ * on success. When specific components are flashed, then the completion's
+ * slot_id will match the command's slot_id.
+ *
+ * On failure the slot_id will be set to PDS_CORE_FW_SLOT_MAX.
+ * On success the slot_id will be PDS_CORE_FW_SLOT_A, PDS_CORE_FW_SLOT_B, or
+ * PDS_CORE_FW_SLOT_GOLD.
+ *
+ * @rsvd: Word boundary padding
+ */
+struct pds_core_send_component_tbl_comp {
+ u8 status;
+ u8 ver;
+ u8 completion_code;
+ u8 response;
+ u8 response_code;
+ u8 slot_id;
+ u8 rsvd[2];
+};
+
+/**
+ * enum pds_core_send_component_op - PDS_CORE_CMD_SEND_COMPONENT operation
+ * @PDS_CORE_SEND_COMPONENT_START: Initial operation to start transfer
+ * @PDS_CORE_SEND_COMPONENT_STATUS: Subsequent calls to check on status
+ * PDS_CORE_CMD_SEND_COMPONENT
+ */
+enum pds_core_send_component_op {
+ PDS_CORE_SEND_COMPONENT_START = 0,
+ PDS_CORE_SEND_COMPONENT_STATUS = 1,
+};
+
+#define PDS_CORE_FW_COMPONENT_ID_INVALID 0xFFFF
+/**
+ * struct pds_core_flash_component - Component details
+ * @comparison_stamp: Comparison stamp used for component version checks
+ * @image_size: Component image size
+ * @classification: Vendor specific classification info
+ * @identifier: Component's ID
+ * @options: Component options
+ * @rsvd: Word boundary padding
+ * @version_str_type: The types of strings used
+ * @version_str_len: Length of @version_str
+ * @version_str: Component version information
+ */
+struct pds_core_flash_component {
+ __le32 comparison_stamp;
+ __le32 image_size;
+ __le16 classification;
+ __le16 identifier;
+ __le16 options;
+ u8 rsvd[3];
+ u8 version_str_type;
+ u8 version_str_len;
+ u8 version_str[];
+};
+
+/**
+ * struct pds_core_send_component_cmd
+ * @opcode: Opcode PDS_CORE_CMD_SEND_COMPONENT
+ * @ver: Driver's max supported version of this command
+ * @slot_id: enum pds_core_fw_slot
+ * @operation: enum pds_core_send_component_op
+ * @offset: Offset into the component, non-zero if multiple commands
+ * are needed for a single component
+ * @data_len: Length of this part of the component stored at @data_pa
+ * @rsvd: Word boundary padding
+ * @data_pa: DMA address of the component
+ *
+ * A component may be too large to store in a single buffer, so multiple
+ * PDS_CORE_CMD_SEND_COMPONENT devcmds may be needed.
+ *
+ * Expects to find flash component info (struct pds_core_flash_component)
+ * in cmd_regs->data. Driver should keep the devcmd interface locked
+ * while preparing and sending the flash component info.
+ */
+struct pds_core_send_component_cmd {
+ u8 opcode;
+ u8 ver;
+ u8 slot_id;
+ u8 operation;
+ __le32 offset;
+ __le32 data_len;
+ u8 rsvd[4];
+ __le64 data_pa;
+};
+
+/**
+ * struct pds_core_send_component_comp
+ * @status: Status of the command (enum pds_core_status_code)
+ * @ver: Device's max supported version of this command
+ * @completion_code: Completion code
+ * @compat_response: Compatibility response (0 = Component can be updated)
+ * @compat_response_code: Compatibility response code
+ * @rsvd: Word boundary padding
+ */
+struct pds_core_send_component_comp {
+ u8 status;
+ u8 ver;
+ u8 completion_code;
+ u8 compat_response;
+ u8 compat_response_code;
+ u8 rsvd[3];
+};
+
+/**
+ * enum pds_core_component_info_flags
+ * @PDS_CORE_FW_COMPONENT_INFO_F_RUNNING: Component is currently running
+ * @PDS_CORE_FW_COMPONENT_INFO_F_STARTUP: Component version on next FW boot
+ * @PDS_CORE_FW_COMPONENT_INFO_F_FIXED: Component is fixed and cannot be updated
+ * @PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME: Component can be updated by name
+ */
+enum pds_core_component_info_flags {
+ PDS_CORE_FW_COMPONENT_INFO_F_RUNNING = BIT(0),
+ PDS_CORE_FW_COMPONENT_INFO_F_STARTUP = BIT(1),
+ PDS_CORE_FW_COMPONENT_INFO_F_FIXED = BIT(2),
+ PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME = BIT(3),
+};
+
+/**
+ * struct pds_core_fw_component_info - GET_COMPONENT_INFO entry
+ * @name: Component's name
+ * @rsvd: Word boundary padding
+ * @flags: enum pds_core_component_info_flags
+ * @identifier: Component's identifier
+ * @slot_id: Component's slot identifier
+ * @version: Component's version
+ */
+struct pds_core_fw_component_info {
+#define PDS_CORE_FW_COMPONENT_NAME_BUFLEN 24
+ char name[PDS_CORE_FW_COMPONENT_NAME_BUFLEN];
+ u8 rsvd[4];
+ __le16 flags;
+ u8 identifier;
+ u8 slot_id;
+#define PDS_CORE_FW_COMPONENT_VER_BUFLEN 32
+ char version[PDS_CORE_FW_COMPONENT_VER_BUFLEN];
+};
+
+#define PDS_CORE_FW_COMPONENT_LIST_LEN ((PDS_PAGE_SIZE - \
+ sizeof(struct pds_core_component_list_info)) / \
+ sizeof(struct pds_core_fw_component_info))
+
+#if defined(__has_attribute) && !__has_attribute(__counted_by__)
+#define __counted_by(member)
+#endif
+
+/**
+ * struct pds_core_component_list_info - GET_COMPONENT_INFO completion data
+ * @num_components: Number of valid components
+ * @info: List of valid components
+ */
+struct pds_core_component_list_info {
+ u8 num_components;
+ struct pds_core_fw_component_info info[] __counted_by(num_components);
+} __packed;
+
+/**
+ * struct pds_core_get_component_info_cmd - GET_COMPONENT_INFO command
+ * @opcode: PDS_CORE_CMD_GET_COMPONENT_INFO
+ * @ver: Driver's max supported version of this command
+ * @data_len: Length of data at data_pa
+ * @rsvd: Word boundary padding
+ * @data_pa: DMA address of data
+ *
+ * FW populates struct pds_core_component_list_info pointed to by @data_pa
+ */
+struct pds_core_get_component_info_cmd {
+ u8 opcode;
+ u8 ver;
+ __le16 data_len;
+ u8 rsvd[4];
+ __le64 data_pa;
+};
+
+/**
+ * struct pds_core_get_component_info_comp - GET_COMPONENT_INFO completion
+ * @status: enum pds_core_status_code
+ * @ver: Device's max supported version of this command
+ * @rsvd: Word boundary padding
+ */
+struct pds_core_get_component_info_comp {
+ u8 status;
+ u8 ver;
+ u8 rsvd[2];
+};
+
+/**
+ * struct pds_core_finalize_update_cmd - FINALIZE_UPDATE command
+ * @opcode: PDS_CORE_CMD_FINALIZE_UPDATE
+ * @ver: Driver's max support version of this command
+ * @rsvd: Word boundary padding
+ *
+ * Driver sends at the end of updating all components to finalize the update
+ */
+struct pds_core_finalize_update_cmd {
+ u8 opcode;
+ u8 ver;
+ u8 rsvd[2];
+};
+
+/**
+ * struct pds_core_finalize_update_comp - FINALIZE_UPDATE completion
+ * @status: enum pds_core_status_code
+ * @ver: Device's max supported version of this command
+ * @rsvd: Word boundary padding
+ */
+struct pds_core_finalize_update_comp {
+ u8 status;
+ u8 ver;
+ u8 rsvd[2];
+};
+
+/**
+ * struct pds_core_match_record_desc_cmd - MATCH_RECORD_DESC command
+ * @opcode: PDS_CORE_CMD_MATCH_RECORD_DESC
+ * @ver: Driver's max supported version of this command
+ * @type: PLDM Descriptor Identifier Type
+ * @size: Length of the Descriptor Identifier Value
+ * @rsvd: Word boundary padding
+ *
+ * Expects to find the Descriptor Identifier Data in cmd_regs->data. Driver
+ * should keep the devcmd interface locked while preparing and sending this
+ * command.
+ */
+struct pds_core_match_record_desc_cmd {
+ u8 opcode;
+ u8 ver;
+ __le16 type;
+ __le16 size;
+ u8 rsvd[2];
+};
+
+/**
+ * struct pds_core_match_record_desc_comp - MATCH_RECORD_DESC completion
+ * @status: enum pds_core_status_code
+ * @ver: Device's max supported version of this command
+ * @match: Whether or not the Record Descriptor matches the device
+ * @rsvd: Word boundary padding
+ *
+ * When status is PDS_RC_SUCCESS, then @match is valid, otherwise it's
+ * undefined.
+ */
+struct pds_core_match_record_desc_comp {
+ u8 status;
+ u8 ver;
+ u8 match;
+ u8 rsvd;
+};
+
/*
* union pds_core_dev_cmd - Overlay of core device command structures
*/
@@ -466,6 +827,13 @@ union pds_core_dev_cmd {
struct pds_core_vf_setattr_cmd vf_setattr;
struct pds_core_vf_getattr_cmd vf_getattr;
struct pds_core_vf_ctrl_cmd vf_ctrl;
+
+ struct pds_core_get_component_info_cmd get_component_info;
+ struct pds_core_send_pkg_data_cmd send_pkg_data;
+ struct pds_core_send_component_tbl_cmd send_component_tbl;
+ 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;
};
/*
@@ -484,6 +852,13 @@ union pds_core_dev_comp {
struct pds_core_vf_setattr_comp vf_setattr;
struct pds_core_vf_getattr_comp vf_getattr;
struct pds_core_vf_ctrl_comp vf_ctrl;
+
+ struct pds_core_get_component_info_comp get_component_info;
+ struct pds_core_send_pkg_data_comp send_pkg_data;
+ struct pds_core_send_component_tbl_comp send_component_tbl;
+ 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;
};
/**
--
2.43.0
^ permalink raw reply related
* [PATCH 4/6] pds_core: add PLDM component info display
From: Nikhil P. Rao @ 2026-04-29 7:58 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner
In-Reply-To: <20260429-b4-pldm-b4-v1-0-e43b6c92e46c@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
Add detailed component information display. This allows users to see
individual firmware components, their versions, and update status via
devlink info. Components are marked as fixed, running, or stored based
on their flags.
Example output:
$ devlink dev info pci/0000:b5:00.0
...
versions:
running:
fw.goldfw 1.2.3
fw.mainfwa 1.2.4
fw.mainfwb 1.2.3
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
---
drivers/net/ethernet/amd/pds_core/devlink.c | 75 ++++++++++++++++++++++++++---
1 file changed, 69 insertions(+), 6 deletions(-)
diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c
index 7f44e1a8d4fd..c519fde45d71 100644
--- a/drivers/net/ethernet/amd/pds_core/devlink.c
+++ b/drivers/net/ethernet/amd/pds_core/devlink.c
@@ -93,14 +93,61 @@ int pdsc_dl_flash_update(struct devlink *dl,
return pdsc_firmware_update(pdsc, params, extack);
}
+static int pdsc_dl_component_info_get(struct devlink *dl,
+ struct devlink_info_req *req,
+ struct netlink_ext_ack *extack)
+{
+ struct pds_core_component_list_info *list_info;
+ struct pdsc *pdsc = devlink_priv(dl);
+ u8 num_components;
+ char buf[32];
+ int err;
+ int i;
+
+ err = pdsc_get_component_info(pdsc);
+ if (err) {
+ dev_err(pdsc->dev, "Failed to get component_info %pe", ERR_PTR(err));
+ return err;
+ }
+
+ list_info = &pdsc->fw_components;
+ num_components = min_t(u8, list_info->num_components,
+ le16_to_cpu(pdsc->dev_ident.max_fw_slots));
+ for (i = 0; i < num_components; i++) {
+ enum devlink_info_version_type dl_ver_type = DEVLINK_INFO_VERSION_TYPE_NONE;
+ struct pds_core_fw_component_info *info = &list_info->info[i];
+ u16 flags = le16_to_cpu(info->flags);
+
+ snprintf(buf, sizeof(buf), "fw.%s", info->name);
+ if (flags & PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME)
+ dl_ver_type = DEVLINK_INFO_VERSION_TYPE_COMPONENT;
+
+ if (flags & PDS_CORE_FW_COMPONENT_INFO_F_FIXED)
+ err = devlink_info_version_fixed_put(req, buf,
+ info->version);
+ else if (flags & PDS_CORE_FW_COMPONENT_INFO_F_RUNNING)
+ err = devlink_info_version_running_put_ext(req, buf,
+ info->version, dl_ver_type);
+ else
+ err = devlink_info_version_stored_put_ext(req, buf,
+ info->version, dl_ver_type);
+
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static char *fw_slotnames[] = {
"fw.goldfw",
"fw.mainfwa",
"fw.mainfwb",
};
-int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
- struct netlink_ext_ack *extack)
+static int pdsc_dl_fw_list_info_get(struct devlink *dl,
+ struct devlink_info_req *req,
+ struct netlink_ext_ack *extack)
{
union pds_core_dev_cmd cmd = {
.fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,
@@ -132,11 +179,27 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
return err;
}
- err = devlink_info_version_running_put(req,
- DEVLINK_INFO_VERSION_GENERIC_FW,
- pdsc->dev_info.fw_version);
- if (err)
+ return devlink_info_version_running_put(req,
+ DEVLINK_INFO_VERSION_GENERIC_FW,
+ pdsc->dev_info.fw_version);
+}
+
+int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
+ struct netlink_ext_ack *extack)
+{
+ struct pdsc *pdsc = devlink_priv(dl);
+ char buf[32];
+ int err;
+
+ if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2)
+ err = pdsc_dl_component_info_get(dl, req, extack);
+ else
+ err = pdsc_dl_fw_list_info_get(dl, req, extack);
+ if (err) {
+ dev_err(pdsc->dev, "Failed to get devlink info for identity version %u: %pe\n",
+ pdsc->dev_ident.version, ERR_PTR(err));
return err;
+ }
snprintf(buf, sizeof(buf), "0x%x", pdsc->dev_info.asic_type);
err = devlink_info_version_fixed_put(req,
--
2.43.0
^ permalink raw reply related
* [PATCH 2/6] pds_core: add support for identity version 2
From: Nikhil P. Rao @ 2026-04-29 7:58 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner
In-Reply-To: <20260429-b4-pldm-b4-v1-0-e43b6c92e46c@amd.com>
From: Brett Creeley <brett.creeley@amd.com>
Add a new capabilities field in struct pds_core_drv_identity,
which requires bumping the identity version to 2, i.e.
PDS_CORE_IDENTITY_VERSION_2. If version 2 negotiation fails,
then quietly fall back to version 1. If version 1 negotiation
fails, then driver load will fail.
Another patch in the series will make use of the capabilities
field.
Signed-off-by: Brett Creeley <brett.creeley@amd.com>
---
drivers/net/ethernet/amd/pds_core/dev.c | 28 +++++++++++++++++++++++-----
include/linux/pds/pds_core_if.h | 4 ++++
2 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
index 5b86d6cd0ac3..f77bd5e48b92 100644
--- a/drivers/net/ethernet/amd/pds_core/dev.c
+++ b/drivers/net/ethernet/amd/pds_core/dev.c
@@ -243,15 +243,17 @@ int pdsc_devcmd_reset(struct pdsc *pdsc)
return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
}
-static int pdsc_devcmd_identify_locked(struct pdsc *pdsc)
+static int pdsc_devcmd_identify_locked(struct pdsc *pdsc, u8 drv_ident_ver,
+ bool do_msg)
{
union pds_core_dev_comp comp = {};
union pds_core_dev_cmd cmd = {
.identify.opcode = PDS_CORE_CMD_IDENTIFY,
- .identify.ver = PDS_CORE_IDENTITY_VERSION_1,
+ .identify.ver = drv_ident_ver,
};
- return pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout);
+ return __pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout,
+ do_msg);
}
static void pdsc_init_devinfo(struct pdsc *pdsc)
@@ -274,8 +276,9 @@ static void pdsc_init_devinfo(struct pdsc *pdsc)
dev_dbg(pdsc->dev, "fw_version %s\n", pdsc->dev_info.fw_version);
}
-static int pdsc_identify(struct pdsc *pdsc)
+static int pdsc_identify_ver(struct pdsc *pdsc, u8 drv_ident_ver)
{
+ bool do_msg = drv_ident_ver == PDS_CORE_IDENTITY_VERSION_1;
struct pds_core_drv_identity drv = {};
size_t sz;
int err;
@@ -298,7 +301,7 @@ static int pdsc_identify(struct pdsc *pdsc)
sz = min_t(size_t, sizeof(drv), sizeof(pdsc->cmd_regs->data));
memcpy_toio(&pdsc->cmd_regs->data, &drv, sz);
- err = pdsc_devcmd_identify_locked(pdsc);
+ err = pdsc_devcmd_identify_locked(pdsc, drv_ident_ver, do_msg);
if (!err) {
sz = min_t(size_t, sizeof(pdsc->dev_ident),
sizeof(pdsc->cmd_regs->data));
@@ -327,6 +330,21 @@ static int pdsc_identify(struct pdsc *pdsc)
return 0;
}
+static int pdsc_identify(struct pdsc *pdsc)
+{
+ int err;
+
+ /* Older firmware rejects anything but PDS_CORE_IDENTIFY_VERSION_1
+ * instead of returning the max supported identify version, so retry if
+ * firmware doesn't support PDS_CORE_IDENTIFY_VERSION_2
+ */
+ err = pdsc_identify_ver(pdsc, PDS_CORE_IDENTITY_VERSION_2);
+ if (err)
+ err = pdsc_identify_ver(pdsc, PDS_CORE_IDENTITY_VERSION_1);
+
+ return err;
+}
+
void pdsc_dev_uninit(struct pdsc *pdsc)
{
if (pdsc->intr_info) {
diff --git a/include/linux/pds/pds_core_if.h b/include/linux/pds/pds_core_if.h
index 17a87c1a55d7..619186f26b5b 100644
--- a/include/linux/pds/pds_core_if.h
+++ b/include/linux/pds/pds_core_if.h
@@ -119,6 +119,8 @@ struct pds_core_drv_identity {
* value in usecs to device units using:
* device units = usecs * mult / div
* @vif_types: How many of each VIF device type is supported
+ * @capabilities: Device capabilities
+ * only supported on version >= PDS_CORE_IDENTITY_VERSION_2
*/
struct pds_core_dev_identity {
u8 version;
@@ -131,9 +133,11 @@ struct pds_core_dev_identity {
__le32 intr_coal_mult;
__le32 intr_coal_div;
__le16 vif_types[PDS_DEV_TYPE_MAX];
+ __le64 capabilities;
};
#define PDS_CORE_IDENTITY_VERSION_1 1
+#define PDS_CORE_IDENTITY_VERSION_2 2
/**
* struct pds_core_dev_identify_cmd - Driver/device identify command
--
2.43.0
^ permalink raw reply related
* [PATCH 0/6] pds_core: Add PLDM firmware update and host backed memory support
From: Nikhil P. Rao @ 2026-04-29 7:58 UTC (permalink / raw)
To: Brett Creeley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Kees Cook, Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, Nikhil P. Rao, eric.joyner,
Vamsi Atluri
This series adds PLDM-based firmware update support to the pds_core
driver. PLDM (Platform Level Data Model) is a DMTF standard for firmware
management that provides a vendor-neutral interface for firmware updates.
The implementation uses the kernel's pldmfw library for package parsing
and component matching. Users can update entire firmware packages or
individual components via devlink flash. Component information is
displayed via devlink info, showing firmware versions and update status
for each component.
The series also adds host backed memory support, allowing firmware to
request memory pages from the host for its operations.
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
---
Brett Creeley (4):
pds_core: add support for quiet devcmd failures
pds_core: add support for identity version 2
pds_core: add PLDM firmware update support via devlink flash
pds_core: add PLDM component info display
Vamsi Atluri (2):
pds_core: add host backed memory support for firmware
pds_core: add debugfs support for host backed memory
drivers/net/ethernet/amd/Kconfig | 1 +
drivers/net/ethernet/amd/pds_core/core.c | 166 +++++++
drivers/net/ethernet/amd/pds_core/core.h | 33 +-
drivers/net/ethernet/amd/pds_core/debugfs.c | 43 ++
drivers/net/ethernet/amd/pds_core/dev.c | 86 +++-
drivers/net/ethernet/amd/pds_core/devlink.c | 77 ++-
drivers/net/ethernet/amd/pds_core/fw.c | 699 +++++++++++++++++++++++++++-
drivers/net/ethernet/amd/pds_core/main.c | 7 +-
include/linux/pds/pds_adminq.h | 132 ++++++
include/linux/pds/pds_core_if.h | 381 +++++++++++++++
10 files changed, 1603 insertions(+), 22 deletions(-)
---
base-commit: 1f5ffc672165ff851063a5fd044b727ab2517ae3
change-id: 20260429-b4-pldm-b4-b36169e986e6
Best regards,
--
Nikhil P. Rao <nikhil.rao@amd.com>
^ permalink raw reply
* Re: [PATCH rc 00/15] Various bug fixes for RDMA drivers in the uapi functions
From: Junxian Huang @ 2026-04-29 7:55 UTC (permalink / raw)
To: Jason Gunthorpe, Andrew Lunn,
Broadcom internal kernel review list, Bryan Tan, Eric Dumazet,
Konstantin Taranov, Jakub Kicinski, Leon Romanovsky, linux-hyperv,
linux-rdma, netdev, Paolo Abeni, Selvin Xavier, Chengchang Tang,
Tariq Toukan, Vishnu Dasa, Yishai Hadas
Cc: Abhijit Gangurde, Adit Ranadive, Allen Hubbe, Andrew Boyer,
Aditya Sarwade, Brad Spengler, Bryan Tan, David S. Miller,
Dexuan Cui, Doug Ledford, George Zhang, Jorgen Hansen, Jianbo Liu,
Kai Aizen, Leon Romanovsky, Leon Romanovsky, Yixian Liu, Long Li,
Lijun Ou, Parav Pandit, patches, Roland Dreier, Roland Dreier,
Sagi Grimberg, Ajay Sharma, stable, Tariq Toukan, Wei Hu (Xavier),
Shaobo Xu, Nenglong Zhao
In-Reply-To: <0-v1-41f3135e5565+9d2-rdma_ai_fixes1_jgg@nvidia.com>
On 2026/4/29 0:17, Jason Gunthorpe wrote:
> All were found by Sashiko or Claude AI tools. They vary in severity, but
> are all things that shouldn't be present.
>
> Jason Gunthorpe (15):
> RDMA/hns: Fix xarray race in hns_roce_create_srq()
> RDMA/hns: Fix xarray race in hns_roce_create_qp_common()
> RDMA/hns: Fix unlocked call to hns_roce_qp_remove()
For hns patches:
Reviewed-by: Junxian Huang <huangjunxian6@hisilicon.com>
Thanks,
Junxian
^ permalink raw reply
* [PATCH 5.15.y] tipc: fix kernel warning when sending SYN message
From: Robert Garcia @ 2026-04-29 7:50 UTC (permalink / raw)
To: stable, Tung Nguyen
Cc: Jakub Kicinski, Jon Maloy, David S . Miller, Robert Garcia,
Al Viro, netdev, tipc-discussion, linux-kernel
From: Tung Nguyen <tung.q.nguyen@dektech.com.au>
[ Upstream commit 11a4d6f67cf55883dc78e31c247d1903ed7feccc ]
When sending a SYN message, this kernel stack trace is observed:
...
[ 13.396352] RIP: 0010:_copy_from_iter+0xb4/0x550
...
[ 13.398494] Call Trace:
[ 13.398630] <TASK>
[ 13.398630] ? __alloc_skb+0xed/0x1a0
[ 13.398630] tipc_msg_build+0x12c/0x670 [tipc]
[ 13.398630] ? shmem_add_to_page_cache.isra.71+0x151/0x290
[ 13.398630] __tipc_sendmsg+0x2d1/0x710 [tipc]
[ 13.398630] ? tipc_connect+0x1d9/0x230 [tipc]
[ 13.398630] ? __local_bh_enable_ip+0x37/0x80
[ 13.398630] tipc_connect+0x1d9/0x230 [tipc]
[ 13.398630] ? __sys_connect+0x9f/0xd0
[ 13.398630] __sys_connect+0x9f/0xd0
[ 13.398630] ? preempt_count_add+0x4d/0xa0
[ 13.398630] ? fpregs_assert_state_consistent+0x22/0x50
[ 13.398630] __x64_sys_connect+0x16/0x20
[ 13.398630] do_syscall_64+0x42/0x90
[ 13.398630] entry_SYSCALL_64_after_hwframe+0x63/0xcd
It is because commit a41dad905e5a ("iov_iter: saner checks for attempt
to copy to/from iterator") has introduced sanity check for copying
from/to iov iterator. Lacking of copy direction from the iterator
viewpoint would lead to kernel stack trace like above.
This commit fixes this issue by initializing the iov iterator with
the correct copy direction when sending SYN or ACK without data.
Fixes: f25dcc7687d4 ("tipc: tipc ->sendmsg() conversion")
Reported-by: syzbot+d43608d061e8847ec9f3@syzkaller.appspotmail.com
Acked-by: Jon Maloy <jmaloy@redhat.com>
Signed-off-by: Tung Nguyen <tung.q.nguyen@dektech.com.au>
Link: https://lore.kernel.org/r/20230214012606.5804-1-tung.q.nguyen@dektech.com.au
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
[ Use WRITE instead of ITER_SOURCE. ]
Signed-off-by: Robert Garcia <rob_garcia@163.com>
---
net/tipc/socket.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index eccb97b530b7..addf8e107485 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -2616,6 +2616,7 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest,
/* Send a 'SYN-' to destination */
m.msg_name = dest;
m.msg_namelen = destlen;
+ iov_iter_kvec(&m.msg_iter, WRITE, NULL, 0, 0);
/* If connect is in non-blocking case, set MSG_DONTWAIT to
* indicate send_msg() is never blocked.
@@ -2778,6 +2779,7 @@ static int tipc_accept(struct socket *sock, struct socket *new_sock, int flags,
__skb_queue_head(&new_sk->sk_receive_queue, buf);
skb_set_owner_r(buf, new_sk);
}
+ iov_iter_kvec(&m.msg_iter, WRITE, NULL, 0, 0);
__tipc_sendstream(new_sock, &m, 0);
release_sock(new_sk);
exit:
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox