.. | .. |
---|
163 | 163 | |
---|
164 | 164 | static int alloc_sample_data_block(unsigned long *sdbt, gfp_t gfp_flags) |
---|
165 | 165 | { |
---|
166 | | - unsigned long sdb, *trailer; |
---|
| 166 | + struct hws_trailer_entry *te; |
---|
| 167 | + unsigned long sdb; |
---|
167 | 168 | |
---|
168 | 169 | /* Allocate and initialize sample-data-block */ |
---|
169 | 170 | sdb = get_zeroed_page(gfp_flags); |
---|
170 | 171 | if (!sdb) |
---|
171 | 172 | return -ENOMEM; |
---|
172 | | - trailer = trailer_entry_ptr(sdb); |
---|
173 | | - *trailer = SDB_TE_ALERT_REQ_MASK; |
---|
| 173 | + te = (struct hws_trailer_entry *)trailer_entry_ptr(sdb); |
---|
| 174 | + te->header.a = 1; |
---|
174 | 175 | |
---|
175 | 176 | /* Link SDB into the sample-data-block-table */ |
---|
176 | 177 | *sdbt = sdb; |
---|
.. | .. |
---|
1206 | 1207 | "%s: Found unknown" |
---|
1207 | 1208 | " sampling data entry: te->f %i" |
---|
1208 | 1209 | " basic.def %#4x (%p)\n", __func__, |
---|
1209 | | - te->f, sample->def, sample); |
---|
| 1210 | + te->header.f, sample->def, sample); |
---|
1210 | 1211 | /* Sample slot is not yet written or other record. |
---|
1211 | 1212 | * |
---|
1212 | 1213 | * This condition can occur if the buffer was reused |
---|
.. | .. |
---|
1217 | 1218 | * that are not full. Stop processing if the first |
---|
1218 | 1219 | * invalid format was detected. |
---|
1219 | 1220 | */ |
---|
1220 | | - if (!te->f) |
---|
| 1221 | + if (!te->header.f) |
---|
1221 | 1222 | break; |
---|
1222 | 1223 | } |
---|
1223 | 1224 | |
---|
.. | .. |
---|
1225 | 1226 | sample->def = 0; |
---|
1226 | 1227 | sample++; |
---|
1227 | 1228 | } |
---|
| 1229 | +} |
---|
| 1230 | + |
---|
| 1231 | +static inline __uint128_t __cdsg(__uint128_t *ptr, __uint128_t old, __uint128_t new) |
---|
| 1232 | +{ |
---|
| 1233 | + asm volatile( |
---|
| 1234 | + " cdsg %[old],%[new],%[ptr]\n" |
---|
| 1235 | + : [old] "+d" (old), [ptr] "+QS" (*ptr) |
---|
| 1236 | + : [new] "d" (new) |
---|
| 1237 | + : "memory", "cc"); |
---|
| 1238 | + return old; |
---|
1228 | 1239 | } |
---|
1229 | 1240 | |
---|
1230 | 1241 | /* hw_perf_event_update() - Process sampling buffer |
---|
.. | .. |
---|
1243 | 1254 | */ |
---|
1244 | 1255 | static void hw_perf_event_update(struct perf_event *event, int flush_all) |
---|
1245 | 1256 | { |
---|
| 1257 | + unsigned long long event_overflow, sampl_overflow, num_sdb; |
---|
| 1258 | + union hws_trailer_header old, prev, new; |
---|
1246 | 1259 | struct hw_perf_event *hwc = &event->hw; |
---|
1247 | 1260 | struct hws_trailer_entry *te; |
---|
1248 | 1261 | unsigned long *sdbt; |
---|
1249 | | - unsigned long long event_overflow, sampl_overflow, num_sdb, te_flags; |
---|
1250 | 1262 | int done; |
---|
1251 | 1263 | |
---|
1252 | 1264 | /* |
---|
.. | .. |
---|
1266 | 1278 | te = (struct hws_trailer_entry *) trailer_entry_ptr(*sdbt); |
---|
1267 | 1279 | |
---|
1268 | 1280 | /* Leave loop if no more work to do (block full indicator) */ |
---|
1269 | | - if (!te->f) { |
---|
| 1281 | + if (!te->header.f) { |
---|
1270 | 1282 | done = 1; |
---|
1271 | 1283 | if (!flush_all) |
---|
1272 | 1284 | break; |
---|
1273 | 1285 | } |
---|
1274 | 1286 | |
---|
1275 | 1287 | /* Check the sample overflow count */ |
---|
1276 | | - if (te->overflow) |
---|
| 1288 | + if (te->header.overflow) |
---|
1277 | 1289 | /* Account sample overflows and, if a particular limit |
---|
1278 | 1290 | * is reached, extend the sampling buffer. |
---|
1279 | 1291 | * For details, see sfb_account_overflows(). |
---|
1280 | 1292 | */ |
---|
1281 | | - sampl_overflow += te->overflow; |
---|
| 1293 | + sampl_overflow += te->header.overflow; |
---|
1282 | 1294 | |
---|
1283 | 1295 | /* Timestamps are valid for full sample-data-blocks only */ |
---|
1284 | 1296 | debug_sprintf_event(sfdbg, 6, "%s: sdbt %#lx " |
---|
1285 | 1297 | "overflow %llu timestamp %#llx\n", |
---|
1286 | | - __func__, (unsigned long)sdbt, te->overflow, |
---|
1287 | | - (te->f) ? trailer_timestamp(te) : 0ULL); |
---|
| 1298 | + __func__, (unsigned long)sdbt, te->header.overflow, |
---|
| 1299 | + (te->header.f) ? trailer_timestamp(te) : 0ULL); |
---|
1288 | 1300 | |
---|
1289 | 1301 | /* Collect all samples from a single sample-data-block and |
---|
1290 | 1302 | * flag if an (perf) event overflow happened. If so, the PMU |
---|
.. | .. |
---|
1294 | 1306 | num_sdb++; |
---|
1295 | 1307 | |
---|
1296 | 1308 | /* Reset trailer (using compare-double-and-swap) */ |
---|
| 1309 | + /* READ_ONCE() 16 byte header */ |
---|
| 1310 | + prev.val = __cdsg(&te->header.val, 0, 0); |
---|
1297 | 1311 | do { |
---|
1298 | | - te_flags = te->flags & ~SDB_TE_BUFFER_FULL_MASK; |
---|
1299 | | - te_flags |= SDB_TE_ALERT_REQ_MASK; |
---|
1300 | | - } while (!cmpxchg_double(&te->flags, &te->overflow, |
---|
1301 | | - te->flags, te->overflow, |
---|
1302 | | - te_flags, 0ULL)); |
---|
| 1312 | + old.val = prev.val; |
---|
| 1313 | + new.val = prev.val; |
---|
| 1314 | + new.f = 0; |
---|
| 1315 | + new.a = 1; |
---|
| 1316 | + new.overflow = 0; |
---|
| 1317 | + prev.val = __cdsg(&te->header.val, old.val, new.val); |
---|
| 1318 | + } while (prev.val != old.val); |
---|
1303 | 1319 | |
---|
1304 | 1320 | /* Advance to next sample-data-block */ |
---|
1305 | 1321 | sdbt++; |
---|
.. | .. |
---|
1384 | 1400 | range_scan = AUX_SDB_NUM_ALERT(aux); |
---|
1385 | 1401 | for (i = 0, idx = aux->head; i < range_scan; i++, idx++) { |
---|
1386 | 1402 | te = aux_sdb_trailer(aux, idx); |
---|
1387 | | - if (!(te->flags & SDB_TE_BUFFER_FULL_MASK)) |
---|
| 1403 | + if (!te->header.f) |
---|
1388 | 1404 | break; |
---|
1389 | 1405 | } |
---|
1390 | 1406 | /* i is num of SDBs which are full */ |
---|
.. | .. |
---|
1392 | 1408 | |
---|
1393 | 1409 | /* Remove alert indicators in the buffer */ |
---|
1394 | 1410 | te = aux_sdb_trailer(aux, aux->alert_mark); |
---|
1395 | | - te->flags &= ~SDB_TE_ALERT_REQ_MASK; |
---|
| 1411 | + te->header.a = 0; |
---|
1396 | 1412 | |
---|
1397 | 1413 | debug_sprintf_event(sfdbg, 6, "%s: SDBs %ld range %ld head %ld\n", |
---|
1398 | 1414 | __func__, i, range_scan, aux->head); |
---|
.. | .. |
---|
1437 | 1453 | idx = aux->empty_mark + 1; |
---|
1438 | 1454 | for (i = 0; i < range_scan; i++, idx++) { |
---|
1439 | 1455 | te = aux_sdb_trailer(aux, idx); |
---|
1440 | | - te->flags &= ~(SDB_TE_BUFFER_FULL_MASK | |
---|
1441 | | - SDB_TE_ALERT_REQ_MASK); |
---|
1442 | | - te->overflow = 0; |
---|
| 1456 | + te->header.f = 0; |
---|
| 1457 | + te->header.a = 0; |
---|
| 1458 | + te->header.overflow = 0; |
---|
1443 | 1459 | } |
---|
1444 | 1460 | /* Save the position of empty SDBs */ |
---|
1445 | 1461 | aux->empty_mark = aux->head + range - 1; |
---|
.. | .. |
---|
1448 | 1464 | /* Set alert indicator */ |
---|
1449 | 1465 | aux->alert_mark = aux->head + range/2 - 1; |
---|
1450 | 1466 | te = aux_sdb_trailer(aux, aux->alert_mark); |
---|
1451 | | - te->flags = te->flags | SDB_TE_ALERT_REQ_MASK; |
---|
| 1467 | + te->header.a = 1; |
---|
1452 | 1468 | |
---|
1453 | 1469 | /* Reset hardware buffer head */ |
---|
1454 | 1470 | head = AUX_SDB_INDEX(aux, aux->head); |
---|
.. | .. |
---|
1475 | 1491 | static bool aux_set_alert(struct aux_buffer *aux, unsigned long alert_index, |
---|
1476 | 1492 | unsigned long long *overflow) |
---|
1477 | 1493 | { |
---|
1478 | | - unsigned long long orig_overflow, orig_flags, new_flags; |
---|
| 1494 | + union hws_trailer_header old, prev, new; |
---|
1479 | 1495 | struct hws_trailer_entry *te; |
---|
1480 | 1496 | |
---|
1481 | 1497 | te = aux_sdb_trailer(aux, alert_index); |
---|
| 1498 | + /* READ_ONCE() 16 byte header */ |
---|
| 1499 | + prev.val = __cdsg(&te->header.val, 0, 0); |
---|
1482 | 1500 | do { |
---|
1483 | | - orig_flags = te->flags; |
---|
1484 | | - *overflow = orig_overflow = te->overflow; |
---|
1485 | | - if (orig_flags & SDB_TE_BUFFER_FULL_MASK) { |
---|
| 1501 | + old.val = prev.val; |
---|
| 1502 | + new.val = prev.val; |
---|
| 1503 | + *overflow = old.overflow; |
---|
| 1504 | + if (old.f) { |
---|
1486 | 1505 | /* |
---|
1487 | 1506 | * SDB is already set by hardware. |
---|
1488 | 1507 | * Abort and try to set somewhere |
---|
.. | .. |
---|
1490 | 1509 | */ |
---|
1491 | 1510 | return false; |
---|
1492 | 1511 | } |
---|
1493 | | - new_flags = orig_flags | SDB_TE_ALERT_REQ_MASK; |
---|
1494 | | - } while (!cmpxchg_double(&te->flags, &te->overflow, |
---|
1495 | | - orig_flags, orig_overflow, |
---|
1496 | | - new_flags, 0ULL)); |
---|
| 1512 | + new.a = 1; |
---|
| 1513 | + new.overflow = 0; |
---|
| 1514 | + prev.val = __cdsg(&te->header.val, old.val, new.val); |
---|
| 1515 | + } while (prev.val != old.val); |
---|
1497 | 1516 | return true; |
---|
1498 | 1517 | } |
---|
1499 | 1518 | |
---|
.. | .. |
---|
1522 | 1541 | static bool aux_reset_buffer(struct aux_buffer *aux, unsigned long range, |
---|
1523 | 1542 | unsigned long long *overflow) |
---|
1524 | 1543 | { |
---|
1525 | | - unsigned long long orig_overflow, orig_flags, new_flags; |
---|
1526 | 1544 | unsigned long i, range_scan, idx, idx_old; |
---|
| 1545 | + union hws_trailer_header old, prev, new; |
---|
| 1546 | + unsigned long long orig_overflow; |
---|
1527 | 1547 | struct hws_trailer_entry *te; |
---|
1528 | 1548 | |
---|
1529 | 1549 | debug_sprintf_event(sfdbg, 6, "%s: range %ld head %ld alert %ld " |
---|
.. | .. |
---|
1554 | 1574 | idx_old = idx = aux->empty_mark + 1; |
---|
1555 | 1575 | for (i = 0; i < range_scan; i++, idx++) { |
---|
1556 | 1576 | te = aux_sdb_trailer(aux, idx); |
---|
| 1577 | + /* READ_ONCE() 16 byte header */ |
---|
| 1578 | + prev.val = __cdsg(&te->header.val, 0, 0); |
---|
1557 | 1579 | do { |
---|
1558 | | - orig_flags = te->flags; |
---|
1559 | | - orig_overflow = te->overflow; |
---|
1560 | | - new_flags = orig_flags & ~SDB_TE_BUFFER_FULL_MASK; |
---|
| 1580 | + old.val = prev.val; |
---|
| 1581 | + new.val = prev.val; |
---|
| 1582 | + orig_overflow = old.overflow; |
---|
| 1583 | + new.f = 0; |
---|
| 1584 | + new.overflow = 0; |
---|
1561 | 1585 | if (idx == aux->alert_mark) |
---|
1562 | | - new_flags |= SDB_TE_ALERT_REQ_MASK; |
---|
| 1586 | + new.a = 1; |
---|
1563 | 1587 | else |
---|
1564 | | - new_flags &= ~SDB_TE_ALERT_REQ_MASK; |
---|
1565 | | - } while (!cmpxchg_double(&te->flags, &te->overflow, |
---|
1566 | | - orig_flags, orig_overflow, |
---|
1567 | | - new_flags, 0ULL)); |
---|
| 1588 | + new.a = 0; |
---|
| 1589 | + prev.val = __cdsg(&te->header.val, old.val, new.val); |
---|
| 1590 | + } while (prev.val != old.val); |
---|
1568 | 1591 | *overflow += orig_overflow; |
---|
1569 | 1592 | } |
---|
1570 | 1593 | |
---|