Support for tagged command queueing on ATA disk drives. This enables
the IDE layer to have multiple in-flight requests on hardware that
supports it. For now this includes the IBM Deskstar series drives,
- such as the GXP75, 40GV, GXP60, and GXP120 (ie any Deskstar made in
- the last couple of years).
+ such as the 22GXP, 75GXP, 40GV, 60GXP, and 120GXP (ie any Deskstar made
+ in the last couple of years), and at least some of the Western
+ Digital drives in the Expert series.
If you have such a drive, say Y here.
-CONFIG_BLK_DEV_IDE_TCQ_FULL
- When a command completes from the drive, the SERVICE bit is checked to
- see if other queued commands are ready to be started. Doing this
- immediately after a command completes has a tendency to 'starve' the
- device hardware queue, since we risk emptying the queue completely
- before starting any new commands. This shows up during stressing the
- drive as a /\/\/\/\ queue size balance, where we could instead try and
- maintain a minimum queue size and get a /---------\ graph instead.
-
- Saying Y here will attempt to always keep the queue full when possible
- at the cost of possibly increasing command turn-around latency.
-
- Generally say Y here.
-
CONFIG_BLK_DEV_IDE_TCQ_DEPTH
Maximum size of commands to enable per-drive. Any value between 1
and 32 is valid, with 32 being the maxium that the hardware supports.
return ide_stopped;
}
+/*
+ * Quiet handler for commands without a data phase -- handy instead of
+ * task_no_data_intr() for commands we _know_ will fail (such as WIN_NOP)
+ */
+ide_startstop_t task_no_data_quiet_intr(ide_drive_t *drive)
+{
+ struct ata_request *ar = IDE_CUR_AR(drive);
+ struct ata_taskfile *args = &ar->ar_task;
+
+ ide__sti(); /* local CPU only */
+
+ if (args)
+ ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+
+ return ide_stopped;
+}
+
/*
* Handler for commands without a data phase
*/
ide_startstop_t task_no_data_intr (ide_drive_t *drive)
{
- struct ata_request *ar = HWGROUP(drive)->rq->special;
+ struct ata_request *ar = IDE_CUR_AR(drive);
struct ata_taskfile *args = &ar->ar_task;
u8 stat = GET_STAT();
return;
case WIN_NOP:
+ args->handler = task_no_data_quiet_intr;
args->command_type = IDE_DRIVE_TASK_NO_DATA;
return;
{
struct request rq;
struct ata_request star;
+ int ret;
ata_ar_init(drive, &star);
init_taskfile_request(&rq);
rq.special = ☆
- return ide_do_drive_cmd(drive, &rq, ide_wait);
+ ret = ide_do_drive_cmd(drive, &rq, ide_wait);
+
+ /*
+ * copy back status etc
+ */
+ memcpy(args, &star.ar_task, sizeof(*args));
+ return ret;
}
/*
ide_startstop_t ide_dmaq_intr(ide_drive_t *drive);
ide_startstop_t ide_service(ide_drive_t *drive);
-static inline void drive_ctl_nien(ide_drive_t *drive, int clear)
+static inline void drive_ctl_nien(ide_drive_t *drive, int set)
{
#ifdef IDE_TCQ_NIEN
if (IDE_CONTROL_REG) {
- int mask = clear ? 0x00 : 0x02;
+ int mask = set ? 0x02 : 0x00;
OUT_BYTE(drive->ctl | mask, IDE_CONTROL_REG);
}
struct ata_request *ar;
int i;
- printk("%s: invalidating pending queue\n", drive->name);
+ printk("%s: invalidating pending queue (%d)\n", drive->name, drive->tcq->queued);
spin_lock_irqsave(&ide_lock, flags);
del_timer(&HWGROUP(drive)->timer);
+ if (test_bit(IDE_DMA, &HWGROUP(drive)->flags))
+ drive->channel->dmaproc(ide_dma_end, drive);
+
/*
* assume oldest commands have the higher tags... doesn't matter
* much. shove requests back into request queue.
#define IDE_TCQ_WAIT (10000)
int ide_tcq_wait_altstat(ide_drive_t *drive, byte *stat, byte busy_mask)
{
- int i;
+ int i = 0;
- /*
- * one initial udelay(1) should be enough, reading alt stat should
- * provide the required delay...
- */
- *stat = 0;
- i = 0;
- do {
- udelay(1);
+ udelay(1);
+
+ while ((*stat = GET_ALTSTAT()) & busy_mask) {
+ udelay(10);
if (unlikely(i++ > IDE_TCQ_WAIT))
return 1;
-
- } while ((*stat = GET_ALTSTAT()) & busy_mask);
+ }
return 0;
}
TCQ_PRINTK("%s: started service\n", drive->name);
- drive->service_pending = 0;
-
+ /*
+ * could be called with IDE_DMA in-progress from invalidate
+ * handler, refuse to do anything
+ */
if (test_bit(IDE_DMA, &HWGROUP(drive)->flags))
- printk("ide_service: DMA in progress\n");
+ return ide_stopped;
/*
* need to select the right drive first...
if (ide_tcq_wait_altstat(drive, &stat, BUSY_STAT)) {
printk("ide_service: BUSY clear took too long\n");
+ ide_dump_status(drive, "ide_service", stat);
ide_tcq_invalidate_queue(drive);
return ide_stopped;
}
TCQ_PRINTK("ide_dmaq_intr: ending %p, tag %d\n", ar, ar->ar_tag);
ide_end_queued_request(drive, !dma_stat, ar->ar_rq);
- IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG);
-
/*
- * keep the queue full, or honor SERVICE? note that this may race
- * and no new command will be started, in which case idedisk_do_request
- * will notice and do the service check
+ * we completed this command, set tcq inactive and check if we
+ * can service a new command
*/
-#if CONFIG_BLK_DEV_IDE_TCQ_FULL
- if (!drive->service_pending && (ide_pending_commands(drive) > 1)) {
- if (!blk_queue_empty(&drive->queue)) {
- drive->service_pending = 1;
- ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr);
- return ide_released;
- }
- }
-#endif
-
+ IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG);
return ide_check_service(drive);
}
return ide_check_service(drive);
}
+/*
+ * check if the ata adapter this drive is attached to supports the
+ * NOP auto-poll for multiple tcq enabled drives on one channel
+ */
+static int ide_tcq_check_autopoll(ide_drive_t *drive)
+{
+ struct ata_channel *ch = HWIF(drive);
+ struct ata_taskfile args;
+ ide_drive_t *next;
+
+ /*
+ * only need to probe if both drives on a channel support tcq
+ */
+ next = drive->next;
+ if (next == drive || !next->using_tcq)
+ return 0;
+
+ memset(&args, 0, sizeof(args));
+
+ args.taskfile.feature = 0x01;
+ args.taskfile.command = WIN_NOP;
+ ide_cmd_type_parser(&args);
+
+ /*
+ * do taskfile and check ABRT bit -- intelligent adapters will not
+ * pass NOP with sub-code 0x01 to device, so the command will not
+ * fail there
+ */
+ ide_raw_taskfile(drive, &args, NULL);
+ if (args.taskfile.feature & ABRT_ERR)
+ return 1;
+
+ ch->auto_poll = 1;
+ printk("%s: NOP Auto-poll enabled\n", ch->name);
+ return 0;
+}
+
/*
* configure the drive for tcq
*/
if (ide_build_commandlist(drive))
return 1;
+ /*
+ * check auto-poll support
+ */
+ ide_tcq_check_autopoll(drive);
+
if (depth != drive->queue_depth)
printk("%s: tagged command queueing enabled, command queue depth %d\n", drive->name, drive->queue_depth);
OUT_BYTE(AR_TASK_CMD(ar), IDE_COMMAND_REG);
if (ide_tcq_wait_altstat(drive, &stat, BUSY_STAT)) {
- printk("ide_dma_queued_start: abort (stat=%x)\n", stat);
+ ide_dump_status(drive, "queued start", stat);
+ ide_tcq_invalidate_queue(drive);
return ide_stopped;
}
IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG);
drive->tcq->immed_rel++;
+ ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr);
+
TCQ_PRINTK("REL in queued_start\n");
if ((stat = GET_STAT()) & SERVICE_STAT)
return ide_service(drive);
- ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr);
return ide_released;
}