Re: [PATCH] fs/ceph/mds_client: drop mdsc->mutex before decoding the MDS reply

Xiubo Li <[email protected]> Mon, 13 Jul 2026 10:39:33 +0800
Newsgroups org.kernel.vger.ceph-devel,org.kernel.vger.linux-kernel
Message-ID <CAOuZWzw_=jQgM=T0P+wTHqNDm415gOqX4=SnCAx9eY8G3ZSzxw@mail.gmail.com>
Hi Max

Nice catch and nice improvement. LGTM.

Reviewed-by: Xiubo Li <[email protected]>

Thanks
- Xiubo

Max Kellermann <[email protected]> 于2026年7月9日周四 04:40写道:
>
> handle_reply() held `mdsc->mutex` across parse_reply_info(),
> i.e. across the full decode of the reply message.  For large replies
> (a big readdir allocates and parses many dir_entries), this can take a
> while and blocks ceph_mdsc_submit_request() calls meanwhile.
>
> The decode does not need `mdsc->mutex`: parse_reply_info() mostly
> fills the request's `r_reply_info`.  Create replies may also add
> delegated inode numbers to the session xarray, but that xarray is
> protected by its own lock and is not serialized by `mdsc->mutex`
> today.  By the time we reach parse_reply_info(), all
> `mdsc->mutex`-protected state has already been updated under the lock
> (the request has either been unregistered (safe reply) or added to the
> session's unsafe list (unsafe reply)) and the request is pinned by the
> reference taken in lookup_get_request().
>
> Drop `mdsc->mutex` before calling parse_reply_info() so reply decoding
> no longer blocks request submission.  This only widens the existing
> unlocked window that already covers the heavier ceph_fill_trace() /
> ceph_readdir_prepopulate() processing, so no new races are introduced.
>
> Signed-off-by: Max Kellermann <[email protected]>
> ---
>  fs/ceph/mds_client.c | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
> index 853bf698b356..bb8570d31208 100644
> --- a/fs/ceph/mds_client.c
> +++ b/fs/ceph/mds_client.c
> @@ -4089,13 +4089,19 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
>                 list_add_tail(&req->r_unsafe_item, &req->r_session->s_unsafe);
>         }
>
> +       /*
> +        * Now that all mutex-protected state has been updated above
> +        * (the request has been unregistered or added to the
> +        * session's unsafe list), we can unlock it.
> +        */
> +       mutex_unlock(&mdsc->mutex);
> +
>         doutc(cl, "tid %lld result %d\n", tid, result);
>         if (test_bit(CEPHFS_FEATURE_REPLY_ENCODING, &session->s_features))
>                 err = parse_reply_info(session, msg, req, (u64)-1);
>         else
>                 err = parse_reply_info(session, msg, req,
>                                        session->s_con.peer_features);
> -       mutex_unlock(&mdsc->mutex);
>
>         /* Must find target inode outside of mutexes to avoid deadlocks */
>         rinfo = &req->r_reply_info;
> --
> 2.47.3
>
>