On 5/13/26 18:50, Gregory Price wrote: > 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);
Nothing too crazy here, however, the question is which of these values we actually want to guarantee that we will provide them with unchanged semantics in the future ... I guess anything we already expose to user space is alright (because it effectively already must remain mostly unchanged I assume). -- Cheers, David

