diff options
author | David Kilroy <kilroyd@googlemail.com> | 2009-01-06 19:23:55 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-01-12 14:24:51 -0500 |
commit | 20953ad68ee522f6420b63c200ac9b23f96d937a (patch) | |
tree | 114d9622390fddf76239be5cf16094e8b87d5f99 | |
parent | 176ddc7dcfe3fd93778f52abf9a947d92932f19e (diff) |
orinoco: take the driver lock in the rx tasklet
Fix the warning reproduced below.
We add to rx_list in interrupt context and remove elements in tasklet
context. While removing elements we need to prevent the interrupt
modifying the list.
Note that "orinoco: Process bulk of receive interrupt in a tasklet" did not
preserve locking semantics on what is now orinoco_rx.
This patch reinstates the locking semantics and ensures it covers
rx_list as well. This leads to additional cleanup required in
free_orinocodev.
[89479.105038] WARNING: at lib/list_debug.c:30 __list_add+0x8f/0xa0()
[89479.105058] list_add corruption. prev->next should be next (dddb3568), but was cbc28978. (prev=dddb3568).
[89479.106002] Pid: 15746, comm: X Not tainted 2.6.28-1avb #26
[89479.106020] Call Trace:
[89479.106062] [<c011d3b0>] warn_slowpath+0x60/0x80
[89479.106104] [<c01073d0>] ? native_sched_clock+0x20/0x70
[89479.106194] [<c013d825>] ? lock_release_holdtime+0x35/0x200
[89479.106218] [<c018d9f0>] ? __slab_alloc+0x550/0x560
[89479.106254] [<c02f9c9d>] ? _spin_unlock+0x1d/0x20
[89479.106270] [<c018d9f0>] ? __slab_alloc+0x550/0x560
[89479.106302] [<c01ff2a7>] ? delay_tsc+0x17/0x24
[89479.106319] [<c01ff221>] ? __const_udelay+0x21/0x30
[89479.106376] [<dfa8b1e2>] ? hermes_bap_seek+0x112/0x1e0 [hermes]
[89479.106396] [<c013d7eb>] ? trace_hardirqs_off+0xb/0x10
[89479.106418] [<c018e307>] ? __kmalloc_track_caller+0xb7/0x110
[89479.106448] [<c028eefc>] ? dev_alloc_skb+0x1c/0x30
[89479.106465] [<c028eefc>] ? dev_alloc_skb+0x1c/0x30
[89479.106482] [<c020e13f>] __list_add+0x8f/0xa0
[89479.106551] [<dfd0fcae>] orinoco_interrupt+0xcae/0x16c0 [orinoco]
[89479.106574] [<c013b0e3>] ? tick_dev_program_event+0x33/0xb0
[89479.106594] [<c01073d0>] ? native_sched_clock+0x20/0x70
[89479.106613] [<c013d825>] ? lock_release_holdtime+0x35/0x200
[89479.106662] [<c013d7eb>] ? trace_hardirqs_off+0xb/0x10
[89479.106892] [<dfe7faa7>] ? usb_hcd_irq+0x97/0xa0 [usbcore]
[89479.106926] [<c015ba79>] handle_IRQ_event+0x29/0x60
[89479.106947] [<c015cf89>] handle_level_irq+0x69/0xe0
[89479.106963] [<c015cf20>] ? handle_level_irq+0x0/0xe0
[89479.106977] <IRQ> [<c02ca933>] ? tcp_v4_rcv+0x633/0x6e0
[89479.107025] [<c0103f0c>] ? common_interrupt+0x28/0x30
[89479.107057] [<c02a0000>] ? sk_run_filter+0x320/0x7a0
[89479.107078] [<c020e041>] ? list_del+0x21/0x90
[89479.107106] [<dfd0d24e>] ? orinoco_rx_isr_tasklet+0x2ce/0x480 [orinoco]
[89479.107131] [<c01402e0>] ? __lock_acquire+0x160/0x1650
[89479.107151] [<c01073d0>] ? native_sched_clock+0x20/0x70
[89479.107169] [<c013d825>] ? lock_release_holdtime+0x35/0x200
[89479.107200] [<c012249a>] ? irq_enter+0xa/0x60
[89479.107217] [<c0104e52>] ? do_IRQ+0xd2/0x130
[89479.107518] [<c010342c>] ? restore_nocheck_notrace+0x0/0xe
[89479.107542] [<c0122830>] ? __do_softirq+0x0/0x110
[89479.107561] [<c013f7b4>] ? trace_hardirqs_on_caller+0x74/0x140
[89479.107583] [<c01ff678>] ? trace_hardirqs_on_thunk+0xc/0x10
[89479.107602] [<c0122087>] ? tasklet_action+0x27/0x90
[89479.107620] [<c013f7b4>] ? trace_hardirqs_on_caller+0x74/0x140
[89479.107638] [<c01220a3>] ? tasklet_action+0x43/0x90
[89479.107655] [<c012289f>] ? __do_softirq+0x6f/0x110
[89479.107674] [<c0122830>] ? __do_softirq+0x0/0x110
[89479.107685] <IRQ> [<c015cf20>] ? handle_level_irq+0x0/0xe0
[89479.107715] [<c012246d>] ? irq_exit+0x5d/0x80
[89479.107732] [<c0104e52>] ? do_IRQ+0xd2/0x130
[89479.107747] [<c0103337>] ? sysenter_exit+0xf/0x16
[89479.107765] [<c013f83d>] ? trace_hardirqs_on_caller+0xfd/0x140
[89479.107782] [<c0103f0c>] ? common_interrupt+0x28/0x30
[89479.107797] ---[ end trace a1fc0a52df4a729d ]---
Reported-by: Andrey Borzenkov <arvidjaar@mail.ru>
Signed-off-by: David Kilroy <kilroyd@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/orinoco/orinoco.c | 28 |
1 files changed, 25 insertions, 3 deletions
diff --git a/drivers/net/wireless/orinoco/orinoco.c b/drivers/net/wireless/orinoco/orinoco.c index bc84e2792f8a..c3bb85e0251e 100644 --- a/drivers/net/wireless/orinoco/orinoco.c +++ b/drivers/net/wireless/orinoco/orinoco.c | |||
@@ -1610,6 +1610,16 @@ static void orinoco_rx_isr_tasklet(unsigned long data) | |||
1610 | struct orinoco_rx_data *rx_data, *temp; | 1610 | struct orinoco_rx_data *rx_data, *temp; |
1611 | struct hermes_rx_descriptor *desc; | 1611 | struct hermes_rx_descriptor *desc; |
1612 | struct sk_buff *skb; | 1612 | struct sk_buff *skb; |
1613 | unsigned long flags; | ||
1614 | |||
1615 | /* orinoco_rx requires the driver lock, and we also need to | ||
1616 | * protect priv->rx_list, so just hold the lock over the | ||
1617 | * lot. | ||
1618 | * | ||
1619 | * If orinoco_lock fails, we've unplugged the card. In this | ||
1620 | * case just abort. */ | ||
1621 | if (orinoco_lock(priv, &flags) != 0) | ||
1622 | return; | ||
1613 | 1623 | ||
1614 | /* extract desc and skb from queue */ | 1624 | /* extract desc and skb from queue */ |
1615 | list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) { | 1625 | list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) { |
@@ -1622,6 +1632,8 @@ static void orinoco_rx_isr_tasklet(unsigned long data) | |||
1622 | 1632 | ||
1623 | kfree(desc); | 1633 | kfree(desc); |
1624 | } | 1634 | } |
1635 | |||
1636 | orinoco_unlock(priv, &flags); | ||
1625 | } | 1637 | } |
1626 | 1638 | ||
1627 | /********************************************************************/ | 1639 | /********************************************************************/ |
@@ -3645,12 +3657,22 @@ struct net_device | |||
3645 | void free_orinocodev(struct net_device *dev) | 3657 | void free_orinocodev(struct net_device *dev) |
3646 | { | 3658 | { |
3647 | struct orinoco_private *priv = netdev_priv(dev); | 3659 | struct orinoco_private *priv = netdev_priv(dev); |
3660 | struct orinoco_rx_data *rx_data, *temp; | ||
3648 | 3661 | ||
3649 | /* No need to empty priv->rx_list: if the tasklet is scheduled | 3662 | /* If the tasklet is scheduled when we call tasklet_kill it |
3650 | * when we call tasklet_kill it will run one final time, | 3663 | * will run one final time. However the tasklet will only |
3651 | * emptying the list */ | 3664 | * drain priv->rx_list if the hw is still available. */ |
3652 | tasklet_kill(&priv->rx_tasklet); | 3665 | tasklet_kill(&priv->rx_tasklet); |
3653 | 3666 | ||
3667 | /* Explicitly drain priv->rx_list */ | ||
3668 | list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) { | ||
3669 | list_del(&rx_data->list); | ||
3670 | |||
3671 | dev_kfree_skb(rx_data->skb); | ||
3672 | kfree(rx_data->desc); | ||
3673 | kfree(rx_data); | ||
3674 | } | ||
3675 | |||
3654 | unregister_pm_notifier(&priv->pm_notifier); | 3676 | unregister_pm_notifier(&priv->pm_notifier); |
3655 | orinoco_uncache_fw(priv); | 3677 | orinoco_uncache_fw(priv); |
3656 | 3678 | ||