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

Commit b38b24ea authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar
Browse files

perf, x86: Fix AMD hotplug & constraint initialization



Commit 3f6da390 ("perf: Rework and fix the arch CPU-hotplug hooks") moved
the amd northbridge allocation from CPUS_ONLINE to CPUS_PREPARE_UP
however amd_nb_id() doesn't work yet on prepare so it would simply bail
basically reverting to a state where we do not properly track node wide
constraints - causing weird perf results.

Fix up the AMD NorthBridge initialization code by allocating from
CPU_UP_PREPARE and installing it from CPU_STARTING once we have the
proper nb_id. It also properly deals with the allocation failing.

Signed-off-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
[ robustify using amd_has_nb() ]
Signed-off-by: default avatarStephane Eranian <eranian@google.com>
LKML-Reference: <1269353485.5109.48.camel@twins>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 85257024
Loading
Loading
Loading
Loading
+5 −3
Original line number Original line Diff line number Diff line
@@ -158,7 +158,7 @@ struct x86_pmu {
						 struct perf_event *event);
						 struct perf_event *event);
	struct event_constraint *event_constraints;
	struct event_constraint *event_constraints;


	void		(*cpu_prepare)(int cpu);
	int		(*cpu_prepare)(int cpu);
	void		(*cpu_starting)(int cpu);
	void		(*cpu_starting)(int cpu);
	void		(*cpu_dying)(int cpu);
	void		(*cpu_dying)(int cpu);
	void		(*cpu_dead)(int cpu);
	void		(*cpu_dead)(int cpu);
@@ -1333,11 +1333,12 @@ static int __cpuinit
x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu)
x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu)
{
{
	unsigned int cpu = (long)hcpu;
	unsigned int cpu = (long)hcpu;
	int ret = NOTIFY_OK;


	switch (action & ~CPU_TASKS_FROZEN) {
	switch (action & ~CPU_TASKS_FROZEN) {
	case CPU_UP_PREPARE:
	case CPU_UP_PREPARE:
		if (x86_pmu.cpu_prepare)
		if (x86_pmu.cpu_prepare)
			x86_pmu.cpu_prepare(cpu);
			ret = x86_pmu.cpu_prepare(cpu);
		break;
		break;


	case CPU_STARTING:
	case CPU_STARTING:
@@ -1350,6 +1351,7 @@ x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu)
			x86_pmu.cpu_dying(cpu);
			x86_pmu.cpu_dying(cpu);
		break;
		break;


	case CPU_UP_CANCELED:
	case CPU_DEAD:
	case CPU_DEAD:
		if (x86_pmu.cpu_dead)
		if (x86_pmu.cpu_dead)
			x86_pmu.cpu_dead(cpu);
			x86_pmu.cpu_dead(cpu);
@@ -1359,7 +1361,7 @@ x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu)
		break;
		break;
	}
	}


	return NOTIFY_OK;
	return ret;
}
}


static void __init pmu_check_apic(void)
static void __init pmu_check_apic(void)
+47 −33
Original line number Original line Diff line number Diff line
@@ -137,6 +137,13 @@ static inline int amd_is_nb_event(struct hw_perf_event *hwc)
	return (hwc->config & 0xe0) == 0xe0;
	return (hwc->config & 0xe0) == 0xe0;
}
}


static inline int amd_has_nb(struct cpu_hw_events *cpuc)
{
	struct amd_nb *nb = cpuc->amd_nb;

	return nb && nb->nb_id != -1;
}

static void amd_put_event_constraints(struct cpu_hw_events *cpuc,
static void amd_put_event_constraints(struct cpu_hw_events *cpuc,
				      struct perf_event *event)
				      struct perf_event *event)
{
{
@@ -147,7 +154,7 @@ static void amd_put_event_constraints(struct cpu_hw_events *cpuc,
	/*
	/*
	 * only care about NB events
	 * only care about NB events
	 */
	 */
	if (!(nb && amd_is_nb_event(hwc)))
	if (!(amd_has_nb(cpuc) && amd_is_nb_event(hwc)))
		return;
		return;


	/*
	/*
@@ -214,7 +221,7 @@ amd_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event)
	/*
	/*
	 * if not NB event or no NB, then no constraints
	 * if not NB event or no NB, then no constraints
	 */
	 */
	if (!(nb && amd_is_nb_event(hwc)))
	if (!(amd_has_nb(cpuc) && amd_is_nb_event(hwc)))
		return &unconstrained;
		return &unconstrained;


	/*
	/*
@@ -293,51 +300,55 @@ static struct amd_nb *amd_alloc_nb(int cpu, int nb_id)
	return nb;
	return nb;
}
}


static void amd_pmu_cpu_online(int cpu)
static int amd_pmu_cpu_prepare(int cpu)
{
	struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);

	WARN_ON_ONCE(cpuc->amd_nb);

	if (boot_cpu_data.x86_max_cores < 2)
		return NOTIFY_OK;

	cpuc->amd_nb = amd_alloc_nb(cpu, -1);
	if (!cpuc->amd_nb)
		return NOTIFY_BAD;

	return NOTIFY_OK;
}

static void amd_pmu_cpu_starting(int cpu)
{
{
	struct cpu_hw_events *cpu1, *cpu2;
	struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
	struct amd_nb *nb = NULL;
	struct amd_nb *nb;
	int i, nb_id;
	int i, nb_id;


	if (boot_cpu_data.x86_max_cores < 2)
	if (boot_cpu_data.x86_max_cores < 2)
		return;
		return;


	/*
	 * function may be called too early in the
	 * boot process, in which case nb_id is bogus
	 */
	nb_id = amd_get_nb_id(cpu);
	nb_id = amd_get_nb_id(cpu);
	if (nb_id == BAD_APICID)
	WARN_ON_ONCE(nb_id == BAD_APICID);
		return;

	cpu1 = &per_cpu(cpu_hw_events, cpu);
	cpu1->amd_nb = NULL;


	raw_spin_lock(&amd_nb_lock);
	raw_spin_lock(&amd_nb_lock);


	for_each_online_cpu(i) {
	for_each_online_cpu(i) {
		cpu2 = &per_cpu(cpu_hw_events, i);
		nb = per_cpu(cpu_hw_events, i).amd_nb;
		nb = cpu2->amd_nb;
		if (WARN_ON_ONCE(!nb))
		if (!nb)
			continue;
			continue;
		if (nb->nb_id == nb_id)
			goto found;
	}


	nb = amd_alloc_nb(cpu, nb_id);
		if (nb->nb_id == nb_id) {
	if (!nb) {
			kfree(cpuc->amd_nb);
		pr_err("perf_events: failed NB allocation for CPU%d\n", cpu);
			cpuc->amd_nb = nb;
		raw_spin_unlock(&amd_nb_lock);
			break;
		return;
		}
		}
found:
	}
	nb->refcnt++;

	cpu1->amd_nb = nb;
	cpuc->amd_nb->nb_id = nb_id;
	cpuc->amd_nb->refcnt++;


	raw_spin_unlock(&amd_nb_lock);
	raw_spin_unlock(&amd_nb_lock);
}
}


static void amd_pmu_cpu_offline(int cpu)
static void amd_pmu_cpu_dead(int cpu)
{
{
	struct cpu_hw_events *cpuhw;
	struct cpu_hw_events *cpuhw;


@@ -349,8 +360,10 @@ static void amd_pmu_cpu_offline(int cpu)
	raw_spin_lock(&amd_nb_lock);
	raw_spin_lock(&amd_nb_lock);


	if (cpuhw->amd_nb) {
	if (cpuhw->amd_nb) {
		if (--cpuhw->amd_nb->refcnt == 0)
		struct amd_nb *nb = cpuhw->amd_nb;
			kfree(cpuhw->amd_nb);

		if (nb->nb_id == -1 || --nb->refcnt == 0)
			kfree(nb);


		cpuhw->amd_nb = NULL;
		cpuhw->amd_nb = NULL;
	}
	}
@@ -379,8 +392,9 @@ static __initconst struct x86_pmu amd_pmu = {
	.get_event_constraints	= amd_get_event_constraints,
	.get_event_constraints	= amd_get_event_constraints,
	.put_event_constraints	= amd_put_event_constraints,
	.put_event_constraints	= amd_put_event_constraints,


	.cpu_prepare		= amd_pmu_cpu_online,
	.cpu_prepare		= amd_pmu_cpu_prepare,
	.cpu_dead		= amd_pmu_cpu_offline,
	.cpu_starting		= amd_pmu_cpu_starting,
	.cpu_dead		= amd_pmu_cpu_dead,
};
};


static __init int amd_pmu_init(void)
static __init int amd_pmu_init(void)