[PATCH RESEND 1/5] ata: pata_ixp4xx_cf: avoid recursive locking in the PIO callback

From: shao.mingyin

Date: Fri Nov 13 2026 - 15:16:39 EST


From: Shao Mingyin <shao.mingyin@xxxxxxxxxx>

ixp4xx_mmio_data_xfer() takes ap->lock, but the SFF state machine
already holds that lock when it invokes ->sff_data_xfer(), both
from interrupt context and from the PIO work item. The first data
transfer, including IDENTIFY during probe, therefore recursively
takes the same spinlock and can hang the CPU.

Use the lock held by the caller while switching the expansion bus
width and transferring data. Assert that the lock is held rather
than acquiring it again.

Fixes: 47adef20e67d ("pata: ixp4xx: Rewrite to use device tree")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Shao Mingyin <shao.mingyin@xxxxxxxxxx>
---
Tested on 7.4.0-rc1-next-20261105 under QEMU/KVM (x86_64, 2 vCPUs),
with lockdep, DEBUG_SPINLOCK and KASAN. Both kernels carry the same
TEST-ONLY harness: a synthetic ATA host and RAM-backed registers feed
the original SFF state machine and IXP4xx PIO callback. Only this fix
differs between the two kernels.

The unpatched kernel reports recursive locking of host->lock from
ixp4xx_mmio_data_xfer() and stops in the first SFF transfer. With the
fix, SFF calls with in_wq=0 and in_wq=1 each transfer 512 bytes and
restore the 8-bit bus timing. Separate 513-byte read/write checks also
pass, including the trailing byte and buffer sentinel. The patched
guest shuts down normally without lockdep or KASAN reports. This
tests the callback and SFF interaction, not IXP4xx hardware, probe,
interrupt delivery or PIO worker scheduling.

drivers/ata/pata_ixp4xx_cf.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c
index 80f6a91acf..6ea671d218 100644
--- a/drivers/ata/pata_ixp4xx_cf.c
+++ b/drivers/ata/pata_ixp4xx_cf.c
@@ -131,11 +131,10 @@ static unsigned int ixp4xx_mmio_data_xfer(struct ata_queued_cmd *qc,
struct ata_port *ap = qc->dev->link->ap;
void __iomem *mmio = ap->ioaddr.data_addr;
struct ixp4xx_pata *ixpp = ap->host->private_data;
- unsigned long flags;

ata_dev_dbg(adev, "%s %d bytes\n", (rw == READ) ? "READ" : "WRITE",
buflen);
- spin_lock_irqsave(ap->lock, flags);
+ lockdep_assert_held(ap->lock);

/* set the expansion bus in 16bit mode and restore
* 8 bit mode after the transaction.
@@ -169,8 +168,6 @@ static unsigned int ixp4xx_mmio_data_xfer(struct ata_queued_cmd *qc,
ixp4xx_set_8bit_timing(ixpp, adev->pio_mode);
udelay(5);

- spin_unlock_irqrestore(ap->lock, flags);
-
return words << 1;
}

--
2.27.0