patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / fs / cifs / transport.c
index aaa7899..3668ab0 100644 (file)
 #include <linux/net.h>
 #include <asm/uaccess.h>
 #include <asm/processor.h>
+#include <linux/mempool.h>
 #include "cifspdu.h"
 #include "cifsglob.h"
 #include "cifsproto.h"
 #include "cifs_debug.h"
   
-extern kmem_cache_t *cifs_mid_cachep;
+extern mempool_t *cifs_mid_poolp;
 extern kmem_cache_t *cifs_oplock_cachep;
 
 struct mid_q_entry *
@@ -47,8 +48,7 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
                return NULL;
        }
        
-       temp = (struct mid_q_entry *) kmem_cache_alloc(cifs_mid_cachep,
-                                                      SLAB_KERNEL);
+       temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,SLAB_KERNEL | SLAB_NOFS);
        if (temp == NULL)
                return temp;
        else {
@@ -79,7 +79,7 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
        atomic_dec(&midCount);
        spin_unlock(&GlobalMid_Lock);
        cifs_buf_release(midEntry->resp_buf);
-       kmem_cache_free(cifs_mid_cachep, midEntry);
+       mempool_free(midEntry, cifs_mid_poolp);
 }
 
 struct oplock_q_entry *
@@ -120,13 +120,13 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
         unsigned int smb_buf_length, struct sockaddr *sin)
 {
        int rc = 0;
+       int i = 0;
        struct msghdr smb_msg;
        struct iovec iov;
        mm_segment_t temp_fs;
 
        if(ssocket == NULL)
                return -ENOTSOCK; /* BB eventually add reconnect code here */
-/*  ssocket->sk->allocation = GFP_BUFFER; *//* BB is this spurious? */
        iov.iov_base = smb_buffer;
        iov.iov_len = smb_buf_length + 4;
 
@@ -152,6 +152,14 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
        while(iov.iov_len > 0) {
                rc = sock_sendmsg(ssocket, &smb_msg, smb_buf_length + 4);
                if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
+                       i++;
+                       if(i > 60) {
+                               cERROR(1,
+                                  ("sends on sock %p stuck for 30 seconds",
+                                   ssocket));
+                               rc = -EAGAIN;
+                               break;
+                       }
                        set_current_state(TASK_INTERRUPTIBLE);
                        schedule_timeout(HZ/2);
                        continue;
@@ -182,31 +190,60 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
        long timeout;
        struct mid_q_entry *midQ;
 
-       if ((ses == NULL) || (ses->server == NULL)) {
-               cERROR(1,("Null tcp session or smb session: %p",ses));
+       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 */
+
+       /* 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);
+       }
+
+       /* 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) {
-               return -ENOENT;
+               rc = -ENOENT;
+               goto out_unlock;
        } else if (ses->server->tcpStatus == CifsNeedReconnect) {
                cFYI(1,("tcp session dead - return to caller to retry"));
-               return -EAGAIN;
+               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)) {
-                       return -EAGAIN;
+                       rc = -EAGAIN;
+                       goto out_unlock;
                } /* else ok - we are setting up session */
        }
-       /* 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); 
        midQ = AllocMidQEntry(in_buf, ses);
        if (midQ == NULL) {
                up(&ses->server->tcpSem);
-               return -EIO;
+               /* 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) {
@@ -215,6 +252,11 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
                       ("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;
        }
 
@@ -226,7 +268,17 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
        midQ->midState = MID_REQUEST_SUBMITTED;
        rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
                      (struct sockaddr *) &(ses->server->addr.sockAddr));
-       up(&ses->server->tcpSem);
+       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 */
@@ -285,10 +337,14 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
                }
                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",
@@ -338,8 +394,22 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
                }
        }
 cifs_no_response_exit:
-       DeleteMidQEntry(midQ);  /* BB what if process is killed?
-                        - BB add background daemon to clean up Mid entries from
-                        killed processes & test killing process with active mid */
+       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;
 }