Re: [RFC PATCH 1/5] x86: introduce preemption disable prefix

From: Nadav Amit
Date: Thu Oct 18 2018 - 21:08:32 EST


at 10:00 AM, Andy Lutomirski <luto@xxxxxxxxxxxxxx> wrote:

>
>
>> On Oct 18, 2018, at 9:47 AM, Nadav Amit <namit@xxxxxxxxxx> wrote:
>>
>> at 8:51 PM, Andy Lutomirski <luto@xxxxxxxxxxxxxx> wrote:
>>
>>>> On Wed, Oct 17, 2018 at 8:12 PM Nadav Amit <namit@xxxxxxxxxx> wrote:
>>>> at 6:22 PM, Andy Lutomirski <luto@xxxxxxxxxxxxxx> wrote:
>>>>
>>>>>> On Oct 17, 2018, at 5:54 PM, Nadav Amit <namit@xxxxxxxxxx> wrote:
>>>>>>
>>>>>> It is sometimes beneficial to prevent preemption for very few
>>>>>> instructions, or prevent preemption for some instructions that precede
>>>>>> a branch (this latter case will be introduced in the next patches).
>>>>>>
>>>>>> To provide such functionality on x86-64, we use an empty REX-prefix
>>>>>> (opcode 0x40) as an indication that preemption is disabled for the
>>>>>> following instruction.
>>>>>
>>>>> Nifty!
>>>>>
>>>>> That being said, I think you have a few bugs. First, you canât just ignore
>>>>> a rescheduling interrupt, as you introduce unbounded latency when this
>>>>> happens â youâre effectively emulating preempt_enable_no_resched(), which
>>>>> is not a drop-in replacement for preempt_enable(). To fix this, you may
>>>>> need to jump to a slow-path trampoline that calls schedule() at the end or
>>>>> consider rewinding one instruction instead. Or use TF, which is only a
>>>>> little bit terrifyingâ
>>>>
>>>> Yes, I didnât pay enough attention here. For my use-case, I think that the
>>>> easiest solution would be to make synchronize_sched() ignore preemptions
>>>> that happen while the prefix is detected. It would slightly change the
>>>> meaning of the prefix.
>>
>> So thinking about it further, rewinding the instruction seems the easiest
>> and most robust solution. Iâll do it.
>>
>>>>> You also arenât accounting for the case where you get an exception that
>>>>> is, in turn, preempted.
>>>>
>>>> Hmm.. Can you give me an example for such an exception in my use-case? I
>>>> cannot think of an exception that might be preempted (assuming #BP, #MC
>>>> cannot be preempted).
>>>
>>> Look for cond_local_irq_enable().
>>
>> I looked at it. Yet, I still donât see how exceptions might happen in my
>> use-case, but having said that - this can be fixed too.
>
> Iâm not totally certain thereâs a case that matters. But itâs worth checking

I am still checking. But, I wanted to ask you whether the existing code is
correct, since it seems to me that others do the same mistake I did, unless
I donât understand the code.

Consider for example do_int3(), and see my inlined comments:

dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code)
{
...
ist_enter(regs); // => preempt_disable()
cond_local_irq_enable(regs); // => assume it enables IRQs

...
// resched irq can be delivered here. It will not caused rescheduling
// since preemption is disabled

cond_local_irq_disable(regs); // => assume it disables IRQs
ist_exit(regs); // => preempt_enable_no_resched()
}

At this point resched will not happen for unbounded length of time (unless
there is another point when exiting the trap handler that checks if
preemption should take place).

Another example is __BPF_PROG_RUN_ARRAY(), which also uses
preempt_enable_no_resched().

Am I missing something?

Thanks,
Nadav