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$"); 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 40 #include <sys/param.h> 41 #include <sys/kernel.h> 42 #include <sys/lock.h> 43 #include <sys/ucred.h> 44 #include <sys/mount.h> 45 #include <sys/mutex.h> 46 #include <sys/resourcevar.h> 47 #include <sys/sdt.h> 48 #include <sys/signal.h> 49 #include <sys/stdint.h> 50 #include <sys/syscallsubr.h> 51 #include <sys/sysproto.h> 52 #include <sys/time.h> 53 #include <sys/systm.h> 54 #include <sys/proc.h> 55 56 #ifdef COMPAT_LINUX32 57 #include <machine/../linux32/linux.h> 58 #include <machine/../linux32/linux32_proto.h> 59 #else 60 #include <machine/../linux/linux.h> 61 #include <machine/../linux/linux_proto.h> 62 #endif 63 64 #include <compat/linux/linux_dtrace.h> 65 #include <compat/linux/linux_timer.h> 66 67 /* DTrace init */ 68 LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); 69 70 /** 71 * DTrace probes in this module. 72 */ 73 LIN_SDT_PROBE_DEFINE2(time, native_to_linux_timespec, entry, 74 "struct l_timespec *", "struct timespec *"); 75 LIN_SDT_PROBE_DEFINE0(time, native_to_linux_timespec, return); 76 LIN_SDT_PROBE_DEFINE2(time, linux_to_native_timespec, entry, 77 "struct timespec *", "struct l_timespec *"); 78 LIN_SDT_PROBE_DEFINE1(time, linux_to_native_timespec, return, "int"); 79 LIN_SDT_PROBE_DEFINE2(time, linux_to_native_clockid, entry, "clockid_t *", 80 "clockid_t"); 81 LIN_SDT_PROBE_DEFINE1(time, linux_to_native_clockid, unsupported_clockid, 82 "clockid_t"); 83 LIN_SDT_PROBE_DEFINE1(time, linux_to_native_clockid, unknown_clockid, 84 "clockid_t"); 85 LIN_SDT_PROBE_DEFINE1(time, linux_to_native_clockid, return, "int"); 86 LIN_SDT_PROBE_DEFINE2(time, linux_clock_gettime, entry, "clockid_t", 87 "struct l_timespec *"); 88 LIN_SDT_PROBE_DEFINE1(time, linux_clock_gettime, conversion_error, "int"); 89 LIN_SDT_PROBE_DEFINE1(time, linux_clock_gettime, gettime_error, "int"); 90 LIN_SDT_PROBE_DEFINE1(time, linux_clock_gettime, copyout_error, "int"); 91 LIN_SDT_PROBE_DEFINE1(time, linux_clock_gettime, return, "int"); 92 LIN_SDT_PROBE_DEFINE2(time, linux_clock_settime, entry, "clockid_t", 93 "struct l_timespec *"); 94 LIN_SDT_PROBE_DEFINE1(time, linux_clock_settime, conversion_error, "int"); 95 LIN_SDT_PROBE_DEFINE1(time, linux_clock_settime, settime_error, "int"); 96 LIN_SDT_PROBE_DEFINE1(time, linux_clock_settime, copyin_error, "int"); 97 LIN_SDT_PROBE_DEFINE1(time, linux_clock_settime, return, "int"); 98 LIN_SDT_PROBE_DEFINE2(time, linux_clock_getres, entry, "clockid_t", 99 "struct l_timespec *"); 100 LIN_SDT_PROBE_DEFINE0(time, linux_clock_getres, nullcall); 101 LIN_SDT_PROBE_DEFINE1(time, linux_clock_getres, conversion_error, "int"); 102 LIN_SDT_PROBE_DEFINE1(time, linux_clock_getres, getres_error, "int"); 103 LIN_SDT_PROBE_DEFINE1(time, linux_clock_getres, copyout_error, "int"); 104 LIN_SDT_PROBE_DEFINE1(time, linux_clock_getres, return, "int"); 105 LIN_SDT_PROBE_DEFINE2(time, linux_nanosleep, entry, "const struct l_timespec *", 106 "struct l_timespec *"); 107 LIN_SDT_PROBE_DEFINE1(time, linux_nanosleep, conversion_error, "int"); 108 LIN_SDT_PROBE_DEFINE1(time, linux_nanosleep, copyout_error, "int"); 109 LIN_SDT_PROBE_DEFINE1(time, linux_nanosleep, copyin_error, "int"); 110 LIN_SDT_PROBE_DEFINE1(time, linux_nanosleep, return, "int"); 111 LIN_SDT_PROBE_DEFINE4(time, linux_clock_nanosleep, entry, "clockid_t", "int", 112 "struct l_timespec *", "struct l_timespec *"); 113 LIN_SDT_PROBE_DEFINE1(time, linux_clock_nanosleep, conversion_error, "int"); 114 LIN_SDT_PROBE_DEFINE1(time, linux_clock_nanosleep, copyout_error, "int"); 115 LIN_SDT_PROBE_DEFINE1(time, linux_clock_nanosleep, copyin_error, "int"); 116 LIN_SDT_PROBE_DEFINE1(time, linux_clock_nanosleep, unsupported_flags, "int"); 117 LIN_SDT_PROBE_DEFINE1(time, linux_clock_nanosleep, unsupported_clockid, "int"); 118 LIN_SDT_PROBE_DEFINE1(time, linux_clock_nanosleep, return, "int"); 119 120 121 void 122 native_to_linux_timespec(struct l_timespec *ltp, struct timespec *ntp) 123 { 124 125 LIN_SDT_PROBE2(time, native_to_linux_timespec, entry, ltp, ntp); 126 127 ltp->tv_sec = ntp->tv_sec; 128 ltp->tv_nsec = ntp->tv_nsec; 129 130 LIN_SDT_PROBE0(time, native_to_linux_timespec, return); 131 } 132 133 int 134 linux_to_native_timespec(struct timespec *ntp, struct l_timespec *ltp) 135 { 136 137 LIN_SDT_PROBE2(time, linux_to_native_timespec, entry, ntp, ltp); 138 139 if (ltp->tv_sec < 0 || ltp->tv_nsec > (l_long)999999999L) { 140 LIN_SDT_PROBE1(time, linux_to_native_timespec, return, EINVAL); 141 return (EINVAL); 142 } 143 ntp->tv_sec = ltp->tv_sec; 144 ntp->tv_nsec = ltp->tv_nsec; 145 146 LIN_SDT_PROBE1(time, linux_to_native_timespec, return, 0); 147 return (0); 148 } 149 150 int 151 linux_to_native_clockid(clockid_t *n, clockid_t l) 152 { 153 154 LIN_SDT_PROBE2(time, linux_to_native_clockid, entry, n, l); 155 156 if (l < 0) { 157 /* cpu-clock */ 158 if ((l & LINUX_CLOCKFD_MASK) == LINUX_CLOCKFD) 159 return (EINVAL); 160 if (LINUX_CPUCLOCK_WHICH(l) >= LINUX_CPUCLOCK_MAX) 161 return (EINVAL); 162 163 if (LINUX_CPUCLOCK_PERTHREAD(l)) 164 *n = CLOCK_THREAD_CPUTIME_ID; 165 else 166 *n = CLOCK_PROCESS_CPUTIME_ID; 167 return (0); 168 } 169 170 switch (l) { 171 case LINUX_CLOCK_REALTIME: 172 *n = CLOCK_REALTIME; 173 break; 174 case LINUX_CLOCK_MONOTONIC: 175 *n = CLOCK_MONOTONIC; 176 break; 177 case LINUX_CLOCK_REALTIME_COARSE: 178 *n = CLOCK_REALTIME_FAST; 179 break; 180 case LINUX_CLOCK_MONOTONIC_COARSE: 181 *n = CLOCK_MONOTONIC_FAST; 182 break; 183 case LINUX_CLOCK_MONOTONIC_RAW: 184 case LINUX_CLOCK_BOOTTIME: 185 case LINUX_CLOCK_REALTIME_ALARM: 186 case LINUX_CLOCK_BOOTTIME_ALARM: 187 case LINUX_CLOCK_SGI_CYCLE: 188 case LINUX_CLOCK_TAI: 189 LIN_SDT_PROBE1(time, linux_to_native_clockid, 190 unsupported_clockid, l); 191 LIN_SDT_PROBE1(time, linux_to_native_clockid, return, EINVAL); 192 return (EINVAL); 193 default: 194 LIN_SDT_PROBE1(time, linux_to_native_clockid, 195 unknown_clockid, l); 196 LIN_SDT_PROBE1(time, linux_to_native_clockid, return, EINVAL); 197 return (EINVAL); 198 } 199 200 LIN_SDT_PROBE1(time, linux_to_native_clockid, return, 0); 201 return (0); 202 } 203 204 int 205 linux_clock_gettime(struct thread *td, struct linux_clock_gettime_args *args) 206 { 207 struct l_timespec lts; 208 struct timespec tp; 209 struct rusage ru; 210 struct thread *targettd; 211 struct proc *p; 212 int error, clockwhich; 213 clockid_t nwhich = 0; /* XXX: GCC */ 214 pid_t pid; 215 lwpid_t tid; 216 217 LIN_SDT_PROBE2(time, linux_clock_gettime, entry, args->which, args->tp); 218 219 error = linux_to_native_clockid(&nwhich, args->which); 220 if (error != 0) { 221 LIN_SDT_PROBE1(time, linux_clock_gettime, conversion_error, 222 error); 223 LIN_SDT_PROBE1(time, linux_clock_gettime, return, error); 224 return (error); 225 } 226 227 switch (nwhich) { 228 case CLOCK_PROCESS_CPUTIME_ID: 229 clockwhich = LINUX_CPUCLOCK_WHICH(args->which); 230 pid = LINUX_CPUCLOCK_ID(args->which); 231 if (pid == 0) { 232 p = td->td_proc; 233 PROC_LOCK(p); 234 } else { 235 error = pget(pid, PGET_CANSEE, &p); 236 if (error != 0) 237 return (EINVAL); 238 } 239 switch (clockwhich) { 240 case LINUX_CPUCLOCK_PROF: 241 PROC_STATLOCK(p); 242 calcru(p, &ru.ru_utime, &ru.ru_stime); 243 PROC_STATUNLOCK(p); 244 PROC_UNLOCK(p); 245 timevaladd(&ru.ru_utime, &ru.ru_stime); 246 TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &tp); 247 break; 248 case LINUX_CPUCLOCK_VIRT: 249 PROC_STATLOCK(p); 250 calcru(p, &ru.ru_utime, &ru.ru_stime); 251 PROC_STATUNLOCK(p); 252 PROC_UNLOCK(p); 253 TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &tp); 254 break; 255 case LINUX_CPUCLOCK_SCHED: 256 PROC_UNLOCK(p); 257 error = kern_clock_getcpuclockid2(td, pid, 258 CPUCLOCK_WHICH_PID, &nwhich); 259 if (error != 0) 260 return (EINVAL); 261 error = kern_clock_gettime(td, nwhich, &tp); 262 break; 263 default: 264 PROC_UNLOCK(p); 265 return (EINVAL); 266 } 267 268 break; 269 270 case CLOCK_THREAD_CPUTIME_ID: 271 clockwhich = LINUX_CPUCLOCK_WHICH(args->which); 272 p = td->td_proc; 273 tid = LINUX_CPUCLOCK_ID(args->which); 274 if (tid == 0) { 275 targettd = td; 276 PROC_LOCK(p); 277 } else { 278 targettd = tdfind(tid, p->p_pid); 279 if (targettd == NULL) 280 return (EINVAL); 281 } 282 switch (clockwhich) { 283 case LINUX_CPUCLOCK_PROF: 284 PROC_STATLOCK(p); 285 thread_lock(targettd); 286 rufetchtd(targettd, &ru); 287 thread_unlock(targettd); 288 PROC_STATUNLOCK(p); 289 PROC_UNLOCK(p); 290 timevaladd(&ru.ru_utime, &ru.ru_stime); 291 TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &tp); 292 break; 293 case LINUX_CPUCLOCK_VIRT: 294 PROC_STATLOCK(p); 295 thread_lock(targettd); 296 rufetchtd(targettd, &ru); 297 thread_unlock(targettd); 298 PROC_STATUNLOCK(p); 299 PROC_UNLOCK(p); 300 TIMEVAL_TO_TIMESPEC(&ru.ru_utime, &tp); 301 break; 302 case LINUX_CPUCLOCK_SCHED: 303 error = kern_clock_getcpuclockid2(td, tid, 304 CPUCLOCK_WHICH_TID, &nwhich); 305 PROC_UNLOCK(p); 306 if (error != 0) 307 return (EINVAL); 308 error = kern_clock_gettime(td, nwhich, &tp); 309 break; 310 default: 311 PROC_UNLOCK(p); 312 return (EINVAL); 313 } 314 break; 315 316 default: 317 error = kern_clock_gettime(td, nwhich, &tp); 318 break; 319 } 320 if (error != 0) { 321 LIN_SDT_PROBE1(time, linux_clock_gettime, gettime_error, error); 322 LIN_SDT_PROBE1(time, linux_clock_gettime, return, error); 323 return (error); 324 } 325 native_to_linux_timespec(<s, &tp); 326 327 error = copyout(<s, args->tp, sizeof lts); 328 if (error != 0) 329 LIN_SDT_PROBE1(time, linux_clock_gettime, copyout_error, error); 330 331 LIN_SDT_PROBE1(time, linux_clock_gettime, return, error); 332 return (error); 333 } 334 335 int 336 linux_clock_settime(struct thread *td, struct linux_clock_settime_args *args) 337 { 338 struct timespec ts; 339 struct l_timespec lts; 340 int error; 341 clockid_t nwhich = 0; /* XXX: GCC */ 342 343 LIN_SDT_PROBE2(time, linux_clock_settime, entry, args->which, args->tp); 344 345 error = linux_to_native_clockid(&nwhich, args->which); 346 if (error != 0) { 347 LIN_SDT_PROBE1(time, linux_clock_settime, conversion_error, 348 error); 349 LIN_SDT_PROBE1(time, linux_clock_settime, return, error); 350 return (error); 351 } 352 error = copyin(args->tp, <s, sizeof lts); 353 if (error != 0) { 354 LIN_SDT_PROBE1(time, linux_clock_settime, copyin_error, error); 355 LIN_SDT_PROBE1(time, linux_clock_settime, return, error); 356 return (error); 357 } 358 error = linux_to_native_timespec(&ts, <s); 359 if (error != 0) { 360 LIN_SDT_PROBE1(time, linux_clock_settime, conversion_error, 361 error); 362 LIN_SDT_PROBE1(time, linux_clock_settime, return, error); 363 return (error); 364 } 365 366 error = kern_clock_settime(td, nwhich, &ts); 367 if (error != 0) 368 LIN_SDT_PROBE1(time, linux_clock_settime, settime_error, error); 369 370 LIN_SDT_PROBE1(time, linux_clock_settime, return, error); 371 return (error); 372 } 373 374 int 375 linux_clock_getres(struct thread *td, struct linux_clock_getres_args *args) 376 { 377 struct proc *p; 378 struct timespec ts; 379 struct l_timespec lts; 380 int error, clockwhich; 381 clockid_t nwhich = 0; /* XXX: GCC */ 382 pid_t pid; 383 lwpid_t tid; 384 385 LIN_SDT_PROBE2(time, linux_clock_getres, entry, args->which, args->tp); 386 387 error = linux_to_native_clockid(&nwhich, args->which); 388 if (error != 0) { 389 LIN_SDT_PROBE1(time, linux_clock_getres, conversion_error, 390 error); 391 LIN_SDT_PROBE1(time, linux_clock_getres, return, error); 392 return (error); 393 } 394 395 /* 396 * Check user supplied clock id in case of per-process 397 * or thread-specific cpu-time clock. 398 */ 399 switch (nwhich) { 400 case CLOCK_THREAD_CPUTIME_ID: 401 tid = LINUX_CPUCLOCK_ID(args->which); 402 if (tid != 0) { 403 p = td->td_proc; 404 if (tdfind(tid, p->p_pid) == NULL) 405 return (ESRCH); 406 PROC_UNLOCK(p); 407 } 408 break; 409 case CLOCK_PROCESS_CPUTIME_ID: 410 pid = LINUX_CPUCLOCK_ID(args->which); 411 if (pid != 0) { 412 error = pget(pid, PGET_CANSEE, &p); 413 if (error != 0) 414 return (EINVAL); 415 PROC_UNLOCK(p); 416 } 417 break; 418 } 419 420 if (args->tp == NULL) { 421 LIN_SDT_PROBE0(time, linux_clock_getres, nullcall); 422 LIN_SDT_PROBE1(time, linux_clock_getres, return, 0); 423 return (0); 424 } 425 426 switch (nwhich) { 427 case CLOCK_THREAD_CPUTIME_ID: 428 case CLOCK_PROCESS_CPUTIME_ID: 429 clockwhich = LINUX_CPUCLOCK_WHICH(args->which); 430 switch (clockwhich) { 431 case LINUX_CPUCLOCK_PROF: 432 nwhich = CLOCK_PROF; 433 break; 434 case LINUX_CPUCLOCK_VIRT: 435 nwhich = CLOCK_VIRTUAL; 436 break; 437 case LINUX_CPUCLOCK_SCHED: 438 break; 439 default: 440 return (EINVAL); 441 } 442 break; 443 444 default: 445 break; 446 } 447 error = kern_clock_getres(td, nwhich, &ts); 448 if (error != 0) { 449 LIN_SDT_PROBE1(time, linux_clock_getres, getres_error, error); 450 LIN_SDT_PROBE1(time, linux_clock_getres, return, error); 451 return (error); 452 } 453 native_to_linux_timespec(<s, &ts); 454 455 error = copyout(<s, args->tp, sizeof lts); 456 if (error != 0) 457 LIN_SDT_PROBE1(time, linux_clock_getres, copyout_error, error); 458 459 LIN_SDT_PROBE1(time, linux_clock_getres, return, error); 460 return (error); 461 } 462 463 int 464 linux_nanosleep(struct thread *td, struct linux_nanosleep_args *args) 465 { 466 struct timespec *rmtp; 467 struct l_timespec lrqts, lrmts; 468 struct timespec rqts, rmts; 469 int error, error2; 470 471 LIN_SDT_PROBE2(time, linux_nanosleep, entry, args->rqtp, args->rmtp); 472 473 error = copyin(args->rqtp, &lrqts, sizeof lrqts); 474 if (error != 0) { 475 LIN_SDT_PROBE1(time, linux_nanosleep, copyin_error, error); 476 LIN_SDT_PROBE1(time, linux_nanosleep, return, error); 477 return (error); 478 } 479 480 if (args->rmtp != NULL) 481 rmtp = &rmts; 482 else 483 rmtp = NULL; 484 485 error = linux_to_native_timespec(&rqts, &lrqts); 486 if (error != 0) { 487 LIN_SDT_PROBE1(time, linux_nanosleep, conversion_error, error); 488 LIN_SDT_PROBE1(time, linux_nanosleep, return, error); 489 return (error); 490 } 491 error = kern_nanosleep(td, &rqts, rmtp); 492 if (args->rmtp != NULL) { 493 native_to_linux_timespec(&lrmts, rmtp); 494 error2 = copyout(&lrmts, args->rmtp, sizeof(lrmts)); 495 if (error2 != 0) { 496 LIN_SDT_PROBE1(time, linux_nanosleep, copyout_error, 497 error2); 498 LIN_SDT_PROBE1(time, linux_nanosleep, return, error2); 499 return (error2); 500 } 501 } 502 503 LIN_SDT_PROBE1(time, linux_nanosleep, return, error); 504 return (error); 505 } 506 507 int 508 linux_clock_nanosleep(struct thread *td, struct linux_clock_nanosleep_args *args) 509 { 510 struct timespec *rmtp; 511 struct l_timespec lrqts, lrmts; 512 struct timespec rqts, rmts; 513 int error, error2; 514 515 LIN_SDT_PROBE4(time, linux_clock_nanosleep, entry, args->which, 516 args->flags, args->rqtp, args->rmtp); 517 518 if (args->flags != 0) { 519 /* XXX deal with TIMER_ABSTIME */ 520 LIN_SDT_PROBE1(time, linux_clock_nanosleep, unsupported_flags, 521 args->flags); 522 LIN_SDT_PROBE1(time, linux_clock_nanosleep, return, EINVAL); 523 return (EINVAL); /* XXX deal with TIMER_ABSTIME */ 524 } 525 526 if (args->which != LINUX_CLOCK_REALTIME) { 527 LIN_SDT_PROBE1(time, linux_clock_nanosleep, unsupported_clockid, 528 args->which); 529 LIN_SDT_PROBE1(time, linux_clock_nanosleep, return, EINVAL); 530 return (EINVAL); 531 } 532 533 error = copyin(args->rqtp, &lrqts, sizeof(lrqts)); 534 if (error != 0) { 535 LIN_SDT_PROBE1(time, linux_clock_nanosleep, copyin_error, 536 error); 537 LIN_SDT_PROBE1(time, linux_clock_nanosleep, return, error); 538 return (error); 539 } 540 541 if (args->rmtp != NULL) 542 rmtp = &rmts; 543 else 544 rmtp = NULL; 545 546 error = linux_to_native_timespec(&rqts, &lrqts); 547 if (error != 0) { 548 LIN_SDT_PROBE1(time, linux_clock_nanosleep, conversion_error, 549 error); 550 LIN_SDT_PROBE1(time, linux_clock_nanosleep, return, error); 551 return (error); 552 } 553 error = kern_nanosleep(td, &rqts, rmtp); 554 if (args->rmtp != NULL) { 555 /* XXX. Not for TIMER_ABSTIME */ 556 native_to_linux_timespec(&lrmts, rmtp); 557 error2 = copyout(&lrmts, args->rmtp, sizeof(lrmts)); 558 if (error2 != 0) { 559 LIN_SDT_PROBE1(time, linux_clock_nanosleep, 560 copyout_error, error2); 561 LIN_SDT_PROBE1(time, linux_clock_nanosleep, 562 return, error2); 563 return (error2); 564 } 565 } 566 567 LIN_SDT_PROBE1(time, linux_clock_nanosleep, return, error); 568 return (error); 569 } 570