[lxc-devel] [lxcfs/master] CPU views based on quotas: several improvements

BurningXFlame on Github lxc-bot at linuxcontainers.org
Mon Jun 10 07:29:21 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 2302 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190610/4e31b3f7/attachment.bin>
-------------- next part --------------
From 63800363bb72cfc83d23523ffc673ec33e9b5aba Mon Sep 17 00:00:00 2001
From: Stephen Xiang <>
Date: Fri, 24 May 2019 14:56:13 +0800
Subject: [PATCH 1/3] fall back to cpuacct.usage_percpu if cpuacct.usage_all
 not exists

---
 .gitignore  |   1 +
 Makefile.am |   7 ++-
 bindings.c  |  48 ++++++++++++++--
 macro.h     |   6 ++
 sb.c        | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 sb.h        |  57 +++++++++++++++++++
 6 files changed, 273 insertions(+), 7 deletions(-)
 create mode 100644 sb.c
 create mode 100644 sb.h

diff --git a/.gitignore b/.gitignore
index 95b724c..6e2130c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,4 @@ lxcfs-*.tar.gz
 .libs
 *.lo
 *.la
+.vscode
\ No newline at end of file
diff --git a/Makefile.am b/Makefile.am
index 99cbb36..4a76467 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,18 +8,21 @@ AM_CFLAGS += $(FUSE_CFLAGS)
 AM_CFLAGS += -DLIBDIR=\"$(LIBDIR)\"
 AM_LDFLAGS = $(FUSE_LIBS) -pthread
 #AM_CFLAGS += -DDEBUG
+AM_CFLAGS += -DVERBOSE
 
 AM_CFLAGS += -DRUNTIME_PATH=\"$(RUNTIME_PATH)\"
 
 liblxcfs_la_SOURCES = bindings.c bindings.h \
 		      cpuset.c \
-		      sysfs_fuse.c syfs_fuse.h
+		      sysfs_fuse.c syfs_fuse.h \
+			  sb.c sb.h
 liblxcfs_la_CFLAGS = $(AM_CFLAGS)
 liblxcfs_la_LDFLAGS = $(AM_CFLAGS) -module -avoid-version -shared
 
 liblxcfstest_la_SOURCES = bindings.c bindings.h \
 			  cpuset.c \
-			  sysfs_fuse.c syfs_fuse.h
+			  sysfs_fuse.c syfs_fuse.h \
+			  sb.c sb.h
 liblxcfstest_la_CFLAGS = $(AM_CFLAGS) -DRELOADTEST
 liblxcfstest_la_LDFLAGS = $(AM_CFLAGS) -module -avoid-version -shared
 
diff --git a/bindings.c b/bindings.c
index a948a5d..7c68691 100644
--- a/bindings.c
+++ b/bindings.c
@@ -38,6 +38,7 @@
 
 #include "bindings.h"
 #include "config.h" // for VERSION
+#include "sb.h"
 
 /* Define pivot_root() if missing from the C library */
 #ifndef HAVE_PIVOT_ROOT
@@ -1155,6 +1156,7 @@ bool cgfs_get_value(const char *controller, const char *cgroup, const char *file
 	char *fnam, *tmpc;
 
 	tmpc = find_mounted_controller(controller, &cfd);
+	lxcfs_v("tmpc: %s\n", tmpc);
 	if (!tmpc)
 		return false;
 
@@ -1164,6 +1166,7 @@ bool cgfs_get_value(const char *controller, const char *cgroup, const char *file
 	len = strlen(cgroup) + strlen(file) + 3;
 	fnam = alloca(len);
 	ret = snprintf(fnam, len, "%s%s/%s", *cgroup == '/' ? "." : "", cgroup, file);
+	lxcfs_v("fnam: %s\n", fnam);
 	if (ret < 0 || (size_t)ret >= len)
 		return false;
 
@@ -1172,6 +1175,7 @@ bool cgfs_get_value(const char *controller, const char *cgroup, const char *file
 		return false;
 
 	*value = slurp_file(fnam, fd);
+	lxcfs_v("*value: %s\n", *value);
 	return *value != NULL;
 }
 
@@ -3738,9 +3742,11 @@ static int proc_cpuinfo_read(char *buf, size_t size, off_t offset,
 	}
 
 	pid_t initpid = lookup_initpid_in_store(fc->pid);
+	lxcfs_v("initpid: %d\n", initpid);
 	if (initpid <= 0)
 		initpid = fc->pid;
 	cg = get_pid_cgroup(initpid, "cpuset");
+	lxcfs_v("cg: %s\n", cg);
 	if (!cg)
 		return read_file("proc/cpuinfo", buf, size, d);
 	prune_init_slice(cg);
@@ -3750,9 +3756,11 @@ static int proc_cpuinfo_read(char *buf, size_t size, off_t offset,
 		goto err;
 
 	use_view = use_cpuview(cg);
+	lxcfs_v("use_view: %d\n", use_view);
 
 	if (use_view)
 		max_cpus = max_cpu_count(cg);
+		lxcfs_v("max_cpus: %d\n", max_cpus);
 
 	f = fopen("/proc/cpuinfo", "r");
 	if (!f)
@@ -4034,7 +4042,7 @@ static int read_cpuacct_usage_all(char *cg, char *cpuset, struct cpuacct_usage *
 	ticks_per_sec = sysconf(_SC_CLK_TCK);
 
 	if (ticks_per_sec < 0 && errno == EINVAL) {
-		lxcfs_debug(
+		lxcfs_v(
 			"%s\n",
 			"read_cpuacct_usage_all failed to determine number of clock ticks "
 			"in a second");
@@ -4042,12 +4050,40 @@ static int read_cpuacct_usage_all(char *cg, char *cpuset, struct cpuacct_usage *
 	}
 
 	cpu_usage = malloc(sizeof(struct cpuacct_usage) * cpucount);
+	bzero(cpu_usage,sizeof(struct cpuacct_usage) * cpucount);
 	if (!cpu_usage)
 		return -ENOMEM;
 
 	if (!cgfs_get_value("cpuacct", cg, "cpuacct.usage_all", &usage_str)) {
-		rv = -1;
-		goto err;
+		lxcfs_v("failed to read cpuacct.usage_all. usage_str: %s\n", usage_str);
+
+		// read cpuacct.usage_percpu instead
+		lxcfs_v("reading cpuacct.usage_percpu instead%s\n", "");
+		if (!cgfs_get_value("cpuacct", cg, "cpuacct.usage_percpu", &usage_str)) {
+			rv = -1;
+			goto err;
+		}
+		lxcfs_v("usage_str: %s\n", usage_str);
+
+		// convert cpuacct.usage_percpu into cpuacct.usage_all
+		lxcfs_v("converting cpuacct.usage_percpu into cpuacct.usage_all%s\n", "");
+		StringBuilder *sb = sb_create();
+		sb_append(sb, "cpu user system\n");
+		int i = 0;
+		while(sscanf(usage_str + read_pos, "%lu %n", &cg_user, &read_cnt) > 0){
+			lxcfs_v("i: %d, cg_user: %lu, read_pos: %d, read_cnt: %d\n", i, cg_user, read_pos, read_cnt);
+			sb_appendf(sb, "%d %lu %lu\n", i, cg_user, 0);
+			i++;
+			read_pos += read_cnt;
+		}
+
+		free(usage_str);
+		usage_str = sb_concat(sb);
+		sb_free(sb);
+		read_pos=0;
+		read_cnt=0;
+
+		lxcfs_v("usage_str: %s\n", usage_str);
 	}
 
 	if (sscanf(usage_str, "cpu user system\n%n", &read_cnt) != 0) {
@@ -4770,9 +4806,11 @@ static int proc_stat_read(char *buf, size_t size, off_t offset,
 	}
 
 	pid_t initpid = lookup_initpid_in_store(fc->pid);
+	lxcfs_v("initpid: %d\n", initpid);
 	if (initpid <= 0)
 		initpid = fc->pid;
 	cg = get_pid_cgroup(initpid, "cpuset");
+	lxcfs_v("cg: %s\n", cg);
 	if (!cg)
 		return read_file("/proc/stat", buf, size, d);
 	prune_init_slice(cg);
@@ -4787,7 +4825,7 @@ static int proc_stat_read(char *buf, size_t size, off_t offset,
 	 * CPU usage. If not, values from the host's /proc/stat are used.
 	 */
 	if (read_cpuacct_usage_all(cg, cpuset, &cg_cpu_usage, &cg_cpu_usage_size) != 0) {
-		lxcfs_debug("%s\n", "proc_stat_read failed to read from cpuacct, "
+		lxcfs_v("%s\n", "proc_stat_read failed to read from cpuacct, "
 				"falling back to the host's /proc/stat");
 	}
 
@@ -6291,4 +6329,4 @@ static void __attribute__((destructor)) free_subsystems(void)
 
 	if (cgroup_mount_ns_fd >= 0)
 		close(cgroup_mount_ns_fd);
-}
+}
\ No newline at end of file
diff --git a/macro.h b/macro.h
index 1763dc2..7213b8f 100644
--- a/macro.h
+++ b/macro.h
@@ -15,4 +15,10 @@
 #define lxcfs_debug(format, ...)
 #endif /* DEBUG */
 
+#ifdef VERBOSE
+#define lxcfs_v(format, ...) lxcfs_error(format, __VA_ARGS__);
+#else
+#define lxcfs_v(format, ...)
+#endif /* VERBOSE */
+
 #endif /* __LXCFS_MACRO_H */
diff --git a/sb.c b/sb.c
new file mode 100644
index 0000000..5b5bf9a
--- /dev/null
+++ b/sb.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2017 Ryan Armstrong <ryan at cavaliercoder.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * C String Builder - https://github.com/cavaliercoder/c-stringbuilder
+ *
+ * sb.c is a simple, non-thread safe String Builder that makes use of a
+ * dynamically-allocated linked-list to enable linear time appending and
+ * concatenation.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include "sb.h"
+
+/*
+ * sb_create returns a pointer to a new StringBuilder or NULL if memory is not
+ * available.
+ */
+StringBuilder *sb_create()
+{
+	StringBuilder *sb = (StringBuilder*) calloc(sizeof(StringBuilder), 1);
+	return sb;
+}
+
+/*
+ * sb_empty returns non-zero if the given StringBuilder is empty.
+ */
+int sb_empty(StringBuilder *sb)
+{
+	return (sb->root == NULL);
+}
+
+/*
+ * sb_append adds a copy of the given string to a StringBuilder.
+ */
+int sb_append(StringBuilder *sb, const char *str)
+{
+	int				length = 0;
+	StringFragment	*frag = NULL;
+
+	if (NULL == str || '\0' == *str)
+		return sb->length;
+
+	length = strlen(str);
+	frag = (StringFragment*) malloc(sizeof(StringFragment) + (sizeof(char) * length));
+	if (NULL == frag)
+		return SB_FAILURE;
+
+	frag->next = NULL;
+	frag->length = length;
+	memcpy((void*) &frag->str, (const void*) str, sizeof(char) * (length + 1));
+
+	sb->length += length;
+	if (NULL == sb->root)
+		sb->root = frag;
+	else
+		sb->trunk->next = frag;
+
+	sb->trunk = frag;
+
+	return sb->length;
+}
+
+/*
+ * sb_appendf adds a copy of the given formatted string to a StringBuilder.
+ */
+int sb_appendf(StringBuilder *sb, const char *format, ...)
+{
+	int			rc = 0;
+	char		buf[SB_MAX_FRAG_LENGTH];
+	va_list		args;
+
+	va_start (args, format);
+	rc = vsnprintf(&buf[0], SB_MAX_FRAG_LENGTH, format, args);
+	va_end(args);
+
+	if (0 > rc)
+		return SB_FAILURE;
+
+	return sb_append(sb, buf);
+}
+
+/*
+ * sb_concat returns a concatenation of strings that have been appended to the
+ * StringBuilder. It is the callers responsibility to free the returned
+ * reference.
+ *
+ * The StringBuilder is not modified by this function and can therefore continue
+ * to be used.
+ */
+char *sb_concat(StringBuilder *sb)
+{
+	char			*buf = NULL;
+	char			*c = NULL;
+	StringFragment	*frag = NULL;
+
+	buf = (char *) malloc((sb->length + 1) * sizeof(char));
+	if (NULL == buf)
+		return NULL;
+
+	c = buf;
+	for (frag = sb->root; frag; frag = frag->next) {
+		memcpy(c, &frag->str, sizeof(char) * frag->length);
+		c += frag->length;
+	}
+
+	*c = '\0';
+
+	return buf;
+}
+
+/*
+ * sb_reset resets the given StringBuilder, freeing all previously appended
+ * strings.
+ */
+void sb_reset(StringBuilder *sb)
+{
+	StringFragment *frag = NULL;
+	StringFragment *next = NULL;
+
+	frag = sb->root;
+	while(frag) {
+		next = frag->next;
+		free(frag);
+		frag = next;
+	}
+
+	sb->root = NULL;
+	sb->trunk = NULL;
+	sb->length = 0;
+}
+
+/*
+ * sb_free frees the given StringBuilder and all of its appended strings.
+ */
+void sb_free(StringBuilder *sb)
+{
+	sb_reset(sb);
+	free(sb);
+}
diff --git a/sb.h b/sb.h
new file mode 100644
index 0000000..2c18d4c
--- /dev/null
+++ b/sb.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017 Ryan Armstrong <ryan at cavaliercoder.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * C String Builder - https://github.com/cavaliercoder/c-stringbuilder
+ *
+ * sb.c is a simple, non-thread safe String Builder that makes use of a
+ * dynamically-allocated linked-list to enable linear time appending and
+ * concatenation.
+ */
+
+#ifndef SB_H
+#define SB_H
+
+#define SB_FAILURE				-1
+#define SB_MAX_FRAG_LENGTH		4096
+
+typedef struct _StringFragment {
+	struct _StringFragment	*next;
+	int						length;
+	char					*str;
+} StringFragment;
+
+typedef struct _StringBuilder {
+	struct _StringFragment	*root;
+	struct _StringFragment	*trunk;
+	int						length;
+} StringBuilder;
+
+StringBuilder	*sb_create();
+int				sb_empty(StringBuilder *sb);
+int				sb_append(StringBuilder *sb, const char *str);
+int				sb_appendf(StringBuilder *sb, const char *format, ...);
+char			*sb_concat(StringBuilder *sb);
+void 			sb_reset(StringBuilder *sb);
+void			sb_free(StringBuilder *sb);
+
+#endif

From 4a9893e24162d740416ac6f90951c6b1f670a392 Mon Sep 17 00:00:00 2001
From: Stephen Xiang <>
Date: Mon, 27 May 2019 17:18:49 +0800
Subject: [PATCH 2/3] bugfix: cpuview_proc_stat: statistic bug of cpu usage

---
 bindings.c | 36 +++++++++++++++++-------------------
 1 file changed, 17 insertions(+), 19 deletions(-)

diff --git a/bindings.c b/bindings.c
index 7c68691..506f75e 100644
--- a/bindings.c
+++ b/bindings.c
@@ -1156,7 +1156,7 @@ bool cgfs_get_value(const char *controller, const char *cgroup, const char *file
 	char *fnam, *tmpc;
 
 	tmpc = find_mounted_controller(controller, &cfd);
-	lxcfs_v("tmpc: %s\n", tmpc);
+	lxcfs_debug("tmpc: %s\n", tmpc);
 	if (!tmpc)
 		return false;
 
@@ -1166,7 +1166,7 @@ bool cgfs_get_value(const char *controller, const char *cgroup, const char *file
 	len = strlen(cgroup) + strlen(file) + 3;
 	fnam = alloca(len);
 	ret = snprintf(fnam, len, "%s%s/%s", *cgroup == '/' ? "." : "", cgroup, file);
-	lxcfs_v("fnam: %s\n", fnam);
+	lxcfs_debug("fnam: %s\n", fnam);
 	if (ret < 0 || (size_t)ret >= len)
 		return false;
 
@@ -1175,7 +1175,7 @@ bool cgfs_get_value(const char *controller, const char *cgroup, const char *file
 		return false;
 
 	*value = slurp_file(fnam, fd);
-	lxcfs_v("*value: %s\n", *value);
+	lxcfs_debug("*value: %s\n", *value);
 	return *value != NULL;
 }
 
@@ -3742,11 +3742,9 @@ static int proc_cpuinfo_read(char *buf, size_t size, off_t offset,
 	}
 
 	pid_t initpid = lookup_initpid_in_store(fc->pid);
-	lxcfs_v("initpid: %d\n", initpid);
 	if (initpid <= 0)
 		initpid = fc->pid;
 	cg = get_pid_cgroup(initpid, "cpuset");
-	lxcfs_v("cg: %s\n", cg);
 	if (!cg)
 		return read_file("proc/cpuinfo", buf, size, d);
 	prune_init_slice(cg);
@@ -3756,11 +3754,9 @@ static int proc_cpuinfo_read(char *buf, size_t size, off_t offset,
 		goto err;
 
 	use_view = use_cpuview(cg);
-	lxcfs_v("use_view: %d\n", use_view);
 
 	if (use_view)
 		max_cpus = max_cpu_count(cg);
-		lxcfs_v("max_cpus: %d\n", max_cpus);
 
 	f = fopen("/proc/cpuinfo", "r");
 	if (!f)
@@ -4063,7 +4059,6 @@ static int read_cpuacct_usage_all(char *cg, char *cpuset, struct cpuacct_usage *
 			rv = -1;
 			goto err;
 		}
-		lxcfs_v("usage_str: %s\n", usage_str);
 
 		// convert cpuacct.usage_percpu into cpuacct.usage_all
 		lxcfs_v("converting cpuacct.usage_percpu into cpuacct.usage_all%s\n", "");
@@ -4071,7 +4066,7 @@ static int read_cpuacct_usage_all(char *cg, char *cpuset, struct cpuacct_usage *
 		sb_append(sb, "cpu user system\n");
 		int i = 0;
 		while(sscanf(usage_str + read_pos, "%lu %n", &cg_user, &read_cnt) > 0){
-			lxcfs_v("i: %d, cg_user: %lu, read_pos: %d, read_cnt: %d\n", i, cg_user, read_pos, read_cnt);
+			lxcfs_debug("i: %d, cg_user: %lu, read_pos: %d, read_cnt: %d\n", i, cg_user, read_pos, read_cnt);
 			sb_appendf(sb, "%d %lu %lu\n", i, cg_user, 0);
 			i++;
 			read_pos += read_cnt;
@@ -4083,7 +4078,7 @@ static int read_cpuacct_usage_all(char *cg, char *cpuset, struct cpuacct_usage *
 		read_pos=0;
 		read_cnt=0;
 
-		lxcfs_v("usage_str: %s\n", usage_str);
+		lxcfs_debug("usage_str: %s\n", usage_str);
 	}
 
 	if (sscanf(usage_str, "cpu user system\n%n", &read_cnt) != 0) {
@@ -4599,14 +4594,14 @@ static int cpuview_proc_stat(const char *cg, const char *cpuset, struct cpuacct_
 		threshold = total_sum / cpu_cnt * max_cpus;
 
 		for (curcpu = 0, i = -1; curcpu < nprocs; curcpu++) {
-			if (i == max_cpus)
-				break;
-
 			if (!stat_node->usage[curcpu].online)
 				continue;
 
 			i++;
 
+			if (max_cpus > 0 && i == max_cpus)
+				break;
+
 			if (diff[curcpu].user + diff[curcpu].system >= threshold)
 				continue;
 
@@ -4634,14 +4629,14 @@ static int cpuview_proc_stat(const char *cg, const char *cpuset, struct cpuacct_
 			lxcfs_debug("leftover system: %lu for %s\n", system_surplus, cg);
 
 		for (curcpu = 0, i = -1; curcpu < nprocs; curcpu++) {
-			if (i == max_cpus)
-				break;
-
 			if (!stat_node->usage[curcpu].online)
 				continue;
 
 			i++;
 
+			if (max_cpus > 0 && i == max_cpus)
+				break;
+
 			stat_node->view[curcpu].user += diff[curcpu].user;
 			stat_node->view[curcpu].system += diff[curcpu].system;
 			stat_node->view[curcpu].idle += diff[curcpu].idle;
@@ -4649,8 +4644,10 @@ static int cpuview_proc_stat(const char *cg, const char *cpuset, struct cpuacct_
 			user_sum += stat_node->view[curcpu].user;
 			system_sum += stat_node->view[curcpu].system;
 			idle_sum += stat_node->view[curcpu].idle;
-		}
 
+			lxcfs_v("user: %lu, system: %lu, idle: %lu\n", stat_node->view[curcpu].user, stat_node->view[curcpu].system, stat_node->view[curcpu].idle);
+			lxcfs_v("user_sum: %lu, system_sum: %lu, idle_sum: %lu\n", user_sum, system_sum, idle_sum);
+		}
 	} else {
 		for (curcpu = 0; curcpu < nprocs; curcpu++) {
 			if (!stat_node->usage[curcpu].online)
@@ -4659,7 +4656,7 @@ static int cpuview_proc_stat(const char *cg, const char *cpuset, struct cpuacct_
 			stat_node->view[curcpu].user = stat_node->usage[curcpu].user;
 			stat_node->view[curcpu].system = stat_node->usage[curcpu].system;
 			stat_node->view[curcpu].idle = stat_node->usage[curcpu].idle;
-
+	
 			user_sum += stat_node->view[curcpu].user;
 			system_sum += stat_node->view[curcpu].system;
 			idle_sum += stat_node->view[curcpu].idle;
@@ -4672,12 +4669,12 @@ static int cpuview_proc_stat(const char *cg, const char *cpuset, struct cpuacct_
 			user_sum,
 			system_sum,
 			idle_sum);
+	lxcfs_v("cpu-all: %s\n", buf);
 
 	if (l < 0) {
 		perror("Error writing to cache");
 		rv = 0;
 		goto err;
-
 	}
 	if (l >= buf_size) {
 		lxcfs_error("%s\n", "Internal error: truncated write to cache.");
@@ -4704,6 +4701,7 @@ static int cpuview_proc_stat(const char *cg, const char *cpuset, struct cpuacct_
 				stat_node->view[curcpu].user,
 				stat_node->view[curcpu].system,
 				stat_node->view[curcpu].idle);
+		lxcfs_v("cpu: %s\n", buf);
 
 		if (l < 0) {
 			perror("Error writing to cache");

From 5ae3728e75e952c7837475315c8bfbbec346fbda Mon Sep 17 00:00:00 2001
From: Stephen Xiang <>
Date: Mon, 3 Jun 2019 18:08:42 +0800
Subject: [PATCH 3/3] Correct CPU usage in partial CPU cases when quota/period
 don't yield an integer.

---
 Makefile.am |  2 +-
 bindings.c  | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 48 insertions(+), 3 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 4a76467..06e9ec3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,7 +8,7 @@ AM_CFLAGS += $(FUSE_CFLAGS)
 AM_CFLAGS += -DLIBDIR=\"$(LIBDIR)\"
 AM_LDFLAGS = $(FUSE_LIBS) -pthread
 #AM_CFLAGS += -DDEBUG
-AM_CFLAGS += -DVERBOSE
+#AM_CFLAGS += -DVERBOSE
 
 AM_CFLAGS += -DRUNTIME_PATH=\"$(RUNTIME_PATH)\"
 
diff --git a/bindings.c b/bindings.c
index 506f75e..637c0bf 100644
--- a/bindings.c
+++ b/bindings.c
@@ -3683,6 +3683,35 @@ int max_cpu_count(const char *cg)
 	return rv;
 }
 
+/*
+ * Return the exact number of visible CPUs based on CPU quotas.
+ * If there is no quota set, zero is returned.
+ */
+double exact_cpu_count(const char *cg)
+{
+	double rv;
+	int nprocs;
+	int64_t cfs_quota, cfs_period;
+
+	if (!read_cpu_cfs_param(cg, "quota", &cfs_quota))
+		return 0;
+
+	if (!read_cpu_cfs_param(cg, "period", &cfs_period))
+		return 0;
+
+	if (cfs_quota <= 0 || cfs_period <= 0)
+		return 0;
+
+	rv = (double)cfs_quota / (double)cfs_period;
+
+	nprocs = get_nprocs();
+
+	if (rv > nprocs)
+		rv = nprocs;
+
+	return rv;
+}
+
 /*
  * Determine whether CPU views should be used or not.
  */
@@ -4628,6 +4657,7 @@ static int cpuview_proc_stat(const char *cg, const char *cpuset, struct cpuacct_
 		if (system_surplus > 0)
 			lxcfs_debug("leftover system: %lu for %s\n", system_surplus, cg);
 
+		unsigned long diff_total = 0;
 		for (curcpu = 0, i = -1; curcpu < nprocs; curcpu++) {
 			if (!stat_node->usage[curcpu].online)
 				continue;
@@ -4645,8 +4675,23 @@ static int cpuview_proc_stat(const char *cg, const char *cpuset, struct cpuacct_
 			system_sum += stat_node->view[curcpu].system;
 			idle_sum += stat_node->view[curcpu].idle;
 
-			lxcfs_v("user: %lu, system: %lu, idle: %lu\n", stat_node->view[curcpu].user, stat_node->view[curcpu].system, stat_node->view[curcpu].idle);
-			lxcfs_v("user_sum: %lu, system_sum: %lu, idle_sum: %lu\n", user_sum, system_sum, idle_sum);
+			diff_total += diff[curcpu].user + diff[curcpu].system + diff[curcpu].idle;
+
+			lxcfs_v("curcpu: %d, user: %lu, system: %lu, idle: %lu\n", curcpu, stat_node->view[curcpu].user, stat_node->view[curcpu].system, stat_node->view[curcpu].idle);
+			lxcfs_v("user_sum: %lu, system_sum: %lu, idle_sum: %lu, diff_total: %lu\n", user_sum, system_sum, idle_sum, diff_total);
+		}
+
+		// revise cpu usage view to support partial cpu case
+		double exact_cpus = exact_cpu_count(cg);
+		if (exact_cpus < (double)max_cpus){
+			lxcfs_v("revising cpu usage view to match the exact cpu count [%f]\n", exact_cpus);
+			unsigned long delta = (unsigned long)((double)diff_total * (1 - exact_cpus / (double)max_cpus));
+			idle_sum = idle_sum > delta ? idle_sum - delta : 0;
+			lxcfs_v("user_sum: %lu, system_sum: %lu, idle_sum: %lu, delta: %lu\n", user_sum, system_sum, idle_sum, delta);
+			
+			curcpu = max_cpus - 1;
+			stat_node->view[curcpu].idle = stat_node->view[curcpu].idle > delta ? stat_node->view[curcpu].idle - delta : 0;
+			lxcfs_v("curcpu: %d, user: %lu, system: %lu, idle: %lu\n", curcpu, stat_node->view[curcpu].user, stat_node->view[curcpu].system, stat_node->view[curcpu].idle);
 		}
 	} else {
 		for (curcpu = 0; curcpu < nprocs; curcpu++) {


More information about the lxc-devel mailing list