xref: /linux/tools/testing/selftests/timers/nanosleep.c (revision fcc8487d477a3452a1d0ccbdd4c5e0e1e3cb8bed)
1 /* Make sure timers don't return early
2  *              by: john stultz (johnstul@us.ibm.com)
3  *		    John Stultz (john.stultz@linaro.org)
4  *              (C) Copyright IBM 2012
5  *              (C) Copyright Linaro 2013 2015
6  *              Licensed under the GPLv2
7  *
8  *  To build:
9  *	$ gcc nanosleep.c -o nanosleep -lrt
10  *
11  *   This program is free software: you can redistribute it and/or modify
12  *   it under the terms of the GNU General Public License as published by
13  *   the Free Software Foundation, either version 2 of the License, or
14  *   (at your option) any later version.
15  *
16  *   This program is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU General Public License for more details.
20  */
21 
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <time.h>
26 #include <sys/time.h>
27 #include <sys/timex.h>
28 #include <string.h>
29 #include <signal.h>
30 #ifdef KTEST
31 #include "../kselftest.h"
32 #else
33 static inline int ksft_exit_pass(void)
34 {
35 	exit(0);
36 }
37 static inline int ksft_exit_fail(void)
38 {
39 	exit(1);
40 }
41 #endif
42 
43 #define NSEC_PER_SEC 1000000000ULL
44 
45 #define CLOCK_REALTIME			0
46 #define CLOCK_MONOTONIC			1
47 #define CLOCK_PROCESS_CPUTIME_ID	2
48 #define CLOCK_THREAD_CPUTIME_ID		3
49 #define CLOCK_MONOTONIC_RAW		4
50 #define CLOCK_REALTIME_COARSE		5
51 #define CLOCK_MONOTONIC_COARSE		6
52 #define CLOCK_BOOTTIME			7
53 #define CLOCK_REALTIME_ALARM		8
54 #define CLOCK_BOOTTIME_ALARM		9
55 #define CLOCK_HWSPECIFIC		10
56 #define CLOCK_TAI			11
57 #define NR_CLOCKIDS			12
58 
59 #define UNSUPPORTED 0xf00f
60 
61 char *clockstring(int clockid)
62 {
63 	switch (clockid) {
64 	case CLOCK_REALTIME:
65 		return "CLOCK_REALTIME";
66 	case CLOCK_MONOTONIC:
67 		return "CLOCK_MONOTONIC";
68 	case CLOCK_PROCESS_CPUTIME_ID:
69 		return "CLOCK_PROCESS_CPUTIME_ID";
70 	case CLOCK_THREAD_CPUTIME_ID:
71 		return "CLOCK_THREAD_CPUTIME_ID";
72 	case CLOCK_MONOTONIC_RAW:
73 		return "CLOCK_MONOTONIC_RAW";
74 	case CLOCK_REALTIME_COARSE:
75 		return "CLOCK_REALTIME_COARSE";
76 	case CLOCK_MONOTONIC_COARSE:
77 		return "CLOCK_MONOTONIC_COARSE";
78 	case CLOCK_BOOTTIME:
79 		return "CLOCK_BOOTTIME";
80 	case CLOCK_REALTIME_ALARM:
81 		return "CLOCK_REALTIME_ALARM";
82 	case CLOCK_BOOTTIME_ALARM:
83 		return "CLOCK_BOOTTIME_ALARM";
84 	case CLOCK_TAI:
85 		return "CLOCK_TAI";
86 	};
87 	return "UNKNOWN_CLOCKID";
88 }
89 
90 /* returns 1 if a <= b, 0 otherwise */
91 static inline int in_order(struct timespec a, struct timespec b)
92 {
93 	if (a.tv_sec < b.tv_sec)
94 		return 1;
95 	if (a.tv_sec > b.tv_sec)
96 		return 0;
97 	if (a.tv_nsec > b.tv_nsec)
98 		return 0;
99 	return 1;
100 }
101 
102 struct timespec timespec_add(struct timespec ts, unsigned long long ns)
103 {
104 	ts.tv_nsec += ns;
105 	while (ts.tv_nsec >= NSEC_PER_SEC) {
106 		ts.tv_nsec -= NSEC_PER_SEC;
107 		ts.tv_sec++;
108 	}
109 	return ts;
110 }
111 
112 int nanosleep_test(int clockid, long long ns)
113 {
114 	struct timespec now, target, rel;
115 
116 	/* First check abs time */
117 	if (clock_gettime(clockid, &now))
118 		return UNSUPPORTED;
119 	target = timespec_add(now, ns);
120 
121 	if (clock_nanosleep(clockid, TIMER_ABSTIME, &target, NULL))
122 		return UNSUPPORTED;
123 	clock_gettime(clockid, &now);
124 
125 	if (!in_order(target, now))
126 		return -1;
127 
128 	/* Second check reltime */
129 	clock_gettime(clockid, &now);
130 	rel.tv_sec = 0;
131 	rel.tv_nsec = 0;
132 	rel = timespec_add(rel, ns);
133 	target = timespec_add(now, ns);
134 	clock_nanosleep(clockid, 0, &rel, NULL);
135 	clock_gettime(clockid, &now);
136 
137 	if (!in_order(target, now))
138 		return -1;
139 	return 0;
140 }
141 
142 int main(int argc, char **argv)
143 {
144 	long long length;
145 	int clockid, ret;
146 
147 	for (clockid = CLOCK_REALTIME; clockid < NR_CLOCKIDS; clockid++) {
148 
149 		/* Skip cputime clockids since nanosleep won't increment cputime */
150 		if (clockid == CLOCK_PROCESS_CPUTIME_ID ||
151 				clockid == CLOCK_THREAD_CPUTIME_ID ||
152 				clockid == CLOCK_HWSPECIFIC)
153 			continue;
154 
155 		printf("Nanosleep %-31s ", clockstring(clockid));
156 
157 		length = 10;
158 		while (length <= (NSEC_PER_SEC * 10)) {
159 			ret = nanosleep_test(clockid, length);
160 			if (ret == UNSUPPORTED) {
161 				printf("[UNSUPPORTED]\n");
162 				goto next;
163 			}
164 			if (ret < 0) {
165 				printf("[FAILED]\n");
166 				return ksft_exit_fail();
167 			}
168 			length *= 100;
169 		}
170 		printf("[OK]\n");
171 next:
172 		ret = 0;
173 	}
174 	return ksft_exit_pass();
175 }
176