Skip to content

Commit

Permalink
libata: Improve ATA queued command allocation
Browse files Browse the repository at this point in the history
Improve ATA queued command allocation as follows:

- For attaining a qc tag for a SAS host we need to allocate a bit in
  ata_port.sas_tag_allocated bitmap.

  However we already have a unique tag per device in range
  [0, ATA_MAX_QUEUE -1] in the scsi cmnd budget token, so just use that
  instead.

- It is a bit pointless to have ata_qc_new_init() in libata-core.c since it
  pokes scsi internals, so inline it in ata_scsi_qc_new() (in
  libata-scsi.c). Also update Doc accordingly.

- Use standard SCSI helpers set_host_byte() and set_status_byte() in
  ata_scsi_qc_new().

Christoph Hellwig originally contributed the change to inline
ata_qc_new_init().

Signed-off-by: John Garry <john.garry@huawei.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
  • Loading branch information
John Garry authored and Damien Le Moal committed Apr 11, 2022
1 parent c956b92 commit 4f1a22e
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 108 deletions.
11 changes: 0 additions & 11 deletions Documentation/driver-api/libata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -424,12 +424,6 @@ How commands are issued
-----------------------

Internal commands
First, qc is allocated and initialized using :c:func:`ata_qc_new_init`.
Although :c:func:`ata_qc_new_init` doesn't implement any wait or retry
mechanism when qc is not available, internal commands are currently
issued only during initialization and error recovery, so no other
command is active and allocation is guaranteed to succeed.

Once allocated qc's taskfile is initialized for the command to be
executed. qc currently has two mechanisms to notify completion. One
is via ``qc->complete_fn()`` callback and the other is completion
Expand All @@ -447,11 +441,6 @@ SCSI commands
translated. No qc is involved in processing a simulated scmd. The
result is computed right away and the scmd is completed.

For a translated scmd, :c:func:`ata_qc_new_init` is invoked to allocate a
qc and the scmd is translated into the qc. SCSI midlayer's
completion notification function pointer is stored into
``qc->scsidone``.

``qc->complete_fn()`` callback is used for completion notification. ATA
commands use :c:func:`ata_scsi_qc_complete` while ATAPI commands use
:c:func:`atapi_qc_complete`. Both functions end up calling ``qc->scsidone``
Expand Down
48 changes: 1 addition & 47 deletions drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -4566,42 +4566,6 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words)
#endif /* __BIG_ENDIAN */
}

/**
* ata_qc_new_init - Request an available ATA command, and initialize it
* @dev: Device from whom we request an available command structure
* @tag: tag
*
* LOCKING:
* None.
*/

struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag)
{
struct ata_port *ap = dev->link->ap;
struct ata_queued_cmd *qc;

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

/* libsas case */
if (ap->flags & ATA_FLAG_SAS_HOST) {
tag = ata_sas_allocate_tag(ap);
if (tag < 0)
return NULL;
}

qc = __ata_qc_from_tag(ap, tag);
qc->tag = qc->hw_tag = tag;
qc->scsicmd = NULL;
qc->ap = ap;
qc->dev = dev;

ata_qc_reinit(qc);

return qc;
}

/**
* ata_qc_free - free unused ata_queued_cmd
* @qc: Command to complete
Expand All @@ -4614,19 +4578,9 @@ struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag)
*/
void ata_qc_free(struct ata_queued_cmd *qc)
{
struct ata_port *ap;
unsigned int tag;

WARN_ON_ONCE(qc == NULL); /* ata_qc_from_tag _might_ return NULL */
ap = qc->ap;

qc->flags = 0;
tag = qc->tag;
if (ata_tag_valid(tag)) {
if (ata_tag_valid(qc->tag))
qc->tag = ATA_TAG_POISON;
if (ap->flags & ATA_FLAG_SAS_HOST)
ata_sas_free_tag(tag, ap);
}
}

void __ata_qc_complete(struct ata_queued_cmd *qc)
Expand Down
25 changes: 0 additions & 25 deletions drivers/ata/libata-sata.c
Original file line number Diff line number Diff line change
Expand Up @@ -1268,31 +1268,6 @@ int ata_sas_queuecmd(struct scsi_cmnd *cmd, struct ata_port *ap)
}
EXPORT_SYMBOL_GPL(ata_sas_queuecmd);

int ata_sas_allocate_tag(struct ata_port *ap)
{
unsigned int max_queue = ap->host->n_tags;
unsigned int i, tag;

for (i = 0, tag = ap->sas_last_tag + 1; i < max_queue; i++, tag++) {
tag = tag < max_queue ? tag : 0;

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

if (!test_and_set_bit(tag, &ap->sas_tag_allocated)) {
ap->sas_last_tag = tag;
return tag;
}
}
return -1;
}

void ata_sas_free_tag(unsigned int tag, struct ata_port *ap)
{
clear_bit(tag, &ap->sas_tag_allocated);
}

/**
* sata_async_notification - SATA async notification handler
* @ap: ATA port where async notification is received
Expand Down
46 changes: 35 additions & 11 deletions drivers/ata/libata-scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -638,24 +638,48 @@ EXPORT_SYMBOL_GPL(ata_scsi_ioctl);
static struct ata_queued_cmd *ata_scsi_qc_new(struct ata_device *dev,
struct scsi_cmnd *cmd)
{
struct ata_port *ap = dev->link->ap;
struct ata_queued_cmd *qc;
int tag;

qc = ata_qc_new_init(dev, scsi_cmd_to_rq(cmd)->tag);
if (qc) {
qc->scsicmd = cmd;
qc->scsidone = scsi_done;

qc->sg = scsi_sglist(cmd);
qc->n_elem = scsi_sg_count(cmd);
if (unlikely(ap->pflags & ATA_PFLAG_FROZEN))
goto fail;

if (scsi_cmd_to_rq(cmd)->rq_flags & RQF_QUIET)
qc->flags |= ATA_QCFLAG_QUIET;
if (ap->flags & ATA_FLAG_SAS_HOST) {
/*
* SAS hosts may queue > ATA_MAX_QUEUE commands so use
* unique per-device budget token as a tag.
*/
if (WARN_ON_ONCE(cmd->budget_token >= ATA_MAX_QUEUE))
goto fail;
tag = cmd->budget_token;
} else {
cmd->result = (DID_OK << 16) | SAM_STAT_TASK_SET_FULL;
scsi_done(cmd);
tag = scsi_cmd_to_rq(cmd)->tag;
}

qc = __ata_qc_from_tag(ap, tag);
qc->tag = qc->hw_tag = tag;
qc->ap = ap;
qc->dev = dev;

ata_qc_reinit(qc);

qc->scsicmd = cmd;
qc->scsidone = scsi_done;

qc->sg = scsi_sglist(cmd);
qc->n_elem = scsi_sg_count(cmd);

if (scsi_cmd_to_rq(cmd)->rq_flags & RQF_QUIET)
qc->flags |= ATA_QCFLAG_QUIET;

return qc;

fail:
set_host_byte(cmd, DID_OK);
set_status_byte(cmd, SAM_STAT_TASK_SET_FULL);
scsi_done(cmd);
return NULL;
}

static void ata_qc_set_pc_nbytes(struct ata_queued_cmd *qc)
Expand Down
13 changes: 0 additions & 13 deletions drivers/ata/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ static inline void ata_force_cbl(struct ata_port *ap) { }
#endif
extern u64 ata_tf_to_lba(const struct ata_taskfile *tf);
extern u64 ata_tf_to_lba48(const struct ata_taskfile *tf);
extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag);
extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev,
u64 block, u32 n_block, unsigned int tf_flags,
unsigned int tag, int class);
Expand Down Expand Up @@ -91,18 +90,6 @@ extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log,

#define to_ata_port(d) container_of(d, struct ata_port, tdev)

/* libata-sata.c */
#ifdef CONFIG_SATA_HOST
int ata_sas_allocate_tag(struct ata_port *ap);
void ata_sas_free_tag(unsigned int tag, struct ata_port *ap);
#else
static inline int ata_sas_allocate_tag(struct ata_port *ap)
{
return -EOPNOTSUPP;
}
static inline void ata_sas_free_tag(unsigned int tag, struct ata_port *ap) { }
#endif

/* libata-acpi.c */
#ifdef CONFIG_ATA_ACPI
extern unsigned int ata_acpi_gtf_filter;
Expand Down
1 change: 0 additions & 1 deletion include/linux/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,6 @@ struct ata_port {
unsigned int cbl; /* cable type; ATA_CBL_xxx */

struct ata_queued_cmd qcmd[ATA_MAX_QUEUE + 1];
unsigned long sas_tag_allocated; /* for sas tag allocation only */
u64 qc_active;
int nr_active_links; /* #links with active qcs */
unsigned int sas_last_tag; /* track next tag hw expects */
Expand Down

0 comments on commit 4f1a22e

Please sign in to comment.