| .. | .. |
|---|
| 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 | |
|---|