/* ugettime.c -- userland alternative to gettimeofday This file is part of "ugettime", a userland alternative to gettimeofday and time system call. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include static void *mmap_base = NULL; struct __xchg_dummy { unsigned long a[100]; }; #define __xg(x) ((struct __xchg_dummy *)(x)) static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) { unsigned long prev; switch (size) { case 1: __asm__ __volatile__("lock; cmpxchgb %b1,%2" : "=a"(prev) : "q"(new), "m"(*__xg(ptr)), "0"(old) : "memory"); return prev; case 2: __asm__ __volatile__("lock; cmpxchgw %w1,%2" : "=a"(prev) : "q"(new), "m"(*__xg(ptr)), "0"(old) : "memory"); return prev; case 4: __asm__ __volatile__("lock; cmpxchgl %1,%2" : "=a"(prev) : "q"(new), "m"(*__xg(ptr)), "0"(old) : "memory"); return prev; } return old; } #define cmpxchg(ptr,o,n)\ ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ (unsigned long)(n),sizeof(*(ptr)))) /* * Setup shared read-only map of tsc_info data structure * if kernel does not support it then leave mmap_base set to MAP_FAILED */ static void setup_mmap(void) { int fd; void *base; const long pgsz = sysconf(_SC_PAGE_SIZE); const long mapsz = (sizeof(struct tsc_info)+(pgsz-1)) &~ (pgsz-1); if ((fd = open("/proc/tscinfo", O_RDONLY)) < 0) base = MAP_FAILED; else { base = mmap(NULL, mapsz, PROT_READ, MAP_SHARED, fd, 0); close(fd); } /* Atomicaly update mmap, * so if two threads try to setup * at same time only one wins. */ if (cmpxchg(&mmap_base, 0, base) != 0) { /* race detected, keep other threads map */ if (base != MAP_FAILED) munmap(base, mapsz); } } static inline const struct tsc_info *get_mmap(void) { if (!mmap_base) setup_mmap(); if (mmap_base == MAP_FAILED || *(unsigned long *) mmap_base != TSCINFO_MAGIC) { return NULL; } return (const struct tsc_info *) mmap_base; } /* Read TSC register */ #define rdtsc(low,high) \ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) #define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory") #define rmb() mb() static inline unsigned long gettimeoffset(unsigned long last_tsc, unsigned long fast_quotient) { register unsigned long tsc_lo, tsc_hi; rdtsc(tsc_lo, tsc_hi); tsc_lo -= last_tsc; /* * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient * = (tsc_low delta) * (usecs_per_clock) * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy) * * Using a mull instead of a divl saves up to 31 clock cycles * in the critical path. */ __asm__("mull %2" :"=a" (tsc_lo), "=d" (tsc_hi) :"rm" (fast_quotient), "0" (tsc_lo)); return tsc_hi; } int gettimeofday (struct timeval *tv, struct timezone *tz) { const struct tsc_info * tsci = get_mmap(); if (!tsci) return syscall(SYS_gettimeofday,tv, tz); if (tv) { unsigned long seq, sec, usec; /* must check to see if clock ticked */ do { seq = tsci->pre_sequence; usec = gettimeoffset(tsci->last_tsc, tsci->offset_quotient); sec = tsci->last_tv.tv_sec; usec += tsci->last_tv.tv_usec; rmb(); } while (tsci->post_sequence != seq); while (usec >= 1000000) { usec -= 1000000; sec++; } tv->tv_usec = usec; tv->tv_sec = sec; } if (tz) { *tz = tsci->last_tz; } return 0; } time_t time (time_t *t) { const struct tsc_info * tsci = get_mmap(); time_t now; if (!tsci) return syscall(SYS_time,t); now = tsci->last_tv.tv_sec; if (t) *t = now; return (time_t) now; }