diff options
author | Cyril Bur <cyril.bur@au1.ibm.com> | 2015-09-03 17:47:07 +1000 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2015-09-07 14:18:06 +1000 |
commit | ba98f282aee64a24d3c949ed34ff66d362fcf4c9 (patch) | |
tree | 26377d184974500f206e8fb78b3e1fea0ec759c4 /libflash | |
parent | cc65fb88f8a360c8744ac9459eea2ebd98966244 (diff) | |
download | skiboot-ba98f282aee64a24d3c949ed34ff66d362fcf4c9.zip skiboot-ba98f282aee64a24d3c949ed34ff66d362fcf4c9.tar.gz skiboot-ba98f282aee64a24d3c949ed34ff66d362fcf4c9.tar.bz2 |
libflash/blocklevel: Smarten up blocklevel_smart_write()
Blocklevel_smart_write() can write in a smart fashion which avoids the need
for the caller to erase the flash before writing, except it never actually
does the erase if it needs to.
Furthermore, a very simple optimisation can be made where it should detect
if it doesn't actually need to clear the flash, but still needs to write.
This optimisation is from flash_smart_write(), which, for reasons, should
continue to exist at least for now.
In the process, this also addresses a bug. blocklevel_smart_write()
operates on erase block covered by the write, detects to see if an erase
needs to be performed and if so, performs the erase and then performs the
write.
Currently blocklevel_smart_write() won't detect that a write can span two
erase blocks and that it should split this process in two. This patch fixes
that.
Signed-off-by: Cyril Bur <cyril.bur@au1.ibm.com>
Reviewed-by: Samuel Mendoza-Jonas <sam.mj@au1.ibm.com>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'libflash')
-rw-r--r-- | libflash/blocklevel.c | 43 |
1 files changed, 40 insertions, 3 deletions
diff --git a/libflash/blocklevel.c b/libflash/blocklevel.c index 90bfffb..83823c5 100644 --- a/libflash/blocklevel.c +++ b/libflash/blocklevel.c @@ -17,6 +17,7 @@ #include <stdlib.h> #include <unistd.h> #include <stdio.h> +#include <stdbool.h> #include <errno.h> #include <string.h> @@ -161,6 +162,34 @@ int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint32_ return rc; } +/* + * Compare flash and memory to determine if: + * a) Erase must happen before write + * b) Flash and memory are identical + * c) Flash can simply be written to + * + * returns -1 for a + * returns 0 for b + * returns 1 for c + */ +static int blocklevel_flashcmp(const void *flash_buf, const void *mem_buf, uint32_t len) +{ + int i, same = true; + const uint8_t *f_buf, *m_buf; + + f_buf = flash_buf; + m_buf = mem_buf; + + for (i = 0; i < len; i++) { + if (m_buf[i] & ~f_buf[i]) + return -1; + if (same && (m_buf[i] != f_buf[i])) + same = false; + } + + return same ? 0 : 1; +} + int blocklevel_smart_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len) { uint32_t erase_size; @@ -209,18 +238,26 @@ int blocklevel_smart_write(struct blocklevel_device *bl, uint32_t pos, const voi uint32_t erase_block = pos & ~(erase_size - 1); uint32_t block_offset = pos & (erase_size - 1); uint32_t size = erase_size > len ? len : erase_size; + int cmp; + + /* Write crosses an erase boundary, shrink the write to the boundary */ + if (erase_size < block_offset + size) { + size = erase_size - block_offset; + } rc = bl->read(bl, erase_block, erase_buf, erase_size); if (rc) goto out; - if (memcmp(erase_buf + block_offset, write_buf, size) != 0) { + cmp = blocklevel_flashcmp(erase_buf + block_offset, write_buf, size); + if (cmp != 0) { + if (cmp == -1) + bl->erase(bl, erase_block, erase_size); memcpy(erase_buf + block_offset, write_buf, size); - rc = bl->write(bl, erase_block, erase_buf + block_offset, size); + rc = bl->write(bl, erase_block, erase_buf, erase_size); if (rc) goto out; } - len -= size; pos += size; write_buf += size; |