From 20953ad68ee522f6420b63c200ac9b23f96d937a Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Wed, 7 Jan 2009 00:23:55 +0000 Subject: 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] [] warn_slowpath+0x60/0x80 [89479.106104] [] ? native_sched_clock+0x20/0x70 [89479.106194] [] ? lock_release_holdtime+0x35/0x200 [89479.106218] [] ? __slab_alloc+0x550/0x560 [89479.106254] [] ? _spin_unlock+0x1d/0x20 [89479.106270] [] ? __slab_alloc+0x550/0x560 [89479.106302] [] ? delay_tsc+0x17/0x24 [89479.106319] [] ? __const_udelay+0x21/0x30 [89479.106376] [] ? hermes_bap_seek+0x112/0x1e0 [hermes] [89479.106396] [] ? trace_hardirqs_off+0xb/0x10 [89479.106418] [] ? __kmalloc_track_caller+0xb7/0x110 [89479.106448] [] ? dev_alloc_skb+0x1c/0x30 [89479.106465] [] ? dev_alloc_skb+0x1c/0x30 [89479.106482] [] __list_add+0x8f/0xa0 [89479.106551] [] orinoco_interrupt+0xcae/0x16c0 [orinoco] [89479.106574] [] ? tick_dev_program_event+0x33/0xb0 [89479.106594] [] ? native_sched_clock+0x20/0x70 [89479.106613] [] ? lock_release_holdtime+0x35/0x200 [89479.106662] [] ? trace_hardirqs_off+0xb/0x10 [89479.106892] [] ? usb_hcd_irq+0x97/0xa0 [usbcore] [89479.106926] [] handle_IRQ_event+0x29/0x60 [89479.106947] [] handle_level_irq+0x69/0xe0 [89479.106963] [] ? handle_level_irq+0x0/0xe0 [89479.106977] [] ? tcp_v4_rcv+0x633/0x6e0 [89479.107025] [] ? common_interrupt+0x28/0x30 [89479.107057] [] ? sk_run_filter+0x320/0x7a0 [89479.107078] [] ? list_del+0x21/0x90 [89479.107106] [] ? orinoco_rx_isr_tasklet+0x2ce/0x480 [orinoco] [89479.107131] [] ? __lock_acquire+0x160/0x1650 [89479.107151] [] ? native_sched_clock+0x20/0x70 [89479.107169] [] ? lock_release_holdtime+0x35/0x200 [89479.107200] [] ? irq_enter+0xa/0x60 [89479.107217] [] ? do_IRQ+0xd2/0x130 [89479.107518] [] ? restore_nocheck_notrace+0x0/0xe [89479.107542] [] ? __do_softirq+0x0/0x110 [89479.107561] [] ? trace_hardirqs_on_caller+0x74/0x140 [89479.107583] [] ? trace_hardirqs_on_thunk+0xc/0x10 [89479.107602] [] ? tasklet_action+0x27/0x90 [89479.107620] [] ? trace_hardirqs_on_caller+0x74/0x140 [89479.107638] [] ? tasklet_action+0x43/0x90 [89479.107655] [] ? __do_softirq+0x6f/0x110 [89479.107674] [] ? __do_softirq+0x0/0x110 [89479.107685] [] ? handle_level_irq+0x0/0xe0 [89479.107715] [] ? irq_exit+0x5d/0x80 [89479.107732] [] ? do_IRQ+0xd2/0x130 [89479.107747] [] ? sysenter_exit+0xf/0x16 [89479.107765] [] ? trace_hardirqs_on_caller+0xfd/0x140 [89479.107782] [] ? common_interrupt+0x28/0x30 [89479.107797] ---[ end trace a1fc0a52df4a729d ]--- Reported-by: Andrey Borzenkov Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco/orinoco.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/orinoco/orinoco.c') 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) struct orinoco_rx_data *rx_data, *temp; struct hermes_rx_descriptor *desc; struct sk_buff *skb; + unsigned long flags; + + /* orinoco_rx requires the driver lock, and we also need to + * protect priv->rx_list, so just hold the lock over the + * lot. + * + * If orinoco_lock fails, we've unplugged the card. In this + * case just abort. */ + if (orinoco_lock(priv, &flags) != 0) + return; /* extract desc and skb from queue */ 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) kfree(desc); } + + orinoco_unlock(priv, &flags); } /********************************************************************/ @@ -3645,12 +3657,22 @@ struct net_device void free_orinocodev(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_rx_data *rx_data, *temp; - /* No need to empty priv->rx_list: if the tasklet is scheduled - * when we call tasklet_kill it will run one final time, - * emptying the list */ + /* If the tasklet is scheduled when we call tasklet_kill it + * will run one final time. However the tasklet will only + * drain priv->rx_list if the hw is still available. */ tasklet_kill(&priv->rx_tasklet); + /* Explicitly drain priv->rx_list */ + list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) { + list_del(&rx_data->list); + + dev_kfree_skb(rx_data->skb); + kfree(rx_data->desc); + kfree(rx_data); + } + unregister_pm_notifier(&priv->pm_notifier); orinoco_uncache_fw(priv); -- cgit v1.2.3 From 7fe99c4e28ab54eada8aa456b417114e6ef21587 Mon Sep 17 00:00:00 2001 From: Andrey Borzenkov Date: Tue, 20 Jan 2009 20:26:46 +0300 Subject: orinoco: move kmalloc(..., GFP_KERNEL) outside spinlock in orinoco_ioctl_set_genie [ 56.923623] BUG: sleeping function called from invalid context at /home/bor/src/linux-git/mm/slub.c:1599 [ 56.923644] in_atomic(): 0, irqs_disabled(): 1, pid: 3031, name: wpa_supplicant [ 56.923656] 2 locks held by wpa_supplicant/3031: [ 56.923662] #0: (rtnl_mutex){--..}, at: [] rtnl_lock+0xf/0x20 [ 56.923703] #1: (&priv->lock){++..}, at: [] orinoco_ioctl_set_genie+0x52/0x130 [orinoco] [ 56.923782] irq event stamp: 910 [ 56.923788] hardirqs last enabled at (909): [] __kmalloc+0x7b/0x140 [ 56.923820] hardirqs last disabled at (910): [] _spin_lock_irqsave+0x19/0x80 [ 56.923847] softirqs last enabled at (880): [] __do_softirq+0xc4/0x110 [ 56.923865] softirqs last disabled at (871): [] do_softirq+0x8e/0xe0 [ 56.923895] Pid: 3031, comm: wpa_supplicant Not tainted 2.6.29-rc2-1avb #1 [ 56.923905] Call Trace: [ 56.923919] [] ? do_softirq+0x8e/0xe0 [ 56.923941] [] __might_sleep+0xd2/0x100 [ 56.923952] [] __kmalloc+0xd7/0x140 [ 56.923963] [] ? _spin_lock_irqsave+0x6a/0x80 [ 56.923981] [] ? orinoco_ioctl_set_genie+0x79/0x130 [orinoco] [ 56.923999] [] ? orinoco_ioctl_set_genie+0x52/0x130 [orinoco] [ 56.924017] [] orinoco_ioctl_set_genie+0x79/0x130 [orinoco] [ 56.924036] [] ? copy_from_user+0x35/0x130 [ 56.924061] [] ioctl_standard_call+0x196/0x380 [ 56.924085] [] ? __dev_get_by_name+0x85/0xb0 [ 56.924096] [] wext_handle_ioctl+0x14f/0x230 [ 56.924113] [] ? orinoco_ioctl_set_genie+0x0/0x130 [orinoco] [ 56.924132] [] dev_ioctl+0x495/0x570 [ 56.924155] [] ? sys_sendto+0xa5/0xd0 [ 56.924171] [] ? mark_held_locks+0x48/0x90 [ 56.924183] [] ? sock_ioctl+0x0/0x280 [ 56.924193] [] sock_ioctl+0xfd/0x280 [ 56.924203] [] ? sock_ioctl+0x0/0x280 [ 56.924235] [] vfs_ioctl+0x20/0x80 [ 56.924246] [] do_vfs_ioctl+0x72/0x570 [ 56.924257] [] ? sys_send+0x32/0x40 [ 56.924268] [] ? sys_socketcall+0x1d0/0x2a0 [ 56.924280] [] ? sysenter_exit+0xf/0x16 [ 56.924292] [] sys_ioctl+0x39/0x70 [ 56.924302] [] sysenter_do_call+0x12/0x31 Signed-off-by: Andrey Borzenkov Cc: stable@kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco/orinoco.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless/orinoco/orinoco.c') diff --git a/drivers/net/wireless/orinoco/orinoco.c b/drivers/net/wireless/orinoco/orinoco.c index c3bb85e0251e..39eab39b065f 100644 --- a/drivers/net/wireless/orinoco/orinoco.c +++ b/drivers/net/wireless/orinoco/orinoco.c @@ -5068,33 +5068,30 @@ static int orinoco_ioctl_set_genie(struct net_device *dev, struct orinoco_private *priv = netdev_priv(dev); u8 *buf; unsigned long flags; - int err = 0; /* cut off at IEEE80211_MAX_DATA_LEN */ if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) || (wrqu->data.length && (extra == NULL))) return -EINVAL; - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - if (wrqu->data.length) { buf = kmalloc(wrqu->data.length, GFP_KERNEL); - if (buf == NULL) { - err = -ENOMEM; - goto out; - } + if (buf == NULL) + return -ENOMEM; memcpy(buf, extra, wrqu->data.length); - kfree(priv->wpa_ie); - priv->wpa_ie = buf; - priv->wpa_ie_len = wrqu->data.length; - } else { - kfree(priv->wpa_ie); - priv->wpa_ie = NULL; - priv->wpa_ie_len = 0; + } else + buf = NULL; + + if (orinoco_lock(priv, &flags) != 0) { + kfree(buf); + return -EBUSY; } + kfree(priv->wpa_ie); + priv->wpa_ie = buf; + priv->wpa_ie_len = wrqu->data.length; + if (priv->wpa_ie) { /* Looks like wl_lkm wants to check the auth alg, and * somehow pass it to the firmware. @@ -5103,9 +5100,8 @@ static int orinoco_ioctl_set_genie(struct net_device *dev, */ } -out: orinoco_unlock(priv, &flags); - return err; + return 0; } static int orinoco_ioctl_get_genie(struct net_device *dev, -- cgit v1.2.3 From 11eaea416716deebcb18383b201ba8033cbf33dc Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Sun, 18 Jan 2009 23:20:58 -0500 Subject: orinoco: use KERN_DEBUG for link status messages KERN_INFO is too "loud" for messages that are generated by the ordinary events, such as accociation. Use of KERN_DEBUG is consistent with mac80211. Suggested by Michael Gilbert Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco/orinoco.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/orinoco/orinoco.c') diff --git a/drivers/net/wireless/orinoco/orinoco.c b/drivers/net/wireless/orinoco/orinoco.c index 39eab39b065f..45a04faa7818 100644 --- a/drivers/net/wireless/orinoco/orinoco.c +++ b/drivers/net/wireless/orinoco/orinoco.c @@ -1673,7 +1673,7 @@ static void print_linkstatus(struct net_device *dev, u16 status) s = "UNKNOWN"; } - printk(KERN_INFO "%s: New link status: %s (%04x)\n", + printk(KERN_DEBUG "%s: New link status: %s (%04x)\n", dev->name, s, status); } -- cgit v1.2.3 From 5c138dcee7d4a9e68cce546a45968bbf5dbfce80 Mon Sep 17 00:00:00 2001 From: Andrey Borzenkov Date: Sun, 15 Feb 2009 12:51:18 +0300 Subject: orinoco: do not resgister NULL pm_notifier function With DEBUG_NOTIFIERS it results in [11330.890966] WARNING: at /home/bor/src/linux-git/kernel/notifier.c:88 notifier_call_chain+0x91/0xa0() [11330.890977] Hardware name: PORTEGE 4000 [11330.890983] Invalid notifier called! ... Without DEBUG_NOTIFIERS it most likely crashes on NULL pointer. Signed-off-by: Andrey Borzenkov Acked-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco/orinoco.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/orinoco/orinoco.c') diff --git a/drivers/net/wireless/orinoco/orinoco.c b/drivers/net/wireless/orinoco/orinoco.c index 45a04faa7818..067d1a9c728b 100644 --- a/drivers/net/wireless/orinoco/orinoco.c +++ b/drivers/net/wireless/orinoco/orinoco.c @@ -3157,8 +3157,20 @@ static int orinoco_pm_notifier(struct notifier_block *notifier, return NOTIFY_DONE; } + +static void orinoco_register_pm_notifier(struct orinoco_private *priv) +{ + priv->pm_notifier.notifier_call = orinoco_pm_notifier; + register_pm_notifier(&priv->pm_notifier); +} + +static void orinoco_unregister_pm_notifier(struct orinoco_private *priv) +{ + unregister_pm_notifier(&priv->pm_notifier); +} #else /* !PM_SLEEP || HERMES_CACHE_FW_ON_INIT */ -#define orinoco_pm_notifier NULL +#define orinoco_register_pm_notifier(priv) do { } while(0) +#define orinoco_unregister_pm_notifier(priv) do { } while(0) #endif /********************************************************************/ @@ -3648,8 +3660,7 @@ struct net_device priv->cached_fw = NULL; /* Register PM notifiers */ - priv->pm_notifier.notifier_call = orinoco_pm_notifier; - register_pm_notifier(&priv->pm_notifier); + orinoco_register_pm_notifier(priv); return dev; } @@ -3673,7 +3684,7 @@ void free_orinocodev(struct net_device *dev) kfree(rx_data); } - unregister_pm_notifier(&priv->pm_notifier); + orinoco_unregister_pm_notifier(priv); orinoco_uncache_fw(priv); priv->wpa_ie_len = 0; -- cgit v1.2.3