Skip to content

Commit b90e579

Browse files
Eric Dumazetdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent db62f68 commit b90e579

File tree

3 files changed

+31
-3
lines changed

3 files changed

+31
-3
lines changed

net/core/dev.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,15 +1441,38 @@ int call_netdevice_notifiers(unsigned long val, struct net_device *dev)
14411441
EXPORT_SYMBOL(call_netdevice_notifiers);
14421442

14431443
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
14441451

14451452
void net_enable_timestamp(void)
14461453
{
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());
14471464
jump_label_inc(&netstamp_needed);
14481465
}
14491466
EXPORT_SYMBOL(net_enable_timestamp);
14501467

14511468
void net_disable_timestamp(void)
14521469
{
1470+
#ifdef HAVE_JUMP_LABEL
1471+
if (in_interrupt()) {
1472+
atomic_inc(&netstamp_needed_deferred);
1473+
return;
1474+
}
1475+
#endif
14531476
jump_label_dec(&netstamp_needed);
14541477
}
14551478
EXPORT_SYMBOL(net_disable_timestamp);

net/ipv4/netfilter/ip_queue.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ __ipq_rcv_skb(struct sk_buff *skb)
404404
int status, type, pid, flags;
405405
unsigned int nlmsglen, skblen;
406406
struct nlmsghdr *nlh;
407+
bool enable_timestamp = false;
407408

408409
skblen = skb->len;
409410
if (skblen < sizeof(*nlh))
@@ -441,12 +442,13 @@ __ipq_rcv_skb(struct sk_buff *skb)
441442
RCV_SKB_FAIL(-EBUSY);
442443
}
443444
} else {
444-
net_enable_timestamp();
445+
enable_timestamp = true;
445446
peer_pid = pid;
446447
}
447448

448449
spin_unlock_bh(&queue_lock);
449-
450+
if (enable_timestamp)
451+
net_enable_timestamp();
450452
status = ipq_receive_peer(NLMSG_DATA(nlh), type,
451453
nlmsglen - NLMSG_LENGTH(0));
452454
if (status < 0)

net/ipv6/netfilter/ip6_queue.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ __ipq_rcv_skb(struct sk_buff *skb)
405405
int status, type, pid, flags;
406406
unsigned int nlmsglen, skblen;
407407
struct nlmsghdr *nlh;
408+
bool enable_timestamp = false;
408409

409410
skblen = skb->len;
410411
if (skblen < sizeof(*nlh))
@@ -442,11 +443,13 @@ __ipq_rcv_skb(struct sk_buff *skb)
442443
RCV_SKB_FAIL(-EBUSY);
443444
}
444445
} else {
445-
net_enable_timestamp();
446+
enable_timestamp = true;
446447
peer_pid = pid;
447448
}
448449

449450
spin_unlock_bh(&queue_lock);
451+
if (enable_timestamp)
452+
net_enable_timestamp();
450453

451454
status = ipq_receive_peer(NLMSG_DATA(nlh), type,
452455
nlmsglen - NLMSG_LENGTH(0));

0 commit comments

Comments
 (0)