Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/littlefs-project/littlefs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Haster <chaster@utexas.edu>2018-10-23 00:42:30 +0300
committerChristopher Haster <chaster@utexas.edu>2018-10-23 01:58:32 +0300
commit5b26c68ae2abae93b0fea4fca669765841dd1d2a (patch)
tree3a6820b25692c2a181676c90c4570c9fd3e30c9f /lfs_util.h
parent4a1b8ae22277fbb0901b3b34649aaf0135bbdc73 (diff)
Tweaked tag endianness to catch power-loss after <1 word is written
There was an interesting subtlety with the existing layout of tags that could become a problem in the future. Basically, littlefs avoids writing to any region of storage it is not absolutely sure has been erased beforehand. This is a part of limiting the number of assumptions about storage. It's possible a storage technology can't support writes without erases in a way that is undetectable at write time (Maybe changing a bit without an erase decreases the longevity of the information stored on the bit). But the existing layout had a very tiny corner case where this wasn't true. Consider the location of the valid bit in the tag struct: [1|--- 31 ---] ^--- valid bit The responsibility of this bit is to indicate if an attempt has been made to write the following commit. If it is not set (the specific value is dependent on a previous read and identified by the preceeding commit), the assumption is that it is safe to write to the next region because it has been erased previously. If it is set, we check if the next commit is valid, if it isn't (because of CRC failure, likely due to power-loss), we discard the commit. But because an attempt has been made to write to that storage, we must then do a compaction to move to the other block in the metadata-pair. This plan looks good on paper, but what does it look like on storage? The problem is that words in littlefs are in little-endian. So on storage the tag actually looks like this: [- 8 -|- 8 -|- 8 -|1|- 7 -] ^-- valid bit This means that we don't actually set the valid bit before writing the tag! We write the lower bytes first. If we lose power, we may have written 3 bytes without this fact being detectable. We could restructure the tag structure to store the valid bit lower, however because none of the fields are 7 bits, this would make the extraction more costly, and we then lose the ability to check this valid bit with a sign comparison. The simple solution is to just store the tag in big-endian. A small benefit is that this will actually have a negative code cost on big-endian machines. This mixture of endiannesses is frustrating, however it is a pragmatic solution with only a 20-byte code size cost.
Diffstat (limited to 'lfs_util.h')
-rw-r--r--lfs_util.h46
1 files changed, 46 insertions, 0 deletions
diff --git a/lfs_util.h b/lfs_util.h
index 8a65694..6e2f002 100644
--- a/lfs_util.h
+++ b/lfs_util.h
@@ -186,6 +186,52 @@ static inline uint16_t lfs_tole16(uint16_t a) {
return lfs_fromle16(a);
}
+// Convert between 32-bit big-endian and native order
+static inline uint32_t lfs_frombe32(uint32_t a) {
+#if !defined(LFS_NO_INTRINSICS) && ( \
+ (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
+ (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
+ (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+ return __builtin_bswap32(a);
+#elif !defined(LFS_NO_INTRINSICS) && ( \
+ (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
+ (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
+ (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
+ return a;
+#else
+ return (((uint8_t*)&a)[0] << 24) |
+ (((uint8_t*)&a)[1] << 16) |
+ (((uint8_t*)&a)[2] << 8) |
+ (((uint8_t*)&a)[3] << 0);
+#endif
+}
+
+static inline uint32_t lfs_tobe32(uint32_t a) {
+ return lfs_frombe32(a);
+}
+
+// Convert between 16-bit big-endian and native order
+static inline uint16_t lfs_frombe16(uint16_t a) {
+#if !defined(LFS_NO_INTRINSICS) && ( \
+ (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
+ (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
+ (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+ return __builtin_bswap16(a);
+#elif !defined(LFS_NO_INTRINSICS) && ( \
+ (defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
+ (defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
+ (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
+ return a;
+#else
+ return (((uint8_t*)&a)[0] << 8) |
+ (((uint8_t*)&a)[1] << 0);
+#endif
+}
+
+static inline uint16_t lfs_tobe16(uint16_t a) {
+ return lfs_frombe16(a);
+}
+
// Calculate CRC-32 with polynomial = 0x04c11db7
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);