diff options
author | Alan Cox <alan@lxorguk.ukuu.org.uk> | 2008-01-09 21:36:01 -0800 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2008-01-25 20:23:28 +0000 |
commit | 01c785dcb4e9fd6c4c370fd9915fc10585ed64bd (patch) | |
tree | 18afb2d73c5f2fe21cae6908c110440320b3fcff /drivers | |
parent | b47711bfbcd4eb77ca61ef0162487b20e023ae55 (diff) |
[WATCHDOG] wdt: fix locking
The audit of _p usage shows various drivers assume inb_p is somehow atomic.
Of course it isn't and the delay can be split from the I/O cycle causing a
timing violation on chips that matter (eg this one)
With the proposed use of udelay() for some _p delays this will cease to be
a mostly theoretical bug (as the delay stall is unsplittable) and wants
fixing.
Lots of other drivers need fixing this way too.
Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/watchdog/wdt.c | 30 |
1 files changed, 27 insertions, 3 deletions
diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c index 53d0bb410df..756fb15fdce 100644 --- a/drivers/watchdog/wdt.c +++ b/drivers/watchdog/wdt.c @@ -70,6 +70,8 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" _ static int io=0x240; static int irq=11; +static DEFINE_SPINLOCK(wdt_lock); + module_param(io, int, 0); MODULE_PARM_DESC(io, "WDT io port (default=0x240)"); module_param(irq, int, 0); @@ -109,6 +111,8 @@ static void wdt_ctr_load(int ctr, int val) static int wdt_start(void) { + unsigned long flags; + spin_lock_irqsave(&wdt_lock, flags); inb_p(WDT_DC); /* Disable watchdog */ wdt_ctr_mode(0,3); /* Program CTR0 for Mode 3: Square Wave Generator */ wdt_ctr_mode(1,2); /* Program CTR1 for Mode 2: Rate Generator */ @@ -117,6 +121,7 @@ static int wdt_start(void) wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */ wdt_ctr_load(2,65535); /* Length of reset pulse */ outb_p(0, WDT_DC); /* Enable watchdog */ + spin_unlock_irqrestore(&wdt_lock, flags); return 0; } @@ -128,9 +133,12 @@ static int wdt_start(void) static int wdt_stop (void) { + unsigned long flags; + spin_lock_irqsave(&wdt_lock, flags); /* Turn the card off */ inb_p(WDT_DC); /* Disable watchdog */ wdt_ctr_load(2,0); /* 0 length reset pulses now */ + spin_unlock_irqrestore(&wdt_lock, flags); return 0; } @@ -143,11 +151,14 @@ static int wdt_stop (void) static int wdt_ping(void) { + unsigned long flags; + spin_lock_irqsave(&wdt_lock, flags); /* Write a watchdog value */ inb_p(WDT_DC); /* Disable watchdog */ wdt_ctr_mode(1,2); /* Re-Program CTR1 for Mode 2: Rate Generator */ wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */ outb_p(0, WDT_DC); /* Enable watchdog */ + spin_unlock_irqrestore(&wdt_lock, flags); return 0; } @@ -182,7 +193,12 @@ static int wdt_set_heartbeat(int t) static int wdt_get_status(int *status) { - unsigned char new_status=inb_p(WDT_SR); + unsigned char new_status; + unsigned long flags; + + spin_lock_irqsave(&wdt_lock, flags); + new_status = inb_p(WDT_SR); + spin_unlock_irqrestore(&wdt_lock, flags); *status=0; if (new_status & WDC_SR_ISOI0) @@ -214,8 +230,12 @@ static int wdt_get_status(int *status) static int wdt_get_temperature(int *temperature) { - unsigned short c=inb_p(WDT_RT); + unsigned short c; + unsigned long flags; + spin_lock_irqsave(&wdt_lock, flags); + c = inb_p(WDT_RT); + spin_unlock_irqrestore(&wdt_lock, flags); *temperature = (c * 11 / 15) + 7; return 0; } @@ -237,7 +257,10 @@ static irqreturn_t wdt_interrupt(int irq, void *dev_id) * Read the status register see what is up and * then printk it. */ - unsigned char status=inb_p(WDT_SR); + unsigned char status; + + spin_lock(&wdt_lock); + status = inb_p(WDT_SR); printk(KERN_CRIT "WDT status %d\n", status); @@ -265,6 +288,7 @@ static irqreturn_t wdt_interrupt(int irq, void *dev_id) printk(KERN_CRIT "Reset in 5ms.\n"); #endif } + spin_unlock(&wdt_lock); return IRQ_HANDLED; } |