[PATCH] cifs: Do not use large smb buffers in response path
authorSteve French <smfrench@austin.rr.com>
Fri, 29 Apr 2005 05:41:07 +0000 (22:41 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Fri, 29 Apr 2005 05:41:07 +0000 (22:41 -0700)
unless response is larger than 256 bytes.  This cuts more than 1/3 of
the large memory allocations that cifs does and should be a huge help to
memory pressure under stress.

Signed-off-by: Steve French (sfrench@us.ibm.com)
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/cifs/cifs_debug.c
fs/cifs/connect.c
fs/cifs/transport.c

index efa099165b22ea5d52a2d8c454cbbaf201596083..bff2ec6e054adbc83c37c4befe9c22d8d68cc919 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs_debug.c
  *
- *   Copyright (C) International Business Machines  Corp., 2000,2003
+ *   Copyright (C) International Business Machines  Corp., 2000,2005
  *
  *   Modified by Steve French (sfrench@us.ibm.com)
  *
@@ -92,8 +92,10 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
                length =
                    sprintf(buf,
                            "\n%d) Name: %s  Domain: %s Mounts: %d ServerOS: %s  \n\tServerNOS: %s\tCapabilities: 0x%x\n\tSMB session status: %d\t",
-                               i, ses->serverName, ses->serverDomain, atomic_read(&ses->inUse),
-                               ses->serverOS, ses->serverNOS, ses->capabilities,ses->status);
+                               i, ses->serverName, ses->serverDomain,
+                               atomic_read(&ses->inUse),
+                               ses->serverOS, ses->serverNOS,
+                               ses->capabilities,ses->status);
                buf += length;
                if(ses->server) {
                        buf += sprintf(buf, "TCP status: %d\n\tLocal Users To Server: %d SecMode: 0x%x Req Active: %d",
@@ -207,7 +209,8 @@ cifs_stats_read(char *buf, char **beginBuffer, off_t offset,
        buf += item_length;      
        item_length = 
                sprintf(buf,"SMB Request/Response Buffer: %d Pool size: %d\n",
-                       bufAllocCount.counter,cifs_min_rcv + tcpSesAllocCount.counter);
+                       bufAllocCount.counter,
+                       cifs_min_rcv + tcpSesAllocCount.counter);
        length += item_length;
        buf += item_length;
        item_length = 
index 383e55fa7d260687930daeb43b1309f58175de33..390f22fa343908003b4241c3d99e7bde2ea84ba6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/connect.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
@@ -28,6 +28,7 @@
 #include <linux/ctype.h>
 #include <linux/utsname.h>
 #include <linux/mempool.h>
+#include <linux/delay.h>
 #include <asm/uaccess.h>
 #include <asm/processor.h>
 #include "cifspdu.h"
@@ -198,6 +199,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
        int length;
        unsigned int pdu_length, total_read;
        struct smb_hdr *smb_buffer = NULL;
+       struct smb_hdr *bigbuf = NULL;
+       struct smb_hdr *smallbuf = NULL;
        struct msghdr smb_msg;
        struct kvec iov;
        struct socket *csocket = server->ssocket;
@@ -206,6 +209,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
        struct task_struct *task_to_wake = NULL;
        struct mid_q_entry *mid_entry;
        char *temp;
+       int isLargeBuf = FALSE;
 
        daemonize("cifsd");
        allow_signal(SIGKILL);
@@ -223,17 +227,33 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
        }
 
        while (server->tcpStatus != CifsExiting) {
-               if (smb_buffer == NULL)
-                       smb_buffer = cifs_buf_get();
-               else
-                       memset(smb_buffer, 0, sizeof (struct smb_hdr));
-
-               if (smb_buffer == NULL) {
-                       cERROR(1,("Can not get memory for SMB response"));
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       schedule_timeout(HZ * 3); /* give system time to free memory */
-                       continue;
+               if (bigbuf == NULL) {
+                       bigbuf = cifs_buf_get();
+                       if(bigbuf == NULL) {
+                               cERROR(1,("No memory for large SMB response"));
+                               msleep(3000);
+                               /* retry will check if exiting */
+                               continue;
+                       }
+               } else if(isLargeBuf) {
+                       /* we are reusing a dirtry large buf, clear its start */
+                       memset(bigbuf, 0, sizeof (struct smb_hdr));
                }
+
+               if (smallbuf == NULL) {
+                       smallbuf = cifs_small_buf_get();
+                       if(smallbuf == NULL) {
+                               cERROR(1,("No memory for SMB response"));
+                               msleep(1000);
+                               /* retry will check if exiting */
+                               continue;
+                       }
+                       /* beginning of smb buffer is cleared in our buf_get */
+               } else /* if existing small buf clear beginning */
+                       memset(smallbuf, 0, sizeof (struct smb_hdr));
+
+               isLargeBuf = FALSE;
+               smb_buffer = smallbuf;
                iov.iov_base = smb_buffer;
                iov.iov_len = 4;
                smb_msg.msg_control = NULL;
@@ -251,8 +271,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
                        csocket = server->ssocket;
                        continue;
                } else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) {
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       schedule_timeout(1); /* minimum sleep to prevent looping
+                       msleep(1); /* minimum sleep to prevent looping
                                allowing socket to clear and app threads to set
                                tcpStatus CifsNeedReconnect if server hung */
                        continue;
@@ -295,8 +314,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
                                } else {
                                        /* give server a second to
                                        clean up before reconnect attempt */
-                                       set_current_state(TASK_INTERRUPTIBLE);
-                                       schedule_timeout(HZ);
+                                       msleep(1000);
                                        /* always try 445 first on reconnect
                                        since we get NACK on some if we ever
                                        connected to port 139 (the NACK is 
@@ -325,6 +343,11 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
                                        wake_up(&server->response_q);
                                        continue;
                                } else { /* length ok */
+                                       if(pdu_length > MAX_CIFS_HDR_SIZE - 4) {
+                                               isLargeBuf = TRUE;
+                                               memcpy(bigbuf, smallbuf, 4);
+                                               smb_buffer = bigbuf;
+                                       }
                                        length = 0;
                                        iov.iov_base = 4 + (char *)smb_buffer;
                                        iov.iov_len = pdu_length;
@@ -377,6 +400,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
                                }
                                spin_unlock(&GlobalMid_Lock);
                                if (task_to_wake) {
+                                       if(isLargeBuf)
+                                               bigbuf = NULL;
+                                       else
+                                               smallbuf = NULL;
                                        smb_buffer = NULL;      /* will be freed by users thread after he is done */
                                        wake_up_process(task_to_wake);
                                } else if (is_valid_oplock_break(smb_buffer) == FALSE) {                          
@@ -406,15 +433,17 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
        and get out of SendReceive.  */
        wake_up_all(&server->request_q);
        /* give those requests time to exit */
-       set_current_state(TASK_INTERRUPTIBLE);
-       schedule_timeout(HZ/8);
-
+       msleep(125);
+       
        if(server->ssocket) {
                sock_release(csocket);
                server->ssocket = NULL;
        }
-       if (smb_buffer) /* buffer usually freed in free_mid - need to free it on error or exit */
-               cifs_buf_release(smb_buffer);
+       /* buffer usuallly freed in free_mid - need to free it here on exit */
+       if (bigbuf != NULL)
+               cifs_buf_release(bigbuf);
+       if (smallbuf != NULL)
+               cifs_small_buf_release(smallbuf);
 
        read_lock(&GlobalSMBSeslock);
        if (list_empty(&server->pending_mid_q)) {
@@ -444,17 +473,15 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
                }
                spin_unlock(&GlobalMid_Lock);
                read_unlock(&GlobalSMBSeslock);
-               set_current_state(TASK_INTERRUPTIBLE);
                /* 1/8th of sec is more than enough time for them to exit */
-               schedule_timeout(HZ/8); 
+               msleep(125);
        }
 
        if (list_empty(&server->pending_mid_q)) {
                /* mpx threads have not exited yet give them 
                at least the smb send timeout time for long ops */
                cFYI(1, ("Wait for exit from demultiplex thread"));
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(46 * HZ);      
+               msleep(46);
                /* if threads still have not exited they are probably never
                coming home not much else we can do but free the memory */
        }
@@ -469,9 +496,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
                        length + cifs_min_rcv,
                        GFP_KERNEL);
        }
-
-       set_current_state(TASK_INTERRUPTIBLE);
-       schedule_timeout(HZ/4);
+       
+       msleep(250);
        return 0;
 }
 
index aab62ed4698223d915e5621339b30396d9c38eea..f9e16b39898c5f137327094d2efaf873c249d46d 100644 (file)
@@ -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
@@ -79,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);
 }