02_atapi_fix-atapi_packet_task-race.patch
Protect ATAPI_NODATA and ATAPI_DMA commands from being
finished by ata_host_intr before atapi_packet_task is done w/
the commands. Previously, such ATAPI commands could have been
completed by interrupts from other ports in the same host_set
or by spurious interrupt, making atapi_packet_task access
already finished commands.
Signed-off-by: Tejun Heo <[EMAIL PROTECTED]>
libata-core.c | 34 +++++++++++++++++++++-------------
1 files changed, 21 insertions(+), 13 deletions(-)
Index: work/drivers/scsi/libata-core.c
===================================================================
--- work.orig/drivers/scsi/libata-core.c 2005-08-20 18:12:30.000000000
+0900
+++ work/drivers/scsi/libata-core.c 2005-08-20 18:16:49.000000000 +0900
@@ -3218,11 +3218,13 @@ int ata_qc_issue_prot(struct ata_queued_
break;
case ATA_PROT_ATAPI_NODATA:
+ ata_qc_set_polling(qc);
ata_tf_to_host_nolock(ap, &qc->tf);
queue_work(ata_wq, &ap->packet_task);
break;
case ATA_PROT_ATAPI_DMA:
+ ata_qc_set_polling(qc);
ap->ops->tf_load(ap, &qc->tf); /* load tf registers */
ap->ops->bmdma_setup(qc); /* set up bmdma */
queue_work(ata_wq, &ap->packet_task);
@@ -3580,8 +3582,8 @@ irqreturn_t ata_interrupt (int irq, void
struct ata_queued_cmd *qc;
qc = ata_qc_from_tag(ap, ap->active_tag);
- if (qc && (!(qc->tf.ctl & ATA_NIEN)) &&
- (qc->flags & ATA_QCFLAG_ACTIVE))
+ if ((!(ap->last_ctl & ATA_NIEN)) &&
+ qc && (qc->flags & ATA_QCFLAG_ACTIVE))
handled |= ata_host_intr(ap, qc);
}
}
@@ -3628,19 +3630,25 @@ static void atapi_packet_task(void *_dat
/* send SCSI cdb */
DPRINTK("send cdb\n");
assert(ap->cdb_len >= 12);
- ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1);
- /* if we are DMA'ing, irq handler takes over from here */
- if (qc->tf.protocol == ATA_PROT_ATAPI_DMA)
- ap->ops->bmdma_start(qc); /* initiate bmdma */
-
- /* non-data commands are also handled via irq */
- else if (qc->tf.protocol == ATA_PROT_ATAPI_NODATA) {
- /* do nothing */
- }
+ if (qc->tf.protocol == ATA_PROT_ATAPI_DMA ||
+ qc->tf.protocol == ATA_PROT_ATAPI_NODATA) {
+ /* Once we're done issuing command and kicking bmdma,
+ * irq handler takes over. To not lose irq, we need
+ * to enable irq before sending cdb, but interrupt
+ * handler shouldn't be invoked before we're finished.
+ * Hence, the following locking.
+ */
+ spin_lock_irq(&ap->host_set->lock);
+ ata_irq_on(ap);
+ ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1);
+ if (qc->tf.protocol == ATA_PROT_ATAPI_DMA)
+ ap->ops->bmdma_start(qc); /* initiate bmdma */
+ spin_unlock_irq(&ap->host_set->lock);
+ } else {
+ ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1);
- /* PIO commands are handled by polling */
- else {
+ /* PIO commands are handled by polling */
ap->pio_task_state = PIO_ST;
queue_work(ata_wq, &ap->pio_task);
}
-
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html