The commit is pushed to "branch-rh10-6.12.0-211.30.1.14.x.vz10-ovz" and will
appear at [email protected]:openvz/vzkernel.git
after rh10-6.12.0-211.30.1.14.1.vz10
------>
commit 4f050875b606b08737d9a7b5a4df5e55bfca6f95
Author: Liu Kui <[email protected]>
Date: Tue Jun 30 11:32:24 2026 +0800
fs/fuse kio: fix kRPC connect issues
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 <[email protected]>
Acked-by: Alexey Kuznetsov <[email protected]>
---
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 3b42fd7d4489c..0930fb4adf125 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 96b4815abf861..803376895cc58 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 {
_______________________________________________
Devel mailing list
[email protected]
https://lists.openvz.org/mailman/listinfo/devel