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
set_vtimer(u64 expires)3627f6b416SMartin Schwidefsky static inline void set_vtimer(u64 expires)
379cfb9b3cSMartin Schwidefsky {
38*46c30311SSven Schnelle struct lowcore *lc = get_lowcore();
3927f6b416SMartin Schwidefsky u64 timer;
409cfb9b3cSMartin Schwidefsky
4127f6b416SMartin Schwidefsky asm volatile(
4227f6b416SMartin Schwidefsky " stpt %0\n" /* Store current cpu timer value */
4327f6b416SMartin Schwidefsky " spt %1" /* Set new value imm. afterwards */
4435af0d46SVasily Gorbik : "=Q" (timer) : "Q" (expires));
45*46c30311SSven Schnelle lc->system_timer += lc->last_update_timer - timer;
46*46c30311SSven Schnelle lc->last_update_timer = expires;
479cfb9b3cSMartin Schwidefsky }
489cfb9b3cSMartin Schwidefsky
virt_timer_forward(u64 elapsed)4927f6b416SMartin Schwidefsky static inline int virt_timer_forward(u64 elapsed)
5027f6b416SMartin Schwidefsky {
5127f6b416SMartin Schwidefsky BUG_ON(!irqs_disabled());
5227f6b416SMartin Schwidefsky
5327f6b416SMartin Schwidefsky if (list_empty(&virt_timer_list))
5427f6b416SMartin Schwidefsky return 0;
5527f6b416SMartin Schwidefsky elapsed = atomic64_add_return(elapsed, &virt_timer_elapsed);
5627f6b416SMartin Schwidefsky return elapsed >= atomic64_read(&virt_timer_current);
5727f6b416SMartin Schwidefsky }
5827f6b416SMartin Schwidefsky
update_mt_scaling(void)5972d38b19SMartin Schwidefsky static void update_mt_scaling(void)
601da177e4SLinus Torvalds {
6172d38b19SMartin Schwidefsky u64 cycles_new[8], *cycles_old;
6272d38b19SMartin Schwidefsky u64 delta, fac, mult, div;
6310ad34bcSMartin Schwidefsky int i;
641da177e4SLinus Torvalds
65346d034dSHendrik Brueckner stcctm(MT_DIAG, smp_cpu_mtid + 1, cycles_new);
6610ad34bcSMartin Schwidefsky cycles_old = this_cpu_ptr(mt_cycles);
6761cc3790SMartin Schwidefsky fac = 1;
6810ad34bcSMartin Schwidefsky mult = div = 0;
6910ad34bcSMartin Schwidefsky for (i = 0; i <= smp_cpu_mtid; i++) {
7010ad34bcSMartin Schwidefsky delta = cycles_new[i] - cycles_old[i];
7161cc3790SMartin Schwidefsky div += delta;
7261cc3790SMartin Schwidefsky mult *= i + 1;
7361cc3790SMartin Schwidefsky mult += delta * fac;
7461cc3790SMartin Schwidefsky fac *= i + 1;
7510ad34bcSMartin Schwidefsky }
7661cc3790SMartin Schwidefsky div *= fac;
7761cc3790SMartin Schwidefsky if (div > 0) {
7810ad34bcSMartin Schwidefsky /* Update scaling factor */
7910ad34bcSMartin Schwidefsky __this_cpu_write(mt_scaling_mult, mult);
8010ad34bcSMartin Schwidefsky __this_cpu_write(mt_scaling_div, div);
8110ad34bcSMartin Schwidefsky memcpy(cycles_old, cycles_new,
8210ad34bcSMartin Schwidefsky sizeof(u64) * (smp_cpu_mtid + 1));
8310ad34bcSMartin Schwidefsky }
84f341b8dfSMartin Schwidefsky __this_cpu_write(mt_scaling_jiffies, jiffies_64);
8510ad34bcSMartin Schwidefsky }
8610ad34bcSMartin Schwidefsky
update_tsk_timer(unsigned long * tsk_vtime,u64 new)87b7394a5fSMartin Schwidefsky static inline u64 update_tsk_timer(unsigned long *tsk_vtime, u64 new)
88b7394a5fSMartin Schwidefsky {
89b7394a5fSMartin Schwidefsky u64 delta;
90b7394a5fSMartin Schwidefsky
91b7394a5fSMartin Schwidefsky delta = new - *tsk_vtime;
92b7394a5fSMartin Schwidefsky *tsk_vtime = new;
93b7394a5fSMartin Schwidefsky return delta;
94b7394a5fSMartin Schwidefsky }
95b7394a5fSMartin Schwidefsky
96b7394a5fSMartin Schwidefsky
scale_vtime(u64 vtime)97b7394a5fSMartin Schwidefsky static inline u64 scale_vtime(u64 vtime)
98b7394a5fSMartin Schwidefsky {
99b7394a5fSMartin Schwidefsky u64 mult = __this_cpu_read(mt_scaling_mult);
100b7394a5fSMartin Schwidefsky u64 div = __this_cpu_read(mt_scaling_div);
101b7394a5fSMartin Schwidefsky
102b7394a5fSMartin Schwidefsky if (smp_cpu_mtid)
103b7394a5fSMartin Schwidefsky return vtime * mult / div;
104b7394a5fSMartin Schwidefsky return vtime;
105b7394a5fSMartin Schwidefsky }
106b7394a5fSMartin Schwidefsky
account_system_index_scaled(struct task_struct * p,u64 cputime,enum cpu_usage_stat index)107b29e061bSMartin Schwidefsky static void account_system_index_scaled(struct task_struct *p, u64 cputime,
108b7394a5fSMartin Schwidefsky enum cpu_usage_stat index)
109b7394a5fSMartin Schwidefsky {
110b29e061bSMartin Schwidefsky p->stimescaled += cputime_to_nsecs(scale_vtime(cputime));
111fb8b049cSFrederic Weisbecker account_system_index_time(p, cputime_to_nsecs(cputime), index);
112b7394a5fSMartin Schwidefsky }
113b7394a5fSMartin Schwidefsky
11472d38b19SMartin Schwidefsky /*
11572d38b19SMartin Schwidefsky * Update process times based on virtual cpu times stored by entry.S
11672d38b19SMartin Schwidefsky * to the lowcore fields user_timer, system_timer & steal_clock.
11772d38b19SMartin Schwidefsky */
do_account_vtime(struct task_struct * tsk)1188f2b468aSMartin Schwidefsky static int do_account_vtime(struct task_struct *tsk)
11972d38b19SMartin Schwidefsky {
120152e9b86SMartin Schwidefsky u64 timer, clock, user, guest, system, hardirq, softirq;
121*46c30311SSven Schnelle struct lowcore *lc = get_lowcore();
12272d38b19SMartin Schwidefsky
123*46c30311SSven Schnelle timer = lc->last_update_timer;
124*46c30311SSven Schnelle clock = lc->last_update_clock;
12510bc15baSVasily Gorbik asm volatile(
12610bc15baSVasily Gorbik " stpt %0\n" /* Store current cpu timer value */
12710bc15baSVasily Gorbik " stckf %1" /* Store current tod clock value */
128*46c30311SSven Schnelle : "=Q" (lc->last_update_timer),
129*46c30311SSven Schnelle "=Q" (lc->last_update_clock)
13010bc15baSVasily Gorbik : : "cc");
131*46c30311SSven Schnelle clock = lc->last_update_clock - clock;
132*46c30311SSven Schnelle timer -= lc->last_update_timer;
133b7394a5fSMartin Schwidefsky
134b7394a5fSMartin Schwidefsky if (hardirq_count())
135*46c30311SSven Schnelle lc->hardirq_timer += timer;
136b7394a5fSMartin Schwidefsky else
137*46c30311SSven Schnelle lc->system_timer += timer;
13872d38b19SMartin Schwidefsky
13972d38b19SMartin Schwidefsky /* Update MT utilization calculation */
14072d38b19SMartin Schwidefsky if (smp_cpu_mtid &&
14172d38b19SMartin Schwidefsky time_after64(jiffies_64, this_cpu_read(mt_scaling_jiffies)))
14272d38b19SMartin Schwidefsky update_mt_scaling();
14372d38b19SMartin Schwidefsky
144b7394a5fSMartin Schwidefsky /* Calculate cputime delta */
145b7394a5fSMartin Schwidefsky user = update_tsk_timer(&tsk->thread.user_timer,
146*46c30311SSven Schnelle READ_ONCE(lc->user_timer));
147b7394a5fSMartin Schwidefsky guest = update_tsk_timer(&tsk->thread.guest_timer,
148*46c30311SSven Schnelle READ_ONCE(lc->guest_timer));
149b7394a5fSMartin Schwidefsky system = update_tsk_timer(&tsk->thread.system_timer,
150*46c30311SSven Schnelle READ_ONCE(lc->system_timer));
151b7394a5fSMartin Schwidefsky hardirq = update_tsk_timer(&tsk->thread.hardirq_timer,
152*46c30311SSven Schnelle READ_ONCE(lc->hardirq_timer));
153b7394a5fSMartin Schwidefsky softirq = update_tsk_timer(&tsk->thread.softirq_timer,
154*46c30311SSven Schnelle READ_ONCE(lc->softirq_timer));
155*46c30311SSven Schnelle lc->steal_timer +=
156b7394a5fSMartin Schwidefsky clock - user - guest - system - hardirq - softirq;
1571da177e4SLinus Torvalds
158b7394a5fSMartin Schwidefsky /* Push account value */
159b7394a5fSMartin Schwidefsky if (user) {
16023244a5cSFrederic Weisbecker account_user_time(tsk, cputime_to_nsecs(user));
1615613fda9SFrederic Weisbecker tsk->utimescaled += cputime_to_nsecs(scale_vtime(user));
162b7394a5fSMartin Schwidefsky }
163b7394a5fSMartin Schwidefsky
164b7394a5fSMartin Schwidefsky if (guest) {
165fb8b049cSFrederic Weisbecker account_guest_time(tsk, cputime_to_nsecs(guest));
1665613fda9SFrederic Weisbecker tsk->utimescaled += cputime_to_nsecs(scale_vtime(guest));
167b7394a5fSMartin Schwidefsky }
168b7394a5fSMartin Schwidefsky
169b7394a5fSMartin Schwidefsky if (system)
170b29e061bSMartin Schwidefsky account_system_index_scaled(tsk, system, CPUTIME_SYSTEM);
171b7394a5fSMartin Schwidefsky if (hardirq)
172b29e061bSMartin Schwidefsky account_system_index_scaled(tsk, hardirq, CPUTIME_IRQ);
173b7394a5fSMartin Schwidefsky if (softirq)
174b29e061bSMartin Schwidefsky account_system_index_scaled(tsk, softirq, CPUTIME_SOFTIRQ);
1751da177e4SLinus Torvalds
176b7394a5fSMartin Schwidefsky return virt_timer_forward(user + guest + system + hardirq + softirq);
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds
vtime_task_switch(struct task_struct * prev)179bf9fae9fSFrederic Weisbecker void vtime_task_switch(struct task_struct *prev)
1801f1c12afSMartin Schwidefsky {
181*46c30311SSven Schnelle struct lowcore *lc = get_lowcore();
182*46c30311SSven Schnelle
1838f2b468aSMartin Schwidefsky do_account_vtime(prev);
184*46c30311SSven Schnelle prev->thread.user_timer = lc->user_timer;
185*46c30311SSven Schnelle prev->thread.guest_timer = lc->guest_timer;
186*46c30311SSven Schnelle prev->thread.system_timer = lc->system_timer;
187*46c30311SSven Schnelle prev->thread.hardirq_timer = lc->hardirq_timer;
188*46c30311SSven Schnelle prev->thread.softirq_timer = lc->softirq_timer;
189*46c30311SSven Schnelle lc->user_timer = current->thread.user_timer;
190*46c30311SSven Schnelle lc->guest_timer = current->thread.guest_timer;
191*46c30311SSven Schnelle lc->system_timer = current->thread.system_timer;
192*46c30311SSven Schnelle lc->hardirq_timer = current->thread.hardirq_timer;
193*46c30311SSven Schnelle lc->softirq_timer = current->thread.softirq_timer;
194aa5e97ceSMartin Schwidefsky }
1951f1c12afSMartin Schwidefsky
196bcebdf84SFrederic Weisbecker /*
197bcebdf84SFrederic Weisbecker * In s390, accounting pending user time also implies
198bcebdf84SFrederic Weisbecker * accounting system time in order to correctly compute
199bcebdf84SFrederic Weisbecker * the stolen time accounting.
200bcebdf84SFrederic Weisbecker */
vtime_flush(struct task_struct * tsk)201c8d7dabfSFrederic Weisbecker void vtime_flush(struct task_struct *tsk)
202aa5e97ceSMartin Schwidefsky {
203*46c30311SSven Schnelle struct lowcore *lc = get_lowcore();
204152e9b86SMartin Schwidefsky u64 steal, avg_steal;
205152e9b86SMartin Schwidefsky
2068f2b468aSMartin Schwidefsky if (do_account_vtime(tsk))
20727f6b416SMartin Schwidefsky virt_timer_expire();
208152e9b86SMartin Schwidefsky
209*46c30311SSven Schnelle steal = lc->steal_timer;
210*46c30311SSven Schnelle avg_steal = lc->avg_steal_timer;
211152e9b86SMartin Schwidefsky if ((s64) steal > 0) {
212*46c30311SSven Schnelle lc->steal_timer = 0;
213d54cb7d5SGerald Schaefer account_steal_time(cputime_to_nsecs(steal));
214152e9b86SMartin Schwidefsky avg_steal += steal;
215152e9b86SMartin Schwidefsky }
216*46c30311SSven Schnelle lc->avg_steal_timer = avg_steal / 2;
2171f1c12afSMartin Schwidefsky }
2181f1c12afSMartin Schwidefsky
vtime_delta(void)2198a6a5920SFrederic Weisbecker static u64 vtime_delta(void)
2208a6a5920SFrederic Weisbecker {
221*46c30311SSven Schnelle struct lowcore *lc = get_lowcore();
222*46c30311SSven Schnelle u64 timer = lc->last_update_timer;
2238a6a5920SFrederic Weisbecker
224*46c30311SSven Schnelle lc->last_update_timer = get_cpu_timer();
225*46c30311SSven Schnelle return timer - lc->last_update_timer;
2268a6a5920SFrederic Weisbecker }
2278a6a5920SFrederic Weisbecker
2281f1c12afSMartin Schwidefsky /*
2291f1c12afSMartin Schwidefsky * Update process times based on virtual cpu times stored by entry.S
2301f1c12afSMartin Schwidefsky * to the lowcore fields user_timer, system_timer & steal_clock.
2311f1c12afSMartin Schwidefsky */
vtime_account_kernel(struct task_struct * tsk)2327197688bSFrederic Weisbecker void vtime_account_kernel(struct task_struct *tsk)
2331da177e4SLinus Torvalds {
234*46c30311SSven Schnelle struct lowcore *lc = get_lowcore();
2358a6a5920SFrederic Weisbecker u64 delta = vtime_delta();
2361da177e4SLinus Torvalds
2378a6a5920SFrederic Weisbecker if (tsk->flags & PF_VCPU)
238*46c30311SSven Schnelle lc->guest_timer += delta;
239b7394a5fSMartin Schwidefsky else
240*46c30311SSven Schnelle lc->system_timer += delta;
24172d38b19SMartin Schwidefsky
2428a6a5920SFrederic Weisbecker virt_timer_forward(delta);
2431da177e4SLinus Torvalds }
244f83eeb1aSFrederic Weisbecker EXPORT_SYMBOL_GPL(vtime_account_kernel);
24511113334SFrederic Weisbecker
vtime_account_softirq(struct task_struct * tsk)2468a6a5920SFrederic Weisbecker void vtime_account_softirq(struct task_struct *tsk)
2478a6a5920SFrederic Weisbecker {
2488a6a5920SFrederic Weisbecker u64 delta = vtime_delta();
2498a6a5920SFrederic Weisbecker
250208da1d5SSven Schnelle get_lowcore()->softirq_timer += delta;
2518a6a5920SFrederic Weisbecker
2528a6a5920SFrederic Weisbecker virt_timer_forward(delta);
2538a6a5920SFrederic Weisbecker }
2548a6a5920SFrederic Weisbecker
vtime_account_hardirq(struct task_struct * tsk)2558a6a5920SFrederic Weisbecker void vtime_account_hardirq(struct task_struct *tsk)
2568a6a5920SFrederic Weisbecker {
2578a6a5920SFrederic Weisbecker u64 delta = vtime_delta();
2588a6a5920SFrederic Weisbecker
259208da1d5SSven Schnelle get_lowcore()->hardirq_timer += delta;
2608a6a5920SFrederic Weisbecker
2618a6a5920SFrederic Weisbecker virt_timer_forward(delta);
2628a6a5920SFrederic Weisbecker }
2638a6a5920SFrederic Weisbecker
2641da177e4SLinus Torvalds /*
2651da177e4SLinus Torvalds * Sorted add to a list. List is linear searched until first bigger
2661da177e4SLinus Torvalds * element is found.
2671da177e4SLinus Torvalds */
list_add_sorted(struct vtimer_list * timer,struct list_head * head)2681da177e4SLinus Torvalds static void list_add_sorted(struct vtimer_list *timer, struct list_head *head)
2691da177e4SLinus Torvalds {
27027f6b416SMartin Schwidefsky struct vtimer_list *tmp;
2711da177e4SLinus Torvalds
27227f6b416SMartin Schwidefsky list_for_each_entry(tmp, head, entry) {
27327f6b416SMartin Schwidefsky if (tmp->expires > timer->expires) {
27427f6b416SMartin Schwidefsky list_add_tail(&timer->entry, &tmp->entry);
2751da177e4SLinus Torvalds return;
2761da177e4SLinus Torvalds }
2771da177e4SLinus Torvalds }
2781da177e4SLinus Torvalds list_add_tail(&timer->entry, head);
2791da177e4SLinus Torvalds }
2801da177e4SLinus Torvalds
2811da177e4SLinus Torvalds /*
28227f6b416SMartin Schwidefsky * Handler for expired virtual CPU timer.
2831da177e4SLinus Torvalds */
virt_timer_expire(void)28427f6b416SMartin Schwidefsky static void virt_timer_expire(void)
2851da177e4SLinus Torvalds {
28627f6b416SMartin Schwidefsky struct vtimer_list *timer, *tmp;
28727f6b416SMartin Schwidefsky unsigned long elapsed;
28827f6b416SMartin Schwidefsky LIST_HEAD(cb_list);
2891da177e4SLinus Torvalds
29027f6b416SMartin Schwidefsky /* walk timer list, fire all expired timers */
29127f6b416SMartin Schwidefsky spin_lock(&virt_timer_lock);
29227f6b416SMartin Schwidefsky elapsed = atomic64_read(&virt_timer_elapsed);
29327f6b416SMartin Schwidefsky list_for_each_entry_safe(timer, tmp, &virt_timer_list, entry) {
29427f6b416SMartin Schwidefsky if (timer->expires < elapsed)
2951da177e4SLinus Torvalds /* move expired timer to the callback queue */
29627f6b416SMartin Schwidefsky list_move_tail(&timer->entry, &cb_list);
2979cfb9b3cSMartin Schwidefsky else
29827f6b416SMartin Schwidefsky timer->expires -= elapsed;
2991da177e4SLinus Torvalds }
30027f6b416SMartin Schwidefsky if (!list_empty(&virt_timer_list)) {
30127f6b416SMartin Schwidefsky timer = list_first_entry(&virt_timer_list,
30227f6b416SMartin Schwidefsky struct vtimer_list, entry);
30327f6b416SMartin Schwidefsky atomic64_set(&virt_timer_current, timer->expires);
3044c1051e3SMartin Schwidefsky }
30527f6b416SMartin Schwidefsky atomic64_sub(elapsed, &virt_timer_elapsed);
30627f6b416SMartin Schwidefsky spin_unlock(&virt_timer_lock);
30727f6b416SMartin Schwidefsky
30827f6b416SMartin Schwidefsky /* Do callbacks and recharge periodic timers */
30927f6b416SMartin Schwidefsky list_for_each_entry_safe(timer, tmp, &cb_list, entry) {
31027f6b416SMartin Schwidefsky list_del_init(&timer->entry);
31127f6b416SMartin Schwidefsky timer->function(timer->data);
31227f6b416SMartin Schwidefsky if (timer->interval) {
31327f6b416SMartin Schwidefsky /* Recharge interval timer */
31427f6b416SMartin Schwidefsky timer->expires = timer->interval +
31527f6b416SMartin Schwidefsky atomic64_read(&virt_timer_elapsed);
31627f6b416SMartin Schwidefsky spin_lock(&virt_timer_lock);
31727f6b416SMartin Schwidefsky list_add_sorted(timer, &virt_timer_list);
31827f6b416SMartin Schwidefsky spin_unlock(&virt_timer_lock);
31927f6b416SMartin Schwidefsky }
32027f6b416SMartin Schwidefsky }
3211da177e4SLinus Torvalds }
3221da177e4SLinus Torvalds
init_virt_timer(struct vtimer_list * timer)3231da177e4SLinus Torvalds void init_virt_timer(struct vtimer_list *timer)
3241da177e4SLinus Torvalds {
3251da177e4SLinus Torvalds timer->function = NULL;
3261da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->entry);
3271da177e4SLinus Torvalds }
3281da177e4SLinus Torvalds EXPORT_SYMBOL(init_virt_timer);
3291da177e4SLinus Torvalds
vtimer_pending(struct vtimer_list * timer)3301da177e4SLinus Torvalds static inline int vtimer_pending(struct vtimer_list *timer)
3311da177e4SLinus Torvalds {
33227f6b416SMartin Schwidefsky return !list_empty(&timer->entry);
3331da177e4SLinus Torvalds }
3341da177e4SLinus Torvalds
internal_add_vtimer(struct vtimer_list * timer)3351da177e4SLinus Torvalds static void internal_add_vtimer(struct vtimer_list *timer)
3361da177e4SLinus Torvalds {
33727f6b416SMartin Schwidefsky if (list_empty(&virt_timer_list)) {
33827f6b416SMartin Schwidefsky /* First timer, just program it. */
33927f6b416SMartin Schwidefsky atomic64_set(&virt_timer_current, timer->expires);
34027f6b416SMartin Schwidefsky atomic64_set(&virt_timer_elapsed, 0);
34127f6b416SMartin Schwidefsky list_add(&timer->entry, &virt_timer_list);
3429cfb9b3cSMartin Schwidefsky } else {
34327f6b416SMartin Schwidefsky /* Update timer against current base. */
34427f6b416SMartin Schwidefsky timer->expires += atomic64_read(&virt_timer_elapsed);
34527f6b416SMartin Schwidefsky if (likely((s64) timer->expires <
34627f6b416SMartin Schwidefsky (s64) atomic64_read(&virt_timer_current)))
3479cfb9b3cSMartin Schwidefsky /* The new timer expires before the current timer. */
34827f6b416SMartin Schwidefsky atomic64_set(&virt_timer_current, timer->expires);
34927f6b416SMartin Schwidefsky /* Insert new timer into the list. */
35027f6b416SMartin Schwidefsky list_add_sorted(timer, &virt_timer_list);
3519cfb9b3cSMartin Schwidefsky }
3521da177e4SLinus Torvalds }
3531da177e4SLinus Torvalds
__add_vtimer(struct vtimer_list * timer,int periodic)35427f6b416SMartin Schwidefsky static void __add_vtimer(struct vtimer_list *timer, int periodic)
3551da177e4SLinus Torvalds {
35627f6b416SMartin Schwidefsky unsigned long flags;
35727f6b416SMartin Schwidefsky
35827f6b416SMartin Schwidefsky timer->interval = periodic ? timer->expires : 0;
35927f6b416SMartin Schwidefsky spin_lock_irqsave(&virt_timer_lock, flags);
36027f6b416SMartin Schwidefsky internal_add_vtimer(timer);
36127f6b416SMartin Schwidefsky spin_unlock_irqrestore(&virt_timer_lock, flags);
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds
3641da177e4SLinus Torvalds /*
3650f5e1558SMasahiro Yamada * add_virt_timer - add a oneshot virtual CPU timer
3661da177e4SLinus Torvalds */
add_virt_timer(struct vtimer_list * timer)36727f6b416SMartin Schwidefsky void add_virt_timer(struct vtimer_list *timer)
3681da177e4SLinus Torvalds {
36927f6b416SMartin Schwidefsky __add_vtimer(timer, 0);
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds EXPORT_SYMBOL(add_virt_timer);
3721da177e4SLinus Torvalds
3731da177e4SLinus Torvalds /*
3741da177e4SLinus Torvalds * add_virt_timer_int - add an interval virtual CPU timer
3751da177e4SLinus Torvalds */
add_virt_timer_periodic(struct vtimer_list * timer)37627f6b416SMartin Schwidefsky void add_virt_timer_periodic(struct vtimer_list *timer)
3771da177e4SLinus Torvalds {
37827f6b416SMartin Schwidefsky __add_vtimer(timer, 1);
3791da177e4SLinus Torvalds }
3801da177e4SLinus Torvalds EXPORT_SYMBOL(add_virt_timer_periodic);
3811da177e4SLinus Torvalds
__mod_vtimer(struct vtimer_list * timer,u64 expires,int periodic)38227f6b416SMartin Schwidefsky static int __mod_vtimer(struct vtimer_list *timer, u64 expires, int periodic)
3831da177e4SLinus Torvalds {
3841da177e4SLinus Torvalds unsigned long flags;
38527f6b416SMartin Schwidefsky int rc;
3861da177e4SLinus Torvalds
387ca366a32SMartin Schwidefsky BUG_ON(!timer->function);
3881da177e4SLinus Torvalds
3891da177e4SLinus Torvalds if (timer->expires == expires && vtimer_pending(timer))
3901da177e4SLinus Torvalds return 1;
39127f6b416SMartin Schwidefsky spin_lock_irqsave(&virt_timer_lock, flags);
39227f6b416SMartin Schwidefsky rc = vtimer_pending(timer);
39327f6b416SMartin Schwidefsky if (rc)
3941da177e4SLinus Torvalds list_del_init(&timer->entry);
39527f6b416SMartin Schwidefsky timer->interval = periodic ? expires : 0;
3961da177e4SLinus Torvalds timer->expires = expires;
3971da177e4SLinus Torvalds internal_add_vtimer(timer);
39827f6b416SMartin Schwidefsky spin_unlock_irqrestore(&virt_timer_lock, flags);
39927f6b416SMartin Schwidefsky return rc;
4001da177e4SLinus Torvalds }
401b6ecfa92SJan Glauber
402b6ecfa92SJan Glauber /*
403b6ecfa92SJan Glauber * returns whether it has modified a pending timer (1) or not (0)
404b6ecfa92SJan Glauber */
mod_virt_timer(struct vtimer_list * timer,u64 expires)40527f6b416SMartin Schwidefsky int mod_virt_timer(struct vtimer_list *timer, u64 expires)
406b6ecfa92SJan Glauber {
407b6ecfa92SJan Glauber return __mod_vtimer(timer, expires, 0);
408b6ecfa92SJan Glauber }
4091da177e4SLinus Torvalds EXPORT_SYMBOL(mod_virt_timer);
4101da177e4SLinus Torvalds
4111da177e4SLinus Torvalds /*
412b6ecfa92SJan Glauber * returns whether it has modified a pending timer (1) or not (0)
413b6ecfa92SJan Glauber */
mod_virt_timer_periodic(struct vtimer_list * timer,u64 expires)41427f6b416SMartin Schwidefsky int mod_virt_timer_periodic(struct vtimer_list *timer, u64 expires)
415b6ecfa92SJan Glauber {
416b6ecfa92SJan Glauber return __mod_vtimer(timer, expires, 1);
417b6ecfa92SJan Glauber }
418b6ecfa92SJan Glauber EXPORT_SYMBOL(mod_virt_timer_periodic);
419b6ecfa92SJan Glauber
420b6ecfa92SJan Glauber /*
42127f6b416SMartin Schwidefsky * Delete a virtual timer.
4221da177e4SLinus Torvalds *
4231da177e4SLinus Torvalds * returns whether the deleted timer was pending (1) or not (0)
4241da177e4SLinus Torvalds */
del_virt_timer(struct vtimer_list * timer)4251da177e4SLinus Torvalds int del_virt_timer(struct vtimer_list *timer)
4261da177e4SLinus Torvalds {
4271da177e4SLinus Torvalds unsigned long flags;
4281da177e4SLinus Torvalds
4291da177e4SLinus Torvalds if (!vtimer_pending(timer))
4301da177e4SLinus Torvalds return 0;
43127f6b416SMartin Schwidefsky spin_lock_irqsave(&virt_timer_lock, flags);
4321da177e4SLinus Torvalds list_del_init(&timer->entry);
43327f6b416SMartin Schwidefsky spin_unlock_irqrestore(&virt_timer_lock, flags);
4341da177e4SLinus Torvalds return 1;
4351da177e4SLinus Torvalds }
4361da177e4SLinus Torvalds EXPORT_SYMBOL(del_virt_timer);
4371da177e4SLinus Torvalds
4381da177e4SLinus Torvalds /*
4391da177e4SLinus Torvalds * Start the virtual CPU timer on the current CPU.
4401da177e4SLinus Torvalds */
vtime_init(void)441b5f87f15SMartin Schwidefsky void vtime_init(void)
4421da177e4SLinus Torvalds {
4438b646bd7SMartin Schwidefsky /* set initial cpu timer */
44427f6b416SMartin Schwidefsky set_vtimer(VTIMER_MAX_SLICE);
445f341b8dfSMartin Schwidefsky /* Setup initial MT scaling values */
446f341b8dfSMartin Schwidefsky if (smp_cpu_mtid) {
447f341b8dfSMartin Schwidefsky __this_cpu_write(mt_scaling_jiffies, jiffies);
448f341b8dfSMartin Schwidefsky __this_cpu_write(mt_scaling_mult, 1);
449f341b8dfSMartin Schwidefsky __this_cpu_write(mt_scaling_div, 1);
450346d034dSHendrik Brueckner stcctm(MT_DIAG, smp_cpu_mtid + 1, this_cpu_ptr(mt_cycles));
451f341b8dfSMartin Schwidefsky }
4521da177e4SLinus Torvalds }
453