Netdev List
 help / color / mirror / Atom feed
From: "Rao, Nikhil" <nikhirao@amd.com>
To: Paolo Abeni <pabeni@redhat.com>
Cc: netdev@vger.kernel.org, kuba@kernel.org, brett.creeley@amd.com,
	eric.joyner@amd.com, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@google.com, jacob.e.keller@intel.com,
	nikhil.rao@amd.com
Subject: Re: [PATCH net-next v6 3/6] pds_core: add PLDM firmware update support via devlink flash
Date: Wed, 8 Jul 2026 11:25:08 -0700	[thread overview]
Message-ID: <536c1128-45ba-4d4c-866c-8ec8f1467898@amd.com> (raw)
In-Reply-To: <20260702091100.93838-1-pabeni@redhat.com>

On 7/2/2026 2:11 AM, Paolo Abeni wrote:
 > From: AI Reviewer <ai@example.com>
> 
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://netdev-ai.bots.linux.dev/sashiko/
> ---
> pds_core: add PLDM firmware update support via devlink flash
> 
> Implements PLDM firmware update via devlink flash using the upstream
> pldmfw API. Adds whole-package and per-component update paths, and
> introduces a deferred-DMA list that releases buffers after timed-out
> devcmds. A few questions inline.
> 
>> diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
>> index b7fe9ad73349..c686f0bbbaeb 100644
>> --- a/drivers/net/ethernet/amd/pds_core/core.h
>> +++ b/drivers/net/ethernet/amd/pds_core/core.h
>> @@ -23,6 +23,14 @@
>>   #define PDSC_SETUP_RECOVERY  false
>>   #define PDSC_SETUP_INIT              true
>>
>> +struct pdsc_deferred_dma {
>> +     struct list_head list;
>> +     dma_addr_t dma_addr;
>> +     void *va;
>> +     size_t size;
>> +     enum dma_data_direction dir;
>> +};
>> +
> 
> [ ... ]
> 
>> diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
>> index 5c0ca3d0b000..6082b28915db 100644
>> --- a/drivers/net/ethernet/amd/pds_core/dev.c
>> +++ b/drivers/net/ethernet/amd/pds_core/dev.c
>> @@ -206,15 +206,53 @@ static int __pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
>>        else
>>                memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp));
>>
>> +     if (err != -ETIMEDOUT && err != -EAGAIN)
>> +             pdsc_deferred_dma_free(pdsc);
>> +
>>        return err;
>>   }
> 
> [High]
> Can the deferred-DMA scheme actually guarantee that the device has
> finished accessing the buffer before it is unmapped and freed?
> 
> The trigger for releasing a deferred entry is "the next devcmd returned
> something other than -ETIMEDOUT/-EAGAIN", plus pdsc_remove() and
> pdsc_reset_prepare(). None of these provide a hardware-level fence that
> the previous in-flight DMA has drained. In the remove/reset paths the
> free runs after pci_disable_device(), which clears bus-master but does
> not synchronously drain pending transactions.
> 
> The DMA_FROM_DEVICE case in pdsc_get_component_info() is particularly
> exposed because the buffer is plain kernel slab memory: a late device
> write after kfree would corrupt unrelated allocations.
Will fix in v7 by adding a config space read after pci_disable_device() 
in pdsc_teardown() before freeing deferred DMA buffers. PCI transaction 
ordering rules guarantee that a read completion cannot return until all 
prior device-initiated memory writes have completed. This ensures 
in-flight DMA has drained before buffers are freed.

> 
> Also note that pdsc_send_package_data(), pdsc_get_component_info(), and
> pdsc_flash_component_chunk() only defer on -EAGAIN/-ETIMEDOUT and
> immediately unmap+kfree on any other error (-EIO, -ENXIO, ...). Is the
> device guaranteed not to have started any DMA in those cases?
Yes. When devcmd returns -EIO or -ENXIO (or any non-timeout error), the 
command has completed, the MMIO read that retrieved the completion 
status acts as a DMA fence. The immediate free is safe because command 
completion implies DMA finished.

> 
>> +void pdsc_deferred_dma_add(struct pdsc *pdsc, struct pdsc_deferred_dma *entry,
>> +                        dma_addr_t dma_addr, void *va, size_t size,
>> +                        enum dma_data_direction dir)
>> +{
>> +     entry->dma_addr = dma_addr;
>> +     entry->va = va;
>> +     entry->size = size;
>> +     entry->dir = dir;
>> +
>> +     spin_lock(&pdsc->deferred_dma_lock);
>> +     list_add_tail(&entry->list, &pdsc->deferred_dma_list);
>> +     spin_unlock(&pdsc->deferred_dma_lock);
>> +}
>> +
>> +void pdsc_deferred_dma_free(struct pdsc *pdsc)
>> +{
>> +     struct pdsc_deferred_dma *entry, *tmp;
>> +
>> +     spin_lock(&pdsc->deferred_dma_lock);
>> +     list_for_each_entry_safe(entry, tmp, &pdsc->deferred_dma_list, list) {
>> +             dma_unmap_single(pdsc->dev, entry->dma_addr,
>> +                              entry->size, entry->dir);
>> +             kfree(entry->va);
>> +             list_del(&entry->list);
>> +             kfree(entry);
>> +     }
>> +     spin_unlock(&pdsc->deferred_dma_lock);
>> +}
> 
> [Medium]
> Is it intentional to hold deferred_dma_lock across dma_unmap_single()
> and two kfree()s per entry? This runs from __pdsc_devcmd_locked() on
> every successful devcmd while devcmd_lock is also held, and
> dma_unmap_single() can trigger IOMMU IOTLB invalidations. Would
> list_splice_init() under the lock followed by iteration on a detached
> list scale better when many entries have accumulated?
Will fix in v7, using list_splice_init() to detach the list under the 
lock, then iterate and free without holding the lock.

> [ ... ]
> 
>> diff --git a/drivers/net/ethernet/amd/pds_core/fw.c b/drivers/net/ethernet/amd/pds_core/fw.c
>> index fa626719e68d..433b0a8df4c1 100644
>> --- a/drivers/net/ethernet/amd/pds_core/fw.c
>> +++ b/drivers/net/ethernet/amd/pds_core/fw.c
> 
> [ ... ]
> 
>> +#define PDSC_FW_COMPONENT_PREFIX             "fw."
>> +#define PDSC_FW_COMPONENT_FULL_NAME_BUFLEN \
>> +     (sizeof(PDSC_FW_COMPONENT_PREFIX) + PDS_CORE_FW_COMPONENT_NAME_BUFLEN)
>> +
>> +/* Driver-defined component type to name mapping.
>> + * PDS_CORE_FW_TYPE_MAIN is NULL - handled specially as "fw" without prefix.
>> + */
>> +static const char * const pdsc_fw_type_names[] = {
>> +     [PDS_CORE_FW_TYPE_MAIN]      = NULL,
>> +     [PDS_CORE_FW_TYPE_BOOT]      = "bootloader",
>> +     [PDS_CORE_FW_TYPE_CPLD]      = "cpld",
>> +     [PDS_CORE_FW_TYPE_SECURE]    = "secure",
>> +     [PDS_CORE_FW_TYPE_FPGA]      = "fpga",
>> +     [PDS_CORE_FW_TYPE_SUC_MAIN]  = "suc",
>> +     [PDS_CORE_FW_TYPE_SUC_BOOT]  = "suc.bootloader",
>> +     [PDS_CORE_FW_TYPE_UBOOT]     = "uboot",
>> +};
> 
> [Low, Low]
> The documentation table added in this same patch lists fw.gold as a
> driver-defined component name, and the kerneldoc on
> enum pds_core_fw_component_type says gold variants are "reported with
> a .gold suffix (e.g., fw.gold)". The table above has no gold entry, so
> pdsc_name_to_fw_type("fw.gold") returns 0 and pdsc_pldm_firmware_update()
> rejects the request with -ENOENT. Should fw.gold either be wired up or
> the docs clarify that gold is read-only?
Will update documentation in v7. The component for fw.gold is "fw". Gold 
firmware can be updated by specifying component "fw" with a goldfw 
package file.

> PDSC_FW_COMPONENT_FULL_NAME_BUFLEN is defined here but never used;
> pdsc_flash_component() uses an ad-hoc sizeof(PDSC_FW_COMPONENT_PREFIX) +
> 16 instead. Should this macro be wired up or dropped?
> 
Will fix in v7, using the PDSC_FW_COMPONENT_FULL_NAME_BUFLEN macro in 
pdsc_flash_component() for the component_name_buf array size.

> [ ... ]
> 
>> +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,
>> +     };
> 
> [ ... ]
> 
>> +     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_once(pdsc->dev,
>> +                           "Incompatible get_component_info version %u reported by firmware\n",
>> +                           comp.get_component_info.ver);
>> +             err = 0;
>> +             goto out;
>> +     }
> 
> [Low]
> This branch returns 0 without populating pdsc->fw_components. The caller
> pdsc_pldm_firmware_update() uses:
> 
>          if (!pdsc->fw_components.num_components) {
>                  err = pdsc_get_component_info(pdsc);
>                  if (err) { ... return err; }
>          }
> 
>          if (params->component) {
>                  u8 type = pdsc_name_to_fw_type(params->component);
> 
>                  if (!type || !pdsc_component_type_exists(pdsc, type))
>                          return -ENOENT;
>          }
> 
> so on a device that reports ver == 0, the cache never populates and
> every per-component flash returns -ENOENT. Should this branch return an
> explicit error instead, or otherwise prevent the per-component lookup
> from running?
> 
The dev_warn_once message tells the user to update their firmware. 
Per-component flash will fail with -ENOENT, but the user needs to 
upgrade firmware anyway to use this feature. Returning an explicit error 
here would prevent the basic flash operation from working on old firmware.

> [ ... ]
> 
>> +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 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));
> 
> [Low]
> In the STATUS retry phase, only cmd.send_component.operation changes
> between iterations; data_pa, data_len, offset, and slot_id remain set
> to the original START values, and pdsc_devcmd_with_data_nomsg() also
> re-issues memcpy_toio(&pdsc->cmd_regs->data, info, info_sz) on every
> poll (potentially up to ~15000 iterations over the 300s timeout at 20ms
> cadence). Does the firmware ignore data_pa/data_len on the STATUS
> opcode, or could it re-initiate DMA from the same buffer? Could the
> status poll send just the operation field?
> 
Firmware ignores data_pa/data_len/offset on STATUS operation. Only the 
operation field is meaningful for polling completion status.

> [ ... ]
> 
>> +static int pdsc_finalize_update(struct pldmfw *context)
>> +{
> [ ... ]
>> +     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);
> 
> [Low]
> Is the !err in if (!err || err != -EAGAIN) intentional? err == 0 already
> satisfies err != -EAGAIN, so the condition reduces to err != -EAGAIN.
Will fix in v7, simplified to just if (err != -EAGAIN).

> [ ... ]
> 
>> +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 (!pdsc->fw_components.num_components) {
>> +             err = pdsc_get_component_info(pdsc);
>> +             if (err) {
>> +                     dev_err(pdsc->dev,
>> +                             "Failed to get component info: %pe\n",
>> +                             ERR_PTR(err));
>> +                     return err;
>> +             }
>> +     }
>> +
>> +     if (params->component) {
>> +             u8 type = pdsc_name_to_fw_type(params->component);
>> +
>> +             if (!type || !pdsc_component_type_exists(pdsc, type))
>> +                     return -ENOENT;
>> +     }
>> +
>> +     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;
>> +}
> 
> [Medium]
> The extack parameter is accepted here and struct pds_core_fwu_priv
> declares a netlink_ext_ack *extack field, but priv.extack is never
> assigned, and none of the PLDM callbacks (pdsc_match_record_descs,
> pdsc_send_package_data, pdsc_send_component_table, pdsc_flash_component,
> pdsc_finalize_update) call NL_SET_ERR_MSG_MOD(). The legacy path uses
> extack at multiple call sites ("Firmware wait timed out", "Segment
> download failed", ...). Was the intent to propagate extack diagnostics
> on the PLDM path too, including for the -ENOENT returned above?
Will fix in v7 by adding NL_SET_ERR_MSG_MOD calls to PLDM error paths.

> 
>> +int pdsc_firmware_update(struct pdsc *pdsc,
>> +                      struct devlink_flash_update_params *params,
>> +                      struct netlink_ext_ack *extack)
>> +{
>> +     int err;
>> +
>> +     if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2 &&
>> +         pdsc->dev_ident.capabilities &
>> +             cpu_to_le64(PDS_CORE_DEV_CAP_PLDM_FW_UPDATE))
>> +             err = pdsc_pldm_firmware_update(pdsc, params, extack,
>> +                                             params->fw);
>> +     else
>> +             err = pdsc_legacy_firmware_update(pdsc, params->fw, extack);
>> +
>> +     /* Invalidate cached component info so next info_get refreshes */
>> +     pdsc->fw_components.num_components = 0;
>> +
>> +     return err;
>> +}
> 
> [Medium]
> What happens here when a user runs:
> 
>      devlink dev flash pci/0000:b5:00.0 file foo component fw.cpld
> 
> against a device that does not advertise PDS_CORE_DEV_CAP_PLDM_FW_UPDATE?
> The dispatcher falls through to pdsc_legacy_firmware_update(pdsc,
> params->fw, extack), which has no notion of components and proceeds to
> flash the entire image.
> 
> The commit message says:
> 
>      Not all components support per-component update - devlink will
>      reject the request if the specified component cannot be updated.
> 
> Should the dispatcher return -EOPNOTSUPP (or similar) when
> params->component != NULL on the legacy path, to match that promise?
The legacy path should reject component parameters, -net fix [1]

> 
>> diff --git a/include/linux/pds/pds_core_if.h b/include/linux/pds/pds_core_if.h
>> index 619186f26b5b..5a1fafaccf20 100644
>> --- a/include/linux/pds/pds_core_if.h
>> +++ b/include/linux/pds/pds_core_if.h
>> @@ -279,11 +299,20 @@ enum pds_core_fw_control_oper {
>>        PDS_CORE_FW_GET_LIST               = 7,
>>   };
>>
>> +/**
>> + * enum pds_core_fw_slot - Firmware slot identifiers
>> + * @PDS_CORE_FW_SLOT_INVALID: Let firmware select slot based on package metadata
>> + * @PDS_CORE_FW_SLOT_A:       Primary firmware slot A
>> + * @PDS_CORE_FW_SLOT_B:       Primary firmware slot B
>> + * @PDS_CORE_FW_SLOT_GOLD:    Gold/recovery firmware slot
>> + * @PDS_CORE_FW_SLOT_MAX:     Sentinel value indicating no slot resolved
>> + */
>>   enum pds_core_fw_slot {
>>        PDS_CORE_FW_SLOT_INVALID    = 0,
>>        PDS_CORE_FW_SLOT_A          = 1,
>>        PDS_CORE_FW_SLOT_B          = 2,
>>        PDS_CORE_FW_SLOT_GOLD       = 3,
>> +     PDS_CORE_FW_SLOT_MAX        = 0xff,
>>   };
> 
> [Low]
> PDS_CORE_FW_SLOT_MAX is added with value 0xff to this wire-protocol
> enum but has no references anywhere. Should it be wired up to something
> or dropped before the enum value is locked in by firmware?
Firmware uses this value internally as a sentinel. The driver doesn't 
branch on it (relies on response_code), but defining it prevents future 
enum additions from colliding with 0xff.

Thanks,
Nikhil


[1] 
https://lore.kernel.org/netdev/20260708163649.128620-1-nikhil.rao@amd.com

  reply	other threads:[~2026-07-08 18:25 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-29 23:01 [PATCH net-next v6 0/6] pds_core: Add PLDM firmware update and host backed memory support Nikhil P. Rao
2026-06-29 23:01 ` [PATCH net-next v6 1/6] pds_core: add support for quiet devcmd failures Nikhil P. Rao
2026-06-29 23:01 ` [PATCH net-next v6 2/6] pds_core: add support for identity version 2 Nikhil P. Rao
2026-06-29 23:01 ` [PATCH net-next v6 3/6] pds_core: add PLDM firmware update support via devlink flash Nikhil P. Rao
2026-07-02  9:11   ` Paolo Abeni
2026-07-08 18:25     ` Rao, Nikhil [this message]
2026-06-29 23:01 ` [PATCH net-next v6 4/6] pds_core: add PLDM component info display Nikhil P. Rao
2026-07-02  9:11   ` Paolo Abeni
2026-07-08 17:55     ` Rao, Nikhil
2026-06-29 23:01 ` [PATCH net-next v6 5/6] pds_core: add host backed memory support for firmware Nikhil P. Rao
2026-06-29 23:02 ` [PATCH net-next v6 6/6] pds_core: add debugfs support for host backed memory Nikhil P. Rao

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=536c1128-45ba-4d4c-866c-8ec8f1467898@amd.com \
    --to=nikhirao@amd.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=brett.creeley@amd.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=eric.joyner@amd.com \
    --cc=jacob.e.keller@intel.com \
    --cc=kuba@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=nikhil.rao@amd.com \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox