Reference resources: http://blog.csdn.net/adaptiver/article/details/6834337
1 Because when an interrupt occurs, the system automatically shuts down and interrupts when it enters by the interrupt door (for x86 platform, if position of eflags register is 0), the interrupt is restored in irq_exit
2 It appears that handle_edge_irq also has operations for interrupt closing and opening
The first is that because linux does not support interrupt priority, any interrupt can preempt other interrupts, but no preemption will occur for the same type of interrupt (that is, an interrupt defined with the same interrupt line), and they will be called to execute this type of interrupt in turn.Second, the so-called "Interrupt can preempt the kernel as long as it occurs" has certain restrictions, because when an interrupt occurs, the system automatically closes and interrupts when the interrupt door enters (for x86 platform, if position of eflags register is 0), only after the interrupt is opened during the execution of the interrupt function (handle_IRQ_event).
For linux systems, interrupts occur as follows:
Jump to the specified position of the interrupt vector table according to the interrupt number --> Protect the field, and convert mode to SVC32 --> Jump further to the processing function as directed by the vector table --> Jump further u irq_svc, and save those registers for interrupt processing when the interrupt returns --> irq_handler Interrupt Processing -->
asm_do_IRQ ->generic_handle_irq(irq) -> desc->handle_irq(irq, desc), handle_edge_irq, etc. ->shield the same interrupt, clear the interrupt so that the interrupt can be registered ->handle_IRQ_event ->action->handler (irq, action->dev_id) ->interrupt returns irq_exit ->invoke_softirq()->on-site recovery if there is still a soft interrupt not executed
The interrupt generation combination function is used to illustrate the interrupt process:
1. Important structures
/include/linux/irq.h
The irq_desc kernel records an array of irq_desc. Each item of the array corresponds to an interrupt or a set of interrupts using the same interrupt number. An irq_desc sentence records almost everything interrupt related. This structure is the core of interrupts.These include two important structures, irq_chip and irqaction.
This structure acts much like gpio_desc, it is an array, and the corresponding chip and other information is found through the index.
struct irq_desc {
unsigned int irq; interrupt number
struct timer_rand_state *timer_rand_state;
unsigned int *kstat_irqs;
#ifdef CONFIG_INTR_REMAP
struct irq_2_iommu *irq_2_iommu;
#endif
irq_flow_handler_t handle_irq; //Interrupt Handling Entry Function
struct irq_chip *chip; //irq interrupt subsystem interface function
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* IRQ action list */ //Hang interrupt functions on this list when registering interrupts
unsigned int status; /* IRQ status */
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
raw_spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_var_t affinity;
const struct cpumask *affinity_hint;
unsigned int node;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;
//Next, look at the irq interrupt subsystem interface function
struct irq_chip {
const char *name;
unsigned int (*startup)(unsigned int irq); Start Interrupt
void (*shutdown)(unsigned int irq); Close Interrupt
void (*enable)(unsigned int irq); Enable interruption
void (*disable)(unsigned int irq); Prohibit interruption
void (*ack)(unsigned int irq); The interrupt response function is to clear the interrupt identification function
void (*mask)(unsigned int irq); Interrupt shielding function
void (*mask_ack)(unsigned int irq); Shielding interrupt response function, commonly used for level triggering, needs shielding before answering
void (*unmask)(unsigned int irq); Open Interrupt
void (*eoi)(unsigned int irq);
void (*end)(unsigned int irq);
int (*set_affinity)(unsigned int irq,
const struct cpumask *dest);
int (*retrigger)(unsigned int irq);
int (*set_type)(unsigned int irq, unsigned int flow_type); Set interrupt type, including settings GPIO Mouth is interrupt input
int (*set_wake)(unsigned int irq, unsigned int on);
void (*bus_lock)(unsigned int irq); Uplock function
void (*bus_sync_unlock)(unsigned int irq); Unlock
/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
void (*release)(unsigned int irq, void *dev_id);
#endif
/*
* For compatibility, ->typename is copied into ->name.
* Will disappear.
*/
const char *typename;
};
We can see that what we're implementing here is a framework that requires us to further populate the functions inside.We are analyzing another structure, irqaction
include/linux/interrupt.h
struct irqaction {
irq_handler_t handler; User-registered interrupt handler
unsigned long flags; Interrupt Identification
const char *name; User registered interrupt name, cat/proc/interrupts As you can see
void *dev_id; It can be a user-passed parameter or used to distinguish shared interrupts
struct irqaction *next; irqaction Structural chains, a shared interrupt can have multiple interrupt handlers
int irq; interrupt number
struct proc_dir_entry *dir;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};
//Step by step analysis of request_irq
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
*/
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
desc = irq_to_desc(irq); according to irq Found Initialized irq_desc Structures, followed by an initializer for this structure
if (!desc)
return -EINVAL;
if (desc->status & IRQ_NOREQUEST) If it can't be requested
return -EINVAL;
if (!handler) { If no interrupt handling function is defined
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler; Give a default function to handler
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); Apply for one action structural morphology
if (!action)
return -ENOMEM;
action->handler = handler; The following assignments are made to each member of the structure
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
chip_bus_lock(irq, desc);
retval = __setup_irq(irq, desc, action); See explanation below
chip_bus_sync_unlock(irq, desc);
if (retval)
kfree(action);
return retval;
}
EXPORT_SYMBOL(request_threaded_irq);
static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
struct irqaction *old, **old_ptr;
const char *old_name = NULL;
unsigned long flags;
int nested, shared = 0;
int ret;
if (!desc)
return -EINVAL;
if (desc->chip == &no_irq_chip) Determine whether to use no_irq_chip Application program interface
return -ENOSYS;
/*
* Some drivers like serial.c use request_irq() heavily,
* so we have to be careful not to interfere with a
* running system.
*/
if (new->flags & IRQF_SAMPLE_RANDOM) { Sometimes used for serial driver
/*
* This function might sleep, we want to call it first,
* outside of the atomic block.
* Yes, this might clear the entropy pool if the wrong
* driver is attempted to be loaded, without actually
* installing a new handler, but is this really a problem,
* only the sysadmin is able to do this.
*/
rand_initialize_irq(irq);
}
/* Oneshot interrupts are not allowed with shared */
if ((new->flags & IRQF_ONESHOT) && (new->flags & IRQF_SHARED)) Oneshot Sharing interrupts are not allowed
return -EINVAL;
/*
* Check whether the interrupt nests into another interrupt
* thread.
*/
nested = desc->status & IRQ_NESTED_THREAD; Whether nested flags are defined, obviously not implemented
if (nested) {
if (!new->thread_fn)
return -EINVAL;
/*
* Replace the primary handler which was provided from
* the driver for non nested interrupt handling by the
* dummy function which warns when called.
*/
new->handler = irq_nested_primary_handler;
}
if (new->thread_fn && !nested) { Is there a thread function definition //Do not execute
struct task_struct *t;
t = kthread_create(irq_thread, new, "irq/%d-%s", irq, Open Thread
new->name);
if (IS_ERR(t))
return PTR_ERR(t);
/*
* We keep the reference to the task struct even if
* the thread dies to avoid that the interrupt code
* references an already freed task_struct.
*/
get_task_struct(t);
new->thread = t;
}
/*
* The following block of code has to be executed atomically
*/
raw_spin_lock_irqsave(&desc->lock, flags);
old_ptr = &desc->action; From the old desc Remove the original from the structure action
old = *old_ptr;
if (old) { Sharing interrupts if any
/*
* Can't share interrupts unless both agree to and are
* 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.
*/
if (!((old->flags & new->flags) & IRQF_SHARED) || Determine if they are all shared or triggered the same way
((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
old_name = old->name;
goto mismatch;
}
#if defined(CONFIG_IRQ_PER_CPU)
/* All handlers must agree on per-cpuness */
if ((old->flags & IRQF_PERCPU) !=
(new->flags & IRQF_PERCPU))
goto mismatch;
#endif
/* add new interrupt at end of irq queue */
do {
old_ptr = &old->next; On already mounted action The last empty position at the end of the list for new action To mount
old = *old_ptr;
} while (old);
shared = 1;
}
if (!shared) {
irq_chip_set_defaults(desc->chip); If individual is not set chip Interface Settings Default Function
init_waitqueue_head(&desc->wait_for_threads);
/* Setup the type (level, edge polarity) if configured: */
if (new->flags & IRQF_TRIGGER_MASK) {
ret = __irq_set_trigger(desc, irq, Set trigger mode
new->flags & IRQF_TRIGGER_MASK);
if (ret)
goto out_thread;
} else
compat_irq_chip_set_default_handler(desc);
#if defined(CONFIG_IRQ_PER_CPU)
if (new->flags & IRQF_PERCPU)
desc->status |= IRQ_PER_CPU;
#endif
desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_ONESHOT |
IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
if (new->flags & IRQF_ONESHOT)
desc->status |= IRQ_ONESHOT;
if (!(desc->status & IRQ_NOAUTOEN)) {
desc->depth = 0;
desc->status &= ~IRQ_DISABLED; Set state
desc->chip->startup(irq); Open Interrupt
} else
/* Undo nested disables: */
desc->depth = 1;
/* Exclude IRQ from balancing if requested */
if (new->flags & IRQF_NOBALANCING)
desc->status |= IRQ_NO_BALANCING;
/* Set default affinity mask once everything is setup */
setup_affinity(irq, desc);
} else if ((new->flags & IRQF_TRIGGER_MASK)
&& (new->flags & IRQF_TRIGGER_MASK)
!= (desc->status & IRQ_TYPE_SENSE_MASK)) {
/* hope the handler works with the actual trigger mode... */
pr_warning("IRQ %d uses trigger mode %d; requested %d\n",
irq, (int)(desc->status & IRQ_TYPE_SENSE_MASK),
(int)(new->flags & IRQF_TRIGGER_MASK));
}
new->irq = irq;
*old_ptr = new; stay irq Owning desc Structures will be new action Hang to Chain List
/* Reset broken irq detection when installing new handler */
desc->irq_count = 0;
desc->irqs_unhandled = 0;
*/
if (shared && (desc->status & IRQ_SPURIOUS_DISABLED)) {
desc->status &= ~IRQ_SPURIOUS_DISABLED;
__enable_irq(desc, irq, false);
}
raw_spin_unlock_irqrestore(&desc->lock, flags);
/*
* Strictly no need to wake it up, but hung_task complains
* when no hard interrupt wakes the thread up.
*/
if (new->thread)
wake_up_process(new->thread);
register_irq_proc(irq, desc); stay proc Create under irq File Node
new->dir = NULL;
register_handler_proc(irq, new); stay proc Create under handler File Node
return 0;
mismatch:
#ifdef CONFIG_DEBUG_SHIRQ
if (!(new->flags & IRQF_PROBE_SHARED)) {
printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
if (old_name)
printk(KERN_ERR "current handler: %s\n", old_name);
dump_stack();
}
#endif
ret = -EBUSY;
out_thread:
raw_spin_unlock_irqrestore(&desc->lock, flags);
if (new->thread) {
struct task_struct *t = new->thread;
new->thread = NULL;
if (likely(!test_bit(IRQTF_DIED, &new->thread_flags)))
kthread_stop(t);
put_task_struct(t);
}
return ret;
}
All of the above are done: create an action structure, take out the original daction structure from the desc structure, and mount the new action structure into the original acton chain table.
_u setup_irq has a lot of content, mainly completes several aspects of work
1. To add the IRQ action structure to the action list of irq_desc, it is necessary to determine whether it is a shared interrupt. Only a shared interrupt can add more than one interrupt handler. If it is a shared interrupt, it is necessary to check whether the interrupt handler is the same as other functions in the chain table, and only a consistent one can be added to the chain table.
2. Set some function pointers in the irq_chip structure to point to default functions
3. Set the trigger mode of interrupt and start interrupt
<1>Interrupt the initialization process
The following is a copy of someone else's blog at the original address http://blog.chinaunix.net/space.php?uid=15193587&do=blog&cuid=2194431
Next we analyze the process of kernel interrupt initialization and how to call the irq initialization function on a new platform.
Here we take the s3c2410 platform as an example. Its interrupt initialization function is defined as:
/* arch/arm/mach-s3c2410/irq.c */
void __init s3c24xx_init_irq(void)
{
......
}
The s3c24xx_init_irq is assigned to the.Init_irq member of the mach_desc structure through the MACHINE_START macro in arch/arm/mach-s3c2410/mach-smdk2410.c.
MACHINE_START(SMDK2410,"SMDK2410")/* @TODO: request a new identifier and switch
* to SMDK2410 */
/* Maintainer: Jonas Dietsche */
.phys_io = S3C2410_PA_UART,
.io_pg_offst =(((u32)S3C24XX_VA_UART)>> 18)& 0xfffc,
.boot_params = S3C2410_SDRAM_PA+ 0x100,
.map_io = smdk2410_map_io,
.init_irq = s3c24xx_init_irq,
.init_machine = smdk_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
//Note: The purpose of the MACHINE_START macro is to initialize the mach_desc structure.Some key architecture-related functions are defined in mach_desc.This structure is critical when Porting kernel s move to a new platform.
init_irq This member is assigned to when the system is initialized init_arch_irq Global variables, as follows:
/* arch/arm/kernel/setup.c */
void __init setup_arch(char**cmdline_p)
{
......
cpu_init();
/*
* Set up various architecture-specific pointers
*/
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
......
}
Note: You can see that not only the init_arch_irq global variable is initialized here, but also the system_timer,init_machine, and other global variables.This is a mechanism for kernel s to support multiple platforms.Of course, I don't describe system_timer and init_machine here much, so you can see them for yourself if you're interested.The mechanism is similar to init_arch_irq.
The init_arch_irq function pointer is defined within Architecture Independent arch/arm/kernel/irq.c
/* arch/arm/kernel/irq.c */
void (*init_arch_irq)(void) __initdata = NULL;
It is executed within the init_IRQ function.
/* arch/arm/kernel/irq.c */
void __init init_IRQ(void)
{
int irq;
for (irq = 0; irq < NR_IRQS; irq++)
irq_desc[irq].status|= IRQ_NOREQUEST| IRQ_DELAYED_DISABLE| //desc status proceed flag
IRQ_NOPROBE;
#ifdef CONFIG_SMP
bad_irq_desc.affinity = CPU_MASK_ALL;
bad_irq_desc.cpu = smp_processor_id();
#endif
init_arch_irq();
}
Where was init_IRQ called? We guess it was called at system initialization.
As a result, init_IRQ is called within the start_kernel function in init/main.c:
asmlinkage void __init start_kernel(void)
{
......
trap_init();
rcu_init();
init_IRQ();
pidhash_init();
clockevents_init();
init_timers();
......
}
init_arch_irq function pointer, which means this is a global function pointer and is architecture dependent.Let me continue with the above analysis of the s3c24xx_init_irq() function
void __init s3c24xx_init_irq(void)
{
unsigned long pend;
unsigned long last;
int irqno;
int i;
#ifdef CONFIG_FIQ
init_FIQ();
#endif
irqdbf("s3c2410_init_irq: clearing interrupt status flags\n");
/* first, clear all interrupts pending... */
last = 0;
for (i = 0; i < 4; i++) {
pend = __raw_readl(S3C24XX_EINTPEND); Clear interrupt operation
if (pend == 0 || pend == last)
break;
__raw_writel(pend, S3C24XX_EINTPEND);
printk("irq: clearing pending ext status %08x\n", (int)pend);
last = pend;
}
last = 0;
for (i = 0; i < 4; i++) {
pend = __raw_readl(S3C2410_INTPND);
if (pend == 0 || pend == last)
break;
__raw_writel(pend, S3C2410_SRCPND);
__raw_writel(pend, S3C2410_INTPND);
printk("irq: clearing pending status %08x\n", (int)pend);
last = pend;
}
last = 0;
for (i = 0; i < 4; i++) {
pend = __raw_readl(S3C2410_SUBSRCPND);
if (pend == 0 || pend == last)
break;
printk("irq: clearing subpending status %08x\n", (int)pend);
__raw_writel(pend, S3C2410_SUBSRCPND);
last = pend;
}
/* register the main interrupts */
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
irqdbf("registering irq %d (ext int)\n", irqno);
set_irq_chip(irqno, &s3c_irq_eint0t4); take chip Structural Initialization Initialization desc in
set_irq_handler(irqno, handle_edge_irq); take handler Function Initialization Initiation desc in
set_irq_flags(irqno, IRQF_VALID); Clear Status Flag Bits IRQ_NOREQUEST
}
. . . . .
}
1 The for loop above clears the interrupt identifier and sets a last variable. I don't know if this is to prevent one erase interrupt from failing.
2 Fill in the irq_chip structure first, then set the handler entry for the interrupt. When this response is interrupted, the user-registered interrupt handler is called through the function entry, and the interrupt identifier is set to be usable.
Above references: http://blog.csdn.net/yimu13/article/details/6803957?locationNum=5
Next, let's look at the interrupt handling process, which starts a series of processes when an interrupt occurs.
Reference resources: https://wenku.baidu.com/view/6c6e30634b35eefdc9d33300.html