diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2012-03-11 11:59:27 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2012-03-11 11:59:28 -0400 |
commit | 4c1051e37a0e2a941115c6fb7ba08c318f25a0f9 (patch) | |
tree | f228f1a90c0d7abb8308f275d0906dd7d1588ba3 /arch/s390/kernel | |
parent | 8b646bd759086f6090fe27acf414c0b5faa737f4 (diff) |
[S390] rework idle code
Whenever the cpu loads an enabled wait PSW it will appear as idle to the
underlying host system. The code in default_idle calls vtime_stop_cpu
which does the necessary voodoo to get the cpu time accounting right.
The udelay code just loads an enabled wait PSW. To correct this rework
the vtime_stop_cpu/vtime_start_cpu logic and move the difficult parts
to entry[64].S, vtime_stop_cpu can now be called from anywhere and
vtime_start_cpu is gone. The correction of the cpu time during wakeup
from an enabled wait PSW is done with a critical section in entry[64].S.
As vtime_start_cpu is gone, s390_idle_check can be removed as well.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel')
-rw-r--r-- | arch/s390/kernel/asm-offsets.c | 8 | ||||
-rw-r--r-- | arch/s390/kernel/entry.S | 76 | ||||
-rw-r--r-- | arch/s390/kernel/entry.h | 5 | ||||
-rw-r--r-- | arch/s390/kernel/entry64.S | 65 | ||||
-rw-r--r-- | arch/s390/kernel/irq.c | 2 | ||||
-rw-r--r-- | arch/s390/kernel/nmi.c | 2 | ||||
-rw-r--r-- | arch/s390/kernel/process.c | 7 | ||||
-rw-r--r-- | arch/s390/kernel/smp.c | 46 | ||||
-rw-r--r-- | arch/s390/kernel/vtime.c | 163 |
9 files changed, 193 insertions, 181 deletions
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index aeeaf896be9b..ed8c913db79e 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c | |||
@@ -8,6 +8,8 @@ | |||
8 | 8 | ||
9 | #include <linux/kbuild.h> | 9 | #include <linux/kbuild.h> |
10 | #include <linux/sched.h> | 10 | #include <linux/sched.h> |
11 | #include <asm/cputime.h> | ||
12 | #include <asm/timer.h> | ||
11 | #include <asm/vdso.h> | 13 | #include <asm/vdso.h> |
12 | #include <asm/pgtable.h> | 14 | #include <asm/pgtable.h> |
13 | #include <asm/system.h> | 15 | #include <asm/system.h> |
@@ -70,6 +72,12 @@ int main(void) | |||
70 | DEFINE(__CLOCK_MONOTONIC, CLOCK_MONOTONIC); | 72 | DEFINE(__CLOCK_MONOTONIC, CLOCK_MONOTONIC); |
71 | DEFINE(__CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC); | 73 | DEFINE(__CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC); |
72 | BLANK(); | 74 | BLANK(); |
75 | /* idle data offsets */ | ||
76 | DEFINE(__IDLE_ENTER, offsetof(struct s390_idle_data, idle_enter)); | ||
77 | DEFINE(__IDLE_EXIT, offsetof(struct s390_idle_data, idle_exit)); | ||
78 | /* vtimer queue offsets */ | ||
79 | DEFINE(__VQ_IDLE_ENTER, offsetof(struct vtimer_queue, idle_enter)); | ||
80 | DEFINE(__VQ_IDLE_EXIT, offsetof(struct vtimer_queue, idle_exit)); | ||
73 | /* lowcore offsets */ | 81 | /* lowcore offsets */ |
74 | DEFINE(__LC_EXT_PARAMS, offsetof(struct _lowcore, ext_params)); | 82 | DEFINE(__LC_EXT_PARAMS, offsetof(struct _lowcore, ext_params)); |
75 | DEFINE(__LC_EXT_CPU_ADDR, offsetof(struct _lowcore, ext_cpu_addr)); | 83 | DEFINE(__LC_EXT_CPU_ADDR, offsetof(struct _lowcore, ext_cpu_addr)); |
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 6143521a4fff..74ee563fe62b 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S | |||
@@ -105,14 +105,14 @@ STACK_SIZE = 1 << STACK_SHIFT | |||
105 | 105 | ||
106 | .macro ADD64 high,low,timer | 106 | .macro ADD64 high,low,timer |
107 | al \high,\timer | 107 | al \high,\timer |
108 | al \low,\timer+4 | 108 | al \low,4+\timer |
109 | brc 12,.+8 | 109 | brc 12,.+8 |
110 | ahi \high,1 | 110 | ahi \high,1 |
111 | .endm | 111 | .endm |
112 | 112 | ||
113 | .macro SUB64 high,low,timer | 113 | .macro SUB64 high,low,timer |
114 | sl \high,\timer | 114 | sl \high,\timer |
115 | sl \low,\timer+4 | 115 | sl \low,4+\timer |
116 | brc 3,.+8 | 116 | brc 3,.+8 |
117 | ahi \high,-1 | 117 | ahi \high,-1 |
118 | .endm | 118 | .endm |
@@ -471,7 +471,6 @@ io_tif: | |||
471 | jnz io_work # there is work to do (signals etc.) | 471 | jnz io_work # there is work to do (signals etc.) |
472 | io_restore: | 472 | io_restore: |
473 | mvc __LC_RETURN_PSW(8),__PT_PSW(%r11) | 473 | mvc __LC_RETURN_PSW(8),__PT_PSW(%r11) |
474 | ni __LC_RETURN_PSW+1,0xfd # clean wait state bit | ||
475 | stpt __LC_EXIT_TIMER | 474 | stpt __LC_EXIT_TIMER |
476 | lm %r0,%r15,__PT_R0(%r11) | 475 | lm %r0,%r15,__PT_R0(%r11) |
477 | lpsw __LC_RETURN_PSW | 476 | lpsw __LC_RETURN_PSW |
@@ -612,6 +611,26 @@ ext_skip: | |||
612 | basr %r14,%r1 # call do_extint | 611 | basr %r14,%r1 # call do_extint |
613 | j io_return | 612 | j io_return |
614 | 613 | ||
614 | /* | ||
615 | * Load idle PSW. The second "half" of this function is in cleanup_idle. | ||
616 | */ | ||
617 | ENTRY(psw_idle) | ||
618 | st %r4,__SF_EMPTY(%r15) | ||
619 | basr %r1,0 | ||
620 | la %r1,psw_idle_lpsw+4-.(%r1) | ||
621 | st %r1,__SF_EMPTY+4(%r15) | ||
622 | oi __SF_EMPTY+4(%r15),0x80 | ||
623 | la %r1,.Lvtimer_max-psw_idle_lpsw-4(%r1) | ||
624 | stck __IDLE_ENTER(%r2) | ||
625 | ltr %r5,%r5 | ||
626 | stpt __VQ_IDLE_ENTER(%r3) | ||
627 | jz psw_idle_lpsw | ||
628 | spt 0(%r1) | ||
629 | psw_idle_lpsw: | ||
630 | lpsw __SF_EMPTY(%r15) | ||
631 | br %r14 | ||
632 | psw_idle_end: | ||
633 | |||
615 | __critical_end: | 634 | __critical_end: |
616 | 635 | ||
617 | /* | 636 | /* |
@@ -673,7 +692,6 @@ mcck_skip: | |||
673 | TRACE_IRQS_ON | 692 | TRACE_IRQS_ON |
674 | mcck_return: | 693 | mcck_return: |
675 | mvc __LC_RETURN_MCCK_PSW(8),__PT_PSW(%r11) # move return PSW | 694 | mvc __LC_RETURN_MCCK_PSW(8),__PT_PSW(%r11) # move return PSW |
676 | ni __LC_RETURN_MCCK_PSW+1,0xfd # clear wait state bit | ||
677 | tm __LC_RETURN_MCCK_PSW+1,0x01 # returning to user ? | 695 | tm __LC_RETURN_MCCK_PSW+1,0x01 # returning to user ? |
678 | jno 0f | 696 | jno 0f |
679 | lm %r0,%r15,__PT_R0(%r11) | 697 | lm %r0,%r15,__PT_R0(%r11) |
@@ -748,6 +766,8 @@ cleanup_table: | |||
748 | .long io_tif + 0x80000000 | 766 | .long io_tif + 0x80000000 |
749 | .long io_restore + 0x80000000 | 767 | .long io_restore + 0x80000000 |
750 | .long io_done + 0x80000000 | 768 | .long io_done + 0x80000000 |
769 | .long psw_idle + 0x80000000 | ||
770 | .long psw_idle_end + 0x80000000 | ||
751 | 771 | ||
752 | cleanup_critical: | 772 | cleanup_critical: |
753 | cl %r9,BASED(cleanup_table) # system_call | 773 | cl %r9,BASED(cleanup_table) # system_call |
@@ -766,6 +786,10 @@ cleanup_critical: | |||
766 | jl cleanup_io_tif | 786 | jl cleanup_io_tif |
767 | cl %r9,BASED(cleanup_table+28) # io_done | 787 | cl %r9,BASED(cleanup_table+28) # io_done |
768 | jl cleanup_io_restore | 788 | jl cleanup_io_restore |
789 | cl %r9,BASED(cleanup_table+32) # psw_idle | ||
790 | jl 0f | ||
791 | cl %r9,BASED(cleanup_table+36) # psw_idle_end | ||
792 | jl cleanup_idle | ||
769 | 0: br %r14 | 793 | 0: br %r14 |
770 | 794 | ||
771 | cleanup_system_call: | 795 | cleanup_system_call: |
@@ -849,7 +873,6 @@ cleanup_io_restore: | |||
849 | jhe 0f | 873 | jhe 0f |
850 | l %r9,12(%r11) # get saved r11 pointer to pt_regs | 874 | l %r9,12(%r11) # get saved r11 pointer to pt_regs |
851 | mvc __LC_RETURN_PSW(8),__PT_PSW(%r9) | 875 | mvc __LC_RETURN_PSW(8),__PT_PSW(%r9) |
852 | ni __LC_RETURN_PSW+1,0xfd # clear wait state bit | ||
853 | mvc 0(32,%r11),__PT_R8(%r9) | 876 | mvc 0(32,%r11),__PT_R8(%r9) |
854 | lm %r0,%r7,__PT_R0(%r9) | 877 | lm %r0,%r7,__PT_R0(%r9) |
855 | 0: lm %r8,%r9,__LC_RETURN_PSW | 878 | 0: lm %r8,%r9,__LC_RETURN_PSW |
@@ -857,11 +880,52 @@ cleanup_io_restore: | |||
857 | cleanup_io_restore_insn: | 880 | cleanup_io_restore_insn: |
858 | .long io_done - 4 + 0x80000000 | 881 | .long io_done - 4 + 0x80000000 |
859 | 882 | ||
883 | cleanup_idle: | ||
884 | # copy interrupt clock & cpu timer | ||
885 | mvc __IDLE_EXIT(8,%r2),__LC_INT_CLOCK | ||
886 | mvc __VQ_IDLE_EXIT(8,%r3),__LC_ASYNC_ENTER_TIMER | ||
887 | chi %r11,__LC_SAVE_AREA_ASYNC | ||
888 | je 0f | ||
889 | mvc __IDLE_EXIT(8,%r2),__LC_MCCK_CLOCK | ||
890 | mvc __VQ_IDLE_EXIT(8,%r3),__LC_MCCK_ENTER_TIMER | ||
891 | 0: # check if stck has been executed | ||
892 | cl %r9,BASED(cleanup_idle_insn) | ||
893 | jhe 1f | ||
894 | mvc __IDLE_ENTER(8,%r2),__IDLE_EXIT(%r2) | ||
895 | mvc __VQ_IDLE_ENTER(8,%r3),__VQ_IDLE_EXIT(%r3) | ||
896 | j 2f | ||
897 | 1: # check if the cpu timer has been reprogrammed | ||
898 | ltr %r5,%r5 | ||
899 | jz 2f | ||
900 | spt __VQ_IDLE_ENTER(%r3) | ||
901 | 2: # account system time going idle | ||
902 | lm %r9,%r10,__LC_STEAL_TIMER | ||
903 | ADD64 %r9,%r10,__IDLE_ENTER(%r2) | ||
904 | SUB64 %r9,%r10,__LC_LAST_UPDATE_CLOCK | ||
905 | stm %r9,%r10,__LC_STEAL_TIMER | ||
906 | mvc __LC_LAST_UPDATE_CLOCK(8),__IDLE_EXIT(%r2) | ||
907 | lm %r9,%r10,__LC_SYSTEM_TIMER | ||
908 | ADD64 %r9,%r10,__LC_LAST_UPDATE_TIMER | ||
909 | SUB64 %r9,%r10,__VQ_IDLE_ENTER(%r3) | ||
910 | stm %r9,%r10,__LC_SYSTEM_TIMER | ||
911 | mvc __LC_LAST_UPDATE_TIMER(8),__VQ_IDLE_EXIT(%r3) | ||
912 | # prepare return psw | ||
913 | n %r8,BASED(cleanup_idle_wait) # clear wait state bit | ||
914 | l %r9,24(%r11) # return from psw_idle | ||
915 | br %r14 | ||
916 | cleanup_idle_insn: | ||
917 | .long psw_idle_lpsw + 0x80000000 | ||
918 | cleanup_idle_wait: | ||
919 | .long 0xfffdffff | ||
920 | |||
860 | /* | 921 | /* |
861 | * Integer constants | 922 | * Integer constants |
862 | */ | 923 | */ |
863 | .align 4 | 924 | .align 4 |
864 | .Lnr_syscalls: .long NR_syscalls | 925 | .Lnr_syscalls: |
926 | .long NR_syscalls | ||
927 | .Lvtimer_max: | ||
928 | .quad 0x7fffffffffffffff | ||
865 | 929 | ||
866 | /* | 930 | /* |
867 | * Symbol constants | 931 | * Symbol constants |
diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 92b1617d0c95..4984785e3078 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h | |||
@@ -4,7 +4,8 @@ | |||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <linux/signal.h> | 5 | #include <linux/signal.h> |
6 | #include <asm/ptrace.h> | 6 | #include <asm/ptrace.h> |
7 | 7 | #include <asm/cputime.h> | |
8 | #include <asm/timer.h> | ||
8 | 9 | ||
9 | extern void (*pgm_check_table[128])(struct pt_regs *); | 10 | extern void (*pgm_check_table[128])(struct pt_regs *); |
10 | extern void *restart_stack; | 11 | extern void *restart_stack; |
@@ -16,6 +17,8 @@ void io_int_handler(void); | |||
16 | void mcck_int_handler(void); | 17 | void mcck_int_handler(void); |
17 | void restart_int_handler(void); | 18 | void restart_int_handler(void); |
18 | void restart_call_handler(void); | 19 | void restart_call_handler(void); |
20 | void psw_idle(struct s390_idle_data *, struct vtimer_queue *, | ||
21 | unsigned long, int); | ||
19 | 22 | ||
20 | asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); | 23 | asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); |
21 | asmlinkage void do_syscall_trace_exit(struct pt_regs *regs); | 24 | asmlinkage void do_syscall_trace_exit(struct pt_regs *regs); |
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index e33789a45752..4e1c292fa7e3 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S | |||
@@ -489,7 +489,6 @@ io_restore: | |||
489 | lg %r14,__LC_VDSO_PER_CPU | 489 | lg %r14,__LC_VDSO_PER_CPU |
490 | lmg %r0,%r10,__PT_R0(%r11) | 490 | lmg %r0,%r10,__PT_R0(%r11) |
491 | mvc __LC_RETURN_PSW(16),__PT_PSW(%r11) | 491 | mvc __LC_RETURN_PSW(16),__PT_PSW(%r11) |
492 | ni __LC_RETURN_PSW+1,0xfd # clear wait state bit | ||
493 | stpt __LC_EXIT_TIMER | 492 | stpt __LC_EXIT_TIMER |
494 | mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER | 493 | mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER |
495 | lmg %r11,%r15,__PT_R11(%r11) | 494 | lmg %r11,%r15,__PT_R11(%r11) |
@@ -631,6 +630,24 @@ ext_skip: | |||
631 | brasl %r14,do_extint | 630 | brasl %r14,do_extint |
632 | j io_return | 631 | j io_return |
633 | 632 | ||
633 | /* | ||
634 | * Load idle PSW. The second "half" of this function is in cleanup_idle. | ||
635 | */ | ||
636 | ENTRY(psw_idle) | ||
637 | stg %r4,__SF_EMPTY(%r15) | ||
638 | larl %r1,psw_idle_lpsw+4 | ||
639 | stg %r1,__SF_EMPTY+8(%r15) | ||
640 | larl %r1,.Lvtimer_max | ||
641 | stck __IDLE_ENTER(%r2) | ||
642 | ltr %r5,%r5 | ||
643 | stpt __VQ_IDLE_ENTER(%r3) | ||
644 | jz psw_idle_lpsw | ||
645 | spt 0(%r1) | ||
646 | psw_idle_lpsw: | ||
647 | lpswe __SF_EMPTY(%r15) | ||
648 | br %r14 | ||
649 | psw_idle_end: | ||
650 | |||
634 | __critical_end: | 651 | __critical_end: |
635 | 652 | ||
636 | /* | 653 | /* |
@@ -696,7 +713,6 @@ mcck_return: | |||
696 | lg %r14,__LC_VDSO_PER_CPU | 713 | lg %r14,__LC_VDSO_PER_CPU |
697 | lmg %r0,%r10,__PT_R0(%r11) | 714 | lmg %r0,%r10,__PT_R0(%r11) |
698 | mvc __LC_RETURN_MCCK_PSW(16),__PT_PSW(%r11) # move return PSW | 715 | mvc __LC_RETURN_MCCK_PSW(16),__PT_PSW(%r11) # move return PSW |
699 | ni __LC_RETURN_MCCK_PSW+1,0xfd # clear wait state bit | ||
700 | tm __LC_RETURN_MCCK_PSW+1,0x01 # returning to user ? | 716 | tm __LC_RETURN_MCCK_PSW+1,0x01 # returning to user ? |
701 | jno 0f | 717 | jno 0f |
702 | stpt __LC_EXIT_TIMER | 718 | stpt __LC_EXIT_TIMER |
@@ -770,6 +786,8 @@ cleanup_table: | |||
770 | .quad io_tif | 786 | .quad io_tif |
771 | .quad io_restore | 787 | .quad io_restore |
772 | .quad io_done | 788 | .quad io_done |
789 | .quad psw_idle | ||
790 | .quad psw_idle_end | ||
773 | 791 | ||
774 | cleanup_critical: | 792 | cleanup_critical: |
775 | clg %r9,BASED(cleanup_table) # system_call | 793 | clg %r9,BASED(cleanup_table) # system_call |
@@ -788,6 +806,10 @@ cleanup_critical: | |||
788 | jl cleanup_io_tif | 806 | jl cleanup_io_tif |
789 | clg %r9,BASED(cleanup_table+56) # io_done | 807 | clg %r9,BASED(cleanup_table+56) # io_done |
790 | jl cleanup_io_restore | 808 | jl cleanup_io_restore |
809 | clg %r9,BASED(cleanup_table+64) # psw_idle | ||
810 | jl 0f | ||
811 | clg %r9,BASED(cleanup_table+72) # psw_idle_end | ||
812 | jl cleanup_idle | ||
791 | 0: br %r14 | 813 | 0: br %r14 |
792 | 814 | ||
793 | 815 | ||
@@ -877,7 +899,6 @@ cleanup_io_restore: | |||
877 | je 0f | 899 | je 0f |
878 | lg %r9,24(%r11) # get saved r11 pointer to pt_regs | 900 | lg %r9,24(%r11) # get saved r11 pointer to pt_regs |
879 | mvc __LC_RETURN_PSW(16),__PT_PSW(%r9) | 901 | mvc __LC_RETURN_PSW(16),__PT_PSW(%r9) |
880 | ni __LC_RETURN_PSW+1,0xfd # clear wait state bit | ||
881 | mvc 0(64,%r11),__PT_R8(%r9) | 902 | mvc 0(64,%r11),__PT_R8(%r9) |
882 | lmg %r0,%r7,__PT_R0(%r9) | 903 | lmg %r0,%r7,__PT_R0(%r9) |
883 | 0: lmg %r8,%r9,__LC_RETURN_PSW | 904 | 0: lmg %r8,%r9,__LC_RETURN_PSW |
@@ -885,6 +906,42 @@ cleanup_io_restore: | |||
885 | cleanup_io_restore_insn: | 906 | cleanup_io_restore_insn: |
886 | .quad io_done - 4 | 907 | .quad io_done - 4 |
887 | 908 | ||
909 | cleanup_idle: | ||
910 | # copy interrupt clock & cpu timer | ||
911 | mvc __IDLE_EXIT(8,%r2),__LC_INT_CLOCK | ||
912 | mvc __VQ_IDLE_EXIT(8,%r3),__LC_ASYNC_ENTER_TIMER | ||
913 | cghi %r11,__LC_SAVE_AREA_ASYNC | ||
914 | je 0f | ||
915 | mvc __IDLE_EXIT(8,%r2),__LC_MCCK_CLOCK | ||
916 | mvc __VQ_IDLE_EXIT(8,%r3),__LC_MCCK_ENTER_TIMER | ||
917 | 0: # check if stck & stpt have been executed | ||
918 | clg %r9,BASED(cleanup_idle_insn) | ||
919 | jhe 1f | ||
920 | mvc __IDLE_ENTER(8,%r2),__IDLE_EXIT(%r2) | ||
921 | mvc __VQ_IDLE_ENTER(8,%r3),__VQ_IDLE_EXIT(%r3) | ||
922 | j 2f | ||
923 | 1: # check if the cpu timer has been reprogrammed | ||
924 | ltr %r5,%r5 | ||
925 | jz 2f | ||
926 | spt __VQ_IDLE_ENTER(%r3) | ||
927 | 2: # account system time going idle | ||
928 | lg %r9,__LC_STEAL_TIMER | ||
929 | alg %r9,__IDLE_ENTER(%r2) | ||
930 | slg %r9,__LC_LAST_UPDATE_CLOCK | ||
931 | stg %r9,__LC_STEAL_TIMER | ||
932 | mvc __LC_LAST_UPDATE_CLOCK(8),__IDLE_EXIT(%r2) | ||
933 | lg %r9,__LC_SYSTEM_TIMER | ||
934 | alg %r9,__LC_LAST_UPDATE_TIMER | ||
935 | slg %r9,__VQ_IDLE_ENTER(%r3) | ||
936 | stg %r9,__LC_SYSTEM_TIMER | ||
937 | mvc __LC_LAST_UPDATE_TIMER(8),__VQ_IDLE_EXIT(%r3) | ||
938 | # prepare return psw | ||
939 | nihh %r8,0xfffd # clear wait state bit | ||
940 | lg %r9,48(%r11) # return from psw_idle | ||
941 | br %r14 | ||
942 | cleanup_idle_insn: | ||
943 | .quad psw_idle_lpsw | ||
944 | |||
888 | /* | 945 | /* |
889 | * Integer constants | 946 | * Integer constants |
890 | */ | 947 | */ |
@@ -893,6 +950,8 @@ cleanup_io_restore_insn: | |||
893 | .quad __critical_start | 950 | .quad __critical_start |
894 | .Lcritical_length: | 951 | .Lcritical_length: |
895 | .quad __critical_end - __critical_start | 952 | .quad __critical_end - __critical_start |
953 | .Lvtimer_max: | ||
954 | .quad 0x7fffffffffffffff | ||
896 | 955 | ||
897 | 956 | ||
898 | #if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) | 957 | #if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) |
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index b9a7fdd9c814..09a014c62537 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c | |||
@@ -219,8 +219,6 @@ void __irq_entry do_extint(struct pt_regs *regs, unsigned int ext_int_code, | |||
219 | 219 | ||
220 | code = (unsigned short) ext_int_code; | 220 | code = (unsigned short) ext_int_code; |
221 | old_regs = set_irq_regs(regs); | 221 | old_regs = set_irq_regs(regs); |
222 | s390_idle_check(regs, S390_lowcore.int_clock, | ||
223 | S390_lowcore.async_enter_timer); | ||
224 | irq_enter(); | 222 | irq_enter(); |
225 | if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) | 223 | if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) |
226 | /* Serve timer interrupts first. */ | 224 | /* Serve timer interrupts first. */ |
diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 0fd2e863e114..8c372ca61350 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c | |||
@@ -254,8 +254,6 @@ void notrace s390_do_machine_check(struct pt_regs *regs) | |||
254 | int umode; | 254 | int umode; |
255 | 255 | ||
256 | nmi_enter(); | 256 | nmi_enter(); |
257 | s390_idle_check(regs, S390_lowcore.mcck_clock, | ||
258 | S390_lowcore.mcck_enter_timer); | ||
259 | kstat_cpu(smp_processor_id()).irqs[NMI_NMI]++; | 257 | kstat_cpu(smp_processor_id()).irqs[NMI_NMI]++; |
260 | mci = (struct mci *) &S390_lowcore.mcck_interruption_code; | 258 | mci = (struct mci *) &S390_lowcore.mcck_interruption_code; |
261 | mcck = &__get_cpu_var(cpu_mcck); | 259 | mcck = &__get_cpu_var(cpu_mcck); |
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index e795933eb2cb..78b3c149b8b6 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c | |||
@@ -77,13 +77,8 @@ static void default_idle(void) | |||
77 | local_irq_enable(); | 77 | local_irq_enable(); |
78 | return; | 78 | return; |
79 | } | 79 | } |
80 | trace_hardirqs_on(); | 80 | /* Halt the cpu and keep track of cpu time accounting. */ |
81 | /* Don't trace preempt off for idle. */ | ||
82 | stop_critical_timings(); | ||
83 | /* Stop virtual timer and halt the cpu. */ | ||
84 | vtime_stop_cpu(); | 81 | vtime_stop_cpu(); |
85 | /* Reenable preemption tracer. */ | ||
86 | start_critical_timings(); | ||
87 | } | 82 | } |
88 | 83 | ||
89 | void cpu_idle(void) | 84 | void cpu_idle(void) |
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 6db8526a602d..afd6e5113a90 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c | |||
@@ -972,22 +972,16 @@ static DEVICE_ATTR(capability, 0444, show_capability, NULL); | |||
972 | static ssize_t show_idle_count(struct device *dev, | 972 | static ssize_t show_idle_count(struct device *dev, |
973 | struct device_attribute *attr, char *buf) | 973 | struct device_attribute *attr, char *buf) |
974 | { | 974 | { |
975 | struct s390_idle_data *idle; | 975 | struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); |
976 | unsigned long long idle_count; | 976 | unsigned long long idle_count; |
977 | unsigned int sequence; | 977 | unsigned int sequence; |
978 | 978 | ||
979 | idle = &per_cpu(s390_idle, dev->id); | 979 | do { |
980 | repeat: | 980 | sequence = ACCESS_ONCE(idle->sequence); |
981 | sequence = idle->sequence; | 981 | idle_count = ACCESS_ONCE(idle->idle_count); |
982 | smp_rmb(); | 982 | if (ACCESS_ONCE(idle->idle_enter)) |
983 | if (sequence & 1) | 983 | idle_count++; |
984 | goto repeat; | 984 | } while ((sequence & 1) || (idle->sequence != sequence)); |
985 | idle_count = idle->idle_count; | ||
986 | if (idle->idle_enter) | ||
987 | idle_count++; | ||
988 | smp_rmb(); | ||
989 | if (idle->sequence != sequence) | ||
990 | goto repeat; | ||
991 | return sprintf(buf, "%llu\n", idle_count); | 985 | return sprintf(buf, "%llu\n", idle_count); |
992 | } | 986 | } |
993 | static DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); | 987 | static DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); |
@@ -995,24 +989,18 @@ static DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); | |||
995 | static ssize_t show_idle_time(struct device *dev, | 989 | static ssize_t show_idle_time(struct device *dev, |
996 | struct device_attribute *attr, char *buf) | 990 | struct device_attribute *attr, char *buf) |
997 | { | 991 | { |
998 | struct s390_idle_data *idle; | 992 | struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); |
999 | unsigned long long now, idle_time, idle_enter; | 993 | unsigned long long now, idle_time, idle_enter, idle_exit; |
1000 | unsigned int sequence; | 994 | unsigned int sequence; |
1001 | 995 | ||
1002 | idle = &per_cpu(s390_idle, dev->id); | 996 | do { |
1003 | now = get_clock(); | 997 | now = get_clock(); |
1004 | repeat: | 998 | sequence = ACCESS_ONCE(idle->sequence); |
1005 | sequence = idle->sequence; | 999 | idle_time = ACCESS_ONCE(idle->idle_time); |
1006 | smp_rmb(); | 1000 | idle_enter = ACCESS_ONCE(idle->idle_enter); |
1007 | if (sequence & 1) | 1001 | idle_exit = ACCESS_ONCE(idle->idle_exit); |
1008 | goto repeat; | 1002 | } while ((sequence & 1) || (idle->sequence != sequence)); |
1009 | idle_time = idle->idle_time; | 1003 | idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0; |
1010 | idle_enter = idle->idle_enter; | ||
1011 | if (idle_enter != 0ULL && idle_enter < now) | ||
1012 | idle_time += now - idle_enter; | ||
1013 | smp_rmb(); | ||
1014 | if (idle->sequence != sequence) | ||
1015 | goto repeat; | ||
1016 | return sprintf(buf, "%llu\n", idle_time >> 12); | 1004 | return sprintf(buf, "%llu\n", idle_time >> 12); |
1017 | } | 1005 | } |
1018 | static DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); | 1006 | static DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); |
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 7bacee9a546f..277ea712b232 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <asm/irq_regs.h> | 26 | #include <asm/irq_regs.h> |
27 | #include <asm/cputime.h> | 27 | #include <asm/cputime.h> |
28 | #include <asm/irq.h> | 28 | #include <asm/irq.h> |
29 | #include "entry.h" | ||
29 | 30 | ||
30 | static DEFINE_PER_CPU(struct vtimer_queue, virt_cpu_timer); | 31 | static DEFINE_PER_CPU(struct vtimer_queue, virt_cpu_timer); |
31 | 32 | ||
@@ -123,153 +124,53 @@ void account_system_vtime(struct task_struct *tsk) | |||
123 | } | 124 | } |
124 | EXPORT_SYMBOL_GPL(account_system_vtime); | 125 | EXPORT_SYMBOL_GPL(account_system_vtime); |
125 | 126 | ||
126 | void __kprobes vtime_start_cpu(__u64 int_clock, __u64 enter_timer) | 127 | void __kprobes vtime_stop_cpu(void) |
127 | { | 128 | { |
128 | struct s390_idle_data *idle = &__get_cpu_var(s390_idle); | 129 | struct s390_idle_data *idle = &__get_cpu_var(s390_idle); |
129 | struct vtimer_queue *vq = &__get_cpu_var(virt_cpu_timer); | 130 | struct vtimer_queue *vq = &__get_cpu_var(virt_cpu_timer); |
130 | __u64 idle_time, expires; | 131 | unsigned long long idle_time; |
132 | unsigned long psw_mask; | ||
131 | 133 | ||
132 | if (idle->idle_enter == 0ULL) | 134 | trace_hardirqs_on(); |
133 | return; | 135 | /* Don't trace preempt off for idle. */ |
136 | stop_critical_timings(); | ||
134 | 137 | ||
135 | /* Account time spent with enabled wait psw loaded as idle time. */ | 138 | /* Wait for external, I/O or machine check interrupt. */ |
136 | idle_time = int_clock - idle->idle_enter; | 139 | psw_mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_DAT | |
137 | account_idle_time(idle_time); | 140 | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; |
138 | S390_lowcore.steal_timer += | 141 | idle->nohz_delay = 0; |
139 | idle->idle_enter - S390_lowcore.last_update_clock; | ||
140 | S390_lowcore.last_update_clock = int_clock; | ||
141 | |||
142 | /* Account system time spent going idle. */ | ||
143 | S390_lowcore.system_timer += S390_lowcore.last_update_timer - vq->idle; | ||
144 | S390_lowcore.last_update_timer = enter_timer; | ||
145 | |||
146 | /* Restart vtime CPU timer */ | ||
147 | if (vq->do_spt) { | ||
148 | /* Program old expire value but first save progress. */ | ||
149 | expires = vq->idle - enter_timer; | ||
150 | expires += get_vtimer(); | ||
151 | set_vtimer(expires); | ||
152 | } else { | ||
153 | /* Don't account the CPU timer delta while the cpu was idle. */ | ||
154 | vq->elapsed -= vq->idle - enter_timer; | ||
155 | } | ||
156 | 142 | ||
143 | /* Call the assembler magic in entry.S */ | ||
144 | psw_idle(idle, vq, psw_mask, !list_empty(&vq->list)); | ||
145 | |||
146 | /* Reenable preemption tracer. */ | ||
147 | start_critical_timings(); | ||
148 | |||
149 | /* Account time spent with enabled wait psw loaded as idle time. */ | ||
157 | idle->sequence++; | 150 | idle->sequence++; |
158 | smp_wmb(); | 151 | smp_wmb(); |
152 | idle_time = idle->idle_exit - idle->idle_enter; | ||
159 | idle->idle_time += idle_time; | 153 | idle->idle_time += idle_time; |
160 | idle->idle_enter = 0ULL; | 154 | idle->idle_enter = idle->idle_exit = 0ULL; |
161 | idle->idle_count++; | 155 | idle->idle_count++; |
156 | account_idle_time(idle_time); | ||
162 | smp_wmb(); | 157 | smp_wmb(); |
163 | idle->sequence++; | 158 | idle->sequence++; |
164 | } | 159 | } |
165 | 160 | ||
166 | void __kprobes vtime_stop_cpu(void) | ||
167 | { | ||
168 | struct s390_idle_data *idle = &__get_cpu_var(s390_idle); | ||
169 | struct vtimer_queue *vq = &__get_cpu_var(virt_cpu_timer); | ||
170 | psw_t psw; | ||
171 | |||
172 | /* Wait for external, I/O or machine check interrupt. */ | ||
173 | psw.mask = psw_kernel_bits | PSW_MASK_WAIT | | ||
174 | PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; | ||
175 | |||
176 | idle->nohz_delay = 0; | ||
177 | |||
178 | /* Check if the CPU timer needs to be reprogrammed. */ | ||
179 | if (vq->do_spt) { | ||
180 | __u64 vmax = VTIMER_MAX_SLICE; | ||
181 | /* | ||
182 | * The inline assembly is equivalent to | ||
183 | * vq->idle = get_cpu_timer(); | ||
184 | * set_cpu_timer(VTIMER_MAX_SLICE); | ||
185 | * idle->idle_enter = get_clock(); | ||
186 | * __load_psw_mask(psw_kernel_bits | PSW_MASK_WAIT | | ||
187 | * PSW_MASK_DAT | PSW_MASK_IO | | ||
188 | * PSW_MASK_EXT | PSW_MASK_MCHECK); | ||
189 | * The difference is that the inline assembly makes sure that | ||
190 | * the last three instruction are stpt, stck and lpsw in that | ||
191 | * order. This is done to increase the precision. | ||
192 | */ | ||
193 | asm volatile( | ||
194 | #ifndef CONFIG_64BIT | ||
195 | " basr 1,0\n" | ||
196 | "0: ahi 1,1f-0b\n" | ||
197 | " st 1,4(%2)\n" | ||
198 | #else /* CONFIG_64BIT */ | ||
199 | " larl 1,1f\n" | ||
200 | " stg 1,8(%2)\n" | ||
201 | #endif /* CONFIG_64BIT */ | ||
202 | " stpt 0(%4)\n" | ||
203 | " spt 0(%5)\n" | ||
204 | " stck 0(%3)\n" | ||
205 | #ifndef CONFIG_64BIT | ||
206 | " lpsw 0(%2)\n" | ||
207 | #else /* CONFIG_64BIT */ | ||
208 | " lpswe 0(%2)\n" | ||
209 | #endif /* CONFIG_64BIT */ | ||
210 | "1:" | ||
211 | : "=m" (idle->idle_enter), "=m" (vq->idle) | ||
212 | : "a" (&psw), "a" (&idle->idle_enter), | ||
213 | "a" (&vq->idle), "a" (&vmax), "m" (vmax), "m" (psw) | ||
214 | : "memory", "cc", "1"); | ||
215 | } else { | ||
216 | /* | ||
217 | * The inline assembly is equivalent to | ||
218 | * vq->idle = get_cpu_timer(); | ||
219 | * idle->idle_enter = get_clock(); | ||
220 | * __load_psw_mask(psw_kernel_bits | PSW_MASK_WAIT | | ||
221 | * PSW_MASK_DAT | PSW_MASK_IO | | ||
222 | * PSW_MASK_EXT | PSW_MASK_MCHECK); | ||
223 | * The difference is that the inline assembly makes sure that | ||
224 | * the last three instruction are stpt, stck and lpsw in that | ||
225 | * order. This is done to increase the precision. | ||
226 | */ | ||
227 | asm volatile( | ||
228 | #ifndef CONFIG_64BIT | ||
229 | " basr 1,0\n" | ||
230 | "0: ahi 1,1f-0b\n" | ||
231 | " st 1,4(%2)\n" | ||
232 | #else /* CONFIG_64BIT */ | ||
233 | " larl 1,1f\n" | ||
234 | " stg 1,8(%2)\n" | ||
235 | #endif /* CONFIG_64BIT */ | ||
236 | " stpt 0(%4)\n" | ||
237 | " stck 0(%3)\n" | ||
238 | #ifndef CONFIG_64BIT | ||
239 | " lpsw 0(%2)\n" | ||
240 | #else /* CONFIG_64BIT */ | ||
241 | " lpswe 0(%2)\n" | ||
242 | #endif /* CONFIG_64BIT */ | ||
243 | "1:" | ||
244 | : "=m" (idle->idle_enter), "=m" (vq->idle) | ||
245 | : "a" (&psw), "a" (&idle->idle_enter), | ||
246 | "a" (&vq->idle), "m" (psw) | ||
247 | : "memory", "cc", "1"); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | cputime64_t s390_get_idle_time(int cpu) | 161 | cputime64_t s390_get_idle_time(int cpu) |
252 | { | 162 | { |
253 | struct s390_idle_data *idle; | 163 | struct s390_idle_data *idle = &per_cpu(s390_idle, cpu); |
254 | unsigned long long now, idle_time, idle_enter; | 164 | unsigned long long now, idle_enter, idle_exit; |
255 | unsigned int sequence; | 165 | unsigned int sequence; |
256 | 166 | ||
257 | idle = &per_cpu(s390_idle, cpu); | 167 | do { |
258 | 168 | now = get_clock(); | |
259 | now = get_clock(); | 169 | sequence = ACCESS_ONCE(idle->sequence); |
260 | repeat: | 170 | idle_enter = ACCESS_ONCE(idle->idle_enter); |
261 | sequence = idle->sequence; | 171 | idle_exit = ACCESS_ONCE(idle->idle_exit); |
262 | smp_rmb(); | 172 | } while ((sequence & 1) || (idle->sequence != sequence)); |
263 | if (sequence & 1) | 173 | return idle_enter ? ((idle_exit ? : now) - idle_enter) : 0; |
264 | goto repeat; | ||
265 | idle_time = 0; | ||
266 | idle_enter = idle->idle_enter; | ||
267 | if (idle_enter != 0ULL && idle_enter < now) | ||
268 | idle_time = now - idle_enter; | ||
269 | smp_rmb(); | ||
270 | if (idle->sequence != sequence) | ||
271 | goto repeat; | ||
272 | return idle_time; | ||
273 | } | 174 | } |
274 | 175 | ||
275 | /* | 176 | /* |
@@ -346,7 +247,6 @@ static void do_cpu_timer_interrupt(unsigned int ext_int_code, | |||
346 | } | 247 | } |
347 | spin_unlock(&vq->lock); | 248 | spin_unlock(&vq->lock); |
348 | 249 | ||
349 | vq->do_spt = list_empty(&cb_list); | ||
350 | do_callbacks(&cb_list); | 250 | do_callbacks(&cb_list); |
351 | 251 | ||
352 | /* next event is first in list */ | 252 | /* next event is first in list */ |
@@ -355,8 +255,7 @@ static void do_cpu_timer_interrupt(unsigned int ext_int_code, | |||
355 | if (!list_empty(&vq->list)) { | 255 | if (!list_empty(&vq->list)) { |
356 | event = list_first_entry(&vq->list, struct vtimer_list, entry); | 256 | event = list_first_entry(&vq->list, struct vtimer_list, entry); |
357 | next = event->expires; | 257 | next = event->expires; |
358 | } else | 258 | } |
359 | vq->do_spt = 0; | ||
360 | spin_unlock(&vq->lock); | 259 | spin_unlock(&vq->lock); |
361 | /* | 260 | /* |
362 | * To improve precision add the time spent by the | 261 | * To improve precision add the time spent by the |