From 72491643469aab736536ae71dcd199b19dabd891 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 13 Sep 2017 23:29:10 +0200 Subject: genirq/irqdomain: Update irq_domain_ops.activate() signature The irq_domain_ops.activate() callback has no return value and no way to tell the function that the activation is early. The upcoming changes to support a reservation scheme which allows to assign interrupt vectors on x86 only when the interrupt is actually requested requires: - A return value, so activation can fail at request_irq() time - Information that the activate invocation is early, i.e. before request_irq(). Signed-off-by: Thomas Gleixner Tested-by: Juergen Gross Tested-by: Yu Chen Acked-by: Juergen Gross Cc: Boris Ostrovsky Cc: Tony Luck Cc: Marc Zyngier Cc: Alok Kataria Cc: Joerg Roedel Cc: "Rafael J. Wysocki" Cc: Steven Rostedt Cc: Christoph Hellwig Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Paolo Bonzini Cc: Rui Zhang Cc: "K. Y. Srinivasan" Cc: Arjan van de Ven Cc: Dan Williams Cc: Len Brown Link: https://lkml.kernel.org/r/20170913213152.848490816@linutronix.de --- drivers/irqchip/irq-gic-v3-its.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index e8d89343d613..20e2b5fac7b9 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -2186,8 +2186,8 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, return 0; } -static void its_irq_domain_activate(struct irq_domain *domain, - struct irq_data *d) +static int its_irq_domain_activate(struct irq_domain *domain, + struct irq_data *d, bool early) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); u32 event = its_get_event_id(d); @@ -2205,6 +2205,7 @@ static void its_irq_domain_activate(struct irq_domain *domain, /* Map the GIC IRQ and event to the device */ its_send_mapti(its_dev, d->hwirq, event); + return 0; } static void its_irq_domain_deactivate(struct irq_domain *domain, @@ -2678,8 +2679,8 @@ static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq return err; } -static void its_vpe_irq_domain_activate(struct irq_domain *domain, - struct irq_data *d) +static int its_vpe_irq_domain_activate(struct irq_domain *domain, + struct irq_data *d, bool early) { struct its_vpe *vpe = irq_data_get_irq_chip_data(d); @@ -2687,6 +2688,7 @@ static void its_vpe_irq_domain_activate(struct irq_domain *domain, vpe->col_idx = cpumask_first(cpu_online_mask); its_send_vmapp(vpe, true); its_send_vinvall(vpe); + return 0; } static void its_vpe_irq_domain_deactivate(struct irq_domain *domain, -- cgit v1.2.3 From fa1500191958660952a3a8466aad54003701a7b6 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 17 Oct 2017 17:55:54 +0100 Subject: irqchip/gic-v3: Probe device ID space before quirks handling Before adding another SoC whose device ID space deviates from the value presented in the GIC ID registers, let's slightly refactor the code so that the ID registers are probed before that quirks handling executes. This allows us to move the device ID override into the quirk handler itself. Signed-off-by: Ard Biesheuvel Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 20e2b5fac7b9..fbb8eba2ac84 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1650,23 +1650,14 @@ static void its_free_tables(struct its_node *its) static int its_alloc_tables(struct its_node *its) { - u64 typer = gic_read_typer(its->base + GITS_TYPER); - u32 ids = GITS_TYPER_DEVBITS(typer); u64 shr = GITS_BASER_InnerShareable; u64 cache = GITS_BASER_RaWaWb; u32 psz = SZ_64K; int err, i; - if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) { - /* - * erratum 22375: only alloc 8MB table size - * erratum 24313: ignore memory access type - */ - cache = GITS_BASER_nCnB; - ids = 0x14; /* 20 bits, 8MB */ - } - - its->device_ids = ids; + if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) + /* erratum 24313: ignore memory access type */ + cache = GITS_BASER_nCnB; for (i = 0; i < GITS_BASER_NR_REGS; i++) { struct its_baser *baser = its->tables + i; @@ -2743,6 +2734,8 @@ static void __maybe_unused its_enable_quirk_cavium_22375(void *data) { struct its_node *its = data; + /* erratum 22375: only alloc 8MB table size */ + its->device_ids = 0x14; /* 20 bits, 8MB */ its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375; } @@ -2944,6 +2937,7 @@ static int __init its_probe_one(struct resource *res, its->base = its_base; its->phys_base = res->start; its->ite_size = GITS_TYPER_ITT_ENTRY_SIZE(typer); + its->device_ids = GITS_TYPER_DEVBITS(typer); its->is_v4 = !!(typer & GITS_TYPER_VLPIS); if (its->is_v4) { if (!(typer & GITS_TYPER_VMOVP)) { -- cgit v1.2.3 From 9d111d49106b61f5a652d5418e85d8741b1a0427 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 17 Oct 2017 17:55:55 +0100 Subject: irqchip/gic: Make quirks matching conditional on init return value As it turns out, the IIDR is not sufficient to distinguish between GICv3 implementations when it comes to enabling quirks. So update the prototype of the init() hook to return a bool, and interpret a 'false' return value as no match, in which case the 'enabling workaround' log message should not be printed. Signed-off-by: Ard Biesheuvel Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-common.c | 5 +++-- drivers/irqchip/irq-gic-common.h | 2 +- drivers/irqchip/irq-gic-v3-its.c | 12 +++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index 9ae71804b5dd..30017df5b54c 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -40,8 +40,9 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks, for (; quirks->desc; quirks++) { if (quirks->iidr != (quirks->mask & iidr)) continue; - quirks->init(data); - pr_info("GIC: enabling workaround for %s\n", quirks->desc); + if (quirks->init(data)) + pr_info("GIC: enabling workaround for %s\n", + quirks->desc); } } diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h index 205e5fddf6da..3919cd7c5285 100644 --- a/drivers/irqchip/irq-gic-common.h +++ b/drivers/irqchip/irq-gic-common.h @@ -23,7 +23,7 @@ struct gic_quirk { const char *desc; - void (*init)(void *data); + bool (*init)(void *data); u32 iidr; u32 mask; }; diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index fbb8eba2ac84..4d432804c2bc 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -2730,28 +2730,34 @@ static int its_force_quiescent(void __iomem *base) } } -static void __maybe_unused its_enable_quirk_cavium_22375(void *data) +static bool __maybe_unused its_enable_quirk_cavium_22375(void *data) { struct its_node *its = data; /* erratum 22375: only alloc 8MB table size */ its->device_ids = 0x14; /* 20 bits, 8MB */ its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375; + + return true; } -static void __maybe_unused its_enable_quirk_cavium_23144(void *data) +static bool __maybe_unused its_enable_quirk_cavium_23144(void *data) { struct its_node *its = data; its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_23144; + + return true; } -static void __maybe_unused its_enable_quirk_qdf2400_e0065(void *data) +static bool __maybe_unused its_enable_quirk_qdf2400_e0065(void *data) { struct its_node *its = data; /* On QDF2400, the size of the ITE is 16Bytes */ its->ite_size = 16; + + return true; } static const struct gic_quirk its_quirks[] = { -- cgit v1.2.3 From 558b01654d92332d3b7b17bf773cdce8ce16ef46 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 17 Oct 2017 17:55:56 +0100 Subject: irqchip/gic-v3: Add workaround for Synquacer pre-ITS The Socionext Synquacer SoC's implementation of GICv3 has a so-called 'pre-ITS', which maps 32-bit writes targeted at a separate window of size '4 << device_id_bits' onto writes to GITS_TRANSLATER with device ID taken from bits [device_id_bits + 1:2] of the window offset. Writes that target GITS_TRANSLATER directly are reported as originating from device ID #0. So add a workaround for this. Given that this breaks isolation, clear the IRQ_DOMAIN_FLAG_MSI_REMAP flag as well. Acked-by: Rob Herring Signed-off-by: Ard Biesheuvel Signed-off-by: Marc Zyngier --- .../bindings/interrupt-controller/arm,gic-v3.txt | 4 ++ arch/arm64/Kconfig | 8 +++ drivers/irqchip/irq-gic-v3-its.c | 72 +++++++++++++++++++++- 3 files changed, 82 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt index 4c29cdab0ea5..c3e6092f3add 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt @@ -75,6 +75,10 @@ These nodes must have the following properties: - reg: Specifies the base physical address and size of the ITS registers. +Optional: +- socionext,synquacer-pre-its: (u32, u32) tuple describing the untranslated + address and size of the pre-ITS window. + The main GIC node must contain the appropriate #address-cells, #size-cells and ranges properties for the reg property of all ITS nodes. diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 0df64a6a56d4..c4361dff2b74 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -539,6 +539,14 @@ config QCOM_QDF2400_ERRATUM_0065 If unsure, say Y. +config SOCIONEXT_SYNQUACER_PREITS + bool "Socionext Synquacer: Workaround for GICv3 pre-ITS" + default y + help + Socionext Synquacer SoCs implement a separate h/w block to generate + MSI doorbell writes with non-zero values for the device ID. + + If unsure, say Y. endmenu diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 4d432804c2bc..54ea4e26c7a9 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -83,6 +83,8 @@ struct its_baser { u32 psz; }; +struct its_device; + /* * The ITS structure - contains most of the infrastructure, with the * top-level MSI domain, the command queue, the collections, and the @@ -97,11 +99,15 @@ struct its_node { struct its_cmd_block *cmd_write; struct its_baser tables[GITS_BASER_NR_REGS]; struct its_collection *collections; + struct fwnode_handle *fwnode_handle; + u64 (*get_msi_base)(struct its_device *its_dev); struct list_head its_device_list; u64 flags; u32 ite_size; u32 device_ids; int numa_node; + unsigned int msi_domain_flags; + u32 pre_its_base; /* for Socionext Synquacer */ bool is_v4; }; @@ -1095,6 +1101,13 @@ static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val, return IRQ_SET_MASK_OK_DONE; } +static u64 its_irq_get_msi_base(struct its_device *its_dev) +{ + struct its_node *its = its_dev->its; + + return its->phys_base + GITS_TRANSLATER; +} + static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); @@ -1102,7 +1115,7 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) u64 addr; its = its_dev->its; - addr = its->phys_base + GITS_TRANSLATER; + addr = its->get_msi_base(its_dev); msg->address_lo = lower_32_bits(addr); msg->address_hi = upper_32_bits(addr); @@ -2760,6 +2773,45 @@ static bool __maybe_unused its_enable_quirk_qdf2400_e0065(void *data) return true; } +static u64 its_irq_get_msi_base_pre_its(struct its_device *its_dev) +{ + struct its_node *its = its_dev->its; + + /* + * The Socionext Synquacer SoC has a so-called 'pre-ITS', + * which maps 32-bit writes targeted at a separate window of + * size '4 << device_id_bits' onto writes to GITS_TRANSLATER + * with device ID taken from bits [device_id_bits + 1:2] of + * the window offset. + */ + return its->pre_its_base + (its_dev->device_id << 2); +} + +static bool __maybe_unused its_enable_quirk_socionext_synquacer(void *data) +{ + struct its_node *its = data; + u32 pre_its_window[2]; + u32 ids; + + if (!fwnode_property_read_u32_array(its->fwnode_handle, + "socionext,synquacer-pre-its", + pre_its_window, + ARRAY_SIZE(pre_its_window))) { + + its->pre_its_base = pre_its_window[0]; + its->get_msi_base = its_irq_get_msi_base_pre_its; + + ids = ilog2(pre_its_window[1]) - 2; + if (its->device_ids > ids) + its->device_ids = ids; + + /* the pre-ITS breaks isolation, so disable MSI remapping */ + its->msi_domain_flags &= ~IRQ_DOMAIN_FLAG_MSI_REMAP; + return true; + } + return false; +} + static const struct gic_quirk its_quirks[] = { #ifdef CONFIG_CAVIUM_ERRATUM_22375 { @@ -2784,6 +2836,19 @@ static const struct gic_quirk its_quirks[] = { .mask = 0xffffffff, .init = its_enable_quirk_qdf2400_e0065, }, +#endif +#ifdef CONFIG_SOCIONEXT_SYNQUACER_PREITS + { + /* + * The Socionext Synquacer SoC incorporates ARM's own GIC-500 + * implementation, but with a 'pre-ITS' added that requires + * special handling in software. + */ + .desc = "ITS: Socionext Synquacer pre-ITS", + .iidr = 0x0001143b, + .mask = 0xffffffff, + .init = its_enable_quirk_socionext_synquacer, + }, #endif { } @@ -2813,7 +2878,7 @@ static int its_init_domain(struct fwnode_handle *handle, struct its_node *its) inner_domain->parent = its_parent; irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS); - inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_REMAP; + inner_domain->flags |= its->msi_domain_flags; info->ops = &its_msi_domain_ops; info->data = its; inner_domain->host_data = info; @@ -2967,6 +3032,9 @@ static int __init its_probe_one(struct resource *res, goto out_free_its; } its->cmd_write = its->cmd_base; + its->fwnode_handle = handle; + its->get_msi_base = its_irq_get_msi_base; + its->msi_domain_flags = IRQ_DOMAIN_FLAG_MSI_REMAP; its_enable_quirks(its); -- cgit v1.2.3 From a19b462f044b1bc5327f0bc871b46a03efb84363 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 4 Aug 2017 17:45:50 +0100 Subject: irqchip/gic-v3-its: Add post-mortem info on command timeout If the ITS stops processing commands, we're pretty much toasted as we cannot update the configuration anymore (and we're not even sure that the ITS still translates interrups). If that happens, let's dump some basic information about the state of affairs before moving on. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 54ea4e26c7a9..0788de9a5407 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -690,9 +690,9 @@ static void its_flush_cmd(struct its_node *its, struct its_cmd_block *cmd) dsb(ishst); } -static void its_wait_for_range_completion(struct its_node *its, - struct its_cmd_block *from, - struct its_cmd_block *to) +static int its_wait_for_range_completion(struct its_node *its, + struct its_cmd_block *from, + struct its_cmd_block *to) { u64 rd_idx, from_idx, to_idx; u32 count = 1000000; /* 1s! */ @@ -713,12 +713,15 @@ static void its_wait_for_range_completion(struct its_node *its, count--; if (!count) { - pr_err_ratelimited("ITS queue timeout\n"); - return; + pr_err_ratelimited("ITS queue timeout (%llu %llu %llu)\n", + from_idx, to_idx, rd_idx); + return -1; } cpu_relax(); udelay(1); } + + return 0; } /* Warning, macro hell follows */ @@ -754,7 +757,8 @@ post: \ next_cmd = its_post_commands(its); \ raw_spin_unlock_irqrestore(&its->lock, flags); \ \ - its_wait_for_range_completion(its, cmd, next_cmd); \ + if (its_wait_for_range_completion(its, cmd, next_cmd)) \ + pr_err_ratelimited("ITS cmd %ps failed\n", builder); \ } static void its_build_sync_cmd(struct its_cmd_block *sync_cmd, -- cgit v1.2.3 From 67047f90d7dd886d3f505185a6d75517bdbd907c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 28 Jul 2017 21:16:58 +0100 Subject: irqchip/gic-v3-its: Pass its_node pointer to each command builder In order to be able to issue command variants depending on how broken an ITS is, let's pass the its pointer to all command building primitives. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 58 ++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 20 deletions(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 0788de9a5407..4aedbdc62aa0 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -274,10 +274,12 @@ struct its_cmd_block { #define ITS_CMD_QUEUE_SZ SZ_64K #define ITS_CMD_QUEUE_NR_ENTRIES (ITS_CMD_QUEUE_SZ / sizeof(struct its_cmd_block)) -typedef struct its_collection *(*its_cmd_builder_t)(struct its_cmd_block *, +typedef struct its_collection *(*its_cmd_builder_t)(struct its_node *, + struct its_cmd_block *, struct its_cmd_desc *); -typedef struct its_vpe *(*its_cmd_vbuilder_t)(struct its_cmd_block *, +typedef struct its_vpe *(*its_cmd_vbuilder_t)(struct its_node *, + struct its_cmd_block *, struct its_cmd_desc *); static void its_mask_encode(u64 *raw_cmd, u64 val, int h, int l) @@ -381,7 +383,8 @@ static inline void its_fixup_cmd(struct its_cmd_block *cmd) cmd->raw_cmd[3] = cpu_to_le64(cmd->raw_cmd[3]); } -static struct its_collection *its_build_mapd_cmd(struct its_cmd_block *cmd, +static struct its_collection *its_build_mapd_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { unsigned long itt_addr; @@ -401,7 +404,8 @@ static struct its_collection *its_build_mapd_cmd(struct its_cmd_block *cmd, return NULL; } -static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd, +static struct its_collection *its_build_mapc_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { its_encode_cmd(cmd, GITS_CMD_MAPC); @@ -414,7 +418,8 @@ static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd, return desc->its_mapc_cmd.col; } -static struct its_collection *its_build_mapti_cmd(struct its_cmd_block *cmd, +static struct its_collection *its_build_mapti_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { struct its_collection *col; @@ -433,7 +438,8 @@ static struct its_collection *its_build_mapti_cmd(struct its_cmd_block *cmd, return col; } -static struct its_collection *its_build_movi_cmd(struct its_cmd_block *cmd, +static struct its_collection *its_build_movi_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { struct its_collection *col; @@ -451,7 +457,8 @@ static struct its_collection *its_build_movi_cmd(struct its_cmd_block *cmd, return col; } -static struct its_collection *its_build_discard_cmd(struct its_cmd_block *cmd, +static struct its_collection *its_build_discard_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { struct its_collection *col; @@ -468,7 +475,8 @@ static struct its_collection *its_build_discard_cmd(struct its_cmd_block *cmd, return col; } -static struct its_collection *its_build_inv_cmd(struct its_cmd_block *cmd, +static struct its_collection *its_build_inv_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { struct its_collection *col; @@ -485,7 +493,8 @@ static struct its_collection *its_build_inv_cmd(struct its_cmd_block *cmd, return col; } -static struct its_collection *its_build_int_cmd(struct its_cmd_block *cmd, +static struct its_collection *its_build_int_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { struct its_collection *col; @@ -502,7 +511,8 @@ static struct its_collection *its_build_int_cmd(struct its_cmd_block *cmd, return col; } -static struct its_collection *its_build_clear_cmd(struct its_cmd_block *cmd, +static struct its_collection *its_build_clear_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { struct its_collection *col; @@ -519,7 +529,8 @@ static struct its_collection *its_build_clear_cmd(struct its_cmd_block *cmd, return col; } -static struct its_collection *its_build_invall_cmd(struct its_cmd_block *cmd, +static struct its_collection *its_build_invall_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { its_encode_cmd(cmd, GITS_CMD_INVALL); @@ -530,7 +541,8 @@ static struct its_collection *its_build_invall_cmd(struct its_cmd_block *cmd, return NULL; } -static struct its_vpe *its_build_vinvall_cmd(struct its_cmd_block *cmd, +static struct its_vpe *its_build_vinvall_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { its_encode_cmd(cmd, GITS_CMD_VINVALL); @@ -541,7 +553,8 @@ static struct its_vpe *its_build_vinvall_cmd(struct its_cmd_block *cmd, return desc->its_vinvall_cmd.vpe; } -static struct its_vpe *its_build_vmapp_cmd(struct its_cmd_block *cmd, +static struct its_vpe *its_build_vmapp_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { unsigned long vpt_addr; @@ -560,7 +573,8 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_cmd_block *cmd, return desc->its_vmapp_cmd.vpe; } -static struct its_vpe *its_build_vmapti_cmd(struct its_cmd_block *cmd, +static struct its_vpe *its_build_vmapti_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { u32 db; @@ -582,7 +596,8 @@ static struct its_vpe *its_build_vmapti_cmd(struct its_cmd_block *cmd, return desc->its_vmapti_cmd.vpe; } -static struct its_vpe *its_build_vmovi_cmd(struct its_cmd_block *cmd, +static struct its_vpe *its_build_vmovi_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { u32 db; @@ -604,7 +619,8 @@ static struct its_vpe *its_build_vmovi_cmd(struct its_cmd_block *cmd, return desc->its_vmovi_cmd.vpe; } -static struct its_vpe *its_build_vmovp_cmd(struct its_cmd_block *cmd, +static struct its_vpe *its_build_vmovp_cmd(struct its_node *its, + struct its_cmd_block *cmd, struct its_cmd_desc *desc) { its_encode_cmd(cmd, GITS_CMD_VMOVP); @@ -741,7 +757,7 @@ void name(struct its_node *its, \ raw_spin_unlock_irqrestore(&its->lock, flags); \ return; \ } \ - sync_obj = builder(cmd, desc); \ + sync_obj = builder(its, cmd, desc); \ its_flush_cmd(its, cmd); \ \ if (sync_obj) { \ @@ -749,7 +765,7 @@ void name(struct its_node *its, \ if (!sync_cmd) \ goto post; \ \ - buildfn(sync_cmd, sync_obj); \ + buildfn(its, sync_cmd, sync_obj); \ its_flush_cmd(its, sync_cmd); \ } \ \ @@ -761,7 +777,8 @@ post: \ pr_err_ratelimited("ITS cmd %ps failed\n", builder); \ } -static void its_build_sync_cmd(struct its_cmd_block *sync_cmd, +static void its_build_sync_cmd(struct its_node *its, + struct its_cmd_block *sync_cmd, struct its_collection *sync_col) { its_encode_cmd(sync_cmd, GITS_CMD_SYNC); @@ -773,7 +790,8 @@ static void its_build_sync_cmd(struct its_cmd_block *sync_cmd, static BUILD_SINGLE_CMD_FUNC(its_send_single_command, its_cmd_builder_t, struct its_collection, its_build_sync_cmd) -static void its_build_vsync_cmd(struct its_cmd_block *sync_cmd, +static void its_build_vsync_cmd(struct its_node *its, + struct its_cmd_block *sync_cmd, struct its_vpe *sync_vpe) { its_encode_cmd(sync_cmd, GITS_CMD_VSYNC); -- cgit v1.2.3 From 5c9a882e940dde2f3e80eb3c7635a3307be511b6 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 28 Jul 2017 21:20:37 +0100 Subject: irqchip/gic-v3-its: Workaround HiSilicon Hip07 redistributor addressing The ITSes on the Hip07 (as present in the Huawei D05) are broken when it comes to addressing the redistributors, and need to be explicitely told to address the VLPI page instead of the redistributor base address. So let's add yet another quirk, fixing up the target address in the command stream. Signed-off-by: Marc Zyngier --- Documentation/arm64/silicon-errata.txt | 1 + arch/arm64/Kconfig | 11 +++++++++++ drivers/irqchip/irq-gic-v3-its.c | 30 ++++++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt index 66e8ce14d23d..304bf22bb83c 100644 --- a/Documentation/arm64/silicon-errata.txt +++ b/Documentation/arm64/silicon-errata.txt @@ -70,6 +70,7 @@ stable kernels. | | | | | | Hisilicon | Hip0{5,6,7} | #161010101 | HISILICON_ERRATUM_161010101 | | Hisilicon | Hip0{6,7} | #161010701 | N/A | +| Hisilicon | Hip07 | #161600802 | HISILICON_ERRATUM_161600802 | | | | | | | Qualcomm Tech. | Falkor v1 | E1003 | QCOM_FALKOR_ERRATUM_1003 | | Qualcomm Tech. | Falkor v1 | E1009 | QCOM_FALKOR_ERRATUM_1009 | diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index c4361dff2b74..22455e4168c1 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -539,6 +539,7 @@ config QCOM_QDF2400_ERRATUM_0065 If unsure, say Y. + config SOCIONEXT_SYNQUACER_PREITS bool "Socionext Synquacer: Workaround for GICv3 pre-ITS" default y @@ -546,6 +547,16 @@ config SOCIONEXT_SYNQUACER_PREITS Socionext Synquacer SoCs implement a separate h/w block to generate MSI doorbell writes with non-zero values for the device ID. + If unsure, say Y. + +config HISILICON_ERRATUM_161600802 + bool "Hip07 161600802: Erroneous redistributor VLPI base" + default y + help + The HiSilicon Hip07 SoC usees the wrong redistributor base + when issued ITS commands such as VMOVP and VMAPP, and requires + a 128kB offset to be applied to the target address in this commands. + If unsure, say Y. endmenu diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 4aedbdc62aa0..6cc57dc142df 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -109,6 +109,7 @@ struct its_node { unsigned int msi_domain_flags; u32 pre_its_base; /* for Socionext Synquacer */ bool is_v4; + int vlpi_redist_offset; }; #define ITS_ITT_ALIGN SZ_256 @@ -558,13 +559,15 @@ static struct its_vpe *its_build_vmapp_cmd(struct its_node *its, struct its_cmd_desc *desc) { unsigned long vpt_addr; + u64 target; vpt_addr = virt_to_phys(page_address(desc->its_vmapp_cmd.vpe->vpt_page)); + target = desc->its_vmapp_cmd.col->target_address + its->vlpi_redist_offset; its_encode_cmd(cmd, GITS_CMD_VMAPP); its_encode_vpeid(cmd, desc->its_vmapp_cmd.vpe->vpe_id); its_encode_valid(cmd, desc->its_vmapp_cmd.valid); - its_encode_target(cmd, desc->its_vmapp_cmd.col->target_address); + its_encode_target(cmd, target); its_encode_vpt_addr(cmd, vpt_addr); its_encode_vpt_size(cmd, LPI_NRBITS - 1); @@ -623,11 +626,14 @@ static struct its_vpe *its_build_vmovp_cmd(struct its_node *its, struct its_cmd_block *cmd, struct its_cmd_desc *desc) { + u64 target; + + target = desc->its_vmovp_cmd.col->target_address + its->vlpi_redist_offset; its_encode_cmd(cmd, GITS_CMD_VMOVP); its_encode_seq_num(cmd, desc->its_vmovp_cmd.seq_num); its_encode_its_list(cmd, desc->its_vmovp_cmd.its_list); its_encode_vpeid(cmd, desc->its_vmovp_cmd.vpe->vpe_id); - its_encode_target(cmd, desc->its_vmovp_cmd.col->target_address); + its_encode_target(cmd, target); its_fixup_cmd(cmd); @@ -2834,6 +2840,18 @@ static bool __maybe_unused its_enable_quirk_socionext_synquacer(void *data) return false; } +static bool __maybe_unused its_enable_quirk_hip07_161600802(void *data) +{ + struct its_node *its = data; + + /* + * Hip07 insists on using the wrong address for the VLPI + * page. Trick it into doing the right thing... + */ + its->vlpi_redist_offset = SZ_128K; + return true; +} + static const struct gic_quirk its_quirks[] = { #ifdef CONFIG_CAVIUM_ERRATUM_22375 { @@ -2871,6 +2889,14 @@ static const struct gic_quirk its_quirks[] = { .mask = 0xffffffff, .init = its_enable_quirk_socionext_synquacer, }, +#endif +#ifdef CONFIG_HISILICON_ERRATUM_161600802 + { + .desc = "ITS: Hip07 erratum 161600802", + .iidr = 0x00000004, + .mask = 0xffffffff, + .init = its_enable_quirk_hip07_161600802, + }, #endif { } -- cgit v1.2.3 From debf6d02bb58a099202375ca2ba88e9775b153c6 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sun, 8 Oct 2017 18:44:42 +0100 Subject: irqchip/gic-v3-its: Track per-ITS list number At boot time, we enumerate all the GICv4-capable ITSs, and build a mask of the available ITSs. Take this opportunity to store the ITS number in the its_node structure so that we can use it at a later time. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 6cc57dc142df..994f0879e7df 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -103,6 +103,7 @@ struct its_node { u64 (*get_msi_base)(struct its_device *its_dev); struct list_head its_device_list; u64 flags; + unsigned long list_nr; u32 ite_size; u32 device_ids; int numa_node; @@ -3064,6 +3065,8 @@ static int __init its_probe_one(struct resource *res, if (err < 0) goto out_free_its; + its->list_nr = err; + pr_info("ITS@%pa: Using ITS number %d\n", &res->start, err); } else { -- cgit v1.2.3 From ab60491ee5d346557f152c7e8d3e7238c9b96c5c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sun, 8 Oct 2017 18:48:06 +0100 Subject: irqchip/gic-v3-its: Make GICv4_ITS_LIST_MAX globally available As we're about to make use of the maximum number of ITSs in a GICv4 system, let's make this value global (and rename it to GICv4_ITS_LIST_MAX). Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 10 ++-------- include/linux/irqchip/arm-gic-v4.h | 6 ++++++ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 994f0879e7df..a63b4ee34860 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -156,12 +156,6 @@ static DEFINE_SPINLOCK(its_lock); static struct rdists *gic_rdists; static struct irq_domain *its_parent; -/* - * We have a maximum number of 16 ITSs in the whole system if we're - * using the ITSList mechanism - */ -#define ITS_LIST_MAX 16 - static unsigned long its_list_map; static u16 vmovp_seq_num; static DEFINE_RAW_SPINLOCK(vmovp_lock); @@ -2988,8 +2982,8 @@ static int __init its_compute_its_list_map(struct resource *res, * locking. Should this change, we should address * this. */ - its_number = find_first_zero_bit(&its_list_map, ITS_LIST_MAX); - if (its_number >= ITS_LIST_MAX) { + its_number = find_first_zero_bit(&its_list_map, GICv4_ITS_LIST_MAX); + if (its_number >= GICv4_ITS_LIST_MAX) { pr_err("ITS@%pa: No ITSList entry available!\n", &res->start); return -EINVAL; diff --git a/include/linux/irqchip/arm-gic-v4.h b/include/linux/irqchip/arm-gic-v4.h index 58a4d89aa82c..e26a668826e6 100644 --- a/include/linux/irqchip/arm-gic-v4.h +++ b/include/linux/irqchip/arm-gic-v4.h @@ -20,6 +20,12 @@ struct its_vpe; +/* + * Maximum number of ITTs when GITS_TYPER.VMOVP == 0, using the + * ITSList mechanism to perform inter-ITS synchronization. + */ +#define GICv4_ITS_LIST_MAX 16 + /* Embedded in kvm.arch */ struct its_vm { struct fwnode_handle *fwnode; -- cgit v1.2.3 From 40619a2ef69d5e183717d28e8c98a59319c78a4f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sun, 8 Oct 2017 15:16:09 +0100 Subject: irqchip/gic-v3-its: Make its_send_vinvall operate on a single ITS Currently, its_send_vinvall operates on all ITSs. As we're about to try and limit the amount of commands we send to ITSs that are not involved in dealing with a given VM, let's redefine that primitive so that it takes a target ITS as a parameter. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index a63b4ee34860..22ee83043785 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -983,18 +983,12 @@ static void its_send_vmovp(struct its_vpe *vpe) raw_spin_unlock_irqrestore(&vmovp_lock, flags); } -static void its_send_vinvall(struct its_vpe *vpe) +static void its_send_vinvall(struct its_node *its, struct its_vpe *vpe) { struct its_cmd_desc desc; - struct its_node *its; desc.its_vinvall_cmd.vpe = vpe; - - list_for_each_entry(its, &its_nodes, entry) { - if (!its->is_v4) - continue; - its_send_single_vcommand(its, its_build_vinvall_cmd, &desc); - } + its_send_single_vcommand(its, its_build_vinvall_cmd, &desc); } /* @@ -2466,6 +2460,18 @@ static void its_vpe_deschedule(struct its_vpe *vpe) } } +static void its_vpe_invall(struct its_vpe *vpe) +{ + struct its_node *its; + + list_for_each_entry(its, &its_nodes, entry) { + if (!its->is_v4) + continue; + + its_send_vinvall(its, vpe); + } +} + static int its_vpe_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) { struct its_vpe *vpe = irq_data_get_irq_chip_data(d); @@ -2481,7 +2487,7 @@ static int its_vpe_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) return 0; case INVALL_VPE: - its_send_vinvall(vpe); + its_vpe_invall(vpe); return 0; default: @@ -2710,11 +2716,19 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, struct irq_data *d, bool early) { struct its_vpe *vpe = irq_data_get_irq_chip_data(d); + struct its_node *its; /* Map the VPE to the first possible CPU */ vpe->col_idx = cpumask_first(cpu_online_mask); its_send_vmapp(vpe, true); - its_send_vinvall(vpe); + + list_for_each_entry(its, &its_nodes, entry) { + if (!its->is_v4) + continue; + + its_send_vinvall(its, vpe); + } + return 0; } -- cgit v1.2.3 From 75fd951be846a494b0f01f80e859dcb74970f0d5 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sun, 8 Oct 2017 18:46:39 +0100 Subject: irqchip/gic-v3-its: Make its_send_vmapp operate on a single ITS Currently, its_send_vmapp operates on all ITSs. As we're about to try and limit the amount of commands we send to ITSs that are not involved in dealing with a given VM, let's redefine that primitive so that it takes a target ITS as a parameter. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 22ee83043785..b14585d6397b 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -924,21 +924,16 @@ static void its_send_vmovi(struct its_device *dev, u32 id) its_send_single_vcommand(dev->its, its_build_vmovi_cmd, &desc); } -static void its_send_vmapp(struct its_vpe *vpe, bool valid) +static void its_send_vmapp(struct its_node *its, + struct its_vpe *vpe, bool valid) { struct its_cmd_desc desc; - struct its_node *its; desc.its_vmapp_cmd.vpe = vpe; desc.its_vmapp_cmd.valid = valid; + desc.its_vmapp_cmd.col = &its->collections[vpe->col_idx]; - list_for_each_entry(its, &its_nodes, entry) { - if (!its->is_v4) - continue; - - desc.its_vmapp_cmd.col = &its->collections[vpe->col_idx]; - its_send_single_vcommand(its, its_build_vmapp_cmd, &desc); - } + its_send_single_vcommand(its, its_build_vmapp_cmd, &desc); } static void its_send_vmovp(struct its_vpe *vpe) @@ -2720,12 +2715,12 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, /* Map the VPE to the first possible CPU */ vpe->col_idx = cpumask_first(cpu_online_mask); - its_send_vmapp(vpe, true); list_for_each_entry(its, &its_nodes, entry) { if (!its->is_v4) continue; + its_send_vmapp(its, vpe, true); its_send_vinvall(its, vpe); } @@ -2736,8 +2731,14 @@ static void its_vpe_irq_domain_deactivate(struct irq_domain *domain, struct irq_data *d) { struct its_vpe *vpe = irq_data_get_irq_chip_data(d); + struct its_node *its; + + list_for_each_entry(its, &its_nodes, entry) { + if (!its->is_v4) + continue; - its_send_vmapp(vpe, false); + its_send_vmapp(its, vpe, false); + } } static const struct irq_domain_ops its_vpe_domain_ops = { -- cgit v1.2.3 From 2247e1bf70639642b1c1375aa9176ccd95736400 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Sun, 8 Oct 2017 18:50:36 +0100 Subject: irqchip/gic-v3-its: Limit scope of VPE mapping to be per ITS So far, we map all VPEs on all ITSs. While this is not wrong, this is quite a big hammer, as moving a VPE around requires all ITSs to be synchronized. Needles to say, this is an expensive proposition. Instead, let's switch to a mode where we issue VMAPP commands only on ITSs that are actually involved in reporting interrupts to the given VM. For that purpose, we refcount the number of interrupts are are mapped for this VM on each ITS, performing the map/unmap operations as required. It then allows us to use this refcount to only issue VMOVP to the ITSs that need to know about this VM. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 75 ++++++++++++++++++++++++++++++++++++++ include/linux/irqchip/arm-gic-v4.h | 1 + 2 files changed, 76 insertions(+) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index b14585d6397b..dc0ece20f964 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -971,6 +971,9 @@ static void its_send_vmovp(struct its_vpe *vpe) if (!its->is_v4) continue; + if (!vpe->its_vm->vlpi_count[its->list_nr]) + continue; + desc.its_vmovp_cmd.col = &its->collections[col_id]; its_send_single_vcommand(its, its_build_vmovp_cmd, &desc); } @@ -1154,6 +1157,58 @@ static int its_irq_set_irqchip_state(struct irq_data *d, return 0; } +static void its_map_vm(struct its_node *its, struct its_vm *vm) +{ + unsigned long flags; + + /* Not using the ITS list? Everything is always mapped. */ + if (!its_list_map) + return; + + raw_spin_lock_irqsave(&vmovp_lock, flags); + + /* + * If the VM wasn't mapped yet, iterate over the vpes and get + * them mapped now. + */ + vm->vlpi_count[its->list_nr]++; + + if (vm->vlpi_count[its->list_nr] == 1) { + int i; + + for (i = 0; i < vm->nr_vpes; i++) { + struct its_vpe *vpe = vm->vpes[i]; + + /* Map the VPE to the first possible CPU */ + vpe->col_idx = cpumask_first(cpu_online_mask); + its_send_vmapp(its, vpe, true); + its_send_vinvall(its, vpe); + } + } + + raw_spin_unlock_irqrestore(&vmovp_lock, flags); +} + +static void its_unmap_vm(struct its_node *its, struct its_vm *vm) +{ + unsigned long flags; + + /* Not using the ITS list? Everything is always mapped. */ + if (!its_list_map) + return; + + raw_spin_lock_irqsave(&vmovp_lock, flags); + + if (!--vm->vlpi_count[its->list_nr]) { + int i; + + for (i = 0; i < vm->nr_vpes; i++) + its_send_vmapp(its, vm->vpes[i], false); + } + + raw_spin_unlock_irqrestore(&vmovp_lock, flags); +} + static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); @@ -1189,6 +1244,9 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) /* Already mapped, move it around */ its_send_vmovi(its_dev, event); } else { + /* Ensure all the VPEs are mapped on this ITS */ + its_map_vm(its_dev->its, info->map->vm); + /* Drop the physical mapping */ its_send_discard(its_dev, event); @@ -1250,6 +1308,9 @@ static int its_vlpi_unmap(struct irq_data *d) LPI_PROP_ENABLED | LPI_PROP_GROUP1)); + /* Potentially unmap the VM from this ITS */ + its_unmap_vm(its_dev->its, its_dev->event_map.vm); + /* * Drop the refcount and make the device available again if * this was the last VLPI. @@ -2463,6 +2524,9 @@ static void its_vpe_invall(struct its_vpe *vpe) if (!its->is_v4) continue; + if (its_list_map && !vpe->its_vm->vlpi_count[its->list_nr]) + continue; + its_send_vinvall(its, vpe); } } @@ -2713,6 +2777,10 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, struct its_vpe *vpe = irq_data_get_irq_chip_data(d); struct its_node *its; + /* If we use the list map, we issue VMAPP on demand... */ + if (its_list_map) + return true; + /* Map the VPE to the first possible CPU */ vpe->col_idx = cpumask_first(cpu_online_mask); @@ -2733,6 +2801,13 @@ static void its_vpe_irq_domain_deactivate(struct irq_domain *domain, struct its_vpe *vpe = irq_data_get_irq_chip_data(d); struct its_node *its; + /* + * If we use the list map, we unmap the VPE once no VLPIs are + * associated with the VM. + */ + if (its_list_map) + return; + list_for_each_entry(its, &its_nodes, entry) { if (!its->is_v4) continue; diff --git a/include/linux/irqchip/arm-gic-v4.h b/include/linux/irqchip/arm-gic-v4.h index e26a668826e6..43cde15f221b 100644 --- a/include/linux/irqchip/arm-gic-v4.h +++ b/include/linux/irqchip/arm-gic-v4.h @@ -36,6 +36,7 @@ struct its_vm { irq_hw_number_t db_lpi_base; unsigned long *db_bitmap; int nr_db_lpis; + u32 vlpi_count[GICv4_ITS_LIST_MAX]; }; /* Embedded in kvm_vcpu.arch */ -- cgit v1.2.3 From 3c1cceeb3d2879d98eda7afd62838fde7b0f920d Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 9 Oct 2017 13:17:43 +0100 Subject: irqchip/gic-v3-its: Only send VINVALL to a single ITS Sending VINVALL to all ITSs is completely pointless, as all we're trying to achieve is to tell the redistributor that the property table for this VPE should be invalidated. Let's issue the command on the first valid ITS and be done with it. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index dc0ece20f964..bc7099352cc9 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -2527,7 +2527,12 @@ static void its_vpe_invall(struct its_vpe *vpe) if (its_list_map && !vpe->its_vm->vlpi_count[its->list_nr]) continue; + /* + * Sending a VINVALL to a single ITS is enough, as all + * we need is to reach the redistributors. + */ its_send_vinvall(its, vpe); + return; } } -- cgit v1.2.3 From 44c4c25e3103d26bee4cc041cbf526e41975055b Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 19 Oct 2017 10:11:34 +0100 Subject: irqchip/gic-v3-its: Update effective affinity on VPE mapping When setting the affinity of a VPE (either because we map or move it), make sure the effective affinity is correctly reported back to the core kernel. Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index bc7099352cc9..6a74f0497f82 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1178,11 +1178,13 @@ static void its_map_vm(struct its_node *its, struct its_vm *vm) for (i = 0; i < vm->nr_vpes; i++) { struct its_vpe *vpe = vm->vpes[i]; + struct irq_data *d = irq_get_irq_data(vpe->irq); /* Map the VPE to the first possible CPU */ vpe->col_idx = cpumask_first(cpu_online_mask); its_send_vmapp(its, vpe, true); its_send_vinvall(its, vpe); + irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx)); } } @@ -2449,6 +2451,8 @@ static int its_vpe_set_affinity(struct irq_data *d, its_vpe_db_proxy_move(vpe, from, cpu); } + irq_data_update_effective_affinity(d, cpumask_of(cpu)); + return IRQ_SET_MASK_OK_DONE; } @@ -2797,6 +2801,8 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, its_send_vinvall(its, vpe); } + irq_data_update_effective_affinity(d, cpumask_of(vpe->col_idx)); + return 0; } -- cgit v1.2.3 From d4d7b4ad2f05c03fb25252aea66f9f3cd7cfbe06 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 26 Oct 2017 10:44:07 +0100 Subject: irqchip/gic-v3-its: Setup VLPI properties at map time So far, we require the hypervisor to update the VLPI properties once the the VLPI mapping has been established. While this makes it easy for the ITS driver, it creates a window where an incoming interrupt can be delivered with an unknown set of properties. Not very nice. Instead, let's add a "properties" field to the mapping structure, and use that to configure the VLPI before it actually gets mapped. Reviewed-by: Christoffer Dall Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 18 ++++++++++++++++-- include/linux/irqchip/arm-gic-v4.h | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 6a74f0497f82..29b2ff5c6841 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1008,9 +1008,15 @@ static void lpi_write_config(struct irq_data *d, u8 clr, u8 set) if (irqd_is_forwarded_to_vcpu(d)) { struct its_device *its_dev = irq_data_get_irq_chip_data(d); u32 event = its_get_event_id(d); + struct its_vlpi_map *map; prop_page = its_dev->event_map.vm->vprop_page; - hwirq = its_dev->event_map.vlpi_maps[event].vintid; + map = &its_dev->event_map.vlpi_maps[event]; + hwirq = map->vintid; + + /* Remember the updated property */ + map->properties &= ~clr; + map->properties |= set | LPI_PROP_GROUP1; } else { prop_page = gic_rdists->prop_page; hwirq = d->hwirq; @@ -1249,12 +1255,20 @@ static int its_vlpi_map(struct irq_data *d, struct its_cmd_info *info) /* Ensure all the VPEs are mapped on this ITS */ its_map_vm(its_dev->its, info->map->vm); + /* + * Flag the interrupt as forwarded so that we can + * start poking the virtual property table. + */ + irqd_set_forwarded_to_vcpu(d); + + /* Write out the property to the prop table */ + lpi_write_config(d, 0xff, info->map->properties); + /* Drop the physical mapping */ its_send_discard(its_dev, event); /* and install the virtual one */ its_send_vmapti(its_dev, event); - irqd_set_forwarded_to_vcpu(d); /* Increment the number of VLPIs */ its_dev->event_map.nr_vlpis++; diff --git a/include/linux/irqchip/arm-gic-v4.h b/include/linux/irqchip/arm-gic-v4.h index 43cde15f221b..447da8ca2156 100644 --- a/include/linux/irqchip/arm-gic-v4.h +++ b/include/linux/irqchip/arm-gic-v4.h @@ -71,12 +71,14 @@ struct its_vpe { * @vm: Pointer to the GICv4 notion of a VM * @vpe: Pointer to the GICv4 notion of a virtual CPU (VPE) * @vintid: Virtual LPI number + * @properties: Priority and enable bits (as written in the prop table) * @db_enabled: Is the VPE doorbell to be generated? */ struct its_vlpi_map { struct its_vm *vm; struct its_vpe *vpe; u32 vintid; + u8 properties; bool db_enabled; }; -- cgit v1.2.3 From 6ef930f20c30f8a7dcffa50fa9f33a9211727a6e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 7 Nov 2017 10:04:38 +0000 Subject: irqchip/gic-v3-its: Fix VPE activate callback return value its_vpe_irq_domain_activate should always return 0. Really. There is not a single case why it wouldn't. So this "return true;" is really a copy/paste issue that got revealed now that we actually check the return value of the activate method. Brown paper bag day. Fixes: 2247e1bf7063 ("irqchip/gic-v3-its: Limit scope of VPE mapping to be per ITS") Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v3-its.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/irqchip/irq-gic-v3-its.c') diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 29b2ff5c6841..be99d59bf636 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -2802,7 +2802,7 @@ static int its_vpe_irq_domain_activate(struct irq_domain *domain, /* If we use the list map, we issue VMAPP on demand... */ if (its_list_map) - return true; + return 0; /* Map the VPE to the first possible CPU */ vpe->col_idx = cpumask_first(cpu_online_mask); -- cgit v1.2.3