diff mbox series

[2/3] ksmbd: add durable scavenger timer

Message ID 20240610141416.8039-2-linkinjeon@kernel.org
State New
Headers show
Series [1/3] ksmbd: avoid reclaiming expired durable opens by the client | expand

Commit Message

Namjae Jeon June 10, 2024, 2:14 p.m. UTC
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 <linkinjeon@kernel.org>
---
 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         | 165 +++++++++++++++++++++++++++++-
 fs/smb/server/vfs_cache.h         |   2 +
 7 files changed, 169 insertions(+), 6 deletions(-)

Comments

Steve French June 10, 2024, 3:30 p.m. UTC | #1
Minor compile warning was generated by this:

/home/smfrench/smb3-kernel/fs/smb/server/vfs_cache.c:39:19: warning:
symbol 'dh_task' was not declared. Should it be static?
/home/smfrench/smb3-kernel/fs/smb/server/vfs_cache.c:40:19: warning:
symbol 'dh_wq' was not declared. Should it be static?

On Mon, Jun 10, 2024 at 9:14 AM Namjae Jeon <linkinjeon@kernel.org> wrote:
>
> 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 <linkinjeon@kernel.org>
> ---
>  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         | 165 +++++++++++++++++++++++++++++-
>  fs/smb/server/vfs_cache.h         |   2 +
>  7 files changed, 169 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 b6c5a8ea3887..4fb5070d3dc5 100644
> --- a/fs/smb/server/smb2pdu.c
> +++ b/fs/smb/server/smb2pdu.c
> @@ -3519,7 +3519,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 a6804545db28..882a87f9e3ab 100644
> --- a/fs/smb/server/vfs_cache.c
> +++ b/fs/smb/server/vfs_cache.c
> @@ -8,6 +8,8 @@
>  #include <linux/filelock.h>
>  #include <linux/slab.h>
>  #include <linux/vmalloc.h>
> +#include <linux/kthread.h>
> +#include <linux/freezer.h>
>
>  #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,11 @@ 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);
> +struct task_struc *dh_task;
> +wait_queue_head_t dh_wq;
> +
>  void ksmbd_set_fd_limit(unsigned long limit)
>  {
>         limit = min(limit, get_max_files());
> @@ -279,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)
> @@ -304,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);
>
> @@ -696,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)
>  {
> @@ -756,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,
> @@ -816,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;
> @@ -855,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);
> --
> 2.25.1
>
Namjae Jeon June 11, 2024, 1:05 p.m. UTC | #2
2024년 6월 11일 (화) 오전 12:31, Steve French <smfrench@gmail.com>님이 작성:
>
> Minor compile warning was generated by this:
Okay, I will fix it.
Thanks for your review:)
>
> /home/smfrench/smb3-kernel/fs/smb/server/vfs_cache.c:39:19: warning:
> symbol 'dh_task' was not declared. Should it be static?
> /home/smfrench/smb3-kernel/fs/smb/server/vfs_cache.c:40:19: warning:
> symbol 'dh_wq' was not declared. Should it be static?
>
> On Mon, Jun 10, 2024 at 9:14 AM Namjae Jeon <linkinjeon@kernel.org> wrote:
> >
> > 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 <linkinjeon@kernel.org>
> > ---
> >  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         | 165 +++++++++++++++++++++++++++++-
> >  fs/smb/server/vfs_cache.h         |   2 +
> >  7 files changed, 169 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 b6c5a8ea3887..4fb5070d3dc5 100644
> > --- a/fs/smb/server/smb2pdu.c
> > +++ b/fs/smb/server/smb2pdu.c
> > @@ -3519,7 +3519,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 a6804545db28..882a87f9e3ab 100644
> > --- a/fs/smb/server/vfs_cache.c
> > +++ b/fs/smb/server/vfs_cache.c
> > @@ -8,6 +8,8 @@
> >  #include <linux/filelock.h>
> >  #include <linux/slab.h>
> >  #include <linux/vmalloc.h>
> > +#include <linux/kthread.h>
> > +#include <linux/freezer.h>
> >
> >  #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,11 @@ 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);
> > +struct task_struc *dh_task;
> > +wait_queue_head_t dh_wq;
> > +
> >  void ksmbd_set_fd_limit(unsigned long limit)
> >  {
> >         limit = min(limit, get_max_files());
> > @@ -279,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)
> > @@ -304,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);
> >
> > @@ -696,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)
> >  {
> > @@ -756,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,
> > @@ -816,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;
> > @@ -855,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);
> > --
> > 2.25.1
> >
>
>
> --
> Thanks,
>
> Steve
diff mbox series

Patch

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 b6c5a8ea3887..4fb5070d3dc5 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -3519,7 +3519,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 a6804545db28..882a87f9e3ab 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -8,6 +8,8 @@ 
 #include <linux/filelock.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
 
 #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,11 @@  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);
+struct task_struc *dh_task;
+wait_queue_head_t dh_wq;
+
 void ksmbd_set_fd_limit(unsigned long limit)
 {
 	limit = min(limit, get_max_files());
@@ -279,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)
@@ -304,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);
 
@@ -696,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)
 {
@@ -756,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,
@@ -816,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;
@@ -855,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);