Replace sessions list in connection with xarray.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
        list_del(&conn->conns_list);
        write_unlock(&conn_list_lock);
 
+       xa_destroy(&conn->sessions);
        kvfree(conn->request_buf);
        kfree(conn->preauth_info);
        kfree(conn);
 
        init_waitqueue_head(&conn->req_running_q);
        INIT_LIST_HEAD(&conn->conns_list);
-       INIT_LIST_HEAD(&conn->sessions);
        INIT_LIST_HEAD(&conn->requests);
        INIT_LIST_HEAD(&conn->async_requests);
        spin_lock_init(&conn->request_lock);
        spin_lock_init(&conn->credits_lock);
        ida_init(&conn->async_ida);
+       xa_init(&conn->sessions);
 
        spin_lock_init(&conn->llist_lock);
        INIT_LIST_HEAD(&conn->lock_list);
 
        struct nls_table                *local_nls;
        struct list_head                conns_list;
        /* smb session 1 per user */
-       struct list_head                sessions;
+       struct xarray                   sessions;
        unsigned long                   last_active;
        /* How many request are running currently */
        atomic_t                        req_running;
 
        if (!atomic_dec_and_test(&sess->refcnt))
                return;
 
-       list_del(&sess->sessions_entry);
-
        down_write(&sessions_table_lock);
        hash_del(&sess->hlist);
        up_write(&sessions_table_lock);
        return NULL;
 }
 
-void ksmbd_session_register(struct ksmbd_conn *conn,
-                           struct ksmbd_session *sess)
+int ksmbd_session_register(struct ksmbd_conn *conn,
+                          struct ksmbd_session *sess)
 {
        sess->conn = conn;
-       list_add(&sess->sessions_entry, &conn->sessions);
+       return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL));
 }
 
 void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
 {
        struct ksmbd_session *sess;
+       unsigned long id;
 
-       while (!list_empty(&conn->sessions)) {
-               sess = list_entry(conn->sessions.next,
-                                 struct ksmbd_session,
-                                 sessions_entry);
-
+       xa_for_each(&conn->sessions, id, sess) {
+               xa_erase(&conn->sessions, sess->id);
                ksmbd_session_destroy(sess);
        }
 }
 
-static bool ksmbd_session_id_match(struct ksmbd_session *sess,
-                                  unsigned long long id)
-{
-       return sess->id == id;
-}
-
 struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
                                           unsigned long long id)
 {
-       struct ksmbd_session *sess = NULL;
-
-       list_for_each_entry(sess, &conn->sessions, sessions_entry) {
-               if (ksmbd_session_id_match(sess, id))
-                       return sess;
-       }
-       return NULL;
+       return xa_load(&conn->sessions, id);
 }
 
 int get_session(struct ksmbd_session *sess)
                goto error;
 
        set_session_flag(sess, protocol);
-       INIT_LIST_HEAD(&sess->sessions_entry);
        xa_init(&sess->tree_conns);
        INIT_LIST_HEAD(&sess->ksmbd_chann_list);
        INIT_LIST_HEAD(&sess->rpc_handle_list);
 
        __u8                            smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE];
        __u8                            smb3signingkey[SMB3_SIGN_KEY_SIZE];
 
-       struct list_head                sessions_entry;
        struct ksmbd_file_table         file_table;
        atomic_t                        refcnt;
 };
 struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id);
 struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
                                           unsigned long long id);
-void ksmbd_session_register(struct ksmbd_conn *conn,
-                           struct ksmbd_session *sess);
+int ksmbd_session_register(struct ksmbd_conn *conn,
+                          struct ksmbd_session *sess);
 void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
 struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
                                               unsigned long long id);
 
        return -EINVAL;
 }
 
-static void destroy_previous_session(struct ksmbd_user *user, u64 id)
+static void destroy_previous_session(struct ksmbd_conn *conn,
+                                    struct ksmbd_user *user, u64 id)
 {
        struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id);
        struct ksmbd_user *prev_user;
        }
 
        put_session(prev_sess);
+       xa_erase(&conn->sessions, prev_sess->id);
        ksmbd_session_destroy(prev_sess);
 }
 
        /* Check for previous session */
        prev_id = le64_to_cpu(req->PreviousSessionId);
        if (prev_id && prev_id != sess->id)
-               destroy_previous_session(user, prev_id);
+               destroy_previous_session(conn, user, prev_id);
 
        if (sess->state == SMB2_SESSION_VALID) {
                /*
        /* Check previous session */
        prev_sess_id = le64_to_cpu(req->PreviousSessionId);
        if (prev_sess_id && prev_sess_id != sess->id)
-               destroy_previous_session(sess->user, prev_sess_id);
+               destroy_previous_session(conn, sess->user, prev_sess_id);
 
        if (sess->state == SMB2_SESSION_VALID)
                ksmbd_free_user(sess->user);
                        goto out_err;
                }
                rsp->hdr.SessionId = cpu_to_le64(sess->id);
-               ksmbd_session_register(conn, sess);
+               rc = ksmbd_session_register(conn, sess);
+               if (rc)
+                       goto out_err;
        } else if (conn->dialect >= SMB30_PROT_ID &&
                   (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL) &&
                   req->Flags & SMB2_SESSION_REQ_FLAG_BINDING) {
                        if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION)
                                try_delay = true;
 
+                       xa_erase(&conn->sessions, sess->id);
                        ksmbd_session_destroy(sess);
                        work->sess = NULL;
                        if (try_delay)