diff options
-rw-r--r-- | include/linux/perf_counter.h | 35 | ||||
-rw-r--r-- | kernel/perf_counter.c | 38 |
2 files changed, 58 insertions, 15 deletions
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h index 0d833228eee5..8ac18852dcfe 100644 --- a/include/linux/perf_counter.h +++ b/include/linux/perf_counter.h | |||
@@ -160,10 +160,45 @@ struct perf_counter_hw_event { | |||
160 | struct perf_counter_mmap_page { | 160 | struct perf_counter_mmap_page { |
161 | __u32 version; /* version number of this structure */ | 161 | __u32 version; /* version number of this structure */ |
162 | __u32 compat_version; /* lowest version this is compat with */ | 162 | __u32 compat_version; /* lowest version this is compat with */ |
163 | |||
164 | /* | ||
165 | * Bits needed to read the hw counters in user-space. | ||
166 | * | ||
167 | * The index and offset should be read atomically using the seqlock: | ||
168 | * | ||
169 | * __u32 seq, index; | ||
170 | * __s64 offset; | ||
171 | * | ||
172 | * again: | ||
173 | * rmb(); | ||
174 | * seq = pc->lock; | ||
175 | * | ||
176 | * if (unlikely(seq & 1)) { | ||
177 | * cpu_relax(); | ||
178 | * goto again; | ||
179 | * } | ||
180 | * | ||
181 | * index = pc->index; | ||
182 | * offset = pc->offset; | ||
183 | * | ||
184 | * rmb(); | ||
185 | * if (pc->lock != seq) | ||
186 | * goto again; | ||
187 | * | ||
188 | * After this, index contains architecture specific counter index + 1, | ||
189 | * so that 0 means unavailable, offset contains the value to be added | ||
190 | * to the result of the raw timer read to obtain this counter's value. | ||
191 | */ | ||
163 | __u32 lock; /* seqlock for synchronization */ | 192 | __u32 lock; /* seqlock for synchronization */ |
164 | __u32 index; /* hardware counter identifier */ | 193 | __u32 index; /* hardware counter identifier */ |
165 | __s64 offset; /* add to hardware counter value */ | 194 | __s64 offset; /* add to hardware counter value */ |
166 | 195 | ||
196 | /* | ||
197 | * Control data for the mmap() data buffer. | ||
198 | * | ||
199 | * User-space reading this value should issue an rmb(), on SMP capable | ||
200 | * platforms, after reading this value -- see perf_counter_wakeup(). | ||
201 | */ | ||
167 | __u32 data_head; /* head in the data section */ | 202 | __u32 data_head; /* head in the data section */ |
168 | }; | 203 | }; |
169 | 204 | ||
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c index f70ff80e79d7..c95e92329b97 100644 --- a/kernel/perf_counter.c +++ b/kernel/perf_counter.c | |||
@@ -1316,10 +1316,22 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
1316 | return err; | 1316 | return err; |
1317 | } | 1317 | } |
1318 | 1318 | ||
1319 | static void __perf_counter_update_userpage(struct perf_counter *counter, | 1319 | /* |
1320 | struct perf_mmap_data *data) | 1320 | * Callers need to ensure there can be no nesting of this function, otherwise |
1321 | * the seqlock logic goes bad. We can not serialize this because the arch | ||
1322 | * code calls this from NMI context. | ||
1323 | */ | ||
1324 | void perf_counter_update_userpage(struct perf_counter *counter) | ||
1321 | { | 1325 | { |
1322 | struct perf_counter_mmap_page *userpg = data->user_page; | 1326 | struct perf_mmap_data *data; |
1327 | struct perf_counter_mmap_page *userpg; | ||
1328 | |||
1329 | rcu_read_lock(); | ||
1330 | data = rcu_dereference(counter->data); | ||
1331 | if (!data) | ||
1332 | goto unlock; | ||
1333 | |||
1334 | userpg = data->user_page; | ||
1323 | 1335 | ||
1324 | /* | 1336 | /* |
1325 | * Disable preemption so as to not let the corresponding user-space | 1337 | * Disable preemption so as to not let the corresponding user-space |
@@ -1333,20 +1345,10 @@ static void __perf_counter_update_userpage(struct perf_counter *counter, | |||
1333 | if (counter->state == PERF_COUNTER_STATE_ACTIVE) | 1345 | if (counter->state == PERF_COUNTER_STATE_ACTIVE) |
1334 | userpg->offset -= atomic64_read(&counter->hw.prev_count); | 1346 | userpg->offset -= atomic64_read(&counter->hw.prev_count); |
1335 | 1347 | ||
1336 | userpg->data_head = atomic_read(&data->head); | ||
1337 | smp_wmb(); | 1348 | smp_wmb(); |
1338 | ++userpg->lock; | 1349 | ++userpg->lock; |
1339 | preempt_enable(); | 1350 | preempt_enable(); |
1340 | } | 1351 | unlock: |
1341 | |||
1342 | void perf_counter_update_userpage(struct perf_counter *counter) | ||
1343 | { | ||
1344 | struct perf_mmap_data *data; | ||
1345 | |||
1346 | rcu_read_lock(); | ||
1347 | data = rcu_dereference(counter->data); | ||
1348 | if (data) | ||
1349 | __perf_counter_update_userpage(counter, data); | ||
1350 | rcu_read_unlock(); | 1352 | rcu_read_unlock(); |
1351 | } | 1353 | } |
1352 | 1354 | ||
@@ -1547,7 +1549,13 @@ void perf_counter_wakeup(struct perf_counter *counter) | |||
1547 | data = rcu_dereference(counter->data); | 1549 | data = rcu_dereference(counter->data); |
1548 | if (data) { | 1550 | if (data) { |
1549 | (void)atomic_xchg(&data->wakeup, POLL_IN); | 1551 | (void)atomic_xchg(&data->wakeup, POLL_IN); |
1550 | __perf_counter_update_userpage(counter, data); | 1552 | /* |
1553 | * Ensure all data writes are issued before updating the | ||
1554 | * user-space data head information. The matching rmb() | ||
1555 | * will be in userspace after reading this value. | ||
1556 | */ | ||
1557 | smp_wmb(); | ||
1558 | data->user_page->data_head = atomic_read(&data->head); | ||
1551 | } | 1559 | } |
1552 | rcu_read_unlock(); | 1560 | rcu_read_unlock(); |
1553 | 1561 | ||