diff options
Diffstat (limited to 'net/core/utils.c')
| -rw-r--r-- | net/core/utils.c | 49 | 
1 files changed, 49 insertions, 0 deletions
| diff --git a/net/core/utils.c b/net/core/utils.c index aa88e23fc87..2f737bf90b3 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -338,3 +338,52 @@ void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb,  				  csum_unfold(*sum)));  }  EXPORT_SYMBOL(inet_proto_csum_replace16); + +struct __net_random_once_work { +	struct work_struct work; +	struct static_key *key; +}; + +static void __net_random_once_deferred(struct work_struct *w) +{ +	struct __net_random_once_work *work = +		container_of(w, struct __net_random_once_work, work); +	if (!static_key_enabled(work->key)) +		static_key_slow_inc(work->key); +	kfree(work); +} + +static void __net_random_once_disable_jump(struct static_key *key) +{ +	struct __net_random_once_work *w; + +	w = kmalloc(sizeof(*w), GFP_ATOMIC); +	if (!w) +		return; + +	INIT_WORK(&w->work, __net_random_once_deferred); +	w->key = key; +	schedule_work(&w->work); +} + +bool __net_get_random_once(void *buf, int nbytes, bool *done, +			   struct static_key *done_key) +{ +	static DEFINE_SPINLOCK(lock); +	unsigned long flags; + +	spin_lock_irqsave(&lock, flags); +	if (*done) { +		spin_unlock_irqrestore(&lock, flags); +		return false; +	} + +	get_random_bytes(buf, nbytes); +	*done = true; +	spin_unlock_irqrestore(&lock, flags); + +	__net_random_once_disable_jump(done_key); + +	return true; +} +EXPORT_SYMBOL(__net_get_random_once); | 
