[PATCH 5/9] handshake: Add a kunit test for the completion gate
Chuck Lever
cel at kernel.org
Fri Jun 5 10:34:39 PDT 2026
From: Chuck Lever <chuck.lever at oracle.com>
The DONE netlink op runs with parallel_ops, so duplicate or
concurrent DONE downcalls for the same socket can both reach
the same handshake_req. The split try/finish pair guarantees
that exactly one caller drives completion to the consumer's
hp_done callback; this invariant lets the session-tag publish
step transfer ownership safely.
Exercise the gate's idempotency with a kunit case: submit and
accept a request, call handshake_try_complete() twice in
sequence and assert the second returns false, drive
handshake_finish_complete() once, then call handshake_complete()
again and confirm hp_done fired exactly once across the
sequence.
Signed-off-by: Chuck Lever <chuck.lever at oracle.com>
---
net/handshake/handshake-test.c | 72 ++++++++++++++++++++++++++++++++++++++++++
net/handshake/request.c | 2 ++
2 files changed, 74 insertions(+)
diff --git a/net/handshake/handshake-test.c b/net/handshake/handshake-test.c
index 55442b2f518a..c172b7a9750f 100644
--- a/net/handshake/handshake-test.c
+++ b/net/handshake/handshake-test.c
@@ -430,6 +430,74 @@ static void handshake_req_cancel_test3(struct kunit *test)
fput(filp);
}
+static int handshake_try_complete_done_count;
+
+static void test_done_count_func(struct handshake_req *req, unsigned int status,
+ struct genl_info *info)
+{
+ handshake_try_complete_done_count++;
+}
+
+static struct handshake_proto handshake_req_alloc_proto_count = {
+ .hp_handler_class = HANDSHAKE_HANDLER_CLASS_TLSHD,
+ .hp_accept = test_accept_func,
+ .hp_done = test_done_count_func,
+};
+
+static void handshake_try_complete_test1(struct kunit *test)
+{
+ struct handshake_req *req, *next;
+ struct handshake_net *hn;
+ struct socket *sock;
+ struct file *filp;
+ struct net *net;
+ bool first, second;
+ int err;
+
+ /* Arrange */
+ handshake_try_complete_done_count = 0;
+
+ req = handshake_req_alloc(&handshake_req_alloc_proto_count, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, req);
+
+ err = __sock_create(&init_net, PF_INET, SOCK_STREAM, IPPROTO_TCP,
+ &sock, 1);
+ KUNIT_ASSERT_EQ(test, err, 0);
+
+ filp = sock_alloc_file(sock, O_NONBLOCK, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filp);
+ KUNIT_ASSERT_NOT_NULL(test, sock->sk);
+ sock->file = filp;
+
+ err = handshake_req_submit(sock, req, GFP_KERNEL);
+ KUNIT_ASSERT_EQ(test, err, 0);
+
+ net = sock_net(sock->sk);
+ hn = handshake_pernet(net);
+ KUNIT_ASSERT_NOT_NULL(test, hn);
+
+ /* Pretend to accept this request */
+ next = handshake_req_next(hn, HANDSHAKE_HANDLER_CLASS_TLSHD);
+ KUNIT_ASSERT_PTR_EQ(test, req, next);
+
+ /* Act */
+ first = handshake_try_complete(req);
+ second = handshake_try_complete(req);
+ handshake_finish_complete(req, 0, NULL);
+ /* handshake_complete() re-enters the gate via
+ * handshake_try_complete(). With the gate already taken,
+ * hp_done must not fire a second time.
+ */
+ handshake_complete(req, 0, NULL);
+
+ /* Assert */
+ KUNIT_EXPECT_TRUE(test, first);
+ KUNIT_EXPECT_FALSE(test, second);
+ KUNIT_EXPECT_EQ(test, handshake_try_complete_done_count, 1);
+
+ fput(filp);
+}
+
static struct handshake_req *handshake_req_destroy_test;
static void test_destroy_func(struct handshake_req *req)
@@ -522,6 +590,10 @@ static struct kunit_case handshake_api_test_cases[] = {
.name = "req_cancel after done",
.run_case = handshake_req_cancel_test3,
},
+ {
+ .name = "try_complete gate is exclusive",
+ .run_case = handshake_try_complete_test1,
+ },
{
.name = "req_destroy works",
.run_case = handshake_req_destroy_test1,
diff --git a/net/handshake/request.c b/net/handshake/request.c
index 2215a9916727..96c27efa9958 100644
--- a/net/handshake/request.c
+++ b/net/handshake/request.c
@@ -311,6 +311,7 @@ bool handshake_try_complete(struct handshake_req *req)
{
return !test_and_set_bit(HANDSHAKE_F_REQ_COMPLETED, &req->hr_flags);
}
+EXPORT_SYMBOL_IF_KUNIT(handshake_try_complete);
/**
* handshake_finish_complete - Deliver completion to the consumer
@@ -341,6 +342,7 @@ void handshake_finish_complete(struct handshake_req *req, unsigned int status,
/* Handshake request is no longer pending */
sock_put(sk);
}
+EXPORT_SYMBOL_IF_KUNIT(handshake_finish_complete);
void handshake_complete(struct handshake_req *req, unsigned int status,
struct genl_info *info)
--
2.54.0
More information about the Linux-nvme
mailing list