/* * Copyright (c) 2016 Linux Test Project. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * 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 (TEST_RETURN) 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(TBROK | TERRNO, "ERROR sched_setscheduler: (0, SCHED_OTHER, param)"); if (sched_getscheduler(0) != SCHED_OTHER) tst_res(TBROK | 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 };