[Patch update] updated draft read cache patch

Dave Collier-Brown davec-b at rogers.com
Sat Jun 21 18:48:42 GMT 2003


This was created with 
	cvs diff -u >all.diff
from last night's CVS, (20 June 2003), then tortured 
overnight with the same sucesses and failures as the 
unpatched copy from CVS.

I added
	1) a belt-and-suspenders case: when the read
	   cache is not where the program wants to 
	   read, invalidate the cache as before, but now
	   add a seek to the new location, so the following
	   real_read is **sure** to read from the specified
	   location.
	2) a check to see that the file is oplocked.

I'll update regularly, and more often if requested (;-))

--dave
-- 
David Collier-Brown,  | Always do right. This will gratify
Americas Customer Eng | some people and astonish the rest.
at Sun Canada         |                      -- Mark Twain
(905) 415-2849        | davecb at spamcop.net
-------------- next part --------------
? Makefile.cscope
? all.diff
? cscope.out
? diff_file
Index: include/smb.h
===================================================================
RCS file: /cvsroot/samba/source/include/smb.h,v
retrieving revision 1.424.2.43
diff -u -r1.424.2.43 smb.h
--- include/smb.h	6 Jun 2003 05:15:14 -0000	1.424.2.43
+++ include/smb.h	21 Jun 2003 01:03:02 -0000
@@ -366,6 +366,7 @@
     size_t alloc_size;
     size_t data_size;
     char *data;
+    char *at; /* Used only in read buffers. */
 } write_cache;
 
 typedef struct
@@ -394,6 +395,7 @@
 	uint16 vuid;
 	write_bmpx_struct *wbmpx_ptr;
 	write_cache *wcp;
+	write_cache *rcp; /* Read cache. */
 	struct timeval open_time;
 	int share_mode;
 	uint32 desired_access;
Index: param/loadparm.c
===================================================================
RCS file: /cvsroot/samba/source/param/loadparm.c,v
retrieving revision 1.397.2.67
diff -u -r1.397.2.67 loadparm.c
--- param/loadparm.c	20 Jun 2003 01:42:13 -0000	1.397.2.67
+++ param/loadparm.c	21 Jun 2003 01:03:09 -0000
@@ -348,6 +348,7 @@
 	int iMaxPrintJobs;
 	int iMaxReportedPrintJobs;
 	int iWriteCacheSize;
+	int iReadCacheSize;
 	int iCreate_mask;
 	int iCreate_force_mode;
 	int iSecurity_mask;
@@ -468,6 +469,7 @@
 	1000,			/* iMaxPrintJobs */
 	0,			/* iMaxReportedPrintJobs */
 	0,			/* iWriteCacheSize */
+	0,			/* iReadCacheSize */
 	0744,			/* iCreate_mask */
 	0000,			/* iCreate_force_mode */
 	0777,			/* iSecurity_mask */
@@ -904,6 +906,7 @@
 	{"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_SHARE},
 
 	{"name cache timeout", P_INTEGER, P_GLOBAL, &Globals.name_cache_timeout, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
 
@@ -1851,6 +1854,7 @@
 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)
Index: smbd/close.c
===================================================================
RCS file: /cvsroot/samba/source/smbd/close.c,v
retrieving revision 1.42.2.6
diff -u -r1.42.2.6 close.c
--- smbd/close.c	14 May 2003 10:58:58 -0000	1.42.2.6
+++ smbd/close.c	21 Jun 2003 01:03:10 -0000
@@ -96,6 +96,7 @@
 			ret = -1;
 
 		delete_write_cache(fsp);
+		delete_read_cache(fsp);
 	}
 
 	conn->num_files_open--;
Index: smbd/fileio.c
===================================================================
RCS file: /cvsroot/samba/source/smbd/fileio.c,v
retrieving revision 1.40.2.9
diff -u -r1.40.2.9 fileio.c
--- smbd/fileio.c	14 May 2003 10:58:58 -0000	1.40.2.9
+++ smbd/fileio.c	21 Jun 2003 01:03:11 -0000
@@ -23,6 +23,12 @@
 #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 void invalidate_read_cache(write_cache *);
+static void delete_cache(write_cache *cp);
+
 
 /****************************************************************************
  Seek a file. Try to avoid the seek if possible.
@@ -71,6 +77,119 @@
 	return True;
 }
 
+/*
+ * Read from the read cache, too.  This variant invalidates the cache if
+ * a non-sequential read comes in, on the grounds that random I/O
+ * should do it's own buffering, if any buffering is done.
+ */
+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 (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !rcp) {
+		/* 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; 
+		}
+	}
+	else if (rcp->offset != pos) {
+		/* This perhaps could become a seek and reread. */
+		DEBUG(0,("invalidating read cache.\n"));
+		seek_file(fsp,pos); /* Correct read position. */
+		invalidate_read_cache(rcp);
+		safe_free(rcp);
+		fsp->rcp = NULL;
+		return 0;
+	}
+
+	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.
 ****************************************************************************/
@@ -83,11 +202,20 @@
 	if (fsp->print_file)
 		return -1;
 
+
+ 	/*
+	 * Serve from read cache if we can. If not, we'll
+	 * continue to the write cache and one last direct read.
+ 	 */
+	if ((ret = read_from_read_cache(fsp, data, pos, n)) > 0) {
+                return ret;
+	}
+ 
+
 	/*
 	 * Serve from write cache if we can.
 	 */
-
-	if(read_from_write_cache(fsp, data, pos, n))
+	if (read_from_write_cache(fsp, data, pos, n))
 		return n;
 
 	flush_write_cache(fsp, READ_FLUSH);
@@ -96,7 +224,24 @@
 		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, readret;
+
+ 
 	if (n > 0) {
 #ifdef DMF_FIX
 		int numretries = 3;
@@ -628,30 +773,41 @@
 }
 
 /****************************************************************************
- Delete the write cache structure.
+ Delete the read/write cache structures.
 ****************************************************************************/
 
 void delete_write_cache(files_struct *fsp)
 {
-	write_cache *wcp;
-
-	if(!fsp)
-		return;
+       if (fsp) {
+               delete_cache(fsp->wcp);
+               DEBUG(10,("delete_write_cache: File %s deleted write cache\n",fsp->fsp_name ));
 
-	if(!(wcp = fsp->wcp))
-		return;
-
-	DO_PROFILE_DEC(writecache_allocated_write_caches);
-	allocated_write_caches--;
-
-	SMB_ASSERT(wcp->data_size == 0);
+       }
+}
 
-	SAFE_FREE(wcp->data);
-	SAFE_FREE(fsp->wcp);
+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 ));
+	}
+}
 
-	DEBUG(10,("delete_write_cache: File %s deleted write cache\n", fsp->fsp_name ));
+static void delete_cache(write_cache *cp) {
+ 
+	if (cp == NULL)
+ 		return;
+ 
+ 	DO_PROFILE_DEC(writecache_allocated_write_caches);
+ 	allocated_write_caches--;
+ 
+	SMB_ASSERT(cp->data_size == 0);
+ 
+	SAFE_FREE(cp->data);
+	SAFE_FREE(cp);
 }
 
+
 /****************************************************************************
  Setup the write cache structure.
 ****************************************************************************/
@@ -667,7 +823,14 @@
 	if(alloc_size == 0 || fsp->wcp)
 		return False;
 
-	if((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) {
+	if (fsp->rcp != NULL) {
+		/* There is a read cache: invalidate it and grab its space. */
+		invalidate_read_cache(fsp->rcp);
+		wcp = fsp->rcp;
+		fsp->rcp = NULL;
+		safe_free(wcp->data);
+	}
+	else if ((wcp = (write_cache *)malloc(sizeof(write_cache))) == NULL) {
 		DEBUG(0,("setup_write_cache: malloc fail.\n"));
 		return False;
 	}
@@ -694,6 +857,64 @@
 
 	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;
+ }
+
+/****************************************************************************
+Invalidate a read cache and remove its buffer. Used if writeb caching is used
+or if cache is out of sync with a read. If the latter is common, and large
+reads are still a good idea, we may just do a seek instead.
+****************************************************************************/
+ static void 
+invalidate_read_cache(write_cache *rcp) {
+	rcp->file_size = 0; 
+	rcp->offset = 0;    
+	rcp->alloc_size = 0;
+	rcp->data_size = 0;
+	safe_free(rcp->data);
+}
+
 
 /****************************************************************************
  Cope with a size change.


More information about the samba-technical mailing list