Re: [PATCH] IRQ resource deallocation[0/2]

From: Kenji Kaneshige
Date: Fri Dec 10 2004 - 01:17:05 EST


I'm attching the updated set of patches.

Andrew, could you consider applying these patches?

Thanks,
Kenji Kaneshige

Zwane Mwaikambo wrote:

On Tue, 7 Dec 2004, Kenji Kaneshige wrote:

I had posted the IRQ resource deallocation patch a couple of monthes
ago and I had incorporated all feedbacks from the mailing list
(http://marc.theaimsgroup.com/?l=linux-kernel&m=109688530703122&w=2).
But it doesn't seems to be included yet, so I would like to try again.
I hope my patch is included onto -mm tree since I want the patches
be tested by many people.

You should remove the config option and make it unconditional.

Thanks,
Zwane



Kernel Version: 2.6.10-rc3
Depends: none
Description:

This patch is ACPI portion of IRQ deallocation. This patch defines the
following new interface. The implementation of this interface depends
on each platform.

o void acpi_unregister_gsi(u32 gsi)

This is a opposite portion of acpi_register_gsi(). This has a
responsibility for deallocating IRQ resources associated with
the specified GSI number.

We need to consider the case of shared interrupt. In the case
of shared interrupt, acpi_register_gsi() is called multiple
times for one gsi. That is, registrations and unregistrations
can be nested.

This function undoes the effect of one call to
acpi_register_gsi(). If this matches the last registration,
IRQ resources associated with the specified GSI number are
freed.

This patch also adds the following new function.

o void acpi_pci_irq_disable (struct pci_dev *dev)

This function is a opposite portion of
acpi_pci_enable_irq(). It clears the device's linux IRQ number
and calls acpi_unregister_gsi() to deallocate IRQ resources.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@xxxxxxxxxxxxxx>


---

linux-2.6.10-rc3-kanesige/drivers/acpi/pci_irq.c | 52 +++++++++++++++++++++++
linux-2.6.10-rc3-kanesige/include/linux/acpi.h | 13 +++++
2 files changed, 65 insertions(+)

diff -puN drivers/acpi/pci_irq.c~IRQ_deallocation_acpi drivers/acpi/pci_irq.c
--- linux-2.6.10-rc3/drivers/acpi/pci_irq.c~IRQ_deallocation_acpi 2004-12-06 19:51:50.274759676 +0900
+++ linux-2.6.10-rc3-kanesige/drivers/acpi/pci_irq.c 2004-12-06 19:54:43.254430880 +0900
@@ -407,3 +407,55 @@ acpi_pci_irq_enable (
}
EXPORT_SYMBOL(acpi_pci_irq_enable);

+
+#ifdef CONFIG_ACPI_DEALLOCATE_IRQ
+void
+acpi_pci_irq_disable (
+ struct pci_dev *dev)
+{
+ u32 gsi = 0;
+ u8 pin = 0;
+ int edge_level = ACPI_LEVEL_SENSITIVE;
+ int active_high_low = ACPI_ACTIVE_LOW;
+
+ ACPI_FUNCTION_TRACE("acpi_pci_irq_disable");
+
+ if (!dev)
+ return_VOID;
+
+ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+ if (!pin)
+ return_VOID;
+ pin--;
+
+ if (!dev->bus)
+ return_VOID;
+
+ /*
+ * First we check the PCI IRQ routing table (PRT) for an IRQ.
+ */
+ gsi = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin,
+ &edge_level, &active_high_low);
+ /*
+ * If no PRT entry was found, we'll try to derive an IRQ from the
+ * device's parent bridge.
+ */
+ if (!gsi)
+ gsi = acpi_pci_irq_derive(dev, pin,
+ &edge_level, &active_high_low);
+ if (!gsi)
+ return_VOID;
+
+ /*
+ * TBD: It might be worth clearing dev->irq by magic constant
+ * (e.g. PCI_UNDEFINED_IRQ).
+ */
+
+ printk(KERN_INFO PREFIX "PCI interrupt for device %s disabled\n",
+ pci_name(dev));
+
+ acpi_unregister_gsi(gsi);
+
+ return_VOID;
+}
+#endif /* CONFIG_ACPI_DEALLOCATE_IRQ */
diff -puN include/linux/acpi.h~IRQ_deallocation_acpi include/linux/acpi.h
--- linux-2.6.10-rc3/include/linux/acpi.h~IRQ_deallocation_acpi 2004-12-06 19:51:50.276712814 +0900
+++ linux-2.6.10-rc3-kanesige/include/linux/acpi.h 2004-12-06 19:51:50.279642521 +0900
@@ -416,6 +416,15 @@ static inline int acpi_boot_init(void)
unsigned int acpi_register_gsi (u32 gsi, int edge_level, int active_high_low);
int acpi_gsi_to_irq (u32 gsi, unsigned int *irq);

+/*
+ * This function undoes the effect of one call to acpi_register_gsi().
+ * If this matches the last registration, any IRQ resources for gsi
+ * are freed.
+ */
+#ifdef CONFIG_ACPI_DEALLOCATE_IRQ
+void acpi_unregister_gsi (u32 gsi);
+#endif
+
#ifdef CONFIG_ACPI_PCI

struct acpi_prt_entry {
@@ -441,6 +450,10 @@ struct pci_dev;
int acpi_pci_irq_enable (struct pci_dev *dev);
void acpi_penalize_isa_irq(int irq);

+#ifdef CONFIG_ACPI_DEALLOCATE_IRQ
+void acpi_pci_irq_disable (struct pci_dev *dev);
+#endif
+
struct acpi_pci_driver {
struct acpi_pci_driver *next;
int (*add)(acpi_handle handle);

_

Kernel Version: 2.6.10-rc3
Depends: IRQ_deallocation_acpi.patch
Description:

This is an ia64 portion of IRQ resource deallocation. It implements
pcibios_disable_device() and acpi_unregister_gsi() for ia64.

o acpi_unregister_gsi()

Summary of changes for implementing this interface:

- Add new function iosapic_unregister_intr() into
arch/ia64/kernel/iosapic.c. This function frees an interrupt
vector and related data structures.

- Add new function free_irq_vector() into
arch/ia64/kernel/irq_ia64.c. This frees an unused vector.

- Change assign_irq_vector() to be able to support
free_irq_vector().

o pcibios_disable_device()

This calls acpi_pci_irq_disable() to deallocate IRQ resources.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@xxxxxxxxxxxxxx>


---

linux-2.6.10-rc3-kanesige/arch/ia64/Kconfig | 5
linux-2.6.10-rc3-kanesige/arch/ia64/kernel/acpi.c | 9 +
linux-2.6.10-rc3-kanesige/arch/ia64/kernel/iosapic.c | 96 ++++++++++++++++--
linux-2.6.10-rc3-kanesige/arch/ia64/kernel/irq.c | 3
linux-2.6.10-rc3-kanesige/arch/ia64/kernel/irq_ia64.c | 27 ++++-
linux-2.6.10-rc3-kanesige/arch/ia64/pci/pci.c | 8 +
linux-2.6.10-rc3-kanesige/include/asm-ia64/hw_irq.h | 2
linux-2.6.10-rc3-kanesige/include/asm-ia64/iosapic.h | 4
8 files changed, 141 insertions(+), 13 deletions(-)

diff -puN arch/ia64/Kconfig~IRQ_deallocation_ia64 arch/ia64/Kconfig
--- linux-2.6.10-rc3/arch/ia64/Kconfig~IRQ_deallocation_ia64 2004-12-06 19:58:12.000000000 +0900
+++ linux-2.6.10-rc3-kanesige/arch/ia64/Kconfig 2004-12-08 14:01:29.040576358 +0900
@@ -302,6 +302,11 @@ config IA64_PALINFO
To use this option, you have to ensure that the "/proc file system
support" (CONFIG_PROC_FS) is enabled, too.

+config ACPI_DEALLOCATE_IRQ
+ bool
+ depends on IOSAPIC && EXPERIMENTAL
+ default y
+
source "drivers/firmware/Kconfig"

source "fs/Kconfig.binfmt"
diff -puN arch/ia64/kernel/acpi.c~IRQ_deallocation_ia64 arch/ia64/kernel/acpi.c
--- linux-2.6.10-rc3/arch/ia64/kernel/acpi.c~IRQ_deallocation_ia64 2004-12-06 19:58:12.000000000 +0900
+++ linux-2.6.10-rc3-kanesige/arch/ia64/kernel/acpi.c 2004-12-06 19:58:12.000000000 +0900
@@ -517,6 +517,15 @@ acpi_register_gsi (u32 gsi, int edge_lev
}
EXPORT_SYMBOL(acpi_register_gsi);

+#ifdef CONFIG_ACPI_DEALLOCATE_IRQ
+void
+acpi_unregister_gsi (u32 gsi)
+{
+ iosapic_unregister_intr(gsi);
+}
+EXPORT_SYMBOL(acpi_unregister_gsi);
+#endif /* CONFIG_ACPI_DEALLOCATE_IRQ */
+
static int __init
acpi_parse_fadt (unsigned long phys_addr, unsigned long size)
{
diff -puN arch/ia64/kernel/iosapic.c~IRQ_deallocation_ia64 arch/ia64/kernel/iosapic.c
--- linux-2.6.10-rc3/arch/ia64/kernel/iosapic.c~IRQ_deallocation_ia64 2004-12-06 19:58:12.000000000 +0900
+++ linux-2.6.10-rc3-kanesige/arch/ia64/kernel/iosapic.c 2004-12-06 19:58:12.000000000 +0900
@@ -111,6 +111,7 @@ static struct iosapic_intr_info {
unsigned char dmode : 3; /* delivery mode (see iosapic.h) */
unsigned char polarity: 1; /* interrupt polarity (see iosapic.h) */
unsigned char trigger : 1; /* trigger mode (see iosapic.h) */
+ int refcnt; /* reference counter */
} iosapic_intr_info[IA64_NUM_VECTORS];

static struct iosapic {
@@ -177,7 +178,7 @@ gsi_to_irq (unsigned int gsi)
static void
set_rte (unsigned int vector, unsigned int dest, int mask)
{
- unsigned long pol, trigger, dmode, flags;
+ unsigned long pol, trigger, dmode;
u32 low32, high32;
char __iomem *addr;
int rte_index;
@@ -218,13 +219,9 @@ set_rte (unsigned int vector, unsigned i
/* dest contains both id and eid */
high32 = (dest << IOSAPIC_DEST_SHIFT);

- spin_lock_irqsave(&iosapic_lock, flags);
- {
- iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
- iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
- iosapic_intr_info[vector].low32 = low32;
- }
- spin_unlock_irqrestore(&iosapic_lock, flags);
+ iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
+ iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
+ iosapic_intr_info[vector].low32 = low32;
}

static void
@@ -475,6 +472,7 @@ register_intr (unsigned int gsi, int vec
iosapic_intr_info[vector].addr = iosapic_address;
iosapic_intr_info[vector].gsi_base = gsi_base;
iosapic_intr_info[vector].trigger = trigger;
+ iosapic_intr_info[vector].refcnt++;

if (trigger == IOSAPIC_EDGE)
irq_type = &irq_type_iosapic_edge;
@@ -581,6 +579,7 @@ iosapic_register_intr (unsigned int gsi,
{
vector = gsi_to_vector(gsi);
if (vector > 0) {
+ iosapic_intr_info[vector].refcnt++;
spin_unlock_irqrestore(&iosapic_lock, flags);
return vector;
}
@@ -589,6 +588,8 @@ iosapic_register_intr (unsigned int gsi,
dest = get_target_cpu(gsi, vector);
register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY,
polarity, trigger);
+
+ set_rte(vector, dest, 1);
}
spin_unlock_irqrestore(&iosapic_lock, flags);

@@ -597,10 +598,87 @@ iosapic_register_intr (unsigned int gsi,
(polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
cpu_logical_id(dest), dest, vector);

- set_rte(vector, dest, 1);
return vector;
}

+#ifdef CONFIG_ACPI_DEALLOCATE_IRQ
+void
+iosapic_unregister_intr (unsigned int gsi)
+{
+ unsigned long flags;
+ int irq, vector;
+ irq_desc_t *idesc;
+ int rte_index;
+ unsigned long trigger, polarity;
+
+ /*
+ * If the irq associated with the gsi is not found,
+ * iosapic_unregister_intr() is unbalanced. We need to check
+ * this again after getting locks.
+ */
+ irq = gsi_to_irq(gsi);
+ if (irq < 0) {
+ printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi);
+ WARN_ON(1);
+ return;
+ }
+ vector = irq_to_vector(irq);
+
+ idesc = irq_descp(irq);
+ spin_lock_irqsave(&idesc->lock, flags);
+ spin_lock(&iosapic_lock);
+ {
+ rte_index = iosapic_intr_info[vector].rte_index;
+ if (rte_index < 0) {
+ spin_unlock(&iosapic_lock);
+ spin_unlock_irqrestore(&idesc->lock, flags);
+ printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi);
+ WARN_ON(1);
+ return;
+ }
+
+ if (--iosapic_intr_info[vector].refcnt > 0) {
+ spin_unlock(&iosapic_lock);
+ spin_unlock_irqrestore(&idesc->lock, flags);
+ return;
+ }
+
+ /*
+ * If interrupt handlers still exist on the irq
+ * associated with the gsi, don't unregister the
+ * interrupt.
+ */
+ if (idesc->action) {
+ iosapic_intr_info[vector].refcnt++;
+ spin_unlock(&iosapic_lock);
+ spin_unlock_irqrestore(&idesc->lock, flags);
+ printk(KERN_WARNING "Cannot unregister GSI. IRQ %u is still in use.\n", irq);
+ return;
+ }
+
+ /* Clear the interrupt controller descriptor. */
+ idesc->handler = &no_irq_type;
+
+ trigger = iosapic_intr_info[vector].trigger;
+ polarity = iosapic_intr_info[vector].polarity;
+
+ /* Clear the interrupt information. */
+ memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info));
+ iosapic_intr_info[vector].rte_index = -1; /* mark as unused */
+ }
+ spin_unlock(&iosapic_lock);
+ spin_unlock_irqrestore(&idesc->lock, flags);
+
+ /* Free the interrupt vector */
+ free_irq_vector(vector);
+
+ printk(KERN_INFO "GSI %u (%s, %s) -> vector %d unregisterd.\n",
+ gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
+ (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
+ vector);
+}
+#endif /* CONFIG_ACPI_DEALLOCATE_IRQ */
+
/*
* ACPI calls this when it finds an entry for a platform interrupt.
* Note that the irq_base and IOSAPIC address must be set in iosapic_init().
diff -puN arch/ia64/kernel/irq.c~IRQ_deallocation_ia64 arch/ia64/kernel/irq.c
--- linux-2.6.10-rc3/arch/ia64/kernel/irq.c~IRQ_deallocation_ia64 2004-12-06 19:58:12.000000000 +0900
+++ linux-2.6.10-rc3-kanesige/arch/ia64/kernel/irq.c 2004-12-06 19:58:12.000000000 +0900
@@ -1030,6 +1030,9 @@ void move_irq(int irq)
irq_desc_t *desc = irq_descp(irq);
int redir = test_bit(irq, pending_irq_redir);

+ if (unlikely(!desc->handler->set_affinity))
+ return;
+
if (!cpus_empty(pending_irq_cpumask[irq])) {
cpus_and(tmp, pending_irq_cpumask[irq], cpu_online_map);
if (unlikely(!cpus_empty(tmp))) {
diff -puN arch/ia64/kernel/irq_ia64.c~IRQ_deallocation_ia64 arch/ia64/kernel/irq_ia64.c
--- linux-2.6.10-rc3/arch/ia64/kernel/irq_ia64.c~IRQ_deallocation_ia64 2004-12-06 19:58:12.000000000 +0900
+++ linux-2.6.10-rc3-kanesige/arch/ia64/kernel/irq_ia64.c 2004-12-06 19:58:12.000000000 +0900
@@ -75,15 +75,34 @@ irq_exit (void)
preempt_enable_no_resched();
}

+static unsigned long ia64_vector_mask[BITS_TO_LONGS(IA64_NUM_DEVICE_VECTORS)];
+
int
assign_irq_vector (int irq)
{
- static int next_vector = IA64_FIRST_DEVICE_VECTOR;
-
- if (next_vector > IA64_LAST_DEVICE_VECTOR)
+ int pos, vector;
+ again:
+ pos = find_first_zero_bit(ia64_vector_mask, IA64_NUM_DEVICE_VECTORS);
+ vector = IA64_FIRST_DEVICE_VECTOR + pos;
+ if (vector > IA64_LAST_DEVICE_VECTOR)
/* XXX could look for sharable vectors instead of panic'ing... */
panic("assign_irq_vector: out of interrupt vectors!");
- return next_vector++;
+ if (test_and_set_bit(pos, ia64_vector_mask))
+ goto again;
+ return vector;
+}
+
+void
+free_irq_vector (int vector)
+{
+ int pos;
+
+ if (vector < IA64_FIRST_DEVICE_VECTOR || vector > IA64_LAST_DEVICE_VECTOR)
+ return;
+
+ pos = vector - IA64_FIRST_DEVICE_VECTOR;
+ if (!test_and_clear_bit(pos, ia64_vector_mask))
+ printk(KERN_WARNING "%s: double free!\n", __FUNCTION__);
}

extern unsigned int do_IRQ(unsigned long irq, struct pt_regs *regs);
diff -puN arch/ia64/pci/pci.c~IRQ_deallocation_ia64 arch/ia64/pci/pci.c
--- linux-2.6.10-rc3/arch/ia64/pci/pci.c~IRQ_deallocation_ia64 2004-12-06 19:58:12.000000000 +0900
+++ linux-2.6.10-rc3-kanesige/arch/ia64/pci/pci.c 2004-12-06 19:58:12.000000000 +0900
@@ -480,6 +480,14 @@ pcibios_enable_device (struct pci_dev *d
return acpi_pci_irq_enable(dev);
}

+#ifdef CONFIG_ACPI_DEALLOCATE_IRQ
+void
+pcibios_disable_device (struct pci_dev *dev)
+{
+ acpi_pci_irq_disable(dev);
+}
+#endif /* CONFIG_ACPI_DEALLOCATE_IRQ */
+
void
pcibios_align_resource (void *data, struct resource *res,
unsigned long size, unsigned long align)
diff -puN include/asm-ia64/hw_irq.h~IRQ_deallocation_ia64 include/asm-ia64/hw_irq.h
--- linux-2.6.10-rc3/include/asm-ia64/hw_irq.h~IRQ_deallocation_ia64 2004-12-06 19:58:12.000000000 +0900
+++ linux-2.6.10-rc3-kanesige/include/asm-ia64/hw_irq.h 2004-12-06 19:58:12.000000000 +0900
@@ -50,6 +50,7 @@ typedef u8 ia64_vector;
*/
#define IA64_FIRST_DEVICE_VECTOR 0x30
#define IA64_LAST_DEVICE_VECTOR 0xe7
+#define IA64_NUM_DEVICE_VECTORS (IA64_LAST_DEVICE_VECTOR - IA64_FIRST_DEVICE_VECTOR + 1)

#define IA64_MCA_RENDEZ_VECTOR 0xe8 /* MCA rendez interrupt */
#define IA64_PERFMON_VECTOR 0xee /* performanc monitor interrupt vector */
@@ -81,6 +82,7 @@ extern __u8 isa_irq_to_vector_map[16];
extern struct hw_interrupt_type irq_type_ia64_lsapic; /* CPU-internal interrupt controller */

extern int assign_irq_vector (int irq); /* allocate a free vector */
+extern void free_irq_vector (int vector);
extern void ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect);
extern void register_percpu_irq (ia64_vector vec, struct irqaction *action);

diff -puN include/asm-ia64/iosapic.h~IRQ_deallocation_ia64 include/asm-ia64/iosapic.h
--- linux-2.6.10-rc3/include/asm-ia64/iosapic.h~IRQ_deallocation_ia64 2004-12-06 19:58:12.000000000 +0900
+++ linux-2.6.10-rc3-kanesige/include/asm-ia64/iosapic.h 2004-12-06 19:58:12.000000000 +0900
@@ -78,6 +78,9 @@ extern int gsi_to_irq (unsigned int gsi)
extern void iosapic_enable_intr (unsigned int vector);
extern int iosapic_register_intr (unsigned int gsi, unsigned long polarity,
unsigned long trigger);
+#ifdef CONFIG_ACPI_DEALLOCATE_IRQ
+extern void iosapic_unregister_intr (unsigned int irq);
+#endif
extern void __init iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi,
unsigned long polarity,
unsigned long trigger);
@@ -97,6 +100,7 @@ extern void __init map_iosapic_to_node (
#define iosapic_system_init(pcat_compat) do { } while (0)
#define iosapic_init(address,gsi_base) do { } while (0)
#define iosapic_register_intr(gsi,polarity,trigger) (gsi)
+#define iosapic_unregister_intr(irq) do { } while (0)
#define iosapic_override_isa_irq(isa_irq,gsi,polarity,trigger) do { } while (0)
#define iosapic_register_platform_intr(type,gsi,pmi,eid,id, \
polarity,trigger) (gsi)

_