static bool intel_cqm_rmid_stabilize(unsigned int *available)
{
struct cqm_rmid_entry *entry, *tmp;
- struct perf_event *event;
lockdep_assert_held(&cache_mutex);
/*
* 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, entry->rmid);
- entry = NULL;
- break;
- }
-
- if (!entry)
+ if (intel_cqm_sched_in_event(entry->rmid))
continue;
/*
/*
* 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);
+}
+
+/*
+ * 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_conflicting_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;
- return rotor;
+ intel_cqm_xchg_rmid(group, INVALID_RMID);
+ __put_rmid(rmid);
+ }
}
/*
* 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.
*
*/
static bool __intel_cqm_rmid_rotate(void)
{
- struct perf_event *group, *rotor, *start = NULL;
+ struct perf_event *group, *start = NULL;
unsigned int threshold_limit;
unsigned int nr_needed = 0;
unsigned int nr_available;
- unsigned int rmid;
bool rotated = false;
mutex_lock(&cache_mutex);
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
* 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
*/
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_conflicting_events(start);
if (__intel_cqm_threshold)
__intel_cqm_threshold--;
}
- __put_rmid(rmid);
-
rotated = true;
stabilize:
*
* 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)
+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)
{
struct perf_event *group = NULL;
bool rotate = false;
- int err;
if (event->attr.type != intel_cqm_pmu.type)
return -ENOENT;
mutex_lock(&cache_mutex);
/* Will also set rmid */
- err = intel_cqm_setup_event(event, &group);
- if (err)
- goto out;
+ intel_cqm_setup_event(event, &group);
if (group) {
list_add_tail(&event->hw.cqm_group_entry,
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");