diff options
Diffstat (limited to 'kernel/perf_counter.c')
-rw-r--r-- | kernel/perf_counter.c | 38 |
1 files changed, 23 insertions, 15 deletions
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 | ||