RE: [PATCH 01/29] iov_iter: Switch to using a table of operations
From: David Laight <hidden>
Date: 2020-11-22 22:35:46
Also in:
linux-fsdevel, lkml
From: Linus Torvalds
Sent: 22 November 2020 19:22 Subject: Re: [PATCH 01/29] iov_iter: Switch to using a table of operations On Sun, Nov 22, 2020 at 5:33 AM David Howells [off-list ref] wrote:quoted
I don't know enough about how spectre v2 works to say if this would be a problem for the ops-table approach, but wouldn't it also affect the chain of conditional branches that we currently use, since it's branch-prediction based?No, regular conditional branches aren't a problem. Yes, they may mispredict, but outside of a few very rare cases that we handle specially, that's not an issue. Why? Because they always mispredict to one or the other side, so the code flow may be mis-predicted, but it is fairly controlled. In contrast, an indirect jump can mispredict the target, and branch _anywhere_, and the attack vectors can poison the BTB (branch target buffer), so our mitigation for that is that every single indirect branch isn't predicted at all (using "retpoline"). So a conditional branch takes zero cycles when predicted (and most will predict quite well). And as David Laight pointed out a compiler can also turn a series of conditional branches into a tree, means that N conditional branches basically only needs log2(N) conditionals executed.
The compiler can convert a switch statement into a branch tree. But I don't think it can convert the 'if chain' in the current code to one. There is also the problem that some x86 cpu can't predict branches if too many happen in the same cache line (or similar).
In contrast, with retpoline in place, an indirect branch will basically always take something like 25-30 cycles, because it always mispredicts.
I also wonder if a retpoline also trashes the return stack optimisation. (If that is ever really a significant gain for real functions.) ...
So this is not in any way "indirect branches are bad". It's more of a "indirect branches really aren't necessarily better than a couple of conditionals, and _may_ be much worse".
Even without retpolines, the jump table is likely to a data-cache miss (and maybe a TLB miss) unless you are running hot-cache. That is probably an extra cache miss on top of the I-cache ones. Even worse if you end up with the jump table near the code since the data cache line and TLB might never be shared. So a very short switch statement is likely to be better as conditional jumps anyway.
For example, look at this gcc bugzilla:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86952
which basically is about the compiler generating a jump table (is a
single indirect branch) vs a series of conditional branches. With
retpoline, the cross-over point is basically when you need to have
over 10 conditional branches - and because of the log2(N) behavior,
that's around a thousand cases!That was a hot-cache test. Cold-cache is likely to favour the retpoline a little sooner. (And the retpoline (probbaly) won't be (much) worse than the mid-predicted indirect jump. I do wonder how much of the kernel actually runs hot-cache? Except for parts that explicitly run things in bursts. David - Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)