Commit 3ccc9372 authored by Michael S. Tsirkin's avatar Michael S. Tsirkin

virtio_balloon: fix handling of PAGE_SIZE != 4k

As reported by David Gibson, current code handles PAGE_SIZE != 4k
completely wrong which can lead to guest memory corruption errors:

- page_to_balloon_pfn is wrong: e.g. on system with 64K page size
 it gives the same pfn value for 16 different pages.

- we also need to convert back to linux pfns when we free.

- for each linux page we need to tell host about multiple balloon
  pages, but code only adds one pfn to the array.

This patch fixes all that, tested with a 64k ppc64 kernel.
Reported-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
Tested-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
parent 1a87228f
...@@ -28,6 +28,13 @@ ...@@ -28,6 +28,13 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
/*
* Balloon device works in 4K page units. So each page is pointed to by
* multiple balloon pages. All memory counters in this driver are in balloon
* page units.
*/
#define VIRTIO_BALLOON_PAGES_PER_PAGE (PAGE_SIZE >> VIRTIO_BALLOON_PFN_SHIFT)
struct virtio_balloon struct virtio_balloon
{ {
struct virtio_device *vdev; struct virtio_device *vdev;
...@@ -42,8 +49,13 @@ struct virtio_balloon ...@@ -42,8 +49,13 @@ struct virtio_balloon
/* Waiting for host to ack the pages we released. */ /* Waiting for host to ack the pages we released. */
struct completion acked; struct completion acked;
/* The pages we've told the Host we're not using. */ /* Number of balloon pages we've told the Host we're not using. */
unsigned int num_pages; unsigned int num_pages;
/*
* The pages we've told the Host we're not using.
* Each page on this list adds VIRTIO_BALLOON_PAGES_PER_PAGE
* to num_pages above.
*/
struct list_head pages; struct list_head pages;
/* The array of pfns we tell the Host about. */ /* The array of pfns we tell the Host about. */
...@@ -66,7 +78,13 @@ static u32 page_to_balloon_pfn(struct page *page) ...@@ -66,7 +78,13 @@ static u32 page_to_balloon_pfn(struct page *page)
BUILD_BUG_ON(PAGE_SHIFT < VIRTIO_BALLOON_PFN_SHIFT); BUILD_BUG_ON(PAGE_SHIFT < VIRTIO_BALLOON_PFN_SHIFT);
/* Convert pfn from Linux page size to balloon page size. */ /* Convert pfn from Linux page size to balloon page size. */
return pfn >> (PAGE_SHIFT - VIRTIO_BALLOON_PFN_SHIFT); return pfn * VIRTIO_BALLOON_PAGES_PER_PAGE;
}
static struct page *balloon_pfn_to_page(u32 pfn)
{
BUG_ON(pfn % VIRTIO_BALLOON_PAGES_PER_PAGE);
return pfn_to_page(pfn / VIRTIO_BALLOON_PAGES_PER_PAGE);
} }
static void balloon_ack(struct virtqueue *vq) static void balloon_ack(struct virtqueue *vq)
...@@ -96,12 +114,23 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq) ...@@ -96,12 +114,23 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq)
wait_for_completion(&vb->acked); wait_for_completion(&vb->acked);
} }
static void set_page_pfns(u32 pfns[], struct page *page)
{
unsigned int i;
/* Set balloon pfns pointing at this page.
* Note that the first pfn points at start of the page. */
for (i = 0; i < VIRTIO_BALLOON_PAGES_PER_PAGE; i++)
pfns[i] = page_to_balloon_pfn(page) + i;
}
static void fill_balloon(struct virtio_balloon *vb, size_t num) static void fill_balloon(struct virtio_balloon *vb, size_t num)
{ {
/* We can only do one array worth at a time. */ /* We can only do one array worth at a time. */
num = min(num, ARRAY_SIZE(vb->pfns)); num = min(num, ARRAY_SIZE(vb->pfns));
for (vb->num_pfns = 0; vb->num_pfns < num; vb->num_pfns++) { for (vb->num_pfns = 0; vb->num_pfns < num;
vb->num_pfns += VIRTIO_BALLOON_PAGES_PER_PAGE) {
struct page *page = alloc_page(GFP_HIGHUSER | __GFP_NORETRY | struct page *page = alloc_page(GFP_HIGHUSER | __GFP_NORETRY |
__GFP_NOMEMALLOC | __GFP_NOWARN); __GFP_NOMEMALLOC | __GFP_NOWARN);
if (!page) { if (!page) {
...@@ -113,9 +142,9 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num) ...@@ -113,9 +142,9 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num)
msleep(200); msleep(200);
break; break;
} }
vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page); set_page_pfns(vb->pfns + vb->num_pfns, page);
vb->num_pages += VIRTIO_BALLOON_PAGES_PER_PAGE;
totalram_pages--; totalram_pages--;
vb->num_pages++;
list_add(&page->lru, &vb->pages); list_add(&page->lru, &vb->pages);
} }
...@@ -130,8 +159,9 @@ static void release_pages_by_pfn(const u32 pfns[], unsigned int num) ...@@ -130,8 +159,9 @@ static void release_pages_by_pfn(const u32 pfns[], unsigned int num)
{ {
unsigned int i; unsigned int i;
for (i = 0; i < num; i++) { /* Find pfns pointing at start of each page, get pages and free them. */
__free_page(pfn_to_page(pfns[i])); for (i = 0; i < num; i += VIRTIO_BALLOON_PAGES_PER_PAGE) {
__free_page(balloon_pfn_to_page(pfns[i]));
totalram_pages++; totalram_pages++;
} }
} }
...@@ -143,11 +173,12 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num) ...@@ -143,11 +173,12 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num)
/* We can only do one array worth at a time. */ /* We can only do one array worth at a time. */
num = min(num, ARRAY_SIZE(vb->pfns)); num = min(num, ARRAY_SIZE(vb->pfns));
for (vb->num_pfns = 0; vb->num_pfns < num; vb->num_pfns++) { for (vb->num_pfns = 0; vb->num_pfns < num;
vb->num_pfns += VIRTIO_BALLOON_PAGES_PER_PAGE) {
page = list_first_entry(&vb->pages, struct page, lru); page = list_first_entry(&vb->pages, struct page, lru);
list_del(&page->lru); list_del(&page->lru);
vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page); set_page_pfns(vb->pfns + vb->num_pfns, page);
vb->num_pages--; vb->num_pages -= VIRTIO_BALLOON_PAGES_PER_PAGE;
} }
/* /*
......
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