Skip to contentSkip to conversation

cgroup/freezer: add per freezer duty ratio control

PATCH Superseded [email protected] 4 participants 6 messages Feb 7, 2011 2 messages not in the archive Documentation

v3 of 9 Superseded Latest is v9 →

lore
Share & tools
lore
#0 lore raw

PATCH v3 1/1 cgroup/freezer: add per freezer duty ratio control

From: Jacob Pan <jacob.jun.pan-VuQAYsv1563Yd54FQh9/[email protected]>

Changelog:
v3: - added documentation

v2:
- use read_u64/write_u64 instead of string read/write
- use period in second instead of ms
- renamed duty_ratio to duty_ratio_pct, be more specific
- removed locking around period and duty_ratio reads

Jacob Pan (1):
cgroup/freezer: add per freezer duty ratio control

 Documentation/cgroups/freezer-subsystem.txt |   23 ++++++
 kernel/cgroup_freezer.c                     |  109 ++++++++++++++++++++++++++-
 2 files changed, 130 insertions(+), 2 deletions(-)
#1 lore raw

PATCH v3 1/1 cgroup/freezer: add per freezer duty ratio control

From: Jacob Pan <jacob.jun.pan-VuQAYsv1563Yd54FQh9/[email protected]>

Freezer subsystem is used to manage batch jobs which can start stop at the same time. However, sometime it is desirable to let the kernel manage the freezer state automatically with a given duty ratio.
For example, if we want to reduce the time that backgroup apps are allowed to run we can put them into a freezer subsystem and set the kernel to turn them THAWED/FROZEN at given duty ratio.

This patch introduces two file nodes under cgroup
freezer.duty_ratio_pct and freezer.period_sec

Usage example: set period to be 5 seconds and frozen duty ratio 90% [root@localhost aoa]# echo 90 > freezer.duty_ratio_pct [root@localhost aoa]# echo 5 > freezer.period_sec

---
 Documentation/cgroups/freezer-subsystem.txt |   23 ++++++
 kernel/cgroup_freezer.c                     |  109 ++++++++++++++++++++++++++-
 2 files changed, 130 insertions(+), 2 deletions(-)
Documentation/cgroups/freezer-subsystem.txt +23 -0
index 41f37fe..2bc1b98 100644
@@ -100,3 +100,26 @@ things happens:
 		and returns EINVAL)
 	3) The tasks that blocked the cgroup from entering the "FROZEN"
 		state disappear from the cgroup's set of tasks.
+
+In embedded systems, it is desirable to manage group of applications
+for power saving. E.g. tasks that are not in the foreground may be
+frozen unfrozen periodically to save power without affecting user
+experience. In this case, user/management software can attach tasks
+into freezer cgroup then specify duty ratio and period that the
+managed tasks are allowed to run.
+
+Usage example:
+Assuming freezer cgroup is already mounted, application being managed
+are included the "tasks" file node of the given freezer cgroup.
+To make the tasks frozen at 90% of the time every 5 seconds, do:
+
+[root@localhost ]# echo 90 > freezer.duty_ratio_pct
+[root@localhost ]# echo 5 > freezer.period_sec
+
+After that, the application in this freezer cgroup will only be
+allowed to run at the following pattern.
+     __                    __                    __
+    |  |<-- 90% frozen -->|  |                  |  |
+____|  |__________________|  |__________________|  |_____
+
+    |<---- 5 seconds ---->|
kernel/cgroup_freezer.c +107 -2

163 lines not on this page · raw message

Signature · 1 line
1.7.0.4

A message that is not in the archive (sent off-list or not yet received): 1297102383-2658-2-git-send-email-jacob.jun.pan-VuQAYsv1563Yd54FQh9/[email protected]

#3 lore raw
Quoted 168 lines · On Mon, Feb 07, 2011 at 10:13:03AM -0800…

From: Jacob Pan <jacob.jun.pan-VuQAYsv1563Yd54FQh9/[email protected]>

Freezer subsystem is used to manage batch jobs which can start stop at the same time. However, sometime it is desirable to let the kernel manage the freezer state automatically with a given duty ratio.
For example, if we want to reduce the time that backgroup apps are allowed to run we can put them into a freezer subsystem and set the kernel to turn them THAWED/FROZEN at given duty ratio.

This patch introduces two file nodes under cgroup
freezer.duty_ratio_pct and freezer.period_sec

Usage example: set period to be 5 seconds and frozen duty ratio 90% [root@localhost aoa]# echo 90 > freezer.duty_ratio_pct [root@localhost aoa]# echo 5 > freezer.period_sec

---
 Documentation/cgroups/freezer-subsystem.txt |   23 ++++++
 kernel/cgroup_freezer.c                     |  109 ++++++++++++++++++++++++++-
 2 files changed, 130 insertions(+), 2 deletions(-)
Documentation/cgroups/freezer-subsystem.txt +23 -0
index 41f37fe..2bc1b98 100644
@@ -100,3 +100,26 @@ things happens:
 		and returns EINVAL)
 	3) The tasks that blocked the cgroup from entering the "FROZEN"
 		state disappear from the cgroup's set of tasks.
+
+In embedded systems, it is desirable to manage group of applications
+for power saving. E.g. tasks that are not in the foreground may be
+frozen unfrozen periodically to save power without affecting user
+experience. In this case, user/management software can attach tasks
+into freezer cgroup then specify duty ratio and period that the
+managed tasks are allowed to run.
+
+Usage example:
+Assuming freezer cgroup is already mounted, application being managed
+are included the "tasks" file node of the given freezer cgroup.
+To make the tasks frozen at 90% of the time every 5 seconds, do:
+
+[root@localhost ]# echo 90 > freezer.duty_ratio_pct
+[root@localhost ]# echo 5 > freezer.period_sec
+
+After that, the application in this freezer cgroup will only be
+allowed to run at the following pattern.
+     __                    __                    __
+    |  |<-- 90% frozen -->|  |                  |  |
+____|  |__________________|  |__________________|  |_____
+
+    |<---- 5 seconds ---->|
kernel/cgroup_freezer.c +64 -1
index e7bebb7..928f2ab 100644
@@ -21,6 +21,7 @@
 #include <linux/uaccess.h>
 #include <linux/freezer.h>
 #include <linux/seq_file.h>
+#include <linux/kthread.h>
 
 enum freezer_state {
 	CGROUP_THAWED = 0,
@@ -28,12 +29,23 @@ enum freezer_state {
 	CGROUP_FROZEN,
 };
 
+struct freezer_duty {
+	u32 ratio; /* percentage of time frozen */
+	u32 period_pct_ms; /* one percent of the period in miliseconds */
+};
+
 struct freezer {
 	struct cgroup_subsys_state css;
 	enum freezer_state state;
+	struct freezer_duty duty;
+	struct task_struct *fkh;
 	spinlock_t lock; /* protects _writes_ to state */
 };
 
+static struct task_struct *freezer_task;
+static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer);
+static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer);
+
 static inline struct freezer *cgroup_freezer(
 		struct cgroup *cgroup)
 {
@@ -63,6 +75,35 @@ int cgroup_freezing_or_frozen(struct task_struct *task)
 	return result;
 }
 
+static DECLARE_WAIT_QUEUE_HEAD(freezer_wait);
+
+static int freezer_kh(void *data)
+{
+	struct cgroup *cgroup = (struct cgroup *)data;
+	struct freezer *freezer = cgroup_freezer(cgroup);
+
+	do {
+		if (freezer->duty.ratio < 100 && freezer->duty.ratio >= 0 &&
+			freezer->duty.period_pct_ms) {
+			if (try_to_freeze_cgroup(cgroup, freezer))
+				pr_info("cannot freeze\n");
+			msleep(freezer->duty.period_pct_ms *
+				freezer->duty.ratio);
+			unfreeze_cgroup(cgroup, freezer);
+			msleep(freezer->duty.period_pct_ms *
+				(100 - freezer->duty.ratio));
+		} else if (freezer->duty.ratio == 100) {
+			if (try_to_freeze_cgroup(cgroup, freezer))
+				pr_info("cannot freeze\n");
+			sleep_on(&freezer_wait);
+		} else {
+			sleep_on(&freezer_wait);
+			pr_debug("freezer thread wake up\n");
+		}
+	} while (!kthread_should_stop());
+	return 0;
+}
+
 /*
  * cgroups_write_string() limits the size of freezer state strings to
  * CGROUP_LOCAL_BUFFER_SIZE
@@ -150,7 +191,11 @@ static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
 static void freezer_destroy(struct cgroup_subsys *ss,
 			    struct cgroup *cgroup)
 {
-	kfree(cgroup_freezer(cgroup));
+	struct freezer *freezer;
+
+	freezer = cgroup_freezer(cgroup);
+	kthread_stop(freezer->fkh);
+	kfree(freezer);
 }
 
 /*
@@ -282,6 +327,16 @@ static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
 	return 0;
 }
 
+static u64 freezer_read_duty_ratio(struct cgroup *cgroup, struct cftype *cft)
+{
+	return cgroup_freezer(cgroup)->duty.ratio;
+}
+
+static u64 freezer_read_period(struct cgroup *cgroup, struct cftype *cft)
+{
+	return cgroup_freezer(cgroup)->duty.period_pct_ms / 10;
+}
+
 static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
 {
 	struct cgroup_iter it;
@@ -368,19 +423,69 @@ static int freezer_write(struct cgroup *cgroup,
 	return retval;
 }
 
+static int freezer_write_duty_ratio(struct cgroup *cgroup, struct cftype *cft,
+		u64 val)
+{
+	if (!cgroup_lock_live_group(cgroup))
+		return -ENODEV;
+	cgroup_freezer(cgroup)->duty.ratio = val;
+	cgroup_unlock();
+	wake_up(&freezer_wait);

kernel/cgroup_freezer.c static int freezer_write(struct cgroup *cgroup, · line 443

Quoted 10 lines
kernel/cgroup_freezer.c +10 -0
+
+	return 0;
+}
+
+static int freezer_write_period(struct cgroup *cgroup, struct cftype *cft,
+		u64 val)
+{
+	if (!cgroup_lock_live_group(cgroup))
+		return -ENODEV;
+	cgroup_freezer(cgroup)->duty.period_pct_ms = val * 10;

1 second == 1000 millisecond, I guess ;)

I think better to use milliseconds for the interface.

Quoted 36 lines
kernel/cgroup_freezer.c +23 -1
+	cgroup_unlock();
+	wake_up(&freezer_wait);
+
+	return 0;
+}
+
 static struct cftype files[] = {
 	{
 		.name = "state",
 		.read_seq_string = freezer_read,
 		.write_string = freezer_write,
 	},
+	{
+		.name = "duty_ratio_pct",
+		.read_u64 = freezer_read_duty_ratio,
+		.write_u64 = freezer_write_duty_ratio,
+	},
+	{
+		.name = "period_sec",
+		.read_u64 = freezer_read_period,
+		.write_u64 = freezer_write_period,
+	},
+
 };
 
+#define FREEZER_KH_PREFIX  "freezer_"
 static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup)
 {
+	int ret = 0;
+	char thread_name[32];
+	struct freezer *freezer;
+
 	if (!cgroup->parent)
 		return 0;
-	return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
+

kernel/cgroup_freezer.c static int freezer_write(struct cgroup *cgroup, · line 488

Quoted 10 lines
kernel/cgroup_freezer.c +10 -0
+	freezer = cgroup_freezer(cgroup);
+	ret = cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
+
+	snprintf(thread_name, 32, "%s%s", FREEZER_KH_PREFIX,
+		cgroup->dentry->d_name.name);
+	freezer->fkh = kthread_run(freezer_kh, (void *)cgroup, thread_name);
+	if (IS_ERR(freezer_task))
+		pr_debug("%s failed to create %s\n", __func__, thread_name);
+
+	return ret;

Hm.. I think it's waste of resources creates one threads for every cgroup. In most cases auto freezing will not be enabled. Can we create a thread when it's really needed (ratio != 0 && period != 0)?
Can we use delayed workqueues instead of separate thread?

Quoted 6 lines
kernel/cgroup_freezer.c +0 -0
 }
 
 struct cgroup_subsys freezer_subsys = {
Signature · 1 line
1.7.0.4
Signature · 1 line
 Kirill A. Shutemov
#4 lore raw
In reply to [email protected]
Quoted 168 lines · On Mon, Feb 07, 2011 at 10:13:03AM -0800, [email protected] wrote:

From: Jacob Pan <[email protected]>

Freezer subsystem is used to manage batch jobs which can start stop at the same time. However, sometime it is desirable to let the kernel manage the freezer state automatically with a given duty ratio.
For example, if we want to reduce the time that backgroup apps are allowed to run we can put them into a freezer subsystem and set the kernel to turn them THAWED/FROZEN at given duty ratio.

This patch introduces two file nodes under cgroup
freezer.duty_ratio_pct and freezer.period_sec

Usage example: set period to be 5 seconds and frozen duty ratio 90% [root@localhost aoa]# echo 90 > freezer.duty_ratio_pct [root@localhost aoa]# echo 5 > freezer.period_sec

---
 Documentation/cgroups/freezer-subsystem.txt |   23 ++++++
 kernel/cgroup_freezer.c                     |  109 ++++++++++++++++++++++++++-
 2 files changed, 130 insertions(+), 2 deletions(-)
Documentation/cgroups/freezer-subsystem.txt +23 -0
index 41f37fe..2bc1b98 100644
@@ -100,3 +100,26 @@ things happens:
 		and returns EINVAL)
 	3) The tasks that blocked the cgroup from entering the "FROZEN"
 		state disappear from the cgroup's set of tasks.
+
+In embedded systems, it is desirable to manage group of applications
+for power saving. E.g. tasks that are not in the foreground may be
+frozen unfrozen periodically to save power without affecting user
+experience. In this case, user/management software can attach tasks
+into freezer cgroup then specify duty ratio and period that the
+managed tasks are allowed to run.
+
+Usage example:
+Assuming freezer cgroup is already mounted, application being managed
+are included the "tasks" file node of the given freezer cgroup.
+To make the tasks frozen at 90% of the time every 5 seconds, do:
+
+[root@localhost ]# echo 90 > freezer.duty_ratio_pct
+[root@localhost ]# echo 5 > freezer.period_sec
+
+After that, the application in this freezer cgroup will only be
+allowed to run at the following pattern.
+     __                    __                    __
+    |  |<-- 90% frozen -->|  |                  |  |
+____|  |__________________|  |__________________|  |_____
+
+    |<---- 5 seconds ---->|
kernel/cgroup_freezer.c +64 -1
index e7bebb7..928f2ab 100644
@@ -21,6 +21,7 @@
 #include <linux/uaccess.h>
 #include <linux/freezer.h>
 #include <linux/seq_file.h>
+#include <linux/kthread.h>
 
 enum freezer_state {
 	CGROUP_THAWED = 0,
@@ -28,12 +29,23 @@ enum freezer_state {
 	CGROUP_FROZEN,
 };
 
+struct freezer_duty {
+	u32 ratio; /* percentage of time frozen */
+	u32 period_pct_ms; /* one percent of the period in miliseconds */
+};
+
 struct freezer {
 	struct cgroup_subsys_state css;
 	enum freezer_state state;
+	struct freezer_duty duty;
+	struct task_struct *fkh;
 	spinlock_t lock; /* protects _writes_ to state */
 };
 
+static struct task_struct *freezer_task;
+static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer);
+static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer);
+
 static inline struct freezer *cgroup_freezer(
 		struct cgroup *cgroup)
 {
@@ -63,6 +75,35 @@ int cgroup_freezing_or_frozen(struct task_struct *task)
 	return result;
 }
 
+static DECLARE_WAIT_QUEUE_HEAD(freezer_wait);
+
+static int freezer_kh(void *data)
+{
+	struct cgroup *cgroup = (struct cgroup *)data;
+	struct freezer *freezer = cgroup_freezer(cgroup);
+
+	do {
+		if (freezer->duty.ratio < 100 && freezer->duty.ratio >= 0 &&
+			freezer->duty.period_pct_ms) {
+			if (try_to_freeze_cgroup(cgroup, freezer))
+				pr_info("cannot freeze\n");
+			msleep(freezer->duty.period_pct_ms *
+				freezer->duty.ratio);
+			unfreeze_cgroup(cgroup, freezer);
+			msleep(freezer->duty.period_pct_ms *
+				(100 - freezer->duty.ratio));
+		} else if (freezer->duty.ratio == 100) {
+			if (try_to_freeze_cgroup(cgroup, freezer))
+				pr_info("cannot freeze\n");
+			sleep_on(&freezer_wait);
+		} else {
+			sleep_on(&freezer_wait);
+			pr_debug("freezer thread wake up\n");
+		}
+	} while (!kthread_should_stop());
+	return 0;
+}
+
 /*
  * cgroups_write_string() limits the size of freezer state strings to
  * CGROUP_LOCAL_BUFFER_SIZE
@@ -150,7 +191,11 @@ static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss,
 static void freezer_destroy(struct cgroup_subsys *ss,
 			    struct cgroup *cgroup)
 {
-	kfree(cgroup_freezer(cgroup));
+	struct freezer *freezer;
+
+	freezer = cgroup_freezer(cgroup);
+	kthread_stop(freezer->fkh);
+	kfree(freezer);
 }
 
 /*
@@ -282,6 +327,16 @@ static int freezer_read(struct cgroup *cgroup, struct cftype *cft,
 	return 0;
 }
 
+static u64 freezer_read_duty_ratio(struct cgroup *cgroup, struct cftype *cft)
+{
+	return cgroup_freezer(cgroup)->duty.ratio;
+}
+
+static u64 freezer_read_period(struct cgroup *cgroup, struct cftype *cft)
+{
+	return cgroup_freezer(cgroup)->duty.period_pct_ms / 10;
+}
+
 static int try_to_freeze_cgroup(struct cgroup *cgroup, struct freezer *freezer)
 {
 	struct cgroup_iter it;
@@ -368,19 +423,69 @@ static int freezer_write(struct cgroup *cgroup,
 	return retval;
 }
 
+static int freezer_write_duty_ratio(struct cgroup *cgroup, struct cftype *cft,
+		u64 val)
+{
+	if (!cgroup_lock_live_group(cgroup))
+		return -ENODEV;
+	cgroup_freezer(cgroup)->duty.ratio = val;
+	cgroup_unlock();
+	wake_up(&freezer_wait);

kernel/cgroup_freezer.c static int freezer_write(struct cgroup *cgroup, · line 443

Quoted 10 lines
kernel/cgroup_freezer.c +10 -0
+
+	return 0;
+}
+
+static int freezer_write_period(struct cgroup *cgroup, struct cftype *cft,
+		u64 val)
+{
+	if (!cgroup_lock_live_group(cgroup))
+		return -ENODEV;
+	cgroup_freezer(cgroup)->duty.period_pct_ms = val * 10;

1 second == 1000 millisecond, I guess ;)

I think better to use milliseconds for the interface.

Quoted 36 lines
kernel/cgroup_freezer.c +23 -1
+	cgroup_unlock();
+	wake_up(&freezer_wait);
+
+	return 0;
+}
+
 static struct cftype files[] = {
 	{
 		.name = "state",
 		.read_seq_string = freezer_read,
 		.write_string = freezer_write,
 	},
+	{
+		.name = "duty_ratio_pct",
+		.read_u64 = freezer_read_duty_ratio,
+		.write_u64 = freezer_write_duty_ratio,
+	},
+	{
+		.name = "period_sec",
+		.read_u64 = freezer_read_period,
+		.write_u64 = freezer_write_period,
+	},
+
 };
 
+#define FREEZER_KH_PREFIX  "freezer_"
 static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup)
 {
+	int ret = 0;
+	char thread_name[32];
+	struct freezer *freezer;
+
 	if (!cgroup->parent)
 		return 0;
-	return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
+

kernel/cgroup_freezer.c static int freezer_write(struct cgroup *cgroup, · line 488

Quoted 10 lines
kernel/cgroup_freezer.c +10 -0
+	freezer = cgroup_freezer(cgroup);
+	ret = cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files));
+
+	snprintf(thread_name, 32, "%s%s", FREEZER_KH_PREFIX,
+		cgroup->dentry->d_name.name);
+	freezer->fkh = kthread_run(freezer_kh, (void *)cgroup, thread_name);
+	if (IS_ERR(freezer_task))
+		pr_debug("%s failed to create %s\n", __func__, thread_name);
+
+	return ret;

Hm.. I think it's waste of resources creates one threads for every cgroup. In most cases auto freezing will not be enabled. Can we create a thread when it's really needed (ratio != 0 && period != 0)?
Can we use delayed workqueues instead of separate thread?

Quoted 6 lines
kernel/cgroup_freezer.c +0 -0
 }
 
 struct cgroup_subsys freezer_subsys = {
Signature · 1 line
1.7.0.4
Signature · 1 line
 Kirill A. Shutemov

A message that is not in the archive (sent off-list or not yet received): [email protected]

#6 lore raw
Quoted 179 lines · On Mon, 7 Feb 2011 20:29:29 +0200 "Kirill A. Shutemov"…
Quoted 177 lines · On Mon, Feb 07, 2011 at 10:13:03AM -0800…

From: Jacob Pan <jacob.jun.pan-VuQAYsv1563Yd54FQh9/[email protected]>

Freezer subsystem is used to manage batch jobs which can start stop at the same time. However, sometime it is desirable to let the kernel manage the freezer state automatically with a given duty ratio.
For example, if we want to reduce the time that backgroup apps are allowed to run we can put them into a freezer subsystem and set the kernel to turn them THAWED/FROZEN at given duty ratio.

This patch introduces two file nodes under cgroup
freezer.duty_ratio_pct and freezer.period_sec

Usage example: set period to be 5 seconds and frozen duty ratio 90% [root@localhost aoa]# echo 90 > freezer.duty_ratio_pct [root@localhost aoa]# echo 5 > freezer.period_sec

---
 Documentation/cgroups/freezer-subsystem.txt |   23 ++++++
 kernel/cgroup_freezer.c                     |  109

++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 2 deletions(-)

(quoted diff) +0 -0

b/Documentation/cgroups/freezer-subsystem.txt index 41f37fe..2bc1b98 100644 --- a/Documentation/cgroups/freezer-subsystem.txt +++ b/Documentation/cgroups/freezer-subsystem.txt @@ -100,3 +100,26 @@ things happens: and returns EINVAL)
3) The tasks that blocked the cgroup from entering the
"FROZEN" state disappear from the cgroup's set of tasks.

(quoted diff) +2 -0
+
+In embedded systems, it is desirable to manage group of

applications +for power saving. E.g. tasks that are not in the foreground may be +frozen unfrozen periodically to save power without affecting user +experience. In this case, user/management software can attach tasks +into freezer cgroup then specify duty ratio and period that the +managed tasks are allowed to run.

Documentation/cgroups/freezer-subsystem.txt +3 -0
+
+Usage example:
+Assuming freezer cgroup is already mounted, application being

managed +are included the "tasks" file node of the given freezer cgroup. +To make the tasks frozen at 90% of the time every 5 seconds, do: +
+[root@localhost ]# echo 90 > freezer.duty_ratio_pct +[root@localhost ]# echo 5 > freezer.period_sec

Documentation/cgroups/freezer-subsystem.txt +8 -0
+
+After that, the application in this freezer cgroup will only be
+allowed to run at the following pattern.
+     __                    __                    __
+    |  |<-- 90% frozen -->|  |                  |  |
+____|  |__________________|  |__________________|  |_____
+
+    |<---- 5 seconds ---->|
kernel/cgroup_freezer.c +9 -0
index e7bebb7..928f2ab 100644
@@ -21,6 +21,7 @@
 #include <linux/uaccess.h>
 #include <linux/freezer.h>
 #include <linux/seq_file.h>
+#include <linux/kthread.h>
 
 enum freezer_state {
 	CGROUP_THAWED = 0,
@@ -28,12 +29,23 @@ enum freezer_state {
 	CGROUP_FROZEN,
 };
 
+struct freezer_duty {
+	u32 ratio; /* percentage of time frozen */
+	u32 period_pct_ms; /* one percent of the period in
 miliseconds */ +};
+
 struct freezer {
 	struct cgroup_subsys_state css;
 	enum freezer_state state;
+	struct freezer_duty duty;
+	struct task_struct *fkh;
 	spinlock_t lock; /* protects _writes_ to state */
 };
 
+static struct task_struct *freezer_task;
+static int try_to_freeze_cgroup(struct cgroup *cgroup, struct

freezer *freezer); +static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer); +
static inline struct freezer *cgroup_freezer(

kernel/cgroup_freezer.c +9 -0
 		struct cgroup *cgroup)
 {
@@ -63,6 +75,35 @@ int cgroup_freezing_or_frozen(struct task_struct
 *task) return result;
 }
 
+static DECLARE_WAIT_QUEUE_HEAD(freezer_wait);
+
+static int freezer_kh(void *data)
+{
+	struct cgroup *cgroup = (struct cgroup *)data;
+	struct freezer *freezer = cgroup_freezer(cgroup);
+
+	do {
+		if (freezer->duty.ratio < 100 &&

freezer->duty.ratio >= 0 &&

kernel/cgroup_freezer.c +20 -0
+			freezer->duty.period_pct_ms) {
+			if (try_to_freeze_cgroup(cgroup, freezer))
+				pr_info("cannot freeze\n");
+			msleep(freezer->duty.period_pct_ms *
+				freezer->duty.ratio);
+			unfreeze_cgroup(cgroup, freezer);
+			msleep(freezer->duty.period_pct_ms *
+				(100 - freezer->duty.ratio));
+		} else if (freezer->duty.ratio == 100) {
+			if (try_to_freeze_cgroup(cgroup, freezer))
+				pr_info("cannot freeze\n");
+			sleep_on(&freezer_wait);
+		} else {
+			sleep_on(&freezer_wait);
+			pr_debug("freezer thread wake up\n");
+		}
+	} while (!kthread_should_stop());
+	return 0;
+}
+
 /*
  * cgroups_write_string() limits the size of freezer state strings

to
* CGROUP_LOCAL_BUFFER_SIZE

kernel/cgroup_freezer.c +13 -1
@@ -150,7 +191,11 @@ static struct cgroup_subsys_state
 *freezer_create(struct cgroup_subsys *ss, static void
 freezer_destroy(struct cgroup_subsys *ss, struct cgroup *cgroup)
 {
-	kfree(cgroup_freezer(cgroup));
+	struct freezer *freezer;
+
+	freezer = cgroup_freezer(cgroup);
+	kthread_stop(freezer->fkh);
+	kfree(freezer);
 }
 
 /*
@@ -282,6 +327,16 @@ static int freezer_read(struct cgroup *cgroup,
 struct cftype *cft, return 0;
 }
 
+static u64 freezer_read_duty_ratio(struct cgroup *cgroup, struct
 cftype *cft) +{
+	return cgroup_freezer(cgroup)->duty.ratio;
+}
+
+static u64 freezer_read_period(struct cgroup *cgroup, struct
 cftype *cft) +{
+	return cgroup_freezer(cgroup)->duty.period_pct_ms / 10;
+}
+
 static int try_to_freeze_cgroup(struct cgroup *cgroup, struct

freezer *freezer) {

kernel/cgroup_freezer.c +13 -0
 	struct cgroup_iter it;
@@ -368,19 +423,69 @@ static int freezer_write(struct cgroup
 *cgroup, return retval;
 }
 
+static int freezer_write_duty_ratio(struct cgroup *cgroup, struct
 cftype *cft,
+		u64 val)
+{
+	if (!cgroup_lock_live_group(cgroup))
+		return -ENODEV;
+	cgroup_freezer(cgroup)->duty.ratio = val;
+	cgroup_unlock();
+	wake_up(&freezer_wait);
+
+	return 0;
+}
+
+static int freezer_write_period(struct cgroup *cgroup, struct
Quoted 8 lines

kernel/cgroup_freezer.c static int freezer_write(struct cgroup *cgroup, · line 443

Quoted 6 lines
kernel/cgroup_freezer.c +5 -0
 cftype *cft,
+		u64 val)
+{
+	if (!cgroup_lock_live_group(cgroup))
+		return -ENODEV;
+	cgroup_freezer(cgroup)->duty.period_pct_ms = val * 10;

1 second == 1000 millisecond, I guess ;)

here is 1% of 1 second, so 1000 / 100 = 10ms.

Quoted 3 lines

I think better to use milliseconds for the interface.

ok, I will change that back. I don't have preference.

Quoted 43 lines
Quoted 43 lines
kernel/cgroup_freezer.c +22 -1
+	cgroup_unlock();
+	wake_up(&freezer_wait);
+
+	return 0;
+}
+
 static struct cftype files[] = {
 	{
 		.name = "state",
 		.read_seq_string = freezer_read,
 		.write_string = freezer_write,
 	},
+	{
+		.name = "duty_ratio_pct",
+		.read_u64 = freezer_read_duty_ratio,
+		.write_u64 = freezer_write_duty_ratio,
+	},
+	{
+		.name = "period_sec",
+		.read_u64 = freezer_read_period,
+		.write_u64 = freezer_write_period,
+	},
+
 };
 
+#define FREEZER_KH_PREFIX  "freezer_"
 static int freezer_populate(struct cgroup_subsys *ss, struct
 cgroup *cgroup) {
+	int ret = 0;
+	char thread_name[32];
+	struct freezer *freezer;
+
 	if (!cgroup->parent)
 		return 0;
-	return cgroup_add_files(cgroup, ss, files,

ARRAY_SIZE(files)); +

kernel/cgroup_freezer.c +2 -0
+	freezer = cgroup_freezer(cgroup);
+	ret = cgroup_add_files(cgroup, ss, files,

ARRAY_SIZE(files)); +

kernel/cgroup_freezer.c +3 -0
+	snprintf(thread_name, 32, "%s%s", FREEZER_KH_PREFIX,
+		cgroup->dentry->d_name.name);
+	freezer->fkh = kthread_run(freezer_kh, (void *)cgroup,

thread_name);

Quoted 8 lines

kernel/cgroup_freezer.c static int freezer_write(struct cgroup *cgroup, · line 488

Quoted 4 lines
kernel/cgroup_freezer.c +2 -0
+	if (IS_ERR(freezer_task))
+		pr_debug("%s failed to create %s\n", __func__,

thread_name); +

kernel/cgroup_freezer.c +1 -0
+	return ret;

Hm.. I think it's waste of resources creates one threads for every cgroup. In most cases auto freezing will not be enabled. Can we create a thread when it's really needed (ratio != 0 && period != 0)?

good point, i will fix that.

Quoted 1 line

Can we use delayed workqueues instead of separate thread?

I guess you mean having one private workqueue for all freezer cgroups. I think it is doable and save memory vs per active freezer kthread, but I am not sure the effect on concurrency. I will give that a try.

Thanks

author
#7 lore raw
In reply to Kirill A. Shutemov
Quoted 179 lines · On Mon, 7 Feb 2011 20:29:29 +0200 "Kirill A. Shutemov" <[email protected]>…
Quoted 177 lines · On Mon, Feb 07, 2011 at 10:13:03AM -0800, [email protected] wrote:

From: Jacob Pan <[email protected]>

Freezer subsystem is used to manage batch jobs which can start stop at the same time. However, sometime it is desirable to let the kernel manage the freezer state automatically with a given duty ratio.
For example, if we want to reduce the time that backgroup apps are allowed to run we can put them into a freezer subsystem and set the kernel to turn them THAWED/FROZEN at given duty ratio.

This patch introduces two file nodes under cgroup
freezer.duty_ratio_pct and freezer.period_sec

Usage example: set period to be 5 seconds and frozen duty ratio 90% [root@localhost aoa]# echo 90 > freezer.duty_ratio_pct [root@localhost aoa]# echo 5 > freezer.period_sec

---
 Documentation/cgroups/freezer-subsystem.txt |   23 ++++++
 kernel/cgroup_freezer.c                     |  109

++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 2 deletions(-)

(quoted diff) +0 -0

b/Documentation/cgroups/freezer-subsystem.txt index 41f37fe..2bc1b98 100644 --- a/Documentation/cgroups/freezer-subsystem.txt +++ b/Documentation/cgroups/freezer-subsystem.txt @@ -100,3 +100,26 @@ things happens: and returns EINVAL)
3) The tasks that blocked the cgroup from entering the
"FROZEN" state disappear from the cgroup's set of tasks.

(quoted diff) +2 -0
+
+In embedded systems, it is desirable to manage group of

applications +for power saving. E.g. tasks that are not in the foreground may be +frozen unfrozen periodically to save power without affecting user +experience. In this case, user/management software can attach tasks +into freezer cgroup then specify duty ratio and period that the +managed tasks are allowed to run.

Documentation/cgroups/freezer-subsystem.txt +3 -0
+
+Usage example:
+Assuming freezer cgroup is already mounted, application being

managed +are included the "tasks" file node of the given freezer cgroup. +To make the tasks frozen at 90% of the time every 5 seconds, do: +
+[root@localhost ]# echo 90 > freezer.duty_ratio_pct +[root@localhost ]# echo 5 > freezer.period_sec

Documentation/cgroups/freezer-subsystem.txt +8 -0
+
+After that, the application in this freezer cgroup will only be
+allowed to run at the following pattern.
+     __                    __                    __
+    |  |<-- 90% frozen -->|  |                  |  |
+____|  |__________________|  |__________________|  |_____
+
+    |<---- 5 seconds ---->|
kernel/cgroup_freezer.c +9 -0
index e7bebb7..928f2ab 100644
@@ -21,6 +21,7 @@
 #include <linux/uaccess.h>
 #include <linux/freezer.h>
 #include <linux/seq_file.h>
+#include <linux/kthread.h>
 
 enum freezer_state {
 	CGROUP_THAWED = 0,
@@ -28,12 +29,23 @@ enum freezer_state {
 	CGROUP_FROZEN,
 };
 
+struct freezer_duty {
+	u32 ratio; /* percentage of time frozen */
+	u32 period_pct_ms; /* one percent of the period in
 miliseconds */ +};
+
 struct freezer {
 	struct cgroup_subsys_state css;
 	enum freezer_state state;
+	struct freezer_duty duty;
+	struct task_struct *fkh;
 	spinlock_t lock; /* protects _writes_ to state */
 };
 
+static struct task_struct *freezer_task;
+static int try_to_freeze_cgroup(struct cgroup *cgroup, struct

freezer *freezer); +static void unfreeze_cgroup(struct cgroup *cgroup, struct freezer *freezer); +
static inline struct freezer *cgroup_freezer(

kernel/cgroup_freezer.c +9 -0
 		struct cgroup *cgroup)
 {
@@ -63,6 +75,35 @@ int cgroup_freezing_or_frozen(struct task_struct
 *task) return result;
 }
 
+static DECLARE_WAIT_QUEUE_HEAD(freezer_wait);
+
+static int freezer_kh(void *data)
+{
+	struct cgroup *cgroup = (struct cgroup *)data;
+	struct freezer *freezer = cgroup_freezer(cgroup);
+
+	do {
+		if (freezer->duty.ratio < 100 &&

freezer->duty.ratio >= 0 &&

kernel/cgroup_freezer.c +20 -0
+			freezer->duty.period_pct_ms) {
+			if (try_to_freeze_cgroup(cgroup, freezer))
+				pr_info("cannot freeze\n");
+			msleep(freezer->duty.period_pct_ms *
+				freezer->duty.ratio);
+			unfreeze_cgroup(cgroup, freezer);
+			msleep(freezer->duty.period_pct_ms *
+				(100 - freezer->duty.ratio));
+		} else if (freezer->duty.ratio == 100) {
+			if (try_to_freeze_cgroup(cgroup, freezer))
+				pr_info("cannot freeze\n");
+			sleep_on(&freezer_wait);
+		} else {
+			sleep_on(&freezer_wait);
+			pr_debug("freezer thread wake up\n");
+		}
+	} while (!kthread_should_stop());
+	return 0;
+}
+
 /*
  * cgroups_write_string() limits the size of freezer state strings

to
* CGROUP_LOCAL_BUFFER_SIZE

kernel/cgroup_freezer.c +13 -1
@@ -150,7 +191,11 @@ static struct cgroup_subsys_state
 *freezer_create(struct cgroup_subsys *ss, static void
 freezer_destroy(struct cgroup_subsys *ss, struct cgroup *cgroup)
 {
-	kfree(cgroup_freezer(cgroup));
+	struct freezer *freezer;
+
+	freezer = cgroup_freezer(cgroup);
+	kthread_stop(freezer->fkh);
+	kfree(freezer);
 }
 
 /*
@@ -282,6 +327,16 @@ static int freezer_read(struct cgroup *cgroup,
 struct cftype *cft, return 0;
 }
 
+static u64 freezer_read_duty_ratio(struct cgroup *cgroup, struct
 cftype *cft) +{
+	return cgroup_freezer(cgroup)->duty.ratio;
+}
+
+static u64 freezer_read_period(struct cgroup *cgroup, struct
 cftype *cft) +{
+	return cgroup_freezer(cgroup)->duty.period_pct_ms / 10;
+}
+
 static int try_to_freeze_cgroup(struct cgroup *cgroup, struct

freezer *freezer) {

kernel/cgroup_freezer.c +13 -0
 	struct cgroup_iter it;
@@ -368,19 +423,69 @@ static int freezer_write(struct cgroup
 *cgroup, return retval;
 }
 
+static int freezer_write_duty_ratio(struct cgroup *cgroup, struct
 cftype *cft,
+		u64 val)
+{
+	if (!cgroup_lock_live_group(cgroup))
+		return -ENODEV;
+	cgroup_freezer(cgroup)->duty.ratio = val;
+	cgroup_unlock();
+	wake_up(&freezer_wait);
+
+	return 0;
+}
+
+static int freezer_write_period(struct cgroup *cgroup, struct
Quoted 8 lines

kernel/cgroup_freezer.c static int freezer_write(struct cgroup *cgroup, · line 443

Quoted 6 lines
kernel/cgroup_freezer.c +5 -0
 cftype *cft,
+		u64 val)
+{
+	if (!cgroup_lock_live_group(cgroup))
+		return -ENODEV;
+	cgroup_freezer(cgroup)->duty.period_pct_ms = val * 10;

1 second == 1000 millisecond, I guess ;)

here is 1% of 1 second, so 1000 / 100 = 10ms.

Quoted 3 lines

I think better to use milliseconds for the interface.

ok, I will change that back. I don't have preference.

Quoted 43 lines
Quoted 43 lines
kernel/cgroup_freezer.c +22 -1
+	cgroup_unlock();
+	wake_up(&freezer_wait);
+
+	return 0;
+}
+
 static struct cftype files[] = {
 	{
 		.name = "state",
 		.read_seq_string = freezer_read,
 		.write_string = freezer_write,
 	},
+	{
+		.name = "duty_ratio_pct",
+		.read_u64 = freezer_read_duty_ratio,
+		.write_u64 = freezer_write_duty_ratio,
+	},
+	{
+		.name = "period_sec",
+		.read_u64 = freezer_read_period,
+		.write_u64 = freezer_write_period,
+	},
+
 };
 
+#define FREEZER_KH_PREFIX  "freezer_"
 static int freezer_populate(struct cgroup_subsys *ss, struct
 cgroup *cgroup) {
+	int ret = 0;
+	char thread_name[32];
+	struct freezer *freezer;
+
 	if (!cgroup->parent)
 		return 0;
-	return cgroup_add_files(cgroup, ss, files,

ARRAY_SIZE(files)); +

kernel/cgroup_freezer.c +2 -0
+	freezer = cgroup_freezer(cgroup);
+	ret = cgroup_add_files(cgroup, ss, files,

ARRAY_SIZE(files)); +

kernel/cgroup_freezer.c +3 -0
+	snprintf(thread_name, 32, "%s%s", FREEZER_KH_PREFIX,
+		cgroup->dentry->d_name.name);
+	freezer->fkh = kthread_run(freezer_kh, (void *)cgroup,

thread_name);

Quoted 8 lines

kernel/cgroup_freezer.c static int freezer_write(struct cgroup *cgroup, · line 488

Quoted 4 lines
kernel/cgroup_freezer.c +2 -0
+	if (IS_ERR(freezer_task))
+		pr_debug("%s failed to create %s\n", __func__,

thread_name); +

kernel/cgroup_freezer.c +1 -0
+	return ret;

Hm.. I think it's waste of resources creates one threads for every cgroup. In most cases auto freezing will not be enabled. Can we create a thread when it's really needed (ratio != 0 && period != 0)?

good point, i will fix that.

Quoted 1 line

Can we use delayed workqueues instead of separate thread?

I guess you mean having one private workqueue for all freezer cgroups. I think it is doable and save memory vs per active freezer kthread, but I am not sure the effect on concurrency. I will give that a try.

Thanks