aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/perf_counter.h35
-rw-r--r--kernel/perf_counter.c38
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 {
160struct perf_counter_mmap_page { 160struct 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
1319static 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 */
1324void 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} 1351unlock:
1341
1342void 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