Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f87eaf90 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "perf: Fix event cleanup across CPU hotplugs"

parents a4193d40 9453757d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -707,8 +707,8 @@ static irqreturn_t armv8pmu_handle_irq(int irq_num, void *dev)
		struct perf_event *event = cpuc->events[idx];
		struct hw_perf_event *hwc;

		/* Ignore if we don't have an event. */
		if (!event)
		/* Ignore if we don't have an event or if it's a zombie event */
		if (!event || event->state == PERF_EVENT_STATE_ZOMBIE)
			continue;

		/*
+9 −0
Original line number Diff line number Diff line
@@ -659,6 +659,15 @@ static void cpu_pm_pmu_setup(struct arm_pmu *armpmu, unsigned long cmd)
			continue;

		event = hw_events->events[idx];
		if (!event)
			continue;

		/*
		 * Check if an attempt was made to free this event during
		 * the CPU went offline.
		 */
		if (event->state == PERF_EVENT_STATE_ZOMBIE)
			continue;

		switch (cmd) {
		case CPU_PM_ENTER:
+4 −1
Original line number Diff line number Diff line
@@ -490,7 +490,8 @@ struct perf_addr_filters_head {
 * enum perf_event_active_state - the states of a event
 */
enum perf_event_active_state {
	PERF_EVENT_STATE_DEAD		= -4,
	PERF_EVENT_STATE_DEAD		= -5,
	PERF_EVENT_STATE_ZOMBIE		= -4,
	PERF_EVENT_STATE_EXIT		= -3,
	PERF_EVENT_STATE_ERROR		= -2,
	PERF_EVENT_STATE_OFF		= -1,
@@ -705,6 +706,8 @@ struct perf_event {
#endif

	struct list_head		sb_list;

	struct list_head		zombie_entry;
#endif /* CONFIG_PERF_EVENTS */
};

+57 −0
Original line number Diff line number Diff line
@@ -4357,6 +4357,14 @@ static void put_event(struct perf_event *event)
	_free_event(event);
}

/*
 * Maintain a zombie list to collect all the zombie events
 */
#if defined CONFIG_HOTPLUG_CPU || defined CONFIG_KEXEC_CORE
static LIST_HEAD(zombie_list);
static DEFINE_SPINLOCK(zombie_list_lock);
#endif

/*
 * Kill an event dead; while event:refcount will preserve the event
 * object, it will not preserve its functionality. Once the last 'user'
@@ -4367,6 +4375,26 @@ int perf_event_release_kernel(struct perf_event *event)
	struct perf_event_context *ctx = event->ctx;
	struct perf_event *child, *tmp;

	/*
	 * If the cpu associated to this event is offline, set the event as a
	 *  zombie event. The cleanup of the cpu would be done if the CPU is
	 *  back online.
	 */
#if defined CONFIG_HOTPLUG_CPU || defined CONFIG_KEXEC_CORE
	if (!cpu_online(event->cpu)) {
		if (event->state == PERF_EVENT_STATE_ZOMBIE)
			return 0;

		event->state = PERF_EVENT_STATE_ZOMBIE;

		spin_lock(&zombie_list_lock);
		list_add_tail(&event->zombie_entry, &zombie_list);
		spin_unlock(&zombie_list_lock);

		return 0;
	}
#endif

	/*
	 * If we got here through err_file: fput(event_file); we will not have
	 * attached to a context yet.
@@ -9559,6 +9587,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
	INIT_LIST_HEAD(&event->rb_entry);
	INIT_LIST_HEAD(&event->active_entry);
	INIT_LIST_HEAD(&event->addr_filters.list);
	INIT_LIST_HEAD(&event->zombie_entry);
	INIT_HLIST_NODE(&event->hlist_entry);


@@ -11307,6 +11336,32 @@ check_hotplug_start_event(struct perf_event *event)
		event->pmu->start(event, 0);
}

static void perf_event_zombie_cleanup(unsigned int cpu)
{
	struct perf_event *event, *tmp;

	spin_lock(&zombie_list_lock);

	list_for_each_entry_safe(event, tmp, &zombie_list, zombie_entry) {
		if (event->cpu != cpu)
			continue;

		list_del(&event->zombie_entry);
		spin_unlock(&zombie_list_lock);

		/*
		 * The detachment of the event with the
		 * PMU expects it to be in an active state
		 */
		event->state = PERF_EVENT_STATE_ACTIVE;
		perf_event_release_kernel(event);

		spin_lock(&zombie_list_lock);
	}

	spin_unlock(&zombie_list_lock);
}

int perf_event_start_swevents(unsigned int cpu)
{
	struct perf_event_context *ctx;
@@ -11314,6 +11369,8 @@ int perf_event_start_swevents(unsigned int cpu)
	struct perf_event *event;
	int idx;

	perf_event_zombie_cleanup(cpu);

	idx = srcu_read_lock(&pmus_srcu);
	list_for_each_entry_rcu(pmu, &pmus, entry) {
		ctx = &per_cpu_ptr(pmu->pmu_cpu_context, cpu)->ctx;