mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	WIP fixed broken wear-leveling when block_cycles = 2n-1
This commit is contained in:
		
							
								
								
									
										52
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -1486,7 +1486,7 @@ static int lfs_dir_compact(lfs_t *lfs, | |||||||
|     // increment revision count |     // increment revision count | ||||||
|     uint32_t nrev = dir->rev + 1; |     uint32_t nrev = dir->rev + 1; | ||||||
|     if (lfs->cfg->block_cycles > 0 && |     if (lfs->cfg->block_cycles > 0 && | ||||||
|             (nrev % (lfs->cfg->block_cycles+1) == 0)) { |             (nrev % ((lfs->cfg->block_cycles+1)|1) == 0)) { | ||||||
|         if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { |         if (lfs_pair_cmp(dir->pair, (const lfs_block_t[2]){0, 1}) == 0) { | ||||||
|             // oh no! we're writing too much to the superblock, |             // oh no! we're writing too much to the superblock, | ||||||
|             // should we expand? |             // should we expand? | ||||||
| @@ -1524,6 +1524,7 @@ static int lfs_dir_compact(lfs_t *lfs, | |||||||
|         } else { |         } else { | ||||||
|             // we're writing too much, time to relocate |             // we're writing too much, time to relocate | ||||||
|             tired = true; |             tired = true; | ||||||
|  |             //nrev += 1;//lfs->cfg->block_cycles & 1; // TODO do this here? | ||||||
|             goto relocate; |             goto relocate; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -1631,17 +1632,33 @@ static int lfs_dir_compact(lfs_t *lfs, | |||||||
|  |  | ||||||
|             // TODO huh? |             // TODO huh? | ||||||
|             LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0); |             LFS_ASSERT(commit.off % lfs->cfg->prog_size == 0); | ||||||
|  |             // update gstate | ||||||
|  |             lfs->gdelta = (lfs_gstate_t){0}; | ||||||
|  |             if (!relocated) { | ||||||
|  |                 lfs->gdisk = lfs->gstate; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |     // TODO here?? | ||||||
|  |     if (relocated) { | ||||||
|  |         // update references if we relocated | ||||||
|  |         LFS_DEBUG("Relocating %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32, | ||||||
|  |                 oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); | ||||||
|  |         int err = lfs_fs_relocate(lfs, oldpair, dir->pair); | ||||||
|  |         if (err) { | ||||||
|  |             // TODO make better | ||||||
|  |             //dir->pair[1] = oldpair[1]; // | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  | //        LFS_DEBUG("Relocated %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32, | ||||||
|  | //                oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|             // successful compaction, swap dir pair to indicate most recent |             // successful compaction, swap dir pair to indicate most recent | ||||||
|             lfs_pair_swap(dir->pair); |             lfs_pair_swap(dir->pair); | ||||||
|             dir->rev = nrev; |             dir->rev = nrev; | ||||||
|             dir->count = end - begin; |             dir->count = end - begin; | ||||||
|             dir->off = commit.off; |             dir->off = commit.off; | ||||||
|             dir->etag = commit.ptag; |             dir->etag = commit.ptag; | ||||||
|             // update gstate |  | ||||||
|             lfs->gdelta = (lfs_gstate_t){0}; |  | ||||||
|             if (!relocated) { |  | ||||||
|                 lfs->gdisk = lfs->gstate; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
| @@ -1669,20 +1686,6 @@ relocate: | |||||||
|         continue; |         continue; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // TODO here?? |  | ||||||
|     if (relocated) { |  | ||||||
|         // update references if we relocated |  | ||||||
|         LFS_DEBUG("Relocating %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32, |  | ||||||
|                 oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); |  | ||||||
|         int err = lfs_fs_relocate(lfs, oldpair, dir->pair); |  | ||||||
|         if (err) { |  | ||||||
|             // TODO make better |  | ||||||
|             //dir->pair[1] = oldpair[1]; // |  | ||||||
|             return err; |  | ||||||
|         } |  | ||||||
|         LFS_DEBUG("Relocated %"PRIx32" %"PRIx32" -> %"PRIx32" %"PRIx32, |  | ||||||
|                 oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -1709,6 +1712,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|  |  | ||||||
|     // calculate changes to the directory |     // calculate changes to the directory | ||||||
|     bool hasdelete = false; |     bool hasdelete = false; | ||||||
|  |     lfs_mdir_t olddir = *dir; // TODO is this a good idea? | ||||||
|     for (int i = 0; i < attrcount; i++) { |     for (int i = 0; i < attrcount; i++) { | ||||||
|         if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE) { |         if (lfs_tag_type3(attrs[i].tag) == LFS_TYPE_CREATE) { | ||||||
|             dir->count += 1; |             dir->count += 1; | ||||||
| @@ -1729,6 +1733,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|         lfs_mdir_t pdir; |         lfs_mdir_t pdir; | ||||||
|         int err = lfs_fs_pred(lfs, dir->pair, &pdir); |         int err = lfs_fs_pred(lfs, dir->pair, &pdir); | ||||||
|         if (err && err != LFS_ERR_NOENT) { |         if (err && err != LFS_ERR_NOENT) { | ||||||
|  |             *dir = olddir; | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -1761,6 +1766,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|             if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { |             if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { | ||||||
|                 goto compact; |                 goto compact; | ||||||
|             } |             } | ||||||
|  |             *dir = olddir; | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -1773,6 +1779,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|         if (!lfs_gstate_iszero(&delta)) { |         if (!lfs_gstate_iszero(&delta)) { | ||||||
|             err = lfs_dir_getgstate(lfs, dir, &delta); |             err = lfs_dir_getgstate(lfs, dir, &delta); | ||||||
|             if (err) { |             if (err) { | ||||||
|  |                 *dir = olddir; | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -1784,6 +1791,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|                 if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { |                 if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { | ||||||
|                     goto compact; |                     goto compact; | ||||||
|                 } |                 } | ||||||
|  |                 *dir = olddir; | ||||||
|                 return err; |                 return err; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -1794,6 +1802,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | |||||||
|             if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { |             if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { | ||||||
|                 goto compact; |                 goto compact; | ||||||
|             } |             } | ||||||
|  |             *dir = olddir; | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -1812,6 +1821,7 @@ compact: | |||||||
|         int err = lfs_dir_compact(lfs, dir, attrs, attrcount, |         int err = lfs_dir_compact(lfs, dir, attrs, attrcount, | ||||||
|                 dir, 0, dir->count); |                 dir, 0, dir->count); | ||||||
|         if (err) { |         if (err) { | ||||||
|  |             *dir = olddir; | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -3845,7 +3855,7 @@ int lfs_fs_traverse(lfs_t *lfs, | |||||||
|                     LFS_TRACE("lfs_fs_traverse -> %d", err); |                     LFS_TRACE("lfs_fs_traverse -> %d", err); | ||||||
|                     return err; |                     return err; | ||||||
|                 } |                 } | ||||||
|             } else if (lfs_gstate_hasorphans(&lfs->gstate) && |             } else if (/*lfs_gstate_hasorphans(&lfs->gstate) && TODO maybe report size-dirs/2 ? */ | ||||||
|                     lfs_tag_type3(tag) == LFS_TYPE_DIRSTRUCT) { |                     lfs_tag_type3(tag) == LFS_TYPE_DIRSTRUCT) { | ||||||
|                 // TODO HMMMMMM HMMMMMMMMMMMMMMMMMMM |                 // TODO HMMMMMM HMMMMMMMMMMMMMMMMMMM | ||||||
|                 for (int i = 0; i < 2; i++) { |                 for (int i = 0; i < 2; i++) { | ||||||
|   | |||||||
| @@ -42,7 +42,7 @@ code = ''' | |||||||
|             if (err == LFS_ERR_NOSPC) { |             if (err == LFS_ERR_NOSPC) { | ||||||
|                 goto exhausted; |                 goto exhausted; | ||||||
|             } |             } | ||||||
|         }     |         } | ||||||
|  |  | ||||||
|         for (uint32_t i = 0; i < FILES; i++) { |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|             // check for errors |             // check for errors | ||||||
| @@ -59,7 +59,7 @@ code = ''' | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             lfs_file_close(&lfs, &file) => 0; |             lfs_file_close(&lfs, &file) => 0; | ||||||
|         }     |         } | ||||||
|         lfs_unmount(&lfs) => 0; |         lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|         cycle += 1; |         cycle += 1; | ||||||
| @@ -72,7 +72,7 @@ exhausted: | |||||||
|         // check for errors |         // check for errors | ||||||
|         sprintf(path, "roadrunner/test%d", i); |         sprintf(path, "roadrunner/test%d", i); | ||||||
|         lfs_stat(&lfs, path, &info) => 0; |         lfs_stat(&lfs, path, &info) => 0; | ||||||
|     }     |     } | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|     LFS_WARN("completed %d cycles", cycle); |     LFS_WARN("completed %d cycles", cycle); | ||||||
| @@ -120,7 +120,7 @@ code = ''' | |||||||
|             if (err == LFS_ERR_NOSPC) { |             if (err == LFS_ERR_NOSPC) { | ||||||
|                 goto exhausted; |                 goto exhausted; | ||||||
|             } |             } | ||||||
|         }     |         } | ||||||
|  |  | ||||||
|         for (uint32_t i = 0; i < FILES; i++) { |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|             // check for errors |             // check for errors | ||||||
| @@ -137,7 +137,7 @@ code = ''' | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             lfs_file_close(&lfs, &file) => 0; |             lfs_file_close(&lfs, &file) => 0; | ||||||
|         }     |         } | ||||||
|         lfs_unmount(&lfs) => 0; |         lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|         cycle += 1; |         cycle += 1; | ||||||
| @@ -150,7 +150,7 @@ exhausted: | |||||||
|         // check for errors |         // check for errors | ||||||
|         sprintf(path, "test%d", i); |         sprintf(path, "test%d", i); | ||||||
|         lfs_stat(&lfs, path, &info) => 0; |         lfs_stat(&lfs, path, &info) => 0; | ||||||
|     }     |     } | ||||||
|     lfs_unmount(&lfs) => 0; |     lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|     LFS_WARN("completed %d cycles", cycle); |     LFS_WARN("completed %d cycles", cycle); | ||||||
| @@ -207,7 +207,7 @@ code = ''' | |||||||
|                 if (err == LFS_ERR_NOSPC) { |                 if (err == LFS_ERR_NOSPC) { | ||||||
|                     goto exhausted; |                     goto exhausted; | ||||||
|                 } |                 } | ||||||
|             }     |             } | ||||||
|  |  | ||||||
|             for (uint32_t i = 0; i < FILES; i++) { |             for (uint32_t i = 0; i < FILES; i++) { | ||||||
|                 // check for errors |                 // check for errors | ||||||
| @@ -224,7 +224,7 @@ code = ''' | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 lfs_file_close(&lfs, &file) => 0; |                 lfs_file_close(&lfs, &file) => 0; | ||||||
|             }     |             } | ||||||
|             lfs_unmount(&lfs) => 0; |             lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|             cycle += 1; |             cycle += 1; | ||||||
| @@ -237,7 +237,7 @@ exhausted: | |||||||
|             // check for errors |             // check for errors | ||||||
|             sprintf(path, "roadrunner/test%d", i); |             sprintf(path, "roadrunner/test%d", i); | ||||||
|             lfs_stat(&lfs, path, &info) => 0; |             lfs_stat(&lfs, path, &info) => 0; | ||||||
|         }     |         } | ||||||
|         lfs_unmount(&lfs) => 0; |         lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|         run_cycles[run] = cycle; |         run_cycles[run] = cycle; | ||||||
| @@ -292,7 +292,7 @@ code = ''' | |||||||
|                 if (err == LFS_ERR_NOSPC) { |                 if (err == LFS_ERR_NOSPC) { | ||||||
|                     goto exhausted; |                     goto exhausted; | ||||||
|                 } |                 } | ||||||
|             }     |             } | ||||||
|  |  | ||||||
|             for (uint32_t i = 0; i < FILES; i++) { |             for (uint32_t i = 0; i < FILES; i++) { | ||||||
|                 // check for errors |                 // check for errors | ||||||
| @@ -309,7 +309,7 @@ code = ''' | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 lfs_file_close(&lfs, &file) => 0; |                 lfs_file_close(&lfs, &file) => 0; | ||||||
|             }     |             } | ||||||
|             lfs_unmount(&lfs) => 0; |             lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|             cycle += 1; |             cycle += 1; | ||||||
| @@ -322,7 +322,7 @@ exhausted: | |||||||
|             // check for errors |             // check for errors | ||||||
|             sprintf(path, "test%d", i); |             sprintf(path, "test%d", i); | ||||||
|             lfs_stat(&lfs, path, &info) => 0; |             lfs_stat(&lfs, path, &info) => 0; | ||||||
|         }     |         } | ||||||
|         lfs_unmount(&lfs) => 0; |         lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|         run_cycles[run] = cycle; |         run_cycles[run] = cycle; | ||||||
| @@ -333,3 +333,113 @@ exhausted: | |||||||
|     // check we increased the lifetime by 2x with ~10% error |     // check we increased the lifetime by 2x with ~10% error | ||||||
|     LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); |     LFS_ASSERT(run_cycles[1]*110/100 > 2*run_cycles[0]); | ||||||
| ''' | ''' | ||||||
|  |  | ||||||
|  | [[case]] # test that we wear blocks roughly evenly | ||||||
|  | define.LFS_ERASE_CYCLES = 0xffffffff | ||||||
|  | define.LFS_BLOCK_COUNT = 256 # small bd so test runs faster | ||||||
|  | define.LFS_BLOCK_CYCLES = [5, 4, 3, 2, 1] | ||||||
|  | #define.LFS_BLOCK_CYCLES = [4, 2] | ||||||
|  | define.CYCLES = 100 | ||||||
|  | define.FILES = 10 | ||||||
|  | code = ''' | ||||||
|  |     lfs_format(&lfs, &cfg) => 0; | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     lfs_mkdir(&lfs, "roadrunner") => 0; | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|  |     uint32_t cycle = 0; | ||||||
|  |     while (cycle < CYCLES) { | ||||||
|  |         lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |             // chose name, roughly random seed, and random 2^n size | ||||||
|  |             sprintf(path, "roadrunner/test%d", i); | ||||||
|  |             srand(cycle * i); | ||||||
|  |             size = 1 << 4; //((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |             lfs_file_open(&lfs, &file, path, | ||||||
|  |                     LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||||
|  |  | ||||||
|  |             for (lfs_size_t j = 0; j < size; j++) { | ||||||
|  |                 char c = 'a' + (rand() % 26); | ||||||
|  |                 lfs_ssize_t res = lfs_file_write(&lfs, &file, &c, 1); | ||||||
|  |                 assert(res == 1 || res == LFS_ERR_NOSPC); | ||||||
|  |                 if (res == LFS_ERR_NOSPC) { | ||||||
|  |                     goto exhausted; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             err = lfs_file_close(&lfs, &file); | ||||||
|  |             assert(err == 0 || err == LFS_ERR_NOSPC); | ||||||
|  |             if (err == LFS_ERR_NOSPC) { | ||||||
|  |                 goto exhausted; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |             // check for errors | ||||||
|  |             sprintf(path, "roadrunner/test%d", i); | ||||||
|  |             srand(cycle * i); | ||||||
|  |             size = 1 << 4; //((rand() % 10)+2); | ||||||
|  |  | ||||||
|  |             lfs_file_open(&lfs, &file, path, LFS_O_RDONLY) => 0; | ||||||
|  |             for (lfs_size_t j = 0; j < size; j++) { | ||||||
|  |                 char c = 'a' + (rand() % 26); | ||||||
|  |                 char r; | ||||||
|  |                 lfs_file_read(&lfs, &file, &r, 1) => 1; | ||||||
|  |                 assert(r == c); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             lfs_file_close(&lfs, &file) => 0; | ||||||
|  |         } | ||||||
|  |         lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|  |         cycle += 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | exhausted: | ||||||
|  |     // should still be readable | ||||||
|  |     lfs_mount(&lfs, &cfg) => 0; | ||||||
|  |     for (uint32_t i = 0; i < FILES; i++) { | ||||||
|  |         // check for errors | ||||||
|  |         sprintf(path, "roadrunner/test%d", i); | ||||||
|  |         lfs_stat(&lfs, path, &info) => 0; | ||||||
|  |     } | ||||||
|  |     lfs_unmount(&lfs) => 0; | ||||||
|  |  | ||||||
|  |     LFS_WARN("completed %d cycles", cycle); | ||||||
|  |  | ||||||
|  |     // check the wear on our block device | ||||||
|  |     lfs_testbd_wear_t minwear = -1; | ||||||
|  |     lfs_testbd_wear_t totalwear = 0; | ||||||
|  |     lfs_testbd_wear_t maxwear = 0; | ||||||
|  |     // skip 0 and 1 as superblock movement is intentionally avoided | ||||||
|  |     for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { | ||||||
|  |         lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); | ||||||
|  |         printf("%08x: wear %d\n", b, wear); | ||||||
|  |         assert(wear >= 0); | ||||||
|  |         if (wear < minwear) { | ||||||
|  |             minwear = wear; | ||||||
|  |         } | ||||||
|  |         if (wear > maxwear) { | ||||||
|  |             maxwear = wear; | ||||||
|  |         } | ||||||
|  |         totalwear += wear; | ||||||
|  |     } | ||||||
|  |     lfs_testbd_wear_t avgwear = totalwear / LFS_BLOCK_COUNT; | ||||||
|  |     LFS_WARN("max wear: %d cycles", maxwear); | ||||||
|  |     LFS_WARN("avg wear: %d cycles", totalwear / LFS_BLOCK_COUNT); | ||||||
|  |     LFS_WARN("min wear: %d cycles", minwear); | ||||||
|  |  | ||||||
|  |     // find standard deviation^2 | ||||||
|  |     lfs_testbd_wear_t dev2 = 0; | ||||||
|  |     for (lfs_block_t b = 2; b < LFS_BLOCK_COUNT; b++) { | ||||||
|  |         lfs_testbd_wear_t wear = lfs_testbd_getwear(&cfg, b); | ||||||
|  |         assert(wear >= 0); | ||||||
|  |         lfs_testbd_swear_t diff = wear - avgwear; | ||||||
|  |         dev2 += diff*diff; | ||||||
|  |     } | ||||||
|  |     dev2 /= totalwear; | ||||||
|  |     LFS_WARN("std dev^2: %d", dev2); | ||||||
|  |     assert(dev2 < 8); | ||||||
|  | ''' | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user