I'm reading "Understanding the Linux Kernel, 3rd Edition", and in Chapter 5, Section "Kernel Preemption", it says:
All process switches are performed by themacro. In both preemptive
and nonpreemptive kernels, a process switch occurs when a process has finished
some thread of kernel activity and the scheduler is invoked. However, in nonpreemptive kernels, the current process cannot be replaced unless it is about to switch
to User Mode.
In a non-preemptive kernel,
schedule() is called when returning to userspace (and wherever a system call blocks, also on the idle task).
In a preemptive kernel,
schedule() is also called when returning from any interrupt, and also in a few other places, e.g. on
mutex_unlock() slow path, on certain conditions while receiving network packets, ...
As an example, imagine a process A which issues a syscall which is interrupted by a device-generated interrupt, that is then interrupted by a timer interrupt:
process A userspace → process A kernelspace → device ISR → timer ISR syscall device IRQ timer IRQ
When the timer ISR ends, it returns to another ISR, that then returns to kernelspace, which then returns to userspace. A preemptive kernel checks if it needs to reschedule processes at every return. A non-preemptive kernel only does that check when returning to userspace.