diff options
-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; |