| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *    Virtual cpu timer based timer functions. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  |  *    Copyright IBM Corp. 2004, 2012 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  *    Author(s): Jan Glauber <jan.glauber@de.ibm.com> | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | #include <linux/kernel_stat.h>
 | 
					
						
							|  |  |  | #include <linux/notifier.h>
 | 
					
						
							|  |  |  | #include <linux/kprobes.h>
 | 
					
						
							|  |  |  | #include <linux/export.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/timex.h>
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | #include <linux/types.h>
 | 
					
						
							|  |  |  | #include <linux/time.h>
 | 
					
						
							| 
									
										
										
										
											2010-12-01 10:08:01 +01:00
										 |  |  | #include <linux/cpu.h>
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | #include <linux/smp.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-10-06 16:38:35 +02:00
										 |  |  | #include <asm/irq_regs.h>
 | 
					
						
							| 
									
										
										
										
											2009-06-12 10:26:21 +02:00
										 |  |  | #include <asm/cputime.h>
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | #include <asm/vtimer.h>
 | 
					
						
							| 
									
										
										
										
											2013-07-16 18:50:52 +02:00
										 |  |  | #include <asm/vtime.h>
 | 
					
						
							| 
									
										
										
										
											2011-05-26 09:48:24 +02:00
										 |  |  | #include <asm/irq.h>
 | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | #include "entry.h"
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | static void virt_timer_expire(void); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-22 12:08:20 +02:00
										 |  |  | DEFINE_PER_CPU(struct s390_idle_data, s390_idle); | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | static LIST_HEAD(virt_timer_list); | 
					
						
							|  |  |  | static DEFINE_SPINLOCK(virt_timer_lock); | 
					
						
							|  |  |  | static atomic64_t virt_timer_current; | 
					
						
							|  |  |  | static atomic64_t virt_timer_elapsed; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline u64 get_vtimer(void) | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	u64 timer; | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	asm volatile("stpt %0" : "=m" (timer)); | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 	return timer; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | static inline void set_vtimer(u64 expires) | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	u64 timer; | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	asm volatile( | 
					
						
							|  |  |  | 		"	stpt	%0\n"	/* Store current cpu timer value */ | 
					
						
							|  |  |  | 		"	spt	%1"	/* Set new value imm. afterwards */ | 
					
						
							|  |  |  | 		: "=m" (timer) : "m" (expires)); | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 	S390_lowcore.system_timer += S390_lowcore.last_update_timer - timer; | 
					
						
							|  |  |  | 	S390_lowcore.last_update_timer = expires; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | static inline int virt_timer_forward(u64 elapsed) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BUG_ON(!irqs_disabled()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (list_empty(&virt_timer_list)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	elapsed = atomic64_add_return(elapsed, &virt_timer_elapsed); | 
					
						
							|  |  |  | 	return elapsed >= atomic64_read(&virt_timer_current); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Update process times based on virtual cpu times stored by entry.S | 
					
						
							|  |  |  |  * to the lowcore fields user_timer, system_timer & steal_clock. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | static int do_account_vtime(struct task_struct *tsk, int hardirq_offset) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:39 +01:00
										 |  |  | 	struct thread_info *ti = task_thread_info(tsk); | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	u64 timer, clock, user, system, steal; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	timer = S390_lowcore.last_update_timer; | 
					
						
							|  |  |  | 	clock = S390_lowcore.last_update_clock; | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	asm volatile( | 
					
						
							|  |  |  | 		"	stpt	%0\n"	/* Store current cpu timer value */ | 
					
						
							|  |  |  | 		"	stck	%1"	/* Store current tod clock value */ | 
					
						
							|  |  |  | 		: "=m" (S390_lowcore.last_update_timer), | 
					
						
							|  |  |  | 		  "=m" (S390_lowcore.last_update_clock)); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer; | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:39 +01:00
										 |  |  | 	S390_lowcore.steal_timer += S390_lowcore.last_update_clock - clock; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:39 +01:00
										 |  |  | 	user = S390_lowcore.user_timer - ti->user_timer; | 
					
						
							|  |  |  | 	S390_lowcore.steal_timer -= user; | 
					
						
							|  |  |  | 	ti->user_timer = S390_lowcore.user_timer; | 
					
						
							|  |  |  | 	account_user_time(tsk, user, user); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:39 +01:00
										 |  |  | 	system = S390_lowcore.system_timer - ti->system_timer; | 
					
						
							|  |  |  | 	S390_lowcore.steal_timer -= system; | 
					
						
							|  |  |  | 	ti->system_timer = S390_lowcore.system_timer; | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 	account_system_time(tsk, hardirq_offset, system, system); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:39 +01:00
										 |  |  | 	steal = S390_lowcore.steal_timer; | 
					
						
							|  |  |  | 	if ((s64) steal > 0) { | 
					
						
							|  |  |  | 		S390_lowcore.steal_timer = 0; | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 		account_steal_time(steal); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return virt_timer_forward(user + system); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-08 15:23:11 +02:00
										 |  |  | void vtime_task_switch(struct task_struct *prev) | 
					
						
							| 
									
										
										
										
											2006-01-14 13:21:03 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:39 +01:00
										 |  |  | 	struct thread_info *ti; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	do_account_vtime(prev, 0); | 
					
						
							|  |  |  | 	ti = task_thread_info(prev); | 
					
						
							|  |  |  | 	ti->user_timer = S390_lowcore.user_timer; | 
					
						
							|  |  |  | 	ti->system_timer = S390_lowcore.system_timer; | 
					
						
							| 
									
										
										
										
											2012-06-18 17:54:14 +02:00
										 |  |  | 	ti = task_thread_info(current); | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:39 +01:00
										 |  |  | 	S390_lowcore.user_timer = ti->user_timer; | 
					
						
							|  |  |  | 	S390_lowcore.system_timer = ti->system_timer; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2006-01-14 13:21:03 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-13 23:51:06 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * In s390, accounting pending user time also implies | 
					
						
							|  |  |  |  * accounting system time in order to correctly compute | 
					
						
							|  |  |  |  * the stolen time accounting. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void vtime_account_user(struct task_struct *tsk) | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:39 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	if (do_account_vtime(tsk, HARDIRQ_OFFSET)) | 
					
						
							|  |  |  | 		virt_timer_expire(); | 
					
						
							| 
									
										
										
										
											2006-01-14 13:21:03 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Update process times based on virtual cpu times stored by entry.S | 
					
						
							|  |  |  |  * to the lowcore fields user_timer, system_timer & steal_clock. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-12-16 20:00:34 +01:00
										 |  |  | void vtime_account_irq_enter(struct task_struct *tsk) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:39 +01:00
										 |  |  | 	struct thread_info *ti = task_thread_info(tsk); | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	u64 timer, system; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-19 17:00:24 +01:00
										 |  |  | 	WARN_ON_ONCE(!irqs_disabled()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	timer = S390_lowcore.last_update_timer; | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 	S390_lowcore.last_update_timer = get_vtimer(); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:39 +01:00
										 |  |  | 	system = S390_lowcore.system_timer - ti->system_timer; | 
					
						
							|  |  |  | 	S390_lowcore.steal_timer -= system; | 
					
						
							|  |  |  | 	ti->system_timer = S390_lowcore.system_timer; | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 	account_system_time(tsk, 0, system, system); | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	virt_timer_forward(system); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2012-12-16 20:00:34 +01:00
										 |  |  | EXPORT_SYMBOL_GPL(vtime_account_irq_enter); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-13 18:21:22 +01:00
										 |  |  | void vtime_account_system(struct task_struct *tsk) | 
					
						
							| 
									
										
										
										
											2012-12-16 20:00:34 +01:00
										 |  |  | __attribute__((alias("vtime_account_irq_enter"))); | 
					
						
							| 
									
										
										
										
											2012-11-13 18:21:22 +01:00
										 |  |  | EXPORT_SYMBOL_GPL(vtime_account_system); | 
					
						
							| 
									
										
										
										
											2012-10-24 18:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | void __kprobes vtime_stop_cpu(void) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 	struct s390_idle_data *idle = &__get_cpu_var(s390_idle); | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | 	unsigned long long idle_time; | 
					
						
							|  |  |  | 	unsigned long psw_mask; | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | 	trace_hardirqs_on(); | 
					
						
							| 
									
										
										
										
											2010-05-17 10:00:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | 	/* Wait for external, I/O or machine check interrupt. */ | 
					
						
							| 
									
										
										
										
											2013-09-24 09:14:56 +02:00
										 |  |  | 	psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_DAT | | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | 		PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; | 
					
						
							|  |  |  | 	idle->nohz_delay = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Call the assembler magic in entry.S */ | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	psw_idle(idle, psw_mask); | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Account time spent with enabled wait psw loaded as idle time. */ | 
					
						
							| 
									
										
										
										
											2009-06-22 12:08:20 +02:00
										 |  |  | 	idle->sequence++; | 
					
						
							|  |  |  | 	smp_wmb(); | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	idle_time = idle->clock_idle_exit - idle->clock_idle_enter; | 
					
						
							|  |  |  | 	idle->clock_idle_enter = idle->clock_idle_exit = 0ULL; | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 	idle->idle_time += idle_time; | 
					
						
							|  |  |  | 	idle->idle_count++; | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | 	account_idle_time(idle_time); | 
					
						
							| 
									
										
										
										
											2009-06-22 12:08:20 +02:00
										 |  |  | 	smp_wmb(); | 
					
						
							|  |  |  | 	idle->sequence++; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-23 13:58:08 +02:00
										 |  |  | cputime64_t s390_get_idle_time(int cpu) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | 	struct s390_idle_data *idle = &per_cpu(s390_idle, cpu); | 
					
						
							|  |  |  | 	unsigned long long now, idle_enter, idle_exit; | 
					
						
							| 
									
										
										
										
											2009-06-22 12:08:20 +02:00
										 |  |  | 	unsigned int sequence; | 
					
						
							| 
									
										
										
										
											2009-04-23 13:58:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | 	do { | 
					
						
							| 
									
										
										
										
											2013-01-30 09:49:40 +01:00
										 |  |  | 		now = get_tod_clock(); | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | 		sequence = ACCESS_ONCE(idle->sequence); | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 		idle_enter = ACCESS_ONCE(idle->clock_idle_enter); | 
					
						
							|  |  |  | 		idle_exit = ACCESS_ONCE(idle->clock_idle_exit); | 
					
						
							| 
									
										
										
										
											2013-10-28 12:15:32 +01:00
										 |  |  | 	} while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	return idle_enter ? ((idle_exit ?: now) - idle_enter) : 0; | 
					
						
							| 
									
										
										
										
											2009-04-23 13:58:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Sorted add to a list. List is linear searched until first bigger | 
					
						
							|  |  |  |  * element is found. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void list_add_sorted(struct vtimer_list *timer, struct list_head *head) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	struct vtimer_list *tmp; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	list_for_each_entry(tmp, head, entry) { | 
					
						
							|  |  |  | 		if (tmp->expires > timer->expires) { | 
					
						
							|  |  |  | 			list_add_tail(&timer->entry, &tmp->entry); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	list_add_tail(&timer->entry, head); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  |  * Handler for expired virtual CPU timer. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | static void virt_timer_expire(void) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	struct vtimer_list *timer, *tmp; | 
					
						
							|  |  |  | 	unsigned long elapsed; | 
					
						
							|  |  |  | 	LIST_HEAD(cb_list); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* walk timer list, fire all expired timers */ | 
					
						
							|  |  |  | 	spin_lock(&virt_timer_lock); | 
					
						
							|  |  |  | 	elapsed = atomic64_read(&virt_timer_elapsed); | 
					
						
							|  |  |  | 	list_for_each_entry_safe(timer, tmp, &virt_timer_list, entry) { | 
					
						
							|  |  |  | 		if (timer->expires < elapsed) | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 			/* move expired timer to the callback queue */ | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 			list_move_tail(&timer->entry, &cb_list); | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 		else | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 			timer->expires -= elapsed; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	if (!list_empty(&virt_timer_list)) { | 
					
						
							|  |  |  | 		timer = list_first_entry(&virt_timer_list, | 
					
						
							|  |  |  | 					 struct vtimer_list, entry); | 
					
						
							|  |  |  | 		atomic64_set(&virt_timer_current, timer->expires); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	atomic64_sub(elapsed, &virt_timer_elapsed); | 
					
						
							|  |  |  | 	spin_unlock(&virt_timer_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Do callbacks and recharge periodic timers */ | 
					
						
							|  |  |  | 	list_for_each_entry_safe(timer, tmp, &cb_list, entry) { | 
					
						
							|  |  |  | 		list_del_init(&timer->entry); | 
					
						
							|  |  |  | 		timer->function(timer->data); | 
					
						
							|  |  |  | 		if (timer->interval) { | 
					
						
							|  |  |  | 			/* Recharge interval timer */ | 
					
						
							|  |  |  | 			timer->expires = timer->interval + | 
					
						
							|  |  |  | 				atomic64_read(&virt_timer_elapsed); | 
					
						
							|  |  |  | 			spin_lock(&virt_timer_lock); | 
					
						
							|  |  |  | 			list_add_sorted(timer, &virt_timer_list); | 
					
						
							|  |  |  | 			spin_unlock(&virt_timer_lock); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:27 -04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void init_virt_timer(struct vtimer_list *timer) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	timer->function = NULL; | 
					
						
							|  |  |  | 	INIT_LIST_HEAD(&timer->entry); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(init_virt_timer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int vtimer_pending(struct vtimer_list *timer) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	return !list_empty(&timer->entry); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void internal_add_vtimer(struct vtimer_list *timer) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	if (list_empty(&virt_timer_list)) { | 
					
						
							|  |  |  | 		/* First timer, just program it. */ | 
					
						
							|  |  |  | 		atomic64_set(&virt_timer_current, timer->expires); | 
					
						
							|  |  |  | 		atomic64_set(&virt_timer_elapsed, 0); | 
					
						
							|  |  |  | 		list_add(&timer->entry, &virt_timer_list); | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 		/* Update timer against current base. */ | 
					
						
							|  |  |  | 		timer->expires += atomic64_read(&virt_timer_elapsed); | 
					
						
							|  |  |  | 		if (likely((s64) timer->expires < | 
					
						
							|  |  |  | 			   (s64) atomic64_read(&virt_timer_current))) | 
					
						
							| 
									
										
										
										
											2008-12-31 15:11:41 +01:00
										 |  |  | 			/* The new timer expires before the current timer. */ | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 			atomic64_set(&virt_timer_current, timer->expires); | 
					
						
							|  |  |  | 		/* Insert new timer into the list. */ | 
					
						
							|  |  |  | 		list_add_sorted(timer, &virt_timer_list); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | static void __add_vtimer(struct vtimer_list *timer, int periodic) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	timer->interval = periodic ? timer->expires : 0; | 
					
						
							|  |  |  | 	spin_lock_irqsave(&virt_timer_lock, flags); | 
					
						
							|  |  |  | 	internal_add_vtimer(timer); | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&virt_timer_lock, flags); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * add_virt_timer - add an oneshot virtual CPU timer | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | void add_virt_timer(struct vtimer_list *timer) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	__add_vtimer(timer, 0); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(add_virt_timer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * add_virt_timer_int - add an interval virtual CPU timer | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | void add_virt_timer_periodic(struct vtimer_list *timer) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	__add_vtimer(timer, 1); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(add_virt_timer_periodic); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | static int __mod_vtimer(struct vtimer_list *timer, u64 expires, int periodic) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	int rc; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-14 09:59:23 +02:00
										 |  |  | 	BUG_ON(!timer->function); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (timer->expires == expires && vtimer_pending(timer)) | 
					
						
							|  |  |  | 		return 1; | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	spin_lock_irqsave(&virt_timer_lock, flags); | 
					
						
							|  |  |  | 	rc = vtimer_pending(timer); | 
					
						
							|  |  |  | 	if (rc) | 
					
						
							|  |  |  | 		list_del_init(&timer->entry); | 
					
						
							|  |  |  | 	timer->interval = periodic ? expires : 0; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	timer->expires = expires; | 
					
						
							|  |  |  | 	internal_add_vtimer(timer); | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	spin_unlock_irqrestore(&virt_timer_lock, flags); | 
					
						
							|  |  |  | 	return rc; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-14 15:36:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * returns whether it has modified a pending timer (1) or not (0) | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | int mod_virt_timer(struct vtimer_list *timer, u64 expires) | 
					
						
							| 
									
										
										
										
											2009-04-14 15:36:20 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	return __mod_vtimer(timer, expires, 0); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | EXPORT_SYMBOL(mod_virt_timer); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-14 15:36:20 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * returns whether it has modified a pending timer (1) or not (0) | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | int mod_virt_timer_periodic(struct vtimer_list *timer, u64 expires) | 
					
						
							| 
									
										
										
										
											2009-04-14 15:36:20 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	return __mod_vtimer(timer, expires, 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(mod_virt_timer_periodic); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  |  * Delete a virtual timer. | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  * returns whether the deleted timer was pending (1) or not (0) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int del_virt_timer(struct vtimer_list *timer) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!vtimer_pending(timer)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	spin_lock_irqsave(&virt_timer_lock, flags); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	list_del_init(&timer->entry); | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	spin_unlock_irqrestore(&virt_timer_lock, flags); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(del_virt_timer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Start the virtual CPU timer on the current CPU. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-06-18 17:04:52 -04:00
										 |  |  | void init_cpu_vtimer(void) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-03-11 11:59:26 -04:00
										 |  |  | 	/* set initial cpu timer */ | 
					
						
							| 
									
										
										
										
											2012-07-20 11:15:08 +02:00
										 |  |  | 	set_vtimer(VTIMER_MAX_SLICE); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-18 17:04:52 -04:00
										 |  |  | static int s390_nohz_notify(struct notifier_block *self, unsigned long action, | 
					
						
							|  |  |  | 			    void *hcpu) | 
					
						
							| 
									
										
										
										
											2010-12-01 10:08:01 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct s390_idle_data *idle; | 
					
						
							|  |  |  | 	long cpu = (long) hcpu; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idle = &per_cpu(s390_idle, cpu); | 
					
						
							| 
									
										
										
										
											2012-08-27 15:43:49 +02:00
										 |  |  | 	switch (action & ~CPU_TASKS_FROZEN) { | 
					
						
							| 
									
										
										
										
											2010-12-01 10:08:01 +01:00
										 |  |  | 	case CPU_DYING: | 
					
						
							|  |  |  | 		idle->nohz_delay = 0; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NOTIFY_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | void __init vtime_init(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2007-02-05 21:18:19 +01:00
										 |  |  | 	/* Enable cpu timer interrupts on the boot cpu. */ | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	init_cpu_vtimer(); | 
					
						
							| 
									
										
										
										
											2010-12-01 10:08:01 +01:00
										 |  |  | 	cpu_notifier(s390_nohz_notify, 0); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } |