| // SPDX-License-Identifier: GPL-2.0 | 
 | #include <linux/slab.h> | 
 | #include <linux/spinlock.h> | 
 | #include <linux/once.h> | 
 | #include <linux/random.h> | 
 | #include <linux/module.h> | 
 |  | 
 | struct once_work { | 
 | 	struct work_struct work; | 
 | 	struct static_key_true *key; | 
 | 	struct module *module; | 
 | }; | 
 |  | 
 | static void once_deferred(struct work_struct *w) | 
 | { | 
 | 	struct once_work *work; | 
 |  | 
 | 	work = container_of(w, struct once_work, work); | 
 | 	BUG_ON(!static_key_enabled(work->key)); | 
 | 	static_branch_disable(work->key); | 
 | 	module_put(work->module); | 
 | 	kfree(work); | 
 | } | 
 |  | 
 | static void once_disable_jump(struct static_key_true *key, struct module *mod) | 
 | { | 
 | 	struct once_work *w; | 
 |  | 
 | 	w = kmalloc(sizeof(*w), GFP_ATOMIC); | 
 | 	if (!w) | 
 | 		return; | 
 |  | 
 | 	INIT_WORK(&w->work, once_deferred); | 
 | 	w->key = key; | 
 | 	w->module = mod; | 
 | 	__module_get(mod); | 
 | 	schedule_work(&w->work); | 
 | } | 
 |  | 
 | static DEFINE_SPINLOCK(once_lock); | 
 |  | 
 | bool __do_once_start(bool *done, unsigned long *flags) | 
 | 	__acquires(once_lock) | 
 | { | 
 | 	spin_lock_irqsave(&once_lock, *flags); | 
 | 	if (*done) { | 
 | 		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 true; | 
 | } | 
 | EXPORT_SYMBOL(__do_once_start); | 
 |  | 
 | void __do_once_done(bool *done, struct static_key_true *once_key, | 
 | 		    unsigned long *flags, struct module *mod) | 
 | 	__releases(once_lock) | 
 | { | 
 | 	*done = true; | 
 | 	spin_unlock_irqrestore(&once_lock, *flags); | 
 | 	once_disable_jump(once_key, mod); | 
 | } | 
 | EXPORT_SYMBOL(__do_once_done); |