diff options
author | Tejun Heo <htejun@gmail.com> | 2005-08-22 10:12:45 +0200 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-08-23 07:05:55 +0200 |
commit | 40e8c82c74b9be793601e098fd1313bc2632c5dc (patch) | |
tree | b3c89b2aaa5d477e8db5acc9bf4ee6f31acf3db8 /drivers/scsi | |
parent | [PATCH] fix atapi_packet_task vs. intr race (take 2) (diff) | |
download | linux-40e8c82c74b9be793601e098fd1313bc2632c5dc.tar.xz linux-40e8c82c74b9be793601e098fd1313bc2632c5dc.zip |
[PATCH] libata: implement ata_poll_qc_complete and use it in polling functions
[PATCH libata-dev-2.6:upstream] implement ata_poll_qc_complete and use it in polling functions
Previously, libata polling functions turned irq back on and completed
qc commands without holding host lock. This creates a race condition
between the polling task and interrupts from other ports on the same
host set or spurious interrupt from itself.
This patch implements ata_poll_qc_complete which enables irq and
completes qc atomically and convert all polling functions.
Note: atapi_packet_task() didn't use to turn irq back on or clear
ATA_FLAG_NOINTR on error exits. This patch makes it use
ata_poll_qc_complete which does both.
Note: With this change, ALL invocations of ata_qc_complete() are now
done under host_set lock.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/libata-core.c | 34 |
1 files changed, 24 insertions, 10 deletions
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index c92439fe5dae..2f68563ac3a3 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -2402,6 +2402,26 @@ static int ata_sg_setup(struct ata_queued_cmd *qc) } /** + * ata_poll_qc_complete - turn irq back on and finish qc + * @qc: Command to complete + * @drv_stat: ATA status register content + * + * LOCKING: + * None. (grabs host lock) + */ + +void ata_poll_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) +{ + struct ata_port *ap = qc->ap; + + spin_lock_irq(&ap->host_set->lock); + ap->flags &= ~ATA_FLAG_NOINTR; + ata_irq_on(ap); + ata_qc_complete(qc, drv_stat); + spin_unlock_irq(&ap->host_set->lock); +} + +/** * ata_pio_poll - * @ap: * @@ -2492,9 +2512,7 @@ static void ata_pio_complete (struct ata_port *ap) ap->pio_task_state = PIO_ST_IDLE; - ata_irq_on(ap); - - ata_qc_complete(qc, drv_stat); + ata_poll_qc_complete(qc, drv_stat); } @@ -2844,9 +2862,7 @@ static void ata_pio_block(struct ata_port *ap) if ((status & ATA_DRQ) == 0) { ap->pio_task_state = PIO_ST_IDLE; - ata_irq_on(ap); - - ata_qc_complete(qc, status); + ata_poll_qc_complete(qc, status); return; } @@ -2876,9 +2892,7 @@ static void ata_pio_error(struct ata_port *ap) ap->pio_task_state = PIO_ST_IDLE; - ata_irq_on(ap); - - ata_qc_complete(qc, drv_stat | ATA_ERR); + ata_poll_qc_complete(qc, drv_stat | ATA_ERR); } static void ata_pio_task(void *_data) @@ -3791,7 +3805,7 @@ static void atapi_packet_task(void *_data) return; err_out: - ata_qc_complete(qc, ATA_ERR); + ata_poll_qc_complete(qc, ATA_ERR); } |