Commit 01c785dcb4e9fd6c4c370fd9915fc10585ed64bd

Authored by Alan Cox
Committed by Wim Van Sebroeck
1 parent b47711bfbc

[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>

Showing 1 changed file with 27 additions and 3 deletions Side-by-side Diff

drivers/watchdog/wdt.c
... ... @@ -70,6 +70,8 @@
70 70 static int io=0x240;
71 71 static int irq=11;
72 72  
  73 +static DEFINE_SPINLOCK(wdt_lock);
  74 +
73 75 module_param(io, int, 0);
74 76 MODULE_PARM_DESC(io, "WDT io port (default=0x240)");
75 77 module_param(irq, int, 0);
... ... @@ -109,6 +111,8 @@
109 111  
110 112 static int wdt_start(void)
111 113 {
  114 + unsigned long flags;
  115 + spin_lock_irqsave(&wdt_lock, flags);
112 116 inb_p(WDT_DC); /* Disable watchdog */
113 117 wdt_ctr_mode(0,3); /* Program CTR0 for Mode 3: Square Wave Generator */
114 118 wdt_ctr_mode(1,2); /* Program CTR1 for Mode 2: Rate Generator */
... ... @@ -117,6 +121,7 @@
117 121 wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */
118 122 wdt_ctr_load(2,65535); /* Length of reset pulse */
119 123 outb_p(0, WDT_DC); /* Enable watchdog */
  124 + spin_unlock_irqrestore(&wdt_lock, flags);
120 125 return 0;
121 126 }
122 127  
123 128  
... ... @@ -128,9 +133,12 @@
128 133  
129 134 static int wdt_stop (void)
130 135 {
  136 + unsigned long flags;
  137 + spin_lock_irqsave(&wdt_lock, flags);
131 138 /* Turn the card off */
132 139 inb_p(WDT_DC); /* Disable watchdog */
133 140 wdt_ctr_load(2,0); /* 0 length reset pulses now */
  141 + spin_unlock_irqrestore(&wdt_lock, flags);
134 142 return 0;
135 143 }
136 144  
137 145  
... ... @@ -143,11 +151,14 @@
143 151  
144 152 static int wdt_ping(void)
145 153 {
  154 + unsigned long flags;
  155 + spin_lock_irqsave(&wdt_lock, flags);
146 156 /* Write a watchdog value */
147 157 inb_p(WDT_DC); /* Disable watchdog */
148 158 wdt_ctr_mode(1,2); /* Re-Program CTR1 for Mode 2: Rate Generator */
149 159 wdt_ctr_load(1,wd_heartbeat); /* Heartbeat */
150 160 outb_p(0, WDT_DC); /* Enable watchdog */
  161 + spin_unlock_irqrestore(&wdt_lock, flags);
151 162 return 0;
152 163 }
153 164  
154 165  
... ... @@ -182,8 +193,13 @@
182 193  
183 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;
186 198  
  199 + spin_lock_irqsave(&wdt_lock, flags);
  200 + new_status = inb_p(WDT_SR);
  201 + spin_unlock_irqrestore(&wdt_lock, flags);
  202 +
187 203 *status=0;
188 204 if (new_status & WDC_SR_ISOI0)
189 205 *status |= WDIOF_EXTERN1;
190 206  
... ... @@ -214,8 +230,12 @@
214 230  
215 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 239 *temperature = (c * 11 / 15) + 7;
220 240 return 0;
221 241 }
222 242  
... ... @@ -237,8 +257,11 @@
237 257 * Read the status register see what is up and
238 258 * then printk it.
239 259 */
240   - unsigned char status=inb_p(WDT_SR);
  260 + unsigned char status;
241 261  
  262 + spin_lock(&wdt_lock);
  263 + status = inb_p(WDT_SR);
  264 +
242 265 printk(KERN_CRIT "WDT status %d\n", status);
243 266  
244 267 #ifdef CONFIG_WDT_501
... ... @@ -265,6 +288,7 @@
265 288 printk(KERN_CRIT "Reset in 5ms.\n");
266 289 #endif
267 290 }
  291 + spin_unlock(&wdt_lock);
268 292 return IRQ_HANDLED;
269 293 }
270 294