lkml.org 
[lkml]   [2002]   [Apr]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    SubjectRe: 2.5.8 IDE oops (TCQ breakage?)
    On Wed, Apr 17 2002, Jens Axboe wrote:
    > On Wed, Apr 17 2002, Martin Dalecki wrote:
    > > Mikael Pettersson wrote:
    > > >I have a 486 box which ran 2.5.7 fine, but 2.5.8 oopses during
    > > >boot at the BUG_ON() in drivers/ide/ide-disk.c, line 360:
    > > >
    > > > if (drive->using_tcq) {
    > > > int tag = ide_get_tag(drive);
    > > >
    > > > BUG_ON(drive->tcq->active_tag != -1);
    > >
    > > OK it could be that the tca goesn't get allocated if there
    > > was no chipset selected. Lets have a look...
    >
    > Add a drive->using_dma check to ide_dma_queued_on in ide-tcq.c, it needs
    > to look like this:
    >
    > ide_tcq_dmaproc()
    > {
    >
    > ...
    >
    > case ide_dma_queued_off:
    > enable_tcq = 0;
    > case ide_dma_queued_on:
    > if (!drive->using_dma)
    > return 1;
    > return ide_enable_queued(drive, enable_tcq);
    > default:
    > break;
    > }
    >
    > that should fix it.

    Should only be done if 'enable_tcq == 1' of course, and we also need to
    switch off tcq when dma is being disabled. 4th patch set against 2.5.8.
    Note to Martin: I'll merge with IDE-XX later today.

    --
    Jens Axboe

    diff -urzN -X /home/axboe/cdrom/exclude /opt/kernel/linux-2.5.8/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
    --- /opt/kernel/linux-2.5.8/drivers/block/ll_rw_blk.c 2002-04-17 12:15:35.000000000 +0200
    +++ linux/drivers/block/ll_rw_blk.c 2002-04-17 11:43:00.000000000 +0200
    @@ -899,10 +899,10 @@
    spin_lock_prefetch(q->queue_lock);

    generic_unplug_device(q);
    - add_wait_queue(&rl->wait, &wait);
    + add_wait_queue_exclusive(&rl->wait, &wait);
    do {
    set_current_state(TASK_UNINTERRUPTIBLE);
    - if (rl->count < batch_requests)
    + if (!rl->count)
    schedule();
    spin_lock_irq(q->queue_lock);
    rq = get_request(q, rw);
    @@ -1720,9 +1720,11 @@
    * Free request slots per queue.
    * (Half for reads, half for writes)
    */
    - queue_nr_requests = 64;
    - if (total_ram > MB(32))
    - queue_nr_requests = 256;
    + queue_nr_requests = (total_ram >> 8) & ~15; /* One per quarter-megabyte */
    + if (queue_nr_requests < 32)
    + queue_nr_requests = 32;
    + if (queue_nr_requests > 512)
    + queue_nr_requests = 512;

    /*
    * Batch frees according to queue length
    diff -urzN -X /home/axboe/cdrom/exclude /opt/kernel/linux-2.5.8/drivers/ide/Config.help linux/drivers/ide/Config.help
    --- /opt/kernel/linux-2.5.8/drivers/ide/Config.help 2002-04-17 12:15:35.000000000 +0200
    +++ linux/drivers/ide/Config.help 2002-04-17 11:43:00.000000000 +0200
    @@ -753,9 +753,17 @@

    If you have such a drive, say Y here.

    -CONFIG_BLK_DEV_IDE_TCQ_DEFAULT
    - Enabled tagged command queueing unconditionally on drives that report
    - support for it.
    +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.

    @@ -766,6 +774,18 @@
    You probably just want the default of 32 here. If you enter an invalid
    number, the default value will be used.

    +CONFIG_BLK_DEV_IDE_TCQ_DEFAULT
    + Enabled tagged command queueing unconditionally on drives that report
    + support for it. Regardless of the chosen value here, tagging can be
    + controlled at run time:
    +
    + echo "using_tcq:32" > /proc/ide/hdX/settings
    +
    + where any value between 1-32 selects chosen queue depth and enables
    + TCQ, and 0 disables it.
    +
    + Generally say Y here.
    +
    CONFIG_BLK_DEV_IT8172
    Say Y here to support the on-board IDE controller on the Integrated
    Technology Express, Inc. ITE8172 SBC. Vendor page at
    diff -urzN -X /home/axboe/cdrom/exclude /opt/kernel/linux-2.5.8/drivers/ide/Config.in linux/drivers/ide/Config.in
    --- /opt/kernel/linux-2.5.8/drivers/ide/Config.in 2002-04-17 12:15:35.000000000 +0200
    +++ linux/drivers/ide/Config.in 2002-04-17 11:43:00.000000000 +0200
    @@ -48,10 +48,11 @@
    dep_bool ' Enable DMA only for disks ' CONFIG_IDEDMA_ONLYDISK $CONFIG_IDEDMA_PCI_AUTO
    define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI
    dep_bool ' ATA tagged command queueing' CONFIG_BLK_DEV_IDE_TCQ $CONFIG_BLK_DEV_IDEDMA_PCI
    - dep_bool ' TCQ on by default' CONFIG_BLK_DEV_IDE_TCQ_DEFAULT $CONFIG_BLK_DEV_IDE_TCQ
    - if [ $CONFIG_BLK_DEV_IDE_TCQ_DEFAULT != "n" ]; then
    - int ' Default queue depth' CONFIG_BLK_DEV_IDE_TCQ_DEPTH 32
    - fi
    + dep_bool ' Attempt to keep queue full' CONFIG_BLK_DEV_IDE_TCQ_FULL $CONFIG_BLK_DEV_IDE_TCQ
    + dep_bool ' TCQ on by default' CONFIG_BLK_DEV_IDE_TCQ_DEFAULT $CONFIG_BLK_DEV_IDE_TCQ
    + if [ "$CONFIG_BLK_DEV_IDE_TCQ" != "n" ]; then
    + int ' Default queue depth' CONFIG_BLK_DEV_IDE_TCQ_DEPTH 8
    + fi
    dep_bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL
    dep_bool ' Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP
    dep_bool ' AEC62XX chipset support' CONFIG_BLK_DEV_AEC62XX $CONFIG_BLK_DEV_IDEDMA_PCI
    diff -urzN -X /home/axboe/cdrom/exclude /opt/kernel/linux-2.5.8/drivers/ide/ide-disk.c linux/drivers/ide/ide-disk.c
    --- /opt/kernel/linux-2.5.8/drivers/ide/ide-disk.c 2002-04-17 12:15:34.000000000 +0200
    +++ linux/drivers/ide/ide-disk.c 2002-04-17 13:59:00.000000000 +0200
    @@ -107,10 +107,7 @@
    * 48-bit commands are pretty sanely laid out
    */
    if (lba48bit) {
    - if (cmd == READ)
    - command = WIN_READ_EXT;
    - else
    - command = WIN_WRITE_EXT;
    + command = cmd == READ ? WIN_READ_EXT : WIN_WRITE_EXT;

    if (drive->using_dma) {
    command++; /* WIN_*DMA_EXT */
    @@ -118,31 +115,33 @@
    command++; /* WIN_*DMA_QUEUED_EXT */
    } else if (drive->mult_count)
    command += 5; /* WIN_MULT*_EXT */
    - } else {
    - /*
    - * 28-bit commands seem not to be, though...
    - */
    - if (cmd == READ) {
    - if (drive->using_dma) {
    - if (drive->using_tcq)
    - command = WIN_READDMA_QUEUED;
    - else
    - command = WIN_READDMA;
    - } else if (drive->mult_count)
    - command = WIN_MULTREAD;
    +
    + return command;
    + }
    +
    + /*
    + * 28-bit commands seem not to be, though...
    + */
    + if (cmd == READ) {
    + if (drive->using_dma) {
    + if (drive->using_tcq)
    + command = WIN_READDMA_QUEUED;
    else
    - command = WIN_READ;
    - } else {
    - if (drive->using_dma) {
    - if (drive->using_tcq)
    - command = WIN_WRITEDMA_QUEUED;
    - else
    - command = WIN_WRITEDMA;
    - } else if (drive->mult_count)
    - command = WIN_MULTWRITE;
    + command = WIN_READDMA;
    + } else if (drive->mult_count)
    + command = WIN_MULTREAD;
    + else
    + command = WIN_READ;
    + } else {
    + if (drive->using_dma) {
    + if (drive->using_tcq)
    + command = WIN_WRITEDMA_QUEUED;
    else
    - command = WIN_WRITE;
    - }
    + command = WIN_WRITEDMA;
    + } else if (drive->mult_count)
    + command = WIN_MULTWRITE;
    + else
    + command = WIN_WRITE;
    }

    return command;
    @@ -895,24 +894,24 @@

    __set_bit(i, &tag_mask);
    len += sprintf(out+len, "%d, ", i);
    - if (ar->ar_time > max_jif)
    - max_jif = ar->ar_time;
    + if (cur_jif - ar->ar_time > max_jif)
    + max_jif = cur_jif - ar->ar_time;
    cmds++;
    }
    len += sprintf(out+len, "]\n");

    + len += sprintf(out+len, "Queue:\t\t\treleased [ %d ] - started [ %d ]\n", drive->tcq->immed_rel, drive->tcq->immed_comp);
    +
    if (drive->tcq->queued != cmds)
    - len += sprintf(out+len, "pending request and queue count mismatch (%d)\n", cmds);
    + len += sprintf(out+len, "pending request and queue count mismatch (counted: %d)\n", cmds);

    if (tag_mask != drive->tcq->tag_mask)
    len += sprintf(out+len, "tag masks differ (counted %lx != %lx\n", tag_mask, drive->tcq->tag_mask);

    len += sprintf(out+len, "DMA status:\t\t%srunning\n", test_bit(IDE_DMA, &HWGROUP(drive)->flags) ? "" : "not ");

    - if (max_jif)
    - len += sprintf(out+len, "Oldest command:\t\t%lu\n", cur_jif - max_jif);
    -
    - len += sprintf(out+len, "immed rel %d, immed comp %d\n", drive->tcq->immed_rel, drive->tcq->immed_comp);
    + len += sprintf(out+len, "Oldest command:\t\t%lu jiffies\n", max_jif);
    + len += sprintf(out+len, "Oldest command ever:\t%lu\n", drive->tcq->oldest_command);

    drive->tcq->max_last_depth = 0;

    @@ -1017,8 +1016,10 @@
    return -EPERM;
    if (!drive->channel->dmaproc)
    return -EPERM;
    + if (arg == drive->queue_depth && drive->using_tcq)
    + return 0;

    - drive->using_tcq = arg;
    + drive->queue_depth = arg ? arg : 1;
    if (drive->channel->dmaproc(arg ? ide_dma_queued_on : ide_dma_queued_off, drive))
    return -EIO;

    diff -urzN -X /home/axboe/cdrom/exclude /opt/kernel/linux-2.5.8/drivers/ide/ide-dma.c linux/drivers/ide/ide-dma.c
    --- /opt/kernel/linux-2.5.8/drivers/ide/ide-dma.c 2002-04-17 12:15:34.000000000 +0200
    +++ linux/drivers/ide/ide-dma.c 2002-04-17 14:19:44.000000000 +0200
    @@ -601,6 +601,9 @@
    set_high = 0;
    drive->using_tcq = 0;
    outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
    +#ifdef CONFIG_BLK_DEV_IDE_TCQ
    + ide_tcq_dmaproc(ide_dma_queued_off, drive);
    +#endif
    case ide_dma_on:
    ide_toggle_bounce(drive, set_high);
    drive->using_dma = (func == ide_dma_on);
    @@ -610,9 +613,6 @@
    case ide_dma_check:
    return config_drive_for_dma (drive);
    case ide_dma_begin:
    -#ifdef DEBUG
    - printk("ide_dma_begin: from %p\n", __builtin_return_address(0));
    -#endif
    if (test_and_set_bit(IDE_DMA, &HWGROUP(drive)->flags))
    BUG();
    /* Note that this is done *after* the cmd has
    @@ -629,19 +629,18 @@
    case ide_dma_write_queued:
    case ide_dma_queued_start:
    return ide_tcq_dmaproc(func, drive);
    -#endif
    +#endif /* CONFIG_BLK_DEV_IDE_TCQ */

    case ide_dma_read:
    reading = 1 << 3;
    case ide_dma_write:
    - ar = HWGROUP(drive)->rq->special;
    + ar = IDE_CUR_AR(drive);

    if (ide_start_dma(hwif, drive, func))
    return 1;

    if (drive->type != ATA_DISK)
    return 0;
    -
    BUG_ON(HWGROUP(drive)->handler);
    ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, dma_timer_expiry); /* issue cmd to drive */
    if ((ar->ar_rq->flags & REQ_DRIVE_TASKFILE) &&
    @@ -655,20 +654,13 @@
    }
    return hwif->dmaproc(ide_dma_begin, drive);
    case ide_dma_end: /* returns 1 on error, 0 otherwise */
    -#ifdef DEBUG
    - printk("ide_dma_end: from %p\n", __builtin_return_address(0));
    -#endif
    - if (!test_and_clear_bit(IDE_DMA, &HWGROUP(drive)->flags)) {
    - printk("ide_dma_end: dma not going? %p\n", __builtin_return_address(0));
    - return 1;
    - }
    + if (!test_and_clear_bit(IDE_DMA, &HWGROUP(drive)->flags))
    + BUG();
    drive->waiting_for_dma = 0;
    outb(inb(dma_base)&~1, dma_base); /* stop DMA */
    dma_stat = inb(dma_base+2); /* get DMA status */
    outb(dma_stat|6, dma_base+2); /* clear the INTR & ERROR bits */
    ide_destroy_dmatable(drive); /* purge DMA mappings */
    - if (drive->tcq)
    - IDE_SET_CUR_TAG(drive, -1);
    return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; /* verify good DMA status */
    case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
    dma_stat = inb(dma_base+2);
    diff -urzN -X /home/axboe/cdrom/exclude /opt/kernel/linux-2.5.8/drivers/ide/ide-probe.c linux/drivers/ide/ide-probe.c
    --- /opt/kernel/linux-2.5.8/drivers/ide/ide-probe.c 2002-04-17 12:15:34.000000000 +0200
    +++ linux/drivers/ide/ide-probe.c 2002-04-17 11:43:00.000000000 +0200
    @@ -192,19 +192,16 @@
    /*
    * it's an ata drive, build command list
    */
    -#ifndef CONFIG_BLK_DEV_IDE_TCQ
    drive->queue_depth = 1;
    +#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEPTH
    + drive->queue_depth = CONFIG_BLK_DEV_IDE_TCQ_DEPTH;
    #else
    -# ifndef CONFIG_BLK_DEV_IDE_TCQ_DEPTH
    -# define CONFIG_BLK_DEV_IDE_TCQ_DEPTH 1
    -# endif
    drive->queue_depth = drive->id->queue_depth + 1;
    - if (drive->queue_depth > CONFIG_BLK_DEV_IDE_TCQ_DEPTH)
    - drive->queue_depth = CONFIG_BLK_DEV_IDE_TCQ_DEPTH;
    +#endif
    if (drive->queue_depth < 1 || drive->queue_depth > IDE_MAX_TAG)
    drive->queue_depth = IDE_MAX_TAG;
    -#endif
    - if (ide_build_commandlist(drive))
    +
    + if (ide_init_commandlist(drive))
    goto err_misc;

    return;
    @@ -482,11 +479,9 @@
    sprintf(hwif->dev.bus_id, "%04x", hwif->io_ports[IDE_DATA_OFFSET]);
    sprintf(hwif->dev.name, "ide");
    hwif->dev.driver_data = hwif;
    -#ifdef CONFIG_BLK_DEV_IDEPCI
    if (hwif->pci_dev)
    hwif->dev.parent = &hwif->pci_dev->dev;
    else
    -#endif
    hwif->dev.parent = NULL; /* Would like to do = &device_legacy */
    device_register(&hwif->dev);

    diff -urzN -X /home/axboe/cdrom/exclude /opt/kernel/linux-2.5.8/drivers/ide/ide-proc.c linux/drivers/ide/ide-proc.c
    --- /opt/kernel/linux-2.5.8/drivers/ide/ide-proc.c 2002-04-17 12:15:34.000000000 +0200
    +++ linux/drivers/ide/ide-proc.c 2002-04-17 11:43:00.000000000 +0200
    @@ -422,7 +422,6 @@
    static void create_proc_ide_drives(struct ata_channel *hwif)
    {
    int d;
    - struct proc_dir_entry *ent;
    struct proc_dir_entry *parent = hwif->proc;
    char name[64];

    diff -urzN -X /home/axboe/cdrom/exclude /opt/kernel/linux-2.5.8/drivers/ide/ide-taskfile.c linux/drivers/ide/ide-taskfile.c
    --- /opt/kernel/linux-2.5.8/drivers/ide/ide-taskfile.c 2002-04-17 12:15:34.000000000 +0200
    +++ linux/drivers/ide/ide-taskfile.c 2002-04-17 13:39:05.000000000 +0200
    @@ -399,6 +399,20 @@
    struct hd_driveid *id = drive->id;
    u8 HIHI = (drive->addressing) ? 0xE0 : 0xEF;

    +#if 0
    + printk("ata_taskfile ... %p\n", args->handler);
    +
    + printk(" sector feature %02x\n", args->taskfile.feature);
    + printk(" sector count %02x\n", args->taskfile.sector_count);
    + printk(" drive/head %02x\n", args->taskfile.device_head);
    + printk(" command %02x\n", args->taskfile.command);
    +
    + if (rq)
    + printk(" rq->nr_sectors %2li\n", rq->nr_sectors);
    + else
    + printk(" rq-> = null\n");
    +#endif
    +
    /* (ks/hs): Moved to start, do not use for multiple out commands */
    if (args->handler != task_mulout_intr) {
    if (IDE_CONTROL_REG)
    @@ -566,18 +580,22 @@
    ide_unmap_rq(rq, pBuf, &flags);
    drive->io_32bit = io_32bit;

    + /*
    + * first segment of the request is complete. note that this does not
    + * necessarily mean that the entire request is done!! this is only
    + * true if ide_end_request() returns 0.
    + */
    if (--rq->current_nr_sectors <= 0) {
    - /* (hs): swapped next 2 lines */
    DTF("Request Ended stat: %02x\n", GET_STAT());
    - if (ide_end_request(drive, 1)) {
    - ide_set_handler(drive, &task_in_intr, WAIT_CMD, NULL);
    - return ide_started;
    - }
    - } else {
    - ide_set_handler(drive, &task_in_intr, WAIT_CMD, NULL);
    - return ide_started;
    + if (!ide_end_request(drive, 1))
    + return ide_stopped;
    }
    - return ide_stopped;
    +
    + /*
    + * still data left to transfer
    + */
    + ide_set_handler(drive, &task_in_intr, WAIT_CMD, NULL);
    + return ide_started;
    }

    static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq)
    @@ -870,7 +888,6 @@
    return;

    case WIN_NOP:
    -
    args->command_type = IDE_DRIVE_TASK_NO_DATA;
    return;

    diff -urzN -X /home/axboe/cdrom/exclude /opt/kernel/linux-2.5.8/drivers/ide/ide-tcq.c linux/drivers/ide/ide-tcq.c
    --- /opt/kernel/linux-2.5.8/drivers/ide/ide-tcq.c 2002-04-17 12:15:35.000000000 +0200
    +++ linux/drivers/ide/ide-tcq.c 2002-04-17 14:19:43.000000000 +0200
    @@ -29,9 +29,12 @@
    #include <asm/delay.h>

    /*
    - * warning: it will be _very_ verbose if set to 1
    + * warning: it will be _very_ verbose if defined
    */
    -#if 0
    +#undef IDE_TCQ_DEBUG
    +
    +#ifdef IDE_TCQ_DEBUG
    +#warning "Building debug enabled TCQ"
    #define TCQ_PRINTK printk
    #else
    #define TCQ_PRINTK(x...)
    @@ -49,16 +52,17 @@
    */
    #undef IDE_TCQ_FIDDLE_SI

    -int ide_tcq_end(ide_drive_t *drive);
    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)
    {
    #ifdef IDE_TCQ_NIEN
    - int mask = clear ? 0 : 2;
    + if (IDE_CONTROL_REG) {
    + int mask = clear ? 0 : 1 << 1;

    - if (IDE_CONTROL_REG)
    OUT_BYTE(drive->ctl | mask, IDE_CONTROL_REG);
    + }
    #endif
    }

    @@ -116,7 +120,6 @@
    init_taskfile_request(ar->ar_rq);
    ar->ar_rq->rq_dev = mk_kdev(drive->channel->major, (drive->select.b.unit)<<PARTN_BITS);
    ar->ar_rq->special = ar;
    - ar->ar_flags |= ATA_AR_RETURN;
    _elv_add_request(q, ar->ar_rq, 0, 0);

    /*
    @@ -142,7 +145,7 @@

    spin_lock_irqsave(&ide_lock, flags);

    - if (test_bit(IDE_BUSY, &hwgroup->flags))
    + if (test_and_set_bit(IDE_BUSY, &hwgroup->flags))
    printk("ide_tcq_intr_timeout: hwgroup not busy\n");
    if (hwgroup->handler == NULL)
    printk("ide_tcq_intr_timeout: missing isr!\n");
    @@ -151,6 +154,13 @@

    spin_unlock_irqrestore(&ide_lock, flags);

    + /*
    + * if pending commands, try service before giving up
    + */
    + if (ide_pending_commands(drive) && (GET_STAT() & SERVICE_STAT))
    + if (ide_service(drive) == ide_started)
    + return;
    +
    if (drive)
    ide_tcq_invalidate_queue(drive);
    }
    @@ -192,6 +202,7 @@

    if (unlikely(i++ > IDE_TCQ_WAIT))
    return 1;
    +
    } while ((*stat = GET_ALTSTAT()) & busy_mask);

    return 0;
    @@ -206,7 +217,12 @@
    ide_startstop_t ide_service(ide_drive_t *drive)
    {
    struct ata_request *ar;
    - byte feat, tag, stat;
    + byte feat, stat;
    + int tag, ret;
    +
    + TCQ_PRINTK("%s: started service\n", drive->name);
    +
    + drive->service_pending = 0;

    if (test_bit(IDE_DMA, &HWGROUP(drive)->flags))
    printk("ide_service: DMA in progress\n");
    @@ -238,9 +254,9 @@
    * FIXME, invalidate queue
    */
    if (stat & ERR_STAT) {
    - printk("%s: error SERVICING drive (%x)\n", drive->name, stat);
    ide_dump_status(drive, "ide_service", stat);
    - return ide_tcq_end(drive);
    + ide_tcq_invalidate_queue(drive);
    + return ide_stopped;
    }

    /*
    @@ -248,13 +264,10 @@
    */
    if ((feat = GET_FEAT()) & NSEC_REL) {
    printk("%s: release in service\n", drive->name);
    - IDE_SET_CUR_TAG(drive, -1);
    + IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG);
    return ide_stopped;
    }

    - /*
    - * start dma
    - */
    tag = feat >> 3;
    IDE_SET_CUR_TAG(drive, tag);

    @@ -265,28 +278,32 @@
    return ide_stopped;
    }

    + HWGROUP(drive)->rq = ar->ar_rq;
    +
    /*
    * we'll start a dma read or write, device will trigger
    * interrupt to indicate end of transfer, release is not allowed
    */
    if (rq_data_dir(ar->ar_rq) == READ) {
    TCQ_PRINTK("ide_service: starting READ %x\n", stat);
    - drive->channel->dmaproc(ide_dma_read_queued, drive);
    + ret = drive->channel->dmaproc(ide_dma_read_queued, drive);
    } else {
    TCQ_PRINTK("ide_service: starting WRITE %x\n", stat);
    - drive->channel->dmaproc(ide_dma_write_queued, drive);
    + ret = drive->channel->dmaproc(ide_dma_write_queued, drive);
    }

    /*
    * dmaproc set intr handler
    */
    - return ide_started;
    + return !ret ? ide_started : ide_stopped;
    }

    ide_startstop_t ide_check_service(ide_drive_t *drive)
    {
    byte stat;

    + TCQ_PRINTK("%s: ide_check_service\n", drive->name);
    +
    if (!ide_pending_commands(drive))
    return ide_stopped;

    @@ -300,40 +317,14 @@
    return ide_started;
    }

    -int ide_tcq_end(ide_drive_t *drive)
    -{
    - byte stat = GET_STAT();
    -
    - if (stat & ERR_STAT) {
    - ide_dump_status(drive, "ide_tcq_end", stat);
    - ide_tcq_invalidate_queue(drive);
    - return ide_stopped;
    - } else if (stat & SERVICE_STAT) {
    - TCQ_PRINTK("ide_tcq_end: serv stat=%x\n", stat);
    - return ide_service(drive);
    - }
    -
    - TCQ_PRINTK("ide_tcq_end: stat=%x, feat=%x\n", stat, GET_FEAT());
    - return ide_stopped;
    -}
    -
    ide_startstop_t ide_dmaq_complete(ide_drive_t *drive, byte stat)
    {
    - struct ata_request *ar;
    + struct ata_request *ar = IDE_CUR_TAG(drive);
    byte dma_stat;

    -#if 0
    - byte feat = GET_FEAT();
    -
    - if ((feat & (NSEC_CD | NSEC_IO)) != (NSEC_CD | NSEC_IO))
    - printk("%s: C/D | I/O not set\n", drive->name);
    -#endif
    -
    /*
    * transfer was in progress, stop DMA engine
    */
    - ar = IDE_CUR_TAG(drive);
    -
    dma_stat = drive->channel->dmaproc(ide_dma_end, drive);

    /*
    @@ -352,7 +343,23 @@
    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, -1);
    + 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
    + */
    +#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
    +
    return ide_check_service(drive);
    }

    @@ -376,7 +383,7 @@
    * if a command completion interrupt is pending, do that first and
    * check service afterwards
    */
    - if (drive->tcq->active_tag != -1)
    + if (drive->tcq->active_tag != IDE_INACTIVE_TAG)
    return ide_dmaq_complete(drive, stat);

    /*
    @@ -396,17 +403,16 @@
    */
    static int ide_tcq_configure(ide_drive_t *drive)
    {
    + int tcq_mask = 1 << 1 | 1 << 14;
    + int tcq_bits = tcq_mask | 1 << 15;
    struct ata_taskfile args;
    - int tcq_supp = 1 << 1 | 1 << 14;

    /*
    * bit 14 and 1 must be set in word 83 of the device id to indicate
    - * support for dma queued protocol
    + * support for dma queued protocol, and bit 15 must be cleared
    */
    - if ((drive->id->command_set_2 & tcq_supp) != tcq_supp) {
    - printk("%s: queued feature set not supported\n", drive->name);
    - return 1;
    - }
    + if ((drive->id->command_set_2 & tcq_bits) ^ tcq_mask)
    + return -EIO;

    memset(&args, 0, sizeof(args));
    args.taskfile.feature = SETFEATURES_EN_WCACHE;
    @@ -446,6 +452,16 @@
    return 1;
    }
    #endif
    +
    + if (!drive->tcq) {
    + drive->tcq = kmalloc(sizeof(ide_tag_info_t), GFP_ATOMIC);
    + if (!drive->tcq)
    + return -ENOMEM;
    +
    + memset(drive->tcq, 0, sizeof(ide_tag_info_t));
    + drive->tcq->active_tag = IDE_INACTIVE_TAG;
    + }
    +
    return 0;
    }

    @@ -461,26 +477,39 @@
    if (!on) {
    printk("%s: TCQ disabled\n", drive->name);
    drive->using_tcq = 0;
    -
    return 0;
    - } else if (drive->using_tcq) {
    - drive->queue_depth = drive->using_tcq;
    -
    - goto out;
    }

    if (ide_tcq_configure(drive)) {
    drive->using_tcq = 0;
    -
    return 1;
    }

    -out:
    - drive->tcq->max_depth = 0;
    + /*
    + * possibly expand command list
    + */
    + if (ide_build_commandlist(drive))
    + return 1;

    printk("%s: tagged command queueing enabled, command queue depth %d\n", drive->name, drive->queue_depth);
    drive->using_tcq = 1;

    + /*
    + * clear stats
    + */
    + drive->tcq->max_depth = 0;
    + return 0;
    +}
    +
    +int ide_tcq_wait_dataphase(ide_drive_t *drive)
    +{
    + ide_startstop_t foo;
    +
    + if (ide_wait_stat(&foo, drive, READY_STAT | DRQ_STAT, BUSY_STAT, WAIT_READY)) {
    + printk("%s: timeout waiting for data phase\n", drive->name);
    + return 1;
    + }
    +
    return 0;
    }

    @@ -488,7 +517,6 @@
    {
    struct ata_channel *hwif = drive->channel;
    unsigned int reading = 0, enable_tcq = 1;
    - ide_startstop_t startstop;
    struct ata_request *ar;
    byte stat, feat;

    @@ -501,15 +529,13 @@
    reading = 1 << 3;
    case ide_dma_write_queued:
    TCQ_PRINTK("ide_dma: setting up queued %d\n", drive->tcq->active_tag);
    - BUG_ON(drive->tcq->active_tag == -1);
    + BUG_ON(drive->tcq->active_tag == IDE_INACTIVE_TAG);

    if (!test_bit(IDE_BUSY, &HWGROUP(drive)->flags))
    printk("queued_rw: IDE_BUSY not set\n");

    - if (ide_wait_stat(&startstop, drive, READY_STAT | DRQ_STAT, BUSY_STAT, WAIT_READY)) {
    - printk("%s: timeout waiting for data phase\n", drive->name);
    - return startstop;
    - }
    + if (ide_tcq_wait_dataphase(drive))
    + return ide_stopped;

    if (ide_start_dma(hwif, drive, func))
    return 1;
    @@ -521,7 +547,7 @@
    * start a queued command from scratch
    */
    case ide_dma_queued_start:
    - BUG_ON(drive->tcq->active_tag == -1);
    + BUG_ON(drive->tcq->active_tag == IDE_INACTIVE_TAG);
    ar = IDE_CUR_TAG(drive);

    /*
    @@ -540,14 +566,19 @@
    drive_ctl_nien(drive, 0);

    if (stat & ERR_STAT) {
    - printk("ide_dma_queued_start: abort (stat=%x)\n", stat);
    + ide_dump_status(drive, "tcq_start", stat);
    return ide_stopped;
    }

    + /*
    + * drive released the bus, clear active tag and
    + * check for service
    + */
    if ((feat = GET_FEAT()) & NSEC_REL) {
    + IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG);
    drive->tcq->immed_rel++;
    +
    TCQ_PRINTK("REL in queued_start\n");
    - IDE_SET_CUR_TAG(drive, -1);

    if ((stat = GET_STAT()) & SERVICE_STAT)
    return ide_service(drive);
    @@ -558,26 +589,33 @@

    drive->tcq->immed_comp++;

    - if (ide_wait_stat(&startstop, drive, READY_STAT | DRQ_STAT, BUSY_STAT, WAIT_READY)) {
    - printk("%s: timeout waiting for data phase\n", drive->name);
    - return startstop;
    - }
    + if (ide_tcq_wait_dataphase(drive))
    + return ide_stopped;

    if (ide_start_dma(hwif, drive, func))
    return ide_stopped;

    + TCQ_PRINTK("IMMED in queued_start\n");
    +
    + /*
    + * need to arm handler before starting dma engine,
    + * transfer could complete right away
    + */
    + ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr);
    +
    if (hwif->dmaproc(ide_dma_begin, drive))
    return ide_stopped;

    /*
    * wait for SERVICE or completion interrupt
    */
    - ide_tcq_set_intr(HWGROUP(drive), ide_dmaq_intr);
    return ide_started;

    case ide_dma_queued_off:
    enable_tcq = 0;
    case ide_dma_queued_on:
    + if (enable_tcq && !drive->using_dma)
    + return 1;
    return ide_enable_queued(drive, enable_tcq);
    default:
    break;
    @@ -590,6 +628,10 @@
    ide_startstop_t ide_start_tag(ide_dma_action_t func, ide_drive_t *drive,
    struct ata_request *ar)
    {
    + ide_startstop_t startstop;
    +
    + TCQ_PRINTK("%s: ide_start_tag: begin tag %p/%d, rq %p\n", drive->name,ar,ar->ar_tag, ar->ar_rq);
    +
    /*
    * do this now, no need to run that with interrupts disabled
    */
    @@ -597,5 +639,14 @@
    return ide_stopped;

    IDE_SET_CUR_TAG(drive, ar->ar_tag);
    - return ide_tcq_dmaproc(func, drive);
    + HWGROUP(drive)->rq = ar->ar_rq;
    +
    + startstop = ide_tcq_dmaproc(func, drive);
    +
    + if (unlikely(startstop == ide_stopped)) {
    + IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG);
    + HWGROUP(drive)->rq = NULL;
    + }
    +
    + return startstop;
    }
    diff -urzN -X /home/axboe/cdrom/exclude /opt/kernel/linux-2.5.8/drivers/ide/ide.c linux/drivers/ide/ide.c
    --- /opt/kernel/linux-2.5.8/drivers/ide/ide.c 2002-04-17 12:15:34.000000000 +0200
    +++ linux/drivers/ide/ide.c 2002-04-17 11:43:00.000000000 +0200
    @@ -381,8 +381,23 @@
    add_blkdev_randomness(major(rq->rq_dev));

    spin_lock_irqsave(&ide_lock, flags);
    +
    + if ((jiffies - ar->ar_time > ATA_AR_MAX_TURNAROUND) && drive->queue_depth > 1) {
    + printk("%s: exceeded max command turn-around time (%d seconds)\n", drive->name, ATA_AR_MAX_TURNAROUND / HZ);
    + drive->queue_depth >>= 1;
    + }
    +
    + if (jiffies - ar->ar_time > drive->tcq->oldest_command)
    + drive->tcq->oldest_command = jiffies - ar->ar_time;
    +
    ata_ar_put(drive, ar);
    end_that_request_last(rq);
    + /*
    + * IDE_SET_CUR_TAG(drive, IDE_INACTIVE_TAG) will do this
    + * too, but it really belongs here. assumes that the
    + * ended request is the active one.
    + */
    + HWGROUP(drive)->rq = NULL;
    spin_unlock_irqrestore(&ide_lock, flags);
    }
    }
    @@ -770,8 +785,7 @@
    struct ata_taskfile *args = &ar->ar_task;

    rq->errors = !OK_STAT(stat, READY_STAT, BAD_STAT);
    - if (args && args->taskfile.command == WIN_NOP)
    - printk(KERN_INFO "%s: NOP completed\n", __FUNCTION__);
    +
    if (args) {
    args->taskfile.feature = err;
    args->taskfile.sector_count = IN_BYTE(IDE_NSECTOR_REG);
    @@ -794,8 +808,7 @@
    args->hobfile.high_cylinder = IN_BYTE(IDE_HCYL_REG);
    }
    }
    - if (ar->ar_flags & ATA_AR_RETURN)
    - ata_ar_put(drive, ar);
    + ata_ar_put(drive, ar);
    }

    blkdev_dequeue_request(rq);
    @@ -1101,11 +1114,7 @@
    while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
    #endif

    - if (test_bit(IDE_DMA, &HWGROUP(drive)->flags))
    - printk("start_request: auch, DMA in progress 1\n");
    SELECT_DRIVE(hwif, drive);
    - if (test_bit(IDE_DMA, &HWGROUP(drive)->flags))
    - printk("start_request: auch, DMA in progress 2\n");
    if (ide_wait_stat(&startstop, drive, drive->ready_stat,
    BUSY_STAT|DRQ_STAT, WAIT_READY)) {
    printk(KERN_WARNING "%s: drive not ready for command\n", drive->name);
    @@ -1277,6 +1286,170 @@
    return best;
    }

    +static ide_drive_t *ide_choose_drive(ide_hwgroup_t *hwgroup)
    +{
    + ide_drive_t *drive = choose_drive(hwgroup);
    + unsigned long sleep = 0;
    +
    + if (drive)
    + return drive;
    +
    + hwgroup->rq = NULL;
    + drive = hwgroup->drive;
    + do {
    + if (drive->PADAM_sleep && (!sleep || time_after(sleep, drive->PADAM_sleep)))
    + sleep = drive->PADAM_sleep;
    + } while ((drive = drive->next) != hwgroup->drive);
    +
    + if (sleep) {
    + /*
    + * Take a short snooze, and then wake up this hwgroup
    + * again. This gives other hwgroups on the same a
    + * chance to play fairly with us, just in case there
    + * are big differences in relative throughputs.. don't
    + * want to hog the cpu too much.
    + */
    + if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep))
    + sleep = jiffies + WAIT_MIN_SLEEP;
    +
    + if (timer_pending(&hwgroup->timer))
    + printk("ide_set_handler: timer already active\n");
    +
    + set_bit(IDE_SLEEP, &hwgroup->flags);
    + mod_timer(&hwgroup->timer, sleep);
    + /* we purposely leave hwgroup busy while
    + * sleeping */
    + } else {
    + /* Ugly, but how can we sleep for the lock
    + * otherwise? perhaps from tq_disk? */
    + ide_release_lock(&ide_intr_lock);/* for atari only */
    + clear_bit(IDE_BUSY, &hwgroup->flags);
    + }
    +
    + return NULL;
    +}
    +
    +#ifdef CONFIG_BLK_DEV_IDE_TCQ
    +ide_startstop_t ide_check_service(ide_drive_t *drive);
    +#else
    +#define ide_check_service(drive) (ide_stopped)
    +#endif
    +
    +/*
    + * feed commands to a drive until it barfs. used to be part of ide_do_request.
    + * called with ide_lock/DRIVE_LOCK held and busy hwgroup
    + */
    +static void ide_queue_commands(ide_drive_t *drive, int masked_irq)
    +{
    + ide_hwgroup_t *hwgroup = HWGROUP(drive);
    + ide_startstop_t startstop = -1;
    + struct request *rq;
    + int do_service = 0;
    +
    + do {
    + rq = NULL;
    +
    + if (!test_bit(IDE_BUSY, &hwgroup->flags))
    + printk("%s: hwgroup not busy while queueing\n", drive->name);
    +
    + /*
    + * abort early if we can't queue another command. for non
    + * tcq, ide_can_queue is always 1 since we never get here
    + * unless the drive is idle.
    + */
    + if (!ide_can_queue(drive)) {
    + if (!ide_pending_commands(drive))
    + clear_bit(IDE_BUSY, &hwgroup->flags);
    + break;
    + }
    +
    + drive->PADAM_sleep = 0;
    + drive->PADAM_service_start = jiffies;
    +
    + if (test_bit(IDE_DMA, &hwgroup->flags)) {
    + printk("ide_do_request: DMA in progress...\n");
    + break;
    + }
    +
    + /*
    + * there's a small window between where the queue could be
    + * replugged while we are in here when using tcq (in which
    + * case the queue is probably empty anyways...), so check
    + * and leave if appropriate. When not using tcq, this is
    + * still a severe BUG!
    + */
    + if (blk_queue_plugged(&drive->queue)) {
    + BUG_ON(!drive->using_tcq);
    + break;
    + }
    +
    + if (!(rq = elv_next_request(&drive->queue))) {
    + if (!ide_pending_commands(drive))
    + clear_bit(IDE_BUSY, &hwgroup->flags);
    + hwgroup->rq = NULL;
    + break;
    + }
    +
    + /*
    + * if there are queued commands, we can't start a non-fs
    + * request (really, a non-queuable command) until the
    + * queue is empty
    + */
    + if (!(rq->flags & REQ_CMD) && ide_pending_commands(drive))
    + break;
    +
    + hwgroup->rq = rq;
    +
    +service:
    + /*
    + * Some systems have trouble with IDE IRQs arriving while
    + * the driver is still setting things up. So, here we disable
    + * the IRQ used by this interface while the request is being
    + * started. This may look bad at first, but pretty much the
    + * same thing happens anyway when any interrupt comes in, IDE
    + * or otherwise -- the kernel masks the IRQ while it is being
    + * handled.
    + */
    + if (masked_irq && HWIF(drive)->irq != masked_irq)
    + disable_irq_nosync(HWIF(drive)->irq);
    +
    + spin_unlock(&ide_lock);
    + ide__sti(); /* allow other IRQs while we start this request */
    + if (!do_service)
    + startstop = start_request(drive, rq);
    + else
    + startstop = ide_check_service(drive);
    +
    + spin_lock_irq(&ide_lock);
    + if (masked_irq && HWIF(drive)->irq != masked_irq)
    + enable_irq(HWIF(drive)->irq);
    +
    + /*
    + * command started, we are busy
    + */
    + if (startstop == ide_started)
    + break;
    +
    + /*
    + * start_request() can return either ide_stopped (no command
    + * was started), ide_started (command started, don't queue
    + * more), or ide_released (command started, try and queue
    + * more).
    + */
    +#if 0
    + if (startstop == ide_stopped)
    + set_bit(IDE_BUSY, &hwgroup->flags);
    +#endif
    +
    + } while (1);
    +
    + if (startstop == ide_started)
    + return;
    +
    + if ((do_service = drive->service_pending))
    + goto service;
    +}
    +
    /*
    * Issue a new request to a drive from hwgroup
    * Caller must have already done spin_lock_irqsave(&ide_lock, ...)
    @@ -1311,115 +1484,34 @@
    {
    ide_drive_t *drive;
    struct ata_channel *hwif;
    - ide_startstop_t startstop;
    - struct request *rq;

    ide_get_lock(&ide_intr_lock, ide_intr, hwgroup);/* for atari only: POSSIBLY BROKEN HERE(?) */

    __cli(); /* necessary paranoia: ensure IRQs are masked on local CPU */

    while (!test_and_set_bit(IDE_BUSY, &hwgroup->flags)) {
    - drive = choose_drive(hwgroup);
    - if (drive == NULL) {
    - unsigned long sleep = 0;
    - hwgroup->rq = NULL;
    - drive = hwgroup->drive;
    - do {
    - if (drive->PADAM_sleep && (!sleep || time_after(sleep, drive->PADAM_sleep)))
    - sleep = drive->PADAM_sleep;
    - } while ((drive = drive->next) != hwgroup->drive);
    - if (sleep) {
    - /*
    - * Take a short snooze, and then wake up this hwgroup again.
    - * This gives other hwgroups on the same a chance to
    - * play fairly with us, just in case there are big differences
    - * in relative throughputs.. don't want to hog the cpu too much.
    - */
    - if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep))
    - sleep = jiffies + WAIT_MIN_SLEEP;
    -#if 1
    - if (timer_pending(&hwgroup->timer))
    - printk("ide_set_handler: timer already active\n");
    -#endif
    - set_bit(IDE_SLEEP, &hwgroup->flags);
    - mod_timer(&hwgroup->timer, sleep);
    - /* we purposely leave hwgroup busy while sleeping */
    - } else {
    - /* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_disk? */
    - ide_release_lock(&ide_intr_lock);/* for atari only */
    - clear_bit(IDE_BUSY, &hwgroup->flags);
    - }
    - return; /* no more work for this hwgroup (for now) */
    - }
    +
    + /*
    + * will clear IDE_BUSY, if appropriate
    + */
    + if ((drive = ide_choose_drive(hwgroup)) == NULL)
    + break;
    +
    hwif = drive->channel;
    - if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif && hwif->io_ports[IDE_CONTROL_OFFSET]) {
    + if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif && IDE_CONTROL_REG) {
    /* set nIEN for previous hwif */
    -
    if (hwif->intrproc)
    hwif->intrproc(drive);
    else
    - OUT_BYTE((drive)->ctl|2, hwif->io_ports[IDE_CONTROL_OFFSET]);
    + OUT_BYTE(drive->ctl|2, IDE_CONTROL_REG);
    }
    hwgroup->hwif = hwif;
    hwgroup->drive = drive;
    - drive->PADAM_sleep = 0;
    -queue_next:
    - drive->PADAM_service_start = jiffies;
    -
    - if (test_bit(IDE_DMA, &hwgroup->flags)) {
    - printk("ide_do_request: DMA in progress...\n");
    - break;
    - }

    /*
    - * there's a small window between where the queue could be
    - * replugged while we are in here when using tcq (in which
    - * case the queue is probably empty anyways...), so check
    - * and leave if appropriate. When not using tcq, this is
    - * still a severe BUG!
    + * main queueing loop
    */
    - if (blk_queue_plugged(&drive->queue)) {
    - BUG_ON(!drive->using_tcq);
    - break;
    - }
    -
    - /*
    - * just continuing an interrupted request maybe
    - */
    - rq = hwgroup->rq = elv_next_request(&drive->queue);
    -
    - if (!rq) {
    - if (!ide_pending_commands(drive))
    - clear_bit(IDE_BUSY, &HWGROUP(drive)->flags);
    - break;
    - }
    -
    - /*
    - * Some systems have trouble with IDE IRQs arriving while
    - * the driver is still setting things up. So, here we disable
    - * the IRQ used by this interface while the request is being started.
    - * This may look bad at first, but pretty much the same thing
    - * happens anyway when any interrupt comes in, IDE or otherwise
    - * -- the kernel masks the IRQ while it is being handled.
    - */
    - if (masked_irq && hwif->irq != masked_irq)
    - disable_irq_nosync(hwif->irq);
    -
    - spin_unlock(&ide_lock);
    - ide__sti(); /* allow other IRQs while we start this request */
    - startstop = start_request(drive, rq);
    -
    - spin_lock_irq(&ide_lock);
    - if (masked_irq && hwif->irq != masked_irq)
    - enable_irq(hwif->irq);
    -
    - if (startstop == ide_released)
    - goto queue_next;
    - else if (startstop == ide_stopped) {
    - if (test_bit(IDE_DMA, &hwgroup->flags))
    - printk("2nd illegal clear\n");
    - clear_bit(IDE_BUSY, &hwgroup->flags);
    - }
    + ide_queue_commands(drive, masked_irq);
    }
    }

    @@ -1673,6 +1765,7 @@
    goto out_lock;

    if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) {
    + printk("ide: unexpected interrupt\n");
    /*
    * Not expecting an interrupt from this drive.
    * That means this could be:
    @@ -1685,9 +1778,7 @@
    * For PCI, we cannot tell the difference,
    * so in that case we just ignore it and hope it goes away.
    */
    -#ifdef CONFIG_BLK_DEV_IDEPCI
    if (hwif->pci_dev && !hwif->pci_dev->vendor)
    -#endif
    {
    /*
    * Probably not a shared PCI interrupt,
    @@ -1751,7 +1842,8 @@
    } else {
    printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name);
    }
    - }
    + } else if (startstop == ide_released)
    + ide_queue_commands(drive, hwif->irq);

    out_lock:
    spin_unlock_irqrestore(&ide_lock, flags);
    @@ -2221,6 +2313,8 @@
    channel->udma_four = old_hwif.udma_four;
    #ifdef CONFIG_BLK_DEV_IDEPCI
    channel->pci_dev = old_hwif.pci_dev;
    +#else
    + channel->pci_dev = NULL;
    #endif
    channel->straight8 = old_hwif.straight8;
    abort:
    @@ -2699,42 +2793,29 @@
    }
    }

    -void ide_teardown_commandlist(ide_drive_t *drive)
    -{
    - struct pci_dev *pdev= drive->channel->pci_dev;
    - struct list_head *entry;
    -
    - list_for_each(entry, &drive->free_req) {
    - struct ata_request *ar = list_ata_entry(entry);
    -
    - list_del(&ar->ar_queue);
    - kfree(ar->ar_sg_table);
    - pci_free_consistent(pdev, PRD_SEGMENTS * PRD_BYTES, ar->ar_dmatable_cpu, ar->ar_dmatable);
    - kfree(ar);
    - }
    -}
    -
    int ide_build_commandlist(ide_drive_t *drive)
    {
    struct pci_dev *pdev= drive->channel->pci_dev;
    + struct list_head *p;
    + unsigned long flags;
    struct ata_request *ar;
    - ide_tag_info_t *tcq;
    - int i, err;
    + int i, cur;

    - tcq = kmalloc(sizeof(ide_tag_info_t), GFP_ATOMIC);
    - if (!tcq)
    - return -ENOMEM;
    + spin_lock_irqsave(&ide_lock, flags);

    - drive->tcq = tcq;
    - memset(drive->tcq, 0, sizeof(ide_tag_info_t));
    + cur = 0;
    + list_for_each(p, &drive->free_req)
    + cur++;

    - INIT_LIST_HEAD(&drive->free_req);
    - drive->using_tcq = 0;
    + /*
    + * for now, just don't shrink it...
    + */
    + if (drive->queue_depth <= cur) {
    + spin_unlock_irqrestore(&ide_lock, flags);
    + return 0;
    + }

    - err = -ENOMEM;
    - for (i = 0; i < drive->queue_depth; i++) {
    - /* Having kzmalloc would help reduce code size at quite
    - * many places in kernel. */
    + for (i = cur; i < drive->queue_depth; i++) {
    ar = kmalloc(sizeof(*ar), GFP_ATOMIC);
    if (!ar)
    break;
    @@ -2755,29 +2836,40 @@
    break;
    }

    +
    +
    /*
    * pheew, all done, add to list
    */
    list_add_tail(&ar->ar_queue, &drive->free_req);
    + cur++;
    }

    - if (i) {
    - drive->queue_depth = i;
    - if (i >= 1) {
    - drive->using_tcq = 1;
    - drive->tcq->queued = 0;
    - drive->tcq->active_tag = -1;
    - return 0;
    - }
    + drive->queue_depth = cur;
    + spin_unlock_irqrestore(&ide_lock, flags);
    + return 0;
    +}

    - kfree(drive->tcq);
    - drive->tcq = NULL;
    - err = 0;
    - }
    +int ide_init_commandlist(ide_drive_t *drive)
    +{
    + INIT_LIST_HEAD(&drive->free_req);

    - kfree(drive->tcq);
    - drive->tcq = NULL;
    - return err;
    + return ide_build_commandlist(drive);
    +}
    +
    +void ide_teardown_commandlist(ide_drive_t *drive)
    +{
    + struct pci_dev *pdev= drive->channel->pci_dev;
    + struct list_head *entry;
    +
    + list_for_each(entry, &drive->free_req) {
    + struct ata_request *ar = list_ata_entry(entry);
    +
    + list_del(&ar->ar_queue);
    + kfree(ar->ar_sg_table);
    + pci_free_consistent(pdev, PRD_SEGMENTS * PRD_BYTES, ar->ar_dmatable_cpu, ar->ar_dmatable);
    + kfree(ar);
    + }
    }

    static int ide_check_media_change (kdev_t i_rdev)
    diff -urzN -X /home/axboe/cdrom/exclude /opt/kernel/linux-2.5.8/include/linux/ide.h linux/include/linux/ide.h
    --- /opt/kernel/linux-2.5.8/include/linux/ide.h 2002-04-17 12:16:08.000000000 +0200
    +++ linux/include/linux/ide.h 2002-04-17 11:47:36.000000000 +0200
    @@ -273,36 +273,44 @@
    } b;
    } special_t;

    -#define IDE_MAX_TAG 32 /* spec says 32 max */
    +#define IDE_MAX_TAG (32) /* spec says 32 max */
    +#define IDE_INACTIVE_TAG (-1)

    struct ata_request;
    typedef struct ide_tag_info_s {
    unsigned long tag_mask; /* next tag bit mask */
    struct ata_request *ar[IDE_MAX_TAG]; /* in-progress requests */
    int active_tag; /* current active tag */
    +
    int queued; /* current depth */

    /*
    * stats ->
    */
    int max_depth; /* max depth ever */
    +
    int max_last_depth; /* max since last check */

    /*
    - * Either the command completed immediately after being started
    + * either the command complete immediately after being started
    * (immed_comp), or the device did a bus release before dma was
    - * started (immed_rel).
    + * started (immed_rel)
    */
    int immed_rel;
    int immed_comp;
    + unsigned long oldest_command;
    } ide_tag_info_t;

    #define IDE_GET_AR(drive, tag) ((drive)->tcq->ar[(tag)])
    #define IDE_CUR_TAG(drive) (IDE_GET_AR((drive), (drive)->tcq->active_tag))
    -#define IDE_SET_CUR_TAG(drive, tag) ((drive)->tcq->active_tag = (tag))
    +#define IDE_SET_CUR_TAG(drive, tag) \
    + do { \
    + ((drive)->tcq->active_tag = (tag)); \
    + if ((tag) == IDE_INACTIVE_TAG) \
    + HWGROUP((drive))->rq = NULL; \
    + } while (0);

    -#define IDE_CUR_AR(drive) \
    - ((drive)->using_tcq ? IDE_CUR_TAG((drive)) : HWGROUP((drive))->rq->special)
    +#define IDE_CUR_AR(drive) (HWGROUP((drive))->rq->special)

    struct ide_settings_s;

    @@ -359,6 +367,7 @@
    unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */
    unsigned remap_0_to_1 : 2; /* 0=remap if ezdrive, 1=remap, 2=noremap */
    unsigned ata_flash : 1; /* 1=present, 0=default */
    + unsigned service_pending: 1;
    unsigned addressing; /* : 2; 0=28-bit, 1=48-bit, 2=64-bit */
    byte scsi; /* 0=default, 1=skip current ide-subdriver for ide-scsi emulation */
    select_t select; /* basic drive/head select reg value */
    @@ -493,9 +502,7 @@

    ide_ioreg_t io_ports[IDE_NR_PORTS]; /* task file registers */
    hw_regs_t hw; /* Hardware info */
    -#ifdef CONFIG_BLK_DEV_IDEPCI
    struct pci_dev *pci_dev; /* for pci chipsets */
    -#endif
    ide_drive_t drives[MAX_DRIVES]; /* drive info */
    struct gendisk *gd; /* gendisk structure */
    ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */
    @@ -546,7 +553,7 @@
    typedef enum {
    ide_stopped, /* no drive operation was started */
    ide_started, /* a drive operation was started, and a handler was set */
    - ide_released /* started and released bus */
    + ide_released, /* started, handler set, bus released */
    } ide_startstop_t;

    /*
    @@ -976,9 +983,14 @@
    /*
    * ata_request flag bits
    */
    -#define ATA_AR_QUEUED 1
    -#define ATA_AR_SETUP 2
    -#define ATA_AR_RETURN 4
    +#define ATA_AR_QUEUED 1 /* was queued */
    +#define ATA_AR_SETUP 2 /* dma table mapped */
    +#define ATA_AR_POOL 4 /* originated from drive pool */
    +
    +/*
    + * if turn-around time is longer than this, halve queue depth
    + */
    +#define ATA_AR_MAX_TURNAROUND (3 * HZ)

    #define list_ata_entry(entry) list_entry((entry), struct ata_request, ar_queue)

    @@ -1005,8 +1017,10 @@

    if (!list_empty(&drive->free_req)) {
    ar = list_ata_entry(drive->free_req.next);
    +
    list_del(&ar->ar_queue);
    ata_ar_init(drive, ar);
    + ar->ar_flags |= ATA_AR_POOL;
    }

    return ar;
    @@ -1014,10 +1028,13 @@

    static inline void ata_ar_put(ide_drive_t *drive, struct ata_request *ar)
    {
    - list_add(&ar->ar_queue, &drive->free_req);
    + if (ar->ar_flags & ATA_AR_POOL)
    + list_add(&ar->ar_queue, &drive->free_req);

    if (ar->ar_flags & ATA_AR_QUEUED) {
    - /* clear the tag */
    + /*
    + * clear tag
    + */
    drive->tcq->ar[ar->ar_tag] = NULL;
    __clear_bit(ar->ar_tag, &drive->tcq->tag_mask);
    drive->tcq->queued--;
    @@ -1026,7 +1043,7 @@
    ar->ar_rq = NULL;
    }

    -extern inline int ide_get_tag(ide_drive_t *drive)
    +static inline int ide_get_tag(ide_drive_t *drive)
    {
    int tag = ffz(drive->tcq->tag_mask);

    @@ -1043,14 +1060,30 @@
    }

    #ifdef CONFIG_BLK_DEV_IDE_TCQ
    -# define ide_pending_commands(drive) ((drive)->using_tcq && (drive)->tcq->queued)
    +static inline int ide_pending_commands(ide_drive_t *drive)
    +{
    + if (!drive->tcq)
    + return 0;
    +
    + return drive->tcq->queued;
    +}
    +
    +static inline int ide_can_queue(ide_drive_t *drive)
    +{
    + if (!drive->tcq)
    + return 1;
    +
    + return drive->tcq->queued < drive->queue_depth;
    +}
    #else
    -# define ide_pending_commands(drive) 0
    +#define ide_pending_commands(drive) (0)
    +#define ide_can_queue(drive) (1)
    #endif

    int ide_build_commandlist(ide_drive_t *);
    +int ide_init_commandlist(ide_drive_t *);
    void ide_teardown_commandlist(ide_drive_t *);
    int ide_tcq_dmaproc(ide_dma_action_t, ide_drive_t *);
    ide_startstop_t ide_start_tag(ide_dma_action_t, ide_drive_t *, struct ata_request *);

    -#endif
    +#endif /* _IDE_H */
    \
     
     \ /
      Last update: 2005-03-22 13:25    [W:0.117 / U:59.788 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site