Add a new vq to report hints of guest free pages to the host.
Signed-off-by: Wei Wang <wei.w.wang@xxxxxxxxx>
Signed-off-by: Liang Li <liang.z.li@xxxxxxxxx>
---
drivers/virtio/virtio_balloon.c | 167 +++++++++++++++++++++++++++++++-----
include/uapi/linux/virtio_balloon.h | 1 +
2 files changed, 147 insertions(+), 21 deletions(-)
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 72041b4..e6755bc 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -54,11 +54,12 @@ static struct vfsmount *balloon_mnt;
struct virtio_balloon {
struct virtio_device *vdev;
- struct virtqueue *inflate_vq, *deflate_vq, *stats_vq;
+ struct virtqueue *inflate_vq, *deflate_vq, *stats_vq, *free_page_vq;
/* The balloon servicing is delegated to a freezable workqueue. */
struct work_struct update_balloon_stats_work;
struct work_struct update_balloon_size_work;
+ struct work_struct report_free_page_work;
/* Prevent updating balloon when it is being canceled. */
spinlock_t stop_update_lock;
@@ -90,6 +91,13 @@ struct virtio_balloon {
/* Memory statistics */
struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR];
+ /*
+ * Used by the device and driver to signal each other.
+ * device->driver: start the free page report.
+ * driver->device: end the free page report.
+ */
+ __virtio32 report_free_page_signal;
+
/* To register callback in oom notifier call chain */
struct notifier_block nb;
};
@@ -174,6 +182,17 @@ static void send_balloon_page_sg(struct virtio_balloon *vb,
} while (unlikely(ret == -ENOSPC));
}
+static void send_free_page_sg(struct virtqueue *vq, void *addr, uint32_t size)
+{
+ unsigned int len;
+
+ add_one_sg(vq, addr, size);
+ virtqueue_kick(vq);
+ /* Release entries if there are */
+ while (virtqueue_get_buf(vq, &len))
+ ;
+}
+
/*
* Send balloon pages in sgs to host. The balloon pages are recorded in the
* page xbitmap. Each bit in the bitmap corresponds to a page of PAGE_SIZE.
@@ -511,42 +530,143 @@ static void update_balloon_size_func(struct work_struct *work)
queue_work(system_freezable_wq, work);
}
+static void virtio_balloon_send_free_pages(void *opaque, unsigned long pfn,
+ unsigned long nr_pages)
+{
+ struct virtio_balloon *vb = (struct virtio_balloon *)opaque;
+ void *addr = (void *)pfn_to_kaddr(pfn);
+ uint32_t len = nr_pages << PAGE_SHIFT;
+
+ send_free_page_sg(vb->free_page_vq, addr, len);
+}
+
+static void report_free_page_completion(struct virtio_balloon *vb)
+{
+ struct virtqueue *vq = vb->free_page_vq;
+ struct scatterlist sg;
+ unsigned int len;
+ int ret;
+
+ sg_init_one(&sg, &vb->report_free_page_signal, sizeof(__virtio32));
+retry:
+ ret = virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL);
+ virtqueue_kick(vq);
+ if (unlikely(ret == -ENOSPC)) {
+ wait_event(vb->acked, virtqueue_get_buf(vq, &len));
+ goto retry;
+ }
+}