linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
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");


  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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).