mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 15:41:52 +02:00
cifs: Replace SendReceiveBlockingLock() with SendReceive() plus flags
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> Reviewed-by: Paulo Alcantara (Red Hat) <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 Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
62432a3f51
commit
28405cb5b2
|
|
@ -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 mid_q_entry *);
|
||||
int (*send_cancel)(struct cifs_ses *ses, struct TCP_Server_Info *server,
|
||||
struct smb_rqst *rqst, struct mid_q_entry *mid,
|
||||
unsigned int xid);
|
||||
bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
|
||||
/* setup request: allocate mid, sign message */
|
||||
struct mid_q_entry *(*setup_request)(struct cifs_ses *,
|
||||
|
|
@ -1689,6 +1690,7 @@ struct mid_q_entry {
|
|||
__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 */
|
||||
|
|
@ -1900,6 +1902,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
|
||||
|
|
|
|||
|
|
@ -130,11 +130,12 @@ extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
|
|||
struct cifs_credits *credits);
|
||||
|
||||
static inline int
|
||||
send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
|
||||
struct mid_q_entry *mid)
|
||||
send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
|
||||
struct smb_rqst *rqst, struct mid_q_entry *mid,
|
||||
unsigned int xid)
|
||||
{
|
||||
return server->ops->send_cancel ?
|
||||
server->ops->send_cancel(server, rqst, mid) : 0;
|
||||
server->ops->send_cancel(ses, server, rqst, mid, xid) : 0;
|
||||
}
|
||||
|
||||
int wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ);
|
||||
|
|
@ -142,9 +143,6 @@ extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
|
|||
struct kvec *, int /* nvec to send */,
|
||||
int * /* type of buf returned */, const int flags,
|
||||
struct kvec * /* resp vec */);
|
||||
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);
|
||||
|
||||
void smb2_query_server_interfaces(struct work_struct *work);
|
||||
void
|
||||
|
|
|
|||
|
|
@ -2059,7 +2059,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",
|
||||
|
|
@ -2104,8 +2104,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);
|
||||
|
|
@ -2130,7 +2131,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;
|
||||
|
|
@ -2173,7 +2174,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
|
||||
|
|
@ -2190,13 +2191,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);
|
||||
|
|
|
|||
|
|
@ -239,13 +239,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 */
|
||||
|
|
@ -261,193 +254,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);
|
||||
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);
|
||||
}
|
||||
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 mid_q_entry *mid;
|
||||
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, &mid);
|
||||
if (rc) {
|
||||
cifs_server_unlock(server);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = cifs_sign_rqst(&rqst, server, &mid->sequence_number);
|
||||
if (rc) {
|
||||
delete_mid(mid);
|
||||
cifs_server_unlock(server);
|
||||
return rc;
|
||||
}
|
||||
|
||||
mid->mid_state = MID_REQUEST_SUBMITTED;
|
||||
rc = __smb_send_rqst(server, 1, &rqst);
|
||||
cifs_save_when_sent(mid);
|
||||
|
||||
if (rc < 0)
|
||||
server->sequence_number -= 2;
|
||||
|
||||
cifs_server_unlock(server);
|
||||
|
||||
if (rc < 0) {
|
||||
delete_mid(mid);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Wait for a reply - allow signals to interrupt. */
|
||||
rc = wait_event_interruptible(server->response_q,
|
||||
(!(mid->mid_state == MID_REQUEST_SUBMITTED ||
|
||||
mid->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) &&
|
||||
(mid->mid_state == MID_REQUEST_SUBMITTED ||
|
||||
mid->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, mid);
|
||||
if (rc) {
|
||||
delete_mid(mid);
|
||||
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(mid);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
rc = wait_for_response(server, mid);
|
||||
if (rc) {
|
||||
send_cancel(server, &rqst, mid);
|
||||
spin_lock(&mid->mid_lock);
|
||||
if (mid->callback) {
|
||||
/* no longer considered to be "in-flight" */
|
||||
mid->callback = release_mid;
|
||||
spin_unlock(&mid->mid_lock);
|
||||
return rc;
|
||||
}
|
||||
spin_unlock(&mid->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(mid, server);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
/* rcvd frame is ok */
|
||||
if (out_buf == NULL || mid->mid_state != MID_RESPONSE_READY) {
|
||||
rc = -EIO;
|
||||
cifs_tcon_dbg(VFS, "Bad MID state?\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
*pbytes_returned = mid->response_pdu_len;
|
||||
memcpy(out_buf, mid->resp_buf, *pbytes_returned);
|
||||
rc = cifs_check_receive(mid, server, 0);
|
||||
out:
|
||||
delete_mid(mid);
|
||||
if (rstart && rc == -EACCES)
|
||||
return -ERESTARTSYS;
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,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 mid_q_entry *mid)
|
||||
send_nt_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
|
||||
struct smb_rqst *rqst, struct mid_q_entry *mid,
|
||||
unsigned int xid)
|
||||
{
|
||||
struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
|
||||
struct kvec iov[1];
|
||||
|
|
@ -71,6 +72,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 mid_q_entry *mid,
|
||||
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 mid_q_entry *mid,
|
||||
unsigned int xid)
|
||||
{
|
||||
if (mid->sr_flags & CIFS_WINDOWS_LOCK)
|
||||
return send_lock_cancel(ses, server, rqst, mid, xid);
|
||||
return send_nt_cancel(ses, server, rqst, mid, xid);
|
||||
}
|
||||
|
||||
static bool
|
||||
cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
|
||||
{
|
||||
|
|
@ -1397,7 +1438,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,
|
||||
|
|
|
|||
|
|
@ -642,12 +642,16 @@ cifs_wait_mtu_credits(struct TCP_Server_Info *server, size_t size,
|
|||
|
||||
int wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
{
|
||||
unsigned int sleep_state = TASK_KILLABLE;
|
||||
int error;
|
||||
|
||||
if (mid->sr_flags & CIFS_INTERRUPTIBLE_WAIT)
|
||||
sleep_state = TASK_INTERRUPTIBLE;
|
||||
|
||||
error = wait_event_state(server->response_q,
|
||||
mid->mid_state != MID_REQUEST_SUBMITTED &&
|
||||
mid->mid_state != MID_RESPONSE_RECEIVED,
|
||||
(TASK_KILLABLE|TASK_FREEZABLE_UNSAFE));
|
||||
(sleep_state | TASK_FREEZABLE_UNSAFE));
|
||||
if (error < 0)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
|
|
@ -701,6 +705,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
|
|||
return PTR_ERR(mid);
|
||||
}
|
||||
|
||||
mid->sr_flags = flags;
|
||||
mid->receive = receive;
|
||||
mid->callback = callback;
|
||||
mid->callback_data = cbdata;
|
||||
|
|
@ -945,6 +950,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
|||
return PTR_ERR(mid[i]);
|
||||
}
|
||||
|
||||
mid[i]->sr_flags = flags;
|
||||
mid[i]->mid_state = MID_REQUEST_SUBMITTED;
|
||||
mid[i]->optype = optype;
|
||||
/*
|
||||
|
|
@ -1014,10 +1020,11 @@ 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",
|
||||
mid[i]->mid, le16_to_cpu(mid[i]->command));
|
||||
send_cancel(server, &rqst[i], mid[i]);
|
||||
send_cancel(ses, server, &rqst[i], mid[i], xid);
|
||||
spin_lock(&mid[i]->mid_lock);
|
||||
mid[i]->wait_cancelled = true;
|
||||
if (mid[i]->callback) {
|
||||
if (mid[i]->mid_state == MID_REQUEST_SUBMITTED ||
|
||||
mid[i]->mid_state == MID_RESPONSE_RECEIVED) {
|
||||
mid[i]->callback = cifs_cancelled_callback;
|
||||
cancelled_mid[i] = true;
|
||||
credits[i].value = 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user