Skip to content

Commit

Permalink
[libata] fix PIO completion race
Browse files Browse the repository at this point in the history
Make sure we that completion is the final action we take; prior to this
change, another CPU may have changed ap->pio_task_state before we tested
it a final time.

Spotted by, and original patch by Albert Lee @ IBM.

Also includes a minor optimization:  eliminate a ton of unnecessary
queue_work() calls, simply by jumping to the beginning of the FSM
function ata_pio_task().
  • Loading branch information
Jeff Garzik committed Sep 16, 2005
1 parent 065d9ca commit 7fb6ec2
Showing 1 changed file with 23 additions and 14 deletions.
37 changes: 23 additions & 14 deletions drivers/scsi/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2465,9 +2465,12 @@ static unsigned long ata_pio_poll(struct ata_port *ap)
*
* LOCKING:
* None. (executing in kernel thread context)
*
* RETURNS:
* Non-zero if qc completed, zero otherwise.
*/

static void ata_pio_complete (struct ata_port *ap)
static int ata_pio_complete (struct ata_port *ap)
{
struct ata_queued_cmd *qc;
u8 drv_stat;
Expand All @@ -2486,14 +2489,14 @@ static void ata_pio_complete (struct ata_port *ap)
if (drv_stat & (ATA_BUSY | ATA_DRQ)) {
ap->pio_task_state = PIO_ST_LAST_POLL;
ap->pio_task_timeout = jiffies + ATA_TMOUT_PIO;
return;
return 0;
}
}

drv_stat = ata_wait_idle(ap);
if (!ata_ok(drv_stat)) {
ap->pio_task_state = PIO_ST_ERR;
return;
return 0;
}

qc = ata_qc_from_tag(ap, ap->active_tag);
Expand All @@ -2502,6 +2505,10 @@ static void ata_pio_complete (struct ata_port *ap)
ap->pio_task_state = PIO_ST_IDLE;

ata_poll_qc_complete(qc, drv_stat);

/* another command may start at this point */

return 1;
}


Expand Down Expand Up @@ -2709,7 +2716,7 @@ static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes)

next_sg:
if (unlikely(qc->cursg >= qc->n_elem)) {
/*
/*
* The end of qc->sg is reached and the device expects
* more data to transfer. In order not to overrun qc->sg
* and fulfill length specified in the byte count register,
Expand All @@ -2721,7 +2728,7 @@ static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes)
unsigned int i;

if (words) /* warning if bytes > 1 */
printk(KERN_WARNING "ata%u: %u bytes trailing data\n",
printk(KERN_WARNING "ata%u: %u bytes trailing data\n",
ap->id, bytes);

for (i = 0; i < words; i++)
Expand Down Expand Up @@ -2849,9 +2856,7 @@ static void ata_pio_block(struct ata_port *ap)
if (is_atapi_taskfile(&qc->tf)) {
/* no more data to transfer or unsupported ATAPI command */
if ((status & ATA_DRQ) == 0) {
ap->pio_task_state = PIO_ST_IDLE;

ata_poll_qc_complete(qc, status);
ap->pio_task_state = PIO_ST_LAST;
return;
}

Expand Down Expand Up @@ -2887,7 +2892,12 @@ static void ata_pio_error(struct ata_port *ap)
static void ata_pio_task(void *_data)
{
struct ata_port *ap = _data;
unsigned long timeout = 0;
unsigned long timeout;
int qc_completed;

fsm_start:
timeout = 0;
qc_completed = 0;

switch (ap->pio_task_state) {
case PIO_ST_IDLE:
Expand All @@ -2898,7 +2908,7 @@ static void ata_pio_task(void *_data)
break;

case PIO_ST_LAST:
ata_pio_complete(ap);
qc_completed = ata_pio_complete(ap);
break;

case PIO_ST_POLL:
Expand All @@ -2913,10 +2923,9 @@ static void ata_pio_task(void *_data)
}

if (timeout)
queue_delayed_work(ata_wq, &ap->pio_task,
timeout);
else
queue_work(ata_wq, &ap->pio_task);
queue_delayed_work(ata_wq, &ap->pio_task, timeout);
else if (!qc_completed)
goto fsm_start;
}

static void atapi_request_sense(struct ata_port *ap, struct ata_device *dev,
Expand Down

0 comments on commit 7fb6ec2

Please sign in to comment.