* [PATCH/RFC 0/4] libata: more irq driven pio follow-up patches
@ 2005-11-01 11:12 Albert Lee
2005-11-01 11:19 ` [PATCH/RFC 1/4] irq-pio: misc fixes Albert Lee
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Albert Lee @ 2005-11-01 11:12 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Doug Maxey, Bartlomiej Zolnierkiewicz, Mark Lord, Linux IDE
Dear all,
More irq driven PIO follow-up patches (revised).
Changes:
1. merge the ata_dataout_task workqueue with the ata_pio_task workqueue
2. add read/write multiple support
patch 1/4: misc fixes
- ata_pio_block(): add ata_altstatus(ap) to prevent reading device status before it is valid
- remove the unnecessary HSM_ST_IDLE state from ata_pio_task()
- raise BUG() when unknown state is found in ata_pio_task()
patch 2/4: merge the ata_dataout_task workqueue with the ata_pio_task workqueue
- remove ap->dataout_task from struct ata_port
- let ata_pio_task() handle the HSM_ST_FIRST state.
- rename ata_dataout_task() to ata_pio_first_block()
patch 3/4: eliminate unnecessary queuing in ata_pio_first_block()
- change the return value of ata_pio_complete()
- add return value to ata_pio_first_block()
- rename the variable "qc_completed" to "has_next" in ata_pio_task()
- use "has_next" to eliminate unnecessary queuing in ata_pio_first_block()
patch 4/4: add read/write multiple support
- add is_multi_taskfile() to ata.h
- initialize ata_device->multi_count with device identify data
- use ata_pio_sectors() to support r/w multiple commands
Patch against the libata-dev irq-pio branch (cd8200e6d4f9f05e6ea48f7c000be890337396ac).
For your review and advice, thanks.
Albert
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH/RFC 1/4] irq-pio: misc fixes 2005-11-01 11:12 [PATCH/RFC 0/4] libata: more irq driven pio follow-up patches Albert Lee @ 2005-11-01 11:19 ` Albert Lee 2005-11-09 6:22 ` Jeff Garzik 2005-11-01 11:24 ` [PATCH/RFC 2/4] irq-pio: merge the ata_dataout_task workqueue with ata_pio_task workqueue Albert Lee ` (2 subsequent siblings) 3 siblings, 1 reply; 7+ messages in thread From: Albert Lee @ 2005-11-01 11:19 UTC (permalink / raw) To: Jeff Garzik; +Cc: Doug Maxey, Bartlomiej Zolnierkiewicz, Mark Lord, Linux IDE Patch 1/4: misc fixes Changes: - ata_pio_block(): add ata_altstatus(ap) to prevent reading device status before it is valid - remove the unnecessary HSM_ST_IDLE state from ata_pio_task() - raise BUG() when unknown state is found in ata_pio_task() For your review, thanks. Albert Signed-off-by: Albert Lee <albertcc@tw.ibm.com> ============ --- linux-ori/drivers/scsi/libata-core.c 2005-11-01 17:22:41.000000000 +0800 +++ id1/drivers/scsi/libata-core.c 2005-11-01 18:45:16.000000000 +0800 @@ -3224,6 +3224,8 @@ static void ata_pio_block(struct ata_por ata_pio_sector(qc); } + + ata_altstatus(ap); /* flush */ } static void ata_pio_error(struct ata_port *ap) @@ -3251,9 +3253,6 @@ fsm_start: qc_completed = 0; switch (ap->hsm_task_state) { - case HSM_ST_IDLE: - return; - case HSM_ST: ata_pio_block(ap); break; @@ -3271,6 +3270,10 @@ fsm_start: case HSM_ST_ERR: ata_pio_error(ap); return; + + default: + BUG(); + return; } if (timeout) ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH/RFC 1/4] irq-pio: misc fixes 2005-11-01 11:19 ` [PATCH/RFC 1/4] irq-pio: misc fixes Albert Lee @ 2005-11-09 6:22 ` Jeff Garzik 0 siblings, 0 replies; 7+ messages in thread From: Jeff Garzik @ 2005-11-09 6:22 UTC (permalink / raw) To: Albert Lee; +Cc: Doug Maxey, Bartlomiej Zolnierkiewicz, Mark Lord, Linux IDE applied 1-4 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH/RFC 2/4] irq-pio: merge the ata_dataout_task workqueue with ata_pio_task workqueue 2005-11-01 11:12 [PATCH/RFC 0/4] libata: more irq driven pio follow-up patches Albert Lee 2005-11-01 11:19 ` [PATCH/RFC 1/4] irq-pio: misc fixes Albert Lee @ 2005-11-01 11:24 ` Albert Lee 2005-11-01 11:30 ` [PATCH/RFC 3/4] irq-pio: eliminate unnecessary queuing in ata_pio_first_block() Albert Lee 2005-11-01 11:33 ` [PATCH/RFC 4/4] irq-pio: add read/write multiple support Albert Lee 3 siblings, 0 replies; 7+ messages in thread From: Albert Lee @ 2005-11-01 11:24 UTC (permalink / raw) To: Jeff Garzik; +Cc: Doug Maxey, Bartlomiej Zolnierkiewicz, Mark Lord, Linux IDE Patch 2/4: merge the ata_dataout_task workqueue with ata_pio_task workqueue Changes: - remove ap->dataout_task from struct ata_port - let ata_pio_task() handle the HSM_ST_FIRST state. - rename ata_dataout_task() to ata_pio_first_block() - replace the ata_dataout_task workqueue with ata_pio_task workqueue For your review, thanks. Albert Signed-off-by: Albert Lee <albertcc@tw.ibm.com> ======== --- id1/include/linux/libata.h 2005-11-01 17:39:14.000000000 +0800 +++ id2/include/linux/libata.h 2005-11-01 17:57:00.000000000 +0800 @@ -334,8 +334,6 @@ struct ata_port { struct ata_host_stats stats; struct ata_host_set *host_set; - struct work_struct dataout_task; - struct work_struct pio_task; unsigned int hsm_task_state; unsigned long pio_task_timeout; --- id1/drivers/scsi/libata-core.c 2005-11-01 18:45:16.000000000 +0800 +++ id2/drivers/scsi/libata-core.c 2005-11-01 18:45:35.000000000 +0800 @@ -2963,8 +2963,8 @@ static void atapi_send_cdb(struct ata_po } /** - * ata_dataout_task - Write first data block to hardware - * @_data: Port to which ATA/ATAPI device is attached. + * ata_pio_first_block - Write first data block to hardware + * @ap: Port to which ATA/ATAPI device is attached. * * When device has indicated its readiness to accept * the data, this function sends out the CDB or @@ -2977,9 +2977,8 @@ static void atapi_send_cdb(struct ata_po * Kernel thread context (may sleep) */ -static void ata_dataout_task(void *_data) +static void ata_pio_first_block(struct ata_port *ap) { - struct ata_port *ap = _data; struct ata_queued_cmd *qc; u8 status; unsigned long flags; @@ -3253,6 +3252,10 @@ fsm_start: qc_completed = 0; switch (ap->hsm_task_state) { + case HSM_ST_FIRST: + ata_pio_first_block(ap); + return; + case HSM_ST: ata_pio_block(ap); break; @@ -3703,10 +3706,10 @@ int ata_qc_issue_prot(struct ata_queued_ if (qc->tf.flags & ATA_TFLAG_WRITE) { /* PIO data out protocol */ ap->hsm_task_state = HSM_ST_FIRST; - queue_work(ata_wq, &ap->dataout_task); + queue_work(ata_wq, &ap->pio_task); /* always send first data block using - * the ata_dataout_task() codepath. + * the ata_pio_task() codepath. */ } else { /* PIO data in protocol */ @@ -3733,7 +3736,7 @@ int ata_qc_issue_prot(struct ata_queued_ /* send cdb by polling if no cdb interrupt */ if ((!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) || (qc->tf.flags & ATA_TFLAG_POLLING)) - queue_work(ata_wq, &ap->dataout_task); + queue_work(ata_wq, &ap->pio_task); break; case ATA_PROT_ATAPI_DMA: @@ -3745,7 +3748,7 @@ int ata_qc_issue_prot(struct ata_queued_ /* send cdb by polling if no cdb interrupt */ if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) - queue_work(ata_wq, &ap->dataout_task); + queue_work(ata_wq, &ap->pio_task); break; default: @@ -4325,7 +4328,6 @@ static void ata_host_init(struct ata_por ap->active_tag = ATA_TAG_POISON; ap->last_ctl = 0xFF; - INIT_WORK(&ap->dataout_task, ata_dataout_task, ap); INIT_WORK(&ap->pio_task, ata_pio_task, ap); for (i = 0; i < ATA_MAX_DEVICES; i++) ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH/RFC 3/4] irq-pio: eliminate unnecessary queuing in ata_pio_first_block() 2005-11-01 11:12 [PATCH/RFC 0/4] libata: more irq driven pio follow-up patches Albert Lee 2005-11-01 11:19 ` [PATCH/RFC 1/4] irq-pio: misc fixes Albert Lee 2005-11-01 11:24 ` [PATCH/RFC 2/4] irq-pio: merge the ata_dataout_task workqueue with ata_pio_task workqueue Albert Lee @ 2005-11-01 11:30 ` Albert Lee 2005-11-01 11:33 ` [PATCH/RFC 4/4] irq-pio: add read/write multiple support Albert Lee 3 siblings, 0 replies; 7+ messages in thread From: Albert Lee @ 2005-11-01 11:30 UTC (permalink / raw) To: Jeff Garzik; +Cc: Doug Maxey, Bartlomiej Zolnierkiewicz, Mark Lord, Linux IDE patch 3/4: eliminate unnecessary queuing in ata_pio_first_block() Changes: - change the return value of ata_pio_complete() 0 <-> 1 - add return value for ata_pio_first_block() - rename variable "qc_completed" to "has_next" in ata_pio_task() - use has_next to eliminate unnecessary queuing in ata_pio_first_block() For your review, thanks. Albert Signed-off-by: Albert Lee <albertcc@tw.ibm.com> ========== --- id2/drivers/scsi/libata-core.c 2005-11-01 18:45:35.000000000 +0800 +++ id3/drivers/scsi/libata-core.c 2005-11-01 18:45:50.000000000 +0800 @@ -2697,7 +2697,8 @@ static unsigned long ata_pio_poll(struct * None. (executing in kernel thread context) * * RETURNS: - * Non-zero if qc completed, zero otherwise. + * Zero if qc completed. + * Non-zero if has next. */ static int ata_pio_complete (struct ata_port *ap) @@ -2719,14 +2720,14 @@ static int ata_pio_complete (struct ata_ if (drv_stat & (ATA_BUSY | ATA_DRQ)) { ap->hsm_task_state = HSM_ST_LAST_POLL; ap->pio_task_timeout = jiffies + ATA_TMOUT_PIO; - return 0; + return 1; } } drv_stat = ata_wait_idle(ap); if (!ata_ok(drv_stat)) { ap->hsm_task_state = HSM_ST_ERR; - return 0; + return 1; } qc = ata_qc_from_tag(ap, ap->active_tag); @@ -2738,7 +2739,7 @@ static int ata_pio_complete (struct ata_ /* another command may start at this point */ - return 1; + return 0; } @@ -2975,27 +2976,42 @@ static void atapi_send_cdb(struct ata_po * * LOCKING: * Kernel thread context (may sleep) + * + * RETURNS: + * Zero if irq handler takes over + * Non-zero if has next (polling). */ -static void ata_pio_first_block(struct ata_port *ap) +static int ata_pio_first_block(struct ata_port *ap) { struct ata_queued_cmd *qc; u8 status; unsigned long flags; + int has_next; qc = ata_qc_from_tag(ap, ap->active_tag); assert(qc != NULL); assert(qc->flags & ATA_QCFLAG_ACTIVE); + /* if polling, we will stay in the work queue after sending the data. + * otherwise, interrupt handler takes over after sending the data. + */ + has_next = (qc->tf.flags & ATA_TFLAG_POLLING); + /* sleep-wait for BSY to clear */ DPRINTK("busy wait\n"); - if (ata_busy_sleep(ap, ATA_TMOUT_DATAOUT_QUICK, ATA_TMOUT_DATAOUT)) + if (ata_busy_sleep(ap, ATA_TMOUT_DATAOUT_QUICK, ATA_TMOUT_DATAOUT)) { + ap->hsm_task_state = HSM_ST_TMOUT; goto err_out; + } /* make sure DRQ is set */ status = ata_chk_status(ap); - if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ) + if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ) { + /* device status error */ + ap->hsm_task_state = HSM_ST_ERR; goto err_out; + } /* Send the CDB (atapi) or the first data block (ata pio out). * During the state transition, interrupt handler shouldn't @@ -3019,18 +3035,15 @@ static void ata_pio_first_block(struct a /* send CDB */ atapi_send_cdb(ap, qc); + spin_unlock_irqrestore(&ap->host_set->lock, flags); + /* if polling, ata_pio_task() handles the rest. * otherwise, interrupt handler takes over from here. */ - if (qc->tf.flags & ATA_TFLAG_POLLING) - queue_work(ata_wq, &ap->pio_task); - - spin_unlock_irqrestore(&ap->host_set->lock, flags); - - return; + return has_next; err_out: - ata_pio_error(ap); + return 1; /* has next */ } /** @@ -3245,23 +3258,23 @@ static void ata_pio_task(void *_data) { struct ata_port *ap = _data; unsigned long timeout; - int qc_completed; + int has_next; fsm_start: timeout = 0; - qc_completed = 0; + has_next = 1; switch (ap->hsm_task_state) { case HSM_ST_FIRST: - ata_pio_first_block(ap); - return; + has_next = ata_pio_first_block(ap); + break; case HSM_ST: ata_pio_block(ap); break; case HSM_ST_LAST: - qc_completed = ata_pio_complete(ap); + has_next = ata_pio_complete(ap); break; case HSM_ST_POLL: @@ -3281,7 +3294,7 @@ fsm_start: if (timeout) queue_delayed_work(ata_wq, &ap->pio_task, timeout); - else if (!qc_completed) + else if (has_next) goto fsm_start; } ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH/RFC 4/4] irq-pio: add read/write multiple support 2005-11-01 11:12 [PATCH/RFC 0/4] libata: more irq driven pio follow-up patches Albert Lee ` (2 preceding siblings ...) 2005-11-01 11:30 ` [PATCH/RFC 3/4] irq-pio: eliminate unnecessary queuing in ata_pio_first_block() Albert Lee @ 2005-11-01 11:33 ` Albert Lee 3 siblings, 0 replies; 7+ messages in thread From: Albert Lee @ 2005-11-01 11:33 UTC (permalink / raw) To: Jeff Garzik; +Cc: Doug Maxey, Bartlomiej Zolnierkiewicz, Mark Lord, Linux IDE patch 4/4: add read/write multiple support Changes: - add is_multi_taskfile() to ata.h - initialize ata_device->multi_count with device identify data - use ata_pio_sectors() to support r/w multiple commands For your review, thanks. Albert Signed-off-by: Albert Lee <albertcc@tw.ibm.com> ======== --- linux-ori/include/linux/ata.h 2005-11-01 17:22:45.000000000 +0800 +++ id4/include/linux/ata.h 2005-11-01 18:27:08.000000000 +0800 @@ -293,6 +293,14 @@ static inline int is_atapi_taskfile(cons (tf->protocol == ATA_PROT_ATAPI_DMA); } +static inline int is_multi_taskfile(struct ata_taskfile *tf) +{ + return (tf->command == ATA_CMD_READ_MULTI) || + (tf->command == ATA_CMD_WRITE_MULTI) || + (tf->command == ATA_CMD_READ_MULTI_EXT) || + (tf->command == ATA_CMD_WRITE_MULTI_EXT); +} + static inline int ata_ok(u8 status) { return ((status & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | ATA_ERR)) --- id3/drivers/scsi/libata-core.c 2005-11-01 18:45:50.000000000 +0800 +++ id4/drivers/scsi/libata-core.c 2005-11-01 18:36:08.000000000 +0800 @@ -1261,6 +1261,12 @@ retry: } + if (dev->id[59] & 0x100) { + dev->multi_count = dev->id[59] & 0xff; + DPRINTK("ata%u: dev %u multi count %u\n", + ap->id, device, dev->multi_count); + } + ap->host->max_cmd_len = 16; } @@ -2711,7 +2717,7 @@ static int ata_pio_complete (struct ata_ * we enter, BSY will be cleared in a chk-status or two. If not, * the drive is probably seeking or something. Snooze for a couple * msecs, then chk-status again. If still busy, fall back to - * HSM_ST_POLL state. + * HSM_ST_LAST_POLL state. */ drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 10); if (drv_stat & (ATA_BUSY | ATA_DRQ)) { @@ -2928,6 +2934,32 @@ static void ata_pio_sector(struct ata_qu } /** + * ata_pio_sectors - Transfer one or many 512-byte sectors. + * @qc: Command on going + * + * Transfer one or many ATA_SECT_SIZE of data from/to the + * ATA device for the DRQ request. + * + * LOCKING: + * Inherited from caller. + */ + +static void ata_pio_sectors(struct ata_queued_cmd *qc) +{ + if (is_multi_taskfile(&qc->tf)) { + /* READ/WRITE MULTIPLE */ + unsigned int nsect; + + assert(qc->dev->multi_count); + + nsect = min(qc->nsect - qc->cursect, qc->dev->multi_count); + while (nsect--) + ata_pio_sector(qc); + } else + ata_pio_sector(qc); +} + +/** * atapi_send_cdb - Write CDB bytes to hardware * @ap: Port to which ATAPI device is attached. * @qc: Taskfile currently active @@ -3025,11 +3057,11 @@ static int ata_pio_first_block(struct at * send first data block. */ - /* ata_pio_sector() might change the state to HSM_ST_LAST. - * so, the state is changed here before ata_pio_sector(). + /* ata_pio_sectors() might change the state to HSM_ST_LAST. + * so, the state is changed here before ata_pio_sectors(). */ ap->hsm_task_state = HSM_ST; - ata_pio_sector(qc); + ata_pio_sectors(qc); ata_altstatus(ap); /* flush */ } else /* send CDB */ @@ -3234,7 +3266,7 @@ static void ata_pio_block(struct ata_por return; } - ata_pio_sector(qc); + ata_pio_sectors(qc); } ata_altstatus(ap); /* flush */ @@ -4120,7 +4152,7 @@ fsm_start: goto fsm_start; } - ata_pio_sector(qc); + ata_pio_sectors(qc); if (ap->hsm_task_state == HSM_ST_LAST && (!(qc->tf.flags & ATA_TFLAG_WRITE))) { ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH RFC] libata: interrupt driven pio
@ 2005-09-09 16:14 Albert Lee
2005-09-09 17:35 ` Jeff Garzik
0 siblings, 1 reply; 7+ messages in thread
From: Albert Lee @ 2005-09-09 16:14 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Linux IDE, Bartlomiej Zolnierkiewicz, Doug Maxey, Tejun Heo
[-- Attachment #1: Type: text/plain, Size: 742 bytes --]
Dear all,
The interrupt driven PIO draft patch is ready for your review.
Changes:
- add PIO_ST_FIRST for the state before sending ATAPI CDB or sending
"ATA PIO data out" first data block.
- remove the ATA_FLAG_NOINTR flag since the interrupt handler is now
aware of the states
- modify ata_pio_sector() and atapi_pio_bytes() to work in the interrupt
context
- modify the ata_host_intr() to handle PIO interrupts
- modify ata_qc_issue_prot() to initialize states
- atapi_packet_task() changed to handle "ATA PIO data out" first data block
- support the pre-ATA4 ATAPI device which raise interrupt when ready to
receive CDB
Patch against 2.6.13 (80ac2912f846c01d702774bb6aa7100ec71e88b9).
For your review and comment, thanks.
Albert
[-- Attachment #2: idpio.diff --]
[-- Type: text/plain, Size: 12670 bytes --]
--- linux/include/linux/ata.h 2005-09-06 16:47:16.000000000 +0800
+++ id1/include/linux/ata.h 2005-09-09 14:34:00.000000000 +0800
@@ -181,6 +181,7 @@ enum {
ATA_TFLAG_ISADDR = (1 << 1), /* enable r/w to nsect/lba regs */
ATA_TFLAG_DEVICE = (1 << 2), /* enable r/w to device reg */
ATA_TFLAG_WRITE = (1 << 3), /* data dir: host->dev==1 (write) */
+ ATA_TFLAG_POLLING = (1 << 4), /* set nIEN to 1 and use polling */
};
enum ata_tf_protocols {
@@ -250,6 +251,8 @@ struct ata_taskfile {
((u64) (id)[(n) + 1] << 16) | \
((u64) (id)[(n) + 0]) )
+#define atapi_id_cdb_intr(id) (((id)[0] & 0x60) == 0x20)
+
static inline int atapi_cdb_len(u16 *dev_id)
{
u16 tmp = dev_id[0] & 0x3;
--- linux/include/linux/libata.h 2005-09-06 16:47:17.000000000 +0800
+++ id1/include/linux/libata.h 2005-09-09 17:41:14.000000000 +0800
@@ -116,8 +116,6 @@ enum {
ATA_FLAG_MMIO = (1 << 6), /* use MMIO, not PIO */
ATA_FLAG_SATA_RESET = (1 << 7), /* use COMRESET */
ATA_FLAG_PIO_DMA = (1 << 8), /* PIO cmds via DMA */
- ATA_FLAG_NOINTR = (1 << 9), /* FIXME: Remove this once
- * proper HSM is in place. */
ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */
ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */
@@ -166,6 +164,7 @@ enum pio_task_states {
PIO_ST_LAST,
PIO_ST_LAST_POLL,
PIO_ST_ERR,
+ PIO_ST_FIRST,
};
/* forward declarations */
--- linux/drivers/scsi/libata-core.c 2005-09-06 16:47:07.000000000 +0800
+++ id1/drivers/scsi/libata-core.c 2005-09-09 23:44:28.000000000 +0800
@@ -2401,7 +2401,6 @@ void ata_poll_qc_complete(struct ata_que
unsigned long flags;
spin_lock_irqsave(&ap->host_set->lock, flags);
- ap->flags &= ~ATA_FLAG_NOINTR;
ata_irq_on(ap);
ata_qc_complete(qc, drv_stat);
spin_unlock_irqrestore(&ap->host_set->lock, flags);
@@ -2660,7 +2659,10 @@ static void ata_pio_sector(struct ata_qu
page = nth_page(page, (offset >> PAGE_SHIFT));
offset %= PAGE_SIZE;
- buf = kmap(page) + offset;
+ if (in_atomic())
+ buf = kmap_atomic(page, KM_IRQ0) + offset;
+ else
+ buf = kmap(page) + offset;
qc->cursect++;
qc->cursg_ofs++;
@@ -2676,7 +2678,10 @@ static void ata_pio_sector(struct ata_qu
do_write = (qc->tf.flags & ATA_TFLAG_WRITE);
ata_data_xfer(ap, buf, ATA_SECT_SIZE, do_write);
- kunmap(page);
+ if (in_atomic())
+ kunmap_atomic(page, KM_IRQ0);
+ else
+ kunmap(page);
}
/**
@@ -2742,7 +2747,10 @@ next_sg:
/* don't cross page boundaries */
count = min(count, (unsigned int)PAGE_SIZE - offset);
- buf = kmap(page) + offset;
+ if (in_atomic())
+ buf = kmap_atomic(page, KM_IRQ0) + offset;
+ else
+ buf = kmap(page) + offset;
bytes -= count;
qc->curbytes += count;
@@ -2758,7 +2766,10 @@ next_sg:
/* do the actual data transfer */
ata_data_xfer(ap, buf, count, do_write);
- kunmap(page);
+ if (in_atomic())
+ kunmap_atomic(page, KM_IRQ0);
+ else
+ kunmap(page);
if (bytes)
goto next_sg;
@@ -2797,6 +2808,8 @@ static void atapi_pio_bytes(struct ata_q
if (do_write != i_write)
goto err_out;
+ VPRINTK("ata%u: xfering %d bytes\n", ap->id, bytes);
+
__atapi_pio_bytes(qc, bytes);
return;
@@ -3335,39 +3348,91 @@ int ata_qc_issue_prot(struct ata_queued_
switch (qc->tf.protocol) {
case ATA_PROT_NODATA:
+ if (qc->tf.flags & ATA_TFLAG_POLLING)
+ ata_qc_set_polling(qc);
+
ata_tf_to_host_nolock(ap, &qc->tf);
+ ap->pio_task_state = PIO_ST_LAST;
+
+ if (qc->tf.flags & ATA_TFLAG_POLLING)
+ queue_work(ata_wq, &ap->pio_task);
+
break;
case ATA_PROT_DMA:
+ assert(!(qc->tf.flags & ATA_TFLAG_POLLING));
+
ap->ops->tf_load(ap, &qc->tf); /* load tf registers */
ap->ops->bmdma_setup(qc); /* set up bmdma */
ap->ops->bmdma_start(qc); /* initiate bmdma */
+ ap->pio_task_state = PIO_ST_LAST;
break;
- case ATA_PROT_PIO: /* load tf registers, initiate polling pio */
- ata_qc_set_polling(qc);
+ case ATA_PROT_PIO:
+ if (qc->tf.flags & ATA_TFLAG_POLLING)
+ ata_qc_set_polling(qc);
+
ata_tf_to_host_nolock(ap, &qc->tf);
- ap->pio_task_state = PIO_ST;
- queue_work(ata_wq, &ap->pio_task);
+
+ if (qc->tf.flags & ATA_TFLAG_POLLING) {
+ ap->pio_task_state = PIO_ST;
+ queue_work(ata_wq, &ap->pio_task);
+ } else {
+ /* Interrupt driven PIO. */
+ if (qc->tf.flags & ATA_TFLAG_WRITE) {
+ /*
+ * PIO data out protocol
+ */
+ ap->pio_task_state = PIO_ST_FIRST;
+ queue_work(ata_wq, &ap->packet_task);
+
+ /* send first data block by polling */
+ } else {
+ ap->pio_task_state = PIO_ST;
+
+ /* interrupt handler takes over from here */
+ }
+ }
+
break;
case ATA_PROT_ATAPI:
- ata_qc_set_polling(qc);
+ if (qc->tf.flags & ATA_TFLAG_POLLING)
+ ata_qc_set_polling(qc);
+
ata_tf_to_host_nolock(ap, &qc->tf);
- queue_work(ata_wq, &ap->packet_task);
+ ap->pio_task_state = PIO_ST_FIRST;
+
+ /* send cdb by polling if no cdb interrupt */
+ if ((!atapi_id_cdb_intr(qc->dev->id)) ||
+ qc->tf.flags & ATA_TFLAG_POLLING)
+ queue_work(ata_wq, &ap->packet_task);
break;
case ATA_PROT_ATAPI_NODATA:
- ap->flags |= ATA_FLAG_NOINTR;
+ if (qc->tf.flags & ATA_TFLAG_POLLING)
+ ata_qc_set_polling(qc);
+
ata_tf_to_host_nolock(ap, &qc->tf);
- queue_work(ata_wq, &ap->packet_task);
+ ap->pio_task_state = PIO_ST_FIRST;
+
+ /* send cdb by polling if no cdb interrupt */
+ if ((!atapi_id_cdb_intr(qc->dev->id)) ||
+ qc->tf.flags & ATA_TFLAG_POLLING)
+ queue_work(ata_wq, &ap->packet_task);
break;
case ATA_PROT_ATAPI_DMA:
- ap->flags |= ATA_FLAG_NOINTR;
+ assert(!(qc->tf.flags & ATA_TFLAG_POLLING));
+
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);
+ ap->pio_task_state = PIO_ST_FIRST;
+
+ /* send cdb by polling if no cdb interrupt */
+ if ((!atapi_id_cdb_intr(qc->dev->id)) ||
+ qc->tf.flags & ATA_TFLAG_POLLING)
+ queue_work(ata_wq, &ap->packet_task);
break;
default:
@@ -3609,6 +3674,29 @@ void ata_bmdma_stop(struct ata_queued_cm
ata_altstatus(ap); /* dummy read */
}
+static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc)
+{
+ /* send SCSI cdb */
+ DPRINTK("send cdb\n");
+ assert(ap->cdb_len >= 12);
+
+ ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1);
+
+ switch (qc->tf.protocol) {
+ case ATA_PROT_ATAPI:
+ ap->pio_task_state = PIO_ST;
+ break;
+ case ATA_PROT_ATAPI_NODATA:
+ ap->pio_task_state = PIO_ST_LAST;
+ break;
+ case ATA_PROT_ATAPI_DMA:
+ ap->pio_task_state = PIO_ST_LAST;
+ /* initiate bmdma */
+ ap->ops->bmdma_start(qc);
+ break;
+ }
+}
+
/**
* ata_host_intr - Handle host interrupt for given (port, task)
* @ap: Port on which interrupt arrived (possibly...)
@@ -3630,45 +3718,132 @@ inline unsigned int ata_host_intr (struc
{
u8 status, host_stat;
- switch (qc->tf.protocol) {
-
- case ATA_PROT_DMA:
- case ATA_PROT_ATAPI_DMA:
- case ATA_PROT_ATAPI:
- /* check status of DMA engine */
- host_stat = ap->ops->bmdma_status(ap);
- VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat);
+ VPRINTK("ata%u: protocol %d task state %d\n",
+ ap->id, qc->tf.protocol, ap->pio_task_state);
- /* if it's not our irq... */
- if (!(host_stat & ATA_DMA_INTR))
+ /* Check whether we are expecting interrupt in this state */
+ switch (ap->pio_task_state) {
+ case PIO_ST_FIRST:
+ if (!(is_atapi_taskfile(&qc->tf) &&
+ atapi_id_cdb_intr(qc->dev->id)))
goto idle_irq;
+ break;
+ case PIO_ST_LAST:
+ if (qc->tf.protocol == ATA_PROT_DMA ||
+ qc->tf.protocol == ATA_PROT_ATAPI_DMA) {
+ /* check status of DMA engine */
+ host_stat = ap->ops->bmdma_status(ap);
+ VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat);
+
+ /* if it's not our irq... */
+ if (!(host_stat & ATA_DMA_INTR))
+ goto idle_irq;
- /* before we do anything else, clear DMA-Start bit */
- ap->ops->bmdma_stop(qc);
+ /* before we do anything else, clear DMA-Start bit */
+ ap->ops->bmdma_stop(qc);
+ }
+ break;
+ case PIO_ST:
+ break;
+ default:
+ goto idle_irq;
+ }
- /* fall through */
+ /* check altstatus */
+ status = ata_altstatus(ap);
+ if (status & ATA_BUSY) {
+ goto idle_irq;
+ }
- case ATA_PROT_ATAPI_NODATA:
- case ATA_PROT_NODATA:
- /* check altstatus */
- status = ata_altstatus(ap);
- if (status & ATA_BUSY)
- goto idle_irq;
+ /* check main status, clearing INTRQ */
+ status = ata_chk_status(ap);
+ if (unlikely(status & ATA_BUSY))
+ goto idle_irq;
- /* check main status, clearing INTRQ */
- status = ata_chk_status(ap);
- if (unlikely(status & ATA_BUSY))
- goto idle_irq;
- DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n",
- ap->id, qc->tf.protocol, status);
+ DPRINTK("ata%u: protocol %d task state %d (dev_stat 0x%X)\n",
+ ap->id, qc->tf.protocol, ap->pio_task_state, status);
+
+ /* check whether error */
+ if (status & ATA_ERR) {
+ ap->pio_task_state = PIO_ST_ERR;
+ }
+
+fsm_start:
+ switch (ap->pio_task_state) {
+ case PIO_ST_FIRST:
+ /* Some pre-ATAPI-4 devices assert INTRQ
+ * at this point when ready to receive CDB.
+ */
+
+ /* check device status */
+ if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ) {
+ /* Wrong status. Let EH handle this */
+ ap->pio_task_state = PIO_ST_ERR;
+ goto fsm_start;
+ }
+
+ atapi_send_cdb(ap, qc);
+
+ break;
+
+ case PIO_ST:
+ /* complete command or read/write the data register */
+ if (qc->tf.protocol == ATA_PROT_ATAPI) {
+ /* ATAPI PIO protocol */
+ if ((status & ATA_DRQ) == 0) {
+ ap->pio_task_state = PIO_ST_LAST;
+ goto fsm_start;
+ }
+
+ atapi_pio_bytes(qc);
+
+ } else {
+ /* ATA PIO protocol */
+ if (unlikely((status & ATA_DRQ) == 0)) {
+ /* handle BSY=0, DRQ=0 as error */
+ ap->pio_task_state = PIO_ST_ERR;
+ goto fsm_start;
+ }
+
+ ata_pio_sector(qc);
+
+ if (ap->pio_task_state == PIO_ST_LAST &&
+ (!(qc->tf.flags & ATA_TFLAG_WRITE))) {
+ /* complete the command */
+ status = ata_altstatus(ap);
+ status = ata_chk_status(ap);
+ goto fsm_start;
+ }
+ }
+
+ break;
+
+ case PIO_ST_LAST:
+ if (status & ATA_DRQ) {
+ /* status error */
+ ap->pio_task_state = PIO_ST_ERR;
+ goto fsm_start;
+ }
+
+ /* no more data to transfer */
+ DPRINTK("ata%u: PIO complete, drv_stat 0x%x\n",
+ ap->id, status);
/* ack bmdma irq events */
ap->ops->irq_clear(ap);
+ ap->pio_task_state = PIO_ST_IDLE;
+
/* complete taskfile transaction */
ata_qc_complete(qc, status);
break;
+ case PIO_ST_ERR:
+ printk(KERN_ERR "ata%u: PIO error, drv_stat 0x%x\n",
+ ap->id, status);
+ ap->pio_task_state = PIO_ST_IDLE;
+ ata_qc_complete(qc, status | ATA_ERR);
+ break;
default:
goto idle_irq;
}
@@ -3720,7 +3895,7 @@ irqreturn_t ata_interrupt (int irq, void
ap = host_set->ports[i];
if (ap &&
- !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) {
+ !(ap->flags & ATA_FLAG_PORT_DISABLED)) {
struct ata_queued_cmd *qc;
qc = ata_qc_from_tag(ap, ap->active_tag);
@@ -3754,6 +3929,7 @@ static void atapi_packet_task(void *_dat
struct ata_port *ap = _data;
struct ata_queued_cmd *qc;
u8 status;
+ unsigned long flags;
qc = ata_qc_from_tag(ap, ap->active_tag);
assert(qc != NULL);
@@ -3769,34 +3945,31 @@ static void atapi_packet_task(void *_dat
if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ)
goto err_out;
- /* send SCSI cdb */
- DPRINTK("send cdb\n");
- assert(ap->cdb_len >= 12);
- if (qc->tf.protocol == ATA_PROT_ATAPI_DMA ||
- qc->tf.protocol == ATA_PROT_ATAPI_NODATA) {
- unsigned long flags;
-
- /* Once we're done issuing command and kicking bmdma,
- * irq handler takes over. To not lose irq, we need
- * to clear NOINTR flag before sending cdb, but
- * interrupt handler shouldn't be invoked before we're
- * finished. Hence, the following locking.
- */
+ if (is_atapi_taskfile(&qc->tf)) {
spin_lock_irqsave(&ap->host_set->lock, flags);
- ap->flags &= ~ATA_FLAG_NOINTR;
- 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 */
+
+ /* send CDB */
+ atapi_send_cdb(ap, qc);
+
+ if (qc->tf.flags & ATA_TFLAG_POLLING)
+ queue_work(ata_wq, &ap->pio_task);
+
spin_unlock_irqrestore(&ap->host_set->lock, flags);
} else {
- ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1);
+ /* PIO data out protocol.
+ * send first data block.
+ */
+ ata_pio_sector(qc);
- /* PIO commands are handled by polling */
+ spin_lock_irqsave(&ap->host_set->lock, flags);
ap->pio_task_state = PIO_ST;
- queue_work(ata_wq, &ap->pio_task);
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+ /* interrupt handler takes over from here */
}
+
return;
err_out:
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH RFC] libata: interrupt driven pio 2005-09-09 16:14 [PATCH RFC] libata: interrupt driven pio Albert Lee @ 2005-09-09 17:35 ` Jeff Garzik 2005-09-22 4:00 ` [PATCH/RFC 0/3] libata: interrupt driven pio (revised) Albert Lee 0 siblings, 1 reply; 7+ messages in thread From: Jeff Garzik @ 2005-09-09 17:35 UTC (permalink / raw) To: Albert Lee; +Cc: Linux IDE, Bartlomiej Zolnierkiewicz, Doug Maxey, Tejun Heo Albert Lee wrote: > Dear all, > > The interrupt driven PIO draft patch is ready for your review. > > Changes: > - add PIO_ST_FIRST for the state before sending ATAPI CDB or sending > "ATA PIO data out" first data block. > - remove the ATA_FLAG_NOINTR flag since the interrupt handler is now > aware of the states > - modify ata_pio_sector() and atapi_pio_bytes() to work in the interrupt > context > - modify the ata_host_intr() to handle PIO interrupts > - modify ata_qc_issue_prot() to initialize states > - atapi_packet_task() changed to handle "ATA PIO data out" first data block > - support the pre-ATA4 ATAPI device which raise interrupt when ready to > receive CDB > > Patch against 2.6.13 (80ac2912f846c01d702774bb6aa7100ec71e88b9). Very nice, thanks for working on this. Comments: 1) With this work, PIO_ST_xxx is no longer an appropriate name. I suggest s/PIO_ST_/HSM_ST_/ 2) ditto with s/pio_task_state/hsm_task_state/ 3) Don't bother with in_atomic(), since we mix contexts in libata. Causes more trouble than its worth. Just use *map_atomic() 4) prefer 'ata_id_cdb_intr' to 'atapi_...' 5) please don't put braces around single-line C statements: + if (status & ATA_BUSY) { + goto idle_irq; + } 6) the following locking is questionable. AFAICS this should be resolved elsewhere, and if you have to take the lock here, you already have problems + /* PIO data out protocol. + * send first data block. + */ + ata_pio_sector(qc); - /* PIO commands are handled by polling */ + spin_lock_irqsave(&ap->host_set->lock, flags); ap->pio_task_state = PIO_ST; - queue_work(ata_wq, &ap->pio_task); + spin_unlock_irqrestore(&ap->host_set->lock, flags); + + /* interrupt handler takes over from here */ ie. why queue the task at all, if the intr handler takes over? 7) Overall, looks pretty good at first glance. This patch will require much testing before global deployment. Once you have a final patch, we'll let it simmer in libata-dev.git for a while, allowing -mm users plenty of time for testing. Possibly a 2.6.16 version target. ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH/RFC 0/3] libata: interrupt driven pio (revised) 2005-09-09 17:35 ` Jeff Garzik @ 2005-09-22 4:00 ` Albert Lee 2005-09-23 9:46 ` Jeff Garzik 0 siblings, 1 reply; 7+ messages in thread From: Albert Lee @ 2005-09-22 4:00 UTC (permalink / raw) To: Jeff Garzik; +Cc: Linux IDE, Bartlomiej Zolnierkiewicz, Doug Maxey, Tejun Heo Dear all, The revised interrupt driven PIO draft patch is ready for your review. Patch 1/3: interrupt driven pio patch for libata-core Patch 2/3: s/PIO_ST_/HSM_ST_/ and s/pio_task_state/hsm_task_state/. Patch 3/3: interrupt driven pio patch for LLD Patch for 2.6.14-rc1 (044a500e46742d39d22f1781cfb64ba93b463e39). Tested ok on x86 with Promise PDC20275, Seagate ST380011A and LITEON 52x CD-ROM drive. For your review and advice, thanks. Albert ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH/RFC 0/3] libata: interrupt driven pio (revised) 2005-09-22 4:00 ` [PATCH/RFC 0/3] libata: interrupt driven pio (revised) Albert Lee @ 2005-09-23 9:46 ` Jeff Garzik 2005-09-27 9:31 ` [PATCH/RFC 0/4] libata: interrupt driven pio (revised 2) Albert Lee 0 siblings, 1 reply; 7+ messages in thread From: Jeff Garzik @ 2005-09-23 9:46 UTC (permalink / raw) To: Albert Lee; +Cc: Linux IDE, Bartlomiej Zolnierkiewicz, Doug Maxey, Tejun Heo Comments: 1) the more trivial (read: "easier" to apply) patches should come first in a patch series. In this case, that means that * patch #2, s/pioxxx/hsmxxx/ should come first * whitespace changes in patch #3 should come before patch #1 2) LLD should signal its interest in using the polling code paths using a new ATA_FLAG_xxx added to its host_flags list of flags. This is greatly preferred over, e.g., hand-coding this indication in pdc_qc_issue_prot() or mv_qc_issue_prot() 3) Testing of CONFIG_HIGHMEM should be eliminated. In general, #ifdefs are discouraged in Linux whereever possible. That may mean that local_irq_save() must be called unconditionally, or some other solution should be found. ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH/RFC 0/4] libata: interrupt driven pio (revised 2) 2005-09-23 9:46 ` Jeff Garzik @ 2005-09-27 9:31 ` Albert Lee 2005-09-27 9:38 ` [PATCH/RFC 3/4] libata: interrupt driven pio for libata-core Albert Lee 0 siblings, 1 reply; 7+ messages in thread From: Albert Lee @ 2005-09-27 9:31 UTC (permalink / raw) To: Jeff Garzik Cc: Linux IDE, Bartlomiej Zolnierkiewicz, Doug Maxey, Tejun Heo, Mark Lord, Brett Russ Dear all, The revised interrupt driven PIO patch is ready for your review. Patch 1/4: indent and white space change Patch 2/4: s/PIO_ST_/HSM_ST_/ and s/pio_task_state/hsm_task_state/. Patch 3/4: interrupt driven pio patch for libata-core Patch 4/4: interrupt driven pio patch for LLD Patch for 2.6.14-rc1 (044a500e46742d39d22f1781cfb64ba93b463e39). Tested ok on x86 with Promise PDC20275, Seagate ST380011A and LITEON 52x CD-ROM drive. Need more testing with other host adapters and devices, especially pre-ATA4 ATAPI CD-ROM drives with CDB interrupt. For your review and advice, thanks. Albert ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH/RFC 3/4] libata: interrupt driven pio for libata-core 2005-09-27 9:31 ` [PATCH/RFC 0/4] libata: interrupt driven pio (revised 2) Albert Lee @ 2005-09-27 9:38 ` Albert Lee 2005-09-29 10:08 ` Bartlomiej Zolnierkiewicz 0 siblings, 1 reply; 7+ messages in thread From: Albert Lee @ 2005-09-27 9:38 UTC (permalink / raw) To: Jeff Garzik Cc: Linux IDE, Bartlomiej Zolnierkiewicz, Doug Maxey, Tejun Heo, Mark Lord, Brett Russ [-- Attachment #1: Type: text/plain, Size: 739 bytes --] Patch 3/4: interrupt driven pio for libata-core Changes: - add PIO_ST_FIRST for the state before sending ATAPI CDB or sending "ATA PIO data out" first data block. - add ATA_TFLAG_POLLING and ATA_DFLAG_CDB_INTR flags - remove the ATA_FLAG_NOINTR flag since the interrupt handler is now aware of the states - modify ata_pio_sector() and atapi_pio_bytes() to work in the interrupt context - modify the ata_host_intr() to handle PIO interrupts - modify ata_qc_issue_prot() to initialize states - atapi_packet_task() changed to handle "ATA PIO data out" first data block - support the pre-ATA4 ATAPI device which raise interrupt when ready to receive CDB For your review, thanks. Albert Signed-off-by: Albert Lee <albertcc@tw.ibm.com> [-- Attachment #2: idpio3.diff --] [-- Type: text/plain, Size: 15758 bytes --] --- id2/include/linux/ata.h 2005-09-26 14:08:23.000000000 +0800 +++ id3/include/linux/ata.h 2005-09-19 16:18:45.000000000 +0800 @@ -181,6 +181,7 @@ enum { ATA_TFLAG_ISADDR = (1 << 1), /* enable r/w to nsect/lba regs */ ATA_TFLAG_DEVICE = (1 << 2), /* enable r/w to device reg */ ATA_TFLAG_WRITE = (1 << 3), /* data dir: host->dev==1 (write) */ + ATA_TFLAG_POLLING = (1 << 4), /* set nIEN to 1 and use polling */ }; enum ata_tf_protocols { @@ -250,6 +251,8 @@ struct ata_taskfile { ((u64) (id)[(n) + 1] << 16) | \ ((u64) (id)[(n) + 0]) ) +#define ata_id_cdb_intr(id) (((id)[0] & 0x60) == 0x20) + static inline int atapi_cdb_len(u16 *dev_id) { u16 tmp = dev_id[0] & 0x3; --- id2/include/linux/libata.h 2005-09-26 14:23:20.000000000 +0800 +++ id3/include/linux/libata.h 2005-09-21 16:25:26.000000000 +0800 @@ -97,6 +97,7 @@ enum { ATA_DFLAG_LBA48 = (1 << 0), /* device supports LBA48 */ ATA_DFLAG_PIO = (1 << 1), /* device currently in PIO mode */ ATA_DFLAG_LOCK_SECTORS = (1 << 2), /* don't adjust max_sectors */ + ATA_DFLAG_CDB_INTR = (1 << 3), /* device asserts INTRQ when ready for CDB */ ATA_DEV_UNKNOWN = 0, /* unknown device */ ATA_DEV_ATA = 1, /* ATA device */ @@ -115,8 +116,6 @@ enum { ATA_FLAG_MMIO = (1 << 6), /* use MMIO, not PIO */ ATA_FLAG_SATA_RESET = (1 << 7), /* use COMRESET */ ATA_FLAG_PIO_DMA = (1 << 8), /* PIO cmds via DMA */ - ATA_FLAG_NOINTR = (1 << 9), /* FIXME: Remove this once - * proper HSM is in place. */ ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */ ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */ @@ -165,6 +164,7 @@ enum hsm_task_states { HSM_ST_LAST, HSM_ST_LAST_POLL, HSM_ST_ERR, + HSM_ST_FIRST, }; /* forward declarations */ --- id2/drivers/scsi/libata-core.c 2005-09-26 14:25:15.000000000 +0800 +++ id3/drivers/scsi/libata-core.c 2005-09-27 13:22:14.000000000 +0800 @@ -1292,6 +1292,9 @@ retry: ap->cdb_len = (unsigned int) rc; ap->host->max_cmd_len = (unsigned char) ap->cdb_len; + if (ata_id_cdb_intr(dev->id)) + dev->flags |= ATA_DFLAG_CDB_INTR; + /* print device info to dmesg */ printk(KERN_INFO "ata%u: dev %u ATAPI, max %s\n", ap->id, device, @@ -2405,7 +2408,6 @@ void ata_poll_qc_complete(struct ata_que unsigned long flags; spin_lock_irqsave(&ap->host_set->lock, flags); - ap->flags &= ~ATA_FLAG_NOINTR; ata_irq_on(ap); ata_qc_complete(qc, drv_stat); spin_unlock_irqrestore(&ap->host_set->lock, flags); @@ -2660,6 +2662,7 @@ static void ata_pio_sector(struct ata_qu struct page *page; unsigned int offset; unsigned char *buf; + unsigned long flags; if (qc->cursect == (qc->nsect - 1)) ap->hsm_task_state = HSM_ST_LAST; @@ -2671,7 +2674,8 @@ static void ata_pio_sector(struct ata_qu page = nth_page(page, (offset >> PAGE_SHIFT)); offset %= PAGE_SIZE; - buf = kmap(page) + offset; + local_irq_save(flags); + buf = kmap_atomic(page, KM_IRQ0) + offset; qc->cursect++; qc->cursg_ofs++; @@ -2687,7 +2691,8 @@ static void ata_pio_sector(struct ata_qu do_write = (qc->tf.flags & ATA_TFLAG_WRITE); ata_data_xfer(ap, buf, ATA_SECT_SIZE, do_write); - kunmap(page); + kunmap_atomic(buf - offset, KM_IRQ0); + local_irq_restore(flags); } /** @@ -2710,6 +2715,7 @@ static void __atapi_pio_bytes(struct ata struct page *page; unsigned char *buf; unsigned int offset, count; + unsigned long flags; if (qc->curbytes + bytes >= qc->nbytes) ap->hsm_task_state = HSM_ST_LAST; @@ -2753,7 +2759,8 @@ next_sg: /* don't cross page boundaries */ count = min(count, (unsigned int)PAGE_SIZE - offset); - buf = kmap(page) + offset; + local_irq_save(flags); + buf = kmap_atomic(page, KM_IRQ0) + offset; bytes -= count; qc->curbytes += count; @@ -2769,7 +2776,8 @@ next_sg: /* do the actual data transfer */ ata_data_xfer(ap, buf, count, do_write); - kunmap(page); + kunmap_atomic(buf - offset, KM_IRQ0); + local_irq_restore(flags); if (bytes) goto next_sg; @@ -2808,6 +2816,8 @@ static void atapi_pio_bytes(struct ata_q if (do_write != i_write) goto err_out; + VPRINTK("ata%u: xfering %d bytes\n", ap->id, bytes); + __atapi_pio_bytes(qc, bytes); return; @@ -3054,6 +3064,8 @@ static void ata_qc_timeout(struct ata_qu printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x host_stat 0x%x\n", ap->id, qc->tf.command, drv_stat, host_stat); + ap->hsm_task_state = HSM_ST_IDLE; + /* complete taskfile transaction */ ata_qc_complete(qc, drv_stat); break; @@ -3344,43 +3356,96 @@ int ata_qc_issue_prot(struct ata_queued_ { struct ata_port *ap = qc->ap; + /* select the device */ ata_dev_select(ap, qc->dev->devno, 1, 0); + /* start the command */ switch (qc->tf.protocol) { case ATA_PROT_NODATA: + if (qc->tf.flags & ATA_TFLAG_POLLING) + ata_qc_set_polling(qc); + ata_tf_to_host_nolock(ap, &qc->tf); + ap->hsm_task_state = HSM_ST_LAST; + + if (qc->tf.flags & ATA_TFLAG_POLLING) + queue_work(ata_wq, &ap->pio_task); + break; case ATA_PROT_DMA: + assert(!(qc->tf.flags & ATA_TFLAG_POLLING)); + ap->ops->tf_load(ap, &qc->tf); /* load tf registers */ ap->ops->bmdma_setup(qc); /* set up bmdma */ ap->ops->bmdma_start(qc); /* initiate bmdma */ + ap->hsm_task_state = HSM_ST_LAST; break; - case ATA_PROT_PIO: /* load tf registers, initiate polling pio */ - ata_qc_set_polling(qc); + case ATA_PROT_PIO: + if (qc->tf.flags & ATA_TFLAG_POLLING) + ata_qc_set_polling(qc); + ata_tf_to_host_nolock(ap, &qc->tf); - ap->hsm_task_state = HSM_ST; - queue_work(ata_wq, &ap->pio_task); + + if (qc->tf.flags & ATA_TFLAG_POLLING) { + /* polling PIO */ + ap->hsm_task_state = HSM_ST; + queue_work(ata_wq, &ap->pio_task); + } else { + /* interrupt driven PIO */ + if (qc->tf.flags & ATA_TFLAG_WRITE) { + /* PIO data out protocol */ + ap->hsm_task_state = HSM_ST_FIRST; + queue_work(ata_wq, &ap->packet_task); + + /* send first data block by polling */ + } else { + /* PIO data in protocol */ + ap->hsm_task_state = HSM_ST; + + /* interrupt handler takes over from here */ + } + } + break; case ATA_PROT_ATAPI: - ata_qc_set_polling(qc); + if (qc->tf.flags & ATA_TFLAG_POLLING) + ata_qc_set_polling(qc); + ata_tf_to_host_nolock(ap, &qc->tf); - queue_work(ata_wq, &ap->packet_task); + ap->hsm_task_state = HSM_ST_FIRST; + + /* send cdb by polling if no cdb interrupt */ + if ((!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) || + (qc->tf.flags & ATA_TFLAG_POLLING)) + queue_work(ata_wq, &ap->packet_task); break; case ATA_PROT_ATAPI_NODATA: - ap->flags |= ATA_FLAG_NOINTR; + if (qc->tf.flags & ATA_TFLAG_POLLING) + ata_qc_set_polling(qc); + ata_tf_to_host_nolock(ap, &qc->tf); - queue_work(ata_wq, &ap->packet_task); + ap->hsm_task_state = HSM_ST_FIRST; + + /* send cdb by polling if no cdb interrupt */ + if ((!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) || + (qc->tf.flags & ATA_TFLAG_POLLING)) + queue_work(ata_wq, &ap->packet_task); break; case ATA_PROT_ATAPI_DMA: - ap->flags |= ATA_FLAG_NOINTR; + assert(!(qc->tf.flags & ATA_TFLAG_POLLING)); + 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); + ap->hsm_task_state = HSM_ST_FIRST; + + /* send cdb by polling if no cdb interrupt */ + if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) + queue_work(ata_wq, &ap->packet_task); break; default: @@ -3623,6 +3688,42 @@ void ata_bmdma_stop(struct ata_queued_cm } /** + * atapi_send_cdb - Write CDB bytes to hardware + * @ap: Port to which ATAPI device is attached. + * @qc: Taskfile currently active + * + * When device has indicated its readiness to accept + * a CDB, this function is called. Send the CDB. + * + * LOCKING: + * caller. + */ + +static void atapi_send_cdb(struct ata_port *ap, struct ata_queued_cmd *qc) +{ + /* send SCSI cdb */ + DPRINTK("send cdb\n"); + assert(ap->cdb_len >= 12); + + ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1); + ata_altstatus(ap); /* flush */ + + switch (qc->tf.protocol) { + case ATA_PROT_ATAPI: + ap->hsm_task_state = HSM_ST; + break; + case ATA_PROT_ATAPI_NODATA: + ap->hsm_task_state = HSM_ST_LAST; + break; + case ATA_PROT_ATAPI_DMA: + ap->hsm_task_state = HSM_ST_LAST; + /* initiate bmdma */ + ap->ops->bmdma_start(qc); + break; + } +} + +/** * ata_host_intr - Handle host interrupt for given (port, task) * @ap: Port on which interrupt arrived (possibly...) * @qc: Taskfile currently active in engine @@ -3641,47 +3742,142 @@ void ata_bmdma_stop(struct ata_queued_cm inline unsigned int ata_host_intr (struct ata_port *ap, struct ata_queued_cmd *qc) { - u8 status, host_stat; - - switch (qc->tf.protocol) { + u8 status, host_stat = 0; - case ATA_PROT_DMA: - case ATA_PROT_ATAPI_DMA: - case ATA_PROT_ATAPI: - /* check status of DMA engine */ - host_stat = ap->ops->bmdma_status(ap); - VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat); + VPRINTK("ata%u: protocol %d task_state %d\n", + ap->id, qc->tf.protocol, ap->hsm_task_state); - /* if it's not our irq... */ - if (!(host_stat & ATA_DMA_INTR)) + /* Check whether we are expecting interrupt in this state */ + switch (ap->hsm_task_state) { + case HSM_ST_FIRST: + /* Check the ATA_DFLAG_CDB_INTR flag is enough here. + * The flag was turned on only for atapi devices. + * No need to check is_atapi_taskfile(&qc->tf) again. + */ + if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) goto idle_irq; + break; + case HSM_ST_LAST: + if (qc->tf.protocol == ATA_PROT_DMA || + qc->tf.protocol == ATA_PROT_ATAPI_DMA) { + /* check status of DMA engine */ + host_stat = ap->ops->bmdma_status(ap); + VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat); + + /* if it's not our irq... */ + if (!(host_stat & ATA_DMA_INTR)) + goto idle_irq; - /* before we do anything else, clear DMA-Start bit */ - ap->ops->bmdma_stop(qc); + /* before we do anything else, clear DMA-Start bit */ + ap->ops->bmdma_stop(qc); + } + break; + case HSM_ST: + break; + default: + goto idle_irq; + } - /* fall through */ + /* check altstatus */ + status = ata_altstatus(ap); + if (status & ATA_BUSY) + goto idle_irq; - case ATA_PROT_ATAPI_NODATA: - case ATA_PROT_NODATA: - /* check altstatus */ - status = ata_altstatus(ap); - if (status & ATA_BUSY) - goto idle_irq; + /* check main status, clearing INTRQ */ + status = ata_chk_status(ap); + if (unlikely(status & ATA_BUSY)) + goto idle_irq; - /* check main status, clearing INTRQ */ - status = ata_chk_status(ap); - if (unlikely(status & ATA_BUSY)) - goto idle_irq; - DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", - ap->id, qc->tf.protocol, status); + DPRINTK("ata%u: protocol %d task_state %d (dev_stat 0x%X)\n", + ap->id, qc->tf.protocol, ap->hsm_task_state, status); - /* ack bmdma irq events */ - ap->ops->irq_clear(ap); + /* ack bmdma irq events */ + ap->ops->irq_clear(ap); + + /* check error */ + if (unlikely((status & ATA_ERR) || (host_stat & ATA_DMA_ERR))) + ap->hsm_task_state = HSM_ST_ERR; + +fsm_start: + switch (ap->hsm_task_state) { + case HSM_ST_FIRST: + /* Some pre-ATAPI-4 devices assert INTRQ + * at this state when ready to receive CDB. + */ + + /* check device status */ + if (unlikely((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ)) { + /* Wrong status. Let EH handle this */ + ap->hsm_task_state = HSM_ST_ERR; + goto fsm_start; + } + + atapi_send_cdb(ap, qc); + + break; + + case HSM_ST: + /* complete command or read/write the data register */ + if (qc->tf.protocol == ATA_PROT_ATAPI) { + /* ATAPI PIO protocol */ + if ((status & ATA_DRQ) == 0) { + /* no more data to transfer */ + ap->hsm_task_state = HSM_ST_LAST; + goto fsm_start; + } + + atapi_pio_bytes(qc); + + if (unlikely(ap->hsm_task_state == HSM_ST_ERR)) + /* bad ireason reported by device */ + goto fsm_start; + + } else { + /* ATA PIO protocol */ + if (unlikely((status & ATA_DRQ) == 0)) { + /* handle BSY=0, DRQ=0 as error */ + ap->hsm_task_state = HSM_ST_ERR; + goto fsm_start; + } + + ata_pio_sector(qc); + + if (ap->hsm_task_state == HSM_ST_LAST && + (!(qc->tf.flags & ATA_TFLAG_WRITE))) { + /* all data read */ + ata_altstatus(ap); + status = ata_chk_status(ap); + goto fsm_start; + } + } + + ata_altstatus(ap); /* flush */ + break; + + case HSM_ST_LAST: + if (unlikely(status & ATA_DRQ)) { + /* handle DRQ=1 as error */ + ap->hsm_task_state = HSM_ST_ERR; + goto fsm_start; + } + + /* no more data to transfer */ + DPRINTK("ata%u: command complete, drv_stat 0x%x\n", + ap->id, status); + + ap->hsm_task_state = HSM_ST_IDLE; /* complete taskfile transaction */ ata_qc_complete(qc, status); break; + case HSM_ST_ERR: + printk(KERN_ERR "ata%u: command error, drv_stat 0x%x host_stat 0x%x\n", + ap->id, status, host_stat); + + ap->hsm_task_state = HSM_ST_IDLE; + ata_qc_complete(qc, status | ATA_ERR); + break; default: goto idle_irq; } @@ -3733,11 +3929,11 @@ irqreturn_t ata_interrupt (int irq, void ap = host_set->ports[i]; if (ap && - !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) { + !(ap->flags & ATA_FLAG_PORT_DISABLED)) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.ctl & ATA_NIEN)) && + if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)) && (qc->flags & ATA_QCFLAG_ACTIVE)) handled |= ata_host_intr(ap, qc); } @@ -3767,6 +3963,7 @@ static void atapi_packet_task(void *_dat struct ata_port *ap = _data; struct ata_queued_cmd *qc; u8 status; + unsigned long flags; qc = ata_qc_from_tag(ap, ap->active_tag); assert(qc != NULL); @@ -3782,38 +3979,40 @@ static void atapi_packet_task(void *_dat if ((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ) goto err_out; - /* send SCSI cdb */ - DPRINTK("send cdb\n"); - assert(ap->cdb_len >= 12); + /* Send the CDB (atapi) or the first data block (ata pio out). + * During the state transition, interrupt handler shouldn't + * be invoked before the data transfer is complete and + * hsm_task_state is changed. Hence, the following locking. + */ + spin_lock_irqsave(&ap->host_set->lock, flags); - if (qc->tf.protocol == ATA_PROT_ATAPI_DMA || - qc->tf.protocol == ATA_PROT_ATAPI_NODATA) { - unsigned long flags; - - /* Once we're done issuing command and kicking bmdma, - * irq handler takes over. To not lose irq, we need - * to clear NOINTR flag before sending cdb, but - * interrupt handler shouldn't be invoked before we're - * finished. Hence, the following locking. - */ - spin_lock_irqsave(&ap->host_set->lock, flags); - ap->flags &= ~ATA_FLAG_NOINTR; - 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_irqrestore(&ap->host_set->lock, flags); + if (is_atapi_taskfile(&qc->tf)) { + /* send CDB */ + atapi_send_cdb(ap, qc); + + if (qc->tf.flags & ATA_TFLAG_POLLING) + queue_work(ata_wq, &ap->pio_task); } else { - ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1); + /* PIO data out protocol. + * send first data block. + */ - /* PIO commands are handled by polling */ + /* ata_pio_sector() might change the state to HSM_ST_LAST. + * so, the state is changed here before ata_pio_sector(). + */ ap->hsm_task_state = HSM_ST; - queue_work(ata_wq, &ap->pio_task); + ata_pio_sector(qc); + ata_altstatus(ap); /* flush */ + + /* interrupt handler takes over from here */ } + spin_unlock_irqrestore(&ap->host_set->lock, flags); + return; err_out: - ata_poll_qc_complete(qc, ATA_ERR); + ata_pio_error(ap); } ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH/RFC 3/4] libata: interrupt driven pio for libata-core 2005-09-27 9:38 ` [PATCH/RFC 3/4] libata: interrupt driven pio for libata-core Albert Lee @ 2005-09-29 10:08 ` Bartlomiej Zolnierkiewicz 2005-09-30 11:04 ` [PATCH/RFC 0/4] libata: irq driven pio follow-up patches Albert Lee 0 siblings, 1 reply; 7+ messages in thread From: Bartlomiej Zolnierkiewicz @ 2005-09-29 10:08 UTC (permalink / raw) To: Albert Lee Cc: Jeff Garzik, Linux IDE, Doug Maxey, Tejun Heo, Mark Lord, Brett Russ Hi, On 9/27/05, Albert Lee <albertcc@tw.ibm.com> wrote: > Patch 3/4: interrupt driven pio for libata-core > > Changes: > - add PIO_ST_FIRST for the state before sending ATAPI CDB or sending HSM_ST_FIRST Could you add some brief comments about HSM_* states (one line per state should be OK)? > @@ -3344,43 +3356,96 @@ int ata_qc_issue_prot(struct ata_queued_ > { > struct ata_port *ap = qc->ap; > > + /* select the device */ > ata_dev_select(ap, qc->dev->devno, 1, 0); > > + /* start the command */ > switch (qc->tf.protocol) { > case ATA_PROT_NODATA: > + if (qc->tf.flags & ATA_TFLAG_POLLING) > + ata_qc_set_polling(qc); > + > ata_tf_to_host_nolock(ap, &qc->tf); > + ap->hsm_task_state = HSM_ST_LAST; > + > + if (qc->tf.flags & ATA_TFLAG_POLLING) > + queue_work(ata_wq, &ap->pio_task); > + > break; > > case ATA_PROT_DMA: > + assert(!(qc->tf.flags & ATA_TFLAG_POLLING)); > + > ap->ops->tf_load(ap, &qc->tf); /* load tf registers */ > ap->ops->bmdma_setup(qc); /* set up bmdma */ > ap->ops->bmdma_start(qc); /* initiate bmdma */ > + ap->hsm_task_state = HSM_ST_LAST; > break; > > - case ATA_PROT_PIO: /* load tf registers, initiate polling pio */ > - ata_qc_set_polling(qc); > + case ATA_PROT_PIO: > + if (qc->tf.flags & ATA_TFLAG_POLLING) > + ata_qc_set_polling(qc); > + > ata_tf_to_host_nolock(ap, &qc->tf); > - ap->hsm_task_state = HSM_ST; > - queue_work(ata_wq, &ap->pio_task); > + > + if (qc->tf.flags & ATA_TFLAG_POLLING) { > + /* polling PIO */ > + ap->hsm_task_state = HSM_ST; > + queue_work(ata_wq, &ap->pio_task); > + } else { > + /* interrupt driven PIO */ > + if (qc->tf.flags & ATA_TFLAG_WRITE) { > + /* PIO data out protocol */ > + ap->hsm_task_state = HSM_ST_FIRST; > + queue_work(ata_wq, &ap->packet_task); atapi_packet_task() is now abused to do ATA PIO out which is very confusing given that name of the function (and all the comments) remains unchanged. Please correct it (I think it would be good idea to separate HSM_ST_CDB from HSM_ST_FIRST). > case ATA_PROT_ATAPI: > - ata_qc_set_polling(qc); > + if (qc->tf.flags & ATA_TFLAG_POLLING) > + ata_qc_set_polling(qc); > + > ata_tf_to_host_nolock(ap, &qc->tf); > - queue_work(ata_wq, &ap->packet_task); > + ap->hsm_task_state = HSM_ST_FIRST; > + > + /* send cdb by polling if no cdb interrupt */ > + if ((!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) || > + (qc->tf.flags & ATA_TFLAG_POLLING)) > + queue_work(ata_wq, &ap->packet_task); > break; > > case ATA_PROT_ATAPI_NODATA: > - ap->flags |= ATA_FLAG_NOINTR; > + if (qc->tf.flags & ATA_TFLAG_POLLING) > + ata_qc_set_polling(qc); > + > ata_tf_to_host_nolock(ap, &qc->tf); > - queue_work(ata_wq, &ap->packet_task); > + ap->hsm_task_state = HSM_ST_FIRST; > + > + /* send cdb by polling if no cdb interrupt */ > + if ((!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) || > + (qc->tf.flags & ATA_TFLAG_POLLING)) > + queue_work(ata_wq, &ap->packet_task); > break; ATA_PROT_ATAPI_NODATA case is now identical to ATA_PROT_ATAPI one - no need to duplicate code. It may be also worth to split ata_qc_issue_prot() on IRQ and polling versions because it is really hard to read now. > +/** > * ata_host_intr - Handle host interrupt for given (port, task) > * @ap: Port on which interrupt arrived (possibly...) > * @qc: Taskfile currently active in engine > @@ -3641,47 +3742,142 @@ void ata_bmdma_stop(struct ata_queued_cm > inline unsigned int ata_host_intr (struct ata_port *ap, > struct ata_queued_cmd *qc) > { We should check first if we are polling and if so we shouldn't touch hardware et all as we can interface with ata_pio_task or atapi_packet_task. Just think about HSM_ST case when hardware is ready for the next transfer and IRQ happens (from some other device). Please note that this bug is present in original libata code. > - u8 status, host_stat; > - > - switch (qc->tf.protocol) { > + u8 status, host_stat = 0; > > - case ATA_PROT_DMA: > - case ATA_PROT_ATAPI_DMA: > - case ATA_PROT_ATAPI: Your patch changes behavior for ATA_PROT_ATAPI case - now DMA engine won't be checked. We had discussion about fixing it before but I don't remember what was the conclusion - in any case it is worth to at least mention this change in the patch description. > - /* check status of DMA engine */ > - host_stat = ap->ops->bmdma_status(ap); > - VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat); > + VPRINTK("ata%u: protocol %d task_state %d\n", > + ap->id, qc->tf.protocol, ap->hsm_task_state); > > - /* if it's not our irq... */ > - if (!(host_stat & ATA_DMA_INTR)) > + /* Check whether we are expecting interrupt in this state */ > + switch (ap->hsm_task_state) { > + case HSM_ST_FIRST: > + /* Check the ATA_DFLAG_CDB_INTR flag is enough here. > + * The flag was turned on only for atapi devices. > + * No need to check is_atapi_taskfile(&qc->tf) again. > + */ > + if (!(qc->dev->flags & ATA_DFLAG_CDB_INTR)) > goto idle_irq; > + break; > + case HSM_ST_LAST: > + if (qc->tf.protocol == ATA_PROT_DMA || > + qc->tf.protocol == ATA_PROT_ATAPI_DMA) { > + /* check status of DMA engine */ > + host_stat = ap->ops->bmdma_status(ap); > + VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat); > + > + /* if it's not our irq... */ > + if (!(host_stat & ATA_DMA_INTR)) > + goto idle_irq; > > - /* before we do anything else, clear DMA-Start bit */ > - ap->ops->bmdma_stop(qc); > + /* before we do anything else, clear DMA-Start bit */ > + ap->ops->bmdma_stop(qc); > + } > + break; > + case HSM_ST: > + break; > + default: > + goto idle_irq; > + } > > - /* fall through */ > + /* check altstatus */ > + status = ata_altstatus(ap); > + if (status & ATA_BUSY) > + goto idle_irq; > > - case ATA_PROT_ATAPI_NODATA: > - case ATA_PROT_NODATA: > - /* check altstatus */ > - status = ata_altstatus(ap); > - if (status & ATA_BUSY) > - goto idle_irq; > + /* check main status, clearing INTRQ */ > + status = ata_chk_status(ap); > + if (unlikely(status & ATA_BUSY)) > + goto idle_irq; > > - /* check main status, clearing INTRQ */ > - status = ata_chk_status(ap); > - if (unlikely(status & ATA_BUSY)) > - goto idle_irq; > - DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", > - ap->id, qc->tf.protocol, status); > + DPRINTK("ata%u: protocol %d task_state %d (dev_stat 0x%X)\n", > + ap->id, qc->tf.protocol, ap->hsm_task_state, status); > > - /* ack bmdma irq events */ > - ap->ops->irq_clear(ap); > + /* ack bmdma irq events */ > + ap->ops->irq_clear(ap); > + > + /* check error */ > + if (unlikely((status & ATA_ERR) || (host_stat & ATA_DMA_ERR))) > + ap->hsm_task_state = HSM_ST_ERR; > + > +fsm_start: > + switch (ap->hsm_task_state) { > + case HSM_ST_FIRST: > + /* Some pre-ATAPI-4 devices assert INTRQ > + * at this state when ready to receive CDB. > + */ > + > + /* check device status */ > + if (unlikely((status & (ATA_BUSY | ATA_DRQ)) != ATA_DRQ)) { > + /* Wrong status. Let EH handle this */ > + ap->hsm_task_state = HSM_ST_ERR; > + goto fsm_start; > + } > + > + atapi_send_cdb(ap, qc); This case can happen also for ATA PIO out: ata_qc_issue_prot() -> IRQ happens -> ata_host_intr(). Sending CDB unconditionally is wrong. The rest of the patch looks fine but it is was quite difficult to review because you've combined two major logical changes into one patch: * switching libata to use host state information * adding support for IRQ driven PIO Thanks, Bartlomiej ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH/RFC 0/4] libata: irq driven pio follow-up patches 2005-09-29 10:08 ` Bartlomiej Zolnierkiewicz @ 2005-09-30 11:04 ` Albert Lee 2005-09-30 11:21 ` Jeff Garzik 0 siblings, 1 reply; 7+ messages in thread From: Albert Lee @ 2005-09-30 11:04 UTC (permalink / raw) To: Bartlomiej Zolnierkiewicz, Jeff Garzik Cc: Linux IDE, Doug Maxey, Tejun Heo, Mark Lord, Brett Russ Dear all, The irq driven PIO follow up patches per Bart's advice. patch 1/4: - add comments for HSM_ST_* ata_qc_issue_prot(): - remove the redundant code for the ATA_PROT_ATAPI case. patch 2/4: - rename atapi_packet_task() to ata_dataout_task() and change comments patch 3/4: - simplify if condition in ata_dataout_task(): Use if (qc->tf.protocol == ATA_PROT_PIO) instead of is_atapi_taskfile() patch 4/4: ata_qc_issue_prot(): - let the PIO data out case always go through the ata_dataout_task() codepath. ata_dataout_task(): - handle the PIO data out + polling case. Patch against libata-dev irq-pio tree (e50362eccd8809a224cda5f71714a088ba37b2ab). For your review and advice, thanks. Albert ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH/RFC 0/4] libata: irq driven pio follow-up patches 2005-09-30 11:04 ` [PATCH/RFC 0/4] libata: irq driven pio follow-up patches Albert Lee @ 2005-09-30 11:21 ` Jeff Garzik 2005-10-03 11:46 ` [PATCH/RFC 0/4] libata: more " Albert Lee 0 siblings, 1 reply; 7+ messages in thread From: Jeff Garzik @ 2005-09-30 11:21 UTC (permalink / raw) To: Albert Lee Cc: Bartlomiej Zolnierkiewicz, Linux IDE, Doug Maxey, Tejun Heo, Mark Lord, Brett Russ Applied patches 1-4 to irq-pio branch. I wonder if dataout_task and pio_task can be merged? Jeff ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH/RFC 0/4] libata: more irq driven pio follow-up patches 2005-09-30 11:21 ` Jeff Garzik @ 2005-10-03 11:46 ` Albert Lee 0 siblings, 0 replies; 7+ messages in thread From: Albert Lee @ 2005-10-03 11:46 UTC (permalink / raw) To: Jeff Garzik Cc: Bartlomiej Zolnierkiewicz, Linux IDE, Doug Maxey, Tejun Heo, Mark Lord, Brett Russ Dear all, More irq driven PIO follow-up patches per Jeff's advice: 1. merge ata_dataout_task and ata_pio_task 2. add read/write multiple support patch 1/4: move functions - move atapi_send_cdb() and ata_dataout_task() to be near ata_pio_*() functions patch 2/4: remove ap->dataout_task - remove ap->dataout_task - let ata_pio_task() handle the HSM_ST_FIRST state. - rename ata_dataout_task() to ata_pio_first_block() patch 3/4: integrate ata_pio_first_block() with ata_pio_task() - change the return value of ata_pio_complete() - add return value for ata_pio_first_block() - rename variable "qc_completed" to "has_next" in ata_pio_task() patch 4/4: add read/write multiple support - add ATA_CMD_READ_MULTI, etc. to ata.h - add multi_count to ata_device and initialize it with device identify data - merge ata_prot_to_cmd() with ata_dev_set_protocol() - ata_pio_complete(): change the wait condition from (ATA_BUSY | ATA_DRQ) to ATA_BUSY - add ata_pio_sectors() to support r/w multiple commands - ata_pio_block(): add ata_altstatus(ap) to prevent reading device status before it is valid Patch against libata-dev irq-pio branch (54f00389563c80fa1de250a21256313ba01ca07d). For your review and advice, thanks. Albert ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2005-11-09 6:22 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2005-11-01 11:12 [PATCH/RFC 0/4] libata: more irq driven pio follow-up patches Albert Lee 2005-11-01 11:19 ` [PATCH/RFC 1/4] irq-pio: misc fixes Albert Lee 2005-11-09 6:22 ` Jeff Garzik 2005-11-01 11:24 ` [PATCH/RFC 2/4] irq-pio: merge the ata_dataout_task workqueue with ata_pio_task workqueue Albert Lee 2005-11-01 11:30 ` [PATCH/RFC 3/4] irq-pio: eliminate unnecessary queuing in ata_pio_first_block() Albert Lee 2005-11-01 11:33 ` [PATCH/RFC 4/4] irq-pio: add read/write multiple support Albert Lee -- strict thread matches above, loose matches on Subject: below -- 2005-09-09 16:14 [PATCH RFC] libata: interrupt driven pio Albert Lee 2005-09-09 17:35 ` Jeff Garzik 2005-09-22 4:00 ` [PATCH/RFC 0/3] libata: interrupt driven pio (revised) Albert Lee 2005-09-23 9:46 ` Jeff Garzik 2005-09-27 9:31 ` [PATCH/RFC 0/4] libata: interrupt driven pio (revised 2) Albert Lee 2005-09-27 9:38 ` [PATCH/RFC 3/4] libata: interrupt driven pio for libata-core Albert Lee 2005-09-29 10:08 ` Bartlomiej Zolnierkiewicz 2005-09-30 11:04 ` [PATCH/RFC 0/4] libata: irq driven pio follow-up patches Albert Lee 2005-09-30 11:21 ` Jeff Garzik 2005-10-03 11:46 ` [PATCH/RFC 0/4] libata: more " Albert Lee
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).