Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/slab.h>
0003 #include <linux/spinlock.h>
0004 #include <linux/once.h>
0005 #include <linux/random.h>
0006 #include <linux/module.h>
0007 
0008 struct once_work {
0009     struct work_struct work;
0010     struct static_key_true *key;
0011     struct module *module;
0012 };
0013 
0014 static void once_deferred(struct work_struct *w)
0015 {
0016     struct once_work *work;
0017 
0018     work = container_of(w, struct once_work, work);
0019     BUG_ON(!static_key_enabled(work->key));
0020     static_branch_disable(work->key);
0021     module_put(work->module);
0022     kfree(work);
0023 }
0024 
0025 static void once_disable_jump(struct static_key_true *key, struct module *mod)
0026 {
0027     struct once_work *w;
0028 
0029     w = kmalloc(sizeof(*w), GFP_ATOMIC);
0030     if (!w)
0031         return;
0032 
0033     INIT_WORK(&w->work, once_deferred);
0034     w->key = key;
0035     w->module = mod;
0036     __module_get(mod);
0037     schedule_work(&w->work);
0038 }
0039 
0040 static DEFINE_SPINLOCK(once_lock);
0041 
0042 bool __do_once_start(bool *done, unsigned long *flags)
0043     __acquires(once_lock)
0044 {
0045     spin_lock_irqsave(&once_lock, *flags);
0046     if (*done) {
0047         spin_unlock_irqrestore(&once_lock, *flags);
0048         /* Keep sparse happy by restoring an even lock count on
0049          * this lock. In case we return here, we don't call into
0050          * __do_once_done but return early in the DO_ONCE() macro.
0051          */
0052         __acquire(once_lock);
0053         return false;
0054     }
0055 
0056     return true;
0057 }
0058 EXPORT_SYMBOL(__do_once_start);
0059 
0060 void __do_once_done(bool *done, struct static_key_true *once_key,
0061             unsigned long *flags, struct module *mod)
0062     __releases(once_lock)
0063 {
0064     *done = true;
0065     spin_unlock_irqrestore(&once_lock, *flags);
0066     once_disable_jump(once_key, mod);
0067 }
0068 EXPORT_SYMBOL(__do_once_done);