[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: Steve French (sfrench@us.ibm.com) Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
c81156dd21
commit
b8643e1b52
3 changed files with 65 additions and 33 deletions
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* fs/cifs_debug.c
|
* 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)
|
* Modified by Steve French (sfrench@us.ibm.com)
|
||||||
*
|
*
|
||||||
|
@ -92,8 +92,10 @@ cifs_debug_data_read(char *buf, char **beginBuffer, off_t offset,
|
||||||
length =
|
length =
|
||||||
sprintf(buf,
|
sprintf(buf,
|
||||||
"\n%d) Name: %s Domain: %s Mounts: %d ServerOS: %s \n\tServerNOS: %s\tCapabilities: 0x%x\n\tSMB session status: %d\t",
|
"\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),
|
i, ses->serverName, ses->serverDomain,
|
||||||
ses->serverOS, ses->serverNOS, ses->capabilities,ses->status);
|
atomic_read(&ses->inUse),
|
||||||
|
ses->serverOS, ses->serverNOS,
|
||||||
|
ses->capabilities,ses->status);
|
||||||
buf += length;
|
buf += length;
|
||||||
if(ses->server) {
|
if(ses->server) {
|
||||||
buf += sprintf(buf, "TCP status: %d\n\tLocal Users To Server: %d SecMode: 0x%x Req Active: %d",
|
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;
|
buf += item_length;
|
||||||
item_length =
|
item_length =
|
||||||
sprintf(buf,"SMB Request/Response Buffer: %d Pool size: %d\n",
|
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;
|
length += item_length;
|
||||||
buf += item_length;
|
buf += item_length;
|
||||||
item_length =
|
item_length =
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* fs/cifs/connect.c
|
* 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)
|
* Author(s): Steve French (sfrench@us.ibm.com)
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or modify
|
* This library is free software; you can redistribute it and/or modify
|
||||||
|
@ -28,6 +28,7 @@
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
#include <linux/utsname.h>
|
#include <linux/utsname.h>
|
||||||
#include <linux/mempool.h>
|
#include <linux/mempool.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <asm/processor.h>
|
#include <asm/processor.h>
|
||||||
#include "cifspdu.h"
|
#include "cifspdu.h"
|
||||||
|
@ -198,6 +199,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
||||||
int length;
|
int length;
|
||||||
unsigned int pdu_length, total_read;
|
unsigned int pdu_length, total_read;
|
||||||
struct smb_hdr *smb_buffer = NULL;
|
struct smb_hdr *smb_buffer = NULL;
|
||||||
|
struct smb_hdr *bigbuf = NULL;
|
||||||
|
struct smb_hdr *smallbuf = NULL;
|
||||||
struct msghdr smb_msg;
|
struct msghdr smb_msg;
|
||||||
struct kvec iov;
|
struct kvec iov;
|
||||||
struct socket *csocket = server->ssocket;
|
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 task_struct *task_to_wake = NULL;
|
||||||
struct mid_q_entry *mid_entry;
|
struct mid_q_entry *mid_entry;
|
||||||
char *temp;
|
char *temp;
|
||||||
|
int isLargeBuf = FALSE;
|
||||||
|
|
||||||
daemonize("cifsd");
|
daemonize("cifsd");
|
||||||
allow_signal(SIGKILL);
|
allow_signal(SIGKILL);
|
||||||
|
@ -223,17 +227,33 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
||||||
}
|
}
|
||||||
|
|
||||||
while (server->tcpStatus != CifsExiting) {
|
while (server->tcpStatus != CifsExiting) {
|
||||||
if (smb_buffer == NULL)
|
if (bigbuf == NULL) {
|
||||||
smb_buffer = cifs_buf_get();
|
bigbuf = cifs_buf_get();
|
||||||
else
|
if(bigbuf == NULL) {
|
||||||
memset(smb_buffer, 0, sizeof (struct smb_hdr));
|
cERROR(1,("No memory for large SMB response"));
|
||||||
|
msleep(3000);
|
||||||
if (smb_buffer == NULL) {
|
/* retry will check if exiting */
|
||||||
cERROR(1,("Can not get memory for SMB response"));
|
continue;
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
}
|
||||||
schedule_timeout(HZ * 3); /* give system time to free memory */
|
} else if(isLargeBuf) {
|
||||||
continue;
|
/* 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_base = smb_buffer;
|
||||||
iov.iov_len = 4;
|
iov.iov_len = 4;
|
||||||
smb_msg.msg_control = NULL;
|
smb_msg.msg_control = NULL;
|
||||||
|
@ -251,8 +271,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
||||||
csocket = server->ssocket;
|
csocket = server->ssocket;
|
||||||
continue;
|
continue;
|
||||||
} else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) {
|
} else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) {
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
msleep(1); /* minimum sleep to prevent looping
|
||||||
schedule_timeout(1); /* minimum sleep to prevent looping
|
|
||||||
allowing socket to clear and app threads to set
|
allowing socket to clear and app threads to set
|
||||||
tcpStatus CifsNeedReconnect if server hung */
|
tcpStatus CifsNeedReconnect if server hung */
|
||||||
continue;
|
continue;
|
||||||
|
@ -295,8 +314,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
||||||
} else {
|
} else {
|
||||||
/* give server a second to
|
/* give server a second to
|
||||||
clean up before reconnect attempt */
|
clean up before reconnect attempt */
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
msleep(1000);
|
||||||
schedule_timeout(HZ);
|
|
||||||
/* always try 445 first on reconnect
|
/* always try 445 first on reconnect
|
||||||
since we get NACK on some if we ever
|
since we get NACK on some if we ever
|
||||||
connected to port 139 (the NACK is
|
connected to port 139 (the NACK is
|
||||||
|
@ -325,6 +343,11 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
||||||
wake_up(&server->response_q);
|
wake_up(&server->response_q);
|
||||||
continue;
|
continue;
|
||||||
} else { /* length ok */
|
} else { /* length ok */
|
||||||
|
if(pdu_length > MAX_CIFS_HDR_SIZE - 4) {
|
||||||
|
isLargeBuf = TRUE;
|
||||||
|
memcpy(bigbuf, smallbuf, 4);
|
||||||
|
smb_buffer = bigbuf;
|
||||||
|
}
|
||||||
length = 0;
|
length = 0;
|
||||||
iov.iov_base = 4 + (char *)smb_buffer;
|
iov.iov_base = 4 + (char *)smb_buffer;
|
||||||
iov.iov_len = pdu_length;
|
iov.iov_len = pdu_length;
|
||||||
|
@ -377,6 +400,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
||||||
}
|
}
|
||||||
spin_unlock(&GlobalMid_Lock);
|
spin_unlock(&GlobalMid_Lock);
|
||||||
if (task_to_wake) {
|
if (task_to_wake) {
|
||||||
|
if(isLargeBuf)
|
||||||
|
bigbuf = NULL;
|
||||||
|
else
|
||||||
|
smallbuf = NULL;
|
||||||
smb_buffer = NULL; /* will be freed by users thread after he is done */
|
smb_buffer = NULL; /* will be freed by users thread after he is done */
|
||||||
wake_up_process(task_to_wake);
|
wake_up_process(task_to_wake);
|
||||||
} else if (is_valid_oplock_break(smb_buffer) == FALSE) {
|
} 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. */
|
and get out of SendReceive. */
|
||||||
wake_up_all(&server->request_q);
|
wake_up_all(&server->request_q);
|
||||||
/* give those requests time to exit */
|
/* give those requests time to exit */
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
msleep(125);
|
||||||
schedule_timeout(HZ/8);
|
|
||||||
|
|
||||||
if(server->ssocket) {
|
if(server->ssocket) {
|
||||||
sock_release(csocket);
|
sock_release(csocket);
|
||||||
server->ssocket = NULL;
|
server->ssocket = NULL;
|
||||||
}
|
}
|
||||||
if (smb_buffer) /* buffer usually freed in free_mid - need to free it on error or exit */
|
/* buffer usuallly freed in free_mid - need to free it here on exit */
|
||||||
cifs_buf_release(smb_buffer);
|
if (bigbuf != NULL)
|
||||||
|
cifs_buf_release(bigbuf);
|
||||||
|
if (smallbuf != NULL)
|
||||||
|
cifs_small_buf_release(smallbuf);
|
||||||
|
|
||||||
read_lock(&GlobalSMBSeslock);
|
read_lock(&GlobalSMBSeslock);
|
||||||
if (list_empty(&server->pending_mid_q)) {
|
if (list_empty(&server->pending_mid_q)) {
|
||||||
|
@ -444,17 +473,15 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
||||||
}
|
}
|
||||||
spin_unlock(&GlobalMid_Lock);
|
spin_unlock(&GlobalMid_Lock);
|
||||||
read_unlock(&GlobalSMBSeslock);
|
read_unlock(&GlobalSMBSeslock);
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
|
||||||
/* 1/8th of sec is more than enough time for them to exit */
|
/* 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)) {
|
if (list_empty(&server->pending_mid_q)) {
|
||||||
/* mpx threads have not exited yet give them
|
/* mpx threads have not exited yet give them
|
||||||
at least the smb send timeout time for long ops */
|
at least the smb send timeout time for long ops */
|
||||||
cFYI(1, ("Wait for exit from demultiplex thread"));
|
cFYI(1, ("Wait for exit from demultiplex thread"));
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
msleep(46);
|
||||||
schedule_timeout(46 * HZ);
|
|
||||||
/* if threads still have not exited they are probably never
|
/* if threads still have not exited they are probably never
|
||||||
coming home not much else we can do but free the memory */
|
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,
|
length + cifs_min_rcv,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
msleep(250);
|
||||||
schedule_timeout(HZ/4);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* fs/cifs/transport.c
|
* 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)
|
* Author(s): Steve French (sfrench@us.ibm.com)
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or modify
|
* 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);
|
list_del(&midEntry->qhead);
|
||||||
atomic_dec(&midCount);
|
atomic_dec(&midCount);
|
||||||
spin_unlock(&GlobalMid_Lock);
|
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);
|
mempool_free(midEntry, cifs_mid_poolp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue