[PATCH v4 08/11] cifs: Replace SendReceiveBlockingLock() with SendReceive() plus flags

David Howells
2025-11-24T12:43:40Z

Replace the smb1 transport's SendReceiveBlockingLock() with SendReceive()
plus a couple of flags. This will then allow that to pick up the transport
changes there.

The first flag, CIFS_INTERRUPTIBLE_WAIT, is added to indicate that the wait
should be interruptible and the second, CIFS_WINDOWS_LOCK, indicates that
we need to send a Lock command with unlock type rather than a Cancel.

send_lock_cancel() is then called from cifs_lock_cancel() which is called
from the main transport loop in compound_send_recv().

[!] I *think* the error code handling is probably right.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Steve French <sfrench@samba.org>
cc: Paulo Alcantara <pc@manguebit.org>
cc: Shyam Prasad N <sprasad@microsoft.com>
cc: Tom Talpey <tom@talpey.com>
cc: linux-cifs@vger.kernel.org
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
---
fs/smb/client/cifsglob.h | 8 +-
fs/smb/client/cifsproto.h | 7 +-
fs/smb/client/cifssmb.c | 18 +--
fs/smb/client/cifstransport.c | 200 +---------------------------------
fs/smb/client/smb1ops.c | 47 +++++++-
fs/smb/client/smb1proto.h | 3 -
fs/smb/client/transport.c | 10 +-
7 files changed, 77 insertions(+), 216 deletions(-)

diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 1e9770910e2b..f1df282640c1 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -311,8 +311,9 @@ struct cifs_open_parms;
struct cifs_credits;

struct smb_version_operations {
- int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *,
- struct smb_message *smb);
+ int (*send_cancel)(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ struct smb_rqst *rqst, struct smb_message *smb,
+ unsigned int xid);
bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
/* setup request: allocate mid, sign message */
struct smb_message *(*setup_request)(struct cifs_ses *ses,
@@ -1672,6 +1673,7 @@ struct smb_message {
__u16 credits_received; /* number of credits from the response */
__u32 pid; /* process id */
__u32 sequence_number; /* for CIFS signing */
+ unsigned int sr_flags; /* Flags passed to send_recv() */
unsigned long when_alloc; /* when mid was created */
#ifdef CONFIG_CIFS_STATS2
unsigned long when_sent; /* time when smb send finished */
@@ -1882,6 +1884,8 @@ enum cifs_writable_file_flags {
#define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */
#define CIFS_NO_SRV_RSP 0x1000 /* there is no server response */
#define CIFS_COMPRESS_REQ 0x4000 /* compress request before sending */
+#define CIFS_INTERRUPTIBLE_WAIT 0x8000 /* Interruptible wait (e.g. lock request) */
+#define CIFS_WINDOWS_LOCK 0x10000 /* We're trying to get a Windows lock */

/* Security Flags: indicate type of session setup needed */
#define CIFSSEC_MAY_SIGN 0x00001
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index 39586f2dfad5..4676268bd621 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -604,11 +604,12 @@ static inline void free_dentry_path(void *page)
}

static inline int
-send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
- struct smb_message *smb)
+send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ struct smb_rqst *rqst, struct smb_message *smb,
+ unsigned int xid)
{
return server->ops->send_cancel ?
- server->ops->send_cancel(server, rqst, smb) : 0;
+ server->ops->send_cancel(ses, server, rqst, smb, xid) : 0;
}

struct cifs_unix_set_info_args {
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index 65e517aaa40f..1460c0daf71f 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -2060,7 +2060,7 @@ CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon,
/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
unsigned int in_len;
int bytes_returned;
- int flags = 0;
+ int flags = CIFS_WINDOWS_LOCK | CIFS_INTERRUPTIBLE_WAIT;
__u16 count;

cifs_dbg(FYI, "CIFSSMBLock timeout %d numLock %d\n",
@@ -2105,8 +2105,9 @@ CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon,
pSMB->ByteCount = cpu_to_le16(count);

if (waitFlag)
- rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, in_len,
- (struct smb_hdr *) pSMB, &bytes_returned);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
+ (struct smb_hdr *) pSMB, &bytes_returned,
+ flags);
else
rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, in_len, flags);
cifs_small_buf_release(pSMB);
@@ -2131,7 +2132,7 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_posix_lock *parm_data;
unsigned int in_len;
int rc = 0;
- int timeout = 0;
+ int sr_flags = CIFS_INTERRUPTIBLE_WAIT;
int bytes_returned = 0;
int resp_buf_type = 0;
__u16 params, param_offset, offset, byte_count, count;
@@ -2174,7 +2175,7 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,

parm_data->lock_type = cpu_to_le16(lock_type);
if (waitFlag) {
- timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
+ sr_flags |= CIFS_BLOCKING_OP; /* blocking operation, no timeout */
parm_data->lock_flags = cpu_to_le16(1);
pSMB->Timeout = cpu_to_le32(-1);
} else
@@ -2191,13 +2192,14 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
in_len += byte_count;
pSMB->ByteCount = cpu_to_le16(byte_count);
if (waitFlag) {
- rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, in_len,
- (struct smb_hdr *) pSMBr, &bytes_returned);
+ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
+ (struct smb_hdr *) pSMBr, &bytes_returned,
+ sr_flags);
} else {
iov[0].iov_base = (char *)pSMB;
iov[0].iov_len = in_len;
rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
- &resp_buf_type, timeout, &rsp_iov);
+ &resp_buf_type, sr_flags, &rsp_iov);
pSMBr = (struct smb_com_transaction2_sfi_rsp *)rsp_iov.iov_base;
}
cifs_small_buf_release(pSMB);
diff --git a/fs/smb/client/cifstransport.c b/fs/smb/client/cifstransport.c
index 6fa60de786e9..4bb673d9b56b 100644
--- a/fs/smb/client/cifstransport.c
+++ b/fs/smb/client/cifstransport.c
@@ -240,13 +240,6 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
return -EIO;
}

- spin_lock(&server->srv_lock);
- if (server->tcpStatus == CifsExiting) {
- spin_unlock(&server->srv_lock);
- return -ENOENT;
- }
- spin_unlock(&server->srv_lock);
-
/* Ensure that we do not send more than 50 overlapping requests
to the same server. We may make this configurable later or
use ses->maxReq */
@@ -262,194 +255,11 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
if (rc < 0)
return rc;

- *pbytes_returned = resp_iov.iov_len;
- if (resp_iov.iov_len)
- memcpy(out_buf, resp_iov.iov_base, resp_iov.iov_len);
- free_rsp_buf(resp_buf_type, resp_iov.iov_base);
- return rc;
-}
-
-/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
- blocking lock to return. */
-
-static int
-send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon,
- struct smb_hdr *in_buf, unsigned int in_len,
- struct smb_hdr *out_buf)
-{
- int bytes_returned;
- struct cifs_ses *ses = tcon->ses;
- LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
-
- /* We just modify the current in_buf to change
- the type of lock from LOCKING_ANDX_SHARED_LOCK
- or LOCKING_ANDX_EXCLUSIVE_LOCK to
- LOCKING_ANDX_CANCEL_LOCK. */
-
- pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
- pSMB->Timeout = 0;
- pSMB->hdr.Mid = get_next_mid(ses->server);
-
- return SendReceive(xid, ses, in_buf, in_len, out_buf,
- &bytes_returned, 0);
-}
-
-int SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
- struct smb_hdr *in_buf, unsigned int in_len,
- struct smb_hdr *out_buf, int *pbytes_returned)
-{
- int rc = 0;
- int rstart = 0;
- struct smb_message *smb;
- struct cifs_ses *ses;
- struct kvec iov = { .iov_base = in_buf, .iov_len = in_len };
- struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
- unsigned int instance;
- struct TCP_Server_Info *server;
-
- if (WARN_ON_ONCE(in_len > 0xffffff))
- return -EIO;
- if (tcon == NULL || tcon->ses == NULL) {
- cifs_dbg(VFS, "Null smb session\n");
- return -EIO;
- }
- ses = tcon->ses;
- server = ses->server;
-
- if (server == NULL) {
- cifs_dbg(VFS, "Null tcp session\n");
- return -EIO;
- }
-
- spin_lock(&server->srv_lock);
- if (server->tcpStatus == CifsExiting) {
- spin_unlock(&server->srv_lock);
- return -ENOENT;
- }
- spin_unlock(&server->srv_lock);
-
- /* Ensure that we do not send more than 50 overlapping requests
- to the same server. We may make this configurable later or
- use ses->maxReq */
-
- if (in_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
- cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
- in_len);
- return -EIO;
- }
-
- rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance);
- if (rc)
- return rc;
-
- /* make sure that we sign in the same order that we send on this socket
- and avoid races inside tcp sendmsg code that could cause corruption
- of smb data */
-
- cifs_server_lock(server);
-
- rc = allocate_mid(ses, in_buf, &smb);
- if (rc) {
- cifs_server_unlock(server);
- return rc;
- }
-
- rc = cifs_sign_rqst(&rqst, server, &smb->sequence_number);
- if (rc) {
- delete_mid(smb);
- cifs_server_unlock(server);
- return rc;
- }
-
- smb->mid_state = MID_REQUEST_SUBMITTED;
- rc = __smb_send_rqst(server, 1, &rqst);
- cifs_save_when_sent(smb);
-
- if (rc < 0)
- server->sequence_number -= 2;
-
- cifs_server_unlock(server);
-
- if (rc < 0) {
- delete_mid(smb);
- return rc;
- }
-
- /* Wait for a reply - allow signals to interrupt. */
- rc = wait_event_interruptible(server->response_q,
- (!(smb->mid_state == MID_REQUEST_SUBMITTED ||
- smb->mid_state == MID_RESPONSE_RECEIVED)) ||
- ((server->tcpStatus != CifsGood) &&
- (server->tcpStatus != CifsNew)));
-
- /* Were we interrupted by a signal ? */
- spin_lock(&server->srv_lock);
- if ((rc == -ERESTARTSYS) &&
- (smb->mid_state == MID_REQUEST_SUBMITTED ||
- smb->mid_state == MID_RESPONSE_RECEIVED) &&
- ((server->tcpStatus == CifsGood) ||
- (server->tcpStatus == CifsNew))) {
- spin_unlock(&server->srv_lock);
-
- if (in_buf->Command == SMB_COM_TRANSACTION2) {
- /* POSIX lock. We send a NT_CANCEL SMB to cause the
- blocking lock to return. */
- rc = send_cancel(server, &rqst, smb);
- if (rc) {
- delete_mid(smb);
- return rc;
- }
- } else {
- /* Windows lock. We send a LOCKINGX_CANCEL_LOCK
- to cause the blocking lock to return. */
-
- rc = send_lock_cancel(xid, tcon, in_buf, in_len, out_buf);
-
- /* If we get -ENOLCK back the lock may have
- already been removed. Don't exit in this case. */
- if (rc && rc != -ENOLCK) {
- delete_mid(smb);
- return rc;
- }
- }
-
- rc = wait_for_response(server, smb);
- if (rc) {
- send_cancel(server, &rqst, smb);
- spin_lock(&smb->mid_lock);
- if (smb->mid_state == MID_REQUEST_SUBMITTED ||
- smb->mid_state == MID_RESPONSE_RECEIVED) {
- /* no longer considered to be "in-flight" */
- smb->callback = release_mid;
- spin_unlock(&smb->mid_lock);
- return rc;
- }
- spin_unlock(&smb->mid_lock);
- }
-
- /* We got the response - restart system call. */
- rstart = 1;
- spin_lock(&server->srv_lock);
- }
- spin_unlock(&server->srv_lock);
-
- rc = cifs_sync_mid_result(smb, server);
- if (rc != 0)
- return rc;
-
- /* rcvd frame is ok */
- if (out_buf == NULL || smb->mid_state != MID_RESPONSE_READY) {
- rc = -EIO;
- cifs_tcon_dbg(VFS, "Bad MID state?\n");
- goto out;
+ if (out_buf) {
+ *pbytes_returned = resp_iov.iov_len;
+ if (resp_iov.iov_len)
+ memcpy(out_buf, resp_iov.iov_base, resp_iov.iov_len);
}
-
- *pbytes_returned = smb->response_pdu_len;
- memcpy(out_buf, smb->resp_buf, *pbytes_returned);
- rc = cifs_check_receive(smb, server, 0);
-out:
- delete_mid(smb);
- if (rstart && rc == -EACCES)
- return -ERESTARTSYS;
+ free_rsp_buf(resp_buf_type, resp_iov.iov_base);
return rc;
}
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index 6b0d0b511b9f..5754427a4d30 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -31,8 +31,9 @@
* SMB_COM_NT_CANCEL request and then sends it.
*/
static int
-send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
- struct smb_message *smb)
+send_nt_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ struct smb_rqst *rqst, struct smb_message *smb,
+ unsigned int xid)
{
struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
struct kvec iov[1];
@@ -72,6 +73,46 @@ send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
return rc;
}

+/*
+ * Send a LOCKINGX_CANCEL_LOCK to cause the Windows blocking lock to
+ * return.
+ */
+static int
+send_lock_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ struct smb_rqst *rqst, struct smb_message *smb,
+ unsigned int xid)
+{
+ struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+ unsigned int in_len = rqst->rq_iov[0].iov_len;
+ LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
+ int rc;
+
+ /* We just modify the current in_buf to change
+ * the type of lock from LOCKING_ANDX_SHARED_LOCK
+ * or LOCKING_ANDX_EXCLUSIVE_LOCK to
+ * LOCKING_ANDX_CANCEL_LOCK.
+ */
+ pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
+ pSMB->Timeout = 0;
+ pSMB->hdr.Mid = get_next_mid(ses->server);
+
+ rc = SendReceive(xid, ses, in_buf, in_len, NULL, NULL, 0);
+ if (rc == -ENOLCK)
+ rc = 0; /* If we get back -ENOLCK, it probably means we managed
+ * to cancel the lock command before it took effect.
+ */
+ return rc;
+}
+
+static int cifs_send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
+ struct smb_rqst *rqst, struct smb_message *smb,
+ unsigned int xid)
+{
+ if (smb->sr_flags & CIFS_WINDOWS_LOCK)
+ return send_lock_cancel(ses, server, rqst, smb, xid);
+ return send_nt_cancel(ses, server, rqst, smb, xid);
+}
+
static bool
cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
{
@@ -1398,7 +1439,7 @@ cifs_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
}

struct smb_version_operations smb1_operations = {
- .send_cancel = send_nt_cancel,
+ .send_cancel = cifs_send_cancel,
.compare_fids = cifs_compare_fids,
.setup_request = cifs_setup_request,
.setup_async_request = cifs_setup_async_request,
diff --git a/fs/smb/client/smb1proto.h b/fs/smb/client/smb1proto.h
index fc278c1dacbb..1588bdf8ba29 100644
--- a/fs/smb/client/smb1proto.h
+++ b/fs/smb/client/smb1proto.h
@@ -223,8 +223,5 @@ int SendReceive2(const unsigned int xid, struct cifs_ses *ses,
int SendReceive(const unsigned int xid, struct cifs_ses *ses,
struct smb_hdr *in_buf, unsigned int in_len,
struct smb_hdr *out_buf, int *pbytes_returned, const int flags);
-int SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
- struct smb_hdr *in_buf, unsigned int in_len,
- struct smb_hdr *out_buf, int *pbytes_returned);

#endif /* _SMB1PROTO_H */
diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c
index c023c9873c88..d347febc821a 100644
--- a/fs/smb/client/transport.c
+++ b/fs/smb/client/transport.c
@@ -643,12 +643,16 @@ cifs_wait_mtu_credits(struct TCP_Server_Info *server, size_t size,

int wait_for_response(struct TCP_Server_Info *server, struct smb_message *smb)
{
+ unsigned int sleep_state = TASK_KILLABLE;
int error;

+ if (smb->sr_flags & CIFS_INTERRUPTIBLE_WAIT)
+ sleep_state = TASK_INTERRUPTIBLE;
+
error = wait_event_state(server->response_q,
smb->mid_state != MID_REQUEST_SUBMITTED &&
smb->mid_state != MID_RESPONSE_RECEIVED,
- (TASK_KILLABLE|TASK_FREEZABLE_UNSAFE));
+ (sleep_state | TASK_FREEZABLE_UNSAFE));
if (error < 0)
return -ERESTARTSYS;

@@ -702,6 +706,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
return PTR_ERR(smb);
}

+ smb->sr_flags = flags;
smb->receive = receive;
smb->callback = callback;
smb->callback_data = cbdata;
@@ -946,6 +951,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
return PTR_ERR(smb[i]);
}

+ smb[i]->sr_flags = flags;
smb[i]->mid_state = MID_REQUEST_SUBMITTED;
smb[i]->optype = optype;
/*
@@ -1012,7 +1018,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
for (; i < num_rqst; i++) {
cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n",
smb[i]->mid, le16_to_cpu(smb[i]->command));
- send_cancel(server, &rqst[i], smb[i]);
+ send_cancel(ses, server, &rqst[i], smb[i], xid);
spin_lock(&smb[i]->mid_lock);
smb[i]->wait_cancelled = true;
if (smb[i]->mid_state == MID_REQUEST_SUBMITTED ||