Stack overflow if softirq preempt by hardware interrupt many times - c

Recently I've been looking into one NIC driver implemented by NAPI which uses softirq to handle tx&rx packets, and lots of docs say softirq can be triggered when hardware interrupt return, then I have two questions about it:
when running softirq, is registers stored by hardware interrupt still on kernel stack?
if yes on 1, will the below sequence cause kernel stack overflow?
hardware interrupt and store registers on kernel stack.
hardware interrupt handler raises softirq.
softirq is running and a new hardware interrupt is coming.
go back to the first step.

I think I got the point:
is registers stored by hardware interrupt still on kernel stack
yes, we are still in hardware interrupt context, but it is after calling the interrupt handler which is usually registered by the driver.
no, that will not happen, when we all do_softirq, it will check preempt count by function "in_interrupt" which check both hardirq and softirq, so on the second round, do_softirq check in_interrupt is true and return directly.

Related

How could we sleep when we are executing a syscall that execute in interrupt mode

When I am executing a system call to do write or something else, the ISR corresponded to the exception is executing in interrupt mode (on cortex-m3 the IPSR register is having a non-zero value, 0xb). And what I have learned is that when we execute a code in an interrupt mode we can not sleep, we can not use functions that might block ...
My question is that: is there any kind of a mechanism with which the ISR could still executing in interrupt mode and in the same time it could use functions that might block, or is there any kind of trick is implemented.
Caveat: This is more of a comment than an answer but is too big to fit in a comment or series of comments.
TL;DR: Needing to sleep or execute a blocking operation from an ISR is a fundamental misdesign. This seems like an XY problem: https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem
Doing sleep [even in application code] is generally a code smell. Why do you feel you need to sleep [vs. some event/completion driven mechanism]?
Please edit your question and add clarification [i.e. don't just add comments]
When I am executing a system call to do write or something else
What is your application doing? A write to what device? What "something else"?
What is the architecture/board, kernel/distro? (e.g. Raspberry Pi running Raspian? nvidia Jetson? Beaglebone? Xilinx FPGA with petalinux?)
What is the H/W device and where did the device driver come from? Did you write the device driver yourself or is it a standard one that comes with the kernel/distro? If you wrote it, please post it in your question.
Is the device configured properly? (e.g.) Are the DTB entries correct?
Is the device a block device, such as a disk controller? Or, is it a character device, such as a UART? Does the device transfer data via DMA? Or, does it transfer data by reading/writing to/from an IO port?
What do you mean by "exception"? Generally, exception is an abnormal condition (e.g. segfault, bus error, etc.). Please describe the exact context/scenario for which this occurs.
Generally, an ISR does little things. (e.g.) Grab [and save] status from the device. Clear/rearm the interrupt in the interrupt controller. Start the next queued transfer request. Wake up the sleeping base level task (usually the task that executed the syscall [waiting on a completion event in kernel mode]).
More elaborate actions are generally deferred and handled in the interrupt's "bottom half" handler and/or tasklet. Or, the base level is woken up and it handles the remaining processing.
What kernel subsystems are involved? Are you using platform drivers? Are you interfacing from within the DMA device driver framework? Are message buses involved (e.g. I2C, SPI, etc.)?
Interrupt and device handling in the linux kernel is somewhat different than what one might do in a "bare metal" system or RTOS (e.g. FreeRTOS). So, if you're coming from those environments, you'll need to think about restructuring your driver code [and/or application code].
What are your requirements for device throughput and latency?
You may wish to consult a good book on linux device driver design. And, you may wish to consult the kernel's Documentation subdirectory.
If you're able to provide more information, I may be able to help you further.
UPDATE:
A system call is not really in the same class as a hardware interrupt as far as the kernel is concerned, even if the CPU hardware uses the same sort of exception vector mechanisms for handling both hardware and software interrupts. The kernel treats the system call as a transition from user mode to kernel mode. – Ian Abbott
This is a succinct/great explanation. The "mode" or "context" has little to do with how we got/get there from a H/W mechanism.
The CPU doesn't really "understand" interrupt mode [as defined by the kernel]. It understands "supervisor" vs "user" privilege level [sometimes called "mode"].
When executing at user privilege level, an interrupt/exception will notice the transition from "user" level to "supvervisor" level. It may have a special register that specifies the address of the [initial] supervisor stack pointer. Atomically, it swaps in the value, pushing the user SP onto the new kernel stack.
If the interrupt is interrupting a CPU that is already at supervisor level, the existing [supervisor] SP will be used unchanged.
Note that x86 has privilege "ring" levels. User mode is ring 3 and the highest [most privileged] level is ring 0. For arm, some arches can have a "hypervisor" privilege level [which is higher privilege than "supervisor" privilege].
The setup of the mode/context is handled in arch/arm/kernel/entry-*.S code.
An svc is a synchronous interrupt [generated by a special CPU instruction]. The resulting context is the context of the currently executing thread. It is analogous to "call function in kernel mode". The resulting context is "kernel thread mode". At that point, it's not terribly useful to think of it as an "interrupt" anymore.
In fact, on some arches, the syscall instruction/mechanism doesn't use the interrupt vector table. It may have a fixed address or use a "call gate" mechanism (e.g. x86).
Each thread has its own stack which is different than the initial/interrupt stack.
Thus, once the entry code has established the context/mode, it is not executing in "interrupt mode". So, the full range of kernel functions is available to it.
An interrupt from a H/W device is asynchronous [may occur at any time the CPU's internal interrupt enable flag is set]. It may interrupt a userspace application [executing in application mode] OR kernel thread mode OR an existing ISR executing in interrupt mode [from another interrupt]. The resulting ISR is executing in "interrupt mode" or "ISR mode".
Because the ISR can interrupt a kernel thread, it may not do certain things. For example, if the CPU were in [kernel] thread mode, and it was in the middle of a kmalloc call [GFP_KERNEL], the ISR would see partial state and any action that tried to adjust the kernel's heap would result in corruption of the heap.
This is a design choice by linux for speed.
Kernel ISRs may be classified as "fast interrupts". The ISR executes with the CPU interrupt enable [IE] flag cleared. No normal H/W interrupt may interrupt the ISR.
So, if another H/W device asserts its interrupt request line [in the external interrupt controller], that request will be "pending". That is, the request line has been asserted but the CPU has not acknowledged it [and the CPU has not jumped via the interrupt table].
The request will remain pending until the CPU allows further interrupts by asserting IE. Or, the CPU may clear the pending interrupt without taking action by clearing the pending interrupt in the interrupt controller.
For a "slow" interrupt ISR, the interrupt entry code will clear the interrupt in the external interrupt controller. It will then rearm interrupts by setting IE and call the ISR. This ISR can be interrupted by other [higher priority] interrupts. The result is a "stacked" interrupt.
I have been searching all over the places, I come to the conclusion is that the interrupts have a higher priority than some of exceptions in the Linux kernel.
An exception [synchronous interrupt] can be interrupted if the IE flag is enabled. An svc is treated differently but after the entry code is executed, the IE flag is set, so the actual syscall code [executing in kernel thread mode] can be interrupted by a H/W interrupt.
Or, in limited circumstances, the kernel code can generate an exception (e.g. a page fault caused by a kernel action [which is usually deemed fatal]).
but I am still looking on how exactly the context switching happen when executing an exception and letting the processor to execute in a thread mode while the SVCall exception is pending (was preempted and have not returned yet)... I think when I understand that, it would be more clear to me.
I think you have to be very careful with the terminology. In particular, when combining terms from disparate sources. Although user mode, kernel thread mode, or interrupt mode can be considered a context [in the dictionary sense of the word], context switching usually means that the current thread is suspended, the scheduler selects a new thread to run and resumes it. That is separate from the user-to-kernel transition.
And if there is any recommended resources about that for ARM-Cortex-M3/4, it would be nice
Here is something: https://interrupt.memfault.com/blog/arm-cortex-m-exceptions-and-nvic But, be very careful in applying the terminology therein. What it considers "pending" only exists in the kernel during the entry code. What is more relevant is what the kernel does to set up mode/context and the terms are not equivalent.
So, from the kernel's standpoint, it's probably better to not consider an svc as "pending".

semaphore like synchronization in ISR (Interrupt service routine)

I have a queue where the put and pull functions of the queue are called when different interrupts happen. Is there a way to prevent race condition in this scenario?
While we can not wait on semaphores in interrupt service routines what is the best way to create a similar functionality.
We are using an ARM-Cortex A5 processor of a Zynq FPGA to develope the code.
Assuming that each interrupt causes the "Interrupt Disabled" state of the processor to be turned on, and assuming that the interrupts you are handling have the same priority (that is, one can't interrupt the execution of the other), then there already can be no race condition and your ISRs can just access the shared queue.
(When an interrupt occurs, the processor goes into interrupt disabled mode, pushes all registers onto the stack, jumps to the ISR entry point and continues execution there. Once the ISR is done, the "iret" instruction does the reverse of the entry. This simple description can be implemented differently in different processors and platforms.)

Why processes cannot preempt interrupts?

I know when an interrupt occurs the process running is put on hold, and the Interrupt Service Routine is called. The current pointer is pointing to the process that was interrupted and I was told that when an interrupt occurs it is not linked to a specific process. So my question is why only another interrupt can preempt an existing interrupt routine?
Also, when a process(p2) preempts another process(p1), who is calling the schedule() method?
the first two answers both show some significant misunderstanding about interrupts and how they work
Of particular interest,
for the CPUs that we are usually using
( 86x.., power PC, 68xxx, ARM, and many others)
each interrupt source has a priority.
sadly, there are some CPUs, for instance the 68HC11, where all the interrupts, except the reset interrupt and the NMI interrupt, have the same priority so servicing any of the other interrupt events will block all the other (same priority) interrupt events.
for our discussion purposes, a higher priority interrupt event can/ will interrupt a lower priority interrupt handler.
(a interrupt handler can modify the appropriate hardware register to disable all interrupt events or just certain interrupt events. or even enable lower priority interrupts by clearing their own interrupt pending flag (usually a bit in a register)
In general, the scheduler is invoked by a interrupt handler,
(or by a process willingly giving up the CPU)
That interrupt is normally the result of a hardware timer expiring/reloading and triggering the interrupt event.
A interrupt is really just an event where the event is waiting to be serviced.
The interrupt event, when allowed, for instance by being the highest priority interrupt that is currently pending, will cause the PC register to load the first address of the related interrupt handler.
the act of diverting the PC register to the interrupt handler will (at a minimum) push the prior PC register value and status register onto the stack. (in some CPUs, there is a special set of save areas for those registers, so they are pushed onto the special area rather than on the stack.
The act of returning from an interrupt, for instance via the RTI instruction, will 'automatically' cause the prior PC and status register values to be restored.
Note: returning from an interrupt handler does not clear the interrupt event pending indication, so the interrupt handler, before exiting needs to modify the appropriate register otherwise the flow of execution will immediately reenter the interrupt handler.
The interrupt handler has to, upon entry, push any other registers that it modifies and, when ready to exit, restore them.
Only interrupts of a lower priority are blocked by the interrupt event diverting the PC to the appropriate interrupt handler. Blocked, not disabled.
on some CPUs, for instance most DSPs, there are also software interrupts that can be triggered by an instruction execution.
This is usually used by hardware interrupt handlers to trigger the data processing after some amount of data has been input/saved in a buffer. This separates the I/O from the processing thereby enabling the hardware interrupt event handler to be quick and still have the data processed in a timely manner
The above contradicts much of what the comments and other answers state. However, those comments and answers are from the misleading view of the 'user' side of the OS, while I normally program right on the bare hardware and so am very familiar with what actually happens.
So my question is why only another interrupt can preempt an existing
interrupt routine?
A hardware interrupt usually puts the processor hardware in an interrupt state where all interrupts are disabled. The interrupt-handler can, and often does, explicitly re-enable interrupts of a higher priority. Such an interrupt can then preempt the lower-priority interrupt. That is the only mechanism that can interrupt a hardware interrupt.
Also, when a process(p2) preempts another process(p1), who is calling
the schedule() method?
That depends somewhat on whether the preemption is initiated by a syscall from a thread already running, or by a hardware interrupt that causes a handler/driver to run and subsequently enter the kernel to request a reschedule. The exact mechansims, (states, stacks etc), used are architecture-dependent.
Regarding your first question: While an interrupt is running, interrupts are disabled on that processor. Therefore, it cannot be interrupted.
Regarding your second question: A process never preempts another process, it is always the OS doing that. The OS calls the scheduler routine regularly, where it decides which process will run next. So p2 doesn't say "i want to run now", it just has some attributes like a priority, remaining time slot, etc., and the OS then decides whether p2 should run now.

Is there a difference between an ISR and an interrupt handler?

I'm studying operating systems and I encountered both the terms ISR and interrupt handler. Are they two words for the same mechanism? If not, what is the difference?
There is no difference in Interrupt handler and ISR.
Wiki says that:
In computer systems programming, an interrupt handler, also known as an interrupt service routine or ISR, is a callback function [...]
ISR is callback for a specific service pertaining to a device/operation/source. There could be multiple ISRs present in a system depending on addresses available in Interrupt Vector table. Where is Interrupt handler is a common routine which is triggered whenever any interrupt comes. Its job is to understand the source of the interrupt and trigger appropriate ISR mapped in Interrupt Vector table.
When interrupt occurs,
interrupt handler performs minimal operations required to respond to the device where as updating the buffer and all other operations are taken care by ISR

What happens in an interrupt service routine?

Can someone please explain to me what happens inside an interrupt service routine (although it depends upon specific routine, a general explanation is enough)? This always used be a black box for me.
There is a good wikipedia page on interrupt handlers.
"An interrupt handler, also known as an interrupt service routine (ISR), is a callback subroutine in an operating system or device driver whose execution is triggered by the reception of an interrupt. Interrupt handlers have a multitude of functions, which vary based on the reason the interrupt was generated and the speed at which the Interrupt Handler completes its task."
Basically when a piece of hardware (a hardware interrupt) or some OS task (software interrupt) needs to run it triggers an interrupt. If these interrupts aren't masked (ignored) the OS will stop what it's doing and call some special code to handle this new event.
One good example is reading from a hard drive. The drive is slow and you don't want your OS to wait for the data to come back; you want the OS to go and do other things. So you set up the system so that when the disk has the data requested, it raises an interrupt. In the interrupt service routine for the disk the CPU will take the data that is now ready and will return it to the requester.
ISRs often need to happen quickly as the hardware can have a limited buffer, which will be overwritten by new data if the older data is not pulled off quickly enough.
It's also important to have your ISR complete quickly as while the CPU is servicing one ISR other interrupts will be masked, which means if the CPU can't get to them quickly enough data can be lost.
Minimal 16-bit example
The best way to understand is to make some minimal examples yourself.
First learn how to create a minimal bootloader OS and run it on QEMU and real hardware as I've explained here: https://stackoverflow.com/a/32483545/895245
Now you can run in 16-bit real mode:
movw $handler0, 0x00
mov %cs, 0x02
movw $handler1, 0x04
mov %cs, 0x06
int $0
int $1
hlt
handler0:
/* Do 0. */
iret
handler1:
/* Do 1. */
iret
This would do in order:
Do 0.
Do 1.
hlt: stop executing
Note how the processor looks for the first handler at address 0, and the second one at 4: that is a table of handlers called the IVT, and each entry has 4 bytes.
Minimal example that does some IO to make handlers visible.
Protected mode
Modern operating systems run in the so called protected mode.
The handling has more options in this mode, so it is more complex, but the spirit is the same.
Minimal example
See also
Related question: What does "int 0x80" mean in assembly code?
While the 8086 is executing a program an interrupt breaks the normal sequence of execution of instruction, divert its execution to some other program called interrupt service Routine (ISR). after executing, control return the back again to the main program.
An interrupt is used to cause a temporary halt in the execution of program. Microprocessor responds to the interrupt service routine, which is short program or subroutine that instruct the microprocessor on how to handle the interrupt.

Resources