Commit 1cf6a6c8 authored by David Vrabel's avatar David Vrabel

xen/balloon: use hotplugged pages for foreign mappings etc.

alloc_xenballooned_pages() is used to get ballooned pages to back
foreign mappings etc.  Instead of having to balloon out real pages,
use (if supported) hotplugged memory.

This makes more memory available to the guest and reduces
fragmentation in the p2m.

This is only enabled if the xen.balloon.hotplug_unpopulated sysctl is
set to 1.  This sysctl defaults to 0 in case the udev rules to
automatically online hotplugged memory do not exist.
Signed-off-by: default avatarDavid Vrabel <david.vrabel@citrix.com>
Reviewed-by: default avatarDaniel Kiper <daniel.kiper@oracle.com>
---
v3:
- Add xen.balloon.hotplug_unpopulated sysctl to enable use of hotplug
  for unpopulated pages.
parent 81b286e0
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#include <linux/memory_hotplug.h> #include <linux/memory_hotplug.h>
#include <linux/percpu-defs.h> #include <linux/percpu-defs.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sysctl.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
...@@ -71,6 +72,46 @@ ...@@ -71,6 +72,46 @@
#include <xen/features.h> #include <xen/features.h>
#include <xen/page.h> #include <xen/page.h>
static int xen_hotplug_unpopulated;
#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
static int zero;
static int one = 1;
static struct ctl_table balloon_table[] = {
{
.procname = "hotplug_unpopulated",
.data = &xen_hotplug_unpopulated,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &zero,
.extra2 = &one,
},
{ }
};
static struct ctl_table balloon_root[] = {
{
.procname = "balloon",
.mode = 0555,
.child = balloon_table,
},
{ }
};
static struct ctl_table xen_root[] = {
{
.procname = "xen",
.mode = 0555,
.child = balloon_root,
},
{ }
};
#endif
/* /*
* balloon_process() state: * balloon_process() state:
* *
...@@ -99,6 +140,7 @@ static xen_pfn_t frame_list[PAGE_SIZE / sizeof(unsigned long)]; ...@@ -99,6 +140,7 @@ static xen_pfn_t frame_list[PAGE_SIZE / sizeof(unsigned long)];
/* List of ballooned pages, threaded through the mem_map array. */ /* List of ballooned pages, threaded through the mem_map array. */
static LIST_HEAD(ballooned_pages); static LIST_HEAD(ballooned_pages);
static DECLARE_WAIT_QUEUE_HEAD(balloon_wq);
/* Main work function, always executed in process context. */ /* Main work function, always executed in process context. */
static void balloon_process(struct work_struct *work); static void balloon_process(struct work_struct *work);
...@@ -127,6 +169,7 @@ static void __balloon_append(struct page *page) ...@@ -127,6 +169,7 @@ static void __balloon_append(struct page *page)
list_add(&page->lru, &ballooned_pages); list_add(&page->lru, &ballooned_pages);
balloon_stats.balloon_low++; balloon_stats.balloon_low++;
} }
wake_up(&balloon_wq);
} }
static void balloon_append(struct page *page) static void balloon_append(struct page *page)
...@@ -242,7 +285,8 @@ static enum bp_state reserve_additional_memory(void) ...@@ -242,7 +285,8 @@ static enum bp_state reserve_additional_memory(void)
int nid, rc; int nid, rc;
unsigned long balloon_hotplug; unsigned long balloon_hotplug;
credit = balloon_stats.target_pages - balloon_stats.total_pages; credit = balloon_stats.target_pages + balloon_stats.target_unpopulated
- balloon_stats.total_pages;
/* /*
* Already hotplugged enough pages? Wait for them to be * Already hotplugged enough pages? Wait for them to be
...@@ -323,7 +367,7 @@ static struct notifier_block xen_memory_nb = { ...@@ -323,7 +367,7 @@ static struct notifier_block xen_memory_nb = {
static enum bp_state reserve_additional_memory(void) static enum bp_state reserve_additional_memory(void)
{ {
balloon_stats.target_pages = balloon_stats.current_pages; balloon_stats.target_pages = balloon_stats.current_pages;
return BP_DONE; return BP_ECANCELED;
} }
#endif /* CONFIG_XEN_BALLOON_MEMORY_HOTPLUG */ #endif /* CONFIG_XEN_BALLOON_MEMORY_HOTPLUG */
...@@ -516,6 +560,28 @@ void balloon_set_new_target(unsigned long target) ...@@ -516,6 +560,28 @@ void balloon_set_new_target(unsigned long target)
} }
EXPORT_SYMBOL_GPL(balloon_set_new_target); EXPORT_SYMBOL_GPL(balloon_set_new_target);
static int add_ballooned_pages(int nr_pages)
{
enum bp_state st;
if (xen_hotplug_unpopulated) {
st = reserve_additional_memory();
if (st != BP_ECANCELED) {
mutex_unlock(&balloon_mutex);
wait_event(balloon_wq,
!list_empty(&ballooned_pages));
mutex_lock(&balloon_mutex);
return 0;
}
}
st = decrease_reservation(nr_pages, GFP_USER);
if (st != BP_DONE)
return -ENOMEM;
return 0;
}
/** /**
* alloc_xenballooned_pages - get pages that have been ballooned out * alloc_xenballooned_pages - get pages that have been ballooned out
* @nr_pages: Number of pages to get * @nr_pages: Number of pages to get
...@@ -526,27 +592,28 @@ int alloc_xenballooned_pages(int nr_pages, struct page **pages) ...@@ -526,27 +592,28 @@ int alloc_xenballooned_pages(int nr_pages, struct page **pages)
{ {
int pgno = 0; int pgno = 0;
struct page *page; struct page *page;
int ret;
mutex_lock(&balloon_mutex); mutex_lock(&balloon_mutex);
balloon_stats.target_unpopulated += nr_pages;
while (pgno < nr_pages) { while (pgno < nr_pages) {
page = balloon_retrieve(true); page = balloon_retrieve(true);
if (page) { if (page) {
pages[pgno++] = page; pages[pgno++] = page;
} else { } else {
enum bp_state st; ret = add_ballooned_pages(nr_pages - pgno);
st = decrease_reservation(nr_pages - pgno, GFP_USER); if (ret < 0)
if (st != BP_DONE)
goto out_undo; goto out_undo;
} }
} }
mutex_unlock(&balloon_mutex); mutex_unlock(&balloon_mutex);
return 0; return 0;
out_undo: out_undo:
while (pgno)
balloon_append(pages[--pgno]);
/* Free the memory back to the kernel soon */
schedule_delayed_work(&balloon_worker, 0);
mutex_unlock(&balloon_mutex); mutex_unlock(&balloon_mutex);
return -ENOMEM; free_xenballooned_pages(pgno, pages);
return ret;
} }
EXPORT_SYMBOL(alloc_xenballooned_pages); EXPORT_SYMBOL(alloc_xenballooned_pages);
...@@ -566,6 +633,8 @@ void free_xenballooned_pages(int nr_pages, struct page **pages) ...@@ -566,6 +633,8 @@ void free_xenballooned_pages(int nr_pages, struct page **pages)
balloon_append(pages[i]); balloon_append(pages[i]);
} }
balloon_stats.target_unpopulated -= nr_pages;
/* The balloon may be too large now. Shrink it if needed. */ /* The balloon may be too large now. Shrink it if needed. */
if (current_credit()) if (current_credit())
schedule_delayed_work(&balloon_worker, 0); schedule_delayed_work(&balloon_worker, 0);
...@@ -623,6 +692,7 @@ static int __init balloon_init(void) ...@@ -623,6 +692,7 @@ static int __init balloon_init(void)
#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG #ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG
set_online_page_callback(&xen_online_page); set_online_page_callback(&xen_online_page);
register_memory_notifier(&xen_memory_nb); register_memory_notifier(&xen_memory_nb);
register_sysctl_table(xen_root);
#endif #endif
/* /*
......
...@@ -8,6 +8,7 @@ struct balloon_stats { ...@@ -8,6 +8,7 @@ struct balloon_stats {
/* We aim for 'current allocation' == 'target allocation'. */ /* We aim for 'current allocation' == 'target allocation'. */
unsigned long current_pages; unsigned long current_pages;
unsigned long target_pages; unsigned long target_pages;
unsigned long target_unpopulated;
/* Number of pages in high- and low-memory balloons. */ /* Number of pages in high- and low-memory balloons. */
unsigned long balloon_low; unsigned long balloon_low;
unsigned long balloon_high; unsigned long balloon_high;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment