| 1 | /* |
| 2 | * kernel/cpu_acct.c - CPU accounting cgroup subsystem |
| 3 | * |
| 4 | * Copyright (C) Google Inc, 2006 |
| 5 | * |
| 6 | * Developed by Paul Menage (menage@google.com) and Balbir Singh |
| 7 | * (balbir@in.ibm.com) |
| 8 | * |
| 9 | */ |
| 10 | |
| 11 | /* |
| 12 | * Example cgroup subsystem for reporting total CPU usage of tasks in a |
| 13 | * cgroup, along with percentage load over a time interval |
| 14 | */ |
| 15 | |
| 16 | #include <linux/module.h> |
| 17 | #include <linux/cgroup.h> |
| 18 | #include <linux/fs.h> |
| 19 | #include <linux/rcupdate.h> |
| 20 | |
| 21 | #include <asm/div64.h> |
| 22 | |
| 23 | struct cpuacct { |
| 24 | struct cgroup_subsys_state css; |
| 25 | spinlock_t lock; |
| 26 | /* total time used by this class */ |
| 27 | cputime64_t time; |
| 28 | |
| 29 | /* time when next load calculation occurs */ |
| 30 | u64 next_interval_check; |
| 31 | |
| 32 | /* time used in current period */ |
| 33 | cputime64_t current_interval_time; |
| 34 | |
| 35 | /* time used in last period */ |
| 36 | cputime64_t last_interval_time; |
| 37 | }; |
| 38 | |
| 39 | struct cgroup_subsys cpuacct_subsys; |
| 40 | |
| 41 | static inline struct cpuacct *cgroup_ca(struct cgroup *cont) |
| 42 | { |
| 43 | return container_of(cgroup_subsys_state(cont, cpuacct_subsys_id), |
| 44 | struct cpuacct, css); |
| 45 | } |
| 46 | |
| 47 | static inline struct cpuacct *task_ca(struct task_struct *task) |
| 48 | { |
| 49 | return container_of(task_subsys_state(task, cpuacct_subsys_id), |
| 50 | struct cpuacct, css); |
| 51 | } |
| 52 | |
| 53 | #define INTERVAL (HZ * 10) |
| 54 | |
| 55 | static inline u64 next_interval_boundary(u64 now) |
| 56 | { |
| 57 | /* calculate the next interval boundary beyond the |
| 58 | * current time */ |
| 59 | do_div(now, INTERVAL); |
| 60 | return (now + 1) * INTERVAL; |
| 61 | } |
| 62 | |
| 63 | static struct cgroup_subsys_state *cpuacct_create( |
| 64 | struct cgroup_subsys *ss, struct cgroup *cont) |
| 65 | { |
| 66 | struct cpuacct *ca = kzalloc(sizeof(*ca), GFP_KERNEL); |
| 67 | |
| 68 | if (!ca) |
| 69 | return ERR_PTR(-ENOMEM); |
| 70 | spin_lock_init(&ca->lock); |
| 71 | ca->next_interval_check = next_interval_boundary(get_jiffies_64()); |
| 72 | return &ca->css; |
| 73 | } |
| 74 | |
| 75 | static void cpuacct_destroy(struct cgroup_subsys *ss, |
| 76 | struct cgroup *cont) |
| 77 | { |
| 78 | kfree(cgroup_ca(cont)); |
| 79 | } |
| 80 | |
| 81 | /* Lazily update the load calculation if necessary. Called with ca locked */ |
| 82 | static void cpuusage_update(struct cpuacct *ca) |
| 83 | { |
| 84 | u64 now = get_jiffies_64(); |
| 85 | |
| 86 | /* If we're not due for an update, return */ |
| 87 | if (ca->next_interval_check > now) |
| 88 | return; |
| 89 | |
| 90 | if (ca->next_interval_check <= (now - INTERVAL)) { |
| 91 | /* If it's been more than an interval since the last |
| 92 | * check, then catch up - the last interval must have |
| 93 | * been zero load */ |
| 94 | ca->last_interval_time = 0; |
| 95 | ca->next_interval_check = next_interval_boundary(now); |
| 96 | } else { |
| 97 | /* If a steal takes the last interval time negative, |
| 98 | * then we just ignore it */ |
| 99 | if ((s64)ca->current_interval_time > 0) |
| 100 | ca->last_interval_time = ca->current_interval_time; |
| 101 | else |
| 102 | ca->last_interval_time = 0; |
| 103 | ca->next_interval_check += INTERVAL; |
| 104 | } |
| 105 | ca->current_interval_time = 0; |
| 106 | } |
| 107 | |
| 108 | static u64 cpuusage_read(struct cgroup *cont, struct cftype *cft) |
| 109 | { |
| 110 | struct cpuacct *ca = cgroup_ca(cont); |
| 111 | u64 time; |
| 112 | |
| 113 | spin_lock_irq(&ca->lock); |
| 114 | cpuusage_update(ca); |
| 115 | time = cputime64_to_jiffies64(ca->time); |
| 116 | spin_unlock_irq(&ca->lock); |
| 117 | |
| 118 | /* Convert 64-bit jiffies to seconds */ |
| 119 | time *= 1000; |
| 120 | do_div(time, HZ); |
| 121 | return time; |
| 122 | } |
| 123 | |
| 124 | static u64 load_read(struct cgroup *cont, struct cftype *cft) |
| 125 | { |
| 126 | struct cpuacct *ca = cgroup_ca(cont); |
| 127 | u64 time; |
| 128 | |
| 129 | /* Find the time used in the previous interval */ |
| 130 | spin_lock_irq(&ca->lock); |
| 131 | cpuusage_update(ca); |
| 132 | time = cputime64_to_jiffies64(ca->last_interval_time); |
| 133 | spin_unlock_irq(&ca->lock); |
| 134 | |
| 135 | /* Convert time to a percentage, to give the load in the |
| 136 | * previous period */ |
| 137 | time *= 100; |
| 138 | do_div(time, INTERVAL); |
| 139 | |
| 140 | return time; |
| 141 | } |
| 142 | |
| 143 | static struct cftype files[] = { |
| 144 | { |
| 145 | .name = "usage", |
| 146 | .read_uint = cpuusage_read, |
| 147 | }, |
| 148 | { |
| 149 | .name = "load", |
| 150 | .read_uint = load_read, |
| 151 | } |
| 152 | }; |
| 153 | |
| 154 | static int cpuacct_populate(struct cgroup_subsys *ss, struct cgroup *cont) |
| 155 | { |
| 156 | return cgroup_add_files(cont, ss, files, ARRAY_SIZE(files)); |
| 157 | } |
| 158 | |
| 159 | void cpuacct_charge(struct task_struct *task, cputime_t cputime) |
| 160 | { |
| 161 | |
| 162 | struct cpuacct *ca; |
| 163 | unsigned long flags; |
| 164 | |
| 165 | if (!cpuacct_subsys.active) |
| 166 | return; |
| 167 | rcu_read_lock(); |
| 168 | ca = task_ca(task); |
| 169 | if (ca) { |
| 170 | spin_lock_irqsave(&ca->lock, flags); |
| 171 | cpuusage_update(ca); |
| 172 | ca->time = cputime64_add(ca->time, cputime); |
| 173 | ca->current_interval_time = |
| 174 | cputime64_add(ca->current_interval_time, cputime); |
| 175 | spin_unlock_irqrestore(&ca->lock, flags); |
| 176 | } |
| 177 | rcu_read_unlock(); |
| 178 | } |
| 179 | |
| 180 | struct cgroup_subsys cpuacct_subsys = { |
| 181 | .name = "cpuacct", |
| 182 | .create = cpuacct_create, |
| 183 | .destroy = cpuacct_destroy, |
| 184 | .populate = cpuacct_populate, |
| 185 | .subsys_id = cpuacct_subsys_id, |
| 186 | }; |