* [PATCH v4 1/2] util/coroutine: trim sysctl's max_map_count newline before parsing
2026-10-14 21:55 [PATCH v4 0/2] util/coroutine: Reduce default idle pool max size Wesley Hershberger
@ 2026-10-14 21:55 ` Wesley Hershberger
2026-10-14 21:55 ` [PATCH v4 2/2] util/coroutine: Reduce default idle pool max size Wesley Hershberger
1 sibling, 0 replies; 4+ messages in thread
From: Wesley Hershberger @ 2026-10-14 21:55 UTC (permalink / raw)
To: qemu-devel
Cc: Stefan Hajnoczi, Kevin Wolf, Hector Cao, Alex Ramírez,
Wesley Hershberger, Daniel P. Berrangé
From: Hector Cao <hector.cao@canonical.com>
Strip trailing whitespace from max_map_count before passing it to
qemu_strtoi(), allowing the strict full-string parse to succeed.
Otherwise, qemu_strtoi() fails to parse the string and returns -22,
making the global cache pool cap size set to UINT_MAX.
Fixes: 86a637e48104 ("coroutine: cap per-thread local pool size")
Signed-off-by: Hector Cao <hector.cao@canonical.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Wesley Hershberger <wesley.hershberger@canonical.com>
---
util/qemu-coroutine.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
index d17135f585a..5af61995437 100644
--- a/util/qemu-coroutine.c
+++ b/util/qemu-coroutine.c
@@ -374,8 +374,12 @@ static unsigned int get_global_pool_hard_max_size(void)
* VMAs.
*/
if (g_file_get_contents("/proc/sys/vm/max_map_count", &contents, NULL,
- NULL) &&
- qemu_strtoi(contents, NULL, 10, &max_map_count) == 0) {
+ NULL)) {
+ /* contents has trailing newline, remove it before conversion */
+ g_strchomp(contents);
+ if (qemu_strtoi(contents, NULL, 10, &max_map_count) != 0) {
+ return UINT_MAX;
+ }
/*
* This is an upper bound that avoids exceeding max_map_count. Leave a
* fixed amount for non-coroutine users like library dependencies,
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH v4 2/2] util/coroutine: Reduce default idle pool max size
2026-10-14 21:55 [PATCH v4 0/2] util/coroutine: Reduce default idle pool max size Wesley Hershberger
2026-10-14 21:55 ` [PATCH v4 1/2] util/coroutine: trim sysctl's max_map_count newline before parsing Wesley Hershberger
@ 2026-10-14 21:55 ` Wesley Hershberger
2026-10-15 7:46 ` Daniel P. Berrangé
1 sibling, 1 reply; 4+ messages in thread
From: Wesley Hershberger @ 2026-10-14 21:55 UTC (permalink / raw)
To: qemu-devel
Cc: Stefan Hajnoczi, Kevin Wolf, Hector Cao, Alex Ramírez,
Wesley Hershberger, Daniel P. Berrangé
If g_file_get_contents fails (AppArmor, etc), use a more conservative
default max size for the idle coroutine pool.
This avoids any possibility of the failure described in 86a637e48104
unless the host kernel is configured with a lower-than-default
vm.max_map_count.
This change is motivated by a discussion of backport risk:
https://bugs.launchpad.net/ubuntu/+source/qemu/+bug/2168803/comments/8
Fixes: 86a637e48104 ("coroutine: cap per-thread local pool size")
Suggested-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Wesley Hershberger <wesley.hershberger@canonical.com>
---
util/qemu-coroutine.c | 73 ++++++++++++++++++++++++++++++++-------------------
1 file changed, 46 insertions(+), 27 deletions(-)
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
index 5af61995437..b3312f7bf99 100644
--- a/util/qemu-coroutine.c
+++ b/util/qemu-coroutine.c
@@ -361,42 +361,61 @@ void qemu_coroutine_dec_pool_size(unsigned int removing_pool_size)
global_pool_max_size -= removing_pool_size;
}
-static unsigned int get_global_pool_hard_max_size(void)
-{
#ifdef __linux__
- g_autofree char *contents = NULL;
- int max_map_count;
+/*
+ * include/linux/mm.h
+ */
+#define DEFAULT_MAX_MAP_COUNT 65530
- /*
- * Linux processes can have up to max_map_count virtual memory areas
- * (VMAs). mmap(2), mprotect(2), etc fail with ENOMEM beyond this limit. We
- * must limit the coroutine pool to a safe size to avoid running out of
- * VMAs.
- */
- if (g_file_get_contents("/proc/sys/vm/max_map_count", &contents, NULL,
- NULL)) {
+static unsigned int get_max_map_count(void)
+{
+ g_autofree char *contents = NULL;
+ int max_map_count = 0;
+ if (g_file_get_contents("/proc/sys/vm/max_map_count", &contents,
+ NULL, NULL)) {
/* contents has trailing newline, remove it before conversion */
g_strchomp(contents);
- if (qemu_strtoi(contents, NULL, 10, &max_map_count) != 0) {
- return UINT_MAX;
- }
- /*
- * This is an upper bound that avoids exceeding max_map_count. Leave a
- * fixed amount for non-coroutine users like library dependencies,
- * vhost-user, etc. Each coroutine takes up 2 VMAs so halve the
- * remaining amount.
- */
- if (max_map_count > 5000) {
- return (max_map_count - 5000) / 2;
- } else {
- /* Disable the global pool but threads still have local pools */
- return 0;
+ if (qemu_strtoi(contents, NULL, 10, &max_map_count) == 0) {
+ return max_map_count;
}
}
-#endif
+ /*
+ * Read from /proc might fail due to mandatory access control (AppArmor,
+ * SELinux, etc). Use the kernel's default in that case.
+ */
+ return DEFAULT_MAX_MAP_COUNT;
+}
+
+/*
+ * Linux processes can have up to max_map_count virtual memory areas
+ * (VMAs). mmap(2), mprotect(2), etc fail with ENOMEM beyond this limit. We
+ * must limit the coroutine pool to a safe size to avoid running out of
+ * VMAs.
+ */
+static unsigned int get_global_pool_hard_max_size(void)
+{
+ int max_map_count = get_max_map_count();
+
+ /*
+ * This is an upper bound that avoids exceeding max_map_count. Leave a
+ * fixed amount for non-coroutine users like library dependencies,
+ * vhost-user, etc. Each coroutine takes up 2 VMAs so halve the
+ * remaining amount.
+ */
+ if (max_map_count > 5000) {
+ return (max_map_count - 5000) / 2;
+ } else {
+ /* Disable the global pool but threads still have local pools */
+ return 0;
+ }
+}
+#else
+static unsigned int get_global_pool_hard_max_size(void)
+{
return UINT_MAX;
}
+#endif
static void __attribute__((constructor)) qemu_coroutine_init(void)
{
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread