linux-bk/lib/kernel_lock.c
<<
>>
Prefs
   1/*
   2 * lib/kernel_lock.c
   3 *
   4 * This is the traditional BKL - big kernel lock. Largely
   5 * relegated to obsolescense, but used by various less
   6 * important (or lazy) subsystems.
   7 */
   8#include <linux/smp_lock.h>
   9#include <linux/module.h>
  10#include <linux/kallsyms.h>
  11
  12#if defined(CONFIG_PREEMPT) && defined(__smp_processor_id) && \
  13                defined(CONFIG_DEBUG_PREEMPT)
  14
  15/*
  16 * Debugging check.
  17 */
  18unsigned int smp_processor_id(void)
  19{
  20        unsigned long preempt_count = preempt_count();
  21        int this_cpu = __smp_processor_id();
  22        cpumask_t this_mask;
  23
  24        if (likely(preempt_count))
  25                goto out;
  26
  27        if (irqs_disabled())
  28                goto out;
  29
  30        /*
  31         * Kernel threads bound to a single CPU can safely use
  32         * smp_processor_id():
  33         */
  34        this_mask = cpumask_of_cpu(this_cpu);
  35
  36        if (cpus_equal(current->cpus_allowed, this_mask))
  37                goto out;
  38
  39        /*
  40         * It is valid to assume CPU-locality during early bootup:
  41         */
  42        if (system_state != SYSTEM_RUNNING)
  43                goto out;
  44
  45        /*
  46         * Avoid recursion:
  47         */
  48        preempt_disable();
  49
  50        if (!printk_ratelimit())
  51                goto out_enable;
  52
  53        printk(KERN_ERR "BUG: using smp_processor_id() in preemptible [%08x] code: %s/%d\n", preempt_count(), current->comm, current->pid);
  54        print_symbol("caller is %s\n", (long)__builtin_return_address(0));
  55        dump_stack();
  56
  57out_enable:
  58        preempt_enable_no_resched();
  59out:
  60        return this_cpu;
  61}
  62
  63EXPORT_SYMBOL(smp_processor_id);
  64
  65#endif /* PREEMPT && __smp_processor_id && DEBUG_PREEMPT */
  66
  67#ifdef CONFIG_PREEMPT_BKL
  68/*
  69 * The 'big kernel semaphore'
  70 *
  71 * This mutex is taken and released recursively by lock_kernel()
  72 * and unlock_kernel().  It is transparently dropped and reaquired
  73 * over schedule().  It is used to protect legacy code that hasn't
  74 * been migrated to a proper locking design yet.
  75 *
  76 * Note: code locked by this semaphore will only be serialized against
  77 * other code using the same locking facility. The code guarantees that
  78 * the task remains on the same CPU.
  79 *
  80 * Don't use in new code.
  81 */
  82DECLARE_MUTEX(kernel_sem);
  83
  84/*
  85 * Re-acquire the kernel semaphore.
  86 *
  87 * This function is called with preemption off.
  88 *
  89 * We are executing in schedule() so the code must be extremely careful
  90 * about recursion, both due to the down() and due to the enabling of
  91 * preemption. schedule() will re-check the preemption flag after
  92 * reacquiring the semaphore.
  93 */
  94int __lockfunc __reacquire_kernel_lock(void)
  95{
  96        struct task_struct *task = current;
  97        int saved_lock_depth = task->lock_depth;
  98
  99        BUG_ON(saved_lock_depth < 0);
 100
 101        task->lock_depth = -1;
 102        preempt_enable_no_resched();
 103
 104        down(&kernel_sem);
 105
 106        preempt_disable();
 107        task->lock_depth = saved_lock_depth;
 108
 109        return 0;
 110}
 111
 112void __lockfunc __release_kernel_lock(void)
 113{
 114        up(&kernel_sem);
 115}
 116
 117/*
 118 * Getting the big kernel semaphore.
 119 */
 120void __lockfunc lock_kernel(void)
 121{
 122        struct task_struct *task = current;
 123        int depth = task->lock_depth + 1;
 124
 125        if (likely(!depth))
 126                /*
 127                 * No recursion worries - we set up lock_depth _after_
 128                 */
 129                down(&kernel_sem);
 130
 131        task->lock_depth = depth;
 132}
 133
 134void __lockfunc unlock_kernel(void)
 135{
 136        struct task_struct *task = current;
 137
 138        BUG_ON(task->lock_depth < 0);
 139
 140        if (likely(--task->lock_depth < 0))
 141                up(&kernel_sem);
 142}
 143
 144#else
 145
 146/*
 147 * The 'big kernel lock'
 148 *
 149 * This spinlock is taken and released recursively by lock_kernel()
 150 * and unlock_kernel().  It is transparently dropped and reaquired
 151 * over schedule().  It is used to protect legacy code that hasn't
 152 * been migrated to a proper locking design yet.
 153 *
 154 * Don't use in new code.
 155 */
 156static  __cacheline_aligned_in_smp DEFINE_SPINLOCK(kernel_flag);
 157
 158
 159/*
 160 * Acquire/release the underlying lock from the scheduler.
 161 *
 162 * This is called with preemption disabled, and should
 163 * return an error value if it cannot get the lock and
 164 * TIF_NEED_RESCHED gets set.
 165 *
 166 * If it successfully gets the lock, it should increment
 167 * the preemption count like any spinlock does.
 168 *
 169 * (This works on UP too - _raw_spin_trylock will never
 170 * return false in that case)
 171 */
 172int __lockfunc __reacquire_kernel_lock(void)
 173{
 174        while (!_raw_spin_trylock(&kernel_flag)) {
 175                if (test_thread_flag(TIF_NEED_RESCHED))
 176                        return -EAGAIN;
 177                cpu_relax();
 178        }
 179        preempt_disable();
 180        return 0;
 181}
 182
 183void __lockfunc __release_kernel_lock(void)
 184{
 185        _raw_spin_unlock(&kernel_flag);
 186        preempt_enable_no_resched();
 187}
 188
 189/*
 190 * These are the BKL spinlocks - we try to be polite about preemption. 
 191 * If SMP is not on (ie UP preemption), this all goes away because the
 192 * _raw_spin_trylock() will always succeed.
 193 */
 194#ifdef CONFIG_PREEMPT
 195static inline void __lock_kernel(void)
 196{
 197        preempt_disable();
 198        if (unlikely(!_raw_spin_trylock(&kernel_flag))) {
 199                /*
 200                 * If preemption was disabled even before this
 201                 * was called, there's nothing we can be polite
 202                 * about - just spin.
 203                 */
 204                if (preempt_count() > 1) {
 205                        _raw_spin_lock(&kernel_flag);
 206                        return;
 207                }
 208
 209                /*
 210                 * Otherwise, let's wait for the kernel lock
 211                 * with preemption enabled..
 212                 */
 213                do {
 214                        preempt_enable();
 215                        while (spin_is_locked(&kernel_flag))
 216                                cpu_relax();
 217                        preempt_disable();
 218                } while (!_raw_spin_trylock(&kernel_flag));
 219        }
 220}
 221
 222#else
 223
 224/*
 225 * Non-preemption case - just get the spinlock
 226 */
 227static inline void __lock_kernel(void)
 228{
 229        _raw_spin_lock(&kernel_flag);
 230}
 231#endif
 232
 233static inline void __unlock_kernel(void)
 234{
 235        _raw_spin_unlock(&kernel_flag);
 236        preempt_enable();
 237}
 238
 239/*
 240 * Getting the big kernel lock.
 241 *
 242 * This cannot happen asynchronously, so we only need to
 243 * worry about other CPU's.
 244 */
 245void __lockfunc lock_kernel(void)
 246{
 247        int depth = current->lock_depth+1;
 248        if (likely(!depth))
 249                __lock_kernel();
 250        current->lock_depth = depth;
 251}
 252
 253void __lockfunc unlock_kernel(void)
 254{
 255        BUG_ON(current->lock_depth < 0);
 256        if (likely(--current->lock_depth < 0))
 257                __unlock_kernel();
 258}
 259
 260#endif
 261
 262EXPORT_SYMBOL(lock_kernel);
 263EXPORT_SYMBOL(unlock_kernel);
 264
 265
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.