3

From Linux kernel code,I can see the preempt_enable() and preempt_disable() are nothing except just barrier():

#define preempt_disable()       barrier()

#define preempt_enable()        barrier()

I can't understand it. Why just a barrier() is enough for disabling or enabling the preemption?

red0ct
  • 4,840
  • 3
  • 17
  • 44
Nan Xiao
  • 16,671
  • 18
  • 103
  • 164

2 Answers2

1

Because you are not using a preemptible Kernel. A user space process is preemptible though.

Check if CONFIG_PREEMPT_VOLUNTARY is set in your kernel config.

See What is preemption / What is a preemtible kernel? What is it good for?

The barrier was added since https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable/+/386afc91144b36b42117b0092893f15bc8798a80%5E!/

wenjianhn
  • 438
  • 3
  • 7
0

In Kernel v4.3, the right definition for preempt_enable is here:

#define preempt_enable() \
do { \
    barrier(); \
    if (unlikely(preempt_count_dec_and_test())) \
           __preempt_schedule(); \
} while (0) 

and similarly for preempt_disable is here:

#define preempt_disable() \
do { \
     preempt_count_inc(); \
     barrier(); \
} while (0)

preempt_enable inserts optimization barrier before preemption is enabled and preempt_disable inserts barrier after the preemption counter is incremented. However as per comment, when there is no preemption involved but just barriers to protect preempted region.

EDIT: In UP and non-preempt respectively, the spinlocks and preemption disable/enable points are stubbed out entirely, because there is no regular code that can ever hit the kind of concurrency they are meant to protect against.

However, while there is no regular code that can cause scheduling, we do end up having some exceptional (literally!) code that can do so, and that we need to make sure does not ever get moved into the critical region by the compiler.

In particular, get_user() and put_user() is generally implemented as inline asm statements (even if the inline asm may then make a call instruction to call out-of-line), and can obviously cause a page fault and IO as a result. If that inline asm has been scheduled into the middle of a preemption-safe (or spinlock-protected) code region, we obviously lose.

Now, admittedly this is very unlikely to actually ever happen, and we've not seen examples of actual bugs related to this. But partly exactly because it's so hard to trigger and the resulting bug is so subtle, we should be extra careful to get this right.

So make sure that even when preemption is disabled, and we don't have to generate any actual code to explicitly tell the system that we are in a preemption-disabled region, we need to at least tell the compiler not to move things around the critical region. Source

Sunil Bojanapally
  • 12,528
  • 4
  • 33
  • 46
  • [preempt_disable](http://lxr.free-electrons.com/source/include/linux/preempt.h#L163)'s implementation requires `CONFIG_PREEMPT_COUNT` is on, [This](http://lxr.free-electrons.com/source/include/linux/preempt.h#L236)(`#define preempt_disable() barrier()`) is the version when `CONFIG_PREEMPT_COUNT` is off. – Nan Xiao Nov 23 '15 at 07:27
  • To add to the above mentioned description, kernel can be compiled in two ways, namely preemptive mode and non preemptive mode. (I'm not taking about userspace preemption which is always there). Kconfig [source] (http://lxr.free-electrons.com/source/kernel/Kconfig.preempt) that controls the enable/disable of preemption. When we enable CONFIG_PREEMPT, CONFIG_PREEMPT_COUNT is automatically enabled (refer kconfig.preempt). When PREEMPT is disabled, the calles are replaced by barrier() which just to flush writes to DRAM. – Nithin Dec 01 '15 at 04:16