aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2009-07-13 00:09:20 -0400
committerDavid S. Miller <davem@davemloft.net>2009-07-13 00:09:20 -0400
commitadeab1afb7de89555c69aab5ca21300c14af6369 (patch)
tree9e300563895b33a75876be58f167006fa8cbde3d /drivers/net/wireless
parent635ecaa70e862f85f652581305fe0074810893be (diff)
NET: Fix locking issues in PPP, 6pack, mkiss and strip line disciplines.
Guido Trentalancia reports: I am trying to use the kiss driver in the Linux kernel that is being shipped with Fedora 10 but unfortunately I get the following oops: mkiss: AX.25 Multikiss, Hans Albas PE1AYX mkiss: ax0: crc mode is auto. ADDRCONF(NETDEV_CHANGE): ax0: link becomes ready ------------[ cut here ]------------ WARNING: at kernel/softirq.c:77 __local_bh_disable+0x2f/0x83() (Not tainted) [...] unloaded: microcode] Pid: 0, comm: swapper Not tainted 2.6.27.25-170.2.72.fc10.i686 #1 [<c042ddfb>] warn_on_slowpath+0x65/0x8b [<c06ab62b>] ? _spin_unlock_irqrestore+0x22/0x38 [<c04228b4>] ? __enqueue_entity+0xe3/0xeb [<c042431e>] ? enqueue_entity+0x203/0x20b [<c0424361>] ? enqueue_task_fair+0x3b/0x3f [<c041f88c>] ? resched_task+0x3a/0x6e [<c06ab62b>] ? _spin_unlock_irqrestore+0x22/0x38 [<c06ab4e2>] ? _spin_lock_bh+0xb/0x16 [<c043255b>] __local_bh_disable+0x2f/0x83 [<c04325ba>] local_bh_disable+0xb/0xd [<c06ab4e2>] _spin_lock_bh+0xb/0x16 [<f8b6f600>] mkiss_receive_buf+0x2fb/0x3a6 [mkiss] [<c0572a30>] flush_to_ldisc+0xf7/0x198 [<c0572b12>] tty_flip_buffer_push+0x41/0x51 [<f89477f2>] ftdi_process_read+0x375/0x4ad [ftdi_sio] [<f8947a5a>] ftdi_read_bulk_callback+0x130/0x138 [ftdi_sio] [<c05d4bec>] usb_hcd_giveback_urb+0x63/0x93 [<c05ea290>] uhci_giveback_urb+0xe5/0x15f [<c05eaabf>] uhci_scan_schedule+0x52e/0x767 [<c05f6288>] ? psmouse_handle_byte+0xc/0xe5 [<c054df78>] ? acpi_ev_gpe_detect+0xd6/0xe1 [<c05ec5b0>] uhci_irq+0x110/0x125 [<c05d4834>] usb_hcd_irq+0x40/0xa3 [<c0465313>] handle_IRQ_event+0x2f/0x64 [<c046642b>] handle_level_irq+0x74/0xbe [<c04663b7>] ? handle_level_irq+0x0/0xbe [<c0406e6e>] do_IRQ+0xc7/0xfe [<c0405668>] common_interrupt+0x28/0x30 [<c056821a>] ? acpi_idle_enter_simple+0x162/0x19d [<c0617f52>] cpuidle_idle_call+0x60/0x92 [<c0403c61>] cpu_idle+0x101/0x134 [<c069b1ba>] rest_init+0x4e/0x50 ======================= ---[ end trace b7cc8076093467ad ]--- ------------[ cut here ]------------ WARNING: at kernel/softirq.c:136 _local_bh_enable_ip+0x3d/0xc4() [...] Pid: 0, comm: swapper Tainted: G W 2.6.27.25-170.2.72.fc10.i686 [<c042ddfb>] warn_on_slowpath+0x65/0x8b [<c06ab62b>] ? _spin_unlock_irqrestore+0x22/0x38 [<c04228b4>] ? __enqueue_entity+0xe3/0xeb [<c042431e>] ? enqueue_entity+0x203/0x20b [<c0424361>] ? enqueue_task_fair+0x3b/0x3f [<c041f88c>] ? resched_task+0x3a/0x6e [<c06ab62b>] ? _spin_unlock_irqrestore+0x22/0x38 [<c06ab4e2>] ? _spin_lock_bh+0xb/0x16 [<f8b6f642>] ? mkiss_receive_buf+0x33d/0x3a6 [mkiss] [<c04325f9>] _local_bh_enable_ip+0x3d/0xc4 [<c0432688>] local_bh_enable_ip+0x8/0xa [<c06ab54d>] _spin_unlock_bh+0x11/0x13 [<f8b6f642>] mkiss_receive_buf+0x33d/0x3a6 [mkiss] [<c0572a30>] flush_to_ldisc+0xf7/0x198 [<c0572b12>] tty_flip_buffer_push+0x41/0x51 [<f89477f2>] ftdi_process_read+0x375/0x4ad [ftdi_sio] [<f8947a5a>] ftdi_read_bulk_callback+0x130/0x138 [ftdi_sio] [<c05d4bec>] usb_hcd_giveback_urb+0x63/0x93 [<c05ea290>] uhci_giveback_urb+0xe5/0x15f [<c05eaabf>] uhci_scan_schedule+0x52e/0x767 [<c05f6288>] ? psmouse_handle_byte+0xc/0xe5 [<c054df78>] ? acpi_ev_gpe_detect+0xd6/0xe1 [<c05ec5b0>] uhci_irq+0x110/0x125 [<c05d4834>] usb_hcd_irq+0x40/0xa3 [<c0465313>] handle_IRQ_event+0x2f/0x64 [<c046642b>] handle_level_irq+0x74/0xbe [<c04663b7>] ? handle_level_irq+0x0/0xbe [<c0406e6e>] do_IRQ+0xc7/0xfe [<c0405668>] common_interrupt+0x28/0x30 [<c056821a>] ? acpi_idle_enter_simple+0x162/0x19d [<c0617f52>] cpuidle_idle_call+0x60/0x92 [<c0403c61>] cpu_idle+0x101/0x134 [<c069b1ba>] rest_init+0x4e/0x50 ======================= ---[ end trace b7cc8076093467ad ]--- mkiss: ax0: Trying crc-smack mkiss: ax0: Trying crc-flexnet The issue was, that the locking code in mkiss was assuming it was only ever being called in process or bh context. Fixed by converting the involved locking code to use irq-safe locks. Review of other networking line disciplines shows that 6pack, both sync and async PPP and STRIP have similar issues. The ppp_async one is the most interesting one as it sorts out half of the issue as far back as 2004 in commit http://git.kernel.org/?p=linux/kernel/git/tglx/history.git;a=commitdiff;h=2996d8deaeddd01820691a872550dc0cfba0c37d Signed-off-by: Ralf Baechle <ralf@linux-mips.org> Reported-by: Guido Trentalancia <guido@trentalancia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/strip.c39
1 files changed, 24 insertions, 15 deletions
diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c
index 38366a56b71..3d39f6587eb 100644
--- a/drivers/net/wireless/strip.c
+++ b/drivers/net/wireless/strip.c
@@ -856,6 +856,7 @@ static int strip_change_mtu(struct net_device *dev, int new_mtu)
856 unsigned char *orbuff = strip_info->rx_buff; 856 unsigned char *orbuff = strip_info->rx_buff;
857 unsigned char *osbuff = strip_info->sx_buff; 857 unsigned char *osbuff = strip_info->sx_buff;
858 unsigned char *otbuff = strip_info->tx_buff; 858 unsigned char *otbuff = strip_info->tx_buff;
859 unsigned long flags;
859 860
860 if (new_mtu > MAX_SEND_MTU) { 861 if (new_mtu > MAX_SEND_MTU) {
861 printk(KERN_ERR 862 printk(KERN_ERR
@@ -864,11 +865,11 @@ static int strip_change_mtu(struct net_device *dev, int new_mtu)
864 return -EINVAL; 865 return -EINVAL;
865 } 866 }
866 867
867 spin_lock_bh(&strip_lock); 868 spin_lock_irqsave(&strip_lock, flags);
868 if (!allocate_buffers(strip_info, new_mtu)) { 869 if (!allocate_buffers(strip_info, new_mtu)) {
869 printk(KERN_ERR "%s: unable to grow strip buffers, MTU change cancelled.\n", 870 printk(KERN_ERR "%s: unable to grow strip buffers, MTU change cancelled.\n",
870 strip_info->dev->name); 871 strip_info->dev->name);
871 spin_unlock_bh(&strip_lock); 872 spin_unlock_irqrestore(&strip_lock, flags);
872 return -ENOMEM; 873 return -ENOMEM;
873 } 874 }
874 875
@@ -892,7 +893,7 @@ static int strip_change_mtu(struct net_device *dev, int new_mtu)
892 } 893 }
893 } 894 }
894 strip_info->tx_head = strip_info->tx_buff; 895 strip_info->tx_head = strip_info->tx_buff;
895 spin_unlock_bh(&strip_lock); 896 spin_unlock_irqrestore(&strip_lock, flags);
896 897
897 printk(KERN_NOTICE "%s: strip MTU changed fom %d to %d.\n", 898 printk(KERN_NOTICE "%s: strip MTU changed fom %d to %d.\n",
898 strip_info->dev->name, old_mtu, strip_info->mtu); 899 strip_info->dev->name, old_mtu, strip_info->mtu);
@@ -983,10 +984,13 @@ static void strip_seq_neighbours(struct seq_file *seq,
983 const MetricomNodeTable * table, 984 const MetricomNodeTable * table,
984 const char *title) 985 const char *title)
985{ 986{
986 /* We wrap this in a do/while loop, so if the table changes */ 987 unsigned long flags;
987 /* while we're reading it, we just go around and try again. */
988 struct timeval t; 988 struct timeval t;
989 989
990 /*
991 * We wrap this in a do/while loop, so if the table changes
992 * while we're reading it, we just go around and try again.
993 */
990 do { 994 do {
991 int i; 995 int i;
992 t = table->timestamp; 996 t = table->timestamp;
@@ -995,9 +999,9 @@ static void strip_seq_neighbours(struct seq_file *seq,
995 for (i = 0; i < table->num_nodes; i++) { 999 for (i = 0; i < table->num_nodes; i++) {
996 MetricomNode node; 1000 MetricomNode node;
997 1001
998 spin_lock_bh(&strip_lock); 1002 spin_lock_irqsave(&strip_lock, flags);
999 node = table->node[i]; 1003 node = table->node[i];
1000 spin_unlock_bh(&strip_lock); 1004 spin_unlock_irqrestore(&strip_lock, flags);
1001 seq_printf(seq, " %s\n", node.c); 1005 seq_printf(seq, " %s\n", node.c);
1002 } 1006 }
1003 } while (table->timestamp.tv_sec != t.tv_sec 1007 } while (table->timestamp.tv_sec != t.tv_sec
@@ -1536,6 +1540,7 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb)
1536static int strip_xmit(struct sk_buff *skb, struct net_device *dev) 1540static int strip_xmit(struct sk_buff *skb, struct net_device *dev)
1537{ 1541{
1538 struct strip *strip_info = netdev_priv(dev); 1542 struct strip *strip_info = netdev_priv(dev);
1543 unsigned long flags;
1539 1544
1540 if (!netif_running(dev)) { 1545 if (!netif_running(dev)) {
1541 printk(KERN_ERR "%s: xmit call when iface is down\n", 1546 printk(KERN_ERR "%s: xmit call when iface is down\n",
@@ -1574,11 +1579,11 @@ static int strip_xmit(struct sk_buff *skb, struct net_device *dev)
1574 strip_info->dev->name, sx_pps_count / 8); 1579 strip_info->dev->name, sx_pps_count / 8);
1575 } 1580 }
1576 1581
1577 spin_lock_bh(&strip_lock); 1582 spin_lock_irqsave(&strip_lock, flags);
1578 1583
1579 strip_send(strip_info, skb); 1584 strip_send(strip_info, skb);
1580 1585
1581 spin_unlock_bh(&strip_lock); 1586 spin_unlock_irqrestore(&strip_lock, flags);
1582 1587
1583 if (skb) 1588 if (skb)
1584 dev_kfree_skb(skb); 1589 dev_kfree_skb(skb);
@@ -2263,12 +2268,13 @@ static void strip_receive_buf(struct tty_struct *tty, const unsigned char *cp,
2263{ 2268{
2264 struct strip *strip_info = tty->disc_data; 2269 struct strip *strip_info = tty->disc_data;
2265 const unsigned char *end = cp + count; 2270 const unsigned char *end = cp + count;
2271 unsigned long flags;
2266 2272
2267 if (!strip_info || strip_info->magic != STRIP_MAGIC 2273 if (!strip_info || strip_info->magic != STRIP_MAGIC
2268 || !netif_running(strip_info->dev)) 2274 || !netif_running(strip_info->dev))
2269 return; 2275 return;
2270 2276
2271 spin_lock_bh(&strip_lock); 2277 spin_lock_irqsave(&strip_lock, flags);
2272#if 0 2278#if 0
2273 { 2279 {
2274 struct timeval tv; 2280 struct timeval tv;
@@ -2335,7 +2341,7 @@ static void strip_receive_buf(struct tty_struct *tty, const unsigned char *cp,
2335 } 2341 }
2336 cp++; 2342 cp++;
2337 } 2343 }
2338 spin_unlock_bh(&strip_lock); 2344 spin_unlock_irqrestore(&strip_lock, flags);
2339} 2345}
2340 2346
2341 2347
@@ -2523,9 +2529,11 @@ static void strip_dev_setup(struct net_device *dev)
2523 2529
2524static void strip_free(struct strip *strip_info) 2530static void strip_free(struct strip *strip_info)
2525{ 2531{
2526 spin_lock_bh(&strip_lock); 2532 unsigned long flags;
2533
2534 spin_lock_irqsave(&strip_lock, flags);
2527 list_del_rcu(&strip_info->list); 2535 list_del_rcu(&strip_info->list);
2528 spin_unlock_bh(&strip_lock); 2536 spin_unlock_irqrestore(&strip_lock, flags);
2529 2537
2530 strip_info->magic = 0; 2538 strip_info->magic = 0;
2531 2539
@@ -2539,6 +2547,7 @@ static void strip_free(struct strip *strip_info)
2539static struct strip *strip_alloc(void) 2547static struct strip *strip_alloc(void)
2540{ 2548{
2541 struct list_head *n; 2549 struct list_head *n;
2550 unsigned long flags;
2542 struct net_device *dev; 2551 struct net_device *dev;
2543 struct strip *strip_info; 2552 struct strip *strip_info;
2544 2553
@@ -2562,7 +2571,7 @@ static struct strip *strip_alloc(void)
2562 strip_info->idle_timer.function = strip_IdleTask; 2571 strip_info->idle_timer.function = strip_IdleTask;
2563 2572
2564 2573
2565 spin_lock_bh(&strip_lock); 2574 spin_lock_irqsave(&strip_lock, flags);
2566 rescan: 2575 rescan:
2567 /* 2576 /*
2568 * Search the list to find where to put our new entry 2577 * Search the list to find where to put our new entry
@@ -2581,7 +2590,7 @@ static struct strip *strip_alloc(void)
2581 sprintf(dev->name, "st%ld", dev->base_addr); 2590 sprintf(dev->name, "st%ld", dev->base_addr);
2582 2591
2583 list_add_tail_rcu(&strip_info->list, &strip_list); 2592 list_add_tail_rcu(&strip_info->list, &strip_list);
2584 spin_unlock_bh(&strip_lock); 2593 spin_unlock_irqrestore(&strip_lock, flags);
2585 2594
2586 return strip_info; 2595 return strip_info;
2587} 2596}