Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc, perf/powerpc/hv-24x7: Use PMU_TXN_READ interface

The 24x7 counters in Powerpc allow monitoring a large number of counters
simultaneously. They also allow reading several counters in a single
HCALL so we can get a more consistent snapshot of the system.

Use the PMU's transaction interface to monitor and read several event
counters at once. The idea is that users can group several 24x7 events
into a single group of events. We use the following logic to submit
the group of events to the PMU and read the values:

pmu->start_txn() // Initialize before first event

for each event in group
pmu->read(event); // Queue each event to be read

pmu->commit_txn() // Read/update all queuedcounters

The ->commit_txn() also updates the event counts in the respective
perf_event objects. The perf subsystem can then directly get the
event counts from the perf_event and can avoid submitting a new
->read() request to the PMU.

Thanks to input from Peter Zijlstra.

Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.ibm.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Michael Ellerman <mpe@ellerman.id.au>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Link: http://lkml.kernel.org/r/1441336073-22750-10-git-send-email-sukadev@linux.vnet.ibm.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Sukadev Bhattiprolu and committed by
Ingo Molnar
88a48613 4a00c16e

+164 -2
+164 -2
arch/powerpc/perf/hv-24x7.c
··· 142 142 143 143 static struct kmem_cache *hv_page_cache; 144 144 145 + DEFINE_PER_CPU(int, hv_24x7_txn_flags); 146 + DEFINE_PER_CPU(int, hv_24x7_txn_err); 147 + 148 + struct hv_24x7_hw { 149 + struct perf_event *events[255]; 150 + }; 151 + 152 + DEFINE_PER_CPU(struct hv_24x7_hw, hv_24x7_hw); 153 + 145 154 /* 146 155 * request_buffer and result_buffer are not required to be 4k aligned, 147 156 * but are not allowed to cross any 4k boundary. Aligning them to 4k is ··· 1240 1231 static void h_24x7_event_read(struct perf_event *event) 1241 1232 { 1242 1233 u64 now; 1234 + struct hv_24x7_request_buffer *request_buffer; 1235 + struct hv_24x7_hw *h24x7hw; 1236 + int txn_flags; 1243 1237 1244 - now = h_24x7_get_value(event); 1245 - update_event_count(event, now); 1238 + txn_flags = __this_cpu_read(hv_24x7_txn_flags); 1239 + 1240 + /* 1241 + * If in a READ transaction, add this counter to the list of 1242 + * counters to read during the next HCALL (i.e commit_txn()). 1243 + * If not in a READ transaction, go ahead and make the HCALL 1244 + * to read this counter by itself. 1245 + */ 1246 + 1247 + if (txn_flags & PERF_PMU_TXN_READ) { 1248 + int i; 1249 + int ret; 1250 + 1251 + if (__this_cpu_read(hv_24x7_txn_err)) 1252 + return; 1253 + 1254 + request_buffer = (void *)get_cpu_var(hv_24x7_reqb); 1255 + 1256 + ret = add_event_to_24x7_request(event, request_buffer); 1257 + if (ret) { 1258 + __this_cpu_write(hv_24x7_txn_err, ret); 1259 + } else { 1260 + /* 1261 + * Assoicate the event with the HCALL request index, 1262 + * so ->commit_txn() can quickly find/update count. 1263 + */ 1264 + i = request_buffer->num_requests - 1; 1265 + 1266 + h24x7hw = &get_cpu_var(hv_24x7_hw); 1267 + h24x7hw->events[i] = event; 1268 + put_cpu_var(h24x7hw); 1269 + } 1270 + 1271 + put_cpu_var(hv_24x7_reqb); 1272 + } else { 1273 + now = h_24x7_get_value(event); 1274 + update_event_count(event, now); 1275 + } 1246 1276 } 1247 1277 1248 1278 static void h_24x7_event_start(struct perf_event *event, int flags) ··· 1303 1255 return 0; 1304 1256 } 1305 1257 1258 + /* 1259 + * 24x7 counters only support READ transactions. They are 1260 + * always counting and dont need/support ADD transactions. 1261 + * Cache the flags, but otherwise ignore transactions that 1262 + * are not PERF_PMU_TXN_READ. 1263 + */ 1264 + static void h_24x7_event_start_txn(struct pmu *pmu, unsigned int flags) 1265 + { 1266 + struct hv_24x7_request_buffer *request_buffer; 1267 + struct hv_24x7_data_result_buffer *result_buffer; 1268 + 1269 + /* We should not be called if we are already in a txn */ 1270 + WARN_ON_ONCE(__this_cpu_read(hv_24x7_txn_flags)); 1271 + 1272 + __this_cpu_write(hv_24x7_txn_flags, flags); 1273 + if (flags & ~PERF_PMU_TXN_READ) 1274 + return; 1275 + 1276 + request_buffer = (void *)get_cpu_var(hv_24x7_reqb); 1277 + result_buffer = (void *)get_cpu_var(hv_24x7_resb); 1278 + 1279 + init_24x7_request(request_buffer, result_buffer); 1280 + 1281 + put_cpu_var(hv_24x7_resb); 1282 + put_cpu_var(hv_24x7_reqb); 1283 + } 1284 + 1285 + /* 1286 + * Clean up transaction state. 1287 + * 1288 + * NOTE: Ignore state of request and result buffers for now. 1289 + * We will initialize them during the next read/txn. 1290 + */ 1291 + static void reset_txn(void) 1292 + { 1293 + __this_cpu_write(hv_24x7_txn_flags, 0); 1294 + __this_cpu_write(hv_24x7_txn_err, 0); 1295 + } 1296 + 1297 + /* 1298 + * 24x7 counters only support READ transactions. They are always counting 1299 + * and dont need/support ADD transactions. Clear ->txn_flags but otherwise 1300 + * ignore transactions that are not of type PERF_PMU_TXN_READ. 1301 + * 1302 + * For READ transactions, submit all pending 24x7 requests (i.e requests 1303 + * that were queued by h_24x7_event_read()), to the hypervisor and update 1304 + * the event counts. 1305 + */ 1306 + static int h_24x7_event_commit_txn(struct pmu *pmu) 1307 + { 1308 + struct hv_24x7_request_buffer *request_buffer; 1309 + struct hv_24x7_data_result_buffer *result_buffer; 1310 + struct hv_24x7_result *resb; 1311 + struct perf_event *event; 1312 + u64 count; 1313 + int i, ret, txn_flags; 1314 + struct hv_24x7_hw *h24x7hw; 1315 + 1316 + txn_flags = __this_cpu_read(hv_24x7_txn_flags); 1317 + WARN_ON_ONCE(!txn_flags); 1318 + 1319 + ret = 0; 1320 + if (txn_flags & ~PERF_PMU_TXN_READ) 1321 + goto out; 1322 + 1323 + ret = __this_cpu_read(hv_24x7_txn_err); 1324 + if (ret) 1325 + goto out; 1326 + 1327 + request_buffer = (void *)get_cpu_var(hv_24x7_reqb); 1328 + result_buffer = (void *)get_cpu_var(hv_24x7_resb); 1329 + 1330 + ret = make_24x7_request(request_buffer, result_buffer); 1331 + if (ret) { 1332 + log_24x7_hcall(request_buffer, result_buffer, ret); 1333 + goto put_reqb; 1334 + } 1335 + 1336 + h24x7hw = &get_cpu_var(hv_24x7_hw); 1337 + 1338 + /* Update event counts from hcall */ 1339 + for (i = 0; i < request_buffer->num_requests; i++) { 1340 + resb = &result_buffer->results[i]; 1341 + count = be64_to_cpu(resb->elements[0].element_data[0]); 1342 + event = h24x7hw->events[i]; 1343 + h24x7hw->events[i] = NULL; 1344 + update_event_count(event, count); 1345 + } 1346 + 1347 + put_cpu_var(hv_24x7_hw); 1348 + 1349 + put_reqb: 1350 + put_cpu_var(hv_24x7_resb); 1351 + put_cpu_var(hv_24x7_reqb); 1352 + out: 1353 + reset_txn(); 1354 + return ret; 1355 + } 1356 + 1357 + /* 1358 + * 24x7 counters only support READ transactions. They are always counting 1359 + * and dont need/support ADD transactions. However, regardless of type 1360 + * of transaction, all we need to do is cleanup, so we don't have to check 1361 + * the type of transaction. 1362 + */ 1363 + static void h_24x7_event_cancel_txn(struct pmu *pmu) 1364 + { 1365 + WARN_ON_ONCE(!__this_cpu_read(hv_24x7_txn_flags)); 1366 + reset_txn(); 1367 + } 1368 + 1306 1369 static struct pmu h_24x7_pmu = { 1307 1370 .task_ctx_nr = perf_invalid_context, 1308 1371 ··· 1425 1266 .start = h_24x7_event_start, 1426 1267 .stop = h_24x7_event_stop, 1427 1268 .read = h_24x7_event_read, 1269 + .start_txn = h_24x7_event_start_txn, 1270 + .commit_txn = h_24x7_event_commit_txn, 1271 + .cancel_txn = h_24x7_event_cancel_txn, 1428 1272 }; 1429 1273 1430 1274 static int hv_24x7_init(void)