From 25097bf153391f7be4c591d47061b3dc4990dac2 Mon Sep 17 00:00:00 2001 From: Christian Ehrhardt Date: Tue, 14 Apr 2009 15:36:16 +0200 Subject: [PATCH 01/14] [S390] s390: move machine flags to lowcore Currently the storage of the machine flags is a globally exported unsigned long long variable. By moving the storage location into the lowcore struct we allow assembler code to check machine_flags directly even without needing a register. Addtionally the lowcore and therefore the machine flags too will be in cache most of the time. Signed-off-by: Christian Ehrhardt Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/cpuid.h | 25 +++++++++++++++++++++++++ arch/s390/include/asm/kvm_host.h | 1 + arch/s390/include/asm/lowcore.h | 12 ++++++++---- arch/s390/include/asm/processor.h | 17 +++-------------- arch/s390/include/asm/ptrace.h | 2 -- arch/s390/include/asm/setup.h | 24 ++++++++++++------------ arch/s390/include/asm/thread_info.h | 3 ++- arch/s390/kernel/early.c | 3 +++ arch/s390/kernel/setup.c | 4 +--- arch/s390/kernel/smp.c | 1 + 10 files changed, 56 insertions(+), 36 deletions(-) create mode 100644 arch/s390/include/asm/cpuid.h diff --git a/arch/s390/include/asm/cpuid.h b/arch/s390/include/asm/cpuid.h new file mode 100644 index 000000000000..07836a2e5222 --- /dev/null +++ b/arch/s390/include/asm/cpuid.h @@ -0,0 +1,25 @@ +/* + * Copyright IBM Corp. 2000,2009 + * Author(s): Hartmut Penner , + * Martin Schwidefsky + * Christian Ehrhardt + */ + +#ifndef _ASM_S390_CPUID_H_ +#define _ASM_S390_CPUID_H_ + +/* + * CPU type and hardware bug flags. Kept separately for each CPU. + * Members of this structure are referenced in head.S, so think twice + * before touching them. [mj] + */ + +typedef struct +{ + unsigned int version : 8; + unsigned int ident : 24; + unsigned int machine : 16; + unsigned int unused : 16; +} __attribute__ ((packed)) cpuid_t; + +#endif /* _ASM_S390_CPUID_H_ */ diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index c6e674f5fca9..54ea39f96ecd 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -15,6 +15,7 @@ #define ASM_KVM_HOST_H #include #include +#include #define KVM_MAX_VCPUS 64 #define KVM_MEMORY_SLOTS 32 diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h index b349f1c7fdfa..3aeca492b147 100644 --- a/arch/s390/include/asm/lowcore.h +++ b/arch/s390/include/asm/lowcore.h @@ -66,6 +66,7 @@ #define __LC_USER_EXEC_ASCE 0x02ac #define __LC_CPUID 0x02b0 #define __LC_INT_CLOCK 0x02c8 +#define __LC_MACHINE_FLAGS 0x02d8 #define __LC_IRB 0x0300 #define __LC_PFAULT_INTPARM 0x0080 #define __LC_CPU_TIMER_SAVE_AREA 0x00d8 @@ -110,6 +111,7 @@ #define __LC_CPUID 0x0320 #define __LC_INT_CLOCK 0x0340 #define __LC_VDSO_PER_CPU 0x0350 +#define __LC_MACHINE_FLAGS 0x0358 #define __LC_IRB 0x0380 #define __LC_PASTE 0x03c0 #define __LC_PFAULT_INTPARM 0x11b8 @@ -127,9 +129,9 @@ #ifndef __ASSEMBLY__ -#include +#include +#include #include -#include void restart_int_handler(void); void ext_int_handler(void); @@ -277,7 +279,8 @@ struct _lowcore __u32 ext_call_fast; /* 0x02c4 */ __u64 int_clock; /* 0x02c8 */ __u64 clock_comparator; /* 0x02d0 */ - __u8 pad_0x02d8[0x0300-0x02d8]; /* 0x02d8 */ + __u32 machine_flags; /* 0x02d8 */ + __u8 pad_0x02dc[0x0300-0x02dc]; /* 0x02dc */ /* Interrupt response block */ __u8 irb[64]; /* 0x0300 */ @@ -381,7 +384,8 @@ struct _lowcore __u64 int_clock; /* 0x0340 */ __u64 clock_comparator; /* 0x0348 */ __u64 vdso_per_cpu_data; /* 0x0350 */ - __u8 pad_0x0358[0x0380-0x0358]; /* 0x0358 */ + __u64 machine_flags; /* 0x0358 */ + __u8 pad_0x0360[0x0380-0x0360]; /* 0x0360 */ /* Interrupt response block. */ __u8 irb[64]; /* 0x0380 */ diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index 61862b3ac794..c139fa7b8e89 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h @@ -14,7 +14,10 @@ #define __ASM_S390_PROCESSOR_H #include +#include +#include #include +#include #ifdef __KERNEL__ /* @@ -23,20 +26,6 @@ */ #define current_text_addr() ({ void *pc; asm("basr %0,0" : "=a" (pc)); pc; }) -/* - * CPU type and hardware bug flags. Kept separately for each CPU. - * Members of this structure are referenced in head.S, so think twice - * before touching them. [mj] - */ - -typedef struct -{ - unsigned int version : 8; - unsigned int ident : 24; - unsigned int machine : 16; - unsigned int unused : 16; -} __attribute__ ((packed)) cpuid_t; - static inline void get_cpu_id(cpuid_t *ptr) { asm volatile("stidp 0(%1)" : "=m" (*ptr) : "a" (ptr)); diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index f1b051630c50..539263fc9ab9 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h @@ -313,8 +313,6 @@ typedef struct #ifdef __KERNEL__ -#include -#include /* * The pt_regs struct defines the way the registers are stored on diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index e8bd6ac22c99..38b0fc221ed7 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -14,6 +14,7 @@ #ifdef __KERNEL__ +#include #include #define PARMAREA 0x10400 @@ -63,7 +64,6 @@ extern unsigned int s390_noexec; /* * Machine features detected in head.S */ -extern unsigned long machine_flags; #define MACHINE_FLAG_VM (1UL << 0) #define MACHINE_FLAG_IEEE (1UL << 1) @@ -77,28 +77,28 @@ extern unsigned long machine_flags; #define MACHINE_FLAG_HPAGE (1UL << 10) #define MACHINE_FLAG_PFMF (1UL << 11) -#define MACHINE_IS_VM (machine_flags & MACHINE_FLAG_VM) -#define MACHINE_IS_KVM (machine_flags & MACHINE_FLAG_KVM) -#define MACHINE_HAS_DIAG9C (machine_flags & MACHINE_FLAG_DIAG9C) +#define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM) +#define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM) +#define MACHINE_HAS_DIAG9C (S390_lowcore.machine_flags & MACHINE_FLAG_DIAG9C) #ifndef __s390x__ -#define MACHINE_HAS_IEEE (machine_flags & MACHINE_FLAG_IEEE) -#define MACHINE_HAS_CSP (machine_flags & MACHINE_FLAG_CSP) +#define MACHINE_HAS_IEEE (S390_lowcore.machine_flags & MACHINE_FLAG_IEEE) +#define MACHINE_HAS_CSP (S390_lowcore.machine_flags & MACHINE_FLAG_CSP) #define MACHINE_HAS_IDTE (0) #define MACHINE_HAS_DIAG44 (1) -#define MACHINE_HAS_MVPG (machine_flags & MACHINE_FLAG_MVPG) +#define MACHINE_HAS_MVPG (S390_lowcore.machine_flags & MACHINE_FLAG_MVPG) #define MACHINE_HAS_MVCOS (0) #define MACHINE_HAS_HPAGE (0) #define MACHINE_HAS_PFMF (0) #else /* __s390x__ */ #define MACHINE_HAS_IEEE (1) #define MACHINE_HAS_CSP (1) -#define MACHINE_HAS_IDTE (machine_flags & MACHINE_FLAG_IDTE) -#define MACHINE_HAS_DIAG44 (machine_flags & MACHINE_FLAG_DIAG44) +#define MACHINE_HAS_IDTE (S390_lowcore.machine_flags & MACHINE_FLAG_IDTE) +#define MACHINE_HAS_DIAG44 (S390_lowcore.machine_flags & MACHINE_FLAG_DIAG44) #define MACHINE_HAS_MVPG (1) -#define MACHINE_HAS_MVCOS (machine_flags & MACHINE_FLAG_MVCOS) -#define MACHINE_HAS_HPAGE (machine_flags & MACHINE_FLAG_HPAGE) -#define MACHINE_HAS_PFMF (machine_flags & MACHINE_FLAG_PFMF) +#define MACHINE_HAS_MVCOS (S390_lowcore.machine_flags & MACHINE_FLAG_MVCOS) +#define MACHINE_HAS_HPAGE (S390_lowcore.machine_flags & MACHINE_FLAG_HPAGE) +#define MACHINE_HAS_PFMF (S390_lowcore.machine_flags & MACHINE_FLAG_PFMF) #endif /* __s390x__ */ #define ZFCPDUMP_HSA_SIZE (32UL<<20) diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h index c544aa524535..461f2abd2e6f 100644 --- a/arch/s390/include/asm/thread_info.h +++ b/arch/s390/include/asm/thread_info.h @@ -31,8 +31,9 @@ #define ASYNC_SIZE (PAGE_SIZE << ASYNC_ORDER) #ifndef __ASSEMBLY__ -#include #include +#include +#include /* * low level task data that entry.S needs immediate access to diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 4d221c81c849..d4e1e5b6cfda 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -34,6 +34,8 @@ char kernel_nss_name[NSS_NAME_SIZE + 1]; +static unsigned long machine_flags; + static void __init setup_boot_command_line(void); @@ -391,5 +393,6 @@ void __init startup_init(void) setup_hpage(); sclp_facilities_detect(); detect_memory_layout(memory_chunk); + S390_lowcore.machine_flags = machine_flags; lockdep_on(); } diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 06201b93cbbf..163bdfe5a6be 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -82,9 +82,6 @@ EXPORT_SYMBOL(console_devno); unsigned int console_irq = -1; EXPORT_SYMBOL(console_irq); -unsigned long machine_flags; -EXPORT_SYMBOL(machine_flags); - unsigned long elf_hwcap = 0; char elf_platform[ELF_PLATFORM_SIZE]; @@ -426,6 +423,7 @@ setup_lowcore(void) __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0) + PAGE_SIZE; lc->current_task = (unsigned long) init_thread_union.thread_info.task; lc->thread_info = (unsigned long) &init_thread_union; + lc->machine_flags = S390_lowcore.machine_flags; #ifndef CONFIG_64BIT if (MACHINE_HAS_IEEE) { lc->extended_save_area_addr = (__u32) diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 006ed5016eb4..796630240715 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -571,6 +571,7 @@ int __cpuinit __cpu_up(unsigned int cpu) cpu_lowcore->current_task = (unsigned long) idle; cpu_lowcore->cpu_nr = cpu; cpu_lowcore->kernel_asce = S390_lowcore.kernel_asce; + cpu_lowcore->machine_flags = S390_lowcore.machine_flags; eieio(); while (signal_processor(cpu, sigp_restart) == sigp_busy) From a93e11f9b9604134373ae790bce738d62109de60 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 14 Apr 2009 15:36:17 +0200 Subject: [PATCH 02/14] [S390] wire up preadv/pwritev system calls Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/unistd.h | 4 +++- arch/s390/kernel/compat_wrapper.S | 18 ++++++++++++++++++ arch/s390/kernel/syscalls.S | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/arch/s390/include/asm/unistd.h b/arch/s390/include/asm/unistd.h index c8ad350d1444..f0f19e6ace6c 100644 --- a/arch/s390/include/asm/unistd.h +++ b/arch/s390/include/asm/unistd.h @@ -265,7 +265,9 @@ #define __NR_pipe2 325 #define __NR_dup3 326 #define __NR_epoll_create1 327 -#define NR_syscalls 328 +#define __NR_preadv 328 +#define __NR_pwritev 329 +#define NR_syscalls 330 /* * There are some system calls that are not present on 64 bit, some diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index 87cf5a79a351..fb38af6316bb 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -1805,3 +1805,21 @@ compat_sys_keyctl_wrapper: llgfr %r5,%r5 # u32 llgfr %r6,%r6 # u32 jg compat_sys_keyctl # branch to system call + + .globl compat_sys_preadv_wrapper +compat_sys_preadv_wrapper: + llgfr %r2,%r2 # unsigned long + llgtr %r3,%r3 # compat_iovec * + llgfr %r4,%r4 # unsigned long + llgfr %r5,%r5 # u32 + llgfr %r6,%r6 # u32 + jg compat_sys_preadv # branch to system call + + .globl compat_sys_pwritev_wrapper +compat_sys_pwritev_wrapper: + llgfr %r2,%r2 # unsigned long + llgtr %r3,%r3 # compat_iovec * + llgfr %r4,%r4 # unsigned long + llgfr %r5,%r5 # u32 + llgfr %r6,%r6 # u32 + jg compat_sys_pwritev # branch to system call diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S index fe5b25a988ab..2c7739fe70b1 100644 --- a/arch/s390/kernel/syscalls.S +++ b/arch/s390/kernel/syscalls.S @@ -336,3 +336,5 @@ SYSCALL(sys_inotify_init1,sys_inotify_init1,sys_inotify_init1_wrapper) SYSCALL(sys_pipe2,sys_pipe2,sys_pipe2_wrapper) /* 325 */ SYSCALL(sys_dup3,sys_dup3,sys_dup3_wrapper) SYSCALL(sys_epoll_create1,sys_epoll_create1,sys_epoll_create1_wrapper) +SYSCALL(sys_preadv,sys_preadv,compat_sys_preadv_wrapper) +SYSCALL(sys_pwritev,sys_pwritev,compat_sys_pwritev_wrapper) From 81f64b87731aa33eef6b88af9d92c3398d48cf41 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 14 Apr 2009 15:36:18 +0200 Subject: [PATCH 03/14] [S390] call nmi_enter/nmi_exit on machine checks nmi_enter/nmi_exit includes the lockdep calls and various other calls which were missing so far. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/nmi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 4bfdc421d7e9..28cf196ba775 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -253,7 +254,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs) struct mci *mci; int umode; - lockdep_off(); + nmi_enter(); s390_idle_check(); mci = (struct mci *) &S390_lowcore.mcck_interruption_code; @@ -363,7 +364,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs) mcck->warning = 1; set_thread_flag(TIF_MCCK_PENDING); } - lockdep_on(); + nmi_exit(); } static int __init machine_check_init(void) From 0436230148c55e3afbe5c57775a1fb44ba4834ac Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 14 Apr 2009 15:36:19 +0200 Subject: [PATCH 04/14] [S390] stp synchronization retry timer Add a timer that retries the clock synchronization via the server time protocol if there is a usable clock but the synchronization failed. Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/time.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index f72d41068dc2..05f93e77870b 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -1423,6 +1423,7 @@ static void *stp_page; static void stp_work_fn(struct work_struct *work); static DEFINE_MUTEX(stp_work_mutex); static DECLARE_WORK(stp_work, stp_work_fn); +static struct timer_list stp_timer; static int __init early_parse_stp(char *p) { @@ -1454,10 +1455,16 @@ static void __init stp_reset(void) } } +static void stp_timeout(unsigned long dummy) +{ + queue_work(time_sync_wq, &stp_work); +} + static int __init stp_init(void) { if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags)) return 0; + setup_timer(&stp_timer, stp_timeout, 0UL); time_init_wq(); if (!stp_online) return 0; @@ -1565,6 +1572,7 @@ static void stp_work_fn(struct work_struct *work) if (!stp_online) { chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000); + del_timer_sync(&stp_timer); goto out_unlock; } @@ -1586,6 +1594,13 @@ static void stp_work_fn(struct work_struct *work) stop_machine(stp_sync_clock, &stp_sync, &cpu_online_map); put_online_cpus(); + if (!check_sync_clock()) + /* + * There is a usable clock but the synchonization failed. + * Retry after a second. + */ + mod_timer(&stp_timer, jiffies + HZ); + out_unlock: mutex_unlock(&stp_work_mutex); } From b6ecfa9273e27b5c7ba04655eb44f78bf4db5b64 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Tue, 14 Apr 2009 15:36:20 +0200 Subject: [PATCH 05/14] [S390] extend virtual timer interface by mod_virt_timer_periodic In case mod_virt_timer is used to add a non pending timer the timer is always added as a one-shot timer. If mod_virt_timer is used for periodic timers they may therfore be degraded to one-shot timers. Add mod_virt_timer_periodic to the interface to allow safe re-programming of the interval value. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/timer.h | 1 + arch/s390/kernel/vtime.c | 57 ++++++++++++++++++++--------------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/arch/s390/include/asm/timer.h b/arch/s390/include/asm/timer.h index e4bcab739c19..814243cafdfe 100644 --- a/arch/s390/include/asm/timer.h +++ b/arch/s390/include/asm/timer.h @@ -41,6 +41,7 @@ extern void init_virt_timer(struct vtimer_list *timer); extern void add_virt_timer(void *new); extern void add_virt_timer_periodic(void *new); extern int mod_virt_timer(struct vtimer_list *timer, __u64 expires); +extern int mod_virt_timer_periodic(struct vtimer_list *timer, __u64 expires); extern int del_virt_timer(struct vtimer_list *timer); extern void init_cpu_vtimer(void); diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index ecf0304e61c1..694b44374a2b 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -425,17 +425,7 @@ void add_virt_timer_periodic(void *new) } EXPORT_SYMBOL(add_virt_timer_periodic); -/* - * If we change a pending timer the function must be called on the CPU - * where the timer is running on, e.g. by smp_call_function_single() - * - * The original mod_timer adds the timer if it is not pending. For - * compatibility we do the same. The timer will be added on the current - * CPU as a oneshot timer. - * - * returns whether it has modified a pending timer (1) or not (0) - */ -int mod_virt_timer(struct vtimer_list *timer, __u64 expires) +int __mod_vtimer(struct vtimer_list *timer, __u64 expires, int periodic) { struct vtimer_queue *vq; unsigned long flags; @@ -444,39 +434,35 @@ int mod_virt_timer(struct vtimer_list *timer, __u64 expires) BUG_ON(!timer->function); BUG_ON(!expires || expires > VTIMER_MAX_SLICE); - /* - * This is a common optimization triggered by the - * networking code - if the timer is re-modified - * to be the same thing then just return: - */ if (timer->expires == expires && vtimer_pending(timer)) return 1; cpu = get_cpu(); vq = &per_cpu(virt_cpu_timer, cpu); - /* check if we run on the right CPU */ - BUG_ON(timer->cpu != cpu); - /* disable interrupts before test if timer is pending */ spin_lock_irqsave(&vq->lock, flags); /* if timer isn't pending add it on the current CPU */ if (!vtimer_pending(timer)) { spin_unlock_irqrestore(&vq->lock, flags); - /* we do not activate an interval timer with mod_virt_timer */ - timer->interval = 0; + + if (periodic) + timer->interval = expires; + else + timer->interval = 0; timer->expires = expires; timer->cpu = cpu; internal_add_vtimer(timer); return 0; } + /* check if we run on the right CPU */ + BUG_ON(timer->cpu != cpu); + list_del_init(&timer->entry); timer->expires = expires; - - /* also change the interval if we have an interval timer */ - if (timer->interval) + if (periodic) timer->interval = expires; /* the timer can't expire anymore so we can release the lock */ @@ -484,8 +470,31 @@ int mod_virt_timer(struct vtimer_list *timer, __u64 expires) internal_add_vtimer(timer); return 1; } + +/* + * If we change a pending timer the function must be called on the CPU + * where the timer is running on. + * + * returns whether it has modified a pending timer (1) or not (0) + */ +int mod_virt_timer(struct vtimer_list *timer, __u64 expires) +{ + return __mod_vtimer(timer, expires, 0); +} EXPORT_SYMBOL(mod_virt_timer); +/* + * If we change a pending timer the function must be called on the CPU + * where the timer is running on. + * + * returns whether it has modified a pending timer (1) or not (0) + */ +int mod_virt_timer_periodic(struct vtimer_list *timer, __u64 expires) +{ + return __mod_vtimer(timer, expires, 1); +} +EXPORT_SYMBOL(mod_virt_timer_periodic); + /* * delete a virtual timer * From 43ae8a1b32735c662ba7ebf3509c4f670f75e3d5 Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Tue, 14 Apr 2009 15:36:21 +0200 Subject: [PATCH 06/14] [S390] appldata: Use new mod_virt_timer_periodic() function. mod_virt_timer() was used to modify/add cpu timers for cpus that were set online. This resulted in a one-shot timer for every cpu that was newly added or previously set offline, instead of an interval timer, which broke the appldata vtime interval setup. To fix this, the new mod_virt_timer_periodic() function is used, which adds interval timers instead of one-shot timers. Signed-off-by: Gerald Schaefer Signed-off-by: Martin Schwidefsky --- arch/s390/appldata/appldata_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index 27b70d8a359c..aeb3cff95f63 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c @@ -176,7 +176,7 @@ static void __appldata_mod_vtimer_wrap(void *p) { struct vtimer_list *timer; u64 expires; } *args = p; - mod_virt_timer(args->timer, args->expires); + mod_virt_timer_periodic(args->timer, args->expires); } #define APPLDATA_ADD_TIMER 0 From 75cb71f3184f3dd5b78d991d5e0e047774865f5d Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Tue, 14 Apr 2009 15:36:22 +0200 Subject: [PATCH 07/14] [S390] qdio: remove dead timeout handler The QDIO ccw devices are started by ccw_device_start so no timeout can occur for the interrupt handler. Remove the dead code. In case of an I/O error set the device state to error and wake up a possibly running qdio_shutdown waiter. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_main.c | 43 ++---------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 9e8a2914259b..accd957454e7 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -881,42 +881,6 @@ no_handler: qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED); } -static void qdio_call_shutdown(struct work_struct *work) -{ - struct ccw_device_private *priv; - struct ccw_device *cdev; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; - qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR); - put_device(&cdev->dev); -} - -static void qdio_int_error(struct ccw_device *cdev) -{ - struct qdio_irq *irq_ptr = cdev->private->qdio_data; - - switch (irq_ptr->state) { - case QDIO_IRQ_STATE_INACTIVE: - case QDIO_IRQ_STATE_CLEANUP: - qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); - break; - case QDIO_IRQ_STATE_ESTABLISHED: - case QDIO_IRQ_STATE_ACTIVE: - qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED); - if (get_device(&cdev->dev)) { - /* Can't call shutdown from interrupt context. */ - PREPARE_WORK(&cdev->private->kick_work, - qdio_call_shutdown); - queue_work(ccw_device_work, &cdev->private->kick_work); - } - break; - default: - WARN_ON(1); - } - wake_up(&cdev->private->wait_q); -} - static int qdio_establish_check_errors(struct ccw_device *cdev, int cstat, int dstat) { @@ -973,10 +937,8 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, switch (PTR_ERR(irb)) { case -EIO: DBF_ERROR("%4x IO error", irq_ptr->schid.sch_no); - return; - case -ETIMEDOUT: - DBF_ERROR("%4x IO timeout", irq_ptr->schid.sch_no); - qdio_int_error(cdev); + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); + wake_up(&cdev->private->wait_q); return; default: WARN_ON(1); @@ -1001,7 +963,6 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, case QDIO_IRQ_STATE_ACTIVE: if (cstat & SCHN_STAT_PCI) { qdio_int_handler_pci(irq_ptr); - /* no state change so no need to wake up wait_q */ return; } if ((cstat & ~SCHN_STAT_PCI) || dstat) { From f3445a1a656bc26b07946cc6d20de1ef07c8d116 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 14 Apr 2009 15:36:23 +0200 Subject: [PATCH 08/14] [S390] dasd: Use the new async framework for autoonlining. The dasd driver can automatically online detected dasds, which especially important for finding the root device. Currently, it will wait for each online operation to finish individually, which may take long if many dasds need to be onlined. When using the new async framework, these onlining operations can run in parallel and presence of the root device is ensured by the fact that prepare_namespace() waits for all async threads to finish. Signed-off-by: Cornelia Huck Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 0570794ccf1c..d1815272c435 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -480,8 +481,10 @@ static void dasd_change_state(struct dasd_device *device) if (rc && rc != -EAGAIN) device->target = device->state; - if (device->state == device->target) + if (device->state == device->target) { wake_up(&dasd_init_waitq); + dasd_put_device(device); + } /* let user-space know that the device status changed */ kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE); @@ -513,12 +516,15 @@ void dasd_kick_device(struct dasd_device *device) */ void dasd_set_target_state(struct dasd_device *device, int target) { + dasd_get_device(device); /* If we are in probeonly mode stop at DASD_STATE_READY. */ if (dasd_probeonly && target > DASD_STATE_READY) target = DASD_STATE_READY; if (device->target != target) { - if (device->state == target) + if (device->state == target) { wake_up(&dasd_init_waitq); + dasd_put_device(device); + } device->target = target; } if (device->state != device->target) @@ -2148,6 +2154,22 @@ dasd_exit(void) * SECTION: common functions for ccw_driver use */ +static void dasd_generic_auto_online(void *data, async_cookie_t cookie) +{ + struct ccw_device *cdev = data; + int ret; + + ret = ccw_device_set_online(cdev); + if (ret) + pr_warning("%s: Setting the DASD online failed with rc=%d\n", + dev_name(&cdev->dev), ret); + else { + struct dasd_device *device = dasd_device_from_cdev(cdev); + wait_event(dasd_init_waitq, _wait_for_device(device)); + dasd_put_device(device); + } +} + /* * Initial attempt at a probe function. this can be simplified once * the other detection code is gone. @@ -2180,10 +2202,7 @@ int dasd_generic_probe(struct ccw_device *cdev, */ if ((dasd_get_feature(cdev, DASD_FEATURE_INITIAL_ONLINE) > 0 ) || (dasd_autodetect && dasd_busid_known(dev_name(&cdev->dev)) != 0)) - ret = ccw_device_set_online(cdev); - if (ret) - pr_warning("%s: Setting the DASD online failed with rc=%d\n", - dev_name(&cdev->dev), ret); + async_schedule(dasd_generic_auto_online, cdev); return 0; } @@ -2290,13 +2309,7 @@ int dasd_generic_set_online(struct ccw_device *cdev, } else pr_debug("dasd_generic device %s found\n", dev_name(&cdev->dev)); - - /* FIXME: we have to wait for the root device but we don't want - * to wait for each single device but for all at once. */ - wait_event(dasd_init_waitq, _wait_for_device(device)); - dasd_put_device(device); - return rc; } From 52db45c3c55a0fca53077dfd7b123e30cd210aad Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Tue, 14 Apr 2009 15:36:24 +0200 Subject: [PATCH 09/14] [S390] dasd: fix idaw boundary checking for track based ccw A ccw command that reads or writes several records at once will usually transfer more data then fits into one page and needs to address memory areas using a list of indirect data address words (idaw). All but the first of these areas must start on a 4KB or 2KB block boundary (depending on the idaw format). A check for this restriction was missing and has been added with this patch. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 21254793c604..cb52da033f06 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2019,15 +2019,23 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( ccw++; recid += count; new_track = 0; + /* first idaw for a ccw may start anywhere */ + if (!idaw_dst) + idaw_dst = dst; } - /* If we start a new idaw, everything is fine and the - * start of the new idaw is the start of this segment. + /* If we start a new idaw, we must make sure that it + * starts on an IDA_BLOCK_SIZE boundary. * If we continue an idaw, we must make sure that the * current segment begins where the so far accumulated * idaw ends */ - if (!idaw_dst) - idaw_dst = dst; + if (!idaw_dst) { + if (__pa(dst) & (IDA_BLOCK_SIZE-1)) { + dasd_sfree_request(cqr, startdev); + return ERR_PTR(-ERANGE); + } else + idaw_dst = dst; + } if ((idaw_dst + idaw_len) != dst) { dasd_sfree_request(cqr, startdev); return ERR_PTR(-ERANGE); From 88e012856d05a5d00ae80c691fb7aa5adda268d7 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 14 Apr 2009 15:36:25 +0200 Subject: [PATCH 10/14] [S390] smp: fix cpu_possible_map initialization The cpu_possible_map by default is initialized with all ones in s390. If the kernel paramert possible_cpus= is passed the cpu_possible_map is supposed to have x bits set. However the current code just sets the x bits without clearing the NR_CPUS bits that were already set. So we end up with an unchanged map that has all bits set. To fix this just clear the map before setting any new bits. This broke with def6cfb70bab83c0094bc0cedd27c4eda563043e "[S390] cpumask: Use accessors code." Cc: Rusty Russell Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/smp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 796630240715..a985a3ba4401 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -591,7 +591,8 @@ static int __init setup_possible_cpus(char *s) int pcpus, cpu; pcpus = simple_strtoul(s, NULL, 0); - for (cpu = 0; cpu < pcpus && cpu < nr_cpu_ids; cpu++) + init_cpu_possible(cpumask_of(0)); + for (cpu = 1; cpu < pcpus && cpu < nr_cpu_ids; cpu++) set_cpu_possible(cpu, true); return 0; } From b86ccca49cd8f22086c1d135ab3051cf48fb1688 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 14 Apr 2009 15:36:26 +0200 Subject: [PATCH 11/14] [S390] fix idle time accounting The steal time is calculated by subtracting the time the virtual cpu has been running on a physical cpu from the wall clock time. To make that work all wall time needs to be added to the steal time field first before the virtual cpu time is subtracted. The time between the last clock update and the load of the enabled wait psw needs to be added to the steal_time field as well to make the sum over all cpu accounting numbers match the wall clock. Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/vtime.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 694b44374a2b..c0870a61f90f 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -134,6 +134,8 @@ void vtime_start_cpu(void) /* Account time spent with enabled wait psw loaded as idle time. */ idle_time = S390_lowcore.int_clock - idle->idle_enter; account_idle_time(idle_time); + S390_lowcore.steal_timer += + idle->idle_enter - S390_lowcore.last_update_clock; S390_lowcore.last_update_clock = S390_lowcore.int_clock; /* Account system time spent going idle. */ From 5b409ed17bb32c8316b1f456466c70529454573a Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 14 Apr 2009 15:36:27 +0200 Subject: [PATCH 12/14] [S390] cpu hotplug and accounting values Reset the cpu timer to the maximum value and correctly initialize the cpu accounting values in the lowcore when the cpu is started. Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/asm-offsets.c | 2 ++ arch/s390/kernel/entry.S | 13 +++++++++++++ arch/s390/kernel/entry64.S | 13 +++++++++++++ 3 files changed, 28 insertions(+) diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 67a60016babb..fa9905ce7d0b 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -27,6 +27,8 @@ int main(void) DEFINE(__TI_flags, offsetof(struct thread_info, flags)); DEFINE(__TI_cpu, offsetof(struct thread_info, cpu)); DEFINE(__TI_precount, offsetof(struct thread_info, preempt_count)); + DEFINE(__TI_user_timer, offsetof(struct thread_info, user_timer)); + DEFINE(__TI_system_timer, offsetof(struct thread_info, system_timer)); BLANK(); DEFINE(__PT_ARGS, offsetof(struct pt_regs, args)); DEFINE(__PT_PSW, offsetof(struct pt_regs, psw)); diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 1268aa2991bf..f3e275934213 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -837,16 +837,29 @@ mcck_return: __CPUINIT .globl restart_int_handler restart_int_handler: + basr %r1,0 +restart_base: + spt restart_vtime-restart_base(%r1) + stck __LC_LAST_UPDATE_CLOCK + mvc __LC_LAST_UPDATE_TIMER(8),restart_vtime-restart_base(%r1) + mvc __LC_EXIT_TIMER(8),restart_vtime-restart_base(%r1) l %r15,__LC_SAVE_AREA+60 # load ksp lctl %c0,%c15,__LC_CREGS_SAVE_AREA # get new ctl regs lam %a0,%a15,__LC_AREGS_SAVE_AREA lm %r6,%r15,__SF_GPRS(%r15) # load registers from clone + l %r1,__LC_THREAD_INFO + mvc __LC_USER_TIMER(8),__TI_user_timer(%r1) + mvc __LC_SYSTEM_TIMER(8),__TI_system_timer(%r1) + xc __LC_STEAL_TIMER(8),__LC_STEAL_TIMER stosm __SF_EMPTY(%r15),0x04 # now we can turn dat on basr %r14,0 l %r14,restart_addr-.(%r14) br %r14 # branch to start_secondary restart_addr: .long start_secondary + .align 8 +restart_vtime: + .long 0x7fffffff,0xffffffff .previous #else /* diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index c6fbde13971a..84a105838e03 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -831,14 +831,27 @@ mcck_return: __CPUINIT .globl restart_int_handler restart_int_handler: + basr %r1,0 +restart_base: + spt restart_vtime-restart_base(%r1) + stck __LC_LAST_UPDATE_CLOCK + mvc __LC_LAST_UPDATE_TIMER(8),restart_vtime-restart_base(%r1) + mvc __LC_EXIT_TIMER(8),restart_vtime-restart_base(%r1) lg %r15,__LC_SAVE_AREA+120 # load ksp lghi %r10,__LC_CREGS_SAVE_AREA lctlg %c0,%c15,0(%r10) # get new ctl regs lghi %r10,__LC_AREGS_SAVE_AREA lam %a0,%a15,0(%r10) lmg %r6,%r15,__SF_GPRS(%r15) # load registers from clone + lg %r1,__LC_THREAD_INFO + mvc __LC_USER_TIMER(8),__TI_user_timer(%r1) + mvc __LC_SYSTEM_TIMER(8),__TI_system_timer(%r1) + xc __LC_STEAL_TIMER(8),__LC_STEAL_TIMER stosm __SF_EMPTY(%r15),0x04 # now we can turn dat on jg start_secondary + .align 8 +restart_vtime: + .long 0x7fffffff,0xffffffff .previous #else /* From b6112ccbff5ec580d46b584ecc3c3a773b830da2 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 14 Apr 2009 15:36:28 +0200 Subject: [PATCH 13/14] [S390] add read_persistent_clock Add a read_persistent_clock function that does not just return 0. Since timekeeping_init calls the function before time_init has been called move reset_tod_clock to early.c to make sure that the TOD clock is running when read_persistent_clock is invoked. Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/timex.h | 5 +++++ arch/s390/kernel/early.c | 16 ++++++++++++++++ arch/s390/kernel/head.S | 7 +++++-- arch/s390/kernel/time.c | 28 +++++++++------------------- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h index d744c3d62de5..cc21e3e20fd7 100644 --- a/arch/s390/include/asm/timex.h +++ b/arch/s390/include/asm/timex.h @@ -11,6 +11,9 @@ #ifndef _ASM_S390_TIMEX_H #define _ASM_S390_TIMEX_H +/* The value of the TOD clock for 1.1.1970. */ +#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL + /* Inline functions for clock register access. */ static inline int set_clock(__u64 time) { @@ -85,4 +88,6 @@ int get_sync_clock(unsigned long long *clock); void init_cpu_timer(void); unsigned long long monotonic_clock(void); +extern u64 sched_clock_base_cc; + #endif diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index d4e1e5b6cfda..cf09948faad6 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -38,6 +38,21 @@ static unsigned long machine_flags; static void __init setup_boot_command_line(void); +/* + * Get the TOD clock running. + */ +static void __init reset_tod_clock(void) +{ + u64 time; + + if (store_clock(&time) == 0) + return; + /* TOD clock not running. Set the clock to Unix Epoch. */ + if (set_clock(TOD_UNIX_EPOCH) != 0 || store_clock(&time) != 0) + disabled_wait(0); + + sched_clock_base_cc = TOD_UNIX_EPOCH; +} #ifdef CONFIG_SHARED_KERNEL int __init savesys_ipl_nss(char *cmd, const int cmdlen); @@ -372,6 +387,7 @@ static void __init setup_boot_command_line(void) */ void __init startup_init(void) { + reset_tod_clock(); ipl_save_parameters(); rescue_initrd(); clear_bss_section(); diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S index 1046c2c9f8d1..16f8975325ed 100644 --- a/arch/s390/kernel/head.S +++ b/arch/s390/kernel/head.S @@ -471,6 +471,8 @@ startup:basr %r13,0 # get base .LPG0: xc 0x200(256),0x200 # partially clear lowcore xc 0x300(256),0x300 + l %r1,5f-.LPG0(%r13) + stck 0(%r1) #ifndef CONFIG_MARCH_G5 # check processor version against MARCH_{G5,Z900,Z990,Z9_109,Z10} @@ -496,9 +498,10 @@ startup:basr %r13,0 # get base brct %r0,0b #endif - l %r13,0f-.LPG0(%r13) + l %r13,4f-.LPG0(%r13) b 0(%r13) -0: .long startup_continue +4: .long startup_continue +5: .long sched_clock_base_cc # # params at 10400 (setup.h) diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 05f93e77870b..369ff02c4ab2 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -52,9 +52,6 @@ #define USECS_PER_JIFFY ((unsigned long) 1000000/HZ) #define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12) -/* The value of the TOD clock for 1.1.1970. */ -#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL - /* * Create a small time difference between the timer interrupts * on the different cpus to avoid lock contention. @@ -63,9 +60,10 @@ #define TICK_SIZE tick +u64 sched_clock_base_cc = -1; /* Force to data section. */ + static ext_int_info_t ext_int_info_cc; static ext_int_info_t ext_int_etr_cc; -static u64 sched_clock_base_cc; static DEFINE_PER_CPU(struct clock_event_device, comparators); @@ -195,22 +193,12 @@ static void timing_alert_interrupt(__u16 code) static void etr_reset(void); static void stp_reset(void); -/* - * Get the TOD clock running. - */ -static u64 __init reset_tod_clock(void) +unsigned long read_persistent_clock(void) { - u64 time; + struct timespec ts; - etr_reset(); - stp_reset(); - if (store_clock(&time) == 0) - return time; - /* TOD clock not running. Set the clock to Unix Epoch. */ - if (set_clock(TOD_UNIX_EPOCH) != 0 || store_clock(&time) != 0) - panic("TOD clock not operational."); - - return TOD_UNIX_EPOCH; + tod_to_timeval(get_clock() - TOD_UNIX_EPOCH, &ts); + return ts.tv_sec; } static cycle_t read_tod_clock(void) @@ -265,7 +253,9 @@ void update_vsyscall_tz(void) */ void __init time_init(void) { - sched_clock_base_cc = reset_tod_clock(); + /* Reset time synchronization interfaces. */ + etr_reset(); + stp_reset(); /* set xtime */ tod_to_timeval(sched_clock_base_cc - TOD_UNIX_EPOCH, &xtime); From ab96e798cbd562a53edd802272e49a5100b29efb Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 14 Apr 2009 15:36:29 +0200 Subject: [PATCH 14/14] [S390] boot cputime accounting Start the cpu time accounting very early to catch the cpu time spent for the initial kernel setup. To make the output of /proc/uptime match the sum of all cpu accounting values of the boot cpu reset xtime and wall_to_monotonic to sane values based on the TOD clock. The values set by timekeeping_init are off by up to a second. Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/head.S | 8 +++++++- arch/s390/kernel/setup.c | 8 ++++++++ arch/s390/kernel/time.c | 36 ++++++++++++++++++++++++++++-------- arch/s390/kernel/vtime.c | 8 -------- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S index 16f8975325ed..bba14494ee00 100644 --- a/arch/s390/kernel/head.S +++ b/arch/s390/kernel/head.S @@ -473,7 +473,10 @@ startup:basr %r13,0 # get base xc 0x300(256),0x300 l %r1,5f-.LPG0(%r13) stck 0(%r1) - + spt 6f-.LPG0(%r13) + mvc __LC_LAST_UPDATE_CLOCK(8),0(%r1) + mvc __LC_LAST_UPDATE_TIMER(8),6f-.LPG0(%r13) + mvc __LC_EXIT_TIMER(8),5f-.LPG0(%r13) #ifndef CONFIG_MARCH_G5 # check processor version against MARCH_{G5,Z900,Z990,Z9_109,Z10} stidp __LC_CPUID # store cpuid @@ -500,8 +503,11 @@ startup:basr %r13,0 # get base l %r13,4f-.LPG0(%r13) b 0(%r13) + .align 4 4: .long startup_continue 5: .long sched_clock_base_cc + .align 8 +6: .long 0x7fffffff,0xffffffff # # params at 10400 (setup.h) diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 163bdfe5a6be..7402b6a39ead 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -434,6 +434,14 @@ setup_lowcore(void) #else lc->vdso_per_cpu_data = (unsigned long) &lc->paste[0]; #endif + lc->sync_enter_timer = S390_lowcore.sync_enter_timer; + lc->async_enter_timer = S390_lowcore.async_enter_timer; + lc->exit_timer = S390_lowcore.exit_timer; + lc->user_timer = S390_lowcore.user_timer; + lc->system_timer = S390_lowcore.system_timer; + lc->steal_timer = S390_lowcore.steal_timer; + lc->last_update_timer = S390_lowcore.last_update_timer; + lc->last_update_clock = S390_lowcore.last_update_clock; set_prefix((u32)(unsigned long) lc); lowcore_ptr[0] = lc; } diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 369ff02c4ab2..6ded50dfa75a 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -253,32 +253,52 @@ void update_vsyscall_tz(void) */ void __init time_init(void) { + struct timespec ts; + unsigned long flags; + cycle_t now; + /* Reset time synchronization interfaces. */ etr_reset(); stp_reset(); - /* set xtime */ - tod_to_timeval(sched_clock_base_cc - TOD_UNIX_EPOCH, &xtime); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - /* request the clock comparator external interrupt */ if (register_early_external_interrupt(0x1004, clock_comparator_interrupt, &ext_int_info_cc) != 0) panic("Couldn't request external interrupt 0x1004"); - if (clocksource_register(&clocksource_tod) != 0) - panic("Could not register TOD clock source"); - /* request the timing alert external interrupt */ if (register_early_external_interrupt(0x1406, timing_alert_interrupt, &ext_int_etr_cc) != 0) panic("Couldn't request external interrupt 0x1406"); + if (clocksource_register(&clocksource_tod) != 0) + panic("Could not register TOD clock source"); + + /* + * The TOD clock is an accurate clock. The xtime should be + * initialized in a way that the difference between TOD and + * xtime is reasonably small. Too bad that timekeeping_init + * sets xtime.tv_nsec to zero. In addition the clock source + * change from the jiffies clock source to the TOD clock + * source add another error of up to 1/HZ second. The same + * function sets wall_to_monotonic to a value that is too + * small for /proc/uptime to be accurate. + * Reset xtime and wall_to_monotonic to sane values. + */ + write_seqlock_irqsave(&xtime_lock, flags); + now = get_clock(); + tod_to_timeval(now - TOD_UNIX_EPOCH, &xtime); + clocksource_tod.cycle_last = now; + clocksource_tod.raw_time = xtime; + tod_to_timeval(sched_clock_base_cc - TOD_UNIX_EPOCH, &ts); + set_normalized_timespec(&wall_to_monotonic, -ts.tv_sec, -ts.tv_nsec); + write_sequnlock_irqrestore(&xtime_lock, flags); + /* Enable TOD clock interrupts on the boot cpu. */ init_cpu_timer(); + /* Enable cpu timer interrupts on the boot cpu. */ vtime_init(); } diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index c0870a61f90f..38ea92ff04f9 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -527,16 +527,8 @@ EXPORT_SYMBOL(del_virt_timer); */ void init_cpu_vtimer(void) { - struct thread_info *ti = current_thread_info(); struct vtimer_queue *vq; - S390_lowcore.user_timer = ti->user_timer; - S390_lowcore.system_timer = ti->system_timer; - - /* kick the virtual timer */ - asm volatile ("STCK %0" : "=m" (S390_lowcore.last_update_clock)); - asm volatile ("STPT %0" : "=m" (S390_lowcore.last_update_timer)); - /* initialize per cpu vtimer structure */ vq = &__get_cpu_var(virt_cpu_timer); INIT_LIST_HEAD(&vq->list);