diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2007-10-18 06:04:46 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-18 17:37:19 -0400 |
commit | d5d8c5976d6adeddb8208c240460411e2198b393 (patch) | |
tree | bc7ee9762366c3790f0c0c84e8de73487b5ef261 | |
parent | e42837bcd35b75bb59ae5d3e62f87be1aeeb05c3 (diff) |
freezer: do not send signals to kernel threads
The freezer should not send signals to kernel threads, since that may lead to
subtle problems. In particular, commit
b74d0deb968e1f85942f17080eace015ce3c332c has changed recalc_sigpending_tsk()
so that it doesn't clear TIF_SIGPENDING. For this reason, if the freezer
continues to send fake signals to kernel threads and the freezing of kernel
threads fails, some of them may be running with TIF_SIGPENDING set forever.
Accordingly, recalc_sigpending_tsk() shouldn't set the task's TIF_SIGPENDING
flag if TIF_FREEZE is set.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Cc: Nigel Cunningham <nigel@nigel.suspend2.net>
Cc: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | Documentation/power/freezing-of-tasks.txt | 31 | ||||
-rw-r--r-- | kernel/power/process.c | 107 | ||||
-rw-r--r-- | kernel/signal.c | 1 |
3 files changed, 93 insertions, 46 deletions
diff --git a/Documentation/power/freezing-of-tasks.txt b/Documentation/power/freezing-of-tasks.txt index d5c65e8d6a37..38b57248fd61 100644 --- a/Documentation/power/freezing-of-tasks.txt +++ b/Documentation/power/freezing-of-tasks.txt | |||
@@ -19,12 +19,13 @@ we only consider hibernation, but the description also applies to suspend). | |||
19 | Namely, as the first step of the hibernation procedure the function | 19 | Namely, as the first step of the hibernation procedure the function |
20 | freeze_processes() (defined in kernel/power/process.c) is called. It executes | 20 | freeze_processes() (defined in kernel/power/process.c) is called. It executes |
21 | try_to_freeze_tasks() that sets TIF_FREEZE for all of the freezable tasks and | 21 | try_to_freeze_tasks() that sets TIF_FREEZE for all of the freezable tasks and |
22 | sends a fake signal to each of them. A task that receives such a signal and has | 22 | either wakes them up, if they are kernel threads, or sends fake signals to them, |
23 | TIF_FREEZE set, should react to it by calling the refrigerator() function | 23 | if they are user space processes. A task that has TIF_FREEZE set, should react |
24 | (defined in kernel/power/process.c), which sets the task's PF_FROZEN flag, | 24 | to it by calling the function called refrigerator() (defined in |
25 | changes its state to TASK_UNINTERRUPTIBLE and makes it loop until PF_FROZEN is | 25 | kernel/power/process.c), which sets the task's PF_FROZEN flag, changes its state |
26 | cleared for it. Then, we say that the task is 'frozen' and therefore the set of | 26 | to TASK_UNINTERRUPTIBLE and makes it loop until PF_FROZEN is cleared for it. |
27 | functions handling this mechanism is called 'the freezer' (these functions are | 27 | Then, we say that the task is 'frozen' and therefore the set of functions |
28 | handling this mechanism is referred to as 'the freezer' (these functions are | ||
28 | defined in kernel/power/process.c and include/linux/freezer.h). User space | 29 | defined in kernel/power/process.c and include/linux/freezer.h). User space |
29 | processes are generally frozen before kernel threads. | 30 | processes are generally frozen before kernel threads. |
30 | 31 | ||
@@ -35,21 +36,27 @@ task enter refrigerator() if the flag is set. | |||
35 | 36 | ||
36 | For user space processes try_to_freeze() is called automatically from the | 37 | For user space processes try_to_freeze() is called automatically from the |
37 | signal-handling code, but the freezable kernel threads need to call it | 38 | signal-handling code, but the freezable kernel threads need to call it |
38 | explicitly in suitable places. The code to do this may look like the following: | 39 | explicitly in suitable places or use the wait_event_freezable() or |
40 | wait_event_freezable_timeout() macros (defined in include/linux/freezer.h) | ||
41 | that combine interruptible sleep with checking if TIF_FREEZE is set and calling | ||
42 | try_to_freeze(). The main loop of a freezable kernel thread may look like the | ||
43 | following one: | ||
39 | 44 | ||
45 | set_freezable(); | ||
40 | do { | 46 | do { |
41 | hub_events(); | 47 | hub_events(); |
42 | wait_event_interruptible(khubd_wait, | 48 | wait_event_freezable(khubd_wait, |
43 | !list_empty(&hub_event_list)); | 49 | !list_empty(&hub_event_list) || |
44 | try_to_freeze(); | 50 | kthread_should_stop()); |
45 | } while (!signal_pending(current)); | 51 | } while (!kthread_should_stop() || !list_empty(&hub_event_list)); |
46 | 52 | ||
47 | (from drivers/usb/core/hub.c::hub_thread()). | 53 | (from drivers/usb/core/hub.c::hub_thread()). |
48 | 54 | ||
49 | If a freezable kernel thread fails to call try_to_freeze() after the freezer has | 55 | If a freezable kernel thread fails to call try_to_freeze() after the freezer has |
50 | set TIF_FREEZE for it, the freezing of tasks will fail and the entire | 56 | set TIF_FREEZE for it, the freezing of tasks will fail and the entire |
51 | hibernation operation will be cancelled. For this reason, freezable kernel | 57 | hibernation operation will be cancelled. For this reason, freezable kernel |
52 | threads must call try_to_freeze() somewhere. | 58 | threads must call try_to_freeze() somewhere or use one of the |
59 | wait_event_freezable() and wait_event_freezable_timeout() macros. | ||
53 | 60 | ||
54 | After the system memory state has been restored from a hibernation image and | 61 | After the system memory state has been restored from a hibernation image and |
55 | devices have been reinitialized, the function thaw_processes() is called in | 62 | devices have been reinitialized, the function thaw_processes() is called in |
diff --git a/kernel/power/process.c b/kernel/power/process.c index dba2f3acb4f8..4da125ee533d 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c | |||
@@ -75,21 +75,79 @@ void refrigerator(void) | |||
75 | __set_current_state(save); | 75 | __set_current_state(save); |
76 | } | 76 | } |
77 | 77 | ||
78 | static void freeze_task(struct task_struct *p) | 78 | static void fake_signal_wake_up(struct task_struct *p, int resume) |
79 | { | 79 | { |
80 | unsigned long flags; | 80 | unsigned long flags; |
81 | 81 | ||
82 | if (!freezing(p)) { | 82 | spin_lock_irqsave(&p->sighand->siglock, flags); |
83 | signal_wake_up(p, resume); | ||
84 | spin_unlock_irqrestore(&p->sighand->siglock, flags); | ||
85 | } | ||
86 | |||
87 | static void send_fake_signal(struct task_struct *p) | ||
88 | { | ||
89 | if (p->state == TASK_STOPPED) | ||
90 | force_sig_specific(SIGSTOP, p); | ||
91 | fake_signal_wake_up(p, p->state == TASK_STOPPED); | ||
92 | } | ||
93 | |||
94 | static int has_mm(struct task_struct *p) | ||
95 | { | ||
96 | return (p->mm && !(p->flags & PF_BORROWED_MM)); | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * freeze_task - send a freeze request to given task | ||
101 | * @p: task to send the request to | ||
102 | * @with_mm_only: if set, the request will only be sent if the task has its | ||
103 | * own mm | ||
104 | * Return value: 0, if @with_mm_only is set and the task has no mm of its | ||
105 | * own or the task is frozen, 1, otherwise | ||
106 | * | ||
107 | * The freeze request is sent by seting the tasks's TIF_FREEZE flag and | ||
108 | * either sending a fake signal to it or waking it up, depending on whether | ||
109 | * or not it has its own mm (ie. it is a user land task). If @with_mm_only | ||
110 | * is set and the task has no mm of its own (ie. it is a kernel thread), | ||
111 | * its TIF_FREEZE flag should not be set. | ||
112 | * | ||
113 | * The task_lock() is necessary to prevent races with exit_mm() or | ||
114 | * use_mm()/unuse_mm() from occuring. | ||
115 | */ | ||
116 | static int freeze_task(struct task_struct *p, int with_mm_only) | ||
117 | { | ||
118 | int ret = 1; | ||
119 | |||
120 | task_lock(p); | ||
121 | if (freezing(p)) { | ||
122 | if (has_mm(p)) { | ||
123 | if (!signal_pending(p)) | ||
124 | fake_signal_wake_up(p, 0); | ||
125 | } else { | ||
126 | if (with_mm_only) | ||
127 | ret = 0; | ||
128 | else | ||
129 | wake_up_state(p, TASK_INTERRUPTIBLE); | ||
130 | } | ||
131 | } else { | ||
83 | rmb(); | 132 | rmb(); |
84 | if (!frozen(p)) { | 133 | if (frozen(p)) { |
85 | set_freeze_flag(p); | 134 | ret = 0; |
86 | if (p->state == TASK_STOPPED) | 135 | } else { |
87 | force_sig_specific(SIGSTOP, p); | 136 | if (has_mm(p)) { |
88 | spin_lock_irqsave(&p->sighand->siglock, flags); | 137 | set_freeze_flag(p); |
89 | signal_wake_up(p, p->state == TASK_STOPPED); | 138 | send_fake_signal(p); |
90 | spin_unlock_irqrestore(&p->sighand->siglock, flags); | 139 | } else { |
140 | if (with_mm_only) { | ||
141 | ret = 0; | ||
142 | } else { | ||
143 | set_freeze_flag(p); | ||
144 | wake_up_state(p, TASK_INTERRUPTIBLE); | ||
145 | } | ||
146 | } | ||
91 | } | 147 | } |
92 | } | 148 | } |
149 | task_unlock(p); | ||
150 | return ret; | ||
93 | } | 151 | } |
94 | 152 | ||
95 | static void cancel_freezing(struct task_struct *p) | 153 | static void cancel_freezing(struct task_struct *p) |
@@ -119,31 +177,14 @@ static int try_to_freeze_tasks(int freeze_user_space) | |||
119 | if (frozen(p) || !freezeable(p)) | 177 | if (frozen(p) || !freezeable(p)) |
120 | continue; | 178 | continue; |
121 | 179 | ||
122 | if (freeze_user_space) { | 180 | if (p->state == TASK_TRACED && frozen(p->parent)) { |
123 | if (p->state == TASK_TRACED && | 181 | cancel_freezing(p); |
124 | frozen(p->parent)) { | 182 | continue; |
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); | ||
146 | } | 183 | } |
184 | |||
185 | if (!freeze_task(p, freeze_user_space)) | ||
186 | continue; | ||
187 | |||
147 | if (!freezer_should_skip(p)) | 188 | if (!freezer_should_skip(p)) |
148 | todo++; | 189 | todo++; |
149 | } while_each_thread(g, p); | 190 | } while_each_thread(g, p); |
diff --git a/kernel/signal.c b/kernel/signal.c index 2124ffadcfde..e4f059cd9867 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -99,7 +99,6 @@ static inline int has_pending_signals(sigset_t *signal, sigset_t *blocked) | |||
99 | static int recalc_sigpending_tsk(struct task_struct *t) | 99 | static int recalc_sigpending_tsk(struct task_struct *t) |
100 | { | 100 | { |
101 | if (t->signal->group_stop_count > 0 || | 101 | if (t->signal->group_stop_count > 0 || |
102 | (freezing(t)) || | ||
103 | PENDING(&t->pending, &t->blocked) || | 102 | PENDING(&t->pending, &t->blocked) || |
104 | PENDING(&t->signal->shared_pending, &t->blocked)) { | 103 | PENDING(&t->signal->shared_pending, &t->blocked)) { |
105 | set_tsk_thread_flag(t, TIF_SIGPENDING); | 104 | set_tsk_thread_flag(t, TIF_SIGPENDING); |