Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 31cc23b3 authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik
Browse files

libata-pmp-prep: implement ops->qc_defer()



Controllers which support PMP have various restrictions on which
combinations of commands are allowed to what number of devices
concurrently.  This patch implements ops->qc_defer() which determines
whether a qc can be issued at the moment or should be deferred.

If the function returns ATA_DEFER_LINK, the qc will be deferred until
a qc completes on the link.  If ATA_DEFER_PORT, until a qc completes
on any link.  The defer conditions are advisory and in general
ATA_DEFER_LINK can be considered as lower priority deferring than
ATA_DEFER_PORT.

ops->qc_defer() replaces fixed ata_scmd_need_defer().  For standard
NCQ/non-NCQ exclusion, ata_std_qc_defer() is implemented.  ahci and
sata_sil24 are converted to use ata_std_qc_defer().

ops->qc_defer() is heavier than the original mechanism because full qc
is prepped before determining to defer it, but various information is
needed to determine defer conditinos and fully translating a qc is the
only way to supply such information in generic manner.

IMHO, this shouldn't cause any noticeable performance issues as

* for most cases deferring occurs rarely (except for NCQ-aware
  cmd-switching PMP)
* translation itself isn't that expensive
* once deferred the command won't be repeated until another command
  completes which usually is a very long time cpu-wise.

Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent fb7fd614
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -268,6 +268,7 @@ static const struct ata_port_operations ahci_ops = {


	.tf_read		= ahci_tf_read,
	.tf_read		= ahci_tf_read,


	.qc_defer		= ata_std_qc_defer,
	.qc_prep		= ahci_qc_prep,
	.qc_prep		= ahci_qc_prep,
	.qc_issue		= ahci_qc_issue,
	.qc_issue		= ahci_qc_issue,


@@ -298,6 +299,7 @@ static const struct ata_port_operations ahci_vt8251_ops = {


	.tf_read		= ahci_tf_read,
	.tf_read		= ahci_tf_read,


	.qc_defer		= ata_std_qc_defer,
	.qc_prep		= ahci_qc_prep,
	.qc_prep		= ahci_qc_prep,
	.qc_issue		= ahci_qc_issue,
	.qc_issue		= ahci_qc_issue,


+31 −0
Original line number Original line Diff line number Diff line
@@ -4345,6 +4345,36 @@ int ata_check_atapi_dma(struct ata_queued_cmd *qc)
	return 0;
	return 0;
}
}


/**
 *	ata_std_qc_defer - Check whether a qc needs to be deferred
 *	@qc: ATA command in question
 *
 *	Non-NCQ commands cannot run with any other command, NCQ or
 *	not.  As upper layer only knows the queue depth, we are
 *	responsible for maintaining exclusion.  This function checks
 *	whether a new command @qc can be issued.
 *
 *	LOCKING:
 *	spin_lock_irqsave(host lock)
 *
 *	RETURNS:
 *	ATA_DEFER_* if deferring is needed, 0 otherwise.
 */
int ata_std_qc_defer(struct ata_queued_cmd *qc)
{
	struct ata_link *link = qc->dev->link;

	if (qc->tf.protocol == ATA_PROT_NCQ) {
		if (!ata_tag_valid(link->active_tag))
			return 0;
	} else {
		if (!ata_tag_valid(link->active_tag) && !link->sactive)
			return 0;
	}

	return ATA_DEFER_LINK;
}

/**
/**
 *	ata_qc_prep - Prepare taskfile for submission
 *	ata_qc_prep - Prepare taskfile for submission
 *	@qc: Metadata associated with taskfile to be prepared
 *	@qc: Metadata associated with taskfile to be prepared
@@ -7111,6 +7141,7 @@ EXPORT_SYMBOL_GPL(ata_interrupt);
EXPORT_SYMBOL_GPL(ata_do_set_mode);
EXPORT_SYMBOL_GPL(ata_do_set_mode);
EXPORT_SYMBOL_GPL(ata_data_xfer);
EXPORT_SYMBOL_GPL(ata_data_xfer);
EXPORT_SYMBOL_GPL(ata_data_xfer_noirq);
EXPORT_SYMBOL_GPL(ata_data_xfer_noirq);
EXPORT_SYMBOL_GPL(ata_std_qc_defer);
EXPORT_SYMBOL_GPL(ata_qc_prep);
EXPORT_SYMBOL_GPL(ata_qc_prep);
EXPORT_SYMBOL_GPL(ata_dumb_qc_prep);
EXPORT_SYMBOL_GPL(ata_dumb_qc_prep);
EXPORT_SYMBOL_GPL(ata_noop_qc_prep);
EXPORT_SYMBOL_GPL(ata_noop_qc_prep);
+26 −36
Original line number Original line Diff line number Diff line
@@ -749,6 +749,13 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev)
{
{
	sdev->use_10_for_rw = 1;
	sdev->use_10_for_rw = 1;
	sdev->use_10_for_ms = 1;
	sdev->use_10_for_ms = 1;

	/* Schedule policy is determined by ->qc_defer() callback and
	 * it needs to see every deferred qc.  Set dev_blocked to 1 to
	 * prevent SCSI midlayer from automatically deferring
	 * requests.
	 */
	sdev->max_device_blocked = 1;
}
}


static void ata_scsi_dev_config(struct scsi_device *sdev,
static void ata_scsi_dev_config(struct scsi_device *sdev,
@@ -1415,37 +1422,6 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
	ata_qc_free(qc);
	ata_qc_free(qc);
}
}


/**
 *	ata_scmd_need_defer - Check whether we need to defer scmd
 *	@dev: ATA device to which the command is addressed
 *	@is_io: Is the command IO (and thus possibly NCQ)?
 *
 *	NCQ and non-NCQ commands cannot run together.  As upper layer
 *	only knows the queue depth, we are responsible for maintaining
 *	exclusion.  This function checks whether a new command can be
 *	issued to @dev.
 *
 *	LOCKING:
 *	spin_lock_irqsave(host lock)
 *
 *	RETURNS:
 *	1 if deferring is needed, 0 otherwise.
 */
static int ata_scmd_need_defer(struct ata_device *dev, int is_io)
{
	struct ata_link *link = dev->link;
	int is_ncq = is_io && ata_ncq_enabled(dev);

	if (is_ncq) {
		if (!ata_tag_valid(link->active_tag))
			return 0;
	} else {
		if (!ata_tag_valid(link->active_tag) && !link->sactive)
			return 0;
	}
	return 1;
}

/**
/**
 *	ata_scsi_translate - Translate then issue SCSI command to ATA device
 *	ata_scsi_translate - Translate then issue SCSI command to ATA device
 *	@dev: ATA device to which the command is addressed
 *	@dev: ATA device to which the command is addressed
@@ -1477,14 +1453,12 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
			      void (*done)(struct scsi_cmnd *),
			      void (*done)(struct scsi_cmnd *),
			      ata_xlat_func_t xlat_func)
			      ata_xlat_func_t xlat_func)
{
{
	struct ata_port *ap = dev->link->ap;
	struct ata_queued_cmd *qc;
	struct ata_queued_cmd *qc;
	int is_io = xlat_func == ata_scsi_rw_xlat;
	int rc;


	VPRINTK("ENTER\n");
	VPRINTK("ENTER\n");


	if (unlikely(ata_scmd_need_defer(dev, is_io)))
		goto defer;

	qc = ata_scsi_qc_new(dev, cmd, done);
	qc = ata_scsi_qc_new(dev, cmd, done);
	if (!qc)
	if (!qc)
		goto err_mem;
		goto err_mem;
@@ -1508,6 +1482,11 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
	if (xlat_func(qc))
	if (xlat_func(qc))
		goto early_finish;
		goto early_finish;


	if (ap->ops->qc_defer) {
		if ((rc = ap->ops->qc_defer(qc)))
			goto defer;
	}

	/* select device, send command to hardware */
	/* select device, send command to hardware */
	ata_qc_issue(qc);
	ata_qc_issue(qc);


@@ -1529,8 +1508,12 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
	return 0;
	return 0;


defer:
defer:
	ata_qc_free(qc);
	DPRINTK("EXIT - defer\n");
	DPRINTK("EXIT - defer\n");
	if (rc == ATA_DEFER_LINK)
		return SCSI_MLQUEUE_DEVICE_BUSY;
		return SCSI_MLQUEUE_DEVICE_BUSY;
	else
		return SCSI_MLQUEUE_HOST_BUSY;
}
}


/**
/**
@@ -3034,6 +3017,13 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
		shost->max_channel = 1;
		shost->max_channel = 1;
		shost->max_cmd_len = 16;
		shost->max_cmd_len = 16;


		/* Schedule policy is determined by ->qc_defer()
		 * callback and it needs to see every deferred qc.
		 * Set host_blocked to 1 to prevent SCSI midlayer from
		 * automatically deferring requests.
		 */
		shost->max_host_blocked = 1;

		rc = scsi_add_host(ap->scsi_host, ap->host->dev);
		rc = scsi_add_host(ap->scsi_host, ap->host->dev);
		if (rc)
		if (rc)
			goto err_add;
			goto err_add;
+1 −0
Original line number Original line Diff line number Diff line
@@ -423,6 +423,7 @@ static const struct ata_port_operations nv_adma_ops = {
	.bmdma_start		= ata_bmdma_start,
	.bmdma_start		= ata_bmdma_start,
	.bmdma_stop		= ata_bmdma_stop,
	.bmdma_stop		= ata_bmdma_stop,
	.bmdma_status		= ata_bmdma_status,
	.bmdma_status		= ata_bmdma_status,
	.qc_defer		= ata_std_qc_defer,
	.qc_prep		= nv_adma_qc_prep,
	.qc_prep		= nv_adma_qc_prep,
	.qc_issue		= nv_adma_qc_issue,
	.qc_issue		= nv_adma_qc_issue,
	.freeze			= nv_adma_freeze,
	.freeze			= nv_adma_freeze,
+1 −0
Original line number Original line Diff line number Diff line
@@ -393,6 +393,7 @@ static const struct ata_port_operations sil24_ops = {


	.tf_read		= sil24_tf_read,
	.tf_read		= sil24_tf_read,


	.qc_defer		= ata_std_qc_defer,
	.qc_prep		= sil24_qc_prep,
	.qc_prep		= sil24_qc_prep,
	.qc_issue		= sil24_qc_issue,
	.qc_issue		= sil24_qc_issue,


Loading