162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/slab.h> 362306a36Sopenharmony_ci#include <linux/spinlock.h> 462306a36Sopenharmony_ci#include <linux/once.h> 562306a36Sopenharmony_ci#include <linux/random.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_cistruct once_work { 962306a36Sopenharmony_ci struct work_struct work; 1062306a36Sopenharmony_ci struct static_key_true *key; 1162306a36Sopenharmony_ci struct module *module; 1262306a36Sopenharmony_ci}; 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic void once_deferred(struct work_struct *w) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci struct once_work *work; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci work = container_of(w, struct once_work, work); 1962306a36Sopenharmony_ci BUG_ON(!static_key_enabled(work->key)); 2062306a36Sopenharmony_ci static_branch_disable(work->key); 2162306a36Sopenharmony_ci module_put(work->module); 2262306a36Sopenharmony_ci kfree(work); 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void once_disable_jump(struct static_key_true *key, struct module *mod) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct once_work *w; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci w = kmalloc(sizeof(*w), GFP_ATOMIC); 3062306a36Sopenharmony_ci if (!w) 3162306a36Sopenharmony_ci return; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci INIT_WORK(&w->work, once_deferred); 3462306a36Sopenharmony_ci w->key = key; 3562306a36Sopenharmony_ci w->module = mod; 3662306a36Sopenharmony_ci __module_get(mod); 3762306a36Sopenharmony_ci schedule_work(&w->work); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(once_lock); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cibool __do_once_start(bool *done, unsigned long *flags) 4362306a36Sopenharmony_ci __acquires(once_lock) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci spin_lock_irqsave(&once_lock, *flags); 4662306a36Sopenharmony_ci if (*done) { 4762306a36Sopenharmony_ci spin_unlock_irqrestore(&once_lock, *flags); 4862306a36Sopenharmony_ci /* Keep sparse happy by restoring an even lock count on 4962306a36Sopenharmony_ci * this lock. In case we return here, we don't call into 5062306a36Sopenharmony_ci * __do_once_done but return early in the DO_ONCE() macro. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci __acquire(once_lock); 5362306a36Sopenharmony_ci return false; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return true; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ciEXPORT_SYMBOL(__do_once_start); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_civoid __do_once_done(bool *done, struct static_key_true *once_key, 6162306a36Sopenharmony_ci unsigned long *flags, struct module *mod) 6262306a36Sopenharmony_ci __releases(once_lock) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci *done = true; 6562306a36Sopenharmony_ci spin_unlock_irqrestore(&once_lock, *flags); 6662306a36Sopenharmony_ci once_disable_jump(once_key, mod); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ciEXPORT_SYMBOL(__do_once_done); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic DEFINE_MUTEX(once_mutex); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cibool __do_once_sleepable_start(bool *done) 7362306a36Sopenharmony_ci __acquires(once_mutex) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci mutex_lock(&once_mutex); 7662306a36Sopenharmony_ci if (*done) { 7762306a36Sopenharmony_ci mutex_unlock(&once_mutex); 7862306a36Sopenharmony_ci /* Keep sparse happy by restoring an even lock count on 7962306a36Sopenharmony_ci * this mutex. In case we return here, we don't call into 8062306a36Sopenharmony_ci * __do_once_done but return early in the DO_ONCE_SLEEPABLE() macro. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci __acquire(once_mutex); 8362306a36Sopenharmony_ci return false; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return true; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ciEXPORT_SYMBOL(__do_once_sleepable_start); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_civoid __do_once_sleepable_done(bool *done, struct static_key_true *once_key, 9162306a36Sopenharmony_ci struct module *mod) 9262306a36Sopenharmony_ci __releases(once_mutex) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci *done = true; 9562306a36Sopenharmony_ci mutex_unlock(&once_mutex); 9662306a36Sopenharmony_ci once_disable_jump(once_key, mod); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ciEXPORT_SYMBOL(__do_once_sleepable_done); 99