diff options
author | Adam Litke <agl@us.ibm.com> | 2009-11-30 11:14:15 -0500 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2010-02-23 22:52:08 -0500 |
commit | 9564e138b1f6eb137f7149772438d3f3fb3277dd (patch) | |
tree | 2e53562c2b7ab7051d448b8f81766dc61ad524b5 /drivers/virtio/virtio_balloon.c | |
parent | 1f08b833ddbdb1c8e81c4b1053c2ebb7b89cb437 (diff) |
virtio: Add memory statistics reporting to the balloon driver (V4)
Changes since V3:
- Do not do endian conversions as they will be done in the host
- Report stats that reference a quantity of memory in bytes
- Minor coding style updates
Changes since V2:
- Increase stat field size to 64 bits
- Report all sizes in kb (not pages)
- Drop anon_pages stat and fix endianness conversion
Changes since V1:
- Use a virtqueue instead of the device config space
When using ballooning to manage overcommitted memory on a host, a system for
guests to communicate their memory usage to the host can provide information
that will minimize the impact of ballooning on the guests. The current method
employs a daemon running in each guest that communicates memory statistics to a
host daemon at a specified time interval. The host daemon aggregates this
information and inflates and/or deflates balloons according to the level of
host memory pressure. This approach is effective but overly complex since a
daemon must be installed inside each guest and coordinated to communicate with
the host. A simpler approach is to collect memory statistics in the virtio
balloon driver and communicate them directly to the hypervisor.
This patch enables the guest-side support by adding stats collection and
reporting to the virtio balloon driver.
Signed-off-by: Adam Litke <agl@us.ibm.com>
Cc: Anthony Liguori <anthony@codemonkey.ws>
Cc: virtualization@lists.linux-foundation.org
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (minor fixes)
Diffstat (limited to 'drivers/virtio/virtio_balloon.c')
-rw-r--r-- | drivers/virtio/virtio_balloon.c | 94 |
1 files changed, 86 insertions, 8 deletions
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 505be88c82ae..cd778b1752b5 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c | |||
@@ -28,7 +28,7 @@ | |||
28 | struct virtio_balloon | 28 | struct virtio_balloon |
29 | { | 29 | { |
30 | struct virtio_device *vdev; | 30 | struct virtio_device *vdev; |
31 | struct virtqueue *inflate_vq, *deflate_vq; | 31 | struct virtqueue *inflate_vq, *deflate_vq, *stats_vq; |
32 | 32 | ||
33 | /* Where the ballooning thread waits for config to change. */ | 33 | /* Where the ballooning thread waits for config to change. */ |
34 | wait_queue_head_t config_change; | 34 | wait_queue_head_t config_change; |
@@ -49,6 +49,9 @@ struct virtio_balloon | |||
49 | /* The array of pfns we tell the Host about. */ | 49 | /* The array of pfns we tell the Host about. */ |
50 | unsigned int num_pfns; | 50 | unsigned int num_pfns; |
51 | u32 pfns[256]; | 51 | u32 pfns[256]; |
52 | |||
53 | /* Memory statistics */ | ||
54 | struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR]; | ||
52 | }; | 55 | }; |
53 | 56 | ||
54 | static struct virtio_device_id id_table[] = { | 57 | static struct virtio_device_id id_table[] = { |
@@ -154,6 +157,62 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num) | |||
154 | } | 157 | } |
155 | } | 158 | } |
156 | 159 | ||
160 | static inline void update_stat(struct virtio_balloon *vb, int idx, | ||
161 | u16 tag, u64 val) | ||
162 | { | ||
163 | BUG_ON(idx >= VIRTIO_BALLOON_S_NR); | ||
164 | vb->stats[idx].tag = tag; | ||
165 | vb->stats[idx].val = val; | ||
166 | } | ||
167 | |||
168 | #define pages_to_bytes(x) ((u64)(x) << PAGE_SHIFT) | ||
169 | |||
170 | static void update_balloon_stats(struct virtio_balloon *vb) | ||
171 | { | ||
172 | unsigned long events[NR_VM_EVENT_ITEMS]; | ||
173 | struct sysinfo i; | ||
174 | int idx = 0; | ||
175 | |||
176 | all_vm_events(events); | ||
177 | si_meminfo(&i); | ||
178 | |||
179 | update_stat(vb, idx++, VIRTIO_BALLOON_S_SWAP_IN, | ||
180 | pages_to_bytes(events[PSWPIN])); | ||
181 | update_stat(vb, idx++, VIRTIO_BALLOON_S_SWAP_OUT, | ||
182 | pages_to_bytes(events[PSWPOUT])); | ||
183 | update_stat(vb, idx++, VIRTIO_BALLOON_S_MAJFLT, events[PGMAJFAULT]); | ||
184 | update_stat(vb, idx++, VIRTIO_BALLOON_S_MINFLT, events[PGFAULT]); | ||
185 | update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMFREE, | ||
186 | pages_to_bytes(i.freeram)); | ||
187 | update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMTOT, | ||
188 | pages_to_bytes(i.totalram)); | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * While most virtqueues communicate guest-initiated requests to the hypervisor, | ||
193 | * the stats queue operates in reverse. The driver initializes the virtqueue | ||
194 | * with a single buffer. From that point forward, all conversations consist of | ||
195 | * a hypervisor request (a call to this function) which directs us to refill | ||
196 | * the virtqueue with a fresh stats buffer. | ||
197 | */ | ||
198 | static void stats_ack(struct virtqueue *vq) | ||
199 | { | ||
200 | struct virtio_balloon *vb; | ||
201 | unsigned int len; | ||
202 | struct scatterlist sg; | ||
203 | |||
204 | vb = vq->vq_ops->get_buf(vq, &len); | ||
205 | if (!vb) | ||
206 | return; | ||
207 | |||
208 | update_balloon_stats(vb); | ||
209 | |||
210 | sg_init_one(&sg, vb->stats, sizeof(vb->stats)); | ||
211 | if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) < 0) | ||
212 | BUG(); | ||
213 | vq->vq_ops->kick(vq); | ||
214 | } | ||
215 | |||
157 | static void virtballoon_changed(struct virtio_device *vdev) | 216 | static void virtballoon_changed(struct virtio_device *vdev) |
158 | { | 217 | { |
159 | struct virtio_balloon *vb = vdev->priv; | 218 | struct virtio_balloon *vb = vdev->priv; |
@@ -204,10 +263,10 @@ static int balloon(void *_vballoon) | |||
204 | static int virtballoon_probe(struct virtio_device *vdev) | 263 | static int virtballoon_probe(struct virtio_device *vdev) |
205 | { | 264 | { |
206 | struct virtio_balloon *vb; | 265 | struct virtio_balloon *vb; |
207 | struct virtqueue *vqs[2]; | 266 | struct virtqueue *vqs[3]; |
208 | vq_callback_t *callbacks[] = { balloon_ack, balloon_ack }; | 267 | vq_callback_t *callbacks[] = { balloon_ack, balloon_ack, stats_ack }; |
209 | const char *names[] = { "inflate", "deflate" }; | 268 | const char *names[] = { "inflate", "deflate", "stats" }; |
210 | int err; | 269 | int err, nvqs; |
211 | 270 | ||
212 | vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL); | 271 | vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL); |
213 | if (!vb) { | 272 | if (!vb) { |
@@ -220,13 +279,29 @@ static int virtballoon_probe(struct virtio_device *vdev) | |||
220 | init_waitqueue_head(&vb->config_change); | 279 | init_waitqueue_head(&vb->config_change); |
221 | vb->vdev = vdev; | 280 | vb->vdev = vdev; |
222 | 281 | ||
223 | /* We expect two virtqueues. */ | 282 | /* We expect two virtqueues: inflate and deflate, |
224 | err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names); | 283 | * and optionally stat. */ |
284 | nvqs = virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ) ? 3 : 2; | ||
285 | err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names); | ||
225 | if (err) | 286 | if (err) |
226 | goto out_free_vb; | 287 | goto out_free_vb; |
227 | 288 | ||
228 | vb->inflate_vq = vqs[0]; | 289 | vb->inflate_vq = vqs[0]; |
229 | vb->deflate_vq = vqs[1]; | 290 | vb->deflate_vq = vqs[1]; |
291 | if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ)) { | ||
292 | struct scatterlist sg; | ||
293 | vb->stats_vq = vqs[2]; | ||
294 | |||
295 | /* | ||
296 | * Prime this virtqueue with one buffer so the hypervisor can | ||
297 | * use it to signal us later. | ||
298 | */ | ||
299 | sg_init_one(&sg, vb->stats, sizeof vb->stats); | ||
300 | if (vb->stats_vq->vq_ops->add_buf(vb->stats_vq, | ||
301 | &sg, 1, 0, vb) < 0) | ||
302 | BUG(); | ||
303 | vb->stats_vq->vq_ops->kick(vb->stats_vq); | ||
304 | } | ||
230 | 305 | ||
231 | vb->thread = kthread_run(balloon, vb, "vballoon"); | 306 | vb->thread = kthread_run(balloon, vb, "vballoon"); |
232 | if (IS_ERR(vb->thread)) { | 307 | if (IS_ERR(vb->thread)) { |
@@ -264,7 +339,10 @@ static void __devexit virtballoon_remove(struct virtio_device *vdev) | |||
264 | kfree(vb); | 339 | kfree(vb); |
265 | } | 340 | } |
266 | 341 | ||
267 | static unsigned int features[] = { VIRTIO_BALLOON_F_MUST_TELL_HOST }; | 342 | static unsigned int features[] = { |
343 | VIRTIO_BALLOON_F_MUST_TELL_HOST, | ||
344 | VIRTIO_BALLOON_F_STATS_VQ, | ||
345 | }; | ||
268 | 346 | ||
269 | static struct virtio_driver virtio_balloon_driver = { | 347 | static struct virtio_driver virtio_balloon_driver = { |
270 | .feature_table = features, | 348 | .feature_table = features, |