[PATCH] flashcp: improve speed & some clean ups

Frans Meulenbroeks fransmeulenbroeks at gmail.com
Thu Apr 7 11:04:01 EDT 2011


the speed of the program is improved by:
- checking if a block needs to be erased before actually erasing it
- only writing blocks that are not modified

In order to implement this, the main loop of the program has
been reworked. I have tried to stay in the style of the
original author

Signed-off-by: Frans Meulenbroeks <fransmeulenbroeks at gmail.com>
---
 flashcp.c |  266 ++++++++++++++++++++++++++++++------------------------------
 1 files changed, 133 insertions(+), 133 deletions(-)

diff --git a/flashcp.c b/flashcp.c
index d58c81b..d3bec5e 100644
--- a/flashcp.c
+++ b/flashcp.c
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2d3D, Inc.
  * Written by Abraham vd Merwe <abraham at 2d3d.co.za>
+ * Almost full rewrte of the main loop: Frans Meulenbroeks
  * All rights reserved.
  *
  * Renamed to flashcp.c to avoid conflicts with fcp from fsh package
@@ -47,9 +48,6 @@ typedef int bool;
 #define true 1
 #define false 0
 
-#define EXIT_FAILURE 1
-#define EXIT_SUCCESS 0
-
 /* for debugging purposes only */
 #ifdef DEBUG
 #undef DEBUG
@@ -62,15 +60,10 @@ typedef int bool;
 #define KB(x) ((x) / 1024)
 #define PERCENTAGE(x,total) (((x) * 100) / (total))
 
-/* size of read/write buffer */
-#define BUFSIZE (10 * 1024)
-
 /* cmd-line flags */
 #define FLAG_NONE		0x00
 #define FLAG_VERBOSE	0x01
 #define FLAG_HELP		0x02
-#define FLAG_FILENAME	0x04
-#define FLAG_DEVICE		0x08
 
 /* error levels */
 #define LOG_NORMAL	1
@@ -86,7 +79,7 @@ static void log_printf (int level,const char *fmt, ...)
 	fflush (fp);
 }
 
-static void showusage(bool error)
+static void showusage (const char *progname,bool error)
 {
 	int level = error ? LOG_ERROR : LOG_NORMAL;
 
@@ -94,15 +87,15 @@ static void showusage(bool error)
 			"\n"
 			"Flash Copy - Written by Abraham van der Merwe <abraham at 2d3d.co.za>\n"
 			"\n"
-			"usage: %1$s [ -v | --verbose ] <filename> <device>\n"
-			"       %1$s -h | --help\n"
+			"usage: %s [ -v | --verbose ] <filename> <device>\n"
+			"       %s -h | --help\n"
 			"\n"
 			"   -h | --help      Show this help message\n"
 			"   -v | --verbose   Show progress reports\n"
 			"   <filename>       File which you want to copy to flash\n"
 			"   <device>         Flash device to write to (e.g. /dev/mtd0, /dev/mtd1, etc.)\n"
 			"\n",
-			PROGRAM_NAME);
+			progname,progname);
 
 	exit (error ? EXIT_FAILURE : EXIT_SUCCESS);
 }
@@ -146,11 +139,11 @@ static void safe_read (int fd,const char *filename,void *buf,size_t count,bool v
 	}
 }
 
-static void safe_rewind (int fd,const char *filename)
+static void safe_seek (int fd,off_t offset,int whence,const char *filename)
 {
-	if (lseek (fd,0L,SEEK_SET) < 0)
+	if (lseek (fd,offset,whence) < 0)
 	{
-		log_printf (LOG_ERROR,"While seeking to start of %s: %m\n",filename);
+		log_printf (LOG_ERROR,"While seeking with offset %ld (whence = %d) in %s: %m\n", (long)offset,whence,filename);
 		exit (EXIT_FAILURE);
 	}
 }
@@ -165,16 +158,62 @@ static void cleanup (void)
 	if (fil_fd > 0) close (fil_fd);
 }
 
+/*
+  The compare function below checks if we need to erase the block.
+  if writing the block only will turn bits from 1 to 0 we do not
+  need to erase.
+  Most likely this is because the block is already erased, but it
+  could be that we are e.g. just appending.
+  This is tested by doing a src AND dest and verifying that
+  this is indeed src.
+  And if we are writing data that is already present in that
+  form we do not need to write at all
+*/
+#define CMP_ERASE 1
+#define CMP_WRITE 2
+
+static int compare(unsigned char *src, unsigned char *dst, ssize_t len)
+{
+	int rv = 0;
+	unsigned long long *sp = (unsigned long long *)src;
+	unsigned long long *dp = (unsigned long long *)dst;
+	unsigned long long *ep; /* end ptr */
+	
+	len = len / sizeof(unsigned long long);
+	ep = sp + len;
+
+	while (sp < ep)
+	{
+		/* if src AND dst == src we do not need to erase */
+		if ((*sp & *dp) != *sp)
+		{
+			rv = CMP_ERASE | CMP_WRITE;
+			return rv;
+		}
+		if (*sp != *dp)
+		{
+			rv = CMP_WRITE;
+		}
+		sp++;
+		dp++;
+	}
+	return rv;
+}
+
 int main (int argc,char *argv[])
 {
-	const char *filename = NULL,*device = NULL;
+	const char *progname,*filename = NULL,*device = NULL;
 	int i,flags = FLAG_NONE;
 	ssize_t result;
-	size_t size,written;
+	size_t size,written,todo;
 	struct mtd_info_user mtd;
 	struct erase_info_user erase;
 	struct stat filestat;
-	unsigned char src[BUFSIZE],dest[BUFSIZE];
+	unsigned char *src, *dest;
+	int blocks;
+	int cmp;
+
+	(progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]);
 
 	/*********************
 	 * parse cmd-line
@@ -206,26 +245,28 @@ int main (int argc,char *argv[])
 				break;
 			default:
 				DEBUG("Unknown parameter: %s\n",argv[option_index]);
-				showusage(true);
+				showusage (progname,true);
 		}
 	}
+
+	if (flags & FLAG_HELP)
+		showusage (progname, false);
+
 	if (optind+2 == argc) {
-		flags |= FLAG_FILENAME;
 		filename = argv[optind];
 		DEBUG("Got filename: %s\n",filename);
 
-		flags |= FLAG_DEVICE;
 		device = argv[optind+1];
 		DEBUG("Got device: %s\n",device);
 	}
 
-	if (flags & FLAG_HELP || device == NULL)
-		showusage(flags != FLAG_HELP);
+	if (progname == NULL || device == NULL)
+		showusage (progname, true);
 
 	atexit (cleanup);
 
+	dev_fd = safe_open (device, O_SYNC | O_RDWR);
 	/* get some info about the flash device */
-	dev_fd = safe_open (device,O_SYNC | O_RDWR);
 	if (ioctl (dev_fd,MEMGETINFO,&mtd) < 0)
 	{
 		DEBUG("ioctl(): %m\n");
@@ -249,142 +290,101 @@ int main (int argc,char *argv[])
 	}
 
 	/*****************************************************
-	 * erase enough blocks so that we can write the file *
+	 * allocate buffers				     *
 	 *****************************************************/
 
+	src = malloc(2 * mtd.erasesize + sizeof(unsigned long long));
+	if (!src)
+	{
+		log_printf (LOG_ERROR,"not enough memory for two %d byte buffers!\n", mtd.erasesize);
+		exit (EXIT_FAILURE);
+	}
+	/* align on unsigned long long boundary */
+	src = (unsigned long long *)(((unsigned long)src + sizeof(unsigned long long)) & (~(sizeof(unsigned long long) - 1))); 
+	dest = src + mtd.erasesize;
+	size = filestat.st_size;
+
 #warning "Check for smaller erase regions"
 
 	erase.start = 0;
-	erase.length = (filestat.st_size + mtd.erasesize - 1) / mtd.erasesize;
-	erase.length *= mtd.erasesize;
+        erase.length = filestat.st_size & ~(mtd.erasesize - 1);
+        if (filestat.st_size % mtd.erasesize) erase.length += mtd.erasesize;
 
-	if (flags & FLAG_VERBOSE)
+	blocks = erase.length / mtd.erasesize;
+	erase.length = mtd.erasesize;
+
+	for (i = 1; i <= blocks; i++)
 	{
-		/* if the user wants verbose output, erase 1 block at a time and show him/her what's going on */
-		int blocks = erase.length / mtd.erasesize;
-		erase.length = mtd.erasesize;
-		log_printf (LOG_NORMAL,"Erasing blocks: 0/%d (0%%)",blocks);
-		for (i = 1; i <= blocks; i++)
+		if (size < mtd.erasesize)
+		{
+			todo = size;
+		}
+		else
+		{
+			todo = mtd.erasesize;
+		}
+		if (flags & FLAG_VERBOSE)
+		{
+			log_printf (LOG_NORMAL,"\rProcessing block: %d/%d (%d%%)",i,blocks,PERCENTAGE (i,blocks));
+		}
+		/* read from filename */
+		safe_read (fil_fd,filename,src,todo,flags & FLAG_VERBOSE);
+
+		/* read from device */
+		safe_read (dev_fd,device,dest,todo,flags & FLAG_VERBOSE);
+
+		cmp = compare(src,dest,todo);
+		if (cmp & CMP_ERASE)
 		{
-			log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (%d%%)",i,blocks,PERCENTAGE (i,blocks));
 			if (ioctl (dev_fd,MEMERASE,&erase) < 0)
 			{
-				log_printf (LOG_NORMAL,"\n");
+				if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n");
 				log_printf (LOG_ERROR,
 						"While erasing blocks 0x%.8x-0x%.8x on %s: %m\n",
 						(unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
 				exit (EXIT_FAILURE);
 			}
-			erase.start += mtd.erasesize;
 		}
-		log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (100%%)\n",blocks,blocks);
-	}
-	else
-	{
-		/* if not, erase the whole chunk in one shot */
-		if (ioctl (dev_fd,MEMERASE,&erase) < 0)
+		if (cmp & CMP_WRITE)
 		{
-			log_printf (LOG_ERROR,
-					"While erasing blocks from 0x%.8x-0x%.8x on %s: %m\n",
-					(unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
-			exit (EXIT_FAILURE);
-		}
-	}
-	DEBUG("Erased %u / %luk bytes\n",erase.length,filestat.st_size);
-
-	/**********************************
-	 * write the entire file to flash *
-	 **********************************/
-
-	if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Writing data: 0k/%luk (0%%)",KB (filestat.st_size));
-	size = filestat.st_size;
-	i = BUFSIZE;
-	written = 0;
-	while (size)
-	{
-		if (size < BUFSIZE) i = size;
-		if (flags & FLAG_VERBOSE)
-			log_printf (LOG_NORMAL,"\rWriting data: %dk/%luk (%lu%%)",
-					KB (written + i),
-					KB (filestat.st_size),
-					PERCENTAGE (written + i,filestat.st_size));
-
-		/* read from filename */
-		safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
+			safe_seek (dev_fd,(off_t)erase.start,SEEK_SET,device);
+		    	result = write (dev_fd,src,todo);
+			if (todo != result)
+			{
+				if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n");
+				if (result < 0)
+				{
+					log_printf (LOG_ERROR,
+							"While writing data to 0x%.8x-0x%.8x on %s: %m\n",
+							written,written + i,device);
+					exit (EXIT_FAILURE);
+				}
+				log_printf (LOG_ERROR,
+						"Short write count returned while writing to x%.8x-0x%.8x on %s: %d/%lu bytes written to flash\n",
+						erase.start,erase.start + todo,device,erase.start + result,filestat.st_size);
+				exit (EXIT_FAILURE);
+			}
+			/* verify */
+			safe_seek (dev_fd,(off_t)erase.start,SEEK_SET,device);
+			safe_read (dev_fd,device,dest,todo,flags & FLAG_VERBOSE);
 
-		/* write to device */
-		result = write (dev_fd,src,i);
-		if (i != result)
-		{
-			if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n");
-			if (result < 0)
+			/* compare buffers */
+			if (memcmp (src,dest,todo))
 			{
 				log_printf (LOG_ERROR,
-						"While writing data to 0x%.8x-0x%.8x on %s: %m\n",
-						written,written + i,device);
+						"File does not seem to match flash data. First mismatch at 0x%.8x-0x%.8x\n",
+						written,written + i);
 				exit (EXIT_FAILURE);
 			}
-			log_printf (LOG_ERROR,
-					"Short write count returned while writing to x%.8x-0x%.8x on %s: %d/%lu bytes written to flash\n",
-					written,written + i,device,written + result,filestat.st_size);
-			exit (EXIT_FAILURE);
 		}
-
-		written += i;
-		size -= i;
+		erase.start += mtd.erasesize;
+		size -= todo;
 	}
+
 	if (flags & FLAG_VERBOSE)
-		log_printf (LOG_NORMAL,
-				"\rWriting data: %luk/%luk (100%%)\n",
-				KB (filestat.st_size),
-				KB (filestat.st_size));
-	DEBUG("Wrote %d / %luk bytes\n",written,filestat.st_size);
-
-	/**********************************
-	 * verify that flash == file data *
-	 **********************************/
-
-	safe_rewind (fil_fd,filename);
-	safe_rewind (dev_fd,device);
-	size = filestat.st_size;
-	i = BUFSIZE;
-	written = 0;
-	if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Verifying data: 0k/%luk (0%%)",KB (filestat.st_size));
-	while (size)
 	{
-		if (size < BUFSIZE) i = size;
-		if (flags & FLAG_VERBOSE)
-			log_printf (LOG_NORMAL,
-					"\rVerifying data: %dk/%luk (%lu%%)",
-					KB (written + i),
-					KB (filestat.st_size),
-					PERCENTAGE (written + i,filestat.st_size));
-
-		/* read from filename */
-		safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
-
-		/* read from device */
-		safe_read (dev_fd,device,dest,i,flags & FLAG_VERBOSE);
-
-		/* compare buffers */
-		if (memcmp (src,dest,i))
-		{
-			log_printf (LOG_ERROR,
-					"File does not seem to match flash data. First mismatch at 0x%.8x-0x%.8x\n",
-					written,written + i);
-			exit (EXIT_FAILURE);
-		}
-
-		written += i;
-		size -= i;
+		log_printf (LOG_NORMAL,"\n");
 	}
-	if (flags & FLAG_VERBOSE)
-		log_printf (LOG_NORMAL,
-				"\rVerifying data: %luk/%luk (100%%)\n",
-				KB (filestat.st_size),
-				KB (filestat.st_size));
-	DEBUG("Verified %d / %luk bytes\n",written,filestat.st_size);
 
 	exit (EXIT_SUCCESS);
 }
-
-- 
1.7.0.4




More information about the linux-mtd mailing list