aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2015-01-23 13:45:45 -0500
committerIngo Molnar <mingo@kernel.org>2015-02-25 07:53:33 -0500
commit35298e554c74b7849875e3676ba8eaf833c7b917 (patch)
tree19c1dbf828dc8a79dfd749870426f622613e6345 /arch/x86
parent4afbb24ce5e723c8a093a6674a3c33062175078a (diff)
perf/x86/intel: Implement LRU monitoring ID allocation for CQM
It's possible to run into issues with re-using unused monitoring IDs because there may be stale cachelines associated with that ID from a previous allocation. This can cause the LLC occupancy values to be inaccurate. To attempt to mitigate this problem we place the IDs on a least recently used list, essentially a FIFO. The basic idea is that the longer the time period between ID re-use the lower the probability that stale cachelines exist in the cache. 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-7-git-send-email-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_cqm.c100
1 files changed, 92 insertions, 8 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_intel_cqm.c b/arch/x86/kernel/cpu/perf_event_intel_cqm.c
index 05b4cd26f426..b5d9d746dbc0 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_cqm.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_cqm.c
@@ -25,7 +25,7 @@ struct intel_cqm_state {
25static DEFINE_PER_CPU(struct intel_cqm_state, cqm_state); 25static DEFINE_PER_CPU(struct intel_cqm_state, cqm_state);
26 26
27/* 27/*
28 * Protects cache_cgroups. 28 * Protects cache_cgroups and cqm_rmid_lru.
29 */ 29 */
30static DEFINE_MUTEX(cache_mutex); 30static DEFINE_MUTEX(cache_mutex);
31 31
@@ -64,36 +64,120 @@ static u64 __rmid_read(unsigned long rmid)
64 return val; 64 return val;
65} 65}
66 66
67static unsigned long *cqm_rmid_bitmap; 67struct cqm_rmid_entry {
68 u64 rmid;
69 struct list_head list;
70};
71
72/*
73 * A least recently used list of RMIDs.
74 *
75 * Oldest entry at the head, newest (most recently used) entry at the
76 * tail. This list is never traversed, it's only used to keep track of
77 * the lru order. That is, we only pick entries of the head or insert
78 * them on the tail.
79 *
80 * All entries on the list are 'free', and their RMIDs are not currently
81 * in use. To mark an RMID as in use, remove its entry from the lru
82 * list.
83 *
84 * This list is protected by cache_mutex.
85 */
86static LIST_HEAD(cqm_rmid_lru);
87
88/*
89 * We use a simple array of pointers so that we can lookup a struct
90 * cqm_rmid_entry in O(1). This alleviates the callers of __get_rmid()
91 * and __put_rmid() from having to worry about dealing with struct
92 * cqm_rmid_entry - they just deal with rmids, i.e. integers.
93 *
94 * Once this array is initialized it is read-only. No locks are required
95 * to access it.
96 *
97 * All entries for all RMIDs can be looked up in the this array at all
98 * times.
99 */
100static struct cqm_rmid_entry **cqm_rmid_ptrs;
101
102static inline struct cqm_rmid_entry *__rmid_entry(int rmid)
103{
104 struct cqm_rmid_entry *entry;
105
106 entry = cqm_rmid_ptrs[rmid];
107 WARN_ON(entry->rmid != rmid);
108
109 return entry;
110}
68 111
69/* 112/*
70 * Returns < 0 on fail. 113 * Returns < 0 on fail.
114 *
115 * We expect to be called with cache_mutex held.
71 */ 116 */
72static int __get_rmid(void) 117static int __get_rmid(void)
73{ 118{
74 return bitmap_find_free_region(cqm_rmid_bitmap, cqm_max_rmid, 0); 119 struct cqm_rmid_entry *entry;
120
121 lockdep_assert_held(&cache_mutex);
122
123 if (list_empty(&cqm_rmid_lru))
124 return -EAGAIN;
125
126 entry = list_first_entry(&cqm_rmid_lru, struct cqm_rmid_entry, list);
127 list_del(&entry->list);
128
129 return entry->rmid;
75} 130}
76 131
77static void __put_rmid(int rmid) 132static void __put_rmid(int rmid)
78{ 133{
79 bitmap_release_region(cqm_rmid_bitmap, rmid, 0); 134 struct cqm_rmid_entry *entry;
135
136 lockdep_assert_held(&cache_mutex);
137
138 entry = __rmid_entry(rmid);
139
140 list_add_tail(&entry->list, &cqm_rmid_lru);
80} 141}
81 142
82static int intel_cqm_setup_rmid_cache(void) 143static int intel_cqm_setup_rmid_cache(void)
83{ 144{
84 cqm_rmid_bitmap = kmalloc(sizeof(long) * BITS_TO_LONGS(cqm_max_rmid), GFP_KERNEL); 145 struct cqm_rmid_entry *entry;
85 if (!cqm_rmid_bitmap) 146 int r;
147
148 cqm_rmid_ptrs = kmalloc(sizeof(struct cqm_rmid_entry *) *
149 (cqm_max_rmid + 1), GFP_KERNEL);
150 if (!cqm_rmid_ptrs)
86 return -ENOMEM; 151 return -ENOMEM;
87 152
88 bitmap_zero(cqm_rmid_bitmap, cqm_max_rmid); 153 for (r = 0; r <= cqm_max_rmid; r++) {
154 struct cqm_rmid_entry *entry;
155
156 entry = kmalloc(sizeof(*entry), GFP_KERNEL);
157 if (!entry)
158 goto fail;
159
160 INIT_LIST_HEAD(&entry->list);
161 entry->rmid = r;
162 cqm_rmid_ptrs[r] = entry;
163
164 list_add_tail(&entry->list, &cqm_rmid_lru);
165 }
89 166
90 /* 167 /*
91 * RMID 0 is special and is always allocated. It's used for all 168 * RMID 0 is special and is always allocated. It's used for all
92 * tasks that are not monitored. 169 * tasks that are not monitored.
93 */ 170 */
94 bitmap_allocate_region(cqm_rmid_bitmap, 0, 0); 171 entry = __rmid_entry(0);
172 list_del(&entry->list);
95 173
96 return 0; 174 return 0;
175fail:
176 while (r--)
177 kfree(cqm_rmid_ptrs[r]);
178
179 kfree(cqm_rmid_ptrs);
180 return -ENOMEM;
97} 181}
98 182
99/* 183/*