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

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

[PATCH] libata: implement ata_exec_internal()



This patch implements ata_exec_internal() function which performs
libata internal command execution.  Previously, this was done by each
user by manually initializing a qc, issueing it, waiting for its
completion and handling errors.  In addition to obvious code
factoring, using ata_exec_internal() fixes the following bugs.

* qc not freed on issue failure
* ap->qactive clearing could race with the next internal command
* race between timeout handling and irq
* ignoring error condition not represented in tf->status

Also, qc & hardware are not accessed anymore once it's completed,
making internal commands more conformant with general semantics.
ata_exec_internal() also makes it easy to issue internal commands from
multiple threads if that becomes necessary.

This patch only implements ata_exec_internal().  A following patch
will convert all users.

Signed-off-by: default avatarTejun Heo <htejun@gmail.com>

--

Jeff, all patches have been regenerated against upstream branch as of
today.  (575ab52a)

Also, I took out a debug printk from ata_exec_internal (don't know how
that one got left there).  Other than that, all patches are identical
to the previous posting.

Thanks. :-)
Signed-off-by: default avatarJeff Garzik <jgarzik@pobox.com>
parent 575ab52a
Loading
Loading
Loading
Loading
+99 −0
Original line number Diff line number Diff line
@@ -1046,6 +1046,105 @@ static unsigned int ata_pio_modes(const struct ata_device *adev)
	return modes;
}

struct ata_exec_internal_arg {
	unsigned int err_mask;
	struct ata_taskfile *tf;
	struct completion *waiting;
};

int ata_qc_complete_internal(struct ata_queued_cmd *qc)
{
	struct ata_exec_internal_arg *arg = qc->private_data;
	struct completion *waiting = arg->waiting;

	if (!(qc->err_mask & ~AC_ERR_DEV))
		qc->ap->ops->tf_read(qc->ap, arg->tf);
	arg->err_mask = qc->err_mask;
	arg->waiting = NULL;
	complete(waiting);

	return 0;
}

/**
 *	ata_exec_internal - execute libata internal command
 *	@ap: Port to which the command is sent
 *	@dev: Device to which the command is sent
 *	@tf: Taskfile registers for the command and the result
 *	@dma_dir: Data tranfer direction of the command
 *	@buf: Data buffer of the command
 *	@buflen: Length of data buffer
 *
 *	Executes libata internal command with timeout.  @tf contains
 *	command on entry and result on return.  Timeout and error
 *	conditions are reported via return value.  No recovery action
 *	is taken after a command times out.  It's caller's duty to
 *	clean up after timeout.
 *
 *	LOCKING:
 *	None.  Should be called with kernel context, might sleep.
 */

static unsigned
ata_exec_internal(struct ata_port *ap, struct ata_device *dev,
		  struct ata_taskfile *tf,
		  int dma_dir, void *buf, unsigned int buflen)
{
	u8 command = tf->command;
	struct ata_queued_cmd *qc;
	DECLARE_COMPLETION(wait);
	unsigned long flags;
	struct ata_exec_internal_arg arg;

	spin_lock_irqsave(&ap->host_set->lock, flags);

	qc = ata_qc_new_init(ap, dev);
	BUG_ON(qc == NULL);

	qc->tf = *tf;
	qc->dma_dir = dma_dir;
	if (dma_dir != DMA_NONE) {
		ata_sg_init_one(qc, buf, buflen);
		qc->nsect = buflen / ATA_SECT_SIZE;
	}

	arg.waiting = &wait;
	arg.tf = tf;
	qc->private_data = &arg;
	qc->complete_fn = ata_qc_complete_internal;

	if (ata_qc_issue(qc))
		goto issue_fail;

	spin_unlock_irqrestore(&ap->host_set->lock, flags);

	if (!wait_for_completion_timeout(&wait, ATA_TMOUT_INTERNAL)) {
		spin_lock_irqsave(&ap->host_set->lock, flags);

		/* We're racing with irq here.  If we lose, the
		 * following test prevents us from completing the qc
		 * again.  If completion irq occurs after here but
		 * before the caller cleans up, it will result in a
		 * spurious interrupt.  We can live with that.
		 */
		if (arg.waiting) {
			qc->err_mask = AC_ERR_OTHER;
			ata_qc_complete(qc);
			printk(KERN_WARNING "ata%u: qc timeout (cmd 0x%x)\n",
			       ap->id, command);
		}

		spin_unlock_irqrestore(&ap->host_set->lock, flags);
	}

	return arg.err_mask;

 issue_fail:
	ata_qc_free(qc);
	spin_unlock_irqrestore(&ap->host_set->lock, flags);
	return AC_ERR_OTHER;
}

static int ata_qc_wait_err(struct ata_queued_cmd *qc,
			   struct completion *wait)
{
+2 −0
Original line number Diff line number Diff line
@@ -135,6 +135,8 @@ enum {
	ATA_TMOUT_BOOT_QUICK	= 7 * HZ,	/* hueristic */
	ATA_TMOUT_CDB		= 30 * HZ,
	ATA_TMOUT_CDB_QUICK	= 5 * HZ,
	ATA_TMOUT_INTERNAL	= 30 * HZ,
	ATA_TMOUT_INTERNAL_QUICK = 5 * HZ,

	/* ATA bus states */
	BUS_UNKNOWN		= 0,