On -rt kernels, it is sometimes desirable to be able to prioritize some hardware interrupts over others, i.e. being able to set the priorities of the irq threads different from the default of 50. While chrt(1) can be used after the threads are created, there are at least two problems with that: (1) threads are created and destroyed as devices are brought up and down, making it necessary to continuously monitor /proc for new irq threads, leading to (2) a device may run for a while with its irq thread(s) at the wrong priority.
This is just a quick POC to start some discussion. Probably the locking is all wrong. But this at least gives userspace a way of assigning a priority to a specific hardware interrupt by matching on the chip_name and hwirq - unfortunately, chip_name is not necessarily unique, so it doesn't work for all cases. This would probably be a better fit in /proc alongside the affinity settings, but the problem with that is that the /proc entries don't exist until __setup_irq() is called the first time. [Btw, why are the sysfs files dependent on CONFIG_SPARSE_IRQ?] Signed-off-by: Rasmus Villemoes <li...@rasmusvillemoes.dk> --- include/linux/irqdesc.h | 2 ++ kernel/irq/irqdesc.c | 57 +++++++++++++++++++++++++++++++++++++++++ kernel/irq/manage.c | 11 ++++---- 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index d6e2ab538ef2..29cf7ff99cf3 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -5,6 +5,7 @@ #include <linux/rcupdate.h> #include <linux/kobject.h> #include <linux/mutex.h> +#include <uapi/linux/sched/types.h> /* * Core internal functions to deal with irq descriptors @@ -106,6 +107,7 @@ struct irq_desc { int parent_irq; struct module *owner; const char *name; + struct sched_param sched_param; } ____cacheline_internodealigned_in_smp; #ifdef CONFIG_SPARSE_IRQ diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index c52b737ab8e3..42f72bf6f046 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -125,6 +125,7 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node, for_each_possible_cpu(cpu) *per_cpu_ptr(desc->kstat_irqs, cpu) = 0; desc_smp_init(desc, node, affinity); + desc->sched_param.sched_priority = MAX_USER_RT_PRIO/2; } int nr_irqs = NR_IRQS; @@ -143,6 +144,9 @@ static struct kobject *irq_kobj_base; #define IRQ_ATTR_RO(_name) \ static struct kobj_attribute _name##_attr = __ATTR_RO(_name) +#define IRQ_ATTR_RW(_name) \ +static struct kobj_attribute _name##_attr = __ATTR_RW(_name) + static ssize_t per_cpu_count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -265,6 +269,58 @@ static ssize_t actions_show(struct kobject *kobj, } IRQ_ATTR_RO(actions); +static ssize_t prio_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj); + int prio; + + mutex_lock(&desc->request_mutex); + prio = desc->sched_param.sched_priority; + mutex_unlock(&desc->request_mutex); + + return scnprintf(buf, PAGE_SIZE, "%d\n", prio); +} + +static ssize_t prio_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj); + struct task_struct *thread; + struct irqaction *action; + ssize_t ret; + int prio; + + ret = kstrtoint(buf, 10, &prio); + if (ret) + return ret; + if (prio < 1 || prio > MAX_USER_RT_PRIO - 1) + return -ERANGE; + + mutex_lock(&desc->request_mutex); + + desc->sched_param.sched_priority = prio; + for (action = desc->action; action; action = action->next) { + struct sched_param sp = desc->sched_param; + + thread = action->thread; + if (thread) + sched_setscheduler_nocheck(thread, SCHED_FIFO, &sp); + if (action->secondary) { + thread = action->secondary->thread; + if (thread) { + sp.sched_priority -= 1; + sched_setscheduler_nocheck(thread, SCHED_FIFO, &sp); + } + } + } + + mutex_unlock(&desc->request_mutex); + + return n; +} +IRQ_ATTR_RW(prio); + static struct attribute *irq_attrs[] = { &per_cpu_count_attr.attr, &chip_name_attr.attr, @@ -273,6 +329,7 @@ static struct attribute *irq_attrs[] = { &wakeup_attr.attr, &name_attr.attr, &actions_attr.attr, + &prio_attr.attr, NULL }; ATTRIBUTE_GROUPS(irq); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 78f3ddeb7fe4..a915c5128211 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1203,12 +1203,11 @@ static void irq_nmi_teardown(struct irq_desc *desc) } static int -setup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary) +setup_irq_thread(struct irqaction *new, struct irq_desc *desc, bool secondary) { + struct sched_param param = desc->sched_param; + unsigned int irq = irq_desc_get_irq(desc); struct task_struct *t; - struct sched_param param = { - .sched_priority = MAX_USER_RT_PRIO/2, - }; if (!secondary) { t = kthread_create(irq_thread, new, "irq/%d-%s", irq, @@ -1312,11 +1311,11 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) * thread. */ if (new->thread_fn && !nested) { - ret = setup_irq_thread(new, irq, false); + ret = setup_irq_thread(new, desc, false); if (ret) goto out_mput; if (new->secondary) { - ret = setup_irq_thread(new->secondary, irq, true); + ret = setup_irq_thread(new->secondary, desc, true); if (ret) goto out_thread; } -- 2.20.1