Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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)

authored by

Adam Litke and committed by
Rusty Russell
9564e138 1f08b833

+101 -8
+86 -8
drivers/virtio/virtio_balloon.c
··· 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 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[] = { ··· 157 154 } 158 155 } 159 156 157 + static inline void update_stat(struct virtio_balloon *vb, int idx, 158 + u16 tag, u64 val) 159 + { 160 + BUG_ON(idx >= VIRTIO_BALLOON_S_NR); 161 + vb->stats[idx].tag = tag; 162 + vb->stats[idx].val = val; 163 + } 164 + 165 + #define pages_to_bytes(x) ((u64)(x) << PAGE_SHIFT) 166 + 167 + static void update_balloon_stats(struct virtio_balloon *vb) 168 + { 169 + unsigned long events[NR_VM_EVENT_ITEMS]; 170 + struct sysinfo i; 171 + int idx = 0; 172 + 173 + all_vm_events(events); 174 + si_meminfo(&i); 175 + 176 + update_stat(vb, idx++, VIRTIO_BALLOON_S_SWAP_IN, 177 + pages_to_bytes(events[PSWPIN])); 178 + update_stat(vb, idx++, VIRTIO_BALLOON_S_SWAP_OUT, 179 + pages_to_bytes(events[PSWPOUT])); 180 + update_stat(vb, idx++, VIRTIO_BALLOON_S_MAJFLT, events[PGMAJFAULT]); 181 + update_stat(vb, idx++, VIRTIO_BALLOON_S_MINFLT, events[PGFAULT]); 182 + update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMFREE, 183 + pages_to_bytes(i.freeram)); 184 + update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMTOT, 185 + pages_to_bytes(i.totalram)); 186 + } 187 + 188 + /* 189 + * While most virtqueues communicate guest-initiated requests to the hypervisor, 190 + * the stats queue operates in reverse. The driver initializes the virtqueue 191 + * with a single buffer. From that point forward, all conversations consist of 192 + * a hypervisor request (a call to this function) which directs us to refill 193 + * the virtqueue with a fresh stats buffer. 194 + */ 195 + static void stats_ack(struct virtqueue *vq) 196 + { 197 + struct virtio_balloon *vb; 198 + unsigned int len; 199 + struct scatterlist sg; 200 + 201 + vb = vq->vq_ops->get_buf(vq, &len); 202 + if (!vb) 203 + return; 204 + 205 + update_balloon_stats(vb); 206 + 207 + sg_init_one(&sg, vb->stats, sizeof(vb->stats)); 208 + if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) < 0) 209 + BUG(); 210 + vq->vq_ops->kick(vq); 211 + } 212 + 160 213 static void virtballoon_changed(struct virtio_device *vdev) 161 214 { 162 215 struct virtio_balloon *vb = vdev->priv; ··· 263 204 static int virtballoon_probe(struct virtio_device *vdev) 264 205 { 265 206 struct virtio_balloon *vb; 266 - struct virtqueue *vqs[2]; 267 - vq_callback_t *callbacks[] = { balloon_ack, balloon_ack }; 268 - const char *names[] = { "inflate", "deflate" }; 269 - int err; 207 + struct virtqueue *vqs[3]; 208 + vq_callback_t *callbacks[] = { balloon_ack, balloon_ack, stats_ack }; 209 + const char *names[] = { "inflate", "deflate", "stats" }; 210 + int err, nvqs; 270 211 271 212 vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL); 272 213 if (!vb) { ··· 279 220 init_waitqueue_head(&vb->config_change); 280 221 vb->vdev = vdev; 281 222 282 - /* We expect two virtqueues. */ 283 - err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names); 223 + /* We expect two virtqueues: inflate and deflate, 224 + * and optionally stat. */ 225 + nvqs = virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ) ? 3 : 2; 226 + err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names); 284 227 if (err) 285 228 goto out_free_vb; 286 229 287 230 vb->inflate_vq = vqs[0]; 288 231 vb->deflate_vq = vqs[1]; 232 + if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ)) { 233 + struct scatterlist sg; 234 + vb->stats_vq = vqs[2]; 235 + 236 + /* 237 + * Prime this virtqueue with one buffer so the hypervisor can 238 + * use it to signal us later. 239 + */ 240 + sg_init_one(&sg, vb->stats, sizeof vb->stats); 241 + if (vb->stats_vq->vq_ops->add_buf(vb->stats_vq, 242 + &sg, 1, 0, vb) < 0) 243 + BUG(); 244 + vb->stats_vq->vq_ops->kick(vb->stats_vq); 245 + } 289 246 290 247 vb->thread = kthread_run(balloon, vb, "vballoon"); 291 248 if (IS_ERR(vb->thread)) { ··· 339 264 kfree(vb); 340 265 } 341 266 342 - static unsigned int features[] = { VIRTIO_BALLOON_F_MUST_TELL_HOST }; 267 + static unsigned int features[] = { 268 + VIRTIO_BALLOON_F_MUST_TELL_HOST, 269 + VIRTIO_BALLOON_F_STATS_VQ, 270 + }; 343 271 344 272 static struct virtio_driver virtio_balloon_driver = { 345 273 .feature_table = features,
+15
include/linux/virtio_balloon.h
··· 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 ··· 19 18 /* Number of pages we've actually got in balloon. */ 20 19 __le32 actual; 21 20 }; 21 + 22 + #define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ 23 + #define VIRTIO_BALLOON_S_SWAP_OUT 1 /* Amount of memory swapped out */ 24 + #define VIRTIO_BALLOON_S_MAJFLT 2 /* Number of major faults */ 25 + #define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */ 26 + #define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */ 27 + #define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ 28 + #define VIRTIO_BALLOON_S_NR 6 29 + 30 + struct virtio_balloon_stat { 31 + u16 tag; 32 + u64 val; 33 + } __attribute__((packed)); 34 + 22 35 #endif /* _LINUX_VIRTIO_BALLOON_H */