Linux PCI subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Lukasz Laguna" <lukasz.laguna@intel.com>, linux-pci@vger.kernel.org
Subject: Re: [PATCH v1 04/13] PCI/IOV: Allow VF drivers to handle resets
Date: Mon, 19 Oct 2026 09:03:31 +0000	[thread overview]
Message-ID: <sashiko-outbox-176858@kernel.org> (raw)
In-Reply-To: <20261019083514.838640-5-lukasz.laguna@intel.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] PCI/IOV: bus and slot resets fail to lock VFs on virtfn bus or other slots
- [High] PCI/IOV: VF reset_prepare() before PF reset_prepare() deadlocks hisi_qm FLR
--

commit f7e0f3b0fa0caf7e168b1c2965897910aa159a0a
Author: Michał Winiarski <michal.winiarski@intel.com>

PCI/IOV: Allow VF drivers to handle resets

This commit saves and restores PCI configuration state for all subordinate
VFs across PF resets and notifies VF drivers via reset_prepare() and
reset_done(). It also updates bus and slot resets so that VFs are managed
through their parent PF rather than walked independently on the bus.

> diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> index 95452c11dea55..ba8553011daea 100644
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c

[ ... ]

> @@ -5164,6 +5195,17 @@ static void pci_dev_save_and_disable(struct pci_dev *dev)
>  		return;
>  
>  	pci_save_state(dev);
> +}
> +
> +static void pci_dev_save_and_disable(struct pci_dev *dev)
> +{
> +	struct pci_dev *vf;
> +
> +	for_each_pci_vf(dev, vf)
> +		pci_dev_reset_prepare_and_save(vf);
> +
> +	pci_dev_reset_prepare_and_save(dev);

[Severity: High]
Does calling pci_dev_reset_prepare_and_save(vf) on every VF before calling
pci_dev_reset_prepare_and_save(dev) on the PF deadlock HiSilicon QM resets?

During pci_reset_function(pf), pci_dev_save_and_disable() now runs
reset_prepare() on all VFs and then on the PF before pci_dev_restore() calls
reset_done() on any of them.

In drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c,
hisi_acc_vf_pci_reset_prepare() sets QM_RESETTING on the parent PF's
qm->misc_ctl (hisi_acc_vdev->pf_qm) and only clears it in
hisi_acc_vf_pci_aer_reset_done():

    while (test_and_set_bit(QM_RESETTING, &qm->misc_ctl)) {
        msleep(1);
        if (++delay > QM_RESET_WAIT_TIMEOUT) {
            dev_err(dev, "reset prepare failed\n");
            return;
        }
    }

When the first VF sets QM_RESETTING on pf_qm->misc_ctl, any subsequent VF's
hisi_acc_vf_pci_reset_prepare() spins for 60 seconds and fails, and the PF's
hisi_qm_reset_prepare() -> qm_reset_prepare_ready(pf_qm) ->
qm_wait_reset_finish(pf_qm) in drivers/crypto/hisilicon/qm.c also spins on
pf_qm->misc_ctl for ~80 seconds before timing out with -EBUSY ("FLR not
ready!") without stopping the PF.

A similar self-deadlock occurs when VFs are bound to the host hisi_qm drivers
(hisi_hpre, hisi_sec2, hisi_zip) in drivers/crypto/hisilicon/qm.c:

    static int qm_reset_prepare_ready(struct hisi_qm *qm)
    {
        struct pci_dev *pdev = qm->pdev;
        struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(pdev));

        if (qm->ver < QM_HW_V3)
            return qm_wait_reset_finish(pf_qm);

        return qm_wait_reset_finish(qm);
    }

On Kunpeng920 (qm->ver < QM_HW_V3), the first VF's hisi_qm_reset_prepare()
sets QM_RESETTING on pf_qm->misc_ctl, blocking subsequent VFs and the PF's
own hisi_qm_reset_prepare(). On Kunpeng930+ (qm->ver >= QM_HW_V3), each VF's
hisi_qm_reset_prepare() sets QM_RESETTING on vf_qm->misc_ctl; when the PF's
hisi_qm_reset_prepare() then calls qm_try_stop_vfs(qm, QM_PF_FLR_PREPARE,
QM_DOWN), each VF's mailbox handler qm_pf_reset_vf_prepare() calls
qm_reset_prepare_ready(vf_qm) -> qm_wait_reset_finish(vf_qm), which waits for
QM_RESETTING on vf_qm->misc_ctl to clear and times out.

[ ... ]

> @@ -5551,6 +5631,11 @@ static int pci_slot_trylock(struct pci_slot *slot)
>  	return __pci_bus_trylock(slot->bus, slot);
>  }
>  
> +static bool pci_dev_skip_bus_reset(struct pci_dev *dev)
> +{
> +	return dev->is_virtfn; /* VFs are managed by their PF */
> +}
> +
>  /*
>   * Save and disable devices from the top of the tree down while holding
>   * the @dev mutex lock for the entire tree.
> @@ -5560,6 +5645,8 @@ static void pci_bus_save_and_disable_locked(struct pci_bus *bus)
>  	struct pci_dev *dev;
>  
>  	list_for_each_entry(dev, &bus->devices, bus_list) {
> +		if (pci_dev_skip_bus_reset(dev))
> +			continue;
>  		pci_dev_save_and_disable(dev);
>  		if (dev->subordinate)
>  			pci_bus_save_and_disable_locked(dev->subordinate);
> @@ -5576,6 +5663,8 @@ static void pci_bus_restore_locked(struct pci_bus *bus)
>  	struct pci_dev *dev;
>  
>  	list_for_each_entry(dev, &bus->devices, bus_list) {
> +		if (pci_dev_skip_bus_reset(dev))
> +			continue;
>  		pci_dev_restore(dev);
>  		if (dev->subordinate) {
>  			pci_bridge_wait_for_secondary_bus(dev, "bus reset");
> @@ -5595,6 +5684,8 @@ static void pci_slot_save_and_disable_locked(struct pci_slot *slot)
>  	list_for_each_entry(dev, &slot->bus->devices, bus_list) {
>  		if (!dev->slot || dev->slot != slot)
>  			continue;
> +		if (pci_dev_skip_bus_reset(dev))
> +			continue;
>  		pci_dev_save_and_disable(dev);
>  		if (dev->subordinate)
>  			pci_bus_save_and_disable_locked(dev->subordinate);
> @@ -5613,6 +5704,8 @@ static void pci_slot_restore_locked(struct pci_slot *slot)
>  	list_for_each_entry(dev, &slot->bus->devices, bus_list) {
>  		if (!dev->slot || dev->slot != slot)
>  			continue;
> +		if (pci_dev_skip_bus_reset(dev))
> +			continue;
>  		pci_dev_restore(dev);
>  		if (dev->subordinate) {
>  			pci_bridge_wait_for_secondary_bus(dev, "slot reset");

[Severity: High]
Can pci_bus_save_and_disable_locked(), pci_bus_restore_locked(),
pci_slot_save_and_disable_locked(), and pci_slot_restore_locked() operate on
unlocked VFs when VFs reside on an SR-IOV virtual bus or do not match the
PF's struct pci_slot?

When pci_dev_save_and_disable() and pci_dev_restore() run on a PF, they
iterate over all VFs with for_each_pci_vf(dev, vf) and call
pci_dev_reset_prepare_and_save(vf) and pci_dev_reset_done(vf), both of which
require the VF lock to be held and assert device_lock_assert(&vf->dev).

However, __pci_bus_lock(), __pci_bus_trylock(), and __pci_bus_unlock() in
drivers/pci/pci.c only walk bus->devices (filtered by dev->slot == slot for
slot resets) and recurse into dev->subordinate:

    list_for_each_entry(dev, &bus->devices, bus_list) {
        if (slot && (!dev->slot || dev->slot != slot))
            continue;
        if (dev->subordinate) {
            if (!pci_bus_trylock(dev->subordinate))
                goto unlock;
        } else if (!pci_dev_trylock(dev))
            goto unlock;
    }

First, if any VFs reside on an SR-IOV virtual bus created by virtfn_add_bus()
in drivers/pci/iov.c:

    child = pci_add_new_bus(bus, NULL, busnr);

that virtual bus has bridge == NULL, so pf->subordinate is NULL and those VFs
are on the virtual bus's devices list rather than pf->bus->devices. As a
result, __pci_bus_lock() and __pci_bus_trylock() never lock VFs on the
virtual bus.

Second, during a slot reset (pci_slot_trylock() / pci_slot_lock()),
pci_dev_matches_slot() in drivers/pci/slot.c only assigns dev->slot = slot
when the device matches the slot number:

    static bool pci_dev_matches_slot(struct pci_dev *dev, struct pci_slot *slot)
    {
        if (slot->per_func_slot)
            return dev->devfn == slot->number;

        return slot->number == PCI_SLOT_ALL_DEVICES ||
            PCI_SLOT(dev->devfn) == slot->number;
    }

Any VF with a different PCI_SLOT(vf->devfn) (when slot->number !=
PCI_SLOT_ALL_DEVICES) or a different devfn (when slot->per_func_slot is set)
has vf->slot != slot and is skipped by __pci_bus_trylock(slot->bus, slot),
while pci_slot_save_and_disable_locked() and pci_slot_restore_locked() still
visit all VFs of the PF via for_each_pci_vf(dev, vf) without holding their
device or config access locks.

Should __pci_bus_lock(), __pci_bus_trylock(), and __pci_bus_unlock() also
skip VFs with pci_dev_skip_bus_reset(dev) and lock/unlock VFs via their PF
using pci_dev_lock_vfs(), pci_dev_trylock_vfs(), and pci_dev_unlock_vfs()?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261019083514.838640-1-lukasz.laguna@intel.com?part=4

  reply	other threads:[~2026-10-19  9:03 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-19  8:35 [PATCH v1 00/13] PCI/IOV: Improve reset handling with VFs enabled Lukasz Laguna
2026-10-19  8:35 ` [PATCH v1 01/13] PCI/IOV: Introduce pci_iov_get_virtfn() Lukasz Laguna
2026-10-19  8:43   ` sashiko-bot
2026-10-19  8:35 ` [PATCH v1 02/13] PCI/IOV: Introduce VF iterators Lukasz Laguna
2026-10-19  8:43   ` sashiko-bot
2026-10-19  8:35 ` [PATCH v1 03/13] PCI/IOV: Introduce VF locking helpers Lukasz Laguna
2026-10-19  8:47   ` sashiko-bot
2026-10-19  8:35 ` [PATCH v1 04/13] PCI/IOV: Allow VF drivers to handle resets Lukasz Laguna
2026-10-19  9:03   ` sashiko-bot [this message]
2026-10-19  8:35 ` [PATCH v1 05/13] ionic: Lock VFs around pci_reset_function_locked() Lukasz Laguna
2026-10-19  8:45   ` sashiko-bot
2026-10-19  8:35 ` [PATCH v1 06/13] pds_core: " Lukasz Laguna
2026-10-19  8:44   ` sashiko-bot
2026-10-19  8:35 ` [PATCH v1 07/13] i40e: Drop VF MSI state restore from PF reset_done Lukasz Laguna
2026-10-19  8:42   ` sashiko-bot
2026-10-19  8:35 ` [PATCH v1 08/13] PCI/IOV: Allow bus reset_method for PF with VFs enabled Lukasz Laguna
2026-10-19  8:48   ` sashiko-bot
2026-10-19  8:35 ` [PATCH v1 09/13] PCI/TSM: Use generic VF iterators Lukasz Laguna
2026-10-19  8:40   ` sashiko-bot
2026-10-19  8:35 ` [PATCH v1 10/13] ixgbe: Use generic VF iterator Lukasz Laguna
2026-10-19  8:41   ` sashiko-bot
2026-10-19  8:35 ` [PATCH v1 11/13] ice: Use pci_iov_get_virtfn() Lukasz Laguna
2026-10-19  8:42   ` sashiko-bot
2026-10-19  8:35 ` [PATCH v1 12/13] liquidio: " Lukasz Laguna
2026-10-19  8:43   ` sashiko-bot
2026-10-19  8:35 ` [PATCH v1 13/13] drm/xe/pf: " Lukasz Laguna
2026-10-19  8:43   ` sashiko-bot

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=sashiko-outbox-176858@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=lukasz.laguna@intel.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /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