mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
perf: Make perf_pmu_unregister() useable
Previously it was only safe to call perf_pmu_unregister() if there were no active events of that pmu around -- which was impossible to guarantee since it races all sorts against perf_init_event(). Rework the whole thing by: - keeping track of all events for a given pmu - 'hiding' the pmu from perf_init_event() - waiting for the appropriate (s)rcu grace periods such that all prior references to the PMU will be completed - detaching all still existing events of that pmu (see first point) and moving them to a new REVOKED state. - actually freeing the pmu data. Where notably the new REVOKED state must inhibit all event actions from reaching code that wants to use event->pmu. Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Reviewed-by: Ravi Bangoria <ravi.bangoria@amd.com> Link: https://lkml.kernel.org/r/20250307193723.525402029@infradead.org
This commit is contained in:
1 parent
4da0600eda
commit
da916e96e2
2 files changed
+280
-55
No files matched your search
@@ -325,6 +325,9 @@ struct perf_output_handle;
|
|||||||
struct pmu {
|
struct pmu {
|
||||||
struct list_head entry;
|
struct list_head entry;
|
||||||
|
|
||||||
|
spinlock_t events_lock;
|
||||||
|
struct list_head events;
|
||||||
|
|
||||||
struct module *module;
|
struct module *module;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct device *parent;
|
struct device *parent;
|
||||||
@@ -622,9 +625,10 @@ struct perf_addr_filter_range {
|
|||||||
* enum perf_event_state - the states of an event:
|
* enum perf_event_state - the states of an event:
|
||||||
*/
|
*/
|
||||||
enum perf_event_state {
|
enum perf_event_state {
|
||||||
PERF_EVENT_STATE_DEAD = -4,
|
PERF_EVENT_STATE_DEAD = -5,
|
||||||
PERF_EVENT_STATE_EXIT = -3,
|
PERF_EVENT_STATE_REVOKED = -4, /* pmu gone, must not touch */
|
||||||
PERF_EVENT_STATE_ERROR = -2,
|
PERF_EVENT_STATE_EXIT = -3, /* task died, still inherit */
|
||||||
|
PERF_EVENT_STATE_ERROR = -2, /* scheduling error, can enable */
|
||||||
PERF_EVENT_STATE_OFF = -1,
|
PERF_EVENT_STATE_OFF = -1,
|
||||||
PERF_EVENT_STATE_INACTIVE = 0,
|
PERF_EVENT_STATE_INACTIVE = 0,
|
||||||
PERF_EVENT_STATE_ACTIVE = 1,
|
PERF_EVENT_STATE_ACTIVE = 1,
|
||||||
@@ -865,6 +869,7 @@ struct perf_event {
|
|||||||
void *security;
|
void *security;
|
||||||
#endif
|
#endif
|
||||||
struct list_head sb_list;
|
struct list_head sb_list;
|
||||||
|
struct list_head pmu_list;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Certain events gets forwarded to another pmu internally by over-
|
* Certain events gets forwarded to another pmu internally by over-
|
||||||
@@ -1155,7 +1160,7 @@ extern void perf_aux_output_flag(struct perf_output_handle *handle, u64 flags);
|
|||||||
extern void perf_event_itrace_started(struct perf_event *event);
|
extern void perf_event_itrace_started(struct perf_event *event);
|
||||||
|
|
||||||
extern int perf_pmu_register(struct pmu *pmu, const char *name, int type);
|
extern int perf_pmu_register(struct pmu *pmu, const char *name, int type);
|
||||||
extern void perf_pmu_unregister(struct pmu *pmu);
|
extern int perf_pmu_unregister(struct pmu *pmu);
|
||||||
|
|
||||||
extern void __perf_event_task_sched_in(struct task_struct *prev,
|
extern void __perf_event_task_sched_in(struct task_struct *prev,
|
||||||
struct task_struct *task);
|
struct task_struct *task);
|
||||||
@@ -1760,7 +1765,7 @@ static inline bool needs_branch_stack(struct perf_event *event)
|
|||||||
|
|
||||||
static inline bool has_aux(struct perf_event *event)
|
static inline bool has_aux(struct perf_event *event)
|
||||||
{
|
{
|
||||||
return event->pmu->setup_aux;
|
return event->pmu && event->pmu->setup_aux;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool has_aux_action(struct perf_event *event)
|
static inline bool has_aux_action(struct perf_event *event)
|
||||||
|
|||||||
+270
-50
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user