Nan Xiao
Nan Xiao

Reputation: 17477

Why a "barrier()" is enough for disabling or enabling the preemption?

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?

Upvotes: 3

Views: 1246

Answers (2)

wenjianhn
wenjianhn

Reputation: 448

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!/

Upvotes: 1

Sunil Bojanapally
Sunil Bojanapally

Reputation: 12658

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

Upvotes: 0

Related Questions