From: Henrique Carvalho <henrique.carvalho@suse.com>
To: linux-cifs@vger.kernel.org
Cc: linkinjeon@kernel.org, sfrench@samba.org, metze@samba.org,
senozhatsky@chromium.org, tom@talpey.com, ematsumiya@suse.de,
Henrique Carvalho <henrique.carvalho@suse.com>
Subject: [PATCH v2 07/11] smb: server: reuse common transport helpers in TCP transport
Date: Tue, 28 Apr 2026 12:57:56 -0300 [thread overview]
Message-ID: <20260428155759.226368-3-henrique.carvalho@suse.com> (raw)
In-Reply-To: <20260428155759.226368-1-henrique.carvalho@suse.com>
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
next prev parent reply other threads:[~2026-04-28 15:58 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` Henrique Carvalho [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260428155759.226368-3-henrique.carvalho@suse.com \
--to=henrique.carvalho@suse.com \
--cc=ematsumiya@suse.de \
--cc=linkinjeon@kernel.org \
--cc=linux-cifs@vger.kernel.org \
--cc=metze@samba.org \
--cc=senozhatsky@chromium.org \
--cc=sfrench@samba.org \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox