All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alice Ryhl <aliceryhl@google.com>
To: Bo Zhang <zhangbo0325@gmail.com>
Cc: gregkh@linuxfoundation.org, cmllamas@google.com,
	arve@android.com,  tkjos@android.com, christian@brauner.io,
	surenb@google.com, baohua@kernel.org,  zhanghongru06@gmail.com,
	linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH 0/1] binder: switch alloc->mutex back to spinlock
Date: Sat, 8 Aug 2026 13:49:46 +0000	[thread overview]
Message-ID: <ancz-vd9v5P5kRJq@google.com> (raw)
In-Reply-To: <20260806142301.2144536-1-zhangbo56@xiaomi.com>

On Thu, Aug 06, 2026 at 10:23:01PM +0800, Bo Zhang wrote:
> On Thu, Aug 06, 2026 at 09:01:19AM +0000, Alice Ryhl wrote:
> > This will not work due to the following race condition between T1 and T2:
> > 
> > T1 binder_alloc_free_page() removes the page from alloc->pages
> > T2 binder_alloc_new_buf_locked() runs
> > T2 binder_install_single_page() gets -EBUSY
> > T2 binder_page_lookup() looks up the page in the vma
> > T1 binder_alloc_free_page() calls zap_vma_range()
> > 
> > In this scenario, T2 looks up a page that it expects to be "pinned" and
> > not removable by the shrinker, but the shrinker zaps it anyway.
> > 
> > Today this cannot happen because binder_alloc_new_buf_locked() runs
> > under the mutex, which ensures that zap_vma_range() finishes removing
> > the page before binder_install_single_page() runs.
> 
> Hi Alice,
> 
> Thanks for pointing out this race. You're right that simply moving
> spin_unlock() before zap_vma_range() opens a window where the install
> side could GUP the old page that is about to be freed.
> 
> However, I think it could be resolved without keeping the lock across
> zap_vma_range(). The solution might be:
> 
> The install side only calls vm_insert_page() when pages[index] == NULL.
> And the shrinker sets pages[index] = NULL before zapping. So if
> vm_insert_page() returns -EBUSY, we can check pages[index] to determine
> which scenario we're in:
> 
>   1. pages[index] != NULL: Another installer won the race. It's safe to
>      GUP the page, which is the normal concurrent install case.
> 
>   2. pages[index] == NULL: The shrinker cleared it but hasn't zapped
>      the PTE yet. We must NOT GUP the old page. Return -EAGAIN and
>      let the caller retry after the shrinker finishes.
> 
> Case 2 is safe because: the install side only attempts installation
> when pages[index] == NULL, so if EBUSY occurs with pages[index] == NULL,
> it must be a PTE left over from the page that the shrinker is currently
> reclaiming. After the shrinker zaps and frees, the retry will find
> pages[index] still NULL, call vm_insert_page() again, and succeed since
> the PTE is now clear.
> 
> Case 1 cannot race with the shrinker because: if pages[index] != NULL
> at the time of the EBUSY check, the page was just installed by another
> thread and is not on the LRU freelist, so the shrinker won't touch it.
> 
> The change would look like:
> 
>   case -EBUSY:
>       binder_free_page(page);
>       if (!binder_get_installed_page(alloc, index)) {
>           /* shrinker is reclaiming, retry */
>           ret = -EAGAIN;
>           break;
>       }
>       /* normal concurrent install, look up the winner's page */
>       page = binder_page_lookup(alloc, addr);
>       ...
> 
> And the caller retries on -EAGAIN with a cond_resched().
> 
> This also should not introduce any performance regression since the
> -EAGAIN retry only triggers in the rare case where EBUSY coincides with
> an active shrinker reclaim on the same page index, a window that lasts
> only for the duration of a single zap_vma_range() call.
> 
> Does this approach look reasonable to you?

No, this will deadlock if the thread installing the page is SCHED_FIFO.

Waiting for the shrinker by repeatedly checking whether the shrinker is
done in a loop does not work.

As an aside, your solution also adds similar waiting for concurrent
installers. If the successful installer gets preempted between
binder_page_insert() and binder_set_installed_page(), then your code
will also repeatedly retry until said thread gets scheduled in again. So
there is a similar deadlock if the inserter that failed the race is
SCHED_FIFO.

Alice

  reply	other threads:[~2026-08-08 13:49 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-05 15:27 [RFC PATCH 0/1] binder: switch alloc->mutex back to spinlock Bo Zhang
2026-08-05 15:27 ` [RFC PATCH 1/1] " Bo Zhang
2026-08-06  9:01 ` [RFC PATCH 0/1] " Alice Ryhl
2026-08-06 14:23   ` Bo Zhang
2026-08-08 13:49     ` Alice Ryhl [this message]
2026-08-10 10:22       ` Bo Zhang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ancz-vd9v5P5kRJq@google.com \
    --to=aliceryhl@google.com \
    --cc=arve@android.com \
    --cc=baohua@kernel.org \
    --cc=christian@brauner.io \
    --cc=cmllamas@google.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=surenb@google.com \
    --cc=tkjos@android.com \
    --cc=zhangbo0325@gmail.com \
    --cc=zhanghongru06@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.