On 06/25/2018 01:31 PM, Emilio G. Cota wrote:
> Commit 0b5c91f ("translate-all: use per-page locking in !user-mode",
> 2018-06-15) introduced per-page locking. It assumed that the physical
> pages corresponding to a TB (at most two pages) are always distinct,
> which is wrong. For instance, an xtensa test provided by Max Filippov
> is broken by the commit, since the test maps two virtual pages
> to the same physical page:
> 
>       virt1: 7fff, virt2: 8000
>       phys1 6000fff, phys2 6000000
> 
> Fix it by removing the assumption from page_lock_pair.
> If the two physical page addresses are equal, we only lock
> the PageDesc once. Note that the two callers of page_lock_pair,
> namely page_unlock_tb and tb_link_page, are also updated so that
> we do not try to unlock the same PageDesc twice.
> 
> Fixes: 0b5c91f74f3c83a36f37740969df8c775c997e69
> Reported-by: Max Filippov <jcmvb...@gmail.com>
> Signed-off-by: Emilio G. Cota <c...@braap.org>

$ make check-tcg (qemu:debian-xtensa-cross)

Tested-by: Philippe Mathieu-Daudé <f4...@amsat.org>

> ---
>  accel/tcg/translate-all.c | 32 +++++++++++++++++++++++++-------
>  1 file changed, 25 insertions(+), 7 deletions(-)
> 
> diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
> index f0c3fd4..8e0203e 100644
> --- a/accel/tcg/translate-all.c
> +++ b/accel/tcg/translate-all.c
> @@ -669,9 +669,15 @@ static inline void page_lock_tb(const TranslationBlock 
> *tb)
>  
>  static inline void page_unlock_tb(const TranslationBlock *tb)
>  {
> -    page_unlock(page_find(tb->page_addr[0] >> TARGET_PAGE_BITS));
> +    PageDesc *p1 = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS);
> +
> +    page_unlock(p1);
>      if (unlikely(tb->page_addr[1] != -1)) {
> -        page_unlock(page_find(tb->page_addr[1] >> TARGET_PAGE_BITS));
> +        PageDesc *p2 = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS);
> +
> +        if (p2 != p1) {
> +            page_unlock(p2);
> +        }
>      }
>  }
>  
> @@ -850,22 +856,34 @@ static void page_lock_pair(PageDesc **ret_p1, 
> tb_page_addr_t phys1,
>                             PageDesc **ret_p2, tb_page_addr_t phys2, int 
> alloc)
>  {
>      PageDesc *p1, *p2;
> +    tb_page_addr_t page1;
> +    tb_page_addr_t page2;
>  
>      assert_memory_lock();
> -    g_assert(phys1 != -1 && phys1 != phys2);
> -    p1 = page_find_alloc(phys1 >> TARGET_PAGE_BITS, alloc);
> +    g_assert(phys1 != -1);
> +
> +    page1 = phys1 >> TARGET_PAGE_BITS;
> +    page2 = phys2 >> TARGET_PAGE_BITS;
> +
> +    p1 = page_find_alloc(page1, alloc);
>      if (ret_p1) {
>          *ret_p1 = p1;
>      }
>      if (likely(phys2 == -1)) {
>          page_lock(p1);
>          return;
> +    } else if (page1 == page2) {
> +        page_lock(p1);
> +        if (ret_p2) {
> +            *ret_p2 = p1;
> +        }
> +        return;
>      }
> -    p2 = page_find_alloc(phys2 >> TARGET_PAGE_BITS, alloc);
> +    p2 = page_find_alloc(page2, alloc);
>      if (ret_p2) {
>          *ret_p2 = p2;
>      }
> -    if (phys1 < phys2) {
> +    if (page1 < page2) {
>          page_lock(p1);
>          page_lock(p2);
>      } else {
> @@ -1623,7 +1641,7 @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t 
> phys_pc,
>          tb = existing_tb;
>      }
>  
> -    if (p2) {
> +    if (p2 && p2 != p) {
>          page_unlock(p2);
>      }
>      page_unlock(p);
> 

Reply via email to