[Devel] [PATCH VZ10] fs/fuse kio: fix kRPC connect issues

Alexey Kuznetsov kuznet at virtuozzo.com
Tue Jun 30 12:55:13 MSK 2026


Acknowledged

On Tue, Jun 30, 2026 at 11:41 AM Liu Kui <kui.liu at virtuozzo.com> wrote:
>
> There are two problems with kRPC connect:
>
> 1) A kernel RPC connect failure is silently dropped instead of being
>    reported as EPOLLERR. Userspace cannot detect the failure promptly
>    and must fall back on its timeout mechanism to retry. Fix this by
>    reporting EPOLLERR on connect failure.
>
> 2) kRPC connect requests can accumulate when the peer node is dead and
>    the kernel RPC connect blocks for longer than the userspace connect
>    timeout. Each new request restarts the kernel RPC connect with no
>    hold-down time, effectively blocking the RPC work forever and
>    preventing teardown of the fuse connection. Fix this by allowing
>    only one pending connect request at a time: if userspace starts a
>    new kRPC connect after a timeout, it fails immediately, forcing
>    userspace to retry later.
>
> Fixes: https://virtuozzo.atlassian.net/browse/VSTOR-135626
>
> Signed-off-by: Liu Kui <kui.liu at virtuozzo.com>
> ---
>  fs/fuse/kio/pcs/pcs_krpc.c | 74 +++++++++++++++++++++++++-------------
>  fs/fuse/kio/pcs/pcs_krpc.h |  2 ++
>  2 files changed, 51 insertions(+), 25 deletions(-)
>
> diff --git a/fs/fuse/kio/pcs/pcs_krpc.c b/fs/fuse/kio/pcs/pcs_krpc.c
> index 3b42fd7d4489..0930fb4adf12 100644
> --- a/fs/fuse/kio/pcs/pcs_krpc.c
> +++ b/fs/fuse/kio/pcs/pcs_krpc.c
> @@ -956,9 +956,10 @@ static __poll_t pcs_krpc_poll(struct file *file, poll_table *wait)
>
>         spin_lock(&krpc->lock);
>
> -       if (krpc->state == PCS_KRPC_STATE_ABORTED)
> +       if (krpc->state == PCS_KRPC_STATE_ABORTED ||
> +           (krpc->state == PCS_KRPC_STATE_CONNECT && !krpc->connect_req)) {
>                 pollflags |= EPOLLERR;
> -       else if (krpc->state == PCS_KRPC_STATE_CONNECTED) {
> +       } else if (krpc->state == PCS_KRPC_STATE_CONNECTED) {
>                 pollflags |= EPOLLOUT;
>                 if (!list_empty(&krpc->completion_queue))
>                         pollflags |= EPOLLIN;
> @@ -1046,6 +1047,7 @@ int pcs_krpc_create(struct pcs_krpc_set *krpcs, PCS_NODE_ID_T *id,
>         krpc->gen = 0;
>         krpc->state = PCS_KRPC_STATE_UNCONN;
>         krpc->cs = NULL;
> +       krpc->connect_req = NULL;
>
>         krpc->rpc = pcs_rpc_clnt_create(&cc_from_krpcset(krpcs)->eng, id, addr, cs_flags);
>         if (!krpc->rpc) {
> @@ -1089,7 +1091,7 @@ static void krpc_connect_done(struct pcs_msg *msg)
>  {
>         struct krpc_connect_req *req = container_of(msg, struct krpc_connect_req, msg);
>         struct pcs_krpc *krpc = req->krpc;
> -       __poll_t pollflags = EPOLLHUP;
> +       __poll_t pollflags = EPOLLHUP | EPOLLERR;
>
>         if (msg->rpc) {
>                 pcs_rpc_put(msg->rpc);
> @@ -1097,6 +1099,8 @@ static void krpc_connect_done(struct pcs_msg *msg)
>         }
>
>         spin_lock(&krpc->lock);
> +       if (krpc->connect_req == req)
> +               krpc->connect_req = NULL;
>         /* from a stale session, do nothing  */
>         if (req->gen != krpc->gen || krpc->state != PCS_KRPC_STATE_CONNECT) {
>                 spin_unlock(&krpc->lock);
> @@ -1122,7 +1126,7 @@ static void krpc_connect_done(struct pcs_msg *msg)
>  int pcs_krpc_connect(struct pcs_krpc_set *krpcs, PCS_NODE_ID_T *id)
>  {
>         struct pcs_krpc *krpc;
> -       int fd;
> +       int fd, err;
>         struct file *file;
>         struct pcs_krpc_context *ctx;
>         struct krpc_connect_req *connect_req;
> @@ -1132,46 +1136,57 @@ int pcs_krpc_connect(struct pcs_krpc_set *krpcs, PCS_NODE_ID_T *id)
>         if (!krpc)
>                 return -ENXIO;
>
> -       if (krpc->state == PCS_KRPC_STATE_CONNECTED ||
> -               krpc->state == PCS_KRPC_STATE_DESTROYED)
> -               return -EPERM;
> -
>         connect_req = kzalloc(sizeof(*connect_req), GFP_KERNEL);
>         if (!connect_req)
>                 return -ENOMEM;
>
> -       ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
> -       if (!ctx) {
> -               kfree(connect_req);
> -               return -ENOMEM;
> -       }
> -
>         fd = get_unused_fd_flags(O_CLOEXEC);
>         if (fd < 0) {
> -               kfree(connect_req);
> -               kfree(ctx);
> -               return fd;
> +               err = fd;
> +               goto free_req;
>         }
>
> +       ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
> +       if (!ctx) {
> +               err = -ENOMEM;
> +               goto put_fd;
> +       }
> +       /*
> +        * Set up ctx before creating the file so that, if we have to bail out
> +        * below, fput() -> pcs_krpc_release() finds a consistent ctx.  gen 0 is
> +        * never a committed session (commit always pre-increments gen), so
> +        * release() will not mistake it for the live session and abort it.
> +        */
> +       ctx->krpc = pcs_krpc_get(krpc);
> +       ctx->gen = 0;
> +
>         file = anon_inode_getfile("[pcs_krpc]", &pcs_krpc_fops, ctx, 0);
>         if (IS_ERR(file)) {
> -               kfree(connect_req);
> -               kfree(ctx);
> -               put_unused_fd(fd);
> -               fd = PTR_ERR(file);
> -               return fd;
> +               err = PTR_ERR(file);
> +               goto put_ctx;
>         }
>
> -       fd_install(fd, file);
> -
>         spin_lock(&krpc->lock);
> +       if (krpc->state == PCS_KRPC_STATE_CONNECTED ||
> +           krpc->state == PCS_KRPC_STATE_DESTROYED ||
> +           krpc->connect_req) {
> +               spin_unlock(&krpc->lock);
> +               err = -EPERM;
> +               /* fput() drops ctx and its krpc reference via pcs_krpc_release() */
> +               fput(file);
> +               goto put_fd;
> +       }
> +
>         ctx->gen = ++krpc->gen;
> -       ctx->krpc = pcs_krpc_get(krpc);
>         connect_req->gen = krpc->gen;
>         connect_req->krpc = pcs_krpc_get(krpc);
>         krpc->state = PCS_KRPC_STATE_CONNECT;
> +       krpc->connect_req = connect_req;
>         spin_unlock(&krpc->lock);
>
> +       /* publish the fd only after the connect is committed */
> +       fd_install(fd, file);
> +
>         /* Send a zero-size msg which should be completed after the rpc enters work state */
>         msg = &connect_req->msg;
>         msg->size = 0;
> @@ -1183,6 +1198,15 @@ int pcs_krpc_connect(struct pcs_krpc_set *krpcs, PCS_NODE_ID_T *id)
>         pcs_rpc_queue(krpc->rpc, msg);
>
>         return fd;
> +
> +put_ctx:
> +       pcs_krpc_put(krpc);
> +       kfree(ctx);
> +put_fd:
> +       put_unused_fd(fd);
> +free_req:
> +       kfree(connect_req);
> +       return err;
>  }
>
>  static void __pcs_krpc_destroy(struct pcs_krpc *krpc)
> diff --git a/fs/fuse/kio/pcs/pcs_krpc.h b/fs/fuse/kio/pcs/pcs_krpc.h
> index 96b4815abf86..803376895cc5 100644
> --- a/fs/fuse/kio/pcs/pcs_krpc.h
> +++ b/fs/fuse/kio/pcs/pcs_krpc.h
> @@ -84,6 +84,8 @@ struct pcs_krpc {
>         /** Wait queue head for poll */
>         wait_queue_head_t               poll_wait;
>         struct pcs_cs                   *cs;
> +
> +       struct krpc_connect_req *connect_req;
>  };
>
>  struct pcs_krpc_context {
> --
> 2.50.1 (Apple Git-155)



More information about the Devel mailing list