diff options
author | Alan Cox <alan@lxorguk.ukuu.org.uk> | 2008-01-10 00:36:01 -0500 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2008-01-25 15:23:28 -0500 |
commit | 01c785dcb4e9fd6c4c370fd9915fc10585ed64bd (patch) | |
tree | 18afb2d73c5f2fe21cae6908c110440320b3fcff | |
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>
-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 53d0bb410df8..756fb15fdce7 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=" _ | |||
70 | static int io=0x240; | 70 | static int io=0x240; |
71 | static int irq=11; | 71 | static int irq=11; |
72 | 72 | ||
73 | static DEFINE_SPINLOCK(wdt_lock); | ||
74 | |||
73 | module_param(io, int, 0); | 75 | module_param(io, int, 0); |
74 | MODULE_PARM_DESC(io, "WDT io port (default=0x240)"); | 76 | MODULE_PARM_DESC(io, "WDT io port (default=0x240)"); |
75 | module_param(irq, int, 0); | 77 | module_param(irq, int, 0); |
@@ -109,6 +111,8 @@ static void wdt_ctr_load(int ctr, int val) | |||
109 | 111 | ||
110 | static int wdt_start(void) | 112 | static int wdt_start(void) |
111 | { | 113 | { |
114 | unsigned long flags; | ||
115 | spin_lock_irqsave(&wdt_lock, flags); | ||
112 | inb_p(WDT_DC); /* Disable watchdog */ | 116 | inb_p(WDT_DC); /* Disable watchdog */ |
113 | wdt_ctr_mode(0,3); /* Program CTR0 for Mode 3: Square Wave Generator */ | 117 | wdt_ctr_mode(0,3); /* Program CTR0 for Mode 3: Square Wave Generator */ |
114 | wdt_ctr_mode(1,2); /* Program CTR1 for Mode 2: Rate Generator */ | 118 | wdt_ctr_mode(1,2); /* Program CTR1 for Mode 2: Rate Generator */ |
@@ -117,6 +121,7 @@ static int wdt_start(void) | |||
117 | wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */ | 121 | wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */ |
118 | wdt_ctr_load(2,65535); /* Length of reset pulse */ | 122 | wdt_ctr_load(2,65535); /* Length of reset pulse */ |
119 | outb_p(0, WDT_DC); /* Enable watchdog */ | 123 | outb_p(0, WDT_DC); /* Enable watchdog */ |
124 | spin_unlock_irqrestore(&wdt_lock, flags); | ||
120 | return 0; | 125 | return 0; |
121 | } | 126 | } |
122 | 127 | ||
@@ -128,9 +133,12 @@ static int wdt_start(void) | |||
128 | 133 | ||
129 | static int wdt_stop (void) | 134 | static int wdt_stop (void) |
130 | { | 135 | { |
136 | unsigned long flags; | ||
137 | spin_lock_irqsave(&wdt_lock, flags); | ||
131 | /* Turn the card off */ | 138 | /* Turn the card off */ |
132 | inb_p(WDT_DC); /* Disable watchdog */ | 139 | inb_p(WDT_DC); /* Disable watchdog */ |
133 | wdt_ctr_load(2,0); /* 0 length reset pulses now */ | 140 | wdt_ctr_load(2,0); /* 0 length reset pulses now */ |
141 | spin_unlock_irqrestore(&wdt_lock, flags); | ||
134 | return 0; | 142 | return 0; |
135 | } | 143 | } |
136 | 144 | ||
@@ -143,11 +151,14 @@ static int wdt_stop (void) | |||
143 | 151 | ||
144 | static int wdt_ping(void) | 152 | static int wdt_ping(void) |
145 | { | 153 | { |
154 | unsigned long flags; | ||
155 | spin_lock_irqsave(&wdt_lock, flags); | ||
146 | /* Write a watchdog value */ | 156 | /* Write a watchdog value */ |
147 | inb_p(WDT_DC); /* Disable watchdog */ | 157 | inb_p(WDT_DC); /* Disable watchdog */ |
148 | wdt_ctr_mode(1,2); /* Re-Program CTR1 for Mode 2: Rate Generator */ | 158 | wdt_ctr_mode(1,2); /* Re-Program CTR1 for Mode 2: Rate Generator */ |
149 | wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */ | 159 | wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */ |
150 | outb_p(0, WDT_DC); /* Enable watchdog */ | 160 | outb_p(0, WDT_DC); /* Enable watchdog */ |
161 | spin_unlock_irqrestore(&wdt_lock, flags); | ||
151 | return 0; | 162 | return 0; |
152 | } | 163 | } |
153 | 164 | ||
@@ -182,7 +193,12 @@ static int wdt_set_heartbeat(int t) | |||
182 | 193 | ||
183 | static int wdt_get_status(int *status) | 194 | static int wdt_get_status(int *status) |
184 | { | 195 | { |
185 | unsigned char new_status=inb_p(WDT_SR); | 196 | unsigned char new_status; |
197 | unsigned long flags; | ||
198 | |||
199 | spin_lock_irqsave(&wdt_lock, flags); | ||
200 | new_status = inb_p(WDT_SR); | ||
201 | spin_unlock_irqrestore(&wdt_lock, flags); | ||
186 | 202 | ||
187 | *status=0; | 203 | *status=0; |
188 | if (new_status & WDC_SR_ISOI0) | 204 | if (new_status & WDC_SR_ISOI0) |
@@ -214,8 +230,12 @@ static int wdt_get_status(int *status) | |||
214 | 230 | ||
215 | static int wdt_get_temperature(int *temperature) | 231 | static int wdt_get_temperature(int *temperature) |
216 | { | 232 | { |
217 | unsigned short c=inb_p(WDT_RT); | 233 | unsigned short c; |
234 | unsigned long flags; | ||
218 | 235 | ||
236 | spin_lock_irqsave(&wdt_lock, flags); | ||
237 | c = inb_p(WDT_RT); | ||
238 | spin_unlock_irqrestore(&wdt_lock, flags); | ||
219 | *temperature = (c * 11 / 15) + 7; | 239 | *temperature = (c * 11 / 15) + 7; |
220 | return 0; | 240 | return 0; |
221 | } | 241 | } |
@@ -237,7 +257,10 @@ static irqreturn_t wdt_interrupt(int irq, void *dev_id) | |||
237 | * Read the status register see what is up and | 257 | * Read the status register see what is up and |
238 | * then printk it. | 258 | * then printk it. |
239 | */ | 259 | */ |
240 | unsigned char status=inb_p(WDT_SR); | 260 | unsigned char status; |
261 | |||
262 | spin_lock(&wdt_lock); | ||
263 | status = inb_p(WDT_SR); | ||
241 | 264 | ||
242 | printk(KERN_CRIT "WDT status %d\n", status); | 265 | printk(KERN_CRIT "WDT status %d\n", status); |
243 | 266 | ||
@@ -265,6 +288,7 @@ static irqreturn_t wdt_interrupt(int irq, void *dev_id) | |||
265 | printk(KERN_CRIT "Reset in 5ms.\n"); | 288 | printk(KERN_CRIT "Reset in 5ms.\n"); |
266 | #endif | 289 | #endif |
267 | } | 290 | } |
291 | spin_unlock(&wdt_lock); | ||
268 | return IRQ_HANDLED; | 292 | return IRQ_HANDLED; |
269 | } | 293 | } |
270 | 294 | ||