aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2009-10-07 19:11:38 -0400
committerInaky Perez-Gonzalez <inaky@linux.intel.com>2009-10-19 02:56:22 -0400
commit4a78fd9a736db4c871bc8b583d66b61c38abd299 (patch)
tree2f8e5c53eb81091c321436b70280b9627e673815 /drivers/net
parent0c96859d7a5f0286ed70d3c4e140ac6816a350da (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')
-rw-r--r--drivers/net/wimax/i2400m/usb-rx.c35
-rw-r--r--drivers/net/wimax/i2400m/usb-tx.c35
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;
369out: 374out:
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
416void i2400mu_rx_release(struct i2400mu *i2400mu) 427void 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
226void i2400mu_tx_release(struct i2400mu *i2400mu) 239void 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}