Skip to contentSkip to conversation

JFFS2 fixes and performance improvements

PATCH Superseded Mark Tomlinson 4 participants 19 messages Jun 29, 2015 – Jun 30, 2015 Journalling flash file system V2 (JFFS2)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover JFFS2 fixes and performance improvements

These patches fix bugs and improve performance of JFFS2. Some of these improvements can already be found in old mailing lists, but for some reason they have not made their way into the u-boot source. I have the feeling that any one of these patches didn't show enough performance gain to warrant adding it to the source. I am hopeful that together, all these patches can be seen to make a big difference.

One of these patches ("Only list each directory entry once") is a bug fix, all the rest are for performance. Although performance is not high on the priority list for a bootloader, the length of time it was taking to scan a JFFS2 filesystem was painfully slow.

The code for mergesort was found in an abandoned u-boot patch, although I have refactored the code somewhat. The original author is still shown at the top of that file.

The timings below are with jffs2_summary_support turned off. With these improvements, summary support was no longer useful - most of our time is now spent loading the actual release. Even without this feature turned on, the code will still load from a filesystem that has summary nodes.

Due to not having other resources, I also have not done anything for NAND flash. At least some of these changes will be directly applicable to NAND as well as NOR.

My own testing is on a system with a 1GHz PowerPC, and 256MB of NOR Flash. The flash accesses are slow compared with processing power and RAM, so minimising the number of flash accesses makes a huge difference. Here are the timing comparisons for three JFFS2 operations on this system:
1) Scanning the file system
2) Getting a director listing of the top level, and
3) Loading a 30MB file.

Times are in seconds, and the contribution from each patch has been measured:

                         Scan    List    Load    Total
Original:               266.0    17.3    23.4   306.7
Speed up comparison:     52.3    17.3    23.4    93.0
List dir entries once:   52.3    11.0    23.4    86.7
Improve read speed:      52.3     0.8     5.4    58.5
Optimize building lists: 31.9     0.8     5.4    38.1
Change scansize:         30.8     0.8     5.4    37.0
Use cleanmarker:         16.0     0.8     5.4    22.2
Use mergesort:            2.0     0.8     5.4     8.2

Note that "List dir entries once" is not a speed improvement as such, but because old versions of a file and deleted files are no longer scanned, there is an improvement on this filesystem (the flash is approx half full).

Also, "change scansize" appears to do very little in this benchmark list. But without it, the "use cleanmarker" would not show as much improvement.

Mark Tomlinson (8):
JFFS2: Return early when file read not necessary
JFFS2: Speed up and fix comparison functions
JFFS2: Only list each directory entry once
JFFS2: Improve speed reading flash files
JFFS2: Optimize building lists during scan
JFFS2: Change scansize to match linux kernel
JFFS2: Use CLEANMARKER to reduce scanning time
JFFS2: Use merge sort when parsing filesystem

 fs/jffs2/Makefile        |   1 +
 fs/jffs2/jffs2_1pass.c   | 245 +++++++++++++++++++++++++++++------------------
 fs/jffs2/jffs2_private.h |   4 +
 fs/jffs2/mergesort.c     |  70 ++++++++++++++
 4 files changed, 228 insertions(+), 92 deletions(-)
 create mode 100644 fs/jffs2/mergesort.c
Signature · 1 line
1.9.1
#1 lore raw

PATCH 1/8 JFFS2: Return early when file read not necessary

If a destination is not provided, jffs2_1pass_read_inode() only returns the length of the file. In this case, avoid reading all the data nodes, and return as soon as the length of the file is known.

---

 fs/jffs2/jffs2_1pass.c | 6 ++++++
 1 file changed, 6 insertions(+)
fs/jffs2/jffs2_1pass.c +6 -0
index b1d6470..2335db1 100644
@@ -719,6 +719,12 @@ jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest)
 		}
 		put_fl_mem(jNode, pL->readbuf);
 	}
+	/* If no destination is provided, we are done.
+	 * Just return the total size.
+	 */
+	if (!dest) {
+		return totalSize;
+	}
 #endif
 
 	for (b = pL->frag.listHead; b != NULL; b = b->next) {
Signature · 1 line
1.9.1
#2 lore raw

PATCH 1/8 JFFS2: Return early when file read not necessary

Hello Mark,

Am 29.06.2015 um 07:02 schrieb Mark Tomlinson:

Quoted 16 lines

If a destination is not provided, jffs2_1pass_read_inode() only returns the length of the file. In this case, avoid reading all the data nodes, and return as soon as the length of the file is known.

---

  fs/jffs2/jffs2_1pass.c | 6 ++++++
  1 file changed, 6 insertions(+)
fs/jffs2/jffs2_1pass.c +0 -0
index b1d6470..2335db1 100644

fs/jffs2/jffs2_1pass.c jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest) · line 724

Quoted 6 lines
fs/jffs2/jffs2_1pass.c +3 -0
@@ -719,6 +719,12 @@ jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest)
  		}
  		put_fl_mem(jNode, pL->readbuf);
  	}
+	/* If no destination is provided, we are done.
+	 * Just return the total size.
+	 */

please change this into

	/*
	 * If no destination is provided, we are done.
	 * Just return the total size.
	 */

to fit with Coding style.

fs/jffs2/jffs2_1pass.c jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest) · line 727

Quoted 3 lines
fs/jffs2/jffs2_1pass.c +3 -0
+	if (!dest) {
+		return totalSize;
+	}

no {} needed

Beside of this:

bye,
Heiko

Quoted 4 lines
fs/jffs2/jffs2_1pass.c +0 -0
  #endif
 
  	for (b = pL->frag.listHead; b != NULL; b = b->next) {
Signature · 2 lines
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
#3 lore raw

PATCH 2/8 JFFS2: Speed up and fix comparison functions

In reply to Mark Tomlinson

Copying complete nodes from flash can be slow if the flash is slow to read. By only reading the data needed, the sorting operation can be made much faster.

The directory entry comparison function also had a two bugs. First, it did not ensure the name was copied, so the name comparison may have been faulty (although it would have worked with NOR flash). Second, setting the ino to zero to ignore the entry did not work, since this was either writing to a temporary buffer, or (for NOR flash) directly to flash. Either way, the change was not remembered.

---

 fs/jffs2/jffs2_1pass.c | 82 ++++++++++++++++++++++++++------------------------
 1 file changed, 42 insertions(+), 40 deletions(-)
fs/jffs2/jffs2_1pass.c +42 -40
index 2335db1..079bb73 100644
@@ -598,14 +598,17 @@ insert_node(struct b_list *list, u32 offset)
  */
 static int compare_inodes(struct b_node *new, struct b_node *old)
 {
-	struct jffs2_raw_inode ojNew;
-	struct jffs2_raw_inode ojOld;
-	struct jffs2_raw_inode *jNew =
-		(struct jffs2_raw_inode *)get_fl_mem(new->offset, sizeof(ojNew), &ojNew);
-	struct jffs2_raw_inode *jOld =
-		(struct jffs2_raw_inode *)get_fl_mem(old->offset, sizeof(ojOld), &ojOld);
-
-	return jNew->version > jOld->version;
+	/* Only read in the version info from flash, not the entire inode.
+	 * This can make a big difference to speed if flash is slow.
+	 */
+	u32 new_version;
+	u32 old_version;
+	get_fl_mem(new->offset + offsetof(struct jffs2_raw_inode, version),
+		   sizeof(new_version), &new_version);
+	get_fl_mem(old->offset + offsetof(struct jffs2_raw_inode, version),
+		   sizeof(old_version), &old_version);
+
+	return new_version > old_version;
 }
 
 /* Sort directory entries so all entries in the same directory
@@ -615,42 +618,41 @@ static int compare_inodes(struct b_node *new, struct b_node *old)
  */
 static int compare_dirents(struct b_node *new, struct b_node *old)
 {
-	struct jffs2_raw_dirent ojNew;
-	struct jffs2_raw_dirent ojOld;
-	struct jffs2_raw_dirent *jNew =
-		(struct jffs2_raw_dirent *)get_fl_mem(new->offset, sizeof(ojNew), &ojNew);
-	struct jffs2_raw_dirent *jOld =
-		(struct jffs2_raw_dirent *)get_fl_mem(old->offset, sizeof(ojOld), &ojOld);
-	int cmp;
-
-	/* ascending sort by pino */
-	if (jNew->pino != jOld->pino)
-		return jNew->pino > jOld->pino;
-
-	/* pino is the same, so use ascending sort by nsize, so
-	 * we don't do strncmp unless we really must.
-	 */
-	if (jNew->nsize != jOld->nsize)
-		return jNew->nsize > jOld->nsize;
-
-	/* length is also the same, so use ascending sort by name
-	 */
-	cmp = strncmp((char *)jNew->name, (char *)jOld->name, jNew->nsize);
-	if (cmp != 0)
-		return cmp > 0;
-
-	/* we have duplicate names in this directory, so use ascending
-	 * sort by version
+	/* Using NULL as the buffer for NOR flash prevents the entire node
+	 * being read. This makes most comparisons much quicker as only one
+	 * or two entries from the node will be used most of the time.
 	 */
-	if (jNew->version > jOld->version) {
-		/* since jNew is newer, we know jOld is not valid, so
-		 * mark it with inode 0 and it will not be used
+	struct jffs2_raw_dirent *jNew = get_node_mem(new->offset, NULL);
+	struct jffs2_raw_dirent *jOld = get_node_mem(old->offset, NULL);
+	int cmp;
+	int ret;
+
+	if (jNew->pino != jOld->pino) {
+		/* ascending sort by pino */
+		ret = jNew->pino > jOld->pino;
+	} else if (jNew->nsize != jOld->nsize) {
+		/* pino is the same, so use ascending sort by nsize, so
+		 * we don't do strncmp unless we really must.
 		 */
-		jOld->ino = 0;
-		return 1;
+		ret = jNew->nsize > jOld->nsize;
+	} else {
+		/* length is also the same, so use ascending sort by name
+		 */
+		cmp = strncmp((char *)jNew->name, (char *)jOld->name,
+			jNew->nsize);
+		if (cmp != 0) {
+			ret = cmp > 0;
+		} else {
+			/* we have duplicate names in this directory,
+			 * so use ascending sort by version
+			 */
+			ret = jNew->version > jOld->version;
+		}
 	}
+	put_fl_mem(jNew, NULL);
+	put_fl_mem(jOld, NULL);
 
-	return 0;
+	return ret;
 }
 #endif
 
Signature · 1 line
1.9.1
#4 lore raw

PATCH 2/8 JFFS2: Speed up and fix comparison functions

Hello Mark,

Am 29.06.2015 um 07:02 schrieb Mark Tomlinson:

Quoted 24 lines

Copying complete nodes from flash can be slow if the flash is slow to read. By only reading the data needed, the sorting operation can be made much faster.

The directory entry comparison function also had a two bugs. First, it did not ensure the name was copied, so the name comparison may have been faulty (although it would have worked with NOR flash). Second, setting the ino to zero to ignore the entry did not work, since this was either writing to a temporary buffer, or (for NOR flash) directly to flash. Either way, the change was not remembered.

---

  fs/jffs2/jffs2_1pass.c | 82 ++++++++++++++++++++++++++------------------------
  1 file changed, 42 insertions(+), 40 deletions(-)
fs/jffs2/jffs2_1pass.c +0 -0
index 2335db1..079bb73 100644
@@ -598,14 +598,17 @@ insert_node(struct b_list *list, u32 offset)
   */
  static int compare_inodes(struct b_node *new, struct b_node *old)

fs/jffs2/jffs2_1pass.c insert_node(struct b_list *list, u32 offset) · line 601

Quoted 10 lines
fs/jffs2/jffs2_1pass.c +1 -8
  {
-	struct jffs2_raw_inode ojNew;
-	struct jffs2_raw_inode ojOld;
-	struct jffs2_raw_inode *jNew =
-		(struct jffs2_raw_inode *)get_fl_mem(new->offset, sizeof(ojNew), &ojNew);
-	struct jffs2_raw_inode *jOld =
-		(struct jffs2_raw_inode *)get_fl_mem(old->offset, sizeof(ojOld), &ojOld);
-
-	return jNew->version > jOld->version;
+	/* Only read in the version info from flash, not the entire inode.

please fix your comment style globally, thanks1

Quoted 76 lines
fs/jffs2/jffs2_1pass.c +36 -31
+	 * This can make a big difference to speed if flash is slow.
+	 */
+	u32 new_version;
+	u32 old_version;
+	get_fl_mem(new->offset + offsetof(struct jffs2_raw_inode, version),
+		   sizeof(new_version), &new_version);
+	get_fl_mem(old->offset + offsetof(struct jffs2_raw_inode, version),
+		   sizeof(old_version), &old_version);
+
+	return new_version > old_version;
  }
 
  /* Sort directory entries so all entries in the same directory
@@ -615,42 +618,41 @@ static int compare_inodes(struct b_node *new, struct b_node *old)
   */
  static int compare_dirents(struct b_node *new, struct b_node *old)
  {
-	struct jffs2_raw_dirent ojNew;
-	struct jffs2_raw_dirent ojOld;
-	struct jffs2_raw_dirent *jNew =
-		(struct jffs2_raw_dirent *)get_fl_mem(new->offset, sizeof(ojNew), &ojNew);
-	struct jffs2_raw_dirent *jOld =
-		(struct jffs2_raw_dirent *)get_fl_mem(old->offset, sizeof(ojOld), &ojOld);
-	int cmp;
-
-	/* ascending sort by pino */
-	if (jNew->pino != jOld->pino)
-		return jNew->pino > jOld->pino;
-
-	/* pino is the same, so use ascending sort by nsize, so
-	 * we don't do strncmp unless we really must.
-	 */
-	if (jNew->nsize != jOld->nsize)
-		return jNew->nsize > jOld->nsize;
-
-	/* length is also the same, so use ascending sort by name
-	 */
-	cmp = strncmp((char *)jNew->name, (char *)jOld->name, jNew->nsize);
-	if (cmp != 0)
-		return cmp > 0;
-
-	/* we have duplicate names in this directory, so use ascending
-	 * sort by version
+	/* Using NULL as the buffer for NOR flash prevents the entire node
+	 * being read. This makes most comparisons much quicker as only one
+	 * or two entries from the node will be used most of the time.
  	 */
-	if (jNew->version > jOld->version) {
-		/* since jNew is newer, we know jOld is not valid, so
-		 * mark it with inode 0 and it will not be used
+	struct jffs2_raw_dirent *jNew = get_node_mem(new->offset, NULL);
+	struct jffs2_raw_dirent *jOld = get_node_mem(old->offset, NULL);
+	int cmp;
+	int ret;
+
+	if (jNew->pino != jOld->pino) {
+		/* ascending sort by pino */
+		ret = jNew->pino > jOld->pino;
+	} else if (jNew->nsize != jOld->nsize) {
+		/* pino is the same, so use ascending sort by nsize, so
+		 * we don't do strncmp unless we really must.
  		 */
-		jOld->ino = 0;
-		return 1;
+		ret = jNew->nsize > jOld->nsize;
+	} else {
+		/* length is also the same, so use ascending sort by name
+		 */
+		cmp = strncmp((char *)jNew->name, (char *)jOld->name,
+			jNew->nsize);
+		if (cmp != 0) {
+			ret = cmp > 0;
+		} else {
+			/* we have duplicate names in this directory,
+			 * so use ascending sort by version
+			 */

fs/jffs2/jffs2_1pass.c static int compare_inodes(struct b_node *new, struct b_node *old) · line 657

Quoted 10 lines
fs/jffs2/jffs2_1pass.c +5 -1
+			ret = jNew->version > jOld->version;
+		}
  	}
+	put_fl_mem(jNew, NULL);
+	put_fl_mem(jOld, NULL);
 
-	return 0;
+	return ret;
  }
  #endif

bye,
Heiko

Signature · 2 lines
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
#5 lore raw

PATCH 3/8 JFFS2: Only list each directory entry once

In reply to Mark Tomlinson

If multiple versions of a file exist, only the most recent version should be used. The scheme to write 0 for the inode in older versions did not work, since this would have required writing to flash.

The only time this caused an issue was listing a directory, where older versions of the file would still be seen. Since the directory entries are sorted, just look at the next entry in the list, and if it's the same move to that entry instead.

---

 fs/jffs2/jffs2_1pass.c | 37 ++++++++++++++++++++++++++++++++-----
 1 file changed, 32 insertions(+), 5 deletions(-)
fs/jffs2/jffs2_1pass.c +32 -5
index 079bb73..1f6eea7 100644
@@ -840,7 +840,6 @@ jffs2_1pass_find_inode(struct b_lists * pL, const char *name, u32 pino)
 		jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset,
 								pL->readbuf);
 		if ((pino == jDir->pino) && (len == jDir->nsize) &&
-		    (jDir->ino) &&	/* 0 for unlink */
 		    (!strncmp((char *)jDir->name, name, len))) {	/* a match */
 			if (jDir->version < version) {
 				put_fl_mem(jDir, pL->readbuf);
@@ -961,13 +960,42 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino)
 	for (b = pL->dir.listHead; b; b = b->next) {
 		jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset,
 								pL->readbuf);
-		if ((pino == jDir->pino) && (jDir->ino)) { /* ino=0 -> unlink */
+		if (pino == jDir->pino) {
 			u32 i_version = 0;
 			struct jffs2_raw_inode ojNode;
 			struct jffs2_raw_inode *jNode, *i = NULL;
-			struct b_node *b2 = pL->frag.listHead;
+			struct b_node *b2;
 
-			while (b2) {
+#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS
+			/* Check for more recent versions of this file */
+			int match;
+			do {
+				struct b_node *next = b->next;
+				struct jffs2_raw_dirent *jDirNext;
+				if (!next)
+					break;
+				jDirNext = (struct jffs2_raw_dirent *)
+					get_node_mem(next->offset, NULL);
+				match = jDirNext->pino == jDir->pino &&
+					jDirNext->nsize == jDir->nsize &&
+					strncmp((char *)jDirNext->name,
+						(char *)jDir->name,
+						jDir->nsize) == 0;
+				if (match) {
+					/* Use next. It is more recent */
+					b = next;
+					/* Update buffer with the new info */
+					*jDir = *jDirNext;
+					put_fl_mem(jDirNext, NULL);
+				}
+			} while (match);
+#endif
+			if (jDir->ino == 0) {
+				/* Deleted file */
+				continue;
+			}
+
+			for (b2 = pL->frag.listHead; b2; b2 = b2->next) {
 				jNode = (struct jffs2_raw_inode *)
 					get_fl_mem(b2->offset, sizeof(ojNode), &ojNode);
 				if (jNode->ino == jDir->ino && jNode->version >= i_version) {
@@ -983,7 +1011,6 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino)
 							       sizeof(*i),
 							       NULL);
 				}
-				b2 = b2->next;
 			}
 
 			dump_inode(pL, jDir, i);
Signature · 1 line
1.9.1
#6 lore raw

PATCH 3/8 JFFS2: Only list each directory entry once

Hello Mark,

Am 29.06.2015 um 07:02 schrieb Mark Tomlinson:

Quoted 9 lines

If multiple versions of a file exist, only the most recent version should be used. The scheme to write 0 for the inode in older versions did not work, since this would have required writing to flash.

The only time this caused an issue was listing a directory, where older versions of the file would still be seen. Since the directory entries are sorted, just look at the next entry in the list, and if it's the same move to that entry instead.

Quoted 5 lines

---

  fs/jffs2/jffs2_1pass.c | 37 ++++++++++++++++++++++++++++++++-----
  1 file changed, 32 insertions(+), 5 deletions(-)

bye,
Heiko

Quoted 68 lines
fs/jffs2/jffs2_1pass.c +32 -5
index 079bb73..1f6eea7 100644
@@ -840,7 +840,6 @@ jffs2_1pass_find_inode(struct b_lists * pL, const char *name, u32 pino)
  		jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset,
  								pL->readbuf);
  		if ((pino == jDir->pino) && (len == jDir->nsize) &&
-		    (jDir->ino) &&	/* 0 for unlink */
  		    (!strncmp((char *)jDir->name, name, len))) {	/* a match */
  			if (jDir->version < version) {
  				put_fl_mem(jDir, pL->readbuf);
@@ -961,13 +960,42 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino)
  	for (b = pL->dir.listHead; b; b = b->next) {
  		jDir = (struct jffs2_raw_dirent *) get_node_mem(b->offset,
  								pL->readbuf);
-		if ((pino == jDir->pino) && (jDir->ino)) { /* ino=0 -> unlink */
+		if (pino == jDir->pino) {
  			u32 i_version = 0;
  			struct jffs2_raw_inode ojNode;
  			struct jffs2_raw_inode *jNode, *i = NULL;
-			struct b_node *b2 = pL->frag.listHead;
+			struct b_node *b2;
 
-			while (b2) {
+#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS
+			/* Check for more recent versions of this file */
+			int match;
+			do {
+				struct b_node *next = b->next;
+				struct jffs2_raw_dirent *jDirNext;
+				if (!next)
+					break;
+				jDirNext = (struct jffs2_raw_dirent *)
+					get_node_mem(next->offset, NULL);
+				match = jDirNext->pino == jDir->pino &&
+					jDirNext->nsize == jDir->nsize &&
+					strncmp((char *)jDirNext->name,
+						(char *)jDir->name,
+						jDir->nsize) == 0;
+				if (match) {
+					/* Use next. It is more recent */
+					b = next;
+					/* Update buffer with the new info */
+					*jDir = *jDirNext;
+					put_fl_mem(jDirNext, NULL);
+				}
+			} while (match);
+#endif
+			if (jDir->ino == 0) {
+				/* Deleted file */
+				continue;
+			}
+
+			for (b2 = pL->frag.listHead; b2; b2 = b2->next) {
  				jNode = (struct jffs2_raw_inode *)
  					get_fl_mem(b2->offset, sizeof(ojNode), &ojNode);
  				if (jNode->ino == jDir->ino && jNode->version >= i_version) {
@@ -983,7 +1011,6 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino)
  							       sizeof(*i),
  							       NULL);
  				}
-				b2 = b2->next;
  			}
 
  			dump_inode(pL, jDir, i);
Signature · 2 lines
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
#7 lore raw

PATCH 4/8 JFFS2: Improve speed reading flash files

In reply to Mark Tomlinson

jffs2_1pass_read_inode() would read the entire data for each node in the filesystem, regardless of whether it was part of the file to be loaded or not. By only reading the header data for an inode, and then reading the data only when it is found to be part of the file to be loaded, much copying of data is saved.

jffs2_1pass_list_inodes() read each inode for every file in the directory into a buffer. By using NULL as a buffer pointer, NOR flash simply returns a pointer, and therefore avoids a memory copy.

---

 fs/jffs2/jffs2_1pass.c | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)
fs/jffs2/jffs2_1pass.c +19 -6
index 1f6eea7..80210be 100644
@@ -730,8 +730,12 @@ jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest)
 #endif
 
 	for (b = pL->frag.listHead; b != NULL; b = b->next) {
-		jNode = (struct jffs2_raw_inode *) get_node_mem(b->offset,
-								pL->readbuf);
+		/* Copy just the node and not the data at this point,
+		 * since we don't yet know if we need this data.
+		 */
+		jNode = (struct jffs2_raw_inode *)get_fl_mem(b->offset,
+				sizeof(struct jffs2_raw_inode),
+				pL->readbuf);
 		if (inode == jNode->ino) {
 #if 0
 			putLabeledWord("\r\n\r\nread_inode: totlen = ", jNode->totlen);
@@ -755,7 +759,14 @@ jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest)
 #endif
 
 			if(dest) {
-				src = ((uchar *) jNode) + sizeof(struct jffs2_raw_inode);
+				/* Now that the inode has been checked,
+				 * read the entire inode, including data.
+				 */
+				put_fl_mem(jNode, pL->readbuf);
+				jNode = (struct jffs2_raw_inode *)
+					get_node_mem(b->offset, pL->readbuf);
+				src = ((uchar *)jNode) +
+					sizeof(struct jffs2_raw_inode);
 				/* ignore data behind latest known EOF */
 				if (jNode->offset > totalSize) {
 					put_fl_mem(jNode, pL->readbuf);
@@ -962,7 +973,6 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino)
 								pL->readbuf);
 		if (pino == jDir->pino) {
 			u32 i_version = 0;
-			struct jffs2_raw_inode ojNode;
 			struct jffs2_raw_inode *jNode, *i = NULL;
 			struct b_node *b2;
 
@@ -997,8 +1007,10 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino)
 
 			for (b2 = pL->frag.listHead; b2; b2 = b2->next) {
 				jNode = (struct jffs2_raw_inode *)
-					get_fl_mem(b2->offset, sizeof(ojNode), &ojNode);
-				if (jNode->ino == jDir->ino && jNode->version >= i_version) {
+					get_fl_mem(b2->offset, sizeof(*jNode),
+						   NULL);
+				if (jNode->ino == jDir->ino &&
+				    jNode->version >= i_version) {
 					i_version = jNode->version;
 					if (i)
 						put_fl_mem(i, NULL);
@@ -1011,6 +1023,7 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino)
 							       sizeof(*i),
 							       NULL);
 				}
+				put_fl_mem(jNode, NULL);
 			}
 
 			dump_inode(pL, jDir, i);
Signature · 1 line
1.9.1
#8 lore raw

PATCH 4/8 JFFS2: Improve speed reading flash files

Hello Mark,

Am 29.06.2015 um 07:02 schrieb Mark Tomlinson:

Quoted 10 lines

jffs2_1pass_read_inode() would read the entire data for each node in the filesystem, regardless of whether it was part of the file to be loaded or not. By only reading the header data for an inode, and then reading the data only when it is found to be part of the file to be loaded, much copying of data is saved.

jffs2_1pass_list_inodes() read each inode for every file in the directory into a buffer. By using NULL as a buffer pointer, NOR flash simply returns a pointer, and therefore avoids a memory copy.

Quoted 5 lines

---

  fs/jffs2/jffs2_1pass.c | 25 +++++++++++++++++++------
  1 file changed, 19 insertions(+), 6 deletions(-)

bye,
Heiko

Quoted 66 lines
fs/jffs2/jffs2_1pass.c +19 -6
index 1f6eea7..80210be 100644
@@ -730,8 +730,12 @@ jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest)
  #endif
 
  	for (b = pL->frag.listHead; b != NULL; b = b->next) {
-		jNode = (struct jffs2_raw_inode *) get_node_mem(b->offset,
-								pL->readbuf);
+		/* Copy just the node and not the data at this point,
+		 * since we don't yet know if we need this data.
+		 */
+		jNode = (struct jffs2_raw_inode *)get_fl_mem(b->offset,
+				sizeof(struct jffs2_raw_inode),
+				pL->readbuf);
  		if (inode == jNode->ino) {
  #if 0
  			putLabeledWord("\r\n\r\nread_inode: totlen = ", jNode->totlen);
@@ -755,7 +759,14 @@ jffs2_1pass_read_inode(struct b_lists *pL, u32 inode, char *dest)
  #endif
 
  			if(dest) {
-				src = ((uchar *) jNode) + sizeof(struct jffs2_raw_inode);
+				/* Now that the inode has been checked,
+				 * read the entire inode, including data.
+				 */
+				put_fl_mem(jNode, pL->readbuf);
+				jNode = (struct jffs2_raw_inode *)
+					get_node_mem(b->offset, pL->readbuf);
+				src = ((uchar *)jNode) +
+					sizeof(struct jffs2_raw_inode);
  				/* ignore data behind latest known EOF */
  				if (jNode->offset > totalSize) {
  					put_fl_mem(jNode, pL->readbuf);
@@ -962,7 +973,6 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino)
  								pL->readbuf);
  		if (pino == jDir->pino) {
  			u32 i_version = 0;
-			struct jffs2_raw_inode ojNode;
  			struct jffs2_raw_inode *jNode, *i = NULL;
  			struct b_node *b2;
 
@@ -997,8 +1007,10 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino)
 
  			for (b2 = pL->frag.listHead; b2; b2 = b2->next) {
  				jNode = (struct jffs2_raw_inode *)
-					get_fl_mem(b2->offset, sizeof(ojNode), &ojNode);
-				if (jNode->ino == jDir->ino && jNode->version >= i_version) {
+					get_fl_mem(b2->offset, sizeof(*jNode),
+						   NULL);
+				if (jNode->ino == jDir->ino &&
+				    jNode->version >= i_version) {
  					i_version = jNode->version;
  					if (i)
  						put_fl_mem(i, NULL);
@@ -1011,6 +1023,7 @@ jffs2_1pass_list_inodes(struct b_lists * pL, u32 pino)
  							       sizeof(*i),
  							       NULL);
  				}
+				put_fl_mem(jNode, NULL);
  			}
 
  			dump_inode(pL, jDir, i);
Signature · 2 lines
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
#9 lore raw

PATCH 5/8 JFFS2: Optimize building lists during scan

In reply to Mark Tomlinson

If the flash is slow, reading less from the flash into buffers makes the process faster.

---

 fs/jffs2/jffs2_1pass.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)
fs/jffs2/jffs2_1pass.c +20 -4
index 80210be..10bd7be 100644
@@ -1493,7 +1493,7 @@ jffs2_1pass_build_lists(struct part_info * part)
 	u32 counterF = 0;
 	u32 counterN = 0;
 	u32 max_totlen = 0;
-	u32 buf_size = DEFAULT_EMPTY_SCAN_SIZE;
+	u32 buf_size;
 	char *buf;
 
 	nr_sectors = lldiv(part->size, part->sector_size);
@@ -1505,7 +1505,7 @@ jffs2_1pass_build_lists(struct part_info * part)
 	/* if we are building a list we need to refresh the cache. */
 	jffs_init_1pass_list(part);
 	pL = (struct b_lists *)part->jffs2_priv;
-	buf = malloc(buf_size);
+	buf = malloc(DEFAULT_EMPTY_SCAN_SIZE);
 	puts ("Scanning JFFS2 FS:   ");
 
 	/* start at the beginning of the partition */
@@ -1521,6 +1521,8 @@ jffs2_1pass_build_lists(struct part_info * part)
 		int ret;
 #endif
 
+		/* Set buf_size to maximum length */
+		buf_size = DEFAULT_EMPTY_SCAN_SIZE;
 		WATCHDOG_RESET();
 
 #ifdef CONFIG_JFFS2_SUMMARY
@@ -1595,6 +1597,10 @@ jffs2_1pass_build_lists(struct part_info * part)
 
 		ofs += sector_ofs;
 		prevofs = ofs - 1;
+		/* Set buf_size down to the minimum size required.
+		 * This prevents reading in chunks of flash data unnecessarily.
+		 */
+		buf_size = sizeof(union jffs2_node_union);
 
 	scan_more:
 		while (ofs < sector_ofs + part->sector_size) {
@@ -1675,13 +1681,18 @@ jffs2_1pass_build_lists(struct part_info * part)
 			case JFFS2_NODETYPE_INODE:
 				if (buf_ofs + buf_len < ofs + sizeof(struct
 							jffs2_raw_inode)) {
+					buf_len = min_t(uint32_t,
+							sizeof(struct jffs2_raw_inode),
+							sector_ofs +
+							part->sector_size -
+							ofs);
 					get_fl_mem((u32)part->offset + ofs,
 						   buf_len, buf);
 					buf_ofs = ofs;
 					node = (void *)buf;
 				}
-				if (!inode_crc((struct jffs2_raw_inode *) node))
-				       break;
+				if (!inode_crc((struct jffs2_raw_inode *)node))
+					break;
 
 				if (insert_node(&pL->frag, (u32) part->offset +
 						ofs) == NULL) {
@@ -1698,6 +1709,11 @@ jffs2_1pass_build_lists(struct part_info * part)
 							((struct
 							 jffs2_raw_dirent *)
 							node)->nsize) {
+					buf_len = min_t(uint32_t,
+							node->totlen,
+							sector_ofs +
+							part->sector_size -
+							ofs);
 					get_fl_mem((u32)part->offset + ofs,
 						   buf_len, buf);
 					buf_ofs = ofs;
Signature · 1 line
1.9.1
#10 lore raw

PATCH 5/8 JFFS2: Optimize building lists during scan

Hello Mark,

please fix Tom rinis mail address globally for your patchset into

Tom Rini <[email protected]>

I fixed it for my response manually, thanks!

BTW: You can use patman for creating patches/patchset. Look into u-boot:tools/patman

Am 29.06.2015 um 07:02 schrieb Mark Tomlinson:

Quoted 8 lines

If the flash is slow, reading less from the flash into buffers makes the process faster.

---

  fs/jffs2/jffs2_1pass.c | 24 ++++++++++++++++++++----
  1 file changed, 20 insertions(+), 4 deletions(-)

bye,
Heiko

Quoted 77 lines
fs/jffs2/jffs2_1pass.c +20 -4
index 80210be..10bd7be 100644
@@ -1493,7 +1493,7 @@ jffs2_1pass_build_lists(struct part_info * part)
  	u32 counterF = 0;
  	u32 counterN = 0;
  	u32 max_totlen = 0;
-	u32 buf_size = DEFAULT_EMPTY_SCAN_SIZE;
+	u32 buf_size;
  	char *buf;
 
  	nr_sectors = lldiv(part->size, part->sector_size);
@@ -1505,7 +1505,7 @@ jffs2_1pass_build_lists(struct part_info * part)
  	/* if we are building a list we need to refresh the cache. */
  	jffs_init_1pass_list(part);
  	pL = (struct b_lists *)part->jffs2_priv;
-	buf = malloc(buf_size);
+	buf = malloc(DEFAULT_EMPTY_SCAN_SIZE);
  	puts ("Scanning JFFS2 FS:   ");
 
  	/* start at the beginning of the partition */
@@ -1521,6 +1521,8 @@ jffs2_1pass_build_lists(struct part_info * part)
  		int ret;
  #endif
 
+		/* Set buf_size to maximum length */
+		buf_size = DEFAULT_EMPTY_SCAN_SIZE;
  		WATCHDOG_RESET();
 
  #ifdef CONFIG_JFFS2_SUMMARY
@@ -1595,6 +1597,10 @@ jffs2_1pass_build_lists(struct part_info * part)
 
  		ofs += sector_ofs;
  		prevofs = ofs - 1;
+		/* Set buf_size down to the minimum size required.
+		 * This prevents reading in chunks of flash data unnecessarily.
+		 */
+		buf_size = sizeof(union jffs2_node_union);
 
  	scan_more:
  		while (ofs < sector_ofs + part->sector_size) {
@@ -1675,13 +1681,18 @@ jffs2_1pass_build_lists(struct part_info * part)
  			case JFFS2_NODETYPE_INODE:
  				if (buf_ofs + buf_len < ofs + sizeof(struct
  							jffs2_raw_inode)) {
+					buf_len = min_t(uint32_t,
+							sizeof(struct jffs2_raw_inode),
+							sector_ofs +
+							part->sector_size -
+							ofs);
  					get_fl_mem((u32)part->offset + ofs,
  						   buf_len, buf);
  					buf_ofs = ofs;
  					node = (void *)buf;
  				}
-				if (!inode_crc((struct jffs2_raw_inode *) node))
-				       break;
+				if (!inode_crc((struct jffs2_raw_inode *)node))
+					break;
 
  				if (insert_node(&pL->frag, (u32) part->offset +
  						ofs) == NULL) {
@@ -1698,6 +1709,11 @@ jffs2_1pass_build_lists(struct part_info * part)
  							((struct
  							 jffs2_raw_dirent *)
  							node)->nsize) {
+					buf_len = min_t(uint32_t,
+							node->totlen,
+							sector_ofs +
+							part->sector_size -
+							ofs);
  					get_fl_mem((u32)part->offset + ofs,
  						   buf_len, buf);
  					buf_ofs = ofs;
#11 lore raw

PATCH 6/8 JFFS2: Change scansize to match linux kernel

In reply to Mark Tomlinson

The scan code is similar to the linux kernel, but the kernel defines a much smaller size to scan through before deciding a sector is blank. Assuming that what is in the kernel is OK, make these two match.

On its own, this change makes no difference to scanning of any sectors which have a clean marker at the beginning, since the entire sector is not blank.

---

 fs/jffs2/jffs2_1pass.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
fs/jffs2/jffs2_1pass.c +1 -1
index 10bd7be..d78fb06 100644
@@ -1472,7 +1472,7 @@ dump_dirents(struct b_lists *pL)
 }
 #endif
 
-#define DEFAULT_EMPTY_SCAN_SIZE	4096
+#define DEFAULT_EMPTY_SCAN_SIZE	256
 
 static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size)
 {
Signature · 1 line
1.9.1
#12 lore raw

PATCH 6/8 JFFS2: Change scansize to match linux kernel

Hello Mark,

Am 29.06.2015 um 07:02 schrieb Mark Tomlinson:

Quoted 8 lines

The scan code is similar to the linux kernel, but the kernel defines a much smaller size to scan through before deciding a sector is blank. Assuming that what is in the kernel is OK, make these two match.

On its own, this change makes no difference to scanning of any sectors which have a clean marker at the beginning, since the entire sector is not blank.

Quoted 5 lines

---

  fs/jffs2/jffs2_1pass.c | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)

bye,
Heiko

Quoted 15 lines
fs/jffs2/jffs2_1pass.c +1 -1
index 10bd7be..d78fb06 100644
@@ -1472,7 +1472,7 @@ dump_dirents(struct b_lists *pL)
  }
  #endif
 
-#define DEFAULT_EMPTY_SCAN_SIZE	4096
+#define DEFAULT_EMPTY_SCAN_SIZE	256
 
  static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size)
  {
#13 lore raw

PATCH 7/8 JFFS2: Use CLEANMARKER to reduce scanning time

In reply to Mark Tomlinson

If a sector has a CLEANMARKER at the beginning, it indicates that the entire sector has been erased. Therefore, if this is found, we can skip the entire block. This was not being done before this patch.

The code now does the same as the kernel does when encountering a CLEANMARKER. It still checks that the next few words are FFFFFFFF, and if so, the block is assumed to be empty, and so is skipped.

---

 fs/jffs2/jffs2_1pass.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
fs/jffs2/jffs2_1pass.c +22 -0
index d78fb06..26a748f 100644
@@ -1520,6 +1520,8 @@ jffs2_1pass_build_lists(struct part_info * part)
 		uint32_t sumlen;
 		int ret;
 #endif
+		/* Indicates a sector with a CLEANMARKER was found */
+		int clean_sector = 0;
 
 		/* Set buf_size to maximum length */
 		buf_size = DEFAULT_EMPTY_SCAN_SIZE;
@@ -1643,6 +1645,13 @@ jffs2_1pass_build_lists(struct part_info * part)
 					ofs += 4;
 				}
 				/* Ran off end. */
+				/* If this sector had a clean marker at the
+				 * beginning, and immediately following this
+				 * have been a bunch of FF bytes, treat the
+				 * entire sector as empty.
+				 */
+				if (clean_sector)
+					break;
 
 				/* See how much more there is to read in this
 				 * eraseblock...
@@ -1664,6 +1673,10 @@ jffs2_1pass_build_lists(struct part_info * part)
 				buf_ofs = ofs;
 				goto more_empty;
 			}
+			/* Found something not erased in the sector, so reset
+			 * the 'clean_sector' flag.
+			 */
+			clean_sector = 0;
 			if (node->magic != JFFS2_MAGIC_BITMASK ||
 					!hdr_crc(node)) {
 				ofs += 4;
@@ -1745,6 +1758,15 @@ jffs2_1pass_build_lists(struct part_info * part)
 						"%d != %zu\n",
 						node->totlen,
 						sizeof(struct jffs2_unknown_node));
+				if ((node->totlen ==
+				     sizeof(struct jffs2_unknown_node)) &&
+				    (ofs == sector_ofs)) {
+					/* Found a CLEANMARKER at the beginning
+					 * of the sector. It's in the correct
+					 * place with correct size and CRC.
+					 */
+					clean_sector = 1;
+				}
 				break;
 			case JFFS2_NODETYPE_PADDING:
 				if (node->totlen < sizeof(struct jffs2_unknown_node))
Signature · 1 line
1.9.1
#14 lore raw

PATCH 7/8 JFFS2: Use CLEANMARKER to reduce scanning time

Hello Mark,

Am 29.06.2015 um 07:02 schrieb Mark Tomlinson:

Quoted 8 lines

If a sector has a CLEANMARKER at the beginning, it indicates that the entire sector has been erased. Therefore, if this is found, we can skip the entire block. This was not being done before this patch.

The code now does the same as the kernel does when encountering a CLEANMARKER. It still checks that the next few words are FFFFFFFF, and if so, the block is assumed to be empty, and so is skipped.

Quoted 5 lines

---

  fs/jffs2/jffs2_1pass.c | 22 ++++++++++++++++++++++
  1 file changed, 22 insertions(+)

Beside of your comment style

bye,
Heiko

Quoted 56 lines
fs/jffs2/jffs2_1pass.c +22 -0
index d78fb06..26a748f 100644
@@ -1520,6 +1520,8 @@ jffs2_1pass_build_lists(struct part_info * part)
  		uint32_t sumlen;
  		int ret;
  #endif
+		/* Indicates a sector with a CLEANMARKER was found */
+		int clean_sector = 0;
 
  		/* Set buf_size to maximum length */
  		buf_size = DEFAULT_EMPTY_SCAN_SIZE;
@@ -1643,6 +1645,13 @@ jffs2_1pass_build_lists(struct part_info * part)
  					ofs += 4;
  				}
  				/* Ran off end. */
+				/* If this sector had a clean marker at the
+				 * beginning, and immediately following this
+				 * have been a bunch of FF bytes, treat the
+				 * entire sector as empty.
+				 */
+				if (clean_sector)
+					break;
 
  				/* See how much more there is to read in this
  				 * eraseblock...
@@ -1664,6 +1673,10 @@ jffs2_1pass_build_lists(struct part_info * part)
  				buf_ofs = ofs;
  				goto more_empty;
  			}
+			/* Found something not erased in the sector, so reset
+			 * the 'clean_sector' flag.
+			 */
+			clean_sector = 0;
  			if (node->magic != JFFS2_MAGIC_BITMASK ||
  					!hdr_crc(node)) {
  				ofs += 4;
@@ -1745,6 +1758,15 @@ jffs2_1pass_build_lists(struct part_info * part)
  						"%d != %zu\n",
  						node->totlen,
  						sizeof(struct jffs2_unknown_node));
+				if ((node->totlen ==
+				     sizeof(struct jffs2_unknown_node)) &&
+				    (ofs == sector_ofs)) {
+					/* Found a CLEANMARKER at the beginning
+					 * of the sector. It's in the correct
+					 * place with correct size and CRC.
+					 */
+					clean_sector = 1;
+				}
  				break;
  			case JFFS2_NODETYPE_PADDING:
  				if (node->totlen < sizeof(struct jffs2_unknown_node))
#15 lore raw

PATCH 7/8 JFFS2: Use CLEANMARKER to reduce scanning time

In reply to Mark Tomlinson

Hello Mark,

Am 29.06.2015 um 07:02 schrieb Mark Tomlinson:

Quoted 8 lines

If a sector has a CLEANMARKER at the beginning, it indicates that the entire sector has been erased. Therefore, if this is found, we can skip the entire block. This was not being done before this patch.

The code now does the same as the kernel does when encountering a CLEANMARKER. It still checks that the next few words are FFFFFFFF, and if so, the block is assumed to be empty, and so is skipped.

Quoted 5 lines

---

  fs/jffs2/jffs2_1pass.c | 22 ++++++++++++++++++++++
  1 file changed, 22 insertions(+)

Beside of your comment style

bye,
Heiko

Quoted 56 lines
fs/jffs2/jffs2_1pass.c +22 -0
index d78fb06..26a748f 100644
@@ -1520,6 +1520,8 @@ jffs2_1pass_build_lists(struct part_info * part)
  		uint32_t sumlen;
  		int ret;
  #endif
+		/* Indicates a sector with a CLEANMARKER was found */
+		int clean_sector = 0;
 
  		/* Set buf_size to maximum length */
  		buf_size = DEFAULT_EMPTY_SCAN_SIZE;
@@ -1643,6 +1645,13 @@ jffs2_1pass_build_lists(struct part_info * part)
  					ofs += 4;
  				}
  				/* Ran off end. */
+				/* If this sector had a clean marker at the
+				 * beginning, and immediately following this
+				 * have been a bunch of FF bytes, treat the
+				 * entire sector as empty.
+				 */
+				if (clean_sector)
+					break;
 
  				/* See how much more there is to read in this
  				 * eraseblock...
@@ -1664,6 +1673,10 @@ jffs2_1pass_build_lists(struct part_info * part)
  				buf_ofs = ofs;
  				goto more_empty;
  			}
+			/* Found something not erased in the sector, so reset
+			 * the 'clean_sector' flag.
+			 */
+			clean_sector = 0;
  			if (node->magic != JFFS2_MAGIC_BITMASK ||
  					!hdr_crc(node)) {
  				ofs += 4;
@@ -1745,6 +1758,15 @@ jffs2_1pass_build_lists(struct part_info * part)
  						"%d != %zu\n",
  						node->totlen,
  						sizeof(struct jffs2_unknown_node));
+				if ((node->totlen ==
+				     sizeof(struct jffs2_unknown_node)) &&
+				    (ofs == sector_ofs)) {
+					/* Found a CLEANMARKER at the beginning
+					 * of the sector. It's in the correct
+					 * place with correct size and CRC.
+					 */
+					clean_sector = 1;
+				}
  				break;
  			case JFFS2_NODETYPE_PADDING:
  				if (node->totlen < sizeof(struct jffs2_unknown_node))
Signature · 2 lines
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
#16 lore raw

PATCH 8/8 JFFS2: Use merge sort when parsing filesystem

In reply to Mark Tomlinson

When building the file system the existing code does an insertion into a linked list. It attempts to speed this up by keeping a pointer to where the last entry was inserted but it's still slow.

Now the nodes are just inserted into the list without searching through for the correct place. This unsorted list is then sorted once using mergesort after all the entries have been added to the list. This speeds up the scanning of the flash file system considerably.

---

 fs/jffs2/Makefile        |  1 +
 fs/jffs2/jffs2_1pass.c   | 47 ++++++++------------------------
 fs/jffs2/jffs2_private.h |  4 +++
 fs/jffs2/mergesort.c     | 70 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 86 insertions(+), 36 deletions(-)
 create mode 100644 fs/jffs2/mergesort.c
fs/jffs2/Makefile +1 -0
index 4cb0600..90524ba 100644
@@ -10,4 +10,5 @@ obj-y += compr_rtime.o
 obj-y += compr_rubin.o
 obj-y += compr_zlib.o
 obj-y += jffs2_1pass.o
+obj-y += mergesort.o
 obj-y += mini_inflate.o
fs/jffs2/jffs2_1pass.c +11 -36

73 lines not on this page · raw message

fs/jffs2/jffs2_private.h +4 -0
index 658b325..06b6ca2 100644
@@ -98,4 +98,8 @@ data_crc(struct jffs2_raw_inode *node)
 	}
 }
 
+#if defined(CONFIG_SYS_JFFS2_SORT_FRAGMENTS)
+/* External merge sort. */
+int sort_list(struct b_list *list);
+#endif
 #endif /* jffs2_private.h */
fs/jffs2/mergesort.c new file +70 -0

70 lines not on this page · raw message

Signature · 1 line
1.9.1
#17 lore raw

PATCH 8/8 JFFS2: Use merge sort when parsing filesystem

Hello Mark,

Am 29.06.2015 um 07:02 schrieb Mark Tomlinson:

Quoted 34 lines

When building the file system the existing code does an insertion into a linked list. It attempts to speed this up by keeping a pointer to where the last entry was inserted but it's still slow.

Now the nodes are just inserted into the list without searching through for the correct place. This unsorted list is then sorted once using mergesort after all the entries have been added to the list. This speeds up the scanning of the flash file system considerably.

---

  fs/jffs2/Makefile        |  1 +
  fs/jffs2/jffs2_1pass.c   | 47 ++++++++------------------------
  fs/jffs2/jffs2_private.h |  4 +++
  fs/jffs2/mergesort.c     | 70 ++++++++++++++++++++++++++++++++++++++++++++++++
  4 files changed, 86 insertions(+), 36 deletions(-)

create mode 100644 fs/jffs2/mergesort.c

fs/jffs2/Makefile +1 -0
index 4cb0600..90524ba 100644
@@ -10,4 +10,5 @@ obj-y += compr_rtime.o
  obj-y += compr_rubin.o
  obj-y += compr_zlib.o
  obj-y += jffs2_1pass.o
+obj-y += mergesort.o
  obj-y += mini_inflate.o
fs/jffs2/jffs2_1pass.c +0 -0
index 26a748f..a456650 100644

fs/jffs2/jffs2_1pass.c line 128

Quoted 4 lines
fs/jffs2/jffs2_1pass.c +0 -1
@@ -125,7 +125,6 @@
 
  #include "jffs2_private.h"
 
-

This change has nothing to do with your commit message ...

Quoted 105 lines
fs/jffs2/jffs2_1pass.c +11 -35
  #define	NODE_CHUNK	1024	/* size of memory allocation chunk in b_nodes */
  #define	SPIN_BLKSIZE	18	/* spin after having scanned 1<<BLKSIZE bytes */
 
@@ -545,49 +544,19 @@ static struct b_node *
  insert_node(struct b_list *list, u32 offset)
  {
  	struct b_node *new;
-#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS
-	struct b_node *b, *prev;
-#endif
 
  	if (!(new = add_node(list))) {
  		putstr("add_node failed!\r\n");
  		return NULL;
  	}
  	new->offset = offset;
+	new->next = NULL;
 
-#ifdef CONFIG_SYS_JFFS2_SORT_FRAGMENTS
-	if (list->listTail != NULL && list->listCompare(new, list->listTail))
-		prev = list->listTail;
-	else if (list->listLast != NULL && list->listCompare(new, list->listLast))
-		prev = list->listLast;
+	if (list->listTail != NULL)
+		list->listTail->next = new;
  	else
-		prev = NULL;
-
-	for (b = (prev ? prev->next : list->listHead);
-	     b != NULL && list->listCompare(new, b);
-	     prev = b, b = b->next) {
-		list->listLoops++;
-	}
-	if (b != NULL)
-		list->listLast = prev;
-
-	if (b != NULL) {
-		new->next = b;
-		if (prev != NULL)
-			prev->next = new;
-		else
-			list->listHead = new;
-	} else
-#endif
-	{
-		new->next = (struct b_node *) NULL;
-		if (list->listTail != NULL) {
-			list->listTail->next = new;
-			list->listTail = new;
-		} else {
-			list->listTail = list->listHead = new;
-		}
-	}
+		list->listHead = new;
+	list->listTail = new;
 
  	return new;
  }
@@ -1788,6 +1757,12 @@ jffs2_1pass_build_lists(struct part_info * part)
  	}
 
  	free(buf);
+#if defined(CONFIG_SYS_JFFS2_SORT_FRAGMENTS)
+	/* Sort the lists.
+	 */
+	sort_list(&pL->frag);
+	sort_list(&pL->dir);
+#endif
  	putstr("\b\b done.\r\n");		/* close off the dots */
 
  	/* We don't care if malloc failed - then each read operation will
fs/jffs2/jffs2_private.h +4 -0
index 658b325..06b6ca2 100644
@@ -98,4 +98,8 @@ data_crc(struct jffs2_raw_inode *node)
  	}
  }
 
+#if defined(CONFIG_SYS_JFFS2_SORT_FRAGMENTS)
+/* External merge sort. */
+int sort_list(struct b_list *list);
+#endif
  #endif /* jffs2_private.h */
fs/jffs2/mergesort.c new file +15 -0
new file mode 100644
index 0000000..10ecc8d
@@ -0,0 +1,70 @@
+/*
+ * This file is copyright 2001 Simon Tatham.
+ * Rewritten from original source 2006 by Dan Merillat for use in u-boot.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.

fs/jffs2/mergesort.c line 25

Quoted 10 lines
fs/jffs2/mergesort.c new file +10 -0
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */

Could you replace this license text with a SPDX-License-Identifier? And a description from where this code comes?
See here:
http://www.denx.de/wiki/view/U-Boot/Patches#Attributing_Code_Copyrights_Sign

Thanks!

fs/jffs2/mergesort.c line 30

Quoted 5 lines
fs/jffs2/mergesort.c new file +5 -0
+
+#include <common.h>
+#include "jffs2_private.h"
+
+#if defined(CONFIG_SYS_JFFS2_SORT_FRAGMENTS)

you can move this into fs/jffs2/Makefile

obj-$(CONFIG_SYS_JFFS2_SORT_FRAGMENTS) += mergesort.o

and remove this
"#if defined ..." here ...

bye,
Heiko

Quoted 41 lines
fs/jffs2/mergesort.c new file +40 -0
+int sort_list(struct b_list *list)
+{
+	struct b_node *p, *q, *e, **tail;
+	int k, psize, qsize;
+
+	if (!list->listHead)
+		return 0;
+
+	for (k = 1; k < list->listCount; k *= 2) {
+		tail = &list->listHead;
+		for (p = q = list->listHead; p; p = q) {
+			/* step 'k' places from p; */
+			for (psize = 0; q && psize < k; psize++)
+				q = q->next;
+			qsize = k;
+
+			/* two lists, merge them. */
+			while (psize || (qsize && q)) {
+				/* merge the next element */
+				if (psize == 0 ||
+				    ((qsize && q) &&
+				     list->listCompare(p, q))) {
+					/* p is empty, or p > q, so q next */
+					e = q;
+					q = q->next;
+					qsize--;
+				} else {
+					e = p;
+					p = p->next;
+					psize--;
+				}
+				e->next = NULL; /* break accidental loops. */
+				*tail = e;
+				tail = &e->next;
+			}
+		}
+	}
+	return 0;
+}
+#endif
Signature · 2 lines
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
#18 lore raw

cover JFFS2 fixes and performance improvements

In reply to Mark Tomlinson

Dear Mark,

Quoted 6 lines · In message <1435554149-18042-1-git-send-email-mark.tomlinson@alliedtelesis.co.n…

These patches fix bugs and improve performance of JFFS2. Some of these improvements can already be found in old mailing lists, but for some reason they have not made their way into the u-boot source. I have the feeling that any one of these patches didn't show enough performance gain to warrant adding it to the source. I am hopeful that together, all these patches can be seen to make a big difference.

It would be a good thing to know where exactly these changes are coming from? Is this independent optoimization within U-Boot only, or did you for example try to synchronize the U-Boot code against a recent Linux kernel version? I feel that would be a pretty useful undertaking...

Best regards,

Wolfgang Denk

Signature · 4 lines
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
Bankers do it with interest (penalty for early withdrawal).