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: David Gibson <david@gibson.dropbear.id.au> Tested-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
1a87228f5f
commit
3ccc9372ed
1 changed files with 41 additions and 10 deletions
|
@ -28,6 +28,13 @@
|
|||
#include <linux/slab.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_device *vdev;
|
||||
|
@ -42,8 +49,13 @@ struct virtio_balloon
|
|||
/* Waiting for host to ack the pages we released. */
|
||||
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;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/* The array of pfns we tell the Host about. */
|
||||
|
@ -66,7 +78,13 @@ static u32 page_to_balloon_pfn(struct page *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 +114,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 +142,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 +159,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 +173,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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue