struct mid_q_entry * cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) { int rc; struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base; struct mid_q_entry *mid; if (rqst->rq_iov[0].iov_len != 4 || rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base) return ERR_PTR(-EIO); /* enable signing if server requires it */ if (server->sign) hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; mid = AllocMidQEntry(hdr, server); if (mid == NULL) return ERR_PTR(-ENOMEM); rc = cifs_sign_rqst(rqst, server, &mid->sequence_number); if (rc) { DeleteMidQEntry(mid); return ERR_PTR(rc); } return mid; }
static int cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) { int rc = 0; cifs_dbg(FYI, "%s: cmd=%d mid=%llu state=%d\n", __func__, le16_to_cpu(mid->command), mid->mid, mid->mid_state); spin_lock(&GlobalMid_Lock); switch (mid->mid_state) { case MID_RESPONSE_RECEIVED: spin_unlock(&GlobalMid_Lock); return rc; case MID_RETRY_NEEDED: rc = -EAGAIN; break; case MID_RESPONSE_MALFORMED: rc = -EIO; break; case MID_SHUTDOWN: rc = -EHOSTDOWN; break; default: list_del_init(&mid->qhead); cifs_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n", __func__, mid->mid, mid->mid_state); rc = -EIO; } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(mid); return rc; }
/* * Send a SMB request and set the callback function in the mid to handle * the result. Caller is responsible for dealing with timeouts. */ int cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, unsigned int nvec, mid_callback_t *callback, void *cbdata, bool ignore_pend) { int rc; struct mid_q_entry *mid; struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base; rc = wait_for_free_request(server, ignore_pend ? CIFS_ASYNC_OP : 0); if (rc) return rc; /* enable signing if server requires it */ if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; mutex_lock(&server->srv_mutex); mid = AllocMidQEntry(hdr, server); if (mid == NULL) { mutex_unlock(&server->srv_mutex); return -ENOMEM; } rc = cifs_sign_smb2(iov, nvec, server, &mid->sequence_number); if (rc) { mutex_unlock(&server->srv_mutex); DeleteMidQEntry(mid); goto out_err; } mid->callback = callback; mid->callback_data = cbdata; mid->midState = MID_REQUEST_SUBMITTED; /* put it on the pending_mid_q */ spin_lock(&GlobalMid_Lock); list_add_tail(&mid->qhead, &server->pending_mid_q); spin_unlock(&GlobalMid_Lock); #ifdef CONFIG_CIFS_STATS2 atomic_inc(&server->inSend); #endif rc = smb_sendv(server, iov, nvec); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&server->inSend); mid->when_sent = jiffies; #endif mutex_unlock(&server->srv_mutex); if (rc == 0) return 0; delete_mid(mid); out_err: atomic_dec(&server->inFlight); wake_up(&server->request_q); return rc; }
void cifs_delete_mid(struct mid_q_entry *mid) { spin_lock(&GlobalMid_Lock); list_del(&mid->qhead); spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(mid); }
void cifs_delete_mid(struct mid_q_entry *mid) { spin_lock(&GlobalMid_Lock); list_del_init(&mid->qhead); mid->mid_flags |= MID_DELETED; spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(mid); }
struct mid_q_entry * smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) { int rc; struct smb2_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; struct mid_q_entry *mid; smb2_seq_num_into_buf(server, hdr); mid = smb2_mid_entry_alloc(hdr, server); if (mid == NULL) return ERR_PTR(-ENOMEM); rc = smb2_sign_rqst(rqst, server); if (rc) { DeleteMidQEntry(mid); return ERR_PTR(rc); } return mid; }
static int sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) { int rc = 0; cFYI(1, "%s: cmd=%d mid=%d state=%d", __func__, mid->command, mid->mid, mid->midState); spin_lock(&GlobalMid_Lock); /* ensure that it's no longer on the pending_mid_q */ list_del_init(&mid->qhead); switch (mid->midState) { case MID_RESPONSE_RECEIVED: spin_unlock(&GlobalMid_Lock); return rc; case MID_REQUEST_SUBMITTED: /* socket is going down, reject all calls */ if (server->tcpStatus == CifsExiting) { cERROR(1, "%s: canceling mid=%d cmd=0x%x state=%d", __func__, mid->mid, mid->command, mid->midState); rc = -EHOSTDOWN; break; } case MID_RETRY_NEEDED: rc = -EAGAIN; break; default: cERROR(1, "%s: invalid mid state mid=%d state=%d", __func__, mid->mid, mid->midState); rc = -EIO; } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(mid); return rc; }
int SendReceive(const unsigned int xid, struct cifsSesInfo *ses, struct smb_hdr *in_buf, struct smb_hdr *out_buf, int *pbytes_returned, const int long_op) { int rc = 0; unsigned int receive_len; unsigned long timeout; struct mid_q_entry *midQ; if (ses == NULL) { cERROR(1,("Null smb session")); return -EIO; } if(ses->server == NULL) { cERROR(1,("Null tcp session")); return -EIO; } /* 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(long_op == -1) { /* oplock breaks must not be held up */ atomic_inc(&ses->server->inFlight); } else { spin_lock(&GlobalMid_Lock); while(1) { if(atomic_read(&ses->server->inFlight) >= CIFS_MAX_REQ){ spin_unlock(&GlobalMid_Lock); wait_event(ses->server->request_q, atomic_read(&ses->server->inFlight) < CIFS_MAX_REQ); spin_lock(&GlobalMid_Lock); } else { if(ses->server->tcpStatus == CifsExiting) { spin_unlock(&GlobalMid_Lock); return -ENOENT; } /* can not count locking commands against total since they are allowed to block on server */ if(long_op < 3) { /* update # of requests on the wire to server */ atomic_inc(&ses->server->inFlight); } spin_unlock(&GlobalMid_Lock); break; } } } /* 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 */ down(&ses->server->tcpSem); if (ses->server->tcpStatus == CifsExiting) { rc = -ENOENT; goto out_unlock; } else if (ses->server->tcpStatus == CifsNeedReconnect) { cFYI(1,("tcp session dead - return to caller to retry")); rc = -EAGAIN; goto out_unlock; } else if (ses->status != CifsGood) { /* check if SMB session is bad because we are setting it up */ if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && (in_buf->Command != SMB_COM_NEGOTIATE)) { rc = -EAGAIN; goto out_unlock; } /* else ok - we are setting up session */ } midQ = AllocMidQEntry(in_buf, ses); if (midQ == NULL) { up(&ses->server->tcpSem); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return -ENOMEM; } if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) { up(&ses->server->tcpSem); cERROR(1, ("Illegal length, greater than maximum frame, %d ", in_buf->smb_buf_length)); DeleteMidQEntry(midQ); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return -EIO; } rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number); midQ->midState = MID_REQUEST_SUBMITTED; rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->addr.sockAddr)); if(rc < 0) { DeleteMidQEntry(midQ); up(&ses->server->tcpSem); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; } else up(&ses->server->tcpSem); if (long_op == -1) goto cifs_no_response_exit; else if (long_op == 2) /* writes past end of file can take looooong time */ timeout = 300 * HZ; else if (long_op == 1) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ else if (long_op > 2) { timeout = MAX_SCHEDULE_TIMEOUT; } else timeout = 15 * HZ; /* wait for 15 seconds or until woken up due to response arriving or due to last connection to this server being unmounted */ if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout but we still give response a change to complete */ timeout = 2 * HZ; } /* No user interrupts in wait - wreaks havoc with performance */ if(timeout != MAX_SCHEDULE_TIMEOUT) { timeout += jiffies; wait_event(ses->server->response_q, (midQ->midState & MID_RESPONSE_RECEIVED) || time_after(jiffies, timeout) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); } else { wait_event(ses->server->response_q, (midQ->midState & MID_RESPONSE_RECEIVED) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); } spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf); } else { cERROR(1,("No response buffer")); if(midQ->midState == MID_REQUEST_SUBMITTED) { if(ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { if(midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; cFYI(1,("marking request for retry")); } else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; } if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; } else { /* rcvd frame is ok */ if (midQ->resp_buf && out_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { out_buf->smb_buf_length = receive_len; memcpy((char *)out_buf + 4, (char *)midQ->resp_buf + 4, receive_len); dump_smb(out_buf, 92); /* convert the length into a more usable form */ if((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, ses->mac_signing_key,midQ->sequence_number); /* BB fix BB */ if(rc) cFYI(1,("Unexpected signature received from server")); } *pbytes_returned = out_buf->smb_buf_length; /* BB special case reconnect tid and reconnect uid here? */ rc = map_smb_to_linux_error(out_buf); /* convert ByteCount if necessary */ if (receive_len >= sizeof (struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) BCC(out_buf) = le16_to_cpu(BCC(out_buf)); } else { rc = -EIO; cFYI(1,("Bad MID state? ")); } } cifs_no_response_exit: DeleteMidQEntry(midQ); if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; out_unlock: up(&ses->server->tcpSem); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; }
int SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, struct smb_hdr *in_buf, struct smb_hdr *out_buf, int *pbytes_returned) { int rc = 0; int rstart = 0; unsigned int receive_len; struct mid_q_entry *midQ; struct cifsSesInfo *ses; if (tcon == NULL || tcon->ses == NULL) { cERROR(1, "Null smb session"); return -EIO; } ses = tcon->ses; if (ses->server == NULL) { cERROR(1, "Null tcp session"); return -EIO; } if (ses->server->tcpStatus == CifsExiting) return -ENOENT; /* 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_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cERROR(1, "Illegal length, greater than maximum frame, %d", in_buf->smb_buf_length); return -EIO; } rc = wait_for_free_request(ses, CIFS_BLOCKING_OP); 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 */ mutex_lock(&ses->server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); return rc; } rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { DeleteMidQEntry(midQ); mutex_unlock(&ses->server->srv_mutex); return rc; } midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif mutex_unlock(&ses->server->srv_mutex); if (rc < 0) { DeleteMidQEntry(midQ); return rc; } /* Wait for a reply - allow signals to interrupt. */ rc = wait_event_interruptible(ses->server->response_q, (!(midQ->midState == MID_REQUEST_SUBMITTED)) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); /* Were we interrupted by a signal ? */ if ((rc == -ERESTARTSYS) && (midQ->midState == MID_REQUEST_SUBMITTED) && ((ses->server->tcpStatus == CifsGood) || (ses->server->tcpStatus == CifsNew))) { if (in_buf->Command == SMB_COM_TRANSACTION2) { /* POSIX lock. We send a NT_CANCEL SMB to cause the blocking lock to return. */ rc = send_nt_cancel(tcon, in_buf, midQ); if (rc) { DeleteMidQEntry(midQ); 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, out_buf); /* If we get -ENOLCK back the lock may have already been removed. Don't exit in this case. */ if (rc && rc != -ENOLCK) { DeleteMidQEntry(midQ); return rc; } } /* Wait 5 seconds for the response. */ if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ) == 0) { /* We got the response - restart system call. */ rstart = 1; } } spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { cERROR(1, "No response for cmd %d mid %d", midQ->command, midQ->mid); if (midQ->midState == MID_REQUEST_SUBMITTED) { if (ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { if (midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; cFYI(1, "marking request for retry"); } else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); return rc; } if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, "Frame too large received. Length: %d Xid: %d", receive_len, xid); rc = -EIO; goto out; } /* rcvd frame is ok */ if ((out_buf == NULL) || (midQ->midState != MID_RESPONSE_RECEIVED)) { rc = -EIO; cERROR(1, "Bad MID state?"); goto out; } out_buf->smb_buf_length = receive_len; memcpy((char *)out_buf + 4, (char *)midQ->resp_buf + 4, receive_len); dump_smb(out_buf, 92); /* convert the length into a more usable form */ if ((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, ses->server, midQ->sequence_number+1); if (rc) { cERROR(1, "Unexpected SMB signature"); /* BB FIXME add code to kill session */ } } *pbytes_returned = out_buf->smb_buf_length; /* BB special case reconnect tid and uid here? */ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ ); /* convert ByteCount if necessary */ if (receive_len >= sizeof(struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); out: DeleteMidQEntry(midQ); if (rstart && rc == -EACCES) return -ERESTARTSYS; return rc; }
int SendReceive(const unsigned int xid, struct cifsSesInfo *ses, struct smb_hdr *in_buf, struct smb_hdr *out_buf, int *pbytes_returned, const int long_op) { int rc = 0; unsigned int receive_len; unsigned long timeout; struct mid_q_entry *midQ; if (ses == NULL) { cERROR(1, "Null smb session"); return -EIO; } if (ses->server == NULL) { cERROR(1, "Null tcp session"); return -EIO; } if (ses->server->tcpStatus == CifsExiting) return -ENOENT; /* 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_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cERROR(1, "Illegal length, greater than maximum frame, %d", in_buf->smb_buf_length); return -EIO; } rc = wait_for_free_request(ses, long_op); 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 */ mutex_lock(&ses->server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { mutex_unlock(&ses->server->srv_mutex); goto out; } midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif mutex_unlock(&ses->server->srv_mutex); if (rc < 0) goto out; if (long_op == CIFS_STD_OP) timeout = 15 * HZ; /* wait for 15 seconds or until woken up due to response arriving or due to last connection to this server being unmounted */ else if (long_op == CIFS_ASYNC_OP) goto out; else if (long_op == CIFS_VLONG_OP) /* writes past EOF can be slow */ timeout = 180 * HZ; else if (long_op == CIFS_LONG_OP) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ else if (long_op == CIFS_BLOCKING_OP) timeout = 0x7FFFFFFF; /* large but no so large as to wrap */ else { cERROR(1, "unknown timeout flag %d", long_op); rc = -EIO; goto out; } if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout but we still give response a chance to complete */ timeout = 2 * HZ; } /* No user interrupts in wait - wreaks havoc with performance */ wait_for_response(ses, midQ, timeout, 10 * HZ); spin_lock(&GlobalMid_Lock); if (midQ->resp_buf == NULL) { cERROR(1, "No response for cmd %d mid %d", midQ->command, midQ->mid); if (midQ->midState == MID_REQUEST_SUBMITTED) { if (ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { if (midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; cFYI(1, "marking request for retry"); } else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, "Frame too large received. Length: %d Xid: %d", receive_len, xid); rc = -EIO; goto out; } /* rcvd frame is ok */ if (midQ->resp_buf && out_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { out_buf->smb_buf_length = receive_len; memcpy((char *)out_buf + 4, (char *)midQ->resp_buf + 4, receive_len); dump_smb(out_buf, 92); /* convert the length into a more usable form */ if ((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, ses->server, midQ->sequence_number+1); if (rc) { cERROR(1, "Unexpected SMB signature"); /* BB FIXME add code to kill session */ } } *pbytes_returned = out_buf->smb_buf_length; /* BB special case reconnect tid and uid here? */ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ ); /* convert ByteCount if necessary */ if (receive_len >= sizeof(struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); } else { rc = -EIO; cERROR(1, "Bad MID state?"); } out: DeleteMidQEntry(midQ); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; }
static void cifs_cancelled_callback(struct mid_q_entry *mid) { cifs_compound_callback(mid); DeleteMidQEntry(mid); }
int CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op) { int rc = 0; unsigned long timeout = 15 * HZ; struct mid_q_entry *midQ = NULL; if (ses == NULL) { cERROR(1,("Null smb session")); return -EIO; } if(ses->server == NULL) { cERROR(1,("Null tcp session")); return -EIO; } if(pbytes_returned == NULL) return -EIO; else *pbytes_returned = 0; /* 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(long_op == -1) { /* oplock breaks must not be held up */ atomic_inc(&ses->server->inFlight); } else { spin_lock(&GlobalMid_Lock); while(1) { if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){ spin_unlock(&GlobalMid_Lock); wait_event(ses->server->request_q, atomic_read(&ses->server->inFlight) < cifs_max_pending); spin_lock(&GlobalMid_Lock); } else { if(ses->server->tcpStatus == CifsExiting) { spin_unlock(&GlobalMid_Lock); return -ENOENT; } /* can not count locking commands against total since they are allowed to block on server */ if(long_op < 3) { /* update # of requests on the wire to server */ atomic_inc(&ses->server->inFlight); } spin_unlock(&GlobalMid_Lock); break; } } } /* 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 */ down(&ses->server->tcpSem); if (ses->server->tcpStatus == CifsExiting) { rc = -ENOENT; goto cifs_out_label; } else if (ses->server->tcpStatus == CifsNeedReconnect) { cFYI(1,("tcp session dead - return to caller to retry")); rc = -EAGAIN; goto cifs_out_label; } else if (ses->status != CifsGood) { /* check if SMB session is bad because we are setting it up */ if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && (in_buf->Command != SMB_COM_NEGOTIATE)) { rc = -EAGAIN; goto cifs_out_label; } /* else ok - we are setting up session */ } midQ = AllocMidQEntry(in_buf, ses); if (midQ == NULL) { up(&ses->server->tcpSem); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return -ENOMEM; } if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { up(&ses->server->tcpSem); cERROR(1, ("Illegal length, greater than maximum frame, %d ", in_buf->smb_buf_length)); DeleteMidQEntry(midQ); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return -EIO; } /* BB can we sign efficiently in this path? */ rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number); midQ->midState = MID_REQUEST_SUBMITTED; /* rc = smb_sendv(ses->server->ssocket, in_buf, in_buf->smb_buf_length, piovec, (struct sockaddr *) &(ses->server->addr.sockAddr));*/ if(rc < 0) { DeleteMidQEntry(midQ); up(&ses->server->tcpSem); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; } else up(&ses->server->tcpSem); cifs_out_label: if(midQ) DeleteMidQEntry(midQ); if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; }
int SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, struct kvec *iov, int n_vec, int * pRespBufType /* ret */, const int long_op) { int rc = 0; unsigned int receive_len; unsigned long timeout; struct mid_q_entry *midQ; struct smb_hdr *in_buf = iov[0].iov_base; *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */ if ((ses == NULL) || (ses->server == NULL)) { cifs_small_buf_release(in_buf); cERROR(1,("Null session")); return -EIO; } if(ses->server->tcpStatus == CifsExiting) { cifs_small_buf_release(in_buf); return -ENOENT; } /* 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 */ rc = wait_for_free_request(ses, long_op); if (rc) { cifs_small_buf_release(in_buf); 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 */ down(&ses->server->tcpSem); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { up(&ses->server->tcpSem); cifs_small_buf_release(in_buf); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif rc = smb_send2(ses->server->ssocket, iov, n_vec, (struct sockaddr *) &(ses->server->addr.sockAddr)); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif up(&ses->server->tcpSem); cifs_small_buf_release(in_buf); if(rc < 0) goto out; if (long_op == -1) goto out; else if (long_op == 2) /* writes past end of file can take loong time */ timeout = 180 * HZ; else if (long_op == 1) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ else timeout = 15 * HZ; /* wait for 15 seconds or until woken up due to response arriving or due to last connection to this server being unmounted */ if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout but we still give response a change to complete */ timeout = 2 * HZ; } /* No user interrupts in wait - wreaks havoc with performance */ wait_for_response(ses, midQ, timeout, 10 * HZ); spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { cERROR(1,("No response to cmd %d mid %d", midQ->command, midQ->mid)); if(midQ->midState == MID_REQUEST_SUBMITTED) { if(ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { if(midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; cFYI(1,("marking request for retry")); } else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; } else { /* rcvd frame is ok */ if (midQ->resp_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { iov[0].iov_base = (char *)midQ->resp_buf; if(midQ->largeBuf) *pRespBufType = CIFS_LARGE_BUFFER; else *pRespBufType = CIFS_SMALL_BUFFER; iov[0].iov_len = receive_len + 4; dump_smb(midQ->resp_buf, 80); /* convert the length into a more usable form */ if((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(midQ->resp_buf, ses->server->mac_signing_key, midQ->sequence_number+1); if(rc) { cERROR(1,("Unexpected SMB signature")); /* BB FIXME add code to kill session */ } } /* BB special case reconnect tid and uid here? */ /* BB special case Errbadpassword and pwdexpired here */ rc = map_smb_to_linux_error(midQ->resp_buf); /* convert ByteCount if necessary */ if (receive_len >= sizeof (struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ ) BCC(midQ->resp_buf) = le16_to_cpu(BCC_LE(midQ->resp_buf)); midQ->resp_buf = NULL; /* mark it so will not be freed by DeleteMidQEntry */ } else { rc = -EIO; cFYI(1,("Bad MID state?")); } } out: DeleteMidQEntry(midQ); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; }