diff options
author | Richard Henderson <richard.henderson@linaro.org> | 2022-12-24 06:37:56 -0800 |
---|---|---|
committer | Richard Henderson <richard.henderson@linaro.org> | 2023-01-05 11:41:29 -0800 |
commit | e630c0126c561b7db26790e8288c4fe9b61ae8dc (patch) | |
tree | fadc0e69ac1747f8817af9f1852945d24323810d /accel | |
parent | 177a8cb83bcf2c8eb013b997173a25339a7ce86f (diff) | |
download | qemu-e630c0126c561b7db26790e8288c4fe9b61ae8dc.zip qemu-e630c0126c561b7db26790e8288c4fe9b61ae8dc.tar.gz qemu-e630c0126c561b7db26790e8288c4fe9b61ae8dc.tar.bz2 |
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>
Diffstat (limited to 'accel')
-rw-r--r-- | accel/tcg/user-exec.c | 41 |
1 files changed, 34 insertions, 7 deletions
diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 2c5c10d..a8eb63a 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -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) { target_ulong last; + int locked; /* tri-state: =0: unlocked, +1: global, -1: local */ + int ret; if (len == 0) { return 0; /* trivial length */ @@ -535,42 +537,67 @@ int page_check_range(target_ulong start, target_ulong len, int flags) return -1; /* wrap around */ } + locked = have_mmap_lock(); while (true) { PageFlagsNode *p = pageflags_find(start, last); int missing; 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) { - return -1; /* initial bytes invalid */ + ret = -1; /* initial bytes invalid */ + break; } missing = flags & ~p->flags; if (missing & PAGE_READ) { - return -1; /* page not readable */ + ret = -1; /* page not readable */ + break; } if (missing & PAGE_WRITE) { 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. */ if (!page_unprotect(start, 0)) { - return -1; + ret = -1; + break; } /* TODO: page_unprotect should take a range, not a single page. */ if (last - start < TARGET_PAGE_SIZE) { - return 0; /* ok */ + ret = 0; /* ok */ + break; } start += TARGET_PAGE_SIZE; continue; } if (last <= p->itree.last) { - return 0; /* ok */ + ret = 0; /* ok */ + break; } 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) |