1. 14 1月, 2011 2 次提交
    • R
      kernel: fix hlist_bl again · 32385c7c
      Russell King 提交于
      __d_rehash is dereferencing an almost-NULL pointer on my ARM926.
      CONFIG_SMP=n and CONFIG_DEBUG_SPINLOCK=y.
      
      The faulting instruction is:    strne   r3, [r2, #4]
      and as can be seen from the register dump below, r2 is 0x00000001, hence
      the faulting 0x00000005 address.
      
      __d_rehash is essentially:
      
             spin_lock_bucket(b);
             entry->d_flags &= ~DCACHE_UNHASHED;
             hlist_bl_add_head_rcu(&entry->d_hash, &b->head);
             spin_unlock_bucket(b);
      
      which is:
      
             bit_spin_lock(0, (unsigned long *)&b->head.first);
             entry->d_flags &= ~DCACHE_UNHASHED;
             hlist_bl_add_head_rcu(&entry->d_hash, &b->head);
             __bit_spin_unlock(0, (unsigned long *)&b->head.first);
      
      bit_spin_lock(0, ptr) sets bit 0 of *ptr, in this case b->head.first if
      CONFIG_SMP or CONFIG_DEBUG_SPINLOCK is set:
      
      #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
             while (unlikely(test_and_set_bit_lock(bitnum, addr))) {
                     while (test_bit(bitnum, addr)) {
                             preempt_enable();
                             cpu_relax();
                             preempt_disable();
                     }
             }
      #endif
      
      So, b->head.first starts off NULL, and becomes a non-NULL (address 1).
      hlist_bl_add_head_rcu() does this:
      
      static inline void hlist_bl_add_head_rcu(struct hlist_bl_node *n,
                                             struct hlist_bl_head *h)
      {
             first = hlist_bl_first(h);
             n->next = first;
             if (first)
                     first->pprev = &n->next;
      
      It is the store to first->pprev which is faulting.
      
      hlist_bl_first():
      
      static inline struct hlist_bl_node *hlist_bl_first(struct hlist_bl_head *h)
      {
             return (struct hlist_bl_node *)
                     ((unsigned long)h->first & ~LIST_BL_LOCKMASK);
      }
      
      but:
      #if defined(CONFIG_SMP)
      #define LIST_BL_LOCKMASK        1UL
      #else
      #define LIST_BL_LOCKMASK        0UL
      #endif
      
      So, we have one piece of code which sets bit 0 of addresses, and another
      bit of code which doesn't clear it before dereferencing the pointer if
      !CONFIG_SMP && CONFIG_DEBUG_SPINLOCK.  With the patch below, I can again
      sucessfully boot the kernel on my Versatile PB/926 platform.
      Signed-off-by: NRussell King <rmk+kernel@arm.linux.org.uk>
      32385c7c
    • N
      fs: hlist UP debug fixup · 2c675598
      Nick Piggin 提交于
      Po-Yu Chuang <ratbert.chuang@gmail.com> noticed that hlist_bl_set_first could
      crash on a UP system when LIST_BL_LOCKMASK is 0, because
      
      	LIST_BL_BUG_ON(!((unsigned long)h->first & LIST_BL_LOCKMASK));
      
      always evaulates to true.
      
      Fix the expression, and also avoid a dependency between bit spinlock
      implementation and list bl code (list code shouldn't know anything
      except that bit 0 is set when adding and removing elements). Eventually
      if a good use case comes up, we might use this list to store 1 or more
      arbitrary bits of data, so it really shouldn't be tied to locking either,
      but for now they are helpful for debugging.
      Signed-off-by: NNick Piggin <npiggin@kernel.dk>
      2c675598
  2. 07 1月, 2011 1 次提交