pds_core: fix cmd_regs access racing BAR unmap on reset

pdsc_reset_prepare() and pdsc_reset_done()'s pdsc_map_bars() error path
clear/iounmap cmd_regs without devcmd_lock, and
pdsc_legacy_firmware_update()'s download loop derefs cmd_regs after
dropping and retaking the lock without re-checking. An FLR concurrent
with a devlink flash can unmap cmd_regs under an in-flight devcmd,
causing a NULL deref or a write to unmapped MMIO.

Take devcmd_lock across the BAR unmap/remap, and re-check cmd_regs in
the download loop. Only the PF maps cmd_regs and runs devcmd, so skip
the unmap on a VF, as pdsc_remove() and pdsc_reset_done() already do.

A reset that completes entirely within the unlocked window is not a
correctness problem for the image: the device clears its update session,
so a resumed download is rejected, and it verifies the staged image
before writing a flash slot, reporting PDS_RC_BAD_FW rather than
activating it.

pdsc_unmap_bars() also clears info_regs, intr_status and intr_ctrl. The
interrupt and start/stop readers of those are quiesced before the unmap
by pdsc_fw_down(), which frees the interrupts and tears down the queues.
The debugfs readers are not, since those files outlive a reset; that is
pre-existing and out of scope here.

Fixes: e96094c1d1 ("pds_core: Clear BARs on reset")
Reported-by: sashiko-bot <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260708212222.296202-1-nikhil.rao%40amd.com?part=3
Signed-off-by: Nikhil P. Rao <nikhil.rao@amd.com>
Link: https://patch.msgid.link/20260901044219.1361466-2-nikhil.rao@amd.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Nikhil P. Rao 2026-09-01 04:42:17 +00:00 committed by Jakub Kicinski
parent 6262acad9d
commit 7980325b2f
2 changed files with 16 additions and 2 deletions

View File

@ -171,8 +171,10 @@ pdsc_legacy_firmware_update(struct pdsc *pdsc,
dev_info(pdsc->dev, "Installing firmware\n");
if (!pdsc->cmd_regs)
if (!pdsc->cmd_regs) {
NL_SET_ERR_MSG_MOD(extack, "BARs not mapped");
return -ENXIO;
}
dl = priv_to_devlink(pdsc);
devlink_flash_update_status_notify(dl, "Preparing to flash",
@ -198,6 +200,12 @@ pdsc_legacy_firmware_update(struct pdsc *pdsc,
copy_sz = min_t(unsigned int, buf_sz, fw->size - offset);
mutex_lock(&pdsc->devcmd_lock);
if (!pdsc->cmd_regs) {
mutex_unlock(&pdsc->devcmd_lock);
err = -ENXIO;
NL_SET_ERR_MSG_MOD(extack, "Device reset during flash");
goto err_out;
}
memcpy_toio(&pdsc->cmd_regs->data, fw->data + offset, copy_sz);
err = pdsc_devcmd_fw_download_locked(pdsc, data_addr,
offset, copy_sz);

View File

@ -513,7 +513,11 @@ static void pdsc_reset_prepare(struct pci_dev *pdev)
pdsc_auxbus_dev_del(pdsc, pdsc, &pdsc->padev);
}
pdsc_unmap_bars(pdsc);
if (!pdev->is_virtfn) {
mutex_lock(&pdsc->devcmd_lock);
pdsc_unmap_bars(pdsc);
mutex_unlock(&pdsc->devcmd_lock);
}
pci_release_regions(pdev);
if (pci_is_enabled(pdev))
pci_disable_device(pdev);
@ -543,7 +547,9 @@ static void pdsc_reset_done(struct pci_dev *pdev)
return;
}
mutex_lock(&pdsc->devcmd_lock);
err = pdsc_map_bars(pdsc);
mutex_unlock(&pdsc->devcmd_lock);
if (err)
return;
}