aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2015-01-23 13:45:44 -0500
committerIngo Molnar <mingo@kernel.org>2015-02-25 07:53:32 -0500
commit4afbb24ce5e723c8a093a6674a3c33062175078a (patch)
tree0ac9eea7a69bbb6085d5659d42308c005d6df252
parentcbc82b17263877ea5d21e84c58ce03f0292458a1 (diff)
perf/x86/intel: Add Intel Cache QoS Monitoring support
Future Intel Xeon processors support a Cache QoS Monitoring feature that allows tracking of the LLC occupancy for a task or task group, i.e. the amount of data in pulled into the LLC for the task (group). Currently the PMU only supports per-cpu events. We create an event for each cpu and read out all the LLC occupancy values. Because this results in duplicate values being written out to userspace, we also export a .per-pkg event file so that the perf tools only accumulate values for one cpu per package. Signed-off-by: Matt Fleming <matt.fleming@intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Arnaldo Carvalho de Melo <acme@kernel.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Kanaka Juvva <kanaka.d.juvva@intel.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Vikas Shivappa <vikas.shivappa@linux.intel.com> Link: http://lkml.kernel.org/r/1422038748-21397-6-git-send-email-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_cqm.c530
-rw-r--r--include/linux/perf_event.h7
2 files changed, 537 insertions, 0 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_intel_cqm.c b/arch/x86/kernel/cpu/perf_event_intel_cqm.c
new file mode 100644
index 000000000000..05b4cd26f426
--- /dev/null
+++ b/arch/x86/kernel/cpu/perf_event_intel_cqm.c
@@ -0,0 +1,530 @@
1/*
2 * Intel Cache Quality-of-Service Monitoring (CQM) support.
3 *
4 * Based very, very heavily on work by Peter Zijlstra.
5 */
6
7#include <linux/perf_event.h>
8#include <linux/slab.h>
9#include <asm/cpu_device_id.h>
10#include "perf_event.h"
11
12#define MSR_IA32_PQR_ASSOC 0x0c8f
13#define MSR_IA32_QM_CTR 0x0c8e
14#define MSR_IA32_QM_EVTSEL 0x0c8d
15
16static unsigned int cqm_max_rmid = -1;
17static unsigned int cqm_l3_scale; /* supposedly cacheline size */
18
19struct intel_cqm_state {
20 raw_spinlock_t lock;
21 int rmid;
22 int cnt;
23};
24
25static DEFINE_PER_CPU(struct intel_cqm_state, cqm_state);
26
27/*
28 * Protects cache_cgroups.
29 */
30static DEFINE_MUTEX(cache_mutex);
31
32/*
33 * Groups of events that have the same target(s), one RMID per group.
34 */
35static LIST_HEAD(cache_groups);
36
37/*
38 * Mask of CPUs for reading CQM values. We only need one per-socket.
39 */
40static cpumask_t cqm_cpumask;
41
42#define RMID_VAL_ERROR (1ULL << 63)
43#define RMID_VAL_UNAVAIL (1ULL << 62)
44
45#define QOS_L3_OCCUP_EVENT_ID (1 << 0)
46
47#define QOS_EVENT_MASK QOS_L3_OCCUP_EVENT_ID
48
49static u64 __rmid_read(unsigned long rmid)
50{
51 u64 val;
52
53 /*
54 * Ignore the SDM, this thing is _NOTHING_ like a regular perfcnt,
55 * it just says that to increase confusion.
56 */
57 wrmsr(MSR_IA32_QM_EVTSEL, QOS_L3_OCCUP_EVENT_ID, rmid);
58 rdmsrl(MSR_IA32_QM_CTR, val);
59
60 /*
61 * Aside from the ERROR and UNAVAIL bits, assume this thing returns
62 * the number of cachelines tagged with @rmid.
63 */
64 return val;
65}
66
67static unsigned long *cqm_rmid_bitmap;
68
69/*
70 * Returns < 0 on fail.
71 */
72static int __get_rmid(void)
73{
74 return bitmap_find_free_region(cqm_rmid_bitmap, cqm_max_rmid, 0);
75}
76
77static void __put_rmid(int rmid)
78{
79 bitmap_release_region(cqm_rmid_bitmap, rmid, 0);
80}
81
82static int intel_cqm_setup_rmid_cache(void)
83{
84 cqm_rmid_bitmap = kmalloc(sizeof(long) * BITS_TO_LONGS(cqm_max_rmid), GFP_KERNEL);
85 if (!cqm_rmid_bitmap)
86 return -ENOMEM;
87
88 bitmap_zero(cqm_rmid_bitmap, cqm_max_rmid);
89
90 /*
91 * RMID 0 is special and is always allocated. It's used for all
92 * tasks that are not monitored.
93 */
94 bitmap_allocate_region(cqm_rmid_bitmap, 0, 0);
95
96 return 0;
97}
98
99/*
100 * Determine if @a and @b measure the same set of tasks.
101 */
102static bool __match_event(struct perf_event *a, struct perf_event *b)
103{
104 if ((a->attach_state & PERF_ATTACH_TASK) !=
105 (b->attach_state & PERF_ATTACH_TASK))
106 return false;
107
108 /* not task */
109
110 return true; /* if not task, we're machine wide */
111}
112
113/*
114 * Determine if @a's tasks intersect with @b's tasks
115 */
116static bool __conflict_event(struct perf_event *a, struct perf_event *b)
117{
118 /*
119 * If one of them is not a task, same story as above with cgroups.
120 */
121 if (!(a->attach_state & PERF_ATTACH_TASK) ||
122 !(b->attach_state & PERF_ATTACH_TASK))
123 return true;
124
125 /*
126 * Must be non-overlapping.
127 */
128 return false;
129}
130
131/*
132 * Find a group and setup RMID.
133 *
134 * If we're part of a group, we use the group's RMID.
135 */
136static int intel_cqm_setup_event(struct perf_event *event,
137 struct perf_event **group)
138{
139 struct perf_event *iter;
140 int rmid;
141
142 list_for_each_entry(iter, &cache_groups, hw.cqm_groups_entry) {
143 if (__match_event(iter, event)) {
144 /* All tasks in a group share an RMID */
145 event->hw.cqm_rmid = iter->hw.cqm_rmid;
146 *group = iter;
147 return 0;
148 }
149
150 if (__conflict_event(iter, event))
151 return -EBUSY;
152 }
153
154 rmid = __get_rmid();
155 if (rmid < 0)
156 return rmid;
157
158 event->hw.cqm_rmid = rmid;
159 return 0;
160}
161
162static void intel_cqm_event_read(struct perf_event *event)
163{
164 unsigned long rmid = event->hw.cqm_rmid;
165 u64 val;
166
167 val = __rmid_read(rmid);
168
169 /*
170 * Ignore this reading on error states and do not update the value.
171 */
172 if (val & (RMID_VAL_ERROR | RMID_VAL_UNAVAIL))
173 return;
174
175 local64_set(&event->count, val);
176}
177
178static void intel_cqm_event_start(struct perf_event *event, int mode)
179{
180 struct intel_cqm_state *state = this_cpu_ptr(&cqm_state);
181 unsigned long rmid = event->hw.cqm_rmid;
182 unsigned long flags;
183
184 if (!(event->hw.cqm_state & PERF_HES_STOPPED))
185 return;
186
187 event->hw.cqm_state &= ~PERF_HES_STOPPED;