Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drivers/perf: xgene: Add CPU hotplug support

If the CPU assigned to the xgene PMU is taken offline, then subsequent
perf invocations on the PMU will fail:

# echo 0 > /sys/devices/system/cpu/cpu0/online
# perf stat -a -e l3c0/cycle-count/,l3c0/write/ sleep 1
Error:
The sys_perf_event_open() syscall returned with 19 (No such device) for event (l3c0/cycle-count/).
/bin/dmesg may provide additional information.
No CONFIG_PERF_EVENTS=y kernel support configured?

This patch implements a hotplug notifier in the xgene PMU driver so that
the PMU context is migrated to another online CPU should its assigned
CPU disappear.

Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Hoan Tran <hoan.tran@amperecomputing.com>
[will: Made naming of new cpuhp_state enum entry consistent]
Signed-off-by: Will Deacon <will.deacon@arm.com>

authored by

Hoan Tran and committed by
Will Deacon
cbb72a3c 472dc9fa

+74 -7
+73 -7
drivers/perf/xgene_pmu.c
··· 21 21 22 22 #include <linux/acpi.h> 23 23 #include <linux/clk.h> 24 + #include <linux/cpuhotplug.h> 24 25 #include <linux/cpumask.h> 25 26 #include <linux/interrupt.h> 26 27 #include <linux/io.h> ··· 131 130 132 131 struct xgene_pmu { 133 132 struct device *dev; 133 + struct hlist_node node; 134 134 int version; 135 135 void __iomem *pcppmu_csr; 136 136 u32 mcb_active_mask; 137 137 u32 mc_active_mask; 138 138 u32 l3c_active_mask; 139 139 cpumask_t cpu; 140 + int irq; 140 141 raw_spinlock_t lock; 141 142 const struct xgene_pmu_ops *ops; 142 143 struct list_head l3cpmus; ··· 1809 1806 MODULE_DEVICE_TABLE(acpi, xgene_pmu_acpi_match); 1810 1807 #endif 1811 1808 1809 + static int xgene_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) 1810 + { 1811 + struct xgene_pmu *xgene_pmu = hlist_entry_safe(node, struct xgene_pmu, 1812 + node); 1813 + 1814 + if (cpumask_empty(&xgene_pmu->cpu)) 1815 + cpumask_set_cpu(cpu, &xgene_pmu->cpu); 1816 + 1817 + /* Overflow interrupt also should use the same CPU */ 1818 + WARN_ON(irq_set_affinity(xgene_pmu->irq, &xgene_pmu->cpu)); 1819 + 1820 + return 0; 1821 + } 1822 + 1823 + static int xgene_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) 1824 + { 1825 + struct xgene_pmu *xgene_pmu = hlist_entry_safe(node, struct xgene_pmu, 1826 + node); 1827 + struct xgene_pmu_dev_ctx *ctx; 1828 + unsigned int target; 1829 + 1830 + if (!cpumask_test_and_clear_cpu(cpu, &xgene_pmu->cpu)) 1831 + return 0; 1832 + target = cpumask_any_but(cpu_online_mask, cpu); 1833 + if (target >= nr_cpu_ids) 1834 + return 0; 1835 + 1836 + list_for_each_entry(ctx, &xgene_pmu->mcpmus, next) { 1837 + perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target); 1838 + } 1839 + list_for_each_entry(ctx, &xgene_pmu->mcbpmus, next) { 1840 + perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target); 1841 + } 1842 + list_for_each_entry(ctx, &xgene_pmu->l3cpmus, next) { 1843 + perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target); 1844 + } 1845 + list_for_each_entry(ctx, &xgene_pmu->iobpmus, next) { 1846 + perf_pmu_migrate_context(&ctx->pmu_dev->pmu, cpu, target); 1847 + } 1848 + 1849 + cpumask_set_cpu(target, &xgene_pmu->cpu); 1850 + /* Overflow interrupt also should use the same CPU */ 1851 + WARN_ON(irq_set_affinity(xgene_pmu->irq, &xgene_pmu->cpu)); 1852 + 1853 + return 0; 1854 + } 1855 + 1812 1856 static int xgene_pmu_probe(struct platform_device *pdev) 1813 1857 { 1814 1858 const struct xgene_pmu_data *dev_data; ··· 1864 1814 struct resource *res; 1865 1815 int irq, rc; 1866 1816 int version; 1817 + 1818 + /* Install a hook to update the reader CPU in case it goes offline */ 1819 + rc = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE, 1820 + "CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE", 1821 + xgene_pmu_online_cpu, 1822 + xgene_pmu_offline_cpu); 1823 + if (rc) 1824 + return rc; 1867 1825 1868 1826 xgene_pmu = devm_kzalloc(&pdev->dev, sizeof(*xgene_pmu), GFP_KERNEL); 1869 1827 if (!xgene_pmu) ··· 1923 1865 dev_err(&pdev->dev, "No IRQ resource\n"); 1924 1866 return -EINVAL; 1925 1867 } 1868 + 1926 1869 rc = devm_request_irq(&pdev->dev, irq, xgene_pmu_isr, 1927 1870 IRQF_NOBALANCING | IRQF_NO_THREAD, 1928 1871 dev_name(&pdev->dev), xgene_pmu); ··· 1931 1872 dev_err(&pdev->dev, "Could not request IRQ %d\n", irq); 1932 1873 return rc; 1933 1874 } 1875 + 1876 + xgene_pmu->irq = irq; 1934 1877 1935 1878 raw_spin_lock_init(&xgene_pmu->lock); 1936 1879 ··· 1944 1883 xgene_pmu->mc_active_mask = 0x1; 1945 1884 } 1946 1885 1947 - /* Pick one core to use for cpumask attributes */ 1948 - cpumask_set_cpu(smp_processor_id(), &xgene_pmu->cpu); 1949 - 1950 - /* Make sure that the overflow interrupt is handled by this CPU */ 1951 - rc = irq_set_affinity(irq, &xgene_pmu->cpu); 1886 + /* Add this instance to the list used by the hotplug callback */ 1887 + rc = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE, 1888 + &xgene_pmu->node); 1952 1889 if (rc) { 1953 - dev_err(&pdev->dev, "Failed to set interrupt affinity!\n"); 1890 + dev_err(&pdev->dev, "Error %d registering hotplug", rc); 1954 1891 return rc; 1955 1892 } 1956 1893 ··· 1956 1897 rc = xgene_pmu_probe_pmu_dev(xgene_pmu, pdev); 1957 1898 if (rc) { 1958 1899 dev_err(&pdev->dev, "No PMU perf devices found!\n"); 1959 - return rc; 1900 + goto out_unregister; 1960 1901 } 1961 1902 1962 1903 /* Enable interrupt */ 1963 1904 xgene_pmu->ops->unmask_int(xgene_pmu); 1964 1905 1965 1906 return 0; 1907 + 1908 + out_unregister: 1909 + cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE, 1910 + &xgene_pmu->node); 1911 + return rc; 1966 1912 } 1967 1913 1968 1914 static void ··· 1988 1924 xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->iobpmus); 1989 1925 xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->mcbpmus); 1990 1926 xgene_pmu_dev_cleanup(xgene_pmu, &xgene_pmu->mcpmus); 1927 + cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE, 1928 + &xgene_pmu->node); 1991 1929 1992 1930 return 0; 1993 1931 }
+1
include/linux/cpuhotplug.h
··· 164 164 CPUHP_AP_PERF_ARM_L2X0_ONLINE, 165 165 CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE, 166 166 CPUHP_AP_PERF_ARM_QCOM_L3_ONLINE, 167 + CPUHP_AP_PERF_ARM_APM_XGENE_ONLINE, 167 168 CPUHP_AP_PERF_POWERPC_NEST_IMC_ONLINE, 168 169 CPUHP_AP_PERF_POWERPC_CORE_IMC_ONLINE, 169 170 CPUHP_AP_PERF_POWERPC_THREAD_IMC_ONLINE,