mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-09-09 00:06:08 +08:00
kernel: ksmbd: avoid busy polling
Backport a patch to use blocking kernel_accept() and a fix for it, avoiding busy polling. Signed-off-by: Qingfang Deng <dqfext@gmail.com> Link: https://github.com/openwrt/openwrt/pull/24987 Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
committed by
Robert Marko
parent
e1f69d04e0
commit
dc4c215080
+130
@@ -0,0 +1,130 @@
|
||||
From 3316a8fc840d82fad5efcf76ad0ea3f76fdca209 Mon Sep 17 00:00:00 2001
|
||||
From: Qingfang Deng <dqfext@gmail.com>
|
||||
Date: Mon, 17 Nov 2025 16:59:00 +0800
|
||||
Subject: [PATCH] ksmbd: server: avoid busy polling in accept loop
|
||||
|
||||
The ksmbd listener thread was using busy waiting on a listening socket by
|
||||
calling kernel_accept() with SOCK_NONBLOCK and retrying every 100ms on
|
||||
-EAGAIN. Since this thread is dedicated to accepting new connections,
|
||||
there is no need for non-blocking mode.
|
||||
|
||||
Switch to a blocking accept() call instead, allowing the thread to sleep
|
||||
until a new connection arrives. This avoids unnecessary wakeups and CPU
|
||||
usage. During teardown, call shutdown() on the listening socket so that
|
||||
accept() returns -EINVAL and the thread exits cleanly.
|
||||
|
||||
The socket release mutex is redundant because kthread_stop() blocks until
|
||||
the listener thread returns, guaranteeing safe teardown ordering.
|
||||
|
||||
Also remove sk_rcvtimeo and sk_sndtimeo assignments, which only caused
|
||||
accept() to return -EAGAIN prematurely.
|
||||
|
||||
Signed-off-by: Qingfang Deng <dqfext@gmail.com>
|
||||
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
||||
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
|
||||
Signed-off-by: Steve French <stfrench@microsoft.com>
|
||||
---
|
||||
fs/smb/server/transport_tcp.c | 41 +++++------------------------------
|
||||
1 file changed, 6 insertions(+), 35 deletions(-)
|
||||
|
||||
--- a/fs/smb/server/transport_tcp.c
|
||||
+++ b/fs/smb/server/transport_tcp.c
|
||||
@@ -22,7 +22,6 @@ struct interface {
|
||||
struct socket *ksmbd_socket;
|
||||
struct list_head entry;
|
||||
char *name;
|
||||
- struct mutex sock_release_lock;
|
||||
int state;
|
||||
};
|
||||
|
||||
@@ -57,21 +56,6 @@ static inline void ksmbd_tcp_reuseaddr(s
|
||||
sock_set_reuseaddr(sock->sk);
|
||||
}
|
||||
|
||||
-static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs)
|
||||
-{
|
||||
- lock_sock(sock->sk);
|
||||
- if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1)
|
||||
- sock->sk->sk_rcvtimeo = secs * HZ;
|
||||
- else
|
||||
- sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
|
||||
- release_sock(sock->sk);
|
||||
-}
|
||||
-
|
||||
-static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs)
|
||||
-{
|
||||
- sock_set_sndtimeo(sock->sk, secs);
|
||||
-}
|
||||
-
|
||||
static struct tcp_transport *alloc_transport(struct socket *client_sk)
|
||||
{
|
||||
struct tcp_transport *t;
|
||||
@@ -254,20 +238,14 @@ static int ksmbd_kthread_fn(void *p)
|
||||
unsigned int max_ip_conns;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
- mutex_lock(&iface->sock_release_lock);
|
||||
if (!iface->ksmbd_socket) {
|
||||
- mutex_unlock(&iface->sock_release_lock);
|
||||
break;
|
||||
}
|
||||
- ret = kernel_accept(iface->ksmbd_socket, &client_sk,
|
||||
- SOCK_NONBLOCK);
|
||||
- mutex_unlock(&iface->sock_release_lock);
|
||||
- if (ret) {
|
||||
- if (ret == -EAGAIN)
|
||||
- /* check for new connections every 100 msecs */
|
||||
- schedule_timeout_interruptible(HZ / 10);
|
||||
+ ret = kernel_accept(iface->ksmbd_socket, &client_sk, 0);
|
||||
+ if (ret == -EINVAL)
|
||||
+ break;
|
||||
+ if (ret)
|
||||
continue;
|
||||
- }
|
||||
|
||||
if (!server_conf.max_ip_connections)
|
||||
goto skip_max_ip_conns_limit;
|
||||
@@ -474,10 +452,6 @@ static void tcp_destroy_socket(struct so
|
||||
if (!ksmbd_socket)
|
||||
return;
|
||||
|
||||
- /* set zero to timeout */
|
||||
- ksmbd_tcp_rcv_timeout(ksmbd_socket, 0);
|
||||
- ksmbd_tcp_snd_timeout(ksmbd_socket, 0);
|
||||
-
|
||||
ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR);
|
||||
if (ret)
|
||||
pr_err("Failed to shutdown socket: %d\n", ret);
|
||||
@@ -548,9 +522,6 @@ static int create_socket(struct interfac
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
- ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
|
||||
- ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
|
||||
-
|
||||
ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG);
|
||||
if (ret) {
|
||||
pr_err("Port listen() error: %d\n", ret);
|
||||
@@ -620,12 +591,11 @@ static int ksmbd_netdev_event(struct not
|
||||
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;
|
||||
- mutex_lock(&iface->sock_release_lock);
|
||||
- tcp_destroy_socket(iface->ksmbd_socket);
|
||||
+ sock_release(iface->ksmbd_socket);
|
||||
iface->ksmbd_socket = NULL;
|
||||
- mutex_unlock(&iface->sock_release_lock);
|
||||
|
||||
iface->state = IFACE_STATE_DOWN;
|
||||
break;
|
||||
@@ -688,7 +658,6 @@ static struct interface *alloc_iface(cha
|
||||
iface->name = ifname;
|
||||
iface->state = IFACE_STATE_DOWN;
|
||||
list_add(&iface->entry, &iface_list);
|
||||
- mutex_init(&iface->sock_release_lock);
|
||||
return iface;
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
||||
|
||||
--- a/fs/smb/server/transport_tcp.c
|
||||
+++ b/fs/smb/server/transport_tcp.c
|
||||
@@ -325,6 +325,12 @@ skip_max_ip_conns_limit:
|
||||
@@ -303,6 +303,12 @@ skip_max_ip_conns_limit:
|
||||
ksmbd_debug(CONN, "connect success: accepted new connection\n");
|
||||
client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
|
||||
client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
|
||||
|
||||
+3
-3
@@ -27,9 +27,9 @@ Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
||||
|
||||
--- a/fs/smb/server/transport_tcp.c
|
||||
+++ b/fs/smb/server/transport_tcp.c
|
||||
@@ -557,6 +557,12 @@ static int create_socket(struct interfac
|
||||
ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
|
||||
ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
|
||||
@@ -528,6 +528,12 @@ static int create_socket(struct interfac
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Accepted sockets inherit the listener's net reference. Keep TCP
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
From a506290f59e1c6ce9ac0a13158640bb8fee93471 Mon Sep 17 00:00:00 2001
|
||||
From: Namjae Jeon <linkinjeon@kernel.org>
|
||||
Date: Fri, 28 Aug 2026 09:24:49 +0900
|
||||
Subject: [PATCH] ksmbd: fix listener task lifetime on netdev events
|
||||
|
||||
The listener thread exits when its listening socket is shutdown. The
|
||||
netdevice notifier shuts down the socket before calling kthread_stop(), so
|
||||
the task_struct can be freed before kthread_stop() gets its reference.
|
||||
|
||||
Create the listener in a stopped state and hold an extra task_struct
|
||||
reference until kthread_stop_put() completes. Also stop and release
|
||||
listeners before freeing their interface records during TCP teardown.
|
||||
|
||||
Fixes: 3316a8fc840d ("ksmbd: server: avoid busy polling in accept loop")
|
||||
Reported-by: Farhad Alemi <farhad.alemi@berkeley.edu>
|
||||
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
||||
---
|
||||
fs/smb/server/transport_tcp.c | 36 ++++++++++++++++++++++++++---------
|
||||
1 file changed, 27 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/fs/smb/server/transport_tcp.c
|
||||
+++ b/fs/smb/server/transport_tcp.c
|
||||
@@ -39,6 +39,7 @@ struct tcp_transport {
|
||||
static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops;
|
||||
|
||||
static void tcp_stop_kthread(struct task_struct *kthread);
|
||||
+static void ksmbd_tcp_stop_listener(struct interface *iface);
|
||||
static struct interface *alloc_iface(char *ifname);
|
||||
static void ksmbd_tcp_disconnect(struct ksmbd_transport *t);
|
||||
|
||||
@@ -332,13 +333,20 @@ static int ksmbd_tcp_run_kthread(struct
|
||||
int rc;
|
||||
struct task_struct *kthread;
|
||||
|
||||
- kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
|
||||
- iface->name);
|
||||
+ kthread = kthread_create(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
|
||||
+ iface->name);
|
||||
if (IS_ERR(kthread)) {
|
||||
rc = PTR_ERR(kthread);
|
||||
return rc;
|
||||
}
|
||||
+
|
||||
+ /*
|
||||
+ * The listener can exit after its socket is shutdown, so keep the
|
||||
+ * task_struct alive until the caller has stopped it.
|
||||
+ */
|
||||
+ get_task_struct(kthread);
|
||||
iface->ksmbd_kthread = kthread;
|
||||
+ wake_up_process(kthread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -603,12 +611,7 @@ static int ksmbd_netdev_event(struct not
|
||||
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;
|
||||
-
|
||||
+ ksmbd_tcp_stop_listener(iface);
|
||||
iface->state = IFACE_STATE_DOWN;
|
||||
break;
|
||||
}
|
||||
@@ -636,11 +639,25 @@ static void tcp_stop_kthread(struct task
|
||||
if (!kthread)
|
||||
return;
|
||||
|
||||
- ret = kthread_stop(kthread);
|
||||
+ ret = kthread_stop_put(kthread);
|
||||
if (ret)
|
||||
pr_err("failed to stop forker thread\n");
|
||||
}
|
||||
|
||||
+static void ksmbd_tcp_stop_listener(struct interface *iface)
|
||||
+{
|
||||
+ if (iface->ksmbd_socket)
|
||||
+ kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR);
|
||||
+
|
||||
+ tcp_stop_kthread(iface->ksmbd_kthread);
|
||||
+ iface->ksmbd_kthread = NULL;
|
||||
+
|
||||
+ if (iface->ksmbd_socket) {
|
||||
+ sock_release(iface->ksmbd_socket);
|
||||
+ iface->ksmbd_socket = NULL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void ksmbd_tcp_destroy(void)
|
||||
{
|
||||
struct interface *iface, *tmp;
|
||||
@@ -648,6 +665,7 @@ void ksmbd_tcp_destroy(void)
|
||||
unregister_netdevice_notifier(&ksmbd_netdev_notifier);
|
||||
|
||||
list_for_each_entry_safe(iface, tmp, &iface_list, entry) {
|
||||
+ ksmbd_tcp_stop_listener(iface);
|
||||
list_del(&iface->entry);
|
||||
kfree(iface->name);
|
||||
kfree(iface);
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
From 3316a8fc840d82fad5efcf76ad0ea3f76fdca209 Mon Sep 17 00:00:00 2001
|
||||
From: Qingfang Deng <dqfext@gmail.com>
|
||||
Date: Mon, 17 Nov 2025 16:59:00 +0800
|
||||
Subject: [PATCH] ksmbd: server: avoid busy polling in accept loop
|
||||
|
||||
The ksmbd listener thread was using busy waiting on a listening socket by
|
||||
calling kernel_accept() with SOCK_NONBLOCK and retrying every 100ms on
|
||||
-EAGAIN. Since this thread is dedicated to accepting new connections,
|
||||
there is no need for non-blocking mode.
|
||||
|
||||
Switch to a blocking accept() call instead, allowing the thread to sleep
|
||||
until a new connection arrives. This avoids unnecessary wakeups and CPU
|
||||
usage. During teardown, call shutdown() on the listening socket so that
|
||||
accept() returns -EINVAL and the thread exits cleanly.
|
||||
|
||||
The socket release mutex is redundant because kthread_stop() blocks until
|
||||
the listener thread returns, guaranteeing safe teardown ordering.
|
||||
|
||||
Also remove sk_rcvtimeo and sk_sndtimeo assignments, which only caused
|
||||
accept() to return -EAGAIN prematurely.
|
||||
|
||||
Signed-off-by: Qingfang Deng <dqfext@gmail.com>
|
||||
Reviewed-by: Stefan Metzmacher <metze@samba.org>
|
||||
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
|
||||
Signed-off-by: Steve French <stfrench@microsoft.com>
|
||||
---
|
||||
fs/smb/server/transport_tcp.c | 41 +++++------------------------------
|
||||
1 file changed, 6 insertions(+), 35 deletions(-)
|
||||
|
||||
--- a/fs/smb/server/transport_tcp.c
|
||||
+++ b/fs/smb/server/transport_tcp.c
|
||||
@@ -22,7 +22,6 @@ struct interface {
|
||||
struct socket *ksmbd_socket;
|
||||
struct list_head entry;
|
||||
char *name;
|
||||
- struct mutex sock_release_lock;
|
||||
int state;
|
||||
};
|
||||
|
||||
@@ -57,19 +56,6 @@ static inline void ksmbd_tcp_reuseaddr(s
|
||||
sock_set_reuseaddr(sock->sk);
|
||||
}
|
||||
|
||||
-static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs)
|
||||
-{
|
||||
- if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1)
|
||||
- WRITE_ONCE(sock->sk->sk_rcvtimeo, secs * HZ);
|
||||
- else
|
||||
- WRITE_ONCE(sock->sk->sk_rcvtimeo, MAX_SCHEDULE_TIMEOUT);
|
||||
-}
|
||||
-
|
||||
-static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs)
|
||||
-{
|
||||
- sock_set_sndtimeo(sock->sk, secs);
|
||||
-}
|
||||
-
|
||||
static struct tcp_transport *alloc_transport(struct socket *client_sk)
|
||||
{
|
||||
struct tcp_transport *t;
|
||||
@@ -239,20 +225,14 @@ static int ksmbd_kthread_fn(void *p)
|
||||
unsigned int max_ip_conns;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
- mutex_lock(&iface->sock_release_lock);
|
||||
if (!iface->ksmbd_socket) {
|
||||
- mutex_unlock(&iface->sock_release_lock);
|
||||
break;
|
||||
}
|
||||
- ret = kernel_accept(iface->ksmbd_socket, &client_sk,
|
||||
- SOCK_NONBLOCK);
|
||||
- mutex_unlock(&iface->sock_release_lock);
|
||||
- if (ret) {
|
||||
- if (ret == -EAGAIN)
|
||||
- /* check for new connections every 100 msecs */
|
||||
- schedule_timeout_interruptible(HZ / 10);
|
||||
+ ret = kernel_accept(iface->ksmbd_socket, &client_sk, 0);
|
||||
+ if (ret == -EINVAL)
|
||||
+ break;
|
||||
+ if (ret)
|
||||
continue;
|
||||
- }
|
||||
|
||||
if (!server_conf.max_ip_connections)
|
||||
goto skip_max_ip_conns_limit;
|
||||
@@ -461,10 +441,6 @@ static void tcp_destroy_socket(struct so
|
||||
if (!ksmbd_socket)
|
||||
return;
|
||||
|
||||
- /* set zero to timeout */
|
||||
- ksmbd_tcp_rcv_timeout(ksmbd_socket, 0);
|
||||
- ksmbd_tcp_snd_timeout(ksmbd_socket, 0);
|
||||
-
|
||||
ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR);
|
||||
if (ret)
|
||||
pr_err("Failed to shutdown socket: %d\n", ret);
|
||||
@@ -535,9 +511,6 @@ static int create_socket(struct interfac
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
- ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
|
||||
- ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
|
||||
-
|
||||
ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG);
|
||||
if (ret) {
|
||||
pr_err("Port listen() error: %d\n", ret);
|
||||
@@ -607,12 +580,11 @@ static int ksmbd_netdev_event(struct not
|
||||
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;
|
||||
- mutex_lock(&iface->sock_release_lock);
|
||||
- tcp_destroy_socket(iface->ksmbd_socket);
|
||||
+ sock_release(iface->ksmbd_socket);
|
||||
iface->ksmbd_socket = NULL;
|
||||
- mutex_unlock(&iface->sock_release_lock);
|
||||
|
||||
iface->state = IFACE_STATE_DOWN;
|
||||
break;
|
||||
@@ -675,7 +647,6 @@ static struct interface *alloc_iface(cha
|
||||
iface->name = ifname;
|
||||
iface->state = IFACE_STATE_DOWN;
|
||||
list_add(&iface->entry, &iface_list);
|
||||
- mutex_init(&iface->sock_release_lock);
|
||||
return iface;
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
||||
|
||||
--- a/fs/smb/server/transport_tcp.c
|
||||
+++ b/fs/smb/server/transport_tcp.c
|
||||
@@ -312,6 +312,12 @@ skip_max_ip_conns_limit:
|
||||
@@ -292,6 +292,12 @@ skip_max_ip_conns_limit:
|
||||
ksmbd_debug(CONN, "connect success: accepted new connection\n");
|
||||
client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
|
||||
client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
|
||||
|
||||
+3
-3
@@ -27,9 +27,9 @@ Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
||||
|
||||
--- a/fs/smb/server/transport_tcp.c
|
||||
+++ b/fs/smb/server/transport_tcp.c
|
||||
@@ -544,6 +544,12 @@ static int create_socket(struct interfac
|
||||
ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
|
||||
ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
|
||||
@@ -517,6 +517,12 @@ static int create_socket(struct interfac
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Accepted sockets inherit the listener's net reference. Keep TCP
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
From a506290f59e1c6ce9ac0a13158640bb8fee93471 Mon Sep 17 00:00:00 2001
|
||||
From: Namjae Jeon <linkinjeon@kernel.org>
|
||||
Date: Fri, 28 Aug 2026 09:24:49 +0900
|
||||
Subject: [PATCH] ksmbd: fix listener task lifetime on netdev events
|
||||
|
||||
The listener thread exits when its listening socket is shutdown. The
|
||||
netdevice notifier shuts down the socket before calling kthread_stop(), so
|
||||
the task_struct can be freed before kthread_stop() gets its reference.
|
||||
|
||||
Create the listener in a stopped state and hold an extra task_struct
|
||||
reference until kthread_stop_put() completes. Also stop and release
|
||||
listeners before freeing their interface records during TCP teardown.
|
||||
|
||||
Fixes: 3316a8fc840d ("ksmbd: server: avoid busy polling in accept loop")
|
||||
Reported-by: Farhad Alemi <farhad.alemi@berkeley.edu>
|
||||
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
||||
---
|
||||
fs/smb/server/transport_tcp.c | 36 ++++++++++++++++++++++++++---------
|
||||
1 file changed, 27 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/fs/smb/server/transport_tcp.c
|
||||
+++ b/fs/smb/server/transport_tcp.c
|
||||
@@ -39,6 +39,7 @@ struct tcp_transport {
|
||||
static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops;
|
||||
|
||||
static void tcp_stop_kthread(struct task_struct *kthread);
|
||||
+static void ksmbd_tcp_stop_listener(struct interface *iface);
|
||||
static struct interface *alloc_iface(char *ifname);
|
||||
static void ksmbd_tcp_disconnect(struct ksmbd_transport *t);
|
||||
|
||||
@@ -321,13 +322,20 @@ static int ksmbd_tcp_run_kthread(struct
|
||||
int rc;
|
||||
struct task_struct *kthread;
|
||||
|
||||
- kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
|
||||
- iface->name);
|
||||
+ kthread = kthread_create(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
|
||||
+ iface->name);
|
||||
if (IS_ERR(kthread)) {
|
||||
rc = PTR_ERR(kthread);
|
||||
return rc;
|
||||
}
|
||||
+
|
||||
+ /*
|
||||
+ * The listener can exit after its socket is shutdown, so keep the
|
||||
+ * task_struct alive until the caller has stopped it.
|
||||
+ */
|
||||
+ get_task_struct(kthread);
|
||||
iface->ksmbd_kthread = kthread;
|
||||
+ wake_up_process(kthread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -592,12 +600,7 @@ static int ksmbd_netdev_event(struct not
|
||||
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;
|
||||
-
|
||||
+ ksmbd_tcp_stop_listener(iface);
|
||||
iface->state = IFACE_STATE_DOWN;
|
||||
break;
|
||||
}
|
||||
@@ -625,11 +628,25 @@ static void tcp_stop_kthread(struct task
|
||||
if (!kthread)
|
||||
return;
|
||||
|
||||
- ret = kthread_stop(kthread);
|
||||
+ ret = kthread_stop_put(kthread);
|
||||
if (ret)
|
||||
pr_err("failed to stop forker thread\n");
|
||||
}
|
||||
|
||||
+static void ksmbd_tcp_stop_listener(struct interface *iface)
|
||||
+{
|
||||
+ if (iface->ksmbd_socket)
|
||||
+ kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR);
|
||||
+
|
||||
+ tcp_stop_kthread(iface->ksmbd_kthread);
|
||||
+ iface->ksmbd_kthread = NULL;
|
||||
+
|
||||
+ if (iface->ksmbd_socket) {
|
||||
+ sock_release(iface->ksmbd_socket);
|
||||
+ iface->ksmbd_socket = NULL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void ksmbd_tcp_destroy(void)
|
||||
{
|
||||
struct interface *iface, *tmp;
|
||||
@@ -637,6 +654,7 @@ void ksmbd_tcp_destroy(void)
|
||||
unregister_netdevice_notifier(&ksmbd_netdev_notifier);
|
||||
|
||||
list_for_each_entry_safe(iface, tmp, &iface_list, entry) {
|
||||
+ ksmbd_tcp_stop_listener(iface);
|
||||
list_del(&iface->entry);
|
||||
kfree(iface->name);
|
||||
kfree(iface);
|
||||
Reference in New Issue
Block a user