ata: libata: Enable context analysis
Subsystems (4)
v5 cover ata: libata: Enable context analysis
Hi Niklas,
This patch series enables context analysis for the ATA subsystem. Please consider this patch series for the next merge window.
Thanks,
Bart.
Changes compared to v4:
- Added a patch that adds the ATA port argument to ata_eh_reset().
- Removed the lockdep_assert_held() statements.
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.
Bart Van Assche (2):
ata: libata: Add an argument to ata_eh_reset()
ata: libata: Document when host->eh_mutex should be held
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 | 11 ++++++++--- drivers/ata/libata-pmp.c | 5 ++++- drivers/ata/libata-sff.c | 2 ++ drivers/ata/libata.h | 14 +++++++++----- 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, 53 insertions(+), 15 deletions(-)
PATCH v5 1/2 ata: libata: Add an argument to ata_eh_reset()
Pass the ATA port pointer as first argument to ata_eh_reset(). No functionality has been changed. This patch prepares for enabling lock context analysis. Without this patch, lockdep_assert_held() statements would have to be added before each ata_eh_reset() call because the compiler doesn't know that ap->link.p == ap. See also ata_link_init().
--- drivers/ata/libata-eh.c | 6 +++--- drivers/ata/libata-pmp.c | 2 +- drivers/ata/libata.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-)
drivers/ata/libata-eh.c +3 -3
@@ -2831,10 +2831,9 @@ 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) { -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 ;
@@ -3882,7 +3881,8 @@ int ata_eh_recover(struct ata_port *ap, struct ata_reset_operations *reset_ops,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;
drivers/ata/libata-pmp.c +1 -1
@@ -763,7 +763,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;
drivers/ata/libata.h +2 -2
@@ -187,8 +187,8 @@ 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); +extern int ata_eh_reset (struct ata_port *ap,struct ata_link *link, +int classify,struct ata_reset_operations *reset_ops);extern int ata_eh_recover (struct ata_port *ap,struct ata_reset_operations *reset_ops,struct ata_link **r_failed_disk);
PATCH v5 2/2 ata: libata: Document when host->eh_mutex should be held
In reply to Bart Van AsscheAnnotate 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/
--- 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 | 5 +++++ drivers/ata/libata-pmp.c | 3 +++ 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, 48 insertions(+), 10 deletions(-)
drivers/ata/Makefile +2 -0
@@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 +CONTEXT_ANALYSIS := y + obj-$(CONFIG_ATA) += libata.o # non-SFF interface
drivers/ata/ahci.h +2 -1
@@ -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,
drivers/ata/ahci_imx.c +1 -0
@@ -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;
drivers/ata/libahci.c +1 -0
@@ -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;
drivers/ata/libata-core.c +2 -0
@@ -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 */ }
drivers/ata/libata-eh.c +5 -0
@@ -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_port *ap,struct ata_link *link,int classify,struct ata_reset_operations *reset_ops) + __must_hold(&ap->host->eh_mutex) {struct ata_link *slave = ap->slave_link;struct ata_eh_context *ehc = &link->eh_context;
@@ -3815,6 +3818,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;
@@ -4112,6 +4116,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;
drivers/ata/libata-pmp.c +3 -0
@@ -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;
@@ -907,6 +908,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 +1086,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);
drivers/ata/libata-sff.c +2 -0
@@ -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;
drivers/ata/libata.h +8 -4
@@ -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_port *ap, struct ata_link *link, -int classify,struct ata_reset_operations *reset_ops); + 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); + 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 *),
drivers/ata/pata_arasan_cf.c +1 -0
@@ -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;
drivers/ata/sata_dwc_460ex.c +1 -0
@@ -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); }
drivers/ata/sata_fsl.c +1 -0
@@ -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); }
drivers/ata/sata_inic162x.c +1 -0
@@ -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);
drivers/ata/sata_mv.c +1 -0
@@ -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;
drivers/ata/sata_nv.c +2 -0
@@ -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;
drivers/ata/sata_promise.c +1 -0
@@ -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);
drivers/ata/sata_qstor.c +1 -0
@@ -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);
drivers/ata/sata_sil24.c +1 -0
@@ -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;
drivers/ata/sata_sx4.c +1 -0
@@ -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);
drivers/ata/sata_via.c +1 -0
@@ -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);
include/linux/libata.h +10 -5
@@ -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);
Quoted 8 lines · Bart Van Assche
This patch series enables context analysis for the ATA subsystem. Please consider this patch series for the next merge window.
Thanks,
Bart.
[...]
Applied to libata/linux.git (for-7.2), thanks!
[1/2] ata: libata: Add an argument to ata_eh_reset()
https://git.kernel.org/libata/linux/c/ef174c36 [2/2] ata: libata: Document when host->eh_mutex should be held https://git.kernel.org/libata/linux/c/e6e1e1da
Kind regards,
Niklas