diff options
| author | Rafael J. Wysocki <rjw@sisk.pl> | 2007-07-19 04:47:33 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-19 13:04:42 -0400 |
| commit | 0c1eecfb345401629aa57c9d3b077273e56c45a7 (patch) | |
| tree | 522d7090966c6e70f3147c30e3308781e0309938 | |
| parent | b1457bcc3a00a0446c7f6e2f22fd24b6d8d0a309 (diff) | |
Freezer: avoid freezing kernel threads prematurely
Kernel threads should not have TIF_FREEZE set when user space processes are
being frozen, since otherwise some of them might be frozen prematurely.
To prevent this from happening we can (1) make exit_mm() unset TIF_FREEZE
unconditionally just after clearing tsk->mm and (2) make try_to_freeze_tasks()
check if p->mm is different from zero and PF_BORROWED_MM is unset in p->flags
when user space processes are to be frozen.
Namely, when user space processes are being frozen, we only should set
TIF_FREEZE for tasks that have p->mm different from NULL and don't have
PF_BORROWED_MM set in p->flags. For this reason task_lock() must be used to
prevent try_to_freeze_tasks() from racing with use_mm()/unuse_mm(), in which
p->mm and p->flags.PF_BORROWED_MM are changed under task_lock(p). Also, we
need to prevent the following scenario from happening:
* daemonize() is called by a task spawned from a user space code path
* freezer checks if the task has p->mm set and the result is positive
* task enters exit_mm() and clears its TIF_FREEZE
* freezer sets TIF_FREEZE for the task
* task calls try_to_freeze() and goes to the refrigerator, which is wrong at
that point
This requires us to acquire task_lock(p) before p->flags.PF_BORROWED_MM and
p->mm are examined and release it after TIF_FREEZE is set for p (or it turns
out that TIF_FREEZE should not be set).
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Gautham R Shenoy <ego@in.ibm.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Nigel Cunningham <nigel@nigel.suspend2.net>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | include/linux/freezer.h | 9 | ||||
| -rw-r--r-- | kernel/exit.c | 3 | ||||
| -rw-r--r-- | kernel/power/process.c | 64 |
3 files changed, 41 insertions, 35 deletions
diff --git a/include/linux/freezer.h b/include/linux/freezer.h index 2d38b1a74662..c8e02de737f6 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h | |||
| @@ -25,7 +25,7 @@ static inline int freezing(struct task_struct *p) | |||
| 25 | /* | 25 | /* |
| 26 | * Request that a process be frozen | 26 | * Request that a process be frozen |
| 27 | */ | 27 | */ |
| 28 | static inline void freeze(struct task_struct *p) | 28 | static inline void set_freeze_flag(struct task_struct *p) |
| 29 | { | 29 | { |
| 30 | set_tsk_thread_flag(p, TIF_FREEZE); | 30 | set_tsk_thread_flag(p, TIF_FREEZE); |
| 31 | } | 31 | } |
| @@ -33,7 +33,7 @@ static inline void freeze(struct task_struct *p) | |||
| 33 | /* | 33 | /* |
| 34 | * Sometimes we may need to cancel the previous 'freeze' request | 34 | * Sometimes we may need to cancel the previous 'freeze' request |
| 35 | */ | 35 | */ |
| 36 | static inline void do_not_freeze(struct task_struct *p) | 36 | static inline void clear_freeze_flag(struct task_struct *p) |
| 37 | { | 37 | { |
| 38 | clear_tsk_thread_flag(p, TIF_FREEZE); | 38 | clear_tsk_thread_flag(p, TIF_FREEZE); |
| 39 | } | 39 | } |
| @@ -56,7 +56,7 @@ static inline int thaw_process(struct task_struct *p) | |||
| 56 | wake_up_process(p); | 56 | wake_up_process(p); |
| 57 | return 1; | 57 | return 1; |
| 58 | } | 58 | } |
| 59 | clear_tsk_thread_flag(p, TIF_FREEZE); | 59 | clear_freeze_flag(p); |
| 60 | task_unlock(p); | 60 | task_unlock(p); |
| 61 | return 0; | 61 | return 0; |
| 62 | } | 62 | } |
| @@ -129,7 +129,8 @@ static inline void set_freezable(void) | |||
| 129 | #else | 129 | #else |
| 130 | static inline int frozen(struct task_struct *p) { return 0; } | 130 | static inline int frozen(struct task_struct *p) { return 0; } |
| 131 | static inline int freezing(struct task_struct *p) { return 0; } | 131 | static inline int freezing(struct task_struct *p) { return 0; } |
| 132 | static inline void freeze(struct task_struct *p) { BUG(); } | 132 | static inline void set_freeze_flag(struct task_struct *p) {} |
| 133 | static inline void clear_freeze_flag(struct task_struct *p) {} | ||
| 133 | static inline int thaw_process(struct task_struct *p) { return 1; } | 134 | static inline int thaw_process(struct task_struct *p) { return 1; } |
| 134 | 135 | ||
| 135 | static inline void refrigerator(void) {} | 136 | static inline void refrigerator(void) {} |
diff --git a/kernel/exit.c b/kernel/exit.c index e8af8d0c2483..464c2b172f07 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
| @@ -45,6 +45,7 @@ | |||
| 45 | #include <linux/resource.h> | 45 | #include <linux/resource.h> |
| 46 | #include <linux/blkdev.h> | 46 | #include <linux/blkdev.h> |
| 47 | #include <linux/task_io_accounting_ops.h> | 47 | #include <linux/task_io_accounting_ops.h> |
| 48 | #include <linux/freezer.h> | ||
| 48 | 49 | ||
| 49 | #include <asm/uaccess.h> | 50 | #include <asm/uaccess.h> |
| 50 | #include <asm/unistd.h> | 51 | #include <asm/unistd.h> |
| @@ -594,6 +595,8 @@ static void exit_mm(struct task_struct * tsk) | |||
| 594 | tsk->mm = NULL; | 595 | tsk->mm = NULL; |
| 595 | up_read(&mm->mmap_sem); | 596 | up_read(&mm->mmap_sem); |
| 596 | enter_lazy_tlb(mm, current); | 597 | enter_lazy_tlb(mm, current); |
| 598 | /* We don't want this task to be frozen prematurely */ | ||
| 599 | clear_freeze_flag(tsk); | ||
| 597 | task_unlock(tsk); | 600 | task_unlock(tsk); |
| 598 | mmput(mm); | 601 | mmput(mm); |
| 599 | } | 602 | } |
diff --git a/kernel/power/process.c b/kernel/power/process.c index b850173e7561..e1bcdedd1464 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c | |||
| @@ -40,7 +40,7 @@ static inline void frozen_process(void) | |||
| 40 | current->flags |= PF_FROZEN; | 40 | current->flags |= PF_FROZEN; |
| 41 | wmb(); | 41 | wmb(); |
| 42 | } | 42 | } |
| 43 | clear_tsk_thread_flag(current, TIF_FREEZE); | 43 | clear_freeze_flag(current); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | /* Refrigerator is place where frozen processes are stored :-). */ | 46 | /* Refrigerator is place where frozen processes are stored :-). */ |
| @@ -75,17 +75,16 @@ void refrigerator(void) | |||
| 75 | current->state = save; | 75 | current->state = save; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | static inline void freeze_process(struct task_struct *p) | 78 | static void freeze_task(struct task_struct *p) |
| 79 | { | 79 | { |
| 80 | unsigned long flags; | 80 | unsigned long flags; |
| 81 | 81 | ||
| 82 | if (!freezing(p)) { | 82 | if (!freezing(p)) { |
| 83 | rmb(); | 83 | rmb(); |
| 84 | if (!frozen(p)) { | 84 | if (!frozen(p)) { |
| 85 | set_freeze_flag(p); | ||
| 85 | if (p->state == TASK_STOPPED) | 86 | if (p->state == TASK_STOPPED) |
| 86 | force_sig_specific(SIGSTOP, p); | 87 | force_sig_specific(SIGSTOP, p); |
| 87 | |||
| 88 | freeze(p); | ||
| 89 | spin_lock_irqsave(&p->sighand->siglock, flags); | 88 | spin_lock_irqsave(&p->sighand->siglock, flags); |
| 90 | signal_wake_up(p, p->state == TASK_STOPPED); | 89 | signal_wake_up(p, p->state == TASK_STOPPED); |
| 91 | spin_unlock_irqrestore(&p->sighand->siglock, flags); | 90 | spin_unlock_irqrestore(&p->sighand->siglock, flags); |
| @@ -99,18 +98,13 @@ static void cancel_freezing(struct task_struct *p) | |||
| 99 | 98 | ||
| 100 | if (freezing(p)) { | 99 | if (freezing(p)) { |
| 101 | pr_debug(" clean up: %s\n", p->comm); | 100 | pr_debug(" clean up: %s\n", p->comm); |
| 102 | do_not_freeze(p); | 101 | clear_freeze_flag(p); |
| 103 | spin_lock_irqsave(&p->sighand->siglock, flags); | 102 | spin_lock_irqsave(&p->sighand->siglock, flags); |
| 104 | recalc_sigpending_and_wake(p); | 103 | recalc_sigpending_and_wake(p); |
| 105 | spin_unlock_irqrestore(&p->sighand->siglock, flags); | 104 | spin_unlock_irqrestore(&p->sighand->siglock, flags); |
| 106 | } | 105 | } |
| 107 | } | 106 | } |
| 108 | 107 | ||
| 109 | static inline int is_user_space(struct task_struct *p) | ||
| 110 | { | ||
| 111 | return p->mm && !(p->flags & PF_BORROWED_MM); | ||
| 112 | } | ||
| 113 | |||
| 114 | static unsigned int try_to_freeze_tasks(int freeze_user_space) | 108 | static unsigned int try_to_freeze_tasks(int freeze_user_space) |
| 115 | { | 109 | { |
| 116 | struct task_struct *g, *p; | 110 | struct task_struct *g, *p; |
| @@ -122,20 +116,34 @@ static unsigned int try_to_freeze_tasks(int freeze_user_space) | |||
| 122 | todo = 0; | 116 | todo = 0; |
| 123 | read_lock(&tasklist_lock); | 117 | read_lock(&tasklist_lock); |
| 124 | do_each_thread(g, p) { | 118 | do_each_thread(g, p) { |
| 125 | if (!freezeable(p)) | 119 | if (frozen(p) || !freezeable(p)) |
| 126 | continue; | ||
| 127 | |||
| 128 | if (frozen(p)) | ||
| 129 | continue; | 120 | continue; |
| 130 | 121 | ||
| 131 | if (p->state == TASK_TRACED && frozen(p->parent)) { | 122 | if (freeze_user_space) { |
| 132 | cancel_freezing(p); | 123 | if (p->state == TASK_TRACED && |
| 133 | continue; | 124 | frozen(p->parent)) { |
| 125 | cancel_freezing(p); | ||
| 126 | continue; | ||
| 127 | } | ||
| 128 | /* | ||
| 129 | * Kernel threads should not have TIF_FREEZE set | ||
| 130 | * at this point, so we must ensure that either | ||
| 131 | * p->mm is not NULL *and* PF_BORROWED_MM is | ||
| 132 | * unset, or TIF_FRREZE is left unset. | ||
| 133 | * The task_lock() is necessary to prevent races | ||
| 134 | * with exit_mm() or use_mm()/unuse_mm() from | ||
| 135 | * occuring. | ||
| 136 | */ | ||
| 137 | task_lock(p); | ||
| 138 | if (!p->mm || (p->flags & PF_BORROWED_MM)) { | ||
| 139 | task_unlock(p); | ||
| 140 | continue; | ||
| 141 | } | ||
| 142 | freeze_task(p); | ||
| 143 | task_unlock(p); | ||
| 144 | } else { | ||
| 145 | freeze_task(p); | ||
| 134 | } | 146 | } |
| 135 | if (freeze_user_space && !is_user_space(p)) | ||
| 136 | continue; | ||
| 137 | |||
| 138 | freeze_process(p); | ||
| 139 | if (!freezer_should_skip(p)) | 147 | if (!freezer_should_skip(p)) |
| 140 | todo++; | 148 | todo++; |
| 141 | } while_each_thread(g, p); | 149 | } while_each_thread(g, p); |
| @@ -152,22 +160,16 @@ static unsigned int try_to_freeze_tasks(int freeze_user_space) | |||
| 152 | * but it cleans up leftover PF_FREEZE requests. | 160 | * but it cleans up leftover PF_FREEZE requests. |
| 153 | */ | 161 | */ |
| 154 | printk("\n"); | 162 | printk("\n"); |
| 155 | printk(KERN_ERR "Stopping %s timed out after %d seconds " | 163 | printk(KERN_ERR "Freezing of %s timed out after %d seconds " |
| 156 | "(%d tasks refusing to freeze):\n", | 164 | "(%d tasks refusing to freeze):\n", |
| 157 | freeze_user_space ? "user space processes" : | 165 | freeze_user_space ? "user space " : "tasks ", |
| 158 | "kernel threads", | ||
| 159 | TIMEOUT / HZ, todo); | 166 | TIMEOUT / HZ, todo); |
| 160 | show_state(); | 167 | show_state(); |
| 161 | read_lock(&tasklist_lock); | 168 | read_lock(&tasklist_lock); |
| 162 | do_each_thread(g, p) { | 169 | do_each_thread(g, p) { |
| 163 | if (freeze_user_space && !is_user_space(p)) | ||
| 164 | continue; | ||
| 165 | |||
| 166 | task_lock(p); | 170 | task_lock(p); |
| 167 | if (freezeable(p) && !frozen(p) && | 171 | if (freezing(p) && !freezer_should_skip(p)) |
| 168 | !freezer_should_skip(p)) | ||
| 169 | printk(KERN_ERR " %s\n", p->comm); | 172 | printk(KERN_ERR " %s\n", p->comm); |
| 170 | |||
| 171 | cancel_freezing(p); | 173 | cancel_freezing(p); |
| 172 | task_unlock(p); | 174 | task_unlock(p); |
| 173 | } while_each_thread(g, p); | 175 | } while_each_thread(g, p); |
| @@ -211,7 +213,7 @@ static void thaw_tasks(int thaw_user_space) | |||
| 211 | if (!freezeable(p)) | 213 | if (!freezeable(p)) |
| 212 | continue; | 214 | continue; |
| 213 | 215 | ||
| 214 | if (is_user_space(p) == !thaw_user_space) | 216 | if (!p->mm == thaw_user_space) |
| 215 | continue; | 217 | continue; |
| 216 | 218 | ||
| 217 | thaw_process(p); | 219 | thaw_process(p); |
