vclock_gettime.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /*
  2. * Copyright 2006 Andi Kleen, SUSE Labs.
  3. * Subject to the GNU Public License, v.2
  4. *
  5. * Fast user context implementation of clock_gettime and gettimeofday.
  6. *
  7. * The code should have no internal unresolved relocations.
  8. * Check with readelf after changing.
  9. * Also alternative() doesn't work.
  10. */
  11. #include <linux/kernel.h>
  12. #include <linux/posix-timers.h>
  13. #include <linux/time.h>
  14. #include <linux/string.h>
  15. #include <asm/vsyscall.h>
  16. #include <asm/vgtod.h>
  17. #include <asm/timex.h>
  18. #include <asm/hpet.h>
  19. #include <asm/unistd.h>
  20. #include <asm/io.h>
  21. #include "vextern.h"
  22. #define gtod vdso_vsyscall_gtod_data
  23. static long vdso_fallback_gettime(long clock, struct timespec *ts)
  24. {
  25. long ret;
  26. asm("syscall" : "=a" (ret) :
  27. "0" (__NR_clock_gettime),"D" (clock), "S" (ts) : "memory");
  28. return ret;
  29. }
  30. static inline long vgetns(void)
  31. {
  32. long v;
  33. cycles_t (*vread)(void);
  34. vread = gtod->clock.vread;
  35. v = (vread() - gtod->clock.cycle_last) & gtod->clock.mask;
  36. return (v * gtod->clock.mult) >> gtod->clock.shift;
  37. }
  38. static noinline int do_realtime(struct timespec *ts)
  39. {
  40. unsigned long seq, ns;
  41. do {
  42. seq = read_seqbegin(&gtod->lock);
  43. ts->tv_sec = gtod->wall_time_sec;
  44. ts->tv_nsec = gtod->wall_time_nsec;
  45. ns = vgetns();
  46. } while (unlikely(read_seqretry(&gtod->lock, seq)));
  47. timespec_add_ns(ts, ns);
  48. return 0;
  49. }
  50. /* Copy of the version in kernel/time.c which we cannot directly access */
  51. static void vset_normalized_timespec(struct timespec *ts, long sec, long nsec)
  52. {
  53. while (nsec >= NSEC_PER_SEC) {
  54. nsec -= NSEC_PER_SEC;
  55. ++sec;
  56. }
  57. while (nsec < 0) {
  58. nsec += NSEC_PER_SEC;
  59. --sec;
  60. }
  61. ts->tv_sec = sec;
  62. ts->tv_nsec = nsec;
  63. }
  64. static noinline int do_monotonic(struct timespec *ts)
  65. {
  66. unsigned long seq, ns, secs;
  67. do {
  68. seq = read_seqbegin(&gtod->lock);
  69. secs = gtod->wall_time_sec;
  70. ns = gtod->wall_time_nsec + vgetns();
  71. secs += gtod->wall_to_monotonic.tv_sec;
  72. ns += gtod->wall_to_monotonic.tv_nsec;
  73. } while (unlikely(read_seqretry(&gtod->lock, seq)));
  74. vset_normalized_timespec(ts, secs, ns);
  75. return 0;
  76. }
  77. int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
  78. {
  79. if (likely(gtod->sysctl_enabled && gtod->clock.vread))
  80. switch (clock) {
  81. case CLOCK_REALTIME:
  82. return do_realtime(ts);
  83. case CLOCK_MONOTONIC:
  84. return do_monotonic(ts);
  85. }
  86. return vdso_fallback_gettime(clock, ts);
  87. }
  88. int clock_gettime(clockid_t, struct timespec *)
  89. __attribute__((weak, alias("__vdso_clock_gettime")));
  90. int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
  91. {
  92. long ret;
  93. if (likely(gtod->sysctl_enabled && gtod->clock.vread)) {
  94. BUILD_BUG_ON(offsetof(struct timeval, tv_usec) !=
  95. offsetof(struct timespec, tv_nsec) ||
  96. sizeof(*tv) != sizeof(struct timespec));
  97. do_realtime((struct timespec *)tv);
  98. tv->tv_usec /= 1000;
  99. if (unlikely(tz != NULL)) {
  100. /* This relies on gcc inlining the memcpy. We'll notice
  101. if it ever fails to do so. */
  102. memcpy(tz, &gtod->sys_tz, sizeof(struct timezone));
  103. }
  104. return 0;
  105. }
  106. asm("syscall" : "=a" (ret) :
  107. "0" (__NR_gettimeofday), "D" (tv), "S" (tz) : "memory");
  108. return ret;
  109. }
  110. int gettimeofday(struct timeval *, struct timezone *)
  111. __attribute__((weak, alias("__vdso_gettimeofday")));