Commit b8643e1b authored by Steve French's avatar Steve French Committed by Linus Torvalds
Browse files

[PATCH] cifs: Do not use large smb buffers in response path



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: default avatarSteve French <(sfrench@us.ibm.com)>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent c81156dd
Loading
Loading
Loading
Loading
+7 −4
Original line number Diff line number Diff line
/*
 *   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 = 
+53 −27
Original line number Diff line number Diff line
/*
 *   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 (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 (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 */
		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 */
	}
@@ -470,8 +497,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
			GFP_KERNEL);
	}
	
	set_current_state(TASK_INTERRUPTIBLE);
	schedule_timeout(HZ/4);
	msleep(250);
	return 0;
}

+5 −2
Original line number Diff line number Diff line
/*
 *   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);
	if(midEntry->largeBuf)
		cifs_buf_release(midEntry->resp_buf);
	else
		cifs_small_buf_release(midEntry->resp_buf);
	mempool_free(midEntry, cifs_mid_poolp);
}