On 24.02.20 23:26, Peter Xu wrote:
> On Fri, Feb 21, 2020 at 05:42:01PM +0100, David Hildenbrand wrote:
>
> [...]
>
>> @@ -3160,7 +3160,13 @@ static int ram_load_postcopy(QEMUFile *f)
>> break;
>> }
>>
>> - if (!offset_in_ramblock(block, addr)) {
>> + /*
>> + * Relying on used_length is racy and can result in false
>> positives.
>> + * We might place pages beyond used_length in case RAM was
>> shrunk
>> + * while in postcopy, which is fine - trying to place via
>> + * UFFDIO_COPY/UFFDIO_ZEROPAGE will never segfault.
>> + */
>> + if (!block->host || addr >= block->postcopy_length) {
>
> I'm thinking whether we can even avoid the -ENOENT failure of
> UFFDIO_COPY. With the postcopy_length you introduced, I think it's
> the case when addr >= used_length && addr < postcopy_length, right?
> Can we skip those?
1. Recall that any check against used_length is completely racy. So no,
it's not that easy. There is no trusting on used_length at all. It
should never be access from asynchronous postcopy code.
2. There is one theoretical case with resizable allocations: Assume you
first shrink and then grow again. You would have some addr < used_length
where you cannot (and don't want to) place.
Note: Before discovering the nice -ENOENT handling, I had a second
variable postcopy_place_length stored in RAM blocks that would be
- Initialized to postcopy_length
- Synchronized by a mutex
- Changed inside the resize callback on any resizes to
-- postcopy_place_length = min(postcopy_place_length, newsize)
But TBH, I find using -ENOENT much more elegant. It was designed to
handle mmap changes like this.
--
Thanks,
David / dhildenb