diff options
-rw-r--r-- | include/linux/res_counter.h | 102 | ||||
-rw-r--r-- | init/Kconfig | 7 | ||||
-rw-r--r-- | kernel/Makefile | 1 | ||||
-rw-r--r-- | kernel/res_counter.c | 120 |
4 files changed, 230 insertions, 0 deletions
diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h new file mode 100644 index 000000000000..eeb3f7749772 --- /dev/null +++ b/include/linux/res_counter.h @@ -0,0 +1,102 @@ +#ifndef __RES_COUNTER_H__ +#define __RES_COUNTER_H__ + +/* + * Resource Counters + * Contain common data types and routines for resource accounting + * + * Copyright 2007 OpenVZ SWsoft Inc + * + * Author: Pavel Emelianov <xemul@openvz.org> + * + */ + +#include <linux/cgroup.h> + +/* + * The core object. the cgroup that wishes to account for some + * resource may include this counter into its structures and use + * the helpers described beyond + */ + +struct res_counter { + /* + * the current resource consumption level + */ + unsigned long usage; + /* + * the limit that usage cannot exceed + */ + unsigned long limit; + /* + * the number of unsuccessful attempts to consume the resource + */ + unsigned long failcnt; + /* + * the lock to protect all of the above. + * the routines below consider this to be IRQ-safe + */ + spinlock_t lock; +}; + +/* + * Helpers to interact with userspace + * res_counter_read/_write - put/get the specified fields from the + * res_counter struct to/from the user + * + * @counter: the counter in question + * @member: the field to work with (see RES_xxx below) + * @buf: the buffer to opeate on,... + * @nbytes: its size... + * @pos: and the offset. + */ + +ssize_t res_counter_read(struct res_counter *counter, int member, + const char __user *buf, size_t nbytes, loff_t *pos); +ssize_t res_counter_write(struct res_counter *counter, int member, + const char __user *buf, size_t nbytes, loff_t *pos); + +/* + * the field descriptors. one for each member of res_counter + */ + +enum { + RES_USAGE, + RES_LIMIT, + RES_FAILCNT, +}; + +/* + * helpers for accounting + */ + +void res_counter_init(struct res_counter *counter); + +/* + * charge - try to consume more resource. + * + * @counter: the counter + * @val: the amount of the resource. each controller defines its own + * units, e.g. numbers, bytes, Kbytes, etc + * + * returns 0 on success and <0 if the counter->usage will exceed the + * counter->limit _locked call expects the counter->lock to be taken + */ + +int res_counter_charge_locked(struct res_counter *counter, unsigned long val); +int res_counter_charge(struct res_counter *counter, unsigned long val); + +/* + * uncharge - tell that some portion of the resource is released + * + * @counter: the counter + * @val: the amount of the resource + * + * these calls check for usage underflow and show a warning on the console + * _locked call expects the counter->lock to be taken + */ + +void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val); +void res_counter_uncharge(struct res_counter *counter, unsigned long val); + +#endif diff --git a/init/Kconfig b/init/Kconfig index 92b23e256614..d372bd616b0c 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -369,6 +369,13 @@ config CGROUP_CPUACCT Provides a simple Resource Controller for monitoring the total CPU consumed by the tasks in a cgroup +config RESOURCE_COUNTERS + bool "Resource counters" + help + This option enables controller independent resource accounting + infrastructure that works with cgroups + depends on CGROUPS + config SYSFS_DEPRECATED bool "Create deprecated sysfs files" depends on SYSFS diff --git a/kernel/Makefile b/kernel/Makefile index 135a1b943446..685697c0a181 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_CGROUP_DEBUG) += cgroup_debug.o obj-$(CONFIG_CPUSETS) += cpuset.o obj-$(CONFIG_CGROUP_NS) += ns_cgroup.o obj-$(CONFIG_IKCONFIG) += configs.o +obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o obj-$(CONFIG_STOP_MACHINE) += stop_machine.o obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o obj-$(CONFIG_AUDIT) += audit.o auditfilter.o diff --git a/kernel/res_counter.c b/kernel/res_counter.c new file mode 100644 index 000000000000..722c484b068b --- /dev/null +++ b/kernel/res_counter.c @@ -0,0 +1,120 @@ +/* + * resource cgroups + * + * Copyright 2007 OpenVZ SWsoft Inc + * + * Author: Pavel Emelianov <xemul@openvz.org> + * + */ + +#include <linux/types.h> +#include <linux/parser.h> +#include <linux/fs.h> +#include <linux/res_counter.h> +#include <linux/uaccess.h> + +void res_counter_init(struct res_counter *counter) +{ + spin_lock_init(&counter->lock); + counter->limit = (unsigned long)LONG_MAX; +} + +int res_counter_charge_locked(struct res_counter *counter, unsigned long val) +{ + if (counter->usage + val > counter->limit) { + counter->failcnt++; + return -ENOMEM; + } + + counter->usage += val; + return 0; +} + +int res_counter_charge(struct res_counter *counter, unsigned long val) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&counter->lock, flags); + ret = res_counter_charge_locked(counter, val); + spin_unlock_irqrestore(&counter->lock, flags); + return ret; +} + +void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) +{ + if (WARN_ON(counter->usage < val)) + val = counter->usage; + + counter->usage -= val; +} + +void res_counter_uncharge(struct res_counter *counter, unsigned long val) +{ + unsigned long flags; + + spin_lock_irqsave(&counter->lock, flags); + res_counter_uncharge_locked(counter, val); + spin_unlock_irqrestore(&counter->lock, flags); +} + + +static inline unsigned long *res_counter_member(struct res_counter *counter, + int member) +{ + switch (member) { + case RES_USAGE: + return &counter->usage; + case RES_LIMIT: + return &counter->limit; + case RES_FAILCNT: + return &counter->failcnt; + }; + + BUG(); + return NULL; +} + +ssize_t res_counter_read(struct res_counter *counter, int member, + const char __user *userbuf, size_t nbytes, loff_t *pos) +{ + unsigned long *val; + char buf[64], *s; + + s = buf; + val = res_counter_member(counter, member); + s += sprintf(s, "%lu\n", *val); + return simple_read_from_buffer((void __user *)userbuf, nbytes, + pos, buf, s - buf); +} + +ssize_t res_counter_write(struct res_counter *counter, int member, + const char __user *userbuf, size_t nbytes, loff_t *pos) +{ + int ret; + char *buf, *end; + unsigned long tmp, *val; + + buf = kmalloc(nbytes + 1, GFP_KERNEL); + ret = -ENOMEM; + if (buf == NULL) + goto out; + + buf[nbytes] = '\0'; + ret = -EFAULT; + if (copy_from_user(buf, userbuf, nbytes)) + goto out_free; + + ret = -EINVAL; + tmp = simple_strtoul(buf, &end, 10); + if (*end != '\0') + goto out_free; + + val = res_counter_member(counter, member); + *val = tmp; + ret = nbytes; +out_free: + kfree(buf); +out: + return ret; +} |