diff options
Diffstat (limited to 'kernel/timer.c')
-rw-r--r-- | kernel/timer.c | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/kernel/timer.c b/kernel/timer.c index 6811436a031d..e5adb9e2e7a7 100644 --- a/kernel/timer.c +++ b/kernel/timer.c | |||
@@ -795,6 +795,169 @@ u64 current_tick_length(long shift) | |||
795 | #include <linux/clocksource.h> | 795 | #include <linux/clocksource.h> |
796 | static struct clocksource *clock; /* pointer to current clocksource */ | 796 | static struct clocksource *clock; /* pointer to current clocksource */ |
797 | static cycle_t last_clock_cycle; /* cycle value at last update_wall_time */ | 797 | static cycle_t last_clock_cycle; /* cycle value at last update_wall_time */ |
798 | |||
799 | #ifdef CONFIG_GENERIC_TIME | ||
800 | /** | ||
801 | * __get_nsec_offset - Returns nanoseconds since last call to periodic_hook | ||
802 | * | ||
803 | * private function, must hold xtime_lock lock when being | ||
804 | * called. Returns the number of nanoseconds since the | ||
805 | * last call to update_wall_time() (adjusted by NTP scaling) | ||
806 | */ | ||
807 | static inline s64 __get_nsec_offset(void) | ||
808 | { | ||
809 | cycle_t cycle_now, cycle_delta; | ||
810 | s64 ns_offset; | ||
811 | |||
812 | /* read clocksource: */ | ||
813 | cycle_now = read_clocksource(clock); | ||
814 | |||
815 | /* calculate the delta since the last update_wall_time: */ | ||
816 | cycle_delta = (cycle_now - last_clock_cycle) & clock->mask; | ||
817 | |||
818 | /* convert to nanoseconds: */ | ||
819 | ns_offset = cyc2ns(clock, cycle_delta); | ||
820 | |||
821 | return ns_offset; | ||
822 | } | ||
823 | |||
824 | /** | ||
825 | * __get_realtime_clock_ts - Returns the time of day in a timespec | ||
826 | * @ts: pointer to the timespec to be set | ||
827 | * | ||
828 | * Returns the time of day in a timespec. Used by | ||
829 | * do_gettimeofday() and get_realtime_clock_ts(). | ||
830 | */ | ||
831 | static inline void __get_realtime_clock_ts(struct timespec *ts) | ||
832 | { | ||
833 | unsigned long seq; | ||
834 | s64 nsecs; | ||
835 | |||
836 | do { | ||
837 | seq = read_seqbegin(&xtime_lock); | ||
838 | |||
839 | *ts = xtime; | ||
840 | nsecs = __get_nsec_offset(); | ||
841 | |||
842 | } while (read_seqretry(&xtime_lock, seq)); | ||
843 | |||
844 | timespec_add_ns(ts, nsecs); | ||
845 | } | ||
846 | |||
847 | /** | ||
848 | * get_realtime_clock_ts - Returns the time of day in a timespec | ||
849 | * @ts: pointer to the timespec to be set | ||
850 | * | ||
851 | * Returns the time of day in a timespec. | ||
852 | */ | ||
853 | void getnstimeofday(struct timespec *ts) | ||
854 | { | ||
855 | __get_realtime_clock_ts(ts); | ||
856 | } | ||
857 | |||
858 | EXPORT_SYMBOL(getnstimeofday); | ||
859 | |||
860 | /** | ||
861 | * do_gettimeofday - Returns the time of day in a timeval | ||
862 | * @tv: pointer to the timeval to be set | ||
863 | * | ||
864 | * NOTE: Users should be converted to using get_realtime_clock_ts() | ||
865 | */ | ||
866 | void do_gettimeofday(struct timeval *tv) | ||
867 | { | ||
868 | struct timespec now; | ||
869 | |||
870 | __get_realtime_clock_ts(&now); | ||
871 | tv->tv_sec = now.tv_sec; | ||
872 | tv->tv_usec = now.tv_nsec/1000; | ||
873 | } | ||
874 | |||
875 | EXPORT_SYMBOL(do_gettimeofday); | ||
876 | /** | ||
877 | * do_settimeofday - Sets the time of day | ||
878 | * @tv: pointer to the timespec variable containing the new time | ||
879 | * | ||
880 | * Sets the time of day to the new time and update NTP and notify hrtimers | ||
881 | */ | ||
882 | int do_settimeofday(struct timespec *tv) | ||
883 | { | ||
884 | unsigned long flags; | ||
885 | time_t wtm_sec, sec = tv->tv_sec; | ||
886 | long wtm_nsec, nsec = tv->tv_nsec; | ||
887 | |||
888 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | ||
889 | return -EINVAL; | ||
890 | |||
891 | write_seqlock_irqsave(&xtime_lock, flags); | ||
892 | |||
893 | nsec -= __get_nsec_offset(); | ||
894 | |||
895 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | ||
896 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | ||
897 | |||
898 | set_normalized_timespec(&xtime, sec, nsec); | ||
899 | set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); | ||
900 | |||
901 | ntp_clear(); | ||
902 | |||
903 | write_sequnlock_irqrestore(&xtime_lock, flags); | ||
904 | |||
905 | /* signal hrtimers about time change */ | ||
906 | clock_was_set(); | ||
907 | |||
908 | return 0; | ||
909 | } | ||
910 | |||
911 | EXPORT_SYMBOL(do_settimeofday); | ||
912 | |||
913 | /** | ||
914 | * change_clocksource - Swaps clocksources if a new one is available | ||
915 | * | ||
916 | * Accumulates current time interval and initializes new clocksource | ||
917 | */ | ||
918 | static int change_clocksource(void) | ||
919 | { | ||
920 | struct clocksource *new; | ||
921 | cycle_t now; | ||
922 | u64 nsec; | ||
923 | new = get_next_clocksource(); | ||
924 | if (clock != new) { | ||
925 | now = read_clocksource(new); | ||
926 | nsec = __get_nsec_offset(); | ||
927 | timespec_add_ns(&xtime, nsec); | ||
928 | |||
929 | clock = new; | ||
930 | last_clock_cycle = now; | ||
931 | printk(KERN_INFO "Time: %s clocksource has been installed.\n", | ||
932 | clock->name); | ||
933 | return 1; | ||
934 | } else if (clock->update_callback) { | ||
935 | return clock->update_callback(); | ||
936 | } | ||
937 | return 0; | ||
938 | } | ||
939 | #else | ||
940 | #define change_clocksource() (0) | ||
941 | #endif | ||
942 | |||
943 | /** | ||
944 | * timeofday_is_continuous - check to see if timekeeping is free running | ||
945 | */ | ||
946 | int timekeeping_is_continuous(void) | ||
947 | { | ||
948 | unsigned long seq; | ||
949 | int ret; | ||
950 | |||
951 | do { | ||
952 | seq = read_seqbegin(&xtime_lock); | ||
953 | |||
954 | ret = clock->is_continuous; | ||
955 | |||
956 | } while (read_seqretry(&xtime_lock, seq)); | ||
957 | |||
958 | return ret; | ||
959 | } | ||
960 | |||
798 | /* | 961 | /* |
799 | * timekeeping_init - Initializes the clocksource and common timekeeping values | 962 | * timekeeping_init - Initializes the clocksource and common timekeeping values |
800 | */ | 963 | */ |
@@ -901,6 +1064,13 @@ static void update_wall_time(void) | |||
901 | /* store full nanoseconds into xtime */ | 1064 | /* store full nanoseconds into xtime */ |
902 | xtime.tv_nsec = remainder_snsecs >> clock->shift; | 1065 | xtime.tv_nsec = remainder_snsecs >> clock->shift; |
903 | remainder_snsecs -= (s64)xtime.tv_nsec << clock->shift; | 1066 | remainder_snsecs -= (s64)xtime.tv_nsec << clock->shift; |
1067 | |||
1068 | /* check to see if there is a new clocksource to use */ | ||
1069 | if (change_clocksource()) { | ||
1070 | error = 0; | ||
1071 | remainder_snsecs = 0; | ||
1072 | calculate_clocksource_interval(clock, tick_nsec); | ||
1073 | } | ||
904 | } | 1074 | } |
905 | 1075 | ||
906 | /* | 1076 | /* |