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