aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuleiman Souhlal <suleiman@google.com>2007-04-10 16:38:37 -0400
committerBartlomiej Zolnierkiewicz <bzolnier@gmail.com>2007-04-10 16:38:37 -0400
commit23450319e2890986c247ec0aa1442f060e657e6d (patch)
treec30fc173d8b97343f8ae7b1b1ed511366af256ab
parent90f30eccf41302a2542f5d374af243061902bd98 (diff)
ide: correctly prevent IDE timer expiry function to run if request was already handled
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>
-rw-r--r--drivers/ide/ide-io.c6
-rw-r--r--drivers/ide/ide-iops.c2
-rw-r--r--include/linux/ide.h2
3 files changed, 9 insertions, 1 deletions
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index 0e0280076fcd..8670112f1d39 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -1226,6 +1226,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
1226#endif 1226#endif
1227 /* so that ide_timer_expiry knows what to do */ 1227 /* so that ide_timer_expiry knows what to do */
1228 hwgroup->sleeping = 1; 1228 hwgroup->sleeping = 1;
1229 hwgroup->req_gen_timer = hwgroup->req_gen;
1229 mod_timer(&hwgroup->timer, sleep); 1230 mod_timer(&hwgroup->timer, sleep);
1230 /* we purposely leave hwgroup->busy==1 1231 /* we purposely leave hwgroup->busy==1
1231 * while sleeping */ 1232 * while sleeping */
@@ -1411,7 +1412,8 @@ void ide_timer_expiry (unsigned long data)
1411 1412
1412 spin_lock_irqsave(&ide_lock, flags); 1413 spin_lock_irqsave(&ide_lock, flags);
1413 1414
1414 if ((handler = hwgroup->handler) == NULL) { 1415 if (((handler = hwgroup->handler) == NULL) ||
1416 (hwgroup->req_gen != hwgroup->req_gen_timer)) {
1415 /* 1417 /*
1416 * Either a marginal timeout occurred 1418 * Either a marginal timeout occurred
1417 * (got the interrupt just as timer expired), 1419 * (got the interrupt just as timer expired),
@@ -1439,6 +1441,7 @@ void ide_timer_expiry (unsigned long data)
1439 if ((wait = expiry(drive)) > 0) { 1441 if ((wait = expiry(drive)) > 0) {
1440 /* reset timer */ 1442 /* reset timer */
1441 hwgroup->timer.expires = jiffies + wait; 1443 hwgroup->timer.expires = jiffies + wait;
1444 hwgroup->req_gen_timer = hwgroup->req_gen;
1442 add_timer(&hwgroup->timer); 1445 add_timer(&hwgroup->timer);
1443 spin_unlock_irqrestore(&ide_lock, flags); 1446 spin_unlock_irqrestore(&ide_lock, flags);
1444 return; 1447 return;
@@ -1653,6 +1656,7 @@ irqreturn_t ide_intr (int irq, void *dev_id)
1653 printk(KERN_ERR "%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name); 1656 printk(KERN_ERR "%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name);
1654 } 1657 }
1655 hwgroup->handler = NULL; 1658 hwgroup->handler = NULL;
1659 hwgroup->req_gen++;
1656 del_timer(&hwgroup->timer); 1660 del_timer(&hwgroup->timer);
1657 spin_unlock(&ide_lock); 1661 spin_unlock(&ide_lock);
1658 1662
diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
index 1ee53a551c3a..3caa176b3155 100644
--- a/drivers/ide/ide-iops.c
+++ b/drivers/ide/ide-iops.c
@@ -889,6 +889,7 @@ static void __ide_set_handler (ide_drive_t *drive, ide_handler_t *handler,
889 hwgroup->handler = handler; 889 hwgroup->handler = handler;
890 hwgroup->expiry = expiry; 890 hwgroup->expiry = expiry;
891 hwgroup->timer.expires = jiffies + timeout; 891 hwgroup->timer.expires = jiffies + timeout;
892 hwgroup->req_gen_timer = hwgroup->req_gen;
892 add_timer(&hwgroup->timer); 893 add_timer(&hwgroup->timer);
893} 894}
894 895
@@ -929,6 +930,7 @@ void ide_execute_command(ide_drive_t *drive, task_ioreg_t cmd, ide_handler_t *ha
929 hwgroup->handler = handler; 930 hwgroup->handler = handler;
930 hwgroup->expiry = expiry; 931 hwgroup->expiry = expiry;
931 hwgroup->timer.expires = jiffies + timeout; 932 hwgroup->timer.expires = jiffies + timeout;
933 hwgroup->req_gen_timer = hwgroup->req_gen;
932 add_timer(&hwgroup->timer); 934 add_timer(&hwgroup->timer);
933 hwif->OUTBSYNC(drive, cmd, IDE_COMMAND_REG); 935 hwif->OUTBSYNC(drive, cmd, IDE_COMMAND_REG);
934 /* Drive takes 400nS to respond, we must avoid the IRQ being 936 /* Drive takes 400nS to respond, we must avoid the IRQ being
diff --git a/include/linux/ide.h b/include/linux/ide.h
index 58564a199862..d3bbc7188b6a 100644
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -861,6 +861,8 @@ typedef struct hwgroup_s {
861 int (*expiry)(ide_drive_t *); 861 int (*expiry)(ide_drive_t *);
862 /* ide_system_bus_speed */ 862 /* ide_system_bus_speed */
863 int pio_clock; 863 int pio_clock;
864 int req_gen;
865 int req_gen_timer;
864 866
865 unsigned char cmd_buf[4]; 867 unsigned char cmd_buf[4];
866} ide_hwgroup_t; 868} ide_hwgroup_t;