summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/misc/vmw_balloon.c358
1 files changed, 165 insertions, 193 deletions
diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c
index 952308997499..96dde120bbd5 100644
--- a/drivers/misc/vmw_balloon.c
+++ b/drivers/misc/vmw_balloon.c
@@ -165,15 +165,7 @@ struct vmballoon_stats {
#define STATS_INC(stat)
#endif
-struct vmballoon;
-
-struct vmballoon_ops {
- void (*add_page)(struct vmballoon *b, int idx, struct page *p);
- int (*lock)(struct vmballoon *b, unsigned int num_pages,
- bool is_2m_pages);
- int (*unlock)(struct vmballoon *b, unsigned int num_pages,
- bool is_2m_pages);
-};
+static DEFINE_STATIC_KEY_TRUE(vmw_balloon_batching);
struct vmballoon_page_size {
/* list of reserved physical pages */
@@ -223,8 +215,6 @@ struct vmballoon {
unsigned int batch_max_pages;
struct page *page;
- const struct vmballoon_ops *ops;
-
#ifdef CONFIG_DEBUG_FS
/* statistics */
struct vmballoon_stats stats;
@@ -393,53 +383,6 @@ static bool vmballoon_send_get_target(struct vmballoon *b)
return false;
}
-/*
- * Notify the host about allocated page so that host can use it without
- * fear that guest will need it. Host may reject some pages, we need to
- * check the return value and maybe submit a different page.
- */
-static int vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn,
- unsigned int *hv_status, bool lock)
-{
- unsigned long status, cmd;
- u32 pfn32;
-
- pfn32 = (u32)pfn;
- if (pfn32 != pfn)
- return -EINVAL;
-
- cmd = lock ? VMW_BALLOON_CMD_LOCK : VMW_BALLOON_CMD_UNLOCK;
-
- *hv_status = status = vmballoon_cmd(b, cmd, pfn, 0);
-
- if (status == VMW_BALLOON_SUCCESS)
- return 0;
-
- return -EIO;
-}
-
-static int vmballoon_send_batched_lock(struct vmballoon *b,
- unsigned int num_pages, bool is_2m_pages,
- bool lock)
-{
- unsigned long pfn = PHYS_PFN(virt_to_phys(b->batch_page));
- unsigned long status, cmd;
-
- if (lock)
- cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_LOCK :
- VMW_BALLOON_CMD_BATCHED_LOCK;
- else
- cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_UNLOCK :
- VMW_BALLOON_CMD_BATCHED_UNLOCK;
-
- status = vmballoon_cmd(b, cmd, pfn, num_pages);
-
- if (status == VMW_BALLOON_SUCCESS)
- return 0;
-
- return 1;
-}
-
static struct page *vmballoon_alloc_page(bool is_2m_page)
{
if (is_2m_page)
@@ -488,88 +431,126 @@ static void vmballoon_pop(struct vmballoon *b)
b->batch_page = NULL;
}
-/*
- * Notify the host of a ballooned page. If host rejects the page put it on the
- * refuse list, those refused page are then released at the end of the
- * inflation cycle.
+/**
+ * vmballoon_status_page - returns the status of (un)lock operation
+ *
+ * @b: pointer to the balloon.
+ * @idx: index for the page for which the operation is performed.
+ * @p: pointer to where the page struct is returned.
+ *
+ * Following a lock or unlock operation, returns the status of the operation for
+ * an individual page. Provides the page that the operation was performed on on
+ * the @page argument.
+ *
+ * Returns: The status of a lock or unlock operation for an individual page.
*/
-static int vmballoon_lock_page(struct vmballoon *b, unsigned int num_pages,
- bool is_2m_pages)
+static unsigned long vmballoon_status_page(struct vmballoon *b, int idx,
+ struct page **p)
{
- int locked, hv_status;
- struct page *page = b->page;
- struct vmballoon_page_size *page_size = &b->page_sizes[false];
-
- /* is_2m_pages can never happen as 2m pages support implies batching */
-
- locked = vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status,
- true);
+ if (static_branch_likely(&vmw_balloon_batching)) {
+ /* batching mode */
+ *p = pfn_to_page(b->batch_page[idx].pfn);
+ return b->batch_page[idx].status;
+ }
- if (locked) {
- STATS_INC(b->stats.refused_alloc[false]);
+ /* non-batching mode */
+ *p = b->page;
- if (locked == -EIO &&
- (hv_status == VMW_BALLOON_ERROR_RESET ||
- hv_status == VMW_BALLOON_ERROR_PPN_NOTNEEDED)) {
- vmballoon_free_page(page, false);
- return -EIO;
- }
+ /*
+ * If a failure occurs, the indication will be provided in the status
+ * of the entire operation, which is considered before the individual
+ * page status. So for non-batching mode, the indication is always of
+ * success.
+ */
+ return VMW_BALLOON_SUCCESS;
+}
- /*
- * Place page on the list of non-balloonable pages
- * and retry allocation, unless we already accumulated
- * too many of them, in which case take a breather.
- */
- if (page_size->n_refused_pages < VMW_BALLOON_MAX_REFUSED) {
- page_size->n_refused_pages++;
- list_add(&page->lru, &page_size->refused_pages);
- } else {
- vmballoon_free_page(page, false);
- }
- return locked;
+/**
+ * vmballoon_lock_op - notifies the host about inflated/deflated pages.
+ * @b: pointer to the balloon.
+ * @num_pages: number of inflated/deflated pages.
+ * @is_2m_pages: whether the page(s) are 2M (or 4k).
+ * @lock: whether the operation is lock (or unlock).
+ *
+ * Notify the host about page(s) that were ballooned (or removed from the
+ * balloon) so that host can use it without fear that guest will need it (or
+ * stop using them since the VM does). Host may reject some pages, we need to
+ * check the return value and maybe submit a different page. The pages that are
+ * inflated/deflated are pointed by @b->page.
+ *
+ * Return: result as provided by the hypervisor.
+ */
+static unsigned long vmballoon_lock_op(struct vmballoon *b,
+ unsigned int num_pages,
+ bool is_2m_pages, bool lock)
+{
+ unsigned long cmd, pfn;
+
+ if (static_branch_likely(&vmw_balloon_batching)) {
+ if (lock)
+ cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_LOCK :
+ VMW_BALLOON_CMD_BATCHED_LOCK;
+ else
+ cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_UNLOCK :
+ VMW_BALLOON_CMD_BATCHED_UNLOCK;
+
+ pfn = PHYS_PFN(virt_to_phys(b->batch_page));
+ } else {
+ cmd = lock ? VMW_BALLOON_CMD_LOCK : VMW_BALLOON_CMD_UNLOCK;
+ pfn = page_to_pfn(b->page);
+
+ /* In non-batching mode, PFNs must fit in 32-bit */
+ if (unlikely(pfn != (u32)pfn))
+ return VMW_BALLOON_ERROR_PPN_INVALID;
}
- /* track allocated page */
- list_add(&page->lru, &page_size->pages);
-
- /* update balloon size */
- b->size++;
-
- return 0;
+ return vmballoon_cmd(b, cmd, pfn, num_pages);
}
-static int vmballoon_lock_batched_page(struct vmballoon *b,
- unsigned int num_pages, bool is_2m_pages)
+static int vmballoon_lock(struct vmballoon *b, unsigned int num_pages,
+ bool is_2m_pages)
{
- int locked, i;
+ unsigned long batch_status;
+ int i;
u16 size_per_page = vmballoon_page_size(is_2m_pages);
- locked = vmballoon_send_batched_lock(b, num_pages, is_2m_pages, true);
-
- if (locked > 0) {
- for (i = 0; i < num_pages; i++) {
- struct page *p = pfn_to_page(b->batch_page[i].pfn);
-
- vmballoon_free_page(p, is_2m_pages);
- }
-
- return -EIO;
- }
+ batch_status = vmballoon_lock_op(b, num_pages, is_2m_pages, true);
for (i = 0; i < num_pages; i++) {
- struct page *p = pfn_to_page(b->batch_page[i].pfn);
+ unsigned long status;
+ struct page *p;
struct vmballoon_page_size *page_size =
&b->page_sizes[is_2m_pages];
- locked = b->batch_page[i].status;
+ status = vmballoon_status_page(b, i, &p);
+
+ /*
+ * Failure of the whole batch overrides a single operation
+ * results.
+ */
+ if (batch_status != VMW_BALLOON_SUCCESS)
+ status = batch_status;
- switch (locked) {
- case VMW_BALLOON_SUCCESS:
+ if (status == VMW_BALLOON_SUCCESS) {
+ /* track allocated page */
list_add(&p->lru, &page_size->pages);
+
+ /* update balloon size */
b->size += size_per_page;
- break;
+ continue;
+ }
+
+ /* Error occurred */
+ STATS_INC(b->stats.refused_alloc[is_2m_pages]);
+
+ switch (status) {
case VMW_BALLOON_ERROR_PPN_PINNED:
case VMW_BALLOON_ERROR_PPN_INVALID:
+ /*
+ * Place page on the list of non-balloonable pages
+ * and retry allocation, unless we already accumulated
+ * too many of them, in which case take a breather.
+ */
if (page_size->n_refused_pages
< VMW_BALLOON_MAX_REFUSED) {
list_add(&p->lru, &page_size->refused_pages);
@@ -587,7 +568,7 @@ static int vmballoon_lock_batched_page(struct vmballoon *b,
}
}
- return 0;
+ return batch_status == VMW_BALLOON_SUCCESS ? 0 : -EIO;
}
/*
@@ -595,51 +576,31 @@ static int vmballoon_lock_batched_page(struct vmballoon *b,
* the host so it can make sure the page will be available for the guest
* to use, if needed.
*/
-static int vmballoon_unlock_page(struct vmballoon *b, unsigned int num_pages,
- bool is_2m_pages)
+static int vmballoon_unlock(struct vmballoon *b, unsigned int num_pages,
+ bool is_2m_pages)
{
- struct page *page = b->page;
- struct vmballoon_page_size *page_size = &b->page_sizes[false];
- unsigned int hv_status;
-
- /* is_2m_pages can never happen as 2m pages support implies batching */
-
- if (!vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status,
- false)) {
- list_add(&page->lru, &page_size->pages);
- return -EIO;
- }
-
- /* deallocate page */
- vmballoon_free_page(page, false);
- STATS_INC(b->stats.free[false]);
-
- /* update balloon size */
- b->size--;
-
- return 0;
-}
-
-static int vmballoon_unlock_batched_page(struct vmballoon *b,
- unsigned int num_pages, bool is_2m_pages)
-{
- int locked, i, ret = 0;
- bool hv_success;
+ int i;
+ unsigned long batch_status;
u16 size_per_page = vmballoon_page_size(is_2m_pages);
- hv_success = vmballoon_send_batched_lock(b, num_pages, is_2m_pages,
- false);
-
- if (!hv_success)
- ret = -EIO;
+ batch_status = vmballoon_lock_op(b, num_pages, is_2m_pages, false);
for (i = 0; i < num_pages; i++) {
- struct page *p = pfn_to_page(b->batch_page[i].pfn);
- struct vmballoon_page_size *page_size =
- &b->page_sizes[is_2m_pages];
+ struct vmballoon_page_size *page_size;
+ unsigned long status;
+ struct page *p;
- locked = b->batch_page[i].status;
- if (!hv_success || locked != VMW_BALLOON_SUCCESS) {
+ status = vmballoon_status_page(b, i, &p);
+ page_size = &b->page_sizes[is_2m_pages];
+
+ /*
+ * Failure of the whole batch overrides a single operation
+ * results.
+ */
+ if (batch_status != VMW_BALLOON_SUCCESS)
+ status = batch_status;
+
+ if (status != VMW_BALLOON_SUCCESS) {
/*
* That page wasn't successfully unlocked by the
* hypervisor, re-add it to the list of pages owned by
@@ -656,7 +617,7 @@ static int vmballoon_unlock_batched_page(struct vmballoon *b,
}
}
- return ret;
+ return batch_status == VMW_BALLOON_SUCCESS ? 0 : -EIO;
}
/*
@@ -681,14 +642,11 @@ static void vmballoon_release_refused_pages(struct vmballoon *b,
static void vmballoon_add_page(struct vmballoon *b, int idx, struct page *p)
{
- b->page = p;
-}
-
-static void vmballoon_add_batched_page(struct vmballoon *b, int idx,
- struct page *p)
-{
- b->batch_page[idx] = (struct vmballoon_batch_entry)
+ if (static_branch_likely(&vmw_balloon_batching))
+ b->batch_page[idx] = (struct vmballoon_batch_entry)
{ .pfn = page_to_pfn(p) };
+ else
+ b->page = p;
}
/*
@@ -737,7 +695,7 @@ static void vmballoon_inflate(struct vmballoon *b)
if (!page) {
STATS_INC(b->stats.alloc_fail[is_2m_pages]);
if (is_2m_pages) {
- b->ops->lock(b, num_pages, true);
+ vmballoon_lock(b, num_pages, true);
/*
* ignore errors from locking as we now switch
@@ -752,9 +710,9 @@ static void vmballoon_inflate(struct vmballoon *b)
break;
}
- b->ops->add_page(b, num_pages++, page);
+ vmballoon_add_page(b, num_pages++, page);
if (num_pages == b->batch_max_pages) {
- error = b->ops->lock(b, num_pages, is_2m_pages);
+ error = vmballoon_lock(b, num_pages, is_2m_pages);
num_pages = 0;
if (error)
@@ -765,7 +723,7 @@ static void vmballoon_inflate(struct vmballoon *b)
}
if (num_pages > 0)
- b->ops->lock(b, num_pages, is_2m_pages);
+ vmballoon_lock(b, num_pages, is_2m_pages);
vmballoon_release_refused_pages(b, true);
vmballoon_release_refused_pages(b, false);
@@ -797,12 +755,12 @@ static void vmballoon_deflate(struct vmballoon *b)
break;
list_del(&page->lru);
- b->ops->add_page(b, num_pages++, page);
+ vmballoon_add_page(b, num_pages++, page);
if (num_pages == b->batch_max_pages) {
int error;
- error = b->ops->unlock(b, num_pages,
+ error = vmballoon_unlock(b, num_pages,
is_2m_pages);
num_pages = 0;
if (error)
@@ -813,32 +771,50 @@ static void vmballoon_deflate(struct vmballoon *b)
}
if (num_pages > 0)
- b->ops->unlock(b, num_pages, is_2m_pages);
+ vmballoon_unlock(b, num_pages, is_2m_pages);
}
}
-static const struct vmballoon_ops vmballoon_basic_ops = {
- .add_page = vmballoon_add_page,
- .lock = vmballoon_lock_page,
- .unlock = vmballoon_unlock_page
-};
-
-static const struct vmballoon_ops vmballoon_batched_ops = {
- .add_page = vmballoon_add_batched_page,
- .lock = vmballoon_lock_batched_page,
- .unlock = vmballoon_unlock_batched_page
-};
+/**
+ * vmballoon_deinit_batching - disables batching mode.
+ *
+ * @b: pointer to &struct vmballoon.
+ *
+ * Disables batching, by deallocating the page for communication with the
+ * hypervisor and disabling the static key to indicate that batching is off.
+ */
+static void vmballoon_deinit_batching(struct vmballoon *b)
+{
+ free_page((unsigned long)b->batch_page);
+ b->batch_page = NULL;
+ static_branch_disable(&vmw_balloon_batching);
+ b->batch_max_pages = 1;
+}
-static bool vmballoon_init_batching(struct vmballoon *b)
+/**
+ * vmballoon_init_batching - enable batching mode.
+ *
+ * @b: pointer to &struct vmballoon.
+ *
+ * Enables batching, by allocating a page for communication with the hypervisor
+ * and enabling the static_key to use batching.
+ *
+ * Return: zero on success or an appropriate error-code.
+ */
+static int vmballoon_init_batching(struct vmballoon *b)
{
struct page *page;
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!page)
- return false;
+ return -ENOMEM;
b->batch_page = page_address(page);
- return true;
+ b->batch_max_pages = PAGE_SIZE / sizeof(struct vmballoon_batch_entry);
+
+ static_branch_enable(&vmw_balloon_batching);
+
+ return 0;
}
/*
@@ -915,10 +891,7 @@ static void vmballoon_reset(struct vmballoon *b)
return;
if ((b->capabilities & VMW_BALLOON_BATCHED_CMDS) != 0) {
- b->ops = &vmballoon_batched_ops;
- b->batch_max_pages = PAGE_SIZE / sizeof(struct
- vmballoon_batch_entry);
- if (!vmballoon_init_batching(b)) {
+ if (vmballoon_init_batching(b)) {
/*
* We failed to initialize batching, inform the monitor
* about it by sending a null capability.
@@ -929,8 +902,7 @@ static void vmballoon_reset(struct vmballoon *b)
return;
}
} else if ((b->capabilities & VMW_BALLOON_BASIC_CMDS) != 0) {
- b->ops = &vmballoon_basic_ops;
- b->batch_max_pages = 1;
+ vmballoon_deinit_batching(b);
}
b->reset_required = false;