* [PATCH v4] ata: libata: Document when host->eh_mutex should be held
@ 2026-05-28 17:47 Bart Van Assche
2026-05-28 19:00 ` sashiko-bot
2026-05-28 20:26 ` Niklas Cassel
0 siblings, 2 replies; 3+ messages in thread
From: Bart Van Assche @ 2026-05-28 17:47 UTC (permalink / raw)
To: Niklas Cassel
Cc: linux-ide, Damien Le Moal, Marco Elver, Bart Van Assche,
Hannes Reinecke, Frank Li, Sascha Hauer, Viresh Kumar,
Mikael Pettersson, Nathan Chancellor
Annotate the following functions with __must_hold(&host->eh_mutex):
* All ata_port_operations.error_handler() implementations.
* ata_eh_reset() and ata_eh_recover() because these functions call
ata_eh_release() and ata_eh_acquire().
* All callers of ata_eh_reset() and ata_eh_recover().
Enable Clang's context analysis. This will cause the build to fail if
e.g. a locking bug would be introduced in an error path. This patch
should not affect the generated assembler code.
Note: although the Linux kernel documentation specifies 22 as minimal
version for Clang for context analysis support, annotating function
pointers is a Clang 23 feature. As one can see here, a patch has been
queued that fixes the kernel documentation:
https://lore.kernel.org/all/177926568868.711.3058599932884307249.tip-bot2@tip-bot2/
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
Changes compared to v3:
- Removed two source code comments.
- Annotated ata_dummy_error_handler().
- Addressed Sashiko's that the address of a synchronization object should be
passed to __acquires() and __releases().
Changes compared to v2:
- Instead of annotating only function declarations, annotate both function
declarations and function definitions.
- Changed __assume_ctx_lock() into lockdep_assert_held().
- Left out the host lock changes because there is no agreement about how to
annotate functions that expect that the host lock is held.
Changes compared to v1:
- Expanded a single patch into a series of nine patches.
- Included a bug fix for ata_exec_internal() and several refactoring patches.
- Added a patch for annotating the code that uses the host lock.
drivers/ata/Makefile | 2 ++
drivers/ata/ahci.h | 3 ++-
drivers/ata/ahci_imx.c | 1 +
drivers/ata/libahci.c | 1 +
drivers/ata/libata-core.c | 2 ++
drivers/ata/libata-eh.c | 7 +++++++
drivers/ata/libata-pmp.c | 5 +++++
drivers/ata/libata-sff.c | 2 ++
drivers/ata/libata.h | 12 ++++++++----
drivers/ata/pata_arasan_cf.c | 1 +
drivers/ata/sata_dwc_460ex.c | 1 +
drivers/ata/sata_fsl.c | 1 +
drivers/ata/sata_inic162x.c | 1 +
drivers/ata/sata_mv.c | 1 +
drivers/ata/sata_nv.c | 2 ++
drivers/ata/sata_promise.c | 1 +
drivers/ata/sata_qstor.c | 1 +
drivers/ata/sata_sil24.c | 1 +
drivers/ata/sata_sx4.c | 1 +
drivers/ata/sata_via.c | 1 +
include/linux/libata.h | 15 ++++++++++-----
21 files changed, 52 insertions(+), 10 deletions(-)
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 20e6645ab737..b96025abd45e 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -1,5 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
+CONTEXT_ANALYSIS := y
+
obj-$(CONFIG_ATA) += libata.o
# non-SFF interface
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 9e8b6319025c..b57cca352faa 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -448,7 +448,8 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
int ahci_reset_em(struct ata_host *host);
void ahci_print_info(struct ata_host *host, const char *scc_s);
int ahci_host_activate(struct ata_host *host, const struct scsi_host_template *sht);
-void ahci_error_handler(struct ata_port *ap);
+void ahci_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex);
u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked);
static inline void __iomem *__ahci_port_base(struct ahci_host_priv *hpriv,
diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c
index 3d26595524d3..6aaa18e29abc 100644
--- a/drivers/ata/ahci_imx.c
+++ b/drivers/ata/ahci_imx.c
@@ -598,6 +598,7 @@ static void imx_sata_disable(struct ahci_host_priv *hpriv)
}
static void ahci_imx_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
u32 reg_val;
struct ata_device *dev;
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index e0af4b5716b3..6d72eb017b49 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -2208,6 +2208,7 @@ static void ahci_thaw(struct ata_port *ap)
}
void ahci_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 3d25dec6ac6c..e9ebcfe20fc4 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -6832,6 +6832,7 @@ EXPORT_SYMBOL_GPL(ata_ratelimit);
* Might sleep.
*/
void ata_msleep(struct ata_port *ap, unsigned int msecs)
+ __context_unsafe(conditional locking)
{
bool owns_eh = ap && ap->host->eh_owner == current;
@@ -6906,6 +6907,7 @@ static unsigned int ata_dummy_qc_issue(struct ata_queued_cmd *qc)
}
static void ata_dummy_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
/* truly dummy */
}
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 6cb79a09423d..8b5fd2a48660 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -469,6 +469,7 @@ static void ata_eh_clear_action(struct ata_link *link, struct ata_device *dev,
* EH context.
*/
void ata_eh_acquire(struct ata_port *ap)
+ __acquires(&ap->host->eh_mutex)
{
mutex_lock(&ap->host->eh_mutex);
WARN_ON_ONCE(ap->host->eh_owner);
@@ -486,6 +487,7 @@ void ata_eh_acquire(struct ata_port *ap)
* EH context.
*/
void ata_eh_release(struct ata_port *ap)
+ __releases(&ap->host->eh_mutex)
{
WARN_ON_ONCE(ap->host->eh_owner != current);
ap->host->eh_owner = NULL;
@@ -2833,6 +2835,7 @@ static bool ata_eh_followup_srst_needed(struct ata_link *link, int rc)
int ata_eh_reset(struct ata_link *link, int classify,
struct ata_reset_operations *reset_ops)
+ __must_hold(&link->ap->host->eh_mutex)
{
struct ata_port *ap = link->ap;
struct ata_link *slave = ap->slave_link;
@@ -3816,6 +3819,7 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err)
*/
int ata_eh_recover(struct ata_port *ap, struct ata_reset_operations *reset_ops,
struct ata_link **r_failed_link)
+ __must_hold(&ap->host->eh_mutex)
{
struct ata_link *link;
struct ata_device *dev;
@@ -3879,6 +3883,8 @@ int ata_eh_recover(struct ata_port *ap, struct ata_reset_operations *reset_ops,
ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
+ lockdep_assert_held(&link->ap->host->eh_mutex);
+
if (!(ehc->i.action & ATA_EH_RESET))
continue;
@@ -4112,6 +4118,7 @@ void ata_eh_finish(struct ata_port *ap)
* Kernel thread context (may sleep).
*/
void ata_std_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
struct ata_reset_operations *reset_ops = &ap->ops->reset;
struct ata_link *link = &ap->link;
diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c
index e3adc008fed1..bf40633f8fb2 100644
--- a/drivers/ata/libata-pmp.c
+++ b/drivers/ata/libata-pmp.c
@@ -742,6 +742,7 @@ static int sata_pmp_revalidate_quick(struct ata_device *dev)
*/
static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
struct ata_reset_operations *reset_ops)
+ __must_hold(&ap->host->eh_mutex)
{
struct ata_link *link = &ap->link;
struct ata_eh_context *ehc = &link->eh_context;
@@ -750,6 +751,8 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
int detach = 0, rc = 0;
int reval_failed = 0;
+ lockdep_assert_held(&ap->link.ap->host->eh_mutex);
+
if (dev->flags & ATA_DFLAG_DETACH) {
detach = 1;
rc = -ENODEV;
@@ -907,6 +910,7 @@ static int sata_pmp_handle_link_fail(struct ata_link *link, int *link_tries)
* 0 on success, -errno on failure.
*/
static int sata_pmp_eh_recover(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
struct ata_port_operations *ops = ap->ops;
int pmp_tries, link_tries[SATA_PMP_MAX_PORTS];
@@ -1084,6 +1088,7 @@ static int sata_pmp_eh_recover(struct ata_port *ap)
* Kernel thread context (may sleep).
*/
void sata_pmp_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
ata_eh_autopsy(ap);
ata_eh_report(ap);
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 785b6e371abf..734e7c88439a 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -2053,6 +2053,7 @@ EXPORT_SYMBOL_GPL(ata_sff_drain_fifo);
* Kernel thread context (may sleep)
*/
void ata_sff_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
struct ata_queued_cmd *qc;
unsigned long flags;
@@ -2769,6 +2770,7 @@ EXPORT_SYMBOL_GPL(ata_bmdma_interrupt);
* Kernel thread context (may sleep)
*/
void ata_bmdma_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
struct ata_queued_cmd *qc;
unsigned long flags;
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index b5423b6e97de..501a7362602a 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -173,8 +173,10 @@ void ata_scsi_requeue_deferred_qc(struct ata_port *ap);
/* libata-eh.c */
extern unsigned int ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd);
extern void ata_internal_cmd_timed_out(struct ata_device *dev, u8 cmd);
-extern void ata_eh_acquire(struct ata_port *ap);
-extern void ata_eh_release(struct ata_port *ap);
+extern void ata_eh_acquire(struct ata_port *ap)
+ __acquires(&ap->host->eh_mutex);
+extern void ata_eh_release(struct ata_port *ap)
+ __releases(&ap->host->eh_mutex);
extern void ata_scsi_error(struct Scsi_Host *host);
extern void ata_eh_fastdrain_timerfn(struct timer_list *t);
extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
@@ -188,10 +190,12 @@ extern void ata_eh_autopsy(struct ata_port *ap);
const char *ata_get_cmd_name(u8 command);
extern void ata_eh_report(struct ata_port *ap);
extern int ata_eh_reset(struct ata_link *link, int classify,
- struct ata_reset_operations *reset_ops);
+ struct ata_reset_operations *reset_ops)
+ __must_hold(&link->ap->host->eh_mutex);
extern int ata_eh_recover(struct ata_port *ap,
struct ata_reset_operations *reset_ops,
- struct ata_link **r_failed_disk);
+ struct ata_link **r_failed_disk)
+ __must_hold(&ap->host->eh_mutex);
extern void ata_eh_finish(struct ata_port *ap);
extern int ata_ering_map(struct ata_ering *ering,
int (*map_fn)(struct ata_ering_entry *, void *),
diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c
index a77fefd320c2..e05a4847e037 100644
--- a/drivers/ata/pata_arasan_cf.c
+++ b/drivers/ata/pata_arasan_cf.c
@@ -658,6 +658,7 @@ static void arasan_cf_freeze(struct ata_port *ap)
}
static void arasan_cf_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
struct arasan_cf_dev *acdev = ap->host->private_data;
diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c
index 64cb544903d8..4fc22ce4bd9a 100644
--- a/drivers/ata/sata_dwc_460ex.c
+++ b/drivers/ata/sata_dwc_460ex.c
@@ -1041,6 +1041,7 @@ static unsigned int sata_dwc_qc_issue(struct ata_queued_cmd *qc)
}
static void sata_dwc_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
ata_sff_error_handler(ap);
}
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index be829fcc584d..70b210afd291 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -1035,6 +1035,7 @@ static int sata_fsl_softreset(struct ata_link *link, unsigned int *class,
}
static void sata_fsl_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
sata_pmp_error_handler(ap);
}
diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c
index 46a8c20daf18..065408975f5b 100644
--- a/drivers/ata/sata_inic162x.c
+++ b/drivers/ata/sata_inic162x.c
@@ -660,6 +660,7 @@ static int inic_hardreset(struct ata_link *link, unsigned int *class,
}
static void inic_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
void __iomem *port_base = inic_port_base(ap);
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index ffb396f61731..f6942fd80bae 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -2398,6 +2398,7 @@ static struct ata_queued_cmd *mv_get_active_qc(struct ata_port *ap)
}
static void mv_pmp_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
unsigned int pmp, pmp_map;
struct mv_port_priv *pp = ap->private_data;
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 841e7de2bba6..19b927065868 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -1626,6 +1626,7 @@ static void nv_mcp55_thaw(struct ata_port *ap)
}
static void nv_adma_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
struct nv_adma_port_priv *pp = ap->private_data;
if (!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) {
@@ -1795,6 +1796,7 @@ static void nv_swncq_ncq_stop(struct ata_port *ap)
}
static void nv_swncq_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
struct ata_eh_context *ehc = &ap->link.eh_context;
diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c
index 2a005aede123..c5fad448bafc 100644
--- a/drivers/ata/sata_promise.c
+++ b/drivers/ata/sata_promise.c
@@ -816,6 +816,7 @@ static int pdc_sata_hardreset(struct ata_link *link, unsigned int *class,
}
static void pdc_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
if (!ata_port_is_frozen(ap))
pdc_reset_port(ap);
diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c
index cfb9b5b61cd7..1add7a7d5ff6 100644
--- a/drivers/ata/sata_qstor.c
+++ b/drivers/ata/sata_qstor.c
@@ -220,6 +220,7 @@ static int qs_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val)
}
static void qs_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
qs_enter_reg_mode(ap);
ata_sff_error_handler(ap);
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index d642ece9f07a..89a10704e98e 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -1167,6 +1167,7 @@ static irqreturn_t sil24_interrupt(int irq, void *dev_instance)
}
static void sil24_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
struct sil24_port_priv *pp = ap->private_data;
diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c
index 0986ebd1eb4e..227dc1d4e85b 100644
--- a/drivers/ata/sata_sx4.c
+++ b/drivers/ata/sata_sx4.c
@@ -853,6 +853,7 @@ static int pdc_softreset(struct ata_link *link, unsigned int *class,
}
static void pdc_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
if (!ata_port_is_frozen(ap))
pdc_reset_port(ap);
diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c
index 68e9003ec2d4..b672a1e05867 100644
--- a/drivers/ata/sata_via.c
+++ b/drivers/ata/sata_via.c
@@ -573,6 +573,7 @@ static irqreturn_t vt642x_interrupt(int irq, void *dev_instance)
}
static void vt6421_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex)
{
struct svia_priv *hpriv = ap->host->private_data;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 5c085ef4eda7..5f6bbe5d504f 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -983,7 +983,8 @@ struct ata_port_operations {
void (*thaw)(struct ata_port *ap);
struct ata_reset_operations reset;
struct ata_reset_operations pmp_reset;
- void (*error_handler)(struct ata_port *ap);
+ void (*error_handler)(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex);
void (*lost_interrupt)(struct ata_port *ap);
void (*post_internal_cmd)(struct ata_queued_cmd *qc);
void (*sched_eh)(struct ata_port *ap);
@@ -1418,7 +1419,8 @@ extern void ata_eh_thaw_port(struct ata_port *ap);
extern void ata_eh_qc_complete(struct ata_queued_cmd *qc);
extern void ata_eh_qc_retry(struct ata_queued_cmd *qc);
-extern void ata_std_error_handler(struct ata_port *ap);
+extern void ata_std_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex);
extern void ata_std_sched_eh(struct ata_port *ap);
extern void ata_std_end_eh(struct ata_port *ap);
extern int ata_link_nr_enabled(struct ata_link *link);
@@ -1998,7 +2000,8 @@ extern void ata_timing_merge(const struct ata_timing *,
extern const struct ata_port_operations sata_pmp_port_ops;
extern int sata_pmp_qc_defer_cmd_switch(struct ata_queued_cmd *qc);
-extern void sata_pmp_error_handler(struct ata_port *ap);
+extern void sata_pmp_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex);
#else /* CONFIG_SATA_PMP */
@@ -2062,7 +2065,8 @@ extern int sata_sff_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
extern void ata_sff_postreset(struct ata_link *link, unsigned int *classes);
extern void ata_sff_drain_fifo(struct ata_queued_cmd *qc);
-extern void ata_sff_error_handler(struct ata_port *ap);
+extern void ata_sff_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex);
extern void ata_sff_std_ports(struct ata_ioports *ioaddr);
#ifdef CONFIG_PCI
extern int ata_pci_sff_init_host(struct ata_host *host);
@@ -2092,7 +2096,8 @@ extern enum ata_completion_errors ata_bmdma_dumb_qc_prep(struct ata_queued_cmd *
extern unsigned int ata_bmdma_port_intr(struct ata_port *ap,
struct ata_queued_cmd *qc);
extern irqreturn_t ata_bmdma_interrupt(int irq, void *dev_instance);
-extern void ata_bmdma_error_handler(struct ata_port *ap);
+extern void ata_bmdma_error_handler(struct ata_port *ap)
+ __must_hold(&ap->host->eh_mutex);
extern void ata_bmdma_post_internal_cmd(struct ata_queued_cmd *qc);
extern void ata_bmdma_irq_clear(struct ata_port *ap);
extern void ata_bmdma_setup(struct ata_queued_cmd *qc);
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH v4] ata: libata: Document when host->eh_mutex should be held
2026-05-28 17:47 [PATCH v4] ata: libata: Document when host->eh_mutex should be held Bart Van Assche
@ 2026-05-28 19:00 ` sashiko-bot
2026-05-28 20:26 ` Niklas Cassel
1 sibling, 0 replies; 3+ messages in thread
From: sashiko-bot @ 2026-05-28 19:00 UTC (permalink / raw)
To: Bart Van Assche; +Cc: linux-ide, dlemoal, cassel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Low] Missing context analysis annotation on ata_exec_internal() causing a build failure.
- [Low] Convoluted and redundant pointer traversal in lockdep_assert_held().
Pre-existing issues:
- [High] Read-modify-write race on pp->pp_flags in mv_pmp_error_handler().
--
commit d2826b7a41176ba50abfe15ca5e18e09e68b29d4
Author: Bart Van Assche <bvanassche@acm.org>
ata: libata: Document when host->eh_mutex should be held
Annotate error handler implementations and helpers with __must_hold,
__acquires, and __releases for host->eh_mutex. Enable Clang's context analysis
to prevent locking bugs in error paths.
Link: https://lore.kernel.org/all/177926568868.711.3058599932884307249.tip-bot2@tip-bot2/
> diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
> index 20e6645ab7371..b96025abd45e3 100644
> --- a/drivers/ata/Makefile
> +++ b/drivers/ata/Makefile
> @@ -1,5 +1,7 @@
> # SPDX-License-Identifier: GPL-2.0
>
> +CONTEXT_ANALYSIS := y
> +
[Severity: Low]
Does enabling context analysis here cause a build failure?
ata_exec_internal() in drivers/ata/libata-core.c unconditionally calls
ata_eh_release() and ata_eh_acquire() without being annotated. Since Clang's
analysis assumes no locks are held at the start of an unannotated function,
calling ata_eh_release() triggers a compiler error for releasing a mutex that
was not held.
Could ata_exec_internal() be annotated to fix this?
[ ... ]
> diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c
> index e3adc008fed1e..bf40633f8fb23 100644
> --- a/drivers/ata/libata-pmp.c
> +++ b/drivers/ata/libata-pmp.c
> @@ -750,6 +751,8 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
> int detach = 0, rc = 0;
> int reval_failed = 0;
>
> + lockdep_assert_held(&ap->link.ap->host->eh_mutex);
[Severity: Low]
Could this pointer traversal be simplified?
Since the variable ap is already a pointer to struct ata_port, traversing
through its embedded link struct to resolve back to the exact same ap pointer
seems redundant.
Could this be simplified to lockdep_assert_held(&ap->host->eh_mutex)?
[ ... ]
> diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
> index ffb396f61731f..f6942fd80bae7 100644
> --- a/drivers/ata/sata_mv.c
> +++ b/drivers/ata/sata_mv.c
> @@ -2398,6 +2398,7 @@ static struct ata_queued_cmd *mv_get_active_qc(struct ata_port *ap)
> }
>
> static void mv_pmp_error_handler(struct ata_port *ap)
> + __must_hold(&ap->host->eh_mutex)
> {
> unsigned int pmp, pmp_map;
> struct mv_port_priv *pp = ap->private_data;
[Severity: High]
This is a pre-existing issue, but does this function have a read-modify-write
race on pp->pp_flags?
This function runs in process context (EH thread) and modifies pp->pp_flags
without holding ap->lock:
if (pp->pp_flags & MV_PP_FLAG_DELAYED_EH) {
pmp_map = pp->delayed_eh_pmp_map;
pp->pp_flags &= ~MV_PP_FLAG_DELAYED_EH;
Concurrently, the hardware interrupt handler (mv_interrupt) can execute,
acquire ap->lock, and modify pp->pp_flags (e.g., setting
MV_PP_FLAG_FAKE_ATA_BUSY).
If the interrupt fires between the read and the write of pp->pp_flags in
mv_pmp_error_handler(), will the interrupt's modifications be silently
overwritten and lost?
--
Sashiko AI review · https://sashiko.dev/#/patchset/230fb523796cd9c1643c5673bddb45fc48eb9514.1779990228.git.bvanassche@acm.org?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH v4] ata: libata: Document when host->eh_mutex should be held
2026-05-28 17:47 [PATCH v4] ata: libata: Document when host->eh_mutex should be held Bart Van Assche
2026-05-28 19:00 ` sashiko-bot
@ 2026-05-28 20:26 ` Niklas Cassel
1 sibling, 0 replies; 3+ messages in thread
From: Niklas Cassel @ 2026-05-28 20:26 UTC (permalink / raw)
To: Bart Van Assche
Cc: linux-ide, Damien Le Moal, Marco Elver, Hannes Reinecke, Frank Li,
Sascha Hauer, Viresh Kumar, Mikael Pettersson, Nathan Chancellor
On Thu, May 28, 2026 at 10:47:26AM -0700, Bart Van Assche wrote:
> Annotate the following functions with __must_hold(&host->eh_mutex):
> * All ata_port_operations.error_handler() implementations.
> * ata_eh_reset() and ata_eh_recover() because these functions call
> ata_eh_release() and ata_eh_acquire().
> * All callers of ata_eh_reset() and ata_eh_recover().
>
> Enable Clang's context analysis. This will cause the build to fail if
> e.g. a locking bug would be introduced in an error path. This patch
> should not affect the generated assembler code.
>
> Note: although the Linux kernel documentation specifies 22 as minimal
> version for Clang for context analysis support, annotating function
> pointers is a Clang 23 feature. As one can see here, a patch has been
> queued that fixes the kernel documentation:
> https://lore.kernel.org/all/177926568868.711.3058599932884307249.tip-bot2@tip-bot2/
>
> Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
> Reviewed-by: Hannes Reinecke <hare@kernel.org>
> Signed-off-by: Bart Van Assche <bvanassche@acm.org>
> ---
After spending the whole day playing with Clang 23, personally I do prefer
growing an ap parameter in ata_eh_reset() (in a patch 1/2), because it will
avoid us having to add two lockdep_assert_held() (which we know, from context
analysis PoV, just fools the compiler to say that we have the lock, without
actually verifying anything. (Yes, when running lockdep in runtime, it
would be verified.)
But at least with an ap parameter to ata_eh_reset(), context analysis will
actually work, and will allow us to verify things already in compile time.
No lockdep needed. Don't get me wrong, lockdep is awesome, but if we can
verify things already in compile time in an easy way, that seems way better
IMO. Let's leave the runtime checking for things that we can't trivially
verify during compile time.
I.e. the diff would be something like this compared to your V4:
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 8b5fd2a48660..a66663d3579e 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -2833,11 +2833,10 @@ static bool ata_eh_followup_srst_needed(struct ata_link *link, int rc)
return false;
}
-int ata_eh_reset(struct ata_link *link, int classify,
+int ata_eh_reset(struct ata_port *ap, struct ata_link *link, int classify,
struct ata_reset_operations *reset_ops)
- __must_hold(&link->ap->host->eh_mutex)
+ __must_hold(&ap->host->eh_mutex)
{
- struct ata_port *ap = link->ap;
struct ata_link *slave = ap->slave_link;
struct ata_eh_context *ehc = &link->eh_context;
struct ata_eh_context *sehc = slave ? &slave->eh_context : NULL;
@@ -3883,12 +3882,11 @@ int ata_eh_recover(struct ata_port *ap, struct ata_reset_operations *reset_ops,
ata_for_each_link(link, ap, EDGE) {
struct ata_eh_context *ehc = &link->eh_context;
- lockdep_assert_held(&link->ap->host->eh_mutex);
-
if (!(ehc->i.action & ATA_EH_RESET))
continue;
- rc = ata_eh_reset(link, ata_link_nr_vacant(link), reset_ops);
+ rc = ata_eh_reset(ap, link, ata_link_nr_vacant(link),
+ reset_ops);
if (rc) {
ata_link_err(link, "reset failed, giving up\n");
goto out;
diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c
index bf40633f8fb2..188fc12c2704 100644
--- a/drivers/ata/libata-pmp.c
+++ b/drivers/ata/libata-pmp.c
@@ -751,8 +751,6 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
int detach = 0, rc = 0;
int reval_failed = 0;
- lockdep_assert_held(&ap->link.ap->host->eh_mutex);
-
if (dev->flags & ATA_DFLAG_DETACH) {
detach = 1;
rc = -ENODEV;
@@ -766,7 +764,7 @@ static int sata_pmp_eh_recover_pmp(struct ata_port *ap,
struct ata_link *tlink;
/* reset */
- rc = ata_eh_reset(link, 0, reset_ops);
+ rc = ata_eh_reset(ap, link, 0, reset_ops);
if (rc) {
ata_link_err(link, "failed to reset PMP, giving up\n");
goto fail;
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 9e3817a1b143..0dd735c2e5b5 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -192,9 +192,9 @@ extern void ata_eh_done(struct ata_link *link, struct ata_device *dev,
extern void ata_eh_autopsy(struct ata_port *ap);
const char *ata_get_cmd_name(u8 command);
extern void ata_eh_report(struct ata_port *ap);
-extern int ata_eh_reset(struct ata_link *link, int classify,
- struct ata_reset_operations *reset_ops)
- __must_hold(&link->ap->host->eh_mutex);
+extern int ata_eh_reset(struct ata_port *ap, struct ata_link *link,
+ int classify, struct ata_reset_operations *reset_ops)
+ __must_hold(&ap->host->eh_mutex);
extern int ata_eh_recover(struct ata_port *ap,
struct ata_reset_operations *reset_ops,
struct ata_link **r_failed_disk)
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-28 20:26 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-28 17:47 [PATCH v4] ata: libata: Document when host->eh_mutex should be held Bart Van Assche
2026-05-28 19:00 ` sashiko-bot
2026-05-28 20:26 ` Niklas Cassel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox