Skip to content

Commit 59bf7fd

Browse files
Matt FlemingIngo Molnar
authored andcommitted
perf/x86/intel: Enable conflicting event scheduling for CQM
We can leverage the workqueue that we use for RMID rotation to support scheduling of conflicting monitoring events. Allowing events that monitor conflicting things is done at various other places in the perf subsystem, so there's precedent there. An example of two conflicting events would be monitoring a cgroup and simultaneously monitoring a task within that cgroup. This uses the cache_groups list as a queuing mechanism, where every event that reaches the front of the list gets the chance to be scheduled in, possibly descheduling any conflicting events that are running. Signed-off-by: Matt Fleming <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Kanaka Juvva <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Vikas Shivappa <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent bff671d commit 59bf7fd

File tree

1 file changed

+84
-46
lines changed

1 file changed

+84
-46
lines changed

arch/x86/kernel/cpu/perf_event_intel_cqm.c

Lines changed: 84 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,6 @@ static unsigned int __rmid_queue_time_ms = RMID_DEFAULT_QUEUE_TIME;
507507
static bool intel_cqm_rmid_stabilize(unsigned int *available)
508508
{
509509
struct cqm_rmid_entry *entry, *tmp;
510-
struct perf_event *event;
511510

512511
lockdep_assert_held(&cache_mutex);
513512

@@ -577,19 +576,9 @@ static bool intel_cqm_rmid_stabilize(unsigned int *available)
577576

578577
/*
579578
* If we have groups waiting for RMIDs, hand
580-
* them one now.
579+
* them one now provided they don't conflict.
581580
*/
582-
list_for_each_entry(event, &cache_groups,
583-
hw.cqm_groups_entry) {
584-
if (__rmid_valid(event->hw.cqm_rmid))
585-
continue;
586-
587-
intel_cqm_xchg_rmid(event, entry->rmid);
588-
entry = NULL;
589-
break;
590-
}
591-
592-
if (!entry)
581+
if (intel_cqm_sched_in_event(entry->rmid))
593582
continue;
594583

595584
/*
@@ -604,25 +593,73 @@ static bool intel_cqm_rmid_stabilize(unsigned int *available)
604593

605594
/*
606595
* Pick a victim group and move it to the tail of the group list.
596+
* @next: The first group without an RMID
607597
*/
608-
static struct perf_event *
609-
__intel_cqm_pick_and_rotate(void)
598+
static void __intel_cqm_pick_and_rotate(struct perf_event *next)
610599
{
611600
struct perf_event *rotor;
601+
unsigned int rmid;
612602

613603
lockdep_assert_held(&cache_mutex);
614-
lockdep_assert_held(&cache_lock);
615604

616605
rotor = list_first_entry(&cache_groups, struct perf_event,
617606
hw.cqm_groups_entry);
607+
608+
/*
609+
* The group at the front of the list should always have a valid
610+
* RMID. If it doesn't then no groups have RMIDs assigned and we
611+
* don't need to rotate the list.
612+
*/
613+
if (next == rotor)
614+
return;
615+
616+
rmid = intel_cqm_xchg_rmid(rotor, INVALID_RMID);
617+
__put_rmid(rmid);
618+
618619
list_rotate_left(&cache_groups);
620+
}
621+
622+
/*
623+
* Deallocate the RMIDs from any events that conflict with @event, and
624+
* place them on the back of the group list.
625+
*/
626+
static void intel_cqm_sched_out_conflicting_events(struct perf_event *event)
627+
{
628+
struct perf_event *group, *g;
629+
unsigned int rmid;
630+
631+
lockdep_assert_held(&cache_mutex);
632+
633+
list_for_each_entry_safe(group, g, &cache_groups, hw.cqm_groups_entry) {
634+
if (group == event)
635+
continue;
636+
637+
rmid = group->hw.cqm_rmid;
638+
639+
/*
640+
* Skip events that don't have a valid RMID.
641+
*/
642+
if (!__rmid_valid(rmid))
643+
continue;
644+
645+
/*
646+
* No conflict? No problem! Leave the event alone.
647+
*/
648+
if (!__conflict_event(group, event))
649+
continue;
619650

620-
return rotor;
651+
intel_cqm_xchg_rmid(group, INVALID_RMID);
652+
__put_rmid(rmid);
653+
}
621654
}
622655

623656
/*
624657
* Attempt to rotate the groups and assign new RMIDs.
625658
*
659+
* We rotate for two reasons,
660+
* 1. To handle the scheduling of conflicting events
661+
* 2. To recycle RMIDs
662+
*
626663
* Rotating RMIDs is complicated because the hardware doesn't give us
627664
* any clues.
628665
*
@@ -642,11 +679,10 @@ __intel_cqm_pick_and_rotate(void)
642679
*/
643680
static bool __intel_cqm_rmid_rotate(void)
644681
{
645-
struct perf_event *group, *rotor, *start = NULL;
682+
struct perf_event *group, *start = NULL;
646683
unsigned int threshold_limit;
647684
unsigned int nr_needed = 0;
648685
unsigned int nr_available;
649-
unsigned int rmid;
650686
bool rotated = false;
651687

652688
mutex_lock(&cache_mutex);
@@ -678,7 +714,9 @@ static bool __intel_cqm_rmid_rotate(void)
678714
goto stabilize;
679715

680716
/*
681-
* We have more event groups without RMIDs than available RMIDs.
717+
* We have more event groups without RMIDs than available RMIDs,
718+
* or we have event groups that conflict with the ones currently
719+
* scheduled.
682720
*
683721
* We force deallocate the rmid of the group at the head of
684722
* cache_groups. The first event group without an RMID then gets
@@ -688,30 +726,22 @@ static bool __intel_cqm_rmid_rotate(void)
688726
* Rotate the cache_groups list so the previous head is now the
689727
* tail.
690728
*/
691-
rotor = __intel_cqm_pick_and_rotate();
692-
rmid = intel_cqm_xchg_rmid(rotor, INVALID_RMID);
693-
694-
/*
695-
* The group at the front of the list should always have a valid
696-
* RMID. If it doesn't then no groups have RMIDs assigned.
697-
*/
698-
if (!__rmid_valid(rmid))
699-
goto stabilize;
729+
__intel_cqm_pick_and_rotate(start);
700730

701731
/*
702732
* If the rotation is going to succeed, reduce the threshold so
703733
* that we don't needlessly reuse dirty RMIDs.
704734
*/
705735
if (__rmid_valid(intel_cqm_rotation_rmid)) {
706736
intel_cqm_xchg_rmid(start, intel_cqm_rotation_rmid);
707-
intel_cqm_rotation_rmid = INVALID_RMID;
737+
intel_cqm_rotation_rmid = __get_rmid();
738+
739+
intel_cqm_sched_out_conflicting_events(start);
708740

709741
if (__intel_cqm_threshold)
710742
__intel_cqm_threshold--;
711743
}
712744

713-
__put_rmid(rmid);
714-
715745
rotated = true;
716746

717747
stabilize:
@@ -794,25 +824,37 @@ static void intel_cqm_rmid_rotate(struct work_struct *work)
794824
*
795825
* If we're part of a group, we use the group's RMID.
796826
*/
797-
static int intel_cqm_setup_event(struct perf_event *event,
798-
struct perf_event **group)
827+
static void intel_cqm_setup_event(struct perf_event *event,
828+
struct perf_event **group)
799829
{
800830
struct perf_event *iter;
831+
unsigned int rmid;
832+
bool conflict = false;
801833

802834
list_for_each_entry(iter, &cache_groups, hw.cqm_groups_entry) {
835+
rmid = iter->hw.cqm_rmid;
836+
803837
if (__match_event(iter, event)) {
804838
/* All tasks in a group share an RMID */
805-
event->hw.cqm_rmid = iter->hw.cqm_rmid;
839+
event->hw.cqm_rmid = rmid;
806840
*group = iter;
807-
return 0;
841+
return;
808842
}
809843

810-
if (__conflict_event(iter, event))
811-
return -EBUSY;
844+
/*
845+
* We only care about conflicts for events that are
846+
* actually scheduled in (and hence have a valid RMID).
847+
*/
848+
if (__conflict_event(iter, event) && __rmid_valid(rmid))
849+
conflict = true;
812850
}
813851

814-
event->hw.cqm_rmid = __get_rmid();
815-
return 0;
852+
if (conflict)
853+
rmid = INVALID_RMID;
854+
else
855+
rmid = __get_rmid();
856+
857+
event->hw.cqm_rmid = rmid;
816858
}
817859

818860
static void intel_cqm_event_read(struct perf_event *event)
@@ -1030,7 +1072,6 @@ static int intel_cqm_event_init(struct perf_event *event)
10301072
{
10311073
struct perf_event *group = NULL;
10321074
bool rotate = false;
1033-
int err;
10341075

10351076
if (event->attr.type != intel_cqm_pmu.type)
10361077
return -ENOENT;
@@ -1056,9 +1097,7 @@ static int intel_cqm_event_init(struct perf_event *event)
10561097
mutex_lock(&cache_mutex);
10571098

10581099
/* Will also set rmid */
1059-
err = intel_cqm_setup_event(event, &group);
1060-
if (err)
1061-
goto out;
1100+
intel_cqm_setup_event(event, &group);
10621101

10631102
if (group) {
10641103
list_add_tail(&event->hw.cqm_group_entry,
@@ -1078,13 +1117,12 @@ static int intel_cqm_event_init(struct perf_event *event)
10781117
rotate = true;
10791118
}
10801119

1081-
out:
10821120
mutex_unlock(&cache_mutex);
10831121

10841122
if (rotate)
10851123
schedule_delayed_work(&intel_cqm_rmid_work, 0);
10861124

1087-
return err;
1125+
return 0;
10881126
}
10891127

10901128
EVENT_ATTR_STR(llc_occupancy, intel_cqm_llc, "event=0x01");

0 commit comments

Comments
 (0)