diff options
author | Daniel Kiper <dkiper@net-space.pl> | 2011-07-25 20:12:06 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-25 23:57:08 -0400 |
commit | 080e2be7884322daffe75a831e879fbe7de383ab (patch) | |
tree | 4f0daffaa6a03ffee4bc730517584cefe9dbdc60 | |
parent | 9d0ad8ca43ce8023bb834a409c2258bd7197fb05 (diff) |
xen/balloon: memory hotplug support for Xen balloon driver
Memory hotplug support for Xen balloon driver. It should be mentioned
that hotplugged memory is not onlined automatically. It should be onlined
by user through standard sysfs interface.
Memory could be hotplugged in following steps:
1) dom0: xl mem-max <domU> <maxmem>
where <maxmem> is >= requested memory size,
2) dom0: xl mem-set <domU> <memory>
where <memory> is requested memory size; alternatively memory
could be added by writing proper value to
/sys/devices/system/xen_memory/xen_memory0/target or
/sys/devices/system/xen_memory/xen_memory0/target_kb on dumU,
3) domU: for i in /sys/devices/system/memory/memory*/state; do \
[ "`cat "$i"`" = offline ] && echo online > "$i"; done
Memory could be onlined automatically on domU by adding following line to
udev rules:
SUBSYSTEM=="memory", ACTION=="add", RUN+="/bin/sh -c '[ -f /sys$devpath/state ] && echo online > /sys$devpath/state'"
In that case step 3 should be omitted.
Signed-off-by: Daniel Kiper <dkiper@net-space.pl>
Acked-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Ian Campbell <ian.campbell@citrix.com>
Cc: Jeremy Fitzhardinge <jeremy@goop.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | drivers/xen/Kconfig | 30 | ||||
-rw-r--r-- | drivers/xen/balloon.c | 139 | ||||
-rw-r--r-- | include/xen/balloon.h | 4 |
3 files changed, 171 insertions, 2 deletions
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 03bc471c3eed..f815283667af 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig | |||
@@ -26,6 +26,36 @@ config XEN_SELFBALLOONING | |||
26 | kernel boot parameter. Note that systems without a sufficiently | 26 | kernel boot parameter. Note that systems without a sufficiently |
27 | large swap device should not enable self-ballooning. | 27 | large swap device should not enable self-ballooning. |
28 | 28 | ||
29 | config XEN_BALLOON_MEMORY_HOTPLUG | ||
30 | bool "Memory hotplug support for Xen balloon driver" | ||
31 | default n | ||
32 | depends on XEN_BALLOON && MEMORY_HOTPLUG | ||
33 | help | ||
34 | Memory hotplug support for Xen balloon driver allows expanding memory | ||
35 | available for the system above limit declared at system startup. | ||
36 | It is very useful on critical systems which require long | ||
37 | run without rebooting. | ||
38 | |||
39 | Memory could be hotplugged in following steps: | ||
40 | |||
41 | 1) dom0: xl mem-max <domU> <maxmem> | ||
42 | where <maxmem> is >= requested memory size, | ||
43 | |||
44 | 2) dom0: xl mem-set <domU> <memory> | ||
45 | where <memory> is requested memory size; alternatively memory | ||
46 | could be added by writing proper value to | ||
47 | /sys/devices/system/xen_memory/xen_memory0/target or | ||
48 | /sys/devices/system/xen_memory/xen_memory0/target_kb on dumU, | ||
49 | |||
50 | 3) domU: for i in /sys/devices/system/memory/memory*/state; do \ | ||
51 | [ "`cat "$i"`" = offline ] && echo online > "$i"; done | ||
52 | |||
53 | Memory could be onlined automatically on domU by adding following line to udev rules: | ||
54 | |||
55 | SUBSYSTEM=="memory", ACTION=="add", RUN+="/bin/sh -c '[ -f /sys$devpath/state ] && echo online > /sys$devpath/state'" | ||
56 | |||
57 | In that case step 3 should be omitted. | ||
58 | |||
29 | config XEN_SCRUB_PAGES | 59 | config XEN_SCRUB_PAGES |
30 | bool "Scrub pages before returning them to system" | 60 | bool "Scrub pages before returning them to system" |
31 | depends on XEN_BALLOON | 61 | depends on XEN_BALLOON |
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index f54290baa3db..5dfd8f8ff07f 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c | |||
@@ -4,6 +4,12 @@ | |||
4 | * Copyright (c) 2003, B Dragovic | 4 | * Copyright (c) 2003, B Dragovic |
5 | * Copyright (c) 2003-2004, M Williamson, K Fraser | 5 | * Copyright (c) 2003-2004, M Williamson, K Fraser |
6 | * Copyright (c) 2005 Dan M. Smith, IBM Corporation | 6 | * Copyright (c) 2005 Dan M. Smith, IBM Corporation |
7 | * Copyright (c) 2010 Daniel Kiper | ||
8 | * | ||
9 | * Memory hotplug support was written by Daniel Kiper. Work on | ||
10 | * it was sponsored by Google under Google Summer of Code 2010 | ||
11 | * program. Jeremy Fitzhardinge from Citrix was the mentor for | ||
12 | * this project. | ||
7 | * | 13 | * |
8 | * This program is free software; you can redistribute it and/or | 14 | * This program is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU General Public License version 2 | 15 | * modify it under the terms of the GNU General Public License version 2 |
@@ -40,6 +46,9 @@ | |||
40 | #include <linux/mutex.h> | 46 | #include <linux/mutex.h> |
41 | #include <linux/list.h> | 47 | #include <linux/list.h> |
42 | #include <linux/gfp.h> | 48 | #include <linux/gfp.h> |
49 | #include <linux/notifier.h> | ||
50 | #include <linux/memory.h> | ||
51 | #include <linux/memory_hotplug.h> | ||
43 | 52 | ||
44 | #include <asm/page.h> | 53 | #include <asm/page.h> |
45 | #include <asm/pgalloc.h> | 54 | #include <asm/pgalloc.h> |
@@ -194,6 +203,87 @@ static enum bp_state update_schedule(enum bp_state state) | |||
194 | return BP_EAGAIN; | 203 | return BP_EAGAIN; |
195 | } | 204 | } |
196 | 205 | ||
206 | #ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG | ||
207 | static long current_credit(void) | ||
208 | { | ||
209 | return balloon_stats.target_pages - balloon_stats.current_pages - | ||
210 | balloon_stats.hotplug_pages; | ||
211 | } | ||
212 | |||
213 | static bool balloon_is_inflated(void) | ||
214 | { | ||
215 | if (balloon_stats.balloon_low || balloon_stats.balloon_high || | ||
216 | balloon_stats.balloon_hotplug) | ||
217 | return true; | ||
218 | else | ||
219 | return false; | ||
220 | } | ||
221 | |||
222 | /* | ||
223 | * reserve_additional_memory() adds memory region of size >= credit above | ||
224 | * max_pfn. New region is section aligned and size is modified to be multiple | ||
225 | * of section size. Those features allow optimal use of address space and | ||
226 | * establish proper alignment when this function is called first time after | ||
227 | * boot (last section not fully populated at boot time contains unused memory | ||
228 | * pages with PG_reserved bit not set; online_pages_range() does not allow page | ||
229 | * onlining in whole range if first onlined page does not have PG_reserved | ||
230 | * bit set). Real size of added memory is established at page onlining stage. | ||
231 | */ | ||
232 | |||
233 | static enum bp_state reserve_additional_memory(long credit) | ||
234 | { | ||
235 | int nid, rc; | ||
236 | u64 hotplug_start_paddr; | ||
237 | unsigned long balloon_hotplug = credit; | ||
238 | |||
239 | hotplug_start_paddr = PFN_PHYS(SECTION_ALIGN_UP(max_pfn)); | ||
240 | balloon_hotplug = round_up(balloon_hotplug, PAGES_PER_SECTION); | ||
241 | nid = memory_add_physaddr_to_nid(hotplug_start_paddr); | ||
242 | |||
243 | rc = add_memory(nid, hotplug_start_paddr, balloon_hotplug << PAGE_SHIFT); | ||
244 | |||
245 | if (rc) { | ||
246 | pr_info("xen_balloon: %s: add_memory() failed: %i\n", __func__, rc); | ||
247 | return BP_EAGAIN; | ||
248 | } | ||
249 | |||
250 | balloon_hotplug -= credit; | ||
251 | |||
252 | balloon_stats.hotplug_pages += credit; | ||
253 | balloon_stats.balloon_hotplug = balloon_hotplug; | ||
254 | |||
255 | return BP_DONE; | ||
256 | } | ||
257 | |||
258 | static void xen_online_page(struct page *page) | ||
259 | { | ||
260 | __online_page_set_limits(page); | ||
261 | |||
262 | mutex_lock(&balloon_mutex); | ||
263 | |||
264 | __balloon_append(page); | ||
265 | |||
266 | if (balloon_stats.hotplug_pages) | ||
267 | --balloon_stats.hotplug_pages; | ||
268 | else | ||
269 | --balloon_stats.balloon_hotplug; | ||
270 | |||
271 | mutex_unlock(&balloon_mutex); | ||
272 | } | ||
273 | |||
274 | static int xen_memory_notifier(struct notifier_block *nb, unsigned long val, void *v) | ||
275 | { | ||
276 | if (val == MEM_ONLINE) | ||
277 | schedule_delayed_work(&balloon_worker, 0); | ||
278 | |||
279 | return NOTIFY_OK; | ||
280 | } | ||
281 | |||
282 | static struct notifier_block xen_memory_nb = { | ||
283 | .notifier_call = xen_memory_notifier, | ||
284 | .priority = 0 | ||
285 | }; | ||
286 | #else | ||
197 | static long current_credit(void) | 287 | static long current_credit(void) |
198 | { | 288 | { |
199 | unsigned long target = balloon_stats.target_pages; | 289 | unsigned long target = balloon_stats.target_pages; |
@@ -206,6 +296,21 @@ static long current_credit(void) | |||
206 | return target - balloon_stats.current_pages; | 296 | return target - balloon_stats.current_pages; |
207 | } | 297 | } |
208 | 298 | ||
299 | static bool balloon_is_inflated(void) | ||
300 | { | ||
301 | if (balloon_stats.balloon_low || balloon_stats.balloon_high) | ||
302 | return true; | ||
303 | else | ||
304 | return false; | ||
305 | } | ||
306 | |||
307 | static enum bp_state reserve_additional_memory(long credit) | ||
308 | { | ||
309 | balloon_stats.target_pages = balloon_stats.current_pages; | ||
310 | return BP_DONE; | ||
311 | } | ||
312 | #endif /* CONFIG_XEN_BALLOON_MEMORY_HOTPLUG */ | ||
313 | |||
209 | static enum bp_state increase_reservation(unsigned long nr_pages) | 314 | static enum bp_state increase_reservation(unsigned long nr_pages) |
210 | { | 315 | { |
211 | int rc; | 316 | int rc; |
@@ -217,6 +322,15 @@ static enum bp_state increase_reservation(unsigned long nr_pages) | |||
217 | .domid = DOMID_SELF | 322 | .domid = DOMID_SELF |
218 | }; | 323 | }; |
219 | 324 | ||
325 | #ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG | ||
326 | if (!balloon_stats.balloon_low && !balloon_stats.balloon_high) { | ||
327 | nr_pages = min(nr_pages, balloon_stats.balloon_hotplug); | ||
328 | balloon_stats.hotplug_pages += nr_pages; | ||
329 | balloon_stats.balloon_hotplug -= nr_pages; | ||
330 | return BP_DONE; | ||
331 | } | ||
332 | #endif | ||
333 | |||
220 | if (nr_pages > ARRAY_SIZE(frame_list)) | 334 | if (nr_pages > ARRAY_SIZE(frame_list)) |
221 | nr_pages = ARRAY_SIZE(frame_list); | 335 | nr_pages = ARRAY_SIZE(frame_list); |
222 | 336 | ||
@@ -279,6 +393,15 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp) | |||
279 | .domid = DOMID_SELF | 393 | .domid = DOMID_SELF |
280 | }; | 394 | }; |
281 | 395 | ||
396 | #ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG | ||
397 | if (balloon_stats.hotplug_pages) { | ||
398 | nr_pages = min(nr_pages, balloon_stats.hotplug_pages); | ||
399 | balloon_stats.hotplug_pages -= nr_pages; | ||
400 | balloon_stats.balloon_hotplug += nr_pages; | ||
401 | return BP_DONE; | ||
402 | } | ||
403 | #endif | ||
404 | |||
282 | if (nr_pages > ARRAY_SIZE(frame_list)) | 405 | if (nr_pages > ARRAY_SIZE(frame_list)) |
283 | nr_pages = ARRAY_SIZE(frame_list); | 406 | nr_pages = ARRAY_SIZE(frame_list); |
284 | 407 | ||
@@ -340,8 +463,12 @@ static void balloon_process(struct work_struct *work) | |||
340 | do { | 463 | do { |
341 | credit = current_credit(); | 464 | credit = current_credit(); |
342 | 465 | ||
343 | if (credit > 0) | 466 | if (credit > 0) { |
344 | state = increase_reservation(credit); | 467 | if (balloon_is_inflated()) |
468 | state = increase_reservation(credit); | ||
469 | else | ||
470 | state = reserve_additional_memory(credit); | ||
471 | } | ||
345 | 472 | ||
346 | if (credit < 0) | 473 | if (credit < 0) |
347 | state = decrease_reservation(-credit, GFP_BALLOON); | 474 | state = decrease_reservation(-credit, GFP_BALLOON); |
@@ -448,6 +575,14 @@ static int __init balloon_init(void) | |||
448 | balloon_stats.retry_count = 1; | 575 | balloon_stats.retry_count = 1; |
449 | balloon_stats.max_retry_count = RETRY_UNLIMITED; | 576 | balloon_stats.max_retry_count = RETRY_UNLIMITED; |
450 | 577 | ||
578 | #ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG | ||
579 | balloon_stats.hotplug_pages = 0; | ||
580 | balloon_stats.balloon_hotplug = 0; | ||
581 | |||
582 | set_online_page_callback(&xen_online_page); | ||
583 | register_memory_notifier(&xen_memory_nb); | ||
584 | #endif | ||
585 | |||
451 | /* | 586 | /* |
452 | * Initialise the balloon with excess memory space. We need | 587 | * Initialise the balloon with excess memory space. We need |
453 | * to make sure we don't add memory which doesn't exist or | 588 | * to make sure we don't add memory which doesn't exist or |
diff --git a/include/xen/balloon.h b/include/xen/balloon.h index 4076ed72afbd..76f7538bb339 100644 --- a/include/xen/balloon.h +++ b/include/xen/balloon.h | |||
@@ -15,6 +15,10 @@ struct balloon_stats { | |||
15 | unsigned long max_schedule_delay; | 15 | unsigned long max_schedule_delay; |
16 | unsigned long retry_count; | 16 | unsigned long retry_count; |
17 | unsigned long max_retry_count; | 17 | unsigned long max_retry_count; |
18 | #ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG | ||
19 | unsigned long hotplug_pages; | ||
20 | unsigned long balloon_hotplug; | ||
21 | #endif | ||
18 | }; | 22 | }; |
19 | 23 | ||
20 | extern struct balloon_stats balloon_stats; | 24 | extern struct balloon_stats balloon_stats; |