diff options
| -rw-r--r-- | arch/x86/events/amd/ibs.c | 52 |
1 files changed, 45 insertions, 7 deletions
diff --git a/arch/x86/events/amd/ibs.c b/arch/x86/events/amd/ibs.c index 3ea25c3917c0..feb90f6730e8 100644 --- a/arch/x86/events/amd/ibs.c +++ b/arch/x86/events/amd/ibs.c | |||
| @@ -28,10 +28,46 @@ static u32 ibs_caps; | |||
| 28 | #define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT) | 28 | #define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT) |
| 29 | #define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT | 29 | #define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT |
| 30 | 30 | ||
| 31 | |||
| 32 | /* | ||
| 33 | * IBS states: | ||
| 34 | * | ||
| 35 | * ENABLED; tracks the pmu::add(), pmu::del() state, when set the counter is taken | ||
| 36 | * and any further add()s must fail. | ||
| 37 | * | ||
| 38 | * STARTED/STOPPING/STOPPED; deal with pmu::start(), pmu::stop() state but are | ||
| 39 | * complicated by the fact that the IBS hardware can send late NMIs (ie. after | ||
| 40 | * we've cleared the EN bit). | ||
| 41 | * | ||
| 42 | * In order to consume these late NMIs we have the STOPPED state, any NMI that | ||
| 43 | * happens after we've cleared the EN state will clear this bit and report the | ||
| 44 | * NMI handled (this is fundamentally racy in the face or multiple NMI sources, | ||
| 45 | * someone else can consume our BIT and our NMI will go unhandled). | ||
| 46 | * | ||
| 47 | * And since we cannot set/clear this separate bit together with the EN bit, | ||
| 48 | * there are races; if we cleared STARTED early, an NMI could land in | ||
| 49 | * between clearing STARTED and clearing the EN bit (in fact multiple NMIs | ||
| 50 | * could happen if the period is small enough), and consume our STOPPED bit | ||
| 51 | * and trigger streams of unhandled NMIs. | ||
| 52 | * | ||
| 53 | * If, however, we clear STARTED late, an NMI can hit between clearing the | ||
| 54 | * EN bit and clearing STARTED, still see STARTED set and process the event. | ||
| 55 | * If this event will have the VALID bit clear, we bail properly, but this | ||
| 56 | * is not a given. With VALID set we can end up calling pmu::stop() again | ||
| 57 | * (the throttle logic) and trigger the WARNs in there. | ||
| 58 | * | ||
| 59 | * So what we do is set STOPPING before clearing EN to avoid the pmu::stop() | ||
| 60 | * nesting, and clear STARTED late, so that we have a well defined state over | ||
| 61 | * the clearing of the EN bit. | ||
| 62 | * | ||
| 63 | * XXX: we could probably be using !atomic bitops for all this. | ||
| 64 | */ | ||
| 65 | |||
| 31 | enum ibs_states { | 66 | enum ibs_states { |
| 32 | IBS_ENABLED = 0, | 67 | IBS_ENABLED = 0, |
| 33 | IBS_STARTED = 1, | 68 | IBS_STARTED = 1, |
| 34 | IBS_STOPPING = 2, | 69 | IBS_STOPPING = 2, |
| 70 | IBS_STOPPED = 3, | ||
| 35 | 71 | ||
| 36 | IBS_MAX_STATES, | 72 | IBS_MAX_STATES, |
| 37 | }; | 73 | }; |
| @@ -377,11 +413,10 @@ static void perf_ibs_start(struct perf_event *event, int flags) | |||
| 377 | 413 | ||
| 378 | perf_ibs_set_period(perf_ibs, hwc, &period); | 414 | perf_ibs_set_period(perf_ibs, hwc, &period); |
| 379 | /* | 415 | /* |
| 380 | * Set STARTED before enabling the hardware, such that | 416 | * Set STARTED before enabling the hardware, such that a subsequent NMI |
| 381 | * a subsequent NMI must observe it. Then clear STOPPING | 417 | * must observe it. |
| 382 | * such that we don't consume NMIs by accident. | ||
| 383 | */ | 418 | */ |
| 384 | set_bit(IBS_STARTED, pcpu->state); | 419 | set_bit(IBS_STARTED, pcpu->state); |
| 385 | clear_bit(IBS_STOPPING, pcpu->state); | 420 | clear_bit(IBS_STOPPING, pcpu->state); |
| 386 | perf_ibs_enable_event(perf_ibs, hwc, period >> 4); | 421 | perf_ibs_enable_event(perf_ibs, hwc, period >> 4); |
| 387 | 422 | ||
| @@ -396,6 +431,9 @@ static void perf_ibs_stop(struct perf_event *event, int flags) | |||
| 396 | u64 config; | 431 | u64 config; |
| 397 | int stopping; | 432 | int stopping; |
| 398 | 433 | ||
| 434 | if (test_and_set_bit(IBS_STOPPING, pcpu->state)) | ||
| 435 | return; | ||
| 436 | |||
| 399 | stopping = test_bit(IBS_STARTED, pcpu->state); | 437 | stopping = test_bit(IBS_STARTED, pcpu->state); |
| 400 | 438 | ||
| 401 | if (!stopping && (hwc->state & PERF_HES_UPTODATE)) | 439 | if (!stopping && (hwc->state & PERF_HES_UPTODATE)) |
| @@ -405,12 +443,12 @@ static void perf_ibs_stop(struct perf_event *event, int flags) | |||
| 405 | 443 | ||
| 406 | if (stopping) { | 444 | if (stopping) { |
| 407 | /* | 445 | /* |
| 408 | * Set STOPPING before disabling the hardware, such that it | 446 | * Set STOPPED before disabling the hardware, such that it |
| 409 | * must be visible to NMIs the moment we clear the EN bit, | 447 | * must be visible to NMIs the moment we clear the EN bit, |
| 410 | * at which point we can generate an !VALID sample which | 448 | * at which point we can generate an !VALID sample which |
| 411 | * we need to consume. | 449 | * we need to consume. |
| 412 | */ | 450 | */ |
| 413 | set_bit(IBS_STOPPING, pcpu->state); | 451 | set_bit(IBS_STOPPED, pcpu->state); |
| 414 | perf_ibs_disable_event(perf_ibs, hwc, config); | 452 | perf_ibs_disable_event(perf_ibs, hwc, config); |
| 415 | /* | 453 | /* |
| 416 | * Clear STARTED after disabling the hardware; if it were | 454 | * Clear STARTED after disabling the hardware; if it were |
| @@ -556,7 +594,7 @@ fail: | |||
| 556 | * with samples that even have the valid bit cleared. | 594 | * with samples that even have the valid bit cleared. |
| 557 | * Mark all this NMIs as handled. | 595 | * Mark all this NMIs as handled. |
| 558 | */ | 596 | */ |
| 559 | if (test_and_clear_bit(IBS_STOPPING, pcpu->state)) | 597 | if (test_and_clear_bit(IBS_STOPPED, pcpu->state)) |
| 560 | return 1; | 598 | return 1; |
| 561 | 599 | ||
| 562 | return 0; | 600 | return 0; |
