mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 05:04:02 +02:00
ksmbd: scope session state changes to bound connections
ksmbd_all_conn_set_status() treats every connection whose transient binding flag is set as belonging to the target SessionId. A logoff or session replacement can consequently move an unrelated connection to NEED_RECONNECT or NEED_SETUP. Pass the target session itself and select connections using either the connection-local session xarray or the session's permanent channel list. Use the same association test while waiting for requests to drain. Serialize session-wide status changes under request_lock and do not overwrite EXITING or RELEASING. Protect the shutdown transition with the same lock so a concurrent session update cannot revive a closing connection. Fixes:f5a544e3ba("ksmbd: add support for SMB3 multichannel") Fixes:abcc506a9a("ksmbd: fix racy issue from smb2 close and logoff with multichannel") Fixes:c444139cb7("ksmbd: rewrite stop_sessions() with restartable iteration") Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
2cbd4a8bf4
commit
c50e628122
|
|
@ -404,15 +404,37 @@ void ksmbd_conn_unlock(struct ksmbd_conn *conn)
|
|||
mutex_unlock(&conn->srv_mutex);
|
||||
}
|
||||
|
||||
void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
|
||||
static bool ksmbd_session_is_bound_to_conn(struct ksmbd_session *sess,
|
||||
struct ksmbd_conn *conn)
|
||||
{
|
||||
bool found;
|
||||
|
||||
rcu_read_lock();
|
||||
found = xa_load(&conn->sessions, sess->id) == sess;
|
||||
rcu_read_unlock();
|
||||
if (found)
|
||||
return true;
|
||||
|
||||
down_read(&sess->chann_lock);
|
||||
found = xa_load(&sess->ksmbd_chann_list, (long)conn);
|
||||
up_read(&sess->chann_lock);
|
||||
return found;
|
||||
}
|
||||
|
||||
void ksmbd_all_conn_set_status(struct ksmbd_session *sess, u32 status)
|
||||
{
|
||||
struct ksmbd_conn *conn;
|
||||
int bkt;
|
||||
|
||||
down_read(&conn_list_lock);
|
||||
hash_for_each(conn_list, bkt, conn, hlist) {
|
||||
if (conn->binding || xa_load(&conn->sessions, sess_id))
|
||||
WRITE_ONCE(conn->status, status);
|
||||
if (ksmbd_session_is_bound_to_conn(sess, conn)) {
|
||||
spin_lock(&conn->request_lock);
|
||||
if (!ksmbd_conn_exiting(conn) &&
|
||||
!ksmbd_conn_releasing(conn))
|
||||
WRITE_ONCE(conn->status, status);
|
||||
spin_unlock(&conn->request_lock);
|
||||
}
|
||||
}
|
||||
up_read(&conn_list_lock);
|
||||
}
|
||||
|
|
@ -422,7 +444,8 @@ void ksmbd_conn_wait_idle(struct ksmbd_conn *conn)
|
|||
wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);
|
||||
}
|
||||
|
||||
int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)
|
||||
int ksmbd_conn_wait_idle_sess(struct ksmbd_conn *curr_conn,
|
||||
struct ksmbd_session *sess)
|
||||
{
|
||||
struct ksmbd_conn *conn;
|
||||
int rc, retry_count = 0, max_timeout = 120;
|
||||
|
|
@ -434,7 +457,7 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)
|
|||
|
||||
down_read(&conn_list_lock);
|
||||
hash_for_each(conn_list, bkt, conn, hlist) {
|
||||
if (conn->binding || xa_load(&conn->sessions, sess_id)) {
|
||||
if (ksmbd_session_is_bound_to_conn(sess, conn)) {
|
||||
rcount = (conn == curr_conn) ? 2 : 1;
|
||||
if (atomic_read(&conn->req_running) >= rcount) {
|
||||
rc = wait_event_timeout(conn->req_running_q,
|
||||
|
|
@ -780,8 +803,10 @@ static void stop_sessions(void)
|
|||
* handler exited its receive loop for an unrelated
|
||||
* reason).
|
||||
*/
|
||||
if (READ_ONCE(conn->status) != KSMBD_SESS_RELEASING)
|
||||
spin_lock(&conn->request_lock);
|
||||
if (!ksmbd_conn_releasing(conn))
|
||||
ksmbd_conn_set_exiting(conn);
|
||||
spin_unlock(&conn->request_lock);
|
||||
target = conn;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "ksmbd_work.h"
|
||||
|
||||
struct smbdirect_buffer_descriptor_v1;
|
||||
struct ksmbd_session;
|
||||
|
||||
#define KSMBD_SOCKET_BACKLOG 16
|
||||
|
||||
|
|
@ -196,7 +197,8 @@ extern struct rw_semaphore conn_list_lock;
|
|||
|
||||
bool ksmbd_conn_alive(struct ksmbd_conn *conn);
|
||||
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn);
|
||||
int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id);
|
||||
int ksmbd_conn_wait_idle_sess(struct ksmbd_conn *curr_conn,
|
||||
struct ksmbd_session *sess);
|
||||
struct ksmbd_conn *ksmbd_conn_alloc(void);
|
||||
void ksmbd_conn_free(struct ksmbd_conn *conn);
|
||||
struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn);
|
||||
|
|
@ -310,5 +312,5 @@ static inline void ksmbd_conn_set_releasing(struct ksmbd_conn *conn)
|
|||
WRITE_ONCE(conn->status, KSMBD_SESS_RELEASING);
|
||||
}
|
||||
|
||||
void ksmbd_all_conn_set_status(u64 sess_id, u32 status);
|
||||
void ksmbd_all_conn_set_status(struct ksmbd_session *sess, u32 status);
|
||||
#endif /* __CONNECTION_H__ */
|
||||
|
|
|
|||
|
|
@ -647,17 +647,17 @@ void destroy_previous_session(struct ksmbd_conn *conn,
|
|||
memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
|
||||
goto out;
|
||||
|
||||
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_RECONNECT);
|
||||
err = ksmbd_conn_wait_idle_sess_id(conn, id);
|
||||
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_RECONNECT);
|
||||
err = ksmbd_conn_wait_idle_sess(conn, prev_sess);
|
||||
if (err) {
|
||||
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
|
||||
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_SETUP);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ksmbd_destroy_file_table(prev_sess);
|
||||
prev_sess->kerberos_expiry = 0;
|
||||
prev_sess->state = SMB2_SESSION_EXPIRED;
|
||||
ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
|
||||
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_SETUP);
|
||||
ksmbd_launch_ksmbd_durable_scavenger();
|
||||
out:
|
||||
up_write(&conn->session_lock);
|
||||
|
|
|
|||
|
|
@ -2899,7 +2899,6 @@ int smb2_session_logoff(struct ksmbd_work *work)
|
|||
struct ksmbd_session *sess = work->sess;
|
||||
struct smb2_logoff_req *req;
|
||||
struct smb2_logoff_rsp *rsp;
|
||||
u64 sess_id;
|
||||
int err;
|
||||
|
||||
WORK_BUFFERS(work, req, rsp);
|
||||
|
|
@ -2913,8 +2912,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
|
|||
smb2_set_err_rsp(work);
|
||||
return -ENOENT;
|
||||
}
|
||||
sess_id = le64_to_cpu(req->hdr.SessionId);
|
||||
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT);
|
||||
ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_RECONNECT);
|
||||
ksmbd_conn_unlock(conn);
|
||||
|
||||
ksmbd_close_session_fds(work);
|
||||
|
|
@ -2932,7 +2930,7 @@ int smb2_session_logoff(struct ksmbd_work *work)
|
|||
sess->state = SMB2_SESSION_EXPIRED;
|
||||
up_write(&conn->session_lock);
|
||||
|
||||
ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_SETUP);
|
||||
ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_SETUP);
|
||||
|
||||
rsp->StructureSize = cpu_to_le16(4);
|
||||
err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user