#include <linux/slab.h>
 #include <linux/bitops.h>
 #include <linux/key.h>
+#include <linux/interrupt.h>
 
 /*
  * UID task count cache, to get fast user lookup in "alloc_uid"
 
 static kmem_cache_t *uid_cachep;
 static struct list_head uidhash_table[UIDHASH_SZ];
+
+/*
+ * The uidhash_lock is mostly taken from process context, but it is
+ * occasionally also taken from softirq/tasklet context, when
+ * task-structs get RCU-freed. Hence all locking must be softirq-safe.
+ */
 static DEFINE_SPINLOCK(uidhash_lock);
 
 struct user_struct root_user = {
 {
        struct user_struct *ret;
 
-       spin_lock(&uidhash_lock);
+       spin_lock_bh(&uidhash_lock);
        ret = uid_hash_find(uid, uidhashentry(uid));
-       spin_unlock(&uidhash_lock);
+       spin_unlock_bh(&uidhash_lock);
        return ret;
 }
 
 void free_uid(struct user_struct *up)
 {
+       local_bh_disable();
        if (up && atomic_dec_and_lock(&up->__count, &uidhash_lock)) {
                uid_hash_remove(up);
                key_put(up->uid_keyring);
                kmem_cache_free(uid_cachep, up);
                spin_unlock(&uidhash_lock);
        }
+       local_bh_enable();
 }
 
 struct user_struct * alloc_uid(uid_t uid)
        struct list_head *hashent = uidhashentry(uid);
        struct user_struct *up;
 
-       spin_lock(&uidhash_lock);
+       spin_lock_bh(&uidhash_lock);
        up = uid_hash_find(uid, hashent);
-       spin_unlock(&uidhash_lock);
+       spin_unlock_bh(&uidhash_lock);
 
        if (!up) {
                struct user_struct *new;
                 * Before adding this, check whether we raced
                 * on adding the same user already..
                 */
-               spin_lock(&uidhash_lock);
+               spin_lock_bh(&uidhash_lock);
                up = uid_hash_find(uid, hashent);
                if (up) {
                        key_put(new->uid_keyring);
                        uid_hash_insert(new, hashent);
                        up = new;
                }
-               spin_unlock(&uidhash_lock);
+               spin_unlock_bh(&uidhash_lock);
 
        }
        return up;
                INIT_LIST_HEAD(uidhash_table + n);
 
        /* Insert the root user immediately (init already runs as root) */
-       spin_lock(&uidhash_lock);
+       spin_lock_bh(&uidhash_lock);
        uid_hash_insert(&root_user, uidhashentry(0));
-       spin_unlock(&uidhash_lock);
+       spin_unlock_bh(&uidhash_lock);
 
        return 0;
 }