⭐❤️Wanzi Long Text takes you to the heart of Linux--interrupts❤️⭐

Posted by jgetner on Mon, 06 Sep 2021 06:55:28 +0200

![Please add a picture description] (https://img-blog.csdnimg.cn/65addb7917a34aa5bce4f707a4f71b35.webp?x-oss-process=image/watermark, type_ZHJvaWRzYW5zmFsbGJhY2s, shadow_50, text_Q1NETiBA6a2U5Yqo5bGx6Zy4, size_17, color_FFFFFF, t_70, g_Se, x_16)

Preface

Interrupt refers to the CPU being temporarily stopped by certain internal and external events or program-specific signals during the normal execution of the CPU to run to execute these interrupts to the CPU. LINUX can be divided into internal interrupt and external interrupt.

1. Conditions for Linux interrupts to occur

There are many interruptions.For example:

  • The up and down edges of the keys are triggered.
  • Transmit/Receive Completion Flag Bit of Serial Port
  • Watchdog interruption
  • Interruption of timer
  • Problem with instructions executed by CPU
    ...
    We only care about some interrupts that come with the Linux system. All these interrupts will be aggregated into an interrupt controller, which depends on the priority of these interrupts, just like the NVIC controller in STM32 single-chip computer, which is responsible for the priority of interrupts.

2. How to handle Linux kernel pair interrupts

1. Interrupt Processing

  • Interrupt handling on ARM chips:
  1. Set the interrupt source so that it can produce interrupts
  2. set priority
  3. Enables interruption, which interrupt you use to open which one, and closes when the default interrupt occurs
  4. Generate interrupt: interrupt->interrupt controller->cpu
  5. Each instruction executed by the cpu checks for interrupts
  6. When checking for interrupts, jump to the address where you want to execute the function
  7. These functions help you save your previous field and register, distinguish which interrupt source handles different functions, and restore the field after execution

2. Abnormal Vector Table

An exception vector table is an exception for each instruction

If an interrupt occurs, for example, the cpu will call the following statement

When a reset operation occurs, the cpu executes the following statement. You only need to know that the vector exception table above corresponds to the statement that it is executing.

3. How to handle Linux kernel pair interrupts

Core-Stack for Interrupt Processing in 3.1

ARM chips are compact instruction set computers. They only have read and write instructions to memory, so the operation of these data is carried out in the CPU.
For example, for an equation such as a=a+b, the following four steps are required:

  • We only need to know that if the CPU is executing a program and suddenly an interrupt instruction comes in, it will execute another program, and then it will execute the previous program after that interrupt program is executed.In this process we need to save the field and restore the field, which means to save the value stored in the registers in the CPU that execute the original program. It is stored in memory, which is called the stack.

  • Let's repeat what we just said. Assuming that function A is executing and suddenly the CPU wants to execute function B, function A will be saved in the stack first. After function B is executed, the data of function A will be taken out of the stack and the execution will continue.

  • -------------------------------------- The same is true for switching between processes

For a single cpu, for multiple programs, the cpu is to switch back and forth between multiple programs at a time that is invisible to the human eye and becomes concurrent.For concurrent programs, a process represents a stack during the process of restoring the stack.So generally Linux systems have a default number of processes, but you can also change the default value yourself.Because more processes consume more stacks.On Linux: The units of resource allocation are processes, and the units of scheduling are threads.That is, there may be multiple threads in a process.These threads are independent of each other and run at the same time, that is, each thread has its own stack.

									The relationship between processes and threads

3.2. Interrupting the evolution of Linux

  • Linux system is divided into hard interrupt and soft interrupt. Hard interrupt can not be interrupted, and can not be nested interrupt. The faster the function inside the hard interrupt, the better because the hard interrupt time process will slow down the efficiency of the entire system and greatly reduce the fluency.
    You can compare a hard interrupt to an array in which many interrupts are stored.When a hardware interrupt occurs, you can call the appropriate function to perform what you did
  • Software Interrupt
    You can also create an interrupt yourself, which is called a software interrupt. The function of the soft interrupt is also placed in an array of soft irq, which can be determined by flag.Kernel source code can be found under include/linux/interrupt.h


    How to trigger software interrupt?The core function is raise_softirq, simply understood as setting softirq_Tag bit for veq[nr].
void raise_softirq(unsigned int nr)
{
	unsigned long flags;

	local_irq_save(flags);
	raise_softirq_irqoff(nr);
	local_irq_restore(flags);
}
							    raise_softirq Function prototype of

Set software interrupt handler using open_softirq function, which software arbitrary is the first parameter, and the second is to execute the corresponding function

void open_softirq(int nr, void (*action)(struct softirq_action *))
{
	softirq_vec[nr].action = action;
}

Software interrupts can be used on the tasklet in the lower part of the interrupt. What will be explained later in the lower part of the interrupt?

Using interrupts on Linux only registers the interrupt function handler for an interrupt, we use request_irq function:
The first parameter represents an interrupt number, the second a function that occurs after an interrupt, the third a state with which to trigger (a flag bit), the fourth a name, and the fifth your structure

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);
}

In functions you write, try to be as efficient as possible, such as a button that needs to be shaken, not in a handler, because it can slow down your system's productivity.So the Linux kernel divides the interrupt function into upper and lower interrupts. The upper part deals with emergencies, the upper part cannot be interrupted by other events, and the lower part does not. The implementation of the lower part of interrupts is mainly achieved by tasklet and work queue.

The code is as follows (example):

data = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())

3.3 Use tasklet to process lower half

We have previously described software interrupts (sortirq), which use tasklet to handle the lower half of an interrupt

3.4 Interrupt the relationship between upper and lower half

  1. Top half cannot be interrupted, function execution is efficient
  2. The lower half can handle less urgent things, which are performed when interruptions are opened
  3. Lower half may be interrupted several times
  4. Trigger the lower half of the interrupt by interrupting the upper half
  5. Cannot sleep when execution is interrupted

3.5 Interrupt lower half using work_queue (work queue)

While other interrupts can be handled during the lower half of an execution interrupt, the application-tier program cannot be executed, and I understand that the priority is higher than the application-tier program.If the lower half of the interrupt takes too long, the application layer will not work.So we can't use software interrupts. We can use kernel threads, which are the smallest unit of scheduling, to wake up the threads in the upper half, so that the kernel threads and the application-level threads are in the same competitive position. Then everyone has the chance to be executed by the CPU, which can be interpreted as concurrent, so that the phenomenon of Karton is not easy.Kernel threads are created by the system for us, mostly kworker threads.We can view the process with the following commands

ps -A |grep  kworker

The kworker thread takes the work out of the work queue and executes its functions

DECLARE_WORK  (struct work_struct name,  void (*func)(void *)); 

After declaring a work, the next step is to put it in the work queue

schedule_work(&my_work); //Work added to the queue is automatically deleted from the queue after completion

When we put work in the work queue and wake kworker up, when kworker grabs the execution time, it automatically calls the functions inside the work.
Now let's just schedule_work is a function that can be concatenated by executing it on the top of the interrupt.
Note: As we said earlier, tasklet cannot sleep, and the work queue can sleep because it uses the kworker thread, which is known to all to be able to sleep.

3.6 threaded irq

Using request_Threaded_The interrupt requested by IRQ is not executed in the interrupt context, but in a newly created thread, which makes the handler very similar to the workqueue and has all the workqueue characteristics, but eliminates the numerous steps of creating, initializing, and dispatching the workqueue.It's very simple to handle.

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)

request_irq is very similar, irq is the interrupt number, handler is the code to execute first when an interrupt occurs, very similar to the top half, this function returns irq_WAKE_THREAD to wake up interrupt threads,

If you use work to handle interrupts, a worker thread can only be executed by one CPU. If it's a single CPU, it's okay. But now most of the time, you're using a multi-core CPU, or SMP, which is a collection of processors (multiple CPUs) on a computer, a shared memory subsystem, and a bus structure among the CPUs.It is a very widely used parallel technology relative to asymmetric multiprocessing technology.

threaded irq creates a kernel thread for each interrupt, and multiple kworker kernel threads can be allocated to execute on the CPU without leaving other cpUs empty to do their work, which improves the CPU's efficiency.

4. Analyzing Linux interrupts from the perspective of architecture,

As mentioned earlier, when a linux kernel interrupt occurs, it jumps to an exception vector table to execute something. If a hardware interrupt is compared to an array, then there will be an array (actually a structure) for each hardware interrupt, which is irq_desc array.

4.1 irq_desc array

Function prototype:

struct irq_desc {
    struct irq_common_data  irq_common_data;
    struct irq_data     irq_data;
    unsigned int __percpu   *kstat_irqs;
    irq_flow_handler_t  handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
    irq_preflow_handler_t   preflow_handler;
#endif
    struct irqaction    *action;    /* IRQ action list */
    unsigned int        status_use_accessors;
    unsigned int        core_internal_state__do_not_mess_with_it;
    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;
    atomic_t        threads_handled;
    int         threads_handled_last;
    raw_spinlock_t      lock;
    struct cpumask      *percpu_enabled;
#ifdef CONFIG_SMP
    const struct cpumask    *affinity_hint;
    struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
    cpumask_var_t       pending_mask;
#endif
#endif
    unsigned long       threads_oneshot;
    atomic_t        threads_active;
    wait_queue_head_t       wait_for_threads;
#ifdef CONFIG_PM_SLEEP
    unsigned int        nr_actions;
    unsigned int        no_suspend_depth;
    unsigned int        cond_suspend_depth;
    unsigned int        force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS
    struct proc_dir_entry   *dir;
#endif
    int         parent_irq;
    struct module       *owner;
    const char      *name;
} ____cacheline_internodealigned_in_smp;

This structure is complex, but let's focus on handle_firstA list of IRQ and an action.The interrupts we normally trigger are ultimately aggregated into the GIC (Interrupt Controller) for judgment, then the GPC interrupts the CPU.
(1) Processing function of GIC:
Assume irq_desc[A].handle_irq is XXX_gpio_irq_handler(XXX refers to manufacturer), this function reads the GPIO controller of the chip, subdivides which GPIO interrupt occurred (assuming B), and then calls irq_desc[B]. handle_irq.
(2) Direct interrupt function

such as GPIO Module issued interrupt to interrupt controller A,Its corresponding processing function is irq_desc[A].handle_irq

(3) Processing functions provided by external equipment
The "external device" mentioned here may be a chip, or it may always be a simple button.For a GPIO interrupt, there may be many interrupt sources, such as keys, network cards, etc. Each interrupt source corresponds to a processing function, which is placed in the action list. When an interrupt occurs, the interrupt source processing function inside the action is traversed to see which interrupt source it is.

4.2 irqaction structure

This structure is the action list that we mentioned above, and it has many definitions.

struct irqaction {
         irq_handler_t handler;      //Equals the user-registered interrupt handler, which runs when an interrupt occurs
         unsigned long flags;         //Interruption flag, settings for registration, such as rise edge interruption, drop edge interruption, etc.
         cpumask_t mask;           //interrupt mask
         const char *name;          //The name of the interrupt, the name of the hardware that generated the interrupt
         void *dev_id;              //Device id
         struct irqaction *next;        //Point to Next Member
         int irq;                    //Interrupt number,
         struct proc_dir_entry *dir;    //Point to IRQn-related/proc/irq/

};

When we call request_irq, request_Threaded_When IRQ registers an interrupt handler, the kernel constructs an irqaction structure.Save name, dev_insideId, etc., most importantly handler, thread_fn, thread.

  • handler is the interrupt upper-half function we mentioned above, which is called when an interrupt occurs
  • thread_fn corresponds to a kernel thread, and when the handler finishes executing, the Linux kernel wakes up the corresponding kernel thread.In a kernel thread, thread_is calledFN function.This kernel thread is what we mean when we use work_queue (work queue) or thread_When irq, a kernel thread is created

4.3irq_data structure

struct irq_data { 
    u32            mask;----------TODO 
    unsigned int        irq;--------Software interrupt number
    unsigned long        hwirq;-------Hardware interrupt number
    unsigned int        node;-------NUMA node index 
    unsigned int        state_use_accessors;--------Bottom state, reference IRQD_xxxx 
    struct irq_chip        *chip;----------The break descriptor corresponds to irq chip data structure 
    struct irq_domain    *domain;--------The break descriptor corresponds to irq domain data structure 
    void            *handler_data;--------And peripherals specific handler Related Private Data 
    void            *chip_data;---------Private data related to interrupt controller 
    struct msi_desc        *msi_desc; 
    cpumask_var_t        affinity;-------and irq affinity Relevant 
 	}

This structure is a transit station with irq_insideChip pointer irq_domain pointer, such as GPIO interrupt B, software interrupt number, irq_can be foundDesc[B] this array item;The Xth interrupt in GPIO is hwirq.The relationship between them is established by irq_domain is represented by this structure, but we have to think about one question, such as how to distinguish the first interrupt in the GPIO controller from the first interrupt in the serial port, when irq_is used.Domain to represent.

4.4irq_domain structure

Function prototype:

static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
					 unsigned int size,
					 const struct irq_domain_ops *ops,
					 void *host_data)
{
	return __irq_domain_add(of_node_to_fwnode(of_node), size, size, 0, ops, host_data);

}

This structure is very useful for the interrupt conversion of the device tree, for example, as you will see in the device tree

interrupt-parent = <&gpio1>;
interrupts = <5 IRQ_TYPE_EDGE_RISING>;

It indicates that hwirq is 5 to use the 5th interrupt in gpio1.We will convert hwirp to irq, which is the software interrupt number. Only if we have the software interrupt number can we register the interrupt and so on, such as this function request_irq(irq, handler).In the properties of the device tree above, gpio1 will have an irq_domain structure, which has an irq_domain_ops structure, which has an xlate function to parse the interrupt properties of the device tree, such as hwirq (hardware interrupt number), type, etc., and finally converts hwirq to IRQ through map function

4.5 irq_chip Structures

Function prototype:

struct irq_chip {
    const char    *name;
    unsigned int    (*irq_startup)(struct irq_data *data);-------------Initialization Interrupt
    void        (*irq_shutdown)(struct irq_data *data);----------------End interruption
    void        (*irq_enable)(struct irq_data *data);------------------Enable interruption
    void        (*irq_disable)(struct irq_data *data);-----------------Close Interrupt
 
    void        (*irq_ack)(struct irq_data *data);---------------------Answer interruption
    void        (*irq_mask)(struct irq_data *data);--------------------Shielding interruption
    void        (*irq_mask_ack)(struct irq_data *data);----------------Answer and block interrupts
    void        (*irq_unmask)(struct irq_data *data);------------------Unblock
    void        (*irq_eoi)(struct irq_data *data);---------------------Send out EOI Signal indicating that hardware interrupt processing is complete.
 
    int        (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);--------Binding breaks to a CPU
    int        (*irq_retrigger)(struct irq_data *data);----------------Resend interrupted to CPU
    int        (*irq_set_type)(struct irq_data *data, unsigned int flow_type);----------------------------Set trigger type
    int        (*irq_set_wake)(struct irq_data *data, unsigned int on);-----------------------------------Enabling/Turn off the interrupt wake-up function in power management.
 
    void        (*irq_bus_lock)(struct irq_data *data);
    void        (*irq_bus_sync_unlock)(struct irq_data *data);
 
    void        (*irq_cpu_online)(struct irq_data *data);
    void        (*irq_cpu_offline)(struct irq_data *data);
 
    void        (*irq_suspend)(struct irq_data *data);
    void        (*irq_resume)(struct irq_data *data);
    void        (*irq_pm_shutdown)(struct irq_data *data);
...
    unsigned long    flags;

We are passing request_When IRQ registration is interrupted, the system calls irq_Functions in the chip help us break.
This function has other functions, such as performing cleanup interrupts on the main chip, but cleanup interrupts on external devices still need to be done by ourselves, because the kernel does not know what external devices you are using.

#Summary The above part is just some theoretical thing. How to write the code will work in my other articles. If you want to know more about the underlying and application knowledge of Linux, I recommend you to go to this website to learn. Many of my knowledge is from the video of station B or Mr. Wei.Linux interrupts are a top priority for the whole system, such as threading processes or timers. Polling mechanisms use interrupts. You may only use them through function calls. However, learning some of the underlying knowledge of Linux will greatly improve your Linux application and driver development.

.Teacher's Tutorial100ask.net/index

Topics: Linux Embedded system multiple processes ARM