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

Configure Feed

Select the types of activity you want to include in your feed.

soc/tegra: pmc: Fix unsafe generic_handle_irq() call

Currently, when resuming from system suspend on Tegra platforms,
the following warning is observed:

WARNING: CPU: 0 PID: 14459 at kernel/irq/irqdesc.c:666
Call trace:
handle_irq_desc+0x20/0x58 (P)
tegra186_pmc_wake_syscore_resume+0xe4/0x15c
syscore_resume+0x3c/0xb8
suspend_devices_and_enter+0x510/0x540
pm_suspend+0x16c/0x1d8

The warning occurs because generic_handle_irq() is being called from
a non-interrupt context which is considered as unsafe.

Fix this warning by deferring generic_handle_irq() call to an IRQ work
which gets executed in hard IRQ context where generic_handle_irq()
can be called safely.

When PREEMPT_RT kernels are used, regular IRQ work (initialized with
init_irq_work) is deferred to run in per-CPU kthreads in preemptible
context rather than hard IRQ context. Hence, use the IRQ_WORK_INIT_HARD
variant so that with PREEMPT_RT kernels, the IRQ work is processed in
hardirq context instead of being deferred to a thread which is required
for calling generic_handle_irq().

On non-PREEMPT_RT kernels, both init_irq_work() and IRQ_WORK_INIT_HARD()
execute in IRQ context, so this change has no functional impact for
standard kernel configurations.

Signed-off-by: Petlozu Pravareshwar <petlozup@nvidia.com>
Signed-off-by: Prathamesh Shete <pshete@nvidia.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Tested-by: Jon Hunter <jonathanh@nvidia.com>
[treding@nvidia.com: miscellaneous cleanups]
Signed-off-by: Thierry Reding <treding@nvidia.com>

authored by

Prathamesh Shete and committed by
Thierry Reding
e6d96073 8f0b4cce

+74 -30
+74 -30
drivers/soc/tegra/pmc.c
··· 28 28 #include <linux/iopoll.h> 29 29 #include <linux/irqdomain.h> 30 30 #include <linux/irq.h> 31 + #include <linux/irq_work.h> 31 32 #include <linux/kernel.h> 32 33 #include <linux/of_address.h> 33 34 #include <linux/of_clk.h> ··· 469 468 unsigned long *wake_sw_status_map; 470 469 unsigned long *wake_cntrl_level_map; 471 470 struct syscore syscore; 471 + 472 + /* Pending wake IRQ processing */ 473 + struct irq_work wake_work; 474 + u32 *wake_status; 472 475 }; 473 476 474 477 static struct tegra_pmc *pmc = &(struct tegra_pmc) { ··· 1910 1905 return 0; 1911 1906 } 1912 1907 1908 + /* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */ 1909 + static void tegra186_pmc_wake_handler(struct irq_work *work) 1910 + { 1911 + struct tegra_pmc *pmc = container_of(work, struct tegra_pmc, wake_work); 1912 + unsigned int i, wake; 1913 + 1914 + for (i = 0; i < pmc->soc->max_wake_vectors; i++) { 1915 + unsigned long status = pmc->wake_status[i]; 1916 + 1917 + for_each_set_bit(wake, &status, 32) { 1918 + irq_hw_number_t hwirq = wake + (i * 32); 1919 + struct irq_desc *desc; 1920 + unsigned int irq; 1921 + 1922 + irq = irq_find_mapping(pmc->domain, hwirq); 1923 + if (!irq) { 1924 + dev_warn(pmc->dev, 1925 + "No IRQ found for WAKE#%lu!\n", 1926 + hwirq); 1927 + continue; 1928 + } 1929 + 1930 + dev_dbg(pmc->dev, 1931 + "Resume caused by WAKE#%lu mapped to IRQ#%u\n", 1932 + hwirq, irq); 1933 + 1934 + desc = irq_to_desc(irq); 1935 + if (!desc) { 1936 + dev_warn(pmc->dev, 1937 + "No descriptor found for IRQ#%u\n", 1938 + irq); 1939 + continue; 1940 + } 1941 + 1942 + if (!desc->action || !desc->action->name) 1943 + continue; 1944 + 1945 + generic_handle_irq(irq); 1946 + } 1947 + 1948 + pmc->wake_status[i] = 0; 1949 + } 1950 + } 1951 + 1913 1952 static int tegra_pmc_init(struct tegra_pmc *pmc) 1914 1953 { 1915 1954 if (pmc->soc->max_wake_events > 0) { ··· 1972 1923 pmc->wake_cntrl_level_map = bitmap_zalloc(pmc->soc->max_wake_events, GFP_KERNEL); 1973 1924 if (!pmc->wake_cntrl_level_map) 1974 1925 return -ENOMEM; 1926 + 1927 + pmc->wake_status = kcalloc(pmc->soc->max_wake_vectors, sizeof(u32), GFP_KERNEL); 1928 + if (!pmc->wake_status) 1929 + return -ENOMEM; 1930 + 1931 + /* 1932 + * Initialize IRQ work for processing wake IRQs. Must use 1933 + * HARD_IRQ variant to run in hard IRQ context on PREEMPT_RT 1934 + * because we call generic_handle_irq() which requires hard 1935 + * IRQ context. 1936 + */ 1937 + pmc->wake_work = IRQ_WORK_INIT_HARD(tegra186_pmc_wake_handler); 1975 1938 } 1976 1939 1977 1940 if (pmc->soc->init) ··· 3190 3129 } 3191 3130 } 3192 3131 3193 - /* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */ 3194 - static void tegra186_pmc_process_wake_events(struct tegra_pmc *pmc, unsigned int index, 3195 - unsigned long status) 3196 - { 3197 - unsigned int wake; 3198 - 3199 - dev_dbg(pmc->dev, "Wake[%d:%d] status=%#lx\n", (index * 32) + 31, index * 32, status); 3200 - 3201 - for_each_set_bit(wake, &status, 32) { 3202 - irq_hw_number_t hwirq = wake + 32 * index; 3203 - struct irq_desc *desc; 3204 - unsigned int irq; 3205 - 3206 - irq = irq_find_mapping(pmc->domain, hwirq); 3207 - 3208 - desc = irq_to_desc(irq); 3209 - if (!desc || !desc->action || !desc->action->name) { 3210 - dev_dbg(pmc->dev, "Resume caused by WAKE%ld, IRQ %d\n", hwirq, irq); 3211 - continue; 3212 - } 3213 - 3214 - dev_dbg(pmc->dev, "Resume caused by WAKE%ld, %s\n", hwirq, desc->action->name); 3215 - generic_handle_irq(irq); 3216 - } 3217 - } 3218 - 3219 3132 static void tegra186_pmc_wake_syscore_resume(void *data) 3220 3133 { 3221 - u32 status, mask; 3222 3134 unsigned int i; 3135 + u32 mask; 3223 3136 3224 3137 for (i = 0; i < pmc->soc->max_wake_vectors; i++) { 3225 3138 mask = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(i)); 3226 - status = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask; 3227 - 3228 - tegra186_pmc_process_wake_events(pmc, i, status); 3139 + pmc->wake_status[i] = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask; 3229 3140 } 3141 + 3142 + /* Schedule IRQ work to process wake IRQs (if any) */ 3143 + irq_work_queue(&pmc->wake_work); 3230 3144 } 3231 3145 3232 3146 static int tegra186_pmc_wake_syscore_suspend(void *data) 3233 3147 { 3148 + unsigned int i; 3149 + 3150 + /* Check if there are unhandled wake IRQs */ 3151 + for (i = 0; i < pmc->soc->max_wake_vectors; i++) 3152 + if (pmc->wake_status[i]) 3153 + dev_warn(pmc->dev, 3154 + "Unhandled wake IRQs pending vector[%u]: 0x%x\n", 3155 + i, pmc->wake_status[i]); 3234 3156 wke_read_sw_wake_status(pmc); 3235 3157 3236 3158 /* flip the wakeup trigger for dual-edge triggered pads