#include <linux/mount.h>
 #include <linux/magic.h>
 #include <linux/pseudo_fs.h>
+#include <linux/page_reporting.h>
 
 /*
  * Balloon device works in 4K page units.  So each page is pointed to by
        VIRTIO_BALLOON_VQ_DEFLATE,
        VIRTIO_BALLOON_VQ_STATS,
        VIRTIO_BALLOON_VQ_FREE_PAGE,
+       VIRTIO_BALLOON_VQ_REPORTING,
        VIRTIO_BALLOON_VQ_MAX
 };
 
 
        /* To register a shrinker to shrink memory upon memory pressure */
        struct shrinker shrinker;
+
+       /* Free page reporting device */
+       struct virtqueue *reporting_vq;
+       struct page_reporting_dev_info pr_dev_info;
 };
 
 static struct virtio_device_id id_table[] = {
 
 }
 
+int virtballoon_free_page_report(struct page_reporting_dev_info *pr_dev_info,
+                                  struct scatterlist *sg, unsigned int nents)
+{
+       struct virtio_balloon *vb =
+               container_of(pr_dev_info, struct virtio_balloon, pr_dev_info);
+       struct virtqueue *vq = vb->reporting_vq;
+       unsigned int unused, err;
+
+       /* We should always be able to add these buffers to an empty queue. */
+       err = virtqueue_add_inbuf(vq, sg, nents, vb, GFP_NOWAIT | __GFP_NOWARN);
+
+       /*
+        * In the extremely unlikely case that something has occurred and we
+        * are able to trigger an error we will simply display a warning
+        * and exit without actually processing the pages.
+        */
+       if (WARN_ON_ONCE(err))
+               return err;
+
+       virtqueue_kick(vq);
+
+       /* When host has read buffer, this completes via balloon_ack */
+       wait_event(vb->acked, virtqueue_get_buf(vq, &unused));
+
+       return 0;
+}
+
 static void set_page_pfns(struct virtio_balloon *vb,
                          __virtio32 pfns[], struct page *page)
 {
        names[VIRTIO_BALLOON_VQ_STATS] = NULL;
        callbacks[VIRTIO_BALLOON_VQ_FREE_PAGE] = NULL;
        names[VIRTIO_BALLOON_VQ_FREE_PAGE] = NULL;
+       names[VIRTIO_BALLOON_VQ_REPORTING] = NULL;
 
        if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_STATS_VQ)) {
                names[VIRTIO_BALLOON_VQ_STATS] = "stats";
                callbacks[VIRTIO_BALLOON_VQ_FREE_PAGE] = NULL;
        }
 
+       if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING)) {
+               names[VIRTIO_BALLOON_VQ_REPORTING] = "reporting_vq";
+               callbacks[VIRTIO_BALLOON_VQ_REPORTING] = balloon_ack;
+       }
+
        err = vb->vdev->config->find_vqs(vb->vdev, VIRTIO_BALLOON_VQ_MAX,
                                         vqs, callbacks, names, NULL, NULL);
        if (err)
        if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
                vb->free_page_vq = vqs[VIRTIO_BALLOON_VQ_FREE_PAGE];
 
+       if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING))
+               vb->reporting_vq = vqs[VIRTIO_BALLOON_VQ_REPORTING];
+
        return 0;
 }
 
                if (err)
                        goto out_del_balloon_wq;
        }
+
+       vb->pr_dev_info.report = virtballoon_free_page_report;
+       if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING)) {
+               unsigned int capacity;
+
+               capacity = virtqueue_get_vring_size(vb->reporting_vq);
+               if (capacity < PAGE_REPORTING_CAPACITY) {
+                       err = -ENOSPC;
+                       goto out_unregister_shrinker;
+               }
+
+               err = page_reporting_register(&vb->pr_dev_info);
+               if (err)
+                       goto out_unregister_shrinker;
+       }
+
        virtio_device_ready(vdev);
 
        if (towards_target(vb))
                virtballoon_changed(vdev);
        return 0;
 
+out_unregister_shrinker:
+       if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM))
+               virtio_balloon_unregister_shrinker(vb);
 out_del_balloon_wq:
        if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT))
                destroy_workqueue(vb->balloon_wq);
 {
        struct virtio_balloon *vb = vdev->priv;
 
+       if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_REPORTING))
+               page_reporting_unregister(&vb->pr_dev_info);
        if (virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM))
                virtio_balloon_unregister_shrinker(vb);
        spin_lock_irq(&vb->stop_update_lock);
        VIRTIO_BALLOON_F_DEFLATE_ON_OOM,
        VIRTIO_BALLOON_F_FREE_PAGE_HINT,
        VIRTIO_BALLOON_F_PAGE_POISON,
+       VIRTIO_BALLOON_F_REPORTING,
 };
 
 static struct virtio_driver virtio_balloon_driver = {