Skip to contentSkip to conversation

Optimized nand_read_buf for kirkwood

PATCH Superseded Phil Sutter 4 participants 46 messages Nov 21, 2012 – Aug 22, 2013

Subsystems (2)

v1 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/4 Optimized nand_read_buf for kirkwood

The basic idea is taken from the linux-kernel, but further optimized.

First align the buffer to 8 bytes, then use ldrd/strd to read and store in 8 byte quantities, then do the final bytes.

Cc Prafulla Wadaskar <[email protected]>
---
 drivers/mtd/nand/kirkwood_nand.c |   29 +++++++++++++++++++++++++++++
 1 files changed, 29 insertions(+), 0 deletions(-)
drivers/mtd/nand/kirkwood_nand.c +29 -0
index bdab5aa..e04a59f 100644
@@ -38,6 +38,34 @@ struct kwnandf_registers {
 static struct kwnandf_registers *nf_reg =
 	(struct kwnandf_registers *)KW_NANDF_BASE;
 
+
+/* The basic idea is stolen from the linux kernel, but the inner loop is optimized a bit more */
+static void kw_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	while (len && (unsigned long)buf & 7)
+	{
+		*buf++ = readb(chip->IO_ADDR_R);
+		len--;
+	};
+
+	asm volatile (
+		".LFlashLoop:\n"
+		"  subs\t%0, #8\n"
+		"  ldrpld\tr2, [%2]\n" // Read 2 words
+		"  strpld\tr2, [%1], #8\n" // Read 2 words
+		"  bpl\t.LFlashLoop\n" // This results in one additional loop if len%8 <> 0
+		"  addne\t%0, #8\n"
+		: "+&r" (len), "+&r" (buf)
+		: "r" (chip->IO_ADDR_R)
+		: "r2", "r3", "memory", "cc"
+	);
+
+	while (len--)
+		*buf++ = readb(chip->IO_ADDR_R);
+}
+
 /*
  * hardware specific access to control-lines/bits
  */
@@ -76,6 +104,7 @@ int board_nand_init(struct nand_chip *nand)
 	nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING;
 	nand->ecc.mode = NAND_ECC_SOFT;
 	nand->cmd_ctrl = kw_nand_hwcontrol;
+	nand->read_buf = kw_nand_read_buf;
 	nand->chip_delay = 40;
 	nand->select_chip = kw_nand_select_chip;
 	return 0;
Signature · 1 line
1.7.3.4
#1 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Without this patch, when the currently chosen environment to be written has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment reading needs no adjustment, as the fallback logic for incomplete writes applies to this case as well.

---
 common/env_nand.c |  100 ++++++++++++++++++++++------------------------------
 1 files changed, 42 insertions(+), 58 deletions(-)
common/env_nand.c +42 -58
index 79e8033..895532b 100644
@@ -168,72 +168,53 @@ int writeenv(size_t offset, u_char *buf)
 	return 0;
 }
 
-#ifdef CONFIG_ENV_OFFSET_REDUND
-static unsigned char env_flags;
+typedef struct env_location {
+	const char *name;
+	nand_erase_options_t *erase_opts;
+	loff_t offset;
+} env_location_t;
 
-int saveenv(void)
+static int erase_and_write_env(env_location_t *location, u_char *env_new)
 {
-	env_t	env_new;
-	ssize_t	len;
-	char	*res;
-	int	ret = 0;
-	nand_erase_options_t nand_erase_options;
-
-	memset(&nand_erase_options, 0, sizeof(nand_erase_options));
-	nand_erase_options.length = CONFIG_ENV_RANGE;
+	int ret = 0;
 
-	if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
+	printf("Erasing %s...\n", location->name);
+	location->erase_opts->offset = location->offset;
+	if (nand_erase_opts(&nand_info[0], location->erase_opts))
 		return 1;
 
-	res = (char *)&env_new.data;
-	len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL);
-	if (len < 0) {
-		error("Cannot export environment: errno = %d\n", errno);
-		return 1;
-	}
-	env_new.crc	= crc32(0, env_new.data, ENV_SIZE);
-	env_new.flags	= ++env_flags; /* increase the serial */
-
-	if (gd->env_valid == 1) {
-		puts("Erasing redundant NAND...\n");
-		nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND;
-		if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-			return 1;
-
-		puts("Writing to redundant NAND... ");
-		ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *)&env_new);
-	} else {
-		puts("Erasing NAND...\n");
-		nand_erase_options.offset = CONFIG_ENV_OFFSET;
-		if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-			return 1;
-
-		puts("Writing to NAND... ");
-		ret = writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new);
-	}
-	if (ret) {
+	printf("Writing to %s... ", location->name);
+	if ((ret = writeenv(location->offset, env_new)))
 		puts("FAILED!\n");
-		return 1;
-	}
-
-	puts("done\n");
-
-	gd->env_valid = gd->env_valid == 2 ? 1 : 2;
 
 	return ret;
 }
-#else /* ! CONFIG_ENV_OFFSET_REDUND */
+
+static unsigned char env_flags;
+
 int saveenv(void)
 {
 	int	ret = 0;
 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
 	ssize_t	len;
 	char	*res;
+	int	env_idx;
 	nand_erase_options_t nand_erase_options;
+	env_location_t location[] = {{
+		.name = "NAND",
+		.erase_opts = &nand_erase_options,
+		.offset = CONFIG_ENV_OFFSET,
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	}, {
+		.name = "redundant NAND",
+		.erase_opts = &nand_erase_options,
+		.offset = CONFIG_ENV_OFFSET_REDUND,
+#endif
+	}};
+
 
 	memset(&nand_erase_options, 0, sizeof(nand_erase_options));
 	nand_erase_options.length = CONFIG_ENV_RANGE;
-	nand_erase_options.offset = CONFIG_ENV_OFFSET;
 
 	if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
 		return 1;
@@ -244,22 +225,25 @@ int saveenv(void)
 		error("Cannot export environment: errno = %d\n", errno);
 		return 1;
 	}
-	env_new->crc = crc32(0, env_new->data, ENV_SIZE);
-
-	puts("Erasing Nand...\n");
-	if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-		return 1;
+	env_new->crc   = crc32(0, env_new->data, ENV_SIZE);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	env_new->flags = ++env_flags; /* increase the serial */
+	env_idx = (gd->env_valid == 1);
+#endif
 
-	puts("Writing to Nand... ");
-	if (writeenv(CONFIG_ENV_OFFSET, (u_char *)env_new)) {
-		puts("FAILED!\n");
-		return 1;
-	}
+	ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	if (ret) {
+		env_idx = (env_idx + 1) & 1;
+		ret = erase_and_write_env(&location[env_idx],
+				(u_char *)env_new);
+	} else
+		gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+#endif
 
 	puts("done\n");
 	return ret;
 }
-#endif /* CONFIG_ENV_OFFSET_REDUND */
 #endif /* CMD_SAVEENV */
 
 int readenv(size_t offset, u_char *buf)
Signature · 1 line
1.7.3.4
#2 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Quoted 9 lines · Phil Sutter

Without this patch, when the currently chosen environment to be written
has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment reading needs no adjustment, as the fallback logic for incomplete writes applies
to this case as well.

Isn't this what CONFIG_ENV_RANGE is supposed to deal with?

Redundant environment is to deal with other problems such as a power failure during saveenv. If you just fall back to the other copy, you're silently losing the redundancy.

-Scott

#3 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Hi,

Quoted 9 lines · Scott Wood
Quoted 8 lines · Phil Sutter

Without this patch, when the currently chosen environment to be written
has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment reading needs no adjustment, as the fallback logic for incomplete writes applies
to this case as well.

Quoted 3 lines
Quoted 1 line

Isn't this what CONFIG_ENV_RANGE is supposed to deal with?

Yes, that is more or less what is supposed to help for cases like this. But given the fact that CONFIG_ENV_RANGE needs to span multiple erase pages which in our case are 128k in size, this is quite a deal. Especially since one needs to have multiple pages for both normal and redundant environment to be really sure.

But, we already have a fixed hashmap in field, so using CONFIG_ENV_RANGE is simply no option.

Quoted 3 lines

Redundant environment is to deal with other problems such as a power failure during saveenv. If you just fall back to the other copy, you're silently losing the redundancy.

The alternative to silently losing redundancy in case one of the blocks in either normal or redundant env areas turns bad is to not being able to save the environment at all anymore. I'd prefer dropping the redundancy but still having a working system then. ;)

Best wishes and thanks for the review,

Phil Sutter
Software Engineer

Signature · 15 lines
Viprinet Europe GmbH
Mainzer Str. 43
55411 Bingen am Rhein
Germany

Phone/Zentrale:               +49 6721 49030-0
Direct line/Durchwahl:        +49 6721 49030-134
Fax:                          +49 6721 49030-109

phil.sutter at viprinet.com
http://www.viprinet.com

Registered office/Sitz der Gesellschaft: Bingen am Rhein, Germany
Commercial register/Handelsregister: Amtsgericht Mainz HRB44090
CEO/Gesch?ftsf?hrer: Simon Kissel
#4 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Quoted 15 lines · Phil Sutter

Hi,

Quoted 5 lines · Scott Wood
Quoted 4 lines · Phil Sutter

Without this patch, when the currently chosen environment to be written
has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment

reading

Quoted 6 lines
Quoted 5 lines

needs no adjustment, as the fallback logic for incomplete writes applies
to this case as well.

Quoted 8 lines
Quoted 1 line

Isn't this what CONFIG_ENV_RANGE is supposed to deal with?

Yes, that is more or less what is supposed to help for cases like this.
But given the fact that CONFIG_ENV_RANGE needs to span multiple erase pages which in our case are 128k in size, this is quite a deal. Especially since one needs to have multiple pages for both normal and redundant environment to be really sure.

And *that* is what CONFIG_ENV_OFFSET_OOB is supposed to deal with. :-)

Though at the moment redundant environment is not supported with CONFIG_ENV_OFFSET_OOB.

Quoted 3 lines

But, we already have a fixed hashmap in field, so using CONFIG_ENV_RANGE
is simply no option.

That's relevant to what you put in your product, but it shouldn't be the basis of how mainline U-Boot does things for all boards.

Quoted 9 lines
Quoted 3 lines

Redundant environment is to deal with other problems such as a power failure during saveenv. If you just fall back to the other copy, you're silently losing the redundancy.

The alternative to silently losing redundancy in case one of the blocks
in either normal or redundant env areas turns bad is to not being able to save the environment at all anymore. I'd prefer dropping the redundancy but still having a working system then. ;)

Another alternative is to noisily lose redundancy.

-Scott

#5 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Scott,

Quoted 18 lines · Scott Wood
Quoted 17 lines · Phil Sutter

Hi,

Quoted 5 lines · Scott Wood
Quoted 4 lines · Phil Sutter

Without this patch, when the currently chosen environment to be written
has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment

reading

Quoted 7 lines
Quoted 5 lines

needs no adjustment, as the fallback logic for incomplete writes applies
to this case as well.

Isn't this what CONFIG_ENV_RANGE is supposed to deal with?

Quoted 8 lines
Quoted 6 lines

Yes, that is more or less what is supposed to help for cases like this.
But given the fact that CONFIG_ENV_RANGE needs to span multiple erase pages which in our case are 128k in size, this is quite a deal. Especially since one needs to have multiple pages for both normal and redundant environment to be really sure.

And *that* is what CONFIG_ENV_OFFSET_OOB is supposed to deal with. :-)

Good to know, I already wondered what exactly this option is there for.

Quoted 2 lines

Though at the moment redundant environment is not supported with CONFIG_ENV_OFFSET_OOB.

I will have a look at it now, because that could be an elegant solution for both of us I think.

Quoted 6 lines
Quoted 3 lines

But, we already have a fixed hashmap in field, so using CONFIG_ENV_RANGE
is simply no option.

That's relevant to what you put in your product, but it shouldn't be the basis of how mainline U-Boot does things for all boards.

Yes, of course. That was merely me explaining myself, not so much of a real point in matters of what should go mainline and what not.

Quoted 11 lines
Quoted 9 lines
Quoted 3 lines

Redundant environment is to deal with other problems such as a power failure during saveenv. If you just fall back to the other copy, you're silently losing the redundancy.

The alternative to silently losing redundancy in case one of the blocks
in either normal or redundant env areas turns bad is to not being able to save the environment at all anymore. I'd prefer dropping the redundancy but still having a working system then. ;)

Another alternative is to noisily lose redundancy.

Would that be an acceptable alternative from your point of view? Having CONFIG_ENV_OFFSET_OOB in mind, there may be situations where all blocks of either one of the redundant environments get bad (quite artificial, I know). Losing redundancy in exchange for keeping env writeability could still be an option then. What do you think?

Best wishes,

Phil Sutter
Software Engineer

Signature · 15 lines
Viprinet Europe GmbH
Mainzer Str. 43
55411 Bingen am Rhein
Germany

Phone/Zentrale:               +49 6721 49030-0
Direct line/Durchwahl:        +49 6721 49030-134
Fax:                          +49 6721 49030-109

phil.sutter at viprinet.com
http://www.viprinet.com

Registered office/Sitz der Gesellschaft: Bingen am Rhein, Germany
Commercial register/Handelsregister: Amtsgericht Mainz HRB44090
CEO/Gesch?ftsf?hrer: Simon Kissel
#6 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Scott,

Quoted 26 lines · Phil Sutter
Quoted 25 lines · Scott Wood
Quoted 23 lines · Phil Sutter

Hi,

Quoted 5 lines · Scott Wood
Quoted 4 lines · Phil Sutter

Without this patch, when the currently chosen environment to be written
has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment

reading

Quoted 7 lines
Quoted 5 lines

needs no adjustment, as the fallback logic for incomplete writes applies
to this case as well.

Isn't this what CONFIG_ENV_RANGE is supposed to deal with?

Yes, that is more or less what is supposed to help for cases like this.
But given the fact that CONFIG_ENV_RANGE needs to span multiple erase pages which in our case are 128k in size, this is quite a deal. Especially since one needs to have multiple pages for both normal and redundant environment to be really sure.

Quoted 3 lines
Quoted 1 line

And *that* is what CONFIG_ENV_OFFSET_OOB is supposed to deal with. :-)

Good to know, I already wondered what exactly this option is there for.

Hmm. Does not look like CONFIG_ENV_OFFSET_OOB is used to select the block(s) within the erase page to save the environment. Looking at common/env_nand.c:318, the environment offset saved in the OOB seems to be in erase page unit.

On the other hand, I could not find code that alters this setting based on bad blocks found or whatever. This seems to simply be an alternative to setting CONFIG_ENV_OFFSET at build-time.

Best wishes,

Phil Sutter
Software Engineer

Signature · 15 lines
Viprinet Europe GmbH
Mainzer Str. 43
55411 Bingen am Rhein
Germany

Phone/Zentrale:               +49 6721 49030-0
Direct line/Durchwahl:        +49 6721 49030-134
Fax:                          +49 6721 49030-109

phil.sutter at viprinet.com
http://www.viprinet.com

Registered office/Sitz der Gesellschaft: Bingen am Rhein, Germany
Commercial register/Handelsregister: Amtsgericht Mainz HRB44090
CEO/Gesch?ftsf?hrer: Simon Kissel
#7 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Quoted 39 lines · Phil Sutter

Scott,

Quoted 7 lines · Phil Sutter
Quoted 6 lines · Scott Wood
Quoted 5 lines · Phil Sutter

Hi,

Quoted 2 lines · Scott Wood
Quoted 1 line · Phil Sutter

Without this patch, when the currently chosen environment

to be

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

written
has bad blocks, saveenv fails completely. Instead, when

there is

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

redundant environment fall back to the other copy.

Environment

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

reading

Quoted 1 line
Quoted 1 line

needs no adjustment, as the fallback logic for incomplete

writes

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

applies
to this case as well.

Isn't this what CONFIG_ENV_RANGE is supposed to deal with?

Yes, that is more or less what is supposed to help for cases

like

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

this.
But given the fact that CONFIG_ENV_RANGE needs to span multiple

erase

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

pages which in our case are 128k in size, this is quite a deal. Especially since one needs to have multiple pages for both

normal and

Quoted 3 lines
Quoted 3 lines
Quoted 1 line

redundant environment to be really sure.

And *that* is what CONFIG_ENV_OFFSET_OOB is supposed to deal

with. :-)

Quoted 1 line
Quoted 8 lines
Quoted 1 line

Good to know, I already wondered what exactly this option is there

for.

Hmm. Does not look like CONFIG_ENV_OFFSET_OOB is used to select the block(s) within the erase page to save the environment. Looking at common/env_nand.c:318, the environment offset saved in the OOB seems to
be in erase page unit.

I'm not sure what you mean by "block(s) within the erase page" -- blocks are the unit of erasing, and of bad block marking.

The block to hold the environment is stored in the OOB of block zero, which is usually guaranteed to not be bad.

Quoted 5 lines

On the other hand, I could not find code that alters this setting based
on bad blocks found or whatever. This seems to simply be an alternative
to setting CONFIG_ENV_OFFSET at build-time.

It is set by the "nand env.oob" command. It is currently a manual process (or rather, automation is left to the user's board preparation process rather than being built into U-Boot), as U-Boot wouldn't know how to give back unused blocks to other purposes.

-Scott

#8 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Quoted 43 lines · Scott Wood
Quoted 42 lines · Phil Sutter

Scott,

Quoted 7 lines · Phil Sutter
Quoted 6 lines · Scott Wood
Quoted 5 lines · Phil Sutter

Hi,

Quoted 2 lines · Scott Wood
Quoted 1 line · Phil Sutter

Without this patch, when the currently chosen environment

to be

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

written
has bad blocks, saveenv fails completely. Instead, when

there is

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

redundant environment fall back to the other copy.

Environment

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

reading

Quoted 1 line
Quoted 1 line

needs no adjustment, as the fallback logic for incomplete

writes

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines

applies
to this case as well.

Isn't this what CONFIG_ENV_RANGE is supposed to deal with?

Yes, that is more or less what is supposed to help for cases

like

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

this.
But given the fact that CONFIG_ENV_RANGE needs to span multiple

erase

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

pages which in our case are 128k in size, this is quite a deal. Especially since one needs to have multiple pages for both

normal and

Quoted 3 lines
Quoted 3 lines
Quoted 1 line

redundant environment to be really sure.

And *that* is what CONFIG_ENV_OFFSET_OOB is supposed to deal

with. :-)

Quoted 2 lines

Good to know, I already wondered what exactly this option is there

for.

Quoted 8 lines
Quoted 5 lines

Hmm. Does not look like CONFIG_ENV_OFFSET_OOB is used to select the block(s) within the erase page to save the environment. Looking at common/env_nand.c:318, the environment offset saved in the OOB seems to
be in erase page unit.

I'm not sure what you mean by "block(s) within the erase page" -- blocks are the unit of erasing, and of bad block marking.

Not always, at least not with NAND flash. Erase pages are mostly bigger than write pages (or "blocks"). In my case, flash consists of 0x800 bytes write pages and 0x2000 bytes erase pages.

Quoted 2 lines

The block to hold the environment is stored in the OOB of block zero, which is usually guaranteed to not be bad.

Erase or write block? Note that every write block has it's own OOB.

Quoted 10 lines
Quoted 5 lines

On the other hand, I could not find code that alters this setting based
on bad blocks found or whatever. This seems to simply be an alternative
to setting CONFIG_ENV_OFFSET at build-time.

It is set by the "nand env.oob" command. It is currently a manual process (or rather, automation is left to the user's board preparation process rather than being built into U-Boot), as U-Boot wouldn't know how to give back unused blocks to other purposes.

So that assumes that any block initially identified 'good' will ever turn 'bad' later on?

Best wishes,

Phil Sutter
Software Engineer

Signature · 15 lines
Viprinet Europe GmbH
Mainzer Str. 43
55411 Bingen am Rhein
Germany

Phone/Zentrale:               +49 6721 49030-0
Direct line/Durchwahl:        +49 6721 49030-134
Fax:                          +49 6721 49030-109

phil.sutter at viprinet.com
http://www.viprinet.com

Registered office/Sitz der Gesellschaft: Bingen am Rhein, Germany
Commercial register/Handelsregister: Amtsgericht Mainz HRB44090
CEO/Gesch?ftsf?hrer: Simon Kissel
#9 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Quoted 10 lines · Phil Sutter
Quoted 2 lines · Scott Wood
Quoted 1 line · Phil Sutter

Hmm. Does not look like CONFIG_ENV_OFFSET_OOB is used to select

the

Quoted 2 lines
Quoted 2 lines

block(s) within the erase page to save the environment. Looking at common/env_nand.c:318, the environment offset saved in the OOB

seems

Quoted 3 lines
Quoted 2 lines

to
be in erase page unit.

Quoted 7 lines
Quoted 2 lines

I'm not sure what you mean by "block(s) within the erase page" -- blocks are the unit of erasing, and of bad block marking.

Not always, at least not with NAND flash. Erase pages are mostly bigger
than write pages (or "blocks"). In my case, flash consists of 0x800 bytes write pages and 0x2000 bytes erase pages.

Erase blocks are larger than write pages, yes. I've never heard erase blocks called "pages" or write pages called "blocks" -- but my main point is that the unit of erasing and the unit of badness are the same.

Quoted 5 lines
Quoted 1 line

The block to hold the environment is stored in the OOB of block

zero,

Quoted 1 line

which is usually guaranteed to not be bad.

Erase or write block? Note that every write block has it's own OOB.

"block" means "erase block".

Every write page has its own OOB, but it is erase blocks that are marked bad. Typically the block can be marked bad in either the first or the second page of the erase block.

Quoted 8 lines
Quoted 8 lines
Quoted 5 lines

On the other hand, I could not find code that alters this setting based
on bad blocks found or whatever. This seems to simply be an alternative
to setting CONFIG_ENV_OFFSET at build-time.

It is set by the "nand env.oob" command. It is currently a manual process (or rather, automation is left to the user's board

Quoted 7 lines

preparation

Quoted 1 line

process rather than being built into U-Boot), as U-Boot wouldn't

know

Quoted 1 line

how to give back unused blocks to other purposes.

So that assumes that any block initially identified 'good' will ever turn 'bad' later on?

We don't currently have any mechanism for that to happen with the environment -- which could be another good reason to have real redundancy that doesn't get crippled from day one by having one copy land on a factory bad block. Of course, that requires someone to implement support for redundant environment combined with CONFIG_ENV_OFFSET_OOB.

Maybe a better option is to implement support for storing the environment in ubi, although usually if your environment is in NAND that means your U-Boot image is in NAND, so you have the same problem there. Maybe you could have an SPL that contains ubi support, that fits in the guaranteed-good first block.

Do you have any data on how often a block might go bad that wasn't factory-bad, to what extent reads versus writes matter, and whether there is anything special about block zero beyond not being factory-bad?

-Scott

#10 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Quoted 14 lines · Scott Wood
Quoted 13 lines · Phil Sutter
Quoted 2 lines · Scott Wood
Quoted 1 line · Phil Sutter

Hmm. Does not look like CONFIG_ENV_OFFSET_OOB is used to select

the

Quoted 2 lines
Quoted 2 lines

block(s) within the erase page to save the environment. Looking at common/env_nand.c:318, the environment offset saved in the OOB

seems

Quoted 5 lines
Quoted 2 lines

to
be in erase page unit.

I'm not sure what you mean by "block(s) within the erase page" -- blocks are the unit of erasing, and of bad block marking.

Quoted 8 lines
Quoted 4 lines

Not always, at least not with NAND flash. Erase pages are mostly bigger
than write pages (or "blocks"). In my case, flash consists of 0x800 bytes write pages and 0x2000 bytes erase pages.

Erase blocks are larger than write pages, yes. I've never heard erase blocks called "pages" or write pages called "blocks" -- but my main point is that the unit of erasing and the unit of badness are the same.

Ah, OK. Please excuse my humble nomenclature, I never cared enough to sort out what is called what. Of course, this is not the best basis for a discussion about these things.

But getting back to the topic: The assumption of blocks getting bad, not pages within a block means that for any kind of bad block prevention, multiple blocks need to be used. Although I'm honestly speaking not really sure why this needs to be like that. Maybe the bad page marking would disappear when erasing the block it belongs to?

Quoted 11 lines
Quoted 5 lines
Quoted 1 line

The block to hold the environment is stored in the OOB of block

zero,

Quoted 1 line

which is usually guaranteed to not be bad.

Erase or write block? Note that every write block has it's own OOB.

"block" means "erase block".

Every write page has its own OOB, but it is erase blocks that are marked bad. Typically the block can be marked bad in either the first or the second page of the erase block.

Interesting. I had the impression of pages being marked bad and the block's badness being taken from whether it contains bad pages. Probably the 'nand markbad' command tricked me.

Quoted 16 lines
Quoted 8 lines
Quoted 8 lines
Quoted 5 lines

On the other hand, I could not find code that alters this setting based
on bad blocks found or whatever. This seems to simply be an alternative
to setting CONFIG_ENV_OFFSET at build-time.

It is set by the "nand env.oob" command. It is currently a manual process (or rather, automation is left to the user's board

Quoted 7 lines

preparation

Quoted 1 line

process rather than being built into U-Boot), as U-Boot wouldn't

know

Quoted 1 line

how to give back unused blocks to other purposes.

So that assumes that any block initially identified 'good' will ever turn 'bad' later on?

Quoted 6 lines

We don't currently have any mechanism for that to happen with the environment -- which could be another good reason to have real redundancy that doesn't get crippled from day one by having one copy land on a factory bad block. Of course, that requires someone to implement support for redundant environment combined with CONFIG_ENV_OFFSET_OOB.

Well, as long as CONFIG_ENV_OFFSET_REDUND supported falling back to the other copy in case of error there would be a working system in three of four cases instead of only one.

Quoted 9 lines

Maybe a better option is to implement support for storing the environment in ubi, although usually if your environment is in NAND that means your U-Boot image is in NAND, so you have the same problem there. Maybe you could have an SPL that contains ubi support, that fits in the guaranteed-good first block.

Do you have any data on how often a block might go bad that wasn't factory-bad, to what extent reads versus writes matter, and whether there is anything special about block zero beyond not being factory-bad?

No, sadly not. I'd guess this information depends on what hardware being used specifically. But I suppose block zero being prone to becoming worn just like any other block, although it not being erased as often should help a lot.

Assuming a certain number of erase cycles after each block is worn out and given the fact that CONFIG_ENV_OFFSET_REDUND has always both blocks written (unless power failure occurs), they would turn bad at the same time and therefore rendering the environment useless with or without fallback. :)

Best wishes, Phil

Signature · 15 lines
Viprinet Europe GmbH
Mainzer Str. 43
55411 Bingen am Rhein
Germany

Phone/Zentrale:               +49 6721 49030-0
Direct line/Durchwahl:        +49 6721 49030-134
Fax:                          +49 6721 49030-109

phil.sutter at viprinet.com
http://www.viprinet.com

Registered office/Sitz der Gesellschaft: Bingen am Rhein, Germany
Commercial register/Handelsregister: Amtsgericht Mainz HRB44090
CEO/Gesch?ftsf?hrer: Simon Kissel
#11 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Quoted 12 lines · Phil Sutter
Quoted 1 line · Scott Wood

Erase blocks are larger than write pages, yes. I've never heard

erase

Quoted 2 lines

blocks called "pages" or write pages called "blocks" -- but my main point is that the unit of erasing and the unit of badness are the

same.

Ah, OK. Please excuse my humble nomenclature, I never cared enough to sort out what is called what. Of course, this is not the best basis for
a discussion about these things.

Quoted 6 lines

But getting back to the topic: The assumption of blocks getting bad, not
pages within a block means that for any kind of bad block prevention, multiple blocks need to be used. Although I'm honestly speaking not really sure why this needs to be like that. Maybe the bad page marking would disappear when erasing the block it belongs to?

Yes, it would disappear. This is why erase operations skip bad blocks, unless the scrub option is uesd.

Quoted 11 lines
Quoted 5 lines
Quoted 5 lines
Quoted 1 line

The block to hold the environment is stored in the OOB of block

zero,

Quoted 1 line

which is usually guaranteed to not be bad.

Erase or write block? Note that every write block has it's own

OOB.

Quoted 5 lines

"block" means "erase block".

Every write page has its own OOB, but it is erase blocks that are marked bad. Typically the block can be marked bad in either the

Quoted 7 lines

first

Quoted 1 line

or the second page of the erase block.

Interesting. I had the impression of pages being marked bad and the block's badness being taken from whether it contains bad pages. Probably
the 'nand markbad' command tricked me.

Do you mean the lack of error checking if you pass a non-block-aligned offset into "nand markbad"?

Quoted 11 lines
Quoted 1 line
Quoted 1 line

So that assumes that any block initially identified 'good' will

ever

Quoted 8 lines
Quoted 1 line

turn 'bad' later on?

We don't currently have any mechanism for that to happen with the environment -- which could be another good reason to have real redundancy that doesn't get crippled from day one by having one copy land on a factory bad block. Of course, that requires someone to implement support for redundant environment combined with CONFIG_ENV_OFFSET_OOB.

Quoted 5 lines

Well, as long as CONFIG_ENV_OFFSET_REDUND supported falling back to the
other copy in case of error there would be a working system in three of
four cases instead of only one.

I'm not sure what you mean here -- where do "three", "four", and "one" come from?

Quoted 18 lines
Quoted 3 lines

Maybe a better option is to implement support for storing the environment in ubi, although usually if your environment is in NAND that means your U-Boot image is in NAND, so you have the same

problem

Quoted 6 lines

there. Maybe you could have an SPL that contains ubi support, that fits in the guaranteed-good first block.

Do you have any data on how often a block might go bad that wasn't factory-bad, to what extent reads versus writes matter, and whether there is anything special about block zero beyond not being

factory-bad?

No, sadly not. I'd guess this information depends on what hardware being
used specifically. But I suppose block zero being prone to becoming worn just like any other block, although it not being erased as often should help a lot.

Quoted 6 lines

Assuming a certain number of erase cycles after each block is worn out and given the fact that CONFIG_ENV_OFFSET_REDUND has always both blocks
written (unless power failure occurs), they would turn bad at the same time and therefore rendering the environment useless with or without fallback. :)

That depends on whether the specified number of erase cycles per block is a minimum for any block not marked factory-bad, or whether some fraction of non-factory-bad blocks may fail early.

-Scott

#12 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Quoted 20 lines · Scott Wood
Quoted 12 lines · Phil Sutter
Quoted 1 line · Scott Wood

Erase blocks are larger than write pages, yes. I've never heard

erase

Quoted 2 lines

blocks called "pages" or write pages called "blocks" -- but my main point is that the unit of erasing and the unit of badness are the

same.

Ah, OK. Please excuse my humble nomenclature, I never cared enough to sort out what is called what. Of course, this is not the best basis for
a discussion about these things.

Quoted 6 lines

But getting back to the topic: The assumption of blocks getting bad, not
pages within a block means that for any kind of bad block prevention, multiple blocks need to be used. Although I'm honestly speaking not really sure why this needs to be like that. Maybe the bad page marking would disappear when erasing the block it belongs to?

Quoted 2 lines

Yes, it would disappear. This is why erase operations skip bad blocks, unless the scrub option is uesd.

Which is apparently preventing good pages in a block with a bad page from being used, isn't it?

Quoted 14 lines
Quoted 14 lines
Quoted 5 lines
Quoted 5 lines
Quoted 1 line

The block to hold the environment is stored in the OOB of block

zero,

Quoted 1 line

which is usually guaranteed to not be bad.

Erase or write block? Note that every write block has it's own

OOB.

Quoted 5 lines

"block" means "erase block".

Every write page has its own OOB, but it is erase blocks that are marked bad. Typically the block can be marked bad in either the

first

Quoted 1 line

or the second page of the erase block.

Quoted 7 lines
Quoted 4 lines

Interesting. I had the impression of pages being marked bad and the block's badness being taken from whether it contains bad pages. Probably
the 'nand markbad' command tricked me.

Do you mean the lack of error checking if you pass a non-block-aligned offset into "nand markbad"?

I think the bigger "problem" is 'nand markbad' updating the bad block table along the go. So no real bad block detection occurs as far as I can tell.

Quoted 11 lines
Quoted 11 lines
Quoted 1 line
Quoted 1 line

So that assumes that any block initially identified 'good' will

ever

Quoted 8 lines
Quoted 1 line

turn 'bad' later on?

We don't currently have any mechanism for that to happen with the environment -- which could be another good reason to have real redundancy that doesn't get crippled from day one by having one copy land on a factory bad block. Of course, that requires someone to implement support for redundant environment combined with CONFIG_ENV_OFFSET_OOB.

Quoted 8 lines
Quoted 5 lines

Well, as long as CONFIG_ENV_OFFSET_REDUND supported falling back to the
other copy in case of error there would be a working system in three of
four cases instead of only one.

I'm not sure what you mean here -- where do "three", "four", and "one" come from?

Just some quantitative approach: given the environment residing at block A and it's redundant copy at block B, four situations may occur: both blocks good, block A bad, block B bad or both blocks bad. Upstream would fail in all cases but both blocks good. My patch would turn that into failing only if both blocks bad. So working in three of four cases instead of in only one of four.

Quoted 25 lines
Quoted 18 lines
Quoted 3 lines

Maybe a better option is to implement support for storing the environment in ubi, although usually if your environment is in NAND that means your U-Boot image is in NAND, so you have the same

problem

Quoted 6 lines

there. Maybe you could have an SPL that contains ubi support, that fits in the guaranteed-good first block.

Do you have any data on how often a block might go bad that wasn't factory-bad, to what extent reads versus writes matter, and whether there is anything special about block zero beyond not being

factory-bad?

No, sadly not. I'd guess this information depends on what hardware being
used specifically. But I suppose block zero being prone to becoming worn just like any other block, although it not being erased as often should help a lot.

Quoted 6 lines

Assuming a certain number of erase cycles after each block is worn out and given the fact that CONFIG_ENV_OFFSET_REDUND has always both blocks
written (unless power failure occurs), they would turn bad at the same time and therefore rendering the environment useless with or without fallback. :)

Quoted 3 lines

That depends on whether the specified number of erase cycles per block is a minimum for any block not marked factory-bad, or whether some fraction of non-factory-bad blocks may fail early.

Sure. Also I'm not sure how "wear" happens, so if blocks get worse or their probability of failure increases from erase to erase. Although the later case would make it hard to guarantee a certain number of erase cycles.

Best wishes, Phil

Signature · 15 lines
Viprinet Europe GmbH
Mainzer Str. 43
55411 Bingen am Rhein
Germany

Phone/Zentrale:               +49 6721 49030-0
Direct line/Durchwahl:        +49 6721 49030-134
Fax:                          +49 6721 49030-109

phil.sutter at viprinet.com
http://www.viprinet.com

Registered office/Sitz der Gesellschaft: Bingen am Rhein, Germany
Commercial register/Handelsregister: Amtsgericht Mainz HRB44090
CEO/Gesch?ftsf?hrer: Simon Kissel
#13 lore raw

PATCH 2/4 env_nand.c: support falling back to redundant env when writing

Quoted 27 lines · Phil Sutter
Quoted 5 lines · Scott Wood
Quoted 4 lines · Phil Sutter
Quoted 1 line · Scott Wood

Erase blocks are larger than write pages, yes. I've never heard

erase

Quoted 1 line

blocks called "pages" or write pages called "blocks" -- but my

main

Quoted 1 line
Quoted 1 line
Quoted 1 line

point is that the unit of erasing and the unit of badness are

the

Quoted 3 lines
Quoted 3 lines

same.

Ah, OK. Please excuse my humble nomenclature, I never cared

enough to

Quoted 1 line
Quoted 1 line

sort out what is called what. Of course, this is not the best

basis

Quoted 4 lines
Quoted 4 lines

for
a discussion about these things.

But getting back to the topic: The assumption of blocks getting

bad,

Quoted 2 lines
Quoted 2 lines

not
pages within a block means that for any kind of bad block

prevention,

Quoted 1 line
Quoted 1 line

multiple blocks need to be used. Although I'm honestly speaking

not

Quoted 1 line
Quoted 1 line

really sure why this needs to be like that. Maybe the bad page

marking

Quoted 8 lines
Quoted 3 lines
Quoted 1 line

would disappear when erasing the block it belongs to?

Yes, it would disappear. This is why erase operations skip bad

blocks,

Quoted 1 line

unless the scrub option is uesd.

Which is apparently preventing good pages in a block with a bad page from being used, isn't it?

Right, plus the knowledge of which pages within the block are bad simply isn't there.

Quoted 6 lines
Quoted 1 line
Quoted 1 line

Interesting. I had the impression of pages being marked bad and

the

Quoted 4 lines
Quoted 3 lines

block's badness being taken from whether it contains bad pages. Probably
the 'nand markbad' command tricked me.

Quoted 7 lines
Quoted 1 line

Do you mean the lack of error checking if you pass a

non-block-aligned

Quoted 1 line

offset into "nand markbad"?

I think the bigger "problem" is 'nand markbad' updating the bad block table along the go. So no real bad block detection occurs as far as I can tell.

I'm not sure what you mean here.

Quoted 12 lines
Quoted 1 line
Quoted 1 line

Well, as long as CONFIG_ENV_OFFSET_REDUND supported falling back

to

Quoted 2 lines
Quoted 2 lines

the
other copy in case of error there would be a working system in

three

Quoted 4 lines
Quoted 2 lines

of
four cases instead of only one.

I'm not sure what you mean here -- where do "three", "four", and

"one"

Quoted 1 line

come from?

Quoted 8 lines

Just some quantitative approach: given the environment residing at block
A and it's redundant copy at block B, four situations may occur: both blocks good, block A bad, block B bad or both blocks bad. Upstream would
fail in all cases but both blocks good. My patch would turn that into failing only if both blocks bad. So working in three of four cases instead of in only one of four.

Those two cases that would suddenly be working would be lacking redundancy -- would you want to ship it like that? If U-Boot is noisy about it, then such units can still have their NAND chips replaced before shipping.

-Scott

#14 lore raw

PATCH 3/4 env_nand.c: do warn only if really no valid environment could be loaded

In reply to Phil Sutter

The warning is misleading, since there is no equivalent success note when reading the other copy succeeds.

---
 common/env_nand.c |    9 +++++----
 1 files changed, 5 insertions(+), 4 deletions(-)
common/env_nand.c +5 -4
index 895532b..58ba558 100644
@@ -315,6 +315,7 @@ int get_nand_env_oob(nand_info_t *nand, unsigned long *result)
 void env_relocate_spec(void)
 {
 #if !defined(ENV_IS_EMBEDDED)
+	int read1_fail = 0, read2_fail = 0;
 	int crc1_ok = 0, crc2_ok = 0;
 	env_t *ep, *tmp_env1, *tmp_env2;
 
@@ -326,11 +327,11 @@ void env_relocate_spec(void)
 		goto done;
 	}
 
-	if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1))
-		puts("No Valid Environment Area found\n");
+	read1_fail = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1);
+	read2_fail = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2);
 
-	if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2))
-		puts("No Valid Redundant Environment Area found\n");
+	if (read1_fail && read2_fail)
+		puts("No Valid Environment Area found\n");
 
 	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
 	crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;
Signature · 1 line
1.7.3.4
#15 lore raw

PATCH 3/4 env_nand.c: do warn only if really no valid environment could be loaded

Quoted 22 lines · Phil Sutter

The warning is misleading, since there is no equivalent success note when reading the other copy succeeds.

---
 common/env_nand.c |    9 +++++----
 1 files changed, 5 insertions(+), 4 deletions(-)
common/env_nand.c +1 -0
index 895532b..58ba558 100644
@@ -315,6 +315,7 @@ int get_nand_env_oob(nand_info_t *nand, unsigned  
 long *result)
 void env_relocate_spec(void)
 {
 #if !defined(ENV_IS_EMBEDDED)
+	int read1_fail = 0, read2_fail = 0;
 	int crc1_ok = 0, crc2_ok = 0;
 	env_t *ep, *tmp_env1, *tmp_env2;

common/env_nand.c void env_relocate_spec(void) · line 332

Quoted 8 lines
common/env_nand.c +2 -2
@@ -326,11 +327,11 @@ void env_relocate_spec(void)
 		goto done;
 	}
 
-	if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1))
-		puts("No Valid Environment Area found\n");
+	read1_fail = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1);
+	read2_fail = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *)  
 tmp_env2);

Why read the redundant environment if the the main one didn't fail?

-Scott

#16 lore raw

PATCH 3/4 env_nand.c: do warn only if really no valid environment could be loaded

Quoted 26 lines · Scott Wood
Quoted 25 lines · Phil Sutter

The warning is misleading, since there is no equivalent success note when reading the other copy succeeds.

---
 common/env_nand.c |    9 +++++----
 1 files changed, 5 insertions(+), 4 deletions(-)
common/env_nand.c +1 -0
index 895532b..58ba558 100644
@@ -315,6 +315,7 @@ int get_nand_env_oob(nand_info_t *nand, unsigned  
 long *result)
 void env_relocate_spec(void)
 {
 #if !defined(ENV_IS_EMBEDDED)
+	int read1_fail = 0, read2_fail = 0;
 	int crc1_ok = 0, crc2_ok = 0;
 	env_t *ep, *tmp_env1, *tmp_env2;
@@ -326,11 +327,11 @@ void env_relocate_spec(void)
 		goto done;
 	}
 
Quoted 7 lines

common/env_nand.c void env_relocate_spec(void)

Quoted 5 lines
common/env_nand.c +2 -2
-	if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1))
-		puts("No Valid Environment Area found\n");
+	read1_fail = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1);
+	read2_fail = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *)  
 tmp_env2);

Why read the redundant environment if the the main one didn't fail?

Never mind, misread the original code (the answer is we want to see which one is more recent).

-Scott

#17 lore raw

PATCH 3/4 env_nand.c: do warn only if really no valid environment could be loaded

In reply to Phil Sutter
Quoted 37 lines · Phil Sutter

The warning is misleading, since there is no equivalent success note when reading the other copy succeeds.

---
 common/env_nand.c |    9 +++++----
 1 files changed, 5 insertions(+), 4 deletions(-)
common/env_nand.c +5 -4
index 895532b..58ba558 100644
@@ -315,6 +315,7 @@ int get_nand_env_oob(nand_info_t *nand, unsigned  
 long *result)
 void env_relocate_spec(void)
 {
 #if !defined(ENV_IS_EMBEDDED)
+	int read1_fail = 0, read2_fail = 0;
 	int crc1_ok = 0, crc2_ok = 0;
 	env_t *ep, *tmp_env1, *tmp_env2;
@@ -326,11 +327,11 @@ void env_relocate_spec(void)
 		goto done;
 	}
 
-	if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1))
-		puts("No Valid Environment Area found\n");
+	read1_fail = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1);
+	read2_fail = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *)  
 tmp_env2);
 
-	if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2))
-		puts("No Valid Redundant Environment Area found\n");
+	if (read1_fail && read2_fail)
+		puts("No Valid Environment Area found\n");
 
 	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
Quoted 1 line

crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;

We should still say something if one of the copies failed to load -- we just need to word it better (see env_flash.c for an example).

-Scott

#18 lore raw

PATCH 4/4 common/env_nand.c: calculate crc only when readenv was OK

In reply to Phil Sutter

Calculating the checksum of incompletely read data is useless.

---
 common/env_nand.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
common/env_nand.c +2 -2
index 58ba558..038aa00 100644
@@ -333,8 +333,8 @@ void env_relocate_spec(void)
 	if (read1_fail && read2_fail)
 		puts("No Valid Environment Area found\n");
 
-	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
-	crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;
+	crc1_ok = !read1_fail && (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
+	crc2_ok = !read2_fail && (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
 
 	if (!crc1_ok && !crc2_ok) {
 		set_default_env("!bad CRC");
Signature · 1 line
1.7.3.4
#19 lore raw

PATCH 1/4 Optimized nand_read_buf for kirkwood

In reply to Phil Sutter
Quoted 9 lines

-----Original Message-----
From: Phil Sutter [mailto:phil.sutter at viprinet.com]
Sent: 21 November 2012 18:29
To: u-boot at lists.denx.de
Cc: Nico Erfurth; Prafulla Wadaskar
Subject: [PATCH 1/4] Optimized nand_read_buf for kirkwood

Quoted 8 lines

The basic idea is taken from the linux-kernel, but further optimized.

First align the buffer to 8 bytes, then use ldrd/strd to read and store
in 8 byte quantities, then do the final bytes.

Cc Prafulla Wadaskar <[email protected]>

Hi Phil,
Have you tested this patch? I don't see your signed-off or tested-by signature in this patch.

Regards...
Prafulla . . .

#20 lore raw

PATCH 1/4 Optimized nand_read_buf for kirkwood

Hello Prafulla,

Quoted 1 line · Prafulla Wadaskar

Have you tested this patch? I don't see your signed-off or tested-by signature in this patch.

Yes, I did. But just for functionality, no real spead measurement or so. That's why I skipped the tested-by line. I just caught up on this, please see the patch I send in reply to this mail.

Best wishes,

Phil Sutter
Software Engineer

Signature · 15 lines
Viprinet Europe GmbH
Mainzer Str. 43
55411 Bingen am Rhein
Germany

Phone/Zentrale:               +49 6721 49030-0
Direct line/Durchwahl:        +49 6721 49030-134
Fax:                          +49 6721 49030-109

phil.sutter at viprinet.com
http://www.viprinet.com

Registered office/Sitz der Gesellschaft: Bingen am Rhein, Germany
Commercial register/Handelsregister: Amtsgericht Mainz HRB44090
CEO/Gesch?ftsf?hrer: Simon Kissel
#21 lore raw

PATCH 1/4 Optimized nand_read_buf for kirkwood

The basic idea is taken from the linux-kernel, but further optimized.

First align the buffer to 8 bytes, then use ldrd/strd to read and store in 8 byte quantities, then do the final bytes.

Tested using: 'date ; nand read.raw 0xE00000 0x0 0x10000 ; date'. Without this patch, NAND read of 132MB took 49s (~2.69MB/s). With this patch in place, reading the same amount of data was done in 27s (~4.89MB/s). So read performance is increased by ~80%!

---
 drivers/mtd/nand/kirkwood_nand.c |   29 +++++++++++++++++++++++++++++
 1 files changed, 29 insertions(+), 0 deletions(-)
drivers/mtd/nand/kirkwood_nand.c +29 -0
index bdab5aa..e04a59f 100644
@@ -38,6 +38,34 @@ struct kwnandf_registers {
 static struct kwnandf_registers *nf_reg =
 	(struct kwnandf_registers *)KW_NANDF_BASE;
 
+
+/* The basic idea is stolen from the linux kernel, but the inner loop is optimized a bit more */
+static void kw_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	while (len && (unsigned long)buf & 7)
+	{
+		*buf++ = readb(chip->IO_ADDR_R);
+		len--;
+	};
+
+	asm volatile (
+		".LFlashLoop:\n"
+		"  subs\t%0, #8\n"
+		"  ldrpld\tr2, [%2]\n" // Read 2 words
+		"  strpld\tr2, [%1], #8\n" // Read 2 words
+		"  bpl\t.LFlashLoop\n" // This results in one additional loop if len%8 <> 0
+		"  addne\t%0, #8\n"
+		: "+&r" (len), "+&r" (buf)
+		: "r" (chip->IO_ADDR_R)
+		: "r2", "r3", "memory", "cc"
+	);
+
+	while (len--)
+		*buf++ = readb(chip->IO_ADDR_R);
+}
+
 /*
  * hardware specific access to control-lines/bits
  */
@@ -76,6 +104,7 @@ int board_nand_init(struct nand_chip *nand)
 	nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING;
 	nand->ecc.mode = NAND_ECC_SOFT;
 	nand->cmd_ctrl = kw_nand_hwcontrol;
+	nand->read_buf = kw_nand_read_buf;
 	nand->chip_delay = 40;
 	nand->select_chip = kw_nand_select_chip;
 	return 0;
Signature · 1 line
1.7.3.4
#22 lore raw

PATCH 1/4 Optimized nand_read_buf for kirkwood

Quoted 32 lines · Phil Sutter

The basic idea is taken from the linux-kernel, but further optimized.

First align the buffer to 8 bytes, then use ldrd/strd to read and store
in 8 byte quantities, then do the final bytes.

Tested using: 'date ; nand read.raw 0xE00000 0x0 0x10000 ; date'. Without this patch, NAND read of 132MB took 49s (~2.69MB/s). With this patch in place, reading the same amount of data was done in 27s (~4.89MB/s). So read performance is increased by ~80%!

---
 drivers/mtd/nand/kirkwood_nand.c |   29 +++++++++++++++++++++++++++++
 1 files changed, 29 insertions(+), 0 deletions(-)
drivers/mtd/nand/kirkwood_nand.c +2 -0
index bdab5aa..e04a59f 100644
@@ -38,6 +38,34 @@ struct kwnandf_registers {
 static struct kwnandf_registers *nf_reg =
 	(struct kwnandf_registers *)KW_NANDF_BASE;
 
+
+/* The basic idea is stolen from the linux kernel, but the inner  

loop is optimized a bit more */
+static void kw_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)

drivers/mtd/nand/kirkwood_nand.c struct kwnandf_registers { · line 48

Quoted 5 lines
drivers/mtd/nand/kirkwood_nand.c +5 -0
+{
+	struct nand_chip *chip = mtd->priv;
+
+	while (len && (unsigned long)buf & 7)
+	{

Brace goes on the previous line.

Quoted 11 lines
drivers/mtd/nand/kirkwood_nand.c +10 -0
+		*buf++ = readb(chip->IO_ADDR_R);
+		len--;
+	};
+
+	asm volatile (
+		".LFlashLoop:\n"
+		"  subs\t%0, #8\n"
+		"  ldrpld\tr2, [%2]\n" // Read 2 words
+		"  strpld\tr2, [%1], #8\n" // Read 2 words
+		"  bpl\t.LFlashLoop\n" // This results in one  

additional loop if len%8 <> 0

drivers/mtd/nand/kirkwood_nand.c struct kwnandf_registers { · line 63

Quoted 5 lines
drivers/mtd/nand/kirkwood_nand.c +5 -0
+		"  addne\t%0, #8\n"
+		: "+&r" (len), "+&r" (buf)
+		: "r" (chip->IO_ADDR_R)
+		: "r2", "r3", "memory", "cc"
+	);

Use a real tab (or a space) rather than \t (which only helps readability in the asm output, rather than the C source that people actually look at).

Should probably use a numeric label to avoid any possibility of conflict.

Would this make more sense as a more generic optimized memcpy_fromio() or similar?

-Scott

#23 lore raw

PATCH 1/4 Optimized nand_read_buf for kirkwood

Quoted 76 lines

-----Original Message-----
From: u-boot-bounces at lists.denx.de [mailto:u-boot- bounces at lists.denx.de] On Behalf Of Scott Wood
Sent: 27 November 2012 05:09
To: Phil Sutter
Cc: u-boot at lists.denx.de; Nico Erfurth
Subject: Re: [U-Boot] [PATCH 1/4] Optimized nand_read_buf for kirkwood

Quoted 1 line · Phil Sutter

The basic idea is taken from the linux-kernel, but further

optimized.

Quoted 7 lines

First align the buffer to 8 bytes, then use ldrd/strd to read and store
in 8 byte quantities, then do the final bytes.

Tested using: 'date ; nand read.raw 0xE00000 0x0 0x10000 ; date'. Without this patch, NAND read of 132MB took 49s (~2.69MB/s). With

this

Quoted 8 lines

patch in place, reading the same amount of data was done in 27s (~4.89MB/s). So read performance is increased by ~80%!

---
 drivers/mtd/nand/kirkwood_nand.c |   29

+++++++++++++++++++++++++++++

Quoted 13 lines
 1 files changed, 29 insertions(+), 0 deletions(-)
drivers/mtd/nand/kirkwood_nand.c +2 -0
index bdab5aa..e04a59f 100644
@@ -38,6 +38,34 @@ struct kwnandf_registers {
 static struct kwnandf_registers *nf_reg =
 	(struct kwnandf_registers *)KW_NANDF_BASE;
 
+
+/* The basic idea is stolen from the linux kernel, but the inner
Quoted 2 lines

loop is optimized a bit more */
+static void kw_nand_read_buf(struct mtd_info *mtd, uint8_t *buf,

int

drivers/mtd/nand/kirkwood_nand.c struct kwnandf_registers { · line 48

Quoted 6 lines

len)

drivers/mtd/nand/kirkwood_nand.c +5 -0
+{
+	struct nand_chip *chip = mtd->priv;
+
+	while (len && (unsigned long)buf & 7)
+	{

Brace goes on the previous line.

Quoted 11 lines
drivers/mtd/nand/kirkwood_nand.c +10 -0
+		*buf++ = readb(chip->IO_ADDR_R);
+		len--;
+	};
+
+	asm volatile (
+		".LFlashLoop:\n"
+		"  subs\t%0, #8\n"
+		"  ldrpld\tr2, [%2]\n" // Read 2 words
+		"  strpld\tr2, [%1], #8\n" // Read 2 words
+		"  bpl\t.LFlashLoop\n" // This results in one

additional loop if len%8 <> 0

drivers/mtd/nand/kirkwood_nand.c struct kwnandf_registers { · line 63

Quoted 5 lines
drivers/mtd/nand/kirkwood_nand.c +5 -0
+		"  addne\t%0, #8\n"
+		: "+&r" (len), "+&r" (buf)
+		: "r" (chip->IO_ADDR_R)
+		: "r2", "r3", "memory", "cc"
+	);

Use a real tab (or a space) rather than \t (which only helps readability in the asm output, rather than the C source that people actually look at).

Quoted 5 lines

Should probably use a numeric label to avoid any possibility of conflict.

Would this make more sense as a more generic optimized memcpy_fromio() or similar?

Hi Phil

For your next post of this patch, please do not forget to add version info and changlog to the patch.

Regards...
Prafulla . . .

Quoted 6 lines

-Scott
_______________________________________________
U-Boot mailing list
U-Boot at lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

#24 lore raw

PATCH 1/4 Optimized nand_read_buf for kirkwood

Hi Prafulla,

Quoted 1 line · Prafulla Wadaskar

For your next post of this patch, please do not forget to add version info and changlog to the patch.

Ah yes, indeed! Thanks a lot for the hint and sorry for the confusion caused.

Best wishes, Phil

Signature · 15 lines
Viprinet Europe GmbH
Mainzer Str. 43
55411 Bingen am Rhein
Germany

Phone/Zentrale:               +49 6721 49030-0
Direct line/Durchwahl:        +49 6721 49030-134
Fax:                          +49 6721 49030-109

phil.sutter at viprinet.com
http://www.viprinet.com

Registered office/Sitz der Gesellschaft: Bingen am Rhein, Germany
Commercial register/Handelsregister: Amtsgericht Mainz HRB44090
CEO/Gesch?ftsf?hrer: Simon Kissel
#25 lore raw

[U-Boot] Version 2 of Kirkwood and env_nand improvements

In reply to Phil Sutter

Respin of my patch series for review. Only changes in patches 2 and 3:

env_nand.c: support falling back to redundant env when writing:
Print status message for each of the redundant copies when writing. So when writing, we see which image is being written to. Also, when the first attempt fails, a second message follows so the user sees that writing was eventually successful.

env_nand.c: clarify log messages when env reading fails:
Print a warning if one of the two redundant copies could not be read and an error if both failed to read. Message text and formatting chosen with common/env_flash.c as example.

#26 lore raw

PATCH v2 1/4 Optimized nand_read_buf for kirkwood (V3)

The basic idea is taken from the linux-kernel, but further optimized.

First align the buffer to 8 bytes, then use ldrd/strd to read and store in 8 byte quantities, then do the final bytes.

Tested using: 'date ; nand read.raw 0xE00000 0x0 0x10000 ; date'. Without this patch, NAND read of 132MB took 49s (~2.69MB/s). With this patch in place, reading the same amount of data was done in 27s (~4.89MB/s). So read performance is increased by ~80%!

---
 drivers/mtd/nand/kirkwood_nand.c |   32 ++++++++++++++++++++++++++++++++
 1 files changed, 32 insertions(+), 0 deletions(-)
drivers/mtd/nand/kirkwood_nand.c +32 -0
index bdab5aa..99e5f35 100644
@@ -38,6 +38,37 @@ struct kwnandf_registers {
 static struct kwnandf_registers *nf_reg =
 	(struct kwnandf_registers *)KW_NANDF_BASE;
 
+
+/*
+ * The basic idea is stolen from the linux kernel, but the inner loop is
+ * optimized a bit more.
+ */
+static void kw_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	while (len && (unsigned long)buf & 7) {
+		*buf++ = readb(chip->IO_ADDR_R);
+		len--;
+	};
+
+	/* This loop reads and writes 64bit per round. */
+	asm volatile (
+		"1:\n"
+		"  subs   %0, #8\n"
+		"  ldrpld r2, [%2]\n"
+		"  strpld r2, [%1], #8\n"
+		"  bhi    1b\n"
+		"  addne  %0, #8\n"
+		: "+&r" (len), "+&r" (buf)
+		: "r" (chip->IO_ADDR_R)
+		: "r2", "r3", "memory", "cc"
+	);
+
+	while (len--)
+		*buf++ = readb(chip->IO_ADDR_R);
+}
+
 /*
  * hardware specific access to control-lines/bits
  */
@@ -76,6 +107,7 @@ int board_nand_init(struct nand_chip *nand)
 	nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING;
 	nand->ecc.mode = NAND_ECC_SOFT;
 	nand->cmd_ctrl = kw_nand_hwcontrol;
+	nand->read_buf = kw_nand_read_buf;
 	nand->chip_delay = 40;
 	nand->select_chip = kw_nand_select_chip;
 	return 0;
Signature · 1 line
1.7.3.4
#27 lore raw

PATCH v2 1/4 Optimized nand_read_buf for kirkwood (V3)

Quoted 10 lines · Phil Sutter

The basic idea is taken from the linux-kernel, but further optimized.

First align the buffer to 8 bytes, then use ldrd/strd to read and store in 8 byte quantities, then do the final bytes.

Tested using: 'date ; nand read.raw 0xE00000 0x0 0x10000 ; date'. Without this patch, NAND read of 132MB took 49s (~2.69MB/s). With this patch in place, reading the same amount of data was done in 27s (~4.89MB/s). So read performance is increased by ~80%!

Quoted 6 lines
---
 drivers/mtd/nand/kirkwood_nand.c |   32 ++++++++++++++++++++++++++++++++
 1 files changed, 32 insertions(+), 0 deletions(-)

Which is it, v2 or v3?

Patch versioning goes inside the [], otherwise "git am" won't strip it out of the patch subject (and Wolfgang doesn't like patch subjects being edited while being applied).

Quoted 28 lines
drivers/mtd/nand/kirkwood_nand.c +20 -0
index bdab5aa..99e5f35 100644
@@ -38,6 +38,37 @@ struct kwnandf_registers {
 static struct kwnandf_registers *nf_reg =
 	(struct kwnandf_registers *)KW_NANDF_BASE;
 
+
+/*
+ * The basic idea is stolen from the linux kernel, but the inner loop is
+ * optimized a bit more.
+ */
+static void kw_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	while (len && (unsigned long)buf & 7) {
+		*buf++ = readb(chip->IO_ADDR_R);
+		len--;
+	};
+
+	/* This loop reads and writes 64bit per round. */
+	asm volatile (
+		"1:\n"
+		"  subs   %0, #8\n"
+		"  ldrpld r2, [%2]\n"
+		"  strpld r2, [%1], #8\n"

drivers/mtd/nand/kirkwood_nand.c struct kwnandf_registers { · line 70

Quoted 10 lines
drivers/mtd/nand/kirkwood_nand.c +10 -0
+		"  bhi    1b\n"
+		"  addne  %0, #8\n"
+		: "+&r" (len), "+&r" (buf)
+		: "r" (chip->IO_ADDR_R)
+		: "r2", "r3", "memory", "cc"
+	);
+
+	while (len--)
+		*buf++ = readb(chip->IO_ADDR_R);
+}

Can someone ACK this from the ARM side?

-Scott

#28 lore raw

PATCH v2 2/4 env_nand.c: support falling back to redundant env when writing

In reply to Phil Sutter

Without this patch, when the currently chosen environment to be written has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment reading needs no adjustment, as the fallback logic for incomplete writes applies to this case as well.

---
 common/env_nand.c |  103 ++++++++++++++++++++++------------------------------
 1 files changed, 44 insertions(+), 59 deletions(-)
common/env_nand.c +44 -59
index 22e72a2..c0c985c 100644
@@ -168,72 +168,55 @@ int writeenv(size_t offset, u_char *buf)
 	return 0;
 }
 
-#ifdef CONFIG_ENV_OFFSET_REDUND
-static unsigned char env_flags;
+typedef struct env_location {
+	const char *name;
+	nand_erase_options_t *erase_opts;
+	loff_t offset;
+} env_location_t;
 
-int saveenv(void)
+static int erase_and_write_env(env_location_t *location, u_char *env_new)
 {
-	env_t	env_new;
-	ssize_t	len;
-	char	*res;
-	int	ret = 0;
-	nand_erase_options_t nand_erase_options;
-
-	memset(&nand_erase_options, 0, sizeof(nand_erase_options));
-	nand_erase_options.length = CONFIG_ENV_RANGE;
+	int ret = 0;
 
-	if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
+	printf("Erasing %s...\n", location->name);
+	location->erase_opts->offset = location->offset;
+	if (nand_erase_opts(&nand_info[0], location->erase_opts))
 		return 1;
 
-	res = (char *)&env_new.data;
-	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
-	if (len < 0) {
-		error("Cannot export environment: errno = %d\n", errno);
-		return 1;
-	}
-	env_new.crc	= crc32(0, env_new.data, ENV_SIZE);
-	env_new.flags	= ++env_flags; /* increase the serial */
-
-	if (gd->env_valid == 1) {
-		puts("Erasing redundant NAND...\n");
-		nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND;
-		if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-			return 1;
-
-		puts("Writing to redundant NAND... ");
-		ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *)&env_new);
-	} else {
-		puts("Erasing NAND...\n");
-		nand_erase_options.offset = CONFIG_ENV_OFFSET;
-		if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-			return 1;
-
-		puts("Writing to NAND... ");
-		ret = writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new);
-	}
-	if (ret) {
+	printf("Writing to %s... ", location->name);
+	if ((ret = writeenv(location->offset, env_new)))
 		puts("FAILED!\n");
-		return 1;
-	}
-
-	puts("done\n");
-
-	gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+	else
+		puts("OK\n");
 
 	return ret;
 }
-#else /* ! CONFIG_ENV_OFFSET_REDUND */
+
+static unsigned char env_flags;
+
 int saveenv(void)
 {
 	int	ret = 0;
 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
 	ssize_t	len;
 	char	*res;
+	int	env_idx;
 	nand_erase_options_t nand_erase_options;
+	env_location_t location[] = {{
+		.name = "NAND",
+		.erase_opts = &nand_erase_options,
+		.offset = CONFIG_ENV_OFFSET,
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	}, {
+		.name = "redundant NAND",
+		.erase_opts = &nand_erase_options,
+		.offset = CONFIG_ENV_OFFSET_REDUND,
+#endif
+	}};
+
 
 	memset(&nand_erase_options, 0, sizeof(nand_erase_options));
 	nand_erase_options.length = CONFIG_ENV_RANGE;
-	nand_erase_options.offset = CONFIG_ENV_OFFSET;
 
 	if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
 		return 1;
@@ -244,22 +227,24 @@ int saveenv(void)
 		error("Cannot export environment: errno = %d\n", errno);
 		return 1;
 	}
-	env_new->crc = crc32(0, env_new->data, ENV_SIZE);
-
-	puts("Erasing Nand...\n");
-	if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-		return 1;
+	env_new->crc   = crc32(0, env_new->data, ENV_SIZE);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	env_new->flags = ++env_flags; /* increase the serial */
+	env_idx = (gd->env_valid == 1);
+#endif
 
-	puts("Writing to Nand... ");
-	if (writeenv(CONFIG_ENV_OFFSET, (u_char *)env_new)) {
-		puts("FAILED!\n");
-		return 1;
-	}
+	ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	if (ret) {
+		env_idx = (env_idx + 1) & 1;
+		ret = erase_and_write_env(&location[env_idx],
+				(u_char *)env_new);
+	} else
+		gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+#endif
 
-	puts("done\n");
 	return ret;
 }
-#endif /* CONFIG_ENV_OFFSET_REDUND */
 #endif /* CMD_SAVEENV */
 
 int readenv(size_t offset, u_char *buf)
Signature · 1 line
1.7.3.4
#29 lore raw

PATCH v2 2/4 env_nand.c: support falling back to redundant env when writing

Quoted 16 lines · Phil Sutter

Without this patch, when the currently chosen environment to be written has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment reading needs no adjustment, as the fallback logic for incomplete writes applies to this case as well.

---
 common/env_nand.c |  103 ++++++++++++++++++++++------------------------------
 1 files changed, 44 insertions(+), 59 deletions(-)
common/env_nand.c +0 -0
index 22e72a2..c0c985c 100644

common/env_nand.c int writeenv(size_t offset, u_char *buf) · line 175

Quoted 10 lines
common/env_nand.c +5 -2
@@ -168,72 +168,55 @@ int writeenv(size_t offset, u_char *buf)
 	return 0;
 }
 
-#ifdef CONFIG_ENV_OFFSET_REDUND
-static unsigned char env_flags;
+typedef struct env_location {
+	const char *name;
+	nand_erase_options_t *erase_opts;
+	loff_t offset;
+} env_location_t;

WARNING: do not add new typedefs
#286: FILE: common/env_nand.c:171:
+typedef struct env_location {

common/env_nand.c int writeenv(size_t offset, u_char *buf) · line 217

Quoted 5 lines
common/env_nand.c +2 -2
+	printf("Writing to %s... ", location->name);
+	if ((ret = writeenv(location->offset, env_new)))
 		puts("FAILED!\n");
-		return 1;
-	}

ERROR: do not use assignment in if condition
#339: FILE: common/env_nand.c:187:
+ if ((ret = writeenv(location->offset, env_new)))

Quoted 23 lines
common/env_nand.c +8 -5
-
-	puts("done\n");
-
-	gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+	else
+		puts("OK\n");
 
 	return ret;
 }
-#else /* ! CONFIG_ENV_OFFSET_REDUND */
+
+static unsigned char env_flags;
+
 int saveenv(void)
 {
 	int	ret = 0;
 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
 	ssize_t	len;
 	char	*res;
+	int	env_idx;
 	nand_erase_options_t nand_erase_options;
+	env_location_t location[] = {{
+		.name = "NAND",

common/env_nand.c int writeenv(size_t offset, u_char *buf) · line 216

Quoted 10 lines
common/env_nand.c +10 -0
+		.erase_opts = &nand_erase_options,
+		.offset = CONFIG_ENV_OFFSET,
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	}, {
+		.name = "redundant NAND",
+		.erase_opts = &nand_erase_options,
+		.offset = CONFIG_ENV_OFFSET_REDUND,
+#endif
+	}};
+

ERROR: space required after that close brace '}'
#374: FILE: common/env_nand.c:215:
+ }};

...or rather, it should be like this:

	static const struct env_location location[] = {
		{
			.name = "NAND";
			...
		},
#ifdef CONFIG_ENV_OFFSET_REDUND
		{
			.name = "redundant NAND",
			...
		},
#endif
	};

Note that without the "static" GCC will emit code to dynamically initialize the array, which will be larger.

common/env_nand.c int saveenv(void) · line 243

Quoted 7 lines
common/env_nand.c +7 -0
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	if (ret) {
+		env_idx = (env_idx + 1) & 1;
+		ret = erase_and_write_env(&location[env_idx],
+				(u_char *)env_new);
+	} else
+		gd->env_valid = gd->env_valid == 2 ? 1 : 2;

Braces around both halves of the if/else if one side has them.

-Scott

#30 lore raw

PATCH v2 3/4 env_nand.c: clarify log messages when env reading fails

In reply to Phil Sutter

The single message is misleading, since there is no equivalent success note when reading the other copy succeeds. Instead, warn if one of the redundant copies could not be loaded and emphasise on the error when reading both fails.

---
 common/env_nand.c |   12 ++++++++----
 1 files changed, 8 insertions(+), 4 deletions(-)
common/env_nand.c +8 -4
index c0c985c..60a87ec 100644
@@ -316,6 +316,7 @@ int get_nand_env_oob(nand_info_t *nand, unsigned long *result)
 void env_relocate_spec(void)
 {
 #if !defined(ENV_IS_EMBEDDED)
+	int read1_fail = 0, read2_fail = 0;
 	int crc1_ok = 0, crc2_ok = 0;
 	env_t *ep, *tmp_env1, *tmp_env2;
 
@@ -327,11 +328,14 @@ void env_relocate_spec(void)
 		goto done;
 	}
 
-	if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1))
-		puts("No Valid Environment Area found\n");
+	read1_fail = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1);
+	read2_fail = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2);
 
-	if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2))
-		puts("No Valid Redundant Environment Area found\n");
+	if (read1_fail && read2_fail)
+		puts("*** Error - No Valid Environment Area found\n");
+	else if (read1_fail || read2_fail)
+		puts("*** Warning - some problems detected "
+		     "reading environment; recovered successfully\n");
 
 	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
 	crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;
Signature · 1 line
1.7.3.4
#31 lore raw

PATCH v2 3/4 env_nand.c: clarify log messages when env reading fails

Quoted 9 lines · Phil Sutter

The single message is misleading, since there is no equivalent success note when reading the other copy succeeds. Instead, warn if one of the redundant copies could not be loaded and emphasise on the error when reading both fails.

---
 common/env_nand.c |   12 ++++++++----
 1 files changed, 8 insertions(+), 4 deletions(-)

Applied to u-boot-nand-flash.

common/env_nand.c void env_relocate_spec(void) · line 341

Quoted 10 lines
common/env_nand.c +5 -2
-	if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2))
-		puts("No Valid Redundant Environment Area found\n");
+	if (read1_fail && read2_fail)
+		puts("*** Error - No Valid Environment Area found\n");
+	else if (read1_fail || read2_fail)
+		puts("*** Warning - some problems detected "
+		     "reading environment; recovered successfully\n");
 
 	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
 	crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;

We should also give a message if one of the CRCs is bad, though that's an existing problem.

-Scott

#32 lore raw

PATCH v2 3/4 env_nand.c: clarify log messages when env reading fails

Scott,

Quoted 21 lines · Scott Wood
Quoted 9 lines · Phil Sutter

The single message is misleading, since there is no equivalent success note when reading the other copy succeeds. Instead, warn if one of the redundant copies could not be loaded and emphasise on the error when reading both fails.

---
 common/env_nand.c |   12 ++++++++----
 1 files changed, 8 insertions(+), 4 deletions(-)

Applied to u-boot-nand-flash.

Quoted 8 lines
common/env_nand.c +5 -2
-	if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2))
-		puts("No Valid Redundant Environment Area found\n");
+	if (read1_fail && read2_fail)
+		puts("*** Error - No Valid Environment Area found\n");
+	else if (read1_fail || read2_fail)
+		puts("*** Warning - some problems detected "
+		     "reading environment; recovered successfully\n");
Quoted 5 lines

common/env_nand.c void env_relocate_spec(void) · line 341

Quoted 2 lines
common/env_nand.c +0 -0
 	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
 	crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;

We should also give a message if one of the CRCs is bad, though that's an existing problem.

Yes, that would be nice. While writing this, I also had the idea of introducing some macros for unified message output, like so:

| #define __print(level, ...) {
|	printf("*** %s - ", level);
|	printf(__VA_ARGS__);
|	printf("\n");
| }
| #define perror(...) __print("Error", __VA_ARGS__)
| #define pwarn(...) __print("Warning", __VA_ARGS__)
| ...

What do you think? That would require diligently touching a lot of source files, of course.

Best wishes,

Phil Sutter
Software Engineer

Signature · 33 lines
Viprinet - Never be offline again!

************************************************
Viprinet auf der CeBIT 2013 - 5.-9. M?rz:
Besuchen Sie uns in Halle 13, Stand D27!
Alle gezeigten Produkte im Livebetrieb,
zahlreiche Beispielapplikationen. Gerne
schicken wir Ihnen kostenlose Eintrittskarten.
http://www.viprinet.com/de/cebit-2013
************************************************
Viprinet at CeBIT 2013, March 5 to 9
in Hannover, Germany. Come and visit us
at Hall 13, Booth D27! All exhibits shown
live, many sample applications. We?ll be
happy to send you free admission tickets.
http://www.viprinet.com/en/cebit-2013
************************************************

Viprinet Europe GmbH
Mainzer Str. 43
55411 Bingen am Rhein
Germany

Phone/Zentrale:               +49 6721 49030-0
Direct line/Durchwahl:        +49 6721 49030-134
Fax:                          +49 6721 49030-109

phil.sutter at viprinet.com
http://www.viprinet.com

Registered office/Sitz der Gesellschaft: Bingen am Rhein, Germany
Commercial register/Handelsregister: Amtsgericht Mainz HRB44090
CEO/Gesch?ftsf?hrer: Simon Kissel
#33 lore raw

PATCH v2 3/4 env_nand.c: clarify log messages when env reading fails

Quoted 19 lines · Phil Sutter

Scott,

Quoted 1 line · Scott Wood

We should also give a message if one of the CRCs is bad, though

that's

Quoted 1 line

an existing problem.

Yes, that would be nice. While writing this, I also had the idea of introducing some macros for unified message output, like so:

| #define __print(level, ...) {
|	printf("*** %s - ", level);
|	printf(__VA_ARGS__);
|	printf("\n");
| }
| #define perror(...) __print("Error", __VA_ARGS__)
| #define pwarn(...) __print("Warning", __VA_ARGS__)
| ...
Quoted 2 lines

What do you think? That would require diligently touching a lot of source files, of course.

This suggestion belongs in its own thread with an appropriate subject, but if we do anything like this we should use pr_warn(), pr_err(), etc. since we share a bunch of code with Linux -- although output from that code may be made worse since it's not expecting the prefixes...

-Scott

#34 lore raw

PATCH v2 4/4 common/env_nand.c: calculate crc only when readenv was OK

In reply to Phil Sutter

Calculating the checksum of incompletely read data is useless.

---
 common/env_nand.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
common/env_nand.c +2 -2
index 60a87ec..0e1d17a 100644
@@ -337,8 +337,8 @@ void env_relocate_spec(void)
 		puts("*** Warning - some problems detected "
 		     "reading environment; recovered successfully\n");
 
-	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
-	crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;
+	crc1_ok = !read1_fail && (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
+	crc2_ok = !read2_fail && (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
 
 	if (!crc1_ok && !crc2_ok) {
 		set_default_env("!bad CRC");
Signature · 1 line
1.7.3.4
#35 lore raw

PATCH v2 4/4 common/env_nand.c: calculate crc only when readenv was OK

Quoted 12 lines · Phil Sutter

Calculating the checksum of incompletely read data is useless.

---
 common/env_nand.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
common/env_nand.c +0 -0
index 60a87ec..0e1d17a 100644

common/env_nand.c void env_relocate_spec(void) · line 341

Quoted 7 lines
common/env_nand.c +2 -2
@@ -337,8 +337,8 @@ void env_relocate_spec(void)
 		puts("*** Warning - some problems detected "
 		     "reading environment; recovered successfully\n");
 
-	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
-	crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;
+	crc1_ok = !read1_fail && (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
+	crc2_ok = !read2_fail && (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);

Lines over 80 columns -- fixed and applied to u-boot-nand-flash.

-Scott

#36 lore raw

[U-Boot] Version 3 of Kirkwood and env_nand improvements

In reply to Phil Sutter

Respin of the remaining two of my patch series for review. First patch is identical but removed the versioning from the title as suggested. Second patch cleaned up so checkpatch.pl does not complain anymore. Kudos to Scott for pointing out the warnings and his further comments.

Greetings, Phil

#37 lore raw

PATCH v3 1/2 Optimized nand_read_buf for kirkwood

The basic idea is taken from the linux-kernel, but further optimized.

First align the buffer to 8 bytes, then use ldrd/strd to read and store in 8 byte quantities, then do the final bytes.

Tested using: 'date ; nand read.raw 0xE00000 0x0 0x10000 ; date'. Without this patch, NAND read of 132MB took 49s (~2.69MB/s). With this patch in place, reading the same amount of data was done in 27s (~4.89MB/s). So read performance is increased by ~80%!

---
 drivers/mtd/nand/kirkwood_nand.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)
drivers/mtd/nand/kirkwood_nand.c +32 -0
index 0a99a10..85ea5d2 100644
@@ -38,6 +38,37 @@ struct kwnandf_registers {
 static struct kwnandf_registers *nf_reg =
 	(struct kwnandf_registers *)KW_NANDF_BASE;
 
+
+/*
+ * The basic idea is stolen from the linux kernel, but the inner loop is
+ * optimized a bit more.
+ */
+static void kw_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	while (len && (unsigned long)buf & 7) {
+		*buf++ = readb(chip->IO_ADDR_R);
+		len--;
+	};
+
+	/* This loop reads and writes 64bit per round. */
+	asm volatile (
+		"1:\n"
+		"  subs   %0, #8\n"
+		"  ldrpld r2, [%2]\n"
+		"  strpld r2, [%1], #8\n"
+		"  bhi    1b\n"
+		"  addne  %0, #8\n"
+		: "+&r" (len), "+&r" (buf)
+		: "r" (chip->IO_ADDR_R)
+		: "r2", "r3", "memory", "cc"
+	);
+
+	while (len--)
+		*buf++ = readb(chip->IO_ADDR_R);
+}
+
 /*
  * hardware specific access to control-lines/bits
  */
@@ -80,6 +111,7 @@ int board_nand_init(struct nand_chip *nand)
 	nand->ecc.mode = NAND_ECC_SOFT;
 #endif
 	nand->cmd_ctrl = kw_nand_hwcontrol;
+	nand->read_buf = kw_nand_read_buf;
 	nand->chip_delay = 40;
 	nand->select_chip = kw_nand_select_chip;
 	return 0;
Signature · 1 line
1.8.1.5
#38 lore raw

PATCH v3 1/2 Optimized nand_read_buf for kirkwood

Hi Phil,

Quoted 10 lines · Phil Sutter

The basic idea is taken from the linux-kernel, but further optimized.

First align the buffer to 8 bytes, then use ldrd/strd to read and store in 8 byte quantities, then do the final bytes.

Tested using: 'date ; nand read.raw 0xE00000 0x0 0x10000 ; date'. Without this patch, NAND read of 132MB took 49s (~2.69MB/s). With this patch in place, reading the same amount of data was done in 27s (~4.89MB/s). So read performance is increased by ~80%!

Quoted 4 lines

---

Patch history missing.

Quoted 27 lines
 drivers/mtd/nand/kirkwood_nand.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)
drivers/mtd/nand/kirkwood_nand.c +16 -0
index 0a99a10..85ea5d2 100644
@@ -38,6 +38,37 @@ struct kwnandf_registers {
 static struct kwnandf_registers *nf_reg =
 	(struct kwnandf_registers *)KW_NANDF_BASE;
 
+
+/*
+ * The basic idea is stolen from the linux kernel, but the inner loop is
+ * optimized a bit more.
+ */
+static void kw_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	while (len && (unsigned long)buf & 7) {
+		*buf++ = readb(chip->IO_ADDR_R);
+		len--;
+	};
+
+	/* This loop reads and writes 64bit per round. */
+	asm volatile (

drivers/mtd/nand/kirkwood_nand.c struct kwnandf_registers { · line 66

Quoted 10 lines
drivers/mtd/nand/kirkwood_nand.c +10 -0
+		"1:\n"
+		"  subs   %0, #8\n"
+		"  ldrpld r2, [%2]\n"
+		"  strpld r2, [%1], #8\n"
+		"  bhi    1b\n"
+		"  addne  %0, #8\n"
+		: "+&r" (len), "+&r" (buf)
+		: "r" (chip->IO_ADDR_R)
+		: "r2", "r3", "memory", "cc"
+	);

Are assembler instructions *really* required? IOW, can you not get enough performance simply with a cleverly written C loop?

Quoted 8 lines
drivers/mtd/nand/kirkwood_nand.c +4 -0
+	while (len--)
+		*buf++ = readb(chip->IO_ADDR_R);
+}
+
 /*
  * hardware specific access to control-lines/bits
  */

drivers/mtd/nand/kirkwood_nand.c int board_nand_init(struct nand_chip *nand) · line 117

Quoted 7 lines
drivers/mtd/nand/kirkwood_nand.c +1 -0
@@ -80,6 +111,7 @@ int board_nand_init(struct nand_chip *nand)
 	nand->ecc.mode = NAND_ECC_SOFT;
 #endif
 	nand->cmd_ctrl = kw_nand_hwcontrol;
+	nand->read_buf = kw_nand_read_buf;
 	nand->chip_delay = 40;
 	nand->select_chip = kw_nand_select_chip;
 	return 0;

Amicalement,

Signature · 1 line
Albert.
#39 lore raw

PATCH v3 1/2 Optimized nand_read_buf for kirkwood

In reply to Phil Sutter
Quoted 5 lines · Phil Sutter

The basic idea is taken from the linux-kernel, but further optimized.

First align the buffer to 8 bytes, then use ldrd/strd to read and store in 8 byte quantities, then do the final bytes.

Quoted 8 lines

Tested using: 'date ; nand read.raw 0xE00000 0x0 0x10000 ; date'. Without this patch, NAND read of 132MB took 49s (~2.69MB/s). With this patch in place, reading the same amount of data was done in 27s (~4.89MB/s). So read performance is increased by ~80%!

Missing your signoff, and if Nico was the main author then there should be a From: line indicating that.

-Scott

#40 lore raw

PATCH v3 2/2 env_nand.c: support falling back to redundant env when writing

In reply to Phil Sutter

Without this patch, when the currently chosen environment to be written has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment reading needs no adjustment, as the fallback logic for incomplete writes applies to this case as well.

---
 common/env_nand.c | 105 ++++++++++++++++++++++++------------------------------
 1 file changed, 46 insertions(+), 59 deletions(-)
common/env_nand.c +46 -59
index b745822..06af5ce 100644
@@ -168,72 +168,56 @@ int writeenv(size_t offset, u_char *buf)
 	return 0;
 }
 
-#ifdef CONFIG_ENV_OFFSET_REDUND
-static unsigned char env_flags;
+struct env_location {
+	const char *name;
+	nand_erase_options_t *erase_opts;
+	loff_t offset;
+};
 
-int saveenv(void)
+static int erase_and_write_env(struct env_location *location, u_char *env_new)
 {
-	env_t	env_new;
-	ssize_t	len;
-	char	*res;
-	int	ret = 0;
-	nand_erase_options_t nand_erase_options;
-
-	memset(&nand_erase_options, 0, sizeof(nand_erase_options));
-	nand_erase_options.length = CONFIG_ENV_RANGE;
-
-	if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
-		return 1;
+	int ret = 0;
 
-	res = (char *)&env_new.data;
-	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
-	if (len < 0) {
-		error("Cannot export environment: errno = %d\n", errno);
+	printf("Erasing %s...\n", location->name);
+	location->erase_opts->offset = location->offset;
+	if (nand_erase_opts(&nand_info[0], location->erase_opts))
 		return 1;
-	}
-	env_new.crc	= crc32(0, env_new.data, ENV_SIZE);
-	env_new.flags	= ++env_flags; /* increase the serial */
 
-	if (gd->env_valid == 1) {
-		puts("Erasing redundant NAND...\n");
-		nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND;
-		if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-			return 1;
-
-		puts("Writing to redundant NAND... ");
-		ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *)&env_new);
-	} else {
-		puts("Erasing NAND...\n");
-		nand_erase_options.offset = CONFIG_ENV_OFFSET;
-		if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-			return 1;
-
-		puts("Writing to NAND... ");
-		ret = writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new);
-	}
-	if (ret) {
-		puts("FAILED!\n");
-		return 1;
-	}
-
-	puts("done\n");
-
-	gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+	printf("Writing to %s... ", location->name);
+	ret = writeenv(location->offset, env_new);
+	puts(ret ? "FAILED!\n" : "OK\n");
 
 	return ret;
 }
-#else /* ! CONFIG_ENV_OFFSET_REDUND */
+
+static unsigned char env_flags;
+
 int saveenv(void)
 {
 	int	ret = 0;
 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
 	ssize_t	len;
 	char	*res;
+	int	env_idx;
 	nand_erase_options_t nand_erase_options;
+	static const struct env_location location[] = {
+		{
+			.name = "NAND",
+			.erase_opts = &nand_erase_options,
+			.offset = CONFIG_ENV_OFFSET,
+		},
+#ifdef CONFIG_ENV_OFFSET_REDUND
+		{
+			.name = "redundant NAND",
+			.erase_opts = &nand_erase_options,
+			.offset = CONFIG_ENV_OFFSET_REDUND,
+		},
+#endif
+	};
+
 
 	memset(&nand_erase_options, 0, sizeof(nand_erase_options));
 	nand_erase_options.length = CONFIG_ENV_RANGE;
-	nand_erase_options.offset = CONFIG_ENV_OFFSET;
 
 	if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
 		return 1;
@@ -244,22 +228,25 @@ int saveenv(void)
 		error("Cannot export environment: errno = %d\n", errno);
 		return 1;
 	}
-	env_new->crc = crc32(0, env_new->data, ENV_SIZE);
-
-	puts("Erasing Nand...\n");
-	if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-		return 1;
+	env_new->crc   = crc32(0, env_new->data, ENV_SIZE);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	env_new->flags = ++env_flags; /* increase the serial */
+	env_idx = (gd->env_valid == 1);
+#endif
 
-	puts("Writing to Nand... ");
-	if (writeenv(CONFIG_ENV_OFFSET, (u_char *)env_new)) {
-		puts("FAILED!\n");
-		return 1;
+	ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	if (ret) {
+		env_idx = (env_idx + 1) & 1;
+		ret = erase_and_write_env(&location[env_idx],
+				(u_char *)env_new);
+	} else {
+		gd->env_valid = gd->env_valid == 2 ? 1 : 2;
 	}
+#endif
 
-	puts("done\n");
 	return ret;
 }
-#endif /* CONFIG_ENV_OFFSET_REDUND */
 #endif /* CMD_SAVEENV */
 
 int readenv(size_t offset, u_char *buf)
Signature · 1 line
1.8.1.5
#41 lore raw

PATCH v3 2/2 env_nand.c: support falling back to redundant env when writing

Quoted 8 lines · Phil Sutter

Without this patch, when the currently chosen environment to be written
has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment reading needs no adjustment, as the fallback logic for incomplete writes applies
to this case as well.

Quoted 5 lines
---
 common/env_nand.c | 105  
++++++++++++++++++++++++------------------------------
 1 file changed, 46 insertions(+), 59 deletions(-)

Missing description of changes since v2

common/env_nand.c int writeenv(size_t offset, u_char *buf) · line 193

Quoted 3 lines
common/env_nand.c +2 -1
-#else /* ! CONFIG_ENV_OFFSET_REDUND */
+
+static unsigned char env_flags;

env_nand.c:193:22: warning: 'env_flags' defined but not used [-Wunused-variable]

(when CONFIG_ENV_OFFSET_REDUND is not defined)

Quoted 13 lines

int saveenv(void)
{

common/env_nand.c +6 -0
 	int	ret = 0;
 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
 	ssize_t	len;
 	char	*res;
+	int	env_idx;
 	nand_erase_options_t nand_erase_options;
+	static const struct env_location location[] = {
+		{
+			.name = "NAND",
+			.erase_opts = &nand_erase_options,
+			.offset = CONFIG_ENV_OFFSET,

common/env_nand.c int writeenv(size_t offset, u_char *buf) · line 217

Quoted 10 lines
common/env_nand.c +10 -0
+		},
+#ifdef CONFIG_ENV_OFFSET_REDUND
+		{
+			.name = "redundant NAND",
+			.erase_opts = &nand_erase_options,
+			.offset = CONFIG_ENV_OFFSET_REDUND,
+		},
+#endif
+	};
+

env_nand.c:206:4: error: initializer element is not constant env_nand.c:206:4: error: (near initialization for 'location[0].erase_opts')

You could make nand_erase_options static, or you could use code to assign
that field.

Is this code untested, or did you accidentally send an old version?

Quoted 6 lines
common/env_nand.c +1 -4
-	puts("Writing to Nand... ");
-	if (writeenv(CONFIG_ENV_OFFSET, (u_char *)env_new)) {
-		puts("FAILED!\n");
-		return 1;
+	ret = erase_and_write_env(&location[env_idx], (u_char  

*)env_new);

env_nand.c:237:2: warning: passing argument 1 of 'erase_and_write_env' discards 'const' qualifier from pointer target type [enabled by default] env_nand.c:177:12: note: expected 'struct env_location *' but argument is
of type 'const struct env_location *'

common/env_nand.c int saveenv(void) · line 242

Quoted 5 lines
common/env_nand.c +5 -0
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	if (ret) {
+		env_idx = (env_idx + 1) & 1;
+		ret = erase_and_write_env(&location[env_idx],
+				(u_char *)env_new);

Can you print a message here specifically saying that redundancy has been
lost? I realize that the previous erase_and_write_env will have printed "FAILED", but it'd be nice to be explicit about the consequences.

-Scott

#42 lore raw

PATCH v3 2/2 env_nand.c: support falling back to redundant env when writing

Hi,

Quoted 54 lines · Scott Wood
Quoted 8 lines · Phil Sutter

Without this patch, when the currently chosen environment to be written
has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment reading needs no adjustment, as the fallback logic for incomplete writes applies
to this case as well.

Quoted 5 lines
---
 common/env_nand.c | 105  
++++++++++++++++++++++++------------------------------
 1 file changed, 46 insertions(+), 59 deletions(-)

Missing description of changes since v2

common/env_nand.c int writeenv(size_t offset, u_char *buf) · line 193

Quoted 3 lines
common/env_nand.c +2 -1
-#else /* ! CONFIG_ENV_OFFSET_REDUND */
+
+static unsigned char env_flags;

env_nand.c:193:22: warning: 'env_flags' defined but not used [-Wunused-variable]

(when CONFIG_ENV_OFFSET_REDUND is not defined)

Quoted 13 lines

int saveenv(void)
{

common/env_nand.c +6 -0
 	int	ret = 0;
 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
 	ssize_t	len;
 	char	*res;
+	int	env_idx;
 	nand_erase_options_t nand_erase_options;
+	static const struct env_location location[] = {
+		{
+			.name = "NAND",
+			.erase_opts = &nand_erase_options,
+			.offset = CONFIG_ENV_OFFSET,

common/env_nand.c int writeenv(size_t offset, u_char *buf) · line 217

Quoted 10 lines
common/env_nand.c +10 -0
+		},
+#ifdef CONFIG_ENV_OFFSET_REDUND
+		{
+			.name = "redundant NAND",
+			.erase_opts = &nand_erase_options,
+			.offset = CONFIG_ENV_OFFSET_REDUND,
+		},
+#endif
+	};
+

env_nand.c:206:4: error: initializer element is not constant env_nand.c:206:4: error: (near initialization for 'location[0].erase_opts')

Quoted 5 lines

You could make nand_erase_options static, or you could use code to assign
that field.

Is this code untested, or did you accidentally send an old version?

Yes, indeed. My apologies for not having tested this, seems like a combination of too much belief in your builtin parser and failure of mine.

Corrected version which incorporates your suggestions and tries to solve the issue above in a cleaner way follows. And yes, this time it has even been tested.

Greetings, Phil

Signature · 15 lines
Viprinet Europe GmbH
Mainzer Str. 43
55411 Bingen am Rhein
Germany

Phone/Zentrale:               +49 6721 49030-0
Direct line/Durchwahl:        +49 6721 49030-134
Fax:                          +49 6721 49030-109

phil.sutter at viprinet.com
http://www.viprinet.com

Registered office/Sitz der Gesellschaft: Bingen am Rhein, Germany
Commercial register/Handelsregister: Amtsgericht Mainz HRB44090
CEO/Gesch?ftsf?hrer: Simon Kissel
#43 lore raw

PATCH 1/1 env_nand.c: support falling back to redundant env when writing

Without this patch, when the currently chosen environment to be written has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment reading needs no adjustment, as the fallback logic for incomplete writes applies to this case as well.

---
Changes since V1:
- Print status message for each of the redundant copies when writing, so the error message for any first failed attempt does not confuse that much.

Changes since V2:
- Checkpatch-compliance:
- do not typedef struct env_location
- avoid assignments in conditionals
- some minor formatting corrections
- make location array static const

Changes since V3:
- constify field 'erase_opts' in struct env_location, no need to assign to it at run-time
- drop unnecessary field 'offset' in struct env_location
- function 'erase_and_write_env' must take passed struct env_location as const
- declare a global var 'env_flags' only if CONFIG_ENV_OFFSET_REDUND is defined
- warn if first attempt to write env failed

---
 common/env_nand.c | 116 +++++++++++++++++++++++++-----------------------------
 1 file changed, 54 insertions(+), 62 deletions(-)
common/env_nand.c +54 -62
index b745822..db7da37 100644
@@ -168,72 +168,57 @@ int writeenv(size_t offset, u_char *buf)
 	return 0;
 }
 
-#ifdef CONFIG_ENV_OFFSET_REDUND
-static unsigned char env_flags;
+struct env_location {
+	const char *name;
+	const nand_erase_options_t erase_opts;
+};
 
-int saveenv(void)
+static int erase_and_write_env(const struct env_location *location,
+		u_char *env_new)
 {
-	env_t	env_new;
-	ssize_t	len;
-	char	*res;
-	int	ret = 0;
-	nand_erase_options_t nand_erase_options;
+	int ret = 0;
 
-	memset(&nand_erase_options, 0, sizeof(nand_erase_options));
-	nand_erase_options.length = CONFIG_ENV_RANGE;
-
-	if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
-		return 1;
-
-	res = (char *)&env_new.data;
-	len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
-	if (len < 0) {
-		error("Cannot export environment: errno = %d\n", errno);
-		return 1;
-	}
-	env_new.crc	= crc32(0, env_new.data, ENV_SIZE);
-	env_new.flags	= ++env_flags; /* increase the serial */
-
-	if (gd->env_valid == 1) {
-		puts("Erasing redundant NAND...\n");
-		nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND;
-		if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-			return 1;
-
-		puts("Writing to redundant NAND... ");
-		ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *)&env_new);
-	} else {
-		puts("Erasing NAND...\n");
-		nand_erase_options.offset = CONFIG_ENV_OFFSET;
-		if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-			return 1;
-
-		puts("Writing to NAND... ");
-		ret = writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new);
-	}
-	if (ret) {
-		puts("FAILED!\n");
+	printf("Erasing %s...\n", location->name);
+	if (nand_erase_opts(&nand_info[0], &location->erase_opts))
 		return 1;
-	}
-
-	puts("done\n");
 
-	gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+	printf("Writing to %s... ", location->name);
+	ret = writeenv(location->erase_opts.offset, env_new);
+	puts(ret ? "FAILED!\n" : "OK\n");
 
 	return ret;
 }
-#else /* ! CONFIG_ENV_OFFSET_REDUND */
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+static unsigned char env_flags;
+#endif
+
 int saveenv(void)
 {
 	int	ret = 0;
 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
 	ssize_t	len;
 	char	*res;
-	nand_erase_options_t nand_erase_options;
+	int	env_idx = 0;
+	static const struct env_location location[] = {
+		{
+			.name = "NAND",
+			.erase_opts = {
+				.length = CONFIG_ENV_RANGE,
+				.offset = CONFIG_ENV_OFFSET,
+			},
+		},
+#ifdef CONFIG_ENV_OFFSET_REDUND
+		{
+			.name = "redundant NAND",
+			.erase_opts = {
+				.length = CONFIG_ENV_RANGE,
+				.offset = CONFIG_ENV_OFFSET_REDUND,
+			},
+		},
+#endif
+	};
 
-	memset(&nand_erase_options, 0, sizeof(nand_erase_options));
-	nand_erase_options.length = CONFIG_ENV_RANGE;
-	nand_erase_options.offset = CONFIG_ENV_OFFSET;
 
 	if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
 		return 1;
@@ -244,22 +229,29 @@ int saveenv(void)
 		error("Cannot export environment: errno = %d\n", errno);
 		return 1;
 	}
-	env_new->crc = crc32(0, env_new->data, ENV_SIZE);
-
-	puts("Erasing Nand...\n");
-	if (nand_erase_opts(&nand_info[0], &nand_erase_options))
-		return 1;
+	env_new->crc   = crc32(0, env_new->data, ENV_SIZE);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	env_new->flags = ++env_flags; /* increase the serial */
+	env_idx = (gd->env_valid == 1);
+#endif
 
-	puts("Writing to Nand... ");
-	if (writeenv(CONFIG_ENV_OFFSET, (u_char *)env_new)) {
-		puts("FAILED!\n");
-		return 1;
+	ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+	if (!ret) {
+		/* preset other copy for next write */
+		gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+		return ret;
 	}
 
-	puts("done\n");
+	env_idx = (env_idx + 1) & 1;
+	ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
+	if (!ret)
+		printf("Warning: primary env write failed,"
+				" redundancy is lost!\n");
+#endif
+
 	return ret;
 }
-#endif /* CONFIG_ENV_OFFSET_REDUND */
 #endif /* CMD_SAVEENV */
 
 int readenv(size_t offset, u_char *buf)
Signature · 1 line
1.8.1.5
#44 lore raw

PATCH 1/1 env_nand.c: support falling back to redundant env when writing

On Fri, Jul 19, 2013 at 12:20:26PM +0200, Phil Sutter wrote:
Subject: [U-Boot] [PATCH] env_nand.c: support falling back to redundant

                   ~~~~~

Heck, PATCHv4 that is.

Signature · 15 lines
Viprinet Europe GmbH
Mainzer Str. 43
55411 Bingen am Rhein
Germany

Phone/Zentrale:               +49 6721 49030-0
Direct line/Durchwahl:        +49 6721 49030-134
Fax:                          +49 6721 49030-109

phil.sutter at viprinet.com
http://www.viprinet.com

Registered office/Sitz der Gesellschaft: Bingen am Rhein, Germany
Commercial register/Handelsregister: Amtsgericht Mainz HRB44090
CEO/Gesch?ftsf?hrer: Simon Kissel
#45 lore raw

[U-Boot] env_nand.c: support falling back to redundant env when writing

In reply to Phil Sutter
Quoted 7 lines · Phil Sutter

Without this patch, when the currently chosen environment to be written has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment reading needs no adjustment, as the fallback logic for incomplete writes applies to this case as well.

Applied to u-boot-nand-flash

-Scott