From 6158e34e594dd150f3f2df81a1b0fe636e156757 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 19 Feb 2026 08:11:35 +0900 Subject: [PATCH 01/18] ata: libata-core: improve tag checks in ata_qc_issue() Make sure to check that the tag of a queued command is valid when ata_qc_issue() is called, and fail the QC if the tag is not valid, or if there is an on-going non-NCQ command already on the link. Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke --- drivers/ata/libata-core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 11909417f017..d61846f03edc 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5146,8 +5146,13 @@ void ata_qc_issue(struct ata_queued_cmd *qc) struct ata_link *link = qc->dev->link; u8 prot = qc->tf.protocol; - /* Make sure only one non-NCQ command is outstanding. */ - WARN_ON_ONCE(ata_tag_valid(link->active_tag)); + /* + * Make sure we have a valid tag and that only one non-NCQ command is + * outstanding. + */ + if (WARN_ON_ONCE(!ata_tag_valid(qc->tag)) || + WARN_ON_ONCE(ata_tag_valid(link->active_tag))) + goto sys_err; if (ata_is_ncq(prot)) { WARN_ON_ONCE(link->sactive & (1 << qc->hw_tag)); From a21b4040b3886555cba5e72ef475a556ad04700d Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 19 Feb 2026 08:18:52 +0900 Subject: [PATCH 02/18] ata: libata-sata: simplify ata_sas_queuecmd() Change ata_sas_queuecmd() to return early the result of __ata_scsi_queuecmd() and remove the rc local variable. This simplifies the code without any functional change. Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke --- drivers/ata/libata-sata.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index b9d635088f5f..2ee54d60ea4b 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -1378,15 +1378,13 @@ EXPORT_SYMBOL_GPL(ata_sas_sdev_configure); int ata_sas_queuecmd(struct scsi_cmnd *cmd, struct ata_port *ap) { - int rc = 0; - if (likely(ata_dev_enabled(ap->link.device))) - rc = __ata_scsi_queuecmd(cmd, ap->link.device); - else { - cmd->result = (DID_BAD_TARGET << 16); - scsi_done(cmd); - } - return rc; + return __ata_scsi_queuecmd(cmd, ap->link.device); + + cmd->result = (DID_BAD_TARGET << 16); + scsi_done(cmd); + + return 0; } EXPORT_SYMBOL_GPL(ata_sas_queuecmd); From 9a5eb2adb1ec90f854d9b45c75f0fcb3ae981356 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 19 Feb 2026 08:49:17 +0900 Subject: [PATCH 03/18] ata: libata-scsi: simplify ata_scsi_requeue_deferred_qc() In ata_scsi_requeue_deferred_qc(), use ata_qc_done() instead of calling ata_qc_free() and scsi_done() directly. Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index c0dd75a0287c..41918e21d0f8 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1685,7 +1685,6 @@ void ata_scsi_deferred_qc_work(struct work_struct *work) void ata_scsi_requeue_deferred_qc(struct ata_port *ap) { struct ata_queued_cmd *qc = ap->deferred_qc; - struct scsi_cmnd *scmd; lockdep_assert_held(ap->lock); @@ -1697,11 +1696,9 @@ void ata_scsi_requeue_deferred_qc(struct ata_port *ap) if (!qc) return; - scmd = qc->scsicmd; ap->deferred_qc = NULL; - ata_qc_free(qc); - scmd->result = (DID_SOFT_ERROR << 16); - scsi_done(scmd); + qc->scsicmd->result = (DID_SOFT_ERROR << 16); + ata_qc_done(qc); } static void ata_scsi_schedule_deferred_qc(struct ata_port *ap) From fa4f81a8c15d4018eb2053b093bf1584777e80d4 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 19 Feb 2026 10:47:03 +0900 Subject: [PATCH 04/18] ata: libata-scsi: make ata_scsi_simulate() static ata_scsi_simulate() is called only from libata-scsi.c. Move this function definition as a static function before its call in __ata_scsi_queuecmd() and remove its declaration from include/linux/libata.h. No functional changes. Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 147 +++++++++++++++++++------------------- include/linux/libata.h | 1 - 2 files changed, 73 insertions(+), 75 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 41918e21d0f8..ad628b398fc3 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -4420,6 +4420,79 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) return NULL; } +/** + * ata_scsi_simulate - simulate SCSI command on ATA device + * @dev: the target device + * @cmd: SCSI command being sent to device. + * + * Interprets and directly executes a select list of SCSI commands + * that can be handled internally. + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +static void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) +{ + const u8 *scsicmd = cmd->cmnd; + u8 tmp8; + + switch (scsicmd[0]) { + case INQUIRY: + ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_inquiry); + break; + + case MODE_SENSE: + case MODE_SENSE_10: + ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_mode_sense); + break; + + case READ_CAPACITY: + case SERVICE_ACTION_IN_16: + ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_read_cap); + break; + + case REPORT_LUNS: + ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_report_luns); + break; + + case REQUEST_SENSE: + ata_scsi_set_sense(dev, cmd, 0, 0, 0); + break; + + /* if we reach this, then writeback caching is disabled, + * turning this into a no-op. + */ + case SYNCHRONIZE_CACHE: + case SYNCHRONIZE_CACHE_16: + fallthrough; + + /* no-op's, complete with success */ + case REZERO_UNIT: + case SEEK_6: + case SEEK_10: + case TEST_UNIT_READY: + break; + + case SEND_DIAGNOSTIC: + tmp8 = scsicmd[1] & ~(1 << 3); + if (tmp8 != 0x4 || scsicmd[3] || scsicmd[4]) + ata_scsi_set_invalid_field(dev, cmd, 1, 0xff); + break; + + case MAINTENANCE_IN: + ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_maint_in); + break; + + /* all other commands */ + default: + ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x20, 0x0); + /* "Invalid command operation code" */ + break; + } + + scsi_done(cmd); +} + enum scsi_qc_status __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_device *dev) { @@ -4522,80 +4595,6 @@ enum scsi_qc_status ata_scsi_queuecmd(struct Scsi_Host *shost, } EXPORT_SYMBOL_GPL(ata_scsi_queuecmd); -/** - * ata_scsi_simulate - simulate SCSI command on ATA device - * @dev: the target device - * @cmd: SCSI command being sent to device. - * - * Interprets and directly executes a select list of SCSI commands - * that can be handled internally. - * - * LOCKING: - * spin_lock_irqsave(host lock) - */ - -void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) -{ - const u8 *scsicmd = cmd->cmnd; - u8 tmp8; - - switch(scsicmd[0]) { - case INQUIRY: - ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_inquiry); - break; - - case MODE_SENSE: - case MODE_SENSE_10: - ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_mode_sense); - break; - - case READ_CAPACITY: - case SERVICE_ACTION_IN_16: - ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_read_cap); - break; - - case REPORT_LUNS: - ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_report_luns); - break; - - case REQUEST_SENSE: - ata_scsi_set_sense(dev, cmd, 0, 0, 0); - break; - - /* if we reach this, then writeback caching is disabled, - * turning this into a no-op. - */ - case SYNCHRONIZE_CACHE: - case SYNCHRONIZE_CACHE_16: - fallthrough; - - /* no-op's, complete with success */ - case REZERO_UNIT: - case SEEK_6: - case SEEK_10: - case TEST_UNIT_READY: - break; - - case SEND_DIAGNOSTIC: - tmp8 = scsicmd[1] & ~(1 << 3); - if (tmp8 != 0x4 || scsicmd[3] || scsicmd[4]) - ata_scsi_set_invalid_field(dev, cmd, 1, 0xff); - break; - - case MAINTENANCE_IN: - ata_scsi_rbuf_fill(dev, cmd, ata_scsiop_maint_in); - break; - - /* all other commands */ - default: - ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x20, 0x0); - /* "Invalid command operation code" */ - break; - } - - scsi_done(cmd); -} - int ata_scsi_add_hosts(struct ata_host *host, const struct scsi_host_template *sht) { int i, rc; diff --git a/include/linux/libata.h b/include/linux/libata.h index 00346ce3af5e..db87c99e4189 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1205,7 +1205,6 @@ extern unsigned int ata_do_dev_read_id(struct ata_device *dev, struct ata_taskfile *tf, __le16 *id); extern void ata_qc_complete(struct ata_queued_cmd *qc); extern u64 ata_qc_get_active(struct ata_port *ap); -extern void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd); extern int ata_std_bios_param(struct scsi_device *sdev, struct gendisk *unused, sector_t capacity, int geom[]); From 99d6b9014bbd128dc73b7097e78dab1f1f70aab0 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 19 Feb 2026 10:58:40 +0900 Subject: [PATCH 05/18] ata: libata-scsi: rename and improve ata_qc_done() Rename ata_qc_done() to ata_scsi_qc_done() and allow to pass a scsi command result value to set for the completed command to simplify the caller sites. No functional changes. Signed-off-by: Damien Le Moal Reviewed-by: Hannes Reinecke --- drivers/ata/libata-scsi.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index ad628b398fc3..4225c6d7ff35 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1649,12 +1649,16 @@ nothing_to_do: return 1; } -static void ata_qc_done(struct ata_queued_cmd *qc) +static void ata_scsi_qc_done(struct ata_queued_cmd *qc, bool set_result, + u32 scmd_result) { struct scsi_cmnd *cmd = qc->scsicmd; void (*done)(struct scsi_cmnd *) = qc->scsidone; ata_qc_free(qc); + + if (set_result) + cmd->result = scmd_result; done(cmd); } @@ -1693,12 +1697,10 @@ void ata_scsi_requeue_deferred_qc(struct ata_port *ap) * do not try to be smart about what to do with this deferred command * and simply retry it by completing it with DID_SOFT_ERROR. */ - if (!qc) - return; - - ap->deferred_qc = NULL; - qc->scsicmd->result = (DID_SOFT_ERROR << 16); - ata_qc_done(qc); + if (qc) { + ap->deferred_qc = NULL; + ata_scsi_qc_done(qc, true, DID_SOFT_ERROR << 16); + } } static void ata_scsi_schedule_deferred_qc(struct ata_port *ap) @@ -1754,7 +1756,7 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) ata_scsi_set_sense_information(qc); } - ata_qc_done(qc); + ata_scsi_qc_done(qc, false, 0); ata_scsi_schedule_deferred_qc(ap); } @@ -2913,17 +2915,15 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) if (qc->cdb[0] == ALLOW_MEDIUM_REMOVAL && qc->dev->sdev) qc->dev->sdev->locked = 0; - qc->scsicmd->result = SAM_STAT_CHECK_CONDITION; - ata_qc_done(qc); + ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION); return; } /* successful completion path */ if (cmd->cmnd[0] == INQUIRY && (cmd->cmnd[1] & 0x03) == 0) atapi_fixup_inquiry(cmd); - cmd->result = SAM_STAT_GOOD; - ata_qc_done(qc); + ata_scsi_qc_done(qc, true, SAM_STAT_GOOD); } /** * atapi_xlat - Initialize PACKET taskfile From db1d3cfaf3cd54b3fdfa63bbf40dc9f13787e65b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 20 Feb 2026 14:35:46 +0100 Subject: [PATCH 06/18] ata: ahci-dwc: Remove not-going-to-be-supported code for Baikal SoC As noticed in the discussion [1] the Baikal SoC and platforms are not going to be finalized, hence remove stale code. Link: https://lore.kernel.org/lkml/22b92ddf-6321-41b5-8073-f9c7064d3432@infradead.org/ [1] Signed-off-by: Andy Shevchenko Acked-by: Rob Herring (Arm) Signed-off-by: Niklas Cassel --- .../bindings/ata/baikal,bt1-ahci.yaml | 115 ------------------ drivers/ata/Kconfig | 1 - drivers/ata/ahci_dwc.c | 55 --------- 3 files changed, 171 deletions(-) delete mode 100644 Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml diff --git a/Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml b/Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml deleted file mode 100644 index 9b7ca4759bd7..000000000000 --- a/Documentation/devicetree/bindings/ata/baikal,bt1-ahci.yaml +++ /dev/null @@ -1,115 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/ata/baikal,bt1-ahci.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Baikal-T1 SoC AHCI SATA controller - -maintainers: - - Serge Semin - -description: - AHCI SATA controller embedded into the Baikal-T1 SoC is based on the - DWC AHCI SATA v4.10a IP-core. - -allOf: - - $ref: snps,dwc-ahci-common.yaml# - -properties: - compatible: - const: baikal,bt1-ahci - - clocks: - items: - - description: Peripheral APB bus clock - - description: Application AXI BIU clock - - description: SATA Ports reference clock - - clock-names: - items: - - const: pclk - - const: aclk - - const: ref - - resets: - items: - - description: Application AXI BIU domain reset - - description: SATA Ports clock domain reset - - reset-names: - items: - - const: arst - - const: ref - - ports-implemented: - maximum: 0x3 - -patternProperties: - "^sata-port@[0-1]$": - $ref: /schemas/ata/snps,dwc-ahci-common.yaml#/$defs/dwc-ahci-port - - properties: - reg: - minimum: 0 - maximum: 1 - - snps,tx-ts-max: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - Due to having AXI3 bus interface utilized the maximum Tx DMA - transaction size can't exceed 16 beats (AxLEN[3:0]). - enum: [ 1, 2, 4, 8, 16 ] - - snps,rx-ts-max: - $ref: /schemas/types.yaml#/definitions/uint32 - description: - Due to having AXI3 bus interface utilized the maximum Rx DMA - transaction size can't exceed 16 beats (AxLEN[3:0]). - enum: [ 1, 2, 4, 8, 16 ] - - unevaluatedProperties: false - -required: - - compatible - - reg - - interrupts - - clocks - - clock-names - - resets - -unevaluatedProperties: false - -examples: - - | - sata@1f050000 { - compatible = "baikal,bt1-ahci"; - reg = <0x1f050000 0x2000>; - #address-cells = <1>; - #size-cells = <0>; - - interrupts = <0 64 4>; - - clocks = <&ccu_sys 1>, <&ccu_axi 2>, <&sata_ref_clk>; - clock-names = "pclk", "aclk", "ref"; - - resets = <&ccu_axi 2>, <&ccu_sys 0>; - reset-names = "arst", "ref"; - - ports-implemented = <0x3>; - - sata-port@0 { - reg = <0>; - - snps,tx-ts-max = <4>; - snps,rx-ts-max = <4>; - }; - - sata-port@1 { - reg = <1>; - - snps,tx-ts-max = <4>; - snps,rx-ts-max = <4>; - }; - }; -... diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 2349bca136e0..fff305ec1e78 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -194,7 +194,6 @@ config AHCI_DM816 config AHCI_DWC tristate "Synopsys DWC AHCI SATA support" select SATA_HOST - select MFD_SYSCON if (MIPS_BAIKAL_T1 || COMPILE_TEST) help This option enables support for the Synopsys DWC AHCI SATA controller implementation. diff --git a/drivers/ata/ahci_dwc.c b/drivers/ata/ahci_dwc.c index 64abf865bb67..feb8a8539b8e 100644 --- a/drivers/ata/ahci_dwc.c +++ b/drivers/ata/ahci_dwc.c @@ -13,12 +13,10 @@ #include #include #include -#include #include #include #include #include -#include #include "ahci.h" @@ -92,20 +90,6 @@ #define AHCI_DWC_PORT_PHYCR 0x74 #define AHCI_DWC_PORT_PHYSR 0x78 -/* Baikal-T1 AHCI SATA specific registers */ -#define AHCI_BT1_HOST_PHYCR AHCI_DWC_HOST_GPCR -#define AHCI_BT1_HOST_MPLM_MASK GENMASK(29, 23) -#define AHCI_BT1_HOST_LOSDT_MASK GENMASK(22, 20) -#define AHCI_BT1_HOST_CRR BIT(19) -#define AHCI_BT1_HOST_CRW BIT(18) -#define AHCI_BT1_HOST_CRCD BIT(17) -#define AHCI_BT1_HOST_CRCA BIT(16) -#define AHCI_BT1_HOST_CRDI_MASK GENMASK(15, 0) - -#define AHCI_BT1_HOST_PHYSR AHCI_DWC_HOST_GPSR -#define AHCI_BT1_HOST_CRA BIT(16) -#define AHCI_BT1_HOST_CRDO_MASK GENMASK(15, 0) - struct ahci_dwc_plat_data { unsigned int pflags; unsigned int hflags; @@ -122,39 +106,6 @@ struct ahci_dwc_host_priv { u32 dmacr[AHCI_MAX_PORTS]; }; -static int ahci_bt1_init(struct ahci_host_priv *hpriv) -{ - struct ahci_dwc_host_priv *dpriv = hpriv->plat_data; - int ret; - - /* APB, application and reference clocks are required */ - if (!ahci_platform_find_clk(hpriv, "pclk") || - !ahci_platform_find_clk(hpriv, "aclk") || - !ahci_platform_find_clk(hpriv, "ref")) { - dev_err(&dpriv->pdev->dev, "No system clocks specified\n"); - return -EINVAL; - } - - /* - * Fully reset the SATA AXI and ref clocks domain to ensure the state - * machine is working from scratch especially if the reference clocks - * source has been changed. - */ - ret = ahci_platform_assert_rsts(hpriv); - if (ret) { - dev_err(&dpriv->pdev->dev, "Couldn't assert the resets\n"); - return ret; - } - - ret = ahci_platform_deassert_rsts(hpriv); - if (ret) { - dev_err(&dpriv->pdev->dev, "Couldn't de-assert the resets\n"); - return ret; - } - - return 0; -} - static struct ahci_host_priv *ahci_dwc_get_resources(struct platform_device *pdev) { struct ahci_dwc_host_priv *dpriv; @@ -457,15 +408,9 @@ static struct ahci_dwc_plat_data ahci_dwc_plat = { .pflags = AHCI_PLATFORM_GET_RESETS, }; -static struct ahci_dwc_plat_data ahci_bt1_plat = { - .pflags = AHCI_PLATFORM_GET_RESETS | AHCI_PLATFORM_RST_TRIGGER, - .init = ahci_bt1_init, -}; - static const struct of_device_id ahci_dwc_of_match[] = { { .compatible = "snps,dwc-ahci", &ahci_dwc_plat }, { .compatible = "snps,spear-ahci", &ahci_dwc_plat }, - { .compatible = "baikal,bt1-ahci", &ahci_bt1_plat }, {}, }; MODULE_DEVICE_TABLE(of, ahci_dwc_of_match); From 46a9d97069cab311738c950d0fcef85a459c7b8f Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 24 Feb 2026 11:06:38 +0900 Subject: [PATCH 07/18] ata: libata-eh: avoid unnecessary calls to ata_scsi_port_error_handler() When handling SCSI command timeouts, if we had no actual command timeouts (either because the command was a deferred qc or the completion path won the race with ata_scsi_cmd_error_handler()), we do not need to go through a port error handling, as there was in fact no errors at all. Modify ata_scsi_cmd_error_handler() to return the number of commands that timed out and use this return value in ata_scsi_error() to call ata_scsi_port_error_handler() only if we had command timeouts, or if the port EH has already been scheduled due to failed commands. Otherwise, simply call scsi_eh_flush_done_q() to finish the completed commands without running the full port error handling. Signed-off-by: Damien Le Moal Reviewed-by: Martin K. Petersen Reviewed-by: Niklas Cassel Signed-off-by: Niklas Cassel --- drivers/ata/libata-eh.c | 28 +++++++++++++++++++--------- include/linux/libata.h | 3 ++- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 72a22b6c9682..12c6740398fa 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -560,21 +560,27 @@ void ata_scsi_error(struct Scsi_Host *host) { struct ata_port *ap = ata_shost_to_port(host); unsigned long flags; + int nr_timedout; LIST_HEAD(eh_work_q); spin_lock_irqsave(host->host_lock, flags); list_splice_init(&host->eh_cmd_q, &eh_work_q); spin_unlock_irqrestore(host->host_lock, flags); - ata_scsi_cmd_error_handler(host, ap, &eh_work_q); + /* + * First check what errors we got with ata_scsi_cmd_error_handler(). + * If we had no command timeouts and EH is not scheduled for this port, + * meaning that we do not have any failed command, then there is no + * need to go through the full port error handling. We only need to + * flush the completed commands we have. + */ + nr_timedout = ata_scsi_cmd_error_handler(host, ap, &eh_work_q); + if (nr_timedout || ata_port_eh_scheduled(ap)) + ata_scsi_port_error_handler(host, ap); + else + scsi_eh_flush_done_q(&ap->eh_done_q); - /* If we timed raced normal completion and there is nothing to - recover nr_timedout == 0 why exactly are we doing error recovery ? */ - ata_scsi_port_error_handler(host, ap); - - /* finish or retry handled scmd's and clean up */ WARN_ON(!list_empty(&eh_work_q)); - } /** @@ -586,9 +592,11 @@ void ata_scsi_error(struct Scsi_Host *host) * process the given list of commands and return those finished to the * ap->eh_done_q. This function is the first part of the libata error * handler which processes a given list of failed commands. + * + * Return the number of commands that timed out. */ -void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, - struct list_head *eh_work_q) +int ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, + struct list_head *eh_work_q) { int i; unsigned long flags; @@ -678,6 +686,8 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, ap->eh_tries = ATA_EH_MAX_TRIES; spin_unlock_irqrestore(ap->lock, flags); + + return nr_timedout; } EXPORT_SYMBOL(ata_scsi_cmd_error_handler); diff --git a/include/linux/libata.h b/include/linux/libata.h index db87c99e4189..5c085ef4eda7 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1225,7 +1225,8 @@ extern int ata_ncq_prio_enable(struct ata_port *ap, struct scsi_device *sdev, extern struct ata_device *ata_dev_pair(struct ata_device *adev); int ata_set_mode(struct ata_link *link, struct ata_device **r_failed_dev); extern void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap); -extern void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, struct list_head *eh_q); +int ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, + struct list_head *eh_q); /* * SATA specific code - drivers/ata/libata-sata.c From a6ac0af4d51081ef63ee588c9b8fa10c0f8e9210 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 20 Mar 2026 11:59:51 +0100 Subject: [PATCH 08/18] ata: libata-scsi: refactor ata_scsiop_maint_in() ata_scsiop_maint_in() is currently quite confusing to read, because it currently only implements support for the service action REPORT SUPPORTED OPERATION CODES. Thus, when this function is checking for "invalid command format", it is not very clear if it is an invalid command format for the MAINTENANCE IN command itself, or an invalid command format for the (currently one and only) service action/subcommand implemented for this command. Move the service action to a separate function, so it is more clear that the "invalid command format" check is actually specific for the REPORT SUPPORTED OPERATION CODES service action. This also makes it easier and less confusing to add support for additional service actions in the future. Reviewed-by: Damien Le Moal Signed-off-by: Niklas Cassel --- drivers/ata/libata-scsi.c | 47 ++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 00b3ffbfe169..06f3a243f037 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3573,28 +3573,13 @@ invalid_opcode: return 1; } -/** - * ata_scsiop_maint_in - Simulate a subset of MAINTENANCE_IN - * @dev: Target device. - * @cmd: SCSI command of interest. - * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. - * - * Yields a subset to satisfy scsi_report_opcode() - * - * LOCKING: - * spin_lock_irqsave(host lock) - */ -static unsigned int ata_scsiop_maint_in(struct ata_device *dev, - struct scsi_cmnd *cmd, u8 *rbuf) +static unsigned int ata_scsi_report_supported_opcodes(struct ata_device *dev, + struct scsi_cmnd *cmd, + u8 *rbuf) { u8 *cdb = cmd->cmnd; u8 supported = 0, cdlp = 0, rwcdlp = 0; - if ((cdb[1] & 0x1f) != MI_REPORT_SUPPORTED_OPERATION_CODES) { - ata_scsi_set_invalid_field(dev, cmd, 1, 0xff); - return 0; - } - if (cdb[2] != 1 && cdb[2] != 3) { ata_dev_warn(dev, "invalid command format %d\n", cdb[2]); ata_scsi_set_invalid_field(dev, cmd, 2, 0xff); @@ -3674,6 +3659,32 @@ static unsigned int ata_scsiop_maint_in(struct ata_device *dev, return 4; } +/** + * ata_scsiop_maint_in - Simulate a subset of MAINTENANCE_IN + * @dev: Target device. + * @cmd: SCSI command of interest. + * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. + * + * Yields a subset to satisfy scsi_report_opcode() + * + * LOCKING: + * spin_lock_irqsave(host lock) + */ +static unsigned int ata_scsiop_maint_in(struct ata_device *dev, + struct scsi_cmnd *cmd, u8 *rbuf) +{ + u8 *cdb = cmd->cmnd; + u8 service_action = cdb[1] & 0x1f; + + switch (service_action) { + case MI_REPORT_SUPPORTED_OPERATION_CODES: + return ata_scsi_report_supported_opcodes(dev, cmd, rbuf); + default: + ata_scsi_set_invalid_field(dev, cmd, 1, 0xff); + return 0; + } +} + /** * ata_scsi_report_zones_complete - convert ATA output * @qc: command structure returning the data From 78ec06991d2cd564ff45f280e0bb57b369be7587 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 20 Mar 2026 23:10:59 +0100 Subject: [PATCH 09/18] ata: libata-transport: remove redundant dynamic sysfs attributes Use the static sysfs attributes directly, this allows to significantly simplify the code. See attribute_container_add_attrs() for why member grp can be used instead of attrs. Signed-off-by: Heiner Kallweit Tested-by: Niklas Cassel Signed-off-by: Niklas Cassel --- drivers/ata/libata-transport.c | 113 ++++++++++++--------------------- 1 file changed, 42 insertions(+), 71 deletions(-) diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 2099d94c4f68..7ad18026547b 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -37,34 +37,17 @@ #include "libata.h" #include "libata-transport.h" -#define ATA_PORT_ATTRS 3 -#define ATA_LINK_ATTRS 3 -#define ATA_DEV_ATTRS 9 - struct scsi_transport_template; struct scsi_transport_template *ata_scsi_transport_template; struct ata_internal { struct scsi_transport_template t; - struct device_attribute private_port_attrs[ATA_PORT_ATTRS]; - struct device_attribute private_link_attrs[ATA_LINK_ATTRS]; - struct device_attribute private_dev_attrs[ATA_DEV_ATTRS]; - struct transport_container link_attr_cont; struct transport_container dev_attr_cont; - - /* - * The array of null terminated pointers to attributes - * needed by scsi_sysfs.c - */ - struct device_attribute *link_attrs[ATA_LINK_ATTRS + 1]; - struct device_attribute *port_attrs[ATA_PORT_ATTRS + 1]; - struct device_attribute *dev_attrs[ATA_DEV_ATTRS + 1]; }; #define to_ata_internal(tmpl) container_of(tmpl, struct ata_internal, t) - #define tdev_to_device(d) \ container_of((d), struct ata_device, tdev) #define transport_class_to_dev(dev) \ @@ -80,13 +63,6 @@ struct ata_internal { #define transport_class_to_port(dev) \ tdev_to_port((dev)->parent) -/* - * Hack to allow attributes of the same name in different objects. - */ -#define ATA_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \ - struct device_attribute device_attr_##_prefix##_##_name = \ - __ATTR(_name,_mode,_show,_store) - #define ata_bitfield_name_match(title, table) \ static ssize_t \ get_ata_##title##_names(u32 table_key, char *buf) \ @@ -214,6 +190,17 @@ ata_port_simple_attr(stats.idle_irq, idle_irq, "%ld\n", unsigned long); /* We want the port_no sysfs attibute to start at 1 (ap->port_no starts at 0) */ ata_port_simple_attr(port_no + 1, port_no, "%u\n", unsigned int); +static const struct attribute *const ata_port_attr_attrs[] = { + &dev_attr_nr_pmp_links.attr, + &dev_attr_idle_irq.attr, + &dev_attr_port_no.attr, + NULL +}; + +static const struct attribute_group ata_port_attr_group = { + .attrs_const = ata_port_attr_attrs, +}; + static DECLARE_TRANSPORT_CLASS(ata_port_class, "ata_port", NULL, NULL, NULL); @@ -496,6 +483,23 @@ show_ata_dev_trim(struct device *dev, static DEVICE_ATTR(trim, S_IRUGO, show_ata_dev_trim, NULL); +static const struct attribute *const ata_device_attr_attrs[] = { + &dev_attr_class.attr, + &dev_attr_pio_mode.attr, + &dev_attr_dma_mode.attr, + &dev_attr_xfer_mode.attr, + &dev_attr_spdn_cnt.attr, + &dev_attr_ering.attr, + &dev_attr_id.attr, + &dev_attr_gscr.attr, + &dev_attr_trim.attr, + NULL +}; + +static const struct attribute_group ata_device_attr_group = { + .attrs_const = ata_device_attr_attrs, +}; + static DECLARE_TRANSPORT_CLASS(ata_dev_class, "ata_device", NULL, NULL, NULL); @@ -626,6 +630,17 @@ ata_link_linkspeed_attr(hw_sata_spd_limit, fls); ata_link_linkspeed_attr(sata_spd_limit, fls); ata_link_linkspeed_attr(sata_spd, noop); +static const struct attribute *const ata_link_attr_attrs[] = { + &dev_attr_hw_sata_spd_limit.attr, + &dev_attr_sata_spd_limit.attr, + &dev_attr_sata_spd.attr, + NULL +}; + +static const struct attribute_group ata_link_attr_group = { + .attrs_const = ata_link_attr_attrs, +}; + static DECLARE_TRANSPORT_CLASS(ata_link_class, "ata_link", NULL, NULL, NULL); @@ -734,29 +749,12 @@ int ata_tlink_add(struct ata_link *link) * Setup / Teardown code */ -#define SETUP_TEMPLATE(attrb, field, perm, test) \ - i->private_##attrb[count] = dev_attr_##field; \ - i->private_##attrb[count].attr.mode = perm; \ - i->attrb[count] = &i->private_##attrb[count]; \ - if (test) \ - count++ - -#define SETUP_LINK_ATTRIBUTE(field) \ - SETUP_TEMPLATE(link_attrs, field, S_IRUGO, 1) - -#define SETUP_PORT_ATTRIBUTE(field) \ - SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1) - -#define SETUP_DEV_ATTRIBUTE(field) \ - SETUP_TEMPLATE(dev_attrs, field, S_IRUGO, 1) - /** * ata_attach_transport -- instantiate ATA transport template */ struct scsi_transport_template *ata_attach_transport(void) { struct ata_internal *i; - int count; i = kzalloc_obj(struct ata_internal); if (!i) @@ -765,48 +763,21 @@ struct scsi_transport_template *ata_attach_transport(void) i->t.eh_strategy_handler = ata_scsi_error; i->t.user_scan = ata_scsi_user_scan; - i->t.host_attrs.ac.attrs = &i->port_attrs[0]; i->t.host_attrs.ac.class = &ata_port_class.class; + i->t.host_attrs.ac.grp = &ata_port_attr_group; i->t.host_attrs.ac.match = ata_tport_match; transport_container_register(&i->t.host_attrs); i->link_attr_cont.ac.class = &ata_link_class.class; - i->link_attr_cont.ac.attrs = &i->link_attrs[0]; + i->link_attr_cont.ac.grp = &ata_link_attr_group; i->link_attr_cont.ac.match = ata_tlink_match; transport_container_register(&i->link_attr_cont); i->dev_attr_cont.ac.class = &ata_dev_class.class; - i->dev_attr_cont.ac.attrs = &i->dev_attrs[0]; + i->dev_attr_cont.ac.grp = &ata_device_attr_group; i->dev_attr_cont.ac.match = ata_tdev_match; transport_container_register(&i->dev_attr_cont); - count = 0; - SETUP_PORT_ATTRIBUTE(nr_pmp_links); - SETUP_PORT_ATTRIBUTE(idle_irq); - SETUP_PORT_ATTRIBUTE(port_no); - BUG_ON(count > ATA_PORT_ATTRS); - i->port_attrs[count] = NULL; - - count = 0; - SETUP_LINK_ATTRIBUTE(hw_sata_spd_limit); - SETUP_LINK_ATTRIBUTE(sata_spd_limit); - SETUP_LINK_ATTRIBUTE(sata_spd); - BUG_ON(count > ATA_LINK_ATTRS); - i->link_attrs[count] = NULL; - - count = 0; - SETUP_DEV_ATTRIBUTE(class); - SETUP_DEV_ATTRIBUTE(pio_mode); - SETUP_DEV_ATTRIBUTE(dma_mode); - SETUP_DEV_ATTRIBUTE(xfer_mode); - SETUP_DEV_ATTRIBUTE(spdn_cnt); - SETUP_DEV_ATTRIBUTE(ering); - SETUP_DEV_ATTRIBUTE(id); - SETUP_DEV_ATTRIBUTE(gscr); - SETUP_DEV_ATTRIBUTE(trim); - BUG_ON(count > ATA_DEV_ATTRS); - i->dev_attrs[count] = NULL; - return &i->t; } From d5d286154b6cc1729b5aa0bc579902e2dbe9be5c Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Mon, 30 Mar 2026 13:37:37 -0700 Subject: [PATCH 10/18] ata: libahci_platform: use flex array for platform PHYs Modify struct ahci_host_priv to use a flexible array member for an adapter port PHYs and use struct_size to combine the allocation of this array together with the adapter private data structure. __counted_by() annotation is added for the phys field to support runtime analysis. Signed-off-by: Rosen Penev Reviewed-by: Damien Le Moal Signed-off-by: Niklas Cassel --- drivers/ata/ahci.h | 3 ++- drivers/ata/libahci_platform.c | 33 +++++++++++++++------------------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 293b7fb216b5..9e8b6319025c 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -357,7 +357,6 @@ struct ahci_host_priv { * If platform uses PHYs. There is a 1:1 relation between the port number and * the PHY position in this array. */ - struct phy **phys; unsigned nports; /* Number of ports */ void *plat_data; /* Other platform data */ unsigned int irq; /* interrupt line */ @@ -379,6 +378,8 @@ struct ahci_host_priv { /* only required for per-port MSI(-X) support */ int (*get_irq_vector)(struct ata_host *host, int port); + + struct phy *phys[] __counted_by(nports); }; /* diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index f37247e7a5cb..6e072d681341 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -482,15 +482,29 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, struct ahci_host_priv *hpriv; u32 mask_port_map = 0; u32 max_port; + int nports; if (!devres_open_group(dev, NULL, GFP_KERNEL)) return ERR_PTR(-ENOMEM); - hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), + /* find maximum port id for allocating structures */ + max_port = ahci_platform_find_max_port_id(dev); + /* + * Set nports according to maximum port id. Clamp at + * AHCI_MAX_PORTS, warning message for invalid port id + * is generated later. + * When DT has no sub-nodes max_port is 0, nports is 1, + * in order to be able to use the + * ahci_platform_[en|dis]able_[phys|regulators] functions. + */ + nports = min(AHCI_MAX_PORTS, max_port + 1); + hpriv = devres_alloc(ahci_platform_put_resources, struct_size(hpriv, phys, nports), GFP_KERNEL); if (!hpriv) goto err_out; + hpriv->nports = nports; + devres_add(dev, hpriv); /* @@ -573,23 +587,6 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev, goto err_out; } - /* find maximum port id for allocating structures */ - max_port = ahci_platform_find_max_port_id(dev); - /* - * Set nports according to maximum port id. Clamp at - * AHCI_MAX_PORTS, warning message for invalid port id - * is generated later. - * When DT has no sub-nodes max_port is 0, nports is 1, - * in order to be able to use the - * ahci_platform_[en|dis]able_[phys|regulators] functions. - */ - hpriv->nports = min(AHCI_MAX_PORTS, max_port + 1); - - hpriv->phys = devm_kcalloc(dev, hpriv->nports, sizeof(*hpriv->phys), GFP_KERNEL); - if (!hpriv->phys) { - rc = -ENOMEM; - goto err_out; - } /* * We cannot use devm_ here, since ahci_platform_put_resources() uses * target_pwrs after devm_ have freed memory From 182caa17360dd48e6df08e18f00ebda0be87ab24 Mon Sep 17 00:00:00 2001 From: Igor Pylypiv Date: Thu, 2 Apr 2026 09:07:05 -0700 Subject: [PATCH 11/18] ata: libata-eh: Do not retry reset if the device is gone If a device is hot-unplugged or otherwise disappears during error handling, ata_eh_reset() may fail with -ENODEV. Currently, the error handler will continue to retry the reset operation up to max_tries times. Prevent unnecessary reset retries by exiting the loop early when ata_do_reset() returns -ENODEV. Reviewed-by: Damien Le Moal Signed-off-by: Igor Pylypiv Signed-off-by: Niklas Cassel --- drivers/ata/libata-eh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index bb0813ea8b18..9a4b67b90b17 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -3181,7 +3181,7 @@ int ata_eh_reset(struct ata_link *link, int classify, sata_scr_read(link, SCR_STATUS, &sstatus)) rc = -ERESTART; - if (try >= max_tries) { + if (try >= max_tries || rc == -ENODEV) { /* * Thaw host port even if reset failed, so that the port * can be retried on the next phy event. This risks From ee1ed7a864389ba727ac9b3549beab08e3966735 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 2 Apr 2026 15:29:21 +0200 Subject: [PATCH 12/18] ata: libata-transport: instantiate struct ata_internal statically Struct ata_internal is only instantiated once, in module init code. So we can also instantiate it statically, which allows simplifying the code. Reviewed-by: Damien Le Moal Signed-off-by: Heiner Kallweit Signed-off-by: Niklas Cassel --- drivers/ata/libata-core.c | 11 ++----- drivers/ata/libata-transport.c | 57 +++++++++++++++------------------- drivers/ata/libata-transport.h | 2 +- 3 files changed, 28 insertions(+), 42 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 81479ddb83b1..ae56567af769 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6779,22 +6779,15 @@ static int __init ata_init(void) libata_transport_init(); ata_scsi_transport_template = ata_attach_transport(); - if (!ata_scsi_transport_template) { - ata_sff_exit(); - rc = -ENOMEM; - goto err_out; - } printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n"); - return 0; -err_out: - return rc; + return 0; } static void __exit ata_exit(void) { - ata_release_transport(ata_scsi_transport_template); + ata_release_transport(); libata_transport_exit(); ata_sff_exit(); ata_free_force_param(); diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 7ad18026547b..0c7ce278ece3 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -745,6 +745,23 @@ int ata_tlink_add(struct ata_link *link) return error; } +static struct ata_internal ata_transport_internal = { + .t.eh_strategy_handler = ata_scsi_error, + .t.user_scan = ata_scsi_user_scan, + + .t.host_attrs.ac.class = &ata_port_class.class, + .t.host_attrs.ac.grp = &ata_port_attr_group, + .t.host_attrs.ac.match = ata_tport_match, + + .link_attr_cont.ac.class = &ata_link_class.class, + .link_attr_cont.ac.grp = &ata_link_attr_group, + .link_attr_cont.ac.match = ata_tlink_match, + + .dev_attr_cont.ac.class = &ata_dev_class.class, + .dev_attr_cont.ac.grp = &ata_device_attr_group, + .dev_attr_cont.ac.match = ata_tdev_match, +}; + /* * Setup / Teardown code */ @@ -754,46 +771,22 @@ int ata_tlink_add(struct ata_link *link) */ struct scsi_transport_template *ata_attach_transport(void) { - struct ata_internal *i; + transport_container_register(&ata_transport_internal.t.host_attrs); + transport_container_register(&ata_transport_internal.link_attr_cont); + transport_container_register(&ata_transport_internal.dev_attr_cont); - i = kzalloc_obj(struct ata_internal); - if (!i) - return NULL; - - i->t.eh_strategy_handler = ata_scsi_error; - i->t.user_scan = ata_scsi_user_scan; - - i->t.host_attrs.ac.class = &ata_port_class.class; - i->t.host_attrs.ac.grp = &ata_port_attr_group; - i->t.host_attrs.ac.match = ata_tport_match; - transport_container_register(&i->t.host_attrs); - - i->link_attr_cont.ac.class = &ata_link_class.class; - i->link_attr_cont.ac.grp = &ata_link_attr_group; - i->link_attr_cont.ac.match = ata_tlink_match; - transport_container_register(&i->link_attr_cont); - - i->dev_attr_cont.ac.class = &ata_dev_class.class; - i->dev_attr_cont.ac.grp = &ata_device_attr_group; - i->dev_attr_cont.ac.match = ata_tdev_match; - transport_container_register(&i->dev_attr_cont); - - return &i->t; + return &ata_transport_internal.t; } /** * ata_release_transport -- release ATA transport template instance * @t: transport template instance */ -void ata_release_transport(struct scsi_transport_template *t) +void ata_release_transport(void) { - struct ata_internal *i = to_ata_internal(t); - - transport_container_unregister(&i->t.host_attrs); - transport_container_unregister(&i->link_attr_cont); - transport_container_unregister(&i->dev_attr_cont); - - kfree(i); + transport_container_unregister(&ata_transport_internal.t.host_attrs); + transport_container_unregister(&ata_transport_internal.link_attr_cont); + transport_container_unregister(&ata_transport_internal.dev_attr_cont); } __init int libata_transport_init(void) diff --git a/drivers/ata/libata-transport.h b/drivers/ata/libata-transport.h index 50cd2cbe8eea..a464b8fcd941 100644 --- a/drivers/ata/libata-transport.h +++ b/drivers/ata/libata-transport.h @@ -9,7 +9,7 @@ int ata_tlink_add(struct ata_link *link); void ata_tlink_delete(struct ata_link *link); struct scsi_transport_template *ata_attach_transport(void); -void ata_release_transport(struct scsi_transport_template *t); +void ata_release_transport(void); __init int libata_transport_init(void); void __exit libata_transport_exit(void); From f2122465398a875ef5a31bde85602e6dfac17a83 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 2 Apr 2026 15:30:05 +0200 Subject: [PATCH 13/18] ata: libata-transport: inline ata_attach|release_transport Both functions are helpers which are used only once. So remove them and merge their code into libata_transport_init() and libata_transport_exit() respectively. Reviewed-by: Damien Le Moal Signed-off-by: Heiner Kallweit Signed-off-by: Niklas Cassel --- drivers/ata/libata-core.c | 2 -- drivers/ata/libata-transport.c | 34 +++++++++++----------------------- drivers/ata/libata-transport.h | 3 --- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index ae56567af769..e76d15411e2a 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6778,7 +6778,6 @@ static int __init ata_init(void) } libata_transport_init(); - ata_scsi_transport_template = ata_attach_transport(); printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n"); @@ -6787,7 +6786,6 @@ static int __init ata_init(void) static void __exit ata_exit(void) { - ata_release_transport(); libata_transport_exit(); ata_sff_exit(); ata_free_force_param(); diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 0c7ce278ece3..9d594562c0a7 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -766,29 +766,6 @@ static struct ata_internal ata_transport_internal = { * Setup / Teardown code */ -/** - * ata_attach_transport -- instantiate ATA transport template - */ -struct scsi_transport_template *ata_attach_transport(void) -{ - transport_container_register(&ata_transport_internal.t.host_attrs); - transport_container_register(&ata_transport_internal.link_attr_cont); - transport_container_register(&ata_transport_internal.dev_attr_cont); - - return &ata_transport_internal.t; -} - -/** - * ata_release_transport -- release ATA transport template instance - * @t: transport template instance - */ -void ata_release_transport(void) -{ - transport_container_unregister(&ata_transport_internal.t.host_attrs); - transport_container_unregister(&ata_transport_internal.link_attr_cont); - transport_container_unregister(&ata_transport_internal.dev_attr_cont); -} - __init int libata_transport_init(void) { int error; @@ -802,6 +779,13 @@ __init int libata_transport_init(void) error = transport_class_register(&ata_dev_class); if (error) goto out_unregister_port; + + transport_container_register(&ata_transport_internal.t.host_attrs); + transport_container_register(&ata_transport_internal.link_attr_cont); + transport_container_register(&ata_transport_internal.dev_attr_cont); + + ata_scsi_transport_template = &ata_transport_internal.t; + return 0; out_unregister_port: @@ -815,6 +799,10 @@ __init int libata_transport_init(void) void __exit libata_transport_exit(void) { + transport_container_unregister(&ata_transport_internal.t.host_attrs); + transport_container_unregister(&ata_transport_internal.link_attr_cont); + transport_container_unregister(&ata_transport_internal.dev_attr_cont); + transport_class_unregister(&ata_link_class); transport_class_unregister(&ata_port_class); transport_class_unregister(&ata_dev_class); diff --git a/drivers/ata/libata-transport.h b/drivers/ata/libata-transport.h index a464b8fcd941..fe5ca66fc33a 100644 --- a/drivers/ata/libata-transport.h +++ b/drivers/ata/libata-transport.h @@ -8,9 +8,6 @@ extern struct scsi_transport_template *ata_scsi_transport_template; int ata_tlink_add(struct ata_link *link); void ata_tlink_delete(struct ata_link *link); -struct scsi_transport_template *ata_attach_transport(void); -void ata_release_transport(void); - __init int libata_transport_init(void); void __exit libata_transport_exit(void); #endif From 365da8c68739a28b44e414696a5ff0ee4c58e7d3 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 2 Apr 2026 15:30:48 +0200 Subject: [PATCH 14/18] ata: libata-transport: use static struct ata_transport_internal to simplify match functions Both matching functions can make use of static struct ata_transport_internal. This eliminates the dependency on static variable ata_scsi_transport_template, and it allows to remove helper to_ata_internal(). Small drawback is that a forward declaration of both functions is needed. Reviewed-by: Damien Le Moal Signed-off-by: Heiner Kallweit Signed-off-by: Niklas Cassel --- drivers/ata/libata-transport.c | 44 ++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 9d594562c0a7..c3055e44d87d 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -46,7 +46,11 @@ struct ata_internal { struct transport_container link_attr_cont; struct transport_container dev_attr_cont; }; -#define to_ata_internal(tmpl) container_of(tmpl, struct ata_internal, t) + +static int ata_tlink_match(struct attribute_container *cont, + struct device *dev); +static int ata_tdev_match(struct attribute_container *cont, + struct device *dev); #define tdev_to_device(d) \ container_of((d), struct ata_device, tdev) @@ -519,16 +523,6 @@ static bool ata_is_ata_dev(const struct device *dev) return dev->release == ata_tdev_release; } -static int ata_tdev_match(struct attribute_container *cont, - struct device *dev) -{ - struct ata_internal *i = to_ata_internal(ata_scsi_transport_template); - - if (!ata_is_ata_dev(dev)) - return 0; - return &i->dev_attr_cont.ac == cont; -} - /** * ata_tdev_free -- free an ATA transport device * @dev: struct ata_device owning the transport device to free @@ -660,16 +654,6 @@ static bool ata_is_link(const struct device *dev) return dev->release == ata_tlink_release; } -static int ata_tlink_match(struct attribute_container *cont, - struct device *dev) -{ - struct ata_internal *i = to_ata_internal(ata_scsi_transport_template); - - if (!ata_is_link(dev)) - return 0; - return &i->link_attr_cont.ac == cont; -} - /** * ata_tlink_delete -- remove an ATA link transport device * @link: struct ata_link owning the link transport device to remove @@ -762,6 +746,24 @@ static struct ata_internal ata_transport_internal = { .dev_attr_cont.ac.match = ata_tdev_match, }; +static int ata_tlink_match(struct attribute_container *cont, + struct device *dev) +{ + if (!ata_is_link(dev)) + return 0; + + return &ata_transport_internal.link_attr_cont.ac == cont; +} + +static int ata_tdev_match(struct attribute_container *cont, + struct device *dev) +{ + if (!ata_is_ata_dev(dev)) + return 0; + + return &ata_transport_internal.dev_attr_cont.ac == cont; +} + /* * Setup / Teardown code */ From 359942ba4c1dfa3795e6137a5c09e2bfd1c444e5 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 2 Apr 2026 15:31:22 +0200 Subject: [PATCH 15/18] ata: libata-transport: split struct ata_internal There's no need for an umbrella struct, so remove it. It's also a prerequisite for making the embedded struct scsi_transport_template public. Reviewed-by: Damien Le Moal Signed-off-by: Heiner Kallweit Signed-off-by: Niklas Cassel --- drivers/ata/libata-transport.c | 53 ++++++++++++++++------------------ 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index c3055e44d87d..c8db6e1b9410 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -40,13 +40,6 @@ struct scsi_transport_template; struct scsi_transport_template *ata_scsi_transport_template; -struct ata_internal { - struct scsi_transport_template t; - - struct transport_container link_attr_cont; - struct transport_container dev_attr_cont; -}; - static int ata_tlink_match(struct attribute_container *cont, struct device *dev); static int ata_tdev_match(struct attribute_container *cont, @@ -729,21 +722,25 @@ int ata_tlink_add(struct ata_link *link) return error; } -static struct ata_internal ata_transport_internal = { - .t.eh_strategy_handler = ata_scsi_error, - .t.user_scan = ata_scsi_user_scan, +static struct scsi_transport_template ata_scsi_transportt = { + .eh_strategy_handler = ata_scsi_error, + .user_scan = ata_scsi_user_scan, - .t.host_attrs.ac.class = &ata_port_class.class, - .t.host_attrs.ac.grp = &ata_port_attr_group, - .t.host_attrs.ac.match = ata_tport_match, + .host_attrs.ac.class = &ata_port_class.class, + .host_attrs.ac.grp = &ata_port_attr_group, + .host_attrs.ac.match = ata_tport_match, +}; - .link_attr_cont.ac.class = &ata_link_class.class, - .link_attr_cont.ac.grp = &ata_link_attr_group, - .link_attr_cont.ac.match = ata_tlink_match, +static struct transport_container ata_link_attr_cont = { + .ac.class = &ata_link_class.class, + .ac.grp = &ata_link_attr_group, + .ac.match = ata_tlink_match, +}; - .dev_attr_cont.ac.class = &ata_dev_class.class, - .dev_attr_cont.ac.grp = &ata_device_attr_group, - .dev_attr_cont.ac.match = ata_tdev_match, +static struct transport_container ata_dev_attr_cont = { + .ac.class = &ata_dev_class.class, + .ac.grp = &ata_device_attr_group, + .ac.match = ata_tdev_match, }; static int ata_tlink_match(struct attribute_container *cont, @@ -752,7 +749,7 @@ static int ata_tlink_match(struct attribute_container *cont, if (!ata_is_link(dev)) return 0; - return &ata_transport_internal.link_attr_cont.ac == cont; + return &ata_link_attr_cont.ac == cont; } static int ata_tdev_match(struct attribute_container *cont, @@ -761,7 +758,7 @@ static int ata_tdev_match(struct attribute_container *cont, if (!ata_is_ata_dev(dev)) return 0; - return &ata_transport_internal.dev_attr_cont.ac == cont; + return &ata_dev_attr_cont.ac == cont; } /* @@ -782,11 +779,11 @@ __init int libata_transport_init(void) if (error) goto out_unregister_port; - transport_container_register(&ata_transport_internal.t.host_attrs); - transport_container_register(&ata_transport_internal.link_attr_cont); - transport_container_register(&ata_transport_internal.dev_attr_cont); + transport_container_register(&ata_scsi_transportt.host_attrs); + transport_container_register(&ata_link_attr_cont); + transport_container_register(&ata_dev_attr_cont); - ata_scsi_transport_template = &ata_transport_internal.t; + ata_scsi_transport_template = &ata_scsi_transportt; return 0; @@ -801,9 +798,9 @@ __init int libata_transport_init(void) void __exit libata_transport_exit(void) { - transport_container_unregister(&ata_transport_internal.t.host_attrs); - transport_container_unregister(&ata_transport_internal.link_attr_cont); - transport_container_unregister(&ata_transport_internal.dev_attr_cont); + transport_container_unregister(&ata_scsi_transportt.host_attrs); + transport_container_unregister(&ata_link_attr_cont); + transport_container_unregister(&ata_dev_attr_cont); transport_class_unregister(&ata_link_class); transport_class_unregister(&ata_port_class); From 7bf6ddc3345663beef7766a804fe9b73909fba57 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 2 Apr 2026 15:32:13 +0200 Subject: [PATCH 16/18] ata: libata-transport: remove static variable ata_scsi_transport_template Simplify the code by making struct ata_scsi_transportt public, instead of using separate variable ata_scsi_transport_template. Reviewed-by: Damien Le Moal Signed-off-by: Heiner Kallweit Signed-off-by: Niklas Cassel --- drivers/ata/libata-scsi.c | 2 +- drivers/ata/libata-transport.c | 9 ++------- drivers/ata/libata-transport.h | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 06f3a243f037..b15830bedd3a 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -4624,7 +4624,7 @@ int ata_scsi_add_hosts(struct ata_host *host, const struct scsi_host_template *s *(struct ata_port **)&shost->hostdata[0] = ap; ap->scsi_host = shost; - shost->transportt = ata_scsi_transport_template; + shost->transportt = &ata_scsi_transportt; shost->unique_id = ap->print_id; shost->max_id = 16; shost->max_lun = 1; diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index c8db6e1b9410..95862dc34419 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -37,9 +37,6 @@ #include "libata.h" #include "libata-transport.h" -struct scsi_transport_template; -struct scsi_transport_template *ata_scsi_transport_template; - static int ata_tlink_match(struct attribute_container *cont, struct device *dev); static int ata_tdev_match(struct attribute_container *cont, @@ -224,7 +221,7 @@ static int ata_tport_match(struct attribute_container *cont, { if (!ata_is_port(dev)) return 0; - return &ata_scsi_transport_template->host_attrs.ac == cont; + return &ata_scsi_transportt.host_attrs.ac == cont; } /** @@ -722,7 +719,7 @@ int ata_tlink_add(struct ata_link *link) return error; } -static struct scsi_transport_template ata_scsi_transportt = { +struct scsi_transport_template ata_scsi_transportt = { .eh_strategy_handler = ata_scsi_error, .user_scan = ata_scsi_user_scan, @@ -783,8 +780,6 @@ __init int libata_transport_init(void) transport_container_register(&ata_link_attr_cont); transport_container_register(&ata_dev_attr_cont); - ata_scsi_transport_template = &ata_scsi_transportt; - return 0; out_unregister_port: diff --git a/drivers/ata/libata-transport.h b/drivers/ata/libata-transport.h index fe5ca66fc33a..629ac843a873 100644 --- a/drivers/ata/libata-transport.h +++ b/drivers/ata/libata-transport.h @@ -3,7 +3,7 @@ #define _LIBATA_TRANSPORT_H -extern struct scsi_transport_template *ata_scsi_transport_template; +extern struct scsi_transport_template ata_scsi_transportt; int ata_tlink_add(struct ata_link *link); void ata_tlink_delete(struct ata_link *link); From 797f629856c56595e138d69b8b0701ffe3cece21 Mon Sep 17 00:00:00 2001 From: Haoyu Lu Date: Fri, 3 Apr 2026 10:07:09 +0800 Subject: [PATCH 17/18] ata: pata_arasan_cf: fix missing newline in dev_err() messages Add missing trailing newlines to dev_err() messages in pata_arasan_cf.c. This keeps the error output as properly terminated log lines. Acked-by: Viresh Kumar Signed-off-by: Haoyu Lu Reviewed-by: Damien Le Moal Signed-off-by: Niklas Cassel --- drivers/ata/pata_arasan_cf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index 514d549286b5..b1e281b64f57 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -380,7 +380,7 @@ static inline int wait4buf(struct arasan_cf_dev *acdev) if (!wait_for_completion_timeout(&acdev->cf_completion, TIMEOUT)) { u32 rw = acdev->qc->tf.flags & ATA_TFLAG_WRITE; - dev_err(acdev->host->dev, "%s TimeOut", rw ? "write" : "read"); + dev_err(acdev->host->dev, "%s TimeOut\n", rw ? "write" : "read"); return -ETIMEDOUT; } @@ -474,7 +474,7 @@ static int sg_xfer(struct arasan_cf_dev *acdev, struct scatterlist *sg) dma_len = min(xfer_cnt, FIFO_SIZE); ret = dma_xfer(acdev, src, dest, dma_len); if (ret) { - dev_err(acdev->host->dev, "dma failed"); + dev_err(acdev->host->dev, "dma failed\n"); goto fail; } From 8ebf408e7d463eee02c348a3c8277b95587b710d Mon Sep 17 00:00:00 2001 From: Igor Pylypiv Date: Sun, 12 Apr 2026 08:36:37 -0700 Subject: [PATCH 18/18] ata: libata-scsi: fix requeue of deferred ATA PASS-THROUGH commands Commit 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") introduced ata_scsi_requeue_deferred_qc() to handle commands deferred during resets or NCQ failures. This deferral logic completed commands with DID_SOFT_ERROR to trigger a retry in the SCSI mid-layer. However, DID_SOFT_ERROR is subject to scsi_cmd_retry_allowed() checks. ATA PASS-THROUGH commands sent via SG_IO ioctl have scmd->allowed set to zero. This causes the mid-layer to fail the command immediately instead of retrying, even though the command was never actually issued to the hardware. Switch to DID_REQUEUE to ensure these commands are inserted back into the request queue regardless of retry limits. Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Reviewed-by: Damien Le Moal Signed-off-by: Igor Pylypiv Signed-off-by: Niklas Cassel --- drivers/ata/libata-scsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index b15830bedd3a..f44612e269a4 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1695,12 +1695,12 @@ void ata_scsi_requeue_deferred_qc(struct ata_port *ap) /* * If we have a deferred qc when a reset occurs or NCQ commands fail, * do not try to be smart about what to do with this deferred command - * and simply retry it by completing it with DID_SOFT_ERROR. + * and simply requeue it by completing it with DID_REQUEUE. */ if (qc) { ap->deferred_qc = NULL; cancel_work(&ap->deferred_qc_work); - ata_scsi_qc_done(qc, true, DID_SOFT_ERROR << 16); + ata_scsi_qc_done(qc, true, DID_REQUEUE << 16); } }