Commit 4f089678 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Ingo Molnar

perf/x86/intel/uncore: Fix error handling

This driver lacks any form of proper error handling. If initialization fails
or hotplug prepare fails, it lets the facility with half initialized stuff
around.

Fix the state and memory leaks in a first step. As a second step we need to
undo the hardware state which is set via uncore_box_init() on some of the
uncore implementations.
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Cc: Andi Kleen <andi.kleen@intel.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Harish Chegondi <harish.chegondi@intel.com>
Cc: Jacob Pan <jacob.jun.pan@linux.intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Cc: linux-kernel@vger.kernel.org
Link: http://lkml.kernel.org/r/20160222221010.848880559@linutronix.deSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent ffeda003
...@@ -38,6 +38,16 @@ int uncore_pcibus_to_physid(struct pci_bus *bus) ...@@ -38,6 +38,16 @@ int uncore_pcibus_to_physid(struct pci_bus *bus)
return phys_id; return phys_id;
} }
static void uncore_free_pcibus_map(void)
{
struct pci2phy_map *map, *tmp;
list_for_each_entry_safe(map, tmp, &pci2phy_map_head, list) {
list_del(&map->list);
kfree(map);
}
}
struct pci2phy_map *__find_pci2phy_map(int segment) struct pci2phy_map *__find_pci2phy_map(int segment)
{ {
struct pci2phy_map *map, *alloc = NULL; struct pci2phy_map *map, *alloc = NULL;
...@@ -760,16 +770,28 @@ static int uncore_pmu_register(struct intel_uncore_pmu *pmu) ...@@ -760,16 +770,28 @@ static int uncore_pmu_register(struct intel_uncore_pmu *pmu)
} }
ret = perf_pmu_register(&pmu->pmu, pmu->name, -1); ret = perf_pmu_register(&pmu->pmu, pmu->name, -1);
if (!ret)
pmu->registered = true;
return ret; return ret;
} }
static void uncore_pmu_unregister(struct intel_uncore_pmu *pmu)
{
if (!pmu->registered)
return;
perf_pmu_unregister(&pmu->pmu);
pmu->registered = false;
}
static void __init uncore_type_exit(struct intel_uncore_type *type) static void __init uncore_type_exit(struct intel_uncore_type *type)
{ {
int i; int i;
if (type->pmus) { if (type->pmus) {
for (i = 0; i < type->num_boxes; i++) for (i = 0; i < type->num_boxes; i++) {
uncore_pmu_unregister(&type->pmus[i]);
free_percpu(type->pmus[i].box); free_percpu(type->pmus[i].box);
}
kfree(type->pmus); kfree(type->pmus);
type->pmus = NULL; type->pmus = NULL;
} }
...@@ -856,8 +878,8 @@ static int uncore_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id ...@@ -856,8 +878,8 @@ static int uncore_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id
struct intel_uncore_pmu *pmu; struct intel_uncore_pmu *pmu;
struct intel_uncore_box *box; struct intel_uncore_box *box;
struct intel_uncore_type *type; struct intel_uncore_type *type;
int phys_id;
bool first_box = false; bool first_box = false;
int phys_id, ret;
phys_id = uncore_pcibus_to_physid(pdev->bus); phys_id = uncore_pcibus_to_physid(pdev->bus);
if (phys_id < 0) if (phys_id < 0)
...@@ -906,9 +928,18 @@ static int uncore_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id ...@@ -906,9 +928,18 @@ static int uncore_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id
list_add_tail(&box->list, &pmu->box_list); list_add_tail(&box->list, &pmu->box_list);
raw_spin_unlock(&uncore_box_lock); raw_spin_unlock(&uncore_box_lock);
if (first_box) if (!first_box)
uncore_pmu_register(pmu); return 0;
return 0;
ret = uncore_pmu_register(pmu);
if (ret) {
pci_set_drvdata(pdev, NULL);
raw_spin_lock(&uncore_box_lock);
list_del(&box->list);
raw_spin_unlock(&uncore_box_lock);
kfree(box);
}
return ret;
} }
static void uncore_pci_remove(struct pci_dev *pdev) static void uncore_pci_remove(struct pci_dev *pdev)
...@@ -954,7 +985,7 @@ static void uncore_pci_remove(struct pci_dev *pdev) ...@@ -954,7 +985,7 @@ static void uncore_pci_remove(struct pci_dev *pdev)
kfree(box); kfree(box);
if (last_box) if (last_box)
perf_pmu_unregister(&pmu->pmu); uncore_pmu_unregister(pmu);
} }
static int __init uncore_pci_init(void) static int __init uncore_pci_init(void)
...@@ -1018,6 +1049,7 @@ static int __init uncore_pci_init(void) ...@@ -1018,6 +1049,7 @@ static int __init uncore_pci_init(void)
err: err:
uncore_types_exit(uncore_pci_uncores); uncore_types_exit(uncore_pci_uncores);
uncore_pci_uncores = empty_uncore; uncore_pci_uncores = empty_uncore;
uncore_free_pcibus_map();
return ret; return ret;
} }
...@@ -1027,6 +1059,7 @@ static void __init uncore_pci_exit(void) ...@@ -1027,6 +1059,7 @@ static void __init uncore_pci_exit(void)
pcidrv_registered = false; pcidrv_registered = false;
pci_unregister_driver(uncore_pci_driver); pci_unregister_driver(uncore_pci_driver);
uncore_types_exit(uncore_pci_uncores); uncore_types_exit(uncore_pci_uncores);
uncore_free_pcibus_map();
} }
} }
...@@ -1223,8 +1256,7 @@ static int uncore_cpu_notifier(struct notifier_block *self, ...@@ -1223,8 +1256,7 @@ static int uncore_cpu_notifier(struct notifier_block *self,
/* allocate/free data structure for uncore box */ /* allocate/free data structure for uncore box */
switch (action & ~CPU_TASKS_FROZEN) { switch (action & ~CPU_TASKS_FROZEN) {
case CPU_UP_PREPARE: case CPU_UP_PREPARE:
uncore_cpu_prepare(cpu, -1); return notifier_from_errno(uncore_cpu_prepare(cpu, -1));
break;
case CPU_STARTING: case CPU_STARTING:
uncore_cpu_starting(cpu); uncore_cpu_starting(cpu);
break; break;
...@@ -1265,9 +1297,29 @@ static struct notifier_block uncore_cpu_nb = { ...@@ -1265,9 +1297,29 @@ static struct notifier_block uncore_cpu_nb = {
.priority = CPU_PRI_PERF + 1, .priority = CPU_PRI_PERF + 1,
}; };
static void __init uncore_cpu_setup(void *dummy) static int __init type_pmu_register(struct intel_uncore_type *type)
{ {
uncore_cpu_starting(smp_processor_id()); int i, ret;
for (i = 0; i < type->num_boxes; i++) {
ret = uncore_pmu_register(&type->pmus[i]);
if (ret)
return ret;
}
return 0;
}
static int __init uncore_msr_pmus_register(void)
{
struct intel_uncore_type **types = uncore_msr_uncores;
int ret;
while (*types) {
ret = type_pmu_register(*types++);
if (ret)
return ret;
}
return 0;
} }
static int __init uncore_cpu_init(void) static int __init uncore_cpu_init(void)
...@@ -1315,6 +1367,10 @@ static int __init uncore_cpu_init(void) ...@@ -1315,6 +1367,10 @@ static int __init uncore_cpu_init(void)
} }
ret = uncore_types_init(uncore_msr_uncores); ret = uncore_types_init(uncore_msr_uncores);
if (ret)
goto err;
ret = uncore_msr_pmus_register();
if (ret) if (ret)
goto err; goto err;
return 0; return 0;
...@@ -1324,26 +1380,14 @@ static int __init uncore_cpu_init(void) ...@@ -1324,26 +1380,14 @@ static int __init uncore_cpu_init(void)
return ret; return ret;
} }
static int __init uncore_pmus_register(void) static void __init uncore_cpu_setup(void *dummy)
{ {
struct intel_uncore_pmu *pmu; uncore_cpu_starting(smp_processor_id());
struct intel_uncore_type *type;
int i, j;
for (i = 0; uncore_msr_uncores[i]; i++) {
type = uncore_msr_uncores[i];
for (j = 0; j < type->num_boxes; j++) {
pmu = &type->pmus[j];
uncore_pmu_register(pmu);
}
}
return 0;
} }
static void __init uncore_cpumask_init(void) static int __init uncore_cpumask_init(void)
{ {
int cpu; int cpu, ret = 0;
cpu_notifier_register_begin(); cpu_notifier_register_begin();
...@@ -1359,17 +1403,20 @@ static void __init uncore_cpumask_init(void) ...@@ -1359,17 +1403,20 @@ static void __init uncore_cpumask_init(void)
if (phys_id < 0) if (phys_id < 0)
continue; continue;
uncore_cpu_prepare(cpu, phys_id); ret = uncore_cpu_prepare(cpu, phys_id);
if (ret)
goto out;
uncore_event_init_cpu(cpu); uncore_event_init_cpu(cpu);
} }
on_each_cpu(uncore_cpu_setup, NULL, 1); on_each_cpu(uncore_cpu_setup, NULL, 1);
__register_cpu_notifier(&uncore_cpu_nb); __register_cpu_notifier(&uncore_cpu_nb);
out:
cpu_notifier_register_done(); cpu_notifier_register_done();
return ret;
} }
static int __init intel_uncore_init(void) static int __init intel_uncore_init(void)
{ {
int ret; int ret;
...@@ -1382,17 +1429,20 @@ static int __init intel_uncore_init(void) ...@@ -1382,17 +1429,20 @@ static int __init intel_uncore_init(void)
ret = uncore_pci_init(); ret = uncore_pci_init();
if (ret) if (ret)
goto fail; return ret;
ret = uncore_cpu_init(); ret = uncore_cpu_init();
if (ret) { if (ret)
uncore_pci_exit(); goto errpci;
goto fail; ret = uncore_cpumask_init();
} if (ret)
uncore_cpumask_init(); goto errcpu;
uncore_pmus_register();
return 0; return 0;
fail:
errcpu:
uncore_types_exit(uncore_msr_uncores);
errpci:
uncore_pci_exit();
return ret; return ret;
} }
device_initcall(intel_uncore_init); device_initcall(intel_uncore_init);
...@@ -73,13 +73,14 @@ struct intel_uncore_ops { ...@@ -73,13 +73,14 @@ struct intel_uncore_ops {
}; };
struct intel_uncore_pmu { struct intel_uncore_pmu {
struct pmu pmu; struct pmu pmu;
char name[UNCORE_PMU_NAME_LEN]; char name[UNCORE_PMU_NAME_LEN];
int pmu_idx; int pmu_idx;
int func_id; int func_id;
struct intel_uncore_type *type; bool registered;
struct intel_uncore_box ** __percpu box; struct intel_uncore_type *type;
struct list_head box_list; struct intel_uncore_box ** __percpu box;
struct list_head box_list;
}; };
struct intel_uncore_extra_reg { struct intel_uncore_extra_reg {
......
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