diff options
| author | Shaojie Sun <sunshaojie@kylinos.cn> | 2026-09-21 21:15:55 +0800 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2026-09-21 07:20:01 -1000 |
| commit | 47f4cdbc14247502479bdde4511022c16d91eed4 (patch) | |
| tree | 562b1247af84da7f98661ad0d99bd6d1aa289c56 /tools/testing/selftests/cgroup | |
| parent | 3c6146e3327b2a2aa1b0ca070ac50c3142932c5e (diff) | |
| download | linux-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.c | 35 |
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. */ |
