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