mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	WIP Added allocation randomization for dynamic wear-leveling
This commit is contained in:
		| @@ -19,6 +19,40 @@ | ||||
| #include <inttypes.h> | ||||
|  | ||||
|  | ||||
| // Emulated block device utils | ||||
| static inline void lfs_emubd_tole32(lfs_emubd_t *emu) { | ||||
|     emu->cfg.read_size     = lfs_tole32(emu->cfg.read_size); | ||||
|     emu->cfg.prog_size     = lfs_tole32(emu->cfg.prog_size); | ||||
|     emu->cfg.block_size    = lfs_tole32(emu->cfg.block_size); | ||||
|     emu->cfg.block_count   = lfs_tole32(emu->cfg.block_count); | ||||
|  | ||||
|     emu->stats.read_count  = lfs_tole32(emu->stats.read_count); | ||||
|     emu->stats.prog_count  = lfs_tole32(emu->stats.prog_count); | ||||
|     emu->stats.erase_count = lfs_tole32(emu->stats.erase_count); | ||||
|  | ||||
|     for (int i = 0; i < sizeof(emu->history.blocks) / | ||||
|             sizeof(emu->history.blocks[0]); i++) { | ||||
|         emu->history.blocks[i] = lfs_tole32(emu->history.blocks[i]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static inline void lfs_emubd_fromle32(lfs_emubd_t *emu) { | ||||
|     emu->cfg.read_size     = lfs_fromle32(emu->cfg.read_size); | ||||
|     emu->cfg.prog_size     = lfs_fromle32(emu->cfg.prog_size); | ||||
|     emu->cfg.block_size    = lfs_fromle32(emu->cfg.block_size); | ||||
|     emu->cfg.block_count   = lfs_fromle32(emu->cfg.block_count); | ||||
|  | ||||
|     emu->stats.read_count  = lfs_fromle32(emu->stats.read_count); | ||||
|     emu->stats.prog_count  = lfs_fromle32(emu->stats.prog_count); | ||||
|     emu->stats.erase_count = lfs_fromle32(emu->stats.erase_count); | ||||
|  | ||||
|     for (int i = 0; i < sizeof(emu->history.blocks) / | ||||
|             sizeof(emu->history.blocks[0]); i++) { | ||||
|         emu->history.blocks[i] = lfs_fromle32(emu->history.blocks[i]); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| // Block device emulated on existing filesystem | ||||
| int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     lfs_emubd_t *emu = cfg->context; | ||||
| @@ -46,20 +80,39 @@ int lfs_emubd_create(const struct lfs_config *cfg, const char *path) { | ||||
|     } | ||||
|  | ||||
|     // Load stats to continue incrementing | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "stats"); | ||||
|     snprintf(emu->child, LFS_NAME_MAX, ".stats"); | ||||
|     FILE *f = fopen(emu->path, "r"); | ||||
|     if (!f) { | ||||
|         return -errno; | ||||
|         memset(&emu->stats, 0, sizeof(emu->stats)); | ||||
|     } else { | ||||
|         size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); | ||||
|         lfs_emubd_fromle32(emu); | ||||
|         if (res < 1) { | ||||
|             return -errno; | ||||
|         } | ||||
|  | ||||
|         err = fclose(f); | ||||
|         if (err) { | ||||
|             return -errno; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f); | ||||
|     if (res < 1) { | ||||
|         return -errno; | ||||
|     } | ||||
|     // Load history | ||||
|     snprintf(emu->child, LFS_NAME_MAX, ".history"); | ||||
|     f = fopen(emu->path, "r"); | ||||
|     if (!f) { | ||||
|         memset(&emu->history, 0, sizeof(emu->history)); | ||||
|     } else { | ||||
|         size_t res = fread(&emu->history, sizeof(emu->history), 1, f); | ||||
|         lfs_emubd_fromle32(emu); | ||||
|         if (res < 1) { | ||||
|             return -errno; | ||||
|         } | ||||
|  | ||||
|     err = fclose(f); | ||||
|     if (err) { | ||||
|         return -errno; | ||||
|         err = fclose(f); | ||||
|         if (err) { | ||||
|             return -errno; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| @@ -161,6 +214,13 @@ int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block, | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     // update history and stats | ||||
|     if (block != emu->history.blocks[0]) { | ||||
|         memcpy(&emu->history.blocks[1], &emu->history.blocks[0], | ||||
|                 sizeof(emu->history) - sizeof(emu->history.blocks[0])); | ||||
|         emu->history.blocks[0] = block; | ||||
|     } | ||||
|  | ||||
|     emu->stats.prog_count += 1; | ||||
|     return 0; | ||||
| } | ||||
| @@ -206,13 +266,15 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { | ||||
|     lfs_emubd_t *emu = cfg->context; | ||||
|  | ||||
|     // Just write out info/stats for later lookup | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "config"); | ||||
|     snprintf(emu->child, LFS_NAME_MAX, ".config"); | ||||
|     FILE *f = fopen(emu->path, "w"); | ||||
|     if (!f) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     lfs_emubd_tole32(emu); | ||||
|     size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f); | ||||
|     lfs_emubd_fromle32(emu); | ||||
|     if (res < 1) { | ||||
|         return -errno; | ||||
|     } | ||||
| @@ -222,13 +284,33 @@ int lfs_emubd_sync(const struct lfs_config *cfg) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     snprintf(emu->child, LFS_NAME_MAX, "stats"); | ||||
|     snprintf(emu->child, LFS_NAME_MAX, ".stats"); | ||||
|     f = fopen(emu->path, "w"); | ||||
|     if (!f) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     lfs_emubd_tole32(emu); | ||||
|     res = fwrite(&emu->stats, sizeof(emu->stats), 1, f); | ||||
|     lfs_emubd_fromle32(emu); | ||||
|     if (res < 1) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     err = fclose(f); | ||||
|     if (err) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     snprintf(emu->child, LFS_NAME_MAX, ".history"); | ||||
|     f = fopen(emu->path, "w"); | ||||
|     if (!f) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     lfs_emubd_tole32(emu); | ||||
|     res = fwrite(&emu->history, sizeof(emu->history), 1, f); | ||||
|     lfs_emubd_fromle32(emu); | ||||
|     if (res < 1) { | ||||
|         return -errno; | ||||
|     } | ||||
|   | ||||
| @@ -45,6 +45,10 @@ typedef struct lfs_emubd { | ||||
|         uint64_t erase_count; | ||||
|     } stats; | ||||
|  | ||||
|     struct { | ||||
|         lfs_block_t blocks[4]; | ||||
|     } history; | ||||
|  | ||||
|     struct { | ||||
|         uint32_t read_size; | ||||
|         uint32_t prog_size; | ||||
|   | ||||
							
								
								
									
										15
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -879,6 +879,8 @@ static int32_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 dir->tail[1] = temptail[1]; | ||||
|                 dir->split = tempsplit; | ||||
|                 dir->locals = templocals; | ||||
|  | ||||
|                 lfs->seed ^= crc; | ||||
|                 crc = 0xffffffff; | ||||
|             } else { | ||||
|                 err = lfs_bd_crc32(lfs, dir->pair[0], | ||||
| @@ -2874,6 +2876,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     lfs->root[0] = 0xffffffff; | ||||
|     lfs->root[1] = 0xffffffff; | ||||
|     lfs->mlist = NULL; | ||||
|     lfs->seed = 0; | ||||
|     lfs->globals.s.movepair[0] = 0xffffffff; | ||||
|     lfs->globals.s.movepair[1] = 0xffffffff; | ||||
|     lfs->globals.s.moveid = 0x3ff; | ||||
| @@ -2962,12 +2965,6 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // setup free lookahead | ||||
|     lfs->free.off = 0; | ||||
|     lfs->free.size = 0; | ||||
|     lfs->free.i = 0; | ||||
|     lfs_alloc_ack(lfs); | ||||
|  | ||||
|     // load superblock | ||||
|     lfs_mdir_t root; | ||||
|     err = lfs_dir_fetch(lfs, &root, (const lfs_block_t[2]){0, 1}); | ||||
| @@ -3065,6 +3062,12 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|                 lfs->globals.s.moveid); | ||||
|     } | ||||
|  | ||||
|     // setup free lookahead | ||||
|     lfs->free.off = lfs->seed % lfs->cfg->block_size; | ||||
|     lfs->free.size = 0; | ||||
|     lfs->free.i = 0; | ||||
|     lfs_alloc_ack(lfs); | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
| cleanup: | ||||
|   | ||||
							
								
								
									
										1
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -382,6 +382,7 @@ typedef struct lfs { | ||||
|  | ||||
|     lfs_block_t root[2]; | ||||
|     lfs_mlist_t *mlist; | ||||
|     uint32_t seed; | ||||
|  | ||||
|     lfs_global_t globals; | ||||
|     lfs_global_t locals; | ||||
|   | ||||
| @@ -3,37 +3,41 @@ | ||||
| import struct | ||||
| import sys | ||||
| import os | ||||
| import argparse | ||||
|  | ||||
| def main(*paths): | ||||
|     # find most recent block | ||||
|     file = None | ||||
|     rev = None | ||||
|     for path in paths: | ||||
|         try: | ||||
|             nfile = open(path, 'r+b') | ||||
|             nrev, = struct.unpack('<I', nfile.read(4)) | ||||
| def corrupt(block): | ||||
|     with open(block, 'r+b') as file: | ||||
|         # skip rev | ||||
|         file.read(4) | ||||
|  | ||||
|             assert rev != nrev | ||||
|             if not file or ((rev - nrev) & 0x80000000): | ||||
|                 file = nfile | ||||
|                 rev = nrev | ||||
|         except IOError: | ||||
|             pass | ||||
|         # go to last commit | ||||
|         tag = 0 | ||||
|         while True: | ||||
|             try: | ||||
|                 ntag, = struct.unpack('<I', file.read(4)) | ||||
|             except struct.error: | ||||
|                 break | ||||
|  | ||||
|     # go to last commit | ||||
|     tag = 0 | ||||
|     while True: | ||||
|         try: | ||||
|             ntag, = struct.unpack('<I', file.read(4)) | ||||
|         except struct.error: | ||||
|             break | ||||
|             tag ^= ntag | ||||
|             file.seek(tag & 0xfff, os.SEEK_CUR) | ||||
|  | ||||
|         tag ^= ntag | ||||
|         file.seek(tag & 0xfff, os.SEEK_CUR) | ||||
|         # lob off last 3 bytes | ||||
|         file.seek(-((tag & 0xfff) + 3), os.SEEK_CUR) | ||||
|         file.truncate() | ||||
|  | ||||
|     # lob off last 3 bytes | ||||
|     file.seek(-((tag & 0xfff) + 3), os.SEEK_CUR) | ||||
|     file.truncate() | ||||
| def main(args): | ||||
|     if args.n or not args.blocks: | ||||
|         with open('blocks/.history', 'rb') as file: | ||||
|             for i in range(int(args.n or 1)): | ||||
|                 last, = struct.unpack('<I', file.read(4)) | ||||
|                 args.blocks.append('blocks/%x' % last) | ||||
|  | ||||
|     for block in args.blocks: | ||||
|         print 'corrupting %s' % block | ||||
|         corrupt(block) | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     main(*sys.argv[1:]) | ||||
|     parser = argparse.ArgumentParser() | ||||
|     parser.add_argument('-n') | ||||
|     parser.add_argument('blocks', nargs='*') | ||||
|     main(parser.parse_args()) | ||||
|   | ||||
							
								
								
									
										98
									
								
								tests/debug.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										98
									
								
								tests/debug.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| #!/usr/bin/env python2 | ||||
|  | ||||
| import struct | ||||
| import binascii | ||||
|  | ||||
| TYPES = { | ||||
|     (0x1ff, 0x001): 'reg', | ||||
|     (0x1ff, 0x002): 'dir', | ||||
|     (0x1ff, 0x011): 'superblock', | ||||
|     (0x1ff, 0x012): 'root', | ||||
|     (0x1ff, 0x030): 'delete', | ||||
|     (0x1f0, 0x080): 'globals', | ||||
|     (0x1ff, 0x0c0): 'tail soft', | ||||
|     (0x1ff, 0x0c1): 'tail hard', | ||||
|     (0x1ff, 0x0f0): 'crc', | ||||
|     (0x1ff, 0x040): 'struct dir', | ||||
|     (0x1ff, 0x041): 'struct inline', | ||||
|     (0x1ff, 0x042): 'struct ctz', | ||||
|     (0x100, 0x100): 'attr', | ||||
| } | ||||
|  | ||||
| def typeof(type): | ||||
|     for prefix in range(9): | ||||
|         mask = 0x1ff & ~((1 << prefix)-1) | ||||
|         if (mask, type & mask) in TYPES: | ||||
|             return TYPES[mask, type & mask] + ( | ||||
|                 ' [%0*x]' % (prefix/4, type & ((1 << prefix)-1)) | ||||
|                 if prefix else '') | ||||
|     else: | ||||
|         return '[%02x]' % type | ||||
|  | ||||
| def main(*blocks): | ||||
|     # find most recent block | ||||
|     file = None | ||||
|     rev = None | ||||
|     crc = None | ||||
|     versions = [] | ||||
|  | ||||
|     for block in blocks: | ||||
|         try: | ||||
|             nfile = open(block, 'rb') | ||||
|             ndata = nfile.read(4) | ||||
|             ncrc = binascii.crc32(ndata) | ||||
|             nrev, = struct.unpack('<I', ndata) | ||||
|  | ||||
|             assert rev != nrev | ||||
|             if not file or ((rev - nrev) & 0x80000000): | ||||
|                 file = nfile | ||||
|                 rev = nrev | ||||
|                 crc = ncrc | ||||
|  | ||||
|             versions.append((nrev, '%s (rev %d)' % (block, nrev))) | ||||
|         except IOError: | ||||
|             pass | ||||
|  | ||||
|     print "--- %s ---" % ', '.join(v for _,v in sorted(versions, reverse=True)) | ||||
|  | ||||
|     # go through each tag, print useful information | ||||
|     print "%-4s  %-8s  %-14s  %3s  %3s  %s" % ( | ||||
|         'off', 'tag', 'type', 'id', 'len', 'dump') | ||||
|  | ||||
|     tag = 0 | ||||
|     off = 4 | ||||
|     while True: | ||||
|         try: | ||||
|             data = file.read(4) | ||||
|             crc = binascii.crc32(data, crc) | ||||
|             ntag, = struct.unpack('<I', data) | ||||
|         except struct.error: | ||||
|             break | ||||
|  | ||||
|         tag ^= ntag | ||||
|         off += 4 | ||||
|  | ||||
|         type = (tag & 0x7fc00000) >> 22 | ||||
|         id   = (tag & 0x003ff000) >> 12 | ||||
|         size = (tag & 0x00000fff) >> 0 | ||||
|  | ||||
|         data = file.read(size) | ||||
|         if type == 0x0f0: | ||||
|             crc = binascii.crc32(data[:4], crc) | ||||
|         else: | ||||
|             crc = binascii.crc32(data, crc) | ||||
|  | ||||
|         print '%04x: %08x  %-14s  %3s  %3d  %-23s  %-8s' % ( | ||||
|             off, tag, | ||||
|             typeof(type) + (' bad!' if type == 0x0f0 and ~crc else ''), | ||||
|             id if id != 0x3ff else '.', size, | ||||
|             ' '.join('%02x' % ord(c) for c in data[:8]), | ||||
|             ''.join(c if c >= ' ' and c <= '~' else '.' for c in data[:8])) | ||||
|  | ||||
|         off += tag & 0xfff | ||||
|         if type == 0x0f0: | ||||
|             crc = 0 | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|     import sys | ||||
|     main(*sys.argv[1:]) | ||||
| @@ -7,7 +7,7 @@ import os | ||||
| import re | ||||
|  | ||||
| def main(): | ||||
|     with open('blocks/config') as file: | ||||
|     with open('blocks/.config') as file: | ||||
|         s = struct.unpack('<LLLL', file.read()) | ||||
|         print 'read_size: %d' % s[0] | ||||
|         print 'prog_size: %d' % s[1] | ||||
| @@ -18,7 +18,7 @@ def main(): | ||||
|         os.path.getsize(os.path.join('blocks', f)) | ||||
|         for f in os.listdir('blocks') if re.match('\d+', f)) | ||||
|  | ||||
|     with open('blocks/stats') as file: | ||||
|     with open('blocks/.stats') as file: | ||||
|         s = struct.unpack('<QQQ', file.read()) | ||||
|         print 'read_count: %d' % s[0] | ||||
|         print 'prog_count: %d' % s[1] | ||||
|   | ||||
| @@ -71,24 +71,25 @@ echo "--- Sanity check ---" | ||||
| rm -rf blocks | ||||
| lfs_mktree | ||||
| lfs_chktree | ||||
| BLOCKS="$(ls blocks | grep -vw '[01]')" | ||||
|  | ||||
| echo "--- Block corruption ---" | ||||
| for i in {2..33} | ||||
| for b in $BLOCKS | ||||
| do  | ||||
|     rm -rf blocks | ||||
|     mkdir blocks | ||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||
|     ln -s /dev/zero blocks/$b | ||||
|     lfs_mktree | ||||
|     lfs_chktree | ||||
| done | ||||
|  | ||||
| echo "--- Block persistance ---" | ||||
| for i in {2..33} | ||||
| for b in $BLOCKS | ||||
| do  | ||||
|     rm -rf blocks | ||||
|     mkdir blocks | ||||
|     lfs_mktree | ||||
|     chmod a-w blocks/$(printf '%x' $i) || true | ||||
|     chmod a-w blocks/$b | ||||
|     lfs_mktree | ||||
|     lfs_chktree | ||||
| done | ||||
| @@ -96,7 +97,7 @@ done | ||||
| echo "--- Big region corruption ---" | ||||
| rm -rf blocks | ||||
| mkdir blocks | ||||
| for i in {2..255} | ||||
| for i in {2..512} | ||||
| do | ||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||
| done | ||||
| @@ -106,7 +107,7 @@ lfs_chktree | ||||
| echo "--- Alternating corruption ---" | ||||
| rm -rf blocks | ||||
| mkdir blocks | ||||
| for i in {2..511..2} | ||||
| for i in {2..1024..2} | ||||
| do | ||||
|     ln -s /dev/zero blocks/$(printf '%x' $i) | ||||
| done | ||||
|   | ||||
| @@ -59,7 +59,7 @@ tests/test.py << TEST | ||||
|     lfs_rename(&lfs, "b/hello", "c/hello") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/corrupt.py blocks/{4,5} | ||||
| tests/corrupt.py -n 1 | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||
| @@ -86,8 +86,7 @@ tests/test.py << TEST | ||||
|     lfs_rename(&lfs, "c/hello", "d/hello") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/corrupt.py blocks/{6,7} | ||||
| tests/corrupt.py blocks/{8,9} | ||||
| tests/corrupt.py -n 2 | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||
| @@ -166,7 +165,7 @@ tests/test.py << TEST | ||||
|     lfs_rename(&lfs, "b/hi", "c/hi") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/corrupt.py blocks/{4,5} | ||||
| tests/corrupt.py -n 1 | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "b") => 0; | ||||
| @@ -193,8 +192,7 @@ tests/test.py << TEST | ||||
|     lfs_rename(&lfs, "c/hi", "d/hi") => 0; | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/corrupt.py blocks/{6,7} | ||||
| tests/corrupt.py blocks/{8,9} | ||||
| tests/corrupt.py -n 2 | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|     lfs_dir_open(&lfs, &dir[0], "c") => 0; | ||||
|   | ||||
| @@ -17,7 +17,7 @@ tests/test.py << TEST | ||||
| TEST | ||||
| # corrupt most recent commit, this should be the update to the previous | ||||
| # linked-list entry and should orphan the child | ||||
| tests/corrupt.py blocks/{6,7} | ||||
| tests/corrupt.py | ||||
| tests/test.py << TEST | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user