diff options
author | Hendrik Brueckner <brueckner@linux.vnet.ibm.com> | 2013-12-12 10:52:48 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-12-16 08:37:52 -0500 |
commit | e28bb79d9935293a8eea5f3c771fde89db645ba7 (patch) | |
tree | 0d552a218e87c70ca55b5b103b5ef2a71477d400 | |
parent | 55baa2f831ae4a41da9617ab9e7cef5ebc991ec9 (diff) |
s390/perf,oprofile: Share sampling facility
Introduce reserve/release functions to share the sampling facility
between perf and oprofile.
Also improve error handling for the sampling facility support in perf.
Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | arch/s390/include/asm/perf_event.h | 4 | ||||
-rw-r--r-- | arch/s390/kernel/perf_cpum_sf.c | 17 | ||||
-rw-r--r-- | arch/s390/kernel/perf_event.c | 30 | ||||
-rw-r--r-- | arch/s390/oprofile/hwsampler.c | 7 | ||||
-rw-r--r-- | arch/s390/oprofile/init.c | 23 |
5 files changed, 73 insertions, 8 deletions
diff --git a/arch/s390/include/asm/perf_event.h b/arch/s390/include/asm/perf_event.h index b4eea25f379e..23d2dfa8201d 100644 --- a/arch/s390/include/asm/perf_event.h +++ b/arch/s390/include/asm/perf_event.h | |||
@@ -52,5 +52,9 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs); | |||
52 | #define TEAR_REG(hwc) ((hwc)->last_tag) | 52 | #define TEAR_REG(hwc) ((hwc)->last_tag) |
53 | #define SAMPL_RATE(hwc) ((hwc)->event_base) | 53 | #define SAMPL_RATE(hwc) ((hwc)->event_base) |
54 | 54 | ||
55 | /* Perf hardware reserve and release functions */ | ||
56 | int perf_reserve_sampling(void); | ||
57 | void perf_release_sampling(void); | ||
58 | |||
55 | #endif /* CONFIG_64BIT */ | 59 | #endif /* CONFIG_64BIT */ |
56 | #endif /* _ASM_S390_PERF_EVENT_H */ | 60 | #endif /* _ASM_S390_PERF_EVENT_H */ |
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c index 52bf36ee91aa..ae5e0192160d 100644 --- a/arch/s390/kernel/perf_cpum_sf.c +++ b/arch/s390/kernel/perf_cpum_sf.c | |||
@@ -260,16 +260,12 @@ static int sf_disable(void) | |||
260 | 260 | ||
261 | #define PMC_INIT 0 | 261 | #define PMC_INIT 0 |
262 | #define PMC_RELEASE 1 | 262 | #define PMC_RELEASE 1 |
263 | #define PMC_FAILURE 2 | ||
263 | static void setup_pmc_cpu(void *flags) | 264 | static void setup_pmc_cpu(void *flags) |
264 | { | 265 | { |
265 | int err; | 266 | int err; |
266 | struct cpu_hw_sf *cpusf = &__get_cpu_var(cpu_hw_sf); | 267 | struct cpu_hw_sf *cpusf = &__get_cpu_var(cpu_hw_sf); |
267 | 268 | ||
268 | /* XXX Improve error handling and pass a flag in the *flags | ||
269 | * variable to indicate failures. Alternatively, ignore | ||
270 | * (print) errors here and let the PMU functions fail if | ||
271 | * the per-cpu PMU_F_RESERVED flag is not. | ||
272 | */ | ||
273 | err = 0; | 269 | err = 0; |
274 | switch (*((int *) flags)) { | 270 | switch (*((int *) flags)) { |
275 | case PMC_INIT: | 271 | case PMC_INIT: |
@@ -299,6 +295,8 @@ static void setup_pmc_cpu(void *flags) | |||
299 | "setup_pmc_cpu: released: cpuhw=%p\n", cpusf); | 295 | "setup_pmc_cpu: released: cpuhw=%p\n", cpusf); |
300 | break; | 296 | break; |
301 | } | 297 | } |
298 | if (err) | ||
299 | *((int *) flags) |= PMC_FAILURE; | ||
302 | } | 300 | } |
303 | 301 | ||
304 | static void release_pmc_hardware(void) | 302 | static void release_pmc_hardware(void) |
@@ -307,13 +305,22 @@ static void release_pmc_hardware(void) | |||
307 | 305 | ||
308 | irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); | 306 | irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); |
309 | on_each_cpu(setup_pmc_cpu, &flags, 1); | 307 | on_each_cpu(setup_pmc_cpu, &flags, 1); |
308 | perf_release_sampling(); | ||
310 | } | 309 | } |
311 | 310 | ||
312 | static int reserve_pmc_hardware(void) | 311 | static int reserve_pmc_hardware(void) |
313 | { | 312 | { |
314 | int flags = PMC_INIT; | 313 | int flags = PMC_INIT; |
314 | int err; | ||
315 | 315 | ||
316 | err = perf_reserve_sampling(); | ||
317 | if (err) | ||
318 | return err; | ||
316 | on_each_cpu(setup_pmc_cpu, &flags, 1); | 319 | on_each_cpu(setup_pmc_cpu, &flags, 1); |
320 | if (flags & PMC_FAILURE) { | ||
321 | release_pmc_hardware(); | ||
322 | return -ENODEV; | ||
323 | } | ||
317 | irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); | 324 | irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); |
318 | 325 | ||
319 | return 0; | 326 | return 0; |
diff --git a/arch/s390/kernel/perf_event.c b/arch/s390/kernel/perf_event.c index b9843ba9829f..4edcdfa4894e 100644 --- a/arch/s390/kernel/perf_event.c +++ b/arch/s390/kernel/perf_event.c | |||
@@ -208,3 +208,33 @@ ssize_t cpumf_events_sysfs_show(struct device *dev, | |||
208 | return sprintf(page, "event=0x%04llx,name=%s\n", | 208 | return sprintf(page, "event=0x%04llx,name=%s\n", |
209 | pmu_attr->id, attr->attr.name); | 209 | pmu_attr->id, attr->attr.name); |
210 | } | 210 | } |
211 | |||
212 | /* Reserve/release functions for sharing perf hardware */ | ||
213 | static DEFINE_SPINLOCK(perf_hw_owner_lock); | ||
214 | static void *perf_sampling_owner; | ||
215 | |||
216 | int perf_reserve_sampling(void) | ||
217 | { | ||
218 | int err; | ||
219 | |||
220 | err = 0; | ||
221 | spin_lock(&perf_hw_owner_lock); | ||
222 | if (perf_sampling_owner) { | ||
223 | pr_warn("The sampling facility is already reserved by %p\n", | ||
224 | perf_sampling_owner); | ||
225 | err = -EBUSY; | ||
226 | } else | ||
227 | perf_sampling_owner = __builtin_return_address(0); | ||
228 | spin_unlock(&perf_hw_owner_lock); | ||
229 | return err; | ||
230 | } | ||
231 | EXPORT_SYMBOL(perf_reserve_sampling); | ||
232 | |||
233 | void perf_release_sampling(void) | ||
234 | { | ||
235 | spin_lock(&perf_hw_owner_lock); | ||
236 | WARN_ON(!perf_sampling_owner); | ||
237 | perf_sampling_owner = NULL; | ||
238 | spin_unlock(&perf_hw_owner_lock); | ||
239 | } | ||
240 | EXPORT_SYMBOL(perf_release_sampling); | ||
diff --git a/arch/s390/oprofile/hwsampler.c b/arch/s390/oprofile/hwsampler.c index bbca76ad6e1b..eb095874540d 100644 --- a/arch/s390/oprofile/hwsampler.c +++ b/arch/s390/oprofile/hwsampler.c | |||
@@ -41,6 +41,7 @@ static DEFINE_MUTEX(hws_sem_oom); | |||
41 | 41 | ||
42 | static unsigned char hws_flush_all; | 42 | static unsigned char hws_flush_all; |
43 | static unsigned int hws_oom; | 43 | static unsigned int hws_oom; |
44 | static unsigned int hws_alert; | ||
44 | static struct workqueue_struct *hws_wq; | 45 | static struct workqueue_struct *hws_wq; |
45 | 46 | ||
46 | static unsigned int hws_state; | 47 | static unsigned int hws_state; |
@@ -182,6 +183,9 @@ static void hws_ext_handler(struct ext_code ext_code, | |||
182 | if (!(param32 & CPU_MF_INT_SF_MASK)) | 183 | if (!(param32 & CPU_MF_INT_SF_MASK)) |
183 | return; | 184 | return; |
184 | 185 | ||
186 | if (!hws_alert) | ||
187 | return; | ||
188 | |||
185 | inc_irq_stat(IRQEXT_CMS); | 189 | inc_irq_stat(IRQEXT_CMS); |
186 | atomic_xchg(&cb->ext_params, atomic_read(&cb->ext_params) | param32); | 190 | atomic_xchg(&cb->ext_params, atomic_read(&cb->ext_params) | param32); |
187 | 191 | ||
@@ -941,6 +945,7 @@ int hwsampler_deallocate(void) | |||
941 | goto deallocate_exit; | 945 | goto deallocate_exit; |
942 | 946 | ||
943 | irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); | 947 | irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); |
948 | hws_alert = 0; | ||
944 | deallocate_sdbt(); | 949 | deallocate_sdbt(); |
945 | 950 | ||
946 | hws_state = HWS_DEALLOCATED; | 951 | hws_state = HWS_DEALLOCATED; |
@@ -1055,6 +1060,7 @@ int hwsampler_shutdown(void) | |||
1055 | 1060 | ||
1056 | if (hws_state == HWS_STOPPED) { | 1061 | if (hws_state == HWS_STOPPED) { |
1057 | irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); | 1062 | irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); |
1063 | hws_alert = 0; | ||
1058 | deallocate_sdbt(); | 1064 | deallocate_sdbt(); |
1059 | } | 1065 | } |
1060 | if (hws_wq) { | 1066 | if (hws_wq) { |
@@ -1129,6 +1135,7 @@ start_all_exit: | |||
1129 | hws_oom = 1; | 1135 | hws_oom = 1; |
1130 | hws_flush_all = 0; | 1136 | hws_flush_all = 0; |
1131 | /* now let them in, 1407 CPUMF external interrupts */ | 1137 | /* now let them in, 1407 CPUMF external interrupts */ |
1138 | hws_alert = 1; | ||
1132 | irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); | 1139 | irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); |
1133 | 1140 | ||
1134 | return 0; | 1141 | return 0; |
diff --git a/arch/s390/oprofile/init.c b/arch/s390/oprofile/init.c index 04e1b6a85362..9ffe645d5989 100644 --- a/arch/s390/oprofile/init.c +++ b/arch/s390/oprofile/init.c | |||
@@ -10,6 +10,7 @@ | |||
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/oprofile.h> | 12 | #include <linux/oprofile.h> |
13 | #include <linux/perf_event.h> | ||
13 | #include <linux/init.h> | 14 | #include <linux/init.h> |
14 | #include <linux/errno.h> | 15 | #include <linux/errno.h> |
15 | #include <linux/fs.h> | 16 | #include <linux/fs.h> |
@@ -67,6 +68,21 @@ module_param_call(cpu_type, set_cpu_type, NULL, NULL, 0); | |||
67 | MODULE_PARM_DESC(cpu_type, "Force legacy basic mode sampling" | 68 | MODULE_PARM_DESC(cpu_type, "Force legacy basic mode sampling" |
68 | "(report cpu_type \"timer\""); | 69 | "(report cpu_type \"timer\""); |
69 | 70 | ||
71 | static int __oprofile_hwsampler_start(void) | ||
72 | { | ||
73 | int retval; | ||
74 | |||
75 | retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks); | ||
76 | if (retval) | ||
77 | return retval; | ||
78 | |||
79 | retval = hwsampler_start_all(oprofile_hw_interval); | ||
80 | if (retval) | ||
81 | hwsampler_deallocate(); | ||
82 | |||
83 | return retval; | ||
84 | } | ||
85 | |||
70 | static int oprofile_hwsampler_start(void) | 86 | static int oprofile_hwsampler_start(void) |
71 | { | 87 | { |
72 | int retval; | 88 | int retval; |
@@ -76,13 +92,13 @@ static int oprofile_hwsampler_start(void) | |||
76 | if (!hwsampler_running) | 92 | if (!hwsampler_running) |
77 | return timer_ops.start(); | 93 | return timer_ops.start(); |
78 | 94 | ||
79 | retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks); | 95 | retval = perf_reserve_sampling(); |
80 | if (retval) | 96 | if (retval) |
81 | return retval; | 97 | return retval; |
82 | 98 | ||
83 | retval = hwsampler_start_all(oprofile_hw_interval); | 99 | retval = __oprofile_hwsampler_start(); |
84 | if (retval) | 100 | if (retval) |
85 | hwsampler_deallocate(); | 101 | perf_release_sampling(); |
86 | 102 | ||
87 | return retval; | 103 | return retval; |
88 | } | 104 | } |
@@ -96,6 +112,7 @@ static void oprofile_hwsampler_stop(void) | |||
96 | 112 | ||
97 | hwsampler_stop_all(); | 113 | hwsampler_stop_all(); |
98 | hwsampler_deallocate(); | 114 | hwsampler_deallocate(); |
115 | perf_release_sampling(); | ||
99 | return; | 116 | return; |
100 | } | 117 | } |
101 | 118 | ||