mirror of https://gitlab.com/qemu-project/qemu
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:
parent
177a8cb83b
commit
e630c0126c
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue