Commit c90aeb948222a7b3d3391d232ec4f50fd8322ad3
Committed by
David S. Miller
1 parent
46234253b9
Exists in
smarct4x-processor-sdk-linux-03.00.00.04
and in
4 other branches
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>
Showing 2 changed files with 76 additions and 35 deletions Side-by-side Diff
include/linux/once.h
... | ... | @@ -4,22 +4,55 @@ |
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 */ |
lib/once.c
... | ... | @@ -3,53 +3,61 @@ |
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); |