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>
1027f6b416SMartin Schwidefsky #include <linux/export.h>
1127f6b416SMartin Schwidefsky #include <linux/kernel.h>
1227f6b416SMartin Schwidefsky #include <linux/timex.h>
1327f6b416SMartin Schwidefsky #include <linux/types.h>
1427f6b416SMartin Schwidefsky #include <linux/time.h>
151c767347SHeiko Carstens #include <asm/alternative.h>
16c8997020SNicholas Piggin #include <asm/cputime.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
get_vtimer(void)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
set_vtimer(u64 expires)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
virt_timer_forward(u64 elapsed)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
update_mt_scaling(void)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
update_tsk_timer(unsigned long * tsk_vtime,u64 new)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
scale_vtime(u64 vtime)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
account_system_index_scaled(struct task_struct * p,u64 cputime,enum cpu_usage_stat index)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 */
do_account_vtime(struct task_struct * tsk)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;
13110bc15baSVasily Gorbik asm volatile(
13210bc15baSVasily Gorbik " stpt %0\n" /* Store current cpu timer value */
13310bc15baSVasily Gorbik " stckf %1" /* Store current tod clock value */
13410bc15baSVasily Gorbik : "=Q" (S390_lowcore.last_update_timer),
13510bc15baSVasily Gorbik "=Q" (S390_lowcore.last_update_clock)
13610bc15baSVasily Gorbik : : "cc");
137b7394a5fSMartin Schwidefsky clock = S390_lowcore.last_update_clock - clock;
138b7394a5fSMartin Schwidefsky timer -= S390_lowcore.last_update_timer;
139b7394a5fSMartin Schwidefsky
140b7394a5fSMartin Schwidefsky if (hardirq_count())
141b7394a5fSMartin Schwidefsky S390_lowcore.hardirq_timer += timer;
142b7394a5fSMartin Schwidefsky else
143b7394a5fSMartin Schwidefsky S390_lowcore.system_timer += timer;
14472d38b19SMartin Schwidefsky
14572d38b19SMartin Schwidefsky /* Update MT utilization calculation */
14672d38b19SMartin Schwidefsky if (smp_cpu_mtid &&
14772d38b19SMartin Schwidefsky time_after64(jiffies_64, this_cpu_read(mt_scaling_jiffies)))
14872d38b19SMartin Schwidefsky update_mt_scaling();
14972d38b19SMartin Schwidefsky
150b7394a5fSMartin Schwidefsky /* Calculate cputime delta */
151b7394a5fSMartin Schwidefsky user = update_tsk_timer(&tsk->thread.user_timer,
152b7394a5fSMartin Schwidefsky READ_ONCE(S390_lowcore.user_timer));
153b7394a5fSMartin Schwidefsky guest = update_tsk_timer(&tsk->thread.guest_timer,
154b7394a5fSMartin Schwidefsky READ_ONCE(S390_lowcore.guest_timer));
155b7394a5fSMartin Schwidefsky system = update_tsk_timer(&tsk->thread.system_timer,
156b7394a5fSMartin Schwidefsky READ_ONCE(S390_lowcore.system_timer));
157b7394a5fSMartin Schwidefsky hardirq = update_tsk_timer(&tsk->thread.hardirq_timer,
158b7394a5fSMartin Schwidefsky READ_ONCE(S390_lowcore.hardirq_timer));
159b7394a5fSMartin Schwidefsky softirq = update_tsk_timer(&tsk->thread.softirq_timer,
160b7394a5fSMartin Schwidefsky READ_ONCE(S390_lowcore.softirq_timer));
161b7394a5fSMartin Schwidefsky S390_lowcore.steal_timer +=
162b7394a5fSMartin Schwidefsky clock - user - guest - system - hardirq - softirq;
1631da177e4SLinus Torvalds
164b7394a5fSMartin Schwidefsky /* Push account value */
165b7394a5fSMartin Schwidefsky if (user) {
16623244a5cSFrederic Weisbecker account_user_time(tsk, cputime_to_nsecs(user));
1675613fda9SFrederic Weisbecker tsk->utimescaled += cputime_to_nsecs(scale_vtime(user));
168b7394a5fSMartin Schwidefsky }
169b7394a5fSMartin Schwidefsky
170b7394a5fSMartin Schwidefsky if (guest) {
171fb8b049cSFrederic Weisbecker account_guest_time(tsk, cputime_to_nsecs(guest));
1725613fda9SFrederic Weisbecker tsk->utimescaled += cputime_to_nsecs(scale_vtime(guest));
173b7394a5fSMartin Schwidefsky }
174b7394a5fSMartin Schwidefsky
175b7394a5fSMartin Schwidefsky if (system)
176b29e061bSMartin Schwidefsky account_system_index_scaled(tsk, system, CPUTIME_SYSTEM);
177b7394a5fSMartin Schwidefsky if (hardirq)
178b29e061bSMartin Schwidefsky account_system_index_scaled(tsk, hardirq, CPUTIME_IRQ);
179b7394a5fSMartin Schwidefsky if (softirq)
180b29e061bSMartin Schwidefsky account_system_index_scaled(tsk, softirq, CPUTIME_SOFTIRQ);
1811da177e4SLinus Torvalds
182b7394a5fSMartin Schwidefsky return virt_timer_forward(user + guest + system + hardirq + softirq);
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds
vtime_task_switch(struct task_struct * prev)185bf9fae9fSFrederic Weisbecker void vtime_task_switch(struct task_struct *prev)
1861f1c12afSMartin Schwidefsky {
1878f2b468aSMartin Schwidefsky do_account_vtime(prev);
18890c53e65SMartin Schwidefsky prev->thread.user_timer = S390_lowcore.user_timer;
189b7394a5fSMartin Schwidefsky prev->thread.guest_timer = S390_lowcore.guest_timer;
19090c53e65SMartin Schwidefsky prev->thread.system_timer = S390_lowcore.system_timer;
191b7394a5fSMartin Schwidefsky prev->thread.hardirq_timer = S390_lowcore.hardirq_timer;
192b7394a5fSMartin Schwidefsky prev->thread.softirq_timer = S390_lowcore.softirq_timer;
19390c53e65SMartin Schwidefsky S390_lowcore.user_timer = current->thread.user_timer;
194b7394a5fSMartin Schwidefsky S390_lowcore.guest_timer = current->thread.guest_timer;
19590c53e65SMartin Schwidefsky S390_lowcore.system_timer = current->thread.system_timer;
196b7394a5fSMartin Schwidefsky S390_lowcore.hardirq_timer = current->thread.hardirq_timer;
197b7394a5fSMartin Schwidefsky S390_lowcore.softirq_timer = current->thread.softirq_timer;
198aa5e97ceSMartin Schwidefsky }
1991f1c12afSMartin Schwidefsky
200bcebdf84SFrederic Weisbecker /*
201bcebdf84SFrederic Weisbecker * In s390, accounting pending user time also implies
202bcebdf84SFrederic Weisbecker * accounting system time in order to correctly compute
203bcebdf84SFrederic Weisbecker * the stolen time accounting.
204bcebdf84SFrederic Weisbecker */
vtime_flush(struct task_struct * tsk)205c8d7dabfSFrederic Weisbecker void vtime_flush(struct task_struct *tsk)
206aa5e97ceSMartin Schwidefsky {
207152e9b86SMartin Schwidefsky u64 steal, avg_steal;
208152e9b86SMartin Schwidefsky
2098f2b468aSMartin Schwidefsky if (do_account_vtime(tsk))
21027f6b416SMartin Schwidefsky virt_timer_expire();
211152e9b86SMartin Schwidefsky
212152e9b86SMartin Schwidefsky steal = S390_lowcore.steal_timer;
213*517a2390SMete Durlu avg_steal = S390_lowcore.avg_steal_timer;
214152e9b86SMartin Schwidefsky if ((s64) steal > 0) {
215152e9b86SMartin Schwidefsky S390_lowcore.steal_timer = 0;
216d54cb7d5SGerald Schaefer account_steal_time(cputime_to_nsecs(steal));
217152e9b86SMartin Schwidefsky avg_steal += steal;
218152e9b86SMartin Schwidefsky }
219*517a2390SMete Durlu S390_lowcore.avg_steal_timer = avg_steal / 2;
2201f1c12afSMartin Schwidefsky }
2211f1c12afSMartin Schwidefsky
vtime_delta(void)2228a6a5920SFrederic Weisbecker static u64 vtime_delta(void)
2238a6a5920SFrederic Weisbecker {
2248a6a5920SFrederic Weisbecker u64 timer = S390_lowcore.last_update_timer;
2258a6a5920SFrederic Weisbecker
2268a6a5920SFrederic Weisbecker S390_lowcore.last_update_timer = get_vtimer();
2278a6a5920SFrederic Weisbecker
2288a6a5920SFrederic Weisbecker return timer - S390_lowcore.last_update_timer;
2298a6a5920SFrederic Weisbecker }
2308a6a5920SFrederic Weisbecker
2311f1c12afSMartin Schwidefsky /*
2321f1c12afSMartin Schwidefsky * Update process times based on virtual cpu times stored by entry.S
2331f1c12afSMartin Schwidefsky * to the lowcore fields user_timer, system_timer & steal_clock.
2341f1c12afSMartin Schwidefsky */
vtime_account_kernel(struct task_struct * tsk)2357197688bSFrederic Weisbecker void vtime_account_kernel(struct task_struct *tsk)
2361da177e4SLinus Torvalds {
2378a6a5920SFrederic Weisbecker u64 delta = vtime_delta();
2381da177e4SLinus Torvalds
2398a6a5920SFrederic Weisbecker if (tsk->flags & PF_VCPU)
2408a6a5920SFrederic Weisbecker S390_lowcore.guest_timer += delta;
241b7394a5fSMartin Schwidefsky else
2428a6a5920SFrederic Weisbecker S390_lowcore.system_timer += delta;
24372d38b19SMartin Schwidefsky
2448a6a5920SFrederic Weisbecker virt_timer_forward(delta);
2451da177e4SLinus Torvalds }
246f83eeb1aSFrederic Weisbecker EXPORT_SYMBOL_GPL(vtime_account_kernel);
24711113334SFrederic Weisbecker
vtime_account_softirq(struct task_struct * tsk)2488a6a5920SFrederic Weisbecker void vtime_account_softirq(struct task_struct *tsk)
2498a6a5920SFrederic Weisbecker {
2508a6a5920SFrederic Weisbecker u64 delta = vtime_delta();
2518a6a5920SFrederic Weisbecker
2528a6a5920SFrederic Weisbecker S390_lowcore.softirq_timer += delta;
2538a6a5920SFrederic Weisbecker
2548a6a5920SFrederic Weisbecker virt_timer_forward(delta);
2558a6a5920SFrederic Weisbecker }
2568a6a5920SFrederic Weisbecker
vtime_account_hardirq(struct task_struct * tsk)2578a6a5920SFrederic Weisbecker void vtime_account_hardirq(struct task_struct *tsk)
2588a6a5920SFrederic Weisbecker {
2598a6a5920SFrederic Weisbecker u64 delta = vtime_delta();
2608a6a5920SFrederic Weisbecker
2618a6a5920SFrederic Weisbecker S390_lowcore.hardirq_timer += delta;
2628a6a5920SFrederic Weisbecker
2638a6a5920SFrederic Weisbecker virt_timer_forward(delta);
2648a6a5920SFrederic Weisbecker }
2658a6a5920SFrederic Weisbecker
2661da177e4SLinus Torvalds /*
2671da177e4SLinus Torvalds * Sorted add to a list. List is linear searched until first bigger
2681da177e4SLinus Torvalds * element is found.
2691da177e4SLinus Torvalds */
list_add_sorted(struct vtimer_list * timer,struct list_head * head)2701da177e4SLinus Torvalds static void list_add_sorted(struct vtimer_list *timer, struct list_head *head)
2711da177e4SLinus Torvalds {
27227f6b416SMartin Schwidefsky struct vtimer_list *tmp;
2731da177e4SLinus Torvalds
27427f6b416SMartin Schwidefsky list_for_each_entry(tmp, head, entry) {
27527f6b416SMartin Schwidefsky if (tmp->expires > timer->expires) {
27627f6b416SMartin Schwidefsky list_add_tail(&timer->entry, &tmp->entry);
2771da177e4SLinus Torvalds return;
2781da177e4SLinus Torvalds }
2791da177e4SLinus Torvalds }
2801da177e4SLinus Torvalds list_add_tail(&timer->entry, head);
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds
2831da177e4SLinus Torvalds /*
28427f6b416SMartin Schwidefsky * Handler for expired virtual CPU timer.
2851da177e4SLinus Torvalds */
virt_timer_expire(void)28627f6b416SMartin Schwidefsky static void virt_timer_expire(void)
2871da177e4SLinus Torvalds {
28827f6b416SMartin Schwidefsky struct vtimer_list *timer, *tmp;
28927f6b416SMartin Schwidefsky unsigned long elapsed;
29027f6b416SMartin Schwidefsky LIST_HEAD(cb_list);
2911da177e4SLinus Torvalds
29227f6b416SMartin Schwidefsky /* walk timer list, fire all expired timers */
29327f6b416SMartin Schwidefsky spin_lock(&virt_timer_lock);
29427f6b416SMartin Schwidefsky elapsed = atomic64_read(&virt_timer_elapsed);
29527f6b416SMartin Schwidefsky list_for_each_entry_safe(timer, tmp, &virt_timer_list, entry) {
29627f6b416SMartin Schwidefsky if (timer->expires < elapsed)
2971da177e4SLinus Torvalds /* move expired timer to the callback queue */
29827f6b416SMartin Schwidefsky list_move_tail(&timer->entry, &cb_list);
2999cfb9b3cSMartin Schwidefsky else
30027f6b416SMartin Schwidefsky timer->expires -= elapsed;
3011da177e4SLinus Torvalds }
30227f6b416SMartin Schwidefsky if (!list_empty(&virt_timer_list)) {
30327f6b416SMartin Schwidefsky timer = list_first_entry(&virt_timer_list,
30427f6b416SMartin Schwidefsky struct vtimer_list, entry);
30527f6b416SMartin Schwidefsky atomic64_set(&virt_timer_current, timer->expires);
3064c1051e3SMartin Schwidefsky }
30727f6b416SMartin Schwidefsky atomic64_sub(elapsed, &virt_timer_elapsed);
30827f6b416SMartin Schwidefsky spin_unlock(&virt_timer_lock);
30927f6b416SMartin Schwidefsky
31027f6b416SMartin Schwidefsky /* Do callbacks and recharge periodic timers */
31127f6b416SMartin Schwidefsky list_for_each_entry_safe(timer, tmp, &cb_list, entry) {
31227f6b416SMartin Schwidefsky list_del_init(&timer->entry);
31327f6b416SMartin Schwidefsky timer->function(timer->data);
31427f6b416SMartin Schwidefsky if (timer->interval) {
31527f6b416SMartin Schwidefsky /* Recharge interval timer */
31627f6b416SMartin Schwidefsky timer->expires = timer->interval +
31727f6b416SMartin Schwidefsky atomic64_read(&virt_timer_elapsed);
31827f6b416SMartin Schwidefsky spin_lock(&virt_timer_lock);
31927f6b416SMartin Schwidefsky list_add_sorted(timer, &virt_timer_list);
32027f6b416SMartin Schwidefsky spin_unlock(&virt_timer_lock);
32127f6b416SMartin Schwidefsky }
32227f6b416SMartin Schwidefsky }
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds
init_virt_timer(struct vtimer_list * timer)3251da177e4SLinus Torvalds void init_virt_timer(struct vtimer_list *timer)
3261da177e4SLinus Torvalds {
3271da177e4SLinus Torvalds timer->function = NULL;
3281da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->entry);
3291da177e4SLinus Torvalds }
3301da177e4SLinus Torvalds EXPORT_SYMBOL(init_virt_timer);
3311da177e4SLinus Torvalds
vtimer_pending(struct vtimer_list * timer)3321da177e4SLinus Torvalds static inline int vtimer_pending(struct vtimer_list *timer)
3331da177e4SLinus Torvalds {
33427f6b416SMartin Schwidefsky return !list_empty(&timer->entry);
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds
internal_add_vtimer(struct vtimer_list * timer)3371da177e4SLinus Torvalds static void internal_add_vtimer(struct vtimer_list *timer)
3381da177e4SLinus Torvalds {
33927f6b416SMartin Schwidefsky if (list_empty(&virt_timer_list)) {
34027f6b416SMartin Schwidefsky /* First timer, just program it. */
34127f6b416SMartin Schwidefsky atomic64_set(&virt_timer_current, timer->expires);
34227f6b416SMartin Schwidefsky atomic64_set(&virt_timer_elapsed, 0);
34327f6b416SMartin Schwidefsky list_add(&timer->entry, &virt_timer_list);
3449cfb9b3cSMartin Schwidefsky } else {
34527f6b416SMartin Schwidefsky /* Update timer against current base. */
34627f6b416SMartin Schwidefsky timer->expires += atomic64_read(&virt_timer_elapsed);
34727f6b416SMartin Schwidefsky if (likely((s64) timer->expires <
34827f6b416SMartin Schwidefsky (s64) atomic64_read(&virt_timer_current)))
3499cfb9b3cSMartin Schwidefsky /* The new timer expires before the current timer. */
35027f6b416SMartin Schwidefsky atomic64_set(&virt_timer_current, timer->expires);
35127f6b416SMartin Schwidefsky /* Insert new timer into the list. */
35227f6b416SMartin Schwidefsky list_add_sorted(timer, &virt_timer_list);
3539cfb9b3cSMartin Schwidefsky }
3541da177e4SLinus Torvalds }
3551da177e4SLinus Torvalds
__add_vtimer(struct vtimer_list * timer,int periodic)35627f6b416SMartin Schwidefsky static void __add_vtimer(struct vtimer_list *timer, int periodic)
3571da177e4SLinus Torvalds {
35827f6b416SMartin Schwidefsky unsigned long flags;
35927f6b416SMartin Schwidefsky
36027f6b416SMartin Schwidefsky timer->interval = periodic ? timer->expires : 0;
36127f6b416SMartin Schwidefsky spin_lock_irqsave(&virt_timer_lock, flags);
36227f6b416SMartin Schwidefsky internal_add_vtimer(timer);
36327f6b416SMartin Schwidefsky spin_unlock_irqrestore(&virt_timer_lock, flags);
3641da177e4SLinus Torvalds }
3651da177e4SLinus Torvalds
3661da177e4SLinus Torvalds /*
3670f5e1558SMasahiro Yamada * add_virt_timer - add a oneshot virtual CPU timer
3681da177e4SLinus Torvalds */
add_virt_timer(struct vtimer_list * timer)36927f6b416SMartin Schwidefsky void add_virt_timer(struct vtimer_list *timer)
3701da177e4SLinus Torvalds {
37127f6b416SMartin Schwidefsky __add_vtimer(timer, 0);
3721da177e4SLinus Torvalds }
3731da177e4SLinus Torvalds EXPORT_SYMBOL(add_virt_timer);
3741da177e4SLinus Torvalds
3751da177e4SLinus Torvalds /*
3761da177e4SLinus Torvalds * add_virt_timer_int - add an interval virtual CPU timer
3771da177e4SLinus Torvalds */
add_virt_timer_periodic(struct vtimer_list * timer)37827f6b416SMartin Schwidefsky void add_virt_timer_periodic(struct vtimer_list *timer)
3791da177e4SLinus Torvalds {
38027f6b416SMartin Schwidefsky __add_vtimer(timer, 1);
3811da177e4SLinus Torvalds }
3821da177e4SLinus Torvalds EXPORT_SYMBOL(add_virt_timer_periodic);
3831da177e4SLinus Torvalds
__mod_vtimer(struct vtimer_list * timer,u64 expires,int periodic)38427f6b416SMartin Schwidefsky static int __mod_vtimer(struct vtimer_list *timer, u64 expires, int periodic)
3851da177e4SLinus Torvalds {
3861da177e4SLinus Torvalds unsigned long flags;
38727f6b416SMartin Schwidefsky int rc;
3881da177e4SLinus Torvalds
389ca366a32SMartin Schwidefsky BUG_ON(!timer->function);
3901da177e4SLinus Torvalds
3911da177e4SLinus Torvalds if (timer->expires == expires && vtimer_pending(timer))
3921da177e4SLinus Torvalds return 1;
39327f6b416SMartin Schwidefsky spin_lock_irqsave(&virt_timer_lock, flags);
39427f6b416SMartin Schwidefsky rc = vtimer_pending(timer);
39527f6b416SMartin Schwidefsky if (rc)
3961da177e4SLinus Torvalds list_del_init(&timer->entry);
39727f6b416SMartin Schwidefsky timer->interval = periodic ? expires : 0;
3981da177e4SLinus Torvalds timer->expires = expires;
3991da177e4SLinus Torvalds internal_add_vtimer(timer);
40027f6b416SMartin Schwidefsky spin_unlock_irqrestore(&virt_timer_lock, flags);
40127f6b416SMartin Schwidefsky return rc;
4021da177e4SLinus Torvalds }
403b6ecfa92SJan Glauber
404b6ecfa92SJan Glauber /*
405b6ecfa92SJan Glauber * returns whether it has modified a pending timer (1) or not (0)
406b6ecfa92SJan Glauber */
mod_virt_timer(struct vtimer_list * timer,u64 expires)40727f6b416SMartin Schwidefsky int mod_virt_timer(struct vtimer_list *timer, u64 expires)
408b6ecfa92SJan Glauber {
409b6ecfa92SJan Glauber return __mod_vtimer(timer, expires, 0);
410b6ecfa92SJan Glauber }
4111da177e4SLinus Torvalds EXPORT_SYMBOL(mod_virt_timer);
4121da177e4SLinus Torvalds
4131da177e4SLinus Torvalds /*
414b6ecfa92SJan Glauber * returns whether it has modified a pending timer (1) or not (0)
415b6ecfa92SJan Glauber */
mod_virt_timer_periodic(struct vtimer_list * timer,u64 expires)41627f6b416SMartin Schwidefsky int mod_virt_timer_periodic(struct vtimer_list *timer, u64 expires)
417b6ecfa92SJan Glauber {
418b6ecfa92SJan Glauber return __mod_vtimer(timer, expires, 1);
419b6ecfa92SJan Glauber }
420b6ecfa92SJan Glauber EXPORT_SYMBOL(mod_virt_timer_periodic);
421b6ecfa92SJan Glauber
422b6ecfa92SJan Glauber /*
42327f6b416SMartin Schwidefsky * Delete a virtual timer.
4241da177e4SLinus Torvalds *
4251da177e4SLinus Torvalds * returns whether the deleted timer was pending (1) or not (0)
4261da177e4SLinus Torvalds */
del_virt_timer(struct vtimer_list * timer)4271da177e4SLinus Torvalds int del_virt_timer(struct vtimer_list *timer)
4281da177e4SLinus Torvalds {
4291da177e4SLinus Torvalds unsigned long flags;
4301da177e4SLinus Torvalds
4311da177e4SLinus Torvalds if (!vtimer_pending(timer))
4321da177e4SLinus Torvalds return 0;
43327f6b416SMartin Schwidefsky spin_lock_irqsave(&virt_timer_lock, flags);
4341da177e4SLinus Torvalds list_del_init(&timer->entry);
43527f6b416SMartin Schwidefsky spin_unlock_irqrestore(&virt_timer_lock, flags);
4361da177e4SLinus Torvalds return 1;
4371da177e4SLinus Torvalds }
4381da177e4SLinus Torvalds EXPORT_SYMBOL(del_virt_timer);
4391da177e4SLinus Torvalds
4401da177e4SLinus Torvalds /*
4411da177e4SLinus Torvalds * Start the virtual CPU timer on the current CPU.
4421da177e4SLinus Torvalds */
vtime_init(void)443b5f87f15SMartin Schwidefsky void vtime_init(void)
4441da177e4SLinus Torvalds {
4458b646bd7SMartin Schwidefsky /* set initial cpu timer */
44627f6b416SMartin Schwidefsky set_vtimer(VTIMER_MAX_SLICE);
447f341b8dfSMartin Schwidefsky /* Setup initial MT scaling values */
448f341b8dfSMartin Schwidefsky if (smp_cpu_mtid) {
449f341b8dfSMartin Schwidefsky __this_cpu_write(mt_scaling_jiffies, jiffies);
450f341b8dfSMartin Schwidefsky __this_cpu_write(mt_scaling_mult, 1);
451f341b8dfSMartin Schwidefsky __this_cpu_write(mt_scaling_div, 1);
452346d034dSHendrik Brueckner stcctm(MT_DIAG, smp_cpu_mtid + 1, this_cpu_ptr(mt_cycles));
453f341b8dfSMartin Schwidefsky }
4541da177e4SLinus Torvalds }
455