Skip to content

Commit f233124

Browse files
committed
ata: libata-scsi: do not use the deferred QC feature on PMPs with CBS
When using Port Multipliers (PMPs) with Command-Based Switching (CBS), you can only issue commands to one link at a time. For PMPs with CBS, there is already code to handle commands being sent to different links in sata_pmp_qc_defer_cmd_switch() using ap->excl_link. sata_sil24 also makes use of ap->excl_link. A user on the list reported that commit 0ea8408 ("ata: libata-scsi: avoid Non-NCQ command starvation") broke PMPs with CBS. The commit introduced code that stores a deferred qc in ap->deferred_qc, to later be issued via a workqueue. It turns out that this change is incompatible with the existing ap->excl_link handling used by PMPs with CBS. Thus, modify sata_pmp_qc_defer_cmd_switch() and sil24_qc_defer() to return ATA_DEFER_LINK_EXCL, and make sure that the deferred QC handling via workqueue is not used for this return value. This way, PMPs with CBS will work once again. Note that the starvation referenced in commit 0ea8408 ("ata: libata-scsi: avoid Non-NCQ command starvation") can only happen on libsas ports, and libsas does not support Port Multipliers, thus there is no harm of reverting back to the previous way of deferring commands for PMPs with CBS. Non-libsas ports connected to anything but a PMP with CBS (e.g. a normal drive or a PMP with FBS) will continue using the deferred workqueue, since it does result in lower completion latencies for non-NCQ commands, even though the workqueue is not strictly needed to avoid starvation for non-libsas ports. If we want to modify the scope of the workqueue issuing to also handle PMPs with CBS, then we should ensure that we can save both NCQ and non-NCQ commands in ap->deferred_qc, while also removing the existing PMP CBS handling using ap->excl_link, such that we don't duplicate features. While at it, also add a comment explaining how the ap->excl_link mechanism works. Fixes: 0ea8408 ("ata: libata-scsi: avoid Non-NCQ command starvation") Tested-by: Tommy Kelly <linux@tkel.ly> Reported-by: Tommy Kelly <linux@tkel.ly> Closes: https://lore.kernel.org/linux-ide/ce09cc21-a8e9-4845-b205-35411e22fba9@tkel.ly/ Reviewed-by: Damien Le Moal <dlemoal@kernel.org> Signed-off-by: Niklas Cassel <cassel@kernel.org>
1 parent ce45488 commit f233124

4 files changed

Lines changed: 26 additions & 2 deletions

File tree

drivers/ata/libata-pmp.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,24 @@ int sata_pmp_qc_defer_cmd_switch(struct ata_queued_cmd *qc)
110110
{
111111
struct ata_link *link = qc->dev->link;
112112
struct ata_port *ap = link->ap;
113+
int ret;
113114

114115
if (ap->excl_link == NULL || ap->excl_link == link) {
115116
if (ap->nr_active_links == 0 || ata_link_active(link)) {
116117
qc->flags |= ATA_QCFLAG_CLEAR_EXCL;
117-
return ata_std_qc_defer(qc);
118+
ret = ata_std_qc_defer(qc);
119+
if (ret == ATA_DEFER_LINK)
120+
return ATA_DEFER_LINK_EXCL;
121+
return ret;
118122
}
119123

124+
/*
125+
* Note: ap->excl_link contains the link that is next in line,
126+
* i.e. implicit round robin. If there is only one link
127+
* dispatching, ap->excl_link will be left unclaimed, allowing
128+
* other links to set ap->excl_link, ensuring that the currently
129+
* active link cannot queue any more.
130+
*/
120131
ap->excl_link = link;
121132
}
122133

drivers/ata/libata-scsi.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,6 +1787,14 @@ static int ata_scsi_qc_issue(struct ata_port *ap, struct ata_queued_cmd *qc)
17871787
case ATA_DEFER_LINK:
17881788
ret = SCSI_MLQUEUE_DEVICE_BUSY;
17891789
goto defer_qc;
1790+
case ATA_DEFER_LINK_EXCL:
1791+
/*
1792+
* Drivers making use of ap->excl_link cannot store the QC in
1793+
* ap->deferred_qc, because the ap->excl_link handling is
1794+
* incompatible with the ap->deferred_qc workqueue handling.
1795+
*/
1796+
ret = SCSI_MLQUEUE_DEVICE_BUSY;
1797+
goto free_qc;
17901798
case ATA_DEFER_PORT:
17911799
ret = SCSI_MLQUEUE_HOST_BUSY;
17921800
goto free_qc;

drivers/ata/sata_sil24.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,7 @@ static int sil24_qc_defer(struct ata_queued_cmd *qc)
789789
struct ata_link *link = qc->dev->link;
790790
struct ata_port *ap = link->ap;
791791
u8 prot = qc->tf.protocol;
792+
int ret;
792793

793794
/*
794795
* There is a bug in the chip:
@@ -826,7 +827,10 @@ static int sil24_qc_defer(struct ata_queued_cmd *qc)
826827
qc->flags |= ATA_QCFLAG_CLEAR_EXCL;
827828
}
828829

829-
return ata_std_qc_defer(qc);
830+
ret = ata_std_qc_defer(qc);
831+
if (ret == ATA_DEFER_LINK)
832+
return ATA_DEFER_LINK_EXCL;
833+
return ret;
830834
}
831835

832836
static enum ata_completion_errors sil24_qc_prep(struct ata_queued_cmd *qc)

include/linux/libata.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ enum {
371371
/* return values for ->qc_defer */
372372
ATA_DEFER_LINK = 1,
373373
ATA_DEFER_PORT = 2,
374+
ATA_DEFER_LINK_EXCL = 3,
374375

375376
/* desc_len for ata_eh_info and context */
376377
ATA_EH_DESC_LEN = 80,

0 commit comments

Comments
 (0)