#include <asm/div64.h>
 
-/* We want to use micro-second resolution. */
+/* We want to use full resolution of the CPU timer: 2**-12 micro-seconds. */
 
 typedef unsigned long long cputime_t;
 typedef unsigned long long cputime64_t;
 #define cputime_ge(__a, __b)           ((__a) >= (__b))
 #define cputime_lt(__a, __b)           ((__a) <  (__b))
 #define cputime_le(__a, __b)           ((__a) <= (__b))
-#define cputime_to_jiffies(__ct)       (__div((__ct), 1000000 / HZ))
+#define cputime_to_jiffies(__ct)       (__div((__ct), 4096000000ULL / HZ))
 #define cputime_to_scaled(__ct)                (__ct)
-#define jiffies_to_cputime(__hz)       ((cputime_t)(__hz) * (1000000 / HZ))
+#define jiffies_to_cputime(__hz)       ((cputime_t)(__hz) * (4096000000ULL / HZ))
 
 #define cputime64_zero                 (0ULL)
 #define cputime64_add(__a, __b)                ((__a) + (__b))
 static inline u64
 cputime64_to_jiffies64(cputime64_t cputime)
 {
-       do_div(cputime, 1000000 / HZ);
+       do_div(cputime, 4096000000ULL / HZ);
        return cputime;
 }
 
 static inline unsigned int
 cputime_to_msecs(const cputime_t cputime)
 {
-       return __div(cputime, 1000);
+       return __div(cputime, 4096000);
 }
 
 static inline cputime_t
 msecs_to_cputime(const unsigned int m)
 {
-       return (cputime_t) m * 1000;
+       return (cputime_t) m * 4096000;
 }
 
 /*
 static inline unsigned int
 cputime_to_secs(const cputime_t cputime)
 {
-       return __div(cputime, 1000000);
+       return __div(cputime, 2048000000) >> 1;
 }
 
 static inline cputime_t
 secs_to_cputime(const unsigned int s)
 {
-       return (cputime_t) s * 1000000;
+       return (cputime_t) s * 4096000000ULL;
 }
 
 /*
 static inline cputime_t
 timespec_to_cputime(const struct timespec *value)
 {
-        return value->tv_nsec / 1000 + (u64) value->tv_sec * 1000000;
+       return value->tv_nsec * 4096 / 1000 + (u64) value->tv_sec * 4096000000ULL;
 }
 
 static inline void
        register_pair rp;
 
        rp.pair = cputime >> 1;
-       asm ("dr %0,%1" : "+d" (rp) : "d" (1000000 >> 1));
-       value->tv_nsec = rp.subreg.even * 1000;
+       asm ("dr %0,%1" : "+d" (rp) : "d" (2048000000UL));
+       value->tv_nsec = rp.subreg.even * 1000 / 4096;
        value->tv_sec = rp.subreg.odd;
 #else
-       value->tv_nsec = (cputime % 1000000) * 1000;
-       value->tv_sec = cputime / 1000000;
+       value->tv_nsec = (cputime % 4096000000ULL) * 1000 / 4096;
+       value->tv_sec = cputime / 4096000000ULL;
 #endif
 }
 
 static inline cputime_t
 timeval_to_cputime(const struct timeval *value)
 {
-        return value->tv_usec + (u64) value->tv_sec * 1000000;
+       return value->tv_usec * 4096 + (u64) value->tv_sec * 4096000000ULL;
 }
 
 static inline void
        register_pair rp;
 
        rp.pair = cputime >> 1;
-       asm ("dr %0,%1" : "+d" (rp) : "d" (1000000 >> 1));
-       value->tv_usec = rp.subreg.even;
+       asm ("dr %0,%1" : "+d" (rp) : "d" (2048000000UL));
+       value->tv_usec = rp.subreg.even / 4096;
        value->tv_sec = rp.subreg.odd;
 #else
-       value->tv_usec = cputime % 1000000;
-       value->tv_sec = cputime / 1000000;
+       value->tv_usec = cputime % 4096000000ULL;
+       value->tv_sec = cputime / 4096000000ULL;
 #endif
 }
 
 static inline clock_t
 cputime_to_clock_t(cputime_t cputime)
 {
-       return __div(cputime, 1000000 / USER_HZ);
+       return __div(cputime, 4096000000ULL / USER_HZ);
 }
 
 static inline cputime_t
 clock_t_to_cputime(unsigned long x)
 {
-       return (cputime_t) x * (1000000 / USER_HZ);
+       return (cputime_t) x * (4096000000ULL / USER_HZ);
 }
 
 /*
 static inline clock_t
 cputime64_to_clock_t(cputime64_t cputime)
 {
-       return __div(cputime, 1000000 / USER_HZ);
+       return __div(cputime, 4096000000ULL / USER_HZ);
 }
 
 #endif /* _S390_CPUTIME_H */
 
 #define __LC_SYNC_ENTER_TIMER          0x248
 #define __LC_ASYNC_ENTER_TIMER         0x250
 #define __LC_EXIT_TIMER                        0x258
-#define __LC_LAST_UPDATE_TIMER         0x260
-#define __LC_USER_TIMER                        0x268
-#define __LC_SYSTEM_TIMER              0x270
-#define __LC_LAST_UPDATE_CLOCK         0x278
-#define __LC_STEAL_CLOCK               0x280
+#define __LC_USER_TIMER                        0x260
+#define __LC_SYSTEM_TIMER              0x268
+#define __LC_STEAL_TIMER               0x270
+#define __LC_LAST_UPDATE_TIMER         0x278
+#define __LC_LAST_UPDATE_CLOCK         0x280
 #define __LC_RETURN_MCCK_PSW            0x288
 #define __LC_KERNEL_STACK               0xC40
 #define __LC_THREAD_INFO               0xC44
 #define __LC_SYNC_ENTER_TIMER          0x250
 #define __LC_ASYNC_ENTER_TIMER         0x258
 #define __LC_EXIT_TIMER                        0x260
-#define __LC_LAST_UPDATE_TIMER         0x268
-#define __LC_USER_TIMER                        0x270
-#define __LC_SYSTEM_TIMER              0x278
-#define __LC_LAST_UPDATE_CLOCK         0x280
-#define __LC_STEAL_CLOCK               0x288
+#define __LC_USER_TIMER                        0x268
+#define __LC_SYSTEM_TIMER              0x270
+#define __LC_STEAL_TIMER               0x278
+#define __LC_LAST_UPDATE_TIMER         0x280
+#define __LC_LAST_UPDATE_CLOCK         0x288
 #define __LC_RETURN_MCCK_PSW            0x290
 #define __LC_KERNEL_STACK               0xD40
 #define __LC_THREAD_INFO               0xD48
        __u64        sync_enter_timer;         /* 0x248 */
        __u64        async_enter_timer;        /* 0x250 */
        __u64        exit_timer;               /* 0x258 */
-       __u64        last_update_timer;        /* 0x260 */
-       __u64        user_timer;               /* 0x268 */
-       __u64        system_timer;             /* 0x270 */
-       __u64        last_update_clock;        /* 0x278 */
-       __u64        steal_clock;              /* 0x280 */
+       __u64        user_timer;               /* 0x260 */
+       __u64        system_timer;             /* 0x268 */
+       __u64        steal_timer;              /* 0x270 */
+       __u64        last_update_timer;        /* 0x278 */
+       __u64        last_update_clock;        /* 0x280 */
         psw_t        return_mcck_psw;          /* 0x288 */
        __u8         pad8[0xc00-0x290];        /* 0x290 */
 
        __u64        sync_enter_timer;         /* 0x250 */
        __u64        async_enter_timer;        /* 0x258 */
        __u64        exit_timer;               /* 0x260 */
-       __u64        last_update_timer;        /* 0x268 */
-       __u64        user_timer;               /* 0x270 */
-       __u64        system_timer;             /* 0x278 */
-       __u64        last_update_clock;        /* 0x280 */
-       __u64        steal_clock;              /* 0x288 */
+       __u64        user_timer;               /* 0x268 */
+       __u64        system_timer;             /* 0x270 */
+       __u64        steal_timer;              /* 0x278 */
+       __u64        last_update_timer;        /* 0x280 */
+       __u64        last_update_clock;        /* 0x288 */
         psw_t        return_mcck_psw;          /* 0x290 */
         __u8         pad8[0xc00-0x2a0];        /* 0x2a0 */
         /* System info area */
 
        prev = __switch_to(prev,next);                                       \
 } while (0)
 
-extern void account_vtime(struct task_struct *);
+extern void account_vtime(struct task_struct *, struct task_struct *);
 extern void account_tick_vtime(struct task_struct *);
 extern void account_system_vtime(struct task_struct *);
 
 
 #define finish_arch_switch(prev) do {                                       \
        set_fs(current->thread.mm_segment);                                  \
-       account_vtime(prev);                                                 \
+       account_vtime(prev, current);                                        \
 } while (0)
 
 #define nop() asm volatile("nop")
 
        unsigned int            cpu;            /* current CPU */
        int                     preempt_count;  /* 0 => preemptable, <0 => BUG */
        struct restart_block    restart_block;
+       __u64                   user_timer;
+       __u64                   system_timer;
 };
 
 /*
 
  * Update process times based on virtual cpu times stored by entry.S
  * to the lowcore fields user_timer, system_timer & steal_clock.
  */
-void account_process_tick(struct task_struct *tsk, int user_tick)
+static void do_account_vtime(struct task_struct *tsk, int hardirq_offset)
 {
-       cputime_t cputime;
-       __u64 timer, clock;
-       int rcu_user_flag;
+       struct thread_info *ti = task_thread_info(tsk);
+       __u64 timer, clock, user, system, steal;
 
        timer = S390_lowcore.last_update_timer;
        clock = S390_lowcore.last_update_clock;
                      : "=m" (S390_lowcore.last_update_timer),
                        "=m" (S390_lowcore.last_update_clock) );
        S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer;
-       S390_lowcore.steal_clock += S390_lowcore.last_update_clock - clock;
+       S390_lowcore.steal_timer += S390_lowcore.last_update_clock - clock;
 
-       cputime = S390_lowcore.user_timer >> 12;
-       rcu_user_flag = cputime != 0;
-       S390_lowcore.user_timer -= cputime << 12;
-       S390_lowcore.steal_clock -= cputime << 12;
-       account_user_time(tsk, cputime, cputime);
+       user = S390_lowcore.user_timer - ti->user_timer;
+       S390_lowcore.steal_timer -= user;
+       ti->user_timer = S390_lowcore.user_timer;
+       account_user_time(tsk, user, user);
 
-       cputime =  S390_lowcore.system_timer >> 12;
-       S390_lowcore.system_timer -= cputime << 12;
-       S390_lowcore.steal_clock -= cputime << 12;
+       system = S390_lowcore.system_timer - ti->system_timer;
+       S390_lowcore.steal_timer -= system;
+       ti->system_timer = S390_lowcore.system_timer;
        if (idle_task(smp_processor_id()) != current)
-               account_system_time(tsk, HARDIRQ_OFFSET, cputime, cputime);
+               account_system_time(tsk, hardirq_offset, system, system);
        else
-               account_idle_time(cputime);
+               account_idle_time(system);
 
-       cputime = S390_lowcore.steal_clock;
-       if ((__s64) cputime > 0) {
-               cputime >>= 12;
-               S390_lowcore.steal_clock -= cputime << 12;
+       steal = S390_lowcore.steal_timer;
+       if ((s64) steal > 0) {
+               S390_lowcore.steal_timer = 0;
                if (idle_task(smp_processor_id()) != current)
-                       account_steal_time(cputime);
+                       account_steal_time(steal);
                else
-                       account_idle_time(cputime);
+                       account_idle_time(steal);
        }
 }
 
-/*
- * Update process times based on virtual cpu times stored by entry.S
- * to the lowcore fields user_timer, system_timer & steal_clock.
- */
-void account_vtime(struct task_struct *tsk)
+void account_vtime(struct task_struct *prev, struct task_struct *next)
 {
-       cputime_t cputime;
-       __u64 timer;
-
-       timer = S390_lowcore.last_update_timer;
-       asm volatile ("  STPT %0"    /* Store current cpu timer value */
-                     : "=m" (S390_lowcore.last_update_timer) );
-       S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer;
-
-       cputime = S390_lowcore.user_timer >> 12;
-       S390_lowcore.user_timer -= cputime << 12;
-       S390_lowcore.steal_clock -= cputime << 12;
-       account_user_time(tsk, cputime, cputime);
+       struct thread_info *ti;
+
+       do_account_vtime(prev, 0);
+       ti = task_thread_info(prev);
+       ti->user_timer = S390_lowcore.user_timer;
+       ti->system_timer = S390_lowcore.system_timer;
+       ti = task_thread_info(next);
+       S390_lowcore.user_timer = ti->user_timer;
+       S390_lowcore.system_timer = ti->system_timer;
+}
 
-       cputime =  S390_lowcore.system_timer >> 12;
-       S390_lowcore.system_timer -= cputime << 12;
-       S390_lowcore.steal_clock -= cputime << 12;
-       if (idle_task(smp_processor_id()) != current)
-               account_system_time(tsk, 0, cputime, cputime);
-       else
-               account_idle_time(cputime);
+void account_process_tick(struct task_struct *tsk, int user_tick)
+{
+       do_account_vtime(tsk, HARDIRQ_OFFSET);
 }
 
 /*
  */
 void account_system_vtime(struct task_struct *tsk)
 {
-       cputime_t cputime;
-       __u64 timer;
+       struct thread_info *ti = task_thread_info(tsk);
+       __u64 timer, system;
 
        timer = S390_lowcore.last_update_timer;
        asm volatile ("  STPT %0"    /* Store current cpu timer value */
                      : "=m" (S390_lowcore.last_update_timer) );
        S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer;
 
-       cputime =  S390_lowcore.system_timer >> 12;
-       S390_lowcore.system_timer -= cputime << 12;
-       S390_lowcore.steal_clock -= cputime << 12;
+       system = S390_lowcore.system_timer - ti->system_timer;
+       S390_lowcore.steal_timer -= system;
+       ti->system_timer = S390_lowcore.system_timer;
        if (in_irq() || idle_task(smp_processor_id()) != current)
-               account_system_time(tsk, 0, cputime, cputime);
+               account_system_time(tsk, 0, system, system);
        else
-               account_idle_time(cputime);
+               account_idle_time(system);
 }
 EXPORT_SYMBOL_GPL(account_system_vtime);
 
        /* kick the virtual timer */
        S390_lowcore.exit_timer = VTIMER_MAX_SLICE;
        S390_lowcore.last_update_timer = VTIMER_MAX_SLICE;
-       asm volatile ("SPT %0" : : "m" (S390_lowcore.last_update_timer));
        asm volatile ("STCK %0" : "=m" (S390_lowcore.last_update_clock));
+       asm volatile ("SPT %0" : : "m" (S390_lowcore.last_update_timer));
 
        /* enable cpu timer interrupts */
        __ctl_set_bit(0,10);