Blame view

kernel/freezer.c 3.67 KB
8174f1503   Matt Helsley   container freezer...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  /*
   * kernel/freezer.c - Function to freeze a process
   *
   * Originally from kernel/power/process.c
   */
  
  #include <linux/interrupt.h>
  #include <linux/suspend.h>
  #include <linux/module.h>
  #include <linux/syscalls.h>
  #include <linux/freezer.h>
  
  /*
   * freezing is complete, mark current process as frozen
   */
  static inline void frozen_process(void)
  {
  	if (!unlikely(current->flags & PF_NOFREEZE)) {
  		current->flags |= PF_FROZEN;
  		wmb();
  	}
  	clear_freeze_flag(current);
  }
  
  /* Refrigerator is place where frozen processes are stored :-). */
  void refrigerator(void)
  {
  	/* Hmm, should we be allowed to suspend when there are realtime
  	   processes around? */
  	long save;
  
  	task_lock(current);
  	if (freezing(current)) {
  		frozen_process();
  		task_unlock(current);
  	} else {
  		task_unlock(current);
  		return;
  	}
  	save = current->state;
  	pr_debug("%s entered refrigerator
  ", current->comm);
  
  	spin_lock_irq(&current->sighand->siglock);
  	recalc_sigpending(); /* We sent fake signal, clean it up */
  	spin_unlock_irq(&current->sighand->siglock);
  
  	for (;;) {
  		set_current_state(TASK_UNINTERRUPTIBLE);
  		if (!frozen(current))
  			break;
  		schedule();
  	}
  	pr_debug("%s left refrigerator
  ", current->comm);
  	__set_current_state(save);
  }
  EXPORT_SYMBOL(refrigerator);
  
  static void fake_signal_wake_up(struct task_struct *p)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&p->sighand->siglock, flags);
  	signal_wake_up(p, 0);
  	spin_unlock_irqrestore(&p->sighand->siglock, flags);
  }
  
  /**
   *	freeze_task - send a freeze request to given task
   *	@p: task to send the request to
   *	@sig_only: if set, the request will only be sent if the task has the
   *		PF_FREEZER_NOSIG flag unset
   *	Return value: 'false', if @sig_only is set and the task has
   *		PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise
   *
   *	The freeze request is sent by setting the tasks's TIF_FREEZE flag and
   *	either sending a fake signal to it or waking it up, depending on whether
   *	or not it has PF_FREEZER_NOSIG set.  If @sig_only is set and the task
   *	has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its
   *	TIF_FREEZE flag will not be set.
   */
  bool freeze_task(struct task_struct *p, bool sig_only)
  {
  	/*
  	 * We first check if the task is freezing and next if it has already
  	 * been frozen to avoid the race with frozen_process() which first marks
  	 * the task as frozen and next clears its TIF_FREEZE.
  	 */
  	if (!freezing(p)) {
  		rmb();
  		if (frozen(p))
  			return false;
  
  		if (!sig_only || should_send_signal(p))
  			set_freeze_flag(p);
  		else
  			return false;
  	}
  
  	if (should_send_signal(p)) {
  		if (!signal_pending(p))
  			fake_signal_wake_up(p);
  	} else if (sig_only) {
  		return false;
  	} else {
  		wake_up_state(p, TASK_INTERRUPTIBLE);
  	}
  
  	return true;
  }
  
  void cancel_freezing(struct task_struct *p)
  {
  	unsigned long flags;
  
  	if (freezing(p)) {
  		pr_debug("  clean up: %s
  ", p->comm);
  		clear_freeze_flag(p);
  		spin_lock_irqsave(&p->sighand->siglock, flags);
  		recalc_sigpending_and_wake(p);
  		spin_unlock_irqrestore(&p->sighand->siglock, flags);
  	}
  }
dc52ddc0e   Matt Helsley   container freezer...
126

00c2e63c3   Li Zefan   freezer_cg: use t...
127
  static int __thaw_process(struct task_struct *p)
dc52ddc0e   Matt Helsley   container freezer...
128
129
130
131
132
133
134
135
  {
  	if (frozen(p)) {
  		p->flags &= ~PF_FROZEN;
  		return 1;
  	}
  	clear_freeze_flag(p);
  	return 0;
  }
00c2e63c3   Li Zefan   freezer_cg: use t...
136
137
138
139
140
141
142
143
144
  /*
   * Wake up a frozen process
   *
   * task_lock() is needed to prevent the race with refrigerator() which may
   * occur if the freezing of tasks fails.  Namely, without the lock, if the
   * freezing of tasks failed, thaw_tasks() might have run before a task in
   * refrigerator() could call frozen_process(), in which case the task would be
   * frozen and no one would thaw it.
   */
dc52ddc0e   Matt Helsley   container freezer...
145
146
147
148
149
150
151
152
153
154
155
156
  int thaw_process(struct task_struct *p)
  {
  	task_lock(p);
  	if (__thaw_process(p) == 1) {
  		task_unlock(p);
  		wake_up_process(p);
  		return 1;
  	}
  	task_unlock(p);
  	return 0;
  }
  EXPORT_SYMBOL(thaw_process);