Re: [RFC] killing the NR_IRQS arrays.

From: Russell King
Date: Fri Feb 16 2007 - 14:53:43 EST


On Fri, Feb 16, 2007 at 08:45:58PM +0100, Arnd Bergmann wrote:
> On Friday 16 February 2007 13:10, Eric W. Biederman wrote:
> > To do this I believe will require a s/unsigned int irq/struct irq_desc *irq/
> > throughout the entire kernel.  Getting the arch specific code and the
> > generic kernel infrastructure fixed and ready for that change looks
> > like a pain but pretty doable.
>
> We did something like this a few years back on the s390 architecture, which
> happens to be lucky enough not to share any interrupt based drivers with
> any of the other architectures.

What you're proposing is looking similar to a proposal I put forward some
4 years ago, but was rejected. Maybe times have changed and there's a
need for it now.

Message attached.

--
Russell King
Linux kernel 2.6 ARM Linux - http://www.arm.linux.org.uk/
maintainer of:
--- Begin Message --- Hi,

I've recently received an updated development system from ARM Ltd,
which has caused me to become concerned about whether the existing
IRQ infrastructure for the ARM architecture is really up to the job
of handling the developments which will occur over the 2.6 lifetime.
Essentially we're going to be seeing the emergence of vectored
interrupt controllers, and knowing hardware designers, they'll
continue the practice of chaining interrupt controllers (which is
pretty common on ARM today.) I have some hardware here today which
has a vectored interrupt controller chained after two non-vectored
controllers. This vectored interrupt controller is on an add-on
card, and so has no fixed address space and no fixed IRQ numbering.

Rather than having the job of rewriting this code during 2.6, I'd much
prefer to get something sorted, even if it is ARM only before 2.6.

I believe that there are some common problems with the existing API
which have been hinted at over the last few days, such as large
NR_IRQS. As such, I think it would be a good idea to try to thrash
this issue out and get something which everyone is happy with.

Additionally, I've added Alan's "reserve then hook" idea to the API;
I seem to remember there is a case in IDE which needs something like
this.

Please note that what I am proposing is not to strip out the existing
API between now and 2.7; what I am proposing is a structure for 2.7
which can optionally be implemented by architectures and used in
architecture specific drivers now if they feel they would benefit
from it.

Comments? (other than "wtf are you thinking about this so close to 2.6,
are you mad" 8))


Linux Interrupt API
===================

Russell King <rmk@xxxxxxxxxxxxxxxx>

The Linux Interrupt API provides a flexible mechanism to handle and
control interrupts within the kernel. The design requirements for
this API are:

- must have as little overhead as possible for commodity hardware
- must be easy and obvious to use
- must allow complex multi-level interrupt implementations to exist
transparently to device drivers
- must be compatible with the existing API

Essentially, this means that implementation of the existing API must
be simple.

------------------------------------------------------------------------------

The API.
========

struct irq {
/* architecture defined information */
/* must not be dereferenced by drivers */
/* eg, x86's irq_desc_t or sparc64's struct ino_bucket */
};

#define NO_IRQ <architecture-defined-int-constant>

/**
* irq_get - increment reference count on the IRQ descriptor
* @irq: interrupt descriptor
*
* IRQ descriptor reference counting is mandatory for
* implementations which provide dynamically allocated IRQ
* descriptors. statically allocated IRQ descriptor
* implementations may define these to be no-ops.
*/
struct irq *irq_get(struct irq *irq);

/**
* irq_put - decrement reference count on IRQ descriptor
* @irq: interrupt descriptor
*
* Decrement the reference counter in an IRQ descriptor.
* If the reference counter drops to zero, the IRQ descriptor
* will be freed.
*
* IRQ descriptor reference counting is mandatory for
* implementations which provide dynamically allocated IRQ
* descriptors. statically allocated IRQ descriptor
* implementations may define these to be no-ops.
*/
void irq_put(struct irq *irq);

/**
* irq_disable_nosync - disable an irq without waiting
* @irq: Interrupt descriptor to disable
*
* Disable the selected interrupt line. Disables and Enables are
* nested.
* Unlike irq_disable(), this function does not ensure existing
* instances of the IRQ handler have completed before returning.
*
* This function may be called from IRQ context.
*/
void irq_disable_nosync(struct irq *irq);

/**
* irq_disable - disable an irq and wait for completion
* @irq: Interrupt descriptor to disable
*
* Disable the selected interrupt line. Enables and Disables are
* nested.
* This function waits for any pending IRQ handlers for this interrupt
* to complete before returning. If you use this function while
* holding a resource the IRQ handler may need you will deadlock.
*
* This function may be called - with care - from IRQ context.
*/
void irq_disable(struct irq *irq);

/**
* irq_enable - enable the specified IRQ source.
* @irq: Interrupt descriptor to enable
*
* Undoes the effect of one call to irq_disable(). If this
* matches the last disable, processing of interrupts on this
* IRQ line is re-enabled.
*
* This function may be called from IRQ context.
*/
void irq_enable(struct irq *irq);

/**
* irq_request - request the use of an IRQ.
* @irq: irq descriptor
* @irqflags: interrupt type flags (SA_SHIRQ, SA_INTERRUPT)
* @devname: ascii name for the claiming device
* @dev_id: A cookie passed back to the handler function
*
* Request use of an interrupt line. We don't actually hook
* the interrupt immediately; any resources necessary are
* allocated. As such, this function may sleep.
*
* This function takes a reference on the IRQ descriptor.
*/
int irq_request(struct irq *irq, unsigned long irqflags, char *devname, void *dev_id);

/**
* irq_register - register an interrupt function to be called
* @irq: interrupt descriptor
* @handler: interrupt handler function
* @dev_id: A cookie passed back to the handler function
*
* Hook a previously registered interrupt line. If the
* interrupt has not been hooked by this instance, we fail.
*/
int irq_register(struct irq *irq,
irqreturn_t (*handler)(void *dev_id, struct pt_regs *),
void *dev_id);

/**
* irq_release - free an interrupt line
* @irq: interrupt descriptor
* @dev_id: device identity to free.
*
* Remove an interrupt handler. The handler is removed and if the
* interrupt line is no longer in use by any driver it is disabled.
* On a shared IRQ the caller must ensure the interrupt is disabled
* on the card it drives before calling this function. The function
* does not return until any executing interrupts for this IRQ
* have completed.
*
* We drop a reference on the IRQ descriptor, and therefore may be
* freed by irq_release.
*
* This function must not be called from interrupt context.
*/
void irq_release(struct irq *irq, void *dev_id);

/**
* irq_name - fill in and return the name of an IRQ
* @buf: buffer to fill with name
* @size: size of buffer
* @irq: irq descriptor
*
* Create an ASCII name of an IRQ and place it in buf.
* Returns the address of the buffer.
*/
const char *irq_name(char *buf, size_t size, struct irq *irq);

/**
* irqisa_to_desc - convert an ISA interrupt number to irq descriptor
* @irqnr: ISA interrupt number
*
* Return the IRQ descriptor for a given "ISA" interrupt number.
* Returns NULL if the "ISA" interrupt number is invalid.
*
* The returned IRQ has been "gotten" so irq_put will be required
* to balance once the IRQ is no longer required.
*/
struct irq *irqisa_to_desc(int irqnr);

/**
* irqdesc_to_isa - convert an irq descriptor to an ISA interrupt number
* @irq: Interrupt descriptor
*
* Returns the "ISA" interrupt number given an interrupt
* descriptor. Other interrupts return NO_IRQ.
*/
int irqdesc_to_isa(struct irq *irq);

/**
* irq_probe_on - begin an interrupt autodetect
*
* Commence probing for an interrupt. The interrupts are scanned
* and a cookie is returned.
*
* The IRQ probe semaphore is taken prior to the commencement of
* interrupt probing.
*/
void *irq_probe_on(void);

/**
* irq_probe_mask - scan a bitmap of interrupt lines
* @cookie: cookie returned from irq_probe_on
*
* Scan the ISA bus interrupt lines and return a bitmap of
* active interrupts. The interrupt probe logic state is then
* returned to its previous value.
*
* The IRQ probe semaphore is released and the cookie destroyed;
* irq_probe_off must not be called.
*/
unsigned int irq_probe_mask(void *cookie);

/**
* irq_probe_off - end an interrupt autodetect
* @cookie: cookie returned from irq_probe_on
*
* Scans the unused interrupt lines and returns the irq descriptor
* which appears to have triggered the interrupt. If no interrupt
* was found then NULL is returned.
*
* The IRQ probe semaphore is released and the cookie destroyed;
* irq_probe_off must not be called.
*/
struct irq *irq_probe_off(void *cookie);

------------------------------------------------------------------------------

Example backwards-compatible IRQ code.
======================================

The following example code shows the expected back-compat code required
for the x86 architecture. Since x86 uses a fixed table of interrupts,
this is relatively straight forward and simple.


static inline void disable_irq_nosync(int inr)
{
irq_disable_nosync(irq_desc + inr);
}

static inline void disable_irq(int inr)
{
irq_disable(irq_desc + inr);
}

static inline void enable_irq(int inr)
{
irq_enable(irq_desc + inr);
}

static inline int request_irq(int inr,
irqreturn_t (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags,
const char * devname,
void *dev_id)
{
struct irq *irq = irq_desc + inr;
int ret;

ret = irq_request(irq, irqflags, devname, dev_id);
if (ret == 0) {
/*
* irq_register_legacy is only implemented for
* implementations providing this compatible
* interface.
*/
ret = irq_register_legacy(irq, handler, dev_id);
if (ret)
irq_release(irq, dev_id);
}
return ret;
}

static inline void free_irq(int inr, void *dev_id)
{
irq_release(irq_desc + inr, dev_id);
}

static inline unsigned long probe_irq_on(void)
{
return (unsigned long)irq_probe_on();
}

static inline unsigned int probe_irq_mask(unsigned long mask)
{
return irq_probe_mask((void *)mask);
}

static inline unsigned int probe_irq_off(unsigned long mask)
{
struct irq *irq = irq_probe_off((void *)mask);

return irq ? irq - irq_desc : 0;
}

- o O o -

--
Russell King (rmk@xxxxxxxxxxxxxxxx) The developer of ARM Linux
http://www.arm.linux.org.uk/personal/aboutme.html


--- End Message ---