Skip to content

Commit

Permalink
libata/ahci: accommodate tag ordered controllers
Browse files Browse the repository at this point in the history
The AHCI spec allows implementations to issue commands in tag order
rather than FIFO order:

	5.3.2.12 P:SelectCmd
	HBA sets pSlotLoc = (pSlotLoc + 1) mod (CAP.NCS + 1)
	or HBA selects the command to issue that has had the
	PxCI bit set to '1' longer than any other command
	pending to be issued.

The result is that commands posted sequentially (time-wise) may play out
of sequence when issued by hardware.

This behavior has likely been hidden by drives that arrange for commands
to complete in issue order.  However, it appears recent drives (two from
different vendors that we have found so far) inflict out-of-order
completions as a matter of course.  So, we need to take care to maintain
ordered submission, otherwise we risk triggering a drive to fall out of
sequential-io automation and back to random-io processing, which incurs
large latency and degrades throughput.

This issue was found in simple benchmarks where QD=2 seq-write
performance was 30-50% *greater* than QD=32 seq-write performance.

Tagging for -stable and making the change globally since it has a low
risk-to-reward ratio.  Also, word is that recent versions of an unnamed
OS also does it this way now.  So, drives in the field are already
experienced with this tag ordering scheme.

Cc: <[email protected]>
Cc: Dave Jiang <[email protected]>
Cc: Ed Ciechanowski <[email protected]>
Reviewed-by: Matthew Wilcox <[email protected]>
Signed-off-by: Dan Williams <[email protected]>
Signed-off-by: Tejun Heo <[email protected]>
  • Loading branch information
djbw authored and htejun committed Apr 18, 2014
1 parent 2cf532f commit 8a4aeec
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 8 deletions.
21 changes: 13 additions & 8 deletions drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -4794,21 +4794,26 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words)
static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap)
{
struct ata_queued_cmd *qc = NULL;
unsigned int i;
unsigned int i, tag;

/* no command while frozen */
if (unlikely(ap->pflags & ATA_PFLAG_FROZEN))
return NULL;

/* the last tag is reserved for internal command. */
for (i = 0; i < ATA_MAX_QUEUE - 1; i++)
if (!test_and_set_bit(i, &ap->qc_allocated)) {
qc = __ata_qc_from_tag(ap, i);
for (i = 0; i < ATA_MAX_QUEUE; i++) {
tag = (i + ap->last_tag + 1) % ATA_MAX_QUEUE;

/* the last tag is reserved for internal command. */
if (tag == ATA_TAG_INTERNAL)
continue;

if (!test_and_set_bit(tag, &ap->qc_allocated)) {
qc = __ata_qc_from_tag(ap, tag);
qc->tag = tag;
ap->last_tag = tag;
break;
}

if (qc)
qc->tag = i;
}

return qc;
}
Expand Down
1 change: 1 addition & 0 deletions include/linux/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,7 @@ struct ata_port {
unsigned long qc_allocated;
unsigned int qc_active;
int nr_active_links; /* #links with active qcs */
unsigned int last_tag; /* track next tag hw expects */

struct ata_link link; /* host default link */
struct ata_link *slave_link; /* see ata_slave_link_init() */
Expand Down

0 comments on commit 8a4aeec

Please sign in to comment.