169 lines
3.8 KiB
C
169 lines
3.8 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (c) 2016 Linux Test Project.
|
|
*/
|
|
|
|
/*
|
|
* A regression test for can_nice call usage in sched_setscheduler,
|
|
* introduced by kernel commit:
|
|
* d50dde5a (sched: Add new scheduler syscalls to support
|
|
*
|
|
* This was fixed by below commit:
|
|
* eaad4513 (sched: Fix __sched_setscheduler() nice test
|
|
*
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <pwd.h>
|
|
#include <sched.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/wait.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "tst_test.h"
|
|
|
|
#define RLIMIT_NICE_NORMAL 20
|
|
|
|
static pid_t zero_pid;
|
|
static struct sched_param param[1] = { {0} };
|
|
|
|
struct test_case_t {
|
|
pid_t *pid;
|
|
int policy;
|
|
struct sched_param *sched_param;
|
|
int error;
|
|
};
|
|
|
|
struct test_case_t cases[] = {
|
|
{
|
|
.pid = &zero_pid,
|
|
.policy = SCHED_OTHER,
|
|
.sched_param = ¶m[0]
|
|
},
|
|
{
|
|
.pid = &zero_pid,
|
|
.policy = SCHED_BATCH,
|
|
.sched_param = ¶m[0]
|
|
},
|
|
#ifdef SCHED_IDLE
|
|
{
|
|
.pid = &zero_pid,
|
|
.policy = SCHED_IDLE,
|
|
.sched_param = ¶m[0]
|
|
}
|
|
#endif
|
|
};
|
|
|
|
static void l_rlimit_show(const int type, struct rlimit *limit)
|
|
{
|
|
SAFE_GETRLIMIT(type, limit);
|
|
tst_res(TINFO,
|
|
"rlimit rlim_cur=%lu", (unsigned long)(limit->rlim_cur));
|
|
tst_res(TINFO,
|
|
"rlimit rlim_max=%lu", (unsigned long)(limit->rlim_max));
|
|
}
|
|
|
|
static void l_rlimit_setup(const int type, struct rlimit *limit)
|
|
{
|
|
struct rlimit tmp_rlimit;
|
|
|
|
tst_res(TINFO,
|
|
"Setting rlim_cur to %lu", (unsigned long)(limit->rlim_cur));
|
|
tst_res(TINFO,
|
|
"Setting rlim_max to %lu", (unsigned long)(limit->rlim_max));
|
|
|
|
SAFE_SETRLIMIT(type, limit);
|
|
|
|
l_rlimit_show(RLIMIT_NICE, &tmp_rlimit);
|
|
|
|
if (tmp_rlimit.rlim_cur != limit->rlim_cur)
|
|
tst_brk(TBROK | TERRNO, "Expect rlim_cur = %lu, get %lu",
|
|
(unsigned long)(limit->rlim_cur),
|
|
(unsigned long)tmp_rlimit.rlim_cur);
|
|
|
|
if (tmp_rlimit.rlim_max != limit->rlim_max)
|
|
tst_brk(TBROK | TERRNO, "Expect rlim_max = %lu, get %lu",
|
|
(unsigned long)(limit->rlim_max),
|
|
(unsigned long)(tmp_rlimit.rlim_max));
|
|
}
|
|
|
|
static void verify_fn(unsigned int i)
|
|
{
|
|
tst_res(TINFO,
|
|
"Verifying case[%d]: policy = %d, priority = %d",
|
|
i + 1, cases[i].policy, cases[i].sched_param->sched_priority);
|
|
|
|
TEST(sched_setscheduler(*cases[i].pid, cases[i].policy,
|
|
cases[i].sched_param));
|
|
if (TST_RET)
|
|
tst_res(TFAIL | TTERRNO, "case[%d] expected: %d, got: ",
|
|
i + 1, cases[i].error);
|
|
else
|
|
tst_res(TPASS, "case[%d] succeeded", i + 1);
|
|
}
|
|
|
|
static void setup(void)
|
|
{
|
|
uid_t ruid, euid, suid;
|
|
struct rlimit limit;
|
|
struct passwd *pw;
|
|
uid_t nobody_uid;
|
|
|
|
pw = SAFE_GETPWNAM("nobody");
|
|
nobody_uid = pw->pw_uid;
|
|
l_rlimit_show(RLIMIT_NICE, &limit);
|
|
|
|
/*
|
|
* nice rlimit ranges from 1 to 40, mapping to real nice
|
|
* value from 19 to -20. We set it to 19, as the default priority
|
|
* of process with fair policy is 120, which will be translated
|
|
* into nice 20, we make this RLIMIT_NICE smaller than that, to
|
|
* verify the can_nice usage issue.
|
|
*/
|
|
limit.rlim_cur = (RLIMIT_NICE_NORMAL - 1);
|
|
limit.rlim_max = (RLIMIT_NICE_NORMAL - 1);
|
|
|
|
l_rlimit_setup(RLIMIT_NICE, &limit);
|
|
|
|
tst_res(TINFO, "Setting init sched policy to SCHED_OTHER");
|
|
if (sched_setscheduler(0, SCHED_OTHER, ¶m[0]) != 0)
|
|
tst_res(TFAIL | TERRNO,
|
|
"ERROR sched_setscheduler: (0, SCHED_OTHER, param)");
|
|
|
|
if (sched_getscheduler(0) != SCHED_OTHER)
|
|
tst_res(TFAIL | TERRNO, "ERROR sched_setscheduler");
|
|
|
|
tst_res(TINFO, "Setting euid to nobody to drop privilege");
|
|
|
|
SAFE_SETEUID(nobody_uid);
|
|
SAFE_GETRESUID(&ruid, &euid, &suid);
|
|
if (euid != nobody_uid)
|
|
tst_brk(TBROK | TERRNO, "ERROR seteuid(nobody_uid)");
|
|
}
|
|
|
|
static void do_test(unsigned int i)
|
|
{
|
|
int status = 0;
|
|
pid_t f_pid = SAFE_FORK();
|
|
|
|
if (f_pid == 0) {
|
|
tst_res(TINFO, "forked pid is %d", getpid());
|
|
verify_fn(i);
|
|
exit(0);
|
|
}
|
|
|
|
SAFE_WAIT(&status);
|
|
}
|
|
|
|
static struct tst_test test = {
|
|
.tcnt = ARRAY_SIZE(cases),
|
|
.test = do_test,
|
|
.setup = setup,
|
|
.needs_root = 1,
|
|
.forks_child = 1
|
|
};
|
|
|