Blame view

lib/once.c 1.35 KB
81f7e3824   Eric Lee   Initial Release, ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
  // SPDX-License-Identifier: GPL-2.0
  #include <linux/slab.h>
  #include <linux/spinlock.h>
  #include <linux/once.h>
  #include <linux/random.h>
  
  struct once_work {
  	struct work_struct work;
  	struct static_key *key;
  };
  
  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_key_slow_dec(work->key);
  	kfree(work);
  }
  
  static void once_disable_jump(struct static_key *key)
  {
  	struct once_work *w;
  
  	w = kmalloc(sizeof(*w), GFP_ATOMIC);
  	if (!w)
  		return;
  
  	INIT_WORK(&w->work, once_deferred);
  	w->key = key;
  	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 *once_key,
  		    unsigned long *flags)
  	__releases(once_lock)
  {
  	*done = true;
  	spin_unlock_irqrestore(&once_lock, *flags);
  	once_disable_jump(once_key);
  }
  EXPORT_SYMBOL(__do_once_done);