1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved.
4 * Author: Xiao Yang <yangx.jy@cn.fujitsu.com>
5 */
6
7/*
8 * Description:
9 * Set CPU time limit for a process and check its behavior
10 * after reaching CPU time limit.
11 * 1) Process got SIGXCPU after reaching soft limit of CPU time.
12 * 2) Process got SIGKILL after reaching hard limit of CPU time.
13 *
14 * Note:
15 * This is also a regression test for the following kernel bug:
16 * 'c3bca5d450b62 ("posix-cpu-timers: Ensure set_process_cpu_timer is always evaluated")'
17 */
18
19#define _GNU_SOURCE
20#include <errno.h>
21#include <sys/types.h>
22#include <unistd.h>
23#include <sys/time.h>
24#include <sys/resource.h>
25#include <sys/wait.h>
26#include <stdlib.h>
27#include <sys/mman.h>
28
29#include "tst_test.h"
30
31static int *end;
32
33static void sighandler(int sig)
34{
35	*end = sig;
36}
37
38static void setup(void)
39{
40	SAFE_SIGNAL(SIGXCPU, sighandler);
41
42	end = SAFE_MMAP(NULL, sizeof(int), PROT_READ | PROT_WRITE,
43			MAP_SHARED | MAP_ANONYMOUS, -1, 0);
44}
45
46static void cleanup(void)
47{
48	if (end)
49		SAFE_MUNMAP(end, sizeof(int));
50}
51
52static void verify_setrlimit(void)
53{
54	int status;
55	pid_t pid;
56
57	*end = 0;
58
59	pid = SAFE_FORK();
60	if (!pid) {
61		struct rlimit rlim = {
62			.rlim_cur = 1,
63			.rlim_max = 2,
64		};
65
66		TEST(setrlimit(RLIMIT_CPU, &rlim));
67		if (TST_RET == -1) {
68			tst_res(TFAIL | TTERRNO,
69				"setrlimit(RLIMIT_CPU) failed");
70			exit(1);
71		}
72
73		alarm(20);
74
75		while (1);
76	}
77
78	SAFE_WAITPID(pid, &status, 0);
79
80	if (WIFEXITED(status) && WEXITSTATUS(status) == 1)
81		return;
82
83	if (WIFSIGNALED(status)) {
84		if (WTERMSIG(status) == SIGKILL && *end == SIGXCPU) {
85			tst_res(TPASS,
86				"Got SIGXCPU then SIGKILL after reaching both limit");
87			return;
88		}
89
90		if (WTERMSIG(status) == SIGKILL && !*end) {
91			tst_res(TFAIL,
92				"Got only SIGKILL after reaching both limit");
93			return;
94		}
95
96		if (WTERMSIG(status) == SIGALRM && *end == SIGXCPU) {
97			tst_res(TFAIL,
98				"Got only SIGXCPU after reaching both limit");
99			return;
100		}
101
102		if (WTERMSIG(status) == SIGALRM && !*end) {
103			tst_res(TFAIL,
104				"Got no signal after reaching both limit");
105			return;
106		}
107	}
108
109	tst_res(TFAIL, "Child %s", tst_strstatus(status));
110}
111
112static struct tst_test test = {
113	.test_all = verify_setrlimit,
114	.setup = setup,
115	.cleanup = cleanup,
116	.forks_child = 1,
117	.tags = (const struct tst_tag[]) {
118		{"linux-git", "c3bca5d450b62"},
119		{}
120	}
121};
122