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