Currently, PowerPC interrupts are handled as follows:
1) The CPU_INTERRUPT_HARD bit of cs->interrupt_request gates all
interrupts;
2) The bits of env->pending_interrupts identify which particular
interrupt is raised;
3) ppc_set_irq can be used to set/clear env->pending_interrupt bit and
CPU_INTERRUPT_HARD, but some places access env->pending_interrupt
directly;
4) ppc_cpu_exec_interrupt is called by cpu_handle_interrupt when
cs->interrupt_request indicates that there is some interrupt pending.
This method checks CPU_INTERRUPT_HARD and calls ppc_hw_interrupt. If
env->pending_interrupt is zero after this call, CPU_INTERRUPT_HARD
will be cleared.
5) ppc_hw_interrupt checks if there is any unmasked interrupt and calls
powerpc_excp with the appropriate POWERPC_EXCP_* value. The method
will also reset the corresponding bit in env->pending_interrupt for
interrupts that clear on delivery.
If all pending interrupts are masked, CPU_INTERRUPT_HARD will be set,
but ppc_hw_interrupt will not deliver or clear the interrupt, so
CPU_INTERRUPT_HARD will not be reset by ppc_cpu_exec_interrupt. With
that, cs->has_work keeps returning true, creating a loop that acquires
and release qemu_mutex_lock_iothread, causing the poor performance
reported in [1].
This patch series attempts to rework the PowerPC interrupt code to set
CPU_INTERRUPT_HARD only when there are unmasked interrupts. Then
cs->has_work can be simplified to a check of CPU_INTERRUPT_HARD, so it
also only returns true when at least one interrupt can be delivered.
To achieve that, we are basically following Alex Bannée's suggestion[2]
in the original thread: the interrupt masking logic will be factored
out of ppc_hw_interrupt in a new method, ppc_pending_interrupts. This
method is then used to decide if CPU_INTERRUPT_HARD should be set or
cleared after changes to MSR, LPCR, env->pending_interrupts, and
power-management instructions.
We used [3] to check for regressions at each patch in this series. After
patch 12, booting a powernv machine with a newer skiboot with "-smp 4"
goes from 1m09s to 20.79s.