[Devel] [PATCH RHEL9 COMMIT] fs/fuse kio: fix krpc state transition on connect
Konstantin Khorenko
khorenko at virtuozzo.com
Mon Oct 7 20:15:06 MSK 2024
The commit is pushed to "branch-rh9-5.14.0-427.35.1.vz9.76.x-ovz" and will appear at git at bitbucket.org:openvz/vzkernel.git
after rh9-5.14.0-427.35.1.vz9.76.4
------>
commit 06e44a360bf5bb5406a4be1172b26d6c195a2422
Author: Liu Kui <kui.liu at virtuozzo.com>
Date: Tue Oct 1 13:07:39 2024 +0800
fs/fuse kio: fix krpc state transition on connect
Currently krpc would report to userspace to be in work state immediately
after connect, even though kernel rpc may not be in work state. Userspace
will start sending msg immediately after connect while kernel is still
establishing the underlying connection. It's not uncommon that it may take
very long time for a connection to be fully established. When this happens,
userspace may abort the connection due to timeout. However krpc abort needs
to acquire rpc's mutex lock, which could have been held for connection
establishment, thus blocking the userspace evloop process.
To avoid above problem, a proper krpc state transition on connect is
implemented here. It now blocks userspace from transiting to work state
on connect until the underlying kernel rpc transits to work state.
Userspace can abort the krpc connection without being blocked while kernel
rpc is still in establishment phrase.
https://virtuozzo.atlassian.net/browse/VSTOR-93162
Signed-off-by: Liu Kui <kui.liu at virtuozzo.com>
Acked-by: Alexey Kuznetsov <kuznet at virtuozzo.com>
---
fs/fuse/kio/pcs/pcs_krpc.c | 63 +++++++++++++++++++++++++++++++++++++++++-----
fs/fuse/kio/pcs/pcs_krpc.h | 7 ++++++
2 files changed, 64 insertions(+), 6 deletions(-)
diff --git a/fs/fuse/kio/pcs/pcs_krpc.c b/fs/fuse/kio/pcs/pcs_krpc.c
index 8f62f12782e5..3bc0d0376761 100644
--- a/fs/fuse/kio/pcs/pcs_krpc.c
+++ b/fs/fuse/kio/pcs/pcs_krpc.c
@@ -535,6 +535,8 @@ static int pcs_krpc_abort(struct pcs_krpc *krpc)
spin_lock(&krpc->lock);
if (krpc->state != PCS_KRPC_STATE_CONNECTED) {
+ if (krpc->state == PCS_KRPC_STATE_CONNECT)
+ krpc->state = PCS_KRPC_STATE_UNCONN;
spin_unlock(&krpc->lock);
return 0;
}
@@ -784,6 +786,38 @@ int pcs_krpc_update_addr(struct pcs_krpc_set *krpcs, PCS_NODE_ID_T *id,
return 0;
}
+
+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;
+
+ if (msg->rpc) {
+ pcs_rpc_put(msg->rpc);
+ msg->rpc = NULL;
+ }
+
+ spin_lock(&krpc->lock);
+ /* from a stale session, do nothing */
+ if (req->gen != krpc->gen || krpc->state != PCS_KRPC_STATE_CONNECT) {
+ spin_unlock(&krpc->lock);
+ goto out;
+ }
+
+ if (!pcs_if_error(&msg->error)) {
+ krpc->state = PCS_KRPC_STATE_CONNECTED;
+ pollflags = EPOLLOUT;
+ }
+ spin_unlock(&krpc->lock);
+
+ wake_up_poll(&krpc->poll_wait, pollflags);
+
+out:
+ pcs_krpc_put(krpc);
+ kfree(req);
+}
+
/*
* Connect to a pcs_krpc, return a valid fd on success.
*/
@@ -793,6 +827,8 @@ int pcs_krpc_connect(struct pcs_krpc_set *krpcs, PCS_NODE_ID_T *id)
int fd;
struct file *file;
struct pcs_krpc_context *ctx;
+ struct krpc_connect_req *connect_req;
+ struct pcs_msg *msg;
krpc = pcs_krpc_lookup(krpcs, id);
if (!krpc)
@@ -802,18 +838,26 @@ int pcs_krpc_connect(struct pcs_krpc_set *krpcs, PCS_NODE_ID_T *id)
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)
+ if (!ctx) {
+ kfree(connect_req);
return -ENOMEM;
+ }
fd = get_unused_fd_flags(O_CLOEXEC);
if (fd < 0) {
+ kfree(connect_req);
kfree(ctx);
return fd;
}
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);
@@ -825,13 +869,20 @@ int pcs_krpc_connect(struct pcs_krpc_set *krpcs, PCS_NODE_ID_T *id)
spin_lock(&krpc->lock);
ctx->gen = ++krpc->gen;
ctx->krpc = pcs_krpc_get(krpc);
- /*
- * the krpc should always be connected regardless state of
- * underlying RPC
- */
- krpc->state = PCS_KRPC_STATE_CONNECTED;
+ connect_req->gen = krpc->gen;
+ connect_req->krpc = pcs_krpc_get(krpc);
+ krpc->state = PCS_KRPC_STATE_CONNECT;
spin_unlock(&krpc->lock);
+ /* Send a zero-size msg which should be completed after the rpc enters work state */
+ msg = &connect_req->msg;
+ msg->size = 0;
+ msg->timeout = 0;
+ msg->rpc = NULL;
+ msg->done = krpc_connect_done;
+ pcs_clear_error(&msg->error);
+ pcs_rpc_queue(krpc->rpc, msg);
+
return fd;
}
diff --git a/fs/fuse/kio/pcs/pcs_krpc.h b/fs/fuse/kio/pcs/pcs_krpc.h
index 33d758e2503e..23b170c00d7a 100644
--- a/fs/fuse/kio/pcs/pcs_krpc.h
+++ b/fs/fuse/kio/pcs/pcs_krpc.h
@@ -39,11 +39,18 @@ struct pcs_krpc_set {
enum {
PCS_KRPC_STATE_UNCONN,
+ PCS_KRPC_STATE_CONNECT,
PCS_KRPC_STATE_CONNECTED,
PCS_KRPC_STATE_ABORTED,
PCS_KRPC_STATE_DESTROYED,
};
+struct krpc_connect_req {
+ struct pcs_msg msg;
+ struct pcs_krpc *krpc;
+ u32 gen;
+};
+
struct pcs_krpc {
struct hlist_node hlist;
struct list_head link;
More information about the Devel
mailing list