David Gibson
2012-Apr-13 02:57 UTC
[PATCH] 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. Signed-off-by: David Gibson <david at gibson.dropbear.id.au> Signed-off-by: Michael S. Tsirkin <mst at redhat.com> Signed-off-by: David Gibson <david at gibson.dropbear.id.au> --- drivers/virtio/virtio_balloon.c | 44 +++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 9e95ca6..0c0c1c3 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -43,6 +43,9 @@ struct virtio_balloon struct completion acked; /* The pages we've told the Host we're not using. */ + /* Note that num_pages is measured in 4k balloon pages, so if + * PAGE_SIZE != 4k, it will be a multiple of the actual number + * of elements in the pages list */ unsigned int num_pages; struct list_head pages; @@ -60,13 +63,23 @@ static struct virtio_device_id id_table[] = { { 0 }, }; +/* Balloon device works in 4K page units. So each page is + * pointed to by multiple balloon pages. */ +#define VIRTIO_BALLOON_PAGES_PER_PAGE (PAGE_SIZE >> VIRTIO_BALLOON_PFN_SHIFT) + static u32 page_to_balloon_pfn(struct page *page) { unsigned long pfn = page_to_pfn(page); BUILD_BUG_ON(PAGE_SHIFT < VIRTIO_BALLOON_PFN_SHIFT); /* 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) @@ -96,12 +109,23 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq) 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) { /* We can only do one array worth at a time. */ 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 | __GFP_NOMEMALLOC | __GFP_NOWARN); if (!page) { @@ -113,9 +137,9 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num) msleep(200); 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--; - vb->num_pages++; list_add(&page->lru, &vb->pages); } @@ -130,8 +154,9 @@ static void release_pages_by_pfn(const u32 pfns[], unsigned int num) { unsigned int i; - for (i = 0; i < num; i++) { - __free_page(pfn_to_page(pfns[i])); + /* Find pfns pointing at start of each page, get pages and free them. */ + for (i = 0; i < num; i += VIRTIO_BALLOON_PAGES_PER_PAGE) { + __free_page(balloon_pfn_to_page(pfns[i])); totalram_pages++; } } @@ -143,11 +168,12 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num) /* We can only do one array worth at a time. */ 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); list_del(&page->lru); - vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page); - vb->num_pages--; + set_page_pfns(vb->pfns + vb->num_pfns, page); + vb->num_pages -= VIRTIO_BALLOON_PAGES_PER_PAGE; } /* -- 1.7.9.5 -- David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson
Reasonably Related Threads
- [PATCH] virtio_balloon: fix handling of PAGE_SIZE != 4k
- [PATCH] virtio_balloon: fix PFN format for virtio-1
- [PATCH] virtio_balloon: fix PFN format for virtio-1
- [PATCH] virtio_balloon: fix PFN format for virtio-1
- [RESEND PATCH v3 kernel 1/7] virtio-balloon: rework deflate to add page to a list