xref: /openbmc/linux/arch/s390/kernel/vtime.c (revision 5ee9cd065836e5934710ca35653bce7905add20b)
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