once: make helper generic for calling functions once
Make the get_random_once() helper generic enough, so that functions in general would only be called once, where one user of this is then net_get_random_once(). The only implementation specific call is to get_random_bytes(), all the rest of this *_once() facility would be duplicated among different subsystems otherwise. The new DO_ONCE() helper will be used by prandom() later on, but might also be useful for other scenarios/subsystems as well where a one-time initialization in often-called, possibly fast path code could occur. Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
46234253b9
commit
c90aeb9482
2 changed files with 76 additions and 35 deletions
|
@ -4,21 +4,54 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/jump_label.h>
|
#include <linux/jump_label.h>
|
||||||
|
|
||||||
bool __get_random_once(void *buf, int nbytes, bool *done,
|
bool __do_once_start(bool *done, unsigned long *flags);
|
||||||
struct static_key *once_key);
|
void __do_once_done(bool *done, struct static_key *once_key,
|
||||||
|
unsigned long *flags);
|
||||||
|
|
||||||
#define get_random_once(buf, nbytes) \
|
/* Call a function exactly once. The idea of DO_ONCE() is to perform
|
||||||
({ \
|
* a function call such as initialization of random seeds, etc, only
|
||||||
bool ___ret = false; \
|
* once, where DO_ONCE() can live in the fast-path. After @func has
|
||||||
static bool ___done = false; \
|
* been called with the passed arguments, the static key will patch
|
||||||
static struct static_key ___once_key = \
|
* out the condition into a nop. DO_ONCE() guarantees type safety of
|
||||||
STATIC_KEY_INIT_TRUE; \
|
* arguments!
|
||||||
if (static_key_true(&___once_key)) \
|
*
|
||||||
___ret = __get_random_once((buf), \
|
* Not that the following is not equivalent ...
|
||||||
(nbytes), \
|
*
|
||||||
&___done, \
|
* DO_ONCE(func, arg);
|
||||||
&___once_key); \
|
* DO_ONCE(func, arg);
|
||||||
___ret; \
|
*
|
||||||
|
* ... to this version:
|
||||||
|
*
|
||||||
|
* void foo(void)
|
||||||
|
* {
|
||||||
|
* DO_ONCE(func, arg);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* foo();
|
||||||
|
* foo();
|
||||||
|
*
|
||||||
|
* In case the one-time invocation could be triggered from multiple
|
||||||
|
* places, then a common helper function must be defined, so that only
|
||||||
|
* a single static key will be placed there!
|
||||||
|
*/
|
||||||
|
#define DO_ONCE(func, ...) \
|
||||||
|
({ \
|
||||||
|
bool ___ret = false; \
|
||||||
|
static bool ___done = false; \
|
||||||
|
static struct static_key ___once_key = STATIC_KEY_INIT_TRUE; \
|
||||||
|
if (static_key_true(&___once_key)) { \
|
||||||
|
unsigned long ___flags; \
|
||||||
|
___ret = __do_once_start(&___done, &___flags); \
|
||||||
|
if (unlikely(___ret)) { \
|
||||||
|
func(__VA_ARGS__); \
|
||||||
|
__do_once_done(&___done, &___once_key, \
|
||||||
|
&___flags); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
___ret; \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#define get_random_once(buf, nbytes) \
|
||||||
|
DO_ONCE(get_random_bytes, (buf), (nbytes))
|
||||||
|
|
||||||
#endif /* _LINUX_ONCE_H */
|
#endif /* _LINUX_ONCE_H */
|
||||||
|
|
50
lib/once.c
50
lib/once.c
|
@ -3,52 +3,60 @@
|
||||||
#include <linux/once.h>
|
#include <linux/once.h>
|
||||||
#include <linux/random.h>
|
#include <linux/random.h>
|
||||||
|
|
||||||
struct __random_once_work {
|
struct once_work {
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
struct static_key *key;
|
struct static_key *key;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void __random_once_deferred(struct work_struct *w)
|
static void once_deferred(struct work_struct *w)
|
||||||
{
|
{
|
||||||
struct __random_once_work *work;
|
struct once_work *work;
|
||||||
|
|
||||||
work = container_of(w, struct __random_once_work, work);
|
work = container_of(w, struct once_work, work);
|
||||||
BUG_ON(!static_key_enabled(work->key));
|
BUG_ON(!static_key_enabled(work->key));
|
||||||
static_key_slow_dec(work->key);
|
static_key_slow_dec(work->key);
|
||||||
kfree(work);
|
kfree(work);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __random_once_disable_jump(struct static_key *key)
|
static void once_disable_jump(struct static_key *key)
|
||||||
{
|
{
|
||||||
struct __random_once_work *w;
|
struct once_work *w;
|
||||||
|
|
||||||
w = kmalloc(sizeof(*w), GFP_ATOMIC);
|
w = kmalloc(sizeof(*w), GFP_ATOMIC);
|
||||||
if (!w)
|
if (!w)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
INIT_WORK(&w->work, __random_once_deferred);
|
INIT_WORK(&w->work, once_deferred);
|
||||||
w->key = key;
|
w->key = key;
|
||||||
schedule_work(&w->work);
|
schedule_work(&w->work);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool __get_random_once(void *buf, int nbytes, bool *done,
|
static DEFINE_SPINLOCK(once_lock);
|
||||||
struct static_key *once_key)
|
|
||||||
{
|
|
||||||
static DEFINE_SPINLOCK(lock);
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&lock, flags);
|
bool __do_once_start(bool *done, unsigned long *flags)
|
||||||
|
__acquires(once_lock)
|
||||||
|
{
|
||||||
|
spin_lock_irqsave(&once_lock, *flags);
|
||||||
if (*done) {
|
if (*done) {
|
||||||
spin_unlock_irqrestore(&lock, flags);
|
spin_unlock_irqrestore(&once_lock, *flags);
|
||||||
|
/* Keep sparse happy by restoring an even lock count on
|
||||||
|
* this lock. In case we return here, we don't call into
|
||||||
|
* __do_once_done but return early in the DO_ONCE() macro.
|
||||||
|
*/
|
||||||
|
__acquire(once_lock);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_random_bytes(buf, nbytes);
|
|
||||||
*done = true;
|
|
||||||
spin_unlock_irqrestore(&lock, flags);
|
|
||||||
|
|
||||||
__random_once_disable_jump(once_key);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__get_random_once);
|
EXPORT_SYMBOL(__do_once_start);
|
||||||
|
|
||||||
|
void __do_once_done(bool *done, struct static_key *once_key,
|
||||||
|
unsigned long *flags)
|
||||||
|
__releases(once_lock)
|
||||||
|
{
|
||||||
|
*done = true;
|
||||||
|
spin_unlock_irqrestore(&once_lock, *flags);
|
||||||
|
once_disable_jump(once_key);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(__do_once_done);
|
||||||
|
|
Loading…
Reference in a new issue