time.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /*
  2. * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
  3. * Licensed under the GPL
  4. */
  5. #include "linux/interrupt.h"
  6. #include "linux/jiffies.h"
  7. #include "linux/threads.h"
  8. #include "asm/irq.h"
  9. #include "asm/param.h"
  10. #include "kern_util.h"
  11. #include "os.h"
  12. /*
  13. * Scheduler clock - returns current time in nanosec units.
  14. */
  15. unsigned long long sched_clock(void)
  16. {
  17. return (unsigned long long)jiffies_64 * (1000000000 / HZ);
  18. }
  19. #ifdef CONFIG_UML_REAL_TIME_CLOCK
  20. static unsigned long long prev_nsecs[NR_CPUS];
  21. static long long delta[NR_CPUS]; /* Deviation per interval */
  22. #endif
  23. void timer_irq(struct uml_pt_regs *regs)
  24. {
  25. unsigned long long ticks = 0;
  26. #ifdef CONFIG_UML_REAL_TIME_CLOCK
  27. int c = cpu();
  28. if (prev_nsecs[c]) {
  29. /* We've had 1 tick */
  30. unsigned long long nsecs = os_nsecs();
  31. delta[c] += nsecs - prev_nsecs[c];
  32. prev_nsecs[c] = nsecs;
  33. /* Protect against the host clock being set backwards */
  34. if (delta[c] < 0)
  35. delta[c] = 0;
  36. ticks += (delta[c] * HZ) / BILLION;
  37. delta[c] -= (ticks * BILLION) / HZ;
  38. }
  39. else prev_nsecs[c] = os_nsecs();
  40. #else
  41. ticks = 1;
  42. #endif
  43. while (ticks > 0) {
  44. do_IRQ(TIMER_IRQ, regs);
  45. ticks--;
  46. }
  47. }
  48. /* Protects local_offset */
  49. static DEFINE_SPINLOCK(timer_spinlock);
  50. static unsigned long long local_offset = 0;
  51. static inline unsigned long long get_time(void)
  52. {
  53. unsigned long long nsecs;
  54. unsigned long flags;
  55. spin_lock_irqsave(&timer_spinlock, flags);
  56. nsecs = os_nsecs();
  57. nsecs += local_offset;
  58. spin_unlock_irqrestore(&timer_spinlock, flags);
  59. return nsecs;
  60. }
  61. irqreturn_t um_timer(int irq, void *dev)
  62. {
  63. unsigned long long nsecs;
  64. unsigned long flags;
  65. write_seqlock_irqsave(&xtime_lock, flags);
  66. do_timer(1);
  67. #ifdef CONFIG_UML_REAL_TIME_CLOCK
  68. nsecs = get_time();
  69. #else
  70. nsecs = (unsigned long long) xtime.tv_sec * BILLION + xtime.tv_nsec +
  71. BILLION / HZ;
  72. #endif
  73. xtime.tv_sec = nsecs / NSEC_PER_SEC;
  74. xtime.tv_nsec = nsecs - xtime.tv_sec * NSEC_PER_SEC;
  75. write_sequnlock_irqrestore(&xtime_lock, flags);
  76. return IRQ_HANDLED;
  77. }
  78. static void register_timer(void)
  79. {
  80. int err;
  81. err = request_irq(TIMER_IRQ, um_timer, IRQF_DISABLED, "timer", NULL);
  82. if (err != 0)
  83. printk(KERN_ERR "register_timer : request_irq failed - "
  84. "errno = %d\n", -err);
  85. err = set_interval();
  86. if (err != 0)
  87. printk(KERN_ERR "register_timer : set_interval failed - "
  88. "errno = %d\n", -err);
  89. }
  90. extern void (*late_time_init)(void);
  91. void time_init(void)
  92. {
  93. long long nsecs;
  94. nsecs = os_nsecs();
  95. set_normalized_timespec(&wall_to_monotonic, -nsecs / BILLION,
  96. -nsecs % BILLION);
  97. set_normalized_timespec(&xtime, nsecs / BILLION, nsecs % BILLION);
  98. late_time_init = register_timer;
  99. }
  100. void do_gettimeofday(struct timeval *tv)
  101. {
  102. #ifdef CONFIG_UML_REAL_TIME_CLOCK
  103. unsigned long long nsecs = get_time();
  104. #else
  105. unsigned long long nsecs = (unsigned long long) xtime.tv_sec * BILLION +
  106. xtime.tv_nsec;
  107. #endif
  108. tv->tv_sec = nsecs / NSEC_PER_SEC;
  109. /*
  110. * Careful about calculations here - this was originally done as
  111. * (nsecs - tv->tv_sec * NSEC_PER_SEC) / NSEC_PER_USEC
  112. * which gave bogus (> 1000000) values. Dunno why, suspect gcc
  113. * (4.0.0) miscompiled it, or there's a subtle 64/32-bit conversion
  114. * problem that I missed.
  115. */
  116. nsecs -= tv->tv_sec * NSEC_PER_SEC;
  117. tv->tv_usec = (unsigned long) nsecs / NSEC_PER_USEC;
  118. }
  119. static inline void set_time(unsigned long long nsecs)
  120. {
  121. unsigned long long now;
  122. unsigned long flags;
  123. spin_lock_irqsave(&timer_spinlock, flags);
  124. now = os_nsecs();
  125. local_offset = nsecs - now;
  126. spin_unlock_irqrestore(&timer_spinlock, flags);
  127. clock_was_set();
  128. }
  129. int do_settimeofday(struct timespec *tv)
  130. {
  131. set_time((unsigned long long) tv->tv_sec * NSEC_PER_SEC + tv->tv_nsec);
  132. return 0;
  133. }
  134. void timer_handler(int sig, struct uml_pt_regs *regs)
  135. {
  136. if (current_thread->cpu == 0)
  137. timer_irq(regs);
  138. local_irq_disable();
  139. irq_enter();
  140. update_process_times(regs->is_user);
  141. irq_exit();
  142. local_irq_enable();
  143. }