Commit ae29783d authored by Nadav Amit's avatar Nadav Amit Committed by Greg Kroah-Hartman

vmw_balloon: Split refused pages

The hypervisor might refuse to inflate pages. While the balloon driver
handles this scenario correctly, a refusal to inflate a 2MB pages might
cause the same page to be allocated again later just for its inflation
to be refused again. This wastes energy and time.

To avoid this situation, split the 2MB page to 4KB pages, and then try
to inflate each one individually. Most of the 4KB pages out of the 2MB
should be inflated successfully, and the balloon is likely to prevent
the scenario of repeated refused inflation.
Reviewed-by: default avatarXavier Deguillard <xdeguillard@vmware.com>
Signed-off-by: default avatarNadav Amit <namit@vmware.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5d1a86ec
......@@ -239,6 +239,7 @@ static DEFINE_STATIC_KEY_FALSE(balloon_stat_enabled);
struct vmballoon_ctl {
struct list_head pages;
struct list_head refused_pages;
struct list_head prealloc_pages;
unsigned int n_refused_pages;
unsigned int n_pages;
enum vmballoon_page_size_type page_size;
......@@ -668,15 +669,25 @@ static int vmballoon_alloc_page_list(struct vmballoon *b,
unsigned int i;
for (i = 0; i < req_n_pages; i++) {
if (ctl->page_size == VMW_BALLOON_2M_PAGE)
page = alloc_pages(__GFP_HIGHMEM|__GFP_NOWARN|
/*
* First check if we happen to have pages that were allocated
* before. This happens when 2MB page rejected during inflation
* by the hypervisor, and then split into 4KB pages.
*/
if (!list_empty(&ctl->prealloc_pages)) {
page = list_first_entry(&ctl->prealloc_pages,
struct page, lru);
list_del(&page->lru);
} else {
if (ctl->page_size == VMW_BALLOON_2M_PAGE)
page = alloc_pages(__GFP_HIGHMEM|__GFP_NOWARN|
__GFP_NOMEMALLOC, VMW_BALLOON_2M_ORDER);
else
page = balloon_page_alloc();
else
page = balloon_page_alloc();
/* Update statistics */
vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_ALLOC,
ctl->page_size);
vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_ALLOC,
ctl->page_size);
}
if (page) {
vmballoon_mark_page_offline(page, ctl->page_size);
......@@ -922,7 +933,8 @@ static void vmballoon_release_page_list(struct list_head *page_list,
__free_pages(page, vmballoon_page_order(page_size));
}
*n_pages = 0;
if (n_pages)
*n_pages = 0;
}
......@@ -1054,6 +1066,32 @@ static void vmballoon_dequeue_page_list(struct vmballoon *b,
*n_pages = i;
}
/**
* vmballoon_split_refused_pages() - Split the 2MB refused pages to 4k.
*
* If inflation of 2MB pages was denied by the hypervisor, it is likely to be
* due to one or few 4KB pages. These 2MB pages may keep being allocated and
* then being refused. To prevent this case, this function splits the refused
* pages into 4KB pages and adds them into @prealloc_pages list.
*
* @ctl: pointer for the %struct vmballoon_ctl, which defines the operation.
*/
static void vmballoon_split_refused_pages(struct vmballoon_ctl *ctl)
{
struct page *page, *tmp;
unsigned int i, order;
order = vmballoon_page_order(ctl->page_size);
list_for_each_entry_safe(page, tmp, &ctl->refused_pages, lru) {
list_del(&page->lru);
split_page(page, order);
for (i = 0; i < (1 << order); i++)
list_add(&page[i].lru, &ctl->prealloc_pages);
}
ctl->n_refused_pages = 0;
}
/**
* vmballoon_inflate() - Inflate the balloon towards its target size.
*
......@@ -1065,6 +1103,7 @@ static void vmballoon_inflate(struct vmballoon *b)
struct vmballoon_ctl ctl = {
.pages = LIST_HEAD_INIT(ctl.pages),
.refused_pages = LIST_HEAD_INIT(ctl.refused_pages),
.prealloc_pages = LIST_HEAD_INIT(ctl.prealloc_pages),
.page_size = b->max_page_size,
.op = VMW_BALLOON_INFLATE
};
......@@ -1112,10 +1151,10 @@ static void vmballoon_inflate(struct vmballoon *b)
break;
/*
* Ignore errors from locking as we now switch to 4k
* pages and we might get different errors.
* Split the refused pages to 4k. This will also empty
* the refused pages list.
*/
vmballoon_release_refused_pages(b, &ctl);
vmballoon_split_refused_pages(&ctl);
ctl.page_size--;
}
......@@ -1129,6 +1168,8 @@ static void vmballoon_inflate(struct vmballoon *b)
*/
if (ctl.n_refused_pages != 0)
vmballoon_release_refused_pages(b, &ctl);
vmballoon_release_page_list(&ctl.prealloc_pages, NULL, ctl.page_size);
}
/**
......
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