Commit 9564e138b1f6eb137f7149772438d3f3fb3277dd

Authored by Adam Litke
Committed by Rusty Russell
1 parent 1f08b833dd

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)

Showing 2 changed files with 101 additions and 8 deletions Side-by-side Diff

drivers/virtio/virtio_balloon.c
... ... @@ -28,7 +28,7 @@
28 28 struct virtio_balloon
29 29 {
30 30 struct virtio_device *vdev;
31   - struct virtqueue *inflate_vq, *deflate_vq;
  31 + struct virtqueue *inflate_vq, *deflate_vq, *stats_vq;
32 32  
33 33 /* Where the ballooning thread waits for config to change. */
34 34 wait_queue_head_t config_change;
... ... @@ -49,6 +49,9 @@
49 49 /* The array of pfns we tell the Host about. */
50 50 unsigned int num_pfns;
51 51 u32 pfns[256];
  52 +
  53 + /* Memory statistics */
  54 + struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR];
52 55 };
53 56  
54 57 static struct virtio_device_id id_table[] = {
... ... @@ -154,6 +157,62 @@
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 216 static void virtballoon_changed(struct virtio_device *vdev)
158 217 {
159 218 struct virtio_balloon *vb = vdev->priv;
... ... @@ -204,10 +263,10 @@
204 263 static int virtballoon_probe(struct virtio_device *vdev)
205 264 {
206 265 struct virtio_balloon *vb;
207   - struct virtqueue *vqs[2];
208   - vq_callback_t *callbacks[] = { balloon_ack, balloon_ack };
209   - const char *names[] = { "inflate", "deflate" };
210   - int err;
  266 + struct virtqueue *vqs[3];
  267 + vq_callback_t *callbacks[] = { balloon_ack, balloon_ack, stats_ack };
  268 + const char *names[] = { "inflate", "deflate", "stats" };
  269 + int err, nvqs;
211 270  
212 271 vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL);
213 272 if (!vb) {
214 273  
215 274  
... ... @@ -220,14 +279,30 @@
220 279 init_waitqueue_head(&vb->config_change);
221 280 vb->vdev = vdev;
222 281  
223   - /* We expect two virtqueues. */
224   - err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names);
  282 + /* We expect two virtqueues: inflate and deflate,
  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 286 if (err)
226 287 goto out_free_vb;
227 288  
228 289 vb->inflate_vq = vqs[0];
229 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];
230 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 + }
  305 +
231 306 vb->thread = kthread_run(balloon, vb, "vballoon");
232 307 if (IS_ERR(vb->thread)) {
233 308 err = PTR_ERR(vb->thread);
... ... @@ -264,7 +339,10 @@
264 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 347 static struct virtio_driver virtio_balloon_driver = {
270 348 .feature_table = features,
include/linux/virtio_balloon.h
... ... @@ -7,6 +7,7 @@
7 7  
8 8 /* The feature bitmap for virtio balloon */
9 9 #define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */
  10 +#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory Stats virtqueue */
10 11  
11 12 /* Size of a PFN in the balloon interface. */
12 13 #define VIRTIO_BALLOON_PFN_SHIFT 12
... ... @@ -18,5 +19,19 @@
18 19 /* Number of pages we've actually got in balloon. */
19 20 __le32 actual;
20 21 };
  22 +
  23 +#define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */
  24 +#define VIRTIO_BALLOON_S_SWAP_OUT 1 /* Amount of memory swapped out */
  25 +#define VIRTIO_BALLOON_S_MAJFLT 2 /* Number of major faults */
  26 +#define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */
  27 +#define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */
  28 +#define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */
  29 +#define VIRTIO_BALLOON_S_NR 6
  30 +
  31 +struct virtio_balloon_stat {
  32 + u16 tag;
  33 + u64 val;
  34 +} __attribute__((packed));
  35 +
21 36 #endif /* _LINUX_VIRTIO_BALLOON_H */