1a17ae4c3SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Virtual cpu timer based timer functions. 41da177e4SLinus Torvalds * 527f6b416SMartin Schwidefsky * Copyright IBM Corp. 2004, 2012 61da177e4SLinus Torvalds * Author(s): Jan Glauber <jan.glauber@de.ibm.com> 71da177e4SLinus Torvalds */ 81da177e4SLinus Torvalds 91da177e4SLinus Torvalds #include <linux/kernel_stat.h> 1032ef5517SIngo Molnar #include <linux/sched/cputime.h> 1127f6b416SMartin Schwidefsky #include <linux/export.h> 1227f6b416SMartin Schwidefsky #include <linux/kernel.h> 1327f6b416SMartin Schwidefsky #include <linux/timex.h> 1427f6b416SMartin Schwidefsky #include <linux/types.h> 1527f6b416SMartin Schwidefsky #include <linux/time.h> 161c767347SHeiko Carstens #include <asm/alternative.h> 1727f6b416SMartin Schwidefsky #include <asm/vtimer.h> 18a5725ac2SFrederic Weisbecker #include <asm/vtime.h> 1910ad34bcSMartin Schwidefsky #include <asm/cpu_mf.h> 2010ad34bcSMartin Schwidefsky #include <asm/smp.h> 211da177e4SLinus Torvalds 22521b00cdSHeiko Carstens #include "entry.h" 23521b00cdSHeiko Carstens 2427f6b416SMartin Schwidefsky static void virt_timer_expire(void); 251da177e4SLinus Torvalds 2627f6b416SMartin Schwidefsky static LIST_HEAD(virt_timer_list); 2727f6b416SMartin Schwidefsky static DEFINE_SPINLOCK(virt_timer_lock); 2827f6b416SMartin Schwidefsky static atomic64_t virt_timer_current; 2927f6b416SMartin Schwidefsky static atomic64_t virt_timer_elapsed; 309cfb9b3cSMartin Schwidefsky 3172d38b19SMartin Schwidefsky DEFINE_PER_CPU(u64, mt_cycles[8]); 3210ad34bcSMartin Schwidefsky static DEFINE_PER_CPU(u64, mt_scaling_mult) = { 1 }; 3310ad34bcSMartin Schwidefsky static DEFINE_PER_CPU(u64, mt_scaling_div) = { 1 }; 34f341b8dfSMartin Schwidefsky static DEFINE_PER_CPU(u64, mt_scaling_jiffies); 3510ad34bcSMartin Schwidefsky 3627f6b416SMartin Schwidefsky static inline u64 get_vtimer(void) 3727f6b416SMartin Schwidefsky { 3827f6b416SMartin Schwidefsky u64 timer; 3927f6b416SMartin Schwidefsky 4035af0d46SVasily Gorbik asm volatile("stpt %0" : "=Q" (timer)); 419cfb9b3cSMartin Schwidefsky return timer; 429cfb9b3cSMartin Schwidefsky } 439cfb9b3cSMartin Schwidefsky 4427f6b416SMartin Schwidefsky static inline void set_vtimer(u64 expires) 459cfb9b3cSMartin Schwidefsky { 4627f6b416SMartin Schwidefsky u64 timer; 479cfb9b3cSMartin Schwidefsky 4827f6b416SMartin Schwidefsky asm volatile( 4927f6b416SMartin Schwidefsky " stpt %0\n" /* Store current cpu timer value */ 5027f6b416SMartin Schwidefsky " spt %1" /* Set new value imm. afterwards */ 5135af0d46SVasily Gorbik : "=Q" (timer) : "Q" (expires)); 529cfb9b3cSMartin Schwidefsky S390_lowcore.system_timer += S390_lowcore.last_update_timer - timer; 539cfb9b3cSMartin Schwidefsky S390_lowcore.last_update_timer = expires; 549cfb9b3cSMartin Schwidefsky } 559cfb9b3cSMartin Schwidefsky 5627f6b416SMartin Schwidefsky static inline int virt_timer_forward(u64 elapsed) 5727f6b416SMartin Schwidefsky { 5827f6b416SMartin Schwidefsky BUG_ON(!irqs_disabled()); 5927f6b416SMartin Schwidefsky 6027f6b416SMartin Schwidefsky if (list_empty(&virt_timer_list)) 6127f6b416SMartin Schwidefsky return 0; 6227f6b416SMartin Schwidefsky elapsed = atomic64_add_return(elapsed, &virt_timer_elapsed); 6327f6b416SMartin Schwidefsky return elapsed >= atomic64_read(&virt_timer_current); 6427f6b416SMartin Schwidefsky } 6527f6b416SMartin Schwidefsky 6672d38b19SMartin Schwidefsky static void update_mt_scaling(void) 671da177e4SLinus Torvalds { 6872d38b19SMartin Schwidefsky u64 cycles_new[8], *cycles_old; 6972d38b19SMartin Schwidefsky u64 delta, fac, mult, div; 7010ad34bcSMartin Schwidefsky int i; 711da177e4SLinus Torvalds 72346d034dSHendrik Brueckner stcctm(MT_DIAG, smp_cpu_mtid + 1, cycles_new); 7310ad34bcSMartin Schwidefsky cycles_old = this_cpu_ptr(mt_cycles); 7461cc3790SMartin Schwidefsky fac = 1; 7510ad34bcSMartin Schwidefsky mult = div = 0; 7610ad34bcSMartin Schwidefsky for (i = 0; i <= smp_cpu_mtid; i++) { 7710ad34bcSMartin Schwidefsky delta = cycles_new[i] - cycles_old[i]; 7861cc3790SMartin Schwidefsky div += delta; 7961cc3790SMartin Schwidefsky mult *= i + 1; 8061cc3790SMartin Schwidefsky mult += delta * fac; 8161cc3790SMartin Schwidefsky fac *= i + 1; 8210ad34bcSMartin Schwidefsky } 8361cc3790SMartin Schwidefsky div *= fac; 8461cc3790SMartin Schwidefsky if (div > 0) { 8510ad34bcSMartin Schwidefsky /* Update scaling factor */ 8610ad34bcSMartin Schwidefsky __this_cpu_write(mt_scaling_mult, mult); 8710ad34bcSMartin Schwidefsky __this_cpu_write(mt_scaling_div, div); 8810ad34bcSMartin Schwidefsky memcpy(cycles_old, cycles_new, 8910ad34bcSMartin Schwidefsky sizeof(u64) * (smp_cpu_mtid + 1)); 9010ad34bcSMartin Schwidefsky } 91f341b8dfSMartin Schwidefsky __this_cpu_write(mt_scaling_jiffies, jiffies_64); 9210ad34bcSMartin Schwidefsky } 9310ad34bcSMartin Schwidefsky 94b7394a5fSMartin Schwidefsky static inline u64 update_tsk_timer(unsigned long *tsk_vtime, u64 new) 95b7394a5fSMartin Schwidefsky { 96b7394a5fSMartin Schwidefsky u64 delta; 97b7394a5fSMartin Schwidefsky 98b7394a5fSMartin Schwidefsky delta = new - *tsk_vtime; 99b7394a5fSMartin Schwidefsky *tsk_vtime = new; 100b7394a5fSMartin Schwidefsky return delta; 101b7394a5fSMartin Schwidefsky } 102b7394a5fSMartin Schwidefsky 103b7394a5fSMartin Schwidefsky 104b7394a5fSMartin Schwidefsky static inline u64 scale_vtime(u64 vtime) 105b7394a5fSMartin Schwidefsky { 106b7394a5fSMartin Schwidefsky u64 mult = __this_cpu_read(mt_scaling_mult); 107b7394a5fSMartin Schwidefsky u64 div = __this_cpu_read(mt_scaling_div); 108b7394a5fSMartin Schwidefsky 109b7394a5fSMartin Schwidefsky if (smp_cpu_mtid) 110b7394a5fSMartin Schwidefsky return vtime * mult / div; 111b7394a5fSMartin Schwidefsky return vtime; 112b7394a5fSMartin Schwidefsky } 113b7394a5fSMartin Schwidefsky 114b29e061bSMartin Schwidefsky static void account_system_index_scaled(struct task_struct *p, u64 cputime, 115b7394a5fSMartin Schwidefsky enum cpu_usage_stat index) 116b7394a5fSMartin Schwidefsky { 117b29e061bSMartin Schwidefsky p->stimescaled += cputime_to_nsecs(scale_vtime(cputime)); 118fb8b049cSFrederic Weisbecker account_system_index_time(p, cputime_to_nsecs(cputime), index); 119b7394a5fSMartin Schwidefsky } 120b7394a5fSMartin Schwidefsky 12172d38b19SMartin Schwidefsky /* 12272d38b19SMartin Schwidefsky * Update process times based on virtual cpu times stored by entry.S 12372d38b19SMartin Schwidefsky * to the lowcore fields user_timer, system_timer & steal_clock. 12472d38b19SMartin Schwidefsky */ 1258f2b468aSMartin Schwidefsky static int do_account_vtime(struct task_struct *tsk) 12672d38b19SMartin Schwidefsky { 127152e9b86SMartin Schwidefsky u64 timer, clock, user, guest, system, hardirq, softirq; 12872d38b19SMartin Schwidefsky 12972d38b19SMartin Schwidefsky timer = S390_lowcore.last_update_timer; 13072d38b19SMartin Schwidefsky clock = S390_lowcore.last_update_clock; 1311c767347SHeiko Carstens /* Use STORE CLOCK by default, STORE CLOCK FAST if available. */ 1321c767347SHeiko Carstens alternative_io("stpt %0\n .insn s,0xb2050000,%1\n", 1331c767347SHeiko Carstens "stpt %0\n .insn s,0xb27c0000,%1\n", 1341c767347SHeiko Carstens 25, 1351c767347SHeiko Carstens ASM_OUTPUT2("=Q" (S390_lowcore.last_update_timer), 1361c767347SHeiko Carstens "=Q" (S390_lowcore.last_update_clock)), 1371c767347SHeiko Carstens ASM_NO_INPUT_CLOBBER("cc")); 138b7394a5fSMartin Schwidefsky clock = S390_lowcore.last_update_clock - clock; 139b7394a5fSMartin Schwidefsky timer -= S390_lowcore.last_update_timer; 140b7394a5fSMartin Schwidefsky 141b7394a5fSMartin Schwidefsky if (hardirq_count()) 142b7394a5fSMartin Schwidefsky S390_lowcore.hardirq_timer += timer; 143b7394a5fSMartin Schwidefsky else 144b7394a5fSMartin Schwidefsky S390_lowcore.system_timer += timer; 14572d38b19SMartin Schwidefsky 14672d38b19SMartin Schwidefsky /* Update MT utilization calculation */ 14772d38b19SMartin Schwidefsky if (smp_cpu_mtid && 14872d38b19SMartin Schwidefsky time_after64(jiffies_64, this_cpu_read(mt_scaling_jiffies))) 14972d38b19SMartin Schwidefsky update_mt_scaling(); 15072d38b19SMartin Schwidefsky 151b7394a5fSMartin Schwidefsky /* Calculate cputime delta */ 152b7394a5fSMartin Schwidefsky user = update_tsk_timer(&tsk->thread.user_timer, 153b7394a5fSMartin Schwidefsky READ_ONCE(S390_lowcore.user_timer)); 154b7394a5fSMartin Schwidefsky guest = update_tsk_timer(&tsk->thread.guest_timer, 155b7394a5fSMartin Schwidefsky READ_ONCE(S390_lowcore.guest_timer)); 156b7394a5fSMartin Schwidefsky system = update_tsk_timer(&tsk->thread.system_timer, 157b7394a5fSMartin Schwidefsky READ_ONCE(S390_lowcore.system_timer)); 158b7394a5fSMartin Schwidefsky hardirq = update_tsk_timer(&tsk->thread.hardirq_timer, 159b7394a5fSMartin Schwidefsky READ_ONCE(S390_lowcore.hardirq_timer)); 160b7394a5fSMartin Schwidefsky softirq = update_tsk_timer(&tsk->thread.softirq_timer, 161b7394a5fSMartin Schwidefsky READ_ONCE(S390_lowcore.softirq_timer)); 162b7394a5fSMartin Schwidefsky S390_lowcore.steal_timer += 163b7394a5fSMartin Schwidefsky clock - user - guest - system - hardirq - softirq; 1641da177e4SLinus Torvalds 165b7394a5fSMartin Schwidefsky /* Push account value */ 166b7394a5fSMartin Schwidefsky if (user) { 16723244a5cSFrederic Weisbecker account_user_time(tsk, cputime_to_nsecs(user)); 1685613fda9SFrederic Weisbecker tsk->utimescaled += cputime_to_nsecs(scale_vtime(user)); 169b7394a5fSMartin Schwidefsky } 170b7394a5fSMartin Schwidefsky 171b7394a5fSMartin Schwidefsky if (guest) { 172fb8b049cSFrederic Weisbecker account_guest_time(tsk, cputime_to_nsecs(guest)); 1735613fda9SFrederic Weisbecker tsk->utimescaled += cputime_to_nsecs(scale_vtime(guest)); 174b7394a5fSMartin Schwidefsky } 175b7394a5fSMartin Schwidefsky 176b7394a5fSMartin Schwidefsky if (system) 177b29e061bSMartin Schwidefsky account_system_index_scaled(tsk, system, CPUTIME_SYSTEM); 178b7394a5fSMartin Schwidefsky if (hardirq) 179b29e061bSMartin Schwidefsky account_system_index_scaled(tsk, hardirq, CPUTIME_IRQ); 180b7394a5fSMartin Schwidefsky if (softirq) 181b29e061bSMartin Schwidefsky account_system_index_scaled(tsk, softirq, CPUTIME_SOFTIRQ); 1821da177e4SLinus Torvalds 183b7394a5fSMartin Schwidefsky return virt_timer_forward(user + guest + system + hardirq + softirq); 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds 186bf9fae9fSFrederic Weisbecker void vtime_task_switch(struct task_struct *prev) 1871f1c12afSMartin Schwidefsky { 1888f2b468aSMartin Schwidefsky do_account_vtime(prev); 18990c53e65SMartin Schwidefsky prev->thread.user_timer = S390_lowcore.user_timer; 190b7394a5fSMartin Schwidefsky prev->thread.guest_timer = S390_lowcore.guest_timer; 19190c53e65SMartin Schwidefsky prev->thread.system_timer = S390_lowcore.system_timer; 192b7394a5fSMartin Schwidefsky prev->thread.hardirq_timer = S390_lowcore.hardirq_timer; 193b7394a5fSMartin Schwidefsky prev->thread.softirq_timer = S390_lowcore.softirq_timer; 19490c53e65SMartin Schwidefsky S390_lowcore.user_timer = current->thread.user_timer; 195b7394a5fSMartin Schwidefsky S390_lowcore.guest_timer = current->thread.guest_timer; 19690c53e65SMartin Schwidefsky S390_lowcore.system_timer = current->thread.system_timer; 197b7394a5fSMartin Schwidefsky S390_lowcore.hardirq_timer = current->thread.hardirq_timer; 198b7394a5fSMartin Schwidefsky S390_lowcore.softirq_timer = current->thread.softirq_timer; 199aa5e97ceSMartin Schwidefsky } 2001f1c12afSMartin Schwidefsky 201bcebdf84SFrederic Weisbecker /* 202bcebdf84SFrederic Weisbecker * In s390, accounting pending user time also implies 203bcebdf84SFrederic Weisbecker * accounting system time in order to correctly compute 204bcebdf84SFrederic Weisbecker * the stolen time accounting. 205bcebdf84SFrederic Weisbecker */ 206c8d7dabfSFrederic Weisbecker void vtime_flush(struct task_struct *tsk) 207aa5e97ceSMartin Schwidefsky { 208152e9b86SMartin Schwidefsky u64 steal, avg_steal; 209152e9b86SMartin Schwidefsky 2108f2b468aSMartin Schwidefsky if (do_account_vtime(tsk)) 21127f6b416SMartin Schwidefsky virt_timer_expire(); 212152e9b86SMartin Schwidefsky 213152e9b86SMartin Schwidefsky steal = S390_lowcore.steal_timer; 214152e9b86SMartin Schwidefsky avg_steal = S390_lowcore.avg_steal_timer / 2; 215152e9b86SMartin Schwidefsky if ((s64) steal > 0) { 216152e9b86SMartin Schwidefsky S390_lowcore.steal_timer = 0; 217*d54cb7d5SGerald Schaefer account_steal_time(cputime_to_nsecs(steal)); 218152e9b86SMartin Schwidefsky avg_steal += steal; 219152e9b86SMartin Schwidefsky } 220152e9b86SMartin Schwidefsky S390_lowcore.avg_steal_timer = avg_steal; 2211f1c12afSMartin Schwidefsky } 2221f1c12afSMartin Schwidefsky 2238a6a5920SFrederic Weisbecker static u64 vtime_delta(void) 2248a6a5920SFrederic Weisbecker { 2258a6a5920SFrederic Weisbecker u64 timer = S390_lowcore.last_update_timer; 2268a6a5920SFrederic Weisbecker 2278a6a5920SFrederic Weisbecker S390_lowcore.last_update_timer = get_vtimer(); 2288a6a5920SFrederic Weisbecker 2298a6a5920SFrederic Weisbecker return timer - S390_lowcore.last_update_timer; 2308a6a5920SFrederic Weisbecker } 2318a6a5920SFrederic Weisbecker 2321f1c12afSMartin Schwidefsky /* 2331f1c12afSMartin Schwidefsky * Update process times based on virtual cpu times stored by entry.S 2341f1c12afSMartin Schwidefsky * to the lowcore fields user_timer, system_timer & steal_clock. 2351f1c12afSMartin Schwidefsky */ 2367197688bSFrederic Weisbecker void vtime_account_kernel(struct task_struct *tsk) 2371da177e4SLinus Torvalds { 2388a6a5920SFrederic Weisbecker u64 delta = vtime_delta(); 2391da177e4SLinus Torvalds 2408a6a5920SFrederic Weisbecker if (tsk->flags & PF_VCPU) 2418a6a5920SFrederic Weisbecker S390_lowcore.guest_timer += delta; 242b7394a5fSMartin Schwidefsky else 2438a6a5920SFrederic Weisbecker S390_lowcore.system_timer += delta; 24472d38b19SMartin Schwidefsky 2458a6a5920SFrederic Weisbecker virt_timer_forward(delta); 2461da177e4SLinus Torvalds } 247f83eeb1aSFrederic Weisbecker EXPORT_SYMBOL_GPL(vtime_account_kernel); 24811113334SFrederic Weisbecker 2498a6a5920SFrederic Weisbecker void vtime_account_softirq(struct task_struct *tsk) 2508a6a5920SFrederic Weisbecker { 2518a6a5920SFrederic Weisbecker u64 delta = vtime_delta(); 2528a6a5920SFrederic Weisbecker 2538a6a5920SFrederic Weisbecker S390_lowcore.softirq_timer += delta; 2548a6a5920SFrederic Weisbecker 2558a6a5920SFrederic Weisbecker virt_timer_forward(delta); 2568a6a5920SFrederic Weisbecker } 2578a6a5920SFrederic Weisbecker 2588a6a5920SFrederic Weisbecker void vtime_account_hardirq(struct task_struct *tsk) 2598a6a5920SFrederic Weisbecker { 2608a6a5920SFrederic Weisbecker u64 delta = vtime_delta(); 2618a6a5920SFrederic Weisbecker 2628a6a5920SFrederic Weisbecker S390_lowcore.hardirq_timer += delta; 2638a6a5920SFrederic Weisbecker 2648a6a5920SFrederic Weisbecker virt_timer_forward(delta); 2658a6a5920SFrederic Weisbecker } 2668a6a5920SFrederic Weisbecker 2671da177e4SLinus Torvalds /* 2681da177e4SLinus Torvalds * Sorted add to a list. List is linear searched until first bigger 2691da177e4SLinus Torvalds * element is found. 2701da177e4SLinus Torvalds */ 2711da177e4SLinus Torvalds static void list_add_sorted(struct vtimer_list *timer, struct list_head *head) 2721da177e4SLinus Torvalds { 27327f6b416SMartin Schwidefsky struct vtimer_list *tmp; 2741da177e4SLinus Torvalds 27527f6b416SMartin Schwidefsky list_for_each_entry(tmp, head, entry) { 27627f6b416SMartin Schwidefsky if (tmp->expires > timer->expires) { 27727f6b416SMartin Schwidefsky list_add_tail(&timer->entry, &tmp->entry); 2781da177e4SLinus Torvalds return; 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds list_add_tail(&timer->entry, head); 2821da177e4SLinus Torvalds } 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds /* 28527f6b416SMartin Schwidefsky * Handler for expired virtual CPU timer. 2861da177e4SLinus Torvalds */ 28727f6b416SMartin Schwidefsky static void virt_timer_expire(void) 2881da177e4SLinus Torvalds { 28927f6b416SMartin Schwidefsky struct vtimer_list *timer, *tmp; 29027f6b416SMartin Schwidefsky unsigned long elapsed; 29127f6b416SMartin Schwidefsky LIST_HEAD(cb_list); 2921da177e4SLinus Torvalds 29327f6b416SMartin Schwidefsky /* walk timer list, fire all expired timers */ 29427f6b416SMartin Schwidefsky spin_lock(&virt_timer_lock); 29527f6b416SMartin Schwidefsky elapsed = atomic64_read(&virt_timer_elapsed); 29627f6b416SMartin Schwidefsky list_for_each_entry_safe(timer, tmp, &virt_timer_list, entry) { 29727f6b416SMartin Schwidefsky if (timer->expires < elapsed) 2981da177e4SLinus Torvalds /* move expired timer to the callback queue */ 29927f6b416SMartin Schwidefsky list_move_tail(&timer->entry, &cb_list); 3009cfb9b3cSMartin Schwidefsky else 30127f6b416SMartin Schwidefsky timer->expires -= elapsed; 3021da177e4SLinus Torvalds } 30327f6b416SMartin Schwidefsky if (!list_empty(&virt_timer_list)) { 30427f6b416SMartin Schwidefsky timer = list_first_entry(&virt_timer_list, 30527f6b416SMartin Schwidefsky struct vtimer_list, entry); 30627f6b416SMartin Schwidefsky atomic64_set(&virt_timer_current, timer->expires); 3074c1051e3SMartin Schwidefsky } 30827f6b416SMartin Schwidefsky atomic64_sub(elapsed, &virt_timer_elapsed); 30927f6b416SMartin Schwidefsky spin_unlock(&virt_timer_lock); 31027f6b416SMartin Schwidefsky 31127f6b416SMartin Schwidefsky /* Do callbacks and recharge periodic timers */ 31227f6b416SMartin Schwidefsky list_for_each_entry_safe(timer, tmp, &cb_list, entry) { 31327f6b416SMartin Schwidefsky list_del_init(&timer->entry); 31427f6b416SMartin Schwidefsky timer->function(timer->data); 31527f6b416SMartin Schwidefsky if (timer->interval) { 31627f6b416SMartin Schwidefsky /* Recharge interval timer */ 31727f6b416SMartin Schwidefsky timer->expires = timer->interval + 31827f6b416SMartin Schwidefsky atomic64_read(&virt_timer_elapsed); 31927f6b416SMartin Schwidefsky spin_lock(&virt_timer_lock); 32027f6b416SMartin Schwidefsky list_add_sorted(timer, &virt_timer_list); 32127f6b416SMartin Schwidefsky spin_unlock(&virt_timer_lock); 32227f6b416SMartin Schwidefsky } 32327f6b416SMartin Schwidefsky } 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds 3261da177e4SLinus Torvalds void init_virt_timer(struct vtimer_list *timer) 3271da177e4SLinus Torvalds { 3281da177e4SLinus Torvalds timer->function = NULL; 3291da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->entry); 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds EXPORT_SYMBOL(init_virt_timer); 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds static inline int vtimer_pending(struct vtimer_list *timer) 3341da177e4SLinus Torvalds { 33527f6b416SMartin Schwidefsky return !list_empty(&timer->entry); 3361da177e4SLinus Torvalds } 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds static void internal_add_vtimer(struct vtimer_list *timer) 3391da177e4SLinus Torvalds { 34027f6b416SMartin Schwidefsky if (list_empty(&virt_timer_list)) { 34127f6b416SMartin Schwidefsky /* First timer, just program it. */ 34227f6b416SMartin Schwidefsky atomic64_set(&virt_timer_current, timer->expires); 34327f6b416SMartin Schwidefsky atomic64_set(&virt_timer_elapsed, 0); 34427f6b416SMartin Schwidefsky list_add(&timer->entry, &virt_timer_list); 3459cfb9b3cSMartin Schwidefsky } else { 34627f6b416SMartin Schwidefsky /* Update timer against current base. */ 34727f6b416SMartin Schwidefsky timer->expires += atomic64_read(&virt_timer_elapsed); 34827f6b416SMartin Schwidefsky if (likely((s64) timer->expires < 34927f6b416SMartin Schwidefsky (s64) atomic64_read(&virt_timer_current))) 3509cfb9b3cSMartin Schwidefsky /* The new timer expires before the current timer. */ 35127f6b416SMartin Schwidefsky atomic64_set(&virt_timer_current, timer->expires); 35227f6b416SMartin Schwidefsky /* Insert new timer into the list. */ 35327f6b416SMartin Schwidefsky list_add_sorted(timer, &virt_timer_list); 3549cfb9b3cSMartin Schwidefsky } 3551da177e4SLinus Torvalds } 3561da177e4SLinus Torvalds 35727f6b416SMartin Schwidefsky static void __add_vtimer(struct vtimer_list *timer, int periodic) 3581da177e4SLinus Torvalds { 35927f6b416SMartin Schwidefsky unsigned long flags; 36027f6b416SMartin Schwidefsky 36127f6b416SMartin Schwidefsky timer->interval = periodic ? timer->expires : 0; 36227f6b416SMartin Schwidefsky spin_lock_irqsave(&virt_timer_lock, flags); 36327f6b416SMartin Schwidefsky internal_add_vtimer(timer); 36427f6b416SMartin Schwidefsky spin_unlock_irqrestore(&virt_timer_lock, flags); 3651da177e4SLinus Torvalds } 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds /* 3680f5e1558SMasahiro Yamada * add_virt_timer - add a oneshot virtual CPU timer 3691da177e4SLinus Torvalds */ 37027f6b416SMartin Schwidefsky void add_virt_timer(struct vtimer_list *timer) 3711da177e4SLinus Torvalds { 37227f6b416SMartin Schwidefsky __add_vtimer(timer, 0); 3731da177e4SLinus Torvalds } 3741da177e4SLinus Torvalds EXPORT_SYMBOL(add_virt_timer); 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds /* 3771da177e4SLinus Torvalds * add_virt_timer_int - add an interval virtual CPU timer 3781da177e4SLinus Torvalds */ 37927f6b416SMartin Schwidefsky void add_virt_timer_periodic(struct vtimer_list *timer) 3801da177e4SLinus Torvalds { 38127f6b416SMartin Schwidefsky __add_vtimer(timer, 1); 3821da177e4SLinus Torvalds } 3831da177e4SLinus Torvalds EXPORT_SYMBOL(add_virt_timer_periodic); 3841da177e4SLinus Torvalds 38527f6b416SMartin Schwidefsky static int __mod_vtimer(struct vtimer_list *timer, u64 expires, int periodic) 3861da177e4SLinus Torvalds { 3871da177e4SLinus Torvalds unsigned long flags; 38827f6b416SMartin Schwidefsky int rc; 3891da177e4SLinus Torvalds 390ca366a32SMartin Schwidefsky BUG_ON(!timer->function); 3911da177e4SLinus Torvalds 3921da177e4SLinus Torvalds if (timer->expires == expires && vtimer_pending(timer)) 3931da177e4SLinus Torvalds return 1; 39427f6b416SMartin Schwidefsky spin_lock_irqsave(&virt_timer_lock, flags); 39527f6b416SMartin Schwidefsky rc = vtimer_pending(timer); 39627f6b416SMartin Schwidefsky if (rc) 3971da177e4SLinus Torvalds list_del_init(&timer->entry); 39827f6b416SMartin Schwidefsky timer->interval = periodic ? expires : 0; 3991da177e4SLinus Torvalds timer->expires = expires; 4001da177e4SLinus Torvalds internal_add_vtimer(timer); 40127f6b416SMartin Schwidefsky spin_unlock_irqrestore(&virt_timer_lock, flags); 40227f6b416SMartin Schwidefsky return rc; 4031da177e4SLinus Torvalds } 404b6ecfa92SJan Glauber 405b6ecfa92SJan Glauber /* 406b6ecfa92SJan Glauber * returns whether it has modified a pending timer (1) or not (0) 407b6ecfa92SJan Glauber */ 40827f6b416SMartin Schwidefsky int mod_virt_timer(struct vtimer_list *timer, u64 expires) 409b6ecfa92SJan Glauber { 410b6ecfa92SJan Glauber return __mod_vtimer(timer, expires, 0); 411b6ecfa92SJan Glauber } 4121da177e4SLinus Torvalds EXPORT_SYMBOL(mod_virt_timer); 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds /* 415b6ecfa92SJan Glauber * returns whether it has modified a pending timer (1) or not (0) 416b6ecfa92SJan Glauber */ 41727f6b416SMartin Schwidefsky int mod_virt_timer_periodic(struct vtimer_list *timer, u64 expires) 418b6ecfa92SJan Glauber { 419b6ecfa92SJan Glauber return __mod_vtimer(timer, expires, 1); 420b6ecfa92SJan Glauber } 421b6ecfa92SJan Glauber EXPORT_SYMBOL(mod_virt_timer_periodic); 422b6ecfa92SJan Glauber 423b6ecfa92SJan Glauber /* 42427f6b416SMartin Schwidefsky * Delete a virtual timer. 4251da177e4SLinus Torvalds * 4261da177e4SLinus Torvalds * returns whether the deleted timer was pending (1) or not (0) 4271da177e4SLinus Torvalds */ 4281da177e4SLinus Torvalds int del_virt_timer(struct vtimer_list *timer) 4291da177e4SLinus Torvalds { 4301da177e4SLinus Torvalds unsigned long flags; 4311da177e4SLinus Torvalds 4321da177e4SLinus Torvalds if (!vtimer_pending(timer)) 4331da177e4SLinus Torvalds return 0; 43427f6b416SMartin Schwidefsky spin_lock_irqsave(&virt_timer_lock, flags); 4351da177e4SLinus Torvalds list_del_init(&timer->entry); 43627f6b416SMartin Schwidefsky spin_unlock_irqrestore(&virt_timer_lock, flags); 4371da177e4SLinus Torvalds return 1; 4381da177e4SLinus Torvalds } 4391da177e4SLinus Torvalds EXPORT_SYMBOL(del_virt_timer); 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds /* 4421da177e4SLinus Torvalds * Start the virtual CPU timer on the current CPU. 4431da177e4SLinus Torvalds */ 444b5f87f15SMartin Schwidefsky void vtime_init(void) 4451da177e4SLinus Torvalds { 4468b646bd7SMartin Schwidefsky /* set initial cpu timer */ 44727f6b416SMartin Schwidefsky set_vtimer(VTIMER_MAX_SLICE); 448f341b8dfSMartin Schwidefsky /* Setup initial MT scaling values */ 449f341b8dfSMartin Schwidefsky if (smp_cpu_mtid) { 450f341b8dfSMartin Schwidefsky __this_cpu_write(mt_scaling_jiffies, jiffies); 451f341b8dfSMartin Schwidefsky __this_cpu_write(mt_scaling_mult, 1); 452f341b8dfSMartin Schwidefsky __this_cpu_write(mt_scaling_div, 1); 453346d034dSHendrik Brueckner stcctm(MT_DIAG, smp_cpu_mtid + 1, this_cpu_ptr(mt_cycles)); 454f341b8dfSMartin Schwidefsky } 4551da177e4SLinus Torvalds } 456