[LTP] [PATCH 1/3] lib/tst_pid: Find cgroup pid.max programmatically
Teo Couprie Diaz
teo.coupriediaz@arm.com
Wed Mar 1 11:19:41 CET 2023
Hello Richard,
Thanks for taking the time to have a look !
On 28/02/2023 13:50, Richard Palethorpe wrote:
> Hello,
>
> Teo Couprie Diaz <teo.coupriediaz@arm.com> writes:
>
>> In some distributions, the two files used in lib/tst_pid.c are not
>> available, but cgroups still imposes a task limit far smaller than
>> the kernel pid_max.
>> If the cgroup sysfs is mounted, we can use it to retrieve the current task
>> limit imposed to the process. Implement the retrieval of this limit.
>>
>> This can be done by first checking /proc/self/cgroup to get the cgroup
>> the process is in, which will be a path under the cgroup sysfs.
>> To get the path to the cgroup sysfs, check /proc/self/mountinfo.
>> Finally, concatenate those two paths with pid.max to get the full path
>> to the file containing the limit.
>>
>> This patch changes the way read_session_pids_limit is called, not passing
>> a format string to be completed anymore, but is still used the same way.
>> A following patch will update this function.
>>
>> This fixes failures for msgstress04.
>>
>> Signed-off-by: Teo Couprie Diaz <teo.coupriediaz@arm.com>
>> ---
>> lib/tst_pid.c | 53 +++++++++++++++++++++++++++++++++++++++++----------
>> 1 file changed, 43 insertions(+), 10 deletions(-)
>>
>> diff --git a/lib/tst_pid.c b/lib/tst_pid.c
>> index 5595e79bd..3d0be6dcd 100644
>> --- a/lib/tst_pid.c
>> +++ b/lib/tst_pid.c
>> @@ -32,8 +32,6 @@
>>
>> #define PID_MAX_PATH "/proc/sys/kernel/pid_max"
>> #define THREADS_MAX_PATH "/proc/sys/kernel/threads-max"
>> -#define CGROUPS_V1_SLICE_FMT "/sys/fs/cgroup/pids/user.slice/user-%d.slice/pids.max"
>> -#define CGROUPS_V2_SLICE_FMT "/sys/fs/cgroup/user.slice/user-%d.slice/pids.max"
>> /* Leave some available processes for the OS */
>> #define PIDS_RESERVE 50
>>
>> @@ -97,18 +95,53 @@ static int read_session_pids_limit(const char *path_fmt, int uid,
>>
>> static int get_session_pids_limit(void (*cleanup_fn) (void))
>> {
>> - int max_pids, uid;
>> + char path[PATH_MAX + 1];
>> + char cgroup_pids[PATH_MAX + 1];
>> + char catchall;
>> + int uid, ret = 0;
>>
>> uid = get_session_uid();
>> - max_pids = read_session_pids_limit(CGROUPS_V2_SLICE_FMT, uid, cleanup_fn);
>> - if (max_pids < 0)
>> - max_pids = read_session_pids_limit(CGROUPS_V1_SLICE_FMT, uid,
>> - cleanup_fn);
>> + /* Check for generic cgroup v1 pid.max */
>> + ret = FILE_LINES_SCANF(cleanup_fn, "/proc/self/cgroup",
>> + "%*d:pids:%s\n", cgroup_pids);
>> + /*
>> + * This is a bit of a hack of scanf format strings. Indeed, if all
>> + * conversion specifications have been matched the return of scanf will be
>> + * the same whether any outstanding literal characters match or not.
>> + * As we want to match the literal part, we can add a catchall after it
>> + * so that it won't be counted if the literal part doesn't match.
>> + * This makes the macro go to the next line until the catchall, thus
>> + * the literal parts, is matched.
>> + *
>> + * Assume that the root of the mount is '/'. It can be anything,
>> + * but it should be '/' on any normal system.
>> + */
>> + if (!ret)
>> + ret = FILE_LINES_SCANF(cleanup_fn, "/proc/self/mountinfo",
>> + "%*s %*s %*s %*s %s %*[^-] - cgroup %*s %*[rw],pid%c",
>> + path,
>> &catchall);
> Uhhff, I already implemented this logic in tst_cg_scan in tst_cgroup. In
> there we scan the current CGroup hierarchy and build a data structure
> which represents it.
>
> I guess you are not aware of tst_cgroup?
Indeed, it appears that I either missed it or misunderstood it while
researching. That's on me, it is indeed already covered there.
>
>> +
>> + if (!ret) {
>> + strncat(path, cgroup_pids, PATH_MAX);
>> + strncat(path, "/pids.max", PATH_MAX);
>> + return read_session_pids_limit(path, uid, cleanup_fn);
>> + }
>>
>> - if (max_pids < 0)
>> - return -1;
>> + /* Check for generic cgroup v2 pid.max */
>> + ret = FILE_LINES_SCANF(cleanup_fn, "/proc/self/cgroup",
>> + "%*d::%s\n",
>> cgroup_pids);
> This has not been added to tst_cgroup because usually tests that need a
> cgroup feature are moved to a cgroup created by LTP. We also check that
> any required CGroups are available and mount them if necessary.
Interesting, that does make sense when testing cgroups and would make it
much easier.
As I mentioned in the cover letter this patch series was motivated by
msgstress03 and msgstress04 which do not set up a cgroup, they just try
to read their current limit. This is why I went to the trouble of
looking for the cgroup directly.
>
> I suppose in this case we do not care if there is no CGroup hierarchy or
> the pids controller is absent?
That is the case indeed : if there's no cgroup hierarchy or pid
controller, it would use the kernel PID limit directly instead.
This cannot be done by default because if there is indeed a cgroup
hierarchy and pid controller, the forks will probably fail far before
the kernel pid limit is hit.
>
> In any case I think tst_cgroup should be used or extended.
I agree, hopefully I can find some time to rework this series to make
use of tst_cgroup as much as possible.
My apologies for the noise and thanks again for the review.
Best regards,
Téo
>
>> + if (!ret)
>> + ret = FILE_LINES_SCANF(cleanup_fn, "/proc/self/mountinfo",
>> + "%*s %*s %*s %*s %s %*[^-] - cgroup2 %c",
>> + path, &catchall);
>> +
>> + if (!ret) {
>> + strncat(path, cgroup_pids, PATH_MAX);
>> + strncat(path, "/pids.max", PATH_MAX);
>> + return read_session_pids_limit(path, uid, cleanup_fn);
>> + }
>>
>> - return max_pids;
>> + return -1;
>> }
>>
>> int tst_get_free_pids_(void (*cleanup_fn) (void))
>> --
>> 2.25.1
>
More information about the ltp
mailing list