Skip to content

Commit 43fb7bc

Browse files
Marios MakassikisSteve French
authored andcommitted
ksmbd: fix broken transfers when exceeding max simultaneous operations
Since commit 0a77d94 ("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: 0a77d94 ("ksmbd: check outstanding simultaneous SMB operations") Signed-off-by: Marios Makassikis <[email protected]> Signed-off-by: Namjae Jeon <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 83c47d9 commit 43fb7bc

File tree

5 files changed

+17
-10
lines changed

5 files changed

+17
-10
lines changed

fs/smb/server/connection.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
7070
atomic_set(&conn->req_running, 0);
7171
atomic_set(&conn->r_count, 0);
7272
atomic_set(&conn->refcnt, 1);
73-
atomic_set(&conn->mux_smb_requests, 0);
7473
conn->total_credits = 1;
7574
conn->outstanding_credits = 0;
7675

@@ -133,6 +132,8 @@ void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
133132
struct ksmbd_conn *conn = work->conn;
134133

135134
atomic_dec(&conn->req_running);
135+
if (waitqueue_active(&conn->req_running_q))
136+
wake_up(&conn->req_running_q);
136137

137138
if (list_empty(&work->request_entry) &&
138139
list_empty(&work->async_request_entry))
@@ -309,7 +310,7 @@ int ksmbd_conn_handler_loop(void *p)
309310
{
310311
struct ksmbd_conn *conn = (struct ksmbd_conn *)p;
311312
struct ksmbd_transport *t = conn->transport;
312-
unsigned int pdu_size, max_allowed_pdu_size;
313+
unsigned int pdu_size, max_allowed_pdu_size, max_req;
313314
char hdr_buf[4] = {0,};
314315
int size;
315316

@@ -319,6 +320,7 @@ int ksmbd_conn_handler_loop(void *p)
319320
if (t->ops->prepare && t->ops->prepare(t))
320321
goto out;
321322

323+
max_req = server_conf.max_inflight_req;
322324
conn->last_active = jiffies;
323325
set_freezable();
324326
while (ksmbd_conn_alive(conn)) {
@@ -328,6 +330,13 @@ int ksmbd_conn_handler_loop(void *p)
328330
kvfree(conn->request_buf);
329331
conn->request_buf = NULL;
330332

333+
recheck:
334+
if (atomic_read(&conn->req_running) + 1 > max_req) {
335+
wait_event_interruptible(conn->req_running_q,
336+
atomic_read(&conn->req_running) < max_req);
337+
goto recheck;
338+
}
339+
331340
size = t->ops->read(t, hdr_buf, sizeof(hdr_buf), -1);
332341
if (size != sizeof(hdr_buf))
333342
break;

fs/smb/server/connection.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ struct ksmbd_conn {
107107
__le16 signing_algorithm;
108108
bool binding;
109109
atomic_t refcnt;
110-
atomic_t mux_smb_requests;
111110
};
112111

113112
struct ksmbd_conn_ops {

fs/smb/server/server.c

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,6 @@ static void handle_ksmbd_work(struct work_struct *wk)
270270

271271
ksmbd_conn_try_dequeue_request(work);
272272
ksmbd_free_work_struct(work);
273-
atomic_dec(&conn->mux_smb_requests);
274273
/*
275274
* Checking waitqueue to dropping pending requests on
276275
* disconnection. waitqueue_active is safe because it
@@ -300,11 +299,6 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn)
300299
if (err)
301300
return 0;
302301

303-
if (atomic_inc_return(&conn->mux_smb_requests) >= conn->vals->max_credits) {
304-
atomic_dec_return(&conn->mux_smb_requests);
305-
return -ENOSPC;
306-
}
307-
308302
work = ksmbd_alloc_work_struct();
309303
if (!work) {
310304
pr_err("allocation for work failed\n");
@@ -367,6 +361,7 @@ static int server_conf_init(void)
367361
server_conf.auth_mechs |= KSMBD_AUTH_KRB5 |
368362
KSMBD_AUTH_MSKRB5;
369363
#endif
364+
server_conf.max_inflight_req = SMB2_MAX_CREDITS;
370365
return 0;
371366
}
372367

fs/smb/server/server.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct ksmbd_server_config {
4242
struct smb_sid domain_sid;
4343
unsigned int auth_mechs;
4444
unsigned int max_connections;
45+
unsigned int max_inflight_req;
4546

4647
char *conf[SERVER_CONF_WORK_GROUP + 1];
4748
struct task_struct *dh_task;

fs/smb/server/transport_ipc.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,11 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
319319
init_smb2_max_write_size(req->smb2_max_write);
320320
if (req->smb2_max_trans)
321321
init_smb2_max_trans_size(req->smb2_max_trans);
322-
if (req->smb2_max_credits)
322+
if (req->smb2_max_credits) {
323323
init_smb2_max_credits(req->smb2_max_credits);
324+
server_conf.max_inflight_req =
325+
req->smb2_max_credits;
326+
}
324327
if (req->smbd_max_io_size)
325328
init_smbd_max_io_size(req->smbd_max_io_size);
326329

0 commit comments

Comments
 (0)