Linux Audio

Check our new training course

Buildroot integration, development and maintenance

Need a Buildroot system for your embedded project?
Loading...
Note: File does not exist in v6.8.
  1/*
  2 * cgroup_freezer.c -  control group freezer subsystem
  3 *
  4 * Copyright IBM Corporation, 2007
  5 *
  6 * Author : Cedric Le Goater <clg@fr.ibm.com>
  7 *
  8 * This program is free software; you can redistribute it and/or modify it
  9 * under the terms of version 2.1 of the GNU Lesser General Public License
 10 * as published by the Free Software Foundation.
 11 *
 12 * This program is distributed in the hope that it would be useful, but
 13 * WITHOUT ANY WARRANTY; without even the implied warranty of
 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 15 */
 16
 17#include <linux/module.h>
 18#include <linux/slab.h>
 19#include <linux/cgroup.h>
 20#include <linux/fs.h>
 21#include <linux/uaccess.h>
 22#include <linux/freezer.h>
 23#include <linux/seq_file.h>
 24
 25enum freezer_state {
 26	CGROUP_THAWED = 0,
 27	CGROUP_FREEZING,
 28	CGROUP_FROZEN,
 29};
 30
 31struct freezer {
 32	struct cgroup_subsys_state css;
 33	enum freezer_state state;
 34	spinlock_t lock; /* protects _writes_ to state */
 35};
 36
 37static inline struct freezer *cgroup_freezer(
 38		struct cgroup *cgroup)
 39{
 40	return container_of(
 41		cgroup_subsys_state(cgroup, freezer_subsys_id),
 42		struct freezer, css);
 43}
 44
 45static inline struct freezer *task_freezer(struct task_struct *task)
 46{
 47	return container_of(task_subsys_state(task, freezer_subsys_id),
 48			    struct freezer, css);
 49}
 50
 51static inline int __cgroup_freezing_or_frozen(struct task_struct *task)
 52{
 53	enum freezer_state state = task_freezer(task)->state;
 54	return (state == CGROUP_FREEZING) || (state == CGROUP_FROZEN);
 55}
 56
 57int cgroup_freezing_or_frozen(struct task_struct *task)
 58{
 59	int result;
 60	task_lock(task);
 61	result = __cgroup_freezing_or_frozen(task);
 62	task_unlock(task);
 63	return result;
 64}
 65
 66/*
 67 * cgroups_write_string() limits the size of freezer state strings to
 68 * CGROUP_LOCAL_BUFFER_SIZE
 69 */
 70static const char *freezer_state_strs[] = {
 71	"THAWED",
 72	"FREEZING",
 73	"FROZEN",
 74};
 75
 76/*
 77 * State diagram
 78 * Transitions are caused by userspace writes to the freezer.state file.
 79 * The values in parenthesis are state labels. The rest are edge labels.
 80 *
 81 * (THAWED) --FROZEN--> (FREEZING) --FROZEN--> (FROZEN)
 82 *    ^ ^                    |                     |
 83 *    | \_______THAWED_______/                     |
 84 *    \__________________________THAWED____________/
 85 */
 86
 87struct cgroup_subsys freezer_subsys;
 88
 89/* Locks taken and their ordering
 90 * ------------------------------
 91 * cgroup_mutex (AKA cgroup_lock)
 92 * freezer->lock
 93 * css_set_lock
 94 * task->alloc_lock (AKA task_lock)
 95 * task->sighand->siglock
 96 *
 97 * cgroup code forces css_set_lock to be taken before task->alloc_lock
 98 *
 99 * freezer_create(), freezer_destroy():
100 * cgroup_mutex [ by cgroup core ]
101 *
102 * freezer_can_attach():
103 * cgroup_mutex (held by caller of can_attach)
104 *
105 * cgroup_freezing_or_frozen():
106 * task->alloc_lock (to get task's cgroup)
107 *
108 * freezer_fork() (preserving fork() performance means can't take cgroup_mutex):
109 * freezer->lock
110 *  sighand->siglock (if the cgroup is freezing)
111 *
112 * freezer_read():
113 * cgroup_mutex
114 *  freezer->lock
115 *   write_lock css_set_lock (cgroup iterator start)
116 *    task->alloc_lock
117 *   read_lock css_set_lock (cgroup iterator start)
118 *
119 * freezer_write() (freeze):
120 * cgroup_mutex
121 *  freezer->lock
122 *   write_lock css_set_lock (cgroup iterator start)
123 *    task->alloc_lock
124 *   read_lock css_set_lock (cgroup iterator start)
125 *    sighand->siglock (fake signal delivery inside freeze_task())
126 *
127 * freezer_write() (unfreeze):
128 * cgroup_mutex
129 *  freezer->lock
130 *   write_lock css_set_lock (cgroup iterator start)
131 *    task->alloc_lock
132 *   read_lock css_set_lock (cgroup iterator start)
133 *    task->alloc_lock (inside thaw_process(), prevents race with refrigerator())
134 *     sighand->siglock
135 */
136static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
137						  struct cgroup *cgroup)
138{
139	struct freezer *freezer;
140
141	freezer = kzalloc(sizeof(struct freezer), GFP_KERNEL);
142	if (!freezer)
143		return ERR_PTR(-ENOMEM);
144
145	spin_lock_init(&freezer->lock);
146	freezer->state = CGROUP_THAWED;
147	return &freezer->css;
148}
149
150static void freezer_destroy(struct cgroup_subsys *ss,
151			    struct cgroup *cgroup)
152{
153	kfree(cgroup_freezer(cgroup));
154}
155
156/*
157 * The call to cgroup_lock() in the freezer.state write method prevents
158 * a write to that file racing against an attach, and hence the
159 * can_attach() result will remain valid until the attach completes.
160 */
161static int freezer_can_attach(struct cgroup_subsys *ss,
162			      struct cgroup *new_cgroup,
163			      struct task_struct *task)
164{
165	struct freezer *freezer;
166
167	/*
168	 * Anything frozen can't move or be moved to/from.
169	 */
170
171	freezer = cgroup_freezer(new_cgroup);
172	if (freezer->state != CGROUP_THAWED)
173		return -EBUSY;
174
175	return 0;
176}
177
178static int freezer_can_attach_task(struct cgroup *cgrp, struct task_struct *tsk)
179{
180	rcu_read_lock();
181	if (__cgroup_freezing_or_frozen(tsk)) {
182		rcu_read_unlock();
183		return -EBUSY;
184	}
185	rcu_read_unlock();
186	return 0;
187}
188
189static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task)
190{
191	struct freezer *freezer;
192
193	/*
194	 * No lock is needed, since the task isn't on tasklist yet,
195	 * so it can't be moved to another cgroup, which means the
196	 * freezer won't be removed and will be valid during this
197	 * function call.  Nevertheless, apply RCU read-side critical
198	 * section to suppress RCU lockdep false positives.
199	 */
200	rcu_read_lock();
201	freezer = task_freezer(task);
202	rcu_read_unlock();
203
204	/*
205	 * The root cgroup is non-freezable, so we can skip the
206	 * following check.
207	 */
208	if (!freezer->css.cgroup->parent)
209		return;
210
211	spin_lock_irq(&freezer->lock);
212	BUG_ON(freezer->state == CGROUP_FROZEN);
213
214	/* Locking avoids race with FREEZING -> THAWED transitions. */
215	if (freezer->state == CGROUP_FREEZING)
216		freeze_task(task, true);
217	spin_unlock_irq(&freezer->lock);
218}
219
220/*
221 * caller must hold freezer->lock
222 */
223static void update_if_frozen(struct cgroup *cgroup,
224				 struct freezer *freezer)
225{
226	struct cgroup_iter it;
227	struct task_struct *task;
228	unsigned int nfrozen = 0, ntotal = 0;
229	enum freezer_state old_state = freezer->state;
230
231	cgroup_iter_start(cgroup, &it);
232	while ((task = cgroup_iter_next(cgroup, &it))) {
233		ntotal++;
234		if (frozen(task))
235			nfrozen++;
236	}
237
238	if (old_state == CGROUP_THAWED) {
239		BUG_ON(nfrozen > 0);
240	} else if (old_state == CGROUP_FREEZING) {
241		if (nfrozen == ntotal)
242			freezer->state = CGROUP_FROZEN;
243	} else { /* old_state == CGROUP_FROZEN */
244		BUG_ON(nfrozen != ntotal);
245	}
246
247	cgroup_iter_end(cgroup, &it);
248}
249
250static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
251			struct seq_file *m)
252{
253	struct freezer *freezer;
254	enum freezer_state state;
255
256	if (!cgroup_lock_live_group(cgroup))
257		return -ENODEV;
258
259	freezer = cgroup_freezer(cgroup);
260	spin_lock_irq(&freezer->lock);
261	state = freezer->state;
262	if (state == CGROUP_FREEZING) {
263		/* We change from FREEZING to FROZEN lazily if the cgroup was
264		 * only partially frozen when we exitted write. */
265		update_if_frozen(cgroup, freezer);
266		state = freezer->state;
267	}
268	spin_unlock_irq(&freezer->lock);
269	cgroup_unlock();
270
271	seq_puts(m, freezer_state_strs[state]);
272	seq_putc(m, '\n');
273	return 0;
274}
275
276static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
277{
278	struct cgroup_iter it;
279	struct task_struct *task;
280	unsigned int num_cant_freeze_now = 0;
281
282	freezer->state = CGROUP_FREEZING;
283	cgroup_iter_start(cgroup, &it);
284	while ((task = cgroup_iter_next(cgroup, &it))) {
285		if (!freeze_task(task, true))
286			continue;
287		if (frozen(task))
288			continue;
289		if (!freezing(task) && !freezer_should_skip(task))
290			num_cant_freeze_now++;
291	}
292	cgroup_iter_end(cgroup, &it);
293
294	return num_cant_freeze_now ? -EBUSY : 0;
295}
296
297static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
298{
299	struct cgroup_iter it;
300	struct task_struct *task;
301
302	cgroup_iter_start(cgroup, &it);
303	while ((task = cgroup_iter_next(cgroup, &it))) {
304		thaw_process(task);
305	}
306	cgroup_iter_end(cgroup, &it);
307
308	freezer->state = CGROUP_THAWED;
309}
310
311static int freezer_change_state(struct cgroup *cgroup,
312				enum freezer_state goal_state)
313{
314	struct freezer *freezer;
315	int retval = 0;
316
317	freezer = cgroup_freezer(cgroup);
318
319	spin_lock_irq(&freezer->lock);
320
321	update_if_frozen(cgroup, freezer);
322	if (goal_state == freezer->state)
323		goto out;
324
325	switch (goal_state) {
326	case CGROUP_THAWED:
327		unfreeze_cgroup(cgroup, freezer);
328		break;
329	case CGROUP_FROZEN:
330		retval = try_to_freeze_cgroup(cgroup, freezer);
331		break;
332	default:
333		BUG();
334	}
335out:
336	spin_unlock_irq(&freezer->lock);
337
338	return retval;
339}
340
341static int freezer_write(struct cgroup *cgroup,
342			 struct cftype *cft,
343			 const char *buffer)
344{
345	int retval;
346	enum freezer_state goal_state;
347
348	if (strcmp(buffer, freezer_state_strs[CGROUP_THAWED]) == 0)
349		goal_state = CGROUP_THAWED;
350	else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0)
351		goal_state = CGROUP_FROZEN;
352	else
353		return -EINVAL;
354
355	if (!cgroup_lock_live_group(cgroup))
356		return -ENODEV;
357	retval = freezer_change_state(cgroup, goal_state);
358	cgroup_unlock();
359	return retval;
360}
361
362static struct cftype files[] = {
363	{
364		.name = "state",
365		.read_seq_string = freezer_read,
366		.write_string = freezer_write,
367	},
368};
369
370static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup)
371{
372	if (!cgroup->parent)
373		return 0;
374	return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
375}
376
377struct cgroup_subsys freezer_subsys = {
378	.name		= "freezer",
379	.create		= freezer_create,
380	.destroy	= freezer_destroy,
381	.populate	= freezer_populate,
382	.subsys_id	= freezer_subsys_id,
383	.can_attach	= freezer_can_attach,
384	.can_attach_task = freezer_can_attach_task,
385	.pre_attach	= NULL,
386	.attach_task	= NULL,
387	.attach		= NULL,
388	.fork		= freezer_fork,
389	.exit		= NULL,
390};