From patchwork Fri Nov 7 16:25:34 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Rutland X-Patchwork-Id: 40438 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-lb0-f198.google.com (mail-lb0-f198.google.com [209.85.217.198]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id C1CDE20D85 for ; Fri, 7 Nov 2014 16:27:57 +0000 (UTC) Received: by mail-lb0-f198.google.com with SMTP id 10sf2088762lbg.5 for ; Fri, 07 Nov 2014 08:27:56 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe; bh=oR6jl74MtCEErk7KZdhH3OWdBOeO4BGDL0Pj3i9umCI=; b=FxGQCaQsrCXacs9PnMXDIjuy5/TLaB2GyxtGPTrymfFgU+9Kc87e//w1b7+DFKRsl/ zKxM0NchiOovb64I6QXnN8TwfS3CL2/i2J3I165O4VtuzMnIhyHj+vdNLGKWKIBnt55e xWOA26t8TSlZ/E57Ths9xEn8Uwp/5znuuoam7oJ6+HYD5UP/jR6z/rW76Z8XZjirT6ST ZORyKUA2bjyXpG76PirP61mNYq+YtDcgOkQq/3LBhLhjQMjRRvy99P7SHkupdWwtZyM8 O9dl/uZvYnx5AqCXYsO72TxsDdh826TQjPmXC20J10YW5o45UbQBtBJu75mbQY0dqu8S jQeQ== X-Gm-Message-State: ALoCoQmZ+A0fbLM3mwFwx2nzoNFi/gU+7Xl67PnKlz/ifSeLSkQzGLzK2sQEQ3KNG+Fw5xuBrkIj X-Received: by 10.112.138.202 with SMTP id qs10mr2129101lbb.5.1415377676619; Fri, 07 Nov 2014 08:27:56 -0800 (PST) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.26.228 with SMTP id o4ls212168lag.66.gmail; Fri, 07 Nov 2014 08:27:56 -0800 (PST) X-Received: by 10.112.185.68 with SMTP id fa4mr3553022lbc.84.1415377676246; Fri, 07 Nov 2014 08:27:56 -0800 (PST) Received: from mail-lb0-f176.google.com (mail-lb0-f176.google.com. [209.85.217.176]) by mx.google.com with ESMTPS id d5si15528548laf.110.2014.11.07.08.27.56 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Fri, 07 Nov 2014 08:27:56 -0800 (PST) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.176 as permitted sender) client-ip=209.85.217.176; Received: by mail-lb0-f176.google.com with SMTP id 10so2976013lbg.35 for ; Fri, 07 Nov 2014 08:27:56 -0800 (PST) X-Received: by 10.152.42.226 with SMTP id r2mr12178727lal.29.1415377676148; Fri, 07 Nov 2014 08:27:56 -0800 (PST) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.184.201 with SMTP id ew9csp228482lbc; Fri, 7 Nov 2014 08:27:55 -0800 (PST) X-Received: by 10.66.119.70 with SMTP id ks6mr13389233pab.74.1415377673632; Fri, 07 Nov 2014 08:27:53 -0800 (PST) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id yz12si9480358pac.125.2014.11.07.08.27.53 for ; Fri, 07 Nov 2014 08:27:53 -0800 (PST) Received-SPF: none (google.com: linux-kernel-owner@vger.kernel.org does not designate permitted sender hosts) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752873AbaKGQ1e (ORCPT + 25 others); Fri, 7 Nov 2014 11:27:34 -0500 Received: from cam-admin0.cambridge.arm.com ([217.140.96.50]:48323 "EHLO cam-admin0.cambridge.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752837AbaKGQ13 (ORCPT ); Fri, 7 Nov 2014 11:27:29 -0500 Received: from leverpostej.cambridge.arm.com (leverpostej.cambridge.arm.com [10.1.205.151]) by cam-admin0.cambridge.arm.com (8.12.6/8.12.6) with ESMTP id sA7GPwwx014702; Fri, 7 Nov 2014 16:27:05 GMT From: Mark Rutland To: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org, will.deacon@arm.com, Mark Rutland Subject: [PATCH 09/11] arm: perf: parse cpu affinity from dt Date: Fri, 7 Nov 2014 16:25:34 +0000 Message-Id: <1415377536-12841-10-git-send-email-mark.rutland@arm.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1415377536-12841-1-git-send-email-mark.rutland@arm.com> References: <1415377536-12841-1-git-send-email-mark.rutland@arm.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: mark.rutland@arm.com X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.176 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , The current way we read interrupts form devicetree assumes that interrupts are in increasing order of logical cpu id (MPIDR.Aff{2,1,0}), and that these logical ids are in a contiguous block. This may not be the case in general - after a kexec cpu ids may be arbitrarily assigned, and multi-cluster systems do not have a contiguous range of cpu ids. This patch parses cpu affinity information for interrupts from an optional "interrupts-affinity" devicetree property described in the devicetree binding document. Support for existing dts and board files remains. Signed-off-by: Mark Rutland --- arch/arm/include/asm/pmu.h | 12 +++ arch/arm/kernel/perf_event_cpu.c | 196 +++++++++++++++++++++++++++++---------- 2 files changed, 161 insertions(+), 47 deletions(-) diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index b630a44..92fc1da 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h @@ -12,6 +12,7 @@ #ifndef __ARM_PMU_H__ #define __ARM_PMU_H__ +#include #include #include @@ -89,6 +90,15 @@ struct pmu_hw_events { struct arm_pmu *percpu_pmu; }; +/* + * For systems with heterogeneous PMUs, we need to know which CPUs each + * (possibly percpu) IRQ targets. Map between them with an array of these. + */ +struct cpu_irq { + cpumask_t cpus; + int irq; +}; + struct arm_pmu { struct pmu pmu; cpumask_t active_irqs; @@ -118,6 +128,8 @@ struct arm_pmu { struct platform_device *plat_device; struct pmu_hw_events __percpu *hw_events; struct notifier_block hotplug_nb; + int nr_irqs; + struct cpu_irq *irq_map; }; #define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu)) diff --git a/arch/arm/kernel/perf_event_cpu.c b/arch/arm/kernel/perf_event_cpu.c index dfcaba5..f09c8a0 100644 --- a/arch/arm/kernel/perf_event_cpu.c +++ b/arch/arm/kernel/perf_event_cpu.c @@ -85,20 +85,27 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu) struct platform_device *pmu_device = cpu_pmu->plat_device; struct pmu_hw_events __percpu *hw_events = cpu_pmu->hw_events; - irqs = min(pmu_device->num_resources, num_possible_cpus()); + irqs = cpu_pmu->nr_irqs; - irq = platform_get_irq(pmu_device, 0); - if (irq >= 0 && irq_is_percpu(irq)) { - on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1); - free_percpu_irq(irq, &hw_events->percpu_pmu); - } else { - for (i = 0; i < irqs; ++i) { - if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs)) - continue; - irq = platform_get_irq(pmu_device, i); - if (irq >= 0) - free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, i)); + for (i = 0; i < irqs; i++) { + struct cpu_irq *map = &cpu_pmu->irq_map[i]; + irq = map->irq; + + if (irq <= 0) + continue; + + if (irq_is_percpu(irq)) { + on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1); + free_percpu_irq(irq, &hw_events->percpu_pmu); + return; } + + if (!cpumask_test_and_clear_cpu(i, &cpu_pmu->active_irqs)) + continue; + + irq = platform_get_irq(pmu_device, i); + if (irq >= 0) + free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, i)); } } @@ -111,51 +118,52 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler) if (!pmu_device) return -ENODEV; - irqs = min(pmu_device->num_resources, num_possible_cpus()); + irqs = cpu_pmu->nr_irqs; if (irqs < 1) { printk_once("perf/ARM: No irqs for PMU defined, sampling events not supported\n"); return 0; } - irq = platform_get_irq(pmu_device, 0); - if (irq >= 0 && irq_is_percpu(irq)) { - err = request_percpu_irq(irq, handler, "arm-pmu", - &hw_events->percpu_pmu); - if (err) { - pr_err("unable to request IRQ%d for ARM PMU counters\n", - irq); - return err; - } - on_each_cpu(cpu_pmu_enable_percpu_irq, &irq, 1); - } else { - for (i = 0; i < irqs; ++i) { - err = 0; - irq = platform_get_irq(pmu_device, i); - if (irq < 0) - continue; - - /* - * If we have a single PMU interrupt that we can't shift, - * assume that we're running on a uniprocessor machine and - * continue. Otherwise, continue without this interrupt. - */ - if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { - pr_warn("unable to set irq affinity (irq=%d, cpu=%u)\n", - irq, i); - continue; - } + for (i = 0; i < irqs; i++) { + struct cpu_irq *map = &cpu_pmu->irq_map[i]; + irq = map->irq; - err = request_irq(irq, handler, - IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu", - per_cpu_ptr(&hw_events->percpu_pmu, i)); + if (irq <= 0) + continue; + + if (irq_is_percpu(map->irq)) { + err = request_percpu_irq(irq, handler, "arm-pmu", + &hw_events->percpu_pmu); if (err) { pr_err("unable to request IRQ%d for ARM PMU counters\n", irq); return err; } + on_each_cpu(cpu_pmu_enable_percpu_irq, &irq, 1); + return 0; + } + + /* + * If we have a single PMU interrupt that we can't shift, + * assume that we're running on a uniprocessor machine and + * continue. Otherwise, continue without this interrupt. + */ + if (irq_set_affinity(irq, &map->cpus) && irqs > 1) { + pr_warn("unable to set irq affinity (irq=%d, cpu=%u)\n", + irq, cpumask_first(&map->cpus)); + continue; + } - cpumask_set_cpu(i, &cpu_pmu->active_irqs); + err = request_irq(irq, handler, + IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu", + per_cpu_ptr(&hw_events->percpu_pmu, i)); + if (err) { + pr_err("unable to request IRQ%d for ARM PMU counters\n", + irq); + return err; } + + cpumask_set_cpu(i, &cpu_pmu->active_irqs); } return 0; @@ -421,6 +429,97 @@ static int arm_dt_affine_get_mask(struct device_node *node, char *prop, return ret; } +static int cpu_pmu_parse_interrupt(struct arm_pmu *pmu, int idx) +{ + struct cpu_irq *map = &pmu->irq_map[idx]; + struct platform_device *pdev = pmu->plat_device; + struct device_node *np = pdev->dev.of_node; + + map->irq = platform_get_irq(pdev, idx); + if (map->irq <= 0) + return -ENOENT; + + cpumask_clear(&map->cpus); + + if (!of_property_read_bool(np, "interrupts-affinity")) { + /* + * If we don't have any affinity information, assume a + * homogeneous system. We assume that CPUs are ordered as in + * the DT, even in the absence of affinity information. + */ + if (irq_is_percpu(map->irq)) + cpumask_setall(&map->cpus); + else + cpumask_set_cpu(idx, &map->cpus); + } else { + return arm_dt_affine_get_mask(np, "interrupts-affinity", idx, + &map->cpus); + } + + return 0; +} + +static int cpu_pmu_parse_interrupts(struct arm_pmu *pmu) +{ + struct platform_device *pdev = pmu->plat_device; + int ret; + int i, irqs; + + /* + * Figure out how many IRQs there are. This may be larger than NR_CPUS, + * and this may be in any arbitrary order... + */ + for (irqs = 0; platform_get_irq(pdev, irqs) > 0; irqs++); + if (!irqs) { + pr_warn("Unable to find interrupts\n"); + return -EINVAL; + } + + pmu->nr_irqs = irqs; + pmu->irq_map = kmalloc_array(irqs, sizeof(*pmu->irq_map), GFP_KERNEL); + if (!pmu->irq_map) { + pr_warn("Unable to allocate irqmap data\n"); + return -ENOMEM; + } + + /* + * Some platforms are insane enough to mux all the PMU IRQs into a + * single IRQ. To enable handling of those cases, assume that if we + * have a single interrupt it targets all CPUs. + */ + if (irqs == 1 && num_possible_cpus() > 1) { + cpumask_copy(&pmu->irq_map[0].cpus, cpu_present_mask); + } else { + for (i = 0; i < irqs; i++) { + ret = cpu_pmu_parse_interrupt(pmu, i); + if (ret) + goto out_free; + } + } + + if (of_property_read_bool(pdev->dev.of_node, "interrupts-affinity")) { + /* The PMU can work on any CPU it has an interrupt. */ + for (i = 0; i < irqs; i++) { + struct cpu_irq *map = &pmu->irq_map[i]; + cpumask_or(&pmu->supported_cpus, &pmu->supported_cpus, + &map->cpus); + } + } else { + /* + * Without affintiy info, assume a homogeneous system with + * potentially missing interrupts, to keep existing DTBs + * working. + */ + cpumask_setall(&pmu->supported_cpus); + } + + return 0; + +out_free: + kfree(pmu->irq_map); + return ret; +} + static int cpu_pmu_device_probe(struct platform_device *pdev) { const struct of_device_id *of_id; @@ -443,8 +542,9 @@ static int cpu_pmu_device_probe(struct platform_device *pdev) cpu_pmu = pmu; cpu_pmu->plat_device = pdev; - /* Assume by default that we're on a homogeneous system */ - cpumask_setall(&pmu->supported_cpus); + ret = cpu_pmu_parse_interrupts(pmu); + if (ret) + goto out_free_pmu; if (node && (of_id = of_match_node(cpu_pmu_of_device_ids, pdev->dev.of_node))) { init_fn = of_id->data; @@ -471,8 +571,10 @@ static int cpu_pmu_device_probe(struct platform_device *pdev) out_destroy: cpu_pmu_destroy(cpu_pmu); out_free: - pr_info("failed to register PMU devices!\n"); + kfree(pmu->irq_map); +out_free_pmu: kfree(pmu); + pr_info("failed to register PMU devices!\n"); return ret; }