diff options
author | Michael S. Tsirkin <mst@redhat.com> | 2012-04-12 09:38:00 -0400 |
---|---|---|
committer | Michael S. Tsirkin <mst@redhat.com> | 2012-04-15 04:51:06 -0400 |
commit | 3ccc9372ed0fab33d20f10be3c1efd5776ff5913 (patch) | |
tree | c0e0422269e8d340906848e4993d268b8d4170ad /drivers/virtio/virtio_balloon.c | |
parent | 1a87228f5f1d316002c7c161316f5524592be766 (diff) |
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>
Diffstat (limited to 'drivers/virtio/virtio_balloon.c')
-rw-r--r-- | drivers/virtio/virtio_balloon.c | 51 |
1 files changed, 41 insertions, 10 deletions
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 9e95ca602006..c2d05a8279fd 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c | |||
@@ -28,6 +28,13 @@ | |||
28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
29 | #include <linux/module.h> | 29 | #include <linux/module.h> |
30 | 30 | ||
31 | /* | ||
32 | * Balloon device works in 4K page units. So each page is pointed to by | ||
33 | * multiple balloon pages. All memory counters in this driver are in balloon | ||
34 | * page units. | ||
35 | */ | ||
36 | #define VIRTIO_BALLOON_PAGES_PER_PAGE (PAGE_SIZE >> VIRTIO_BALLOON_PFN_SHIFT) | ||
37 | |||
31 | struct virtio_balloon | 38 | struct virtio_balloon |
32 | { | 39 | { |
33 | struct virtio_device *vdev; | 40 | struct virtio_device *vdev; |
@@ -42,8 +49,13 @@ struct virtio_balloon | |||
42 | /* Waiting for host to ack the pages we released. */ | 49 | /* Waiting for host to ack the pages we released. */ |
43 | struct completion acked; | 50 | struct completion acked; |
44 | 51 | ||
45 | /* The pages we've told the Host we're not using. */ | 52 | /* Number of balloon pages we've told the Host we're not using. */ |
46 | unsigned int num_pages; | 53 | unsigned int num_pages; |
54 | /* | ||
55 | * The pages we've told the Host we're not using. | ||
56 | * Each page on this list adds VIRTIO_BALLOON_PAGES_PER_PAGE | ||
57 | * to num_pages above. | ||
58 | */ | ||
47 | struct list_head pages; | 59 | struct list_head pages; |
48 | 60 | ||
49 | /* The array of pfns we tell the Host about. */ | 61 | /* The array of pfns we tell the Host about. */ |
@@ -66,7 +78,13 @@ static u32 page_to_balloon_pfn(struct page *page) | |||
66 | 78 | ||
67 | BUILD_BUG_ON(PAGE_SHIFT < VIRTIO_BALLOON_PFN_SHIFT); | 79 | BUILD_BUG_ON(PAGE_SHIFT < VIRTIO_BALLOON_PFN_SHIFT); |
68 | /* Convert pfn from Linux page size to balloon page size. */ | 80 | /* Convert pfn from Linux page size to balloon page size. */ |
69 | return pfn >> (PAGE_SHIFT - VIRTIO_BALLOON_PFN_SHIFT); | 81 | return pfn * VIRTIO_BALLOON_PAGES_PER_PAGE; |
82 | } | ||
83 | |||
84 | static struct page *balloon_pfn_to_page(u32 pfn) | ||
85 | { | ||
86 | BUG_ON(pfn % VIRTIO_BALLOON_PAGES_PER_PAGE); | ||
87 | return pfn_to_page(pfn / VIRTIO_BALLOON_PAGES_PER_PAGE); | ||
70 | } | 88 | } |
71 | 89 | ||
72 | static void balloon_ack(struct virtqueue *vq) | 90 | static void balloon_ack(struct virtqueue *vq) |
@@ -96,12 +114,23 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq) | |||
96 | wait_for_completion(&vb->acked); | 114 | wait_for_completion(&vb->acked); |
97 | } | 115 | } |
98 | 116 | ||
117 | static void set_page_pfns(u32 pfns[], struct page *page) | ||
118 | { | ||
119 | unsigned int i; | ||
120 | |||
121 | /* Set balloon pfns pointing at this page. | ||
122 | * Note that the first pfn points at start of the page. */ | ||
123 | for (i = 0; i < VIRTIO_BALLOON_PAGES_PER_PAGE; i++) | ||
124 | pfns[i] = page_to_balloon_pfn(page) + i; | ||
125 | } | ||
126 | |||
99 | static void fill_balloon(struct virtio_balloon *vb, size_t num) | 127 | static void fill_balloon(struct virtio_balloon *vb, size_t num) |
100 | { | 128 | { |
101 | /* We can only do one array worth at a time. */ | 129 | /* We can only do one array worth at a time. */ |
102 | num = min(num, ARRAY_SIZE(vb->pfns)); | 130 | num = min(num, ARRAY_SIZE(vb->pfns)); |
103 | 131 | ||
104 | for (vb->num_pfns = 0; vb->num_pfns < num; vb->num_pfns++) { | 132 | for (vb->num_pfns = 0; vb->num_pfns < num; |
133 | vb->num_pfns += VIRTIO_BALLOON_PAGES_PER_PAGE) { | ||
105 | struct page *page = alloc_page(GFP_HIGHUSER | __GFP_NORETRY | | 134 | struct page *page = alloc_page(GFP_HIGHUSER | __GFP_NORETRY | |
106 | __GFP_NOMEMALLOC | __GFP_NOWARN); | 135 | __GFP_NOMEMALLOC | __GFP_NOWARN); |
107 | if (!page) { | 136 | if (!page) { |
@@ -113,9 +142,9 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num) | |||
113 | msleep(200); | 142 | msleep(200); |
114 | break; | 143 | break; |
115 | } | 144 | } |
116 | vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page); | 145 | set_page_pfns(vb->pfns + vb->num_pfns, page); |
146 | vb->num_pages += VIRTIO_BALLOON_PAGES_PER_PAGE; | ||
117 | totalram_pages--; | 147 | totalram_pages--; |
118 | vb->num_pages++; | ||
119 | list_add(&page->lru, &vb->pages); | 148 | list_add(&page->lru, &vb->pages); |
120 | } | 149 | } |
121 | 150 | ||
@@ -130,8 +159,9 @@ static void release_pages_by_pfn(const u32 pfns[], unsigned int num) | |||
130 | { | 159 | { |
131 | unsigned int i; | 160 | unsigned int i; |
132 | 161 | ||
133 | for (i = 0; i < num; i++) { | 162 | /* Find pfns pointing at start of each page, get pages and free them. */ |
134 | __free_page(pfn_to_page(pfns[i])); | 163 | for (i = 0; i < num; i += VIRTIO_BALLOON_PAGES_PER_PAGE) { |
164 | __free_page(balloon_pfn_to_page(pfns[i])); | ||
135 | totalram_pages++; | 165 | totalram_pages++; |
136 | } | 166 | } |
137 | } | 167 | } |
@@ -143,11 +173,12 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num) | |||
143 | /* We can only do one array worth at a time. */ | 173 | /* We can only do one array worth at a time. */ |
144 | num = min(num, ARRAY_SIZE(vb->pfns)); | 174 | num = min(num, ARRAY_SIZE(vb->pfns)); |
145 | 175 | ||
146 | for (vb->num_pfns = 0; vb->num_pfns < num; vb->num_pfns++) { | 176 | for (vb->num_pfns = 0; vb->num_pfns < num; |
177 | vb->num_pfns += VIRTIO_BALLOON_PAGES_PER_PAGE) { | ||
147 | page = list_first_entry(&vb->pages, struct page, lru); | 178 | page = list_first_entry(&vb->pages, struct page, lru); |
148 | list_del(&page->lru); | 179 | list_del(&page->lru); |
149 | vb->pfns[vb->num_pfns] = page_to_balloon_pfn(page); | 180 | set_page_pfns(vb->pfns + vb->num_pfns, page); |
150 | vb->num_pages--; | 181 | vb->num_pages -= VIRTIO_BALLOON_PAGES_PER_PAGE; |
151 | } | 182 | } |
152 | 183 | ||
153 | /* | 184 | /* |