summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNatanael Copa <ncopa@alpinelinux.org>2024-07-24 17:50:36 +0200
committerNatanael Copa <ncopa@alpinelinux.org>2024-07-24 17:56:48 +0200
commit6c9c9e6e262026b3d257046ff23442a312ad2749 (patch)
tree26dbff8e3617d618854ba0257e8b1f81f209c8e7
parentf8f6855eb73433486adf5d60d4f469cf2329aceb (diff)
downloadaports-6c9c9e6e262026b3d257046ff23442a312ad2749.zip
main/lxc: fix lxc-top with cgroups2
ref: https://gitlab.alpinelinux.org/alpine/aports/-/issues/15607
-rw-r--r--main/lxc/APKBUILD4
-rw-r--r--main/lxc/lxc-top-cgroupv2.patch620
2 files changed, 535 insertions, 89 deletions
diff --git a/main/lxc/APKBUILD b/main/lxc/APKBUILD
index d2bf9d078ac..0c495f3f2ff 100644
--- a/main/lxc/APKBUILD
+++ b/main/lxc/APKBUILD
@@ -4,7 +4,7 @@
pkgname=lxc
pkgver=6.0.1
_pkgver=${pkgver/_rc/.rc}
-pkgrel=4
+pkgrel=5
pkgdesc="Userspace interface for the Linux kernel containment features"
url="https://linuxcontainers.org/lxc/"
arch="all"
@@ -176,7 +176,7 @@ _user_nic() {
sha512sums="
7e8b9740178ae46a2ad3ba3f1e509f69f978d0a2a1f7e1938213ae9cb9a80f496d01be64958cad4aa6f01a73f1d8c3759c3e9df9df4f67c77e603ea0809d79c2 lxc-6.0.1.tar.gz
-e7b7a443a71804f21e6225bed98310ccf2cad1a5bc2b9ad397e7d79fcfab6216e10ce02e249fbc0e6df2cfbe13e03320d9690db502027ae008a6f48218b81d17 lxc-top-cgroupv2.patch
+3619a16604683d5cfa4b9d997cb8435232c016b71d0d7be2a0553d8136fa1914f916ef89d7092d3f51ff2e56714c918fef4e4e42bab89054fc92aec617228bec lxc-top-cgroupv2.patch
db71783366277a68a5c8116604cf845da4780fe4aebdb5820ae2c4fe028cfe52a9c94246db362476f2f195be6a9c2b835edbe521423f116fc66eb50023d6daab lxc.initd
91de43db5369a9e10102933514d674e9c875218a1ff2910dd882e5b9c308f9e430deacb13d1d7e0b2ed1ef682d0bb035aa6f8a6738f54fa2ca3a05acce04e467 lxc.confd
"
diff --git a/main/lxc/lxc-top-cgroupv2.patch b/main/lxc/lxc-top-cgroupv2.patch
index a88b8546bd0..5e6098aa9c1 100644
--- a/main/lxc/lxc-top-cgroupv2.patch
+++ b/main/lxc/lxc-top-cgroupv2.patch
@@ -1,118 +1,564 @@
-From ba823cc3cd2ea8b7eef714c317a212a9d7b5afe0 Mon Sep 17 00:00:00 2001
-From: Anoop Rachakonda <anooprac@utexas.edu>
-Date: Wed, 1 May 2024 13:56:30 -0500
-Subject: [PATCH] stats_get: Changed paths to be aligned with cgroup2
- specifications
+From 40857b9de3714b3314f5c22e924d5993c458acbe Mon Sep 17 00:00:00 2001
+From: Natanael Copa <ncopa@alpinelinux.org>
+Date: Wed, 24 Jul 2024 15:23:11 +0200
+Subject: [PATCH 1/5] tools: lxc-top: refactor lxc-top stat structs
-Closes #4376
+Create separate structs for each controller class. This will make it
+easier to add cgroupv2 support.
-Signed-off-by: Devon Schwartz <devon.s.schwartz@utexas.edu>
+No functional changes.
+
+Signed-off-by: Natanael Copa <ncopa@alpinelinux.org>
+---
+ src/lxc/tools/lxc_top.c | 125 +++++++++++++++++++++++-----------------
+ 1 file changed, 72 insertions(+), 53 deletions(-)
+
+diff --git a/src/lxc/tools/lxc_top.c b/src/lxc/tools/lxc_top.c
+index aa6e7209e..bcc695b3e 100644
+--- a/src/lxc/tools/lxc_top.c
++++ b/src/lxc/tools/lxc_top.c
+@@ -35,16 +35,24 @@ struct blkio_stats {
+ uint64_t total;
+ };
+
+-struct stats {
+- uint64_t mem_used;
+- uint64_t mem_limit;
+- uint64_t memsw_used;
+- uint64_t memsw_limit;
++struct cpu_stats {
++ uint64_t use_nanos;
++ uint64_t use_user;
++ uint64_t use_sys;
++};
++
++struct mem_stats {
++ uint64_t used;
++ uint64_t limit;
++ uint64_t swap_used;
++ uint64_t swap_limit;
+ uint64_t kmem_used;
+ uint64_t kmem_limit;
+- uint64_t cpu_use_nanos;
+- uint64_t cpu_use_user;
+- uint64_t cpu_use_sys;
++};
++
++struct stats {
++ struct mem_stats mem;
++ struct cpu_stats cpu;
+ struct blkio_stats io_service_bytes;
+ struct blkio_stats io_serviced;
+ };
+@@ -314,34 +322,45 @@ out:
+ return;
+ }
+
++static void cg1_mem_stats(struct lxc_container *c, struct mem_stats *mem)
++{
++ mem->used = stat_get_int(c, "memory.usage_in_bytes");
++ mem->limit = stat_get_int(c, "memory.limit_in_bytes");
++ mem->swap_used = stat_get_int(c, "memory.memsw.usage_in_bytes");
++ mem->swap_limit = stat_get_int(c, "memory.memsw.limit_in_bytes");
++ mem->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes");
++ mem->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes");
++}
++
++static void cg1_cpu_stats(struct lxc_container *c, struct cpu_stats *cpu)
++{
++ cpu->use_nanos = stat_get_int(c, "cpuacct.usage");
++ cpu->use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1);
++ cpu->use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1);
++}
++
+ static void stats_get(struct lxc_container *c, struct container_stats *ct, struct stats *total)
+ {
+ ct->c = c;
+- ct->stats->mem_used = stat_get_int(c, "memory.usage_in_bytes");
+- ct->stats->mem_limit = stat_get_int(c, "memory.limit_in_bytes");
+- ct->stats->memsw_used = stat_get_int(c, "memory.memsw.usage_in_bytes");
+- ct->stats->memsw_limit = stat_get_int(c, "memory.memsw.limit_in_bytes");
+- ct->stats->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes");
+- ct->stats->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes");
+- ct->stats->cpu_use_nanos = stat_get_int(c, "cpuacct.usage");
+- ct->stats->cpu_use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1);
+- ct->stats->cpu_use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1);
+-
++ cg1_mem_stats(c, &ct->stats->mem);
++ cg1_cpu_stats(c, &ct->stats->cpu);
+ stat_get_blk_stats(c, "blkio.throttle.io_service_bytes", &ct->stats->io_service_bytes);
+ stat_get_blk_stats(c, "blkio.throttle.io_serviced", &ct->stats->io_serviced);
+
+ if (total) {
+- total->mem_used = total->mem_used + ct->stats->mem_used;
+- total->mem_limit = total->mem_limit + ct->stats->mem_limit;
+- total->memsw_used = total->memsw_used + ct->stats->memsw_used;
+- total->memsw_limit = total->memsw_limit + ct->stats->memsw_limit;
+- total->kmem_used = total->kmem_used + ct->stats->kmem_used;
+- total->kmem_limit = total->kmem_limit + ct->stats->kmem_limit;
+- total->cpu_use_nanos = total->cpu_use_nanos + ct->stats->cpu_use_nanos;
+- total->cpu_use_user = total->cpu_use_user + ct->stats->cpu_use_user;
+- total->cpu_use_sys = total->cpu_use_sys + ct->stats->cpu_use_sys;
++ total->mem.used += ct->stats->mem.used;
++ total->mem.limit += ct->stats->mem.limit;
++ total->mem.swap_used += ct->stats->mem.swap_used;
++ total->mem.swap_limit += ct->stats->mem.swap_limit;
++ total->mem.kmem_used += ct->stats->mem.kmem_used;
++ total->mem.kmem_limit += ct->stats->mem.kmem_limit;
++
++ total->cpu.use_nanos += ct->stats->cpu.use_nanos;
++ total->cpu.use_user += ct->stats->cpu.use_user;
++ total->cpu.use_sys += ct->stats->cpu.use_sys;
++
+ total->io_service_bytes.total += ct->stats->io_service_bytes.total;
+- total->io_service_bytes.read += ct->stats->io_service_bytes.read;
++ total->io_service_bytes.read += ct->stats->io_service_bytes.read;
+ total->io_service_bytes.write += ct->stats->io_service_bytes.write;
+ }
+ }
+@@ -351,19 +370,19 @@ static void stats_print_header(struct stats *stats)
+ printf(TERMRVRS TERMBOLD);
+ printf("%-18s %12s %12s %12s %36s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem");
+
+- if (stats->memsw_used > 0)
++ if (stats->mem.swap_used > 0)
+ printf(" %10s", "MemSw");
+
+- if (stats->kmem_used > 0)
++ if (stats->mem.kmem_used > 0)
+ printf(" %10s", "KMem");
+ printf("\n");
+
+ printf("%-18s %12s %12s %12s %36s %10s", "Name", "Used", "Sys", "User", "Total(Read/Write)", "Used");
+
+- if (stats->memsw_used > 0)
++ if (stats->mem.swap_used > 0)
+ printf(" %10s", "Used");
+
+- if (stats->kmem_used > 0)
++ if (stats->mem.kmem_used > 0)
+ printf(" %10s", "Used");
+
+ printf("\n");
+@@ -388,7 +407,7 @@ static void stats_print(const char *name, const struct stats *stats,
+ size_humanize(stats->io_service_bytes.total, iosb_total_str, sizeof(iosb_total_str));
+ size_humanize(stats->io_service_bytes.read, iosb_read_str, sizeof(iosb_read_str));
+ size_humanize(stats->io_service_bytes.write, iosb_write_str, sizeof(iosb_write_str));
+- size_humanize(stats->mem_used, mem_used_str, sizeof(mem_used_str));
++ size_humanize(stats->mem.used, mem_used_str, sizeof(mem_used_str));
+
+ ret = snprintf(iosb_str, sizeof(iosb_str), "%s(%s/%s)", iosb_total_str, iosb_read_str, iosb_write_str);
+ if (ret < 0 || (size_t)ret >= sizeof(iosb_str))
+@@ -396,18 +415,18 @@ static void stats_print(const char *name, const struct stats *stats,
+
+ printf("%-18.18s %12.2f %12.2f %12.2f %36s %10s",
+ name,
+- (float)stats->cpu_use_nanos / 1000000000,
+- (float)stats->cpu_use_sys / USER_HZ,
+- (float)stats->cpu_use_user / USER_HZ,
++ (float)stats->cpu.use_nanos / 1000000000,
++ (float)stats->cpu.use_sys / USER_HZ,
++ (float)stats->cpu.use_user / USER_HZ,
+ iosb_str,
+ mem_used_str);
+
+- if (total->memsw_used > 0) {
+- size_humanize(stats->memsw_used, memsw_used_str, sizeof(memsw_used_str));
++ if (total->mem.swap_used > 0) {
++ size_humanize(stats->mem.swap_used, memsw_used_str, sizeof(memsw_used_str));
+ printf(" %10s", memsw_used_str);
+ }
+- if (total->kmem_used > 0) {
+- size_humanize(stats->kmem_used, kmem_used_str, sizeof(kmem_used_str));
++ if (total->mem.kmem_used > 0) {
++ size_humanize(stats->mem.kmem_used, kmem_used_str, sizeof(kmem_used_str));
+ printf(" %10s", kmem_used_str);
+ }
+ } else {
+@@ -415,11 +434,11 @@ static void stats_print(const char *name, const struct stats *stats,
+ time_ms = (unsigned long long) (time_val.tv_sec) * 1000 + (unsigned long long) (time_val.tv_usec) / 1000;
+ printf("%" PRIu64 ",%s,%" PRIu64 ",%" PRIu64 ",%" PRIu64
+ ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64,
+- (uint64_t)time_ms, name, (uint64_t)stats->cpu_use_nanos,
+- (uint64_t)stats->cpu_use_sys,
+- (uint64_t)stats->cpu_use_user, (uint64_t)stats->io_service_bytes.total,
+- (uint64_t)stats->io_serviced.total, (uint64_t)stats->mem_used,
+- (uint64_t)stats->memsw_used, (uint64_t)stats->kmem_used);
++ (uint64_t)time_ms, name, (uint64_t)stats->cpu.use_nanos,
++ (uint64_t)stats->cpu.use_sys,
++ (uint64_t)stats->cpu.use_user, (uint64_t)stats->io_service_bytes.total,
++ (uint64_t)stats->io_serviced.total, (uint64_t)stats->mem.used,
++ (uint64_t)stats->mem.swap_used, (uint64_t)stats->mem.kmem_used);
+ }
+
+ }
+@@ -441,9 +460,9 @@ static int cmp_cpuuse(const void *sct1, const void *sct2)
+ const struct container_stats *ct2 = sct2;
+
+ if (sort_reverse)
+- return ct2->stats->cpu_use_nanos < ct1->stats->cpu_use_nanos;
++ return ct2->stats->cpu.use_nanos < ct1->stats->cpu.use_nanos;
+
+- return ct1->stats->cpu_use_nanos < ct2->stats->cpu_use_nanos;
++ return ct1->stats->cpu.use_nanos < ct2->stats->cpu.use_nanos;
+ }
+
+ static int cmp_blkio(const void *sct1, const void *sct2)
+@@ -463,9 +482,9 @@ static int cmp_memory(const void *sct1, const void *sct2)
+ const struct container_stats *ct2 = sct2;
+
+ if (sort_reverse)
+- return ct2->stats->mem_used < ct1->stats->mem_used;
++ return ct2->stats->mem.used < ct1->stats->mem.used;
+
+- return ct1->stats->mem_used < ct2->stats->mem_used;
++ return ct1->stats->mem.used < ct2->stats->mem.used;
+ }
+
+ static int cmp_memorysw(const void *sct1, const void *sct2)
+@@ -474,9 +493,9 @@ static int cmp_memorysw(const void *sct1, const void *sct2)
+ const struct container_stats *ct2 = sct2;
+
+ if (sort_reverse)
+- return ct2->stats->memsw_used < ct1->stats->memsw_used;
++ return ct2->stats->mem.swap_used < ct1->stats->mem.swap_used;
+
+- return ct1->stats->memsw_used < ct2->stats->memsw_used;
++ return ct1->stats->mem.swap_used < ct2->stats->mem.swap_used;
+ }
+
+ static int cmp_kmemory(const void *sct1, const void *sct2)
+@@ -485,9 +504,9 @@ static int cmp_kmemory(const void *sct1, const void *sct2)
+ const struct container_stats *ct2 = sct2;
+
+ if (sort_reverse)
+- return ct2->stats->kmem_used < ct1->stats->kmem_used;
++ return ct2->stats->mem.kmem_used < ct1->stats->mem.kmem_used;
+
+- return ct1->stats->kmem_used < ct2->stats->kmem_used;
++ return ct1->stats->mem.kmem_used < ct2->stats->mem.kmem_used;
+ }
+
+ static void ct_sort(int active)
+--
+2.45.2
+
+
+From ec11c2dad1ccf5fd6dd9a57e2812c4551f8a710f Mon Sep 17 00:00:00 2001
+From: Natanael Copa <ncopa@alpinelinux.org>
+Date: Wed, 24 Jul 2024 15:47:55 +0200
+Subject: [PATCH 2/5] tools: lxc-top: get memory stats from cgroups2
+
+Signed-off-by: Natanael Copa <ncopa@alpinelinux.org>
---
- src/lxc/tools/lxc_top.c | 64 ++++++++++++++++++++++++++++++++---------
- 1 file changed, 51 insertions(+), 13 deletions(-)
+ src/lxc/tools/lxc_top.c | 23 ++++++++++++++++++++---
+ 1 file changed, 20 insertions(+), 3 deletions(-)
diff --git a/src/lxc/tools/lxc_top.c b/src/lxc/tools/lxc_top.c
-index aa6e7209e3..f27025f730 100644
+index bcc695b3e..29d81563f 100644
--- a/src/lxc/tools/lxc_top.c
+++ b/src/lxc/tools/lxc_top.c
-@@ -276,27 +276,33 @@ static uint64_t stat_match_get_int(struct lxc_container *c, const char *item,
+@@ -212,7 +212,6 @@ static uint64_t stat_get_int(struct lxc_container *c, const char *item)
+
+ len = c->get_cgroup_item(c, item, buf, sizeof(buf));
+ if (len <= 0) {
+- fprintf(stderr, "Unable to read cgroup item %s\n", item);
+ return 0;
+ }
+
+@@ -322,7 +321,7 @@ out:
+ return;
+ }
+
+-static void cg1_mem_stats(struct lxc_container *c, struct mem_stats *mem)
++static int cg1_mem_stats(struct lxc_container *c, struct mem_stats *mem)
+ {
+ mem->used = stat_get_int(c, "memory.usage_in_bytes");
+ mem->limit = stat_get_int(c, "memory.limit_in_bytes");
+@@ -330,6 +329,20 @@ static void cg1_mem_stats(struct lxc_container *c, struct mem_stats *mem)
+ mem->swap_limit = stat_get_int(c, "memory.memsw.limit_in_bytes");
+ mem->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes");
+ mem->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes");
++ return mem->used > 0 ? 0 : -1;
++}
++
++static int cg2_mem_stats(struct lxc_container *c, struct mem_stats *mem)
++{
++ mem->used = stat_get_int(c, "memory.current");
++ mem->limit = stat_get_int(c, "memory.max");
++ mem->swap_used = stat_get_int(c, "memory.swap.current");
++ mem->swap_limit = stat_get_int(c, "memory.swap.max");
++ /* TODO: find the kernel usage */
++ mem->kmem_used = 0;
++ /* does not exist in cgroup v2 */
++ mem->kmem_limit = 0;
++ return mem->used > 0 ? 0 : -1;
+ }
+
+ static void cg1_cpu_stats(struct lxc_container *c, struct cpu_stats *cpu)
+@@ -342,7 +355,11 @@ static void cg1_cpu_stats(struct lxc_container *c, struct cpu_stats *cpu)
+ static void stats_get(struct lxc_container *c, struct container_stats *ct, struct stats *total)
+ {
+ ct->c = c;
+- cg1_mem_stats(c, &ct->stats->mem);
++ if (cg1_mem_stats(c, &ct->stats->mem) < 0) {
++ if (cg2_mem_stats(c, &ct->stats->mem) < 0) {
++ fprintf(stderr, "Unable to read memory stats\n");
++ }
++ }
+ cg1_cpu_stats(c, &ct->stats->cpu);
+ stat_get_blk_stats(c, "blkio.throttle.io_service_bytes", &ct->stats->io_service_bytes);
+ stat_get_blk_stats(c, "blkio.throttle.io_serviced", &ct->stats->io_serviced);
+--
+2.45.2
+
+
+From df219e84ca11294ba29ff9e85d09f4190942b30c Mon Sep 17 00:00:00 2001
+From: Natanael Copa <ncopa@alpinelinux.org>
+Date: Wed, 24 Jul 2024 16:32:30 +0200
+Subject: [PATCH 3/5] lxc-top: CPU stats for cgroups2
+
+Recalculate the usec to nanoseconds and USER_HZ
+
+Signed-off-by: Natanael Copa <ncopa@alpinelinux.org>
+---
+ src/lxc/tools/lxc_top.c | 21 ++++++++++++++++++---
+ 1 file changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/src/lxc/tools/lxc_top.c b/src/lxc/tools/lxc_top.c
+index 29d81563f..3a6c49e82 100644
+--- a/src/lxc/tools/lxc_top.c
++++ b/src/lxc/tools/lxc_top.c
+@@ -230,7 +230,6 @@ static uint64_t stat_match_get_int(struct lxc_container *c, const char *item,
+
+ len = c->get_cgroup_item(c, item, buf, sizeof(buf));
+ if (len <= 0) {
+- fprintf(stderr, "Unable to read cgroup item %s\n", item);
+ goto out;
+ }
+
+@@ -345,11 +344,22 @@ static int cg2_mem_stats(struct lxc_container *c, struct mem_stats *mem)
+ return mem->used > 0 ? 0 : -1;
+ }
+
+-static void cg1_cpu_stats(struct lxc_container *c, struct cpu_stats *cpu)
++static int cg1_cpu_stats(struct lxc_container *c, struct cpu_stats *cpu)
+ {
+ cpu->use_nanos = stat_get_int(c, "cpuacct.usage");
+ cpu->use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1);
+ cpu->use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1);
++ return cpu->use_nanos > 0 ? 0 : -1;
++}
++
++static int cg2_cpu_stats(struct lxc_container *c, struct cpu_stats *cpu)
++{
++ /* convert microseconds to nanoseconds */
++ cpu->use_nanos = stat_match_get_int(c, "cpu.stat", "usage_usec", 1) * 1000;
++
++ cpu->use_user = stat_match_get_int(c, "cpu.stat", "user_usec", 1) * USER_HZ / 1000000;
++ cpu->use_sys = stat_match_get_int(c, "cpu.stat", "system_usec", 1) * USER_HZ / 1000000;
++ return cpu->use_nanos > 0 ? 0 : -1;
+ }
+
+ static void stats_get(struct lxc_container *c, struct container_stats *ct, struct stats *total)
+@@ -360,7 +370,12 @@ static void stats_get(struct lxc_container *c, struct container_stats *ct, struc
+ fprintf(stderr, "Unable to read memory stats\n");
+ }
+ }
+- cg1_cpu_stats(c, &ct->stats->cpu);
++ if (cg1_cpu_stats(c, &ct->stats->cpu) < 0) {
++ if (cg2_cpu_stats(c, &ct->stats->cpu) < 0) {
++ fprintf(stderr, "Unable to read CPU stats\n");
++ }
++ }
++
+ stat_get_blk_stats(c, "blkio.throttle.io_service_bytes", &ct->stats->io_service_bytes);
+ stat_get_blk_stats(c, "blkio.throttle.io_serviced", &ct->stats->io_serviced);
+
+--
+2.45.2
+
+
+From 094f05141db5968f8e3a855e4230579b12721a65 Mon Sep 17 00:00:00 2001
+From: Natanael Copa <ncopa@alpinelinux.org>
+Date: Wed, 24 Jul 2024 16:54:00 +0200
+Subject: [PATCH 4/5] tools: lxc-top: get the user HZ at runtime
+
+The USER_HZ depends on the kernel configuration. Get it run-time instead
+of assume it is 100 HZ.
+
+Signed-off-by: Natanael Copa <ncopa@alpinelinux.org>
+---
+ src/lxc/tools/lxc_top.c | 15 ++++++++++-----
+ 1 file changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/src/lxc/tools/lxc_top.c b/src/lxc/tools/lxc_top.c
+index 3a6c49e82..deea1b41d 100644
+--- a/src/lxc/tools/lxc_top.c
++++ b/src/lxc/tools/lxc_top.c
+@@ -22,7 +22,6 @@
+ #include "mainloop.h"
+ #include "utils.h"
+
+-#define USER_HZ 100
+ #define ESC "\033"
+ #define TERMCLEAR ESC "[H" ESC "[J"
+ #define TERMNORM ESC "[0m"
+@@ -70,6 +69,7 @@ static int sort_reverse = 0;
+ static struct termios oldtios;
+ static struct container_stats *container_stats = NULL;
+ static int ct_alloc_cnt = 0;
++static long user_hz = 0;
+
+ static int my_parser(struct lxc_arguments *args, int c, char *arg)
+ {
+@@ -357,8 +357,8 @@ static int cg2_cpu_stats(struct lxc_container *c, struct cpu_stats *cpu)
+ /* convert microseconds to nanoseconds */
+ cpu->use_nanos = stat_match_get_int(c, "cpu.stat", "usage_usec", 1) * 1000;
+
+- cpu->use_user = stat_match_get_int(c, "cpu.stat", "user_usec", 1) * USER_HZ / 1000000;
+- cpu->use_sys = stat_match_get_int(c, "cpu.stat", "system_usec", 1) * USER_HZ / 1000000;
++ cpu->use_user = stat_match_get_int(c, "cpu.stat", "user_usec", 1) * user_hz / 1000000;
++ cpu->use_sys = stat_match_get_int(c, "cpu.stat", "system_usec", 1) * user_hz / 1000000;
+ return cpu->use_nanos > 0 ? 0 : -1;
+ }
+
+@@ -448,8 +448,8 @@ static void stats_print(const char *name, const struct stats *stats,
+ printf("%-18.18s %12.2f %12.2f %12.2f %36s %10s",
+ name,
+ (float)stats->cpu.use_nanos / 1000000000,
+- (float)stats->cpu.use_sys / USER_HZ,
+- (float)stats->cpu.use_user / USER_HZ,
++ (float)stats->cpu.use_sys / user_hz,
++ (float)stats->cpu.use_user / user_hz,
+ iosb_str,
+ mem_used_str);
+
+@@ -640,6 +640,11 @@ int lxc_top_main(int argc, char *argv[])
+ signal(SIGINT, sig_handler);
+ signal(SIGQUIT, sig_handler);
+
++ user_hz = sysconf(_SC_CLK_TCK);
++ if (user_hz == 0) {
++ user_hz = 100;
++ }
++
+ if (lxc_mainloop_open(&descr)) {
+ fprintf(stderr, "Failed to create mainloop\n");
+ goto out;
+--
+2.45.2
+
+
+From cf8f1e067f9be356f9879566ddfa31333929af22 Mon Sep 17 00:00:00 2001
+From: Natanael Copa <ncopa@alpinelinux.org>
+Date: Wed, 24 Jul 2024 17:29:10 +0200
+Subject: [PATCH 5/5] tools: lxc-top: add cgroups2 IO stats
+
+Signed-off-by: Natanael Copa <ncopa@alpinelinux.org>
+---
+ src/lxc/tools/lxc_top.c | 60 ++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 53 insertions(+), 7 deletions(-)
+
+diff --git a/src/lxc/tools/lxc_top.c b/src/lxc/tools/lxc_top.c
+index deea1b41d..6cc54988d 100644
+--- a/src/lxc/tools/lxc_top.c
++++ b/src/lxc/tools/lxc_top.c
+@@ -281,21 +281,21 @@ examples:
+ 8:0 Total 149327872
Total 149327872
*/
- static void stat_get_blk_stats(struct lxc_container *c, const char *item,
-- struct blkio_stats *stats) {
-+ struct blkio_stats *stats, bool *success) {
+-static void stat_get_blk_stats(struct lxc_container *c, const char *item,
++static int cg1_get_blk_stats(struct lxc_container *c, const char *item,
+ struct blkio_stats *stats) {
char buf[4096];
int i, len;
char **lines, **cols;
-+ *success = true;
++ int ret = -1;
len = c->get_cgroup_item(c, item, buf, sizeof(buf));
if (len <= 0 || (size_t)len >= sizeof(buf)) {
- fprintf(stderr, "Unable to read cgroup item %s\n", item);
-+ *success = false;
- return;
+- fprintf(stderr, "Unable to read cgroup item %s\n", item);
+- return;
++ return ret;
}
lines = lxc_string_split_and_trim(buf, '\n');
-- if (!lines)
-+ if (!lines) {
-+ *success = false;
- return;
-+ }
+ if (!lines)
+- return;
++ return ret;
memset(stats, 0, sizeof(struct blkio_stats));
- for (i = 0; lines[i]; i++) {
- cols = lxc_string_split_and_trim(lines[i], ' ');
-- if (!cols)
-+ if (!cols) {
-+ *success = false;
- goto out;
-+ }
-
- if (strncmp(cols[1], "Read", strlen(cols[1])) == 0)
- stats->read += strtoull(cols[2], NULL, 0);
-@@ -314,21 +320,53 @@ static void stat_get_blk_stats(struct lxc_container *c, const char *item,
- return;
- }
+@@ -314,10 +314,50 @@ static void stat_get_blk_stats(struct lxc_container *c, const char *item,
-+static void try_cgroup2(struct lxc_container *c, u_int64_t *stat, const char* path_cgroup1, const char* path_cgroup2,
-+ const char* match, bool call_match) {
-+
-+ int ret_cgroup2;
-+
-+ if (call_match) {
-+ ret_cgroup2 = stat_match_get_int(c, path_cgroup2, match, 1);
-+ if (ret_cgroup2 < 0) {
-+ *stat = stat_match_get_int(c, path_cgroup1, match, 1);
-+ }
-+
-+ } else {
-+ ret_cgroup2 = stat_get_int(c, path_cgroup2);
-+
-+ if (ret_cgroup2 < 0) {
-+ *stat = stat_get_int(c, path_cgroup1);
-+ } else {
-+ *stat = ret_cgroup2;
-+ }
-+ }
+ lxc_free_array((void **)cols, free);
+ }
++ ret = 0;
++out:
++ lxc_free_array((void **)lines, free);
++ return ret;
+}
+
++static int cg2_get_blk_stats(struct lxc_container *c, const char *item,
++ struct blkio_stats *stats) {
++ char buf[4096];
++ int i, j, len;
++ char **lines, **cols;
++ int ret = -1;
+
- static void stats_get(struct lxc_container *c, struct container_stats *ct, struct stats *total)
- {
- ct->c = c;
-- ct->stats->mem_used = stat_get_int(c, "memory.usage_in_bytes");
-- ct->stats->mem_limit = stat_get_int(c, "memory.limit_in_bytes");
-- ct->stats->memsw_used = stat_get_int(c, "memory.memsw.usage_in_bytes");
-- ct->stats->memsw_limit = stat_get_int(c, "memory.memsw.limit_in_bytes");
-+
-+ // handle stat_get_int cases
-+ try_cgroup2(c, &(ct->stats->mem_used), "memory.usage_in_bytes", "memory.current", NULL, false);
-+ try_cgroup2(c, &(ct->stats->mem_limit), "memory.limit_in_bytes", "memory.max", NULL, false);
-+ try_cgroup2(c, &(ct->stats->memsw_used), "memory.memsw.usage_in_bytes", "memory.swap.current", NULL, false);
-+ try_cgroup2(c, &(ct->stats->memsw_limit), "memory.memsw.limit_in_bytes", "memory.swap.max", NULL, false);
-+ try_cgroup2(c, &(ct->stats->cpu_use_nanos), "cpuacct.usage", "cpu.stat", NULL, false);
-+ try_cgroup2(c, &(ct->stats->cpu_use_user), "cpuacct.stat", "cpu.stat", "user", true);
-+ try_cgroup2(c, &(ct->stats->cpu_use_sys), "cpuacct.stat", "cpu.stat", "system", true);
-+
-+ // singular cgroup2 case for get blk stats
-+ bool success;
-+ stat_get_blk_stats(c, "io.stat", &ct->stats->io_service_bytes, &success);
-+ if (!success) {
-+ stat_get_blk_stats(c, "blkio.throttle.io_service_bytes", &ct->stats->io_service_bytes, &success);
++ len = c->get_cgroup_item(c, item, buf, sizeof(buf));
++ if (len <= 0 || (size_t)len >= sizeof(buf)) {
++ return ret;
+ }
+
-+ // paths only exist in cgroup1
- ct->stats->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes");
- ct->stats->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes");
-- ct->stats->cpu_use_nanos = stat_get_int(c, "cpuacct.usage");
-- ct->stats->cpu_use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1);
-- ct->stats->cpu_use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1);
--
++ lines = lxc_string_split_and_trim(buf, '\n');
++ if (!lines)
++ return ret;
++
++ memset(stats, 0, sizeof(struct blkio_stats));
++
++ for (i = 0; lines[i]; i++) {
++ cols = lxc_string_split_and_trim(lines[i], ' ');
++ if (!cols)
++ goto out;
+
++ for (j = 0; cols[j]; j++) {
++ if (strncmp(cols[j], "rbytes=", 7) == 0) {
++ stats->read += strtoull(&cols[j][7], NULL, 0);
++ } else if (strncmp(cols[j], "wbytes=", 7) == 0) {
++ stats->write += strtoull(&cols[j][7], NULL, 0);
++ }
++ }
++
++ lxc_free_array((void **)cols, free);
++ }
++ stats->total = stats->read + stats->write;
++ ret = 0;
+ out:
+ lxc_free_array((void **)lines, free);
+- return;
++ return ret;
+ }
+
+ static int cg1_mem_stats(struct lxc_container *c, struct mem_stats *mem)
+@@ -376,8 +416,14 @@ static void stats_get(struct lxc_container *c, struct container_stats *ct, struc
+ }
+ }
+
- stat_get_blk_stats(c, "blkio.throttle.io_service_bytes", &ct->stats->io_service_bytes);
- stat_get_blk_stats(c, "blkio.throttle.io_serviced", &ct->stats->io_serviced);
-+ stat_get_blk_stats(c, "blkio.throttle.io_serviced", &ct->stats->io_serviced, &success);
++ if (cg1_get_blk_stats(c, "blkio.throttle.io_service_bytes", &ct->stats->io_service_bytes) < 0) {
++ if (cg2_get_blk_stats(c, "io.stat", &ct->stats->io_service_bytes) < 0) {
++ fprintf(stderr, "Unable to read CPU stats\n");
++ }
++ } else {
++ /* only with cgroups v1 */
++ cg1_get_blk_stats(c, "blkio.throttle.io_serviced", &ct->stats->io_serviced);
++ }
if (total) {
- total->mem_used = total->mem_used + ct->stats->mem_used;
+ total->mem.used += ct->stats->mem.used;
+--
+2.45.2
+