Merge branch 'sched/urgent' into sched/core to pick up fixes
[linux-2.6-block.git] / kernel / locking / lockdep.c
index ed9410936a220632995d40006f024357d5abe38a..68bc6a654ca309429012130015f71b0c87009a5a 100644 (file)
@@ -45,6 +45,7 @@
 #include <linux/bitops.h>
 #include <linux/gfp.h>
 #include <linux/kmemcheck.h>
+#include <linux/random.h>
 
 #include <asm/sections.h>
 
@@ -2176,15 +2177,37 @@ cache_hit:
        chain->irq_context = hlock->irq_context;
        i = get_first_held_lock(curr, hlock);
        chain->depth = curr->lockdep_depth + 1 - i;
+
+       BUILD_BUG_ON((1UL << 24) <= ARRAY_SIZE(chain_hlocks));
+       BUILD_BUG_ON((1UL << 6)  <= ARRAY_SIZE(curr->held_locks));
+       BUILD_BUG_ON((1UL << 8*sizeof(chain_hlocks[0])) <= ARRAY_SIZE(lock_classes));
+
        if (likely(nr_chain_hlocks + chain->depth <= MAX_LOCKDEP_CHAIN_HLOCKS)) {
                chain->base = nr_chain_hlocks;
-               nr_chain_hlocks += chain->depth;
                for (j = 0; j < chain->depth - 1; j++, i++) {
                        int lock_id = curr->held_locks[i].class_idx - 1;
                        chain_hlocks[chain->base + j] = lock_id;
                }
                chain_hlocks[chain->base + j] = class - lock_classes;
        }
+
+       if (nr_chain_hlocks < MAX_LOCKDEP_CHAIN_HLOCKS)
+               nr_chain_hlocks += chain->depth;
+
+#ifdef CONFIG_DEBUG_LOCKDEP
+       /*
+        * Important for check_no_collision().
+        */
+       if (unlikely(nr_chain_hlocks > MAX_LOCKDEP_CHAIN_HLOCKS)) {
+               if (debug_locks_off_graph_unlock())
+                       return 0;
+
+               print_lockdep_off("BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low!");
+               dump_stack();
+               return 0;
+       }
+#endif
+
        hlist_add_head_rcu(&chain->entry, hash_head);
        debug_atomic_inc(chain_lookup_misses);
        inc_chains();
@@ -2932,6 +2955,11 @@ static int mark_irqflags(struct task_struct *curr, struct held_lock *hlock)
        return 1;
 }
 
+static inline unsigned int task_irq_context(struct task_struct *task)
+{
+       return 2 * !!task->hardirq_context + !!task->softirq_context;
+}
+
 static int separate_irq_context(struct task_struct *curr,
                struct held_lock *hlock)
 {
@@ -2940,8 +2968,6 @@ static int separate_irq_context(struct task_struct *curr,
        /*
         * Keep track of points where we cross into an interrupt context:
         */
-       hlock->irq_context = 2*(curr->hardirq_context ? 1 : 0) +
-                               curr->softirq_context;
        if (depth) {
                struct held_lock *prev_hlock;
 
@@ -2973,6 +2999,11 @@ static inline int mark_irqflags(struct task_struct *curr,
        return 1;
 }
 
+static inline unsigned int task_irq_context(struct task_struct *task)
+{
+       return 0;
+}
+
 static inline int separate_irq_context(struct task_struct *curr,
                struct held_lock *hlock)
 {
@@ -3241,6 +3272,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
        hlock->acquire_ip = ip;
        hlock->instance = lock;
        hlock->nest_lock = nest_lock;
+       hlock->irq_context = task_irq_context(curr);
        hlock->trylock = trylock;
        hlock->read = read;
        hlock->check = check;
@@ -3554,7 +3586,35 @@ static int __lock_is_held(struct lockdep_map *lock)
        return 0;
 }
 
-static void __lock_pin_lock(struct lockdep_map *lock)
+static struct pin_cookie __lock_pin_lock(struct lockdep_map *lock)
+{
+       struct pin_cookie cookie = NIL_COOKIE;
+       struct task_struct *curr = current;
+       int i;
+
+       if (unlikely(!debug_locks))
+               return cookie;
+
+       for (i = 0; i < curr->lockdep_depth; i++) {
+               struct held_lock *hlock = curr->held_locks + i;
+
+               if (match_held_lock(hlock, lock)) {
+                       /*
+                        * Grab 16bits of randomness; this is sufficient to not
+                        * be guessable and still allows some pin nesting in
+                        * our u32 pin_count.
+                        */
+                       cookie.val = 1 + (prandom_u32() >> 16);
+                       hlock->pin_count += cookie.val;
+                       return cookie;
+               }
+       }
+
+       WARN(1, "pinning an unheld lock\n");
+       return cookie;
+}
+
+static void __lock_repin_lock(struct lockdep_map *lock, struct pin_cookie cookie)
 {
        struct task_struct *curr = current;
        int i;
@@ -3566,7 +3626,7 @@ static void __lock_pin_lock(struct lockdep_map *lock)
                struct held_lock *hlock = curr->held_locks + i;
 
                if (match_held_lock(hlock, lock)) {
-                       hlock->pin_count++;
+                       hlock->pin_count += cookie.val;
                        return;
                }
        }
@@ -3574,7 +3634,7 @@ static void __lock_pin_lock(struct lockdep_map *lock)
        WARN(1, "pinning an unheld lock\n");
 }
 
-static void __lock_unpin_lock(struct lockdep_map *lock)
+static void __lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie cookie)
 {
        struct task_struct *curr = current;
        int i;
@@ -3589,7 +3649,11 @@ static void __lock_unpin_lock(struct lockdep_map *lock)
                        if (WARN(!hlock->pin_count, "unpinning an unpinned lock\n"))
                                return;
 
-                       hlock->pin_count--;
+                       hlock->pin_count -= cookie.val;
+
+                       if (WARN((int)hlock->pin_count < 0, "pin count corrupted\n"))
+                               hlock->pin_count = 0;
+
                        return;
                }
        }
@@ -3720,24 +3784,44 @@ int lock_is_held(struct lockdep_map *lock)
 }
 EXPORT_SYMBOL_GPL(lock_is_held);
 
-void lock_pin_lock(struct lockdep_map *lock)
+struct pin_cookie lock_pin_lock(struct lockdep_map *lock)
 {
+       struct pin_cookie cookie = NIL_COOKIE;
        unsigned long flags;
 
        if (unlikely(current->lockdep_recursion))
-               return;
+               return cookie;
 
        raw_local_irq_save(flags);
        check_flags(flags);
 
        current->lockdep_recursion = 1;
-       __lock_pin_lock(lock);
+       cookie = __lock_pin_lock(lock);
        current->lockdep_recursion = 0;
        raw_local_irq_restore(flags);
+
+       return cookie;
 }
 EXPORT_SYMBOL_GPL(lock_pin_lock);
 
-void lock_unpin_lock(struct lockdep_map *lock)
+void lock_repin_lock(struct lockdep_map *lock, struct pin_cookie cookie)
+{
+       unsigned long flags;
+
+       if (unlikely(current->lockdep_recursion))
+               return;
+
+       raw_local_irq_save(flags);
+       check_flags(flags);
+
+       current->lockdep_recursion = 1;
+       __lock_repin_lock(lock, cookie);
+       current->lockdep_recursion = 0;
+       raw_local_irq_restore(flags);
+}
+EXPORT_SYMBOL_GPL(lock_repin_lock);
+
+void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie cookie)
 {
        unsigned long flags;
 
@@ -3748,7 +3832,7 @@ void lock_unpin_lock(struct lockdep_map *lock)
        check_flags(flags);
 
        current->lockdep_recursion = 1;
-       __lock_unpin_lock(lock);
+       __lock_unpin_lock(lock, cookie);
        current->lockdep_recursion = 0;
        raw_local_irq_restore(flags);
 }