Commit d842de87 authored by Srivatsa Vaddagiri's avatar Srivatsa Vaddagiri Committed by Ingo Molnar

sched: cpu accounting controller (V2)

Commit cfb52856 removed a useful feature for
us, which provided a cpu accounting resource controller.  This feature would be
useful if someone wants to group tasks only for accounting purpose and doesnt
really want to exercise any control over their cpu consumption.

The patch below reintroduces the feature. It is based on Paul Menage's
original patch (Commit 62d0df64), with
these differences:

        - Removed load average information. I felt it needs more thought (esp
	  to deal with SMP and virtualized platforms) and can be added for
	  2.6.25 after more discussions.
        - Convert group cpu usage to be nanosecond accurate (as rest of the cfs
	  stats are) and invoke cpuacct_charge() from the respective scheduler
	  classes
	- Make accounting scalable on SMP systems by splitting the usage
	  counter to be per-cpu
	- Move the code from kernel/cpu_acct.c to kernel/sched.c (since the
	  code is not big enough to warrant a new file and also this rightly
	  needs to live inside the scheduler. Also things like accessing
	  rq->lock while reading cpu usage becomes easier if the code lived in
	  kernel/sched.c)

The patch also modifies the cpu controller not to provide the same accounting
information.
Tested-by: default avatarBalbir Singh <balbir@linux.vnet.ibm.com>

 Tested the patches on top of 2.6.24-rc3. The patches work fine. Ran
 some simple tests like cpuspin (spin on the cpu), ran several tasks in
 the same group and timed them. Compared their time stamps with
 cpuacct.usage.
Signed-off-by: default avatarSrivatsa Vaddagiri <vatsa@linux.vnet.ibm.com>
Signed-off-by: default avatarBalbir Singh <balbir@linux.vnet.ibm.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 92d499d9
...@@ -30,3 +30,10 @@ SUBSYS(cpu_cgroup) ...@@ -30,3 +30,10 @@ SUBSYS(cpu_cgroup)
#endif #endif
/* */ /* */
#ifdef CONFIG_CGROUP_CPUACCT
SUBSYS(cpuacct)
#endif
/* */
...@@ -354,6 +354,13 @@ config FAIR_CGROUP_SCHED ...@@ -354,6 +354,13 @@ config FAIR_CGROUP_SCHED
endchoice endchoice
config CGROUP_CPUACCT
bool "Simple CPU accounting cgroup subsystem"
depends on CGROUPS
help
Provides a simple Resource Controller for monitoring the
total CPU consumed by the tasks in a cgroup
config SYSFS_DEPRECATED config SYSFS_DEPRECATED
bool "Create deprecated sysfs files" bool "Create deprecated sysfs files"
default y default y
......
...@@ -854,6 +854,12 @@ iter_move_one_task(struct rq *this_rq, int this_cpu, struct rq *busiest, ...@@ -854,6 +854,12 @@ iter_move_one_task(struct rq *this_rq, int this_cpu, struct rq *busiest,
struct rq_iterator *iterator); struct rq_iterator *iterator);
#endif #endif
#ifdef CONFIG_CGROUP_CPUACCT
static void cpuacct_charge(struct task_struct *tsk, u64 cputime);
#else
static inline void cpuacct_charge(struct task_struct *tsk, u64 cputime) {}
#endif
#include "sched_stats.h" #include "sched_stats.h"
#include "sched_idletask.c" #include "sched_idletask.c"
#include "sched_fair.c" #include "sched_fair.c"
...@@ -7221,38 +7227,12 @@ static u64 cpu_shares_read_uint(struct cgroup *cgrp, struct cftype *cft) ...@@ -7221,38 +7227,12 @@ static u64 cpu_shares_read_uint(struct cgroup *cgrp, struct cftype *cft)
return (u64) tg->shares; return (u64) tg->shares;
} }
static u64 cpu_usage_read(struct cgroup *cgrp, struct cftype *cft)
{
struct task_group *tg = cgroup_tg(cgrp);
unsigned long flags;
u64 res = 0;
int i;
for_each_possible_cpu(i) {
/*
* Lock to prevent races with updating 64-bit counters
* on 32-bit arches.
*/
spin_lock_irqsave(&cpu_rq(i)->lock, flags);
res += tg->se[i]->sum_exec_runtime;
spin_unlock_irqrestore(&cpu_rq(i)->lock, flags);
}
/* Convert from ns to ms */
do_div(res, NSEC_PER_MSEC);
return res;
}
static struct cftype cpu_files[] = { static struct cftype cpu_files[] = {
{ {
.name = "shares", .name = "shares",
.read_uint = cpu_shares_read_uint, .read_uint = cpu_shares_read_uint,
.write_uint = cpu_shares_write_uint, .write_uint = cpu_shares_write_uint,
}, },
{
.name = "usage",
.read_uint = cpu_usage_read,
},
}; };
static int cpu_cgroup_populate(struct cgroup_subsys *ss, struct cgroup *cont) static int cpu_cgroup_populate(struct cgroup_subsys *ss, struct cgroup *cont)
...@@ -7272,3 +7252,126 @@ struct cgroup_subsys cpu_cgroup_subsys = { ...@@ -7272,3 +7252,126 @@ struct cgroup_subsys cpu_cgroup_subsys = {
}; };
#endif /* CONFIG_FAIR_CGROUP_SCHED */ #endif /* CONFIG_FAIR_CGROUP_SCHED */
#ifdef CONFIG_CGROUP_CPUACCT
/*
* CPU accounting code for task groups.
*
* Based on the work by Paul Menage (menage@google.com) and Balbir Singh
* (balbir@in.ibm.com).
*/
/* track cpu usage of a group of tasks */
struct cpuacct {
struct cgroup_subsys_state css;
/* cpuusage holds pointer to a u64-type object on every cpu */
u64 *cpuusage;
};
struct cgroup_subsys cpuacct_subsys;
/* return cpu accounting group corresponding to this container */
static inline struct cpuacct *cgroup_ca(struct cgroup *cont)
{
return container_of(cgroup_subsys_state(cont, cpuacct_subsys_id),
struct cpuacct, css);
}
/* return cpu accounting group to which this task belongs */
static inline struct cpuacct *task_ca(struct task_struct *tsk)
{
return container_of(task_subsys_state(tsk, cpuacct_subsys_id),
struct cpuacct, css);
}
/* create a new cpu accounting group */
static struct cgroup_subsys_state *cpuacct_create(
struct cgroup_subsys *ss, struct cgroup *cont)
{
struct cpuacct *ca = kzalloc(sizeof(*ca), GFP_KERNEL);
if (!ca)
return ERR_PTR(-ENOMEM);
ca->cpuusage = alloc_percpu(u64);
if (!ca->cpuusage) {
kfree(ca);
return ERR_PTR(-ENOMEM);
}
return &ca->css;
}
/* destroy an existing cpu accounting group */
static void cpuacct_destroy(struct cgroup_subsys *ss,
struct cgroup *cont)
{
struct cpuacct *ca = cgroup_ca(cont);
free_percpu(ca->cpuusage);
kfree(ca);
}
/* return total cpu usage (in nanoseconds) of a group */
static u64 cpuusage_read(struct cgroup *cont, struct cftype *cft)
{
struct cpuacct *ca = cgroup_ca(cont);
u64 totalcpuusage = 0;
int i;
for_each_possible_cpu(i) {
u64 *cpuusage = percpu_ptr(ca->cpuusage, i);
/*
* Take rq->lock to make 64-bit addition safe on 32-bit
* platforms.
*/
spin_lock_irq(&cpu_rq(i)->lock);
totalcpuusage += *cpuusage;
spin_unlock_irq(&cpu_rq(i)->lock);
}
return totalcpuusage;
}
static struct cftype files[] = {
{
.name = "usage",
.read_uint = cpuusage_read,
},
};
static int cpuacct_populate(struct cgroup_subsys *ss, struct cgroup *cont)
{
return cgroup_add_files(cont, ss, files, ARRAY_SIZE(files));
}
/*
* charge this task's execution time to its accounting group.
*
* called with rq->lock held.
*/
static void cpuacct_charge(struct task_struct *tsk, u64 cputime)
{
struct cpuacct *ca;
if (!cpuacct_subsys.active)
return;
ca = task_ca(tsk);
if (ca) {
u64 *cpuusage = percpu_ptr(ca->cpuusage, task_cpu(tsk));
*cpuusage += cputime;
}
}
struct cgroup_subsys cpuacct_subsys = {
.name = "cpuacct",
.create = cpuacct_create,
.destroy = cpuacct_destroy,
.populate = cpuacct_populate,
.subsys_id = cpuacct_subsys_id,
};
#endif /* CONFIG_CGROUP_CPUACCT */
...@@ -351,6 +351,12 @@ static void update_curr(struct cfs_rq *cfs_rq) ...@@ -351,6 +351,12 @@ static void update_curr(struct cfs_rq *cfs_rq)
__update_curr(cfs_rq, curr, delta_exec); __update_curr(cfs_rq, curr, delta_exec);
curr->exec_start = now; curr->exec_start = now;
if (entity_is_task(curr)) {
struct task_struct *curtask = task_of(curr);
cpuacct_charge(curtask, delta_exec);
}
} }
static inline void static inline void
......
...@@ -23,6 +23,7 @@ static void update_curr_rt(struct rq *rq) ...@@ -23,6 +23,7 @@ static void update_curr_rt(struct rq *rq)
curr->se.sum_exec_runtime += delta_exec; curr->se.sum_exec_runtime += delta_exec;
curr->se.exec_start = rq->clock; curr->se.exec_start = rq->clock;
cpuacct_charge(curr, delta_exec);
} }
static void enqueue_task_rt(struct rq *rq, struct task_struct *p, int wakeup) static void enqueue_task_rt(struct rq *rq, struct task_struct *p, int wakeup)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment