WARN_ON(event->parent);
}
-static int engine_event_init(struct perf_event *event)
+static int
+engine_event_status(struct intel_engine_cs *engine,
+ enum drm_i915_pmu_engine_sample sample)
{
- struct drm_i915_private *i915 =
- container_of(event->pmu, typeof(*i915), pmu.base);
-
- if (!intel_engine_lookup_user(i915, engine_event_class(event),
- engine_event_instance(event)))
- return -ENODEV;
-
- switch (engine_event_sample(event)) {
+ switch (sample) {
case I915_SAMPLE_BUSY:
case I915_SAMPLE_WAIT:
break;
case I915_SAMPLE_SEMA:
+ if (INTEL_GEN(engine->i915) < 6)
+ return -ENODEV;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int
+config_status(struct drm_i915_private *i915, u64 config)
+{
+ switch (config) {
+ case I915_PMU_ACTUAL_FREQUENCY:
+ if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915))
+ /* Requires a mutex for sampling! */
+ return -ENODEV;
+ /* Fall-through. */
+ case I915_PMU_REQUESTED_FREQUENCY:
if (INTEL_GEN(i915) < 6)
return -ENODEV;
break;
+ case I915_PMU_INTERRUPTS:
+ break;
+ case I915_PMU_RC6_RESIDENCY:
+ if (!HAS_RC6(i915))
+ return -ENODEV;
+ break;
default:
return -ENOENT;
}
return 0;
}
+static int engine_event_init(struct perf_event *event)
+{
+ struct drm_i915_private *i915 =
+ container_of(event->pmu, typeof(*i915), pmu.base);
+ struct intel_engine_cs *engine;
+
+ engine = intel_engine_lookup_user(i915, engine_event_class(event),
+ engine_event_instance(event));
+ if (!engine)
+ return -ENODEV;
+
+ return engine_event_status(engine, engine_event_sample(event));
+}
+
static int i915_pmu_event_init(struct perf_event *event)
{
struct drm_i915_private *i915 =
if (!cpumask_test_cpu(event->cpu, &i915_pmu_cpumask))
return -EINVAL;
- if (is_engine_event(event)) {
+ if (is_engine_event(event))
ret = engine_event_init(event);
- } else {
- ret = 0;
- switch (event->attr.config) {
- case I915_PMU_ACTUAL_FREQUENCY:
- if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915))
- /* Requires a mutex for sampling! */
- ret = -ENODEV;
- case I915_PMU_REQUESTED_FREQUENCY:
- if (INTEL_GEN(i915) < 6)
- ret = -ENODEV;
- break;
- case I915_PMU_INTERRUPTS:
- break;
- case I915_PMU_RC6_RESIDENCY:
- if (!HAS_RC6(i915))
- ret = -ENODEV;
- break;
- default:
- ret = -ENOENT;
- break;
- }
- }
+ else
+ ret = config_status(i915, event->attr.config);
if (ret)
return ret;
return sprintf(buf, "config=0x%lx\n", eattr->val);
}
-#define I915_EVENT_ATTR(_name, _config) \
- (&((struct i915_ext_attribute[]) { \
- { .attr = __ATTR(_name, 0444, i915_pmu_event_show, NULL), \
- .val = _config, } \
- })[0].attr.attr)
-
-#define I915_EVENT_STR(_name, _str) \
- (&((struct perf_pmu_events_attr[]) { \
- { .attr = __ATTR(_name, 0444, perf_event_sysfs_show, NULL), \
- .id = 0, \
- .event_str = _str, } \
- })[0].attr.attr)
-
-#define I915_EVENT(_name, _config, _unit) \
- I915_EVENT_ATTR(_name, _config), \
- I915_EVENT_STR(_name.unit, _unit)
-
-#define I915_ENGINE_EVENT(_name, _class, _instance, _sample) \
- I915_EVENT_ATTR(_name, __I915_PMU_ENGINE(_class, _instance, _sample)), \
- I915_EVENT_STR(_name.unit, "ns")
-
-#define I915_ENGINE_EVENTS(_name, _class, _instance) \
- I915_ENGINE_EVENT(_name##_instance-busy, _class, _instance, I915_SAMPLE_BUSY), \
- I915_ENGINE_EVENT(_name##_instance-sema, _class, _instance, I915_SAMPLE_SEMA), \
- I915_ENGINE_EVENT(_name##_instance-wait, _class, _instance, I915_SAMPLE_WAIT)
-
-static struct attribute *i915_pmu_events_attrs[] = {
- I915_ENGINE_EVENTS(rcs, I915_ENGINE_CLASS_RENDER, 0),
- I915_ENGINE_EVENTS(bcs, I915_ENGINE_CLASS_COPY, 0),
- I915_ENGINE_EVENTS(vcs, I915_ENGINE_CLASS_VIDEO, 0),
- I915_ENGINE_EVENTS(vcs, I915_ENGINE_CLASS_VIDEO, 1),
- I915_ENGINE_EVENTS(vecs, I915_ENGINE_CLASS_VIDEO_ENHANCE, 0),
-
- I915_EVENT(actual-frequency, I915_PMU_ACTUAL_FREQUENCY, "MHz"),
- I915_EVENT(requested-frequency, I915_PMU_REQUESTED_FREQUENCY, "MHz"),
-
- I915_EVENT_ATTR(interrupts, I915_PMU_INTERRUPTS),
-
- I915_EVENT(rc6-residency, I915_PMU_RC6_RESIDENCY, "ns"),
-
- NULL,
-};
-
-static const struct attribute_group i915_pmu_events_attr_group = {
+static struct attribute_group i915_pmu_events_attr_group = {
.name = "events",
- .attrs = i915_pmu_events_attrs,
+ /* Patch in attrs at runtime. */
};
static ssize_t
NULL,
};
-static struct attribute_group i915_pmu_cpumask_attr_group = {
+static const struct attribute_group i915_pmu_cpumask_attr_group = {
.attrs = i915_cpumask_attrs,
};
NULL
};
+#define __event(__config, __name, __unit) \
+{ \
+ .config = (__config), \
+ .name = (__name), \
+ .unit = (__unit), \
+}
+
+#define __engine_event(__sample, __name) \
+{ \
+ .sample = (__sample), \
+ .name = (__name), \
+}
+
+static struct i915_ext_attribute *
+add_i915_attr(struct i915_ext_attribute *attr, const char *name, u64 config)
+{
+ attr->attr.attr.name = name;
+ attr->attr.attr.mode = 0444;
+ attr->attr.show = i915_pmu_event_show;
+ attr->val = config;
+
+ return ++attr;
+}
+
+static struct perf_pmu_events_attr *
+add_pmu_attr(struct perf_pmu_events_attr *attr, const char *name,
+ const char *str)
+{
+ attr->attr.attr.name = name;
+ attr->attr.attr.mode = 0444;
+ attr->attr.show = perf_event_sysfs_show;
+ attr->event_str = str;
+
+ return ++attr;
+}
+
+static struct attribute **
+create_event_attributes(struct drm_i915_private *i915)
+{
+ static const struct {
+ u64 config;
+ const char *name;
+ const char *unit;
+ } events[] = {
+ __event(I915_PMU_ACTUAL_FREQUENCY, "actual-frequency", "MHz"),
+ __event(I915_PMU_REQUESTED_FREQUENCY, "requested-frequency", "MHz"),
+ __event(I915_PMU_INTERRUPTS, "interrupts", NULL),
+ __event(I915_PMU_RC6_RESIDENCY, "rc6-residency", "ns"),
+ };
+ static const struct {
+ enum drm_i915_pmu_engine_sample sample;
+ char *name;
+ } engine_events[] = {
+ __engine_event(I915_SAMPLE_BUSY, "busy"),
+ __engine_event(I915_SAMPLE_SEMA, "sema"),
+ __engine_event(I915_SAMPLE_WAIT, "wait"),
+ };
+ unsigned int count = 0;
+ struct perf_pmu_events_attr *pmu_attr = NULL, *pmu_iter;
+ struct i915_ext_attribute *i915_attr = NULL, *i915_iter;
+ struct attribute **attr = NULL, **attr_iter;
+ struct intel_engine_cs *engine;
+ enum intel_engine_id id;
+ unsigned int i;
+
+ /* Count how many counters we will be exposing. */
+ for (i = 0; i < ARRAY_SIZE(events); i++) {
+ if (!config_status(i915, events[i].config))
+ count++;
+ }
+
+ for_each_engine(engine, i915, id) {
+ for (i = 0; i < ARRAY_SIZE(engine_events); i++) {
+ if (!engine_event_status(engine,
+ engine_events[i].sample))
+ count++;
+ }
+ }
+
+ /* Allocate attribute objects and table. */
+ i915_attr = kzalloc(count * sizeof(*i915_attr), GFP_KERNEL);
+ if (!i915_attr)
+ goto err_alloc;
+
+ pmu_attr = kzalloc(count * sizeof(*pmu_attr), GFP_KERNEL);
+ if (!pmu_attr)
+ goto err_alloc;
+
+ /* Max one pointer of each attribute type plus a termination entry. */
+ attr = kzalloc((count * 2 + 1) * sizeof(attr), GFP_KERNEL);
+ if (!attr)
+ goto err_alloc;
+
+ i915_iter = i915_attr;
+ pmu_iter = pmu_attr;
+ attr_iter = attr;
+
+ /* Initialize supported non-engine counters. */
+ for (i = 0; i < ARRAY_SIZE(events); i++) {
+ char *str;
+
+ if (config_status(i915, events[i].config))
+ continue;
+
+ str = kstrdup(events[i].name, GFP_KERNEL);
+ if (!str)
+ goto err;
+
+ *attr_iter++ = &i915_iter->attr.attr;
+ i915_iter = add_i915_attr(i915_iter, str, events[i].config);
+
+ if (events[i].unit) {
+ str = kasprintf(GFP_KERNEL, "%s.unit", events[i].name);
+ if (!str)
+ goto err;
+
+ *attr_iter++ = &pmu_iter->attr.attr;
+ pmu_iter = add_pmu_attr(pmu_iter, str, events[i].unit);
+ }
+ }
+
+ /* Initialize supported engine counters. */
+ for_each_engine(engine, i915, id) {
+ for (i = 0; i < ARRAY_SIZE(engine_events); i++) {
+ char *str;
+
+ if (engine_event_status(engine,
+ engine_events[i].sample))
+ continue;
+
+ str = kasprintf(GFP_KERNEL, "%s-%s",
+ engine->name, engine_events[i].name);
+ if (!str)
+ goto err;
+
+ *attr_iter++ = &i915_iter->attr.attr;
+ i915_iter =
+ add_i915_attr(i915_iter, str,
+ __I915_PMU_ENGINE(engine->class,
+ engine->instance,
+ engine_events[i].sample));
+
+ str = kasprintf(GFP_KERNEL, "%s-%s.unit",
+ engine->name, engine_events[i].name);
+ if (!str)
+ goto err;
+
+ *attr_iter++ = &pmu_iter->attr.attr;
+ pmu_iter = add_pmu_attr(pmu_iter, str, "ns");
+ }
+ }
+
+ i915->pmu.i915_attr = i915_attr;
+ i915->pmu.pmu_attr = pmu_attr;
+
+ return attr;
+
+err:;
+ for (attr_iter = attr; *attr_iter; attr_iter++)
+ kfree((*attr_iter)->name);
+
+err_alloc:
+ kfree(attr);
+ kfree(i915_attr);
+ kfree(pmu_attr);
+
+ return NULL;
+}
+
+static void free_event_attributes(struct drm_i915_private *i915)
+{
+ struct attribute **attr_iter = i915_pmu_events_attr_group.attrs;
+
+ for (; *attr_iter; attr_iter++)
+ kfree((*attr_iter)->name);
+
+ kfree(i915_pmu_events_attr_group.attrs);
+ kfree(i915->pmu.i915_attr);
+ kfree(i915->pmu.pmu_attr);
+
+ i915_pmu_events_attr_group.attrs = NULL;
+ i915->pmu.i915_attr = NULL;
+ i915->pmu.pmu_attr = NULL;
+}
+
static int i915_pmu_cpu_online(unsigned int cpu, struct hlist_node *node)
{
struct i915_pmu *pmu = hlist_entry_safe(node, typeof(*pmu), node);
return;
}
+ i915_pmu_events_attr_group.attrs = create_event_attributes(i915);
+ if (!i915_pmu_events_attr_group.attrs) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
i915->pmu.base.attr_groups = i915_pmu_attr_groups;
i915->pmu.base.task_ctx_nr = perf_invalid_context;
i915->pmu.base.event_init = i915_pmu_event_init;
perf_pmu_unregister(&i915->pmu.base);
err:
i915->pmu.base.event_init = NULL;
+ free_event_attributes(i915);
DRM_NOTE("Failed to register PMU! (err=%d)\n", ret);
}
perf_pmu_unregister(&i915->pmu.base);
i915->pmu.base.event_init = NULL;
+ free_event_attributes(i915);
}