Skip to content

Commit d23bc2b

Browse files
Valentin SchneiderMarc Zyngier
authored andcommitted
irqchip/gic-v3-its: Postpone LPI pending table freeing and memreserve
Memory used by the LPI tables have to be made persistent for kexec to have a chance to work, as explained in [1]. If they have been made persistent and we are booting into a kexec'd kernel, we also need to free the pages that were preemptively allocated by the new kernel for those tables. Both of those operations currently happen during its_cpu_init(), which happens in a _STARTING (IOW atomic) cpuhp callback for secondary CPUs. efi_mem_reserve_iomem() issues a GFP_ATOMIC allocation, which unfortunately doesn't work under PREEMPT_RT (this ends up grabbing a non-raw spinlock, which can sleep under PREEMPT_RT). Similarly, freeing the pages ends up grabbing a sleepable spinlock. Since the memreserve is only required by kexec, it doesn't have to be done so early in the secondary boot process. Issue the reservation in a new CPUHP_AP_ONLINE_DYN cpuhp callback, and piggy-back the page freeing on top of it. A CPU gets to run the body of this new callback exactly once. As kexec issues a machine_shutdown() prior to machine_kexec(), it will be serialized vs a CPU being plugged to life by the hotplug machinery - either the CPU will have been brought up and have had its redistributor's pending table memreserved, or it never went online and will have its table allocated by the new kernel. [1]: https://lore.kernel.org/lkml/[email protected]/ Signed-off-by: Valentin Schneider <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent c0cdc89 commit d23bc2b

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

drivers/irqchip/irq-gic-v3-its.c

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
#define RDIST_FLAGS_RD_TABLES_PREALLOCATED (1 << 1)
4848

4949
#define RD_LOCAL_LPI_ENABLED BIT(0)
50+
#define RD_LOCAL_PENDTABLE_PREALLOCATED BIT(1)
51+
#define RD_LOCAL_MEMRESERVE_DONE BIT(2)
5052

5153
static u32 lpi_id_bits;
5254

@@ -3065,15 +3067,13 @@ static void its_cpu_init_lpis(void)
30653067
paddr &= GENMASK_ULL(51, 16);
30663068

30673069
WARN_ON(!gic_check_reserved_range(paddr, LPI_PENDBASE_SZ));
3068-
its_free_pending_table(gic_data_rdist()->pend_page);
3069-
gic_data_rdist()->pend_page = NULL;
3070+
gic_data_rdist()->flags |= RD_LOCAL_PENDTABLE_PREALLOCATED;
30703071

30713072
goto out;
30723073
}
30733074

30743075
pend_page = gic_data_rdist()->pend_page;
30753076
paddr = page_to_phys(pend_page);
3076-
WARN_ON(gic_reserve_range(paddr, LPI_PENDBASE_SZ));
30773077

30783078
/* set PROPBASE */
30793079
val = (gic_rdists->prop_table_pa |
@@ -3163,7 +3163,8 @@ static void its_cpu_init_lpis(void)
31633163
gic_data_rdist()->flags |= RD_LOCAL_LPI_ENABLED;
31643164
pr_info("GICv3: CPU%d: using %s LPI pending table @%pa\n",
31653165
smp_processor_id(),
3166-
gic_data_rdist()->pend_page ? "allocated" : "reserved",
3166+
gic_data_rdist()->flags & RD_LOCAL_PENDTABLE_PREALLOCATED ?
3167+
"reserved" : "allocated",
31673168
&paddr);
31683169
}
31693170

@@ -5202,6 +5203,38 @@ int its_cpu_init(void)
52025203
return 0;
52035204
}
52045205

5206+
static int its_cpu_memreserve_lpi(unsigned int cpu)
5207+
{
5208+
struct page *pend_page;
5209+
int ret = 0;
5210+
5211+
/* This gets to run exactly once per CPU */
5212+
if (gic_data_rdist()->flags & RD_LOCAL_MEMRESERVE_DONE)
5213+
return 0;
5214+
5215+
pend_page = gic_data_rdist()->pend_page;
5216+
if (WARN_ON(!pend_page)) {
5217+
ret = -ENOMEM;
5218+
goto out;
5219+
}
5220+
/*
5221+
* If the pending table was pre-programmed, free the memory we
5222+
* preemptively allocated. Otherwise, reserve that memory for
5223+
* later kexecs.
5224+
*/
5225+
if (gic_data_rdist()->flags & RD_LOCAL_PENDTABLE_PREALLOCATED) {
5226+
its_free_pending_table(pend_page);
5227+
gic_data_rdist()->pend_page = NULL;
5228+
} else {
5229+
phys_addr_t paddr = page_to_phys(pend_page);
5230+
WARN_ON(gic_reserve_range(paddr, LPI_PENDBASE_SZ));
5231+
}
5232+
5233+
out:
5234+
gic_data_rdist()->flags |= RD_LOCAL_MEMRESERVE_DONE;
5235+
return ret;
5236+
}
5237+
52055238
static const struct of_device_id its_device_id[] = {
52065239
{ .compatible = "arm,gic-v3-its", },
52075240
{},
@@ -5385,6 +5418,23 @@ static void __init its_acpi_probe(void)
53855418
static void __init its_acpi_probe(void) { }
53865419
#endif
53875420

5421+
int __init its_lpi_memreserve_init(void)
5422+
{
5423+
int state;
5424+
5425+
if (!efi_enabled(EFI_CONFIG_TABLES))
5426+
return 0;
5427+
5428+
state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
5429+
"irqchip/arm/gicv3/memreserve:online",
5430+
its_cpu_memreserve_lpi,
5431+
NULL);
5432+
if (state < 0)
5433+
return state;
5434+
5435+
return 0;
5436+
}
5437+
53885438
int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
53895439
struct irq_domain *parent_domain)
53905440
{

drivers/irqchip/irq-gic-v3.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1802,6 +1802,7 @@ static int __init gic_init_bases(void __iomem *dist_base,
18021802
if (gic_dist_supports_lpis()) {
18031803
its_init(handle, &gic_data.rdists, gic_data.domain);
18041804
its_cpu_init();
1805+
its_lpi_memreserve_init();
18051806
} else {
18061807
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
18071808
gicv2m_init(handle, gic_data.domain);

include/linux/irqchip/arm-gic-v3.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,7 @@ struct rdists {
632632

633633
struct irq_domain;
634634
struct fwnode_handle;
635+
int __init its_lpi_memreserve_init(void);
635636
int its_cpu_init(void);
636637
int its_init(struct fwnode_handle *handle, struct rdists *rdists,
637638
struct irq_domain *domain);

0 commit comments

Comments
 (0)