From: Jan Kiszka Date: Mon, 27 Jun 2016 06:41:14 +0000 (+0200) Subject: arm: Rework interrupt affinity management on cell creation X-Git-Url: https://rtime.felk.cvut.cz/gitweb/jailhouse.git/commitdiff_plain/011ab917a15057f52d36b1542bf3454abec55160 arm: Rework interrupt affinity management on cell creation So far, we only ensured that the affinities of interrupts given to new cells match with the corresponding CPU set. However, we also need to check that Linux properly adjusted the affinity of all its remaining interrupts properly. This introduces a new irqchip callback adjust_irq_target which performs the check and the potential adjustment to the first CPU of a cell on a per-interrupt basis. A single loop in irqchip_cell_init triggers them. gic_target_spis and gic_route_spis become obsolete. Signed-off-by: Jan Kiszka --- diff --git a/hypervisor/arch/arm/gic-common.c b/hypervisor/arch/arm/gic-common.c index 5d0eed8..60aa6fe 100644 --- a/hypervisor/arch/arm/gic-common.c +++ b/hypervisor/arch/arm/gic-common.c @@ -31,7 +31,7 @@ extern unsigned int gicd_size; static DEFINE_SPINLOCK(dist_lock); /* The GIC interface numbering does not necessarily match the logical map */ -static u8 target_cpu_map[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +u8 target_cpu_map[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /* Check that the targeted interface belongs to the cell */ bool gic_targets_in_cell(struct cell *cell, u8 targets) @@ -344,40 +344,3 @@ void gic_handle_irq(struct per_cpu *cpu_data) irqchip_eoi_irq(irq_id, handled); } } - -void gic_target_spis(struct cell *config_cell, struct cell *dest_cell) -{ - unsigned int i, first_cpu, cpu_itf; - unsigned int shift = 0; - void *itargetsr = gicd_base + GICD_ITARGETSR; - u32 targets; - u32 mask = 0; - u32 bits = 0; - - /* Always route to the first logical CPU on reset */ - for_each_cpu(first_cpu, dest_cell->cpu_set) - break; - - cpu_itf = target_cpu_map[first_cpu]; - - /* ITARGETSR0-7 contain the PPIs and SGIs, and are read-only. */ - itargetsr += 4 * 8; - - for (i = 0; i < 64; i++, shift = (shift + 8) % 32) { - if (irqchip_irq_in_cell(config_cell, 32 + i)) { - mask |= (0xff << shift); - bits |= (cpu_itf << shift); - } - - /* ITARGETRs have 4 IRQ per register */ - if ((i + 1) % 4 == 0) { - targets = mmio_read32(itargetsr); - targets &= ~mask; - targets |= bits; - mmio_write32(itargetsr, targets); - itargetsr += 4; - mask = 0; - bits = 0; - } - } -} diff --git a/hypervisor/arch/arm/gic-v2.c b/hypervisor/arch/arm/gic-v2.c index 9d1d311..a4ca99c 100644 --- a/hypervisor/arch/arm/gic-v2.c +++ b/hypervisor/arch/arm/gic-v2.c @@ -181,15 +181,6 @@ static int gic_cell_init(struct cell *cell) { int err; - /* - * target_cpu_map has not been populated by all available CPUs when the - * setup code initialises the root cell. It is assumed that the kernel - * already has configured all its SPIs anyway, and that it will redirect - * them when unplugging a CPU. - */ - if (cell != &root_cell) - gic_target_spis(cell, cell); - /* * Let the guest access the virtual CPU interface instead of the * physical one. @@ -218,6 +209,21 @@ static void gic_cell_exit(struct cell *cell) PAGING_NON_COHERENT); } +static void gic_adjust_irq_target(struct cell *cell, u16 irq_id) +{ + void *itargetsr = gicd_base + GICD_ITARGETSR + (irq_id & ~0x3); + u32 targets = mmio_read32(itargetsr); + unsigned int shift = irq_id % 4; + + if (gic_targets_in_cell(cell, (u8)(targets >> shift))) + return; + + targets &= ~(0xff << shift); + targets |= target_cpu_map[first_cpu(cell->cpu_set)] << shift; + + mmio_write32(itargetsr, targets); +} + static int gic_send_sgi(struct sgi *sgi) { u32 val; @@ -304,6 +310,7 @@ struct irqchip_ops irqchip = { .cpu_reset = gic_cpu_reset, .cell_init = gic_cell_init, .cell_exit = gic_cell_exit, + .adjust_irq_target = gic_adjust_irq_target, .send_sgi = gic_send_sgi, .handle_irq = gic_handle_irq, diff --git a/hypervisor/arch/arm/gic-v3.c b/hypervisor/arch/arm/gic-v3.c index 8d076f9..25e560d 100644 --- a/hypervisor/arch/arm/gic-v3.c +++ b/hypervisor/arch/arm/gic-v3.c @@ -202,19 +202,13 @@ static int gic_cpu_init(struct per_cpu *cpu_data) return 0; } -static void gic_route_spis(struct cell *config_cell, struct cell *dest_cell) +static void gic_adjust_irq_target(struct cell *cell, u16 irq_id) { - int i; - void *irouter = gicd_base + GICD_IROUTER; - unsigned int first_cpu; - - /* Use the core functions to retrieve the first physical id */ - for_each_cpu(first_cpu, dest_cell->cpu_set) - break; + void *irouter = gicd_base + GICD_IROUTER + irq_id; + u32 route = mmio_read32(irouter); - for (i = 0; i < 64; i++, irouter += 8) - if (irqchip_irq_in_cell(config_cell, 32 + i)) - mmio_write64(irouter, first_cpu); + if (!cell_owns_cpu(cell, route)) + mmio_write32(irouter, first_cpu(cell->cpu_set)); } static enum mmio_result gic_handle_redist_access(void *arg, @@ -273,8 +267,6 @@ static enum mmio_result gic_handle_redist_access(void *arg, static int gic_cell_init(struct cell *cell) { - gic_route_spis(cell, cell); - mmio_region_register(cell, (unsigned long)gicd_base, gicd_size, gic_handle_dist_access, NULL); mmio_region_register(cell, (unsigned long)gicr_base, gicr_size, @@ -448,6 +440,7 @@ struct irqchip_ops irqchip = { .cpu_init = gic_cpu_init, .cpu_reset = gic_cpu_reset, .cell_init = gic_cell_init, + .adjust_irq_target = gic_adjust_irq_target, .send_sgi = gic_send_sgi, .handle_irq = gic_handle_irq, .inject_irq = gic_inject_irq, diff --git a/hypervisor/arch/arm/include/asm/gic_common.h b/hypervisor/arch/arm/include/asm/gic_common.h index 1625953..df43b97 100644 --- a/hypervisor/arch/arm/include/asm/gic_common.h +++ b/hypervisor/arch/arm/include/asm/gic_common.h @@ -17,6 +17,7 @@ #include #define GICD_CTLR 0x0000 +# define GICD_CTLR_ARE_NS (1 << 4) #define GICD_TYPER 0x0004 #define GICD_IIDR 0x0008 #define GICD_IGROUPR 0x0080 @@ -48,13 +49,14 @@ struct arm_mmio_access; struct per_cpu; struct sgi; +extern u8 target_cpu_map[]; + int gic_probe_cpu_id(unsigned int cpu); enum mmio_result gic_handle_dist_access(void *arg, struct mmio_access *mmio); enum mmio_result gic_handle_irq_route(struct mmio_access *mmio, unsigned int irq); void gic_handle_sgir_write(struct sgi *sgi, bool virt_input); void gic_handle_irq(struct per_cpu *cpu_data); -void gic_target_spis(struct cell *config_cell, struct cell *dest_cell); bool gic_targets_in_cell(struct cell *cell, u8 targets); #endif /* !__ASSEMBLY__ */ diff --git a/hypervisor/arch/arm/include/asm/irqchip.h b/hypervisor/arch/arm/include/asm/irqchip.h index eb78a58..eb4c25e 100644 --- a/hypervisor/arch/arm/include/asm/irqchip.h +++ b/hypervisor/arch/arm/include/asm/irqchip.h @@ -45,6 +45,7 @@ struct irqchip_ops { int (*cell_init)(struct cell *cell); void (*cell_exit)(struct cell *cell); int (*cpu_reset)(struct per_cpu *cpu_data, bool is_shutdown); + void (*adjust_irq_target)(struct cell *cell, u16 irq_id); int (*send_sgi)(struct sgi *sgi); void (*handle_irq)(struct per_cpu *cpu_data); diff --git a/hypervisor/arch/arm/irqchip.c b/hypervisor/arch/arm/irqchip.c index ef01791..a6fce01 100644 --- a/hypervisor/arch/arm/irqchip.c +++ b/hypervisor/arch/arm/irqchip.c @@ -176,7 +176,7 @@ int irqchip_cell_init(struct cell *cell) if (err) return err; - if (cell != &root_cell) + if (cell != &root_cell) { for_each_irqchip(chip, cell->config, n) { if (chip->address != (unsigned long)gicd_base) continue; @@ -185,6 +185,14 @@ int irqchip_cell_init(struct cell *cell) ~chip->pin_bitmap[pos]; } + for (n = 32; n < sizeof(cell->arch.irq_bitmap) * 8; n++) { + if (irqchip_irq_in_cell(cell, n)) + irqchip.adjust_irq_target(cell, n); + if (irqchip_irq_in_cell(&root_cell, n)) + irqchip.adjust_irq_target(&root_cell, n); + } + } + return 0; }