mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	Added revision sum as tracking for filesystem stability
The idea is pretty simple: The revision count for each metadata block only really needs 3 distinct states. The additional bit-width of the revision count is available for other information. In this case, we can sum all revision counts in the filesystem to encode state that can be updated for free during any metadata block update. The obvious state to store first is if the filesystem is in a bad state that requires a deorphan step. The allows us to sidestep the expensive O(n^2) operation for the O(n) sum in the much more common case. This patch adds the following rule: If the sum of all revision count is odd, the filesystem was caught mid-operation and must be deorphaned.
This commit is contained in:
		
							
								
								
									
										49
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -358,9 +358,9 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_dir_t *dir) { | |||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |     lfs->sum += dir->d.rev; | ||||||
|  |  | ||||||
|     // set defaults |     // set defaults | ||||||
|     dir->d.rev += 1; |  | ||||||
|     dir->d.size = sizeof(dir->d)+4; |     dir->d.size = sizeof(dir->d)+4; | ||||||
|     dir->d.tail[0] = -1; |     dir->d.tail[0] = -1; | ||||||
|     dir->d.tail[1] = -1; |     dir->d.tail[1] = -1; | ||||||
| @@ -431,8 +431,10 @@ struct lfs_region { | |||||||
|  |  | ||||||
| static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, | static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, | ||||||
|         const struct lfs_region *regions, int count) { |         const struct lfs_region *regions, int count) { | ||||||
|     // increment revision count |     // increment rev such that, even == stable, odd == unstable | ||||||
|     dir->d.rev += 1; |     uint32_t diff = 1 + ((lfs->sum & 0x1) ^ !lfs->unstable); | ||||||
|  |     dir->d.rev += diff; | ||||||
|  |     lfs->sum += diff; | ||||||
|  |  | ||||||
|     // keep pairs in order such that pair[0] is most recent |     // keep pairs in order such that pair[0] is most recent | ||||||
|     lfs_pairswap(dir->pair); |     lfs_pairswap(dir->pair); | ||||||
| @@ -626,6 +628,7 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { | |||||||
|                     {entry->off, lfs_entry_size(entry), NULL, 0}, |                     {entry->off, lfs_entry_size(entry), NULL, 0}, | ||||||
|                 }, 1); |                 }, 1); | ||||||
|         } else { |         } else { | ||||||
|  |             lfs->sum -= dir->d.rev; | ||||||
|             pdir.d.size &= dir->d.size | 0x7fffffff; |             pdir.d.size &= dir->d.size | 0x7fffffff; | ||||||
|             pdir.d.tail[0] = dir->d.tail[0]; |             pdir.d.tail[0] = dir->d.tail[0]; | ||||||
|             pdir.d.tail[1] = dir->d.tail[1]; |             pdir.d.tail[1] = dir->d.tail[1]; | ||||||
| @@ -788,7 +791,7 @@ static int lfs_dir_find(lfs_t *lfs, lfs_dir_t *dir, | |||||||
| /// Top level directory operations /// | /// Top level directory operations /// | ||||||
| int lfs_mkdir(lfs_t *lfs, const char *path) { | int lfs_mkdir(lfs_t *lfs, const char *path) { | ||||||
|     // deorphan if we haven't yet, needed at most once after poweron |     // deorphan if we haven't yet, needed at most once after poweron | ||||||
|     if (!lfs->deorphaned) { |     if (lfs->unstable) { | ||||||
|         int err = lfs_deorphan(lfs); |         int err = lfs_deorphan(lfs); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
| @@ -1161,7 +1164,7 @@ static int lfs_index_traverse(lfs_t *lfs, | |||||||
| int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||||
|         const char *path, int flags) { |         const char *path, int flags) { | ||||||
|     // deorphan if we haven't yet, needed at most once after poweron |     // deorphan if we haven't yet, needed at most once after poweron | ||||||
|     if ((flags & 3) != LFS_O_RDONLY && !lfs->deorphaned) { |     if ((flags & 3) != LFS_O_RDONLY && lfs->unstable) { | ||||||
|         int err = lfs_deorphan(lfs); |         int err = lfs_deorphan(lfs); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
| @@ -1654,7 +1657,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { | |||||||
|  |  | ||||||
| int lfs_remove(lfs_t *lfs, const char *path) { | int lfs_remove(lfs_t *lfs, const char *path) { | ||||||
|     // deorphan if we haven't yet, needed at most once after poweron |     // deorphan if we haven't yet, needed at most once after poweron | ||||||
|     if (!lfs->deorphaned) { |     if (lfs->unstable) { | ||||||
|         int err = lfs_deorphan(lfs); |         int err = lfs_deorphan(lfs); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
| @@ -1687,6 +1690,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // remove the entry |     // remove the entry | ||||||
|  |     lfs->unstable += (entry.d.type == LFS_TYPE_DIR); | ||||||
|     err = lfs_dir_remove(lfs, &cwd, &entry); |     err = lfs_dir_remove(lfs, &cwd, &entry); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
| @@ -1700,9 +1704,11 @@ int lfs_remove(lfs_t *lfs, const char *path) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         assert(res); // must have pred |         assert(res); // must have pred | ||||||
|  |         lfs->sum -= dir.d.rev; | ||||||
|         cwd.d.tail[0] = dir.d.tail[0]; |         cwd.d.tail[0] = dir.d.tail[0]; | ||||||
|         cwd.d.tail[1] = dir.d.tail[1]; |         cwd.d.tail[1] = dir.d.tail[1]; | ||||||
|  |  | ||||||
|  |         lfs->unstable -= 1; | ||||||
|         int err = lfs_dir_commit(lfs, &cwd, NULL, 0); |         int err = lfs_dir_commit(lfs, &cwd, NULL, 0); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
| @@ -1714,7 +1720,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { | |||||||
|  |  | ||||||
| int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||||
|     // deorphan if we haven't yet, needed at most once after poweron |     // deorphan if we haven't yet, needed at most once after poweron | ||||||
|     if (!lfs->deorphaned) { |     if (lfs->unstable) { | ||||||
|         int err = lfs_deorphan(lfs); |         int err = lfs_deorphan(lfs); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
| @@ -1769,6 +1775,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // mark as moving |     // mark as moving | ||||||
|  |     lfs->unstable += 1 + (prevexists && preventry.d.type == LFS_TYPE_DIR); | ||||||
|     oldentry.d.type |= 0x80; |     oldentry.d.type |= 0x80; | ||||||
|     err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL); |     err = lfs_dir_update(lfs, &oldcwd, &oldentry, NULL); | ||||||
|     if (err) { |     if (err) { | ||||||
| @@ -1804,6 +1811,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // remove old entry |     // remove old entry | ||||||
|  |     lfs->unstable -= 1; | ||||||
|     err = lfs_dir_remove(lfs, &oldcwd, &oldentry); |     err = lfs_dir_remove(lfs, &oldcwd, &oldentry); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
| @@ -1817,9 +1825,11 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         assert(res); // must have pred |         assert(res); // must have pred | ||||||
|  |         lfs->sum -= dir.d.rev; | ||||||
|         newcwd.d.tail[0] = dir.d.tail[0]; |         newcwd.d.tail[0] = dir.d.tail[0]; | ||||||
|         newcwd.d.tail[1] = dir.d.tail[1]; |         newcwd.d.tail[1] = dir.d.tail[1]; | ||||||
|  |  | ||||||
|  |         lfs->unstable -= 1; | ||||||
|         int err = lfs_dir_commit(lfs, &newcwd, NULL, 0); |         int err = lfs_dir_commit(lfs, &newcwd, NULL, 0); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
| @@ -1873,7 +1883,8 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|     lfs->root[0] = 0xffffffff; |     lfs->root[0] = 0xffffffff; | ||||||
|     lfs->root[1] = 0xffffffff; |     lfs->root[1] = 0xffffffff; | ||||||
|     lfs->files = NULL; |     lfs->files = NULL; | ||||||
|     lfs->deorphaned = false; |     lfs->unstable = false; | ||||||
|  |     lfs->sum = 0; | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| @@ -2007,6 +2018,8 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|     if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { |     if (err || memcmp(superblock.d.magic, "littlefs", 8) != 0) { | ||||||
|         LFS_ERROR("Invalid superblock at %d %d", dir.pair[0], dir.pair[1]); |         LFS_ERROR("Invalid superblock at %d %d", dir.pair[0], dir.pair[1]); | ||||||
|         return LFS_ERR_CORRUPT; |         return LFS_ERR_CORRUPT; | ||||||
|  |     } else if (err) { | ||||||
|  |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (superblock.d.version > (0x00010001 | 0x0000ffff)) { |     if (superblock.d.version > (0x00010001 | 0x0000ffff)) { | ||||||
| @@ -2016,6 +2029,22 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|         return LFS_ERR_INVAL; |         return LFS_ERR_INVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // sum rev counts in fs, even = stable, odd = unstable | ||||||
|  |     lfs->sum += dir.d.rev; | ||||||
|  |     while (!lfs_pairisnull(dir.d.tail)) { | ||||||
|  |         int err = lfs_dir_fetch(lfs, &dir, dir.d.tail); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs->sum += dir.d.rev; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (lfs->sum & 0x1) { | ||||||
|  |         LFS_DEBUG("Power-loss detected %x", lfs->sum); | ||||||
|  |         lfs->unstable += 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -2212,6 +2241,7 @@ static int lfs_relocate(lfs_t *lfs, | |||||||
|         entry.d.u.dir[0] = newpair[0]; |         entry.d.u.dir[0] = newpair[0]; | ||||||
|         entry.d.u.dir[1] = newpair[1]; |         entry.d.u.dir[1] = newpair[1]; | ||||||
|  |  | ||||||
|  |         lfs->unstable += 1; | ||||||
|         int err = lfs_dir_update(lfs, &parent, &entry, NULL); |         int err = lfs_dir_update(lfs, &parent, &entry, NULL); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
| @@ -2247,8 +2277,6 @@ static int lfs_relocate(lfs_t *lfs, | |||||||
| } | } | ||||||
|  |  | ||||||
| int lfs_deorphan(lfs_t *lfs) { | int lfs_deorphan(lfs_t *lfs) { | ||||||
|     lfs->deorphaned = true; |  | ||||||
|  |  | ||||||
|     if (lfs_pairisnull(lfs->root)) { |     if (lfs_pairisnull(lfs->root)) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| @@ -2347,6 +2375,7 @@ int lfs_deorphan(lfs_t *lfs) { | |||||||
|         memcpy(&pdir, &cwd, sizeof(pdir)); |         memcpy(&pdir, &cwd, sizeof(pdir)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     lfs->unstable -= 1; | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user