hugetlb_cgroup.c 10.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 *
 * Copyright IBM Corporation, 2012
 * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2.1 of the GNU Lesser General Public License
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it would be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 */

#include <linux/cgroup.h>
17
#include <linux/page_counter.h>
18 19 20 21 22 23 24 25 26
#include <linux/slab.h>
#include <linux/hugetlb.h>
#include <linux/hugetlb_cgroup.h>

struct hugetlb_cgroup {
	struct cgroup_subsys_state css;
	/*
	 * the counter to account for hugepages from hugetlb.
	 */
27
	struct page_counter hugepage[HUGE_MAX_HSTATE];
28 29
};

30 31 32 33
#define MEMFILE_PRIVATE(x, val)	(((x) << 16) | (val))
#define MEMFILE_IDX(val)	(((val) >> 16) & 0xffff)
#define MEMFILE_ATTR(val)	((val) & 0xffff)

34 35 36 37 38
static struct hugetlb_cgroup *root_h_cgroup __read_mostly;

static inline
struct hugetlb_cgroup *hugetlb_cgroup_from_css(struct cgroup_subsys_state *s)
{
39
	return s ? container_of(s, struct hugetlb_cgroup, css) : NULL;
40 41 42 43 44
}

static inline
struct hugetlb_cgroup *hugetlb_cgroup_from_task(struct task_struct *task)
{
45
	return hugetlb_cgroup_from_css(task_css(task, hugetlb_cgrp_id));
46 47 48 49 50 51 52
}

static inline bool hugetlb_cgroup_is_root(struct hugetlb_cgroup *h_cg)
{
	return (h_cg == root_h_cgroup);
}

53 54
static inline struct hugetlb_cgroup *
parent_hugetlb_cgroup(struct hugetlb_cgroup *h_cg)
55
{
T
Tejun Heo 已提交
56
	return hugetlb_cgroup_from_css(h_cg->css.parent);
57 58
}

59
static inline bool hugetlb_cgroup_have_usage(struct hugetlb_cgroup *h_cg)
60 61 62 63
{
	int idx;

	for (idx = 0; idx < hugetlb_max_hstate; idx++) {
64
		if (page_counter_read(&h_cg->hugepage[idx]))
65 66 67 68 69
			return true;
	}
	return false;
}

70 71
static struct cgroup_subsys_state *
hugetlb_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
72
{
73 74
	struct hugetlb_cgroup *parent_h_cgroup = hugetlb_cgroup_from_css(parent_css);
	struct hugetlb_cgroup *h_cgroup;
75 76 77 78 79 80
	int idx;

	h_cgroup = kzalloc(sizeof(*h_cgroup), GFP_KERNEL);
	if (!h_cgroup)
		return ERR_PTR(-ENOMEM);

81
	if (parent_h_cgroup) {
82
		for (idx = 0; idx < HUGE_MAX_HSTATE; idx++)
83 84
			page_counter_init(&h_cgroup->hugepage[idx],
					  &parent_h_cgroup->hugepage[idx]);
85 86 87
	} else {
		root_h_cgroup = h_cgroup;
		for (idx = 0; idx < HUGE_MAX_HSTATE; idx++)
88
			page_counter_init(&h_cgroup->hugepage[idx], NULL);
89 90 91 92
	}
	return &h_cgroup->css;
}

93
static void hugetlb_cgroup_css_free(struct cgroup_subsys_state *css)
94 95 96
{
	struct hugetlb_cgroup *h_cgroup;

97
	h_cgroup = hugetlb_cgroup_from_css(css);
98 99 100
	kfree(h_cgroup);
}

101 102 103 104 105 106 107 108

/*
 * Should be called with hugetlb_lock held.
 * Since we are holding hugetlb_lock, pages cannot get moved from
 * active list or uncharged from the cgroup, So no need to get
 * page reference and test for page active here. This function
 * cannot fail.
 */
109
static void hugetlb_cgroup_move_parent(int idx, struct hugetlb_cgroup *h_cg,
110 111
				       struct page *page)
{
112 113
	unsigned int nr_pages;
	struct page_counter *counter;
114
	struct hugetlb_cgroup *page_hcg;
115
	struct hugetlb_cgroup *parent = parent_hugetlb_cgroup(h_cg);
116 117 118 119 120 121 122 123 124 125

	page_hcg = hugetlb_cgroup_from_page(page);
	/*
	 * We can have pages in active list without any cgroup
	 * ie, hugepage with less than 3 pages. We can safely
	 * ignore those pages.
	 */
	if (!page_hcg || page_hcg != h_cg)
		goto out;

126
	nr_pages = 1 << compound_order(page);
127 128 129
	if (!parent) {
		parent = root_h_cgroup;
		/* root has no limit */
130
		page_counter_charge(&parent->hugepage[idx], nr_pages);
131 132
	}
	counter = &h_cg->hugepage[idx];
133 134
	/* Take the pages off the local counter */
	page_counter_cancel(counter, nr_pages);
135 136 137 138 139 140 141 142 143 144

	set_hugetlb_cgroup(page, parent);
out:
	return;
}

/*
 * Force the hugetlb cgroup to empty the hugetlb resources by moving them to
 * the parent cgroup.
 */
145
static void hugetlb_cgroup_css_offline(struct cgroup_subsys_state *css)
146
{
147
	struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(css);
148 149
	struct hstate *h;
	struct page *page;
150
	int idx = 0;
151 152 153 154 155

	do {
		for_each_hstate(h) {
			spin_lock(&hugetlb_lock);
			list_for_each_entry(page, &h->hugepage_activelist, lru)
156
				hugetlb_cgroup_move_parent(idx, h_cg, page);
157 158 159 160 161

			spin_unlock(&hugetlb_lock);
			idx++;
		}
		cond_resched();
162
	} while (hugetlb_cgroup_have_usage(h_cg));
163 164
}

165 166 167 168
int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
				 struct hugetlb_cgroup **ptr)
{
	int ret = 0;
169
	struct page_counter *counter;
170 171 172 173 174 175 176 177 178 179 180 181 182
	struct hugetlb_cgroup *h_cg = NULL;

	if (hugetlb_cgroup_disabled())
		goto done;
	/*
	 * We don't charge any cgroup if the compound page have less
	 * than 3 pages.
	 */
	if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER)
		goto done;
again:
	rcu_read_lock();
	h_cg = hugetlb_cgroup_from_task(current);
183
	if (!css_tryget_online(&h_cg->css)) {
184 185 186 187 188
		rcu_read_unlock();
		goto again;
	}
	rcu_read_unlock();

189 190
	if (!page_counter_try_charge(&h_cg->hugepage[idx], nr_pages, &counter))
		ret = -ENOMEM;
191 192 193 194 195 196
	css_put(&h_cg->css);
done:
	*ptr = h_cg;
	return ret;
}

197
/* Should be called with hugetlb_lock held */
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages,
				  struct hugetlb_cgroup *h_cg,
				  struct page *page)
{
	if (hugetlb_cgroup_disabled() || !h_cg)
		return;

	set_hugetlb_cgroup(page, h_cg);
	return;
}

/*
 * Should be called with hugetlb_lock held
 */
void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages,
				  struct page *page)
{
	struct hugetlb_cgroup *h_cg;

	if (hugetlb_cgroup_disabled())
		return;
219
	lockdep_assert_held(&hugetlb_lock);
220 221 222 223
	h_cg = hugetlb_cgroup_from_page(page);
	if (unlikely(!h_cg))
		return;
	set_hugetlb_cgroup(page, NULL);
224
	page_counter_uncharge(&h_cg->hugepage[idx], nr_pages);
225 226 227 228 229 230 231 232 233 234 235 236
	return;
}

void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages,
				    struct hugetlb_cgroup *h_cg)
{
	if (hugetlb_cgroup_disabled() || !h_cg)
		return;

	if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER)
		return;

237
	page_counter_uncharge(&h_cg->hugepage[idx], nr_pages);
238 239 240
	return;
}

241 242 243 244 245 246 247
enum {
	RES_USAGE,
	RES_LIMIT,
	RES_MAX_USAGE,
	RES_FAILCNT,
};

248 249
static u64 hugetlb_cgroup_read_u64(struct cgroup_subsys_state *css,
				   struct cftype *cft)
250
{
251
	struct page_counter *counter;
252
	struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(css);
253

254
	counter = &h_cg->hugepage[MEMFILE_IDX(cft->private)];
255

256 257 258 259 260 261 262 263 264 265 266 267
	switch (MEMFILE_ATTR(cft->private)) {
	case RES_USAGE:
		return (u64)page_counter_read(counter) * PAGE_SIZE;
	case RES_LIMIT:
		return (u64)counter->limit * PAGE_SIZE;
	case RES_MAX_USAGE:
		return (u64)counter->watermark * PAGE_SIZE;
	case RES_FAILCNT:
		return counter->failcnt;
	default:
		BUG();
	}
268 269
}

270 271
static DEFINE_MUTEX(hugetlb_limit_mutex);

272 273
static ssize_t hugetlb_cgroup_write(struct kernfs_open_file *of,
				    char *buf, size_t nbytes, loff_t off)
274
{
275 276
	int ret, idx;
	unsigned long nr_pages;
277
	struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(of_css(of));
278

279 280 281
	if (hugetlb_cgroup_is_root(h_cg)) /* Can't set limit on root */
		return -EINVAL;

282
	buf = strstrip(buf);
283
	ret = page_counter_memparse(buf, "-1", &nr_pages);
284 285 286
	if (ret)
		return ret;

287
	idx = MEMFILE_IDX(of_cft(of)->private);
288

289
	switch (MEMFILE_ATTR(of_cft(of)->private)) {
290
	case RES_LIMIT:
291 292 293
		mutex_lock(&hugetlb_limit_mutex);
		ret = page_counter_limit(&h_cg->hugepage[idx], nr_pages);
		mutex_unlock(&hugetlb_limit_mutex);
294 295 296 297 298
		break;
	default:
		ret = -EINVAL;
		break;
	}
299
	return ret ?: nbytes;
300 301
}

302 303
static ssize_t hugetlb_cgroup_reset(struct kernfs_open_file *of,
				    char *buf, size_t nbytes, loff_t off)
304
{
305 306
	int ret = 0;
	struct page_counter *counter;
307
	struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(of_css(of));
308

309
	counter = &h_cg->hugepage[MEMFILE_IDX(of_cft(of)->private)];
310

311
	switch (MEMFILE_ATTR(of_cft(of)->private)) {
312
	case RES_MAX_USAGE:
313
		page_counter_reset_watermark(counter);
314 315
		break;
	case RES_FAILCNT:
316
		counter->failcnt = 0;
317 318 319 320 321
		break;
	default:
		ret = -EINVAL;
		break;
	}
322
	return ret ?: nbytes;
323 324 325 326 327 328 329 330 331 332 333 334 335
}

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;
}

336
static void __init __hugetlb_cgroup_file_init(int idx)
337 338 339 340 341 342 343 344 345 346 347 348
{
	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);
349
	cft->read_u64 = hugetlb_cgroup_read_u64;
350
	cft->write = hugetlb_cgroup_write;
351 352 353 354 355

	/* 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);
356
	cft->read_u64 = hugetlb_cgroup_read_u64;
357 358 359 360 361

	/* 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);
362
	cft->write = hugetlb_cgroup_reset;
363
	cft->read_u64 = hugetlb_cgroup_read_u64;
364 365 366 367 368

	/* Add the failcntfile */
	cft = &h->cgroup_files[3];
	snprintf(cft->name, MAX_CFTYPE_NAME, "%s.failcnt", buf);
	cft->private  = MEMFILE_PRIVATE(idx, RES_FAILCNT);
369
	cft->write = hugetlb_cgroup_reset;
370
	cft->read_u64 = hugetlb_cgroup_read_u64;
371 372 373 374 375

	/* NULL terminate the last cft */
	cft = &h->cgroup_files[4];
	memset(cft, 0, sizeof(*cft));

376 377
	WARN_ON(cgroup_add_legacy_cftypes(&hugetlb_cgrp_subsys,
					  h->cgroup_files));
378 379 380 381 382 383 384 385 386 387
}

void __init hugetlb_cgroup_file_init(void)
{
	struct hstate *h;

	for_each_hstate(h) {
		/*
		 * Add cgroup control files only if the huge page consists
		 * of more than two normal pages. This is because we use
388
		 * page[2].private for storing cgroup details.
389 390 391 392
		 */
		if (huge_page_order(h) >= HUGETLB_CGROUP_MIN_ORDER)
			__hugetlb_cgroup_file_init(hstate_index(h));
	}
393 394
}

395 396 397 398
/*
 * hugetlb_lock will make sure a parallel cgroup rmdir won't happen
 * when we migrate hugepages
 */
399 400 401
void hugetlb_cgroup_migrate(struct page *oldhpage, struct page *newhpage)
{
	struct hugetlb_cgroup *h_cg;
402
	struct hstate *h = page_hstate(oldhpage);
403 404 405 406

	if (hugetlb_cgroup_disabled())
		return;

407
	VM_BUG_ON_PAGE(!PageHuge(oldhpage), oldhpage);
408 409 410 411 412 413
	spin_lock(&hugetlb_lock);
	h_cg = hugetlb_cgroup_from_page(oldhpage);
	set_hugetlb_cgroup(oldhpage, NULL);

	/* move the h_cg details to new cgroup */
	set_hugetlb_cgroup(newhpage, h_cg);
414
	list_move(&newhpage->lru, &h->hugepage_activelist);
415 416 417 418
	spin_unlock(&hugetlb_lock);
	return;
}

419
struct cgroup_subsys hugetlb_cgrp_subsys = {
420 421 422
	.css_alloc	= hugetlb_cgroup_css_alloc,
	.css_offline	= hugetlb_cgroup_css_offline,
	.css_free	= hugetlb_cgroup_css_free,
423
};