From nobody Fri Jun 12 17:18:13 2026 Received: from mail-qt1-f169.google.com (mail-qt1-f169.google.com [209.85.160.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D0DE24BCAA5 for ; Wed, 13 May 2026 16:50:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778691021; cv=none; b=BhsM0Kb98uEEjm/droJTxwF38FK8oZMvpIN6liIW3se3Ys1ucg9EuXMfO+JW+eFBoqSlDfKnZTTeUZcw4xvKLhM7kaMVcW4nxLvpzN+JEOenVhOquUKSGbdUJ9zM/Qy0VhlfTTPzR6gnjrP8quUJJLlGVu+6LSmbFtFLlpqbmtM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778691021; c=relaxed/simple; bh=ubWLVrkqIfMjNhK3RiA5i6R8ocZ29s4Mn+vxxSfA+gg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=TaITt0y3+dyNWGwBsTNT4HPqOTmVWMe3V5S+mf+NPRUT6Nq99sQKYddL9/Med6MDSB38vB03cTEq8cLPo47lxHElTW0HjkGCceeFJzkUNkxK1NO693pyLlBYm1E5/aYg3RA5WMEWamnxGvBLlk7TAZwYE1mIDZ1u8SbdeBiH1m4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net; spf=pass smtp.mailfrom=gourry.net; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b=ekcklQLL; arc=none smtp.client-ip=209.85.160.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gourry.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b="ekcklQLL" Received: by mail-qt1-f169.google.com with SMTP id d75a77b69052e-50faf8ed9c5so37203631cf.2 for ; Wed, 13 May 2026 09:50:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gourry.net; s=google; t=1778691012; x=1779295812; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=aLcL6E1qyfzlUGSv6Nk/PekuP6kW11aRlu3ns23KnZ4=; b=ekcklQLLqrJCCq2R1JWsj5FVfkxnCTkkDgJRtHKCH6/9mrKcnV0/GY/OzrIN7cZjyB aEMRajNI5gF//lswkVV52nnF37I4pTHNMp8UiS/uozE9/DH7EtkFTQUfH+AiKmdIo65E 1EXCCyorsyPOHLK/C2/ZSKKW02qAY28fmIypdBZPfM+0UYEYqye8YftBPb+2w4ASdjTd UWPXOaREcvwz/3y0itXnuO+89BuUzX62i5u0XEwPB5Wvl+AluMB0RAjHSPEto/hK/eSd zLl7co/v2J2f4Lz+cZTkUwMCvk1shGmTawd6KT4KDQOlQUD0tb6E3stK3AsYvdxZjIE5 QufQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778691012; x=1779295812; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=aLcL6E1qyfzlUGSv6Nk/PekuP6kW11aRlu3ns23KnZ4=; b=iVxR48bOe8cJ7MEh/a8PkluECHTn8pn4UH6QBP5BPid9ctDbEX5MUzbBIB+ZC3bWRD BhH8XXmQaMz1OBIzX/04Hnf9gM5Ild13xoTwd9ralXCfkCDXfU+URWwJZJGysKVQEOPI JV1+D+7um0IL1SnVTdu9yUsIwqIiUM1cWQU48Ev6+yItHdzpU+zPrwwakklfb6Gljtz9 Il/J0JyVthVJCrG4pnrSFdsSTw4hELgCVocTiYWnQrPtogftRgX/wtC3804aZ58FFQ6n i0h4uUfgl2JTyWRfyxgH/FwU3tC6rOPq7oIn4jFJdrNDD7m6o+Q25RZJSSgg52GOr6MS QIrw== X-Gm-Message-State: AOJu0Yxn64uf39nL/ma6c0f5rAsaKJvt5cMcuDTQRkGhYOaUCC6V4RqG u7fMozU7Qle6KR/07CLT0on6U3I2aZ4wJW+zwDD24RGOhWC2+Ovf6PTAPYPkKtGkWMk= X-Gm-Gg: Acq92OE0mw6MxEAa6uBzGYCz6OqM96oZHfk77CHIkkGTn3heC6zGjhN2CIMYc5z8GpT qLTIjBFbV1clnmHYh2SIm5ipnZYAZ8ZQ5PWIurtA4K5vtE4ch1ApwLISijaBoku5x1vW37oafeg paW7DLQ9g7xm+HndtWUX6ABR6rH5sUrEfHJ8AhRouoxTJJP1jzDD38fAa6k8nQD7n7vaninfVkG NY1drl9WU1nTRDsKEEgeeP7/FGE1tCHJOEO7vVkSIf5Hnlr1bDUVTuAgZ3RrxR/GbS2bGh8vVsA VJ8WkrfV+tpdbBm0GE7s/4xiw8JW6/ceMaDLJ0keytoSh1uvzW8q8OLroAmOXT019MYvwJ/bREI Ps675ZacH9gPrzS/v2ulTxqxLwY9it/k7SQ+CllRn8uM9Aml6HglZsEp7Rb10HQ2Li+O6ZaMbBt GUHavbP+QpqKy7HnMeEwhv9rhxp6rhIuPkjcAL7ldDaZ+qdJn9Xx3lp1h7LgGlS0gwzYBQPURqZ wnQC9XssPzm2pgcvg== X-Received: by 2002:ac8:7f0a:0:b0:50f:ae60:25ab with SMTP id d75a77b69052e-5162fe3d0d4mr54299461cf.12.1778691011304; Wed, 13 May 2026 09:50:11 -0700 (PDT) Received: from gourry-fedora-PF4VCD3F.lan (pool-100-36-248-188.washdc.fios.verizon.net. [100.36.248.188]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-5148e677e63sm152498891cf.12.2026.05.13.09.50.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 May 2026 09:50:10 -0700 (PDT) From: Gregory Price To: virtualization@lists.linux.dev Cc: linux-kernel@vger.kernel.org, kernel-team@meta.com, mst@redhat.com, david@kernel.org, jasowang@redhat.com, xuanzhuo@linux.alibaba.com, eperezma@redhat.com, hannes@cmpxchg.org, surenb@google.com, peterz@infradead.org, mingo@redhat.com, juri.lelli@redhat.com, vincent.guittot@linaro.org, dietmar.eggemann@arm.com, rostedt@goodmis.org, bsegall@google.com, mgorman@suse.de, vschneid@redhat.com, kprateek.nayak@amd.com Subject: [RFC PATCH 1/2] virtio-balloon: extend stats with memory composition and pressure data Date: Wed, 13 May 2026 12:50:05 -0400 Message-ID: <20260513165006.2790857-2-gourry@gourry.net> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260513165006.2790857-1-gourry@gourry.net> References: <20260513165006.2790857-1-gourry@gourry.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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=3Dm with CONFIG_PSI=3Dy). Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Gregory Price --- 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_balloo= n.c index f6c2dff33f8a..8fa33aec4ce7 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include =20 /* * 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)); =20 + 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; } =20 diff --git a/include/uapi/linux/virtio_balloon.h b/include/uapi/linux/virti= o_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 dire= ctly */ #define VIRTIO_BALLOON_S_ASYNC_RECLAIM 14 /* Amount of memory reclaimed as= ynchronously */ #define VIRTIO_BALLOON_S_DIRECT_RECLAIM 15 /* Amount of memory reclaimed d= irectly */ -#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 (byte= s) */ +#define VIRTIO_BALLOON_S_SLAB_RECLAIM 20 /* Reclaimable slab (bytes) */ +#define VIRTIO_BALLOON_S_WS_REFAULT_A 21 /* Workingset refaults anon (cou= nt) */ +#define VIRTIO_BALLOON_S_WS_REFAULT_F 22 /* Workingset refaults file (cou= nt) */ +#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 =20 #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" \ } =20 #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_grou= p_pcpu); struct psi_group psi_system =3D { .pcpu =3D &system_group_pcpu, }; +EXPORT_SYMBOL_GPL(psi_system); =20 static DEFINE_PER_CPU(seqcount_t, psi_seq) =3D SEQCNT_ZERO(psi_seq); =20 --=20 2.54.0 From nobody Fri Jun 12 17:18:13 2026 Received: from mail-qk1-f173.google.com (mail-qk1-f173.google.com [209.85.222.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D15ED4D990D for ; Wed, 13 May 2026 16:50:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778691030; cv=none; b=bIHntGtXXhaBtFB1nqhTv+1NWGbUl/qEPguu7bpCh39R0EnMWxd4sqvvsGt9k6tjl2uPHD3kkfcbnCifPVPC0XCwXoa42YOJ0Z+9Grz+G1YST/IaW8we0TjqFR+cUff/EtiMn7dAE2vp9a51jX6q6e89xvBDcoBDiJotrtbVmfo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778691030; c=relaxed/simple; bh=K9ud04g2GtJZTP7N5+sbsYRFa3+YAwr9aoxLcR+A5V4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WpUoZz7YYp0oymmPTMCy0vtrBVSXdTcZUZSsAVI8Zb4t2pKz4tiDK1n3IqUj9gVcitzK0tFe7nYPzxHSIy57S1TKz4ge1W9QkDi+xO3sMP3/JS+gZou9n+7wPdw4RqOOutomWuvYUL2kEaVzjlyQ9Dw1G6uzQuz/eKh0Cl7S/lE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net; spf=pass smtp.mailfrom=gourry.net; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b=d+1eZN2x; arc=none smtp.client-ip=209.85.222.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=gourry.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gourry.net Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gourry.net header.i=@gourry.net header.b="d+1eZN2x" Received: by mail-qk1-f173.google.com with SMTP id af79cd13be357-8d65f4073bfso946420985a.3 for ; Wed, 13 May 2026 09:50:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gourry.net; s=google; t=1778691013; x=1779295813; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=rpqzNVPBldqpjotoU+BkXiOr5oMwQ73fbPB9arzcGvQ=; b=d+1eZN2xAFGDnTospIZidJVvnS4dwTsBrhR6vwM0t/tYmfOVZ+upb7cVe2hc9A3qI6 U8Oe+TD0Aqy4qEfATIiVv5cgyFO2TcUgM6uCE2ShlvIy+ycEiBnUemq6hdYO3YpraN1W ZfsYd+7Gf7QgwDkOXVOQUdNqjhzcoQ3YAafwQhJe/47vJ4gAPRG6cmuffZxgOxLa1M47 azWGVbX+F02kPV7hu6ibT5AeH52qDC7FswqXA1HF8ODEqslq9vJY9s8DdhBoUVbeH1zO mxKEuTw1hB+n34Mzkm6Frsq6vYrCFDXmi3hynDInSGZ2wkHIW1YZrMi8SIk5PIB0ii9S B0kA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778691013; x=1779295813; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=rpqzNVPBldqpjotoU+BkXiOr5oMwQ73fbPB9arzcGvQ=; b=jK+UIldhsHiA1uapwtynw/glZUE43OH4tRuLdDXeQ+0ufdX3Yr5Hpy0DnmyOUWBQ8u o73tXWBi9YJZiCaeGzxx0b3Q9GkvbPipS12KJ8iXlGfmybHDnkU8L+v8Z3w25U23Fuq+ rQi2Gos6fdStsaexE4wyyokArbQtdz9o46tWOltdugBraAWkjh4rRKFTPCvaVN6aT7cN U9cGw7SN/RWAlQCu8xfV+YYL8NlLssSdNYOnaQRKdx2CkPqRAbyeahSITnRxfS1I2jaQ QFPywfDsRG9qhCqupIhcNqc7EkEJBdJVenquvHsSmhuWPAmYHvSvX7cEhjdzbyj3bnwO hElA== X-Gm-Message-State: AOJu0YwXrxo/t6Avw/CcAQf82NeftkMH5ZVPgOWwl9ItMpPd1/f2WrGl q1UmBhxaaltuMpxK0hoB6eN4JYapkrLk6WYWRB3qhuWR7KdjguASLpjqKKAI+BKYqdg= X-Gm-Gg: Acq92OEppSA4n/ZhSAk6q3VS7+3iP/GeMd5gqI1D8WQFmtR5T/LqSYRkdF5UxreHr2R +Jqexg8RQ60E+lpmn6x3q51B/kRxO9dUBGlfkPLeGbye4ggOiSzANq+6i61YXpUmcyG0YNDZ4MG pFvZydRaa1NfxM8fgb0f6qOHMiB/1xS7h/Dd9RR+0S9gUbBMB6TW9Vp1xxJrWwrIVTnsSoIRCwX SKTMq2NIJReP3P10a4yPyqr8CNvGFFBjC9hDtZWO+ihtSXOu3d1IFW+qs/bclxd+AiEhmueCQGM sa929uaMpBmInX7fsyV9zRbpygL+e3I0A3P24W1Oovsiq6UdtHp6l0nRs7JZbjo8dQLdBEBJ56k 4UjZ5c8TUYFkGFwFtWCKpTM3e8GgWYvOXhPLnckmREBkzPm2yWQcr6mszuO3uNMJtE0SiSPUhgE rnqRb5XbEUnyeNFa/tRwM1ANz+ZjC78NBZdfsJv9O8Bbs55KXE727+wM4NUS/ofe3kbTxJwrvDt 8laNd0MAWRZ5diVOw== X-Received: by 2002:ac8:5a8c:0:b0:50f:ad91:8912 with SMTP id d75a77b69052e-5162f4aecadmr58343841cf.13.1778691013143; Wed, 13 May 2026 09:50:13 -0700 (PDT) Received: from gourry-fedora-PF4VCD3F.lan (pool-100-36-248-188.washdc.fios.verizon.net. [100.36.248.188]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-5148e677e63sm152498891cf.12.2026.05.13.09.50.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 May 2026 09:50:12 -0700 (PDT) From: Gregory Price To: virtualization@lists.linux.dev Cc: linux-kernel@vger.kernel.org, kernel-team@meta.com, mst@redhat.com, david@kernel.org, jasowang@redhat.com, xuanzhuo@linux.alibaba.com, eperezma@redhat.com, hannes@cmpxchg.org, surenb@google.com, peterz@infradead.org, mingo@redhat.com, juri.lelli@redhat.com, vincent.guittot@linaro.org, dietmar.eggemann@arm.com, rostedt@goodmis.org, bsegall@google.com, mgorman@suse.de, vschneid@redhat.com, kprateek.nayak@amd.com Subject: [RFC PATCH 2/2] virtio-balloon: add stats push mode Date: Wed, 13 May 2026 12:50:06 -0400 Message-ID: <20260513165006.2790857-3-gourry@gourry.net> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260513165006.2790857-1-gourry@gourry.net> References: <20260513165006.2790857-1-gourry@gourry.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" When doing aggressive overcommit of VMs on a single host, a pull model of stat retrieval is problematic if a guest becomes some form of unresponsive. In particular, it's difficult to discern the difference between a hung guest and a slow guest - and why the guest is experiencing that. Add VIRTIO_BALLOON_F_STATS_PUSH feature that allows the host to configure the guest to push stats on a timer instead of the default pull model. The host sets stats_push_interval_ms in the balloon config space: 0 =3D disabled (pull-only, default) N > 0 =3D guest pushes stats every N milliseconds The push mode reuses the existing stats VQ, same buffer format, same tags. The host can change the interval at runtime by updating the config field. Push mode provides two advantages over pull: 1. Guest liveness detection: in pull mode, the host cannot distinguish a slow guest from a hung guest without implementing its own timeout tracking. In push mode, the absence of expected stats buffers is an implicit liveness signal; if the guest fails to push within the expected interval, the host can conclude it is unresponsive. 2. Latency-sensitive consumers (e.g., memory pressure response loops) receive fresh stats at a guaranteed cadence without the host needing to poll. STATS_PUSH requires STATS_VQ; the driver clears STATS_PUSH during feature validation if STATS_VQ is absent. When push mode is active, the pull callback is suppressed to avoid racing on buffer submission. The pull model remains available and is the default. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Gregory Price --- drivers/virtio/virtio_balloon.c | 71 +++++++++++++++++++++++++++++ include/uapi/linux/virtio_balloon.h | 7 +++ 2 files changed, 78 insertions(+) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloo= n.c index 8fa33aec4ce7..47bde1d2b388 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -112,6 +112,10 @@ struct virtio_balloon { /* Memory statistics */ struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR]; =20 + /* Stats push mode */ + struct delayed_work stats_push_work; + uint32_t stats_push_interval_ms; + /* Shrinker to return free pages - VIRTIO_BALLOON_F_FREE_PAGE_HINT */ struct shrinker *shrinker; =20 @@ -463,6 +467,13 @@ static void stats_request(struct virtqueue *vq) { struct virtio_balloon *vb =3D vq->vdev->priv; =20 + /* + * In push mode, the push timer owns the VQ. Ignore pull + * requests to avoid racing on buffer submission. + */ + if (vb->stats_push_interval_ms) + return; + spin_lock(&vb->stop_update_lock); if (!vb->stop_update) { start_wakeup_event(vb, VIRTIO_BALLOON_WAKEUP_SIGNAL_STATS); @@ -558,6 +569,20 @@ static void virtballoon_changed(struct virtio_device *= vdev) virtio_balloon_queue_free_page_work(vb); } spin_unlock_irqrestore(&vb->stop_update_lock, flags); + + if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_STATS_PUSH)) { + uint32_t interval; + + virtio_cread_le(vdev, struct virtio_balloon_config, + stats_push_interval_ms, &interval); + if (interval !=3D vb->stats_push_interval_ms) { + vb->stats_push_interval_ms =3D interval; + cancel_delayed_work(&vb->stats_push_work); + if (interval) + schedule_delayed_work(&vb->stats_push_work, + msecs_to_jiffies(interval)); + } + } } =20 static void update_balloon_size(struct virtio_balloon *vb) @@ -581,6 +606,32 @@ static void update_balloon_stats_func(struct work_stru= ct *work) finish_wakeup_event(vb); } =20 +static void stats_push_func(struct work_struct *work) +{ + struct virtio_balloon *vb =3D container_of(work, struct virtio_balloon, + stats_push_work.work); + struct virtqueue *vq; + struct scatterlist sg; + unsigned int num_stats, len; + + if (!vb->stats_push_interval_ms) + return; + + vq =3D vb->stats_vq; + + /* Reclaim previous buffer */ + while (virtqueue_get_buf(vq, &len)) + ; + + num_stats =3D update_balloon_stats(vb); + sg_init_one(&sg, vb->stats, sizeof(vb->stats[0]) * num_stats); + virtqueue_add_outbuf(vq, &sg, 1, vb, GFP_KERNEL); + virtqueue_kick(vq); + + schedule_delayed_work(&vb->stats_push_work, + msecs_to_jiffies(vb->stats_push_interval_ms)); +} + static void update_balloon_size_func(struct work_struct *work) { struct virtio_balloon *vb; @@ -967,6 +1018,7 @@ static int virtballoon_probe(struct virtio_device *vde= v) } =20 INIT_WORK(&vb->update_balloon_stats_work, update_balloon_stats_func); + INIT_DELAYED_WORK(&vb->stats_push_work, stats_push_func); INIT_WORK(&vb->update_balloon_size_work, update_balloon_size_func); spin_lock_init(&vb->stop_update_lock); mutex_init(&vb->balloon_lock); @@ -1094,6 +1146,19 @@ static int virtballoon_probe(struct virtio_device *v= dev) =20 if (towards_target(vb)) virtballoon_changed(vdev); + + if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_STATS_PUSH)) { + uint32_t interval; + + virtio_cread_le(vdev, struct virtio_balloon_config, + stats_push_interval_ms, &interval); + if (interval) { + vb->stats_push_interval_ms =3D interval; + schedule_delayed_work(&vb->stats_push_work, + msecs_to_jiffies(interval)); + } + } + return 0; =20 out_unregister_oom: @@ -1145,6 +1210,7 @@ static void virtballoon_remove(struct virtio_device *= vdev) spin_unlock_irq(&vb->stop_update_lock); cancel_work_sync(&vb->update_balloon_size_work); cancel_work_sync(&vb->update_balloon_stats_work); + cancel_delayed_work_sync(&vb->stats_push_work); =20 if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT)) { cancel_work_sync(&vb->report_free_page_work); @@ -1199,6 +1265,10 @@ static int virtballoon_validate(struct virtio_device= *vdev) else if (!virtio_has_feature(vdev, VIRTIO_BALLOON_F_PAGE_POISON)) __virtio_clear_bit(vdev, VIRTIO_BALLOON_F_REPORTING); =20 + if (virtio_has_feature(vdev, VIRTIO_BALLOON_F_STATS_PUSH) && + !virtio_has_feature(vdev, VIRTIO_BALLOON_F_STATS_VQ)) + __virtio_clear_bit(vdev, VIRTIO_BALLOON_F_STATS_PUSH); + __virtio_clear_bit(vdev, VIRTIO_F_ACCESS_PLATFORM); return 0; } @@ -1210,6 +1280,7 @@ static unsigned int features[] =3D { VIRTIO_BALLOON_F_FREE_PAGE_HINT, VIRTIO_BALLOON_F_PAGE_POISON, VIRTIO_BALLOON_F_REPORTING, + VIRTIO_BALLOON_F_STATS_PUSH, }; =20 static struct virtio_driver virtio_balloon_driver =3D { diff --git a/include/uapi/linux/virtio_balloon.h b/include/uapi/linux/virti= o_balloon.h index 37ec8a8466c4..90e9b5247e5e 100644 --- a/include/uapi/linux/virtio_balloon.h +++ b/include/uapi/linux/virtio_balloon.h @@ -37,6 +37,7 @@ #define VIRTIO_BALLOON_F_FREE_PAGE_HINT 3 /* VQ to report free pages */ #define VIRTIO_BALLOON_F_PAGE_POISON 4 /* Guest is using page poisoning */ #define VIRTIO_BALLOON_F_REPORTING 5 /* Page reporting virtqueue */ +#define VIRTIO_BALLOON_F_STATS_PUSH 6 /* Guest pushes stats on a timer */ =20 /* Size of a PFN in the balloon interface. */ #define VIRTIO_BALLOON_PFN_SHIFT 12 @@ -59,6 +60,12 @@ struct virtio_balloon_config { }; /* Stores PAGE_POISON if page poisoning is in use */ __le32 poison_val; + /* + * Stats push interval in milliseconds. 0 =3D disabled (pull only). + * Valid with VIRTIO_BALLOON_F_STATS_PUSH. Host-writable, can change + * at runtime via config updates. + */ + __le32 stats_push_interval_ms; }; =20 #define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ --=20 2.54.0