X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fcifs%2Ftransport.c;h=0046c219833d6cfbef77e85addc663e1be71e290;hb=f7f1b0f1e2fbadeab12d24236000e778aa9b1ead;hp=3668ab00b3ef2a290287510a8051dd7d77affae6;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 3668ab00b..0046c2198 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -1,7 +1,7 @@ /* * fs/cifs/transport.c * - * Copyright (C) International Business Machines Corp., 2002,2004 + * Copyright (C) International Business Machines Corp., 2002,2005 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -34,13 +35,13 @@ extern mempool_t *cifs_mid_poolp; extern kmem_cache_t *cifs_oplock_cachep; -struct mid_q_entry * +static struct mid_q_entry * AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) { struct mid_q_entry *temp; if (ses == NULL) { - cERROR(1, ("Null session passed in to AllocMidQEntry ")); + cERROR(1, ("Null session passed in to AllocMidQEntry")); return NULL; } if (ses->server == NULL) { @@ -70,7 +71,7 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) return temp; } -void +static void DeleteMidQEntry(struct mid_q_entry *midEntry) { spin_lock(&GlobalMid_Lock); @@ -78,7 +79,10 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) list_del(&midEntry->qhead); atomic_dec(&midCount); spin_unlock(&GlobalMid_Lock); - cifs_buf_release(midEntry->resp_buf); + if(midEntry->largeBuf) + cifs_buf_release(midEntry->resp_buf); + else + cifs_small_buf_release(midEntry->resp_buf); mempool_free(midEntry, cifs_mid_poolp); } @@ -122,18 +126,16 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, int rc = 0; int i = 0; struct msghdr smb_msg; - struct iovec iov; - mm_segment_t temp_fs; + struct kvec iov; + unsigned len = smb_buf_length + 4; if(ssocket == NULL) return -ENOTSOCK; /* BB eventually add reconnect code here */ iov.iov_base = smb_buffer; - iov.iov_len = smb_buf_length + 4; + iov.iov_len = len; smb_msg.msg_name = sin; smb_msg.msg_namelen = sizeof (struct sockaddr); - smb_msg.msg_iov = &iov; - smb_msg.msg_iovlen = 1; smb_msg.msg_control = NULL; smb_msg.msg_controllen = 0; smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/ @@ -145,12 +147,10 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); cFYI(1, ("Sending smb of length %d ", smb_buf_length)); - dump_smb(smb_buffer, smb_buf_length + 4); + dump_smb(smb_buffer, len); - temp_fs = get_fs(); /* we must turn off socket api parm checking */ - set_fs(get_ds()); - while(iov.iov_len > 0) { - rc = sock_sendmsg(ssocket, &smb_msg, smb_buf_length + 4); + while (len > 0) { + rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; if(i > 60) { @@ -160,16 +160,15 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, rc = -EAGAIN; break; } - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/2); + msleep(500); continue; } if (rc < 0) break; iov.iov_base += rc; iov.iov_len -= rc; + len -= rc; } - set_fs(temp_fs); if (rc < 0) { cERROR(1,("Error %d sending data on socket to server.", rc)); @@ -180,6 +179,212 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, return rc; } +#ifdef CIFS_EXPERIMENTAL +/* BB finish off this function, adding support for writing set of pages as iovec */ +/* and also adding support for operations that need to parse the response smb */ + +int +smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, + unsigned int smb_buf_length, struct kvec * write_vector + /* page list */, struct sockaddr *sin) +{ + int rc = 0; + int i = 0; + struct msghdr smb_msg; + number_of_pages += 1; /* account for SMB header */ + struct kvec * piov = kmalloc(number_of_pages * sizeof(struct kvec)); + unsigned len = smb_buf_length + 4; + + if(ssocket == NULL) + return -ENOTSOCK; /* BB eventually add reconnect code here */ + iov.iov_base = smb_buffer; + iov.iov_len = len; + + smb_msg.msg_name = sin; + smb_msg.msg_namelen = sizeof (struct sockaddr); + smb_msg.msg_control = NULL; + smb_msg.msg_controllen = 0; + smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/ + + /* smb header is converted in header_assemble. bcc and rest of SMB word + area, and byte area if necessary, is converted to littleendian in + cifssmb.c and RFC1001 len is converted to bigendian in smb_send + Flags2 is converted in SendReceive */ + + smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); + cFYI(1, ("Sending smb of length %d ", smb_buf_length)); + dump_smb(smb_buffer, len); + + while (len > 0) { + rc = kernel_sendmsg(ssocket, &smb_msg, &iov, number_of_pages, + len); + if ((rc == -ENOSPC) || (rc == -EAGAIN)) { + i++; + if(i > 60) { + cERROR(1, + ("sends on sock %p stuck for 30 seconds", + ssocket)); + rc = -EAGAIN; + break; + } + msleep(500); + continue; + } + if (rc < 0) + break; + iov.iov_base += rc; + iov.iov_len -= rc; + len -= rc; + } + + if (rc < 0) { + cERROR(1,("Error %d sending data on socket to server.", rc)); + } else { + rc = 0; + } + + return rc; +} + + +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; + + + + if(ses->server->tcpStatus == CIFS_EXITING) + 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(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->server, &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; +} + + +#endif /* CIFS_EXPERIMENTAL */ + int SendReceive(const unsigned int xid, struct cifsSesInfo *ses, struct smb_hdr *in_buf, struct smb_hdr *out_buf, @@ -187,7 +392,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, { int rc = 0; unsigned int receive_len; - long timeout; + unsigned long timeout; struct mid_q_entry *midQ; if (ses == NULL) { @@ -199,24 +404,46 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, 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(ses->server->tcpStatus == CifsExiting) + return -ENOENT; - /* can not count locking commands against the total since - they are allowed to block on server */ - if(long_op < 3) { - /* update # of requests on the wire to this server */ - atomic_inc(&ses->server->inFlight); - } - - if(atomic_read(&ses->server->inFlight) > CIFS_MAX_REQ) { - wait_event(ses->server->request_q,atomic_read(&ses->server->inFlight) <= CIFS_MAX_REQ); + /* 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 */ + and avoid races inside tcp sendmsg code that could cause corruption + of smb data */ down(&ses->server->tcpSem); @@ -246,7 +473,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, return -ENOMEM; } - if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) { + 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 ", @@ -260,10 +487,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, return -EIO; } - if (in_buf->smb_buf_length > 12) - in_buf->Flags2 = cpu_to_le16(in_buf->Flags2); - - rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number); + rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); midQ->midState = MID_REQUEST_SUBMITTED; rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, @@ -281,7 +505,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, 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 */ + else if (long_op == 2) /* writes past end of file can take loong time */ timeout = 300 * HZ; else if (long_op == 1) timeout = 45 * HZ; /* should be greater than @@ -295,27 +519,28 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, 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 */ - if(midQ->midState & MID_REQUEST_SUBMITTED) { - set_current_state(TASK_UNINTERRUPTIBLE); - timeout = sleep_on_timeout(&ses->server->response_q,2 * HZ); - } - } else { /* using normal timeout */ - /* timeout = wait_event_interruptible_timeout(ses->server->response_q, - (midQ->midState & MID_RESPONSE_RECEIVED) || + 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_REQUEST_SUBMITTED)) || + time_after(jiffies, timeout) || ((ses->server->tcpStatus != CifsGood) && - (ses->server->tcpStatus != CifsNew)), - timeout); */ - /* Can not allow user interrupts- wreaks havoc with performance */ - if(midQ->midState & MID_REQUEST_SUBMITTED) { - set_current_state(TASK_UNINTERRUPTIBLE); - timeout = sleep_on_timeout(&ses->server->response_q,timeout); - } + (ses->server->tcpStatus != CifsNew))); + } else { + wait_event(ses->server->response_q, + (!(midQ->midState & MID_REQUEST_SUBMITTED)) || + ((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(midQ->resp_buf->smb_buf_length); + receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf); } else { cERROR(1,("No response buffer")); if(midQ->midState == MID_REQUEST_SUBMITTED) { @@ -345,41 +570,36 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, return rc; } - if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) { - cERROR(1, - ("Frame too large received. Length: %d Xid: %d", + 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 && out_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { - memcpy(out_buf, midQ->resp_buf, - receive_len + - 4 /* include 4 byte RFC1001 header */ ); + 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 */ - out_buf->smb_buf_length = - be32_to_cpu(out_buf->smb_buf_length); - if((out_buf->smb_buf_length > 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")); + if((receive_len > 24) && + (ses->server->secMode & (SECMODE_SIGN_REQUIRED | + SECMODE_SIGN_ENABLED))) { + rc = cifs_verify_signature(out_buf, + ses->server->mac_signing_key, + midQ->sequence_number+1); + if(rc) { + cERROR(1,("Unexpected SMB signature")); + /* BB FIXME add code to kill session */ + } } - if (out_buf->smb_buf_length > 12) - out_buf->Flags2 = le16_to_cpu(out_buf->Flags2); - if (out_buf->smb_buf_length > 28) - out_buf->Pid = le16_to_cpu(out_buf->Pid); - if (out_buf->smb_buf_length > 28) - out_buf->PidHigh = - le16_to_cpu(out_buf->PidHigh); - *pbytes_returned = out_buf->smb_buf_length; - /* BB special case reconnect tid and reconnect uid here? */ + /* BB special case reconnect tid and uid here? */ rc = map_smb_to_linux_error(out_buf); /* convert ByteCount if necessary */