static void balloon_process(struct work_struct *work);
static DECLARE_DELAYED_WORK(balloon_worker, balloon_process);
-/* When ballooning out (allocating memory to return to Xen) we don't really
- want the kernel to try too hard since that can trigger the oom killer. */
-#define GFP_BALLOON \
- (GFP_HIGHUSER | __GFP_NOWARN | __GFP_NORETRY | __GFP_NOMEMALLOC)
-
static void scrub_page(struct page *page)
{
#ifdef CONFIG_XEN_SCRUB_PAGES
xen_balloon.balloon_stats.balloon_low += count;
}
-/* balloon_append: add the given page to the balloon. */
-static void __balloon_append(struct page *page)
+/* balloon_append: add the given page to the balloon. "managed"
+ * indicates whether we should add the page to our own list.
+ */
+static void __balloon_append(struct page *page, bool managed)
{
- /* Lowmem is re-populated first, so highmem pages go at list tail. */
- if (PageHighMem(page))
- list_add_tail(&page->lru, &xen_balloon.ballooned_pages);
- else
- list_add(&page->lru, &xen_balloon.ballooned_pages);
+ if (managed) {
+ /* Lowmem is re-populated first, so highmem pages go
+ * at list tail. */
+ if (PageHighMem(page))
+ list_add_tail(&page->lru,
+ &xen_balloon.ballooned_pages);
+ else
+ list_add(&page->lru, &xen_balloon.ballooned_pages);
+ }
update_balloon_stats(page, 1);
}
-static void balloon_append(struct page *page)
+static void balloon_append(struct page *page, bool managed)
{
- __balloon_append(page);
+ __balloon_append(page, managed);
adjust_managed_page_count(page, -1);
}
mutex_lock(&xen_balloon.balloon_mutex);
- __balloon_append(page);
+ __balloon_append(page, true);
if (xen_balloon.balloon_stats.hotplug_pages)
--xen_balloon.balloon_stats.hotplug_pages;
}
#endif /* CONFIG_XEN_BALLOON_MEMORY_HOTPLUG */
+/* This is used to reinsert pages back to generic balloon driver. We
+ * need to use balloon_page_insert. list_splice is not sufficient.
+ */
+static inline void __reinsert_balloon_pages(struct list_head *head)
+{
+ unsigned long flags;
+ struct page *page, *n;
+
+ spin_lock_irqsave(&xen_balloon.xb_dev_info->pages_lock, flags);
+ list_for_each_entry_safe_reverse(page, n, head, lru)
+ balloon_page_insert(page, xen_balloon.xb_dev_info->mapping,
+ &xen_balloon.xb_dev_info->pages);
+ spin_unlock_irqrestore(&xen_balloon.xb_dev_info->pages_lock, flags);
+}
+
+/* This function will always try to fill in pages managed by Xen
+ * balloon driver, then pages managed by generic balloon driver.
+ */
static enum bp_state increase_reservation(unsigned long nr_pages)
{
int rc;
unsigned long pfn, i;
struct page *page;
LIST_HEAD(queue);
+ bool xen_pages;
struct xen_memory_reservation reservation = {
.address_bits = 0,
.extent_order = 0,
if (nr_pages > ARRAY_SIZE(frame_list))
nr_pages = ARRAY_SIZE(frame_list);
+ /* We always try to fill in pages managed by Xen balloon
+ * driver first. And we separate the process to fill in Xen
+ * balloon driver pages v.s. generic balloon driver in
+ * different rounds for the sake of simplicity.
+ */
+ xen_pages = !list_empty(&xen_balloon.ballooned_pages);
+
/* First step: grab all pages we need to balloon in */
for (i = 0; i < nr_pages; i++) {
- page = balloon_retrieve(false);
+ if (xen_pages)
+ page = balloon_retrieve(false);
+ else
+ page = balloon_page_dequeue(xen_balloon.xb_dev_info, false);
if (!page) {
nr_pages = i;
break;
#endif
/* Relinquish the page back to the allocator. */
- __free_reserved_page(page);
+ if (xen_pages)
+ __free_reserved_page(page);
+ else
+ balloon_page_free(page);
/* We only account for those pages that have been populated. */
update_balloon_stats(page, -1);
move_pages_back:
/* Final step: move back any unpopulated pages to balloon driver */
- list_splice_init(&queue, &xen_balloon.ballooned_pages);
+ if (!list_empty(&queue)) {
+ if (xen_pages)
+ list_splice(&queue, &xen_balloon.ballooned_pages);
+ else
+ __reinsert_balloon_pages(&queue);
+ }
return rc;
}
-static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp)
+/* decrease_reservation:
+ * core_driver == true:
+ * Page is not movable and managed by Xen balloon driver. Honor gfp
+ * when allocating pages.
+ * core_driver == false:
+ * Page is movable and managed by generic balloon driver, subject to
+ * migration. Gfp is ignored as page is allocated by generic balloon
+ * driver.
+ */
+static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp,
+ bool core_driver)
{
enum bp_state state = BP_DONE;
unsigned long pfn, i;
nr_pages = ARRAY_SIZE(frame_list);
for (i = 0; i < nr_pages; i++) {
- page = alloc_page(gfp);
+ if (core_driver)
+ page = alloc_page(gfp);
+ else
+ page = balloon_page_enqueue(xen_balloon.xb_dev_info);
+
if (page == NULL) {
nr_pages = i;
state = BP_EAGAIN;
}
#endif
- balloon_append(page);
+ balloon_append(page, core_driver);
}
flush_tlb_all();
}
if (credit < 0)
- state = decrease_reservation(-credit, GFP_BALLOON);
+ state = decrease_reservation(-credit, 0, false);
state = update_schedule(state);
adjust_managed_page_count(page, 1);
} else {
enum bp_state st;
+ gfp_t gfp = highmem ? GFP_HIGHUSER : GFP_USER;
if (page)
- balloon_append(page);
- st = decrease_reservation(nr_pages - pgno,
- highmem ? GFP_HIGHUSER : GFP_USER);
+ balloon_append(page, true);
+ st = decrease_reservation(nr_pages - pgno, gfp, true);
if (st != BP_DONE)
goto out_undo;
}
return 0;
out_undo:
while (pgno)
- balloon_append(pages[--pgno]);
+ balloon_append(pages[--pgno], true);
/* Free the memory back to the kernel soon */
schedule_delayed_work(&balloon_worker, 0);
mutex_unlock(&xen_balloon.balloon_mutex);
for (i = 0; i < nr_pages; i++) {
if (pages[i])
- balloon_append(pages[i]);
+ balloon_append(pages[i], true);
}
/* The balloon may be too large now. Shrink it if needed. */
/* totalram_pages and totalhigh_pages do not
include the boot-time balloon extension, so
don't subtract from it. */
- __balloon_append(page);
+ __balloon_append(page, true);
}
}
.notifier_call = balloon_cpu_notify,
};
+static const struct address_space_operations xen_balloon_aops;
+#ifdef CONFIG_BALLOON_COMPACTION
+static int xen_balloon_migratepage(struct address_space *mapping,
+ struct page *newpage, struct page *page,
+ enum migrate_mode mode)
+{
+ return -EAGAIN;
+}
+
+static const struct address_space_operations xen_balloon_aops = {
+ .migratepage = xen_balloon_migratepage,
+};
+#endif /* CONFIG_BALLOON_COMPACTION */
+
static int __init balloon_init(void)
{
int i, cpu;
+ struct address_space *mapping;
+ int err = 0;
if (!xen_domain())
return -ENODEV;
memset(&xen_balloon, 0, sizeof(xen_balloon));
+ /* Allocate core balloon driver which is in charge of movable
+ * pages.
+ */
+ xen_balloon.xb_dev_info = balloon_devinfo_alloc(&xen_balloon);
+
+ if (IS_ERR(xen_balloon.xb_dev_info)) {
+ err = PTR_ERR(xen_balloon.xb_dev_info);
+ goto out_err;
+ }
+
+ /* Check CONFIG_BALLOON_COMPACTION */
+ if (balloon_compaction_check()) {
+ mapping = balloon_mapping_alloc(xen_balloon.xb_dev_info,
+ &xen_balloon_aops);
+ if (IS_ERR(mapping)) {
+ err = PTR_ERR(mapping);
+ goto out_free_xb_dev_info;
+ }
+ pr_info("balloon page compaction enabled\n");
+ }
+
mutex_init(&xen_balloon.balloon_mutex);
INIT_LIST_HEAD(&xen_balloon.ballooned_pages);
PFN_DOWN(xen_extra_mem[i].size));
return 0;
+
+out_free_xb_dev_info:
+ balloon_devinfo_free(xen_balloon.xb_dev_info);
+out_err:
+ return err;
}
subsys_initcall(balloon_init);