We have uncovered a very difficult to trip AB-BA deadlock between the
uidhash_lock and tasklist_lock.

reparent_to_init() does write_lock_irq(&tasklist_lock) then calls
switch_uid() which calls free_uid() which grabs the uidhash_lock.

Independent of that, we have seen a different cpu call free_uid as a
result of sys_wait4 and, immediately after acquiring the uidhash_lock,
receive a timer interrupt which eventually leads to an attempt to grab
the tasklist_lock.


Signed-off-by: Robin Holt <[EMAIL PROTECTED]>


Index: linux/kernel/user.c
===================================================================
--- linux.orig/kernel/user.c    2004-12-22 13:10:49.000000000 -0600
+++ linux/kernel/user.c 2004-12-23 11:07:21.100577562 -0600
@@ -90,6 +90,9 @@
 
 void free_uid(struct user_struct *up)
 {
+       unsigned long flags;
+
+       local_irq_save(flags);
        if (up && atomic_dec_and_lock(&up->__count, &uidhash_lock)) {
                uid_hash_remove(up);
                key_put(up->uid_keyring);
@@ -97,16 +100,18 @@
                kmem_cache_free(uid_cachep, up);
                spin_unlock(&uidhash_lock);
        }
+       local_irq_restore(flags);
 }
 
 struct user_struct * alloc_uid(uid_t uid)
 {
        struct list_head *hashent = uidhashentry(uid);
        struct user_struct *up;
+       unsigned long flags;
 
-       spin_lock(&uidhash_lock);
+       spin_lock_irqsave(&uidhash_lock, flags);
        up = uid_hash_find(uid, hashent);
-       spin_unlock(&uidhash_lock);
+       spin_unlock_irqrestore(&uidhash_lock, flags);
 
        if (!up) {
                struct user_struct *new;
@@ -132,7 +137,7 @@
                 * Before adding this, check whether we raced
                 * on adding the same user already..
                 */
-               spin_lock(&uidhash_lock);
+               spin_lock_irqsave(&uidhash_lock, flags);
                up = uid_hash_find(uid, hashent);
                if (up) {
                        key_put(new->uid_keyring);
@@ -142,7 +147,7 @@
                        uid_hash_insert(new, hashent);
                        up = new;
                }
-               spin_unlock(&uidhash_lock);
+               spin_unlock_irqrestore(&uidhash_lock, flags);
 
        }
        return up;
@@ -170,6 +175,7 @@
 static int __init uid_cache_init(void)
 {
        int n;
+       unsigned long flags;
 
        uid_cachep = kmem_cache_create("uid_cache", sizeof(struct user_struct),
                        0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
@@ -178,9 +184,9 @@
                INIT_LIST_HEAD(uidhash_table + n);
 
        /* Insert the root user immediately (init already runs as root) */
-       spin_lock(&uidhash_lock);
+       spin_lock_irqsave(&uidhash_lock, flags);
        uid_hash_insert(&root_user, uidhashentry(0));
-       spin_unlock(&uidhash_lock);
+       spin_unlock_irqrestore(&uidhash_lock, flags);
 
        return 0;
 }
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to