|
@@ -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);
|
|
|
}
|