diff options
author | Matt Fleming <matt.fleming@intel.com> | 2015-01-23 13:45:45 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-02-25 07:53:33 -0500 |
commit | 35298e554c74b7849875e3676ba8eaf833c7b917 (patch) | |
tree | 19c1dbf828dc8a79dfd749870426f622613e6345 /arch/x86 | |
parent | 4afbb24ce5e723c8a093a6674a3c33062175078a (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.c | 100 |
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 { | |||
25 | static DEFINE_PER_CPU(struct intel_cqm_state, cqm_state); | 25 | static 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 | */ |
30 | static DEFINE_MUTEX(cache_mutex); | 30 | static 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 | ||
67 | static unsigned long *cqm_rmid_bitmap; | 67 | struct 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 | */ | ||
86 | static 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 | */ | ||
100 | static struct cqm_rmid_entry **cqm_rmid_ptrs; | ||
101 | |||
102 | static 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 | */ |
72 | static int __get_rmid(void) | 117 | static 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 | ||
77 | static void __put_rmid(int rmid) | 132 | static 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 | ||
82 | static int intel_cqm_setup_rmid_cache(void) | 143 | static 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; |
175 | fail: | ||
176 | while (r--) | ||
177 | kfree(cqm_rmid_ptrs[r]); | ||
178 | |||
179 | kfree(cqm_rmid_ptrs); | ||
180 | return -ENOMEM; | ||
97 | } | 181 | } |
98 | 182 | ||
99 | /* | 183 | /* |