diff options
author | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-10-07 19:11:38 -0400 |
---|---|---|
committer | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-10-19 02:56:22 -0400 |
commit | 4a78fd9a736db4c871bc8b583d66b61c38abd299 (patch) | |
tree | 2f8e5c53eb81091c321436b70280b9627e673815 /drivers/net/wimax | |
parent | 0c96859d7a5f0286ed70d3c4e140ac6816a350da (diff) |
wimax/i2400m: fix oops caused by race condition when exiting USB kthreads
Current i2400m USB code had to threads (one for processing RX, one for
TX). When calling i2400m_{tx,rx}_release(), it would crash if the
thread had exited already due to an error.
So changed the code to have the thread fill in/out
i2400mu->{tx,rx}_kthread under a spinlock; then the _release()
function will call kthread_stop() only if {rx,tx}_kthread is still
set.
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
Diffstat (limited to 'drivers/net/wimax')
-rw-r--r-- | drivers/net/wimax/i2400m/usb-rx.c | 35 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/usb-tx.c | 35 |
2 files changed, 59 insertions, 11 deletions
diff --git a/drivers/net/wimax/i2400m/usb-rx.c b/drivers/net/wimax/i2400m/usb-rx.c index e494e3774ec0..245587feb8c8 100644 --- a/drivers/net/wimax/i2400m/usb-rx.c +++ b/drivers/net/wimax/i2400m/usb-rx.c | |||
@@ -316,10 +316,15 @@ int i2400mu_rxd(void *_i2400mu) | |||
316 | size_t pending; | 316 | size_t pending; |
317 | int rx_size; | 317 | int rx_size; |
318 | struct sk_buff *rx_skb; | 318 | struct sk_buff *rx_skb; |
319 | unsigned long flags; | ||
319 | 320 | ||
320 | d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); | 321 | d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); |
322 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
323 | BUG_ON(i2400mu->rx_kthread != NULL); | ||
324 | i2400mu->rx_kthread = current; | ||
325 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
321 | while (1) { | 326 | while (1) { |
322 | d_printf(2, dev, "TX: waiting for messages\n"); | 327 | d_printf(2, dev, "RX: waiting for messages\n"); |
323 | pending = 0; | 328 | pending = 0; |
324 | wait_event_interruptible( | 329 | wait_event_interruptible( |
325 | i2400mu->rx_wq, | 330 | i2400mu->rx_wq, |
@@ -367,6 +372,9 @@ int i2400mu_rxd(void *_i2400mu) | |||
367 | } | 372 | } |
368 | result = 0; | 373 | result = 0; |
369 | out: | 374 | out: |
375 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
376 | i2400mu->rx_kthread = NULL; | ||
377 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
370 | d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result); | 378 | d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result); |
371 | return result; | 379 | return result; |
372 | 380 | ||
@@ -403,18 +411,33 @@ int i2400mu_rx_setup(struct i2400mu *i2400mu) | |||
403 | struct i2400m *i2400m = &i2400mu->i2400m; | 411 | struct i2400m *i2400m = &i2400mu->i2400m; |
404 | struct device *dev = &i2400mu->usb_iface->dev; | 412 | struct device *dev = &i2400mu->usb_iface->dev; |
405 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; | 413 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; |
414 | struct task_struct *kthread; | ||
406 | 415 | ||
407 | i2400mu->rx_kthread = kthread_run(i2400mu_rxd, i2400mu, "%s-rx", | 416 | kthread = kthread_run(i2400mu_rxd, i2400mu, "%s-rx", |
408 | wimax_dev->name); | 417 | wimax_dev->name); |
409 | if (IS_ERR(i2400mu->rx_kthread)) { | 418 | /* the kthread function sets i2400mu->rx_thread */ |
410 | result = PTR_ERR(i2400mu->rx_kthread); | 419 | if (IS_ERR(kthread)) { |
420 | result = PTR_ERR(kthread); | ||
411 | dev_err(dev, "RX: cannot start thread: %d\n", result); | 421 | dev_err(dev, "RX: cannot start thread: %d\n", result); |
412 | } | 422 | } |
413 | return result; | 423 | return result; |
414 | } | 424 | } |
415 | 425 | ||
426 | |||
416 | void i2400mu_rx_release(struct i2400mu *i2400mu) | 427 | void i2400mu_rx_release(struct i2400mu *i2400mu) |
417 | { | 428 | { |
418 | kthread_stop(i2400mu->rx_kthread); | 429 | unsigned long flags; |
430 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
431 | struct device *dev = i2400m_dev(i2400m); | ||
432 | struct task_struct *kthread; | ||
433 | |||
434 | spin_lock_irqsave(&i2400m->rx_lock, flags); | ||
435 | kthread = i2400mu->rx_kthread; | ||
436 | i2400mu->rx_kthread = NULL; | ||
437 | spin_unlock_irqrestore(&i2400m->rx_lock, flags); | ||
438 | if (kthread) | ||
439 | kthread_stop(kthread); | ||
440 | else | ||
441 | d_printf(1, dev, "RX: kthread had already exited\n"); | ||
419 | } | 442 | } |
420 | 443 | ||
diff --git a/drivers/net/wimax/i2400m/usb-tx.c b/drivers/net/wimax/i2400m/usb-tx.c index 90dfff1afb8a..a3c46e99c804 100644 --- a/drivers/net/wimax/i2400m/usb-tx.c +++ b/drivers/net/wimax/i2400m/usb-tx.c | |||
@@ -161,9 +161,15 @@ int i2400mu_txd(void *_i2400mu) | |||
161 | struct device *dev = &i2400mu->usb_iface->dev; | 161 | struct device *dev = &i2400mu->usb_iface->dev; |
162 | struct i2400m_msg_hdr *tx_msg; | 162 | struct i2400m_msg_hdr *tx_msg; |
163 | size_t tx_msg_size; | 163 | size_t tx_msg_size; |
164 | unsigned long flags; | ||
164 | 165 | ||
165 | d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); | 166 | d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu); |
166 | 167 | ||
168 | spin_lock_irqsave(&i2400m->tx_lock, flags); | ||
169 | BUG_ON(i2400mu->tx_kthread != NULL); | ||
170 | i2400mu->tx_kthread = current; | ||
171 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | ||
172 | |||
167 | while (1) { | 173 | while (1) { |
168 | d_printf(2, dev, "TX: waiting for messages\n"); | 174 | d_printf(2, dev, "TX: waiting for messages\n"); |
169 | tx_msg = NULL; | 175 | tx_msg = NULL; |
@@ -183,6 +189,11 @@ int i2400mu_txd(void *_i2400mu) | |||
183 | if (result < 0) | 189 | if (result < 0) |
184 | break; | 190 | break; |
185 | } | 191 | } |
192 | |||
193 | spin_lock_irqsave(&i2400m->tx_lock, flags); | ||
194 | i2400mu->tx_kthread = NULL; | ||
195 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | ||
196 | |||
186 | d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result); | 197 | d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result); |
187 | return result; | 198 | return result; |
188 | } | 199 | } |
@@ -213,11 +224,13 @@ int i2400mu_tx_setup(struct i2400mu *i2400mu) | |||
213 | struct i2400m *i2400m = &i2400mu->i2400m; | 224 | struct i2400m *i2400m = &i2400mu->i2400m; |
214 | struct device *dev = &i2400mu->usb_iface->dev; | 225 | struct device *dev = &i2400mu->usb_iface->dev; |
215 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; | 226 | struct wimax_dev *wimax_dev = &i2400m->wimax_dev; |
227 | struct task_struct *kthread; | ||
216 | 228 | ||
217 | i2400mu->tx_kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx", | 229 | kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx", |
218 | wimax_dev->name); | 230 | wimax_dev->name); |
219 | if (IS_ERR(i2400mu->tx_kthread)) { | 231 | /* the kthread function sets i2400mu->tx_thread */ |
220 | result = PTR_ERR(i2400mu->tx_kthread); | 232 | if (IS_ERR(kthread)) { |
233 | result = PTR_ERR(kthread); | ||
221 | dev_err(dev, "TX: cannot start thread: %d\n", result); | 234 | dev_err(dev, "TX: cannot start thread: %d\n", result); |
222 | } | 235 | } |
223 | return result; | 236 | return result; |
@@ -225,5 +238,17 @@ int i2400mu_tx_setup(struct i2400mu *i2400mu) | |||
225 | 238 | ||
226 | void i2400mu_tx_release(struct i2400mu *i2400mu) | 239 | void i2400mu_tx_release(struct i2400mu *i2400mu) |
227 | { | 240 | { |
228 | kthread_stop(i2400mu->tx_kthread); | 241 | unsigned long flags; |
242 | struct i2400m *i2400m = &i2400mu->i2400m; | ||
243 | struct device *dev = i2400m_dev(i2400m); | ||
244 | struct task_struct *kthread; | ||
245 | |||
246 | spin_lock_irqsave(&i2400m->tx_lock, flags); | ||
247 | kthread = i2400mu->tx_kthread; | ||
248 | i2400mu->tx_kthread = NULL; | ||
249 | spin_unlock_irqrestore(&i2400m->tx_lock, flags); | ||
250 | if (kthread) | ||
251 | kthread_stop(kthread); | ||
252 | else | ||
253 | d_printf(1, dev, "TX: kthread had already exited\n"); | ||
229 | } | 254 | } |