staticint arm_pmu_acpi_register_irq(int cpu)
{ struct acpi_madt_generic_interrupt *gicc; int gsi, trigger;
gicc = acpi_cpu_get_madt_gicc(cpu);
gsi = gicc->performance_interrupt;
/* * Per the ACPI spec, the MADT cannot describe a PMU that doesn't * have an interrupt. QEMU advertises this by using a GSI of zero, * which is not known to be valid on any hardware despite being * valid per the spec. Take the pragmatic approach and reject a * GSI of zero for now.
*/ if (!gsi) return 0;
/* * Helpfully, the MADT GICC doesn't have a polarity flag for the * "performance interrupt". Luckily, on compliant GICs the polarity is * a fixed value in HW (for both SPIs and PPIs) that we cannot change * from SW. * * Here we pass in ACPI_ACTIVE_HIGH to keep the core code happy. This * may not match the real polarity, but that should not matter. * * Other interrupt controllers are not supported with ACPI.
*/ return acpi_register_gsi(NULL, gsi, trigger, ACPI_ACTIVE_HIGH);
}
staticvoid arm_pmu_acpi_unregister_irq(int cpu)
{ struct acpi_madt_generic_interrupt *gicc; int gsi;
gicc = acpi_cpu_get_madt_gicc(cpu);
gsi = gicc->performance_interrupt; if (gsi)
acpi_unregister_gsi(gsi);
}
/* * Ensure that platform device must have IORESOURCE_IRQ * resource to hold gsi interrupt.
*/ if (pdev->num_resources != 1) return -ENXIO;
if (pdev->resource[0].flags != IORESOURCE_IRQ) return -ENXIO;
/* * Sanity check all the GICC tables for the same interrupt * number. For now, only support homogeneous ACPI machines.
*/
for_each_possible_cpu(cpu) { struct acpi_madt_generic_interrupt *gicc;
/* * For lack of a better place, hook the normal PMU MADT walk * and create a SPE device if we detect a recent MADT with * a homogeneous PPI mapping.
*/ staticvoid arm_spe_acpi_register_device(void)
{ int ret = arm_acpi_register_pmu_device(&spe_dev, ACPI_MADT_GICC_SPE,
arm_spe_parse_gsi); if (ret)
pr_warn("ACPI: SPE: Unable to register device\n");
} #else staticinlinevoid arm_spe_acpi_register_device(void)
{
} #endif/* CONFIG_ARM_SPE_PMU */
staticvoid arm_trbe_acpi_register_device(void)
{ int ret = arm_acpi_register_pmu_device(&trbe_dev, ACPI_MADT_GICC_TRBE,
arm_trbe_parse_gsi); if (ret)
pr_warn("ACPI: TRBE: Unable to register device\n");
} #else staticinlinevoid arm_trbe_acpi_register_device(void)
{
} #endif/* CONFIG_CORESIGHT_TRBE */
staticint arm_pmu_acpi_parse_irqs(void)
{ int irq, cpu, irq_cpu, err;
for_each_possible_cpu(cpu) {
irq = arm_pmu_acpi_register_irq(cpu); if (irq < 0) {
err = irq;
pr_warn("Unable to parse ACPI PMU IRQ for CPU%d: %d\n",
cpu, err); goto out_err;
} elseif (irq == 0) {
pr_warn("No ACPI PMU IRQ for CPU%d\n", cpu);
}
/* * Log and request the IRQ so the core arm_pmu code can manage * it. We'll have to sanity-check IRQs later when we associate * them with their PMUs.
*/
per_cpu(pmu_irqs, cpu) = irq;
err = armpmu_request_irq(irq, cpu); if (err) goto out_err;
}
return 0;
out_err:
for_each_possible_cpu(cpu) {
irq = per_cpu(pmu_irqs, cpu); if (!irq) continue;
arm_pmu_acpi_unregister_irq(cpu);
/* * Blat all copies of the IRQ so that we only unregister the * corresponding GSI once (e.g. when we have PPIs).
*/
for_each_possible_cpu(irq_cpu) { if (per_cpu(pmu_irqs, irq_cpu) == irq)
per_cpu(pmu_irqs, irq_cpu) = 0;
}
}
/* * Check whether the new IRQ is compatible with those already associated with * the PMU (e.g. we don't have mismatched PPIs).
*/ staticbool pmu_irq_matches(struct arm_pmu *pmu, int irq)
{ struct pmu_hw_events __percpu *hw_events = pmu->hw_events; int cpu;
if (!irq) returntrue;
for_each_cpu(cpu, &pmu->supported_cpus) { int other_irq = per_cpu(hw_events->irq, cpu); if (!other_irq) continue;
if (irq == other_irq) continue; if (!irq_is_percpu_devid(irq) && !irq_is_percpu_devid(other_irq)) continue;
/* * This must run before the common arm_pmu hotplug logic, so that we can * associate a CPU and its interrupt before the common code tries to manage the * affinity and so on. * * Note that hotplug events are serialized, so we cannot race with another CPU * coming up. The perf core won't open events while a hotplug event is in * progress.
*/ staticint arm_pmu_acpi_cpu_starting(unsignedint cpu)
{ struct arm_pmu *pmu;
/* If we've already probed this CPU, we have nothing to do */ if (per_cpu(probed_pmus, cpu)) return 0;
pmu = arm_pmu_acpi_find_pmu(); if (!pmu) {
pr_warn_ratelimited("Unable to associate CPU%d with a PMU\n",
cpu); return 0;
}
if (cpu_cpuid == cpuid)
arm_pmu_acpi_associate_pmu_cpu(pmu, cpu);
}
}
int arm_pmu_acpi_probe(armpmu_init_fn init_fn)
{ int pmu_idx = 0; unsignedint cpu; int ret;
ret = arm_pmu_acpi_parse_irqs(); if (ret) return ret;
ret = cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_ACPI_STARTING, "perf/arm/pmu_acpi:starting",
arm_pmu_acpi_cpu_starting, NULL); if (ret) return ret;
/* * Initialise and register the set of PMUs which we know about right * now. Ideally we'd do this in arm_pmu_acpi_cpu_starting() so that we * could handle late hotplug, but this may lead to deadlock since we * might try to register a hotplug notifier instance from within a * hotplug notifier. * * There's also the problem of having access to the right init_fn, * without tying this too deeply into the "real" PMU driver. * * For the moment, as with the platform/DT case, we need at least one * of a PMU's CPUs to be online at probe time.
*/
for_each_online_cpu(cpu) { struct arm_pmu *pmu = per_cpu(probed_pmus, cpu); unsignedlong cpuid; char *base_name;
/* If we've already probed this CPU, we have nothing to do */ if (pmu) continue;
pmu = armpmu_alloc(); if (!pmu) {
pr_warn("Unable to allocate PMU for CPU%d\n",
cpu); return -ENOMEM;
}
ret = init_fn(pmu); if (ret == -ENODEV) { /* PMU not handled by this driver, or not present */ continue;
} elseif (ret) {
pr_warn("Unable to initialise PMU for CPU%d\n", cpu); return ret;
}
base_name = pmu->name;
pmu->name = kasprintf(GFP_KERNEL, "%s_%d", base_name, pmu_idx++); if (!pmu->name) {
pr_warn("Unable to allocate PMU name for CPU%d\n", cpu); return -ENOMEM;
}
ret = armpmu_register(pmu); if (ret) {
pr_warn("Failed to register PMU for CPU%d\n", cpu);
kfree(pmu->name); return ret;
}
}
return ret;
}
staticint arm_pmu_acpi_init(void)
{ if (acpi_disabled) return 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.