The following is the (approximate) call graph that's used on x86 when a hardware interrupt is triggered:
do_IRQ() -> handle_irq() -> entering_irq() -> hardirq top-half runs -> exiting_irq() -> irq_exit() -> invoke_softirq() -> do_softirq() -> ... bottom half runs: tasklet/softirq ... -> restore context
Some of the preceding code paths are arch-dependent. Note that the "marking the context as an interrupt" context is really an artifact. The kernel is marked as having entered this context in the entering_irq() function and as having left it once exiting_irq() returns (on x86). But hang on! The exiting_irq() inline function invokes the kernel/softirq.c:irq_exit() function (https://elixir.bootlin.com/linux/v5.4/source/kernel/softirq.c#L403). It's within this routine that the kernel processes, and consumes, all pending softirqs. The basic...