diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /mm/page_cgroup.c | |
parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) |
Patched in Tegra support.
Diffstat (limited to 'mm/page_cgroup.c')
-rw-r--r-- | mm/page_cgroup.c | 187 |
1 files changed, 115 insertions, 72 deletions
diff --git a/mm/page_cgroup.c b/mm/page_cgroup.c index 6d757e3a872..39d216d535e 100644 --- a/mm/page_cgroup.c +++ b/mm/page_cgroup.c | |||
@@ -11,6 +11,13 @@ | |||
11 | #include <linux/swapops.h> | 11 | #include <linux/swapops.h> |
12 | #include <linux/kmemleak.h> | 12 | #include <linux/kmemleak.h> |
13 | 13 | ||
14 | static void __meminit init_page_cgroup(struct page_cgroup *pc, unsigned long id) | ||
15 | { | ||
16 | pc->flags = 0; | ||
17 | set_page_cgroup_array_id(pc, id); | ||
18 | pc->mem_cgroup = NULL; | ||
19 | INIT_LIST_HEAD(&pc->lru); | ||
20 | } | ||
14 | static unsigned long total_usage; | 21 | static unsigned long total_usage; |
15 | 22 | ||
16 | #if !defined(CONFIG_SPARSEMEM) | 23 | #if !defined(CONFIG_SPARSEMEM) |
@@ -28,27 +35,35 @@ struct page_cgroup *lookup_page_cgroup(struct page *page) | |||
28 | struct page_cgroup *base; | 35 | struct page_cgroup *base; |
29 | 36 | ||
30 | base = NODE_DATA(page_to_nid(page))->node_page_cgroup; | 37 | base = NODE_DATA(page_to_nid(page))->node_page_cgroup; |
31 | #ifdef CONFIG_DEBUG_VM | ||
32 | /* | ||
33 | * The sanity checks the page allocator does upon freeing a | ||
34 | * page can reach here before the page_cgroup arrays are | ||
35 | * allocated when feeding a range of pages to the allocator | ||
36 | * for the first time during bootup or memory hotplug. | ||
37 | */ | ||
38 | if (unlikely(!base)) | 38 | if (unlikely(!base)) |
39 | return NULL; | 39 | return NULL; |
40 | #endif | 40 | |
41 | offset = pfn - NODE_DATA(page_to_nid(page))->node_start_pfn; | 41 | offset = pfn - NODE_DATA(page_to_nid(page))->node_start_pfn; |
42 | return base + offset; | 42 | return base + offset; |
43 | } | 43 | } |
44 | 44 | ||
45 | struct page *lookup_cgroup_page(struct page_cgroup *pc) | ||
46 | { | ||
47 | unsigned long pfn; | ||
48 | struct page *page; | ||
49 | pg_data_t *pgdat; | ||
50 | |||
51 | pgdat = NODE_DATA(page_cgroup_array_id(pc)); | ||
52 | pfn = pc - pgdat->node_page_cgroup + pgdat->node_start_pfn; | ||
53 | page = pfn_to_page(pfn); | ||
54 | VM_BUG_ON(pc != lookup_page_cgroup(page)); | ||
55 | return page; | ||
56 | } | ||
57 | |||
45 | static int __init alloc_node_page_cgroup(int nid) | 58 | static int __init alloc_node_page_cgroup(int nid) |
46 | { | 59 | { |
47 | struct page_cgroup *base; | 60 | struct page_cgroup *base, *pc; |
48 | unsigned long table_size; | 61 | unsigned long table_size; |
49 | unsigned long nr_pages; | 62 | unsigned long start_pfn, nr_pages, index; |
50 | 63 | ||
64 | start_pfn = NODE_DATA(nid)->node_start_pfn; | ||
51 | nr_pages = NODE_DATA(nid)->node_spanned_pages; | 65 | nr_pages = NODE_DATA(nid)->node_spanned_pages; |
66 | |||
52 | if (!nr_pages) | 67 | if (!nr_pages) |
53 | return 0; | 68 | return 0; |
54 | 69 | ||
@@ -58,6 +73,10 @@ static int __init alloc_node_page_cgroup(int nid) | |||
58 | table_size, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)); | 73 | table_size, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)); |
59 | if (!base) | 74 | if (!base) |
60 | return -ENOMEM; | 75 | return -ENOMEM; |
76 | for (index = 0; index < nr_pages; index++) { | ||
77 | pc = base + index; | ||
78 | init_page_cgroup(pc, nid); | ||
79 | } | ||
61 | NODE_DATA(nid)->node_page_cgroup = base; | 80 | NODE_DATA(nid)->node_page_cgroup = base; |
62 | total_usage += table_size; | 81 | total_usage += table_size; |
63 | return 0; | 82 | return 0; |
@@ -92,45 +111,67 @@ struct page_cgroup *lookup_page_cgroup(struct page *page) | |||
92 | { | 111 | { |
93 | unsigned long pfn = page_to_pfn(page); | 112 | unsigned long pfn = page_to_pfn(page); |
94 | struct mem_section *section = __pfn_to_section(pfn); | 113 | struct mem_section *section = __pfn_to_section(pfn); |
95 | #ifdef CONFIG_DEBUG_VM | 114 | |
96 | /* | ||
97 | * The sanity checks the page allocator does upon freeing a | ||
98 | * page can reach here before the page_cgroup arrays are | ||
99 | * allocated when feeding a range of pages to the allocator | ||
100 | * for the first time during bootup or memory hotplug. | ||
101 | */ | ||
102 | if (!section->page_cgroup) | 115 | if (!section->page_cgroup) |
103 | return NULL; | 116 | return NULL; |
104 | #endif | ||
105 | return section->page_cgroup + pfn; | 117 | return section->page_cgroup + pfn; |
106 | } | 118 | } |
107 | 119 | ||
120 | struct page *lookup_cgroup_page(struct page_cgroup *pc) | ||
121 | { | ||
122 | struct mem_section *section; | ||
123 | struct page *page; | ||
124 | unsigned long nr; | ||
125 | |||
126 | nr = page_cgroup_array_id(pc); | ||
127 | section = __nr_to_section(nr); | ||
128 | page = pfn_to_page(pc - section->page_cgroup); | ||
129 | VM_BUG_ON(pc != lookup_page_cgroup(page)); | ||
130 | return page; | ||
131 | } | ||
132 | |||
108 | static void *__meminit alloc_page_cgroup(size_t size, int nid) | 133 | static void *__meminit alloc_page_cgroup(size_t size, int nid) |
109 | { | 134 | { |
110 | gfp_t flags = GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN; | ||
111 | void *addr = NULL; | 135 | void *addr = NULL; |
112 | 136 | ||
113 | addr = alloc_pages_exact_nid(nid, size, flags); | 137 | addr = alloc_pages_exact_nid(nid, size, GFP_KERNEL | __GFP_NOWARN); |
114 | if (addr) { | 138 | if (addr) |
115 | kmemleak_alloc(addr, size, 1, flags); | ||
116 | return addr; | 139 | return addr; |
117 | } | ||
118 | 140 | ||
119 | if (node_state(nid, N_HIGH_MEMORY)) | 141 | if (node_state(nid, N_HIGH_MEMORY)) |
120 | addr = vzalloc_node(size, nid); | 142 | addr = vmalloc_node(size, nid); |
121 | else | 143 | else |
122 | addr = vzalloc(size); | 144 | addr = vmalloc(size); |
123 | 145 | ||
124 | return addr; | 146 | return addr; |
125 | } | 147 | } |
126 | 148 | ||
149 | #ifdef CONFIG_MEMORY_HOTPLUG | ||
150 | static void free_page_cgroup(void *addr) | ||
151 | { | ||
152 | if (is_vmalloc_addr(addr)) { | ||
153 | vfree(addr); | ||
154 | } else { | ||
155 | struct page *page = virt_to_page(addr); | ||
156 | size_t table_size = | ||
157 | sizeof(struct page_cgroup) * PAGES_PER_SECTION; | ||
158 | |||
159 | BUG_ON(PageReserved(page)); | ||
160 | free_pages_exact(addr, table_size); | ||
161 | } | ||
162 | } | ||
163 | #endif | ||
164 | |||
127 | static int __meminit init_section_page_cgroup(unsigned long pfn, int nid) | 165 | static int __meminit init_section_page_cgroup(unsigned long pfn, int nid) |
128 | { | 166 | { |
167 | struct page_cgroup *base, *pc; | ||
129 | struct mem_section *section; | 168 | struct mem_section *section; |
130 | struct page_cgroup *base; | ||
131 | unsigned long table_size; | 169 | unsigned long table_size; |
170 | unsigned long nr; | ||
171 | int index; | ||
132 | 172 | ||
133 | section = __pfn_to_section(pfn); | 173 | nr = pfn_to_section_nr(pfn); |
174 | section = __nr_to_section(nr); | ||
134 | 175 | ||
135 | if (section->page_cgroup) | 176 | if (section->page_cgroup) |
136 | return 0; | 177 | return 0; |
@@ -150,6 +191,10 @@ static int __meminit init_section_page_cgroup(unsigned long pfn, int nid) | |||
150 | return -ENOMEM; | 191 | return -ENOMEM; |
151 | } | 192 | } |
152 | 193 | ||
194 | for (index = 0; index < PAGES_PER_SECTION; index++) { | ||
195 | pc = base + index; | ||
196 | init_page_cgroup(pc, nr); | ||
197 | } | ||
153 | /* | 198 | /* |
154 | * The passed "pfn" may not be aligned to SECTION. For the calculation | 199 | * The passed "pfn" may not be aligned to SECTION. For the calculation |
155 | * we need to apply a mask. | 200 | * we need to apply a mask. |
@@ -160,20 +205,6 @@ static int __meminit init_section_page_cgroup(unsigned long pfn, int nid) | |||
160 | return 0; | 205 | return 0; |
161 | } | 206 | } |
162 | #ifdef CONFIG_MEMORY_HOTPLUG | 207 | #ifdef CONFIG_MEMORY_HOTPLUG |
163 | static void free_page_cgroup(void *addr) | ||
164 | { | ||
165 | if (is_vmalloc_addr(addr)) { | ||
166 | vfree(addr); | ||
167 | } else { | ||
168 | struct page *page = virt_to_page(addr); | ||
169 | size_t table_size = | ||
170 | sizeof(struct page_cgroup) * PAGES_PER_SECTION; | ||
171 | |||
172 | BUG_ON(PageReserved(page)); | ||
173 | free_pages_exact(addr, table_size); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | void __free_page_cgroup(unsigned long pfn) | 208 | void __free_page_cgroup(unsigned long pfn) |
178 | { | 209 | { |
179 | struct mem_section *ms; | 210 | struct mem_section *ms; |
@@ -251,9 +282,6 @@ static int __meminit page_cgroup_callback(struct notifier_block *self, | |||
251 | mn->nr_pages, mn->status_change_nid); | 282 | mn->nr_pages, mn->status_change_nid); |
252 | break; | 283 | break; |
253 | case MEM_CANCEL_ONLINE: | 284 | case MEM_CANCEL_ONLINE: |
254 | offline_page_cgroup(mn->start_pfn, | ||
255 | mn->nr_pages, mn->status_change_nid); | ||
256 | break; | ||
257 | case MEM_GOING_OFFLINE: | 285 | case MEM_GOING_OFFLINE: |
258 | break; | 286 | break; |
259 | case MEM_ONLINE: | 287 | case MEM_ONLINE: |
@@ -274,7 +302,7 @@ void __init page_cgroup_init(void) | |||
274 | if (mem_cgroup_disabled()) | 302 | if (mem_cgroup_disabled()) |
275 | return; | 303 | return; |
276 | 304 | ||
277 | for_each_node_state(nid, N_MEMORY) { | 305 | for_each_node_state(nid, N_HIGH_MEMORY) { |
278 | unsigned long start_pfn, end_pfn; | 306 | unsigned long start_pfn, end_pfn; |
279 | 307 | ||
280 | start_pfn = node_start_pfn(nid); | 308 | start_pfn = node_start_pfn(nid); |
@@ -320,7 +348,7 @@ void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat) | |||
320 | #endif | 348 | #endif |
321 | 349 | ||
322 | 350 | ||
323 | #ifdef CONFIG_MEMCG_SWAP | 351 | #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP |
324 | 352 | ||
325 | static DEFINE_MUTEX(swap_cgroup_mutex); | 353 | static DEFINE_MUTEX(swap_cgroup_mutex); |
326 | struct swap_cgroup_ctrl { | 354 | struct swap_cgroup_ctrl { |
@@ -329,12 +357,13 @@ struct swap_cgroup_ctrl { | |||
329 | spinlock_t lock; | 357 | spinlock_t lock; |
330 | }; | 358 | }; |
331 | 359 | ||
332 | static struct swap_cgroup_ctrl swap_cgroup_ctrl[MAX_SWAPFILES]; | 360 | struct swap_cgroup_ctrl swap_cgroup_ctrl[MAX_SWAPFILES]; |
333 | 361 | ||
334 | struct swap_cgroup { | 362 | struct swap_cgroup { |
335 | unsigned short id; | 363 | unsigned short id; |
336 | }; | 364 | }; |
337 | #define SC_PER_PAGE (PAGE_SIZE/sizeof(struct swap_cgroup)) | 365 | #define SC_PER_PAGE (PAGE_SIZE/sizeof(struct swap_cgroup)) |
366 | #define SC_POS_MASK (SC_PER_PAGE - 1) | ||
338 | 367 | ||
339 | /* | 368 | /* |
340 | * SwapCgroup implements "lookup" and "exchange" operations. | 369 | * SwapCgroup implements "lookup" and "exchange" operations. |
@@ -376,26 +405,9 @@ not_enough_page: | |||
376 | return -ENOMEM; | 405 | return -ENOMEM; |
377 | } | 406 | } |
378 | 407 | ||
379 | static struct swap_cgroup *lookup_swap_cgroup(swp_entry_t ent, | ||
380 | struct swap_cgroup_ctrl **ctrlp) | ||
381 | { | ||
382 | pgoff_t offset = swp_offset(ent); | ||
383 | struct swap_cgroup_ctrl *ctrl; | ||
384 | struct page *mappage; | ||
385 | struct swap_cgroup *sc; | ||
386 | |||
387 | ctrl = &swap_cgroup_ctrl[swp_type(ent)]; | ||
388 | if (ctrlp) | ||
389 | *ctrlp = ctrl; | ||
390 | |||
391 | mappage = ctrl->map[offset / SC_PER_PAGE]; | ||
392 | sc = page_address(mappage); | ||
393 | return sc + offset % SC_PER_PAGE; | ||
394 | } | ||
395 | |||
396 | /** | 408 | /** |
397 | * swap_cgroup_cmpxchg - cmpxchg mem_cgroup's id for this swp_entry. | 409 | * swap_cgroup_cmpxchg - cmpxchg mem_cgroup's id for this swp_entry. |
398 | * @ent: swap entry to be cmpxchged | 410 | * @end: swap entry to be cmpxchged |
399 | * @old: old id | 411 | * @old: old id |
400 | * @new: new id | 412 | * @new: new id |
401 | * | 413 | * |
@@ -405,13 +417,21 @@ static struct swap_cgroup *lookup_swap_cgroup(swp_entry_t ent, | |||
405 | unsigned short swap_cgroup_cmpxchg(swp_entry_t ent, | 417 | unsigned short swap_cgroup_cmpxchg(swp_entry_t ent, |
406 | unsigned short old, unsigned short new) | 418 | unsigned short old, unsigned short new) |
407 | { | 419 | { |
420 | int type = swp_type(ent); | ||
421 | unsigned long offset = swp_offset(ent); | ||
422 | unsigned long idx = offset / SC_PER_PAGE; | ||
423 | unsigned long pos = offset & SC_POS_MASK; | ||
408 | struct swap_cgroup_ctrl *ctrl; | 424 | struct swap_cgroup_ctrl *ctrl; |
425 | struct page *mappage; | ||
409 | struct swap_cgroup *sc; | 426 | struct swap_cgroup *sc; |
410 | unsigned long flags; | 427 | unsigned long flags; |
411 | unsigned short retval; | 428 | unsigned short retval; |
412 | 429 | ||
413 | sc = lookup_swap_cgroup(ent, &ctrl); | 430 | ctrl = &swap_cgroup_ctrl[type]; |
414 | 431 | ||
432 | mappage = ctrl->map[idx]; | ||
433 | sc = page_address(mappage); | ||
434 | sc += pos; | ||
415 | spin_lock_irqsave(&ctrl->lock, flags); | 435 | spin_lock_irqsave(&ctrl->lock, flags); |
416 | retval = sc->id; | 436 | retval = sc->id; |
417 | if (retval == old) | 437 | if (retval == old) |
@@ -425,20 +445,28 @@ unsigned short swap_cgroup_cmpxchg(swp_entry_t ent, | |||
425 | /** | 445 | /** |
426 | * swap_cgroup_record - record mem_cgroup for this swp_entry. | 446 | * swap_cgroup_record - record mem_cgroup for this swp_entry. |
427 | * @ent: swap entry to be recorded into | 447 | * @ent: swap entry to be recorded into |
428 | * @id: mem_cgroup to be recorded | 448 | * @mem: mem_cgroup to be recorded |
429 | * | 449 | * |
430 | * Returns old value at success, 0 at failure. | 450 | * Returns old value at success, 0 at failure. |
431 | * (Of course, old value can be 0.) | 451 | * (Of course, old value can be 0.) |
432 | */ | 452 | */ |
433 | unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id) | 453 | unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id) |
434 | { | 454 | { |
455 | int type = swp_type(ent); | ||
456 | unsigned long offset = swp_offset(ent); | ||
457 | unsigned long idx = offset / SC_PER_PAGE; | ||
458 | unsigned long pos = offset & SC_POS_MASK; | ||
435 | struct swap_cgroup_ctrl *ctrl; | 459 | struct swap_cgroup_ctrl *ctrl; |
460 | struct page *mappage; | ||
436 | struct swap_cgroup *sc; | 461 | struct swap_cgroup *sc; |
437 | unsigned short old; | 462 | unsigned short old; |
438 | unsigned long flags; | 463 | unsigned long flags; |
439 | 464 | ||
440 | sc = lookup_swap_cgroup(ent, &ctrl); | 465 | ctrl = &swap_cgroup_ctrl[type]; |
441 | 466 | ||
467 | mappage = ctrl->map[idx]; | ||
468 | sc = page_address(mappage); | ||
469 | sc += pos; | ||
442 | spin_lock_irqsave(&ctrl->lock, flags); | 470 | spin_lock_irqsave(&ctrl->lock, flags); |
443 | old = sc->id; | 471 | old = sc->id; |
444 | sc->id = id; | 472 | sc->id = id; |
@@ -448,14 +476,28 @@ unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id) | |||
448 | } | 476 | } |
449 | 477 | ||
450 | /** | 478 | /** |
451 | * lookup_swap_cgroup_id - lookup mem_cgroup id tied to swap entry | 479 | * lookup_swap_cgroup - lookup mem_cgroup tied to swap entry |
452 | * @ent: swap entry to be looked up. | 480 | * @ent: swap entry to be looked up. |
453 | * | 481 | * |
454 | * Returns CSS ID of mem_cgroup at success. 0 at failure. (0 is invalid ID) | 482 | * Returns CSS ID of mem_cgroup at success. 0 at failure. (0 is invalid ID) |
455 | */ | 483 | */ |
456 | unsigned short lookup_swap_cgroup_id(swp_entry_t ent) | 484 | unsigned short lookup_swap_cgroup(swp_entry_t ent) |
457 | { | 485 | { |
458 | return lookup_swap_cgroup(ent, NULL)->id; | 486 | int type = swp_type(ent); |
487 | unsigned long offset = swp_offset(ent); | ||
488 | unsigned long idx = offset / SC_PER_PAGE; | ||
489 | unsigned long pos = offset & SC_POS_MASK; | ||
490 | struct swap_cgroup_ctrl *ctrl; | ||
491 | struct page *mappage; | ||
492 | struct swap_cgroup *sc; | ||
493 | unsigned short ret; | ||
494 | |||
495 | ctrl = &swap_cgroup_ctrl[type]; | ||
496 | mappage = ctrl->map[idx]; | ||
497 | sc = page_address(mappage); | ||
498 | sc += pos; | ||
499 | ret = sc->id; | ||
500 | return ret; | ||
459 | } | 501 | } |
460 | 502 | ||
461 | int swap_cgroup_swapon(int type, unsigned long max_pages) | 503 | int swap_cgroup_swapon(int type, unsigned long max_pages) |
@@ -471,10 +513,11 @@ int swap_cgroup_swapon(int type, unsigned long max_pages) | |||
471 | length = DIV_ROUND_UP(max_pages, SC_PER_PAGE); | 513 | length = DIV_ROUND_UP(max_pages, SC_PER_PAGE); |
472 | array_size = length * sizeof(void *); | 514 | array_size = length * sizeof(void *); |
473 | 515 | ||
474 | array = vzalloc(array_size); | 516 | array = vmalloc(array_size); |
475 | if (!array) | 517 | if (!array) |
476 | goto nomem; | 518 | goto nomem; |
477 | 519 | ||
520 | memset(array, 0, array_size); | ||
478 | ctrl = &swap_cgroup_ctrl[type]; | 521 | ctrl = &swap_cgroup_ctrl[type]; |
479 | mutex_lock(&swap_cgroup_mutex); | 522 | mutex_lock(&swap_cgroup_mutex); |
480 | ctrl->length = length; | 523 | ctrl->length = length; |