ksmbd: fix broken transfers when exceeding max simultaneous operations
BugLink: https://bugs.launchpad.net/bugs/2100292 [ Upstream commit 43fb7bce8866e793275c4f9f25af6a37745f3416 ] Since commit 0a77d947f599 ("ksmbd: check outstanding simultaneous SMB operations"), ksmbd enforces a maximum number of simultaneous operations for a connection. The problem is that reaching the limit causes ksmbd to close the socket, and the client has no indication that it should have slowed down. This behaviour can be reproduced by setting "smb2 max credits = 128" (or lower), and transferring a large file (25GB). smbclient fails as below: $ smbclient //192.168.1.254/testshare -U user%pass smb: \> put file.bin cli_push returned NT_STATUS_USER_SESSION_DELETED putting file file.bin as \file.bin smb2cli_req_compound_submit: Insufficient credits. 0 available, 1 needed NT_STATUS_INTERNAL_ERROR closing remote file \file.bin smb: \> smb2cli_req_compound_submit: Insufficient credits. 0 available, 1 needed Windows clients fail with 0x8007003b (with smaller files even). Fix this by delaying reading from the socket until there's room to allocate a request. This effectively applies backpressure on the client, so the transfer completes, albeit at a slower rate. Fixes: 0a77d947f599 ("ksmbd: check outstanding simultaneous SMB operations") Signed-off-by: Marios Makassikis <mmakassikis@freebox.fr> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org> (cherry picked from commit 55a81dcf79bc09730ad92a925d01b9c40e4c0cfa linux-6.6.y) [koichiroden: pulled in follow-up fix from v6.6.68] Signed-off-by: Koichiro Den <koichiro.den@canonical.com> Signed-off-by: Stefan Bader <stefan.bader@canonical.com>
This commit is contained in:
committed by
Stefan Bader
parent
cac907fa60
commit
dc21866837
@@ -70,7 +70,6 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
|
||||
atomic_set(&conn->req_running, 0);
|
||||
atomic_set(&conn->r_count, 0);
|
||||
atomic_set(&conn->refcnt, 1);
|
||||
atomic_set(&conn->mux_smb_requests, 0);
|
||||
conn->total_credits = 1;
|
||||
conn->outstanding_credits = 0;
|
||||
|
||||
@@ -133,6 +132,8 @@ void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
|
||||
atomic_dec(&conn->req_running);
|
||||
if (waitqueue_active(&conn->req_running_q))
|
||||
wake_up(&conn->req_running_q);
|
||||
|
||||
if (list_empty(&work->request_entry) &&
|
||||
list_empty(&work->async_request_entry))
|
||||
@@ -277,7 +278,7 @@ int ksmbd_conn_handler_loop(void *p)
|
||||
{
|
||||
struct ksmbd_conn *conn = (struct ksmbd_conn *)p;
|
||||
struct ksmbd_transport *t = conn->transport;
|
||||
unsigned int pdu_size, max_allowed_pdu_size;
|
||||
unsigned int pdu_size, max_allowed_pdu_size, max_req;
|
||||
char hdr_buf[4] = {0,};
|
||||
int size;
|
||||
|
||||
@@ -287,6 +288,7 @@ int ksmbd_conn_handler_loop(void *p)
|
||||
if (t->ops->prepare && t->ops->prepare(t))
|
||||
goto out;
|
||||
|
||||
max_req = server_conf.max_inflight_req;
|
||||
conn->last_active = jiffies;
|
||||
set_freezable();
|
||||
while (ksmbd_conn_alive(conn)) {
|
||||
@@ -296,6 +298,13 @@ int ksmbd_conn_handler_loop(void *p)
|
||||
kvfree(conn->request_buf);
|
||||
conn->request_buf = NULL;
|
||||
|
||||
recheck:
|
||||
if (atomic_read(&conn->req_running) + 1 > max_req) {
|
||||
wait_event_interruptible(conn->req_running_q,
|
||||
atomic_read(&conn->req_running) < max_req);
|
||||
goto recheck;
|
||||
}
|
||||
|
||||
size = t->ops->read(t, hdr_buf, sizeof(hdr_buf), -1);
|
||||
if (size != sizeof(hdr_buf))
|
||||
break;
|
||||
|
||||
@@ -107,7 +107,6 @@ struct ksmbd_conn {
|
||||
__le16 signing_algorithm;
|
||||
bool binding;
|
||||
atomic_t refcnt;
|
||||
atomic_t mux_smb_requests;
|
||||
};
|
||||
|
||||
struct ksmbd_conn_ops {
|
||||
|
||||
@@ -270,7 +270,6 @@ static void handle_ksmbd_work(struct work_struct *wk)
|
||||
|
||||
ksmbd_conn_try_dequeue_request(work);
|
||||
ksmbd_free_work_struct(work);
|
||||
atomic_dec(&conn->mux_smb_requests);
|
||||
/*
|
||||
* Checking waitqueue to dropping pending requests on
|
||||
* disconnection. waitqueue_active is safe because it
|
||||
@@ -300,11 +299,6 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn)
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
if (atomic_inc_return(&conn->mux_smb_requests) >= conn->vals->max_credits) {
|
||||
atomic_dec_return(&conn->mux_smb_requests);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
work = ksmbd_alloc_work_struct();
|
||||
if (!work) {
|
||||
pr_err("allocation for work failed\n");
|
||||
@@ -367,6 +361,7 @@ static int server_conf_init(void)
|
||||
server_conf.auth_mechs |= KSMBD_AUTH_KRB5 |
|
||||
KSMBD_AUTH_MSKRB5;
|
||||
#endif
|
||||
server_conf.max_inflight_req = SMB2_MAX_CREDITS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ struct ksmbd_server_config {
|
||||
struct smb_sid domain_sid;
|
||||
unsigned int auth_mechs;
|
||||
unsigned int max_connections;
|
||||
unsigned int max_inflight_req;
|
||||
|
||||
char *conf[SERVER_CONF_WORK_GROUP + 1];
|
||||
};
|
||||
|
||||
@@ -305,8 +305,11 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
|
||||
init_smb2_max_write_size(req->smb2_max_write);
|
||||
if (req->smb2_max_trans)
|
||||
init_smb2_max_trans_size(req->smb2_max_trans);
|
||||
if (req->smb2_max_credits)
|
||||
if (req->smb2_max_credits) {
|
||||
init_smb2_max_credits(req->smb2_max_credits);
|
||||
server_conf.max_inflight_req =
|
||||
req->smb2_max_credits;
|
||||
}
|
||||
if (req->smbd_max_io_size)
|
||||
init_smbd_max_io_size(req->smbd_max_io_size);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user