ACPI spec is x86 oriented thus it use NMI for error reporting as low latency error signalling way. Fast response to error occurrance is what ARM trying to satisfy using interrupt prioritization since there is no NMI direct equivalent for ARM architecture.
Patch set are divided into three step: 1. Create generic code responsible for setting interrupt priority. 2. Use it in interrupt setup. 3. Example of interrupt controller priority mapping for GIC platform dependent code.
Patch set tries to meet requirements like: - not breaking existing code - easy to expand to new priority levels - easy to map generic priority levels to platform dependent priority values
See commit logs for more detailed explanation.
Some of the interrupt controller allow to prioritize interrupts. All of interrupts have equal priority so far. This commit add infrastructure to manipulate interrupt signaling order which means that some of them can take precedence over the others.
Initial implementation assume three priority level: HIGH - interrupt which need to be served ASAP DEFAULT - default interrupt priority LOW - interrupt which doesn't care about response latency
Such generic priority levels approach require mapping to the architecture specific value. It could be done using static allocated table like:
static unsigned int priority_map [SIZE] = { [IRQP_HIGH] = 0x00, [IRQP_DEFAULT] = 0xa0, [IRQP_LOW] = 0xe0, }; It allow us to be compatible in case of irqpriority_t (see include/linux/irqpriority.h) further modification.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- include/linux/irq.h | 2 ++ include/linux/irqpriority.h | 24 ++++++++++++++++++++++++ kernel/irq/chip.c | 9 +++++++++ kernel/irq/internals.h | 1 + 4 files changed, 36 insertions(+) create mode 100644 include/linux/irqpriority.h
diff --git a/include/linux/irq.h b/include/linux/irq.h index f04d3ba..5ab6096 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -17,6 +17,7 @@ #include <linux/gfp.h> #include <linux/irqreturn.h> #include <linux/irqnr.h> +#include <linux/irqpriority.h> #include <linux/errno.h> #include <linux/topology.h> #include <linux/wait.h> @@ -317,6 +318,7 @@ struct irq_chip { int (*irq_retrigger)(struct irq_data *data); int (*irq_set_type)(struct irq_data *data, unsigned int flow_type); int (*irq_set_wake)(struct irq_data *data, unsigned int on); + int (*irq_set_priority)(struct irq_data *data, irqpriority_t priority);
void (*irq_bus_lock)(struct irq_data *data); void (*irq_bus_sync_unlock)(struct irq_data *data); diff --git a/include/linux/irqpriority.h b/include/linux/irqpriority.h new file mode 100644 index 0000000..cf6bf8d --- /dev/null +++ b/include/linux/irqpriority.h @@ -0,0 +1,24 @@ +#ifndef _LINUX_IRQPRIORITY_H +#define _LINUX_IRQPRIORITY_H + +/** + * enum irqpriority + * @IRQP_HIGH address to low response latency interrupt e.g. error + * signaling + * @IRQP_DEFAULT default priority and set for all interrupt sources + * during interrupt controller initialization + * @IRQP_LOW interrupt which doesn't really care about response + * latency + * @... place for priority extension + */ +enum irqpriority { + IRQP_HIGH = 0, + IRQP_DEFAULT, + IRQP_LOW, + + IRQP_LEVELS_NR +}; + +typedef enum irqpriority irqpriority_t; + +#endif /* _LINUX_IRQPRIORITY_H */ diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index a3bb14f..369e860 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -14,6 +14,7 @@ #include <linux/msi.h> #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/irqpriority.h> #include <linux/kernel_stat.h>
#include <trace/events/irq.h> @@ -281,6 +282,14 @@ void unmask_irq(struct irq_desc *desc) } }
+int irq_set_priority(struct irq_desc *desc, irqpriority_t priority) +{ + if (!desc->irq_data.chip->irq_set_priority) + return -EINVAL; + + return desc->irq_data.chip->irq_set_priority(&desc->irq_data, priority); +} + /* * handle_nested_irq - Handle a nested irq from a irq thread * @irq: the interrupt number diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 001fa5b..c264f5f 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -73,6 +73,7 @@ extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu); extern void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu); extern void mask_irq(struct irq_desc *desc); extern void unmask_irq(struct irq_desc *desc); +extern int irq_set_priority(struct irq_desc *desc, irqpriority_t priority);
extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr);
Once interrupt is shared all actions co-related to given interrupt need to have IRQF_HIGH_PRIORITY flag which means that actions have the same priority.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- include/linux/interrupt.h | 1 + kernel/irq/manage.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 5fa5afe..a566e54 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -71,6 +71,7 @@ #define IRQF_FORCE_RESUME 0x00008000 #define IRQF_NO_THREAD 0x00010000 #define IRQF_EARLY_RESUME 0x00020000 +#define IRQF_HIGH_PRIORITY 0x00040000
#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 514bcfd..ea885e6 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/random.h> #include <linux/interrupt.h> +#include <linux/irqpriority.h> #include <linux/slab.h> #include <linux/sched.h> #include <linux/sched/rt.h> @@ -1006,11 +1007,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) * the same type (level, edge, polarity). So both flag * fields must have IRQF_SHARED set and the bits which * set the trigger type must match. Also all must - * agree on ONESHOT. + * agree on ONESHOT and IRQF_HIGH_PRIORITY. */ if (!((old->flags & new->flags) & IRQF_SHARED) || ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) || - ((old->flags ^ new->flags) & IRQF_ONESHOT)) + ((old->flags ^ new->flags) & IRQF_ONESHOT) || + ((old->flags ^ new->flags) & IRQF_HIGH_PRIORITY)) goto mismatch;
/* All handlers must agree on per-cpuness */ @@ -1115,6 +1117,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) if (new->flags & IRQF_ONESHOT) desc->istate |= IRQS_ONESHOT;
+ if (new->flags & IRQF_HIGH_PRIORITY) { + ret = irq_set_priority(desc, IRQP_HIGH); + if (ret) + goto out_mask; + } + if (irq_settings_can_autoenable(desc)) irq_startup(desc, true); else
Couple of notes: o new handler has been added irq_set_priority o this could be an example of how to map generic priority level to architecture priority value (see comments in code)
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- drivers/irqchip/irq-gic.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 13b2849..97e919f 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -37,6 +37,7 @@ #include <linux/of_irq.h> #include <linux/irqdomain.h> #include <linux/interrupt.h> +#include <linux/irqpriority.h> #include <linux/percpu.h> #include <linux/slab.h> #include <linux/irqchip/chained_irq.h> @@ -93,6 +94,19 @@ struct irq_chip gic_arch_extn = { .irq_set_wake = NULL, };
+/* + * Map generic interrupt priority levels (irqpriority_t) to GIC_DIST_PRI + * register value. Value should be mapped using table index assignment: + * [priority level] = <vale for GIC_DIST_PRI> which allow us to be compatible + * in case of irqpriority_t (see include/linux/irqpriority.h) further + * modification. + */ +static unsigned int priority_map [IRQP_LEVELS_NR] = { + [IRQP_HIGH] = 0x00, + [IRQP_DEFAULT] = 0xa0, + [IRQP_LOW] = 0xe0, +}; + #ifndef MAX_GIC_NR #define MAX_GIC_NR 1 #endif @@ -332,12 +346,30 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); }
+int gic_set_priority(struct irq_data *data, irqpriority_t priority) +{ + unsigned int hw_irq = gic_irq(data); + u32 cur_priority; + + if (hw_irq < 32) + return -EINVAL; + + raw_spin_lock(&irq_controller_lock); + cur_priority = readl_relaxed(gic_dist_base(data) + GIC_DIST_PRI + (hw_irq / 4) * 4); + cur_priority &= ~(0xff << (hw_irq % 4)); + cur_priority |= priority_map[priority] << (hw_irq % 4); + writel_relaxed(cur_priority, gic_dist_base(data) + GIC_DIST_PRI + (hw_irq / 4) * 4); + raw_spin_unlock(&irq_controller_lock); + return 0; +} + static struct irq_chip gic_chip = { .name = "GIC", .irq_mask = gic_mask_irq, .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoi_irq, .irq_set_type = gic_set_type, + .irq_set_priority = gic_set_priority, .irq_retrigger = gic_retrigger, #ifdef CONFIG_SMP .irq_set_affinity = gic_set_affinity,
Hi Tomasz,
The patches look good to me, but I can only really analyse them at the base level. I think for proper review you will need to send them as a RFC on arm-linux list and put on your asbestos jacket.
Graeme
On Mon, Aug 26, 2013 at 12:17:12PM +0200, Tomasz Nowicki wrote:
ACPI spec is x86 oriented thus it use NMI for error reporting as low latency error signalling way. Fast response to error occurrance is what ARM trying to satisfy using interrupt prioritization since there is no NMI direct equivalent for ARM architecture.
Patch set are divided into three step:
- Create generic code responsible for setting interrupt priority.
- Use it in interrupt setup.
- Example of interrupt controller priority mapping for GIC platform dependent code.
Patch set tries to meet requirements like:
- not breaking existing code
- easy to expand to new priority levels
- easy to map generic priority levels to platform dependent priority values
See commit logs for more detailed explanation.
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi
Yep, touching the whole irq subsystem is indeed... "touchy" business :-)
What if we start by setting one single interrupt as high prio to start with? This would leave all other interrupts untouched?
The main concern would be in changing the irq handler to look into priorities... With one single high prio irq only, the changes would be hopefully minimal
/Andrea
On 26/ago/2013, at 13:00, Graeme Gregory graeme.gregory@linaro.org wrote:
Hi Tomasz,
The patches look good to me, but I can only really analyse them at the base level. I think for proper review you will need to send them as a RFC on arm-linux list and put on your asbestos jacket.
Graeme
On Mon, Aug 26, 2013 at 12:17:12PM +0200, Tomasz Nowicki wrote:
ACPI spec is x86 oriented thus it use NMI for error reporting as low latency error signalling way. Fast response to error occurrance is what ARM trying to satisfy using interrupt prioritization since there is no NMI direct equivalent for ARM architecture.
Patch set are divided into three step:
- Create generic code responsible for setting interrupt priority.
- Use it in interrupt setup.
- Example of interrupt controller priority mapping for GIC platform dependent
code.
Patch set tries to meet requirements like:
- not breaking existing code
- easy to expand to new priority levels
- easy to map generic priority levels to platform dependent priority values
See commit logs for more detailed explanation.
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi
W dniu 26.08.2013 13:19, Andrea Gallo pisze:
Yep, touching the whole irq subsystem is indeed... "touchy" business :-)
What if we start by setting one single interrupt as high prio to start with? This would leave all other interrupts untouched?
Initially I added 3 levels. The middle is default and setup initially for all interrupts. Once one interrupt is requested with high priority all others are untouched. This one interrupt will take precedence over all others. In case flag would not be added, nothing change in regards to interrupt priority, Basically high priority should be reserved for errors only and triggered in case of OS crash so it has no influence to all others interrupts during normal OS work.
This is rather ARMv7 solution for NMI. ARMv8 should handle with error signals using SError exception. However, v8 can still define some non-standard peripheral interrupt (error signal) as high priority.
Tomasz
The main concern would be in changing the irq handler to look into priorities... With one single high prio irq only, the changes would be hopefully minimal
/Andrea
On 26/ago/2013, at 13:00, Graeme Gregory graeme.gregory@linaro.org wrote:
Hi Tomasz,
The patches look good to me, but I can only really analyse them at the base level. I think for proper review you will need to send them as a RFC on arm-linux list and put on your asbestos jacket.
Graeme
On Mon, Aug 26, 2013 at 12:17:12PM +0200, Tomasz Nowicki wrote:
ACPI spec is x86 oriented thus it use NMI for error reporting as low latency error signalling way. Fast response to error occurrance is what ARM trying to satisfy using interrupt prioritization since there is no NMI direct equivalent for ARM architecture.
Patch set are divided into three step:
- Create generic code responsible for setting interrupt priority.
- Use it in interrupt setup.
- Example of interrupt controller priority mapping for GIC platform dependent code.
Patch set tries to meet requirements like:
- not breaking existing code
- easy to expand to new priority levels
- easy to map generic priority levels to platform dependent priority values
See commit logs for more detailed explanation.
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi