From 6dcc24c8fc713189e042116ba0ca36f95b050de7 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 31 Jan 2006 16:34:26 -0800 Subject: [PATCH] --- yaml --- r: 19179 b: refs/heads/master c: 3fa97c9db4f6f93f41f7a40d08872dbfd8dc907e h: refs/heads/master i: 19177: 15081d85bdc337a3f4199411a12ba5ddb35a1b6b 19175: 198c1b8cf526f43f3af74dc0a917ee612d985002 v: v3 --- [refs] | 2 +- trunk/kernel/user.c | 27 +++++++++++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/[refs] b/[refs] index 16b0d45ce837..32e573c1e0f0 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: b8c475be7bf9b79e6417c08d7a921b2e8cb04258 +refs/heads/master: 3fa97c9db4f6f93f41f7a40d08872dbfd8dc907e diff --git a/trunk/kernel/user.c b/trunk/kernel/user.c index d1ae2349347e..d9deae43a9ab 100644 --- a/trunk/kernel/user.c +++ b/trunk/kernel/user.c @@ -33,6 +33,10 @@ 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. + * But free_uid() is also called with local interrupts disabled, and running + * local_bh_enable() with local interrupts disabled is an error - we'll run + * softirq callbacks, and they can unconditionally enable interrupts, and + * the caller of free_uid() didn't expect that.. */ static DEFINE_SPINLOCK(uidhash_lock); @@ -89,16 +93,19 @@ static inline struct user_struct *uid_hash_find(uid_t uid, struct list_head *has struct user_struct *find_user(uid_t uid) { struct user_struct *ret; + unsigned long flags; - spin_lock_bh(&uidhash_lock); + spin_lock_irqsave(&uidhash_lock, flags); ret = uid_hash_find(uid, uidhashentry(uid)); - spin_unlock_bh(&uidhash_lock); + spin_unlock_irqrestore(&uidhash_lock, flags); return ret; } void free_uid(struct user_struct *up) { - local_bh_disable(); + 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); @@ -106,7 +113,7 @@ void free_uid(struct user_struct *up) kmem_cache_free(uid_cachep, up); spin_unlock(&uidhash_lock); } - local_bh_enable(); + local_irq_restore(flags); } struct user_struct * alloc_uid(uid_t uid) @@ -114,9 +121,9 @@ struct user_struct * alloc_uid(uid_t uid) struct list_head *hashent = uidhashentry(uid); struct user_struct *up; - spin_lock_bh(&uidhash_lock); + spin_lock_irq(&uidhash_lock); up = uid_hash_find(uid, hashent); - spin_unlock_bh(&uidhash_lock); + spin_unlock_irq(&uidhash_lock); if (!up) { struct user_struct *new; @@ -146,7 +153,7 @@ struct user_struct * alloc_uid(uid_t uid) * Before adding this, check whether we raced * on adding the same user already.. */ - spin_lock_bh(&uidhash_lock); + spin_lock_irq(&uidhash_lock); up = uid_hash_find(uid, hashent); if (up) { key_put(new->uid_keyring); @@ -156,7 +163,7 @@ struct user_struct * alloc_uid(uid_t uid) uid_hash_insert(new, hashent); up = new; } - spin_unlock_bh(&uidhash_lock); + spin_unlock_irq(&uidhash_lock); } return up; @@ -192,9 +199,9 @@ static int __init uid_cache_init(void) INIT_LIST_HEAD(uidhash_table + n); /* Insert the root user immediately (init already runs as root) */ - spin_lock_bh(&uidhash_lock); + spin_lock_irq(&uidhash_lock); uid_hash_insert(&root_user, uidhashentry(0)); - spin_unlock_bh(&uidhash_lock); + spin_unlock_irq(&uidhash_lock); return 0; }