]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
cifs: fix max_credits implementation
authorShyam Prasad N <sprasad@microsoft.com>
Fri, 9 Jun 2023 17:46:58 +0000 (17:46 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 13 Sep 2023 07:42:25 +0000 (09:42 +0200)
[ Upstream commit 5e90aa21eb1372736e08cee0c0bf47735c5c4b95 ]

The current implementation of max_credits on the client does
not work because the CreditRequest logic for several commands
does not take max_credits into account.

Still, we can end up asking the server for more credits, depending
on the number of credits in flight. For this, we need to
limit the credits while parsing the responses too.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/smb/client/smb2ops.c
fs/smb/client/smb2pdu.c

index bcd4c3a507601c47974efe7abb9980ffaf5208eb..6b020d80bb949af9e46b1712beb6a5a2c70607d1 100644 (file)
@@ -34,6 +34,8 @@ static int
 change_conf(struct TCP_Server_Info *server)
 {
        server->credits += server->echo_credits + server->oplock_credits;
+       if (server->credits > server->max_credits)
+               server->credits = server->max_credits;
        server->oplock_credits = server->echo_credits = 0;
        switch (server->credits) {
        case 0:
index ba46156e326808aeb592be52864e14ab243e57d3..ae17d78f6ba176e767e5d2105e1f208d7fc4d6fe 100644 (file)
@@ -1312,7 +1312,12 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
        }
 
        /* enough to enable echos and oplocks and one max size write */
-       req->hdr.CreditRequest = cpu_to_le16(130);
+       if (server->credits >= server->max_credits)
+               req->hdr.CreditRequest = cpu_to_le16(0);
+       else
+               req->hdr.CreditRequest = cpu_to_le16(
+                       min_t(int, server->max_credits -
+                             server->credits, 130));
 
        /* only one of SMB2 signing flags may be set in SMB2 request */
        if (server->sign)
@@ -1907,7 +1912,12 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
        rqst.rq_nvec = 2;
 
        /* Need 64 for max size write so ask for more in case not there yet */
-       req->hdr.CreditRequest = cpu_to_le16(64);
+       if (server->credits >= server->max_credits)
+               req->hdr.CreditRequest = cpu_to_le16(0);
+       else
+               req->hdr.CreditRequest = cpu_to_le16(
+                       min_t(int, server->max_credits -
+                             server->credits, 64));
 
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags, &rsp_iov);
@@ -4291,6 +4301,7 @@ smb2_async_readv(struct cifs_readdata *rdata)
        struct TCP_Server_Info *server;
        struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
        unsigned int total_len;
+       int credit_request;
 
        cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
                 __func__, rdata->offset, rdata->bytes);
@@ -4322,7 +4333,13 @@ smb2_async_readv(struct cifs_readdata *rdata)
        if (rdata->credits.value > 0) {
                shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
                                                SMB2_MAX_BUFFER_SIZE));
-               shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8);
+               credit_request = le16_to_cpu(shdr->CreditCharge) + 8;
+               if (server->credits >= server->max_credits)
+                       shdr->CreditRequest = cpu_to_le16(0);
+               else
+                       shdr->CreditRequest = cpu_to_le16(
+                               min_t(int, server->max_credits -
+                                               server->credits, credit_request));
 
                rc = adjust_credits(server, &rdata->credits, rdata->bytes);
                if (rc)
@@ -4532,6 +4549,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
        unsigned int total_len;
        struct cifs_io_parms _io_parms;
        struct cifs_io_parms *io_parms = NULL;
+       int credit_request;
 
        if (!wdata->server)
                server = wdata->server = cifs_pick_channel(tcon->ses);
@@ -4649,7 +4667,13 @@ smb2_async_writev(struct cifs_writedata *wdata,
        if (wdata->credits.value > 0) {
                shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
                                                    SMB2_MAX_BUFFER_SIZE));
-               shdr->CreditRequest = cpu_to_le16(le16_to_cpu(shdr->CreditCharge) + 8);
+               credit_request = le16_to_cpu(shdr->CreditCharge) + 8;
+               if (server->credits >= server->max_credits)
+                       shdr->CreditRequest = cpu_to_le16(0);
+               else
+                       shdr->CreditRequest = cpu_to_le16(
+                               min_t(int, server->max_credits -
+                                               server->credits, credit_request));
 
                rc = adjust_credits(server, &wdata->credits, io_parms->length);
                if (rc)