.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Perf support for the Statistical Profiling Extension, introduced as |
---|
3 | 4 | * part of ARMv8.2. |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or modify |
---|
6 | | - * it under the terms of the GNU General Public License version 2 as |
---|
7 | | - * published by the Free Software Foundation. |
---|
8 | | - * |
---|
9 | | - * This program is distributed in the hope that it will be useful, |
---|
10 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
12 | | - * GNU General Public License for more details. |
---|
13 | | - * |
---|
14 | | - * You should have received a copy of the GNU General Public License |
---|
15 | | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
16 | 5 | * |
---|
17 | 6 | * Copyright (C) 2016 ARM Limited |
---|
18 | 7 | * |
---|
.. | .. |
---|
38 | 27 | #include <linux/of_address.h> |
---|
39 | 28 | #include <linux/of_device.h> |
---|
40 | 29 | #include <linux/perf_event.h> |
---|
| 30 | +#include <linux/perf/arm_pmu.h> |
---|
41 | 31 | #include <linux/platform_device.h> |
---|
42 | 32 | #include <linux/printk.h> |
---|
43 | 33 | #include <linux/slab.h> |
---|
.. | .. |
---|
48 | 38 | #include <asm/cpufeature.h> |
---|
49 | 39 | #include <asm/mmu.h> |
---|
50 | 40 | #include <asm/sysreg.h> |
---|
| 41 | + |
---|
| 42 | +/* |
---|
| 43 | + * Cache if the event is allowed to trace Context information. |
---|
| 44 | + * This allows us to perform the check, i.e, perfmon_capable(), |
---|
| 45 | + * in the context of the event owner, once, during the event_init(). |
---|
| 46 | + */ |
---|
| 47 | +#define SPE_PMU_HW_FLAGS_CX BIT(0) |
---|
| 48 | + |
---|
| 49 | +static void set_spe_event_has_cx(struct perf_event *event) |
---|
| 50 | +{ |
---|
| 51 | + if (IS_ENABLED(CONFIG_PID_IN_CONTEXTIDR) && perfmon_capable()) |
---|
| 52 | + event->hw.flags |= SPE_PMU_HW_FLAGS_CX; |
---|
| 53 | +} |
---|
| 54 | + |
---|
| 55 | +static bool get_spe_event_has_cx(struct perf_event *event) |
---|
| 56 | +{ |
---|
| 57 | + return !!(event->hw.flags & SPE_PMU_HW_FLAGS_CX); |
---|
| 58 | +} |
---|
51 | 59 | |
---|
52 | 60 | #define ARM_SPE_BUF_PAD_BYTE 0 |
---|
53 | 61 | |
---|
.. | .. |
---|
284 | 292 | if (!attr->exclude_kernel) |
---|
285 | 293 | reg |= BIT(SYS_PMSCR_EL1_E1SPE_SHIFT); |
---|
286 | 294 | |
---|
287 | | - if (IS_ENABLED(CONFIG_PID_IN_CONTEXTIDR) && capable(CAP_SYS_ADMIN)) |
---|
| 295 | + if (get_spe_event_has_cx(event)) |
---|
288 | 296 | reg |= BIT(SYS_PMSCR_EL1_CX_SHIFT); |
---|
289 | 297 | |
---|
290 | 298 | return reg; |
---|
.. | .. |
---|
709 | 717 | !(spe_pmu->features & SPE_PMU_FEAT_FILT_LAT)) |
---|
710 | 718 | return -EOPNOTSUPP; |
---|
711 | 719 | |
---|
| 720 | + set_spe_event_has_cx(event); |
---|
712 | 721 | reg = arm_spe_event_to_pmscr(event); |
---|
713 | | - if (!capable(CAP_SYS_ADMIN) && |
---|
| 722 | + if (!perfmon_capable() && |
---|
714 | 723 | (reg & (BIT(SYS_PMSCR_EL1_PA_SHIFT) | |
---|
715 | | - BIT(SYS_PMSCR_EL1_CX_SHIFT) | |
---|
716 | 724 | BIT(SYS_PMSCR_EL1_PCT_SHIFT)))) |
---|
717 | 725 | return -EACCES; |
---|
718 | 726 | |
---|
.. | .. |
---|
841 | 849 | * parts and give userspace a fighting chance of getting some |
---|
842 | 850 | * useful data out of it. |
---|
843 | 851 | */ |
---|
844 | | - if (!nr_pages || (snapshot && (nr_pages & 1))) |
---|
| 852 | + if (snapshot && (nr_pages & 1)) |
---|
845 | 853 | return NULL; |
---|
846 | 854 | |
---|
847 | 855 | if (cpu == -1) |
---|
.. | .. |
---|
855 | 863 | if (!pglist) |
---|
856 | 864 | goto out_free_buf; |
---|
857 | 865 | |
---|
858 | | - for (i = 0; i < nr_pages; ++i) { |
---|
859 | | - struct page *page = virt_to_page(pages[i]); |
---|
860 | | - |
---|
861 | | - if (PagePrivate(page)) { |
---|
862 | | - pr_warn("unexpected high-order page for auxbuf!"); |
---|
863 | | - goto out_free_pglist; |
---|
864 | | - } |
---|
865 | | - |
---|
| 866 | + for (i = 0; i < nr_pages; ++i) |
---|
866 | 867 | pglist[i] = virt_to_page(pages[i]); |
---|
867 | | - } |
---|
868 | 868 | |
---|
869 | 869 | buf->base = vmap(pglist, nr_pages, VM_MAP, PAGE_KERNEL); |
---|
870 | 870 | if (!buf->base) |
---|
.. | .. |
---|
1020 | 1020 | default: |
---|
1021 | 1021 | dev_warn(dev, "unknown PMSIDR_EL1.Interval [%d]; assuming 8\n", |
---|
1022 | 1022 | fld); |
---|
1023 | | - /* Fallthrough */ |
---|
| 1023 | + fallthrough; |
---|
1024 | 1024 | case 8: |
---|
1025 | 1025 | spe_pmu->min_period = 4096; |
---|
1026 | 1026 | } |
---|
.. | .. |
---|
1039 | 1039 | default: |
---|
1040 | 1040 | dev_warn(dev, "unknown PMSIDR_EL1.CountSize [%d]; assuming 2\n", |
---|
1041 | 1041 | fld); |
---|
1042 | | - /* Fallthrough */ |
---|
| 1042 | + fallthrough; |
---|
1043 | 1043 | case 2: |
---|
1044 | 1044 | spe_pmu->counter_sz = 12; |
---|
1045 | 1045 | } |
---|
.. | .. |
---|
1151 | 1151 | struct platform_device *pdev = spe_pmu->pdev; |
---|
1152 | 1152 | int irq = platform_get_irq(pdev, 0); |
---|
1153 | 1153 | |
---|
1154 | | - if (irq < 0) { |
---|
1155 | | - dev_err(&pdev->dev, "failed to get IRQ (%d)\n", irq); |
---|
| 1154 | + if (irq < 0) |
---|
1156 | 1155 | return -ENXIO; |
---|
1157 | | - } |
---|
1158 | 1156 | |
---|
1159 | 1157 | if (!irq_is_percpu(irq)) { |
---|
1160 | 1158 | dev_err(&pdev->dev, "expected PPI but got SPI (%d)\n", irq); |
---|
.. | .. |
---|
1174 | 1172 | { .compatible = "arm,statistical-profiling-extension-v1", .data = (void *)1 }, |
---|
1175 | 1173 | { /* Sentinel */ }, |
---|
1176 | 1174 | }; |
---|
| 1175 | +MODULE_DEVICE_TABLE(of, arm_spe_pmu_of_match); |
---|
1177 | 1176 | |
---|
1178 | | -static int arm_spe_pmu_device_dt_probe(struct platform_device *pdev) |
---|
| 1177 | +static const struct platform_device_id arm_spe_match[] = { |
---|
| 1178 | + { ARMV8_SPE_PDEV_NAME, 0}, |
---|
| 1179 | + { } |
---|
| 1180 | +}; |
---|
| 1181 | +MODULE_DEVICE_TABLE(platform, arm_spe_match); |
---|
| 1182 | + |
---|
| 1183 | +static int arm_spe_pmu_device_probe(struct platform_device *pdev) |
---|
1179 | 1184 | { |
---|
1180 | 1185 | int ret; |
---|
1181 | 1186 | struct arm_spe_pmu *spe_pmu; |
---|
.. | .. |
---|
1235 | 1240 | } |
---|
1236 | 1241 | |
---|
1237 | 1242 | static struct platform_driver arm_spe_pmu_driver = { |
---|
| 1243 | + .id_table = arm_spe_match, |
---|
1238 | 1244 | .driver = { |
---|
1239 | 1245 | .name = DRVNAME, |
---|
1240 | 1246 | .of_match_table = of_match_ptr(arm_spe_pmu_of_match), |
---|
| 1247 | + .suppress_bind_attrs = true, |
---|
1241 | 1248 | }, |
---|
1242 | | - .probe = arm_spe_pmu_device_dt_probe, |
---|
| 1249 | + .probe = arm_spe_pmu_device_probe, |
---|
1243 | 1250 | .remove = arm_spe_pmu_device_remove, |
---|
1244 | 1251 | }; |
---|
1245 | 1252 | |
---|