summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/cgroup
diff options
context:
space:
mode:
authorShaojie Sun <sunshaojie@kylinos.cn>2026-09-21 21:15:55 +0800
committerTejun Heo <tj@kernel.org>2026-09-21 07:20:01 -1000
commit47f4cdbc14247502479bdde4511022c16d91eed4 (patch)
tree562b1247af84da7f98661ad0d99bd6d1aa289c56 /tools/testing/selftests/cgroup
parent3c6146e3327b2a2aa1b0ca070ac50c3142932c5e (diff)
downloadlinux-next-47f4cdbc14247502479bdde4511022c16d91eed4.tar.gz
linux-next-47f4cdbc14247502479bdde4511022c16d91eed4.zip
selftests/cgroup: read the entire file in read_text()
read_text() issues a single read(2) and returns whatever it got, so a caller that asks for a whole file silently gets a prefix of it instead. For a seq_file larger than one page it never gets more than that prefix: seq_read_iter() copies out at most one internal PAGE_SIZE buffer per call and leaves the rest for the next read(2), no matter how much room the caller's buffer has. /proc/self/mounts is one of those files, so cg_find_root() only ever sees the first 4K of the mount table. On a machine with ~80 mounts the cgroup2 entry already sits past that limit: the file is 7177 bytes, the single read(2) into the 40K buffer in cg_find_root() returns 4035 bytes, and the cgroup2 line starts at offset 4953. cg_find_root() then fails, and every test that calls cg_find_unified_root() -- test_core, test_cpu, test_cpuset, test_freezer, test_hugetlb_memcg, test_kill, test_kmem, test_memcontrol, test_pids and test_zswap -- exits with SKIP "cgroup v2 isn't mounted" without running a single test. proc_mount_contains() searches only that same prefix, so a mount option listed later in /proc/mounts is reported as absent, which is what the probes in test_memcontrol and test_hugetlb_memcg are based on. Read until the buffer is full or EOF instead. A partial read is still possible when max_len is too small, so callers that deliberately read a prefix, such as cg_read_strcmp(), keep working. Signed-off-by: Shaojie Sun <sunshaojie@kylinos.cn> Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'tools/testing/selftests/cgroup')
-rw-r--r--tools/testing/selftests/cgroup/lib/cgroup_util.c35
1 files changed, 29 insertions, 6 deletions
diff --git a/tools/testing/selftests/cgroup/lib/cgroup_util.c b/tools/testing/selftests/cgroup/lib/cgroup_util.c
index 2596c12cd864..65cd85c467bb 100644
--- a/tools/testing/selftests/cgroup/lib/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/lib/cgroup_util.c
@@ -24,20 +24,43 @@ bool cg_test_v1_named;
/* Returns read len on success, or -errno on failure. */
ssize_t read_text(const char *path, char *buf, size_t max_len)
{
- ssize_t len;
+ size_t total = 0;
+ ssize_t len, ret;
int fd;
fd = open(path, O_RDONLY);
if (fd < 0)
return -errno;
- len = read(fd, buf, max_len - 1);
-
- if (len >= 0)
- buf[len] = 0;
+ /*
+ * A single read() is not enough. procfs and sysfs are backed by
+ * seq_file, and seq_read_iter() copies out at most one internal
+ * buffer (PAGE_SIZE) per call, leaving the rest for the next read().
+ * Reading only once therefore silently drops everything past the
+ * first page, no matter how big the caller's buffer is.
+ *
+ * Loop until the buffer is full or EOF. A full buffer still means
+ * the file may be longer than max_len, but that is now limited by
+ * the caller's buffer rather than by a page of seq_file output.
+ */
+ while (total < max_len - 1) {
+ len = read(fd, buf + total, max_len - 1 - total);
+ if (len < 0) {
+ if (errno == EINTR)
+ continue;
+ ret = -errno;
+ goto out;
+ }
+ if (!len)
+ break;
+ total += len;
+ }
+ buf[total] = 0;
+ ret = total;
+out:
close(fd);
- return len < 0 ? -errno : len;
+ return ret;
}
/* Returns written len on success, or -errno on failure. */