diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index c74101e..886faa8 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -35,13 +35,56 @@
 #include "hw/virtio/virtio-bus.h"
 #include "hw/virtio/virtio-access.h"
 
-static void balloon_page(void *addr, int deflate)
+#define BALLOON_PAGE_SIZE        (1 << VIRTIO_BALLOON_PFN_SHIFT)
+#define BALLOON_NO_CURRENT_PAGE  ((void *)-1)
+
+static void balloon_page(VirtIOBalloon *s, void *addr, int deflate)
 {
 #if defined(__linux__)
-    if (!qemu_balloon_is_inhibited() && (!kvm_enabled() ||
-                                         kvm_has_sync_mmu())) {
-        qemu_madvise(addr, TARGET_PAGE_SIZE,
-                deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED);
+    size_t host_page_size;
+    void *aligned_addr;
+
+    if (qemu_balloon_is_inhibited() || (kvm_enabled() && !kvm_has_sync_mmu())) {
+        return;
+    }
+
+    host_page_size = getpagesize();
+    if (host_page_size <= BALLOON_PAGE_SIZE) {
+        qemu_madvise(addr, BALLOON_PAGE_SIZE,
+                     deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED);
+        return;
+    }
+
+    /* Host page size > ballon page size ==> use aligned host address */
+    aligned_addr = (void *)((uintptr_t)addr & ~(host_page_size - 1));
+    if (deflate) {
+        /* MADV_WILLNEED is just a hint for the host kernel, the guest should
+         * also be able to use the memory again without this call, so let's
+         * only do it for the first, aligned fragment of a host page and
+         * ignore it for the others.
+         */
+        if (addr == aligned_addr) {
+            qemu_madvise(aligned_addr, host_page_size, QEMU_MADV_WILLNEED);
+        }
+        s->current_addr = BALLOON_NO_CURRENT_PAGE;
+    } else {
+        const int max_frags = host_page_size / BALLOON_PAGE_SIZE;
+        /* If we end up here, that means we want to evict balloon pages, but
+         * the host's page size is bigger than the 4k pages from the balloon.
+         * Since madvise() only works on the granularity of the host page size,
+         * we've got to collect all the 4k fragments from the guest first
+         * before we can issue the MADV_DONTNEED call.
+         */
+        if (aligned_addr != s->current_addr) {
+            memset(s->fragment_bits, 0, s->fragment_bits_size);
+            s->current_addr = aligned_addr;
+        }
+        set_bit((addr - aligned_addr) / BALLOON_PAGE_SIZE, s->fragment_bits);
+        if (find_first_zero_bit(s->fragment_bits, max_frags) == max_frags) {
+            qemu_madvise(aligned_addr, host_page_size, QEMU_MADV_DONTNEED);
+            memset(s->fragment_bits, 0, s->fragment_bits_size);
+            s->current_addr = BALLOON_NO_CURRENT_PAGE;
+        }
     }
 #endif
 }
@@ -240,7 +283,7 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
             /* Using memory_region_get_ram_ptr is bending the rules a bit, but
                should be OK because we only want a single page.  */
             addr = section.offset_within_region;
-            balloon_page(memory_region_get_ram_ptr(section.mr) + addr,
+            balloon_page(s, memory_region_get_ram_ptr(section.mr) + addr,
                          !!(vq == s->dvq));
             memory_region_unref(section.mr);
         }
@@ -455,6 +498,13 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp)
     s->dvq = virtio_add_queue(vdev, 128, virtio_balloon_handle_output);
     s->svq = virtio_add_queue(vdev, 128, virtio_balloon_receive_stats);
 
+    if (getpagesize() > BALLOON_PAGE_SIZE) {
+        s->fragment_bits_size = (getpagesize() / BALLOON_PAGE_SIZE
+                                 + sizeof(long) * 8 - 1) / 8;
+        s->fragment_bits = g_malloc0(s->fragment_bits_size);
+        s->current_addr = BALLOON_NO_CURRENT_PAGE;
+    }
+
     reset_stats(s);
 
     register_savevm(dev, "virtio-balloon", -1, 1,
@@ -466,6 +516,7 @@ static void virtio_balloon_device_unrealize(DeviceState *dev, Error **errp)
     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
     VirtIOBalloon *s = VIRTIO_BALLOON(dev);
 
+    g_free(s->fragment_bits);
     balloon_stats_destroy_timer(s);
     qemu_remove_balloon_handler(s);
     unregister_savevm(dev, "virtio-balloon", s);
@@ -480,6 +531,11 @@ static void virtio_balloon_device_reset(VirtIODevice *vdev)
         g_free(s->stats_vq_elem);
         s->stats_vq_elem = NULL;
     }
+
+    if (s->fragment_bits) {
+        memset(s->fragment_bits, 0, s->fragment_bits_size);
+        s->current_addr = BALLOON_NO_CURRENT_PAGE;
+    }
 }
 
 static void virtio_balloon_instance_init(Object *obj)
diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h
index 35f62ac..04b7c0c 100644
--- a/include/hw/virtio/virtio-balloon.h
+++ b/include/hw/virtio/virtio-balloon.h
@@ -43,6 +43,9 @@ typedef struct VirtIOBalloon {
     int64_t stats_last_update;
     int64_t stats_poll_interval;
     uint32_t host_features;
+    void *current_addr;
+    unsigned long *fragment_bits;
+    int fragment_bits_size;
 } VirtIOBalloon;
 
 #endif
