diff options
Diffstat (limited to 'arch/s390')
-rw-r--r-- | arch/s390/Kconfig | 6 | ||||
-rw-r--r-- | arch/s390/appldata/appldata_os.c | 16 | ||||
-rw-r--r-- | arch/s390/include/asm/cputime.h | 142 | ||||
-rw-r--r-- | arch/s390/kernel/process.c | 6 | ||||
-rw-r--r-- | arch/s390/kernel/setup.c | 4 | ||||
-rw-r--r-- | arch/s390/oprofile/hwsampler.c | 7 | ||||
-rw-r--r-- | arch/s390/oprofile/init.c | 373 | ||||
-rw-r--r-- | arch/s390/oprofile/op_counter.h | 23 |
8 files changed, 455 insertions, 122 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 373679b3744a..d48ede334434 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig | |||
@@ -92,6 +92,9 @@ config S390 | |||
92 | select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 | 92 | select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 |
93 | select HAVE_RCU_TABLE_FREE if SMP | 93 | select HAVE_RCU_TABLE_FREE if SMP |
94 | select ARCH_SAVE_PAGE_KEYS if HIBERNATION | 94 | select ARCH_SAVE_PAGE_KEYS if HIBERNATION |
95 | select HAVE_MEMBLOCK | ||
96 | select HAVE_MEMBLOCK_NODE_MAP | ||
97 | select ARCH_DISCARD_MEMBLOCK | ||
95 | select ARCH_INLINE_SPIN_TRYLOCK | 98 | select ARCH_INLINE_SPIN_TRYLOCK |
96 | select ARCH_INLINE_SPIN_TRYLOCK_BH | 99 | select ARCH_INLINE_SPIN_TRYLOCK_BH |
97 | select ARCH_INLINE_SPIN_LOCK | 100 | select ARCH_INLINE_SPIN_LOCK |
@@ -345,9 +348,6 @@ config WARN_DYNAMIC_STACK | |||
345 | 348 | ||
346 | Say N if you are unsure. | 349 | Say N if you are unsure. |
347 | 350 | ||
348 | config ARCH_POPULATES_NODE_MAP | ||
349 | def_bool y | ||
350 | |||
351 | comment "Kernel preemption" | 351 | comment "Kernel preemption" |
352 | 352 | ||
353 | source "kernel/Kconfig.preempt" | 353 | source "kernel/Kconfig.preempt" |
diff --git a/arch/s390/appldata/appldata_os.c b/arch/s390/appldata/appldata_os.c index 92f1cb745d69..4de031d6b76c 100644 --- a/arch/s390/appldata/appldata_os.c +++ b/arch/s390/appldata/appldata_os.c | |||
@@ -115,21 +115,21 @@ static void appldata_get_os_data(void *data) | |||
115 | j = 0; | 115 | j = 0; |
116 | for_each_online_cpu(i) { | 116 | for_each_online_cpu(i) { |
117 | os_data->os_cpu[j].per_cpu_user = | 117 | os_data->os_cpu[j].per_cpu_user = |
118 | cputime_to_jiffies(kstat_cpu(i).cpustat.user); | 118 | cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_USER]); |
119 | os_data->os_cpu[j].per_cpu_nice = | 119 | os_data->os_cpu[j].per_cpu_nice = |
120 | cputime_to_jiffies(kstat_cpu(i).cpustat.nice); | 120 | cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_NICE]); |
121 | os_data->os_cpu[j].per_cpu_system = | 121 | os_data->os_cpu[j].per_cpu_system = |
122 | cputime_to_jiffies(kstat_cpu(i).cpustat.system); | 122 | cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]); |
123 | os_data->os_cpu[j].per_cpu_idle = | 123 | os_data->os_cpu[j].per_cpu_idle = |
124 | cputime_to_jiffies(kstat_cpu(i).cpustat.idle); | 124 | cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_IDLE]); |
125 | os_data->os_cpu[j].per_cpu_irq = | 125 | os_data->os_cpu[j].per_cpu_irq = |
126 | cputime_to_jiffies(kstat_cpu(i).cpustat.irq); | 126 | cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_IRQ]); |
127 | os_data->os_cpu[j].per_cpu_softirq = | 127 | os_data->os_cpu[j].per_cpu_softirq = |
128 | cputime_to_jiffies(kstat_cpu(i).cpustat.softirq); | 128 | cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]); |
129 | os_data->os_cpu[j].per_cpu_iowait = | 129 | os_data->os_cpu[j].per_cpu_iowait = |
130 | cputime_to_jiffies(kstat_cpu(i).cpustat.iowait); | 130 | cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT]); |
131 | os_data->os_cpu[j].per_cpu_steal = | 131 | os_data->os_cpu[j].per_cpu_steal = |
132 | cputime_to_jiffies(kstat_cpu(i).cpustat.steal); | 132 | cputime_to_jiffies(kcpustat_cpu(i).cpustat[CPUTIME_STEAL]); |
133 | os_data->os_cpu[j].cpu_id = i; | 133 | os_data->os_cpu[j].cpu_id = i; |
134 | j++; | 134 | j++; |
135 | } | 135 | } |
diff --git a/arch/s390/include/asm/cputime.h b/arch/s390/include/asm/cputime.h index 081434878296..c23c3900c304 100644 --- a/arch/s390/include/asm/cputime.h +++ b/arch/s390/include/asm/cputime.h | |||
@@ -16,114 +16,100 @@ | |||
16 | 16 | ||
17 | /* We want to use full resolution of the CPU timer: 2**-12 micro-seconds. */ | 17 | /* We want to use full resolution of the CPU timer: 2**-12 micro-seconds. */ |
18 | 18 | ||
19 | typedef unsigned long long cputime_t; | 19 | typedef unsigned long long __nocast cputime_t; |
20 | typedef unsigned long long cputime64_t; | 20 | typedef unsigned long long __nocast cputime64_t; |
21 | 21 | ||
22 | #ifndef __s390x__ | 22 | static inline unsigned long __div(unsigned long long n, unsigned long base) |
23 | |||
24 | static inline unsigned int | ||
25 | __div(unsigned long long n, unsigned int base) | ||
26 | { | 23 | { |
24 | #ifndef __s390x__ | ||
27 | register_pair rp; | 25 | register_pair rp; |
28 | 26 | ||
29 | rp.pair = n >> 1; | 27 | rp.pair = n >> 1; |
30 | asm ("dr %0,%1" : "+d" (rp) : "d" (base >> 1)); | 28 | asm ("dr %0,%1" : "+d" (rp) : "d" (base >> 1)); |
31 | return rp.subreg.odd; | 29 | return rp.subreg.odd; |
30 | #else /* __s390x__ */ | ||
31 | return n / base; | ||
32 | #endif /* __s390x__ */ | ||
32 | } | 33 | } |
33 | 34 | ||
34 | #else /* __s390x__ */ | 35 | #define cputime_one_jiffy jiffies_to_cputime(1) |
36 | |||
37 | /* | ||
38 | * Convert cputime to jiffies and back. | ||
39 | */ | ||
40 | static inline unsigned long cputime_to_jiffies(const cputime_t cputime) | ||
41 | { | ||
42 | return __div((__force unsigned long long) cputime, 4096000000ULL / HZ); | ||
43 | } | ||
35 | 44 | ||
36 | static inline unsigned int | 45 | static inline cputime_t jiffies_to_cputime(const unsigned int jif) |
37 | __div(unsigned long long n, unsigned int base) | ||
38 | { | 46 | { |
39 | return n / base; | 47 | return (__force cputime_t)(jif * (4096000000ULL / HZ)); |
40 | } | 48 | } |
41 | 49 | ||
42 | #endif /* __s390x__ */ | 50 | static inline u64 cputime64_to_jiffies64(cputime64_t cputime) |
51 | { | ||
52 | unsigned long long jif = (__force unsigned long long) cputime; | ||
53 | do_div(jif, 4096000000ULL / HZ); | ||
54 | return jif; | ||
55 | } | ||
43 | 56 | ||
44 | #define cputime_zero (0ULL) | 57 | static inline cputime64_t jiffies64_to_cputime64(const u64 jif) |
45 | #define cputime_one_jiffy jiffies_to_cputime(1) | 58 | { |
46 | #define cputime_max ((~0UL >> 1) - 1) | 59 | return (__force cputime64_t)(jif * (4096000000ULL / HZ)); |
47 | #define cputime_add(__a, __b) ((__a) + (__b)) | ||
48 | #define cputime_sub(__a, __b) ((__a) - (__b)) | ||
49 | #define cputime_div(__a, __n) ({ \ | ||
50 | unsigned long long __div = (__a); \ | ||
51 | do_div(__div,__n); \ | ||
52 | __div; \ | ||
53 | }) | ||
54 | #define cputime_halve(__a) ((__a) >> 1) | ||
55 | #define cputime_eq(__a, __b) ((__a) == (__b)) | ||
56 | #define cputime_gt(__a, __b) ((__a) > (__b)) | ||
57 | #define cputime_ge(__a, __b) ((__a) >= (__b)) | ||
58 | #define cputime_lt(__a, __b) ((__a) < (__b)) | ||
59 | #define cputime_le(__a, __b) ((__a) <= (__b)) | ||
60 | #define cputime_to_jiffies(__ct) (__div((__ct), 4096000000ULL / HZ)) | ||
61 | #define cputime_to_scaled(__ct) (__ct) | ||
62 | #define jiffies_to_cputime(__hz) ((cputime_t)(__hz) * (4096000000ULL / HZ)) | ||
63 | |||
64 | #define cputime64_zero (0ULL) | ||
65 | #define cputime64_add(__a, __b) ((__a) + (__b)) | ||
66 | #define cputime_to_cputime64(__ct) (__ct) | ||
67 | |||
68 | static inline u64 | ||
69 | cputime64_to_jiffies64(cputime64_t cputime) | ||
70 | { | ||
71 | do_div(cputime, 4096000000ULL / HZ); | ||
72 | return cputime; | ||
73 | } | 60 | } |
74 | 61 | ||
75 | /* | 62 | /* |
76 | * Convert cputime to microseconds and back. | 63 | * Convert cputime to microseconds and back. |
77 | */ | 64 | */ |
78 | static inline unsigned int | 65 | static inline unsigned int cputime_to_usecs(const cputime_t cputime) |
79 | cputime_to_usecs(const cputime_t cputime) | ||
80 | { | 66 | { |
81 | return cputime_div(cputime, 4096); | 67 | return (__force unsigned long long) cputime >> 12; |
82 | } | 68 | } |
83 | 69 | ||
84 | static inline cputime_t | 70 | static inline cputime_t usecs_to_cputime(const unsigned int m) |
85 | usecs_to_cputime(const unsigned int m) | ||
86 | { | 71 | { |
87 | return (cputime_t) m * 4096; | 72 | return (__force cputime_t)(m * 4096ULL); |
88 | } | 73 | } |
89 | 74 | ||
75 | #define usecs_to_cputime64(m) usecs_to_cputime(m) | ||
76 | |||
90 | /* | 77 | /* |
91 | * Convert cputime to milliseconds and back. | 78 | * Convert cputime to milliseconds and back. |
92 | */ | 79 | */ |
93 | static inline unsigned int | 80 | static inline unsigned int cputime_to_secs(const cputime_t cputime) |
94 | cputime_to_secs(const cputime_t cputime) | ||
95 | { | 81 | { |
96 | return __div(cputime, 2048000000) >> 1; | 82 | return __div((__force unsigned long long) cputime, 2048000000) >> 1; |
97 | } | 83 | } |
98 | 84 | ||
99 | static inline cputime_t | 85 | static inline cputime_t secs_to_cputime(const unsigned int s) |
100 | secs_to_cputime(const unsigned int s) | ||
101 | { | 86 | { |
102 | return (cputime_t) s * 4096000000ULL; | 87 | return (__force cputime_t)(s * 4096000000ULL); |
103 | } | 88 | } |
104 | 89 | ||
105 | /* | 90 | /* |
106 | * Convert cputime to timespec and back. | 91 | * Convert cputime to timespec and back. |
107 | */ | 92 | */ |
108 | static inline cputime_t | 93 | static inline cputime_t timespec_to_cputime(const struct timespec *value) |
109 | timespec_to_cputime(const struct timespec *value) | ||
110 | { | 94 | { |
111 | return value->tv_nsec * 4096 / 1000 + (u64) value->tv_sec * 4096000000ULL; | 95 | unsigned long long ret = value->tv_sec * 4096000000ULL; |
96 | return (__force cputime_t)(ret + value->tv_nsec * 4096 / 1000); | ||
112 | } | 97 | } |
113 | 98 | ||
114 | static inline void | 99 | static inline void cputime_to_timespec(const cputime_t cputime, |
115 | cputime_to_timespec(const cputime_t cputime, struct timespec *value) | 100 | struct timespec *value) |
116 | { | 101 | { |
102 | unsigned long long __cputime = (__force unsigned long long) cputime; | ||
117 | #ifndef __s390x__ | 103 | #ifndef __s390x__ |
118 | register_pair rp; | 104 | register_pair rp; |
119 | 105 | ||
120 | rp.pair = cputime >> 1; | 106 | rp.pair = __cputime >> 1; |
121 | asm ("dr %0,%1" : "+d" (rp) : "d" (2048000000UL)); | 107 | asm ("dr %0,%1" : "+d" (rp) : "d" (2048000000UL)); |
122 | value->tv_nsec = rp.subreg.even * 1000 / 4096; | 108 | value->tv_nsec = rp.subreg.even * 1000 / 4096; |
123 | value->tv_sec = rp.subreg.odd; | 109 | value->tv_sec = rp.subreg.odd; |
124 | #else | 110 | #else |
125 | value->tv_nsec = (cputime % 4096000000ULL) * 1000 / 4096; | 111 | value->tv_nsec = (__cputime % 4096000000ULL) * 1000 / 4096; |
126 | value->tv_sec = cputime / 4096000000ULL; | 112 | value->tv_sec = __cputime / 4096000000ULL; |
127 | #endif | 113 | #endif |
128 | } | 114 | } |
129 | 115 | ||
@@ -132,50 +118,52 @@ cputime_to_timespec(const cputime_t cputime, struct timespec *value) | |||
132 | * Since cputime and timeval have the same resolution (microseconds) | 118 | * Since cputime and timeval have the same resolution (microseconds) |
133 | * this is easy. | 119 | * this is easy. |
134 | */ | 120 | */ |
135 | static inline cputime_t | 121 | static inline cputime_t timeval_to_cputime(const struct timeval *value) |
136 | timeval_to_cputime(const struct timeval *value) | ||
137 | { | 122 | { |
138 | return value->tv_usec * 4096 + (u64) value->tv_sec * 4096000000ULL; | 123 | unsigned long long ret = value->tv_sec * 4096000000ULL; |
124 | return (__force cputime_t)(ret + value->tv_usec * 4096ULL); | ||
139 | } | 125 | } |
140 | 126 | ||
141 | static inline void | 127 | static inline void cputime_to_timeval(const cputime_t cputime, |
142 | cputime_to_timeval(const cputime_t cputime, struct timeval *value) | 128 | struct timeval *value) |
143 | { | 129 | { |
130 | unsigned long long __cputime = (__force unsigned long long) cputime; | ||
144 | #ifndef __s390x__ | 131 | #ifndef __s390x__ |
145 | register_pair rp; | 132 | register_pair rp; |
146 | 133 | ||
147 | rp.pair = cputime >> 1; | 134 | rp.pair = __cputime >> 1; |
148 | asm ("dr %0,%1" : "+d" (rp) : "d" (2048000000UL)); | 135 | asm ("dr %0,%1" : "+d" (rp) : "d" (2048000000UL)); |
149 | value->tv_usec = rp.subreg.even / 4096; | 136 | value->tv_usec = rp.subreg.even / 4096; |
150 | value->tv_sec = rp.subreg.odd; | 137 | value->tv_sec = rp.subreg.odd; |
151 | #else | 138 | #else |
152 | value->tv_usec = (cputime % 4096000000ULL) / 4096; | 139 | value->tv_usec = (__cputime % 4096000000ULL) / 4096; |
153 | value->tv_sec = cputime / 4096000000ULL; | 140 | value->tv_sec = __cputime / 4096000000ULL; |
154 | #endif | 141 | #endif |
155 | } | 142 | } |
156 | 143 | ||
157 | /* | 144 | /* |
158 | * Convert cputime to clock and back. | 145 | * Convert cputime to clock and back. |
159 | */ | 146 | */ |
160 | static inline clock_t | 147 | static inline clock_t cputime_to_clock_t(cputime_t cputime) |
161 | cputime_to_clock_t(cputime_t cputime) | ||
162 | { | 148 | { |
163 | return cputime_div(cputime, 4096000000ULL / USER_HZ); | 149 | unsigned long long clock = (__force unsigned long long) cputime; |
150 | do_div(clock, 4096000000ULL / USER_HZ); | ||
151 | return clock; | ||
164 | } | 152 | } |
165 | 153 | ||
166 | static inline cputime_t | 154 | static inline cputime_t clock_t_to_cputime(unsigned long x) |
167 | clock_t_to_cputime(unsigned long x) | ||
168 | { | 155 | { |
169 | return (cputime_t) x * (4096000000ULL / USER_HZ); | 156 | return (__force cputime_t)(x * (4096000000ULL / USER_HZ)); |
170 | } | 157 | } |
171 | 158 | ||
172 | /* | 159 | /* |
173 | * Convert cputime64 to clock. | 160 | * Convert cputime64 to clock. |
174 | */ | 161 | */ |
175 | static inline clock_t | 162 | static inline clock_t cputime64_to_clock_t(cputime64_t cputime) |
176 | cputime64_to_clock_t(cputime64_t cputime) | ||
177 | { | 163 | { |
178 | return cputime_div(cputime, 4096000000ULL / USER_HZ); | 164 | unsigned long long clock = (__force unsigned long long) cputime; |
165 | do_div(clock, 4096000000ULL / USER_HZ); | ||
166 | return clock; | ||
179 | } | 167 | } |
180 | 168 | ||
181 | struct s390_idle_data { | 169 | struct s390_idle_data { |
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 9451b210a1b4..3201ae447990 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c | |||
@@ -91,10 +91,12 @@ static void default_idle(void) | |||
91 | void cpu_idle(void) | 91 | void cpu_idle(void) |
92 | { | 92 | { |
93 | for (;;) { | 93 | for (;;) { |
94 | tick_nohz_stop_sched_tick(1); | 94 | tick_nohz_idle_enter(); |
95 | rcu_idle_enter(); | ||
95 | while (!need_resched()) | 96 | while (!need_resched()) |
96 | default_idle(); | 97 | default_idle(); |
97 | tick_nohz_restart_sched_tick(); | 98 | rcu_idle_exit(); |
99 | tick_nohz_idle_exit(); | ||
98 | preempt_enable_no_resched(); | 100 | preempt_enable_no_resched(); |
99 | schedule(); | 101 | schedule(); |
100 | preempt_disable(); | 102 | preempt_disable(); |
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index e54c4ff8abaa..f11d1b037c50 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/sched.h> | 22 | #include <linux/sched.h> |
23 | #include <linux/kernel.h> | 23 | #include <linux/kernel.h> |
24 | #include <linux/memblock.h> | ||
24 | #include <linux/mm.h> | 25 | #include <linux/mm.h> |
25 | #include <linux/stddef.h> | 26 | #include <linux/stddef.h> |
26 | #include <linux/unistd.h> | 27 | #include <linux/unistd.h> |
@@ -820,7 +821,8 @@ setup_memory(void) | |||
820 | end_chunk = min(end_chunk, end_pfn); | 821 | end_chunk = min(end_chunk, end_pfn); |
821 | if (start_chunk >= end_chunk) | 822 | if (start_chunk >= end_chunk) |
822 | continue; | 823 | continue; |
823 | add_active_range(0, start_chunk, end_chunk); | 824 | memblock_add_node(PFN_PHYS(start_chunk), |
825 | PFN_PHYS(end_chunk - start_chunk), 0); | ||
824 | pfn = max(start_chunk, start_pfn); | 826 | pfn = max(start_chunk, start_pfn); |
825 | for (; pfn < end_chunk; pfn++) | 827 | for (; pfn < end_chunk; pfn++) |
826 | page_set_storage_key(PFN_PHYS(pfn), | 828 | page_set_storage_key(PFN_PHYS(pfn), |
diff --git a/arch/s390/oprofile/hwsampler.c b/arch/s390/oprofile/hwsampler.c index f43c0e4282af..9daee91e6c3f 100644 --- a/arch/s390/oprofile/hwsampler.c +++ b/arch/s390/oprofile/hwsampler.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <asm/irq.h> | 22 | #include <asm/irq.h> |
23 | 23 | ||
24 | #include "hwsampler.h" | 24 | #include "hwsampler.h" |
25 | #include "op_counter.h" | ||
25 | 26 | ||
26 | #define MAX_NUM_SDB 511 | 27 | #define MAX_NUM_SDB 511 |
27 | #define MIN_NUM_SDB 1 | 28 | #define MIN_NUM_SDB 1 |
@@ -896,6 +897,8 @@ static void add_samples_to_oprofile(unsigned int cpu, unsigned long *sdbt, | |||
896 | if (sample_data_ptr->P == 1) { | 897 | if (sample_data_ptr->P == 1) { |
897 | /* userspace sample */ | 898 | /* userspace sample */ |
898 | unsigned int pid = sample_data_ptr->prim_asn; | 899 | unsigned int pid = sample_data_ptr->prim_asn; |
900 | if (!counter_config.user) | ||
901 | goto skip_sample; | ||
899 | rcu_read_lock(); | 902 | rcu_read_lock(); |
900 | tsk = pid_task(find_vpid(pid), PIDTYPE_PID); | 903 | tsk = pid_task(find_vpid(pid), PIDTYPE_PID); |
901 | if (tsk) | 904 | if (tsk) |
@@ -903,6 +906,8 @@ static void add_samples_to_oprofile(unsigned int cpu, unsigned long *sdbt, | |||
903 | rcu_read_unlock(); | 906 | rcu_read_unlock(); |
904 | } else { | 907 | } else { |
905 | /* kernelspace sample */ | 908 | /* kernelspace sample */ |
909 | if (!counter_config.kernel) | ||
910 | goto skip_sample; | ||
906 | regs = task_pt_regs(current); | 911 | regs = task_pt_regs(current); |
907 | } | 912 | } |
908 | 913 | ||
@@ -910,7 +915,7 @@ static void add_samples_to_oprofile(unsigned int cpu, unsigned long *sdbt, | |||
910 | oprofile_add_ext_hw_sample(sample_data_ptr->ia, regs, 0, | 915 | oprofile_add_ext_hw_sample(sample_data_ptr->ia, regs, 0, |
911 | !sample_data_ptr->P, tsk); | 916 | !sample_data_ptr->P, tsk); |
912 | mutex_unlock(&hws_sem); | 917 | mutex_unlock(&hws_sem); |
913 | 918 | skip_sample: | |
914 | sample_data_ptr++; | 919 | sample_data_ptr++; |
915 | } | 920 | } |
916 | } | 921 | } |
diff --git a/arch/s390/oprofile/init.c b/arch/s390/oprofile/init.c index bd58b72454cf..2297be406c61 100644 --- a/arch/s390/oprofile/init.c +++ b/arch/s390/oprofile/init.c | |||
@@ -2,10 +2,11 @@ | |||
2 | * arch/s390/oprofile/init.c | 2 | * arch/s390/oprofile/init.c |
3 | * | 3 | * |
4 | * S390 Version | 4 | * S390 Version |
5 | * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation | 5 | * Copyright (C) 2002-2011 IBM Deutschland Entwicklung GmbH, IBM Corporation |
6 | * Author(s): Thomas Spatzier (tspat@de.ibm.com) | 6 | * Author(s): Thomas Spatzier (tspat@de.ibm.com) |
7 | * Author(s): Mahesh Salgaonkar (mahesh@linux.vnet.ibm.com) | 7 | * Author(s): Mahesh Salgaonkar (mahesh@linux.vnet.ibm.com) |
8 | * Author(s): Heinz Graalfs (graalfs@linux.vnet.ibm.com) | 8 | * Author(s): Heinz Graalfs (graalfs@linux.vnet.ibm.com) |
9 | * Author(s): Andreas Krebbel (krebbel@linux.vnet.ibm.com) | ||
9 | * | 10 | * |
10 | * @remark Copyright 2002-2011 OProfile authors | 11 | * @remark Copyright 2002-2011 OProfile authors |
11 | */ | 12 | */ |
@@ -14,6 +15,8 @@ | |||
14 | #include <linux/init.h> | 15 | #include <linux/init.h> |
15 | #include <linux/errno.h> | 16 | #include <linux/errno.h> |
16 | #include <linux/fs.h> | 17 | #include <linux/fs.h> |
18 | #include <linux/module.h> | ||
19 | #include <asm/processor.h> | ||
17 | 20 | ||
18 | #include "../../../drivers/oprofile/oprof.h" | 21 | #include "../../../drivers/oprofile/oprof.h" |
19 | 22 | ||
@@ -22,6 +25,7 @@ extern void s390_backtrace(struct pt_regs * const regs, unsigned int depth); | |||
22 | #ifdef CONFIG_64BIT | 25 | #ifdef CONFIG_64BIT |
23 | 26 | ||
24 | #include "hwsampler.h" | 27 | #include "hwsampler.h" |
28 | #include "op_counter.h" | ||
25 | 29 | ||
26 | #define DEFAULT_INTERVAL 4127518 | 30 | #define DEFAULT_INTERVAL 4127518 |
27 | 31 | ||
@@ -35,16 +39,41 @@ static unsigned long oprofile_max_interval; | |||
35 | static unsigned long oprofile_sdbt_blocks = DEFAULT_SDBT_BLOCKS; | 39 | static unsigned long oprofile_sdbt_blocks = DEFAULT_SDBT_BLOCKS; |
36 | static unsigned long oprofile_sdb_blocks = DEFAULT_SDB_BLOCKS; | 40 | static unsigned long oprofile_sdb_blocks = DEFAULT_SDB_BLOCKS; |
37 | 41 | ||
38 | static int hwsampler_file; | 42 | static int hwsampler_enabled; |
39 | static int hwsampler_running; /* start_mutex must be held to change */ | 43 | static int hwsampler_running; /* start_mutex must be held to change */ |
44 | static int hwsampler_available; | ||
40 | 45 | ||
41 | static struct oprofile_operations timer_ops; | 46 | static struct oprofile_operations timer_ops; |
42 | 47 | ||
48 | struct op_counter_config counter_config; | ||
49 | |||
50 | enum __force_cpu_type { | ||
51 | reserved = 0, /* do not force */ | ||
52 | timer, | ||
53 | }; | ||
54 | static int force_cpu_type; | ||
55 | |||
56 | static int set_cpu_type(const char *str, struct kernel_param *kp) | ||
57 | { | ||
58 | if (!strcmp(str, "timer")) { | ||
59 | force_cpu_type = timer; | ||
60 | printk(KERN_INFO "oprofile: forcing timer to be returned " | ||
61 | "as cpu type\n"); | ||
62 | } else { | ||
63 | force_cpu_type = 0; | ||
64 | } | ||
65 | |||
66 | return 0; | ||
67 | } | ||
68 | module_param_call(cpu_type, set_cpu_type, NULL, NULL, 0); | ||
69 | MODULE_PARM_DESC(cpu_type, "Force legacy basic mode sampling" | ||
70 | "(report cpu_type \"timer\""); | ||
71 | |||
43 | static int oprofile_hwsampler_start(void) | 72 | static int oprofile_hwsampler_start(void) |
44 | { | 73 | { |
45 | int retval; | 74 | int retval; |
46 | 75 | ||
47 | hwsampler_running = hwsampler_file; | 76 | hwsampler_running = hwsampler_enabled; |
48 | 77 | ||
49 | if (!hwsampler_running) | 78 | if (!hwsampler_running) |
50 | return timer_ops.start(); | 79 | return timer_ops.start(); |
@@ -72,10 +101,16 @@ static void oprofile_hwsampler_stop(void) | |||
72 | return; | 101 | return; |
73 | } | 102 | } |
74 | 103 | ||
104 | /* | ||
105 | * File ops used for: | ||
106 | * /dev/oprofile/0/enabled | ||
107 | * /dev/oprofile/hwsampling/hwsampler (cpu_type = timer) | ||
108 | */ | ||
109 | |||
75 | static ssize_t hwsampler_read(struct file *file, char __user *buf, | 110 | static ssize_t hwsampler_read(struct file *file, char __user *buf, |
76 | size_t count, loff_t *offset) | 111 | size_t count, loff_t *offset) |
77 | { | 112 | { |
78 | return oprofilefs_ulong_to_user(hwsampler_file, buf, count, offset); | 113 | return oprofilefs_ulong_to_user(hwsampler_enabled, buf, count, offset); |
79 | } | 114 | } |
80 | 115 | ||
81 | static ssize_t hwsampler_write(struct file *file, char const __user *buf, | 116 | static ssize_t hwsampler_write(struct file *file, char const __user *buf, |
@@ -91,6 +126,9 @@ static ssize_t hwsampler_write(struct file *file, char const __user *buf, | |||
91 | if (retval <= 0) | 126 | if (retval <= 0) |
92 | return retval; | 127 | return retval; |
93 | 128 | ||
129 | if (val != 0 && val != 1) | ||
130 | return -EINVAL; | ||
131 | |||
94 | if (oprofile_started) | 132 | if (oprofile_started) |
95 | /* | 133 | /* |
96 | * save to do without locking as we set | 134 | * save to do without locking as we set |
@@ -99,7 +137,7 @@ static ssize_t hwsampler_write(struct file *file, char const __user *buf, | |||
99 | */ | 137 | */ |
100 | return -EBUSY; | 138 | return -EBUSY; |
101 | 139 | ||
102 | hwsampler_file = val; | 140 | hwsampler_enabled = val; |
103 | 141 | ||
104 | return count; | 142 | return count; |
105 | } | 143 | } |
@@ -109,38 +147,311 @@ static const struct file_operations hwsampler_fops = { | |||
109 | .write = hwsampler_write, | 147 | .write = hwsampler_write, |
110 | }; | 148 | }; |
111 | 149 | ||
150 | /* | ||
151 | * File ops used for: | ||
152 | * /dev/oprofile/0/count | ||
153 | * /dev/oprofile/hwsampling/hw_interval (cpu_type = timer) | ||
154 | * | ||
155 | * Make sure that the value is within the hardware range. | ||
156 | */ | ||
157 | |||
158 | static ssize_t hw_interval_read(struct file *file, char __user *buf, | ||
159 | size_t count, loff_t *offset) | ||
160 | { | ||
161 | return oprofilefs_ulong_to_user(oprofile_hw_interval, buf, | ||
162 | count, offset); | ||
163 | } | ||
164 | |||
165 | static ssize_t hw_interval_write(struct file *file, char const __user *buf, | ||
166 | size_t count, loff_t *offset) | ||
167 | { | ||
168 | unsigned long val; | ||
169 | int retval; | ||
170 | |||
171 | if (*offset) | ||
172 | return -EINVAL; | ||
173 | retval = oprofilefs_ulong_from_user(&val, buf, count); | ||
174 | if (retval) | ||
175 | return retval; | ||
176 | if (val < oprofile_min_interval) | ||
177 | oprofile_hw_interval = oprofile_min_interval; | ||
178 | else if (val > oprofile_max_interval) | ||
179 | oprofile_hw_interval = oprofile_max_interval; | ||
180 | else | ||
181 | oprofile_hw_interval = val; | ||
182 | |||
183 | return count; | ||
184 | } | ||
185 | |||
186 | static const struct file_operations hw_interval_fops = { | ||
187 | .read = hw_interval_read, | ||
188 | .write = hw_interval_write, | ||
189 | }; | ||
190 | |||
191 | /* | ||
192 | * File ops used for: | ||
193 | * /dev/oprofile/0/event | ||
194 | * Only a single event with number 0 is supported with this counter. | ||
195 | * | ||
196 | * /dev/oprofile/0/unit_mask | ||
197 | * This is a dummy file needed by the user space tools. | ||
198 | * No value other than 0 is accepted or returned. | ||
199 | */ | ||
200 | |||
201 | static ssize_t hwsampler_zero_read(struct file *file, char __user *buf, | ||
202 | size_t count, loff_t *offset) | ||
203 | { | ||
204 | return oprofilefs_ulong_to_user(0, buf, count, offset); | ||
205 | } | ||
206 | |||
207 | static ssize_t hwsampler_zero_write(struct file *file, char const __user *buf, | ||
208 | size_t count, loff_t *offset) | ||
209 | { | ||
210 | unsigned long val; | ||
211 | int retval; | ||
212 | |||
213 | if (*offset) | ||
214 | return -EINVAL; | ||
215 | |||
216 | retval = oprofilefs_ulong_from_user(&val, buf, count); | ||
217 | if (retval) | ||
218 | return retval; | ||
219 | if (val != 0) | ||
220 | return -EINVAL; | ||
221 | return count; | ||
222 | } | ||
223 | |||
224 | static const struct file_operations zero_fops = { | ||
225 | .read = hwsampler_zero_read, | ||
226 | .write = hwsampler_zero_write, | ||
227 | }; | ||
228 | |||
229 | /* /dev/oprofile/0/kernel file ops. */ | ||
230 | |||
231 | static ssize_t hwsampler_kernel_read(struct file *file, char __user *buf, | ||
232 | size_t count, loff_t *offset) | ||
233 | { | ||
234 | return oprofilefs_ulong_to_user(counter_config.kernel, | ||
235 | buf, count, offset); | ||
236 | } | ||
237 | |||
238 | static ssize_t hwsampler_kernel_write(struct file *file, char const __user *buf, | ||
239 | size_t count, loff_t *offset) | ||
240 | { | ||
241 | unsigned long val; | ||
242 | int retval; | ||
243 | |||
244 | if (*offset) | ||
245 | return -EINVAL; | ||
246 | |||
247 | retval = oprofilefs_ulong_from_user(&val, buf, count); | ||
248 | if (retval) | ||
249 | return retval; | ||
250 | |||
251 | if (val != 0 && val != 1) | ||
252 | return -EINVAL; | ||
253 | |||
254 | counter_config.kernel = val; | ||
255 | |||
256 | return count; | ||
257 | } | ||
258 | |||
259 | static const struct file_operations kernel_fops = { | ||
260 | .read = hwsampler_kernel_read, | ||
261 | .write = hwsampler_kernel_write, | ||
262 | }; | ||
263 | |||
264 | /* /dev/oprofile/0/user file ops. */ | ||
265 | |||
266 | static ssize_t hwsampler_user_read(struct file *file, char __user *buf, | ||
267 | size_t count, loff_t *offset) | ||
268 | { | ||
269 | return oprofilefs_ulong_to_user(counter_config.user, | ||
270 | buf, count, offset); | ||
271 | } | ||
272 | |||
273 | static ssize_t hwsampler_user_write(struct file *file, char const __user *buf, | ||
274 | size_t count, loff_t *offset) | ||
275 | { | ||
276 | unsigned long val; | ||
277 | int retval; | ||
278 | |||
279 | if (*offset) | ||
280 | return -EINVAL; | ||
281 | |||
282 | retval = oprofilefs_ulong_from_user(&val, buf, count); | ||
283 | if (retval) | ||
284 | return retval; | ||
285 | |||
286 | if (val != 0 && val != 1) | ||
287 | return -EINVAL; | ||
288 | |||
289 | counter_config.user = val; | ||
290 | |||
291 | return count; | ||
292 | } | ||
293 | |||
294 | static const struct file_operations user_fops = { | ||
295 | .read = hwsampler_user_read, | ||
296 | .write = hwsampler_user_write, | ||
297 | }; | ||
298 | |||
299 | |||
300 | /* | ||
301 | * File ops used for: /dev/oprofile/timer/enabled | ||
302 | * The value always has to be the inverted value of hwsampler_enabled. So | ||
303 | * no separate variable is created. That way we do not need locking. | ||
304 | */ | ||
305 | |||
306 | static ssize_t timer_enabled_read(struct file *file, char __user *buf, | ||
307 | size_t count, loff_t *offset) | ||
308 | { | ||
309 | return oprofilefs_ulong_to_user(!hwsampler_enabled, buf, count, offset); | ||
310 | } | ||
311 | |||
312 | static ssize_t timer_enabled_write(struct file *file, char const __user *buf, | ||
313 | size_t count, loff_t *offset) | ||
314 | { | ||
315 | unsigned long val; | ||
316 | int retval; | ||
317 | |||
318 | if (*offset) | ||
319 | return -EINVAL; | ||
320 | |||
321 | retval = oprofilefs_ulong_from_user(&val, buf, count); | ||
322 | if (retval) | ||
323 | return retval; | ||
324 | |||
325 | if (val != 0 && val != 1) | ||
326 | return -EINVAL; | ||
327 | |||
328 | /* Timer cannot be disabled without having hardware sampling. */ | ||
329 | if (val == 0 && !hwsampler_available) | ||
330 | return -EINVAL; | ||
331 | |||
332 | if (oprofile_started) | ||
333 | /* | ||
334 | * save to do without locking as we set | ||
335 | * hwsampler_running in start() when start_mutex is | ||
336 | * held | ||
337 | */ | ||
338 | return -EBUSY; | ||
339 | |||
340 | hwsampler_enabled = !val; | ||
341 | |||
342 | return count; | ||
343 | } | ||
344 | |||
345 | static const struct file_operations timer_enabled_fops = { | ||
346 | .read = timer_enabled_read, | ||
347 | .write = timer_enabled_write, | ||
348 | }; | ||
349 | |||
350 | |||
112 | static int oprofile_create_hwsampling_files(struct super_block *sb, | 351 | static int oprofile_create_hwsampling_files(struct super_block *sb, |
113 | struct dentry *root) | 352 | struct dentry *root) |
114 | { | 353 | { |
115 | struct dentry *hw_dir; | 354 | struct dentry *dir; |
355 | |||
356 | dir = oprofilefs_mkdir(sb, root, "timer"); | ||
357 | if (!dir) | ||
358 | return -EINVAL; | ||
359 | |||
360 | oprofilefs_create_file(sb, dir, "enabled", &timer_enabled_fops); | ||
361 | |||
362 | if (!hwsampler_available) | ||
363 | return 0; | ||
116 | 364 | ||
117 | /* reinitialize default values */ | 365 | /* reinitialize default values */ |
118 | hwsampler_file = 1; | 366 | hwsampler_enabled = 1; |
367 | counter_config.kernel = 1; | ||
368 | counter_config.user = 1; | ||
119 | 369 | ||
120 | hw_dir = oprofilefs_mkdir(sb, root, "hwsampling"); | 370 | if (!force_cpu_type) { |
121 | if (!hw_dir) | 371 | /* |
122 | return -EINVAL; | 372 | * Create the counter file system. A single virtual |
373 | * counter is created which can be used to | ||
374 | * enable/disable hardware sampling dynamically from | ||
375 | * user space. The user space will configure a single | ||
376 | * counter with a single event. The value of 'event' | ||
377 | * and 'unit_mask' are not evaluated by the kernel code | ||
378 | * and can only be set to 0. | ||
379 | */ | ||
380 | |||
381 | dir = oprofilefs_mkdir(sb, root, "0"); | ||
382 | if (!dir) | ||
383 | return -EINVAL; | ||
123 | 384 | ||
124 | oprofilefs_create_file(sb, hw_dir, "hwsampler", &hwsampler_fops); | 385 | oprofilefs_create_file(sb, dir, "enabled", &hwsampler_fops); |
125 | oprofilefs_create_ulong(sb, hw_dir, "hw_interval", | 386 | oprofilefs_create_file(sb, dir, "event", &zero_fops); |
126 | &oprofile_hw_interval); | 387 | oprofilefs_create_file(sb, dir, "count", &hw_interval_fops); |
127 | oprofilefs_create_ro_ulong(sb, hw_dir, "hw_min_interval", | 388 | oprofilefs_create_file(sb, dir, "unit_mask", &zero_fops); |
128 | &oprofile_min_interval); | 389 | oprofilefs_create_file(sb, dir, "kernel", &kernel_fops); |
129 | oprofilefs_create_ro_ulong(sb, hw_dir, "hw_max_interval", | 390 | oprofilefs_create_file(sb, dir, "user", &user_fops); |
130 | &oprofile_max_interval); | 391 | oprofilefs_create_ulong(sb, dir, "hw_sdbt_blocks", |
131 | oprofilefs_create_ulong(sb, hw_dir, "hw_sdbt_blocks", | 392 | &oprofile_sdbt_blocks); |
132 | &oprofile_sdbt_blocks); | ||
133 | 393 | ||
394 | } else { | ||
395 | /* | ||
396 | * Hardware sampling can be used but the cpu_type is | ||
397 | * forced to timer in order to deal with legacy user | ||
398 | * space tools. The /dev/oprofile/hwsampling fs is | ||
399 | * provided in that case. | ||
400 | */ | ||
401 | dir = oprofilefs_mkdir(sb, root, "hwsampling"); | ||
402 | if (!dir) | ||
403 | return -EINVAL; | ||
404 | |||
405 | oprofilefs_create_file(sb, dir, "hwsampler", | ||
406 | &hwsampler_fops); | ||
407 | oprofilefs_create_file(sb, dir, "hw_interval", | ||
408 | &hw_interval_fops); | ||
409 | oprofilefs_create_ro_ulong(sb, dir, "hw_min_interval", | ||
410 | &oprofile_min_interval); | ||
411 | oprofilefs_create_ro_ulong(sb, dir, "hw_max_interval", | ||
412 | &oprofile_max_interval); | ||
413 | oprofilefs_create_ulong(sb, dir, "hw_sdbt_blocks", | ||
414 | &oprofile_sdbt_blocks); | ||
415 | } | ||
134 | return 0; | 416 | return 0; |
135 | } | 417 | } |
136 | 418 | ||
137 | static int oprofile_hwsampler_init(struct oprofile_operations *ops) | 419 | static int oprofile_hwsampler_init(struct oprofile_operations *ops) |
138 | { | 420 | { |
421 | /* | ||
422 | * Initialize the timer mode infrastructure as well in order | ||
423 | * to be able to switch back dynamically. oprofile_timer_init | ||
424 | * is not supposed to fail. | ||
425 | */ | ||
426 | if (oprofile_timer_init(ops)) | ||
427 | BUG(); | ||
428 | |||
429 | memcpy(&timer_ops, ops, sizeof(timer_ops)); | ||
430 | ops->create_files = oprofile_create_hwsampling_files; | ||
431 | |||
432 | /* | ||
433 | * If the user space tools do not support newer cpu types, | ||
434 | * the force_cpu_type module parameter | ||
435 | * can be used to always return \"timer\" as cpu type. | ||
436 | */ | ||
437 | if (force_cpu_type != timer) { | ||
438 | struct cpuid id; | ||
439 | |||
440 | get_cpu_id (&id); | ||
441 | |||
442 | switch (id.machine) { | ||
443 | case 0x2097: case 0x2098: ops->cpu_type = "s390/z10"; break; | ||
444 | case 0x2817: case 0x2818: ops->cpu_type = "s390/z196"; break; | ||
445 | default: return -ENODEV; | ||
446 | } | ||
447 | } | ||
448 | |||
139 | if (hwsampler_setup()) | 449 | if (hwsampler_setup()) |
140 | return -ENODEV; | 450 | return -ENODEV; |
141 | 451 | ||
142 | /* | 452 | /* |
143 | * create hwsampler files only if hwsampler_setup() succeeds. | 453 | * Query the range for the sampling interval from the |
454 | * hardware. | ||
144 | */ | 455 | */ |
145 | oprofile_min_interval = hwsampler_query_min_interval(); | 456 | oprofile_min_interval = hwsampler_query_min_interval(); |
146 | if (oprofile_min_interval == 0) | 457 | if (oprofile_min_interval == 0) |
@@ -155,23 +466,17 @@ static int oprofile_hwsampler_init(struct oprofile_operations *ops) | |||
155 | if (oprofile_hw_interval > oprofile_max_interval) | 466 | if (oprofile_hw_interval > oprofile_max_interval) |
156 | oprofile_hw_interval = oprofile_max_interval; | 467 | oprofile_hw_interval = oprofile_max_interval; |
157 | 468 | ||
158 | if (oprofile_timer_init(ops)) | 469 | printk(KERN_INFO "oprofile: System z hardware sampling " |
159 | return -ENODEV; | 470 | "facility found.\n"); |
160 | |||
161 | printk(KERN_INFO "oprofile: using hardware sampling\n"); | ||
162 | |||
163 | memcpy(&timer_ops, ops, sizeof(timer_ops)); | ||
164 | 471 | ||
165 | ops->start = oprofile_hwsampler_start; | 472 | ops->start = oprofile_hwsampler_start; |
166 | ops->stop = oprofile_hwsampler_stop; | 473 | ops->stop = oprofile_hwsampler_stop; |
167 | ops->create_files = oprofile_create_hwsampling_files; | ||
168 | 474 | ||
169 | return 0; | 475 | return 0; |
170 | } | 476 | } |
171 | 477 | ||
172 | static void oprofile_hwsampler_exit(void) | 478 | static void oprofile_hwsampler_exit(void) |
173 | { | 479 | { |
174 | oprofile_timer_exit(); | ||
175 | hwsampler_shutdown(); | 480 | hwsampler_shutdown(); |
176 | } | 481 | } |
177 | 482 | ||
@@ -182,7 +487,15 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) | |||
182 | ops->backtrace = s390_backtrace; | 487 | ops->backtrace = s390_backtrace; |
183 | 488 | ||
184 | #ifdef CONFIG_64BIT | 489 | #ifdef CONFIG_64BIT |
185 | return oprofile_hwsampler_init(ops); | 490 | |
491 | /* | ||
492 | * -ENODEV is not reported to the caller. The module itself | ||
493 | * will use the timer mode sampling as fallback and this is | ||
494 | * always available. | ||
495 | */ | ||
496 | hwsampler_available = oprofile_hwsampler_init(ops) == 0; | ||
497 | |||
498 | return 0; | ||
186 | #else | 499 | #else |
187 | return -ENODEV; | 500 | return -ENODEV; |
188 | #endif | 501 | #endif |
diff --git a/arch/s390/oprofile/op_counter.h b/arch/s390/oprofile/op_counter.h new file mode 100644 index 000000000000..1a8d3ca09014 --- /dev/null +++ b/arch/s390/oprofile/op_counter.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /** | ||
2 | * arch/s390/oprofile/op_counter.h | ||
3 | * | ||
4 | * Copyright (C) 2011 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
5 | * Author(s): Andreas Krebbel (krebbel@linux.vnet.ibm.com) | ||
6 | * | ||
7 | * @remark Copyright 2011 OProfile authors | ||
8 | */ | ||
9 | |||
10 | #ifndef OP_COUNTER_H | ||
11 | #define OP_COUNTER_H | ||
12 | |||
13 | struct op_counter_config { | ||
14 | /* `enabled' maps to the hwsampler_file variable. */ | ||
15 | /* `count' maps to the oprofile_hw_interval variable. */ | ||
16 | /* `event' and `unit_mask' are unused. */ | ||
17 | unsigned long kernel; | ||
18 | unsigned long user; | ||
19 | }; | ||
20 | |||
21 | extern struct op_counter_config counter_config; | ||
22 | |||
23 | #endif /* OP_COUNTER_H */ | ||