accel/tcg: Handle false negative lookup in page_check_range

As in page_get_flags, we need to try again with the mmap
lock held if we fail a page lookup.

Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2022-12-24 06:37:56 -08:00
parent 177a8cb83b
commit e630c0126c

View file

@ -525,6 +525,8 @@ void page_set_flags(target_ulong start, target_ulong end, int flags)
int page_check_range(target_ulong start, target_ulong len, int flags) int page_check_range(target_ulong start, target_ulong len, int flags)
{ {
target_ulong last; target_ulong last;
int locked; /* tri-state: =0: unlocked, +1: global, -1: local */
int ret;
if (len == 0) { if (len == 0) {
return 0; /* trivial length */ return 0; /* trivial length */
@ -535,42 +537,67 @@ int page_check_range(target_ulong start, target_ulong len, int flags)
return -1; /* wrap around */ return -1; /* wrap around */
} }
locked = have_mmap_lock();
while (true) { while (true) {
PageFlagsNode *p = pageflags_find(start, last); PageFlagsNode *p = pageflags_find(start, last);
int missing; int missing;
if (!p) { if (!p) {
return -1; /* entire region invalid */ if (!locked) {
/*
* Lockless lookups have false negatives.
* Retry with the lock held.
*/
mmap_lock();
locked = -1;
p = pageflags_find(start, last);
}
if (!p) {
ret = -1; /* entire region invalid */
break;
}
} }
if (start < p->itree.start) { if (start < p->itree.start) {
return -1; /* initial bytes invalid */ ret = -1; /* initial bytes invalid */
break;
} }
missing = flags & ~p->flags; missing = flags & ~p->flags;
if (missing & PAGE_READ) { if (missing & PAGE_READ) {
return -1; /* page not readable */ ret = -1; /* page not readable */
break;
} }
if (missing & PAGE_WRITE) { if (missing & PAGE_WRITE) {
if (!(p->flags & PAGE_WRITE_ORG)) { if (!(p->flags & PAGE_WRITE_ORG)) {
return -1; /* page not writable */ ret = -1; /* page not writable */
break;
} }
/* Asking about writable, but has been protected: undo. */ /* Asking about writable, but has been protected: undo. */
if (!page_unprotect(start, 0)) { if (!page_unprotect(start, 0)) {
return -1; ret = -1;
break;
} }
/* TODO: page_unprotect should take a range, not a single page. */ /* TODO: page_unprotect should take a range, not a single page. */
if (last - start < TARGET_PAGE_SIZE) { if (last - start < TARGET_PAGE_SIZE) {
return 0; /* ok */ ret = 0; /* ok */
break;
} }
start += TARGET_PAGE_SIZE; start += TARGET_PAGE_SIZE;
continue; continue;
} }
if (last <= p->itree.last) { if (last <= p->itree.last) {
return 0; /* ok */ ret = 0; /* ok */
break;
} }
start = p->itree.last + 1; start = p->itree.last + 1;
} }
/* Release the lock if acquired locally. */
if (locked < 0) {
mmap_unlock();
}
return ret;
} }
void page_protect(tb_page_addr_t address) void page_protect(tb_page_addr_t address)