diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c
index 97d49b1..40a97e7 100644
--- a/hw/virtio-balloon.c
+++ b/hw/virtio-balloon.c
@@ -37,6 +37,13 @@ typedef struct VirtIOBalloon
     VirtQueueElement stats_vq_elem;
     size_t stats_vq_offset;
     DeviceState *qdev;
+
+    /* auto-balloon */
+    bool auto_balloon_enabled;
+    int cfd;
+    int lfd;
+    float granularity;
+    EventNotifier mempressure_ev;
 } VirtIOBalloon;
 
 static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev)
@@ -157,7 +164,14 @@ static void virtio_balloon_set_config(VirtIODevice *vdev,
 
 static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f)
 {
+    VirtIOBalloon *s = to_virtio_balloon(vdev);
+
     f |= (1 << VIRTIO_BALLOON_F_STATS_VQ);
+
+    if (s->auto_balloon_enabled) {
+        f |= (1 << VIRTIO_BALLOON_F_AUTO_BALLOON);
+    }
+
     return f;
 }
 
@@ -166,6 +180,11 @@ static ram_addr_t guest_get_actual_ram(const VirtIOBalloon *s)
     return ram_size - ((uint64_t) s->actual << VIRTIO_BALLOON_PFN_SHIFT);
 }
 
+static bool guest_supports_auto_balloon(const VirtIOBalloon *s)
+{
+    return s->vdev.guest_features & (1 << VIRTIO_BALLOON_F_AUTO_BALLOON);
+}
+
 static void virtio_balloon_stat(void *opaque, BalloonInfo *info)
 {
     VirtIOBalloon *dev = opaque;
@@ -235,6 +254,133 @@ static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id)
     return 0;
 }
 
+static int open_sysfile(const char *path, const char *file, mode_t mode)
+{
+    char *p;
+    int fd;
+
+    p = g_strjoin("/", path, file, NULL);
+    fd = qemu_open(p, mode);
+    if (fd < 0) {
+        error_report("balloon: can't open '%s': %s", p, strerror(errno));
+    }
+
+    g_free(p);
+    return fd;
+}
+
+static int balloon_ack_event(EventNotifier *ev)
+{
+    uint64_t res;
+    int ret, fd;
+
+    fd = event_notifier_get_fd(ev);
+
+    do {
+        ret = read(fd, &res, sizeof(res));
+    } while (ret == -1 && errno == EINTR);
+
+    return ret;
+}
+
+static void host_mempressure_cleanup(VirtIOBalloon *s);
+
+static void host_mempressure_cb(EventNotifier *ev)
+{
+    VirtIOBalloon *s = container_of(ev, VirtIOBalloon, mempressure_ev);
+    ram_addr_t target;
+    int ret;
+
+    ret = balloon_ack_event(&s->mempressure_ev);
+    if (ret < 0) {
+        fprintf(stderr, "balloon: failed to ack event: %s\n", strerror(errno));
+        return;
+    }
+
+    if (!guest_supports_auto_balloon(s)) {
+        fprintf(stderr,
+          "balloon: oops guest doesn't support auto-ballooning, disabling..\n");
+        host_mempressure_cleanup(s);
+        return;
+    }
+
+    target = guest_get_actual_ram(s) -
+             (guest_get_actual_ram(s) * s->granularity);
+    virtio_balloon_to_target(s, target);
+}
+
+static int host_mempressure_init(VirtIOBalloon *s,
+                                 const virtio_balloon_conf *conf)
+{
+    char *line;
+    int ret, fd;
+
+    if (!conf->path || !conf->level) {
+        error_report("balloon: mempressure path or level missing");
+        return -1;
+    }
+
+    if (conf->granularity > 100) {
+        error_report("balloon: invalid granularity value (should be 0..100)");
+        return -1;
+    }
+
+    s->lfd = open_sysfile(conf->path, "mempressure.level", O_RDONLY);
+    if (s->lfd < 0) {
+        return -1;
+    }
+
+    s->cfd = open_sysfile(conf->path, "cgroup.event_control", O_WRONLY);
+    if (s->cfd < 0) {
+        close(s->lfd);
+        return -1;
+    }
+
+    ret = event_notifier_init(&s->mempressure_ev, false);
+    if (ret < 0) {
+        error_report("failed to create notifier: %s", strerror(-ret));
+        goto out_err;
+    }
+
+    fd = event_notifier_get_fd(&s->mempressure_ev);
+    line = g_strdup_printf("%d %d %s", fd, s->lfd, conf->level);
+
+    do {
+        ret = write(s->cfd, line, strlen(line));
+    } while (ret < 0 && errno == EINTR);
+
+    if (ret < 0) {
+        error_report("balloon: write failed: %s", strerror(errno));
+        g_free(line);
+        goto out_ev;
+    }
+
+    g_free(line);
+
+    s->auto_balloon_enabled = true;
+    s->granularity = conf->granularity / 100.0;
+    event_notifier_set_handler(&s->mempressure_ev, host_mempressure_cb);
+
+    return 0;
+
+out_ev:
+    event_notifier_cleanup(&s->mempressure_ev);
+out_err:
+    close(s->lfd);
+    close(s->cfd);
+    return -1;
+}
+
+static void host_mempressure_cleanup(VirtIOBalloon *s)
+{
+    if (s->auto_balloon_enabled) {
+        close(s->lfd);
+        close(s->cfd);
+        event_notifier_cleanup(&s->mempressure_ev);
+        s->auto_balloon_enabled = false;
+    }
+}
+
 VirtIODevice *virtio_balloon_init(DeviceState *dev, virtio_balloon_conf *conf)
 {
     VirtIOBalloon *s;
@@ -248,9 +394,18 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev, virtio_balloon_conf *conf)
     s->vdev.set_config = virtio_balloon_set_config;
     s->vdev.get_features = virtio_balloon_get_features;
 
+    if (conf->path || conf->level || conf->granularity > 0) {
+        ret = host_mempressure_init(s, conf);
+        if (ret < 0) {
+            virtio_cleanup(&s->vdev);
+            return NULL;
+        }
+    }
+
     ret = qemu_add_balloon_handler(virtio_balloon_to_target,
                                    virtio_balloon_stat, s);
     if (ret < 0) {
+        host_mempressure_cleanup(s);
         virtio_cleanup(&s->vdev);
         return NULL;
     }
@@ -273,6 +428,7 @@ void virtio_balloon_exit(VirtIODevice *vdev)
     VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev);
 
     qemu_remove_balloon_handler(s);
+    host_mempressure_cleanup(s);
     unregister_savevm(s->qdev, "virtio-balloon", s);
     virtio_cleanup(vdev);
 }
diff --git a/hw/virtio-balloon.h b/hw/virtio-balloon.h
index 9d631d5..fcf0e3c 100644
--- a/hw/virtio-balloon.h
+++ b/hw/virtio-balloon.h
@@ -26,6 +26,7 @@
 /* The feature bitmap for virtio balloon */
 #define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */
 #define VIRTIO_BALLOON_F_STATS_VQ 1       /* Memory stats virtqueue */
+#define VIRTIO_BALLOON_F_AUTO_BALLOON 2   /* Automatic ballooning */
 
 /* Size of a PFN in the balloon interface. */
 #define VIRTIO_BALLOON_PFN_SHIFT 12
@@ -40,6 +41,9 @@ struct virtio_balloon_config
 
 typedef struct virtio_balloon_conf
 {
+    char *path;
+    char *level;
+    uint32_t granularity;
 } virtio_balloon_conf;
 
 /* Memory Statistics */
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 026222b..487b7f2 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -991,6 +991,11 @@ static TypeInfo virtio_serial_info = {
 static Property virtio_balloon_properties[] = {
     DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
     DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
+#ifdef __linux__
+    DEFINE_PROP_STRING("auto-balloon-mempressure-path", VirtIOPCIProxy, balloon.path),
+    DEFINE_PROP_STRING("auto-balloon-level", VirtIOPCIProxy, balloon.level),
+    DEFINE_PROP_UINT32("auto-balloon-granularity", VirtIOPCIProxy, balloon.granularity, 0),
+#endif
     DEFINE_PROP_END_OF_LIST(),
 };
 
