net: net_enable_timestamp() can be called from irq contexts
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 commit5fa8bbda38
("net: use a work queue to defer net_disable_timestamp() work") Fixes:b90e5794c5
("net: dont call jump_label_dec from irq context") Signed-off-by: Eric Dumazet <edumazet@google.com> Reported-by: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
540e2894f7
Коммит
13baa00ad0
|
@ -1698,27 +1698,54 @@ EXPORT_SYMBOL_GPL(net_dec_egress_queue);
|
||||||
static struct static_key netstamp_needed __read_mostly;
|
static struct static_key netstamp_needed __read_mostly;
|
||||||
#ifdef HAVE_JUMP_LABEL
|
#ifdef HAVE_JUMP_LABEL
|
||||||
static atomic_t netstamp_needed_deferred;
|
static atomic_t netstamp_needed_deferred;
|
||||||
|
static atomic_t netstamp_wanted;
|
||||||
static void netstamp_clear(struct work_struct *work)
|
static void netstamp_clear(struct work_struct *work)
|
||||||
{
|
{
|
||||||
int deferred = atomic_xchg(&netstamp_needed_deferred, 0);
|
int deferred = atomic_xchg(&netstamp_needed_deferred, 0);
|
||||||
|
int wanted;
|
||||||
|
|
||||||
while (deferred--)
|
wanted = atomic_add_return(deferred, &netstamp_wanted);
|
||||||
static_key_slow_dec(&netstamp_needed);
|
if (wanted > 0)
|
||||||
|
static_key_enable(&netstamp_needed);
|
||||||
|
else
|
||||||
|
static_key_disable(&netstamp_needed);
|
||||||
}
|
}
|
||||||
static DECLARE_WORK(netstamp_work, netstamp_clear);
|
static DECLARE_WORK(netstamp_work, netstamp_clear);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void net_enable_timestamp(void)
|
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);
|
static_key_slow_inc(&netstamp_needed);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(net_enable_timestamp);
|
EXPORT_SYMBOL(net_enable_timestamp);
|
||||||
|
|
||||||
void net_disable_timestamp(void)
|
void net_disable_timestamp(void)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_JUMP_LABEL
|
#ifdef HAVE_JUMP_LABEL
|
||||||
/* net_disable_timestamp() can be called from non process context */
|
int wanted;
|
||||||
atomic_inc(&netstamp_needed_deferred);
|
|
||||||
|
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);
|
schedule_work(&netstamp_work);
|
||||||
#else
|
#else
|
||||||
static_key_slow_dec(&netstamp_needed);
|
static_key_slow_dec(&netstamp_needed);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче