Commit 1dcf51b8 authored by Eric Dumazet's avatar Eric Dumazet Committed by Ben Hutchings

net: net_enable_timestamp() can be called from irq contexts

commit 13baa00a upstream.

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 5fa8bbda ("net: use
a work queue to defer net_disable_timestamp() work")

Fixes: b90e5794 ("net: dont call jump_label_dec from irq context")
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Reported-by: default avatarDmitry Vyukov <dvyukov@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 0331b8df
......@@ -1601,27 +1601,54 @@ EXPORT_SYMBOL(call_netdevice_notifiers);
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment