From ac5399d48616644cb6ddfe39f8babe807d5f5cbd Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Wed, 19 Jun 2024 16:17:53 +0000 Subject: [PATCH 1/4] ksmbd: remove duplicate SMB2 Oplock levels definitions smb/common already have SMB2 Oplock levels definitions, remove duplicate definitions in server. Signed-off-by: ChenXiaoSong Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/oplock.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h index e9da63f25b20..72bc88a63a40 100644 --- a/fs/smb/server/oplock.h +++ b/fs/smb/server/oplock.h @@ -11,13 +11,6 @@ #define OPLOCK_WAIT_TIME (35 * HZ) -/* SMB2 Oplock levels */ -#define SMB2_OPLOCK_LEVEL_NONE 0x00 -#define SMB2_OPLOCK_LEVEL_II 0x01 -#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 -#define SMB2_OPLOCK_LEVEL_BATCH 0x09 -#define SMB2_OPLOCK_LEVEL_LEASE 0xFF - /* Oplock states */ #define OPLOCK_STATE_NONE 0x00 #define OPLOCK_ACK_WAIT 0x01 From 051d469be1b355ffc62fcd9c82195230c780228a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 23 Jun 2024 20:07:58 +0200 Subject: [PATCH 2/4] ksmbd: Constify struct ksmbd_transport_ops 'struct ksmbd_transport_ops' is not modified in these drivers. Constifying this structure moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig, as an example: Before: ====== text data bss dec hex filename 52184 2085 256 54525 d4fd fs/smb/server/transport_rdma.o After: ===== text data bss dec hex filename 52260 2021 256 54537 d509 fs/smb/server/transport_rdma.o Signed-off-by: Christophe JAILLET Acked-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/connection.h | 4 ++-- fs/smb/server/transport_rdma.c | 4 ++-- fs/smb/server/transport_tcp.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index 0e04cf8b1d89..5c2845e47cf2 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -133,8 +133,8 @@ struct ksmbd_transport_ops { }; struct ksmbd_transport { - struct ksmbd_conn *conn; - struct ksmbd_transport_ops *ops; + struct ksmbd_conn *conn; + const struct ksmbd_transport_ops *ops; }; #define KSMBD_TCP_RECV_TIMEOUT (7 * HZ) diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c index 8faa25c6e129..cf4418f72772 100644 --- a/fs/smb/server/transport_rdma.c +++ b/fs/smb/server/transport_rdma.c @@ -164,7 +164,7 @@ enum { SMB_DIRECT_MSG_DATA_TRANSFER }; -static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops; +static const struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops; struct smb_direct_send_ctx { struct list_head msg_list; @@ -2292,7 +2292,7 @@ out: return rdma_capable; } -static struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { +static const struct ksmbd_transport_ops ksmbd_smb_direct_transport_ops = { .prepare = smb_direct_prepare, .disconnect = smb_direct_disconnect, .shutdown = smb_direct_shutdown, diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c index 6633fa78e9b9..a84788396daa 100644 --- a/fs/smb/server/transport_tcp.c +++ b/fs/smb/server/transport_tcp.c @@ -37,7 +37,7 @@ struct tcp_transport { unsigned int nr_iov; }; -static struct ksmbd_transport_ops ksmbd_tcp_transport_ops; +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); @@ -649,7 +649,7 @@ int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz) return 0; } -static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = { +static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops = { .read = ksmbd_tcp_read, .writev = ksmbd_tcp_writev, .disconnect = ksmbd_tcp_disconnect, From 520da3c488c5bb177871634e713eb8a106082e6b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 21 May 2024 22:57:52 +0900 Subject: [PATCH 3/4] ksmbd: avoid reclaiming expired durable opens by the client The expired durable opens should not be reclaimed by client. This patch add ->durable_scavenger_timeout to fp and check it in ksmbd_lookup_durable_fd(). Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 9 ++++++++- fs/smb/server/vfs_cache.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 8b2e37c8716e..baf7f67ec212 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -477,7 +477,10 @@ struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) struct ksmbd_file *fp; fp = __ksmbd_lookup_fd(&global_ft, id); - if (fp && fp->conn) { + if (fp && (fp->conn || + (fp->durable_scavenger_timeout && + (fp->durable_scavenger_timeout < + jiffies_to_msecs(jiffies))))) { ksmbd_put_durable_fd(fp); fp = NULL; } @@ -718,6 +721,10 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, fp->tcon = NULL; fp->volatile_id = KSMBD_NO_FID; + if (fp->durable_timeout) + fp->durable_scavenger_timeout = + jiffies_to_msecs(jiffies) + fp->durable_timeout; + return true; } diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 5a225e7055f1..f2ab1514e81a 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -101,6 +101,7 @@ struct ksmbd_file { struct list_head lock_list; int durable_timeout; + int durable_scavenger_timeout; /* if ls is happening on directory, below is valid*/ struct ksmbd_readdir_data readdir_data; From d484d621d40f4a8b8959008802d79bef3609641b Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 15 Jul 2024 08:29:39 +0900 Subject: [PATCH 4/4] ksmbd: add durable scavenger timer Launch ksmbd-durable-scavenger kernel thread to scan durable fps that have not been reclaimed by a client within the configured time. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/mgmt/user_session.c | 2 + fs/smb/server/server.c | 1 + fs/smb/server/server.h | 1 + fs/smb/server/smb2pdu.c | 2 +- fs/smb/server/smb2pdu.h | 2 + fs/smb/server/vfs_cache.c | 164 +++++++++++++++++++++++++++++- fs/smb/server/vfs_cache.h | 2 + 7 files changed, 168 insertions(+), 6 deletions(-) diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index aec0a7a12405..162a12685d2c 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -149,6 +149,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) ksmbd_tree_conn_session_logoff(sess); ksmbd_destroy_file_table(&sess->file_table); + ksmbd_launch_ksmbd_durable_scavenger(); ksmbd_session_rpc_clear_list(sess); free_channel_list(sess); kfree(sess->Preauth_HashValue); @@ -326,6 +327,7 @@ void destroy_previous_session(struct ksmbd_conn *conn, ksmbd_destroy_file_table(&prev_sess->file_table); prev_sess->state = SMB2_SESSION_EXPIRED; + ksmbd_launch_ksmbd_durable_scavenger(); out: up_write(&conn->session_lock); up_write(&sessions_table_lock); diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index c67fbc8d6683..4d24cc105ef6 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -377,6 +377,7 @@ static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) { ksmbd_ipc_soft_reset(); ksmbd_conn_transport_destroy(); + ksmbd_stop_durable_scavenger(); server_conf_free(); server_conf_init(); WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); diff --git a/fs/smb/server/server.h b/fs/smb/server/server.h index db7278181760..4fc529335271 100644 --- a/fs/smb/server/server.h +++ b/fs/smb/server/server.h @@ -44,6 +44,7 @@ struct ksmbd_server_config { unsigned int max_connections; char *conf[SERVER_CONF_WORK_GROUP + 1]; + struct task_struct *dh_task; }; extern struct ksmbd_server_config server_conf; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 840c71c66b30..37a39ab4ee65 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3526,7 +3526,7 @@ int smb2_open(struct ksmbd_work *work) SMB2_CREATE_GUID_SIZE); if (dh_info.timeout) fp->durable_timeout = min(dh_info.timeout, - 300000); + DURABLE_HANDLE_MAX_TIMEOUT); else fp->durable_timeout = 60; } diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h index 643f5e1cfe35..3be7d5ae65a8 100644 --- a/fs/smb/server/smb2pdu.h +++ b/fs/smb/server/smb2pdu.h @@ -72,6 +72,8 @@ struct create_durable_req_v2 { __u8 CreateGuid[16]; } __packed; +#define DURABLE_HANDLE_MAX_TIMEOUT 300000 + struct create_durable_reconn_req { struct create_context_hdr ccontext; __u8 Name[8]; diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index baf7f67ec212..4d4ee696e37c 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "glob.h" #include "vfs_cache.h" @@ -17,6 +19,7 @@ #include "mgmt/tree_connect.h" #include "mgmt/user_session.h" #include "smb_common.h" +#include "server.h" #define S_DEL_PENDING 1 #define S_DEL_ON_CLS 2 @@ -31,6 +34,10 @@ static struct ksmbd_file_table global_ft; static atomic_long_t fd_limit; static struct kmem_cache *filp_cache; +static bool durable_scavenger_running; +static DEFINE_MUTEX(durable_scavenger_lock); +static wait_queue_head_t dh_wq; + void ksmbd_set_fd_limit(unsigned long limit) { limit = min(limit, get_max_files()); @@ -280,9 +287,16 @@ static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) if (!has_file_id(fp->persistent_id)) return; - write_lock(&global_ft.lock); idr_remove(global_ft.idr, fp->persistent_id); +} + +static void ksmbd_remove_durable_fd(struct ksmbd_file *fp) +{ + write_lock(&global_ft.lock); + __ksmbd_remove_durable_fd(fp); write_unlock(&global_ft.lock); + if (waitqueue_active(&dh_wq)) + wake_up(&dh_wq); } static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) @@ -305,7 +319,7 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) struct ksmbd_lock *smb_lock, *tmp_lock; fd_limit_close(); - __ksmbd_remove_durable_fd(fp); + ksmbd_remove_durable_fd(fp); if (ft) __ksmbd_remove_fd(ft, fp); @@ -697,6 +711,142 @@ static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon, return fp->tcon != tcon; } +static bool ksmbd_durable_scavenger_alive(void) +{ + mutex_lock(&durable_scavenger_lock); + if (!durable_scavenger_running) { + mutex_unlock(&durable_scavenger_lock); + return false; + } + mutex_unlock(&durable_scavenger_lock); + + if (kthread_should_stop()) + return false; + + if (idr_is_empty(global_ft.idr)) + return false; + + return true; +} + +static void ksmbd_scavenger_dispose_dh(struct list_head *head) +{ + while (!list_empty(head)) { + struct ksmbd_file *fp; + + fp = list_first_entry(head, struct ksmbd_file, node); + list_del_init(&fp->node); + __ksmbd_close_fd(NULL, fp); + } +} + +static int ksmbd_durable_scavenger(void *dummy) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + unsigned int min_timeout = 1; + bool found_fp_timeout; + LIST_HEAD(scavenger_list); + unsigned long remaining_jiffies; + + __module_get(THIS_MODULE); + + set_freezable(); + while (ksmbd_durable_scavenger_alive()) { + if (try_to_freeze()) + continue; + + found_fp_timeout = false; + + remaining_jiffies = wait_event_timeout(dh_wq, + ksmbd_durable_scavenger_alive() == false, + __msecs_to_jiffies(min_timeout)); + if (remaining_jiffies) + min_timeout = jiffies_to_msecs(remaining_jiffies); + else + min_timeout = DURABLE_HANDLE_MAX_TIMEOUT; + + write_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + if (!fp->durable_timeout) + continue; + + if (atomic_read(&fp->refcount) > 1 || + fp->conn) + continue; + + found_fp_timeout = true; + if (fp->durable_scavenger_timeout <= + jiffies_to_msecs(jiffies)) { + __ksmbd_remove_durable_fd(fp); + list_add(&fp->node, &scavenger_list); + } else { + unsigned long durable_timeout; + + durable_timeout = + fp->durable_scavenger_timeout - + jiffies_to_msecs(jiffies); + + if (min_timeout > durable_timeout) + min_timeout = durable_timeout; + } + } + write_unlock(&global_ft.lock); + + ksmbd_scavenger_dispose_dh(&scavenger_list); + + if (found_fp_timeout == false) + break; + } + + mutex_lock(&durable_scavenger_lock); + durable_scavenger_running = false; + mutex_unlock(&durable_scavenger_lock); + + module_put(THIS_MODULE); + + return 0; +} + +void ksmbd_launch_ksmbd_durable_scavenger(void) +{ + if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)) + return; + + mutex_lock(&durable_scavenger_lock); + if (durable_scavenger_running == true) { + mutex_unlock(&durable_scavenger_lock); + return; + } + + durable_scavenger_running = true; + + server_conf.dh_task = kthread_run(ksmbd_durable_scavenger, + (void *)NULL, "ksmbd-durable-scavenger"); + if (IS_ERR(server_conf.dh_task)) + pr_err("cannot start conn thread, err : %ld\n", + PTR_ERR(server_conf.dh_task)); + mutex_unlock(&durable_scavenger_lock); +} + +void ksmbd_stop_durable_scavenger(void) +{ + if (!(server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)) + return; + + mutex_lock(&durable_scavenger_lock); + if (!durable_scavenger_running) { + mutex_unlock(&durable_scavenger_lock); + return; + } + + durable_scavenger_running = false; + if (waitqueue_active(&dh_wq)) + wake_up(&dh_wq); + mutex_unlock(&durable_scavenger_lock); + kthread_stop(server_conf.dh_task); +} + static bool session_fd_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp) { @@ -757,11 +907,12 @@ void ksmbd_free_global_file_table(void) unsigned int id; idr_for_each_entry(global_ft.idr, fp, id) { - __ksmbd_remove_durable_fd(fp); - kmem_cache_free(filp_cache, fp); + ksmbd_remove_durable_fd(fp); + __ksmbd_close_fd(NULL, fp); } - ksmbd_destroy_file_table(&global_ft); + idr_destroy(global_ft.idr); + kfree(global_ft.idr); } int ksmbd_validate_name_reconnect(struct ksmbd_share_config *share, @@ -817,6 +968,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) } up_write(&ci->m_lock); + fp->f_state = FP_NEW; __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID); if (!has_file_id(fp->volatile_id)) { fp->conn = NULL; @@ -856,6 +1008,8 @@ int ksmbd_init_file_cache(void) if (!filp_cache) goto out; + init_waitqueue_head(&dh_wq); + return 0; out: diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index f2ab1514e81a..b0f6d0f94cb8 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -153,6 +153,8 @@ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); +void ksmbd_launch_ksmbd_durable_scavenger(void); +void ksmbd_stop_durable_scavenger(void); void ksmbd_close_tree_conn_fds(struct ksmbd_work *work); void ksmbd_close_session_fds(struct ksmbd_work *work); int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode);