ata: sata_dwc_460ex: cleanups and interrupt ordering fix
v2 cover ata: sata_dwc_460ex: cleanups and interrupt ordering fix
This series modernizes the sata_dwc_460ex driver by replacing legacy OF APIs with their modern device-property and platform-device counterparts, converting old DMA resource teardown to devm-managed cleanup, fixing a latent interrupt-ordering bug, and dropping a redundant stack copy of port_info.
Patch 4 fixes a real issue: sata_dwc_enable_interrupts() was called before the IRQ handler was registered, so a probe failure or an asserted interrupt could trigger an unhandled irq storm.
v2: sashiko fixes.
Rosen Penev (8):
ata: sata_dwc_460ex: use device_property_present()
ata: sata_dwc_460ex: use platform_get_irq()
ata: sata_dwc_460ex: enable SATA interrupts only after IRQ handler is
registered
ata: sata_dwc_460ex: drop redundant struct copy of port_info
ata: sata_dwc_460ex: fix data race on hsdev->sactive_issued in
interrupt handler
ata: sata_dwc_460ex: disable SATA interrupts on device removal
ata: sata_dwc_460ex: fix PHY lifecycle ordering on device removal
ata: sata_dwc_460ex: use devm for old DMA resource lifetime management
drivers/ata/sata_dwc_460ex.c | 99 ++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 50 deletions(-)
--
2.55.0
PATCH v2 1/8 ata: sata_dwc_460ex: use device_property_present()
There's no need to use np here when dev is enough. Follows the similar pattern elsewhere where of APIs are replaced with device ones, especially when using platform_device.
--- drivers/ata/sata_dwc_460ex.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/ata/sata_dwc_460ex.c +3 -2
@@ -21,6 +21,7 @@#include <linux/of.h> #include <linux/of_irq.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/phy/phy.h> #include <linux/libata.h> #include <linux/slab.h>
@@ -809,7 +810,7 @@ static int sata_dwc_dma_get_channel(struct sata_dwc_device_port *hsdevp)struct device *dev = hsdev->dev;#ifdef CONFIG_SATA_DWC_OLD_DMA -if (!of_property_present (dev->of_node,"dmas" )) +if (!device_property_present (dev,"dmas" ))return sata_dwc_dma_get_channel_old (hsdevp);#endif
@@ -1180,7 +1181,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) }#ifdef CONFIG_SATA_DWC_OLD_DMA -if (!of_property_present (np,"dmas" )) { +if (!device_property_present (dev,"dmas" )) { err =sata_dwc_dma_init_old (ofdev, hsdev);if (err)return err;
Signature · 1 line
2.55.0
PATCH v2 2/8 ata: sata_dwc_460ex: use platform_get_irq()
In reply to Rosen PenevReplace irq_of_parse_and_map() with platform_get_irq() in both sata_dwc_dma_init_old() and sata_dwc_probe(). This is the preferred way to obtain IRQs for platform devices and provides better error reporting. Remove the now-unnecessary #include <linux/of_irq.h>.
irq_of_parse_and_map() requires irq_dispose_mapping(), which is missing.
--- drivers/ata/sata_dwc_460ex.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-)
drivers/ata/sata_dwc_460ex.c +6 -13
@@ -19,7 +19,6 @@#include <linux/device.h> #include <linux/dmaengine.h> #include <linux/of.h> -#include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/phy/phy.h>
@@ -227,7 +226,6 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev, struct sata_dwc_device *hsdev) {struct device *dev = &pdev->dev; -struct device_node *np = dev->of_node; hsdev->dma =devm_kzalloc (dev,sizeof (*hsdev->dma),GFP_KERNEL );if (!hsdev->dma)
@@ -237,11 +235,9 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev, hsdev->dma->id = pdev->id;/* Get SATA DMA interrupt number */ - hsdev->dma->irq =irq_of_parse_and_map (np,1 ); -if (!hsdev->dma->irq) { -dev_err (dev,"no SATA DMA irq\n" ); -return -ENODEV ; - } + hsdev->dma->irq =platform_get_irq (pdev,1 ); +if (hsdev->dma->irq <0 ) +return hsdev->dma->irq;/* Get physical SATA DMA register base address */ hsdev->dma->regs =devm_platform_ioremap_resource (pdev,1 );
@@ -1127,7 +1123,6 @@ static const struct ata_port_info sata_dwc_port_info[] = {static int sata_dwc_probe (struct platform_device *ofdev) {struct device *dev = &ofdev->dev; -struct device_node *np = dev->of_node;struct sata_dwc_device *hsdev;u32 idr, versionr;char *ver = (char *)&versionr;
@@ -1174,11 +1169,9 @@ static int sata_dwc_probe(struct platform_device *ofdev)sata_dwc_enable_interrupts (hsdev);/* Get SATA interrupt number */ - irq =irq_of_parse_and_map (np,0 ); -if (!irq) { -dev_err (dev,"no SATA DMA irq\n" ); -return -ENODEV ; - } + irq =platform_get_irq (ofdev,0 ); +if (irq <0 ) +return irq;#ifdef CONFIG_SATA_DWC_OLD_DMA if (!device_property_present (dev,"dmas" )) {
Signature · 1 line
2.55.0
PATCH v2 3/8 ata: sata_dwc_460ex: enable SATA interrupts only after IRQ handler is registered
In reply to Rosen Penevsata_dwc_enable_interrupts() is called before platform_get_irq() and ata_host_activate(), leaving the SATA controller's interrupt mask enabled without a registered handler. If a later step fails (irq request, phy init, etc.) or if the controller asserts an interrupt during probe, the irq line may fire with no handler, causing a spurious interrupt storm.
Move sata_dwc_enable_interrupts() after ata_host_activate() so that interrupts are only unmasked once the handler is registered and the core is fully initialized.
--- drivers/ata/sata_dwc_460ex.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/ata/sata_dwc_460ex.c +2 -3
@@ -1165,9 +1165,6 @@ static int sata_dwc_probe(struct platform_device *ofdev)/* Save dev for later use in dev_xxx() routines */ hsdev->dev = dev; -/* Enable SATA Interrupts */ -sata_dwc_enable_interrupts (hsdev); -/* Get SATA interrupt number */ irq =platform_get_irq (ofdev,0 );if (irq <0 )
@@ -1198,6 +1195,8 @@ static int sata_dwc_probe(struct platform_device *ofdev)if (err)dev_err (dev,"failed to activate host" ); +/* Enable SATA Interrupts */ +sata_dwc_enable_interrupts (hsdev);return 0 ; error_out:
Signature · 1 line
2.55.0
PATCH v2 4/8 ata: sata_dwc_460ex: drop redundant struct copy of port_info
In reply to Rosen Penevsata_dwc_port_info[0] was copied to a local auto variable before being referenced via a pointer array. The local copy is never modified, so drop it and point ppi directly at the static data. This saves ~104 bytes of stack and makes the const qualification consistent.
--- drivers/ata/sata_dwc_460ex.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/ata/sata_dwc_460ex.c +2 -2
@@ -1130,8 +1130,8 @@ static int sata_dwc_probe(struct platform_device *ofdev)int err =0 ;int irq;struct ata_host *host; -struct ata_port_info pi = sata_dwc_port_info[0 ]; -const struct ata_port_info *ppi[] = { &pi,NULL }; +const struct ata_port_info *pi = &sata_dwc_port_info[0 ]; +const struct ata_port_info *ppi[] = { pi,NULL };struct resource *res;/* Allocate DWC SATA device */
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCHv2 4/8] ata: sata_dwc_460ex: drop redundant struct copy of port_info
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Returning success (0) on ata_host_activate() failure causes a kernel panic on device removal.
- [High] Missing cleanup of DMA resources on probe error paths leads to Use-After-Free. --
commit 1231cd1ac0d6ac3ad6a4b61a9f9cf20df21198d9
Author: Rosen Penev <[email protected]>
Subject: ata: sata_dwc_460ex: drop redundant struct copy of port_info
This patch optimizes sata_dwc_probe() by dropping an unnecessary local copy of sata_dwc_port_info[0]. It directly points to the static data instead, saving stack space and correcting the const qualification.
Quoted 5 lines
drivers/ata/sata_dwc_460ex.c +0
-0
drivers/ata/sata_dwc_460ex.c static int sata_dwc_probe(struct platform_device *ofdev) · line 1141
Quoted 10 lines
drivers/ata/sata_dwc_460ex.c +2
-2@@ -1134,8 +1134,8 @@ static int sata_dwc_probe(struct platform_device *ofdev)int err =0 ;int irq;struct ata_host *host; -struct ata_port_info pi = sata_dwc_port_info[0 ]; -const struct ata_port_info *ppi[] = { &pi,NULL }; +const struct ata_port_info *pi = &sata_dwc_port_info[0 ]; +const struct ata_port_info *ppi[] = { pi,NULL };struct resource *res;/* Allocate DWC SATA device */
[Severity: High]
This is a pre-existing issue, but does sata_dwc_probe() correctly handle ata_host_activate() failures?
Looking further down in drivers/ata/sata_dwc_460ex.c:sata_dwc_probe(), the driver logs an error but returns 0 if activation fails:
err = ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht); if (err) dev_err(dev, "failed to activate host"); return 0;
If ata_host_activate() fails (for example, if devm_request_irq() or scsi_host_alloc() fails), returning 0 falsely reports successful initialization to the driver core.
When the device is later unbound or removed, sata_dwc_remove() is invoked and calls ata_host_detach() which calls ata_port_detach(). Since the scsi_host was never properly allocated or was freed, scsi_remove_host() will dereference a null pointer and cause a kernel panic.
Could the return statement be updated to return the error code instead?
[Severity: High]
This is a pre-existing issue, but does sata_dwc_probe() leak dma resources on error paths?
If CONFIG_SATA_DWC_OLD_DMA is enabled and no "dmas" property is present, sata_dwc_dma_init_old() is called:
err = sata_dwc_dma_init_old(ofdev, hsdev); if (err) return err;
This allocates a dma controller via devm_kzalloc, requests an interrupt, and registers it with the dma engine subsystem.
If a subsequent probe step fails, such as the phy retrieval:
if (IS_ERR(hsdev->phy)) return PTR_ERR(hsdev->phy);
or later in the main error path:
error_out:
phy_exit(hsdev->phy); return err;
The function returns without calling sata_dwc_dma_exit_old(). The driver core will free the devm-managed memory, including the dma controller structures. However, because explicit teardown was bypassed, the dma engine subsystem and the interrupt layer will retain pointers to this freed memory. This can lead to a use-after-free if the shared interrupt fires or the dma subsystem accesses the controller.
Should the error paths be updated to call sata_dwc_dma_exit_old() instead?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Re: [PATCHv2 4/8] ata: sata_dwc_460ex: drop redundant struct copy of port_info
Quoted 6 lines · On Wed, Jul 1, 2026 at 5:13 PM <[email protected]> wrote:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Returning success (0) onata_host_activate()failure causes a kernel panic on device removal.
- [High] Missing cleanup of DMA resources on probe error paths leads to Use-After-Free.
Both fixed later in the series. This thing is still broken when changing [PATCH] to something else.
Quoted 87 lines
--
commit
1231cd1ac0d6ac3ad6a4b61a9f9cf20df21198d9
Author: Rosen Penev <[email protected]>
Subject: ata:sata_dwc_460ex: drop redundant struct copy ofport_infoThis patch optimizes
sata_dwc_probe()by dropping an unnecessary local copy of sata_dwc_port_info[0]. It directly points to the static data instead, saving stack space and correcting the const qualification.Quoted 5 lines
drivers/ata/sata_dwc_460ex.c +0
-0drivers/ata/sata_dwc_460ex.c static int sata_dwc_probe(struct platform_device *ofdev) · line 1141
Quoted 10 lines
drivers/ata/sata_dwc_460ex.c +2
-2@@ -1134,8 +1134,8 @@ static int sata_dwc_probe(struct platform_device *ofdev)int err =0 ;int irq;struct ata_host *host; -struct ata_port_info pi = sata_dwc_port_info[0 ]; -const struct ata_port_info *ppi[] = { &pi,NULL }; +const struct ata_port_info *pi = &sata_dwc_port_info[0 ]; +const struct ata_port_info *ppi[] = { pi,NULL };struct resource *res;/* Allocate DWC SATA device */ [Severity: High]
This is a pre-existing issue, but doessata_dwc_probe()correctly handleata_host_activate()failures?Looking further down in drivers/ata/sata_dwc_460ex.c:sata_dwc_probe(), the driver logs an error but returns 0 if activation fails:
err = ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht); if (err) dev_err(dev, "failed to activate host"); return 0;If
ata_host_activate()fails (for example, ifdevm_request_irq()orscsi_host_alloc()fails), returning 0 falsely reports successful initialization to the driver core.When the device is later unbound or removed,
sata_dwc_remove()is invoked and callsata_host_detach()which callsata_port_detach(). Since thescsi_hostwas never properly allocated or was freed,scsi_remove_host()will dereference a null pointer and cause a kernel panic.Could the return statement be updated to return the error code instead?
[Severity: High]
This is a pre-existing issue, but doessata_dwc_probe()leak dma resources on error paths?If
CONFIG_SATA_DWC_OLD_DMAis enabled and no "dmas" property is present,sata_dwc_dma_init_old()is called:err = sata_dwc_dma_init_old(ofdev, hsdev); if (err) return err;This allocates a dma controller via
devm_kzalloc, requests an interrupt, and registers it with the dma engine subsystem.If a subsequent probe step fails, such as the phy retrieval:
if (IS_ERR(hsdev->phy)) return PTR_ERR(hsdev->phy);or later in the main error path:
error_out:phy_exit(hsdev->phy); return err;The function returns without calling
sata_dwc_dma_exit_old(). The driver core will free the devm-managed memory, including the dma controller structures. However, because explicit teardown was bypassed, the dma engine subsystem and the interrupt layer will retain pointers to this freed memory. This can lead to a use-after-free if the shared interrupt fires or the dma subsystem accesses the controller.Should the error paths be updated to call
sata_dwc_dma_exit_old()instead?--
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
PATCH v2 5/8 ata: sata_dwc_460ex: fix data race on hsdev->sactive_issued in interrupt handler
In reply to Rosen Penevhsdev->sactive_issued is written locklessly in sata_dwc_isr() before acquiring host->lock, while sata_dwc_qc_complete() performs a read-modify-write on the same field under the lock. This creates a data race that can corrupt NCQ tag tracking state.
Move the zero assignment inside the critical section so all accesses to sactive_issued are serialized by host->lock.
--- drivers/ata/sata_dwc_460ex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/ata/sata_dwc_460ex.c +1 -1
@@ -467,9 +467,9 @@ static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)int handled, port =0 ;uint intpr, sactive, sactive2, tag_mask;struct sata_dwc_device_port *hsdevp; - hsdev->sactive_issued =0 ;spin_lock_irqsave (&host->lock, flags); + hsdev->sactive_issued =0 ;/* Read the interrupt register */ intpr =sata_dwc_readl (&hsdev->sata_dwc_regs->intpr);
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCHv2 5/8] ata: sata_dwc_460ex: fix data race on hsdev->sactive_issued in interrupt handler
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [Critical] The NCQ tag completion logic contains an infinite loop due to a left shift instead of a right shift, which will cause a hard CPU hang inside a spinlock-protected hardirq context.
- [High] Unconditionally zeroing hsdev->sactive_issued on every interrupt destroys persistent NCQ state, breaking NCQ completion tracking.
- [High] The clear_interrupt_bit() function ignores the bit argument and inadvertently clears all pending hardware interrupts.
--
commit 2746dc992432ebc7a9253d28cee91b6dd821a51d
Author: Rosen Penev <[email protected]>
ata: sata_dwc_460ex: fix data race on hsdev->sactive_issued in interrupt handler
hsdev->sactive_issued is written locklessly in sata_dwc_isr() before acquiring host->lock, while sata_dwc_qc_complete() performs a read-modify-write on the same field under the lock. This creates a data race that can corrupt NCQ tag tracking state.
Move the zero assignment inside the critical section so all accesses to sactive_issued are serialized by host->lock.
drivers/ata/sata_dwc_460ex.c static irqreturn_t sata_dwc_isr(int irq, void *dev_instance) · line 475
Quoted 12 lines
drivers/ata/sata_dwc_460ex.c +1
-1@@ -470,9 +470,9 @@ static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)int handled, port =0 ;uint intpr, sactive, sactive2, tag_mask;struct sata_dwc_device_port *hsdevp; - hsdev->sactive_issued =0 ;spin_lock_irqsave (&host->lock, flags); + hsdev->sactive_issued =0 ;
[Severity: High]
This is a pre-existing issue, but does unconditionally zeroing hsdev->sactive_issued on every entry into sata_dwc_isr() destroy persistent NCQ state?
It looks like this breaks NCQ completion tracking by erasing tags set during SATA_DWC_INTPR_NEWFP handling.
Would this force a fallback to a non-standard NCQ completion path that handles commands individually?
drivers/ata/sata_dwc_460ex.c static irqreturn_t sata_dwc_isr(int irq, void *dev_instance) · line 475
Quoted 3 lines
drivers/ata/sata_dwc_460ex.c +0
-0/* Read the interrupt register */ intpr =sata_dwc_readl (&hsdev->sata_dwc_regs->intpr);
[Severity: Critical]
This is a pre-existing issue, but does the NCQ tag completion logic contain an infinite loop when processing completions?
Inside the NCQ completion block of sata_dwc_isr():
while (!(tag_mask & 0x00000001)) {
tag++;
tag_mask <<= 1;
}Because tag_mask is left-shifted, bit 0 will remain 0 and tag_mask eventually evaluates to 0. At that point, !(0 & 1) is always true.
Would this cause the loop to spin forever inside a spinlock-protected hardirq context?
[Severity: High]
This isn't a bug introduced by this patch, but does the clear_interrupt_bit() function ignore the bit argument and inadvertently clear all pending hardware interrupts?
drivers/ata/sata_dwc_460ex.c:clear_interrupt_bit()
sata_dwc_writel(&hsdev->sata_dwc_regs->intpr,
sata_dwc_readl(&hsdev->sata_dwc_regs->intpr));Since it reads the intpr register and writes the exact read value back, assuming a standard Write-1-to-Clear (W1C) register model, would this clear all pending interrupts?
Could hardware interrupts that occur simultaneously with NEWFP interrupts be permanently lost?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
PATCH v2 6/8 ata: sata_dwc_460ex: disable SATA interrupts on device removal
In reply to Rosen Penevsata_dwc_remove() fails to mask the controller's INTMR and ERRMR registers before tearing down the host. If the hardware asserts an interrupt during teardown (after ata_host_detach() but before the IRQ handler is unregistered by devres), this can trigger an unhandled interrupt storm and potentially lock up the system.
Add sata_dwc_disable_interrupts() and call it at the beginning of sata_dwc_remove(), before ata_host_detach().
--- drivers/ata/sata_dwc_460ex.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/ata/sata_dwc_460ex.c +8 -0
@@ -759,6 +759,12 @@ static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc)return 0 ; } +static void sata_dwc_disable_interrupts (struct sata_dwc_device *hsdev) +{ +sata_dwc_writel (&hsdev->sata_dwc_regs->intmr,0 ); +sata_dwc_writel (&hsdev->sata_dwc_regs->errmr,0 ); +} +static void sata_dwc_enable_interrupts (struct sata_dwc_device *hsdev) {/* Enable selective interrupts by setting the interrupt maskregister*/
@@ -1210,6 +1216,8 @@ static void sata_dwc_remove(struct platform_device *ofdev)struct ata_host *host =dev_get_drvdata (dev);struct sata_dwc_device *hsdev = host->private_data; +sata_dwc_disable_interrupts (hsdev); +ata_host_detach (host);phy_exit (hsdev->phy);
Signature · 1 line
2.55.0
[email protected]
AI reviewRe: [PATCHv2 6/8] ata: sata_dwc_460ex: disable SATA interrupts on device removal
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] Masking hardware interrupts before calling ata_host_detach() breaks the SCSI teardown sequence, leading to command timeouts, shutdown hangs, and potentially unwritten disk caches.
- [Medium] Missing read-back to flush posted writes when disabling hardware interrupts in sata_dwc_disable_interrupts().
Pre-existing issues:
- [High] sata_dwc_probe() incorrectly returns 0 (success) even if ata_host_activate() fails, leading to unhandled error paths and crashes on remove.
- [High] Masking interrupts without calling synchronize_irq() allows an in-flight interrupt handler to execute concurrently with hardware teardown. --
commit df81e2008992f864aa17e308c42b08931c2b48f5
Author: Rosen Penev <[email protected]>
ata: sata_dwc_460ex: disable SATA interrupts on device removal
This commit adds sata_dwc_disable_interrupts() and calls it at the beginning of sata_dwc_remove(). It masks the controller's INTMR and ERRMR registers to prevent unhandled interrupt storms during teardown.
drivers/ata/sata_dwc_460ex.c static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc) · line 768
Quoted 12 lines
drivers/ata/sata_dwc_460ex.c +4
-0@@ -762,6 +762,12 @@ static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc)return 0 ; } +static void sata_dwc_disable_interrupts (struct sata_dwc_device *hsdev) +{ +sata_dwc_writel (&hsdev->sata_dwc_regs->intmr,0 ); +sata_dwc_writel (&hsdev->sata_dwc_regs->errmr,0 );
[Severity: Medium]
Will these posted writes reach the hardware predictably before the CPU moves on to tear down the device?
Since sata_dwc_writel() wraps writel_relaxed(), writes to the memory-mapped registers over the system bus can be posted. Without a subsequent dummy read to a controller register to flush them, the interrupt mask configuration might be delayed in write buffers. This could allow an interrupt to fire during teardown, which is exactly what this change is intended to prevent.
Quoted 2 lines
drivers/ata/sata_dwc_460ex.c +2
-0+} +
[ ... ]
drivers/ata/sata_dwc_460ex.c static void sata_dwc_remove(struct platform_device *ofdev) · line 1227
Quoted 6 lines
drivers/ata/sata_dwc_460ex.c +2
-0@@ -1217,6 +1223,8 @@ static void sata_dwc_remove(struct platform_device *ofdev)struct ata_host *host =dev_get_drvdata (dev);struct sata_dwc_device *hsdev = host->private_data; +sata_dwc_disable_interrupts (hsdev); +
[Severity: High]
Does masking interrupts here break the SCSI teardown sequence?
When ata_host_detach() is called, it instructs the SCSI layer to issue cache flush and spindown commands. If hardware interrupts are already masked, won't these commands fail to complete and eventually time out? This could lead to a system hang during device removal and potential data loss if caches are not successfully flushed.
[Severity: High]
This isn't a bug introduced by this patch, but does the teardown sequence leave a race window with the still-registered ISR?
The interrupt handler is registered using devm_request_irq() and is only freed by devres after sata_dwc_remove() returns. While the new masking stops new interrupts, it does not wait for an already-running ISR on another CPU to finish. Since synchronize_irq() isn't called, could the ISR execute concurrently with ata_host_detach(), phy_exit(), and sata_dwc_dma_exit_old(), accessing freed memory or hardware in an invalid state?
[Severity: High]
This is a pre-existing issue, but can sata_dwc_remove() crash if the initial device probe failed?
Looking at sata_dwc_probe():
err = ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht);
if (err)
dev_err(dev, "failed to activate host");
return 0;If ata_host_activate() fails, probe still returns 0 (success). When the driver is later unbound, sata_dwc_remove() will unconditionally call ata_host_detach() and attempt to tear down host structures that were never successfully initialized, potentially causing a NULL pointer dereference or list corruption.
Quoted 1 line
drivers/ata/sata_dwc_460ex.c +0
-0ata_host_detach (host);
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
PATCH v2 7/8 ata: sata_dwc_460ex: fix PHY lifecycle ordering on device removal
In reply to Rosen Penevsata_dwc_remove() calls phy_exit() while phy_power_off() is still pending in sata_dwc_port_stop(), which runs later during device teardown. This violates the expected PHY sequencing of power_off before exit.
--- drivers/ata/sata_dwc_460ex.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-)
drivers/ata/sata_dwc_460ex.c +19 -13
@@ -864,10 +864,14 @@ static int sata_dwc_port_start(struct ata_port *ap)if (err) goto CLEANUP_ALLOC; - err =phy_power_on (hsdev->phy); + err =phy_init (hsdev->phy);if (err) goto CLEANUP_ALLOC; + err =phy_power_on (hsdev->phy); +if (err) + goto CLEANUP_PHY; +for (i =0 ; i <SATA_DWC_QCMD_MAX ; i++) hsdevp->cmd_issued[i] =SATA_DWC_CMD_ISSUED_NOT ;
@@ -893,6 +897,8 @@ static int sata_dwc_port_start(struct ata_port *ap)dev_dbg (ap->dev,"%s: done\n" , __func__);return 0 ; +CLEANUP_PHY: +phy_exit (hsdev->phy); CLEANUP_ALLOC:kfree (hsdevp); CLEANUP:
@@ -910,6 +916,7 @@ static void sata_dwc_port_stop(struct ata_port *ap)dmaengine_terminate_sync (hsdevp->chan);dma_release_channel (hsdevp->chan);phy_power_off (hsdev->phy); +phy_exit (hsdev->phy);kfree (hsdevp); ap->private_data =NULL ;
@@ -1176,6 +1183,10 @@ static int sata_dwc_probe(struct platform_device *ofdev)if (irq <0 )return irq; + hsdev->phy =devm_phy_optional_get (dev,"sata-phy" ); +if (IS_ERR (hsdev->phy)) +return PTR_ERR (hsdev->phy); +#ifdef CONFIG_SATA_DWC_OLD_DMA if (!device_property_present (dev,"dmas" )) { err =sata_dwc_dma_init_old (ofdev, hsdev);
@@ -1184,29 +1195,26 @@ static int sata_dwc_probe(struct platform_device *ofdev) }#endif - hsdev->phy =devm_phy_optional_get (dev,"sata-phy" ); -if (IS_ERR (hsdev->phy)) -return PTR_ERR (hsdev->phy); - - err =phy_init (hsdev->phy); -if (err) - goto error_out; -/* * Now, register with libATA core, this will also initiate the * device discovery process, invoking our port_start() handler & * error_handler() to execute a dummy Softreset EH session */ err =ata_host_activate (host, irq, sata_dwc_isr,0 , &sata_dwc_sht); -if (err) +if (err) {dev_err (dev,"failed to activate host" ); + goto error_out; + }/* Enable SATA Interrupts */ sata_dwc_enable_interrupts (hsdev);return 0 ; error_out: -phy_exit (hsdev->phy); +#ifdef CONFIG_SATA_DWC_OLD_DMA +if (!device_property_present (dev,"dmas" )) +sata_dwc_dma_exit_old (hsdev); +#endif return err; }
@@ -1220,8 +1228,6 @@ static void sata_dwc_remove(struct platform_device *ofdev)ata_host_detach (host); -phy_exit (hsdev->phy); -#ifdef CONFIG_SATA_DWC_OLD_DMA /* Free SATA DMA resources */ sata_dwc_dma_exit_old (hsdev);
Signature · 1 line
2.55.0
PATCH v2 8/8 ata: sata_dwc_460ex: use devm for old DMA resource lifetime management
In reply to Rosen PenevConvert sata_dwc_dma_exit_old() to a devm action callback and register it via devm_add_action_or_reset() after dw_dma_probe() succeeds. This lets the devm framework handle DMA controller teardown automatically on probe failure and driver removal.
As a result the probe function can use plain return-on-error for all failure paths - the dma_initialized flag, the goto labels, and the explicit sata_dwc_dma_exit_old() call in sata_dwc_remove() are all eliminated.
--- drivers/ata/sata_dwc_460ex.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-)
drivers/ata/sata_dwc_460ex.c +13 -21
@@ -222,10 +222,18 @@ static int sata_dwc_dma_get_channel_old(struct sata_dwc_device_port *hsdevp)return 0 ; } +static void sata_dwc_dma_exit_old (void *data) +{ +struct sata_dwc_device *hsdev = data; + +dw_dma_remove (hsdev->dma); +} +static int sata_dwc_dma_init_old (struct platform_device *pdev,struct sata_dwc_device *hsdev) {struct device *dev = &pdev->dev; +int err; hsdev->dma =devm_kzalloc (dev,sizeof (*hsdev->dma),GFP_KERNEL );if (!hsdev->dma)
@@ -245,15 +253,11 @@ static int sata_dwc_dma_init_old(struct platform_device *pdev,return PTR_ERR (hsdev->dma->regs);/* Initialize AHB DMAC */ - return dw_dma_probe(hsdev->dma); -} - -static void sata_dwc_dma_exit_old (struct sata_dwc_device *hsdev) -{ -if (!hsdev->dma) -return ; + err =dw_dma_probe (hsdev->dma); +if (err) +return err; -dw_dma_remove (hsdev->dma); + return devm_add_action_or_reset(dev, sata_dwc_dma_exit_old, hsdev); }#endif
@@ -1203,19 +1207,12 @@ static int sata_dwc_probe(struct platform_device *ofdev) err =ata_host_activate (host, irq, sata_dwc_isr,0 , &sata_dwc_sht);if (err) {dev_err (dev,"failed to activate host" ); - goto error_out; +return err; }/* Enable SATA Interrupts */ sata_dwc_enable_interrupts (hsdev);return 0 ; - -error_out: -#ifdef CONFIG_SATA_DWC_OLD_DMA -if (!device_property_present (dev,"dmas" )) -sata_dwc_dma_exit_old (hsdev); -#endif -return err; }static void sata_dwc_remove (struct platform_device *ofdev)
@@ -1228,11 +1225,6 @@ static void sata_dwc_remove(struct platform_device *ofdev)ata_host_detach (host); -#ifdef CONFIG_SATA_DWC_OLD_DMA -/* Free SATA DMA resources */ -sata_dwc_dma_exit_old (hsdev); -#endif -dev_dbg (dev,"done\n" ); }
Signature · 1 line
2.55.0
Damien Le Moal
maintainerQuoted 11 lines · Rosen Penev
This series modernizes the
sata_dwc_460exdriver by replacing legacy OF APIs with their modern device-property and platform-device counterparts, converting old DMA resource teardown to devm-managed cleanup, fixing a latent interrupt-ordering bug, and dropping a redundant stack copy ofport_info.Patch 4 fixes a real issue:
sata_dwc_enable_interrupts()was called before the IRQ handler was registered, so a probe failure or an asserted interrupt could trigger an unhandled irq storm.v2: sashiko fixes.
Several patches in this series are clearly bug fixes that should come first in the series and have a fixes tag and cc-stable. Can you please reorganize the series with the fixes first ?
Also please try to avoid growing the patch series with more patches in response to sachiko "not introduced by this patch" comments on valid problems. Send incremental patches instead. Otherwise, making progress will take a longer time, and bigger series are harder to review.
Quoted 19 lines
Rosen Penev (8):
ata:sata_dwc_460ex: usedevice_property_present()
ata:sata_dwc_460ex: useplatform_get_irq()
ata:sata_dwc_460ex: enable SATA interrupts only after IRQ handler is
registered
ata:sata_dwc_460ex: drop redundant struct copy ofport_info
ata:sata_dwc_460ex: fix data race onhsdev->sactive_issuedin
interrupt handler
ata:sata_dwc_460ex: disable SATA interrupts on device removal
ata:sata_dwc_460ex: fix PHY lifecycle ordering on device removal
ata:sata_dwc_460ex: use devm for old DMA resource lifetime managementdrivers/ata/sata_dwc_460ex.c | 99 ++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 50 deletions(-)--
2.55.0
Signature · 2 lines
Damien Le Moal Western Digital Research