Commit bc06a9e0 authored by Shanker Donthineni's avatar Shanker Donthineni Committed by Thomas Gleixner
Browse files

genirq: Use hlist for managing resend handlers



The current implementation utilizes a bitmap for managing interrupt resend
handlers, which is allocated based on the SPARSE_IRQ/NR_IRQS macros.
However, this method may not efficiently utilize memory during runtime,
particularly when IRQ_BITMAP_BITS is large.

Address this issue by using an hlist to manage interrupt resend handlers
instead of relying on a static bitmap memory allocation. Additionally, a
new function, clear_irq_resend(), is introduced and called from
irq_shutdown to ensure a graceful teardown of the interrupt.

Signed-off-by: default avatarShanker Donthineni <sdonthineni@nvidia.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20230519134902.1495562-2-sdonthineni@nvidia.com
parent d15121be
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -102,6 +102,9 @@ struct irq_desc {
	int			parent_irq;
	struct module		*owner;
	const char		*name;
#ifdef CONFIG_HARDIRQS_SW_RESEND
	struct hlist_node	resend_node;
#endif
} ____cacheline_internodealigned_in_smp;

#ifdef CONFIG_SPARSE_IRQ
+1 −0
Original line number Diff line number Diff line
@@ -306,6 +306,7 @@ static void __irq_disable(struct irq_desc *desc, bool mask);
void irq_shutdown(struct irq_desc *desc)
{
	if (irqd_is_started(&desc->irq_data)) {
		clear_irq_resend(desc);
		desc->depth = 1;
		if (desc->irq_data.chip->irq_shutdown) {
			desc->irq_data.chip->irq_shutdown(&desc->irq_data);
+2 −0
Original line number Diff line number Diff line
@@ -113,6 +113,8 @@ irqreturn_t handle_irq_event(struct irq_desc *desc);

/* Resending of interrupts :*/
int check_irq_resend(struct irq_desc *desc, bool inject);
void clear_irq_resend(struct irq_desc *desc);
void irq_resend_init(struct irq_desc *desc);
bool irq_wait_for_poll(struct irq_desc *desc);
void __irq_wake_thread(struct irq_desc *desc, struct irqaction *action);

+2 −0
Original line number Diff line number Diff line
@@ -415,6 +415,7 @@ static struct irq_desc *alloc_desc(int irq, int node, unsigned int flags,
	desc_set_defaults(irq, desc, node, affinity, owner);
	irqd_set(&desc->irq_data, flags);
	kobject_init(&desc->kobj, &irq_kobj_type);
	irq_resend_init(desc);

	return desc;

@@ -581,6 +582,7 @@ int __init early_irq_init(void)
		mutex_init(&desc[i].request_mutex);
		init_waitqueue_head(&desc[i].wait_for_threads);
		desc_set_defaults(i, &desc[i], node, NULL, NULL);
		irq_resend_init(desc);
	}
	return arch_early_irq_init();
}
+30 −17
Original line number Diff line number Diff line
@@ -21,8 +21,9 @@

#ifdef CONFIG_HARDIRQS_SW_RESEND

/* Bitmap to handle software resend of interrupts: */
static DECLARE_BITMAP(irqs_resend, IRQ_BITMAP_BITS);
/* hlist_head to handle software resend of interrupts: */
static HLIST_HEAD(irq_resend_list);
static DEFINE_RAW_SPINLOCK(irq_resend_lock);

/*
 * Run software resends of IRQ's
@@ -30,18 +31,17 @@ static DECLARE_BITMAP(irqs_resend, IRQ_BITMAP_BITS);
static void resend_irqs(struct tasklet_struct *unused)
{
	struct irq_desc *desc;
	int irq;

	while (!bitmap_empty(irqs_resend, nr_irqs)) {
		irq = find_first_bit(irqs_resend, nr_irqs);
		clear_bit(irq, irqs_resend);
		desc = irq_to_desc(irq);
		if (!desc)
			continue;
		local_irq_disable();
	raw_spin_lock_irq(&irq_resend_lock);
	while (!hlist_empty(&irq_resend_list)) {
		desc = hlist_entry(irq_resend_list.first, struct irq_desc,
				   resend_node);
		hlist_del_init(&desc->resend_node);
		raw_spin_unlock(&irq_resend_lock);
		desc->handle_irq(desc);
		local_irq_enable();
		raw_spin_lock(&irq_resend_lock);
	}
	raw_spin_unlock_irq(&irq_resend_lock);
}

/* Tasklet to handle resend: */
@@ -49,8 +49,6 @@ static DECLARE_TASKLET(resend_tasklet, resend_irqs);

static int irq_sw_resend(struct irq_desc *desc)
{
	unsigned int irq = irq_desc_get_irq(desc);

	/*
	 * Validate whether this interrupt can be safely injected from
	 * non interrupt context
@@ -70,16 +68,31 @@ static int irq_sw_resend(struct irq_desc *desc)
		 */
		if (!desc->parent_irq)
			return -EINVAL;
		irq = desc->parent_irq;
	}

	/* Set it pending and activate the softirq: */
	set_bit(irq, irqs_resend);
	/* Add to resend_list and activate the softirq: */
	raw_spin_lock(&irq_resend_lock);
	hlist_add_head(&desc->resend_node, &irq_resend_list);
	raw_spin_unlock(&irq_resend_lock);
	tasklet_schedule(&resend_tasklet);
	return 0;
}

void clear_irq_resend(struct irq_desc *desc)
{
	raw_spin_lock(&irq_resend_lock);
	hlist_del_init(&desc->resend_node);
	raw_spin_unlock(&irq_resend_lock);
}

void irq_resend_init(struct irq_desc *desc)
{
	INIT_HLIST_NODE(&desc->resend_node);
}
#else
void clear_irq_resend(struct irq_desc *desc) {}
void irq_resend_init(struct irq_desc *desc) {}

static int irq_sw_resend(struct irq_desc *desc)
{
	return -EINVAL;