[8u] RFR: nativeInstruction atomicity and correctness of patching
Andrew Haley
aph at redhat.com
Tue Jul 5 18:47:08 UTC 2016
On 05/07/16 18:51, Anton Kozlov wrote:
>
> Hi, All!
>
> Andrew, let me clarify, the concern is in cores cache coherence after executing
> NativeTrampolineCall::set_destination_mt_safe?
>
> From my point of view, since
>
>>>> looking at linux/arch/arm/include/asm/cacheflush.h and I can't see
>>>> anything like that. All I see is that do_cache_op calls
>>>> __flush_cache_user_range, and all that does is ensure that the I and
>>>> D
>>>> caches are coherent.
>>
>
> after any case from
> 1) instruction modification and ICache::flush
> 2) NativeTrampolineCall::set_destination_mt_safe and no data flush
> we came to the same state, when new data is in the dcache of single core.
We don't know that: the data is at least in the local dcache. Some
of the data may be in other caches too.
> NativeTrampolineCall::set_destination_mt_safe is not different from any other
> instruction modification methods.
It's not wrong, but it does a pointless ICache::invalidate_word().
It's pointless because the word at addr() + 8 isn't an instruction,
as far as I can see.
> And we both agree that it's OK to call old destination even after modification.
Yes.
> And eventually data change will land to other core dchache, because there is
> fence on executing site, which you've mentioned.
> set_destination_mt_safe is called when PatchingLock is taken, so it's followed
> by release.
> Executing thread could see only 2 options, since patch is word-aligned single
> word data chage:
> 1) new data. Then everything is OK, just call new destination
> 2) old data. Then execution stream should go to same patching code, c1_Runtime,
> which takes same PatchingLock,
> and therefore executes acquire. Then it should see new data, perform no
> patching, leave patching routine and call new destination.
There is never any problem patching a single word. That can be done
MT-safe if it's a word of data or it is a single patchable
instruction. Both of these are guaranteed: nothing else is.
This is the code that smells bad:
void NativeGeneralJump::replace_mt_safe(address instr_addr, address code_buffer) {
- ShouldNotCallThis();
+ // FIXME NativeCall from patching_epilog nops filling
+ const int bytes_to_copy = NativeCall::instruction_size;
+ const address patching_switch_addr = code_buffer + bytes_to_copy;
+ NativeImmJump* patching_switch = NativeImmJump::from(patching_switch_addr);
+ assert(patching_switch->destination() == patching_switch_addr + NativeInstruction::arm_insn_sz,
+ "switch should be branch to next instr at this point");
+ patching_switch->set_destination(instr_addr + bytes_to_copy);
+ ICache::invalidate_word(patching_switch_addr);
+
+ NativeImmJump* nj = NativeImmJump::from(instr_addr); // checking that it is a jump
+ nj->set_destination(code_buffer);
+ ICache::invalidate_word(instr_addr);
+
}
I don't think this is MT-safe. I don't know why you call
ICache::invalidate_word twice.
As far as I can see the only place where
NativeGeneralJump::replace_mt_safe is used is in the C1 patching code,
but that multiple-instruction patching relies on ordering guarantees
we don't have on ARM. It's hoping that if you patch things in a
certain order they will be seen by other cores in the same order.
Andrew.
More information about the aarch32-port-dev
mailing list