When save states while dealing with tasklets? - linux-kernel

I'm reading Linux Kernel development and get confused in the tasklets chapter (https://doc.lagout.org/operating%20system%20/linux/Linux%20Kernel%20Development%2C%203rd%20Edition.pdf page143).
In the tasklet_schedule function, the interrupt state is saved while in the taslet_action it is not. The author explains that the context is not saved in taslet_action because the function knows that interrupts are always enabled. I fail to understand how does the set of interrupts interfere with saving the context? Thank you!

The author states that tasklet_schedule can be called with either interrupts disabled or enabled. Since it wants them disabled, it needs to save whether they are already disabled. Then after the work is done it knows whether to enable them (if they were enabled prior to the call it enables them, if they were disabled prior to the call it leaves them disabled). In contrast, tasklet_action is only called with interrupts enabled, so there is no point in checking their state. They always get disabled and enabled on return.

In case of tasklet_schedule :
We do not want interrupt to disturb us when we are scheduling a tasklet, so we have to disable it. But we also know that, when we are done with scheduling the tasklet, we want to go back to the state of IRQ as it was before schedule tasklet was called. To achieve this, we save the state of the IRQ register before doing anything, then disable the IRQ as per our requirement, do the scheduling, now before going back restore the state of the IRQ and then return from the function.
Now coming to the complicated part, Why dont we need to save the IRQ when executing the tasklet i.e when the handler calls the tasklet function ?
To understand that we need to look at two different paragraphs :
On page 141 :
The softirq handlers run with interrupts enabled and cannot
sleep .While a handler runs, softirqs on the current processor are
disabled.Another processor, however, can exe-cute other softirqs. If
the same softirq is raised again while it is executing, another
processor can run it simultaneously.
So this declares that interrupts are always enabled.
Now going to pg 143 :
Disable local interrupt delivery (there is no need to first save their state because the code here is always called as a softirq
handler and interrupts are always enabled) and retrieve the
tasklet_vec or tasklet_hi_vec list for this processor
So we can conclude that we dont need to save the IRQ state as we already know its state and it will remain so in all the conditions so we just disable the IRQ and enable it later.

Related

When we use irq_set_chained_handler the irq line will be disabled or not?

When we use irq_set_chained_handler the irq line will not be disabled or not, when we are servicing the associated handler, as in case of request_irq.
It doesn't matter how the interrupt was setup. When any interrupt occurred, all interrupts (for this CPU) will be disabled during the interrupt handler. For example, on ARM architecture first place in C code where interrupt handling is found is asm_do_IRQ() function (defined in arch/arm/kernel/irq.c). It's being called from assembler code. For any interrupt (whether it was requested by request_irq() or by irq_set_chained_handler()) the same asm_do_IRQ() function is called, and interrupts are disabled automatically by ARM CPU. See this answer for details.
Historical notes
Also, it worth to be mentioned that some time ago Linux kernel was providing two types of interrupts: "fast" and "slow" ones. Fast interrupts (when using IRQF_DISABLED or SA_INTERRUPT flag) were running with disabled interrupts, and those handlers supposed to be very short and quick. Slow interrupts, on the other hand, were running with re-enabled interrupts, because handlers for slow interrupts may take much of time to be handled.
On modern versions of Linux kernel all interrupts are considered as "fast" and are running with interrupts disabled. Interrupts with huge handlers must be implemented as threaded (or enable interrupts manually in ISR using local_irq_enable_in_hardirq()).
That behavior was changed in Linux kernel v2.6.35 by this commit. You can find more details about this here.
Refer https://www.kernel.org/doc/Documentation/gpio/driver.txt
This means the GPIO irqchip is registered using
irq_set_chained_handler() or the corresponding
gpiochip_set_chained_irqchip() helper function, and the GPIO irqchip
handler will be called immediately from the parent irqchip, while
holding the IRQs disabled. The GPIO irqchip will then end up calling
something like this sequence in its interrupt handler:

use of spin variants in network processing

I have written a Kernel module that is interacting with net-filter hooks.
The net-filter hooks operate in Softirq context.
I am accessing a global data structure
"Hash Table" from the softirq context as well as from Process context. The process context access is due to a sysctl file being used to modify the contents of the Hash-table.
I am using spinlock_irq_save.
Is this choice of spin_lock api correct ?? In terms of performance and locking standards.
what would happen if an interrupt is scheduled on another processor? while on the current processor lock is already hold by a process context code?
Firstly:
So, with all the above details I concluded that my softirqs can run concurrently on both cores.
Yes, this is correct. Your softirq handler may be executed "simultaneously on more than one CPU".
Your conclusion to use spinlocks sounds correct to me. However, this assumes that the critical section (ie., that which is executed with the spinlock held) has the following properties:
It must not sleep (for example, acquire a blocking mutex)
It should be as short as possible
Generally, if you're just updating your hash table, you should be fine here.
If an IRQ handler tries to acquire a spinlock that is held by a process context, that's fine. As long as your process context does not sleep with that lock held, the lock should be released within a short amount of time, allowing the IRQ handler to make forward progress.
I think the solution is appropriate . Softirqs anyways runs with preemption disabled . To share a data with a process, the process must also disable both preemption and interrupts. In case of timer, which only reduces the time stamp of an entry can do it atomically i.e. the time stamp variable must be atomic. If in another core softirqs run and wants to acquire the spinlock, when it is already held in the other core,it must wait.

How kernel disable the softirq in the local processor when softirq handler runs

Recently I study the Linux-Kernel-Development by Robert Love.
There is a paragraph describes mechanism of softirq.
The softirq handlers run with interrupts enabled and cannot sleep.
While a handler runs, softirqs on the current processor are disabled.
Another processor, however, can execute other softirqs.
I don't understand the meaning of "softirqs on the current processor are disabled."
Does this mean that when running __do_softirq, even if some of the bit in the softirq_pending is raising again, the __do_softirq function cannot be interrupted? If yes then what statements in the __do_softirq do this kind of protection?
When tracing the code in __do_softirq, I found that there are a pair of __local_bh_disable and __local_bh_enable functions.
Do they disable the local softirq?
Thanks.
Yes, __local_bh_disable and __local_bh_enable disable and enable processing of softirqs on the current CPU. Softirqs are also known as "bottom halves", which is what the "bh" in those names represents.

Should my interrupt handler disable interrupts or does the ARM processor do it automatically?

Our group is using a custom driver to interface four MAX3107 UARTs on a shared I2C bus. The interrupts of the four MAX3107's are connected (i.e. shared interrupt via logic or'ing)) to a GPIO pin on the ARM9 processor (LPC3180 module). When one or more of these devices interrupt, they pull the GPIO line, which is configured as a level-sensitive interrupt, low. My question concerns the need, or not, to disable the specific interrupt line in the handler code. (I should add that we are running Linux 2.6.10).
Based on my reading of several ARM-specific app notes on interrupts, it seems that when the ARM processor receives an interrupt, it automatically disables (masks?) the corresponding interrupt line (in our case this would seem to be the line corresponding to the GPIO pin we selected). If this is true, then it seems that we should not have to disable interrupts for this GPIO pin in our interrupt handler code as doing so would seem redundant (though it seems to work okay). Stated differently, it seems to me that if the ARM processor automatically disables the GPIO interrupt upon an interrupt occurring, then if anything, our interrupt handler code should only have to re-enable the interrupt once the device is serviced.
The interrupt handler code that we are using includes disable_irq_nosync(irqno); at the very beginning of the handler and a corresponding enable_irq() at the end of the handler. If the ARM processor has already disabled the interrupt line (in hardware), what is the effect of these calls (i.e. a call to disable_irq_nosync() followed by a call to enable(irq())?
From the Arm Information Center Documentation:
On entry to an exception (interrupt):
interrupt requests (IRQs) are disabled for all exceptions
fast interrupt requests (FIQs) are disabled for FIQ and Reset exceptions.
It then goes on to say:
Handling an FIQ causes IRQs and subsequent FIQs to be disabled,
preventing them from being handled until after the FIQ handler enables
them. This is usually done by restoring the CPSR from the SPSR at the
end of the handler.
So you do not have to worry about disabling them, but you do have to worry about re-enabling them.
You will need to include enable_irq() at the end of your routine, but you shouldn't need to disable anything at the beginning. I wouldn't think that calling disable_irq_nosync(irqno) in software after it has been called in hardware would effect anything. Since the hardware call is most definitely called before the software call has a chance to take over. But it's probably better to remove it from the code to follow convention and not confuse the next programmer who takes a look at it.
More info here:
Arm Information Center

For a shared interrupt line how do I find which interrupt handler to use?

For a shared interrupt line,I can have several interrupt handlers. The kernel will sequentially invoke all the handlers for that particular shared line.
As far as I know, each handler, when invoked informs the kernel whether it was the correct handler to be invoked or not.
My questions is how is this determined,is there a way it checks a memory mapped register that tells status of a particular device or is there some other hardware mechanism ? How does the handler know that the corresponding device is indeed the one that issued the interrupt or not ?
Is this information relayed through the interrupt controller that is between the devices and the processor interrupt line ??
The kernel will sequentially invoke all the handlers for that particular shared line.
Exactly. Say Dev1 and Dev2 shares the IRQ10. When an interrupt is generated for IRQ10, all ISRs registered with this line will be invoked one by one.
In our scenario, say Dev2 was the one that generated the interrupt. If Dev1's ISR is registered first, than its ISR (i.e Dev1's ISR) only called first. In that ISR, the interrupt status register will be verified for interrupt. If no interrupt bit is set (which is the case, cause Dev2 raised the interrupt) then we can confirm that interrupt was not generated by Dev1 - so Dev1's ISR should return to the kernel IRQ_NONE - which means:"I did not handled that interrupt", so on the kernel continues to the next ISR (i.e Dev2's ISR), which in turn, will indeed verify that its corresponding device generated the interrupt, thus, this handler should handle it and eventually return IRQ_HANDLED - which means:"I handled this one".
See the return values IRQ_NONE/IRQ_HANDLED for more information.
How does the handler know that the corresponding device issued the interrupt or not ?
By reading the Interrupt status register only.
Is this information relayed through the interrupt controller that is between the devices and the processor interrupt line ??
I'm not sure about this. But the OS will take care of calling ISRs based on the return values from ISR.

Resources