Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

race #447

Closed
wants to merge 8 commits into from
Closed

race #447

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 52 additions & 20 deletions connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ static DEFINE_MUTEX(init_lock);

static struct ksmbd_conn_ops default_conn_ops;

LIST_HEAD(conn_list);
LIST_HEAD(global_conn_list);
DECLARE_RWSEM(conn_list_lock);

/**
Expand All @@ -36,7 +36,7 @@ DECLARE_RWSEM(conn_list_lock);
void ksmbd_conn_free(struct ksmbd_conn *conn)
{
down_write(&conn_list_lock);
list_del(&conn->conns_list);
list_del(&conn->conn_entry);
up_write(&conn_list_lock);

xa_destroy(&conn->sessions);
Expand Down Expand Up @@ -80,7 +80,9 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)

init_waitqueue_head(&conn->req_running_q);
init_waitqueue_head(&conn->r_count_q);
INIT_LIST_HEAD(&conn->conns_list);
INIT_LIST_HEAD(&conn->conn_entry);
INIT_LIST_HEAD(&conn->bind_conn_entry);
INIT_LIST_HEAD(&conn->bind_conn_list);
INIT_LIST_HEAD(&conn->requests);
INIT_LIST_HEAD(&conn->async_requests);
spin_lock_init(&conn->request_lock);
Expand All @@ -91,8 +93,10 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
spin_lock_init(&conn->llist_lock);
INIT_LIST_HEAD(&conn->lock_list);

init_rwsem(&conn->session_lock);

down_write(&conn_list_lock);
list_add(&conn->conns_list, &conn_list);
list_add(&conn->conn_entry, &global_conn_list);
up_write(&conn_list_lock);
return conn;
}
Expand All @@ -103,7 +107,7 @@ bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c)
bool ret = false;

down_read(&conn_list_lock);
list_for_each_entry(t, &conn_list, conns_list) {
list_for_each_entry(t, &global_conn_list, conn_entry) {
if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE))
continue;

Expand Down Expand Up @@ -169,37 +173,65 @@ void ksmbd_conn_unlock(struct ksmbd_conn *conn)
mutex_unlock(&conn->srv_mutex);
}

void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
struct ksmbd_conn *ksmbd_find_master_conn(struct ksmbd_conn *bind_conn,
u64 sess_id)
{
struct ksmbd_conn *conn;
struct ksmbd_conn *master_conn;

down_read(&conn_list_lock);
list_for_each_entry(conn, &conn_list, conns_list) {
if (conn->binding || xa_load(&conn->sessions, sess_id))
WRITE_ONCE(conn->status, status);
list_for_each_entry(master_conn, &global_conn_list, conn_entry) {
if (bind_conn == master_conn)
continue;
if (xa_load(&master_conn->sessions, sess_id)) {
up_read(&conn_list_lock);
return master_conn;
}
}
up_read(&conn_list_lock);

return NULL;
}

void ksmbd_all_conn_set_status(struct ksmbd_conn *conn, u64 sess_id, u32 status)
{
struct ksmbd_conn *master_conn =
conn->master_conn ? conn->master_conn : conn;
struct ksmbd_conn *bind_conn;

if (conn->master_conn)
ksmbd_conn_lock(conn->master_conn);
list_for_each_entry(bind_conn, &master_conn->bind_conn_list,
bind_conn_entry)
WRITE_ONCE(bind_conn->status, status);
WRITE_ONCE(master_conn->status, status);
if (conn->master_conn)
ksmbd_conn_unlock(conn->master_conn);
}

void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id)
{
struct ksmbd_conn *master_conn =
conn->master_conn ? conn->master_conn : conn;
struct ksmbd_conn *bind_conn;

wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);

down_read(&conn_list_lock);
list_for_each_entry(bind_conn, &conn_list, conns_list) {
ksmbd_conn_lock(master_conn);
list_for_each_entry(bind_conn, &master_conn->bind_conn_list,
bind_conn_entry) {
if (bind_conn == conn)
continue;

if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) &&
!ksmbd_conn_releasing(bind_conn) &&
atomic_read(&bind_conn->req_running)) {
if (!ksmbd_conn_releasing(bind_conn) &&
atomic_read(&bind_conn->req_running))
wait_event(bind_conn->req_running_q,
atomic_read(&bind_conn->req_running) == 0);
}
atomic_read(&bind_conn->req_running) == 0);
}
up_read(&conn_list_lock);

if (conn->master_conn)
wait_event(master_conn->req_running_q,
atomic_read(&master_conn->req_running) == 0);
ksmbd_conn_unlock(master_conn);
}

int ksmbd_conn_write(struct ksmbd_work *work)
Expand Down Expand Up @@ -480,7 +512,7 @@ static void stop_sessions(void)

again:
down_read(&conn_list_lock);
list_for_each_entry(conn, &conn_list, conns_list) {
list_for_each_entry(conn, &global_conn_list, conn_entry) {
struct task_struct *task;

t = conn->transport;
Expand All @@ -497,7 +529,7 @@ static void stop_sessions(void)
}
up_read(&conn_list_lock);

if (!list_empty(&conn_list)) {
if (!list_empty(&global_conn_list)) {
schedule_timeout_interruptible(HZ / 10); /* 100ms */
goto again;
}
Expand Down
13 changes: 9 additions & 4 deletions connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ struct ksmbd_conn {
struct ksmbd_transport *transport;
struct nls_table *local_nls;
struct unicode_map *um;
struct list_head conns_list;
struct list_head conn_entry;
struct rw_semaphore session_lock;
/* smb session 1 per user */
struct xarray sessions;
unsigned long last_active;
Expand Down Expand Up @@ -104,6 +105,9 @@ struct ksmbd_conn {
bool signing_negotiated;
__le16 signing_algorithm;
bool binding;
void *master_conn;
struct list_head bind_conn_entry;
struct list_head bind_conn_list;
};

struct ksmbd_conn_ops {
Expand Down Expand Up @@ -140,7 +144,7 @@ struct ksmbd_transport {
#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ)
#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr))

extern struct list_head conn_list;
extern struct list_head global_conn_list;
extern struct rw_semaphore conn_list_lock;

bool ksmbd_conn_alive(struct ksmbd_conn *conn);
Expand All @@ -165,6 +169,9 @@ int ksmbd_conn_transport_init(void);
void ksmbd_conn_transport_destroy(void);
void ksmbd_conn_lock(struct ksmbd_conn *conn);
void ksmbd_conn_unlock(struct ksmbd_conn *conn);
struct ksmbd_conn *ksmbd_find_master_conn(struct ksmbd_conn *bind_conn,
u64 sess_id);
void ksmbd_all_conn_set_status(struct ksmbd_conn *conn, u64 sess_id, u32 status);

/*
* WARNING
Expand Down Expand Up @@ -226,6 +233,4 @@ 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);
#endif /* __CONNECTION_H__ */
42 changes: 39 additions & 3 deletions mgmt/tree_connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,

tree_conn->user = sess->user;
tree_conn->share_conf = sc;
tree_conn->t_state = TREE_NEW;
status.tree_conn = tree_conn;
atomic_set(&tree_conn->refcount, 1);
init_waitqueue_head(&tree_conn->refcount_q);

ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
GFP_KERNEL));
Expand All @@ -93,14 +96,33 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
return status;
}

void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
{
/*
* Checking waitqueue to releasing tree connect on
* tree disconnect. waitqueue_active is safe because it
* uses atomic operation for condition.
*/
if (!atomic_dec_return(&tcon->refcount) &&
waitqueue_active(&tcon->refcount_q))
wake_up(&tcon->refcount_q);
}

int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tree_conn)
{
int ret;

write_lock(&sess->tree_conns_lock);
xa_erase(&sess->tree_conns, tree_conn->id);
write_unlock(&sess->tree_conns_lock);

if (!atomic_dec_and_test(&tree_conn->refcount))
wait_event(tree_conn->refcount_q,
atomic_read(&tree_conn->refcount) == 0);

ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
ksmbd_release_tree_conn_id(sess, tree_conn->id);
xa_erase(&sess->tree_conns, tree_conn->id);
ksmbd_share_config_put(tree_conn->share_conf);
kfree(tree_conn);
return ret;
Expand All @@ -111,11 +133,15 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
{
struct ksmbd_tree_connect *tcon;

read_lock(&sess->tree_conns_lock);
tcon = xa_load(&sess->tree_conns, id);
if (tcon) {
if (test_bit(TREE_CONN_EXPIRE, &tcon->status))
if (tcon->t_state != TREE_CONNECTED)
tcon = NULL;
else if (!atomic_inc_not_zero(&tcon->refcount))
tcon = NULL;
}
read_unlock(&sess->tree_conns_lock);

return tcon;
}
Expand All @@ -129,8 +155,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
if (!sess)
return -EINVAL;

xa_for_each(&sess->tree_conns, id, tc)
xa_for_each(&sess->tree_conns, id, tc) {
write_lock(&sess->tree_conns_lock);
if (tc->t_state == TREE_DISCONNECTED) {
write_unlock(&sess->tree_conns_lock);
ret = -ENOENT;
continue;
}
tc->t_state = TREE_DISCONNECTED;
write_unlock(&sess->tree_conns_lock);

ret |= ksmbd_tree_conn_disconnect(sess, tc);
}
xa_destroy(&sess->tree_conns);
return ret;
}
11 changes: 9 additions & 2 deletions mgmt/tree_connect.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ struct ksmbd_share_config;
struct ksmbd_user;
struct ksmbd_conn;

#define TREE_CONN_EXPIRE 1
enum {
TREE_NEW = 0,
TREE_CONNECTED,
TREE_DISCONNECTED
};

struct ksmbd_tree_connect {
int id;
Expand All @@ -27,7 +31,9 @@ struct ksmbd_tree_connect {

int maximal_access;
bool posix_extensions;
unsigned long status;
atomic_t refcount;
wait_queue_head_t refcount_q;
unsigned int t_state;
};

struct ksmbd_tree_conn_status {
Expand All @@ -46,6 +52,7 @@ struct ksmbd_session;
struct ksmbd_tree_conn_status
ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
const char *share_name);
void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon);

int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tree_conn);
Expand Down
11 changes: 8 additions & 3 deletions mgmt/user_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
unsigned long id;
struct ksmbd_session *sess;

down_write(&sessions_table_lock);
down_write(&conn->session_lock);
xa_for_each(&conn->sessions, id, sess) {
if (sess->state != SMB2_SESSION_VALID ||
time_after(jiffies,
Expand All @@ -190,7 +190,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
continue;
}
}
up_write(&sessions_table_lock);
up_write(&conn->session_lock);
}

int ksmbd_session_register(struct ksmbd_conn *conn,
Expand Down Expand Up @@ -237,7 +237,9 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
}
}
}
up_write(&sessions_table_lock);

down_write(&conn->session_lock);
xa_for_each(&conn->sessions, id, sess) {
unsigned long chann_id;
struct channel *chann;
Expand All @@ -259,17 +261,19 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
ksmbd_session_destroy(sess);
}
}
up_write(&sessions_table_lock);
up_write(&conn->session_lock);
}

struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
unsigned long long id)
{
struct ksmbd_session *sess;

down_read(&conn->session_lock);
sess = xa_load(&conn->sessions, id);
if (sess)
sess->last_active = jiffies;
up_read(&conn->session_lock);
return sess;
}

Expand Down Expand Up @@ -375,6 +379,7 @@ static struct ksmbd_session *__session_create(int protocol)
xa_init(&sess->ksmbd_chann_list);
xa_init(&sess->rpc_handle_list);
sess->sequence_number = 1;
rwlock_init(&sess->tree_conns_lock);

switch (protocol) {
#ifdef CONFIG_SMB_INSECURE_SERVER
Expand Down
1 change: 1 addition & 0 deletions mgmt/user_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ struct ksmbd_session {

struct ksmbd_file_table file_table;
unsigned long last_active;
rwlock_t tree_conns_lock;
};

static inline int test_session_flag(struct ksmbd_session *sess, int bit)
Expand Down
2 changes: 2 additions & 0 deletions server.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
} while (is_chained == true);

send:
if (work->tcon)
ksmbd_tree_connect_put(work->tcon);
smb3_preauth_hash_rsp(work);
if (work->sess && work->sess->enc && work->encrypted &&
conn->ops->encrypt_resp) {
Expand Down
4 changes: 2 additions & 2 deletions smb1pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -1676,7 +1676,7 @@ int smb_locking_andx(struct ksmbd_work *work)
list_del(&smb_lock->llist);
/* check locks in connections */
down_read(&conn_list_lock);
list_for_each_entry(conn, &conn_list, conns_list) {
list_for_each_entry(conn, &global_conn_list, conn_entry) {
spin_lock(&conn->llist_lock);
list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) {
if (file_inode(cmp_lock->fl->fl_file) !=
Expand Down Expand Up @@ -1845,7 +1845,7 @@ int smb_locking_andx(struct ksmbd_work *work)

locked = 0;
up_read(&conn_list_lock);
list_for_each_entry(conn, &conn_list, conns_list) {
list_for_each_entry(conn, &global_conn_list, conn_entry) {
spin_lock(&conn->llist_lock);
list_for_each_entry(cmp_lock, &conn->lock_list, clist) {
if (file_inode(cmp_lock->fl->fl_file) !=
Expand Down
Loading