Skip to content

Commit 93c4aa4

Browse files
Niklas CasselDamien Le Moal
authored andcommitted
ata: libata: read the shared status for successful NCQ commands once
Currently, the status is being read for each QC, inside ata_qc_complete(), which means that QCs being completed by ata_qc_complete_multiple() (i.e. multiple QCs completed during a single interrupt), can have different status and error bits set. This is because the FIS Receive Area will get updated as soon as the HBA receives a new FIS from the device in the NCQ case. Here is an example of the problem: ata14.00: ata_qc_complete_multiple: done_mask: 0x180000 qc tag: 19 cmd: 0x61 flags: 0x11b err_mask: 0x0 tf->status: 0x40 qc tag: 20 cmd: 0x61 flags: 0x11b err_mask: 0x0 tf->status: 0x43 A print in ata_qc_complete_multiple(), shows that done_mask is: 0x180000 which means that tag 19 and 20 were completed. Another print in ata_qc_complete(), after the call to fill_result_tf(), shows that tag 19 and 20 have different status values, even though they were completed in the same ata_qc_complete_multiple() call. If PMP is not enabled, simply read the status and error once, before calling ata_qc_complete() for each QC. Without PMP, we know that all QCs must share the same status and error values. If PMP is enabled, we also read the status before calling ata_qc_complete(), however, we still read the status for each QC, since the QCs can belong to different PMP links (which means that the QCs does not necessarily share the same status and error values). Do all this by introducing the new port operation .qc_ncq_fill_rtf. If set, this operation is called in ata_qc_complete_multiple() to set the result tf for all completed QCs signaled by the last SDB FIS received. QCs that have their result tf filled are marked with the new flag ATA_QCFLAG_RTF_FILLED so that any later execution of the qc_fill_rtf port operation does nothing (e.g. when called from ata_qc_complete()). Co-developed-by: Damien Le Moal <[email protected]> Signed-off-by: Damien Le Moal <[email protected]> Signed-off-by: Niklas Cassel <[email protected]>
1 parent 931139a commit 93c4aa4

File tree

3 files changed

+92
-3
lines changed

3 files changed

+92
-3
lines changed

drivers/ata/libahci.c

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
5656
static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
5757
static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
5858
static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
59+
static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask);
5960
static int ahci_port_start(struct ata_port *ap);
6061
static void ahci_port_stop(struct ata_port *ap);
6162
static enum ata_completion_errors ahci_qc_prep(struct ata_queued_cmd *qc);
@@ -157,6 +158,7 @@ struct ata_port_operations ahci_ops = {
157158
.qc_prep = ahci_qc_prep,
158159
.qc_issue = ahci_qc_issue,
159160
.qc_fill_rtf = ahci_qc_fill_rtf,
161+
.qc_ncq_fill_rtf = ahci_qc_ncq_fill_rtf,
160162

161163
.freeze = ahci_freeze,
162164
.thaw = ahci_thaw,
@@ -2058,6 +2060,13 @@ static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
20582060
struct ahci_port_priv *pp = qc->ap->private_data;
20592061
u8 *rx_fis = pp->rx_fis;
20602062

2063+
/*
2064+
* rtf may already be filled (e.g. for successful NCQ commands).
2065+
* If that is the case, we have nothing to do.
2066+
*/
2067+
if (qc->flags & ATA_QCFLAG_RTF_FILLED)
2068+
return;
2069+
20612070
if (pp->fbs_enabled)
20622071
rx_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ;
20632072

@@ -2071,6 +2080,9 @@ static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
20712080
!(qc->flags & ATA_QCFLAG_EH)) {
20722081
ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf);
20732082
qc->result_tf.status = (rx_fis + RX_FIS_PIO_SETUP)[15];
2083+
qc->flags |= ATA_QCFLAG_RTF_FILLED;
2084+
return;
2085+
}
20742086

20752087
/*
20762088
* For NCQ commands, we never get a D2H FIS, so reading the D2H Register
@@ -2080,13 +2092,85 @@ static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
20802092
* instead. However, the SDB FIS does not contain the LBA, so we can't
20812093
* use the ata_tf_from_fis() helper.
20822094
*/
2083-
} else if (ata_is_ncq(qc->tf.protocol)) {
2095+
if (ata_is_ncq(qc->tf.protocol)) {
20842096
const u8 *fis = rx_fis + RX_FIS_SDB;
20852097

2098+
/*
2099+
* Successful NCQ commands have been filled already.
2100+
* A failed NCQ command will read the status here.
2101+
* (Note that a failed NCQ command will get a more specific
2102+
* error when reading the NCQ Command Error log.)
2103+
*/
20862104
qc->result_tf.status = fis[2];
20872105
qc->result_tf.error = fis[3];
2088-
} else
2089-
ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
2106+
qc->flags |= ATA_QCFLAG_RTF_FILLED;
2107+
return;
2108+
}
2109+
2110+
ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
2111+
qc->flags |= ATA_QCFLAG_RTF_FILLED;
2112+
}
2113+
2114+
static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask)
2115+
{
2116+
struct ahci_port_priv *pp = ap->private_data;
2117+
const u8 *fis;
2118+
2119+
/* No outstanding commands. */
2120+
if (!ap->qc_active)
2121+
return;
2122+
2123+
/*
2124+
* FBS not enabled, so read status and error once, since they are shared
2125+
* for all QCs.
2126+
*/
2127+
if (!pp->fbs_enabled) {
2128+
u8 status, error;
2129+
2130+
/* No outstanding NCQ commands. */
2131+
if (!pp->active_link->sactive)
2132+
return;
2133+
2134+
fis = pp->rx_fis + RX_FIS_SDB;
2135+
status = fis[2];
2136+
error = fis[3];
2137+
2138+
while (done_mask) {
2139+
struct ata_queued_cmd *qc;
2140+
unsigned int tag = __ffs64(done_mask);
2141+
2142+
qc = ata_qc_from_tag(ap, tag);
2143+
if (qc && ata_is_ncq(qc->tf.protocol)) {
2144+
qc->result_tf.status = status;
2145+
qc->result_tf.error = error;
2146+
qc->flags |= ATA_QCFLAG_RTF_FILLED;
2147+
}
2148+
done_mask &= ~(1ULL << tag);
2149+
}
2150+
2151+
return;
2152+
}
2153+
2154+
/*
2155+
* FBS enabled, so read the status and error for each QC, since the QCs
2156+
* can belong to different PMP links. (Each PMP link has its own FIS
2157+
* Receive Area.)
2158+
*/
2159+
while (done_mask) {
2160+
struct ata_queued_cmd *qc;
2161+
unsigned int tag = __ffs64(done_mask);
2162+
2163+
qc = ata_qc_from_tag(ap, tag);
2164+
if (qc && ata_is_ncq(qc->tf.protocol)) {
2165+
fis = pp->rx_fis;
2166+
fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ;
2167+
fis += RX_FIS_SDB;
2168+
qc->result_tf.status = fis[2];
2169+
qc->result_tf.error = fis[3];
2170+
qc->flags |= ATA_QCFLAG_RTF_FILLED;
2171+
}
2172+
done_mask &= ~(1ULL << tag);
2173+
}
20902174
}
20912175

20922176
static void ahci_freeze(struct ata_port *ap)

drivers/ata/libata-sata.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,9 @@ int ata_qc_complete_multiple(struct ata_port *ap, u64 qc_active)
655655
return -EINVAL;
656656
}
657657

658+
if (ap->ops->qc_ncq_fill_rtf)
659+
ap->ops->qc_ncq_fill_rtf(ap, done_mask);
660+
658661
while (done_mask) {
659662
struct ata_queued_cmd *qc;
660663
unsigned int tag = __ffs64(done_mask);

include/linux/libata.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ enum {
200200
/* struct ata_queued_cmd flags */
201201
ATA_QCFLAG_ACTIVE = (1 << 0), /* cmd not yet ack'd to scsi lyer */
202202
ATA_QCFLAG_DMAMAP = (1 << 1), /* SG table is DMA mapped */
203+
ATA_QCFLAG_RTF_FILLED = (1 << 2), /* result TF has been filled */
203204
ATA_QCFLAG_IO = (1 << 3), /* standard IO command */
204205
ATA_QCFLAG_RESULT_TF = (1 << 4), /* result TF requested */
205206
ATA_QCFLAG_CLEAR_EXCL = (1 << 5), /* clear excl_link on completion */
@@ -877,6 +878,7 @@ struct ata_port_operations {
877878
enum ata_completion_errors (*qc_prep)(struct ata_queued_cmd *qc);
878879
unsigned int (*qc_issue)(struct ata_queued_cmd *qc);
879880
void (*qc_fill_rtf)(struct ata_queued_cmd *qc);
881+
void (*qc_ncq_fill_rtf)(struct ata_port *ap, u64 done_mask);
880882

881883
/*
882884
* Configuration and exception handling

0 commit comments

Comments
 (0)