aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel_lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel_lock.c')
-rw-r--r--lib/kernel_lock.c119
1 files changed, 40 insertions, 79 deletions
diff --git a/lib/kernel_lock.c b/lib/kernel_lock.c
index b135d04aa48a..bc62ed84f71f 100644
--- a/lib/kernel_lock.c
+++ b/lib/kernel_lock.c
@@ -7,114 +7,64 @@
7 */ 7 */
8#include <linux/module.h> 8#include <linux/module.h>
9#include <linux/kallsyms.h> 9#include <linux/kallsyms.h>
10#include <linux/semaphore.h> 10#include <linux/mutex.h>
11#include <linux/smp_lock.h> 11#include <linux/smp_lock.h>
12 12
13#define CREATE_TRACE_POINTS 13#define CREATE_TRACE_POINTS
14#include <trace/events/bkl.h> 14#include <trace/events/bkl.h>
15 15
16/* 16/*
17 * The 'big kernel lock' 17 * The 'big kernel semaphore'
18 * 18 *
19 * This spinlock is taken and released recursively by lock_kernel() 19 * This mutex is taken and released recursively by lock_kernel()
20 * and unlock_kernel(). It is transparently dropped and reacquired 20 * and unlock_kernel(). It is transparently dropped and reacquired
21 * over schedule(). It is used to protect legacy code that hasn't 21 * over schedule(). It is used to protect legacy code that hasn't
22 * been migrated to a proper locking design yet. 22 * been migrated to a proper locking design yet.
23 * 23 *
24 * Note: code locked by this semaphore will only be serialized against
25 * other code using the same locking facility. The code guarantees that
26 * the task remains on the same CPU.
27 *
24 * Don't use in new code. 28 * Don't use in new code.
25 */ 29 */
26static __cacheline_aligned_in_smp DEFINE_RAW_SPINLOCK(kernel_flag); 30DEFINE_MUTEX(kernel_sem);
27
28 31
29/* 32/*
30 * Acquire/release the underlying lock from the scheduler. 33 * Re-acquire the kernel semaphore.
31 * 34 *
32 * This is called with preemption disabled, and should 35 * This function is called with preemption off.
33 * return an error value if it cannot get the lock and
34 * TIF_NEED_RESCHED gets set.
35 * 36 *
36 * If it successfully gets the lock, it should increment 37 * We are executing in schedule() so the code must be extremely careful
37 * the preemption count like any spinlock does. 38 * about recursion, both due to the down() and due to the enabling of
39 * preemption. schedule() will re-check the preemption flag after
40 * reacquiring the semaphore.
38 * 41 *
39 * (This works on UP too - do_raw_spin_trylock will never 42 * Called with interrupts disabled.
40 * return false in that case)
41 */ 43 */
42int __lockfunc __reacquire_kernel_lock(void) 44int __lockfunc __reacquire_kernel_lock(void)
43{ 45{
44 while (!do_raw_spin_trylock(&kernel_flag)) { 46 int saved_lock_depth = current->lock_depth;
45 if (need_resched())
46 return -EAGAIN;
47 cpu_relax();
48 }
49 preempt_disable();
50 return 0;
51}
52 47
53void __lockfunc __release_kernel_lock(void) 48 BUG_ON(saved_lock_depth < 0);
54{
55 do_raw_spin_unlock(&kernel_flag);
56 preempt_enable_no_resched();
57}
58 49
59/* 50 current->lock_depth = -1;
60 * These are the BKL spinlocks - we try to be polite about preemption. 51 local_irq_enable();
61 * If SMP is not on (ie UP preemption), this all goes away because the
62 * do_raw_spin_trylock() will always succeed.
63 */
64#ifdef CONFIG_PREEMPT
65static inline void __lock_kernel(void)
66{
67 preempt_disable();
68 if (unlikely(!do_raw_spin_trylock(&kernel_flag))) {
69 /*
70 * If preemption was disabled even before this
71 * was called, there's nothing we can be polite
72 * about - just spin.
73 */
74 if (preempt_count() > 1) {
75 do_raw_spin_lock(&kernel_flag);
76 return;
77 }
78 52
79 /* 53 mutex_lock(&kernel_sem);
80 * Otherwise, let's wait for the kernel lock
81 * with preemption enabled..
82 */
83 do {
84 preempt_enable();
85 while (raw_spin_is_locked(&kernel_flag))
86 cpu_relax();
87 preempt_disable();
88 } while (!do_raw_spin_trylock(&kernel_flag));
89 }
90}
91 54
92#else 55 local_irq_disable();
56 current->lock_depth = saved_lock_depth;
93 57
94/* 58 return 0;
95 * Non-preemption case - just get the spinlock
96 */
97static inline void __lock_kernel(void)
98{
99 do_raw_spin_lock(&kernel_flag);
100} 59}
101#endif
102 60
103static inline void __unlock_kernel(void) 61void __lockfunc __release_kernel_lock(void)
104{ 62{
105 /* 63 mutex_unlock(&kernel_sem);
106 * the BKL is not covered by lockdep, so we open-code the
107 * unlocking sequence (and thus avoid the dep-chain ops):
108 */
109 do_raw_spin_unlock(&kernel_flag);
110 preempt_enable();
111} 64}
112 65
113/* 66/*
114 * Getting the big kernel lock. 67 * Getting the big kernel semaphore.
115 *
116 * This cannot happen asynchronously, so we only need to
117 * worry about other CPU's.
118 */ 68 */
119void __lockfunc _lock_kernel(const char *func, const char *file, int line) 69void __lockfunc _lock_kernel(const char *func, const char *file, int line)
120{ 70{
@@ -124,17 +74,28 @@ void __lockfunc _lock_kernel(const char *func, const char *file, int line)
124 74
125 if (likely(!depth)) { 75 if (likely(!depth)) {
126 might_sleep(); 76 might_sleep();
127 __lock_kernel(); 77 /*
78 * No recursion worries - we set up lock_depth _after_
79 */
80 mutex_lock(&kernel_sem);
81#ifdef CONFIG_DEBUG_RT_MUTEXES
82 current->last_kernel_lock = __builtin_return_address(0);
83#endif
128 } 84 }
85
129 current->lock_depth = depth; 86 current->lock_depth = depth;
130} 87}
131 88
132void __lockfunc _unlock_kernel(const char *func, const char *file, int line) 89void __lockfunc _unlock_kernel(const char *func, const char *file, int line)
133{ 90{
134 BUG_ON(current->lock_depth < 0); 91 BUG_ON(current->lock_depth < 0);
135 if (likely(--current->lock_depth < 0))
136 __unlock_kernel();
137 92
93 if (likely(--current->lock_depth < 0)) {
94#ifdef CONFIG_DEBUG_RT_MUTEXES
95 current->last_kernel_lock = NULL;
96#endif
97 mutex_unlock(&kernel_sem);
98 }
138 trace_unlock_kernel(func, file, line); 99 trace_unlock_kernel(func, file, line);
139} 100}
140 101