Commit b00bccb3 authored by Tvrtko Ursulin's avatar Tvrtko Ursulin

drm/i915/pmu: Handle PCI unbind

Mark the device as closed and keep references to driver data alive to
allow for safe driver unbind with active PMU clients. Perf core does not
otherwise handle this case so we have to do it manually like this.
Signed-off-by: default avatarTvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Link: https://patchwork.freedesktop.org/patch/msgid/20201020100822.543332-1-tvrtko.ursulin@linux.intel.com
parent 44c2200a
...@@ -445,6 +445,8 @@ static void i915_pmu_event_destroy(struct perf_event *event) ...@@ -445,6 +445,8 @@ static void i915_pmu_event_destroy(struct perf_event *event)
container_of(event->pmu, typeof(*i915), pmu.base); container_of(event->pmu, typeof(*i915), pmu.base);
drm_WARN_ON(&i915->drm, event->parent); drm_WARN_ON(&i915->drm, event->parent);
drm_dev_put(&i915->drm);
} }
static int static int
...@@ -510,8 +512,12 @@ static int i915_pmu_event_init(struct perf_event *event) ...@@ -510,8 +512,12 @@ static int i915_pmu_event_init(struct perf_event *event)
{ {
struct drm_i915_private *i915 = struct drm_i915_private *i915 =
container_of(event->pmu, typeof(*i915), pmu.base); container_of(event->pmu, typeof(*i915), pmu.base);
struct i915_pmu *pmu = &i915->pmu;
int ret; int ret;
if (pmu->closed)
return -ENODEV;
if (event->attr.type != event->pmu->type) if (event->attr.type != event->pmu->type)
return -ENOENT; return -ENOENT;
...@@ -536,8 +542,10 @@ static int i915_pmu_event_init(struct perf_event *event) ...@@ -536,8 +542,10 @@ static int i915_pmu_event_init(struct perf_event *event)
if (ret) if (ret)
return ret; return ret;
if (!event->parent) if (!event->parent) {
drm_dev_get(&i915->drm);
event->destroy = i915_pmu_event_destroy; event->destroy = i915_pmu_event_destroy;
}
return 0; return 0;
} }
...@@ -594,9 +602,16 @@ static u64 __i915_pmu_event_read(struct perf_event *event) ...@@ -594,9 +602,16 @@ static u64 __i915_pmu_event_read(struct perf_event *event)
static void i915_pmu_event_read(struct perf_event *event) static void i915_pmu_event_read(struct perf_event *event)
{ {
struct drm_i915_private *i915 =
container_of(event->pmu, typeof(*i915), pmu.base);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
struct i915_pmu *pmu = &i915->pmu;
u64 prev, new; u64 prev, new;
if (pmu->closed) {
event->hw.state = PERF_HES_STOPPED;
return;
}
again: again:
prev = local64_read(&hwc->prev_count); prev = local64_read(&hwc->prev_count);
new = __i915_pmu_event_read(event); new = __i915_pmu_event_read(event);
...@@ -724,6 +739,13 @@ static void i915_pmu_disable(struct perf_event *event) ...@@ -724,6 +739,13 @@ static void i915_pmu_disable(struct perf_event *event)
static void i915_pmu_event_start(struct perf_event *event, int flags) static void i915_pmu_event_start(struct perf_event *event, int flags)
{ {
struct drm_i915_private *i915 =
container_of(event->pmu, typeof(*i915), pmu.base);
struct i915_pmu *pmu = &i915->pmu;
if (pmu->closed)
return;
i915_pmu_enable(event); i915_pmu_enable(event);
event->hw.state = 0; event->hw.state = 0;
} }
...@@ -738,6 +760,13 @@ static void i915_pmu_event_stop(struct perf_event *event, int flags) ...@@ -738,6 +760,13 @@ static void i915_pmu_event_stop(struct perf_event *event, int flags)
static int i915_pmu_event_add(struct perf_event *event, int flags) static int i915_pmu_event_add(struct perf_event *event, int flags)
{ {
struct drm_i915_private *i915 =
container_of(event->pmu, typeof(*i915), pmu.base);
struct i915_pmu *pmu = &i915->pmu;
if (pmu->closed)
return -ENODEV;
if (flags & PERF_EF_START) if (flags & PERF_EF_START)
i915_pmu_event_start(event, flags); i915_pmu_event_start(event, flags);
...@@ -1167,7 +1196,13 @@ void i915_pmu_unregister(struct drm_i915_private *i915) ...@@ -1167,7 +1196,13 @@ void i915_pmu_unregister(struct drm_i915_private *i915)
if (!pmu->base.event_init) if (!pmu->base.event_init)
return; return;
drm_WARN_ON(&i915->drm, pmu->enable); /*
* "Disconnect" the PMU callbacks - since all are atomic synchronize_rcu
* ensures all currently executing ones will have exited before we
* proceed with unregistration.
*/
pmu->closed = true;
synchronize_rcu();
hrtimer_cancel(&pmu->timer); hrtimer_cancel(&pmu->timer);
......
...@@ -49,6 +49,10 @@ struct i915_pmu { ...@@ -49,6 +49,10 @@ struct i915_pmu {
* @base: PMU base. * @base: PMU base.
*/ */
struct pmu base; struct pmu base;
/**
* @closed: i915 is unregistering.
*/
bool closed;
/** /**
* @name: Name as registered with perf core. * @name: Name as registered with perf core.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment