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