xref: /freebsd/sys/compat/linux/linux_time.c (revision dd41de95a84d979615a2ef11df6850622bf6184e)
1 /*	$NetBSD: linux_time.c,v 1.14 2006/05/14 03:40:54 christos Exp $ */
2 
3 /*-
4  * SPDX-License-Identifier: BSD-2-Clause-NetBSD
5  *
6  * Copyright (c) 2001 The NetBSD Foundation, Inc.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to The NetBSD Foundation
10  * by Emmanuel Dreyfus.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 #if 0
37 __KERNEL_RCSID(0, "$NetBSD: linux_time.c,v 1.14 2006/05/14 03:40:54 christos Exp $");
38 #endif
39 
40 #include "opt_compat.h"
41 
42 #include <sys/param.h>
43 #include <sys/kernel.h>
44 #include <sys/lock.h>
45 #include <sys/ucred.h>
46 #include <sys/limits.h>
47 #include <sys/mount.h>
48 #include <sys/mutex.h>
49 #include <sys/resourcevar.h>
50 #include <sys/sdt.h>
51 #include <sys/signal.h>
52 #include <sys/stdint.h>
53 #include <sys/syscallsubr.h>
54 #include <sys/sysproto.h>
55 #include <sys/time.h>
56 #include <sys/systm.h>
57 #include <sys/proc.h>
58 
59 #ifdef COMPAT_LINUX32
60 #include <machine/../linux32/linux.h>
61 #include <machine/../linux32/linux32_proto.h>
62 #else
63 #include <machine/../linux/linux.h>
64 #include <machine/../linux/linux_proto.h>
65 #endif
66 
67 #include <compat/linux/linux_dtrace.h>
68 #include <compat/linux/linux_misc.h>
69 #include <compat/linux/linux_timer.h>
70 #include <compat/linux/linux_util.h>
71 
72 /* DTrace init */
73 LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE);
74 
75 /**
76  * DTrace probes in this module.
77  */
78 LIN_SDT_PROBE_DEFINE1(time, linux_to_native_clockid, unsupported_clockid,
79     "clockid_t");
80 LIN_SDT_PROBE_DEFINE1(time, linux_to_native_clockid, unknown_clockid,
81     "clockid_t");
82 LIN_SDT_PROBE_DEFINE1(time, linux_clock_gettime, conversion_error, "int");
83 LIN_SDT_PROBE_DEFINE1(time, linux_clock_gettime, gettime_error, "int");
84 LIN_SDT_PROBE_DEFINE1(time, linux_clock_gettime, copyout_error, "int");
85 LIN_SDT_PROBE_DEFINE1(time, linux_clock_settime, conversion_error, "int");
86 LIN_SDT_PROBE_DEFINE1(time, linux_clock_settime, settime_error, "int");
87 LIN_SDT_PROBE_DEFINE1(time, linux_clock_settime, copyin_error, "int");
88 LIN_SDT_PROBE_DEFINE0(time, linux_clock_getres, nullcall);
89 LIN_SDT_PROBE_DEFINE1(time, linux_clock_getres, conversion_error, "int");
90 LIN_SDT_PROBE_DEFINE1(time, linux_clock_getres, getres_error, "int");
91 LIN_SDT_PROBE_DEFINE1(time, linux_clock_getres, copyout_error, "int");
92 LIN_SDT_PROBE_DEFINE1(time, linux_nanosleep, conversion_error, "int");
93 LIN_SDT_PROBE_DEFINE1(time, linux_nanosleep, copyout_error, "int");
94 LIN_SDT_PROBE_DEFINE1(time, linux_nanosleep, copyin_error, "int");
95 LIN_SDT_PROBE_DEFINE1(time, linux_clock_nanosleep, conversion_error, "int");
96 LIN_SDT_PROBE_DEFINE1(time, linux_clock_nanosleep, copyout_error, "int");
97 LIN_SDT_PROBE_DEFINE1(time, linux_clock_nanosleep, copyin_error, "int");
98 LIN_SDT_PROBE_DEFINE1(time, linux_clock_nanosleep, unsupported_flags, "int");
99 LIN_SDT_PROBE_DEFINE1(time, linux_clock_nanosleep, unsupported_clockid, "int");
100 
101 int
102 native_to_linux_timespec(struct l_timespec *ltp, struct timespec *ntp)
103 {
104 
105 #ifdef COMPAT_LINUX32
106 	if (ntp->tv_sec > INT_MAX || ntp->tv_sec < INT_MIN)
107 		return (EOVERFLOW);
108 #endif
109 	ltp->tv_sec = ntp->tv_sec;
110 	ltp->tv_nsec = ntp->tv_nsec;
111 
112 	return (0);
113 }
114 
115 int
116 linux_to_native_timespec(struct timespec *ntp, struct l_timespec *ltp)
117 {
118 
119 	if (ltp->tv_sec < 0 || ltp->tv_nsec < 0 || ltp->tv_nsec > 999999999)
120 		return (EINVAL);
121 	ntp->tv_sec = ltp->tv_sec;
122 	ntp->tv_nsec = ltp->tv_nsec;
123 
124 	return (0);
125 }
126 
127 int
128 native_to_linux_itimerspec(struct l_itimerspec *ltp, struct itimerspec *ntp)
129 {
130 	int error;
131 
132 	error = native_to_linux_timespec(&ltp->it_interval, &ntp->it_interval);
133 	if (error == 0)
134 		error = native_to_linux_timespec(&ltp->it_value, &ntp->it_interval);
135 	return (error);
136 }
137 
138 int
139 linux_to_native_itimerspec(struct itimerspec *ntp, struct l_itimerspec *ltp)
140 {
141 	int error;
142 
143 	error = linux_to_native_timespec(&ntp->it_interval, &ltp->it_interval);
144 	if (error == 0)
145 		error = linux_to_native_timespec(&ntp->it_value, &ltp->it_value);
146 	return (error);
147 }
148 
149 int
150 linux_to_native_clockid(clockid_t *n, clockid_t l)
151 {
152 
153 	if (l < 0) {
154 		/* cpu-clock */
155 		if ((l & LINUX_CLOCKFD_MASK) == LINUX_CLOCKFD)
156 			return (EINVAL);
157 		if (LINUX_CPUCLOCK_WHICH(l) >= LINUX_CPUCLOCK_MAX)
158 			return (EINVAL);
159 
160 		if (LINUX_CPUCLOCK_PERTHREAD(l))
161 			*n = CLOCK_THREAD_CPUTIME_ID;
162 		else
163 			*n = CLOCK_PROCESS_CPUTIME_ID;
164 		return (0);
165 	}
166 
167 	switch (l) {
168 	case LINUX_CLOCK_REALTIME:
169 		*n = CLOCK_REALTIME;
170 		break;
171 	case LINUX_CLOCK_MONOTONIC:
172 		*n = CLOCK_MONOTONIC;
173 		break;
174 	case LINUX_CLOCK_PROCESS_CPUTIME_ID:
175 		*n = CLOCK_PROCESS_CPUTIME_ID;
176 		break;
177 	case LINUX_CLOCK_THREAD_CPUTIME_ID:
178 		*n = CLOCK_THREAD_CPUTIME_ID;
179 		break;
180 	case LINUX_CLOCK_REALTIME_COARSE:
181 		*n = CLOCK_REALTIME_FAST;
182 		break;
183 	case LINUX_CLOCK_MONOTONIC_COARSE:
184 	case LINUX_CLOCK_MONOTONIC_RAW:
185 		*n = CLOCK_MONOTONIC_FAST;
186 		break;
187 	case LINUX_CLOCK_BOOTTIME:
188 		*n = CLOCK_UPTIME;
189 		break;
190 	case LINUX_CLOCK_REALTIME_ALARM:
191 	case LINUX_CLOCK_BOOTTIME_ALARM:
192 	case LINUX_CLOCK_SGI_CYCLE:
193 	case LINUX_CLOCK_TAI:
194 		LIN_SDT_PROBE1(time, linux_to_native_clockid,
195 		    unsupported_clockid, l);
196 		return (EINVAL);
197 	default:
198 		LIN_SDT_PROBE1(time, linux_to_native_clockid,
199 		    unknown_clockid, l);
200 		return (EINVAL);
201 	}
202 
203 	return (0);
204 }
205 
206 int
207 linux_to_native_timerflags(int *nflags, int flags)
208 {
209 
210 	if (flags & ~LINUX_TIMER_ABSTIME)
211 		return (EINVAL);
212 	*nflags = 0;
213 	if (flags & LINUX_TIMER_ABSTIME)
214 		*nflags |= TIMER_ABSTIME;
215 	return (0);
216 }
217 
218 int
219 linux_clock_gettime(struct thread *td, struct linux_clock_gettime_args *args)
220 {
221 	struct l_timespec lts;
222 	struct timespec tp;
223 	struct rusage ru;
224 	struct thread *targettd;
225 	struct proc *p;
226 	int error, clockwhich;
227 	clockid_t nwhich;
228 	pid_t pid;
229 	lwpid_t tid;
230 
231 	error = linux_to_native_clockid(&nwhich, args->which);
232 	if (error != 0) {
233 		linux_msg(curthread,
234 		    "unsupported clock_gettime clockid %d", args->which);
235 		LIN_SDT_PROBE1(time, linux_clock_gettime, conversion_error,
236 		    error);
237 		return (error);
238 	}
239 
240 	switch (nwhich) {
241 	case CLOCK_PROCESS_CPUTIME_ID:
242 		if (args->which < 0) {
243 			clockwhich = LINUX_CPUCLOCK_WHICH(args->which);
244 			pid = LINUX_CPUCLOCK_ID(args->which);
245 		} else {
246 			clockwhich = LINUX_CPUCLOCK_SCHED;
247 			pid = 0;
248 		}
249 		if (pid == 0) {
250 			p = td->td_proc;
251 			PROC_LOCK(p);
252 		} else {
253 			error = pget(pid, PGET_CANSEE, &p);
254 			if (error != 0)
255 				return (EINVAL);
256 		}
257 		switch (clockwhich) {
258 		case LINUX_CPUCLOCK_PROF:
259 			PROC_STATLOCK(p);
260 			calcru(p, &ru.ru_utime, &ru.ru_stime);
261 			PROC_STATUNLOCK(p);
262 			PROC_UNLOCK(p);
263 			timevaladd(&ru.ru_utime, &ru.ru_stime);
264 			TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &tp);
265 			break;
266 		case LINUX_CPUCLOCK_VIRT:
267 			PROC_STATLOCK(p);
268 			calcru(p, &ru.ru_utime, &ru.ru_stime);
269 			PROC_STATUNLOCK(p);
270 			PROC_UNLOCK(p);
271 			TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &tp);
272 			break;
273 		case LINUX_CPUCLOCK_SCHED:
274 			kern_process_cputime(p, &tp);
275 			PROC_UNLOCK(p);
276 			break;
277 		default:
278 			PROC_UNLOCK(p);
279 			return (EINVAL);
280 		}
281 
282 		break;
283 
284 	case CLOCK_THREAD_CPUTIME_ID:
285 		if (args->which < 0) {
286 			clockwhich = LINUX_CPUCLOCK_WHICH(args->which);
287 			tid = LINUX_CPUCLOCK_ID(args->which);
288 		} else {
289 			clockwhich = LINUX_CPUCLOCK_SCHED;
290 			tid = 0;
291 		}
292 		p = td->td_proc;
293 		if (tid == 0) {
294 			targettd = td;
295 			PROC_LOCK(p);
296 		} else {
297 			targettd = linux_tdfind(td, tid, p->p_pid);
298 			if (targettd == NULL)
299 				return (EINVAL);
300 		}
301 		switch (clockwhich) {
302 		case LINUX_CPUCLOCK_PROF:
303 			PROC_STATLOCK(p);
304 			thread_lock(targettd);
305 			rufetchtd(targettd, &ru);
306 			thread_unlock(targettd);
307 			PROC_STATUNLOCK(p);
308 			PROC_UNLOCK(p);
309 			timevaladd(&ru.ru_utime, &ru.ru_stime);
310 			TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &tp);
311 			break;
312 		case LINUX_CPUCLOCK_VIRT:
313 			PROC_STATLOCK(p);
314 			thread_lock(targettd);
315 			rufetchtd(targettd, &ru);
316 			thread_unlock(targettd);
317 			PROC_STATUNLOCK(p);
318 			PROC_UNLOCK(p);
319 			TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &tp);
320 			break;
321 		case LINUX_CPUCLOCK_SCHED:
322 			if (td == targettd)
323 				targettd = NULL;
324 			kern_thread_cputime(targettd, &tp);
325 			PROC_UNLOCK(p);
326 			break;
327 		default:
328 			PROC_UNLOCK(p);
329 			return (EINVAL);
330 		}
331 		break;
332 
333 	default:
334 		error = kern_clock_gettime(td, nwhich, &tp);
335 		break;
336 	}
337 	if (error != 0) {
338 		LIN_SDT_PROBE1(time, linux_clock_gettime, gettime_error, error);
339 		return (error);
340 	}
341 	error = native_to_linux_timespec(&lts, &tp);
342 	if (error != 0)
343 		return (error);
344 	error = copyout(&lts, args->tp, sizeof lts);
345 	if (error != 0)
346 		LIN_SDT_PROBE1(time, linux_clock_gettime, copyout_error, error);
347 
348 	return (error);
349 }
350 
351 int
352 linux_clock_settime(struct thread *td, struct linux_clock_settime_args *args)
353 {
354 	struct timespec ts;
355 	struct l_timespec lts;
356 	int error;
357 	clockid_t nwhich;
358 
359 	error = linux_to_native_clockid(&nwhich, args->which);
360 	if (error != 0) {
361 		linux_msg(curthread,
362 		    "unsupported clock_settime clockid %d", args->which);
363 		LIN_SDT_PROBE1(time, linux_clock_settime, conversion_error,
364 		    error);
365 		return (error);
366 	}
367 	error = copyin(args->tp, &lts, sizeof lts);
368 	if (error != 0) {
369 		LIN_SDT_PROBE1(time, linux_clock_settime, copyin_error, error);
370 		return (error);
371 	}
372 	error = linux_to_native_timespec(&ts, &lts);
373 	if (error != 0) {
374 		LIN_SDT_PROBE1(time, linux_clock_settime, conversion_error,
375 		    error);
376 		return (error);
377 	}
378 
379 	error = kern_clock_settime(td, nwhich, &ts);
380 	if (error != 0)
381 		LIN_SDT_PROBE1(time, linux_clock_settime, settime_error, error);
382 
383 	return (error);
384 }
385 
386 int
387 linux_clock_getres(struct thread *td, struct linux_clock_getres_args *args)
388 {
389 	struct proc *p;
390 	struct timespec ts;
391 	struct l_timespec lts;
392 	int error, clockwhich;
393 	clockid_t nwhich;
394 	pid_t pid;
395 	lwpid_t tid;
396 
397 	error = linux_to_native_clockid(&nwhich, args->which);
398 	if (error != 0) {
399 		linux_msg(curthread,
400 		    "unsupported clock_getres clockid %d", args->which);
401 		LIN_SDT_PROBE1(time, linux_clock_getres, conversion_error,
402 		    error);
403 		return (error);
404 	}
405 
406 	/*
407 	 * Check user supplied clock id in case of per-process
408 	 * or thread-specific cpu-time clock.
409 	 */
410 	if (args->which < 0) {
411 		switch (nwhich) {
412 		case CLOCK_THREAD_CPUTIME_ID:
413 			tid = LINUX_CPUCLOCK_ID(args->which);
414 			if (tid != 0) {
415 				p = td->td_proc;
416 				if (linux_tdfind(td, tid, p->p_pid) == NULL)
417 					return (EINVAL);
418 				PROC_UNLOCK(p);
419 			}
420 			break;
421 		case CLOCK_PROCESS_CPUTIME_ID:
422 			pid = LINUX_CPUCLOCK_ID(args->which);
423 			if (pid != 0) {
424 				error = pget(pid, PGET_CANSEE, &p);
425 				if (error != 0)
426 					return (EINVAL);
427 				PROC_UNLOCK(p);
428 			}
429 			break;
430 		}
431 	}
432 
433 	if (args->tp == NULL) {
434 		LIN_SDT_PROBE0(time, linux_clock_getres, nullcall);
435 		return (0);
436 	}
437 
438 	switch (nwhich) {
439 	case CLOCK_THREAD_CPUTIME_ID:
440 	case CLOCK_PROCESS_CPUTIME_ID:
441 		clockwhich = LINUX_CPUCLOCK_WHICH(args->which);
442 		/*
443 		 * In both cases (when the clock id obtained by a call to
444 		 * clock_getcpuclockid() or using the clock
445 		 * ID CLOCK_PROCESS_CPUTIME_ID Linux hardcodes precision
446 		 * of clock. The same for the CLOCK_THREAD_CPUTIME_ID clock.
447 		 *
448 		 * See Linux posix_cpu_clock_getres() implementation.
449 		 */
450 		if (args->which > 0 || clockwhich == LINUX_CPUCLOCK_SCHED) {
451 			ts.tv_sec = 0;
452 			ts.tv_nsec = 1;
453 			goto out;
454 		}
455 
456 		switch (clockwhich) {
457 		case LINUX_CPUCLOCK_PROF:
458 			nwhich = CLOCK_PROF;
459 			break;
460 		case LINUX_CPUCLOCK_VIRT:
461 			nwhich = CLOCK_VIRTUAL;
462 			break;
463 		default:
464 			return (EINVAL);
465 		}
466 		break;
467 
468 	default:
469 		break;
470 	}
471 	error = kern_clock_getres(td, nwhich, &ts);
472 	if (error != 0) {
473 		LIN_SDT_PROBE1(time, linux_clock_getres, getres_error, error);
474 		return (error);
475 	}
476 
477 out:
478 	error = native_to_linux_timespec(&lts, &ts);
479 	if (error != 0)
480 		return (error);
481 	error = copyout(&lts, args->tp, sizeof lts);
482 	if (error != 0)
483 		LIN_SDT_PROBE1(time, linux_clock_getres, copyout_error, error);
484 
485 	return (error);
486 }
487 
488 int
489 linux_nanosleep(struct thread *td, struct linux_nanosleep_args *args)
490 {
491 	struct timespec *rmtp;
492 	struct l_timespec lrqts, lrmts;
493 	struct timespec rqts, rmts;
494 	int error, error2;
495 
496 	error = copyin(args->rqtp, &lrqts, sizeof lrqts);
497 	if (error != 0) {
498 		LIN_SDT_PROBE1(time, linux_nanosleep, copyin_error, error);
499 		return (error);
500 	}
501 
502 	if (args->rmtp != NULL)
503 		rmtp = &rmts;
504 	else
505 		rmtp = NULL;
506 
507 	error = linux_to_native_timespec(&rqts, &lrqts);
508 	if (error != 0) {
509 		LIN_SDT_PROBE1(time, linux_nanosleep, conversion_error, error);
510 		return (error);
511 	}
512 	error = kern_nanosleep(td, &rqts, rmtp);
513 	if (error == EINTR && args->rmtp != NULL) {
514 		error2 = native_to_linux_timespec(&lrmts, rmtp);
515 		if (error2 != 0)
516 			return (error2);
517 		error2 = copyout(&lrmts, args->rmtp, sizeof(lrmts));
518 		if (error2 != 0) {
519 			LIN_SDT_PROBE1(time, linux_nanosleep, copyout_error,
520 			    error2);
521 			return (error2);
522 		}
523 	}
524 
525 	return (error);
526 }
527 
528 int
529 linux_clock_nanosleep(struct thread *td, struct linux_clock_nanosleep_args *args)
530 {
531 	struct timespec *rmtp;
532 	struct l_timespec lrqts, lrmts;
533 	struct timespec rqts, rmts;
534 	int error, error2, flags;
535 	clockid_t clockid;
536 
537 	error = linux_to_native_timerflags(&flags, args->flags);
538 	if (error != 0) {
539 		LIN_SDT_PROBE1(time, linux_clock_nanosleep, unsupported_flags,
540 		    args->flags);
541 		return (error);
542 	}
543 
544 	error = linux_to_native_clockid(&clockid, args->which);
545 	if (error != 0) {
546 		linux_msg(curthread,
547 		    "unsupported clock_nanosleep clockid %d", args->which);
548 		LIN_SDT_PROBE1(time, linux_clock_nanosleep, unsupported_clockid,
549 		    args->which);
550 		return (error);
551 	}
552 
553 	error = copyin(args->rqtp, &lrqts, sizeof(lrqts));
554 	if (error != 0) {
555 		LIN_SDT_PROBE1(time, linux_clock_nanosleep, copyin_error,
556 		    error);
557 		return (error);
558 	}
559 
560 	if (args->rmtp != NULL)
561 		rmtp = &rmts;
562 	else
563 		rmtp = NULL;
564 
565 	error = linux_to_native_timespec(&rqts, &lrqts);
566 	if (error != 0) {
567 		LIN_SDT_PROBE1(time, linux_clock_nanosleep, conversion_error,
568 		    error);
569 		return (error);
570 	}
571 	error = kern_clock_nanosleep(td, clockid, flags, &rqts, rmtp);
572 	if (error == EINTR && (flags & TIMER_ABSTIME) == 0 &&
573 	    args->rmtp != NULL) {
574 		error2 = native_to_linux_timespec(&lrmts, rmtp);
575 		if (error2 != 0)
576 			return (error2);
577 		error2 = copyout(&lrmts, args->rmtp, sizeof(lrmts));
578 		if (error2 != 0) {
579 			LIN_SDT_PROBE1(time, linux_clock_nanosleep,
580 			    copyout_error, error2);
581 			return (error2);
582 		}
583 	}
584 
585 	return (error);
586 }
587