diff options
Diffstat (limited to 'drivers/ata/libata-sff.c')
-rw-r--r-- | drivers/ata/libata-sff.c | 72 |
1 files changed, 54 insertions, 18 deletions
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 8a1396f52a3a..e78ad76861f4 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -40,6 +40,8 @@ #include "libata.h" +static struct workqueue_struct *ata_sff_wq; + const struct ata_port_operations ata_sff_port_ops = { .inherits = &ata_base_port_ops, @@ -1293,7 +1295,7 @@ fsm_start: if (in_wq) spin_unlock_irqrestore(ap->lock, flags); - /* if polling, ata_pio_task() handles the rest. + /* if polling, ata_sff_pio_task() handles the rest. * otherwise, interrupt handler takes over from here. */ break; @@ -1458,14 +1460,38 @@ fsm_start: } EXPORT_SYMBOL_GPL(ata_sff_hsm_move); -void ata_pio_task(struct work_struct *work) +void ata_sff_queue_pio_task(struct ata_port *ap, unsigned long delay) +{ + /* may fail if ata_sff_flush_pio_task() in progress */ + queue_delayed_work(ata_sff_wq, &ap->sff_pio_task, + msecs_to_jiffies(delay)); +} +EXPORT_SYMBOL_GPL(ata_sff_queue_pio_task); + +void ata_sff_flush_pio_task(struct ata_port *ap) +{ + DPRINTK("ENTER\n"); + + cancel_rearming_delayed_work(&ap->sff_pio_task); + ap->hsm_task_state = HSM_ST_IDLE; + + if (ata_msg_ctl(ap)) + ata_port_printk(ap, KERN_DEBUG, "%s: EXIT\n", __func__); +} + +static void ata_sff_pio_task(struct work_struct *work) { struct ata_port *ap = - container_of(work, struct ata_port, port_task.work); - struct ata_queued_cmd *qc = ap->port_task_data; + container_of(work, struct ata_port, sff_pio_task.work); + struct ata_queued_cmd *qc; u8 status; int poll_next; + /* qc can be NULL if timeout occurred */ + qc = ata_qc_from_tag(ap, ap->link.active_tag); + if (!qc) + return; + fsm_start: WARN_ON_ONCE(ap->hsm_task_state == HSM_ST_IDLE); @@ -1481,7 +1507,7 @@ fsm_start: msleep(2); status = ata_sff_busy_wait(ap, ATA_BUSY, 10); if (status & ATA_BUSY) { - ata_pio_queue_task(ap, qc, ATA_SHORT_PAUSE); + ata_sff_queue_pio_task(ap, ATA_SHORT_PAUSE); return; } } @@ -1551,7 +1577,7 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) ap->hsm_task_state = HSM_ST_LAST; if (qc->tf.flags & ATA_TFLAG_POLLING) - ata_pio_queue_task(ap, qc, 0); + ata_sff_queue_pio_task(ap, 0); break; @@ -1573,20 +1599,21 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) if (qc->tf.flags & ATA_TFLAG_WRITE) { /* PIO data out protocol */ ap->hsm_task_state = HSM_ST_FIRST; - ata_pio_queue_task(ap, qc, 0); + ata_sff_queue_pio_task(ap, 0); - /* always send first data block using - * the ata_pio_task() codepath. + /* always send first data block using the + * ata_sff_pio_task() codepath. */ } else { /* PIO data in protocol */ ap->hsm_task_state = HSM_ST; if (qc->tf.flags & ATA_TFLAG_POLLING) - ata_pio_queue_task(ap, qc, 0); + ata_sff_queue_pio_task(ap, 0); - /* if polling, ata_pio_task() handles the rest. - * otherwise, interrupt handler takes over from here. + /* if polling, ata_sff_pio_task() handles the + * rest. otherwise, interrupt handler takes + * over from here. */ } @@ -1604,7 +1631,7 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) /* send cdb by polling if no cdb interrupt */ if ((!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) || (qc->tf.flags & ATA_TFLAG_POLLING)) - ata_pio_queue_task(ap, qc, 0); + ata_sff_queue_pio_task(ap, 0); break; case ATAPI_PROT_DMA: @@ -1616,7 +1643,7 @@ unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc) /* send cdb by polling if no cdb interrupt */ if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) - ata_pio_queue_task(ap, qc, 0); + ata_sff_queue_pio_task(ap, 0); break; default: @@ -2360,8 +2387,6 @@ void ata_sff_error_handler(struct ata_port *ap) /* reset PIO HSM and stop DMA engine */ spin_lock_irqsave(ap->lock, flags); - ap->hsm_task_state = HSM_ST_IDLE; - if (ap->ioaddr.bmdma_addr && qc && (qc->tf.protocol == ATA_PROT_DMA || qc->tf.protocol == ATAPI_PROT_DMA)) { @@ -2432,8 +2457,6 @@ void ata_sff_post_internal_cmd(struct ata_queued_cmd *qc) spin_lock_irqsave(ap->lock, flags); - ap->hsm_task_state = HSM_ST_IDLE; - if (ap->ioaddr.bmdma_addr) ap->ops->bmdma_stop(qc); @@ -3074,15 +3097,28 @@ EXPORT_SYMBOL_GPL(ata_pci_bmdma_init); */ void ata_sff_port_init(struct ata_port *ap) { + INIT_DELAYED_WORK(&ap->sff_pio_task, ata_sff_pio_task); ap->ctl = ATA_DEVCTL_OBS; ap->last_ctl = 0xFF; } int __init ata_sff_init(void) { + /* + * FIXME: In UP case, there is only one workqueue thread and if you + * have more than one PIO device, latency is bloody awful, with + * occasional multi-second "hiccups" as one PIO device waits for + * another. It's an ugly wart that users DO occasionally complain + * about; luckily most users have at most one PIO polled device. + */ + ata_sff_wq = create_workqueue("ata_sff"); + if (!ata_sff_wq) + return -ENOMEM; + return 0; } void __exit ata_sff_exit(void) { + destroy_workqueue(ata_sff_wq); } |