Skip to content

Commit a320122

Browse files
committed
freezer: make freezing() test freeze conditions in effect instead of TIF_FREEZE
Using TIF_FREEZE for freezing worked when there was only single freezing condition (the PM one); however, now there is also the cgroup_freezer and single bit flag is getting clumsy. thaw_processes() is already testing whether cgroup freezing in in effect to avoid thawing tasks which were frozen by both PM and cgroup freezers. This is racy (nothing prevents race against cgroup freezing) and fragile. A much simpler way is to test actual freeze conditions from freezing() - ie. directly test whether PM or cgroup freezing is in effect. This patch adds variables to indicate whether and what type of freezing conditions are in effect and reimplements freezing() such that it directly tests whether any of the two freezing conditions is active and the task should freeze. On fast path, freezing() is still very cheap - it only tests system_freezing_cnt. This makes the clumsy dancing aroung TIF_FREEZE unnecessary and freeze/thaw operations more usual - updating state variables for the new state and nudging target tasks so that they notice the new state and comply. As long as the nudging happens after state update, it's race-free. * This allows use of freezing() in freeze_task(). Replace the open coded tests with freezing(). * p != current test is added to warning printing conditions in try_to_freeze_tasks() failure path. This is necessary as freezing() is now true for the task which initiated freezing too. -v2: Oleg pointed out that re-freezing FROZEN cgroup could increment system_freezing_cnt. Fixed. Signed-off-by: Tejun Heo <[email protected]> Acked-by: Paul Menage <[email protected]> (for the cgroup portions)
1 parent 22b4e11 commit a320122

File tree

5 files changed

+72
-49
lines changed

5 files changed

+72
-49
lines changed

include/linux/freezer.h

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@
55

66
#include <linux/sched.h>
77
#include <linux/wait.h>
8+
#include <linux/atomic.h>
89

910
#ifdef CONFIG_FREEZER
11+
extern atomic_t system_freezing_cnt; /* nr of freezing conds in effect */
12+
extern bool pm_freezing; /* PM freezing in effect */
13+
extern bool pm_nosig_freezing; /* PM nosig freezing in effect */
14+
1015
/*
1116
* Check if a process has been frozen
1217
*/
@@ -15,28 +20,16 @@ static inline int frozen(struct task_struct *p)
1520
return p->flags & PF_FROZEN;
1621
}
1722

18-
/*
19-
* Check if there is a request to freeze a process
20-
*/
21-
static inline int freezing(struct task_struct *p)
22-
{
23-
return test_tsk_thread_flag(p, TIF_FREEZE);
24-
}
23+
extern bool freezing_slow_path(struct task_struct *p);
2524

2625
/*
27-
* Request that a process be frozen
28-
*/
29-
static inline void set_freeze_flag(struct task_struct *p)
30-
{
31-
set_tsk_thread_flag(p, TIF_FREEZE);
32-
}
33-
34-
/*
35-
* Sometimes we may need to cancel the previous 'freeze' request
26+
* Check if there is a request to freeze a process
3627
*/
37-
static inline void clear_freeze_flag(struct task_struct *p)
28+
static inline bool freezing(struct task_struct *p)
3829
{
39-
clear_tsk_thread_flag(p, TIF_FREEZE);
30+
if (likely(!atomic_read(&system_freezing_cnt)))
31+
return false;
32+
return freezing_slow_path(p);
4033
}
4134

4235
static inline bool should_send_signal(struct task_struct *p)
@@ -174,9 +167,7 @@ static inline void set_freezable_with_signal(void)
174167
})
175168
#else /* !CONFIG_FREEZER */
176169
static inline int frozen(struct task_struct *p) { return 0; }
177-
static inline int freezing(struct task_struct *p) { return 0; }
178-
static inline void set_freeze_flag(struct task_struct *p) {}
179-
static inline void clear_freeze_flag(struct task_struct *p) {}
170+
static inline bool freezing(struct task_struct *p) { return false; }
180171

181172
static inline bool __refrigerator(bool check_kthr_stop) { return false; }
182173
static inline int freeze_processes(void) { return -ENOSYS; }

kernel/cgroup_freezer.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,11 @@ static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
145145
static void freezer_destroy(struct cgroup_subsys *ss,
146146
struct cgroup *cgroup)
147147
{
148-
kfree(cgroup_freezer(cgroup));
148+
struct freezer *freezer = cgroup_freezer(cgroup);
149+
150+
if (freezer->state != CGROUP_THAWED)
151+
atomic_dec(&system_freezing_cnt);
152+
kfree(freezer);
149153
}
150154

151155
/*
@@ -307,10 +311,14 @@ static int freezer_change_state(struct cgroup *cgroup,
307311

308312
switch (goal_state) {
309313
case CGROUP_THAWED:
314+
if (freezer->state != CGROUP_THAWED)
315+
atomic_dec(&system_freezing_cnt);
310316
freezer->state = CGROUP_THAWED;
311317
unfreeze_cgroup(cgroup, freezer);
312318
break;
313319
case CGROUP_FROZEN:
320+
if (freezer->state == CGROUP_THAWED)
321+
atomic_inc(&system_freezing_cnt);
314322
freezer->state = CGROUP_FREEZING;
315323
retval = try_to_freeze_cgroup(cgroup, freezer);
316324
break;

kernel/fork.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,6 @@ static void copy_flags(unsigned long clone_flags, struct task_struct *p)
997997
new_flags |= PF_FORKNOEXEC;
998998
new_flags |= PF_STARTING;
999999
p->flags = new_flags;
1000-
clear_freeze_flag(p);
10011000
}
10021001

10031002
SYSCALL_DEFINE1(set_tid_address, int __user *, tidptr)

kernel/freezer.c

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,41 @@
1111
#include <linux/freezer.h>
1212
#include <linux/kthread.h>
1313

14+
/* total number of freezing conditions in effect */
15+
atomic_t system_freezing_cnt = ATOMIC_INIT(0);
16+
EXPORT_SYMBOL(system_freezing_cnt);
17+
18+
/* indicate whether PM freezing is in effect, protected by pm_mutex */
19+
bool pm_freezing;
20+
bool pm_nosig_freezing;
21+
1422
/* protects freezing and frozen transitions */
1523
static DEFINE_SPINLOCK(freezer_lock);
1624

25+
/**
26+
* freezing_slow_path - slow path for testing whether a task needs to be frozen
27+
* @p: task to be tested
28+
*
29+
* This function is called by freezing() if system_freezing_cnt isn't zero
30+
* and tests whether @p needs to enter and stay in frozen state. Can be
31+
* called under any context. The freezers are responsible for ensuring the
32+
* target tasks see the updated state.
33+
*/
34+
bool freezing_slow_path(struct task_struct *p)
35+
{
36+
if (p->flags & PF_NOFREEZE)
37+
return false;
38+
39+
if (pm_nosig_freezing || cgroup_freezing(p))
40+
return true;
41+
42+
if (pm_freezing && !(p->flags & PF_FREEZER_NOSIG))
43+
return true;
44+
45+
return false;
46+
}
47+
EXPORT_SYMBOL(freezing_slow_path);
48+
1749
/* Refrigerator is place where frozen processes are stored :-). */
1850
bool __refrigerator(bool check_kthr_stop)
1951
{
@@ -23,17 +55,11 @@ bool __refrigerator(bool check_kthr_stop)
2355
long save;
2456

2557
/*
26-
* Enter FROZEN. If NOFREEZE, schedule immediate thawing by
27-
* clearing freezing.
58+
* No point in checking freezing() again - the caller already did.
59+
* Proceed to enter FROZEN.
2860
*/
2961
spin_lock_irq(&freezer_lock);
3062
repeat:
31-
if (!freezing(current)) {
32-
spin_unlock_irq(&freezer_lock);
33-
return was_frozen;
34-
}
35-
if (current->flags & PF_NOFREEZE)
36-
clear_freeze_flag(current);
3763
current->flags |= PF_FROZEN;
3864
spin_unlock_irq(&freezer_lock);
3965

@@ -99,18 +125,12 @@ static void fake_signal_wake_up(struct task_struct *p)
99125
bool freeze_task(struct task_struct *p, bool sig_only)
100126
{
101127
unsigned long flags;
102-
bool ret = false;
103128

104129
spin_lock_irqsave(&freezer_lock, flags);
105-
106-
if ((p->flags & PF_NOFREEZE) ||
107-
(sig_only && !should_send_signal(p)))
108-
goto out_unlock;
109-
110-
if (frozen(p))
111-
goto out_unlock;
112-
113-
set_freeze_flag(p);
130+
if (!freezing(p) || frozen(p)) {
131+
spin_unlock_irqrestore(&freezer_lock, flags);
132+
return false;
133+
}
114134

115135
if (should_send_signal(p)) {
116136
fake_signal_wake_up(p);
@@ -123,10 +143,9 @@ bool freeze_task(struct task_struct *p, bool sig_only)
123143
} else {
124144
wake_up_state(p, TASK_INTERRUPTIBLE);
125145
}
126-
ret = true;
127-
out_unlock:
146+
128147
spin_unlock_irqrestore(&freezer_lock, flags);
129-
return ret;
148+
return true;
130149
}
131150

132151
void __thaw_task(struct task_struct *p)
@@ -143,7 +162,6 @@ void __thaw_task(struct task_struct *p)
143162
* avoid leaving dangling TIF_SIGPENDING behind.
144163
*/
145164
spin_lock_irqsave(&freezer_lock, flags);
146-
clear_freeze_flag(p);
147165
if (frozen(p)) {
148166
wake_up_process(p);
149167
} else {

kernel/power/process.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ static int try_to_freeze_tasks(bool sig_only)
101101
read_lock(&tasklist_lock);
102102
do_each_thread(g, p) {
103103
if (!wakeup && !freezer_should_skip(p) &&
104-
freezing(p) && !frozen(p))
104+
p != current && freezing(p) && !frozen(p))
105105
sched_show_task(p);
106106
} while_each_thread(g, p);
107107
read_unlock(&tasklist_lock);
@@ -122,7 +122,11 @@ int freeze_processes(void)
122122
{
123123
int error;
124124

125+
if (!pm_freezing)
126+
atomic_inc(&system_freezing_cnt);
127+
125128
printk("Freezing user space processes ... ");
129+
pm_freezing = true;
126130
error = try_to_freeze_tasks(true);
127131
if (!error) {
128132
printk("done.");
@@ -146,6 +150,7 @@ int freeze_kernel_threads(void)
146150
int error;
147151

148152
printk("Freezing remaining freezable tasks ... ");
153+
pm_nosig_freezing = true;
149154
error = try_to_freeze_tasks(false);
150155
if (!error)
151156
printk("done.");
@@ -162,6 +167,11 @@ void thaw_processes(void)
162167
{
163168
struct task_struct *g, *p;
164169

170+
if (pm_freezing)
171+
atomic_dec(&system_freezing_cnt);
172+
pm_freezing = false;
173+
pm_nosig_freezing = false;
174+
165175
oom_killer_enable();
166176

167177
printk("Restarting tasks ... ");
@@ -170,9 +180,6 @@ void thaw_processes(void)
170180

171181
read_lock(&tasklist_lock);
172182
do_each_thread(g, p) {
173-
if (cgroup_freezing(p))
174-
continue;
175-
176183
__thaw_task(p);
177184
} while_each_thread(g, p);
178185
read_unlock(&tasklist_lock);

0 commit comments

Comments
 (0)