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(<p->it_interval, &ntp->it_interval); 133 if (error == 0) 134 error = native_to_linux_timespec(<p->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, <p->it_interval); 144 if (error == 0) 145 error = linux_to_native_timespec(&ntp->it_value, <p->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(<s, &tp); 342 if (error != 0) 343 return (error); 344 error = copyout(<s, 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, <s, 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, <s); 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(<s, &ts); 479 if (error != 0) 480 return (error); 481 error = copyout(<s, 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