* [PATCH v2 05/11] smb: server: split interface management from TCP in preparation for QUIC
@ 2026-04-28 15:57 Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 06/11] smb: server: add shared transport helpers " Henrique Carvalho
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Henrique Carvalho @ 2026-04-28 15:57 UTC (permalink / raw)
To: linux-cifs
Cc: linkinjeon, sfrench, metze, senozhatsky, tom, ematsumiya,
Henrique Carvalho
Move active_num_conn out of transport_tcp.c and define it in
connection.c.
Move the transport-agnostic bits out into a new interface.c /
interface.h, network-interface bookkeeping, the iface_list, the
netdevice notifier, alloc/find/set helpers, and the per-iface forker
kthread plumbing.
Make the necessary changes to transport_tcp.c, without functional change
for the TCP path; this is purely a layering rearrangement to make room
for additional transports.
Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
---
fs/smb/server/Makefile | 2 +-
fs/smb/server/connection.c | 9 +-
fs/smb/server/connection.h | 1 +
fs/smb/server/interface.c | 196 ++++++++++++++++++++++++++++++
fs/smb/server/interface.h | 34 ++++++
fs/smb/server/transport_ipc.c | 2 +-
fs/smb/server/transport_tcp.c | 223 ++++------------------------------
fs/smb/server/transport_tcp.h | 7 +-
8 files changed, 265 insertions(+), 209 deletions(-)
create mode 100644 fs/smb/server/interface.c
create mode 100644 fs/smb/server/interface.h
diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile
index 6407ba6b9340..7bd6501bfdc9 100644
--- a/fs/smb/server/Makefile
+++ b/fs/smb/server/Makefile
@@ -8,7 +8,7 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \
misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \
mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \
mgmt/tree_connect.o mgmt/user_session.o smb_common.o \
- transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \
+ interface.o transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \
smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \
ksmbd_spnego_negtokentarg.asn1.o asn1.o
diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index 31392d245c30..7465b364c35c 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -12,6 +12,7 @@
#include "smb_common.h"
#include "mgmt/ksmbd_ida.h"
#include "connection.h"
+#include "interface.h"
#include "transport_tcp.h"
#include "transport_rdma.h"
#include "misc.h"
@@ -79,6 +80,8 @@ static int create_proc_clients(void) { return 0; }
static void delete_proc_clients(void) {}
#endif
+atomic_t active_num_conn = ATOMIC_INIT(0);
+
/**
* ksmbd_conn_free() - free resources of the connection instance
*
@@ -512,9 +515,9 @@ int ksmbd_conn_transport_init(void)
int ret;
mutex_lock(&init_lock);
- ret = ksmbd_tcp_init();
+ ret = ksmbd_interface_init();
if (ret) {
- pr_err("Failed to init TCP subsystem: %d\n", ret);
+ pr_err("Failed to init interface: %d\n", ret);
goto out;
}
@@ -558,7 +561,7 @@ void ksmbd_conn_transport_destroy(void)
{
delete_proc_clients();
mutex_lock(&init_lock);
- ksmbd_tcp_destroy();
+ ksmbd_interface_destroy();
ksmbd_rdma_stop_listening();
stop_sessions();
mutex_unlock(&init_lock);
diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h
index ae21a1bd4c70..b060baf5f688 100644
--- a/fs/smb/server/connection.h
+++ b/fs/smb/server/connection.h
@@ -157,6 +157,7 @@ struct ksmbd_transport {
#define CONN_HASH_BITS 12
extern DECLARE_HASHTABLE(conn_list, CONN_HASH_BITS);
extern struct rw_semaphore conn_list_lock;
+extern atomic_t active_num_conn;
bool ksmbd_conn_alive(struct ksmbd_conn *conn);
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn);
diff --git a/fs/smb/server/interface.c b/fs/smb/server/interface.c
new file mode 100644
index 000000000000..a5e59a420639
--- /dev/null
+++ b/fs/smb/server/interface.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/freezer.h>
+
+#include "smb_common.h"
+#include "server.h"
+#include "auth.h"
+#include "connection.h"
+#include "transport_tcp.h"
+#include "interface.h"
+
+static LIST_HEAD(iface_list);
+static int bind_additional_ifaces;
+
+static struct interface *alloc_iface(char *ifname)
+{
+ struct interface *iface;
+
+ if (!ifname)
+ return NULL;
+
+ iface = kzalloc_obj(struct interface, KSMBD_DEFAULT_GFP);
+ if (!iface) {
+ kfree(ifname);
+ return NULL;
+ }
+
+ iface->name = ifname;
+ iface->state = IFACE_STATE_DOWN;
+ list_add(&iface->entry, &iface_list);
+ return iface;
+}
+
+/**
+ * ksmbd_interface_run_kthread() - start a per-interface forker thread
+ * @iface: pointer to struct interface
+ * @kthread_fn: thread routine to run
+ * @suf: suffix to append to the thread name (e.g. "tcp")
+ *
+ * Return: the started task_struct, or ERR_PTR on failure.
+ */
+struct task_struct *ksmbd_interface_run_kthread(struct interface *iface,
+ int (*kthread_fn)(void *), const char *suf)
+{
+ struct task_struct *kthread;
+
+ kthread = kthread_run(kthread_fn, (void *)iface, "ksmbd-%s-%s", iface->name, suf);
+ return kthread;
+}
+
+void ksmbd_interface_stop_kthread(struct task_struct *kthread)
+{
+ int ret;
+
+ if (!kthread)
+ return;
+
+ ret = kthread_stop(kthread);
+ if (ret)
+ pr_err("failed to stop forker thread\n");
+}
+
+static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
+ void *ptr)
+{
+ struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
+ struct interface *iface;
+ int ret;
+
+ switch (event) {
+ case NETDEV_UP:
+ if (netif_is_bridge_port(netdev))
+ return NOTIFY_OK;
+
+ iface = ksmbd_find_netdev_name_iface_list(netdev->name);
+ if (iface && iface->state == IFACE_STATE_DOWN) {
+ ksmbd_debug(CONN, "netdev-up event: netdev(%s) is going up\n",
+ iface->name);
+
+ if (!iface->net)
+ iface->net = get_net(dev_net(netdev));
+
+ ret = ksmbd_tcp_create_socket(iface);
+ if (ret)
+ break;
+ }
+ if (!iface && bind_additional_ifaces) {
+ iface = alloc_iface(kstrdup(netdev->name, KSMBD_DEFAULT_GFP));
+ if (!iface)
+ return NOTIFY_OK;
+
+ ksmbd_debug(CONN, "netdev-up event: netdev(%s) is going up\n",
+ iface->name);
+
+ iface->net = get_net(dev_net(netdev));
+
+ ret = ksmbd_tcp_create_socket(iface);
+ if (ret)
+ break;
+ }
+ break;
+ case NETDEV_DOWN:
+ iface = ksmbd_find_netdev_name_iface_list(netdev->name);
+ if (iface && iface->state == IFACE_STATE_CONFIGURED) {
+ ksmbd_debug(CONN, "netdev-down event: netdev(%s) is going down\n",
+ iface->name);
+
+ if (iface->ksmbd_tcp_socket)
+ kernel_sock_shutdown(iface->ksmbd_tcp_socket, SHUT_RDWR);
+
+ if (iface->ksmbd_tcp_kthread)
+ ksmbd_interface_stop_kthread(iface->ksmbd_tcp_kthread);
+
+ if (iface->ksmbd_tcp_socket)
+ sock_release(iface->ksmbd_tcp_socket);
+
+ iface->ksmbd_tcp_kthread = NULL;
+ iface->ksmbd_tcp_socket = NULL;
+
+ put_net(iface->net);
+ iface->net = NULL;
+
+ iface->state = IFACE_STATE_DOWN;
+ break;
+ }
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ksmbd_netdev_notifier = {
+ .notifier_call = ksmbd_netdev_event,
+};
+
+int ksmbd_interface_init(void)
+{
+ register_netdevice_notifier(&ksmbd_netdev_notifier);
+
+ return 0;
+}
+
+
+void ksmbd_interface_destroy(void)
+{
+ struct interface *iface, *tmp;
+
+ unregister_netdevice_notifier(&ksmbd_netdev_notifier);
+
+ list_for_each_entry_safe(iface, tmp, &iface_list, entry) {
+ list_del(&iface->entry);
+ kfree(iface->name);
+ kfree(iface);
+ }
+}
+
+struct interface *
+ksmbd_find_netdev_name_iface_list(char *netdev_name)
+{
+ struct interface *iface;
+
+ list_for_each_entry(iface, &iface_list, entry)
+ if (!strcmp(iface->name, netdev_name))
+ return iface;
+ return NULL;
+}
+
+int ksmbd_set_interfaces(char *ifc_list, int ifc_list_sz)
+{
+ int sz = 0;
+
+ if (!ifc_list_sz) {
+ bind_additional_ifaces = 1;
+ return 0;
+ }
+
+ while (ifc_list_sz > 0) {
+ if (!alloc_iface(kstrdup(ifc_list, KSMBD_DEFAULT_GFP)))
+ return -ENOMEM;
+
+ sz = strlen(ifc_list);
+ if (!sz)
+ break;
+
+ ifc_list += sz + 1;
+ ifc_list_sz -= (sz + 1);
+ }
+
+ bind_additional_ifaces = 0;
+
+ return 0;
+}
diff --git a/fs/smb/server/interface.h b/fs/smb/server/interface.h
new file mode 100644
index 000000000000..7e35645076a9
--- /dev/null
+++ b/fs/smb/server/interface.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd.
+ *
+ */
+
+#ifndef __KSMBD_INTERFACE_H__
+#define __KSMBD_INTERFACE_H__
+
+#include <linux/freezer.h>
+
+#define IFACE_STATE_DOWN BIT(0)
+#define IFACE_STATE_CONFIGURED BIT(1)
+
+struct interface {
+ struct task_struct *ksmbd_tcp_kthread;
+ struct socket *ksmbd_tcp_socket;
+ struct list_head entry;
+ char *name;
+ int state;
+ struct net *net;
+};
+
+int ksmbd_interface_init(void);
+void ksmbd_interface_destroy(void);
+int ksmbd_set_interfaces(char *ifc_list, int ifc_list_sz);
+struct interface *ksmbd_find_netdev_name_iface_list(char *netdev_name);
+struct task_struct *ksmbd_interface_run_kthread(struct interface *iface,
+ int (*kthread_fn)(void *),
+ const char *suf);
+void ksmbd_interface_stop_kthread(struct task_struct *kthread);
+
+#endif /* _KSMBD_INTERFACE_H */
diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
index 2dbabe2d8005..6ccfb1cf8ed7 100644
--- a/fs/smb/server/transport_ipc.c
+++ b/fs/smb/server/transport_ipc.c
@@ -348,7 +348,7 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
ret |= ksmbd_set_server_string(req->server_string);
ret |= ksmbd_set_work_group(req->work_group);
server_conf.bind_interfaces_only = req->bind_interfaces_only;
- ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req),
+ ret |= ksmbd_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req),
req->ifc_list_sz);
out:
if (ret) {
diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c
index 7e29b06820e2..37f4238f72f5 100644
--- a/fs/smb/server/transport_tcp.c
+++ b/fs/smb/server/transport_tcp.c
@@ -11,23 +11,7 @@
#include "auth.h"
#include "connection.h"
#include "transport_tcp.h"
-
-#define IFACE_STATE_DOWN BIT(0)
-#define IFACE_STATE_CONFIGURED BIT(1)
-
-static atomic_t active_num_conn;
-
-struct interface {
- struct task_struct *ksmbd_kthread;
- struct socket *ksmbd_socket;
- struct list_head entry;
- char *name;
- int state;
-};
-
-static LIST_HEAD(iface_list);
-
-static int bind_additional_ifaces;
+#include "interface.h"
struct tcp_transport {
struct ksmbd_transport transport;
@@ -38,8 +22,6 @@ struct tcp_transport {
static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops;
-static void tcp_stop_kthread(struct task_struct *kthread);
-static struct interface *alloc_iface(char *ifname);
static void ksmbd_tcp_disconnect(struct ksmbd_transport *t);
#define KSMBD_TRANS(t) (&(t)->transport)
@@ -214,7 +196,7 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk)
*
* Return: 0 on success, error number otherwise
*/
-static int ksmbd_kthread_fn(void *p)
+static int tcp_kthread_fn(void *p)
{
struct socket *client_sk = NULL;
struct interface *iface = (struct interface *)p;
@@ -223,10 +205,9 @@ static int ksmbd_kthread_fn(void *p)
unsigned int max_ip_conns;
while (!kthread_should_stop()) {
- if (!iface->ksmbd_socket) {
+ if (!iface->ksmbd_tcp_socket)
break;
- }
- ret = kernel_accept(iface->ksmbd_socket, &client_sk, 0);
+ ret = kernel_accept(iface->ksmbd_tcp_socket, &client_sk, 0);
if (ret == -EINVAL)
break;
if (ret)
@@ -298,32 +279,6 @@ static int ksmbd_kthread_fn(void *p)
return 0;
}
-/**
- * ksmbd_tcp_run_kthread() - start forker thread
- * @iface: pointer to struct interface
- *
- * start forker thread(ksmbd/0) at module init time to listen
- * on port 445 for new SMB connection requests. It creates per connection
- * server threads(ksmbd/x)
- *
- * Return: 0 on success or error number
- */
-static int ksmbd_tcp_run_kthread(struct interface *iface)
-{
- int rc;
- struct task_struct *kthread;
-
- kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
- iface->name);
- if (IS_ERR(kthread)) {
- rc = PTR_ERR(kthread);
- return rc;
- }
- iface->ksmbd_kthread = kthread;
-
- return 0;
-}
-
/**
* ksmbd_tcp_readv() - read data from socket in given iovec
* @t: TCP transport instance
@@ -432,7 +387,7 @@ static void ksmbd_tcp_disconnect(struct ksmbd_transport *t)
atomic_dec(&active_num_conn);
}
-static void tcp_destroy_socket(struct socket *ksmbd_socket)
+void ksmbd_tcp_destroy_socket(struct socket *ksmbd_socket)
{
int ret;
@@ -451,21 +406,22 @@ static void tcp_destroy_socket(struct socket *ksmbd_socket)
*
* Return: 0 on success, error number otherwise
*/
-static int create_socket(struct interface *iface)
+int ksmbd_tcp_create_socket(struct interface *iface)
{
int ret;
struct sockaddr_in6 sin6;
struct sockaddr_in sin;
struct socket *ksmbd_socket;
+ struct task_struct *kthread;
bool ipv4 = false;
- ret = sock_create_kern(current->nsproxy->net_ns, PF_INET6, SOCK_STREAM,
- IPPROTO_TCP, &ksmbd_socket);
+ ret = sock_create_kern(iface->net, PF_INET6, SOCK_STREAM,
+ IPPROTO_TCP, &ksmbd_socket);
if (ret) {
if (ret != -EAFNOSUPPORT)
pr_err("Can't create socket for ipv6, fallback to ipv4: %d\n", ret);
- ret = sock_create_kern(current->nsproxy->net_ns, PF_INET,
- SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket);
+ ret = sock_create_kern(iface->net, PF_INET, SOCK_STREAM,
+ IPPROTO_TCP, &ksmbd_socket);
if (ret) {
pr_err("Can't create socket for ipv4: %d\n", ret);
goto out_clear;
@@ -515,165 +471,28 @@ static int create_socket(struct interface *iface)
goto out_error;
}
- iface->ksmbd_socket = ksmbd_socket;
- ret = ksmbd_tcp_run_kthread(iface);
- if (ret) {
+ iface->ksmbd_tcp_socket = ksmbd_socket;
+
+ kthread = ksmbd_interface_run_kthread(iface, tcp_kthread_fn, "tcp");
+ if (IS_ERR(kthread)) {
pr_err("Can't start ksmbd main kthread: %d\n", ret);
+ ret = PTR_ERR(kthread);
goto out_error;
}
+
+ iface->ksmbd_tcp_kthread = kthread;
iface->state = IFACE_STATE_CONFIGURED;
return 0;
out_error:
- tcp_destroy_socket(ksmbd_socket);
+ ksmbd_tcp_destroy_socket(ksmbd_socket);
out_clear:
- iface->ksmbd_socket = NULL;
+ iface->ksmbd_tcp_socket = NULL;
+ iface->ksmbd_tcp_kthread = NULL;
return ret;
}
-struct interface *ksmbd_find_netdev_name_iface_list(char *netdev_name)
-{
- struct interface *iface;
-
- list_for_each_entry(iface, &iface_list, entry)
- if (!strcmp(iface->name, netdev_name))
- return iface;
- return NULL;
-}
-
-static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
- void *ptr)
-{
- struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
- struct interface *iface;
- int ret;
-
- switch (event) {
- case NETDEV_UP:
- if (netif_is_bridge_port(netdev))
- return NOTIFY_OK;
-
- iface = ksmbd_find_netdev_name_iface_list(netdev->name);
- if (iface && iface->state == IFACE_STATE_DOWN) {
- ksmbd_debug(CONN, "netdev-up event: netdev(%s) is going up\n",
- iface->name);
- ret = create_socket(iface);
- if (ret)
- return NOTIFY_OK;
- }
- if (!iface && bind_additional_ifaces) {
- iface = alloc_iface(kstrdup(netdev->name, KSMBD_DEFAULT_GFP));
- if (!iface)
- return NOTIFY_OK;
- ksmbd_debug(CONN, "netdev-up event: netdev(%s) is going up\n",
- iface->name);
- ret = create_socket(iface);
- if (ret)
- break;
- }
- break;
- case NETDEV_DOWN:
- iface = ksmbd_find_netdev_name_iface_list(netdev->name);
- if (iface && iface->state == IFACE_STATE_CONFIGURED) {
- ksmbd_debug(CONN, "netdev-down event: netdev(%s) is going down\n",
- iface->name);
- kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR);
- tcp_stop_kthread(iface->ksmbd_kthread);
- iface->ksmbd_kthread = NULL;
- sock_release(iface->ksmbd_socket);
- iface->ksmbd_socket = NULL;
-
- iface->state = IFACE_STATE_DOWN;
- break;
- }
- break;
- }
-
- return NOTIFY_DONE;
-}
-
-static struct notifier_block ksmbd_netdev_notifier = {
- .notifier_call = ksmbd_netdev_event,
-};
-
-int ksmbd_tcp_init(void)
-{
- register_netdevice_notifier(&ksmbd_netdev_notifier);
-
- return 0;
-}
-
-static void tcp_stop_kthread(struct task_struct *kthread)
-{
- int ret;
-
- if (!kthread)
- return;
-
- ret = kthread_stop(kthread);
- if (ret)
- pr_err("failed to stop forker thread\n");
-}
-
-void ksmbd_tcp_destroy(void)
-{
- struct interface *iface, *tmp;
-
- unregister_netdevice_notifier(&ksmbd_netdev_notifier);
-
- list_for_each_entry_safe(iface, tmp, &iface_list, entry) {
- list_del(&iface->entry);
- kfree(iface->name);
- kfree(iface);
- }
-}
-
-static struct interface *alloc_iface(char *ifname)
-{
- struct interface *iface;
-
- if (!ifname)
- return NULL;
-
- iface = kzalloc_obj(struct interface, KSMBD_DEFAULT_GFP);
- if (!iface) {
- kfree(ifname);
- return NULL;
- }
-
- iface->name = ifname;
- iface->state = IFACE_STATE_DOWN;
- list_add(&iface->entry, &iface_list);
- return iface;
-}
-
-int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz)
-{
- int sz = 0;
-
- if (!ifc_list_sz) {
- bind_additional_ifaces = 1;
- return 0;
- }
-
- while (ifc_list_sz > 0) {
- if (!alloc_iface(kstrdup(ifc_list, KSMBD_DEFAULT_GFP)))
- return -ENOMEM;
-
- sz = strlen(ifc_list);
- if (!sz)
- break;
-
- ifc_list += sz + 1;
- ifc_list_sz -= (sz + 1);
- }
-
- bind_additional_ifaces = 0;
-
- return 0;
-}
-
static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops = {
.read = ksmbd_tcp_read,
.writev = ksmbd_tcp_writev,
diff --git a/fs/smb/server/transport_tcp.h b/fs/smb/server/transport_tcp.h
index 1e51675ee1b2..d122e4b69d65 100644
--- a/fs/smb/server/transport_tcp.h
+++ b/fs/smb/server/transport_tcp.h
@@ -6,10 +6,13 @@
#ifndef __KSMBD_TRANSPORT_TCP_H__
#define __KSMBD_TRANSPORT_TCP_H__
-int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz);
-struct interface *ksmbd_find_netdev_name_iface_list(char *netdev_name);
+#include "interface.h"
+
void ksmbd_free_transport(struct ksmbd_transport *kt);
int ksmbd_tcp_init(void);
void ksmbd_tcp_destroy(void);
+int ksmbd_tcp_create_socket(struct interface *iface);
+void ksmbd_tcp_stop_kthread(struct task_struct *kthread);
+void ksmbd_tcp_destroy_socket(struct socket *ksmbd_socket);
#endif /* __KSMBD_TRANSPORT_TCP_H__ */
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 06/11] smb: server: add shared transport helpers in preparation for QUIC
2026-04-28 15:57 [PATCH v2 05/11] smb: server: split interface management from TCP in preparation for QUIC Henrique Carvalho
@ 2026-04-28 15:57 ` Henrique Carvalho
2026-04-29 22:24 ` Namjae Jeon
2026-04-28 15:57 ` [PATCH v2 07/11] smb: server: reuse common transport helpers in TCP transport Henrique Carvalho
` (3 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Henrique Carvalho @ 2026-04-28 15:57 UTC (permalink / raw)
To: linux-cifs
Cc: linkinjeon, sfrench, metze, senozhatsky, tom, ematsumiya,
Henrique Carvalho
Move the generic transport functions out of the TCP transport code into
a shared transport layer. This keeps the common socket setup and checks,
accepted-connection initialization, and receive helpers in one place
instead of open-coding them in the individual transport implementation.
Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
---
fs/smb/server/Makefile | 3 +-
fs/smb/server/connection.c | 1 +
fs/smb/server/connection.h | 5 -
fs/smb/server/transport.c | 344 ++++++++++++++++++++++++++++++++++
fs/smb/server/transport.h | 40 ++++
fs/smb/server/transport_tcp.c | 1 +
6 files changed, 388 insertions(+), 6 deletions(-)
create mode 100644 fs/smb/server/transport.c
create mode 100644 fs/smb/server/transport.h
diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile
index 7bd6501bfdc9..89a9955f607d 100644
--- a/fs/smb/server/Makefile
+++ b/fs/smb/server/Makefile
@@ -8,7 +8,8 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \
misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \
mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \
mgmt/tree_connect.o mgmt/user_session.o smb_common.o \
- interface.o transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \
+ transport.o transport_tcp.o transport_ipc.o interface.o \
+ smbacl.o smb2pdu.o \
smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \
ksmbd_spnego_negtokentarg.asn1.o asn1.o
diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index 7465b364c35c..07bbf4eb7b05 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -13,6 +13,7 @@
#include "mgmt/ksmbd_ida.h"
#include "connection.h"
#include "interface.h"
+#include "transport.h"
#include "transport_tcp.h"
#include "transport_rdma.h"
#include "misc.h"
diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h
index b060baf5f688..0e87283a9ddb 100644
--- a/fs/smb/server/connection.h
+++ b/fs/smb/server/connection.h
@@ -145,11 +145,6 @@ struct ksmbd_transport_ops {
void (*free_transport)(struct ksmbd_transport *kt);
};
-struct ksmbd_transport {
- struct ksmbd_conn *conn;
- const struct ksmbd_transport_ops *ops;
-};
-
#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ)
#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ)
#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr))
diff --git a/fs/smb/server/transport.c b/fs/smb/server/transport.c
new file mode 100644
index 000000000000..0a99fb821e02
--- /dev/null
+++ b/fs/smb/server/transport.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/freezer.h>
+
+#include "server.h"
+#include "glob.h"
+#include "connection.h"
+#include "interface.h"
+#include "transport.h"
+
+/*
+ * ksmbd_transport_iovec - get iovec for reading from socket
+ * @t: ksmbd transport struct
+ * @nr_segs: number of iov segments needed for reading
+ *
+ * Return: return existing or newly allocate iovec
+ */
+struct kvec *ksmbd_transport_iovec(struct ksmbd_transport *t,
+ unsigned int nr_segs)
+{
+ struct kvec *new_iov;
+
+ if (t->io.iov && nr_segs <= t->io.nr_iov)
+ return t->io.iov;
+
+ /* not big enough -- allocate a new one and release the old */
+ new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), KSMBD_DEFAULT_GFP);
+ if (new_iov) {
+ kfree(t->io.iov);
+ t->io.iov = new_iov;
+ t->io.nr_iov = nr_segs;
+ }
+ return new_iov;
+}
+
+int ksmbd_transport_alloc_conn(struct ksmbd_transport *t,
+ struct socket *client_sk)
+{
+ struct task_struct *handler;
+ struct ksmbd_conn *conn;
+
+ conn = ksmbd_conn_alloc();
+ if (!conn)
+ return -ENOMEM;
+
+ conn->transport = t;
+ t->conn = conn;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (client_sk->sk->sk_family == AF_INET6) {
+ memcpy(&conn->inet6_addr, &client_sk->sk->sk_v6_daddr, 16);
+ conn->inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr);
+ } else {
+ conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr;
+ conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
+ }
+#else
+ conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr;
+ conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
+#endif
+ down_write(&conn_list_lock);
+ hash_add(conn_list, &conn->hlist, conn->inet_hash);
+ up_write(&conn_list_lock);
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (client_sk->sk->sk_family == AF_INET6)
+ handler = kthread_run(ksmbd_conn_handler_loop,
+ conn, "ksmbd:%pI6c",
+ &conn->inet6_addr);
+ else
+ handler = kthread_run(ksmbd_conn_handler_loop,
+ conn, "ksmbd:%pI4",
+ &conn->inet_addr);
+#else
+ handler = kthread_run(ksmbd_conn_handler_loop,
+ conn, "ksmbd:%pI4",
+ &conn->inet_addr);
+#endif
+ if (IS_ERR(handler)) {
+ pr_err("cannot start conn thread\n");
+ return PTR_ERR(handler);
+ }
+
+ return 0;
+}
+
+/**
+ * kvec_array_init() - initialize a IO vector segment
+ * @new: IO vector to be initialized
+ * @iov: base IO vector
+ * @nr_segs: number of segments in base iov
+ * @bytes: total iovec length so far for read
+ *
+ * Return: Number of IO segments
+ */
+static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov,
+ unsigned int nr_segs, size_t bytes)
+{
+ size_t base = 0;
+
+ while (bytes || !iov->iov_len) {
+ int copy = min(bytes, iov->iov_len);
+
+ bytes -= copy;
+ base += copy;
+ if (iov->iov_len == base) {
+ iov++;
+ nr_segs--;
+ base = 0;
+ }
+ }
+
+ memcpy(new, iov, sizeof(*iov) * nr_segs);
+ new->iov_base += base;
+ new->iov_len -= base;
+ return nr_segs;
+}
+
+/**
+ * ksmbd_readv() - read data from socket in given iovec
+ * @t: ksmbd transport instance
+ * @sock: socket to read from
+ * @iov_orig: base IO vector
+ * @nr_segs: number of segments in base iov
+ * @to_read: number of bytes to read from socket
+ * @max_retries: maximum retry count
+ *
+ * Return: on success return number of bytes read from socket,
+ * otherwise return error number
+ */
+int ksmbd_readv(struct ksmbd_transport *t, struct socket *sock,
+ struct kvec *iov_orig, unsigned int nr_segs,
+ unsigned int to_read, int max_retries)
+{
+ int length = 0;
+ int total_read;
+ unsigned int segs;
+ struct msghdr ksmbd_msg;
+ struct kvec *iov;
+ struct ksmbd_conn *conn = t->conn;
+
+ iov = ksmbd_transport_iovec(t, nr_segs);
+ if (!iov)
+ return -ENOMEM;
+
+ ksmbd_msg.msg_control = NULL;
+ ksmbd_msg.msg_controllen = 0;
+
+ for (total_read = 0; to_read; total_read += length, to_read -= length) {
+ try_to_freeze();
+
+ if (!ksmbd_conn_alive(conn)) {
+ total_read = -ESHUTDOWN;
+ break;
+ }
+ segs = kvec_array_init(iov, iov_orig, nr_segs, total_read);
+
+ length = kernel_recvmsg(sock, &ksmbd_msg,
+ iov, segs, to_read, 0);
+
+ if (length == -EINTR) {
+ total_read = -ESHUTDOWN;
+ break;
+ } else if (ksmbd_conn_need_reconnect(conn)) {
+ total_read = -EAGAIN;
+ break;
+ } else if (length == -ERESTARTSYS || length == -EAGAIN) {
+ /*
+ * If max_retries is negative, Allow unlimited
+ * retries to keep connection with inactive sessions.
+ */
+ if (max_retries == 0) {
+ total_read = length;
+ break;
+ } else if (max_retries > 0) {
+ max_retries--;
+ }
+
+ usleep_range(1000, 2000);
+ length = 0;
+ continue;
+ } else if (length <= 0) {
+ total_read = length;
+ break;
+ }
+ }
+ return total_read;
+}
+
+struct socket *ksmbd_create_socket(struct interface *iface, int sock_type,
+ int ipproto, int port)
+{
+ int ret;
+ struct sockaddr_in6 sin6;
+ struct sockaddr_in sin;
+ struct socket *ksmbd_socket;
+ bool ipv4 = false;
+
+ ret = sock_create_kern(iface->net, PF_INET6, sock_type,
+ ipproto, &ksmbd_socket);
+ if (ret) {
+ if (ret != -EAFNOSUPPORT)
+ pr_err("Can't create socket for ipv6, fallback to ipv4: %d\n", ret);
+ ret = sock_create_kern(iface->net, PF_INET, sock_type,
+ ipproto, &ksmbd_socket);
+ if (ret) {
+ pr_err("Can't create socket for ipv4: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ sin.sin_family = PF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = htons(port);
+ ipv4 = true;
+ } else {
+ sin6.sin6_family = PF_INET6;
+ sin6.sin6_addr = in6addr_any;
+ sin6.sin6_port = htons(port);
+
+ lock_sock(ksmbd_socket->sk);
+ ksmbd_socket->sk->sk_ipv6only = false;
+ release_sock(ksmbd_socket->sk);
+ }
+
+ ret = sock_setsockopt(ksmbd_socket,
+ SOL_SOCKET,
+ SO_BINDTODEVICE,
+ KERNEL_SOCKPTR(iface->name),
+ strlen(iface->name));
+ if (ret != -ENODEV && ret < 0) {
+ pr_err("Failed to set SO_BINDTODEVICE: %d\n", ret);
+ goto out_error;
+ }
+
+ if (ipv4)
+ ret = kernel_bind(ksmbd_socket, (struct sockaddr_unsized *)&sin,
+ sizeof(sin));
+ else
+ ret = kernel_bind(ksmbd_socket, (struct sockaddr_unsized *)&sin6,
+ sizeof(sin6));
+ if (ret) {
+ pr_err("Failed to bind socket: %d\n", ret);
+ goto out_error;
+ }
+
+ ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG);
+ if (ret) {
+ pr_err("Port listen() error: %d\n", ret);
+ goto out_error;
+ }
+
+ return ksmbd_socket;
+
+out_error:
+ ksmbd_destroy_socket(ksmbd_socket);
+ return ERR_PTR(ret);
+}
+
+void ksmbd_destroy_socket(struct socket *ksmbd_socket)
+{
+ int ret;
+
+ if (!ksmbd_socket)
+ return;
+
+ ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR);
+ if (ret)
+ pr_err("Failed to shutdown socket: %d\n", ret);
+ sock_release(ksmbd_socket);
+}
+
+/**
+ * ksmbd_check_max_ip_conns() - reject when per-IP connection cap reached
+ * @client_sk: freshly accepted client socket
+ *
+ * Return: 0 if a new connection from this peer is allowed, -EAGAIN
+ * when server_conf.max_ip_connections has been reached.
+ */
+int ksmbd_check_max_ip_conns(struct socket *client_sk)
+{
+ struct ksmbd_conn *conn;
+ unsigned int max_ip_conns = 0;
+ int inet_hash;
+ int ret = 0;
+
+ if (!server_conf.max_ip_connections)
+ return 0;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (client_sk->sk->sk_family == AF_INET6)
+ inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr);
+ else
+ inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
+#else
+ inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
+#endif
+
+ down_read(&conn_list_lock);
+ hash_for_each_possible(conn_list, conn, hlist, inet_hash) {
+#if IS_ENABLED(CONFIG_IPV6)
+ if (client_sk->sk->sk_family == AF_INET6) {
+ if (memcmp(&client_sk->sk->sk_v6_daddr,
+ &conn->inet6_addr, 16) == 0)
+ max_ip_conns++;
+ } else if (inet_sk(client_sk->sk)->inet_daddr ==
+ conn->inet_addr)
+ max_ip_conns++;
+#else
+ if (inet_sk(client_sk->sk)->inet_daddr == conn->inet_addr)
+ max_ip_conns++;
+#endif
+ if (server_conf.max_ip_connections <= max_ip_conns) {
+ pr_info_ratelimited("Maximum IP connections exceeded (%u/%u)\n",
+ max_ip_conns,
+ server_conf.max_ip_connections);
+ ret = -EAGAIN;
+ break;
+ }
+ }
+ up_read(&conn_list_lock);
+ return ret;
+}
+
+/**
+ * ksmbd_check_max_conns() - reserve a slot under the global connection cap
+ *
+ * On success the global counter is incremented; the caller is responsible for
+ * decrementing it on disconnect.
+ *
+ * Return: 0 if allowed, -EAGAIN if server_conf.max_connections reached.
+ */
+int ksmbd_check_max_conns(void)
+{
+ if (!server_conf.max_connections)
+ return 0;
+
+ if (atomic_inc_return(&active_num_conn) >= server_conf.max_connections) {
+ pr_info_ratelimited("Limit the maximum number of connections(%u)\n",
+ atomic_read(&active_num_conn));
+ atomic_dec(&active_num_conn);
+ return -EAGAIN;
+ }
+ return 0;
+}
diff --git a/fs/smb/server/transport.h b/fs/smb/server/transport.h
new file mode 100644
index 000000000000..6b2fb4f9005c
--- /dev/null
+++ b/fs/smb/server/transport.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026, SUSE LLC
+ *
+ * Author: Henrique Carvalho <henrique.carvalho@suse.com>
+ */
+
+#ifndef __KSMBD_TRANSPORT_H__
+#define __KSMBD_TRANSPORT_H__
+
+#include <net/sock.h>
+
+#include "connection.h"
+#include "interface.h"
+
+struct ksmbd_transport {
+ struct ksmbd_conn *conn;
+ const struct ksmbd_transport_ops *ops;
+ struct {
+ struct kvec *iov;
+ unsigned int nr_iov;
+ } io;
+};
+
+#define KSMBD_TRANS(t) (&(t)->transport)
+
+struct kvec *ksmbd_transport_iovec(struct ksmbd_transport *t,
+ unsigned int nr_segs);
+int ksmbd_transport_alloc_conn(struct ksmbd_transport *t,
+ struct socket *client_sk);
+int ksmbd_readv(struct ksmbd_transport *t, struct socket *sock,
+ struct kvec *iov_orig, unsigned int nr_segs,
+ unsigned int to_read, int max_retries);
+struct socket *ksmbd_create_socket(struct interface *iface, int sock_type,
+ int ipproto, int port);
+void ksmbd_destroy_socket(struct socket *ksmbd_socket);
+int ksmbd_check_max_ip_conns(struct socket *client_sk);
+int ksmbd_check_max_conns(void);
+
+#endif /* __KSMBD_TRANSPORT_H__ */
diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c
index 37f4238f72f5..07123ebcf8fd 100644
--- a/fs/smb/server/transport_tcp.c
+++ b/fs/smb/server/transport_tcp.c
@@ -10,6 +10,7 @@
#include "server.h"
#include "auth.h"
#include "connection.h"
+#include "transport.h"
#include "transport_tcp.h"
#include "interface.h"
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 07/11] smb: server: reuse common transport helpers in TCP transport
2026-04-28 15:57 [PATCH v2 05/11] smb: server: split interface management from TCP in preparation for QUIC Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 06/11] smb: server: add shared transport helpers " Henrique Carvalho
@ 2026-04-28 15:57 ` Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 08/11] smb: server: add QUIC transport support Henrique Carvalho
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Henrique Carvalho @ 2026-04-28 15:57 UTC (permalink / raw)
To: linux-cifs
Cc: linkinjeon, sfrench, metze, senozhatsky, tom, ematsumiya,
Henrique Carvalho
The new transport core already provides shared helpers for listener
creation, accepted-connection setup, receive-side iovec handling, and
connection-limit checks. Keep the TCP transport focused on TCP-specific
socket options and send/disconnect behavior by switching it over to the
common helpers.
Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
---
fs/smb/server/transport_tcp.c | 312 +++-------------------------------
1 file changed, 19 insertions(+), 293 deletions(-)
diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c
index 07123ebcf8fd..1496f0336af6 100644
--- a/fs/smb/server/transport_tcp.c
+++ b/fs/smb/server/transport_tcp.c
@@ -17,8 +17,6 @@
struct tcp_transport {
struct ksmbd_transport transport;
struct socket *sock;
- struct kvec *iov;
- unsigned int nr_iov;
};
static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops;
@@ -39,50 +37,11 @@ static inline void ksmbd_tcp_reuseaddr(struct socket *sock)
sock_set_reuseaddr(sock->sk);
}
-static struct tcp_transport *alloc_transport(struct socket *client_sk)
-{
- struct tcp_transport *t;
- struct ksmbd_conn *conn;
-
- t = kzalloc_obj(*t, KSMBD_DEFAULT_GFP);
- if (!t)
- return NULL;
- t->sock = client_sk;
-
- conn = ksmbd_conn_alloc();
- if (!conn) {
- kfree(t);
- return NULL;
- }
-
-#if IS_ENABLED(CONFIG_IPV6)
- if (client_sk->sk->sk_family == AF_INET6) {
- memcpy(&conn->inet6_addr, &client_sk->sk->sk_v6_daddr, 16);
- conn->inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr);
- } else {
- conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr;
- conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
- }
-#else
- conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr;
- conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
-#endif
- down_write(&conn_list_lock);
- hash_add(conn_list, &conn->hlist, conn->inet_hash);
- up_write(&conn_list_lock);
-
- conn->transport = KSMBD_TRANS(t);
- KSMBD_TRANS(t)->conn = conn;
- KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops;
- return t;
-}
-
static void ksmbd_tcp_free_transport(struct ksmbd_transport *kt)
{
struct tcp_transport *t = TCP_TRANS(kt);
sock_release(t->sock);
- kfree(t->iov);
kfree(t);
}
@@ -92,62 +51,6 @@ static void free_transport(struct tcp_transport *t)
ksmbd_conn_free(KSMBD_TRANS(t)->conn);
}
-/**
- * kvec_array_init() - initialize a IO vector segment
- * @new: IO vector to be initialized
- * @iov: base IO vector
- * @nr_segs: number of segments in base iov
- * @bytes: total iovec length so far for read
- *
- * Return: Number of IO segments
- */
-static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov,
- unsigned int nr_segs, size_t bytes)
-{
- size_t base = 0;
-
- while (bytes || !iov->iov_len) {
- int copy = min(bytes, iov->iov_len);
-
- bytes -= copy;
- base += copy;
- if (iov->iov_len == base) {
- iov++;
- nr_segs--;
- base = 0;
- }
- }
-
- memcpy(new, iov, sizeof(*iov) * nr_segs);
- new->iov_base += base;
- new->iov_len -= base;
- return nr_segs;
-}
-
-/**
- * get_conn_iovec() - get connection iovec for reading from socket
- * @t: TCP transport instance
- * @nr_segs: number of segments in iov
- *
- * Return: return existing or newly allocate iovec
- */
-static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs)
-{
- struct kvec *new_iov;
-
- if (t->iov && nr_segs <= t->nr_iov)
- return t->iov;
-
- /* not big enough -- allocate a new one and release the old */
- new_iov = kmalloc_objs(*new_iov, nr_segs, KSMBD_DEFAULT_GFP);
- if (new_iov) {
- kfree(t->iov);
- t->iov = new_iov;
- t->nr_iov = nr_segs;
- }
- return new_iov;
-}
-
/**
* ksmbd_tcp_new_connection() - create a new tcp session on mount
* @client_sk: socket associated with new connection
@@ -161,33 +64,19 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk)
{
int rc = 0;
struct tcp_transport *t;
- struct task_struct *handler;
- t = alloc_transport(client_sk);
+ t = kzalloc_obj(*t, KSMBD_DEFAULT_GFP);
if (!t) {
sock_release(client_sk);
return -ENOMEM;
}
+ t->sock = client_sk;
+ KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops;
-#if IS_ENABLED(CONFIG_IPV6)
- if (client_sk->sk->sk_family == AF_INET6)
- handler = kthread_run(ksmbd_conn_handler_loop,
- KSMBD_TRANS(t)->conn, "ksmbd:%pI6c",
- &KSMBD_TRANS(t)->conn->inet6_addr);
- else
- handler = kthread_run(ksmbd_conn_handler_loop,
- KSMBD_TRANS(t)->conn, "ksmbd:%pI4",
- &KSMBD_TRANS(t)->conn->inet_addr);
-#else
- handler = kthread_run(ksmbd_conn_handler_loop,
- KSMBD_TRANS(t)->conn, "ksmbd:%pI4",
- &KSMBD_TRANS(t)->conn->inet_addr);
-#endif
- if (IS_ERR(handler)) {
- pr_err("cannot start conn thread\n");
- rc = PTR_ERR(handler);
+ rc = ksmbd_transport_alloc_conn(KSMBD_TRANS(t), client_sk);
+ if (rc)
ksmbd_tcp_disconnect(KSMBD_TRANS(t));
- }
+
return rc;
}
@@ -201,9 +90,7 @@ static int tcp_kthread_fn(void *p)
{
struct socket *client_sk = NULL;
struct interface *iface = (struct interface *)p;
- struct ksmbd_conn *conn;
- int ret, inet_hash;
- unsigned int max_ip_conns;
+ int ret;
while (!kthread_should_stop()) {
if (!iface->ksmbd_tcp_socket)
@@ -214,57 +101,14 @@ static int tcp_kthread_fn(void *p)
if (ret)
continue;
- if (!server_conf.max_ip_connections)
- goto skip_max_ip_conns_limit;
-
- /*
- * Limits repeated connections from clients with the same IP.
- */
-#if IS_ENABLED(CONFIG_IPV6)
- if (client_sk->sk->sk_family == AF_INET6)
- inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr);
- else
- inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
-#else
- inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr);
-#endif
-
- max_ip_conns = 0;
- down_read(&conn_list_lock);
- hash_for_each_possible(conn_list, conn, hlist, inet_hash) {
-#if IS_ENABLED(CONFIG_IPV6)
- if (client_sk->sk->sk_family == AF_INET6) {
- if (memcmp(&client_sk->sk->sk_v6_daddr,
- &conn->inet6_addr, 16) == 0)
- max_ip_conns++;
- } else if (inet_sk(client_sk->sk)->inet_daddr ==
- conn->inet_addr)
- max_ip_conns++;
-#else
- if (inet_sk(client_sk->sk)->inet_daddr ==
- conn->inet_addr)
- max_ip_conns++;
-#endif
- if (server_conf.max_ip_connections <= max_ip_conns) {
- pr_info_ratelimited("Maximum IP connections exceeded (%u/%u)\n",
- max_ip_conns, server_conf.max_ip_connections);
- ret = -EAGAIN;
- break;
- }
- }
- up_read(&conn_list_lock);
+ ret = ksmbd_check_max_ip_conns(client_sk);
if (ret == -EAGAIN) {
- /* Per-IP limit hit: release the just-accepted socket. */
sock_release(client_sk);
continue;
}
-skip_max_ip_conns_limit:
- if (server_conf.max_connections &&
- atomic_inc_return(&active_num_conn) >= server_conf.max_connections) {
- pr_info_ratelimited("Limit the maximum number of connections(%u)\n",
- atomic_read(&active_num_conn));
- atomic_dec(&active_num_conn);
+ ret = ksmbd_check_max_conns();
+ if (ret == -EAGAIN) {
sock_release(client_sk);
continue;
}
@@ -280,76 +124,6 @@ static int tcp_kthread_fn(void *p)
return 0;
}
-/**
- * ksmbd_tcp_readv() - read data from socket in given iovec
- * @t: TCP transport instance
- * @iov_orig: base IO vector
- * @nr_segs: number of segments in base iov
- * @to_read: number of bytes to read from socket
- * @max_retries: maximum retry count
- *
- * Return: on success return number of bytes read from socket,
- * otherwise return error number
- */
-static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig,
- unsigned int nr_segs, unsigned int to_read,
- int max_retries)
-{
- int length = 0;
- int total_read;
- unsigned int segs;
- struct msghdr ksmbd_msg;
- struct kvec *iov;
- struct ksmbd_conn *conn = KSMBD_TRANS(t)->conn;
-
- iov = get_conn_iovec(t, nr_segs);
- if (!iov)
- return -ENOMEM;
-
- ksmbd_msg.msg_control = NULL;
- ksmbd_msg.msg_controllen = 0;
-
- for (total_read = 0; to_read; total_read += length, to_read -= length) {
- try_to_freeze();
-
- if (!ksmbd_conn_alive(conn)) {
- total_read = -ESHUTDOWN;
- break;
- }
- segs = kvec_array_init(iov, iov_orig, nr_segs, total_read);
-
- length = kernel_recvmsg(t->sock, &ksmbd_msg,
- iov, segs, to_read, 0);
-
- if (length == -EINTR) {
- total_read = -ESHUTDOWN;
- break;
- } else if (ksmbd_conn_need_reconnect(conn)) {
- total_read = -EAGAIN;
- break;
- } else if (length == -ERESTARTSYS || length == -EAGAIN) {
- /*
- * If max_retries is negative, Allow unlimited
- * retries to keep connection with inactive sessions.
- */
- if (max_retries == 0) {
- total_read = length;
- break;
- } else if (max_retries > 0) {
- max_retries--;
- }
-
- usleep_range(1000, 2000);
- length = 0;
- continue;
- } else if (length <= 0) {
- total_read = length;
- break;
- }
- }
- return total_read;
-}
-
/**
* ksmbd_tcp_read() - read data from socket in given buffer
* @t: TCP transport instance
@@ -368,7 +142,8 @@ static int ksmbd_tcp_read(struct ksmbd_transport *t, char *buf,
iov.iov_base = buf;
iov.iov_len = to_read;
- return ksmbd_tcp_readv(TCP_TRANS(t), &iov, 1, to_read, max_retries);
+ return ksmbd_readv(t, TCP_TRANS(t)->sock, &iov, 1, to_read,
+ max_retries);
}
static int ksmbd_tcp_writev(struct ksmbd_transport *t, struct kvec *iov,
@@ -409,69 +184,20 @@ void ksmbd_tcp_destroy_socket(struct socket *ksmbd_socket)
*/
int ksmbd_tcp_create_socket(struct interface *iface)
{
- int ret;
- struct sockaddr_in6 sin6;
- struct sockaddr_in sin;
struct socket *ksmbd_socket;
struct task_struct *kthread;
- bool ipv4 = false;
-
- ret = sock_create_kern(iface->net, PF_INET6, SOCK_STREAM,
- IPPROTO_TCP, &ksmbd_socket);
- if (ret) {
- if (ret != -EAFNOSUPPORT)
- pr_err("Can't create socket for ipv6, fallback to ipv4: %d\n", ret);
- ret = sock_create_kern(iface->net, PF_INET, SOCK_STREAM,
- IPPROTO_TCP, &ksmbd_socket);
- if (ret) {
- pr_err("Can't create socket for ipv4: %d\n", ret);
- goto out_clear;
- }
+ int ret;
- sin.sin_family = PF_INET;
- sin.sin_addr.s_addr = htonl(INADDR_ANY);
- sin.sin_port = htons(server_conf.tcp_port);
- ipv4 = true;
- } else {
- sin6.sin6_family = PF_INET6;
- sin6.sin6_addr = in6addr_any;
- sin6.sin6_port = htons(server_conf.tcp_port);
-
- lock_sock(ksmbd_socket->sk);
- ksmbd_socket->sk->sk_ipv6only = false;
- release_sock(ksmbd_socket->sk);
+ ksmbd_socket = ksmbd_create_socket(iface, SOCK_STREAM, IPPROTO_TCP,
+ server_conf.tcp_port);
+ if (IS_ERR(ksmbd_socket)) {
+ ret = PTR_ERR(ksmbd_socket);
+ goto out_clear;
}
ksmbd_tcp_nodelay(ksmbd_socket);
ksmbd_tcp_reuseaddr(ksmbd_socket);
- ret = sock_setsockopt(ksmbd_socket,
- SOL_SOCKET,
- SO_BINDTODEVICE,
- KERNEL_SOCKPTR(iface->name),
- strlen(iface->name));
- if (ret != -ENODEV && ret < 0) {
- pr_err("Failed to set SO_BINDTODEVICE: %d\n", ret);
- goto out_error;
- }
-
- if (ipv4)
- ret = kernel_bind(ksmbd_socket, (struct sockaddr_unsized *)&sin,
- sizeof(sin));
- else
- ret = kernel_bind(ksmbd_socket, (struct sockaddr_unsized *)&sin6,
- sizeof(sin6));
- if (ret) {
- pr_err("Failed to bind socket: %d\n", ret);
- goto out_error;
- }
-
- ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG);
- if (ret) {
- pr_err("Port listen() error: %d\n", ret);
- goto out_error;
- }
-
iface->ksmbd_tcp_socket = ksmbd_socket;
kthread = ksmbd_interface_run_kthread(iface, tcp_kthread_fn, "tcp");
@@ -487,7 +213,7 @@ int ksmbd_tcp_create_socket(struct interface *iface)
return 0;
out_error:
- ksmbd_tcp_destroy_socket(ksmbd_socket);
+ ksmbd_destroy_socket(ksmbd_socket);
out_clear:
iface->ksmbd_tcp_socket = NULL;
iface->ksmbd_tcp_kthread = NULL;
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 08/11] smb: server: add QUIC transport support
2026-04-28 15:57 [PATCH v2 05/11] smb: server: split interface management from TCP in preparation for QUIC Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 06/11] smb: server: add shared transport helpers " Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 07/11] smb: server: reuse common transport helpers in TCP transport Henrique Carvalho
@ 2026-04-28 15:57 ` Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 09/11] smb: server: refactor TCP transport definitions Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 10/11] smb: server: track TCP and QUIC listener state independently Henrique Carvalho
4 siblings, 0 replies; 7+ messages in thread
From: Henrique Carvalho @ 2026-04-28 15:57 UTC (permalink / raw)
To: linux-cifs
Cc: linkinjeon, sfrench, metze, senozhatsky, tom, ematsumiya,
Henrique Carvalho
Add a QUIC listener and connection transport for ksmbd on top of the
shared server transport helpers. This wires in the QUIC transport file,
adds the per-interface QUIC socket/kthread state, and enables QUIC
listener startup and teardown alongside the existing TCP path.
Also add the server-side QUIC port configuration and pass the client
peer address through the existing tree connect management path for QUIC
connections.
Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
---
fs/smb/server/Makefile | 4 +-
fs/smb/server/connection.c | 1 +
fs/smb/server/interface.c | 30 ++-
fs/smb/server/interface.h | 2 +
fs/smb/server/server.h | 1 +
fs/smb/server/smb2pdu.c | 1 +
fs/smb/server/transport_ipc.c | 1 +
fs/smb/server/transport_quic.c | 327 +++++++++++++++++++++++++++++++++
fs/smb/server/transport_quic.h | 20 ++
9 files changed, 380 insertions(+), 7 deletions(-)
create mode 100644 fs/smb/server/transport_quic.c
create mode 100644 fs/smb/server/transport_quic.h
diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile
index 89a9955f607d..569be14b32d4 100644
--- a/fs/smb/server/Makefile
+++ b/fs/smb/server/Makefile
@@ -8,8 +8,8 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \
misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \
mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \
mgmt/tree_connect.o mgmt/user_session.o smb_common.o \
- transport.o transport_tcp.o transport_ipc.o interface.o \
- smbacl.o smb2pdu.o \
+ transport.o transport_tcp.o transport_ipc.o transport_quic.o \
+ interface.o smbacl.o smb2pdu.o \
smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \
ksmbd_spnego_negtokentarg.asn1.o asn1.o
diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index 07bbf4eb7b05..1db8e0a8b47d 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -15,6 +15,7 @@
#include "interface.h"
#include "transport.h"
#include "transport_tcp.h"
+#include "transport_quic.h"
#include "transport_rdma.h"
#include "misc.h"
diff --git a/fs/smb/server/interface.c b/fs/smb/server/interface.c
index a5e59a420639..25b8e7f44059 100644
--- a/fs/smb/server/interface.c
+++ b/fs/smb/server/interface.c
@@ -11,6 +11,7 @@
#include "auth.h"
#include "connection.h"
#include "transport_tcp.h"
+#include "transport_quic.h"
#include "interface.h"
static LIST_HEAD(iface_list);
@@ -36,12 +37,14 @@ static struct interface *alloc_iface(char *ifname)
}
/**
- * ksmbd_interface_run_kthread() - start a per-interface forker thread
- * @iface: pointer to struct interface
- * @kthread_fn: thread routine to run
- * @suf: suffix to append to the thread name (e.g. "tcp")
+ * ksmbd_tcp_run_kthread() - start forker thread
+ * @iface: pointer to struct interface
*
- * Return: the started task_struct, or ERR_PTR on failure.
+ * start forker thread(ksmbd/0) at module init time to listen
+ * on port 445 for new SMB connection requests. It creates per connection
+ * server threads(ksmbd/x)
+ *
+ * Return: 0 on success or error number
*/
struct task_struct *ksmbd_interface_run_kthread(struct interface *iface,
int (*kthread_fn)(void *), const char *suf)
@@ -87,6 +90,10 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
ret = ksmbd_tcp_create_socket(iface);
if (ret)
break;
+
+ ret = ksmbd_quic_create_socket(iface);
+ if (ret)
+ break;
}
if (!iface && bind_additional_ifaces) {
iface = alloc_iface(kstrdup(netdev->name, KSMBD_DEFAULT_GFP));
@@ -101,6 +108,10 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
ret = ksmbd_tcp_create_socket(iface);
if (ret)
break;
+
+ ret = ksmbd_quic_create_socket(iface);
+ if (ret)
+ break;
}
break;
case NETDEV_DOWN:
@@ -111,15 +122,23 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
if (iface->ksmbd_tcp_socket)
kernel_sock_shutdown(iface->ksmbd_tcp_socket, SHUT_RDWR);
+ if (iface->ksmbd_quic_socket)
+ kernel_sock_shutdown(iface->ksmbd_quic_socket, SHUT_RDWR);
if (iface->ksmbd_tcp_kthread)
ksmbd_interface_stop_kthread(iface->ksmbd_tcp_kthread);
+ if (iface->ksmbd_quic_kthread)
+ ksmbd_interface_stop_kthread(iface->ksmbd_quic_kthread);
if (iface->ksmbd_tcp_socket)
sock_release(iface->ksmbd_tcp_socket);
+ if (iface->ksmbd_quic_socket)
+ sock_release(iface->ksmbd_quic_socket);
iface->ksmbd_tcp_kthread = NULL;
+ iface->ksmbd_quic_kthread = NULL;
iface->ksmbd_tcp_socket = NULL;
+ iface->ksmbd_quic_socket = NULL;
put_net(iface->net);
iface->net = NULL;
@@ -194,3 +213,4 @@ int ksmbd_set_interfaces(char *ifc_list, int ifc_list_sz)
return 0;
}
+
diff --git a/fs/smb/server/interface.h b/fs/smb/server/interface.h
index 7e35645076a9..94ffb73fd635 100644
--- a/fs/smb/server/interface.h
+++ b/fs/smb/server/interface.h
@@ -16,6 +16,8 @@
struct interface {
struct task_struct *ksmbd_tcp_kthread;
struct socket *ksmbd_tcp_socket;
+ struct task_struct *ksmbd_quic_kthread;
+ struct socket *ksmbd_quic_socket;
struct list_head entry;
char *name;
int state;
diff --git a/fs/smb/server/server.h b/fs/smb/server/server.h
index b8a7317be86b..197d99264071 100644
--- a/fs/smb/server/server.h
+++ b/fs/smb/server/server.h
@@ -35,6 +35,7 @@ struct ksmbd_server_config {
short min_protocol;
short max_protocol;
unsigned short tcp_port;
+ unsigned short quic_port;
unsigned short ipc_timeout;
unsigned long ipc_last_active;
unsigned long deadtime;
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 1f579ef0e9d6..6f82f6bd378a 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -31,6 +31,7 @@
#include "server.h"
#include "smb_common.h"
+#include "interface.h"
#include "../common/smb2status.h"
#include "ksmbd_work.h"
#include "mgmt/user_config.h"
diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
index 6ccfb1cf8ed7..d14f282f3337 100644
--- a/fs/smb/server/transport_ipc.c
+++ b/fs/smb/server/transport_ipc.c
@@ -315,6 +315,7 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
server_conf.flags = req->flags;
server_conf.signing = req->signing;
server_conf.tcp_port = req->tcp_port;
+ server_conf.quic_port = 443; /* MS-SMB2 1.9 */
server_conf.ipc_timeout = req->ipc_timeout * HZ;
if (check_mul_overflow(req->deadtime, SMB_ECHO_INTERVAL,
&server_conf.deadtime)) {
diff --git a/fs/smb/server/transport_quic.c b/fs/smb/server/transport_quic.c
new file mode 100644
index 000000000000..84067225c1d3
--- /dev/null
+++ b/fs/smb/server/transport_quic.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026, SUSE LLC
+ *
+ * Author: Henrique Carvalho <henrique.carvalho@suse.com>
+ */
+
+#include <linux/freezer.h>
+#include <linux/socket.h>
+#include <linux/quic.h>
+#include <linux/key.h>
+#include <net/handshake.h>
+#include <keys/asymmetric-type.h>
+
+#include "smb_common.h"
+#include "server.h"
+#include "auth.h"
+#include "connection.h"
+#include "transport.h"
+#include "glob.h"
+#include "transport_quic.h"
+#include "interface.h"
+
+struct quic_transport {
+ struct ksmbd_transport transport;
+ struct socket *sock;
+};
+
+static const struct ksmbd_transport_ops ksmbd_quic_transport_ops;
+
+#define QUIC_TRANS(t) ((struct quic_transport *)container_of(t, struct quic_transport, transport))
+
+static inline void ksmbd_quic_reuseaddr(struct socket *sock)
+{
+ sock_set_reuseaddr(sock->sk);
+}
+
+static void ksmbd_quic_disconnect(struct ksmbd_transport *kt)
+{
+ struct quic_transport *t = QUIC_TRANS(kt);
+
+ kernel_sock_shutdown(t->sock, SHUT_RDWR);
+ ksmbd_conn_free(KSMBD_TRANS(t)->conn);
+ if (server_conf.max_connections)
+ atomic_dec(&active_num_conn);
+}
+
+static void ksmbd_quic_close_client_sk(struct socket *client_sk)
+{
+ if (client_sk->file)
+ fput(client_sk->file);
+ else
+ sock_release(client_sk);
+}
+
+static struct quic_transport *ksmbd_quic_alloc(void)
+{
+ struct quic_transport *t;
+
+ t = kzalloc_obj(*t, KSMBD_DEFAULT_GFP);
+ if (!t)
+ return NULL;
+
+ KSMBD_TRANS(t)->ops = &ksmbd_quic_transport_ops;
+ return t;
+}
+
+static void ksmbd_quic_free(struct ksmbd_transport *kt)
+{
+ struct quic_transport *t = QUIC_TRANS(kt);
+
+ ksmbd_quic_close_client_sk(t->sock);
+ kfree(kt->io.iov); /* TODO (transport.c): move to ksmbd_conn_free */
+ kfree(t);
+}
+
+/**
+ * ksmbd_quic_new_connection() - create a new quic session on mount
+ * @client_sk: socket associated with new connection
+ *
+ * whenever a new connection is requested, create a conn thread
+ * (session thread) to handle new incoming smb requests from the connection
+ *
+ * Return: 0 on success, otherwise error
+ */
+static int ksmbd_quic_new_connection(struct socket *client_sk)
+{
+ int rc;
+ struct quic_transport *t;
+
+ t = ksmbd_quic_alloc();
+ if (!t) {
+ ksmbd_quic_close_client_sk(client_sk);
+ return -ENOMEM;
+ }
+
+ t->sock = client_sk;
+
+ rc = ksmbd_transport_alloc_conn(KSMBD_TRANS(t), client_sk);
+ if (rc) {
+ if (KSMBD_TRANS(t)->conn) {
+ /*
+ * conn was allocated and hashed but the handler
+ * thread failed; tear down via the conn refcount
+ * path which also calls ->free_transport().
+ */
+ ksmbd_quic_disconnect(KSMBD_TRANS(t));
+ } else {
+ ksmbd_quic_close_client_sk(client_sk);
+ kfree(t);
+ }
+ }
+
+ return rc;
+}
+
+static void ksmbd_tls_done(void *data, int status, key_serial_t peerid)
+{
+ struct smb_tls *kstls = (struct smb_tls *)data;
+
+ kstls->status = status;
+ kstls->peerid = peerid;
+
+ complete(&kstls->handshake_done);
+}
+
+static struct smb_tls *ksmbd_tls_init(void)
+{
+ struct smb_tls *kstls;
+ const unsigned int timeout_ms = 5000;
+
+ kstls = kzalloc_obj(*kstls, KSMBD_DEFAULT_GFP);
+ if (!kstls)
+ return ERR_PTR(-ENOMEM);
+
+ kstls->status = 0;
+
+ kstls->args.ta_done = ksmbd_tls_done;
+ kstls->args.ta_data = kstls;
+ kstls->args.ta_timeout_ms = timeout_ms;
+
+ init_completion(&kstls->handshake_done);
+
+ return kstls;
+}
+
+static void ksmbd_tls_reinit(struct smb_tls *kstls, struct socket *client_sk)
+{
+ kstls->status = 0;
+ kstls->peerid = TLS_NO_PEERID;
+ kstls->args.ta_sock = client_sk;
+ reinit_completion(&kstls->handshake_done);
+}
+
+static void ksmbd_tls_free(struct smb_tls **kstls)
+{
+ kfree_sensitive(*kstls);
+ *kstls = NULL;
+}
+
+/**
+ * ksmbd_quic_kthread_fn() - listen to new SMB QUIC connections and callback server
+ * @p: arguments to forker thread
+ *
+ * Return: 0 on success, error number otherwise
+ */
+static int ksmbd_quic_kthread_fn(void *p)
+{
+ struct socket *client_sk = NULL;
+ struct interface *iface = (struct interface *)p;
+ struct smb_tls *ksmbd_tls;
+ int ret;
+
+ ksmbd_tls = ksmbd_tls_init();
+ if (IS_ERR(ksmbd_tls))
+ return PTR_ERR(ksmbd_tls);
+
+ while (!kthread_should_stop()) {
+ struct file *filp;
+
+ if (!iface->ksmbd_quic_socket)
+ break;
+
+ ret = kernel_accept(iface->ksmbd_quic_socket, &client_sk, 0);
+ if (ret == -EINVAL)
+ break;
+ if (ret)
+ continue;
+
+ ret = quic_do_setsockopt(client_sk->sk,
+ QUIC_SOCKOPT_ALPN,
+ KERNEL_SOCKPTR("smb"),
+ 3);
+ if (ret < 0) {
+ ksmbd_debug(CONN, "Failed to set QUIC_SOCKOPT_ALPN: %d\n", ret);
+ ksmbd_quic_close_client_sk(client_sk);
+ continue;
+ }
+
+ filp = sock_alloc_file(client_sk, 0, NULL);
+ if (IS_ERR(filp)) {
+ ksmbd_debug(CONN, "Socket file allocation failed: %ld", PTR_ERR(filp));
+ ksmbd_quic_close_client_sk(client_sk);
+ continue;
+ }
+
+ ksmbd_tls_reinit(ksmbd_tls, client_sk);
+
+ ret = tls_server_hello_x509(&ksmbd_tls->args, KSMBD_DEFAULT_GFP);
+ if (ret < 0) {
+ ksmbd_debug(CONN, "Handshake failed: %d", ret);
+ ksmbd_quic_close_client_sk(client_sk);
+ continue;
+ }
+
+ ret = wait_for_completion_interruptible(&ksmbd_tls->handshake_done);
+ if (ret < 0) {
+ tls_handshake_cancel(client_sk->sk);
+ ksmbd_quic_close_client_sk(client_sk);
+ ksmbd_debug(CONN, "Handshake failed: %d", ret);
+ continue;
+ }
+ if (ksmbd_tls->status) {
+ ksmbd_quic_close_client_sk(client_sk);
+ ksmbd_debug(CONN, "Handshake failed: %d", ksmbd_tls->status);
+ continue;
+ }
+ ksmbd_debug(CONN, "Handshake successful");
+
+ if (ksmbd_check_max_ip_conns(client_sk)) {
+ ksmbd_quic_close_client_sk(client_sk);
+ continue;
+ }
+
+ if (ksmbd_check_max_conns()) {
+ ksmbd_quic_close_client_sk(client_sk);
+ continue;
+ }
+
+ ksmbd_debug(CONN, "connect success: accepted new connection\n");
+ client_sk->sk->sk_rcvtimeo = KSMBD_QUIC_RECV_TIMEOUT;
+ client_sk->sk->sk_sndtimeo = KSMBD_QUIC_SEND_TIMEOUT;
+
+ ksmbd_quic_new_connection(client_sk);
+ }
+
+ ksmbd_tls_free(&ksmbd_tls);
+
+ ksmbd_debug(CONN, "releasing socket\n");
+ return 0;
+}
+
+/**
+ * ksmbd_quic_read() - read data from socket in given buffer
+ * @t: transport instance
+ * @buf: buffer to store read data from socket
+ * @to_read: number of bytes to read from socket
+ * @max_retries: number of retries if reading from socket fails
+ *
+ * Return: on success return number of bytes read from socket,
+ * otherwise return error number
+ */
+static int ksmbd_quic_readv(struct ksmbd_transport *t, char *buf,
+ unsigned int to_read, int max_retries)
+{
+ struct kvec iov;
+
+ iov.iov_base = buf;
+ iov.iov_len = to_read;
+
+ return ksmbd_readv(t, QUIC_TRANS(t)->sock, &iov, 1, to_read, max_retries);
+}
+
+static int ksmbd_quic_writev(struct ksmbd_transport *t, struct kvec *iov,
+ int nvecs, int size, bool need_invalidate,
+ unsigned int remote_key)
+
+{
+ int err = -EOPNOTSUPP;
+#if IS_ENABLED(CONFIG_IP_QUIC)
+ struct msghdr ksmbd_msg = {.msg_flags = MSG_NOSIGNAL};
+
+ err = kernel_sendmsg(QUIC_TRANS(t)->sock, &ksmbd_msg, iov, nvecs, size);
+#endif
+ return err;
+}
+
+void ksmbd_quic_destroy_socket(struct socket *ksmbd_socket)
+{
+ ksmbd_destroy_socket(ksmbd_socket);
+}
+
+int ksmbd_quic_create_socket(struct interface *iface)
+{
+#if IS_ENABLED(CONFIG_IP_QUIC)
+ struct task_struct *kthread;
+ struct socket *ksmbd_socket;
+ int ret;
+
+ ksmbd_socket = ksmbd_create_socket(iface, SOCK_DGRAM, IPPROTO_QUIC,
+ server_conf.quic_port);
+ if (IS_ERR(ksmbd_socket))
+ return PTR_ERR(ksmbd_socket);
+
+ ksmbd_quic_reuseaddr(ksmbd_socket);
+
+ kthread = ksmbd_interface_run_kthread(iface, ksmbd_quic_kthread_fn, "quic");
+ if (IS_ERR(kthread)) {
+ ret = PTR_ERR(kthread);
+ pr_err("Can't start ksmbd main kthread for quic: %d\n", ret);
+ ksmbd_destroy_socket(ksmbd_socket);
+ return ret;
+ }
+
+ iface->ksmbd_quic_socket = ksmbd_socket;
+ iface->ksmbd_quic_kthread = kthread;
+ iface->state = IFACE_STATE_CONFIGURED;
+#endif /* CONFIG_IP_QUIC */
+ return 0;
+}
+
+static const struct ksmbd_transport_ops ksmbd_quic_transport_ops = {
+ .read = ksmbd_quic_readv,
+ .writev = ksmbd_quic_writev,
+ .disconnect = ksmbd_quic_disconnect,
+ .free_transport = ksmbd_quic_free,
+};
diff --git a/fs/smb/server/transport_quic.h b/fs/smb/server/transport_quic.h
new file mode 100644
index 000000000000..5c62c45affc4
--- /dev/null
+++ b/fs/smb/server/transport_quic.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026, SUSE LLC.
+ */
+
+#ifndef __KSMBD_TRANSPORT_QUIC_H__
+#define __KSMBD_TRANSPORT_QUIC_H__
+
+#include "interface.h"
+
+#define KSMBD_QUIC_RECV_TIMEOUT (7 * HZ)
+#define KSMBD_QUIC_SEND_TIMEOUT (5 * HZ)
+
+int ksmbd_quic_init(void);
+void ksmbd_quic_destroy(void);
+int ksmbd_quic_create_socket(struct interface *iface);
+void ksmbd_quic_destroy_socket(struct socket *ksmbd_socket);
+void ksmbd_quic_stop_kthread(struct task_struct *kthread);
+
+#endif /* __KSMBD_TRANSPORT_QUIC_H__ */
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 09/11] smb: server: refactor TCP transport definitions
2026-04-28 15:57 [PATCH v2 05/11] smb: server: split interface management from TCP in preparation for QUIC Henrique Carvalho
` (2 preceding siblings ...)
2026-04-28 15:57 ` [PATCH v2 08/11] smb: server: add QUIC transport support Henrique Carvalho
@ 2026-04-28 15:57 ` Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 10/11] smb: server: track TCP and QUIC listener state independently Henrique Carvalho
4 siblings, 0 replies; 7+ messages in thread
From: Henrique Carvalho @ 2026-04-28 15:57 UTC (permalink / raw)
To: linux-cifs
Cc: linkinjeon, sfrench, metze, senozhatsky, tom, ematsumiya,
Henrique Carvalho
Move the TCP timeout definitions into transport_tcp.h and rename the
peer address accessor in connection.h to a transport-neutral name.
Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
---
fs/smb/server/connection.h | 4 +---
fs/smb/server/mgmt/tree_connect.c | 2 +-
fs/smb/server/transport_tcp.h | 3 +++
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h
index 0e87283a9ddb..f874bbdd4b34 100644
--- a/fs/smb/server/connection.h
+++ b/fs/smb/server/connection.h
@@ -145,9 +145,7 @@ struct ksmbd_transport_ops {
void (*free_transport)(struct ksmbd_transport *kt);
};
-#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ)
-#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ)
-#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr))
+#define KSMBD_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr))
#define CONN_HASH_BITS 12
extern DECLARE_HASHTABLE(conn_list, CONN_HASH_BITS);
diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c
index a72d7e42a6c2..7704d8554591 100644
--- a/fs/smb/server/mgmt/tree_connect.c
+++ b/fs/smb/server/mgmt/tree_connect.c
@@ -44,7 +44,7 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name)
goto out_error;
}
- peer_addr = KSMBD_TCP_PEER_SOCKADDR(conn);
+ peer_addr = KSMBD_PEER_SOCKADDR(conn);
resp = ksmbd_ipc_tree_connect_request(sess,
sc,
tree_conn,
diff --git a/fs/smb/server/transport_tcp.h b/fs/smb/server/transport_tcp.h
index d122e4b69d65..ef5ba9affc3f 100644
--- a/fs/smb/server/transport_tcp.h
+++ b/fs/smb/server/transport_tcp.h
@@ -8,6 +8,9 @@
#include "interface.h"
+#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ)
+#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ)
+
void ksmbd_free_transport(struct ksmbd_transport *kt);
int ksmbd_tcp_init(void);
void ksmbd_tcp_destroy(void);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 10/11] smb: server: track TCP and QUIC listener state independently
2026-04-28 15:57 [PATCH v2 05/11] smb: server: split interface management from TCP in preparation for QUIC Henrique Carvalho
` (3 preceding siblings ...)
2026-04-28 15:57 ` [PATCH v2 09/11] smb: server: refactor TCP transport definitions Henrique Carvalho
@ 2026-04-28 15:57 ` Henrique Carvalho
4 siblings, 0 replies; 7+ messages in thread
From: Henrique Carvalho @ 2026-04-28 15:57 UTC (permalink / raw)
To: linux-cifs
Cc: linkinjeon, sfrench, metze, senozhatsky, tom, ematsumiya,
Henrique Carvalho
An interface can host both TCP and QUIC listeners, so a single
configured/down state is no longer precise enough once QUIC support is
added (e.g. a TCP listener may already be up when QUIC setup fails).
Turn iface->state into a transport bitmask and track TCP and QUIC
listener bring-up independently. This lets NETDEV_UP retry only the
missing transport and lets NETDEV_DOWN tear down only the listeners that
were actually started.
Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
---
fs/smb/server/interface.c | 61 ++++++++++++++++++++++------------
fs/smb/server/interface.h | 6 ++--
fs/smb/server/transport_quic.c | 2 +-
fs/smb/server/transport_tcp.c | 2 +-
4 files changed, 45 insertions(+), 26 deletions(-)
diff --git a/fs/smb/server/interface.c b/fs/smb/server/interface.c
index 25b8e7f44059..272c4aaad347 100644
--- a/fs/smb/server/interface.c
+++ b/fs/smb/server/interface.c
@@ -17,6 +17,11 @@
static LIST_HEAD(iface_list);
static int bind_additional_ifaces;
+static inline bool ksmbd_iface_transport_up(const struct interface *iface)
+{
+ return iface->state != 0;
+}
+
static struct interface *alloc_iface(char *ifname)
{
struct interface *iface;
@@ -31,7 +36,7 @@ static struct interface *alloc_iface(char *ifname)
}
iface->name = ifname;
- iface->state = IFACE_STATE_DOWN;
+ iface->state = 0;
list_add(&iface->entry, &iface_list);
return iface;
}
@@ -80,20 +85,30 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
return NOTIFY_OK;
iface = ksmbd_find_netdev_name_iface_list(netdev->name);
- if (iface && iface->state == IFACE_STATE_DOWN) {
+ if (iface) {
ksmbd_debug(CONN, "netdev-up event: netdev(%s) is going up\n",
iface->name);
if (!iface->net)
iface->net = get_net(dev_net(netdev));
- ret = ksmbd_tcp_create_socket(iface);
- if (ret)
- break;
-
- ret = ksmbd_quic_create_socket(iface);
- if (ret)
- break;
+ if (!(iface->state & IFACE_STATE_TCP_UP)) {
+ ret = ksmbd_tcp_create_socket(iface);
+ if (ret && !ksmbd_iface_transport_up(iface)) {
+ put_net(iface->net);
+ iface->net = NULL;
+ break;
+ }
+ }
+
+ if (!(iface->state & IFACE_STATE_QUIC_UP)) {
+ ret = ksmbd_quic_create_socket(iface);
+ if (ret && !ksmbd_iface_transport_up(iface)) {
+ put_net(iface->net);
+ iface->net = NULL;
+ break;
+ }
+ }
}
if (!iface && bind_additional_ifaces) {
iface = alloc_iface(kstrdup(netdev->name, KSMBD_DEFAULT_GFP));
@@ -106,44 +121,49 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
iface->net = get_net(dev_net(netdev));
ret = ksmbd_tcp_create_socket(iface);
- if (ret)
+ if (ret && !ksmbd_iface_transport_up(iface)) {
+ put_net(iface->net);
+ iface->net = NULL;
break;
+ }
ret = ksmbd_quic_create_socket(iface);
- if (ret)
+ if (ret && !ksmbd_iface_transport_up(iface)) {
+ put_net(iface->net);
+ iface->net = NULL;
break;
+ }
}
break;
case NETDEV_DOWN:
iface = ksmbd_find_netdev_name_iface_list(netdev->name);
- if (iface && iface->state == IFACE_STATE_CONFIGURED) {
+ if (iface && ksmbd_iface_transport_up(iface)) {
ksmbd_debug(CONN, "netdev-down event: netdev(%s) is going down\n",
iface->name);
- if (iface->ksmbd_tcp_socket)
+ if (iface->state & IFACE_STATE_TCP_UP)
kernel_sock_shutdown(iface->ksmbd_tcp_socket, SHUT_RDWR);
- if (iface->ksmbd_quic_socket)
+ if (iface->state & IFACE_STATE_QUIC_UP)
kernel_sock_shutdown(iface->ksmbd_quic_socket, SHUT_RDWR);
- if (iface->ksmbd_tcp_kthread)
+ if (iface->state & IFACE_STATE_TCP_UP)
ksmbd_interface_stop_kthread(iface->ksmbd_tcp_kthread);
- if (iface->ksmbd_quic_kthread)
+ if (iface->state & IFACE_STATE_QUIC_UP)
ksmbd_interface_stop_kthread(iface->ksmbd_quic_kthread);
- if (iface->ksmbd_tcp_socket)
+ if (iface->state & IFACE_STATE_TCP_UP)
sock_release(iface->ksmbd_tcp_socket);
- if (iface->ksmbd_quic_socket)
+ if (iface->state & IFACE_STATE_QUIC_UP)
sock_release(iface->ksmbd_quic_socket);
iface->ksmbd_tcp_kthread = NULL;
iface->ksmbd_quic_kthread = NULL;
iface->ksmbd_tcp_socket = NULL;
iface->ksmbd_quic_socket = NULL;
+ iface->state = 0;
put_net(iface->net);
iface->net = NULL;
-
- iface->state = IFACE_STATE_DOWN;
break;
}
break;
@@ -213,4 +233,3 @@ int ksmbd_set_interfaces(char *ifc_list, int ifc_list_sz)
return 0;
}
-
diff --git a/fs/smb/server/interface.h b/fs/smb/server/interface.h
index 94ffb73fd635..d99b1f3c8bc0 100644
--- a/fs/smb/server/interface.h
+++ b/fs/smb/server/interface.h
@@ -10,8 +10,8 @@
#include <linux/freezer.h>
-#define IFACE_STATE_DOWN BIT(0)
-#define IFACE_STATE_CONFIGURED BIT(1)
+#define IFACE_STATE_TCP_UP BIT(0)
+#define IFACE_STATE_QUIC_UP BIT(1)
struct interface {
struct task_struct *ksmbd_tcp_kthread;
@@ -20,7 +20,7 @@ struct interface {
struct socket *ksmbd_quic_socket;
struct list_head entry;
char *name;
- int state;
+ unsigned int state;
struct net *net;
};
diff --git a/fs/smb/server/transport_quic.c b/fs/smb/server/transport_quic.c
index 84067225c1d3..da089b655e6c 100644
--- a/fs/smb/server/transport_quic.c
+++ b/fs/smb/server/transport_quic.c
@@ -314,7 +314,7 @@ int ksmbd_quic_create_socket(struct interface *iface)
iface->ksmbd_quic_socket = ksmbd_socket;
iface->ksmbd_quic_kthread = kthread;
- iface->state = IFACE_STATE_CONFIGURED;
+ iface->state |= IFACE_STATE_QUIC_UP;
#endif /* CONFIG_IP_QUIC */
return 0;
}
diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c
index 1496f0336af6..81bf6996a6f7 100644
--- a/fs/smb/server/transport_tcp.c
+++ b/fs/smb/server/transport_tcp.c
@@ -208,7 +208,7 @@ int ksmbd_tcp_create_socket(struct interface *iface)
}
iface->ksmbd_tcp_kthread = kthread;
- iface->state = IFACE_STATE_CONFIGURED;
+ iface->state |= IFACE_STATE_TCP_UP;
return 0;
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 06/11] smb: server: add shared transport helpers in preparation for QUIC
2026-04-28 15:57 ` [PATCH v2 06/11] smb: server: add shared transport helpers " Henrique Carvalho
@ 2026-04-29 22:24 ` Namjae Jeon
0 siblings, 0 replies; 7+ messages in thread
From: Namjae Jeon @ 2026-04-29 22:24 UTC (permalink / raw)
To: Henrique Carvalho
Cc: linux-cifs, sfrench, metze, senozhatsky, tom, ematsumiya
> diff --git a/fs/smb/server/transport.c b/fs/smb/server/transport.c
> new file mode 100644
> index 000000000000..0a99fb821e02
> --- /dev/null
> +++ b/fs/smb/server/transport.c
> @@ -0,0 +1,344 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
You have to also move the copyright here.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-04-29 22:24 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-28 15:57 [PATCH v2 05/11] smb: server: split interface management from TCP in preparation for QUIC Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 06/11] smb: server: add shared transport helpers " Henrique Carvalho
2026-04-29 22:24 ` Namjae Jeon
2026-04-28 15:57 ` [PATCH v2 07/11] smb: server: reuse common transport helpers in TCP transport Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 08/11] smb: server: add QUIC transport support Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 09/11] smb: server: refactor TCP transport definitions Henrique Carvalho
2026-04-28 15:57 ` [PATCH v2 10/11] smb: server: track TCP and QUIC listener state independently Henrique Carvalho
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox