diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-11-28 06:16:50 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-11-29 00:26:25 -0500 |
commit | b90e5794c5bdef91d26c623e992257947c506e35 (patch) | |
tree | 16bb31a00ad39d7fba91bb8d691bb0133e06312f | |
parent | db62f684deeb291ab2533b99843d5df9a36b1f19 (diff) |
net: dont call jump_label_dec from irq context
Igor Maravic reported an error caused by jump_label_dec() being called
from IRQ context :
BUG: sleeping function called from invalid context at kernel/mutex.c:271
in_atomic(): 1, irqs_disabled(): 0, pid: 0, name: swapper
1 lock held by swapper/0:
#0: (&n->timer){+.-...}, at: [<ffffffff8107ce90>] call_timer_fn+0x0/0x340
Pid: 0, comm: swapper Not tainted 3.2.0-rc2-net-next-mpls+ #1
Call Trace:
<IRQ> [<ffffffff8104f417>] __might_sleep+0x137/0x1f0
[<ffffffff816b9a2f>] mutex_lock_nested+0x2f/0x370
[<ffffffff810a89fd>] ? trace_hardirqs_off+0xd/0x10
[<ffffffff8109a37f>] ? local_clock+0x6f/0x80
[<ffffffff810a90a5>] ? lock_release_holdtime.part.22+0x15/0x1a0
[<ffffffff81557929>] ? sock_def_write_space+0x59/0x160
[<ffffffff815e936e>] ? arp_error_report+0x3e/0x90
[<ffffffff810969cd>] atomic_dec_and_mutex_lock+0x5d/0x80
[<ffffffff8112fc1d>] jump_label_dec+0x1d/0x50
[<ffffffff81566525>] net_disable_timestamp+0x15/0x20
[<ffffffff81557a75>] sock_disable_timestamp+0x45/0x50
[<ffffffff81557b00>] __sk_free+0x80/0x200
[<ffffffff815578d0>] ? sk_send_sigurg+0x70/0x70
[<ffffffff815e936e>] ? arp_error_report+0x3e/0x90
[<ffffffff81557cba>] sock_wfree+0x3a/0x70
[<ffffffff8155c2b0>] skb_release_head_state+0x70/0x120
[<ffffffff8155c0b6>] __kfree_skb+0x16/0x30
[<ffffffff8155c119>] kfree_skb+0x49/0x170
[<ffffffff815e936e>] arp_error_report+0x3e/0x90
[<ffffffff81575bd9>] neigh_invalidate+0x89/0xc0
[<ffffffff81578dbe>] neigh_timer_handler+0x9e/0x2a0
[<ffffffff81578d20>] ? neigh_update+0x640/0x640
[<ffffffff81073558>] __do_softirq+0xc8/0x3a0
Since jump_label_{inc|dec} must be called from process context only,
we must defer jump_label_dec() if net_disable_timestamp() is called
from interrupt context.
Reported-by: Igor Maravic <igorm@etf.rs>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/core/dev.c | 23 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_queue.c | 6 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_queue.c | 5 |
3 files changed, 31 insertions, 3 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 962e3de25a35..c7ef6c5d3782 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -1441,15 +1441,38 @@ int call_netdevice_notifiers(unsigned long val, struct net_device *dev) | |||
1441 | EXPORT_SYMBOL(call_netdevice_notifiers); | 1441 | EXPORT_SYMBOL(call_netdevice_notifiers); |
1442 | 1442 | ||
1443 | static struct jump_label_key netstamp_needed __read_mostly; | 1443 | static struct jump_label_key netstamp_needed __read_mostly; |
1444 | #ifdef HAVE_JUMP_LABEL | ||
1445 | /* We are not allowed to call jump_label_dec() from irq context | ||
1446 | * If net_disable_timestamp() is called from irq context, defer the | ||
1447 | * jump_label_dec() calls. | ||
1448 | */ | ||
1449 | static atomic_t netstamp_needed_deferred; | ||
1450 | #endif | ||
1444 | 1451 | ||
1445 | void net_enable_timestamp(void) | 1452 | void net_enable_timestamp(void) |
1446 | { | 1453 | { |
1454 | #ifdef HAVE_JUMP_LABEL | ||
1455 | int deferred = atomic_xchg(&netstamp_needed_deferred, 0); | ||
1456 | |||
1457 | if (deferred) { | ||
1458 | while (--deferred) | ||
1459 | jump_label_dec(&netstamp_needed); | ||
1460 | return; | ||
1461 | } | ||
1462 | #endif | ||
1463 | WARN_ON(in_interrupt()); | ||
1447 | jump_label_inc(&netstamp_needed); | 1464 | jump_label_inc(&netstamp_needed); |
1448 | } | 1465 | } |
1449 | EXPORT_SYMBOL(net_enable_timestamp); | 1466 | EXPORT_SYMBOL(net_enable_timestamp); |
1450 | 1467 | ||
1451 | void net_disable_timestamp(void) | 1468 | void net_disable_timestamp(void) |
1452 | { | 1469 | { |
1470 | #ifdef HAVE_JUMP_LABEL | ||
1471 | if (in_interrupt()) { | ||
1472 | atomic_inc(&netstamp_needed_deferred); | ||
1473 | return; | ||
1474 | } | ||
1475 | #endif | ||
1453 | jump_label_dec(&netstamp_needed); | 1476 | jump_label_dec(&netstamp_needed); |
1454 | } | 1477 | } |
1455 | EXPORT_SYMBOL(net_disable_timestamp); | 1478 | EXPORT_SYMBOL(net_disable_timestamp); |
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c index e59aabd0eae4..a057fe64debd 100644 --- a/net/ipv4/netfilter/ip_queue.c +++ b/net/ipv4/netfilter/ip_queue.c | |||
@@ -404,6 +404,7 @@ __ipq_rcv_skb(struct sk_buff *skb) | |||
404 | int status, type, pid, flags; | 404 | int status, type, pid, flags; |
405 | unsigned int nlmsglen, skblen; | 405 | unsigned int nlmsglen, skblen; |
406 | struct nlmsghdr *nlh; | 406 | struct nlmsghdr *nlh; |
407 | bool enable_timestamp = false; | ||
407 | 408 | ||
408 | skblen = skb->len; | 409 | skblen = skb->len; |
409 | if (skblen < sizeof(*nlh)) | 410 | if (skblen < sizeof(*nlh)) |
@@ -441,12 +442,13 @@ __ipq_rcv_skb(struct sk_buff *skb) | |||
441 | RCV_SKB_FAIL(-EBUSY); | 442 | RCV_SKB_FAIL(-EBUSY); |
442 | } | 443 | } |
443 | } else { | 444 | } else { |
444 | net_enable_timestamp(); | 445 | enable_timestamp = true; |
445 | peer_pid = pid; | 446 | peer_pid = pid; |
446 | } | 447 | } |
447 | 448 | ||
448 | spin_unlock_bh(&queue_lock); | 449 | spin_unlock_bh(&queue_lock); |
449 | 450 | if (enable_timestamp) | |
451 | net_enable_timestamp(); | ||
450 | status = ipq_receive_peer(NLMSG_DATA(nlh), type, | 452 | status = ipq_receive_peer(NLMSG_DATA(nlh), type, |
451 | nlmsglen - NLMSG_LENGTH(0)); | 453 | nlmsglen - NLMSG_LENGTH(0)); |
452 | if (status < 0) | 454 | if (status < 0) |
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c index e63c3972a739..fb80a23c6640 100644 --- a/net/ipv6/netfilter/ip6_queue.c +++ b/net/ipv6/netfilter/ip6_queue.c | |||
@@ -405,6 +405,7 @@ __ipq_rcv_skb(struct sk_buff *skb) | |||
405 | int status, type, pid, flags; | 405 | int status, type, pid, flags; |
406 | unsigned int nlmsglen, skblen; | 406 | unsigned int nlmsglen, skblen; |
407 | struct nlmsghdr *nlh; | 407 | struct nlmsghdr *nlh; |
408 | bool enable_timestamp = false; | ||
408 | 409 | ||
409 | skblen = skb->len; | 410 | skblen = skb->len; |
410 | if (skblen < sizeof(*nlh)) | 411 | if (skblen < sizeof(*nlh)) |
@@ -442,11 +443,13 @@ __ipq_rcv_skb(struct sk_buff *skb) | |||
442 | RCV_SKB_FAIL(-EBUSY); | 443 | RCV_SKB_FAIL(-EBUSY); |
443 | } | 444 | } |
444 | } else { | 445 | } else { |
445 | net_enable_timestamp(); | 446 | enable_timestamp = true; |
446 | peer_pid = pid; | 447 | peer_pid = pid; |
447 | } | 448 | } |
448 | 449 | ||
449 | spin_unlock_bh(&queue_lock); | 450 | spin_unlock_bh(&queue_lock); |
451 | if (enable_timestamp) | ||
452 | net_enable_timestamp(); | ||
450 | 453 | ||
451 | status = ipq_receive_peer(NLMSG_DATA(nlh), type, | 454 | status = ipq_receive_peer(NLMSG_DATA(nlh), type, |
452 | nlmsglen - NLMSG_LENGTH(0)); | 455 | nlmsglen - NLMSG_LENGTH(0)); |