|
@@ -26,6 +26,10 @@ struct hugetlb_cgroup {
|
|
|
struct res_counter hugepage[HUGE_MAX_HSTATE];
|
|
|
};
|
|
|
|
|
|
+#define MEMFILE_PRIVATE(x, val) (((x) << 16) | (val))
|
|
|
+#define MEMFILE_IDX(val) (((val) >> 16) & 0xffff)
|
|
|
+#define MEMFILE_ATTR(val) ((val) & 0xffff)
|
|
|
+
|
|
|
struct cgroup_subsys hugetlb_subsys __read_mostly;
|
|
|
static struct hugetlb_cgroup *root_h_cgroup __read_mostly;
|
|
|
|
|
@@ -257,6 +261,131 @@ void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages,
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+static ssize_t hugetlb_cgroup_read(struct cgroup *cgroup, struct cftype *cft,
|
|
|
+ struct file *file, char __user *buf,
|
|
|
+ size_t nbytes, loff_t *ppos)
|
|
|
+{
|
|
|
+ u64 val;
|
|
|
+ char str[64];
|
|
|
+ int idx, name, len;
|
|
|
+ struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup);
|
|
|
+
|
|
|
+ idx = MEMFILE_IDX(cft->private);
|
|
|
+ name = MEMFILE_ATTR(cft->private);
|
|
|
+
|
|
|
+ val = res_counter_read_u64(&h_cg->hugepage[idx], name);
|
|
|
+ len = scnprintf(str, sizeof(str), "%llu\n", (unsigned long long)val);
|
|
|
+ return simple_read_from_buffer(buf, nbytes, ppos, str, len);
|
|
|
+}
|
|
|
+
|
|
|
+static int hugetlb_cgroup_write(struct cgroup *cgroup, struct cftype *cft,
|
|
|
+ const char *buffer)
|
|
|
+{
|
|
|
+ int idx, name, ret;
|
|
|
+ unsigned long long val;
|
|
|
+ struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup);
|
|
|
+
|
|
|
+ idx = MEMFILE_IDX(cft->private);
|
|
|
+ name = MEMFILE_ATTR(cft->private);
|
|
|
+
|
|
|
+ switch (name) {
|
|
|
+ case RES_LIMIT:
|
|
|
+ if (hugetlb_cgroup_is_root(h_cg)) {
|
|
|
+ /* Can't set limit on root */
|
|
|
+ ret = -EINVAL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ /* This function does all necessary parse...reuse it */
|
|
|
+ ret = res_counter_memparse_write_strategy(buffer, &val);
|
|
|
+ if (ret)
|
|
|
+ break;
|
|
|
+ ret = res_counter_set_limit(&h_cg->hugepage[idx], val);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ ret = -EINVAL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int hugetlb_cgroup_reset(struct cgroup *cgroup, unsigned int event)
|
|
|
+{
|
|
|
+ int idx, name, ret = 0;
|
|
|
+ struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup);
|
|
|
+
|
|
|
+ idx = MEMFILE_IDX(event);
|
|
|
+ name = MEMFILE_ATTR(event);
|
|
|
+
|
|
|
+ switch (name) {
|
|
|
+ case RES_MAX_USAGE:
|
|
|
+ res_counter_reset_max(&h_cg->hugepage[idx]);
|
|
|
+ break;
|
|
|
+ case RES_FAILCNT:
|
|
|
+ res_counter_reset_failcnt(&h_cg->hugepage[idx]);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ ret = -EINVAL;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static char *mem_fmt(char *buf, int size, unsigned long hsize)
|
|
|
+{
|
|
|
+ if (hsize >= (1UL << 30))
|
|
|
+ snprintf(buf, size, "%luGB", hsize >> 30);
|
|
|
+ else if (hsize >= (1UL << 20))
|
|
|
+ snprintf(buf, size, "%luMB", hsize >> 20);
|
|
|
+ else
|
|
|
+ snprintf(buf, size, "%luKB", hsize >> 10);
|
|
|
+ return buf;
|
|
|
+}
|
|
|
+
|
|
|
+int __init hugetlb_cgroup_file_init(int idx)
|
|
|
+{
|
|
|
+ char buf[32];
|
|
|
+ struct cftype *cft;
|
|
|
+ struct hstate *h = &hstates[idx];
|
|
|
+
|
|
|
+ /* format the size */
|
|
|
+ mem_fmt(buf, 32, huge_page_size(h));
|
|
|
+
|
|
|
+ /* Add the limit file */
|
|
|
+ cft = &h->cgroup_files[0];
|
|
|
+ snprintf(cft->name, MAX_CFTYPE_NAME, "%s.limit_in_bytes", buf);
|
|
|
+ cft->private = MEMFILE_PRIVATE(idx, RES_LIMIT);
|
|
|
+ cft->read = hugetlb_cgroup_read;
|
|
|
+ cft->write_string = hugetlb_cgroup_write;
|
|
|
+
|
|
|
+ /* Add the usage file */
|
|
|
+ cft = &h->cgroup_files[1];
|
|
|
+ snprintf(cft->name, MAX_CFTYPE_NAME, "%s.usage_in_bytes", buf);
|
|
|
+ cft->private = MEMFILE_PRIVATE(idx, RES_USAGE);
|
|
|
+ cft->read = hugetlb_cgroup_read;
|
|
|
+
|
|
|
+ /* Add the MAX usage file */
|
|
|
+ cft = &h->cgroup_files[2];
|
|
|
+ snprintf(cft->name, MAX_CFTYPE_NAME, "%s.max_usage_in_bytes", buf);
|
|
|
+ cft->private = MEMFILE_PRIVATE(idx, RES_MAX_USAGE);
|
|
|
+ cft->trigger = hugetlb_cgroup_reset;
|
|
|
+ cft->read = hugetlb_cgroup_read;
|
|
|
+
|
|
|
+ /* Add the failcntfile */
|
|
|
+ cft = &h->cgroup_files[3];
|
|
|
+ snprintf(cft->name, MAX_CFTYPE_NAME, "%s.failcnt", buf);
|
|
|
+ cft->private = MEMFILE_PRIVATE(idx, RES_FAILCNT);
|
|
|
+ cft->trigger = hugetlb_cgroup_reset;
|
|
|
+ cft->read = hugetlb_cgroup_read;
|
|
|
+
|
|
|
+ /* NULL terminate the last cft */
|
|
|
+ cft = &h->cgroup_files[4];
|
|
|
+ memset(cft, 0, sizeof(*cft));
|
|
|
+
|
|
|
+ WARN_ON(cgroup_add_cftypes(&hugetlb_subsys, h->cgroup_files));
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
struct cgroup_subsys hugetlb_subsys = {
|
|
|
.name = "hugetlb",
|
|
|
.create = hugetlb_cgroup_create,
|