Som initial tests using read caching

David Collier-Brown -- Customer Engineering David.Collier-Brown at Sun.COM
Tue May 20 15:48:02 GMT 2003


   I sent these initial patches to Jeremy on the weekend.
The cache code assumes that
1) if the buffer size is set, cach everything that's read
2) if write caching is being done befor the first read,
    no read caching should take place.

   These criteria probably need to be improved on...

--dave
-- 
David Collier-Brown,           | Always do right. This will gratify
Sun Microsystems DCMO          | some people and astonish the rest.
Toronto, Ontario               |
(905) 415-2849 or x52849       | davecb at canada.sun.com

-------------- next part --------------
--- close.old.c	Sat May 17 21:03:46 2003
+++ close.c	Tue May 13 18:22:35 2003
@@ -96,6 +96,7 @@ static int close_filestruct(files_struct
 			ret = -1;
 
 		delete_write_cache(fsp);
+		delete_read_cache(fsp);
 	}
 
 	conn->num_files_open--;

-------------- next part --------------
--- fileio.old.c	Thu May  1 17:23:28 2003
+++ fileio.c	Sat May 17 19:28:07 2003
@@ -23,6 +23,11 @@
 #include "includes.h"
 
 static BOOL setup_write_cache(files_struct *, SMB_OFF_T);
+static BOOL setup_read_cache(files_struct *);
+static int copy_from_cache(write_cache *, char *, size_t);
+static int read_from_read_cache(files_struct *,char *,SMB_OFF_T,size_t);
+static BOOL setup_read_cache(files_struct *);
+static void delete_cache(write_cache *cp);
 
 /****************************************************************************
  Seek a file. Try to avoid the seek if possible.
@@ -52,8 +57,6 @@ static SMB_OFF_T seek_file(files_struct 
 /****************************************************************************
  Read from write cache if we can.
 ****************************************************************************/
-
-
 static BOOL read_from_write_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
 {
 	write_cache *wcp = fsp->wcp;
@@ -71,38 +74,157 @@ static BOOL read_from_write_cache(files_
 	return True;
 }
 
+
+/* 
+ * Read from the read cache, too.
+ */
+static int read_from_read_cache(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
+{
+	write_cache *rcp;
+	int	rc, 
+		ncopied = 0;
+
+	DEBUG(10,("read_from_read_cache\n"));
+	rcp = fsp->rcp; 
+	if (rcp == NULL && fsp->wcp != NULL) {
+		/* There is a write cache, and it takes precedence. */
+		return 0;
+	}
+	else if (rcp == NULL) {
+		/* There is no write cache, so make a read cache. */
+		if (setup_read_cache(fsp) == False) {
+			return 0;
+		}
+		else {
+			/* Reload our copy of rcp. */
+			rcp = fsp->rcp; 
+		}
+	}
+
+
+	if (rcp->data_size == -1) {
+		/* We are already at eof. */
+		return EOF;
+	}
+	else if (n <= rcp->data_size) {
+		/* It can be read straight from the buffer. */
+		(void) copy_from_cache(rcp, &data[ncopied], n);
+		return n;
+	}
+	else {
+		/* It requires a buffer-refilling loop. */
+		ncopied = 0;
+		rc = copy_from_cache(rcp, &data[ncopied], rcp->data_size);
+		ncopied += rc;
+		n -= rc;
+
+		while ((rc = reload_read_cache(fsp)) != 0 && rc != -1) {
+			if (n <= rc) {
+				rc = copy_from_cache(rcp, &data[ncopied], n);
+				ncopied += rc;
+				return ncopied;
+			}
+			else {
+				rc = copy_from_cache(rcp, &data[ncopied], rcp->data_size);
+				ncopied += rc;
+				n -= rc;
+			}
+		}
+	}
+
+	/* DO_PROFILE_INC(readcache_read_hits); */
+	return ncopied;
+}
+
+
+/*
+ * copy_from_cache -- do the bookkeeping in one place.
+ */
+ static int
+copy_from_cache(write_cache *rcp, char *data, size_t size) {
+	size_t i = MIN(size, rcp->data_size);
+
+	(void) memcpy(data, rcp->at, i);
+	rcp->at += i;
+	rcp->offset += i;
+	rcp->data_size -= i;
+	return i;
+}
+
+
+/*
+ * reload_read_cache -- return a non-zero length or EOF.
+ */
+ int
+reload_read_cache(files_struct *fsp) {
+	write_cache *rcp = fsp->rcp; /* The read cache. */
+	int	nread;
+
+	rcp->at = rcp->data;
+	nread = read(fsp->fd, rcp->data, rcp->alloc_size);
+	if (nread == -1 || nread == 0) {
+		rcp->data_size = -1; /* We hit EOF. */
+		return -1;
+	}
+	else {
+		rcp->data_size = nread;
+		rcp->data[nread] = '\0';
+		return nread;
+	}
+}
+
+
+
 /****************************************************************************
  Read from a file.
 ****************************************************************************/
 
 ssize_t read_file(files_struct *fsp,char *data,SMB_OFF_T pos,size_t n)
 {
-	ssize_t ret=0,readret;
+	ssize_t ret=0,
+		nread;
 
 	/* you can't read from print files */
 	if (fsp->print_file)
 		return -1;
-
 	/*
-	 * Serve from write cache if we can.
+	 * Serve from read cache if we can. If not, we'll
+	 * continue to the write cache and one last direct read.
 	 */
+	if ((nread = read_from_read_cache(fsp, data, pos, n)) > 0) {
+                return nread;
+	}
 
-	if(read_from_write_cache(fsp, data, pos, n))
+	/* Ditto write cache, but flush & reseek if not. */
+	if (read_from_write_cache(fsp, data, pos, n))
 		return n;
-
 	flush_write_cache(fsp, READ_FLUSH);
-
 	if (seek_file(fsp,pos) == -1) {
 		DEBUG(3,("read_file: Failed to seek to %.0f\n",(double)pos));
 		return(ret);
 	}
   
+	ret = really_read_file(fsp, data, pos, n);
+
+	DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
+		fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
+
+	return(ret);
+}
+
+/* 
+ * really_read_file -- read with optional retry hack.
+ */
+ int 
+really_read_file(files_struct *fsp, char *data, SMB_OFF_T pos, size_t n) {
+	int	 ret, rc;
+
 	if (n > 0) {
 #ifdef DMF_FIX
 		int numretries = 3;
 tryagain:
-		readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
-		if (readret == -1) {
+		rc = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
+		if (rc == -1) {
 			if ((errno == EAGAIN) && numretries) {
 				DEBUG(3,("read_file EAGAIN retry in 10 seconds\n"));
 				(void)sleep(10);
@@ -112,17 +234,13 @@ tryagain:
 			return -1;
 		}
 #else /* NO DMF fix. */
-		readret = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
-		if (readret == -1)
+		rc = fsp->conn->vfs_ops.read(fsp,fsp->fd,data,n);
+		if (rc == -1)
 			return -1;
 #endif
-		if (readret > 0)
-			ret += readret;
+		if (rc > 0)
+			ret += rc;
 	}
-
-	DEBUG(10,("read_file (%s): pos = %.0f, size = %lu, returned %lu\n",
-		fsp->fsp_name, (double)pos, (unsigned long)n, (long)ret ));
-
 	return(ret);
 }
 
@@ -628,28 +746,38 @@ n = %u, wcp->offset=%.0f, wcp->data_size
 }
 
 /****************************************************************************
- Delete the write cache structure.
+ Delete the read/write cache structures.
 ****************************************************************************/
 
 void delete_write_cache(files_struct *fsp)
 {
-	write_cache *wcp;
+	if (fsp) {
+		delete_cache(fsp->wcp);
+		DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
+	}
+}
 
-	if(!fsp)
-		return;
+void delete_read_cache(files_struct *fsp)
+{
+	if (fsp) {
+		delete_cache(fsp->rcp);
+		DEBUG(10,("delete_read_cache: File %s deleted read cache\n", fsp->fsp_name ));
+	}
+}
+
+static void delete_cache(write_cache *cp) {
 
-	if(!(wcp = fsp->wcp))
+	if (cp == NULL)
 		return;
 
 	DO_PROFILE_DEC(writecache_allocated_write_caches);
 	allocated_write_caches--;
 
-	SMB_ASSERT(wcp->data_size == 0);
+	SMB_ASSERT(cp->data_size == 0);
 
-	SAFE_FREE(wcp->data);
-	SAFE_FREE(fsp->wcp);
+	SAFE_FREE(cp->data);
+	SAFE_FREE(cp);
 
-	DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
 }
 
 /****************************************************************************
@@ -691,6 +819,50 @@ static BOOL setup_write_cache(files_stru
 
 	DEBUG(10,("setup_write_cache: File %s allocated write cache size %u\n",
 		fsp->fsp_name, wcp->alloc_size ));
+
+	return True;
+}
+
+/* Setup read cache -- use same cache pool as write caches. */
+static BOOL setup_read_cache(files_struct *fsp)
+{
+	ssize_t alloc_size = lp_read_cache_size(SNUM(fsp->conn));
+	write_cache *rcp;
+
+	DEBUG(10,("setup_read_cache\n"));
+	if (alloc_size == 0) {
+		return False;
+	}
+	if (allocated_write_caches >= MAX_WRITE_CACHES) 
+		return False;
+
+	if (fsp->rcp)
+		return False;
+
+	if((rcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) {
+		DEBUG(0,("setup_read_cache: malloc fail.\n"));
+		return False;
+	}
+
+
+	rcp->file_size = 0;  /* Junk value.*/
+	rcp->offset = 0;     /* -1 would mean EOF. */
+	rcp->alloc_size = alloc_size;
+	rcp->data_size = 0;
+	if((rcp->data = malloc(rcp->alloc_size)) == NULL) {
+		DEBUG(0,("setup_write_cache: malloc fail for buffer size %u.\n",
+			(unsigned int)rcp->alloc_size ));
+		SAFE_FREE(rcp);
+		return False;
+	}
+	rcp->at = rcp->data;
+
+	fsp->rcp = rcp;
+	/* DO_PROFILE_INC(writecache_allocated_read_caches); */
+	allocated_write_caches++;
+
+	DEBUG(10,("setup_read_cache: File %s allocated read cache size %u\n",
+		fsp->fsp_name, rcp->alloc_size ));
 
 	return True;
 }

-------------- next part --------------
--- loadparm.old.c	Tue May  6 17:16:06 2003
+++ loadparm.c	Sat May 17 20:23:47 2003
@@ -193,7 +193,7 @@ typedef struct
 	int max_ttl;
 	int max_wins_ttl;
 	int min_wins_ttl;
-	int ReadSize;
+	int ReadCacheSize;
 	int lm_announce;
 	int lm_interval;
 	int announce_as;	/* This is initialised in init_globals */
@@ -331,6 +331,7 @@ typedef struct
 	int iMaxPrintJobs;
 	int iMaxReportedPrintJobs;
 	int iWriteCacheSize;
+	int iReadCacheSize;
 	int iCreate_mask;
 	int iCreate_force_mode;
 	int iSecurity_mask;
@@ -451,6 +452,7 @@ static service sDefault = {
 	1000,			/* iMaxPrintJobs */
 	0,			/* iMaxReportedPrintJobs */
 	0,			/* iWriteCacheSize */
+	0,			/* iReadCacheSize */
 	0744,			/* iCreate_mask */
 	0000,			/* iCreate_force_mode */
 	0777,			/* iSecurity_mask */
@@ -877,7 +879,6 @@ static struct parm_struct parm_table[] =
 	{"max disk size", P_INTEGER, P_GLOBAL, &Globals.maxdisksize, NULL, NULL, FLAG_DEVELOPER},
 	{"max open files", P_INTEGER, P_GLOBAL, &Globals.max_open_files, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
 	{"min print space", P_INTEGER, P_LOCAL, &sDefault.iMinPrintSpace, NULL, NULL, FLAG_PRINT},
-	{"read size", P_INTEGER, P_GLOBAL, &Globals.ReadSize, NULL, NULL, FLAG_DEVELOPER},
 	
 	{"socket options", P_GSTRING, P_GLOBAL, user_socket_options, NULL, NULL, FLAG_DEVELOPER},
 	{"stat cache size", P_INTEGER, P_GLOBAL, &Globals.stat_cache_size, NULL, NULL, FLAG_DEVELOPER},
@@ -888,6 +889,7 @@ static struct parm_struct parm_table[] =
 	{"use sendfile", P_BOOL, P_LOCAL, &sDefault.bUseSendfile, NULL, NULL, FLAG_SHARE},
 	{"hostname lookups", P_BOOL, P_GLOBAL, &Globals.bHostnameLookups, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
 	{"write cache size", P_INTEGER, P_LOCAL, &sDefault.iWriteCacheSize, NULL, NULL, FLAG_SHARE},
+	{"read cache size", P_INTEGER, P_LOCAL, &sDefault.iReadCacheSize, NULL, NULL, FLAG_DEVELOPER},
 
 	{"name cache timeout", P_INTEGER, P_GLOBAL, &Globals.name_cache_timeout, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
 
@@ -1354,7 +1356,7 @@ static void init_globals(void)
 	Globals.machine_password_timeout = 60 * 60 * 24 * 7;	/* 7 days default. */
 	Globals.change_notify_timeout = 60;	/* 1 minute default. */
 	Globals.bKernelChangeNotify = True;	/* On if we have it. */
-	Globals.ReadSize = 16 * 1024;
+	Globals.ReadCacheSize = 0;	/* Used to be overlap 16 * 1024, now buffer size. */
 	Globals.lm_announce = 2;	/* = Auto: send only if LM clients found */
 	Globals.lm_interval = 60;
 	Globals.stat_cache_size = 50;	/* Number of stat translations we'll keep */
@@ -1703,7 +1705,6 @@ FN_GLOBAL_INTEGER(lp_maxxmit, &Globals.m
 FN_GLOBAL_INTEGER(lp_maxmux, &Globals.max_mux)
 FN_GLOBAL_INTEGER(lp_passwordlevel, &Globals.pwordlevel)
 FN_GLOBAL_INTEGER(lp_usernamelevel, &Globals.unamelevel)
-FN_GLOBAL_INTEGER(lp_readsize, &Globals.ReadSize)
 FN_GLOBAL_INTEGER(lp_deadtime, &Globals.deadtime)
 FN_GLOBAL_INTEGER(lp_maxprotocol, &Globals.maxprotocol)
 FN_GLOBAL_INTEGER(lp_minprotocol, &Globals.minprotocol)
@@ -1832,6 +1833,7 @@ FN_LOCAL_INTEGER(lp_max_reported_jobs, i
 FN_LOCAL_INTEGER(lp_oplock_contention_limit, iOplockContentionLimit)
 FN_LOCAL_INTEGER(lp_csc_policy, iCSCPolicy)
 FN_LOCAL_INTEGER(lp_write_cache_size, iWriteCacheSize)
+FN_LOCAL_INTEGER(lp_read_cache_size, iReadCacheSize)
 FN_LOCAL_INTEGER(lp_block_size, iBlock_size)
 FN_LOCAL_CHAR(lp_magicchar, magic_char)
 FN_GLOBAL_INTEGER(lp_winbind_cache_time, &Globals.winbind_cache_time)

-------------- next part --------------
--- smb.old.h	Thu May  1 17:32:22 2003
+++ smb.h	Sun May 11 19:52:05 2003
@@ -352,6 +352,7 @@ typedef struct write_cache
     size_t alloc_size;
     size_t data_size;
     char *data;
+    char *at; /* Use only in write buffers. */
 } write_cache;
 
 typedef struct
@@ -379,6 +380,7 @@ typedef struct files_struct
 	uint16 vuid;
 	write_bmpx_struct *wbmpx_ptr;
 	write_cache *wcp;
+	write_cache *rcp;
 	struct timeval open_time;
 	int share_mode;
 	uint32 desired_access;



More information about the samba-technical mailing list