Commit 9564e138b1f6eb137f7149772438d3f3fb3277dd
Committed by
Rusty Russell
1 parent
1f08b833dd
Exists in
master
and in
7 other branches
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 */ |