Skip to content

Commit

Permalink
net: net_enable_timestamp() can be called from irq contexts
Browse files Browse the repository at this point in the history
[ Upstream commit 13baa00 ]

It is now very clear that silly TCP listeners might play with
enabling/disabling timestamping while new children are added
to their accept queue.

Meaning net_enable_timestamp() can be called from BH context
while current state of the static key is not enabled.

Lets play safe and allow all contexts.

The work queue is scheduled only under the problematic cases,
which are the static key enable/disable transition, to not slow down
critical paths.

This extends and improves what we did in commit 5fa8bbd ("net: use
a work queue to defer net_disable_timestamp() work")

Fixes: b90e579 ("net: dont call jump_label_dec from irq context")
Signed-off-by: Eric Dumazet <[email protected]>
Reported-by: Dmitry Vyukov <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
Eric Dumazet authored and gregkh committed Mar 22, 2017
1 parent f331d64 commit a70c328
Showing 1 changed file with 31 additions and 4 deletions.
35 changes: 31 additions & 4 deletions net/core/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1677,27 +1677,54 @@ EXPORT_SYMBOL_GPL(net_dec_ingress_queue);
static struct static_key netstamp_needed __read_mostly;
#ifdef HAVE_JUMP_LABEL
static atomic_t netstamp_needed_deferred;
static atomic_t netstamp_wanted;
static void netstamp_clear(struct work_struct *work)
{
int deferred = atomic_xchg(&netstamp_needed_deferred, 0);
int wanted;

while (deferred--)
static_key_slow_dec(&netstamp_needed);
wanted = atomic_add_return(deferred, &netstamp_wanted);
if (wanted > 0)
static_key_enable(&netstamp_needed);
else
static_key_disable(&netstamp_needed);
}
static DECLARE_WORK(netstamp_work, netstamp_clear);
#endif

void net_enable_timestamp(void)
{
#ifdef HAVE_JUMP_LABEL
int wanted;

while (1) {
wanted = atomic_read(&netstamp_wanted);
if (wanted <= 0)
break;
if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted + 1) == wanted)
return;
}
atomic_inc(&netstamp_needed_deferred);
schedule_work(&netstamp_work);
#else
static_key_slow_inc(&netstamp_needed);
#endif
}
EXPORT_SYMBOL(net_enable_timestamp);

void net_disable_timestamp(void)
{
#ifdef HAVE_JUMP_LABEL
/* net_disable_timestamp() can be called from non process context */
atomic_inc(&netstamp_needed_deferred);
int wanted;

while (1) {
wanted = atomic_read(&netstamp_wanted);
if (wanted <= 1)
break;
if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted - 1) == wanted)
return;
}
atomic_dec(&netstamp_needed_deferred);
schedule_work(&netstamp_work);
#else
static_key_slow_dec(&netstamp_needed);
Expand Down

0 comments on commit a70c328

Please sign in to comment.