diff options
| author | Heinz Graalfs <graalfs@linux.vnet.ibm.com> | 2011-01-21 05:06:52 -0500 |
|---|---|---|
| committer | Robert Richter <robert.richter@amd.com> | 2011-02-15 05:08:19 -0500 |
| commit | ec6a3df1c008d9e8664e53b0363f6847c5c0dc3f (patch) | |
| tree | 29026a43fe081f6fb28a2417f582038b89056376 | |
| parent | 54ebbe7ba51d97a28a9a406203d171d61858e4b9 (diff) | |
oprofile, s390: Add support for hardware based sampling on System z processors
This adds support for hardware based sampling on System z processors
(models z10 and up).
System z's hardware sampling is described in detail in:
SA23-2260-01 "The Load-Program-Parameter and CPU-Measurement Facilities"
The patch introduces
- support for System z's hardware sampler in OProfile's kernel module
- it adds functions that control all hardware sampling related
operations as:
- checking if hardware sampling feature is available, i.e.: on
System z models z10 and up, in LPAR mode only, and authorised
during LPAR activation
- allocating memory for the hardware sampling feature
- starting/stopping hardware sampling
All functions required to start and stop hardware sampling have to be
invoked by the oprofile kernel module as provided by the other patches
of this patch set.
In case hardware based sampling cannot be setup standard timer based
sampling is used by OProfile.
Applied with following changes:
* enable compilation in Makefile
Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Signed-off-by: Maran Pakkirisamy <maranp@linux.vnet.ibm.com>
Signed-off-by: Heinz Graalfs <graalfs@linux.vnet.ibm.com>
Acked-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Robert Richter <robert.richter@amd.com>
| -rw-r--r-- | arch/Kconfig | 3 | ||||
| -rw-r--r-- | arch/s390/Kconfig | 1 | ||||
| -rw-r--r-- | arch/s390/oprofile/Makefile | 2 | ||||
| -rw-r--r-- | arch/s390/oprofile/hwsampler.c | 1256 | ||||
| -rw-r--r-- | arch/s390/oprofile/hwsampler.h | 113 |
5 files changed, 1374 insertions, 1 deletions
diff --git a/arch/Kconfig b/arch/Kconfig index f78c2be4242b..43abf3c6da8e 100644 --- a/arch/Kconfig +++ b/arch/Kconfig | |||
| @@ -30,6 +30,9 @@ config OPROFILE_EVENT_MULTIPLEX | |||
| 30 | config HAVE_OPROFILE | 30 | config HAVE_OPROFILE |
| 31 | bool | 31 | bool |
| 32 | 32 | ||
| 33 | config HAVE_HWSAMPLER | ||
| 34 | bool | ||
| 35 | |||
| 33 | config KPROBES | 36 | config KPROBES |
| 34 | bool "Kprobes" | 37 | bool "Kprobes" |
| 35 | depends on MODULES | 38 | depends on MODULES |
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index ff19efdf6fef..0cf20adfbb45 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig | |||
| @@ -115,6 +115,7 @@ config S390 | |||
| 115 | select ARCH_INLINE_WRITE_UNLOCK_BH | 115 | select ARCH_INLINE_WRITE_UNLOCK_BH |
| 116 | select ARCH_INLINE_WRITE_UNLOCK_IRQ | 116 | select ARCH_INLINE_WRITE_UNLOCK_IRQ |
| 117 | select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE | 117 | select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE |
| 118 | select HAVE_HWSAMPLER | ||
| 118 | 119 | ||
| 119 | config SCHED_OMIT_FRAME_POINTER | 120 | config SCHED_OMIT_FRAME_POINTER |
| 120 | def_bool y | 121 | def_bool y |
diff --git a/arch/s390/oprofile/Makefile b/arch/s390/oprofile/Makefile index 537b2d840e69..d698cddcfbdd 100644 --- a/arch/s390/oprofile/Makefile +++ b/arch/s390/oprofile/Makefile | |||
| @@ -6,4 +6,4 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ | |||
| 6 | oprofilefs.o oprofile_stats.o \ | 6 | oprofilefs.o oprofile_stats.o \ |
| 7 | timer_int.o ) | 7 | timer_int.o ) |
| 8 | 8 | ||
| 9 | oprofile-y := $(DRIVER_OBJS) init.o backtrace.o | 9 | oprofile-y := $(DRIVER_OBJS) init.o backtrace.o hwsampler.o |
diff --git a/arch/s390/oprofile/hwsampler.c b/arch/s390/oprofile/hwsampler.c new file mode 100644 index 000000000000..ab3f770e35ba --- /dev/null +++ b/arch/s390/oprofile/hwsampler.c | |||
| @@ -0,0 +1,1256 @@ | |||
| 1 | /** | ||
| 2 | * arch/s390/oprofile/hwsampler.c | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2010 | ||
| 5 | * Author: Heinz Graalfs <graalfs@de.ibm.com> | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <linux/kernel.h> | ||
| 9 | #include <linux/module.h> | ||
| 10 | #include <linux/smp.h> | ||
| 11 | #include <linux/errno.h> | ||
| 12 | #include <linux/workqueue.h> | ||
| 13 | #include <linux/interrupt.h> | ||
| 14 | #include <linux/notifier.h> | ||
| 15 | #include <linux/cpu.h> | ||
| 16 | #include <linux/semaphore.h> | ||
| 17 | #include <linux/oom.h> | ||
| 18 | #include <linux/oprofile.h> | ||
| 19 | |||
| 20 | #include <asm/lowcore.h> | ||
| 21 | #include <asm/s390_ext.h> | ||
| 22 | |||
| 23 | #include "hwsampler.h" | ||
| 24 | |||
| 25 | #define MAX_NUM_SDB 511 | ||
| 26 | #define MIN_NUM_SDB 1 | ||
| 27 | |||
| 28 | #define ALERT_REQ_MASK 0x4000000000000000ul | ||
| 29 | #define BUFFER_FULL_MASK 0x8000000000000000ul | ||
| 30 | |||
| 31 | #define EI_IEA (1 << 31) /* invalid entry address */ | ||
| 32 | #define EI_ISE (1 << 30) /* incorrect SDBT entry */ | ||
| 33 | #define EI_PRA (1 << 29) /* program request alert */ | ||
| 34 | #define EI_SACA (1 << 23) /* sampler authorization change alert */ | ||
| 35 | #define EI_LSDA (1 << 22) /* loss of sample data alert */ | ||
| 36 | |||
| 37 | DECLARE_PER_CPU(struct hws_cpu_buffer, sampler_cpu_buffer); | ||
| 38 | |||
| 39 | struct hws_execute_parms { | ||
| 40 | void *buffer; | ||
| 41 | signed int rc; | ||
| 42 | }; | ||
| 43 | |||
| 44 | DEFINE_PER_CPU(struct hws_cpu_buffer, sampler_cpu_buffer); | ||
| 45 | EXPORT_PER_CPU_SYMBOL(sampler_cpu_buffer); | ||
| 46 | |||
| 47 | static DEFINE_MUTEX(hws_sem); | ||
| 48 | static DEFINE_MUTEX(hws_sem_oom); | ||
| 49 | |||
| 50 | static unsigned char hws_flush_all; | ||
| 51 | static unsigned int hws_oom; | ||
| 52 | static struct workqueue_struct *hws_wq; | ||
| 53 | |||
| 54 | static unsigned int hws_state; | ||
| 55 | enum { | ||
| 56 | HWS_INIT = 1, | ||
| 57 | HWS_DEALLOCATED, | ||
| 58 | HWS_STOPPED, | ||
| 59 | HWS_STARTED, | ||
| 60 | HWS_STOPPING }; | ||
| 61 | |||
| 62 | /* set to 1 if called by kernel during memory allocation */ | ||
| 63 | static unsigned char oom_killer_was_active; | ||
| 64 | /* size of SDBT and SDB as of allocate API */ | ||
| 65 | static unsigned long num_sdbt = 100; | ||
| 66 | static unsigned long num_sdb = 511; | ||
| 67 | /* sampling interval (machine cycles) */ | ||
| 68 | static unsigned long interval; | ||
| 69 | |||
| 70 | static unsigned long min_sampler_rate; | ||
| 71 | static unsigned long max_sampler_rate; | ||
| 72 | |||
| 73 | static int ssctl(void *buffer) | ||
| 74 | { | ||
| 75 | int cc; | ||
| 76 | |||
| 77 | /* set in order to detect a program check */ | ||
| 78 | cc = 1; | ||
| 79 | |||
| 80 | asm volatile( | ||
| 81 | "0: .insn s,0xB2870000,0(%1)\n" | ||
| 82 | "1: ipm %0\n" | ||
| 83 | " srl %0,28\n" | ||
| 84 | "2:\n" | ||
| 85 | EX_TABLE(0b, 2b) EX_TABLE(1b, 2b) | ||
| 86 | : "+d" (cc), "+a" (buffer) | ||
| 87 | : "m" (*((struct hws_ssctl_request_block *)buffer)) | ||
| 88 | : "cc", "memory"); | ||
| 89 | |||
| 90 | return cc ? -EINVAL : 0 ; | ||
| 91 | } | ||
| 92 | |||
| 93 | static int qsi(void *buffer) | ||
| 94 | { | ||
| 95 | int cc; | ||
| 96 | cc = 1; | ||
| 97 | |||
| 98 | asm volatile( | ||
| 99 | "0: .insn s,0xB2860000,0(%1)\n" | ||
| 100 | "1: lhi %0,0\n" | ||
| 101 | "2:\n" | ||
| 102 | EX_TABLE(0b, 2b) EX_TABLE(1b, 2b) | ||
| 103 | : "=d" (cc), "+a" (buffer) | ||
| 104 | : "m" (*((struct hws_qsi_info_block *)buffer)) | ||
| 105 | : "cc", "memory"); | ||
| 106 | |||
| 107 | return cc ? -EINVAL : 0; | ||
| 108 | } | ||
| 109 | |||
| 110 | static void execute_qsi(void *parms) | ||
| 111 | { | ||
| 112 | struct hws_execute_parms *ep = parms; | ||
| 113 | |||
| 114 | ep->rc = qsi(ep->buffer); | ||
| 115 | } | ||
| 116 | |||
| 117 | static void execute_ssctl(void *parms) | ||
| 118 | { | ||
| 119 | struct hws_execute_parms *ep = parms; | ||
| 120 | |||
| 121 | ep->rc = ssctl(ep->buffer); | ||
| 122 | } | ||
| 123 | |||
| 124 | static int smp_ctl_ssctl_stop(int cpu) | ||
| 125 | { | ||
| 126 | int rc; | ||
| 127 | struct hws_execute_parms ep; | ||
| 128 | struct hws_cpu_buffer *cb; | ||
| 129 | |||
| 130 | cb = &per_cpu(sampler_cpu_buffer, cpu); | ||
| 131 | |||
| 132 | cb->ssctl.es = 0; | ||
| 133 | cb->ssctl.cs = 0; | ||
| 134 | |||
| 135 | ep.buffer = &cb->ssctl; | ||
| 136 | smp_call_function_single(cpu, execute_ssctl, &ep, 1); | ||
| 137 | rc = ep.rc; | ||
| 138 | if (rc) { | ||
| 139 | printk(KERN_ERR "hwsampler: CPU %d CPUMF SSCTL failed.\n", cpu); | ||
| 140 | dump_stack(); | ||
| 141 | } | ||
| 142 | |||
| 143 | ep.buffer = &cb->qsi; | ||
| 144 | smp_call_function_single(cpu, execute_qsi, &ep, 1); | ||
| 145 | |||
| 146 | if (cb->qsi.es || cb->qsi.cs) { | ||
| 147 | printk(KERN_EMERG "CPUMF sampling did not stop properly.\n"); | ||
| 148 | dump_stack(); | ||
| 149 | } | ||
| 150 | |||
| 151 | return rc; | ||
| 152 | } | ||
| 153 | |||
| 154 | static int smp_ctl_ssctl_deactivate(int cpu) | ||
| 155 | { | ||
| 156 | |||
