From: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
To: Konstantin Khorenko <khorenko@virtuozzo.com>, ptikhomirov@virtuozzo.com
Cc: devel@openvz.org
Subject: Re: [Devel] [PATCH hci-8.1 v3 2/2] selftests: ve: Test sched_getaffinity in VE
Date: Thu, 29 Oct 2026 11:50:48 +0100 [thread overview]
Message-ID: <ab62df52-5ded-43b7-a9a7-044e38863f9d@virtuozzo.com> (raw)
In-Reply-To: <010701a1872e7a0e-b9a72472-754e-4582-8ed8-c4019b63a955-000000@eu-central-1.amazonses.com>
On 10/29/26 11:22, no-reply@sashiko.virtuozzo.com wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [Medium] selftests/ve: get_nth_cpu() bounds loop by sizeof(cpu_set_t) bytes, not CPU_SETSIZE
Replied in v2.
> - [Medium] selftests/ve: expected CT CPU count taken from caller's affinity, not CT cpuset
This is not a CT. This test is a root-only, no need for complications.
> - [Low] selftests/ve: FIXTURE_SETUP leaks CT cgroup if prepare_namespaces() ASSERT fails
This is fine IMO. With skip it was correct to move it to the top.
Here if something fails we should leave all resources, to simplify
debugging.
> --
>
> commit 55fc819ea7f7954caf6a8e968697a2cc48fd1982
> Author: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
>
> selftests: ve: Test sched_getaffinity in VE
>
> This adds a selftest that checks sched_getaffinity() inside a VE respects
> both the cgroup cpuset and the cpu.nr_cpus limit. It adds three test cases
> and registers the binary in the Makefile and .gitignore.
>
> Link: https://virtuozzo.atlassian.net/browse/VSTOR-141682
>
>> diff --git a/tools/testing/selftests/ve/ve_sched_getaffinity_test.c b/tools/testing/selftests/ve/ve_sched_getaffinity_test.c
>> new file mode 100644
>> index 000000000000..98acc246ede0
>> --- /dev/null
>> +++ b/tools/testing/selftests/ve/ve_sched_getaffinity_test.c
>
> [ ... ]
>
>> +static int get_nth_cpu(cpu_set_t *mask, int n)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < sizeof(*mask); i++) {
>> + if (!CPU_ISSET(i, mask) || --n)
>> + continue;
>> +
>> + return i;
>> + }
>> + return -1;
>> +}
>
> [Severity: Medium]
> Is the loop bound in get_nth_cpu() correct? sizeof(*mask) is the size of
> cpu_set_t in bytes (128), while CPU_ISSET() takes a bit index and the set
> holds CPU_SETSIZE (1024) bits:
>
> for (i = 0; i < sizeof(*mask); i++) {
> if (!CPU_ISSET(i, mask) || --n)
>
> The scan stops at CPU 127. If the 2nd or 3rd allowed CPU in orig_cpuset
> is numbered 128 or higher (for example under taskset, a restricted cpuset,
> or a large sparse host), get_nth_cpu() returns -1.
>
> update_cg_cpuset then fails ASSERT_GE(cpu_leave, 0), and limits_interaction
> fails ASSERT_TRUE(c1 >= 0 && c3 >= 0), without sched_getaffinity() in the
> VE being exercised.
>
> The loop also compares a signed int with a size_t. Should the bound be
> CPU_SETSIZE?
>
>> +FIXTURE_SETUP(ve_getaff_test)
>> +{
>> + ASSERT_EQ(sched_getaffinity(getpid(), sizeof(cpu_set_t), &self->orig_cpuset), 0);
>> + self->orig_cpucount = CPU_COUNT(&self->orig_cpuset);
>
> [Severity: Medium]
> Is orig_cpucount the right expected value for the CT? It is taken from the
> runner's own affinity mask, not from the CT cgroup's effective cpuset.
>
> In getaffinity_ve(), run_in_ve() forks a child that does:
>
> enter_cgroup(cgv2_fd, ctid);
> enter_namespaces(ctx, flags);
> ret = fn(arg);
>
> Nothing in the child restores the parent's affinity after joining the
> cgroup. So the mask that __getaffinity_ve() reports derives from the CT
> cgroup's cpuset, which is inherited from the parent cgroup.
>
> I did not read cpuset_attach() in this tree. From memory of mainline, it
> resets cpus_mask to the cgroup's effective_cpus on attach, but that is
> unverified.
>
> If the runner was restricted, for example with taskset -c 0,1 on a host
> with more CPUs, the CT may report more CPUs than orig_cpucount. Then this
> fails in update_cg_cpuset, nr_proc_bound and limits_interaction:
>
> ASSERT_TRUE(check_cpumask_is_prefix(&in_ve, self->orig_cpucount));
>
> Would it be better to derive the expected count from the CT cgroup's
> effective cpuset, or to skip when the two differ?
>
> Separately, sched_getaffinity() with a fixed sizeof(cpu_set_t) returns
> EINVAL when nr_cpu_ids is greater than 1024. FIXTURE_SETUP asserts on that
> call rather than skipping.
>
> [ ... ]
>
>> + self->ctid = make_ve(self->cgv2_fd, CTID_MIN);
>> + ASSERT_GE(self->ctid, 0);
>> +
>> + ASSERT_EQ(prepare_namespaces(&self->ns_ctx, CLONE_NEWVE,
>> + self->cgv2_fd, self->ctid), 0);
>> +}
>
> [Severity: Low]
> Can this leak the CT cgroup created by make_ve()? If prepare_namespaces()
> fails, the ASSERT_EQ() aborts FIXTURE_SETUP before setup_completed is set.
>
> In kselftest_harness.h, __TEST_F_IMPL only calls the teardown when
> _metadata->setup_completed is true:
>
> if (_metadata->setup_completed && !fixture_name##_teardown_parent && \
> __sync_bool_compare_and_swap(teardown, false, true)) \
> fixture_name##_teardown(_metadata, self, variant->data); \
>
> So FIXTURE_TEARDOWN(ve_getaff_test) never runs. close_namespace_fds(),
> destroy_ve() and close(cgv2_fd) are all skipped.
>
> I inferred, but did not verify in this tree, that the cgroup persists after
> the process exits. If so, each leaked cgroup keeps the first free id in
> the CTID_MIN..CTID_MAX range that make_ve() scans:
>
> for (id = from; id < CTID_MAX; id++) {
> snprintf(name, sizeof(name), "%d", id);
> if (faccessat(cgv2_fd, name, F_OK, 0) != 0 && errno == ENOENT)
> break;
> }
>
> This only happens on a failure path. The same pattern exists in other ve
> selftests such as ve_ns_owner_test and ve_perms_test, so this patch adds
> another instance rather than a new class of problem.
>
> Could the cleanup be done on that failure path, for example by destroying
> the CT before asserting?
>
--
Best regards, Riabchun Vladimir
Linux Kernel Developer, Virtuozzo
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
prev parent reply other threads:[~2026-10-29 10:52 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-29 10:16 [Devel] [PATCH hci-8.1 v3 0/2] sched: Fix incorrect nproc report inside CT Vladimir Riabchun
2026-10-29 10:16 ` [Devel] [PATCH hci-8.1 v3 1/2] sched: Fix sched_getaffinity syscall Vladimir Riabchun
2026-10-29 10:21 ` no-reply
2026-10-29 10:48 ` Vladimir Riabchun
2026-10-29 10:16 ` [Devel] [PATCH hci-8.1 v3 2/2] selftests: ve: Test sched_getaffinity in VE Vladimir Riabchun
2026-10-29 10:22 ` no-reply
2026-10-29 10:50 ` Vladimir Riabchun [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ab62df52-5ded-43b7-a9a7-044e38863f9d@virtuozzo.com \
--to=vladimir.riabchun@virtuozzo.com \
--cc=devel@openvz.org \
--cc=khorenko@virtuozzo.com \
--cc=ptikhomirov@virtuozzo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.