Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Hannes Frederic Sowa and committed by
David S. Miller
c90aeb94 46234253

+76 -35
+47 -14
include/linux/once.h
··· 4 4 #include <linux/types.h> 5 5 #include <linux/jump_label.h> 6 6 7 - bool __get_random_once(void *buf, int nbytes, bool *done, 8 - struct static_key *once_key); 7 + bool __do_once_start(bool *done, unsigned long *flags); 8 + void __do_once_done(bool *done, struct static_key *once_key, 9 + unsigned long *flags); 9 10 10 - #define get_random_once(buf, nbytes) \ 11 - ({ \ 12 - bool ___ret = false; \ 13 - static bool ___done = false; \ 14 - static struct static_key ___once_key = \ 15 - STATIC_KEY_INIT_TRUE; \ 16 - if (static_key_true(&___once_key)) \ 17 - ___ret = __get_random_once((buf), \ 18 - (nbytes), \ 19 - &___done, \ 20 - &___once_key); \ 21 - ___ret; \ 11 + /* Call a function exactly once. The idea of DO_ONCE() is to perform 12 + * a function call such as initialization of random seeds, etc, only 13 + * once, where DO_ONCE() can live in the fast-path. After @func has 14 + * been called with the passed arguments, the static key will patch 15 + * out the condition into a nop. DO_ONCE() guarantees type safety of 16 + * arguments! 17 + * 18 + * Not that the following is not equivalent ... 19 + * 20 + * DO_ONCE(func, arg); 21 + * DO_ONCE(func, arg); 22 + * 23 + * ... to this version: 24 + * 25 + * void foo(void) 26 + * { 27 + * DO_ONCE(func, arg); 28 + * } 29 + * 30 + * foo(); 31 + * foo(); 32 + * 33 + * In case the one-time invocation could be triggered from multiple 34 + * places, then a common helper function must be defined, so that only 35 + * a single static key will be placed there! 36 + */ 37 + #define DO_ONCE(func, ...) \ 38 + ({ \ 39 + bool ___ret = false; \ 40 + static bool ___done = false; \ 41 + static struct static_key ___once_key = STATIC_KEY_INIT_TRUE; \ 42 + if (static_key_true(&___once_key)) { \ 43 + unsigned long ___flags; \ 44 + ___ret = __do_once_start(&___done, &___flags); \ 45 + if (unlikely(___ret)) { \ 46 + func(__VA_ARGS__); \ 47 + __do_once_done(&___done, &___once_key, \ 48 + &___flags); \ 49 + } \ 50 + } \ 51 + ___ret; \ 22 52 }) 53 + 54 + #define get_random_once(buf, nbytes) \ 55 + DO_ONCE(get_random_bytes, (buf), (nbytes)) 23 56 24 57 #endif /* _LINUX_ONCE_H */
+29 -21
lib/once.c
··· 3 3 #include <linux/once.h> 4 4 #include <linux/random.h> 5 5 6 - struct __random_once_work { 6 + struct once_work { 7 7 struct work_struct work; 8 8 struct static_key *key; 9 9 }; 10 10 11 - static void __random_once_deferred(struct work_struct *w) 11 + static void once_deferred(struct work_struct *w) 12 12 { 13 - struct __random_once_work *work; 13 + struct once_work *work; 14 14 15 - work = container_of(w, struct __random_once_work, work); 15 + work = container_of(w, struct once_work, work); 16 16 BUG_ON(!static_key_enabled(work->key)); 17 17 static_key_slow_dec(work->key); 18 18 kfree(work); 19 19 } 20 20 21 - static void __random_once_disable_jump(struct static_key *key) 21 + static void once_disable_jump(struct static_key *key) 22 22 { 23 - struct __random_once_work *w; 23 + struct once_work *w; 24 24 25 25 w = kmalloc(sizeof(*w), GFP_ATOMIC); 26 26 if (!w) 27 27 return; 28 28 29 - INIT_WORK(&w->work, __random_once_deferred); 29 + INIT_WORK(&w->work, once_deferred); 30 30 w->key = key; 31 31 schedule_work(&w->work); 32 32 } 33 33 34 - bool __get_random_once(void *buf, int nbytes, bool *done, 35 - struct static_key *once_key) 36 - { 37 - static DEFINE_SPINLOCK(lock); 38 - unsigned long flags; 34 + static DEFINE_SPINLOCK(once_lock); 39 35 40 - spin_lock_irqsave(&lock, flags); 36 + bool __do_once_start(bool *done, unsigned long *flags) 37 + __acquires(once_lock) 38 + { 39 + spin_lock_irqsave(&once_lock, *flags); 41 40 if (*done) { 42 - spin_unlock_irqrestore(&lock, flags); 41 + spin_unlock_irqrestore(&once_lock, *flags); 42 + /* Keep sparse happy by restoring an even lock count on 43 + * this lock. In case we return here, we don't call into 44 + * __do_once_done but return early in the DO_ONCE() macro. 45 + */ 46 + __acquire(once_lock); 43 47 return false; 44 48 } 45 49 46 - get_random_bytes(buf, nbytes); 47 - *done = true; 48 - spin_unlock_irqrestore(&lock, flags); 49 - 50 - __random_once_disable_jump(once_key); 51 - 52 50 return true; 53 51 } 54 - EXPORT_SYMBOL(__get_random_once); 52 + EXPORT_SYMBOL(__do_once_start); 53 + 54 + void __do_once_done(bool *done, struct static_key *once_key, 55 + unsigned long *flags) 56 + __releases(once_lock) 57 + { 58 + *done = true; 59 + spin_unlock_irqrestore(&once_lock, *flags); 60 + once_disable_jump(once_key); 61 + } 62 + EXPORT_SYMBOL(__do_once_done);