From: Jeff Garzik <jgarzik@pobox.com>
To: Tejun Heo <htejun@gmail.com>
Cc: Albert Lee <albertcc@tw.ibm.com>, linux-ide@vger.kernel.org
Subject: Re: [PATCH 1/2] libata: implement ata_exec_internal()
Date: Sat, 03 Dec 2005 20:50:18 -0500 [thread overview]
Message-ID: <43924B5A.1070904@pobox.com> (raw)
In-Reply-To: <20051129131717.GA8505@htj.dyndns.org>
Tejun Heo wrote:
> Each user libata internal commands initializes a qc, issues it, waits
> for its completion and checks for errors. This patch factors internal
> command execution sequence into ata_qc_exec_internal(). This change
> 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. This
> change also makes it easy to issue internal commands from multiple
> threads if that becomes necessary.
>
> Signed-off-by: Tejun Heo <htejun@gmail.com>
This is OK in concept, but there are problems in implementation...
General comment: would be nice split up as 'implement
ata.exec.internal' and 'use ata.exec.internal' patches.
> This is take #2 of ata_exec_internal(). qc allocation and tf
> reading-back have also been moved into ata_exec_internal(). Now all
> qc handling is done while properly holding host_set lock removing
> above mentioned rare race conditon (the second item).
>
> Index: work/drivers/scsi/libata-core.c
> ===================================================================
> --- work.orig/drivers/scsi/libata-core.c 2005-11-29 22:04:15.000000000 +0900
> +++ work/drivers/scsi/libata-core.c 2005-11-29 22:06:20.000000000 +0900
> @@ -1047,28 +1047,119 @@ static unsigned int ata_pio_modes(const
> return modes;
> }
>
> -static int ata_qc_wait_err(struct ata_queued_cmd *qc,
> - struct completion *wait)
> +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, unsigned int err_mask)
> {
> - int rc = 0;
> + struct ata_exec_internal_arg *arg = qc->complete_data;
This is what private_data is for. No need to add 'complete_data'.
> + struct completion *waiting = arg->waiting;
>
> - if (wait_for_completion_timeout(wait, 30 * HZ) < 1) {
> - /* timeout handling */
> - unsigned int err_mask = ac_err_mask(ata_chk_status(qc->ap));
> -
> - if (!err_mask) {
> - printk(KERN_WARNING "ata%u: slow completion (cmd %x)\n",
> - qc->ap->id, qc->tf.command);
> - } else {
> - printk(KERN_WARNING "ata%u: qc timeout (cmd %x)\n",
> - qc->ap->id, qc->tf.command);
> - rc = -EIO;
> - }
> + if (!(err_mask & ~AC_ERR_DEV))
> + qc->ap->ops->tf_read(qc->ap, arg->tf);
> + arg->err_mask = 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;
> +
> + if (ata_busy_sleep(ap, ATA_TMOUT_INTERNAL_QUICK, ATA_TMOUT_INTERNAL))
> + return AC_ERR_OTHER;
NAK. This sort of thing really belongs in the execution engine for each
driver. Advanced controllers have zero need for this.
Moreover, why are you adding this, anyway? This is a distinct
regression from the nice "do everything inside ata_qc_issue" model
that's been sprouting.
> + 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;
> + printk("dma_dir=%d buflen=%u nsect=%d\n",
> + dma_dir, buflen, qc->nsect);
> + }
> +
> + arg.waiting = &wait;
> + arg.tf = tf;
> + qc->complete_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)) {
> + int timedout;
> +
> + 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.
> + */
> + timedout = arg.waiting != NULL;
> + if (timedout)
> + ata_qc_complete(qc, 0);
This is not very wise, to pass 'nothing is wrong' to ata_qc_complete()
upon timeout.
> + spin_unlock_irqrestore(&ap->host_set->lock, flags);
>
> - ata_qc_complete(qc, err_mask);
> + if (timedout) {
> + arg.err_mask = ac_err_mask(tf->command);
> + if (!arg.err_mask)
> + printk(KERN_WARNING
> + "ata%u: slow completion (cmd %x)\n",
> + ap->id, command);
> + else
> + printk(KERN_WARNING
> + "ata%u: qc timeout (cmd %x)\n",
> + ap->id, command);
> + }
> }
>
> - return rc;
> + return arg.err_mask;
> +
> + issue_fail:
> + ata_qc_free(qc);
> + spin_unlock_irqrestore(&ap->host_set->lock, flags);
> + return AC_ERR_OTHER;
> }
>
> /**
> @@ -1100,9 +1191,8 @@ static void ata_dev_identify(struct ata_
> u16 tmp;
> unsigned long xfer_modes;
> unsigned int using_edd;
> - DECLARE_COMPLETION(wait);
> - struct ata_queued_cmd *qc;
> - unsigned long flags;
> + struct ata_taskfile tf;
> + unsigned int err_mask;
> int rc;
>
> if (!ata_dev_present(dev)) {
> @@ -1123,40 +1213,26 @@ static void ata_dev_identify(struct ata_
>
> ata_dev_select(ap, device, 1, 1); /* select device 0/1 */
>
> - qc = ata_qc_new_init(ap, dev);
> - BUG_ON(qc == NULL);
> -
> - ata_sg_init_one(qc, dev->id, sizeof(dev->id));
> - qc->dma_dir = DMA_FROM_DEVICE;
> - qc->tf.protocol = ATA_PROT_PIO;
> - qc->nsect = 1;
> -
> retry:
> + ata_tf_init(ap, &tf, device);
> if (dev->class == ATA_DEV_ATA) {
> - qc->tf.command = ATA_CMD_ID_ATA;
> + tf.command = ATA_CMD_ID_ATA;
> DPRINTK("do ATA identify\n");
> } else {
> - qc->tf.command = ATA_CMD_ID_ATAPI;
> + tf.command = ATA_CMD_ID_ATAPI;
> DPRINTK("do ATAPI identify\n");
> }
>
> - qc->waiting = &wait;
> - qc->complete_fn = ata_qc_complete_noop;
> + tf.protocol = ATA_PROT_PIO;
>
> - spin_lock_irqsave(&ap->host_set->lock, flags);
> - rc = ata_qc_issue(qc);
> - spin_unlock_irqrestore(&ap->host_set->lock, flags);
> + err_mask = ata_exec_internal(ap, dev, &tf, DMA_FROM_DEVICE,
> + dev->id, sizeof(dev->id));
>
> - if (rc)
> - goto err_out;
> - else
> - ata_qc_wait_err(qc, &wait);
> -
> - spin_lock_irqsave(&ap->host_set->lock, flags);
> - ap->ops->tf_read(ap, &qc->tf);
> - spin_unlock_irqrestore(&ap->host_set->lock, flags);
> + if (err_mask) {
> + if (err_mask & ~AC_ERR_DEV)
> + goto err_out;
>
> - if (qc->tf.command & ATA_ERR) {
> /*
> * arg! EDD works for all test cases, but seems to return
> * the ATA signature for some ATAPI devices. Until the
> @@ -1169,13 +1245,9 @@ retry:
> * to have this problem.
> */
> if ((using_edd) && (dev->class == ATA_DEV_ATA)) {
> - u8 err = qc->tf.feature;
> + u8 err = tf.feature;
> if (err & ATA_ABORTED) {
> dev->class = ATA_DEV_ATAPI;
> - qc->cursg = 0;
> - qc->cursg_ofs = 0;
> - qc->cursect = 0;
> - qc->nsect = 1;
> goto retry;
> }
> }
> @@ -2288,34 +2360,23 @@ static int ata_choose_xfer_mode(const st
>
> static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev)
> {
> - DECLARE_COMPLETION(wait);
> - struct ata_queued_cmd *qc;
> - int rc;
> - unsigned long flags;
> + struct ata_taskfile tf;
>
> /* set up set-features taskfile */
> DPRINTK("set features - xfer mode\n");
>
> - qc = ata_qc_new_init(ap, dev);
> - BUG_ON(qc == NULL);
> -
> - qc->tf.command = ATA_CMD_SET_FEATURES;
> - qc->tf.feature = SETFEATURES_XFER;
> - qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
> - qc->tf.protocol = ATA_PROT_NODATA;
> - qc->tf.nsect = dev->xfer_mode;
> -
> - qc->waiting = &wait;
> - qc->complete_fn = ata_qc_complete_noop;
> -
> - spin_lock_irqsave(&ap->host_set->lock, flags);
> - rc = ata_qc_issue(qc);
> - spin_unlock_irqrestore(&ap->host_set->lock, flags);
> + ata_tf_init(ap, &tf, dev->devno);
> + tf.command = ATA_CMD_SET_FEATURES;
> + tf.feature = SETFEATURES_XFER;
> + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
> + tf.protocol = ATA_PROT_NODATA;
> + tf.nsect = dev->xfer_mode;
>
> - if (rc)
> + if (ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0)) {
> + printk(KERN_ERR "ata%u: failed to set xfermode, disabled\n",
> + ap->id);
> ata_port_disable(ap);
> - else
> - ata_qc_wait_err(qc, &wait);
> + }
>
> DPRINTK("EXIT\n");
> }
> @@ -2330,41 +2391,25 @@ static void ata_dev_set_xfermode(struct
>
> static void ata_dev_reread_id(struct ata_port *ap, struct ata_device *dev)
> {
> - DECLARE_COMPLETION(wait);
> - struct ata_queued_cmd *qc;
> - unsigned long flags;
> - int rc;
> -
> - qc = ata_qc_new_init(ap, dev);
> - BUG_ON(qc == NULL);
> + struct ata_taskfile tf;
>
> - ata_sg_init_one(qc, dev->id, sizeof(dev->id));
> - qc->dma_dir = DMA_FROM_DEVICE;
> + ata_tf_init(ap, &tf, dev->devno);
>
> if (dev->class == ATA_DEV_ATA) {
> - qc->tf.command = ATA_CMD_ID_ATA;
> + tf.command = ATA_CMD_ID_ATA;
> DPRINTK("do ATA identify\n");
> } else {
> - qc->tf.command = ATA_CMD_ID_ATAPI;
> + tf.command = ATA_CMD_ID_ATAPI;
> DPRINTK("do ATAPI identify\n");
> }
>
> - qc->tf.flags |= ATA_TFLAG_DEVICE;
> - qc->tf.protocol = ATA_PROT_PIO;
> - qc->nsect = 1;
> -
> - qc->waiting = &wait;
> - qc->complete_fn = ata_qc_complete_noop;
> + tf.flags |= ATA_TFLAG_DEVICE;
> + tf.protocol = ATA_PROT_PIO;
>
> - spin_lock_irqsave(&ap->host_set->lock, flags);
> - rc = ata_qc_issue(qc);
> - spin_unlock_irqrestore(&ap->host_set->lock, flags);
> -
> - if (rc)
> + if (ata_exec_internal(ap, dev, &tf, DMA_FROM_DEVICE,
> + dev->id, sizeof(dev->id)))
> goto err_out;
>
> - ata_qc_wait_err(qc, &wait);
> -
> swap_buf_le16(dev->id, ATA_ID_WORDS);
>
> ata_dump_id(dev);
> @@ -2373,6 +2418,7 @@ static void ata_dev_reread_id(struct ata
>
> return;
> err_out:
> + printk(KERN_ERR "ata%u: failed to reread ID, disabled\n", ap->id);
> ata_port_disable(ap);
> }
>
> @@ -2386,10 +2432,7 @@ err_out:
>
> static void ata_dev_init_params(struct ata_port *ap, struct ata_device *dev)
> {
> - DECLARE_COMPLETION(wait);
> - struct ata_queued_cmd *qc;
> - int rc;
> - unsigned long flags;
> + struct ata_taskfile tf;
> u16 sectors = dev->id[6];
> u16 heads = dev->id[3];
>
> @@ -2400,26 +2443,18 @@ static void ata_dev_init_params(struct a
> /* set up init dev params taskfile */
> DPRINTK("init dev params \n");
>
> - qc = ata_qc_new_init(ap, dev);
> - BUG_ON(qc == NULL);
> -
> - qc->tf.command = ATA_CMD_INIT_DEV_PARAMS;
> - qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
> - qc->tf.protocol = ATA_PROT_NODATA;
> - qc->tf.nsect = sectors;
> - qc->tf.device |= (heads - 1) & 0x0f; /* max head = num. of heads - 1 */
> -
> - qc->waiting = &wait;
> - qc->complete_fn = ata_qc_complete_noop;
> -
> - spin_lock_irqsave(&ap->host_set->lock, flags);
> - rc = ata_qc_issue(qc);
> - spin_unlock_irqrestore(&ap->host_set->lock, flags);
> + ata_tf_init(ap, &tf, dev->devno);
> + tf.command = ATA_CMD_INIT_DEV_PARAMS;
> + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
> + tf.protocol = ATA_PROT_NODATA;
> + tf.nsect = sectors;
> + tf.device |= (heads - 1) & 0x0f; /* max head = num. of heads - 1 */
>
> - if (rc)
> + if (ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0)) {
> + printk(KERN_ERR "ata%u: failed to init parameters, disabled\n",
> + ap->id);
> ata_port_disable(ap);
> - else
> - ata_qc_wait_err(qc, &wait);
> + }
>
> DPRINTK("EXIT\n");
next prev parent reply other threads:[~2005-12-04 1:50 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-11-28 16:42 [PATCH] libata: implement ata_qc_exec_internal() Tejun Heo
2005-11-28 17:48 ` Raz Ben-Jehuda(caro)
2005-11-29 3:08 ` Tejun Heo
2005-11-29 3:09 ` Tejun Heo
2005-11-29 3:12 ` Tejun Heo
2005-11-29 7:41 ` Albert Lee
2005-11-29 13:17 ` [PATCH 1/2] libata: implement ata_exec_internal() Tejun Heo
2005-12-04 1:50 ` Jeff Garzik [this message]
2005-12-04 14:04 ` Tejun Heo
2005-12-04 19:21 ` Jeff Garzik
2005-12-05 8:26 ` [PATCH 1/3] " Tejun Heo
2005-12-05 8:28 ` [PATCH 2/4] libata: use ata_exec_internal() Tejun Heo
2005-12-05 8:30 ` [PATCH 3/4] libata: remove unused functions Tejun Heo
2005-12-05 8:31 ` [PATCH 4/4] libata: remove unused qc->waiting Tejun Heo
2005-12-09 9:33 ` [PATCH 1/2] libata: implement ata_exec_internal() Tejun Heo
2005-12-11 4:23 ` [PATCH 1/4] " Tejun Heo
2005-12-13 4:58 ` Jeff Garzik
2005-12-13 5:06 ` Tejun Heo
2005-12-13 5:18 ` Jeff Garzik
2005-12-13 5:48 ` Tejun Heo
2005-12-13 5:49 ` [PATCH 2/4] libata: use ata_exec_internal() Tejun Heo
2005-12-13 5:50 ` [PATCH 3/4] libata: remove unused functions Tejun Heo
2005-12-13 5:51 ` [PATCH 4/4] libata: remove unused qc->waiting Tejun Heo
2005-12-13 6:34 ` [PATCH 1/4] libata: implement ata_exec_internal() Jeff Garzik
2005-12-14 10:01 ` Albert Lee
2005-12-13 5:22 ` Jeff Garzik
2005-12-11 4:24 ` [PATCH 2/4] libata: use ata_exec_internal() Tejun Heo
2005-12-11 4:25 ` [PATCH 3/4] libata: removed unused functions Tejun Heo
2005-12-11 4:26 ` [PATCH 4/4] libata: remove unused qc->waiting Tejun Heo
2005-11-29 13:19 ` [PATCH 2/2] libata: remove qc->waiting Tejun Heo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=43924B5A.1070904@pobox.com \
--to=jgarzik@pobox.com \
--cc=albertcc@tw.ibm.com \
--cc=htejun@gmail.com \
--cc=linux-ide@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.