From: Shailabh Nagar Use irqsave variants of spin_lock for newly introduced tsk->signal->stats_lock The lock is nested within tasklist_lock as part of release_task() and hence there is possibility of a AB-BA deadlock if a timer interrupt occurs while stats_lock is held. Thanks to Arjan van de Ven for pointing out the lock bug (akpm: found by the locking validator). The patch conservatively converts all use of stats_lock to irqsave variant rather than only the call within taskstats_tgid_free which is the function called within tasklist_lock protection. Signed-off-by: Shailabh Nagar Cc: Arjan van de Ven Signed-off-by: Andrew Morton --- include/linux/taskstats_kern.h | 11 +++++++---- kernel/taskstats.c | 16 ++++++++++------ 2 files changed, 17 insertions(+), 10 deletions(-) diff -puN include/linux/taskstats_kern.h~delay-accounting-taskstats-interface-send-tgid-once-locking include/linux/taskstats_kern.h --- a/include/linux/taskstats_kern.h~delay-accounting-taskstats-interface-send-tgid-once-locking +++ a/include/linux/taskstats_kern.h @@ -39,17 +39,18 @@ static inline void taskstats_tgid_init(s static inline void taskstats_tgid_alloc(struct signal_struct *sig) { struct taskstats *stats; + unsigned long flags; stats = kmem_cache_zalloc(taskstats_cache, SLAB_KERNEL); if (!stats) return; - spin_lock(&sig->stats_lock); + spin_lock_irqsave(&sig->stats_lock, flags); if (!sig->stats) { sig->stats = stats; stats = NULL; } - spin_unlock(&sig->stats_lock); + spin_unlock_irqrestore(&sig->stats_lock, flags); if (stats) kmem_cache_free(taskstats_cache, stats); @@ -58,12 +59,14 @@ static inline void taskstats_tgid_alloc( static inline void taskstats_tgid_free(struct signal_struct *sig) { struct taskstats *stats = NULL; - spin_lock(&sig->stats_lock); + unsigned long flags; + + spin_lock_irqsave(&sig->stats_lock, flags); if (sig->stats) { stats = sig->stats; sig->stats = NULL; } - spin_unlock(&sig->stats_lock); + spin_unlock_irqrestore(&sig->stats_lock, flags); if (stats) kmem_cache_free(taskstats_cache, stats); } diff -puN kernel/taskstats.c~delay-accounting-taskstats-interface-send-tgid-once-locking kernel/taskstats.c --- a/kernel/taskstats.c~delay-accounting-taskstats-interface-send-tgid-once-locking +++ a/kernel/taskstats.c @@ -133,6 +133,7 @@ static int fill_tgid(pid_t tgid, struct struct taskstats *stats) { struct task_struct *tsk, *first; + unsigned long flags; /* * Add additional stats from live tasks except zombie thread group @@ -152,10 +153,10 @@ static int fill_tgid(pid_t tgid, struct get_task_struct(first); /* Start with stats from dead tasks */ - spin_lock(&first->signal->stats_lock); + spin_lock_irqsave(&first->signal->stats_lock, flags); if (first->signal->stats) memcpy(stats, first->signal->stats, sizeof(*stats)); - spin_unlock(&first->signal->stats_lock); + spin_unlock_irqrestore(&first->signal->stats_lock, flags); tsk = first; read_lock(&tasklist_lock); @@ -185,7 +186,9 @@ static int fill_tgid(pid_t tgid, struct static void fill_tgid_exit(struct task_struct *tsk) { - spin_lock(&tsk->signal->stats_lock); + unsigned long flags; + + spin_lock_irqsave(&tsk->signal->stats_lock, flags); if (!tsk->signal->stats) goto ret; @@ -197,7 +200,7 @@ static void fill_tgid_exit(struct task_s */ delayacct_add_tsk(tsk->signal->stats, tsk); ret: - spin_unlock(&tsk->signal->stats_lock); + spin_unlock_irqrestore(&tsk->signal->stats_lock, flags); return; } @@ -268,13 +271,14 @@ void taskstats_exit_send(struct task_str size_t size; int is_thread_group; struct nlattr *na; + unsigned long flags; if (!family_registered || !tidstats) return; - spin_lock(&tsk->signal->stats_lock); + spin_lock_irqsave(&tsk->signal->stats_lock, flags); is_thread_group = tsk->signal->stats ? 1 : 0; - spin_unlock(&tsk->signal->stats_lock); + spin_unlock_irqrestore(&tsk->signal->stats_lock, flags); rc = 0; /* _