Commit 57e19bfe authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390 update (16/27): timer interrupts.

Make timer interrupt independent from boot cpu and do several ticks in one
go if a virtual cpu didn't get an interrupt for a period of time > HZ.
parent 9f0ff519
...@@ -52,7 +52,6 @@ unsigned long machine_flags = 0; ...@@ -52,7 +52,6 @@ unsigned long machine_flags = 0;
struct { unsigned long addr, size, type; } memory_chunk[16] = { { 0 } }; struct { unsigned long addr, size, type; } memory_chunk[16] = { { 0 } };
#define CHUNK_READ_WRITE 0 #define CHUNK_READ_WRITE 0
#define CHUNK_READ_ONLY 1 #define CHUNK_READ_ONLY 1
__u16 boot_cpu_addr;
int cpus_initialized = 0; int cpus_initialized = 0;
unsigned long cpu_initialized = 0; unsigned long cpu_initialized = 0;
volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */
...@@ -474,8 +473,7 @@ void __init setup_arch(char **cmdline_p) ...@@ -474,8 +473,7 @@ void __init setup_arch(char **cmdline_p)
lowcore->jiffy_timer = -1LL; lowcore->jiffy_timer = -1LL;
set_prefix((__u32) lowcore); set_prefix((__u32) lowcore);
cpu_init(); cpu_init();
boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; __cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr;
__cpu_logical_map[0] = boot_cpu_addr;
/* /*
* Create kernel page tables and switch to virtual addressing. * Create kernel page tables and switch to virtual addressing.
......
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
/* prototypes */ /* prototypes */
extern int cpu_idle(void * unused); extern int cpu_idle(void * unused);
extern __u16 boot_cpu_addr;
extern volatile int __cpu_logical_map[]; extern volatile int __cpu_logical_map[];
/* /*
...@@ -426,6 +425,7 @@ void smp_ctl_clear_bit(int cr, int bit) { ...@@ -426,6 +425,7 @@ void smp_ctl_clear_bit(int cr, int bit) {
void __init smp_check_cpus(unsigned int max_cpus) void __init smp_check_cpus(unsigned int max_cpus)
{ {
int curr_cpu, num_cpus; int curr_cpu, num_cpus;
__u16 boot_cpu_addr;
boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
current_thread_info()->cpu = 0; current_thread_info()->cpu = 0;
......
...@@ -38,11 +38,18 @@ ...@@ -38,11 +38,18 @@
#define USECS_PER_JIFFY ((unsigned long) 1000000/HZ) #define USECS_PER_JIFFY ((unsigned long) 1000000/HZ)
#define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12) #define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
/*
* Create a small time difference between the timer interrupts
* on the different cpus to avoid lock contention.
*/
#define CPU_DEVIATION (smp_processor_id() << 12)
#define TICK_SIZE tick #define TICK_SIZE tick
u64 jiffies_64; u64 jiffies_64;
static ext_int_info_t ext_int_info_timer; static ext_int_info_t ext_int_info_timer;
static uint64_t xtime_cc;
static uint64_t init_timer_cc; static uint64_t init_timer_cc;
extern rwlock_t xtime_lock; extern rwlock_t xtime_lock;
...@@ -118,58 +125,90 @@ void do_settimeofday(struct timeval *tv) ...@@ -118,58 +125,90 @@ void do_settimeofday(struct timeval *tv)
write_unlock_irq(&xtime_lock); write_unlock_irq(&xtime_lock);
} }
static inline __u32 div64_32(__u64 dividend, __u32 divisor)
{
register_pair rp;
rp.pair = dividend;
asm ("dr %0,%1" : "+d" (rp) : "d" (divisor));
return rp.subreg.odd;
}
/* /*
* timer_interrupt() needs to keep up the real-time clock, * timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick * as well as call the "do_timer()" routine every clocktick
*/ */
#ifdef CONFIG_SMP
extern __u16 boot_cpu_addr;
#endif
static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code) static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
{ {
int cpu = smp_processor_id(); int cpu = smp_processor_id();
__u64 tmp;
__u32 ticks;
/* Calculate how many ticks have passed. */
asm volatile ("STCK 0(%0)" : : "a" (&tmp) : "memory", "cc");
tmp = tmp - S390_lowcore.jiffy_timer;
if (tmp >= 2*CLK_TICKS_PER_JIFFY) { /* more than one tick ? */
ticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1);
S390_lowcore.jiffy_timer +=
CLK_TICKS_PER_JIFFY * (__u64) ticks;
} else {
ticks = 1;
S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
}
/* set clock comparator for next tick */
tmp = S390_lowcore.jiffy_timer + CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
asm volatile ("SCKC %0" : : "m" (tmp));
irq_enter(); irq_enter();
#ifdef CONFIG_SMP
/* /*
* set clock comparator for next tick * Do not rely on the boot cpu to do the calls to do_timer.
* Spread it over all cpus instead.
*/ */
S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY; write_lock(&xtime_lock);
asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer)); if (S390_lowcore.jiffy_timer > xtime_cc) {
__u32 xticks;
#ifdef CONFIG_SMP
if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) tmp = S390_lowcore.jiffy_timer - xtime_cc;
write_lock(&xtime_lock); if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
xticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1);
update_process_times(user_mode(regs)); xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY;
} else {
if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) { xticks = 1;
do_timer(regs); xtime_cc += CLK_TICKS_PER_JIFFY;
write_unlock(&xtime_lock); }
while (xticks--)
do_timer(regs);
} }
write_unlock(&xtime_lock);
while (ticks--)
update_process_times(user_mode(regs));
#else #else
do_timer(regs); while (ticks--)
do_timer(regs);
#endif #endif
irq_exit(); irq_exit();
} }
/* /*
* Start the clock comparator on the current CPU * Start the clock comparator on the current CPU.
*/ */
void init_cpu_timer(void) void init_cpu_timer(void)
{ {
unsigned long cr0; unsigned long cr0;
__u64 timer;
/* allow clock comparator timer interrupt */ /* allow clock comparator timer interrupt */
asm volatile ("STCTL 0,0,%0" : "=m" (cr0) : : "memory"); asm volatile ("STCTL 0,0,%0" : "=m" (cr0) : : "memory");
cr0 |= 0x800; cr0 |= 0x800;
asm volatile ("LCTL 0,0,%0" : : "m" (cr0) : "memory"); asm volatile ("LCTL 0,0,%0" : : "m" (cr0) : "memory");
S390_lowcore.jiffy_timer = (__u64) jiffies * CLK_TICKS_PER_JIFFY; timer = init_timer_cc + jiffies_64 * CLK_TICKS_PER_JIFFY;
S390_lowcore.jiffy_timer += init_timer_cc + CLK_TICKS_PER_JIFFY; S390_lowcore.jiffy_timer = timer;
asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer)); timer += CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
asm volatile ("SCKC %0" : : "m" (timer));
} }
/* /*
...@@ -178,7 +217,7 @@ void init_cpu_timer(void) ...@@ -178,7 +217,7 @@ void init_cpu_timer(void)
*/ */
void __init time_init(void) void __init time_init(void)
{ {
__u64 set_time_cc; __u64 set_time_cc;
int cc; int cc;
/* kick the TOD clock */ /* kick the TOD clock */
...@@ -201,8 +240,9 @@ void __init time_init(void) ...@@ -201,8 +240,9 @@ void __init time_init(void)
} }
/* set xtime */ /* set xtime */
set_time_cc = init_timer_cc - 0x8126d60e46000000LL + xtime_cc = init_timer_cc;
(0x3c26700LL*1000000*4096); set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
(0x3c26700LL*1000000*4096);
tod_to_timeval(set_time_cc, &xtime); tod_to_timeval(set_time_cc, &xtime);
/* request the 0x1004 external interrupt */ /* request the 0x1004 external interrupt */
......
...@@ -52,7 +52,6 @@ unsigned long machine_flags = 0; ...@@ -52,7 +52,6 @@ unsigned long machine_flags = 0;
struct { unsigned long addr, size, type; } memory_chunk[16] = { { 0 } }; struct { unsigned long addr, size, type; } memory_chunk[16] = { { 0 } };
#define CHUNK_READ_WRITE 0 #define CHUNK_READ_WRITE 0
#define CHUNK_READ_ONLY 1 #define CHUNK_READ_ONLY 1
__u16 boot_cpu_addr;
int cpus_initialized = 0; int cpus_initialized = 0;
unsigned long cpu_initialized = 0; unsigned long cpu_initialized = 0;
volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */
...@@ -464,8 +463,7 @@ void __init setup_arch(char **cmdline_p) ...@@ -464,8 +463,7 @@ void __init setup_arch(char **cmdline_p)
lowcore->jiffy_timer = -1LL; lowcore->jiffy_timer = -1LL;
set_prefix((__u32)(__u64) lowcore); set_prefix((__u32)(__u64) lowcore);
cpu_init(); cpu_init();
boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; __cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr;
__cpu_logical_map[0] = boot_cpu_addr;
/* /*
* Create kernel page tables and switch to virtual addressing. * Create kernel page tables and switch to virtual addressing.
......
...@@ -40,7 +40,6 @@ ...@@ -40,7 +40,6 @@
/* prototypes */ /* prototypes */
extern int cpu_idle(void * unused); extern int cpu_idle(void * unused);
extern __u16 boot_cpu_addr;
extern volatile int __cpu_logical_map[]; extern volatile int __cpu_logical_map[];
/* /*
...@@ -407,6 +406,7 @@ void smp_ctl_clear_bit(int cr, int bit) { ...@@ -407,6 +406,7 @@ void smp_ctl_clear_bit(int cr, int bit) {
void __init smp_check_cpus(unsigned int max_cpus) void __init smp_check_cpus(unsigned int max_cpus)
{ {
int curr_cpu, num_cpus; int curr_cpu, num_cpus;
__u16 boot_cpu_addr;
boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
current_thread_info()->cpu = 0; current_thread_info()->cpu = 0;
......
...@@ -37,11 +37,18 @@ ...@@ -37,11 +37,18 @@
#define USECS_PER_JIFFY ((unsigned long) 1000000/HZ) #define USECS_PER_JIFFY ((unsigned long) 1000000/HZ)
#define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12) #define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
/*
* Create a small time difference between the timer interrupts
* on the different cpus to avoid lock contention.
*/
#define CPU_DEVIATION (smp_processor_id() << 12)
#define TICK_SIZE tick #define TICK_SIZE tick
u64 jiffies_64; u64 jiffies_64;
static ext_int_info_t ext_int_info_timer; static ext_int_info_t ext_int_info_timer;
static uint64_t xtime_cc;
static uint64_t init_timer_cc; static uint64_t init_timer_cc;
extern rwlock_t xtime_lock; extern rwlock_t xtime_lock;
...@@ -117,54 +124,77 @@ void do_settimeofday(struct timeval *tv) ...@@ -117,54 +124,77 @@ void do_settimeofday(struct timeval *tv)
* timer_interrupt() needs to keep up the real-time clock, * timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick * as well as call the "do_timer()" routine every clocktick
*/ */
#ifdef CONFIG_SMP
extern __u16 boot_cpu_addr;
#endif
static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code) static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
{ {
int cpu = smp_processor_id(); int cpu = smp_processor_id();
__u64 tmp;
__u32 ticks;
/* Calculate how many ticks have passed. */
asm volatile ("STCK 0(%0)" : : "a" (&tmp) : "memory", "cc");
tmp = tmp - S390_lowcore.jiffy_timer;
if (tmp >= 2*CLK_TICKS_PER_JIFFY) { /* more than one tick ? */
ticks = tmp / CLK_TICKS_PER_JIFFY;
S390_lowcore.jiffy_timer +=
CLK_TICKS_PER_JIFFY * (__u64) ticks;
} else {
ticks = 1;
S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
}
/* set clock comparator for next tick */
tmp = S390_lowcore.jiffy_timer + CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
asm volatile ("SCKC %0" : : "m" (tmp));
irq_enter(); irq_enter();
#ifdef CONFIG_SMP
/* /*
* set clock comparator for next tick * Do not rely on the boot cpu to do the calls to do_timer.
* Spread it over all cpus instead.
*/ */
S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY; write_lock(&xtime_lock);
asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer)); if (S390_lowcore.jiffy_timer > xtime_cc) {
__u32 xticks;
#ifdef CONFIG_SMP
if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) tmp = S390_lowcore.jiffy_timer - xtime_cc;
write_lock(&xtime_lock); if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
xticks = tmp / CLK_TICKS_PER_JIFFY;
update_process_times(user_mode(regs)); xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY;
} else {
if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) { xticks = 1;
do_timer(regs); xtime_cc += CLK_TICKS_PER_JIFFY;
write_unlock(&xtime_lock); }
while (xticks--)
do_timer(regs);
} }
write_unlock(&xtime_lock);
while (ticks--)
update_process_times(user_mode(regs));
#else #else
do_timer(regs); while (ticks--)
do_timer(regs);
#endif #endif
irq_exit(); irq_exit();
} }
/* /*
* Start the clock comparator on the current CPU * Start the clock comparator on the current CPU.
*/ */
void init_cpu_timer(void) void init_cpu_timer(void)
{ {
unsigned long cr0; unsigned long cr0;
__u64 timer;
/* allow clock comparator timer interrupt */ /* allow clock comparator timer interrupt */
asm volatile ("STCTG 0,0,%0" : "=m" (cr0) : : "memory"); asm volatile ("STCTG 0,0,%0" : "=m" (cr0) : : "memory");
cr0 |= 0x800; cr0 |= 0x800;
asm volatile ("LCTLG 0,0,%0" : : "m" (cr0) : "memory"); asm volatile ("LCTLG 0,0,%0" : : "m" (cr0) : "memory");
S390_lowcore.jiffy_timer = (__u64) jiffies * CLK_TICKS_PER_JIFFY; timer = init_timer_cc + jiffies_64 * CLK_TICKS_PER_JIFFY;
S390_lowcore.jiffy_timer += init_timer_cc + CLK_TICKS_PER_JIFFY; S390_lowcore.jiffy_timer = timer;
asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer)); timer += CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
asm volatile ("SCKC %0" : : "m" (timer));
} }
/* /*
...@@ -173,7 +203,7 @@ void init_cpu_timer(void) ...@@ -173,7 +203,7 @@ void init_cpu_timer(void)
*/ */
void __init time_init(void) void __init time_init(void)
{ {
__u64 set_time_cc; __u64 set_time_cc;
int cc; int cc;
/* kick the TOD clock */ /* kick the TOD clock */
...@@ -196,8 +226,9 @@ void __init time_init(void) ...@@ -196,8 +226,9 @@ void __init time_init(void)
} }
/* set xtime */ /* set xtime */
set_time_cc = init_timer_cc - 0x8126d60e46000000LL + xtime_cc = init_timer_cc;
(0x3c26700LL*1000000*4096); set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
(0x3c26700LL*1000000*4096);
tod_to_timeval(set_time_cc, &xtime); tod_to_timeval(set_time_cc, &xtime);
/* request the 0x1004 external interrupt */ /* request the 0x1004 external interrupt */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment