* [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 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 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 4/6] pds_core: add PLDM component info display
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 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 net-next 5/6] pds_core: add host backed memory support for firmware
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>
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
* Re: [PATCH v3 2/2] pinctrl: qcom: add the TLMM driver for the Nord platforms
From: Pankaj Patil @ 2026-04-29 8:28 UTC (permalink / raw)
To: Bartosz Golaszewski, Bjorn Andersson, Linus Walleij, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Richard Cochran,
Bartosz Golaszewski, Shawn Guo, Arnd Bergmann, Dmitry Baryshkov
Cc: linux-arm-msm, linux-gpio, devicetree, linux-kernel, netdev
In-Reply-To: <20260428-nord-tlmm-v3-2-f16f08d084cc@oss.qualcomm.com>
On 4/28/2026 7:18 PM, Bartosz Golaszewski wrote:
> Add support for the TLMM controller on the Qualcomm Nord platform.
>
> Co-developed-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
> Signed-off-by: Shawn Guo <shengchao.guo@oss.qualcomm.com>
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> ---
> drivers/pinctrl/qcom/Kconfig.msm | 8 +
> drivers/pinctrl/qcom/Makefile | 1 +
> drivers/pinctrl/qcom/pinctrl-nord.c | 1771 +++++++++++++++++++++++++++++++++++
> 3 files changed, 1780 insertions(+)
>
Reviewed-by: Pankaj Patil <pankaj.patil@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH] xfrm: protect __xfrm_state_delete against double-unhash of byseq/byspi
From: Michal Kosiorek @ 2026-04-29 8:29 UTC (permalink / raw)
To: Steffen Klassert
Cc: Herbert Xu, David S. Miller, Eric Dumazet, Jakub Kicinski, Abeni,
Simon Horman, Greg KH, sd, netdev, linux-kernel, stable
In-Reply-To: <afGug2nzdfjEGHxO@secunet.com>
v2 addresses your two points:
- Added Fixes: tags (fe9f1d8779cb for byseq, 7b4dc3600e48 for byspi).
- Rebased on ipsec.git master (HEAD fa90a3145c03), `git apply --check`
clean. Same hunk applies to torvalds/master without changes.
Also added `Cc: stable@vger.kernel.org` per stable-kernel-rules.html
Option 1, since the Fixes: tags otherwise leave AUTOSEL doing the
disambiguation work alone.
Patch follows.
---
KASAN reproduces a slab-use-after-free in __xfrm_state_delete()'s
hlist_del_rcu calls under syzkaller load on linux-6.12.y stable
(reproduced on 6.12.47, also reachable via the same code path on
torvalds/master and on the ipsec tree). Nine unique signatures cluster
in the xfrm_state lifecycle, the load-bearing one being:
BUG: KASAN: slab-use-after-free in __hlist_del
include/linux/list.h:990 [inline]
BUG: KASAN: slab-use-after-free in hlist_del_rcu
include/linux/rculist.h:516 [inline]
BUG: KASAN: slab-use-after-free in __xfrm_state_delete net/xfrm/xfrm_state.c
Write of size 8 at addr ffff8881198bcb70 by task kworker/u8:9/435
Workqueue: netns cleanup_net
Call Trace:
__hlist_del / hlist_del_rcu
__xfrm_state_delete
xfrm_state_delete
xfrm_state_flush
xfrm_state_fini
ops_exit_list
cleanup_net
The other observed signatures hit the same slab object from
__xfrm_state_lookup, xfrm_alloc_spi, __xfrm_state_insert and an OOB
write variant of __xfrm_state_delete, all on the byseq/byspi
hash chains.
__xfrm_state_delete() guards its byseq and byspi unhashes with
value-based predicates:
if (x->km.seq)
hlist_del_rcu(&x->byseq);
if (x->id.spi)
hlist_del_rcu(&x->byspi);
while everywhere else in the file (e.g. state_cache, state_cache_input)
the safer hlist_unhashed() check is used. xfrm_alloc_spi() sets
x->id.spi = newspi inside xfrm_state_lock and then immediately inserts
into byspi, but a path that observes x->id.spi != 0 outside of
xfrm_state_lock can still skip-or-hit the byspi unhash inconsistently
with whether x is actually on the list. The same holds for x->km.seq
versus byseq, and the bydst/bysrc unhashes have no predicate at all,
so a second __xfrm_state_delete() on the same object writes through
LIST_POISON pprev.
The defensive change here:
- Use hlist_del_init_rcu() instead of hlist_del_rcu() on bydst,
bysrc, byseq and byspi so a second deletion is a no-op rather
than a write through LIST_POISON pprev. The byseq/byspi nodes
are already initialised in xfrm_state_alloc().
- Test hlist_unhashed() rather than the value predicate for
byseq/byspi, so the unhash decision tracks list state rather than
mutable scalar fields.
Empirical verification: applied this patch on top of v6.12.47, rebuilt,
and re-ran the same syzkaller harness for 1h16m on a previously-crashy
configuration that produced ~100 hits each of slab-use-after-free
Read in xfrm_alloc_spi / Read in __xfrm_state_lookup / Write in
__xfrm_state_delete. After the patch, 7.1M execs across 32 VMs at
~1550 exec/sec produced zero xfrm_state UAF/OOB hits. /proc/slabinfo
confirms the xfrm_state slab is actively allocated and freed during
the run (~143 KiB resident), so the fuzzer is still exercising those
code paths -- they just no longer crash.
Reproduction:
- Linux 6.12.47 x86_64 + KASAN_GENERIC + KASAN_INLINE + KCOV
- syzkaller @ 746545b8b1e4c3a128db8652b340d3df90ce61db
- 32 QEMU/KVM VMs x 2 vCPU on AWS c5.metal bare metal
- 9 unique signatures collected in ~9h, all within xfrm_state
lifecycle
Fixes: fe9f1d8779cb ("xfrm: add state hashtable keyed by seq")
Fixes: 7b4dc3600e48 ("[XFRM]: Do not add a state whose SPI is zero to
the SPI hash.")
Reported-by: Michal Kosiorek <mkosiorek121@gmail.com>
Tested-by: Michal Kosiorek <mkosiorek121@gmail.com>
Cc: stable@vger.kernel.org
Signed-off-by: Michal Kosiorek <mkosiorek121@gmail.com>
---
net/xfrm/xfrm_state.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 1748d374abca..686014d39429 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -818,17 +818,17 @@ int __xfrm_state_delete(struct xfrm_state *x)
spin_lock(&net->xfrm.xfrm_state_lock);
list_del(&x->km.all);
- hlist_del_rcu(&x->bydst);
- hlist_del_rcu(&x->bysrc);
- if (x->km.seq)
- hlist_del_rcu(&x->byseq);
+ hlist_del_init_rcu(&x->bydst);
+ hlist_del_init_rcu(&x->bysrc);
+ if (!hlist_unhashed(&x->byseq))
+ hlist_del_init_rcu(&x->byseq);
if (!hlist_unhashed(&x->state_cache))
hlist_del_rcu(&x->state_cache);
if (!hlist_unhashed(&x->state_cache_input))
hlist_del_rcu(&x->state_cache_input);
- if (x->id.spi)
- hlist_del_rcu(&x->byspi);
+ if (!hlist_unhashed(&x->byspi))
+ hlist_del_init_rcu(&x->byspi);
net->xfrm.state_num--;
xfrm_nat_keepalive_state_updated(x);
spin_unlock(&net->xfrm.xfrm_state_lock);
--
2.54.0
śr., 29 kwi 2026 o 09:08 Steffen Klassert
<steffen.klassert@secunet.com> napisał(a):
>
> On Tue, Apr 28, 2026 at 09:53:45AM +0200, Michal Kosiorek wrote:
> ...
> >
> > Reported-by: Michal Kosiorek <mkosiorek121@gmail.com>
> > Tested-by: Michal Kosiorek <mkosiorek121@gmail.com>
> > Signed-off-by: Michal Kosiorek <mkosiorek121@gmail.com>
>
> Please add a 'Fixes:' tag so the patch can be backported
> to the stable trees.
>
> > ---
> > net/xfrm/xfrm_state.c | 12 ++++++------
> > 1 file changed, 6 insertions(+), 6 deletions(-)
> >
> > diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
> > --- a/net/xfrm/xfrm_state.c
> > +++ b/net/xfrm/xfrm_state.c
> > @@ -758,16 +758,16 @@ int __xfrm_state_delete(struct xfrm_state *x)
> >
> > spin_lock(&net->xfrm.xfrm_state_lock);
> > list_del(&x->km.all);
> > - hlist_del_rcu(&x->bydst);
> > - hlist_del_rcu(&x->bysrc);
> > - if (x->km.seq)
> > - hlist_del_rcu(&x->byseq);
> > + hlist_del_init_rcu(&x->bydst);
> > + hlist_del_init_rcu(&x->bysrc);
> > + if (!hlist_unhashed(&x->byseq))
> > + hlist_del_init_rcu(&x->byseq);
> > if (!hlist_unhashed(&x->state_cache))
> > hlist_del_rcu(&x->state_cache);
> > if (!hlist_unhashed(&x->state_cache_input))
> > hlist_del_rcu(&x->state_cache_input);
> >
> > - if (x->id.spi)
> > - hlist_del_rcu(&x->byspi);
> > + if (!hlist_unhashed(&x->byspi))
> > + hlist_del_init_rcu(&x->byspi);
> > net->xfrm.state_num--;
> > xfrm_nat_keepalive_state_updated(x);
> > spin_unlock(&net->xfrm.xfrm_state_lock);
>
> This does not allpy to the ipsec tree. Please
> rebase on the ipsec tree and resend.
>
> Thanks!
^ permalink raw reply related
* Re: [bug report] Potential refcounting
From: Tariq Toukan @ 2026-04-29 8:36 UTC (permalink / raw)
To: Ginger; +Cc: netdev, linux-rdma, linux-kernel, ttoukan.linux
In-Reply-To: <CAGp+u1bdbe_5Xk6icnDcs70Krbr_6M4yXjhs0HVo8T4953wNSQ@mail.gmail.com>
On 27/04/2026 5:07, Ginger wrote:
> Dear Linux kernel maintainers,
>
> My research-based static analyzer found a potential
> refcounting/atomicity bug within the
> 'drivers/net/ethernet/mellanox/mlx4' subsystem, more specifically, in
> 'drivers/net/ethernet/mellanox/mlx4/cq.c'.
>
> Kernel version: long-term kernel v6.18.9
>
> Potential concurrent triggering executions:
> T0:
> mlx4_cq_tasklet_cb
> --> if (refcount_dec_and_test(&mcq->refcount))
> --> complete(&mcq->free)
>
> T1:
> mlx4_cq_completion
> --> cq->comp(cq);
> --> mlx4_add_cq_to_tasklet(struct mlx4_cq *cq)
> --> spin_lock_irqsave(&tasklet_ctx->lock, flags);
> --> refcount_inc(&cq->refcount);
> --> spin_unlock_irqrestore(&tasklet_ctx->lock, flags);
>
> In T1, the refcounting increment on 'cq->refcount)', although within
> the protection range of the 'tasklet_ctx->locl', is not synchronized
> against T0 because 'refcount_inc()' does not check whether the
> refcount has reached zero in T0. This case is potentially problematic
> because T0 decrements he 'mcq->refcount' and can enable the
> 'mlx4_cq_free()' to proceed.
>
> Thank you for your time and consideration.
>
> Best regards,
> Ginger
>
Hi,
Thanks for your report.
IMO the described race is impossible.
CQs that work with mlx4_add_cq_to_tasklet as their comp() callback (i.e.
T1) are added to the relevant list only after refcount is incremented.
Hence, if a CQ exists in the list in T0, it necessarily means that
refcount is already elevated, and calling refcount_dec_and_test is safe.
Regards,
Tariq
^ permalink raw reply
* [PATCH net 1/2] net: libwx: fix VF illegal register access
From: Jiawen Wu @ 2026-04-29 8:37 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Kees Cook, Jiawen Wu,
stable
Register WX_CFG_PORT_ST is a PF restricted register. When a VF is
initialized, attempting to read this register triggers an illegal
register access, which lead to a system hang.
When the device is VF, the bus function ID can be obtained directly from
the PCI_FUNC(pdev->devfn).
Fixes: a04ea57aae37 ("net: libwx: fix device bus LAN ID")
Cc: stable@vger.kernel.org
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index d3772d01e00b..2451f6b20b11 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -2480,8 +2480,11 @@ int wx_sw_init(struct wx *wx)
wx->oem_svid = pdev->subsystem_vendor;
wx->oem_ssid = pdev->subsystem_device;
wx->bus.device = PCI_SLOT(pdev->devfn);
- wx->bus.func = FIELD_GET(WX_CFG_PORT_ST_LANID,
- rd32(wx, WX_CFG_PORT_ST));
+ if (pdev->is_virtfn)
+ wx->bus.func = PCI_FUNC(pdev->devfn);
+ else
+ wx->bus.func = FIELD_GET(WX_CFG_PORT_ST_LANID,
+ rd32(wx, WX_CFG_PORT_ST));
if (wx->oem_svid == PCI_VENDOR_ID_WANGXUN ||
pdev->is_virtfn) {
--
2.51.0
^ permalink raw reply related
* [PATCH net 2/2] net: libwx: use request_irq for VF misc interrupt
From: Jiawen Wu @ 2026-04-29 8:37 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Kees Cook, Jiawen Wu,
stable
In-Reply-To: <20260429083743.88961-1-jiawenwu@trustnetic.com>
Currently, request_threaded_irq() is used with a primary handler but a
NULL threaded handler, while also setting the IRQF_ONESHOT flag. This
specific combination triggers a WARNING since the commit aef30c8d569c
("genirq: Warn about using IRQF_ONESHOT without a threaded handler").
WARNING: kernel/irq/manage.c:1502 at __setup_irq+0x4fa/0x760
Fix the issue by switching to request_irq(), which is the appropriate
interface or a non-threaded interrupt handler, and removing the
unnecessary IRQF_ONESHOT flag.
Fixes: eb4898fde1de ("net: libwx: add wangxun vf common api")
Cc: stable@vger.kernel.org
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/wx_vf_common.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
index 29cdbed2e5ec..94ff8f5f0b4c 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
@@ -99,8 +99,8 @@ int wx_request_msix_irqs_vf(struct wx *wx)
}
}
- err = request_threaded_irq(wx->msix_entry->vector, wx_msix_misc_vf,
- NULL, IRQF_ONESHOT, netdev->name, wx);
+ err = request_irq(wx->msix_entry->vector, wx_msix_misc_vf,
+ 0, netdev->name, wx);
if (err) {
wx_err(wx, "request_irq for msix_other failed: %d\n", err);
goto free_queue_irqs;
--
2.51.0
^ permalink raw reply related
* Re: [PATCH] xfrm: protect __xfrm_state_delete against double-unhash of byseq/byspi
From: Steffen Klassert @ 2026-04-29 8:42 UTC (permalink / raw)
To: Michal Kosiorek
Cc: Herbert Xu, David S. Miller, Eric Dumazet, Jakub Kicinski, Abeni,
Simon Horman, Greg KH, sd, netdev, linux-kernel, stable
In-Reply-To: <CAFRy_HgkpfzxtJEJWRa=wfJ+m_++FbGsaezywRXQWt7uhkKoTQ@mail.gmail.com>
On Wed, Apr 29, 2026 at 10:29:10AM +0200, Michal Kosiorek wrote:
...
>
> Fixes: fe9f1d8779cb ("xfrm: add state hashtable keyed by seq")
> Fixes: 7b4dc3600e48 ("[XFRM]: Do not add a state whose SPI is zero to
> the SPI hash.")
> Reported-by: Michal Kosiorek <mkosiorek121@gmail.com>
> Tested-by: Michal Kosiorek <mkosiorek121@gmail.com>
> Cc: stable@vger.kernel.org
> Signed-off-by: Michal Kosiorek <mkosiorek121@gmail.com>
> ---
> net/xfrm/xfrm_state.c | 12 ++++++------
> 1 file changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
> index 1748d374abca..686014d39429 100644
> --- a/net/xfrm/xfrm_state.c
> +++ b/net/xfrm/xfrm_state.c
> @@ -818,17 +818,17 @@ int __xfrm_state_delete(struct xfrm_state *x)
>
> spin_lock(&net->xfrm.xfrm_state_lock);
> list_del(&x->km.all);
> - hlist_del_rcu(&x->bydst);
> - hlist_del_rcu(&x->bysrc);
> - if (x->km.seq)
> - hlist_del_rcu(&x->byseq);
> + hlist_del_init_rcu(&x->bydst);
> + hlist_del_init_rcu(&x->bysrc);
> + if (!hlist_unhashed(&x->byseq))
> + hlist_del_init_rcu(&x->byseq);
> if (!hlist_unhashed(&x->state_cache))
> hlist_del_rcu(&x->state_cache);
> if (!hlist_unhashed(&x->state_cache_input))
> hlist_del_rcu(&x->state_cache_input);
>
> - if (x->id.spi)
> - hlist_del_rcu(&x->byspi);
> + if (!hlist_unhashed(&x->byspi))
> + hlist_del_init_rcu(&x->byspi);
> net->xfrm.state_num--;
> xfrm_nat_keepalive_state_updated(x);
> spin_unlock(&net->xfrm.xfrm_state_lock);
This looks still odd, the indentation seems to be wrong.
It does not apply, maybe your mail client malformed
the patch.
^ permalink raw reply
* Re: [RFC PATCH net-next 1/2] net: napi: Fix interrupts permanently disabled during busy poll
From: Dragos Tatulea @ 2026-04-29 8:43 UTC (permalink / raw)
To: Jakub Kicinski
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Daniel Borkmann, Björn Töpel, Martin Karsten,
Gal Pressman, Tariq Toukan, Joe Damato, Frederik Deweerdt, netdev,
linux-kernel
In-Reply-To: <20260428173844.1354aabe@kernel.org>
On Tue, Apr 28, 2026 at 05:38:44PM -0700, Jakub Kicinski wrote:
> On Tue, 28 Apr 2026 17:51:30 +0000 Dragos Tatulea wrote:
> > + local_irq_save(flags);
> > + hrtimer_start(&napi->timer, ns_to_ktime(timeout),
> > + HRTIMER_MODE_REL_PINNED);
> > clear_bit(NAPI_STATE_SCHED, &napi->state);
> > + local_irq_restore(flags);
>
> I don't think disabling IRQ is necessary?
> Isn't it legal to clear the bit first then schedule the timer?
> The timer does not own the napi instance.
Isn't the following scenario possible (but extremely unlikely)?
1. busy_poll_stop(): napi timer is schedueled.
2. Hard irq pre-empts busy_poll_stop() and takes an unusually long time.
4. napi timer triggers (also hard irq), napi_watchdog() skips schedule
because NAPI_STATE_SCHED is set.
5. busy_poll_stop(): NAPI_STATE_SCHED gets cleared.
Thanks,
Dragos
^ permalink raw reply
* Re: [PATCH net-next] net: Consistently define pci_device_ids using named initializers
From: Petr Machata @ 2026-04-29 8:48 UTC (permalink / raw)
To: Uwe Kleine-König (The Capable Hub)
Cc: Michael Grzeschik, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Marc Kleine-Budde, Vincent Mailhol,
Krzysztof Halasa, Johannes Berg, Markus Schneider-Pargmann,
Steffen Klassert, David Dillow, Ion Badulescu, Mark Einon,
Rasesh Mody, GR-Linux-NIC-Dev, Sudarsana Kalluru, Manish Chopra,
Potnuri Bharat Teja, Denis Kirjanov, Jijie Shao, Jian Shen,
Cai Huoqing, Fan Gong, Tony Nguyen, Przemek Kitszel, Tariq Toukan,
Saeed Mahameed, Leon Romanovsky, Mark Bloch, Ido Schimmel,
Petr Machata, Yibo Dong, Simon Horman, Heiner Kallweit, nic_swsd,
Jiri Pirko, Francois Romieu, Daniele Venzano, Samuel Chessman,
Jiawen Wu, Mengyuan Lou, Kevin Curtis, Arend van Spriel,
Stanislav Yakovlev, Richard Cochran, Kees Cook, Thomas Gleixner,
Thomas Fourier, Ingo Molnar, Kory Maincent, Zilin Guan,
Marco Crivellari, Vadim Fedorenko, Jacob Keller, Philipp Stanner,
Bjorn Helgaas, Yeounsu Moon, Denis Benato, Peiyang Wang,
Yonglong Liu, Andy Shevchenko, Yicong Hui, Randy Dunlap,
MD Danish Anwar, Nathan Chancellor, Sai Krishna,
Ethan Nelson-Moore, Larysa Zaremba, Joe Damato, Double Lo,
Chi-hsien Lin, Colin Ian King, netdev, linux-kernel, linux-can,
linux-parisc, intel-wired-lan, linux-rdma, oss-drivers,
linux-wireless, brcm80211, brcm80211-dev-list.pdl
In-Reply-To: <20260428171845.2288395-2-u.kleine-koenig@baylibre.com>
Uwe Kleine-König (The Capable Hub) <u.kleine-koenig@baylibre.com> writes:
> .../net/ethernet/mellanox/mlxsw/spectrum.c | 16 +-
Reviewed-by: Petr Machata <petrm@nvidia.com> # for mlxsw
^ permalink raw reply
* Re: [PATCH 7/9] Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval
From: Bartosz Golaszewski @ 2026-04-29 8:53 UTC (permalink / raw)
To: Loic Poulain
Cc: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-mmc, devicetree,
linux-kernel, linux-arm-msm, linux-block, linux-wireless, ath10k,
linux-bluetooth, netdev, daniel
In-Reply-To: <20260428-block-as-nvmem-v1-7-6ad23e75190a@oss.qualcomm.com>
On Tue, Apr 28, 2026 at 4:23 PM Loic Poulain
<loic.poulain@oss.qualcomm.com> wrote:
>
> Some devices store the Bluetooth BD address in non-volatile
> memory, which can be accessed through the NVMEM framework.
> Similar to Ethernet or WiFi MAC addresses, add support for
> reading the BD address from a 'local-bd-address' NVMEM cell.
>
> As with the device-tree provided BD address, add a quirk to
> indicate whether a device or platform should attempt to read
> the address from NVMEM when no valid in-chip address is present.
> Also add a quirk to indicate if the address is stored in
> big-endian byte order.
>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
Is there any reason why we can't extend the existing
of_get_mac_address() with another property name and use it here? It
already has support for mac addresses from nvmem.
Bart
^ permalink raw reply
* [PATCH ipsec v2] xfrm: defensively unhash xfrm_state lists in __xfrm_state_delete
From: Michal Kosiorek @ 2026-04-29 8:54 UTC (permalink / raw)
To: Steffen Klassert
Cc: Herbert Xu, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Greg Kroah-Hartman, Sabrina Dubroca,
netdev, linux-kernel, stable
In-Reply-To: <afHEWqYEiA07An7W@secunet.com>
KASAN reproduces a slab-use-after-free in __xfrm_state_delete()'s
hlist_del_rcu calls under syzkaller load on linux-6.12.y stable
(reproduced on 6.12.47, also reachable via the same code path on
torvalds/master and on the ipsec tree). Nine unique signatures cluster
in the xfrm_state lifecycle, the load-bearing one being:
BUG: KASAN: slab-use-after-free in __hlist_del include/linux/list.h:990 [inline]
BUG: KASAN: slab-use-after-free in hlist_del_rcu include/linux/rculist.h:516 [inline]
BUG: KASAN: slab-use-after-free in __xfrm_state_delete net/xfrm/xfrm_state.c
Write of size 8 at addr ffff8881198bcb70 by task kworker/u8:9/435
Workqueue: netns cleanup_net
Call Trace:
__hlist_del / hlist_del_rcu
__xfrm_state_delete
xfrm_state_delete
xfrm_state_flush
xfrm_state_fini
ops_exit_list
cleanup_net
The other observed signatures hit the same slab object from
__xfrm_state_lookup, xfrm_alloc_spi, __xfrm_state_insert and an OOB
write variant of __xfrm_state_delete, all on the byseq/byspi
hash chains.
__xfrm_state_delete() guards its byseq and byspi unhashes with
value-based predicates:
if (x->km.seq)
hlist_del_rcu(&x->byseq);
if (x->id.spi)
hlist_del_rcu(&x->byspi);
while everywhere else in the file (e.g. state_cache, state_cache_input)
the safer hlist_unhashed() check is used. xfrm_alloc_spi() sets
x->id.spi = newspi inside xfrm_state_lock and then immediately inserts
into byspi, but a path that observes x->id.spi != 0 outside of
xfrm_state_lock can still skip-or-hit the byspi unhash inconsistently
with whether x is actually on the list. The same holds for x->km.seq
versus byseq, and the bydst/bysrc unhashes have no predicate at all,
so a second __xfrm_state_delete() on the same object writes through
LIST_POISON pprev.
The defensive change here:
- Use hlist_del_init_rcu() instead of hlist_del_rcu() on bydst,
bysrc, byseq and byspi so a second deletion is a no-op rather
than a write through LIST_POISON pprev. The byseq/byspi nodes
are already initialised in xfrm_state_alloc().
- Test hlist_unhashed() rather than the value predicate for
byseq/byspi, so the unhash decision tracks list state rather than
mutable scalar fields.
Empirical verification: applied this patch on top of v6.12.47, rebuilt,
and re-ran the same syzkaller harness for 1h16m on a previously-crashy
configuration that produced ~100 hits each of slab-use-after-free
Read in xfrm_alloc_spi / Read in __xfrm_state_lookup / Write in
__xfrm_state_delete. After the patch, 7.1M execs across 32 VMs at
~1550 exec/sec produced zero xfrm_state UAF/OOB hits. /proc/slabinfo
confirms the xfrm_state slab is actively allocated and freed during
the run (~143 KiB resident), so the fuzzer is still exercising those
code paths -- they just no longer crash.
Reproduction:
- Linux 6.12.47 x86_64 + KASAN_GENERIC + KASAN_INLINE + KCOV
- syzkaller @ 746545b8b1e4c3a128db8652b340d3df90ce61db
- 32 QEMU/KVM VMs x 2 vCPU on AWS c5.metal bare metal
- 9 unique signatures collected in ~9h, all within xfrm_state
lifecycle
Fixes: fe9f1d8779cb ("xfrm: add state hashtable keyed by seq")
Fixes: 7b4dc3600e48 ("[XFRM]: Do not add a state whose SPI is zero to the SPI hash.")
Reported-by: Michal Kosiorek <mkosiorek121@gmail.com>
Tested-by: Michal Kosiorek <mkosiorek121@gmail.com>
Cc: stable@vger.kernel.org
Signed-off-by: Michal Kosiorek <mkosiorek121@gmail.com>
---
Resending v2 via git send-email -- the previous post had been sent
through Gmail's web client which stripped all tabs from the diff
hunk and made the patch un-applyable. Apologies for the noise. No
other changes versus the prior v2 send.
net/xfrm/xfrm_state.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 1748d374abca..686014d39429 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -818,17 +818,17 @@ int __xfrm_state_delete(struct xfrm_state *x)
spin_lock(&net->xfrm.xfrm_state_lock);
list_del(&x->km.all);
- hlist_del_rcu(&x->bydst);
- hlist_del_rcu(&x->bysrc);
- if (x->km.seq)
- hlist_del_rcu(&x->byseq);
+ hlist_del_init_rcu(&x->bydst);
+ hlist_del_init_rcu(&x->bysrc);
+ if (!hlist_unhashed(&x->byseq))
+ hlist_del_init_rcu(&x->byseq);
if (!hlist_unhashed(&x->state_cache))
hlist_del_rcu(&x->state_cache);
if (!hlist_unhashed(&x->state_cache_input))
hlist_del_rcu(&x->state_cache_input);
- if (x->id.spi)
- hlist_del_rcu(&x->byspi);
+ if (!hlist_unhashed(&x->byspi))
+ hlist_del_init_rcu(&x->byspi);
net->xfrm.state_num--;
xfrm_nat_keepalive_state_updated(x);
spin_unlock(&net->xfrm.xfrm_state_lock);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH net-next 1/2] dpll: add pin operational state
From: Jiri Pirko @ 2026-04-29 8:55 UTC (permalink / raw)
To: Ivan Vecera
Cc: netdev, Arkadiusz Kubalewski, David S. Miller, Donald Hunter,
Eric Dumazet, Jakub Kicinski, Jonathan Corbet, Michal Schmidt,
Paolo Abeni, Pasi Vaananen, Petr Oros, Prathosh Satish,
Shuah Khan, Simon Horman, Vadim Fedorenko, linux-doc,
linux-kernel
In-Reply-To: <20260428154907.2820654-2-ivecera@redhat.com>
Tue, Apr 28, 2026 at 05:49:06PM +0200, ivecera@redhat.com wrote:
>Add pin-operstate enum and operstate_on_dpll_get callback to report
>the actual hardware status of a pin with respect to its parent DPLL
>device. Unlike pin-state (which reflects administrative intent set
>by the user), operstate reflects what the hardware is actually doing.
>
>Defined operational states:
> - active: pin is qualified and actively used by the DPLL
> - standby: pin is qualified but not actively used by the DPLL
> - no-signal: pin does not have a valid signal
> - qual-failed: pin signal failed qualification
>
>The operstate is reported inside the pin-parent-device nested
>attribute alongside the existing state and phase-offset attributes.
>
>Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Looks great.
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Thanks!
^ permalink raw reply
* Re: [PATCH v3 net-next] net/intel: Replace manual array size calculation with ARRAY_SIZE
From: Przemek Kitszel @ 2026-04-29 9:01 UTC (permalink / raw)
To: Dan Carpenter
Cc: Jakub Raczynski, netdev, kuba, intel-wired-lan, linux-kernel,
kernel-janitors
In-Reply-To: <afDL02Z4QV6G0UxF@stanley.mountain>
On 4/28/26 17:01, Dan Carpenter wrote:
> On Tue, Apr 28, 2026 at 04:06:48PM +0200, Przemek Kitszel wrote:
>> On 4/28/26 12:36, Jakub Raczynski wrote:
>>> There are still places in the code where manual calculation of array size
>>> exist, but it is good to enforce usage of single macro through the whole
>>> code as it makes code bit more readable.
>>> While at it, beautify condition surrounding it by reversing check and remove
>>> unnecessary casting.
>>>
>>> Signed-off-by: Jakub Raczynski <j.raczynski@samsung.com>
>>> Reviewed-by: Dan Carpenter <error27@gmail.com>
>>
>> thank you,
>> Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
>>
>> (next time use "iwl-next" in the title, no need to repost just for that)
>>
>
> Which sub directories go through iwl-next? I was trying to update
> my script
Thank you for all the patches so far you have provided and willingness
to continue.
F: Documentation/networking/device_drivers/ethernet/intel/
F: drivers/net/ethernet/intel/
F: drivers/net/ethernet/intel/*/
F: include/linux/avf/virtchnl.h
F: include/linux/net/intel/*/
Perhaps instead of you managing your script, and everybody else doing
the same, there could be some extension added to MAINTAINERS file to
encode the prefix?
In our case, the prefix itself is a message for net maintainers:
iwl or iwl-next means the patch will go first via our tree, and be sent
later as a PR for net/net-next.
Without the prefix it requires guessing what was the submitter intent.
Most patches that go through IWL receive additional round of testing on
real HW too, thanks to our VAL.
Patches that go straight to net are just merged faster.
As intel ethernet maintainer, I want our code tested more, instead of
merged faster (in most cases).
>
>>>
>>> drivers/net/ethernet/intel/i40e/i40e_adminq.h | 2 +-
>>> drivers/net/ethernet/intel/iavf/iavf_adminq.h | 2 +-
>>> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> but look at these file names. There is no "iwl" anywhere in
> the names! :(
>
> regards,
> dan carpenter
>
^ permalink raw reply
* Re: [PATCH net-next 1/2] bridge: Do not suppress ARP probes and DAD NS unconditionally
From: Nikolay Aleksandrov @ 2026-04-29 9:04 UTC (permalink / raw)
To: Danielle Ratson, netdev
Cc: idosch, davem, edumazet, kuba, pabeni, horms, shuah, bridge,
linux-kernel, linux-kselftest
In-Reply-To: <20260429062405.1386417-2-danieller@nvidia.com>
On 29/04/2026 09:24, Danielle Ratson wrote:
> When neighbor suppression is enabled on a VXLAN port, the bridge is
> expected to reply to ARP/NS messages on behalf of remote hosts when both
> FDB and neighbor entries exist. This allows the bridge to suppress
> flooding of these messages to the VXLAN overlay.
>
> According to RFC 9161 ("Operational Aspects of Proxy ARP/ND in Ethernet
> Virtual Private Networks"):
> "A PE SHOULD reply to broadcast/multicast address resolution messages,
> i.e., ARP Requests, ARP probes, NS messages, as well as DAD NS messages.
> An ARP probe is an ARP Request constructed with an all-zero sender IP
> address that may be used by hosts for IPv4 Address Conflict Detection as
> specified in [RFC5227]".
>
> However, the current implementation unconditionally suppresses ARP probes
> and DAD Neighbor Solicitations, which breaks Duplicate Address Detection
> (DAD) over EVPN.
>
> For DAD to work correctly over the VXLAN fabric:
> - When the bridge does not know the answer:
> flood the probe/DAD packet to allow remote VTEPs to respond.
> - When the bridge knows the answer:
> reply to indicate the address is in use.
>
> Fix by adjusting the early suppression checks to exclude ARP probes and
> DAD NS from unconditional suppression.
>
> When replying to a DAD NS, br_nd_send() is adjusted to set the NA
> destination to the all-nodes multicast address (ff02::1) and clear the
> Solicited flag, in accordance with RFC 4861 section 7.2.4.
>
> Reviewed-by: Ido Schimmel <idosch@nvidia.com>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
> net/bridge/br_arp_nd_proxy.c | 16 +++++++++++-----
> 1 file changed, 11 insertions(+), 5 deletions(-)
>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
^ permalink raw reply
* Re: [PATCH net-next 2/2] selftests: net: Add tests for ARP probe and DAD NS handling
From: Nikolay Aleksandrov @ 2026-04-29 9:04 UTC (permalink / raw)
To: Danielle Ratson, netdev
Cc: idosch, davem, edumazet, kuba, pabeni, horms, shuah, bridge,
linux-kernel, linux-kselftest
In-Reply-To: <20260429062405.1386417-3-danieller@nvidia.com>
On 29/04/2026 09:24, Danielle Ratson wrote:
> Add test cases to verify that ARP probes and DAD Neighbor Solicitations
> are handled correctly by the bridge neighbor suppression feature.
>
> When neighbor suppression is enabled on a bridge VXLAN port, the bridge
> should reply to ARP/NS messages on behalf of remote hosts when both FDB
> and neighbor entries exist, and the answer is known. However, when
> either the FDB or the neighbor exists, ARP probes / DAD NS should be
> treated like regular ARP requests / NS and flood to VXLAN.
>
> Add two new test functions:
>
> neigh_suppress_arp_probe(): Tests ARP probe handling by triggering
> duplicate address detection using arping -D. Verifies that probes are
> flooded when the bridge doesn't know the answer, and suppressed when FDB
> and neighbor entries exist.
>
> neigh_suppress_dad_ns(): Tests DAD NS handling by constructing DAD NS
> packets using mausezahn and verifies correct flooding/suppression
> behavior.
>
> Before the previous patch:
>
> $ ./test_bridge_neigh_suppress.sh -t "neigh_suppress_arp_probe neigh_suppress_dad_ns"
>
> Per-port ARP probe suppression
> ------------------------------
> TEST: ARP probe suppression [ OK ]
> TEST: "neigh_suppress" is on [ OK ]
> TEST: ARP probe suppression [FAIL]
> TEST: FDB and neighbor entry installation [ OK ]
> TEST: arping [FAIL]
> TEST: ARP probe suppression [FAIL]
> TEST: neighbor removal [ OK ]
> TEST: ARP probe suppression [FAIL]
> TEST: "neigh_suppress" is off [ OK ]
> TEST: ARP probe suppression [FAIL]
>
> Per-port DAD NS suppression
> ---------------------------
> TEST: DAD NS suppression [ OK ]
> TEST: "neigh_suppress" is on [ OK ]
> TEST: DAD NS suppression [FAIL]
> TEST: FDB and neighbor entry installation [ OK ]
> TEST: DAD NS suppression [FAIL]
> TEST: neighbor removal [ OK ]
> TEST: DAD NS suppression [FAIL]
> TEST: DAD NS proxy NA reply [FAIL]
> TEST: "neigh_suppress" is off [ OK ]
> TEST: DAD NS suppression [FAIL]
>
> Tests passed: 10
> Tests failed: 10
>
> After the previous patch:
>
> $ ./test_bridge_neigh_suppress.sh -t "neigh_suppress_arp_probe neigh_suppress_dad_ns"
>
> Per-port ARP probe suppression
> ------------------------------
> TEST: ARP probe suppression [ OK ]
> TEST: "neigh_suppress" is on [ OK ]
> TEST: ARP probe suppression [ OK ]
> TEST: FDB and neighbor entry installation [ OK ]
> TEST: arping [ OK ]
> TEST: ARP probe suppression [ OK ]
> TEST: neighbor removal [ OK ]
> TEST: ARP probe suppression [ OK ]
> TEST: "neigh_suppress" is off [ OK ]
> TEST: ARP probe suppression [ OK ]
>
> Per-port DAD NS suppression
> ---------------------------
> TEST: DAD NS suppression [ OK ]
> TEST: "neigh_suppress" is on [ OK ]
> TEST: DAD NS suppression [ OK ]
> TEST: FDB and neighbor entry installation [ OK ]
> TEST: DAD NS suppression [ OK ]
> TEST: neighbor removal [ OK ]
> TEST: DAD NS suppression [ OK ]
> TEST: DAD NS proxy NA reply [ OK ]
> TEST: "neigh_suppress" is off [ OK ]
> TEST: DAD NS suppression [ OK ]
>
> Tests passed: 20
> Tests failed: 0
>
> Signed-off-by: Danielle Ratson <danieller@nvidia.com>
> ---
> .../net/test_bridge_neigh_suppress.sh | 126 ++++++++++++++++++
> 1 file changed, 126 insertions(+)
>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
^ permalink raw reply
* Re: [PATCH] mctp i2c: check packet length before marking flow active
From: William A. Kennington III @ 2026-04-29 9:04 UTC (permalink / raw)
To: Jeremy Kerr, Matt Johnston, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Wolfram Sang
Cc: netdev, linux-kernel
In-Reply-To: <1651e98dcc86f38a0b39679b1a6f9ef604e0812a.camel@codeconstruct.com.au>
On 4/23/26 21:16, Jeremy Kerr wrote:
> Hi William,
>
>>> Out of curiosity though, how did you hit the hdr_byte_count mismatch in
>>> the first place?
>> Our current theory is that we have known buggy firmware on our NVME MCTP
>> devices and we are seeing some kind of corruption on the bus that we are
>> going to fix in on the firmware side.
> OK, sounds good for the overall fix, but I don't think that would be
> causing the path that you're addressing here. The fix is definitely
> valid, but can't be hit through any RX data corruption (we're in the
> TX path).
Yeah I think you might be right, the hard part is reproducing this is so
infrequent for us that it takes a long time to iterate on testing these
changes.
>
> The header byte count is populated during header construction, so a
> mismatch here would indicate modification of the skb between that point
> at the actual xmit. Do you see the "Bad TX len" warning in these cases?
I double checked and so far I can’t find evidence of it. Probably we
still want to keep this change, but it’s not the root of our problems.
>> We started also seeing kernel
>> crashes along with the bad firmware symptoms, walked through ~110 kdumps
>> and found i2c locks that were held by 2 owners (eeprom reading and the
>> MCTP TX queue).
> Just to clarify my understanding of the state: "being held by two
> owners" would indicate a violation of the lock itself. Or is it that
> there are two threads blocked waiting to acquire the mutex?
I think it’s actually this, 2 threads are waiting on acquiring the lock.
There was a theory that it was a lock underflow that allowed 2 threads
to acquire the lock that lead to this patch.
> For NVMe-MI, you're likely using manual tag allocation, where the tag
> allocation (and hence flow state) is entirely controlled by userspace.
> It may be that the NVMe protocol-level errors are causing that tags to
> be held for long durations, perhaps?
Yeah, this is very plausible given the device(s) stop responding
correctly. I imagine we are getting stuck with manual allocations and
not releasing locks. Can we reset the state machine back to NEW instead
of holding the lock?
>
> Cheers,
>
>
> Jeremy
^ permalink raw reply
* [PATCH net v2] net: mana: Optimize irq affinity for low vcpu configs
From: Shradha Gupta @ 2026-04-29 9:06 UTC (permalink / raw)
To: Dexuan Cui, Wei Liu, Haiyang Zhang, K. Y. Srinivasan, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Konstantin Taranov, Simon Horman, Erni Sri Satya Vennela,
Dipayaan Roy, Shiraz Saleem, Michael Kelley, Long Li, Yury Norov
Cc: Shradha Gupta, linux-hyperv, linux-kernel, netdev, Paul Rosswurm,
Shradha Gupta, Saurabh Singh Sengar, stable
In mana driver, the number of IRQs allocated is capped by the
min(num_cpu + 1, queue count). In cases, where the IRQ count is greater
than the vcpu count, we want to utilize all the vCPUs, irrespective of
their NUMA/core bindings.
This is important, especially in the envs where number of vCPUs are so
few that the softIRQ handling overhead on two IRQs on the same vCPU is
much more than their overheads if they were spread across sibling vCPUs.
This behaviour is more evident with dynamic IRQ allocation. Since MANA
IRQs are assigned at a later stage compared to static allocation, other
device IRQs may already be affinitized to the vCPUs. As a result, IRQ
weights become imbalanced, causing multiple MANA IRQs to land on the
same vCPU, while some vCPUs have none.
In such cases when many parallel TCP connections are tested, the
throughput drops significantly.
Test envs:
=======================================================
Case 1: without this patch
=======================================================
4 vcpu(2 cores), 5 MANA IRQs (1 HWC + 4 Queue)
TYPE effective vCPU aff
=======================================================
IRQ0: HWC 0
IRQ1: mana_q1 0
IRQ2: mana_q2 2
IRQ3: mana_q3 0
IRQ4: mana_q4 3
%soft on each vCPU(mpstat -P ALL 1) on receiver
vCPU 0 1 2 3
=======================================================
pass 1: 38.85 0.03 24.89 24.65
pass 2: 39.15 0.03 24.57 25.28
pass 3: 40.36 0.03 23.20 23.17
=======================================================
Case 2: with this patch
=======================================================
4 vcpu(2 cores), 5 MANA IRQs (1 HWC + 4 Queue)
TYPE effective vCPU aff
=======================================================
IRQ0: HWC 0
IRQ1: mana_q1 0
IRQ2: mana_q2 1
IRQ3: mana_q3 2
IRQ4: mana_q4 3
%soft on each vCPU(mpstat -P ALL 1) on receiver
vCPU 0 1 2 3
=======================================================
pass 1: 15.42 15.85 14.99 14.51
pass 2: 15.53 15.94 15.81 15.93
pass 3: 16.41 16.35 16.40 16.36
=======================================================
Throughput Impact(in Gbps, same env)
=======================================================
TCP conn with patch w/o patch
20480 15.65 7.73
10240 15.63 8.93
8192 15.64 9.69
6144 15.64 13.16
4096 15.69 15.75
2048 15.69 15.83
1024 15.71 15.28
Fixes: 755391121038 ("net: mana: Allocate MSI-X vectors dynamically")
Cc: stable@vger.kernel.org
Co-developed-by: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
Signed-off-by: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
Signed-off-by: Shradha Gupta <shradhagupta@linux.microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
---
Changes in v2
* Removed the unused skip_first_cpu variable
* fixed exit condition in irq_setup_linear() with len == 0
* changed return type of irq_setup_linear() as it will always be 0
* removed the unnecessary rcu_read_lock() in irq_setup_linear()
* added appropriate comments to indicate expected behaviour when
IRQs are more than or equal to num_online_cpus()
---
.../net/ethernet/microsoft/mana/gdma_main.c | 47 ++++++++++++++++---
1 file changed, 40 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 098fbda0d128..d740d1dc43da 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -167,6 +167,8 @@ static int mana_gd_query_max_resources(struct pci_dev *pdev)
} else {
/* If dynamic allocation is enabled we have already allocated
* hwc msi
+ * Also, we make sure in this case the following is always true
+ * (num_msix_usable - 1 HWC) <= num_online_cpus()
*/
gc->num_msix_usable = min(resp.max_msix, num_online_cpus() + 1);
}
@@ -1672,11 +1674,24 @@ static int irq_setup(unsigned int *irqs, unsigned int len, int node,
return 0;
}
+/* should be called with cpus_read_lock() held */
+static void irq_setup_linear(unsigned int *irqs, unsigned int len)
+{
+ int cpu;
+
+ for_each_online_cpu(cpu) {
+ if (len == 0)
+ break;
+
+ irq_set_affinity_and_hint(*irqs++, cpumask_of(cpu));
+ len--;
+ }
+}
+
static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
struct gdma_irq_context *gic;
- bool skip_first_cpu = false;
int *irqs, irq, err, i;
irqs = kmalloc_objs(int, nvec);
@@ -1722,13 +1737,31 @@ static int mana_gd_setup_dyn_irqs(struct pci_dev *pdev, int nvec)
* first CPU sibling group since they are already affinitized to HWC IRQ
*/
cpus_read_lock();
- if (gc->num_msix_usable <= num_online_cpus())
- skip_first_cpu = true;
+ if (gc->num_msix_usable <= num_online_cpus()) {
+ err = irq_setup(irqs, nvec, gc->numa_node, true);
+ if (err) {
+ cpus_read_unlock();
+ goto free_irq;
+ }
+ } else {
+ /*
+ * When num_msix_usable are more than num_online_cpus, we try to
+ * make sure we are using all vcpus. In such a case NUMA or
+ * CPU core affinity does not matter.
+ * Note: in this case the total mana IRQ should always be
+ * num_online_cpus + 1. The first HWC IRQ is already handled
+ * in HWC setup calls
+ * However, if CPUs went offline since num_msix_usable was
+ * computed, nvec count will be more than num_online_cpus().
+ * In such cases remaining extra IRQs will retain their default
+ * affinity.
+ */
+ if (nvec > num_online_cpus())
+ dev_dbg(&pdev->dev,
+ "IRQ count %d exceeds online CPU count %d. Some IRQs will share CPU\n",
+ nvec, num_online_cpus());
- err = irq_setup(irqs, nvec, gc->numa_node, skip_first_cpu);
- if (err) {
- cpus_read_unlock();
- goto free_irq;
+ irq_setup_linear(irqs, nvec);
}
cpus_read_unlock();
base-commit: e728258debd553c95d2e70f9cd97c9fde27c7130
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net v4] ipv6: addrconf: skip ERRDAD transition when address already DEAD
From: Ido Schimmel @ 2026-04-29 9:08 UTC (permalink / raw)
To: Linmao Li
Cc: davem, dsahern, edumazet, kuba, pabeni, horms, stephen, netdev,
linux-kernel
In-Reply-To: <20260429012651.1580303-1-lilinmao@kylinos.cn>
On Wed, Apr 29, 2026 at 09:26:51AM +0800, Linmao Li wrote:
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 5476b6536eb7..b58bd9f11606 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2166,16 +2166,18 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
> struct net *net = dev_net(idev->dev);
> int max_addresses;
>
> - if (addrconf_dad_end(ifp)) {
> + spin_lock_bh(&ifp->lock);
> +
> + if (ifp->state != INET6_IFADDR_STATE_DAD) {
> + spin_unlock_bh(&ifp->lock);
> in6_ifa_put(ifp);
> return;
> }
> + ifp->state = INET6_IFADDR_STATE_POSTDAD;
>
> net_info_ratelimited("%s: IPv6 duplicate address %pI6c used by %pM detected!\n",
> ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source);
>
> - spin_lock_bh(&ifp->lock);
> -
> if (ifp->flags & IFA_F_STABLE_PRIVACY) {
> struct in6_addr new_addr;
> struct inet6_ifaddr *ifp2;
> @@ -2223,6 +2225,11 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
> in6_ifa_put(ifp2);
> lock_errdad:
> spin_lock_bh(&ifp->lock);
> + if (ifp->state == INET6_IFADDR_STATE_DEAD) {
The code below expects the state to be POSTDAD, so wouldn't it be more
robust to check for anything but POSTDAD rather than specifically
checking for DEAD? Otherwise we risk overwriting a state that was set
while we weren't holding the lock.
Also:
1. Please post new versions in a new thread:
https://docs.kernel.org/process/maintainer-netdev.html#resending-after-review
2. Please include a change log:
https://docs.kernel.org/process/maintainer-netdev.html#changes-requested
> + spin_unlock_bh(&ifp->lock);
> + in6_ifa_put(ifp);
> + return;
> + }
> }
>
> errdad:
> --
> 2.25.1
>
^ permalink raw reply
* Re: [PATCH net-next] net: Consistently define pci_device_ids using named initializers
From: Marc Kleine-Budde @ 2026-04-29 9:10 UTC (permalink / raw)
To: Uwe Kleine-König (The Capable Hub)
Cc: Michael Grzeschik, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Vincent Mailhol, Krzysztof Halasa,
Johannes Berg, Markus Schneider-Pargmann, Steffen Klassert,
David Dillow, Ion Badulescu, Mark Einon, Rasesh Mody,
GR-Linux-NIC-Dev, Sudarsana Kalluru, Manish Chopra,
Potnuri Bharat Teja, Denis Kirjanov, Jijie Shao, Jian Shen,
Cai Huoqing, Fan Gong, Tony Nguyen, Przemek Kitszel, Tariq Toukan,
Saeed Mahameed, Leon Romanovsky, Mark Bloch, Ido Schimmel,
Petr Machata, Yibo Dong, Simon Horman, Heiner Kallweit, nic_swsd,
Jiri Pirko, Francois Romieu, Daniele Venzano, Samuel Chessman,
Jiawen Wu, Mengyuan Lou, Kevin Curtis, Arend van Spriel,
Stanislav Yakovlev, Richard Cochran, Kees Cook, Thomas Gleixner,
Thomas Fourier, Ingo Molnar, Kory Maincent, Zilin Guan,
Marco Crivellari, Vadim Fedorenko, Jacob Keller, Philipp Stanner,
Bjorn Helgaas, Yeounsu Moon, Denis Benato, Peiyang Wang,
Yonglong Liu, Andy Shevchenko, Yicong Hui, Randy Dunlap,
MD Danish Anwar, Nathan Chancellor, Sai Krishna,
Ethan Nelson-Moore, Larysa Zaremba, Joe Damato, Double Lo,
Chi-hsien Lin, Colin Ian King, netdev, linux-kernel, linux-can,
linux-parisc, intel-wired-lan, linux-rdma, oss-drivers,
linux-wireless, brcm80211, brcm80211-dev-list.pdl
In-Reply-To: <20260428171845.2288395-2-u.kleine-koenig@baylibre.com>
[-- Attachment #1: Type: text/plain, Size: 8338 bytes --]
On 28.04.2026 19:18:44, Uwe Kleine-König (The Capable Hub) wrote:
> drivers/net/can/m_can/m_can_pci.c | 6 +-
> drivers/net/can/sja1000/plx_pci.c | 167 +++----
[...]
> diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c
> index 08183833c9bc..a03553b80a5d 100644
> --- a/drivers/net/can/sja1000/plx_pci.c
> +++ b/drivers/net/can/sja1000/plx_pci.c
> @@ -272,124 +272,89 @@ static struct plx_pci_card_info plx_pci_card_info_asem_dual_can = {
> static const struct pci_device_id plx_pci_tbl[] = {
> {
> /* Adlink PCI-7841/cPCI-7841 */
> - ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID,
> - PCI_ANY_ID, PCI_ANY_ID,
> - PCI_CLASS_NETWORK_OTHER << 8, ~0,
> - (kernel_ulong_t)&plx_pci_card_info_adlink
> - },
> - {
> + PCI_DEVICE(ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID),
> + .class = PCI_CLASS_NETWORK_OTHER << 8,
> + .class_mask = ~0,
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_adlink,
> + }, {
> /* Adlink PCI-7841/cPCI-7841 SE */
> - ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID,
> - PCI_ANY_ID, PCI_ANY_ID,
> - PCI_CLASS_COMMUNICATION_OTHER << 8, ~0,
> - (kernel_ulong_t)&plx_pci_card_info_adlink_se
> - },
> - {
> + PCI_DEVICE(ADLINK_PCI_VENDOR_ID, ADLINK_PCI_DEVICE_ID),
> + .class = PCI_CLASS_COMMUNICATION_OTHER << 8,
> + .class_mask = ~0,
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_adlink_se,
> + }, {
> /* esd CAN-PCI/200 */
> - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
> - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI200,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_esd200
> - },
> - {
> + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9050,
> + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI200),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd200,
> + }, {
> /* esd CAN-CPCI/200 */
> - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
> - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_CPCI200,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_esd200
> - },
> - {
> + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030,
> + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_CPCI200),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd200,
> + }, {
> /* esd CAN-PCI104/200 */
> - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
> - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI104200,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_esd200
> - },
> - {
> + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030,
> + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI104200),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd200,
> + }, {
> /* esd CAN-PCI/266 */
> - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056,
> - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI266,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_esd266
> - },
> - {
> + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9056,
> + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCI266),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd266,
> + }, {
> /* esd CAN-PMC/266 */
> - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056,
> - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PMC266,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_esd266
> - },
> - {
> + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9056,
> + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PMC266),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd266,
> + }, {
> /* esd CAN-PCIE/2000 */
> - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9056,
> - PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCIE2000,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_esd2000
> - },
> - {
> + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9056,
> + PCI_VENDOR_ID_ESDGMBH, ESD_PCI_SUB_SYS_ID_PCIE2000),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_esd2000,
> + }, {
> /* IXXAT PC-I 04/PCI card */
> - IXXAT_PCI_VENDOR_ID, IXXAT_PCI_DEVICE_ID,
> - PCI_ANY_ID, IXXAT_PCI_SUB_SYS_ID,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_ixxat
> - },
> - {
> + PCI_DEVICE_SUB(IXXAT_PCI_VENDOR_ID, IXXAT_PCI_DEVICE_ID,
> + PCI_ANY_ID, IXXAT_PCI_SUB_SYS_ID),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_ixxat,
> + }, {
> /* Marathon CAN-bus-PCI card */
> - PCI_VENDOR_ID_PLX, MARATHON_PCI_DEVICE_ID,
> - PCI_ANY_ID, PCI_ANY_ID,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_marathon_pci
> - },
> - {
> + PCI_VDEVICE(PLX, MARATHON_PCI_DEVICE_ID),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_marathon_pci,
> + }, {
> /* Marathon CAN-bus-PCIe card */
> - PCI_VENDOR_ID_PLX, MARATHON_PCIE_DEVICE_ID,
> - PCI_ANY_ID, PCI_ANY_ID,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_marathon_pcie
> - },
> - {
> + PCI_VDEVICE(PLX, MARATHON_PCIE_DEVICE_ID),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_marathon_pcie,
> + }, {
> /* TEWS TECHNOLOGIES TPMC810 card */
> - TEWS_PCI_VENDOR_ID, TEWS_PCI_DEVICE_ID_TMPC810,
> - PCI_ANY_ID, PCI_ANY_ID,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_tews
> - },
> - {
> + PCI_DEVICE(TEWS_PCI_VENDOR_ID, TEWS_PCI_DEVICE_ID_TMPC810),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_tews,
> + }, {
> /* Connect Tech Inc. CANpro/104-Plus Opto (CRG001) card */
> - PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
> - PCI_SUBVENDOR_ID_CONNECT_TECH, CTI_PCI_DEVICE_ID_CRG001,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_cti
> - },
> - {
> + PCI_VDEVICE_SUB(PLX, PCI_DEVICE_ID_PLX_9030,
> + PCI_SUBVENDOR_ID_CONNECT_TECH, CTI_PCI_DEVICE_ID_CRG001),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_cti,
> + }, {
> /* Elcus CAN-200-PCI */
> - CAN200PCI_VENDOR_ID, CAN200PCI_DEVICE_ID,
> - CAN200PCI_SUB_VENDOR_ID, CAN200PCI_SUB_DEVICE_ID,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_elcus
> - },
> - {
> + PCI_DEVICE_SUB(CAN200PCI_VENDOR_ID, CAN200PCI_DEVICE_ID,
> + CAN200PCI_SUB_VENDOR_ID, CAN200PCI_SUB_DEVICE_ID),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_elcus,
> + }, {
> /* moxa */
> - MOXA_PCI_VENDOR_ID, MOXA_PCI_DEVICE_ID,
> - PCI_ANY_ID, PCI_ANY_ID,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_moxa
> - },
> - {
> + PCI_DEVICE(MOXA_PCI_VENDOR_ID, MOXA_PCI_DEVICE_ID),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_moxa,
> + }, {
> /* ASEM Dual CAN raw */
> - ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,
> - ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_asem_dual_can
> - },
> - {
> + PCI_DEVICE_SUB(ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,
> + ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_asem_dual_can,
> + }, {
> /* ASEM Dual CAN raw -new model */
> - ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,
> - ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID_BIS,
> - 0, 0,
> - (kernel_ulong_t)&plx_pci_card_info_asem_dual_can
> + PCI_DEVICE_SUB(ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,
> + ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID_BIS),
> + .driver_data = (kernel_ulong_t)&plx_pci_card_info_asem_dual_can,
> },
> - { 0,}
> + { }
Nitpick: can you convert the terminating entry to follow the same style
as the rest of the driver:
diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c
index a03553b80a5d..d69ff0ccfd94 100644
--- a/drivers/net/can/sja1000/plx_pci.c
+++ b/drivers/net/can/sja1000/plx_pci.c
@@ -353,8 +353,8 @@ static const struct pci_device_id plx_pci_tbl[] = {
PCI_DEVICE_SUB(ASEM_RAW_CAN_VENDOR_ID, ASEM_RAW_CAN_DEVICE_ID,
ASEM_RAW_CAN_SUB_VENDOR_ID, ASEM_RAW_CAN_SUB_DEVICE_ID_BIS),
.driver_data = (kernel_ulong_t)&plx_pci_card_info_asem_dual_can,
- },
- { }
+ }, {
+ }
};
MODULE_DEVICE_TABLE(pci, plx_pci_tbl);
> };
> MODULE_DEVICE_TABLE(pci, plx_pci_tbl);
>
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply related
* Re: [PATCH net] MAINTAINERS: update the IPv4/IPv6 entry and add Ido Schimmel
From: Nikolay Aleksandrov @ 2026-04-29 9:14 UTC (permalink / raw)
To: Jakub Kicinski, davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, David Ahern,
Ido Schimmel
In-Reply-To: <20260428203924.1229169-1-kuba@kernel.org>
On 28/04/2026 23:39, Jakub Kicinski wrote:
> The IPv4/IPv6 and routing code is not very well separated from
> the TCP/UDP code. Scope it down properly by providing a more
> accurate file list, instead of net/ipv4/ and net/ipv6/
>
> Now that the entry is more accurately representing layer 3
> and routing merge in the nexthop entry into it.
>
> Add Ido Schimmel as a co-maintainer, Ido's git history speaks
> for itself.
>
> Reviewed-by: David Ahern <dsahern@kernel.org>
> Reviewed-by: Ido Schimmel <idosch@nvidia.com>
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> ---
> MAINTAINERS | 63 +++++++++++++++++++++++++++++++++++++++--------------
> 1 file changed, 47 insertions(+), 16 deletions(-)
>
Wohoo, great!
Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
^ permalink raw reply
* [PATCH net v1 0/3] af_packet/tcp: fix late hardware timestamp handling
From: Kohei Enju @ 2026-04-29 9:16 UTC (permalink / raw)
To: netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Kuniyuki Iwashima, Willem de Bruijn, David Ahern,
Neal Cardwell, Gerhard Engleder, Jonathan Lemon, Richard Cochran,
Kohei Enju
Since commit 97dc7cd92ac6 ("ptp: Support late timestamp determination"),
skb_shared_hwtstamps may carry netdev_data instead of a resolved
hwtstamp. AF_PACKET and TCP could still read that storage as a ktime_t
and report bogus hardware timestamps to userspace.
This series factors the late timestamp resolution logic into a common
helper, then switches AF_PACKET and TCP to use it.
Notes on SOF_TIMESTAMPING_BIND_PHC:
The generic socket receive timestamping path honors it, but the
AF_PACKET and TCP receive paths touched here haven't implemented that
behavior.
This series doesn't change that; those paths always resolve timestamps
with cycles == false and preserve their timestamp-domain semantics.
Kohei Enju (3):
net: introduce helper to resolve hardware timestamps from skb
af_packet: use skb_get_hwtstamp() for hardware timestamps
tcp: use skb_get_hwtstamp() for hardware timestamps
include/linux/skbuff.h | 11 +++++++++++
include/net/tcp.h | 2 +-
net/core/skbuff.c | 27 +++++++++++++++++++++++++++
net/ipv4/tcp_input.c | 3 ++-
net/ipv4/tcp_ipv4.c | 6 ++++--
net/ipv6/tcp_ipv6.c | 3 ++-
net/packet/af_packet.c | 2 +-
net/socket.c | 27 +++------------------------
8 files changed, 51 insertions(+), 30 deletions(-)
--
2.53.0
^ permalink raw reply
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