]> Git Repo - J-linux.git/commitdiff
ksmbd: fix broken transfers when exceeding max simultaneous operations
authorMarios Makassikis <[email protected]>
Sat, 14 Dec 2024 03:17:23 +0000 (12:17 +0900)
committerSteve French <[email protected]>
Mon, 16 Dec 2024 04:20:03 +0000 (22:20 -0600)
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 <[email protected]>
Signed-off-by: Namjae Jeon <[email protected]>
Signed-off-by: Steve French <[email protected]>
fs/smb/server/connection.c
fs/smb/server/connection.h
fs/smb/server/server.c
fs/smb/server/server.h
fs/smb/server/transport_ipc.c

index be9656d524a4c3918e9dcc581013ece0c67feb0c..f8a40f65db6aeea7af9be1e71653c849f42a6022 100644 (file)
@@ -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))
@@ -309,7 +310,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;
 
@@ -319,6 +320,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)) {
@@ -328,6 +330,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;
index 8ddd5a3c7bafb669c10aeebb5ae43401a3f1fb1d..b379ae4fdcdffa98bfe854b77c64423ad943c3b0 100644 (file)
@@ -107,7 +107,6 @@ struct ksmbd_conn {
        __le16                          signing_algorithm;
        bool                            binding;
        atomic_t                        refcnt;
-       atomic_t                        mux_smb_requests;
 };
 
 struct ksmbd_conn_ops {
index 3ba95bd8edeb230dc259c0bffe1623d87491c718..601e7fcbcf1e685247bcbca3b9a0c4200da38bde 100644 (file)
@@ -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;
 }
 
index 4fc529335271f7eac52c93e3978e9e259b9e6d82..94187628ff089fe3b272db72077c51f4bdac64f6 100644 (file)
@@ -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];
        struct task_struct      *dh_task;
index 48cda3350e5a22f403f512dfbe76e33998c1c9ad..befaf42b84cc347270c5fd22c40f6bc569d49326 100644 (file)
@@ -319,8 +319,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);
 
This page took 0.055316 seconds and 4 git commands to generate.