//// This function is called when the do_IRQ function exits after the hardware ISR is executed. // Void irq_exit (void) {account_system_vtime (current); trace_hardirq_exit (); sub_preempt_count (IRQ_EXIT_OFFSET); /// determine whether there is nested hardware interruption currently, whether there is a soft interrupt in the // pending status. note: only when the two conditions are met at the same time can do_softirq () be called to enter the soft interrupt state. That is to say, it will only enter when you confirm that all the current hardware interrupt processing is completed, and the hardware interrupt is installed // when the soft interrupt processing is completed. // If (! In_interrupt () & local_softirq_pending () // In fact, here we call do_softirq () to execute // invoke_softirq (); preempt_enable_no_resched ();} # ifndef _ ARCH_HAS_DO_SOFTIRQasmlinkage void do_softirq (void) {_ u32 pending; unsigned long flags; // This function determines if there is a nested hardware interrupt, or, if a soft interrupt is being executed, return immediately. The judgment at this // entry mainly aims to be mutually exclusive with ksoftirqd. // If (in_interrupt () return; // execute the following code for Guanzhong disconnection // local_irq_save (flags); /// determine whether a pending soft interrupt needs to be handled. // Pending = local_softirq_pending (); // if yes, call _ do_softirq () for actual processing. // if (pending) _ do_softirq (); //// enable interrupt to continue executing // local_irq_restore (flags);} // the maximum number of soft interrupt calls is 10. /// # Define MAX_SOFTIRQ_RESTART 10 asmlinkage void _ do_softirq (void) {// The interrupt processing structure of the software. this structure includes the callback functions registered in ISR. // Struct softirq_action * h; _ u32 pending; int max_restart = MAX_SOFTIRQ_RESTART; int cpu; // obtain all the current pending soft interruptions. // Pending = local_softirq_pending (); account_system_vtime (current); // prevent other soft interruptions from being executed here, here it is proved that // Each CPU can run only one soft interrupt simultaneously. // _ Local_bh_disable (unsigned long) _ builtin_return_address (0); trace_softirq_enter (); //// obtain the CPU being processed for SMP /// cpu = smp_processor_id (); //// cycle flag // restart: //// each cycle first resets the flag of the soft interrupt before the hardware ISR is allowed. /// * Reset the pending bitmask before enabling irqs */set_softirq_pending (0); // the operation is interrupted only when it is reached. note: in the past, the operating status was always Guanzhong disconnected/run. in this case, the current soft interrupt processing may be preemptible by hardware interruption. That is to say, when a soft interrupt occurs, it will not be preemptible by the hardware interrupt in the first place. Only codes later than // Can be preemptible by hardware interruption. // Local_irq_enable (); // note that the following code can be preempted by hardware interruption during running, but // after the hardware ISR is executed, the registered soft interrupt cannot be run immediately. // do not forget that although the hardware interrupt is enabled, the previous _ local_bh_disable () // function shields the soft interrupt. Therefore, this environment can only be preemptible by hardware interruptions, but this/soft interrupt callback function cannot run. To ask why, it is because the // _ local_bh_disable () function sets a flag as the mutex, and this // flag is exactly the irq_exit () and do_softirq () above () one of the conditions for determining the // in_interrupt () function in the function, that is, in_interrupt () // function not only detects hard interruptions but also determines soft interruptions. Therefore, when a hard interrupt is triggered in this environment, the soft interrupt of registration cannot be re-entered into this function. only // makes a flag, wait for the following repeating loop (MAX_SOFTIRQ_RESTART at the maximum) // you can handle the soft interrupt registered for the hardware interrupt triggered at this time. ////// Obtain the soft interrupt vector table. // H = softirq_vec; // cyclically process all softirq soft interrupt registration functions. // Do {//// if the pending flag is set for the soft interrupt, it indicates that // you need to further process the function it registers. // If (pending & 1) {/// the callback function registered for this soft interrupt is executed here. // H-> action (h); rcu_bh_qsctr_inc (cpu) ;}/// continue to find the soft/interrupt processing of all pending in the soft interrupt vector table. // H ++; // The code shows the bitwise operation, indicating that only the callback function that processes 32 soft interrupts in one loop. // Pending >>=1 ;}while (pending); /// You can run the following code to cancel the operation. Note: Once again, the hardware interruption cannot be preemptible during the following // code execution. // Local_irq_disable (); /// as mentioned earlier, the execution environment of hardware interruptions can only be interrupted by hardware and cannot be preemptible, because the hardware interruption can be preemptible multiple times during the process of opening/discontinuing execution, you can register a soft interrupt each time. Therefore, you need to retrieve all the soft interruptions again. // So that the following code can be processed and then jump back to restart for repeated execution. // Pending = local_softirq_pending (); // if the hardware interrupt is triggered in the preceding open interrupt execution environment and each of them registers a soft interrupt, this soft interrupt will be set to a pending bit, // but cannot be executed in the environment where the soft interrupt is blocked. I mentioned it before // because of irq_exit () and do_softirq () you cannot enter the // processing process at all. This is a detailed record above. Then there is another opportunity for execution. Note: Although the current environment is always // in the environment where soft interrupt execution is blocked, however, here is another example of the soft interrupt that was registered when the hardware interrupt was triggered during the startup of the interrupt environment. In fact, you only need to understand the soft interrupt mechanism, it is nothing more than calling the functions registered to the soft interrupt vector table in some special/specific environments. ///// If the soft interrupt is registered and the number of repeated executions does not reach 10, then jump to the restart flag and repeat all the above steps: Set the soft Interrupt flag, re-open the interrupted execution... // Note: The preceding steps can be repeated only when both conditions are met. // If (pending & -- max_restart) goto restart; // if the above steps have been repeated for 10 times and then there is a soft suspension of pending, // the system may reach a peak value within a certain period of time to balance this. // The system creates a ksoftirqd thread for processing, so as to avoid loading too much at a specific time. The ksoftirqd thread itself is a large loop. // in some conditions, it can be preemptible by other processes to avoid heavy load. // note that, it indicates that preempt_xxx () and schedule () are called to be preemptible and switched. The reason for this is that once the/local_softirq_pending () function is called and the pending soft interrupt is detected, the call do_softirq () is displayed () to process the process of being soft/disconnected. That is to say, the ksoftirqd thread awakened by the following code may return // to this function, especially when the system needs to respond to many soft interruptions, its call entry is do_softirq (), which is why in_interrupt () is also used at the entry of do_softirq () the function is used to determine whether there is a soft interrupt // The cause of the processing. The purpose is to prevent re-entry. Ksoftirqd implementation // you can see the analysis of the ksoftirqd () function. // If (pending) // This function actually calls wake_up_process () to wake up ksoftirqd // wakeup_softirqd (); trace_softirq_exit (); account_system_vtime (current ); //// the soft interrupt execution environment is enabled until the end, and soft interrupt execution is allowed. Note: Here // does not use local_bh_enable (), and do_softirq () // is not triggered again. // _ Local_bh_enable ();} static int ksoftirqd (void * _ bind_cpu) {// display the static priority of the current process by calling this function. Of course, // This priority will change with the scheduler policy. // Set_user_nice (current, 19); // you can specify that the current process cannot be started. // current-> flags | = PF_NOFREEZE; //// set the status of the current process to an interrupted state, such as sleep status and response signal processing. // Set_current_state (TASK_INTERRUPTIBLE); // The following is a large loop, which cyclically determines whether the current process will stop, // If not, continue to judge whether there is a pending soft interrupt. // you need to handle it. // While (! Kthread_should_stop () {// If processing is possible, the current process is prohibited during this processing period. // The current process is preemptible. // Preempt_disable (); //// first, judge the pending status that the system does not need to handle. // Soft interrupt // if (! Local_softirq_pending () {// if no, preemptible is allowed before giving up the CPU, because // The code is always executed when the preemptible state is not allowed. // Preempt_enable_no_resched) // schedule (); //// note: If the progress that is actively switched by calling the schedule () function is shown to be scheduled again, the execution starts from the next // statement that calls this function. In other words, if the current process is executed again, the following preempt_disable () function will be executed. ///// When a process is scheduled again, the current process is prohibited from being preemptible during the following processing periods. // Preempt_disable () ;}//// set the current process to running. Note: The current process has been set to be unpreemptible. // after the process enters the loop, the above two branches will be executed here no matter which one goes. First, // when the stream enters the loop, the pending soft interrupt needs to be executed. Second, when a loop is started, // There is no pending soft interrupt. when the current process is scheduled again to obtain the CPU, it continues to run. // _ Set_current_state (TASK_RUNNING); // cyclically determines whether a pending soft interrupt exists. If yes, call do_softirq () // for specific processing. Note: Here is an entry point of do_softirq (). // After _ do_softirq () processes the soft interrupt callback function for 10 times cyclically. // If pending exists, will be called again here. In this case, // it is possible to call _ do_softirq () to process the soft interrupt callback function. As mentioned in the introduction to _ do_softirq () in the front/plane, the system is in a busy state if the system cannot be processed for 10 times. Based on the above analysis, we can imagine that when the // system is very busy, this process will run alternately with do_softirq (). // at this time, this process will occupy a high CPU, although the following cond_resched () // Function performs some processing, the current processing process may reduce the CPU load due to scheduling after processing a soft interrupt, however, when the process is very busy, it still occupies a lot of CPU. // While (local_softirq_pending () {/* Preempt disable stops cpu going offline. if already offline, we'll be on wrong CPU: don't process */if (cpu_is_offline (long) _ bind_cpu )) //// if the associated CPU cannot continue to process, jump to // Mark wait_to_die, wait for the end and exit. // Goto wait_to_die; // execute do_softirq () to process the soft interrupt callback function. Note // if a soft interrupt is being processed at this time, it will be immediately // returned. do you still remember the in_interrupt () function described earlier. // Do_softirq (); /// allow the current process to be preemptible. // Preempt_enable_no_resched (); // This function may indirectly call schedule () to switch the current // process and allow the current process to be preemptible. That is, when processing a soft interrupt callback function, it may switch to another process. I think the purpose of this operation is to prevent the process from occupying a large amount of CPU for a long time when some loads/exceeds the standard, // second, prevent other processes from responding when many soft interruptions need to be handled. // Cond_resched (); /// the current process is prohibited from being preemptible. /// Preempt_disable (); /// has all the soft interruptions been processed? If not, continue to the previous steps. // after all the processes are completed, allow the current process to be preemptible and set the // Current process status to be interrupted, continue to cycle all the above processes. // Preempt_enable (); set_current_state (TASK_INTERRUPTIBLE);} // if it will stop, set the current process to the running state and return directly. // The scheduler runs the current process based on the priority. // _ Set_current_state (TASK_RUNNING); return 0; // wait until the current process is stopped // wait_to_die: // allow the current process to be preemptible. // Preempt_enable ();/* Wait for kthread_stop * // you can set the state of the current process to be interrupted, and such a sleep status can respond to signal processing. // Set_current_state (TASK_INTERRUPTIBLE); // determines whether the current process will be stopped. if not, /// sets the process status to the interrupted state and discards the current CPU // actively switches. That is to say, it will wait until the current process is stopped. // While (! Kthread_should_stop () {schedule (); set_current_state (TASK_INTERRUPTIBLE) ;}/// if it will stop, set the current process to the running state and return directly. // The scheduler runs the current process based on the priority. // _ Set_current_state (TASK_RUNNING); return 0 ;}
|