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