aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2014-10-24 12:49:08 +0100
committerMatt Fleming <matt.fleming@intel.com>2014-10-30 13:12:44 +0000
commitc6ba7deb17786ba0d9f135a86cded2baa243192b (patch)
treeaa5c69390e15ab8d10410172c4697ff7629f4bb9
parent475f341143846c31c6f14002f853957ef44fe753 (diff)
downloadlinux-jolsa/perf-intel-cqm.tar.gz
perf/x86/intel: Enable conflicting event schedulingjolsa/perf-intel-cqm
We can leverage the workqueue that we use for RMID rotation to support scheduling of conflicting monitoring events. An example of two conflicting events would be monitoring a cgroup and simultaneously monitoring a task within that cgroup. Cc: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_cqm.c161
1 files changed, 115 insertions, 46 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_intel_cqm.c b/arch/x86/kernel/cpu/perf_event_intel_cqm.c
index 0e1cee56d7a057..30de4cc8f876eb 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_cqm.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_cqm.c
@@ -455,6 +455,35 @@ static void intel_cqm_stable(void *arg)
}
}
+/*
+ * If we have group events waiting for an RMID that don't conflict with
+ * events already running, assign @rmid.
+ */
+static bool intel_cqm_sched_in_event(unsigned int rmid)
+{
+ struct perf_event *leader, *event;
+
+ lockdep_assert_held(&cache_mutex);
+
+ leader = list_first_entry(&cache_groups, struct perf_event,
+ hw.cqm_groups_entry);
+ event = leader;
+
+ list_for_each_entry_continue(event, &cache_groups,
+ hw.cqm_groups_entry) {
+ if (__rmid_valid(event->hw.cqm_rmid))
+ continue;
+
+ if (__conflict_event(event, leader))
+ continue;
+
+ intel_cqm_xchg_rmid(event, rmid);
+ return true;
+ }
+
+ return false;
+}
+
static unsigned int __rotation_period = 250; /* ms */
/*
@@ -480,7 +509,6 @@ static bool intel_cqm_rmid_stabilize(bool *available)
{
struct cqm_rmid_entry *entry;
unsigned int nr_bits;
- struct perf_event *event;
lockdep_assert_held(&cache_mutex);
@@ -554,19 +582,9 @@ static bool intel_cqm_rmid_stabilize(bool *available)
/*
* If we have groups waiting for RMIDs, hand
- * them one now.
+ * them one now provided they don't conflict.
*/
- list_for_each_entry(event, &cache_groups,
- hw.cqm_groups_entry) {
- if (__rmid_valid(event->hw.cqm_rmid))
- continue;
-
- intel_cqm_xchg_rmid(event, i);
- entry = NULL;
- break;
- }
-
- if (!entry)
+ if (intel_cqm_sched_in_event(i))
continue;
/*
@@ -581,25 +599,75 @@ static bool intel_cqm_rmid_stabilize(bool *available)
/*
* Pick a victim group and move it to the tail of the group list.
+ * @next: The first group without an RMID
*/
-static struct perf_event *
-__intel_cqm_pick_and_rotate(void)
+static void __intel_cqm_pick_and_rotate(struct perf_event *next)
{
struct perf_event *rotor;
+ unsigned int rmid;
lockdep_assert_held(&cache_mutex);
- lockdep_assert_held(&cache_lock);
rotor = list_first_entry(&cache_groups, struct perf_event,
hw.cqm_groups_entry);
+
+ /*
+ * The group at the front of the list should always have a valid
+ * RMID. If it doesn't then no groups have RMIDs assigned and we
+ * don't need to rotate the list.
+ */
+ if (next == rotor)
+ return;
+
+ rmid = intel_cqm_xchg_rmid(rotor, INVALID_RMID);
+ __put_rmid(rmid);
+
list_rotate_left(&cache_groups);
+}
- return rotor;
+/*
+ * Deallocate the RMIDs from any events that conflict with @event, and
+ * place them on the back of the group list.
+ */
+static void intel_cqm_sched_out_events(struct perf_event *event)
+{
+ struct perf_event *group, *g;
+ unsigned int rmid;
+
+ lockdep_assert_held(&cache_mutex);
+
+ list_for_each_entry_safe(group, g, &cache_groups, hw.cqm_groups_entry) {
+ if (group == event)
+ continue;
+
+ rmid = group->hw.cqm_rmid;
+
+ /*
+ * Skip events that don't have a valid RMID.
+ */
+ if (!__rmid_valid(rmid))
+ continue;
+
+ /*
+ * No conflict? No problem! Leave the event alone.
+ */
+ if (!__conflict_event(group, event))
+ continue;
+
+ intel_cqm_xchg_rmid(group, INVALID_RMID);
+ __put_rmid(rmid);
+
+ list_move_tail(&group->hw.cqm_groups_entry, &cache_groups);
+ }
}
/*
* Attempt to rotate the groups and assign new RMIDs.
*
+ * We rotate for two reasons,
+ * 1. To handle the scheduling of conflicting events
+ * 2. To recycle RMIDs
+ *
* Rotating RMIDs is complicated because the hardware doesn't give us
* any clues.
*
@@ -619,9 +687,8 @@ __intel_cqm_pick_and_rotate(void)
*/
static bool __intel_cqm_rmid_rotate(void)
{
- struct perf_event *group, *rotor, *start = NULL;
+ struct perf_event *group, *start = NULL;
unsigned int nr_needed = 0;
- unsigned int rmid;
bool rotated = false;
bool available;
@@ -654,7 +721,9 @@ again:
goto stabilize;
/*
- * We have more event groups without RMIDs than available RMIDs.
+ * We have more event groups without RMIDs than available RMIDs,
+ * or we have event groups that conflict with the ones currently
+ * scheduled.
*
* We force deallocate the rmid of the group at the head of
* cache_groups. The first event group without an RMID then gets
@@ -664,15 +733,7 @@ again:
* Rotate the cache_groups list so the previous head is now the
* tail.
*/
- rotor = __intel_cqm_pick_and_rotate();
- rmid = intel_cqm_xchg_rmid(rotor, INVALID_RMID);
-
- /*
- * The group at the front of the list should always have a valid
- * RMID. If it doesn't then no groups have RMIDs assigned.
- */
- if (!__rmid_valid(rmid))
- goto stabilize;
+ __intel_cqm_pick_and_rotate(start);
/*
* If the rotation is going to succeed, reduce the threshold so
@@ -680,14 +741,14 @@ again:
*/
if (__rmid_valid(intel_cqm_rotation_rmid)) {
intel_cqm_xchg_rmid(start, intel_cqm_rotation_rmid);
- intel_cqm_rotation_rmid = INVALID_RMID;
+ intel_cqm_rotation_rmid = __get_rmid();
+
+ intel_cqm_sched_out_events(start);
if (__intel_cqm_threshold)
__intel_cqm_threshold--;
}
- __put_rmid(rmid);
-
rotated = true;
stabilize:
@@ -731,25 +792,37 @@ static void intel_cqm_rmid_rotate(struct work_struct *work)
*
* If we're part of a group, we use the group's RMID.
*/
-static int intel_cqm_setup_event(struct perf_event *event,
- struct perf_event **group, int cpu)
+static void intel_cqm_setup_event(struct perf_event *event,
+ struct perf_event **group)
{
struct perf_event *iter;
+ unsigned int rmid;
+ bool conflict = false;
list_for_each_entry(iter, &cache_groups, hw.cqm_groups_entry) {
+ rmid = iter->hw.cqm_rmid;
+
if (__match_event(iter, event)) {
/* All tasks in a group share an RMID */
- event->hw.cqm_rmid = iter->hw.cqm_rmid;
+ event->hw.cqm_rmid = rmid;
*group = iter;
- return 0;
+ return;
}
- if (__conflict_event(iter, event))
- return -EBUSY;
+ /*
+ * We only care about conflicts for events that are
+ * actually scheduled in (and hence have a valid RMID).
+ */
+ if (__conflict_event(iter, event) && __rmid_valid(rmid))
+ conflict = true;
}
- event->hw.cqm_rmid = __get_rmid();
- return 0;
+ if (conflict)
+ rmid = INVALID_RMID;
+ else
+ rmid = __get_rmid();
+
+ event->hw.cqm_rmid = rmid;
}
static void intel_cqm_event_read(struct perf_event *event)
@@ -979,7 +1052,6 @@ static int intel_cqm_event_init(struct perf_event *event)
{
struct perf_event *group = NULL;
bool rotate = false;
- int err;
if (event->attr.type != intel_cqm_pmu.type)
return -ENOENT;
@@ -1005,9 +1077,7 @@ static int intel_cqm_event_init(struct perf_event *event)
mutex_lock(&cache_mutex);
/* Will also set rmid */
- err = intel_cqm_setup_event(event, &group, event->cpu);
- if (err)
- goto out;
+ intel_cqm_setup_event(event, &group);
if (group) {
list_add_tail(&event->hw.cqm_group_entry,
@@ -1027,13 +1097,12 @@ static int intel_cqm_event_init(struct perf_event *event)
rotate = true;
}
-out:
mutex_unlock(&cache_mutex);
if (rotate)
schedule_delayed_work(&intel_cqm_rmid_work, 0);
- return err;
+ return 0;
}
EVENT_ATTR_STR(llc_occupancy, intel_cqm_llc, "event=0x01");