Pull IRQ fixes from Ingo Molnar: - Fix use-after-free in irq_work_single() on PREEMPT_RT (Jiayuan Chen) - Don't call add_interrupt_randomness() for NMIs in handle_percpu_devid_irq() (Mark Rutland) - Remove unused function in the ath79-cpu irqchip driver causing LKP CI build warnings (Rosen Penev) - Fix IRQ allocation/teardown leakage regressions in the GICv5 irqchip driver (Sascha Bischoff) - Fix an IRQ trigger type regression in the Meson S4 SoC irqchip driver (Xianwei Zhao) - Fix CPU offlining regression in the RiscV IMSIC irqchip driver (Yong-Xuan Wang) * tag 'irq-urgent-2026-05-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT irqchip/riscv-imsic: Clear interrupt move state during CPU offlining irqchip/meson-gpio: Use the correct register in meson_s4_gpio_irq_set_type() irqchip/ath79-cpu: Remove unused function genirq/chip: Don't call add_interrupt_randomness() for NMIs irqchip/gic-v5: Allocate ITS parent LPIs as a range irqchip/gic-v5: Support range allocation for LPIs irqchip/gic-v5: Move LPI allocation into the LPI domain
Unified · Split
| @@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( · 84 unmodified lines | |||
| 85 | } | 85 | } |
| 86 | IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", | 86 | IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", |
| 87 | ar79_cpu_intc_of_init); | 87 | ar79_cpu_intc_of_init); |
| 88 | |||
| 89 | void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) | ||
| 90 | { | ||
| 91 | irq_wb_chan[2] = irq_wb_chan2; | ||
| 92 | irq_wb_chan[3] = irq_wb_chan3; | ||
| 93 | mips_cpu_irq_init(); | ||
| 94 | } | ||
| @@ -929,14 +929,15 @@ static void gicv5_its_free_eventid(struct gicv5_its_dev *its_dev, u32 event_id_b · 928 unmodified lines | |||
| 929 | static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | 929 | static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, |
| 930 | unsigned int nr_irqs, void *arg) | 930 | unsigned int nr_irqs, void *arg) |
| 931 | { | 931 | { |
| 932 | u32 device_id, event_id_base, lpi; | ||
| 933 | struct gicv5_its_dev *its_dev; | 932 | struct gicv5_its_dev *its_dev; |
| 933 | u32 device_id, event_id_base; | ||
| 934 | msi_alloc_info_t *info = arg; | 934 | msi_alloc_info_t *info = arg; |
| 935 | irq_hw_number_t hwirq; | 935 | irq_hw_number_t hwirq; |
| 936 | struct irq_data *irqd; | 936 | struct irq_data *irqd; |
| 937 | int ret, i; | 937 | int ret, i; |
| 938 | 938 | ||
| 939 | its_dev = info->scratchpad[0].ptr; | 939 | its_dev = info->scratchpad[0].ptr; |
| 940 | device_id = its_dev->device_id; | ||
| 940 | 941 | ||
| 941 | ret = gicv5_its_alloc_eventid(its_dev, info, nr_irqs, &event_id_base); | 942 | ret = gicv5_its_alloc_eventid(its_dev, info, nr_irqs, &event_id_base); |
| 942 | if (ret) | 943 | if (ret) |
| @@ -946,22 +947,11 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi · 3 unmodified lines | |||
| 946 | if (ret) | 947 | if (ret) |
| 947 | goto out_eventid; | 948 | goto out_eventid; |
| 948 | 949 | ||
| 949 | device_id = its_dev->device_id; | 950 | ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, NULL); |
| 951 | if (ret) | ||
| 952 | goto out_eventid; | ||
| 950 | 953 | ||
| 951 | for (i = 0; i < nr_irqs; i++) { | 954 | for (i = 0; i < nr_irqs; i++) { |
| 952 | ret = gicv5_alloc_lpi(); | ||
| 953 | if (ret < 0) { | ||
| 954 | pr_debug("Failed to find free LPI!\n"); | ||
| 955 | goto out_free_irqs; | ||
| 956 | } | ||
| 957 | lpi = ret; | ||
| 958 | |||
| 959 | ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi); | ||
| 960 | if (ret) { | ||
| 961 | gicv5_free_lpi(lpi); | ||
| 962 | goto out_free_irqs; | ||
| 963 | } | ||
| 964 | |||
| 965 | /* | 955 | /* |
| 966 | * Store eventid and deviceid into the hwirq for later use. | 956 | * Store eventid and deviceid into the hwirq for later use. |
| 967 | * | 957 | * |
| @@ -980,13 +970,6 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi · 12 unmodified lines | |||
| 980 | 970 | ||
| 981 | return 0; | 971 | return 0; |
| 982 | 972 | ||
| 983 | out_free_irqs: | ||
| 984 | while (--i >= 0) { | ||
| 985 | irqd = irq_domain_get_irq_data(domain, virq + i); | ||
| 986 | gicv5_free_lpi(irqd->parent_data->hwirq); | ||
| 987 | irq_domain_reset_irq_data(irqd); | ||
| 988 | irq_domain_free_irqs_parent(domain, virq + i, 1); | ||
| 989 | } | ||
| 990 | out_eventid: | 973 | out_eventid: |
| 991 | gicv5_its_free_eventid(its_dev, event_id_base, nr_irqs); | 974 | gicv5_its_free_eventid(its_dev, event_id_base, nr_irqs); |
| 992 | return ret; | 975 | return ret; |
| @@ -1009,15 +992,14 @@ static void gicv5_its_irq_domain_free(struct irq_domain *domain, unsigned int vi · 16 unmodified lines | |||
| 1009 | bitmap_release_region(its_dev->event_map, event_id_base, | 992 | bitmap_release_region(its_dev->event_map, event_id_base, |
| 1010 | get_count_order(nr_irqs)); | 993 | get_count_order(nr_irqs)); |
| 1011 | 994 | ||
| 1012 | /* Hierarchically free irq data */ | ||
| 1013 | for (i = 0; i < nr_irqs; i++) { | 995 | for (i = 0; i < nr_irqs; i++) { |
| 1014 | d = irq_domain_get_irq_data(domain, virq + i); | 996 | d = irq_domain_get_irq_data(domain, virq + i); |
| 1015 | |||
| 1016 | gicv5_free_lpi(d->parent_data->hwirq); | ||
| 1017 | irq_domain_reset_irq_data(d); | 997 | irq_domain_reset_irq_data(d); |
| 1018 | irq_domain_free_irqs_parent(domain, virq + i, 1); | ||
| 1019 | } | 998 | } |
| 1020 | 999 | ||
| 1000 | /* Hierarchically free irq data */ | ||
| 1001 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); | ||
| 1002 | |||
| 1021 | gicv5_its_syncr(its, its_dev); | 1003 | gicv5_its_syncr(its, its_dev); |
| 1022 | gicv5_irs_syncr(); | 1004 | gicv5_irs_syncr(); |
| 1023 | } | 1005 | } |
| @@ -59,16 +59,6 @@ static void release_lpi(u32 lpi) · 58 unmodified lines | |||
| 59 | ida_free(&lpi_ida, lpi); | 59 | ida_free(&lpi_ida, lpi); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | int gicv5_alloc_lpi(void) | ||
| 63 | { | ||
| 64 | return alloc_lpi(); | ||
| 65 | } | ||
| 66 | |||
| 67 | void gicv5_free_lpi(u32 lpi) | ||
| 68 | { | ||
| 69 | release_lpi(lpi); | ||
| 70 | } | ||
| 71 | |||
| 72 | static void gicv5_ppi_priority_init(void) | 62 | static void gicv5_ppi_priority_init(void) |
| 73 | { | 63 | { |
| 74 | write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR0_EL1); | 64 | write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR0_EL1); |
| @@ -806,38 +796,64 @@ static void gicv5_lpi_config_reset(struct irq_data *d) · 731 unmodified lines | |||
| 806 | gicv5_lpi_irq_write_pending_state(d, false); | 796 | gicv5_lpi_irq_write_pending_state(d, false); |
| 807 | } | 797 | } |
| 808 | 798 | ||
| 799 | static void gicv5_irq_lpi_domain_free(struct irq_domain *domain, unsigned int virq, | ||
| 800 | unsigned int nr_irqs) | ||
| 801 | { | ||
| 802 | struct irq_data *d; | ||
| 803 | |||
| 804 | for (unsigned int i = 0; i < nr_irqs; i++, virq++) { | ||
| 805 | d = irq_domain_get_irq_data(domain, virq); | ||
| 806 | |||
| 807 | release_lpi(d->hwirq); | ||
| 808 | |||
| 809 | irq_set_handler(virq, NULL); | ||
| 810 | irq_domain_reset_irq_data(d); | ||
| 811 | } | ||
| 812 | } | ||
| 813 | |||
| 809 | static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq, | 814 | static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq, |
| 810 | unsigned int nr_irqs, void *arg) | 815 | unsigned int nr_irqs, void *arg) |
| 811 | { | 816 | { |
| 812 | irq_hw_number_t hwirq; | 817 | irq_hw_number_t hwirq; |
| 813 | struct irq_data *irqd; | 818 | struct irq_data *irqd; |
| 814 | u32 *lpi = arg; | 819 | unsigned int i; |
| 815 | int ret; | 820 | int ret; |
| 816 | 821 | ||
| 817 | if (WARN_ON_ONCE(nr_irqs != 1)) | 822 | for (i = 0; i < nr_irqs; i++) { |
| 818 | return -EINVAL; | 823 | ret = alloc_lpi(); |
| 824 | if (ret < 0) | ||
| 825 | goto out_free_lpis; | ||
| 826 | hwirq = ret; | ||
| 827 | |||
| 828 | ret = gicv5_irs_iste_alloc(hwirq); | ||
| 829 | if (ret < 0) { | ||
| 830 | /* Undo partial state first, then clean up the rest */ | ||
| 831 | release_lpi(hwirq); | ||
| 832 | goto out_free_lpis; | ||
| 833 | } | ||
| 819 | 834 | ||
| 820 | hwirq = *lpi; | 835 | irqd = irq_domain_get_irq_data(domain, virq + i); |
| 821 | 836 | ||
| 822 | irqd = irq_domain_get_irq_data(domain, virq); | 837 | irq_domain_set_info(domain, virq + i, hwirq, &gicv5_lpi_irq_chip, |
| 838 | NULL, handle_fasteoi_irq, NULL, NULL); | ||
| 839 | irqd_set_single_target(irqd); | ||
| 823 | 840 | ||
| 824 | irq_domain_set_info(domain, virq, hwirq, &gicv5_lpi_irq_chip, NULL, | 841 | gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI); |
| 825 | handle_fasteoi_irq, NULL, NULL); | 842 | gicv5_lpi_config_reset(irqd); |
| 826 | irqd_set_single_target(irqd); | 843 | } |
| 827 | 844 | ||
| 828 | ret = gicv5_irs_iste_alloc(hwirq); | 845 | return 0; |
| 829 | if (ret < 0) | ||
| 830 | return ret; | ||
| 831 | 846 | ||
| 832 | gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI); | 847 | out_free_lpis: |
| 833 | gicv5_lpi_config_reset(irqd); | 848 | if (i) |
| 849 | gicv5_irq_lpi_domain_free(domain, virq, i); | ||
| 834 | 850 | ||
| 835 | return 0; | 851 | return ret; |
| 836 | } | 852 | } |
| 837 | 853 | ||
| 838 | static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = { | 854 | static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = { |
| 839 | .alloc = gicv5_irq_lpi_domain_alloc, | 855 | .alloc = gicv5_irq_lpi_domain_alloc, |
| 840 | .free = gicv5_irq_domain_free, | 856 | .free = gicv5_irq_lpi_domain_free, |
| 841 | }; | 857 | }; |
| 842 | 858 | ||
| 843 | void __init gicv5_init_lpi_domain(void) | 859 | void __init gicv5_init_lpi_domain(void) |
| @@ -858,30 +874,21 @@ static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain, unsigned int vi · 14 unmodified lines | |||
| 858 | unsigned int nr_irqs, void *arg) | 874 | unsigned int nr_irqs, void *arg) |
| 859 | { | 875 | { |
| 860 | struct irq_data *irqd; | 876 | struct irq_data *irqd; |
| 861 | int ret, i; | 877 | int ret; |
| 862 | u32 lpi; | ||
| 863 | |||
| 864 | for (i = 0; i < nr_irqs; i++) { | ||
| 865 | ret = gicv5_alloc_lpi(); | ||
| 866 | if (ret < 0) | ||
| 867 | return ret; | ||
| 868 | |||
| 869 | lpi = ret; | ||
| 870 | 878 | ||
| 871 | ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi); | 879 | ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); |
| 872 | if (ret) { | 880 | if (ret) |
| 873 | gicv5_free_lpi(lpi); | 881 | return ret; |
| 874 | return ret; | ||
| 875 | } | ||
| 876 | 882 | ||
| 877 | irqd = irq_domain_get_irq_data(domain, virq + i); | 883 | for (unsigned int i = 0; i < nr_irqs; i++, virq++) { |
| 884 | irqd = irq_domain_get_irq_data(domain, virq); | ||
| 878 | 885 | ||
| 879 | irq_domain_set_hwirq_and_chip(domain, virq + i, i, | 886 | irq_domain_set_hwirq_and_chip(domain, virq, i, |
| 880 | &gicv5_ipi_irq_chip, NULL); | 887 | &gicv5_ipi_irq_chip, NULL); |
| 881 | 888 | ||
| 882 | irqd_set_single_target(irqd); | 889 | irqd_set_single_target(irqd); |
| 883 | 890 | ||
| 884 | irq_set_handler(virq + i, handle_percpu_irq); | 891 | irq_set_handler(virq, handle_percpu_irq); |
| 885 | } | 892 | } |
| 886 | 893 | ||
| 887 | return 0; | 894 | return 0; |
| @@ -899,12 +906,11 @@ static void gicv5_irq_ipi_domain_free(struct irq_domain *domain, unsigned int vi · 11 unmodified lines | |||
| 899 | if (!d) | 906 | if (!d) |
| 900 | return; | 907 | return; |
| 901 | 908 | ||
| 902 | gicv5_free_lpi(d->parent_data->hwirq); | ||
| 903 | |||
| 904 | irq_set_handler(virq + i, NULL); | 909 | irq_set_handler(virq + i, NULL); |
| 905 | irq_domain_reset_irq_data(d); | 910 | irq_domain_reset_irq_data(d); |
| 906 | irq_domain_free_irqs_parent(domain, virq + i, 1); | ||
| 907 | } | 911 | } |
| 912 | |||
| 913 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); | ||
| 908 | } | 914 | } |
| 909 | 915 | ||
| 910 | static const struct irq_domain_ops gicv5_irq_ipi_domain_ops = { | 916 | static const struct irq_domain_ops gicv5_irq_ipi_domain_ops = { |
| @@ -415,8 +415,7 @@ static int meson_s4_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl, · 414 unmodified lines | |||
| 415 | if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) | 415 | if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) |
| 416 | val |= BIT(ctl->params->edge_single_offset + idx); | 416 | val |= BIT(ctl->params->edge_single_offset + idx); |
| 417 | 417 | ||
| 418 | meson_gpio_irq_update_bits(ctl, params->edge_pol_reg, | 418 | meson_gpio_irq_update_bits(ctl, REG_EDGE_POL, BIT(idx) | BIT(12 + idx), val); |
| 419 | BIT(idx) | BIT(12 + idx), val); | ||
| 420 | return 0; | 419 | return 0; |
| 421 | }; | 420 | }; |
| 422 | 421 | ||
| @@ -158,6 +158,8 @@ static int imsic_dying_cpu(unsigned int cpu) · 157 unmodified lines | |||
| 158 | /* Cleanup IPIs */ | 158 | /* Cleanup IPIs */ |
| 159 | imsic_ipi_dying_cpu(); | 159 | imsic_ipi_dying_cpu(); |
| 160 | 160 | ||
| 161 | imsic_local_sync_all(false); | ||
| 162 | |||
| 161 | /* Mark per-CPU IMSIC state as offline */ | 163 | /* Mark per-CPU IMSIC state as offline */ |
| 162 | imsic_state_offline(); | 164 | imsic_state_offline(); |
| 163 | 165 | ||
| @@ -425,9 +425,6 @@ struct gicv5_its_itt_cfg { · 424 unmodified lines | |||
| 425 | void gicv5_init_lpis(u32 max); | 425 | void gicv5_init_lpis(u32 max); |
| 426 | void gicv5_deinit_lpis(void); | 426 | void gicv5_deinit_lpis(void); |
| 427 | 427 | ||
| 428 | int gicv5_alloc_lpi(void); | ||
| 429 | void gicv5_free_lpi(u32 lpi); | ||
| 430 | |||
| 431 | void __init gicv5_its_of_probe(struct device_node *parent); | 428 | void __init gicv5_its_of_probe(struct device_node *parent); |
| 432 | void __init gicv5_its_acpi_probe(void); | 429 | void __init gicv5_its_acpi_probe(void); |
| 433 | #endif | 430 | #endif |
| @@ -14,6 +14,7 @@ · 13 unmodified lines | |||
| 14 | #include <linux/interrupt.h> | 14 | #include <linux/interrupt.h> |
| 15 | #include <linux/kernel_stat.h> | 15 | #include <linux/kernel_stat.h> |
| 16 | #include <linux/irqdomain.h> | 16 | #include <linux/irqdomain.h> |
| 17 | #include <linux/preempt.h> | ||
| 17 | #include <linux/random.h> | 18 | #include <linux/random.h> |
| 18 | 19 | ||
| 19 | #include <trace/events/irq.h> | 20 | #include <trace/events/irq.h> |
| @@ -893,7 +894,10 @@ void handle_percpu_irq(struct irq_desc *desc) · 873 unmodified lines | |||
| 893 | * | 894 | * |
| 894 | * action->percpu_dev_id is a pointer to percpu variables which | 895 | * action->percpu_dev_id is a pointer to percpu variables which |
| 895 | * contain the real device id for the cpu on which this handler is | 896 | * contain the real device id for the cpu on which this handler is |
| 896 | * called | 897 | * called. |
| 898 | * | ||
| 899 | * May be used for NMI interrupt lines, and so may be called in IRQ or NMI | ||
| 900 | * context. | ||
| 897 | */ | 901 | */ |
| 898 | void handle_percpu_devid_irq(struct irq_desc *desc) | 902 | void handle_percpu_devid_irq(struct irq_desc *desc) |
| 899 | { | 903 | { |
| @@ -930,7 +934,8 @@ void handle_percpu_devid_irq(struct irq_desc *desc) · 30 unmodified lines | |||
| 930 | enabled ? " and unmasked" : "", irq, cpu); | 934 | enabled ? " and unmasked" : "", irq, cpu); |
| 931 | } | 935 | } |
| 932 | 936 | ||
| 933 | add_interrupt_randomness(irq); | 937 | if (!in_nmi()) |
| 938 | add_interrupt_randomness(irq); | ||
| 934 | 939 | ||
| 935 | if (chip->irq_eoi) | 940 | if (chip->irq_eoi) |
| 936 | chip->irq_eoi(&desc->irq_data); | 941 | chip->irq_eoi(&desc->irq_data); |
| @@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work) · 291 unmodified lines | |||
| 292 | !arch_irq_work_has_interrupt()) { | 292 | !arch_irq_work_has_interrupt()) { |
| 293 | rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work), | 293 | rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work), |
| 294 | TASK_UNINTERRUPTIBLE); | 294 | TASK_UNINTERRUPTIBLE); |
| 295 | /* | ||
| 296 | * Ensure irq_work_single() does not access @work | ||
| 297 | * after removing IRQ_WORK_BUSY. It is always | ||
| 298 | * accessed within a RCU-read section. | ||
| 299 | */ | ||
| 300 | synchronize_rcu(); | ||
| 295 | return; | 301 | return; |
| 296 | } | 302 | } |
| 297 | 303 | ||
| @@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync); · 4 unmodified lines | |||
| 302 | 308 | ||
| 303 | static void run_irq_workd(unsigned int cpu) | 309 | static void run_irq_workd(unsigned int cpu) |
| 304 | { | 310 | { |
| 311 | guard(rcu)(); | ||
| 305 | irq_work_run_list(this_cpu_ptr(&lazy_list)); | 312 | irq_work_run_list(this_cpu_ptr(&lazy_list)); |
| 306 | } | 313 | } |
| 307 | 314 | ||