When doing proactive ballooning to reduce the size of a guest, some
additional vmstat information is useful in deciding how much pressure
to exert on the VM and when the VM starts experiencing undesirable
behavior (memory and io pressure).

Add 11 new statistics tags to the existing balloon stats virtqueue
for improved balloon sizing decisions. Old hosts ignore unknown tags,
so no feature negotiation is required.

New memory composition stats (bytes):
  S_DIRTY:         dirty pages awaiting writeback
  S_WRITEBACK:     pages under active writeback
  S_ANON:          anonymous pages (for balloon ceiling calculation)
  S_INACTIVE_FILE: inactive file LRU (safely reclaimable subset of cache)
  S_SLAB_RECLAIM:  reclaimable slab memory

New workingset stats (counts):
  S_WS_REFAULT_A:  anon workingset refaults
  S_WS_REFAULT_F:  file workingset refaults

New PSI stats (microseconds, cumulative):
  S_PSI_MEM_SOME:  memory pressure (some stalled)
  S_PSI_MEM_FULL:  memory pressure (all stalled)
  S_PSI_IO_SOME:   IO pressure (some stalled)
  S_PSI_IO_FULL:   IO pressure (all stalled)

Export psi_system for module builds (CONFIG_VIRTIO_BALLOON=m with
CONFIG_PSI=y).

Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Gregory Price <[email protected]>
---
 drivers/virtio/virtio_balloon.c     | 33 +++++++++++++++++++++++++++++
 include/uapi/linux/virtio_balloon.h | 26 +++++++++++++++++++++--
 kernel/sched/psi.c                  |  1 +
 3 files changed, 58 insertions(+), 2 deletions(-)

diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index f6c2dff33f8a..8fa33aec4ce7 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -18,6 +18,8 @@
 #include <linux/wait.h>
 #include <linux/mm.h>
 #include <linux/page_reporting.h>
+#include <linux/vmstat.h>
+#include <linux/psi.h>
 
 /*
  * Balloon device works in 4K page units.  So each page is pointed to by
@@ -414,6 +416,37 @@ static unsigned int update_balloon_stats(struct 
virtio_balloon *vb)
        update_stat(vb, idx++, VIRTIO_BALLOON_S_CACHES,
                                pages_to_bytes(caches));
 
+       update_stat(vb, idx++, VIRTIO_BALLOON_S_DIRTY,
+                   pages_to_bytes(global_node_page_state(NR_FILE_DIRTY)));
+       update_stat(vb, idx++, VIRTIO_BALLOON_S_WRITEBACK,
+                   pages_to_bytes(global_node_page_state(NR_WRITEBACK)));
+       update_stat(vb, idx++, VIRTIO_BALLOON_S_ANON,
+                   pages_to_bytes(global_node_page_state(NR_ANON_MAPPED)));
+       update_stat(vb, idx++, VIRTIO_BALLOON_S_INACTIVE_FILE,
+                   pages_to_bytes(global_node_page_state(NR_INACTIVE_FILE)));
+       update_stat(vb, idx++, VIRTIO_BALLOON_S_SLAB_RECLAIM,
+                   pages_to_bytes(
+                       global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B)));
+       update_stat(vb, idx++, VIRTIO_BALLOON_S_WS_REFAULT_A,
+                   global_node_page_state(WORKINGSET_REFAULT_ANON));
+       update_stat(vb, idx++, VIRTIO_BALLOON_S_WS_REFAULT_F,
+                   global_node_page_state(WORKINGSET_REFAULT_FILE));
+
+#ifdef CONFIG_PSI
+       update_stat(vb, idx++, VIRTIO_BALLOON_S_PSI_MEM_SOME,
+                   div_u64(psi_system.total[PSI_AVGS][PSI_MEM_SOME],
+                           NSEC_PER_USEC));
+       update_stat(vb, idx++, VIRTIO_BALLOON_S_PSI_MEM_FULL,
+                   div_u64(psi_system.total[PSI_AVGS][PSI_MEM_FULL],
+                           NSEC_PER_USEC));
+       update_stat(vb, idx++, VIRTIO_BALLOON_S_PSI_IO_SOME,
+                   div_u64(psi_system.total[PSI_AVGS][PSI_IO_SOME],
+                           NSEC_PER_USEC));
+       update_stat(vb, idx++, VIRTIO_BALLOON_S_PSI_IO_FULL,
+                   div_u64(psi_system.total[PSI_AVGS][PSI_IO_FULL],
+                           NSEC_PER_USEC));
+#endif
+
        return idx;
 }
 
diff --git a/include/uapi/linux/virtio_balloon.h 
b/include/uapi/linux/virtio_balloon.h
index ee35a372805d..37ec8a8466c4 100644
--- a/include/uapi/linux/virtio_balloon.h
+++ b/include/uapi/linux/virtio_balloon.h
@@ -77,7 +77,18 @@ struct virtio_balloon_config {
 #define VIRTIO_BALLOON_S_DIRECT_SCAN   13 /* Amount of memory scanned directly 
*/
 #define VIRTIO_BALLOON_S_ASYNC_RECLAIM 14 /* Amount of memory reclaimed 
asynchronously */
 #define VIRTIO_BALLOON_S_DIRECT_RECLAIM 15 /* Amount of memory reclaimed 
directly */
-#define VIRTIO_BALLOON_S_NR       16
+#define VIRTIO_BALLOON_S_DIRTY        16 /* Dirty pages (bytes) */
+#define VIRTIO_BALLOON_S_WRITEBACK     17 /* Pages under writeback (bytes) */
+#define VIRTIO_BALLOON_S_ANON         18 /* Anonymous pages (bytes) */
+#define VIRTIO_BALLOON_S_INACTIVE_FILE 19 /* Inactive file LRU pages (bytes) */
+#define VIRTIO_BALLOON_S_SLAB_RECLAIM  20 /* Reclaimable slab (bytes) */
+#define VIRTIO_BALLOON_S_WS_REFAULT_A  21 /* Workingset refaults anon (count) 
*/
+#define VIRTIO_BALLOON_S_WS_REFAULT_F  22 /* Workingset refaults file (count) 
*/
+#define VIRTIO_BALLOON_S_PSI_MEM_SOME  23 /* PSI memory some total (us) */
+#define VIRTIO_BALLOON_S_PSI_MEM_FULL  24 /* PSI memory full total (us) */
+#define VIRTIO_BALLOON_S_PSI_IO_SOME   25 /* PSI IO some total (us) */
+#define VIRTIO_BALLOON_S_PSI_IO_FULL   26 /* PSI IO full total (us) */
+#define VIRTIO_BALLOON_S_NR           27
 
 #define VIRTIO_BALLOON_S_NAMES_WITH_PREFIX(VIRTIO_BALLOON_S_NAMES_prefix) { \
        VIRTIO_BALLOON_S_NAMES_prefix "swap-in", \
@@ -95,7 +106,18 @@ struct virtio_balloon_config {
        VIRTIO_BALLOON_S_NAMES_prefix "async-scans", \
        VIRTIO_BALLOON_S_NAMES_prefix "direct-scans", \
        VIRTIO_BALLOON_S_NAMES_prefix "async-reclaims", \
-       VIRTIO_BALLOON_S_NAMES_prefix "direct-reclaims" \
+       VIRTIO_BALLOON_S_NAMES_prefix "direct-reclaims", \
+       VIRTIO_BALLOON_S_NAMES_prefix "dirty", \
+       VIRTIO_BALLOON_S_NAMES_prefix "writeback", \
+       VIRTIO_BALLOON_S_NAMES_prefix "anon-pages", \
+       VIRTIO_BALLOON_S_NAMES_prefix "inactive-file", \
+       VIRTIO_BALLOON_S_NAMES_prefix "slab-reclaimable", \
+       VIRTIO_BALLOON_S_NAMES_prefix "ws-refault-anon", \
+       VIRTIO_BALLOON_S_NAMES_prefix "ws-refault-file", \
+       VIRTIO_BALLOON_S_NAMES_prefix "psi-mem-some-us", \
+       VIRTIO_BALLOON_S_NAMES_prefix "psi-mem-full-us", \
+       VIRTIO_BALLOON_S_NAMES_prefix "psi-io-some-us", \
+       VIRTIO_BALLOON_S_NAMES_prefix "psi-io-full-us" \
 }
 
 #define VIRTIO_BALLOON_S_NAMES VIRTIO_BALLOON_S_NAMES_WITH_PREFIX("")
diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c
index d9c9d9480a45..8ab3aa1c4ef5 100644
--- a/kernel/sched/psi.c
+++ b/kernel/sched/psi.c
@@ -175,6 +175,7 @@ static DEFINE_PER_CPU(struct psi_group_cpu, 
system_group_pcpu);
 struct psi_group psi_system = {
        .pcpu = &system_group_pcpu,
 };
+EXPORT_SYMBOL_GPL(psi_system);
 
 static DEFINE_PER_CPU(seqcount_t, psi_seq) = SEQCNT_ZERO(psi_seq);
 
-- 
2.54.0


Reply via email to