diff options
Diffstat (limited to 'drivers/virtio/virtio_balloon.c')
-rw-r--r-- | drivers/virtio/virtio_balloon.c | 109 |
1 files changed, 101 insertions, 8 deletions
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 505be88c82ae..369f2eebbad1 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,10 @@ 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 | int need_stats_update; | ||
55 | struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR]; | ||
52 | }; | 56 | }; |
53 | 57 | ||
54 | static struct virtio_device_id id_table[] = { | 58 | static struct virtio_device_id id_table[] = { |
@@ -154,6 +158,72 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num) | |||
154 | } | 158 | } |
155 | } | 159 | } |
156 | 160 | ||
161 | static inline void update_stat(struct virtio_balloon *vb, int idx, | ||
162 | u16 tag, u64 val) | ||
163 | { | ||
164 | BUG_ON(idx >= VIRTIO_BALLOON_S_NR); | ||
165 | vb->stats[idx].tag = tag; | ||
166 | vb->stats[idx].val = val; | ||
167 | } | ||
168 | |||
169 | #define pages_to_bytes(x) ((u64)(x) << PAGE_SHIFT) | ||
170 | |||
171 | static void update_balloon_stats(struct virtio_balloon *vb) | ||
172 | { | ||
173 | unsigned long events[NR_VM_EVENT_ITEMS]; | ||
174 | struct sysinfo i; | ||
175 | int idx = 0; | ||
176 | |||
177 | all_vm_events(events); | ||
178 | si_meminfo(&i); | ||
179 | |||
180 | update_stat(vb, idx++, VIRTIO_BALLOON_S_SWAP_IN, | ||
181 | pages_to_bytes(events[PSWPIN])); | ||
182 | update_stat(vb, idx++, VIRTIO_BALLOON_S_SWAP_OUT, | ||
183 | pages_to_bytes(events[PSWPOUT])); | ||
184 | update_stat(vb, idx++, VIRTIO_BALLOON_S_MAJFLT, events[PGMAJFAULT]); | ||
185 | update_stat(vb, idx++, VIRTIO_BALLOON_S_MINFLT, events[PGFAULT]); | ||
186 | update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMFREE, | ||
187 | pages_to_bytes(i.freeram)); | ||
188 | update_stat(vb, idx++, VIRTIO_BALLOON_S_MEMTOT, | ||
189 | pages_to_bytes(i.totalram)); | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | * While most virtqueues communicate guest-initiated requests to the hypervisor, | ||
194 | * the stats queue operates in reverse. The driver initializes the virtqueue | ||
195 | * with a single buffer. From that point forward, all conversations consist of | ||
196 | * a hypervisor request (a call to this function) which directs us to refill | ||
197 | * the virtqueue with a fresh stats buffer. Since stats collection can sleep, | ||
198 | * we notify our kthread which does the actual work via stats_handle_request(). | ||
199 | */ | ||
200 | static void stats_request(struct virtqueue *vq) | ||
201 | { | ||
202 | struct virtio_balloon *vb; | ||
203 | unsigned int len; | ||
204 | |||
205 | vb = vq->vq_ops->get_buf(vq, &len); | ||
206 | if (!vb) | ||
207 | return; | ||
208 | vb->need_stats_update = 1; | ||
209 | wake_up(&vb->config_change); | ||
210 | } | ||
211 | |||
212 | static void stats_handle_request(struct virtio_balloon *vb) | ||
213 | { | ||
214 | struct virtqueue *vq; | ||
215 | struct scatterlist sg; | ||
216 | |||
217 | vb->need_stats_update = 0; | ||
218 | update_balloon_stats(vb); | ||
219 | |||
220 | vq = vb->stats_vq; | ||
221 | sg_init_one(&sg, vb->stats, sizeof(vb->stats)); | ||
222 | if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) < 0) | ||
223 | BUG(); | ||
224 | vq->vq_ops->kick(vq); | ||
225 | } | ||
226 | |||
157 | static void virtballoon_changed(struct virtio_device *vdev) | 227 | static void virtballoon_changed(struct virtio_device *vdev) |
158 | { | 228 | { |
159 | struct virtio_balloon *vb = vdev->priv; | 229 | struct virtio_balloon *vb = vdev->priv; |
@@ -190,8 +260,11 @@ static int balloon(void *_vballoon) | |||
190 | try_to_freeze(); | 260 | try_to_freeze(); |
191 | wait_event_interruptible(vb->config_change, | 261 | wait_event_interruptible(vb->config_change, |
192 | (diff = towards_target(vb)) != 0 | 262 | (diff = towards_target(vb)) != 0 |
263 | || vb->need_stats_update | ||
193 | || kthread_should_stop() | 264 | || kthread_should_stop() |
194 | || freezing(current)); | 265 | || freezing(current)); |
266 | if (vb->need_stats_update) | ||
267 | stats_handle_request(vb); | ||
195 | if (diff > 0) | 268 | if (diff > 0) |
196 | fill_balloon(vb, diff); | 269 | fill_balloon(vb, diff); |
197 | else if (diff < 0) | 270 | else if (diff < 0) |
@@ -204,10 +277,10 @@ static int balloon(void *_vballoon) | |||
204 | static int virtballoon_probe(struct virtio_device *vdev) | 277 | static int virtballoon_probe(struct virtio_device *vdev) |
205 | { | 278 | { |
206 | struct virtio_balloon *vb; | 279 | struct virtio_balloon *vb; |
207 | struct virtqueue *vqs[2]; | 280 | struct virtqueue *vqs[3]; |
208 | vq_callback_t *callbacks[] = { balloon_ack, balloon_ack }; | 281 | vq_callback_t *callbacks[] = { balloon_ack, balloon_ack, stats_request }; |
209 | const char *names[] = { "inflate", "deflate" }; | 282 | const char *names[] = { "inflate", "deflate", "stats" }; |
210 | int err; | 283 | int err, nvqs; |
211 | 284 | ||
212 | vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL); | 285 | vdev->priv = vb = kmalloc(sizeof(*vb), GFP_KERNEL); |
213 | if (!vb) { | 286 | if (!vb) { |
@@ -219,14 +292,31 @@ static int virtballoon_probe(struct virtio_device *vdev) | |||
219 | vb->num_pages = 0; | 292 | vb->num_pages = 0; |
220 | init_waitqueue_head(&vb->config_change); | 293 | init_waitqueue_head(&vb->config_change); |
221 | vb->vdev = vdev; | 294 | vb->vdev = vdev; |
295 | vb->need_stats_update = 0; | ||
222 | 296 | ||
223 | /* We expect two virtqueues. */ | 297 | /* We expect two virtqueues: inflate and deflate, |
224 | err = vdev->config->find_vqs(vdev, 2, vqs, callbacks, names); | 298 | * and optionally stat. */ |
299 | nvqs = virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ) ? 3 : 2; | ||
300 | err = vdev->config->find_vqs(vdev, nvqs, vqs, callbacks, names); | ||
225 | if (err) | 301 | if (err) |
226 | goto out_free_vb; | 302 | goto out_free_vb; |
227 | 303 | ||
228 | vb->inflate_vq = vqs[0]; | 304 | vb->inflate_vq = vqs[0]; |
229 | vb->deflate_vq = vqs[1]; | 305 | vb->deflate_vq = vqs[1]; |
306 | if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ)) { | ||
307 | struct scatterlist sg; | ||
308 | vb->stats_vq = vqs[2]; | ||
309 | |||
310 | /* | ||
311 | * Prime this virtqueue with one buffer so the hypervisor can | ||
312 | * use it to signal us later. | ||
313 | */ | ||
314 | sg_init_one(&sg, vb->stats, sizeof vb->stats); | ||
315 | if (vb->stats_vq->vq_ops->add_buf(vb->stats_vq, | ||
316 | &sg, 1, 0, vb) < 0) | ||
317 | BUG(); | ||
318 | vb->stats_vq->vq_ops->kick(vb->stats_vq); | ||
319 | } | ||
230 | 320 | ||
231 | vb->thread = kthread_run(balloon, vb, "vballoon"); | 321 | vb->thread = kthread_run(balloon, vb, "vballoon"); |
232 | if (IS_ERR(vb->thread)) { | 322 | if (IS_ERR(vb->thread)) { |
@@ -264,7 +354,10 @@ static void __devexit virtballoon_remove(struct virtio_device *vdev) | |||
264 | kfree(vb); | 354 | kfree(vb); |
265 | } | 355 | } |
266 | 356 | ||
267 | static unsigned int features[] = { VIRTIO_BALLOON_F_MUST_TELL_HOST }; | 357 | static unsigned int features[] = { |
358 | VIRTIO_BALLOON_F_MUST_TELL_HOST, | ||
359 | VIRTIO_BALLOON_F_STATS_VQ, | ||
360 | }; | ||
268 | 361 | ||
269 | static struct virtio_driver virtio_balloon_driver = { | 362 | static struct virtio_driver virtio_balloon_driver = { |
270 | .feature_table = features, | 363 | .feature_table = features, |