ide: correctly prevent IDE timer expiry function to run if request was already handled
authorSuleiman Souhlal <suleiman@google.com>
Tue, 10 Apr 2007 20:38:37 +0000 (22:38 +0200)
committerBartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Tue, 10 Apr 2007 20:38:37 +0000 (22:38 +0200)
It is possible for the timer expiry function to run even though the
request has already been handled: ide_timer_expiry() only checks that
the handler is not NULL, but it is possible that we have handled a
request (thus clearing the handler) and then started a new request
(thus starting the timer again, and setting a handler).

A simple way to exhibit this is to set the DMA timeout to 1 jiffy and
run dd: The kernel will panic after a few minutes because
ide_timer_expiry() tries to add a timer when it's already active.

To fix this, we simply add a request generation count that gets
incremented at every interrupt, and check in ide_timer_expiry() that
we have not already handled a new interrupt before running the expiry
function.

Signed-off-by: Suleiman Souhlal <suleiman@google.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
drivers/ide/ide-io.c
drivers/ide/ide-iops.c
include/linux/ide.h

index 0e0280076fcde4b1274d5dee331e547509a3f335..8670112f1d3947a579fef59d59ec431485c9d288 100644 (file)
@@ -1226,6 +1226,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
 #endif
                                /* so that ide_timer_expiry knows what to do */
                                hwgroup->sleeping = 1;
+                               hwgroup->req_gen_timer = hwgroup->req_gen;
                                mod_timer(&hwgroup->timer, sleep);
                                /* we purposely leave hwgroup->busy==1
                                 * while sleeping */
@@ -1411,7 +1412,8 @@ void ide_timer_expiry (unsigned long data)
 
        spin_lock_irqsave(&ide_lock, flags);
 
-       if ((handler = hwgroup->handler) == NULL) {
+       if (((handler = hwgroup->handler) == NULL) ||
+           (hwgroup->req_gen != hwgroup->req_gen_timer)) {
                /*
                 * Either a marginal timeout occurred
                 * (got the interrupt just as timer expired),
@@ -1439,6 +1441,7 @@ void ide_timer_expiry (unsigned long data)
                                if ((wait = expiry(drive)) > 0) {
                                        /* reset timer */
                                        hwgroup->timer.expires  = jiffies + wait;
+                                       hwgroup->req_gen_timer = hwgroup->req_gen;
                                        add_timer(&hwgroup->timer);
                                        spin_unlock_irqrestore(&ide_lock, flags);
                                        return;
@@ -1653,6 +1656,7 @@ irqreturn_t ide_intr (int irq, void *dev_id)
                printk(KERN_ERR "%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name);
        }
        hwgroup->handler = NULL;
+       hwgroup->req_gen++;
        del_timer(&hwgroup->timer);
        spin_unlock(&ide_lock);
 
index 1ee53a551c3adf6fe6397aea0945c5f1f4cbff20..3caa176b31553bd4b2b79cee575aa9b4c8a43595 100644 (file)
@@ -889,6 +889,7 @@ static void __ide_set_handler (ide_drive_t *drive, ide_handler_t *handler,
        hwgroup->handler        = handler;
        hwgroup->expiry         = expiry;
        hwgroup->timer.expires  = jiffies + timeout;
+       hwgroup->req_gen_timer = hwgroup->req_gen;
        add_timer(&hwgroup->timer);
 }
 
@@ -929,6 +930,7 @@ void ide_execute_command(ide_drive_t *drive, task_ioreg_t cmd, ide_handler_t *ha
        hwgroup->handler        = handler;
        hwgroup->expiry         = expiry;
        hwgroup->timer.expires  = jiffies + timeout;
+       hwgroup->req_gen_timer = hwgroup->req_gen;
        add_timer(&hwgroup->timer);
        hwif->OUTBSYNC(drive, cmd, IDE_COMMAND_REG);
        /* Drive takes 400nS to respond, we must avoid the IRQ being
index 58564a199862fe1011fe9c3f3b0aac755114d07c..d3bbc7188b6a035d1f74622f33fc5fad66b54b9d 100644 (file)
@@ -861,6 +861,8 @@ typedef struct hwgroup_s {
        int (*expiry)(ide_drive_t *);
                /* ide_system_bus_speed */
        int pio_clock;
+       int req_gen;
+       int req_gen_timer;
 
        unsigned char cmd_buf[4];
 } ide_hwgroup_t;