mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 16:14:16 +01:00 
			
		
		
		
	Switched to separate-tag encoding of forward-looking CRCs
Previously forward-looking CRCs was just two new CRC types, one for
commits with forward-looking CRCs, one without. These both contained the
CRC needed to complete the current commit (note that the commit CRC
must come last!).
         [--   32   --|--   32   --|--   32   --|--   32   --]
with:    [  crc3 tag  | nprog size |  nprog crc | commit crc ]
without: [  crc2 tag  | commit crc ]
This meant there had to be several checks for the two possible structure
sizes, messying up the implementation.
         [--   32   --|--   32   --|--   32   --|--   32   --|--   32   --]
with:    [nprogcrc tag| nprog size |  nprog crc | commit tag | commit crc ]
without: [ commit tag | commit crc ]
But we already have a mechanism for storing optional metadata! The
different metadata tags! So why not use a separate tage for the
forward-looking CRC, separate from the commit CRC?
I wasn't sure this would actually help that much, there are still
necessary conditions for wether or not a forward-looking CRC is there,
but in the end it simplified the code quite nicely, and resulted in a ~200 byte
code-cost saving.
			
			
This commit is contained in:
		
							
								
								
									
										130
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										130
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -330,6 +330,10 @@ static inline uint16_t lfs_tag_type1(lfs_tag_t tag) { | ||||
|     return (tag & 0x70000000) >> 20; | ||||
| } | ||||
|  | ||||
| static inline uint16_t lfs_tag_type2(lfs_tag_t tag) { | ||||
|     return (tag & 0x78000000) >> 20; | ||||
| } | ||||
|  | ||||
| static inline uint16_t lfs_tag_type3(lfs_tag_t tag) { | ||||
|     return (tag & 0x7ff00000) >> 20; | ||||
| } | ||||
| @@ -916,6 +920,9 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|         bool tempsplit = false; | ||||
|         lfs_stag_t tempbesttag = besttag; | ||||
|  | ||||
|         bool hasestate = false; | ||||
|         struct lfs_estate estate; | ||||
|  | ||||
|         dir->rev = lfs_tole32(dir->rev); | ||||
|         uint32_t crc = lfs_crc(0xffffffff, &dir->rev, sizeof(dir->rev)); | ||||
|         dir->rev = lfs_fromle32(dir->rev); | ||||
| @@ -947,32 +954,12 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|  | ||||
|             ptag = tag; | ||||
|  | ||||
|             if (lfs_tag_type1(tag) == LFS_TYPE_CRC) { | ||||
|                 lfs_off_t noff = off + sizeof(tag); | ||||
|                 struct lfs_estate estate; | ||||
|  | ||||
|                 if (lfs_tag_chunk(tag) == 3) { | ||||
|                     err = lfs_bd_read(lfs, | ||||
|                             NULL, &lfs->rcache, lfs->cfg->block_size, | ||||
|                             dir->pair[0], noff, &estate, sizeof(estate)); | ||||
|                     if (err) { | ||||
|                         if (err == LFS_ERR_CORRUPT) { | ||||
|                             dir->erased = false; | ||||
|                             break; | ||||
|                         } | ||||
|                         return err; | ||||
|                     } | ||||
|  | ||||
|                     crc = lfs_crc(crc, &estate, sizeof(estate)); | ||||
|                     lfs_estate_fromle32(&estate); | ||||
|                     noff += sizeof(estate); | ||||
|                 } | ||||
|  | ||||
|             if (lfs_tag_type2(tag) == LFS_TYPE_CRC) { | ||||
|                 // check the crc attr | ||||
|                 uint32_t dcrc; | ||||
|                 err = lfs_bd_read(lfs, | ||||
|                         NULL, &lfs->rcache, lfs->cfg->block_size, | ||||
|                         dir->pair[0], noff, &dcrc, sizeof(dcrc)); | ||||
|                         dir->pair[0], off+sizeof(tag), &dcrc, sizeof(dcrc)); | ||||
|                 if (err) { | ||||
|                     if (err == LFS_ERR_CORRUPT) { | ||||
|                         dir->erased = false; | ||||
| @@ -1002,10 +989,7 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 dir->tail[1] = temptail[1]; | ||||
|                 dir->split = tempsplit; | ||||
|  | ||||
|                 // reset crc | ||||
|                 crc = 0xffffffff; | ||||
|  | ||||
|                 if (lfs_tag_chunk(tag) == 3) { | ||||
|                 if (hasestate) { | ||||
|                     // check if the next page is erased | ||||
|                     // | ||||
|                     // this may look inefficient, but since cache_size is | ||||
| @@ -1041,15 +1025,19 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                         dir->erased = true; | ||||
|                         break; | ||||
|                     } | ||||
|                 } else if (lfs_tag_chunk(tag) == 2) { | ||||
|                     // end of block commit | ||||
|                     dir->erased = false; | ||||
|                     break; | ||||
|                 } else { | ||||
|                 } else if (lfs_tag_chunk(tag) < 2) { | ||||
|                     // for backwards compatibility we fall through here on | ||||
|                     // unrecognized tags, leaving it up to the CRC to reject | ||||
|                     // bad commits | ||||
|                 } else { | ||||
|                     // end of block commit | ||||
|                     dir->erased = false; | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
|                 // reset crc | ||||
|                 crc = 0xffffffff; | ||||
|                 hasestate = false; | ||||
|             } else { | ||||
|                 // crc the entry first, hopefully leaving it in the cache | ||||
|                 err = lfs_bd_crc(lfs, | ||||
| @@ -1085,7 +1073,8 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|  | ||||
|                     err = lfs_bd_read(lfs, | ||||
|                             NULL, &lfs->rcache, lfs->cfg->block_size, | ||||
|                             dir->pair[0], off+sizeof(tag), &temptail, 8); | ||||
|                             dir->pair[0], off+sizeof(tag), | ||||
|                             &temptail, sizeof(temptail)); | ||||
|                     if (err) { | ||||
|                         if (err == LFS_ERR_CORRUPT) { | ||||
|                             dir->erased = false; | ||||
| @@ -1093,6 +1082,19 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                         } | ||||
|                     } | ||||
|                     lfs_pair_fromle32(temptail); | ||||
|                 } else if (lfs_tag_type1(tag) == LFS_TYPE_NPROGCRC) { | ||||
|                     err = lfs_bd_read(lfs, | ||||
|                             NULL, &lfs->rcache, lfs->cfg->block_size, | ||||
|                             dir->pair[0], off+sizeof(tag), | ||||
|                             &estate, sizeof(estate)); | ||||
|                     if (err) { | ||||
|                         if (err == LFS_ERR_CORRUPT) { | ||||
|                             dir->erased = false; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                     lfs_estate_fromle32(&estate); | ||||
|                     hasestate = true; | ||||
|                 } | ||||
|  | ||||
|                 // found a match for our fetcher? | ||||
| @@ -1434,13 +1436,9 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { | ||||
|     // - 4-word crc with estate to check following prog (middle of block) | ||||
|     // - 2-word crc with no following prog (end of block) | ||||
|     const lfs_off_t end = lfs_alignup( | ||||
|             lfs_min(commit->off + 4*sizeof(uint32_t), lfs->cfg->block_size), | ||||
|             lfs_min(commit->off + 5*sizeof(uint32_t), lfs->cfg->block_size), | ||||
|             lfs->cfg->prog_size); | ||||
|  | ||||
|     // clamp erase size to tag size, this gives us the full tag as potential | ||||
|     // to intentionally invalidate erase CRCs | ||||
|     const lfs_size_t esize = lfs_max(lfs->cfg->prog_size, sizeof(lfs_tag_t)); | ||||
|  | ||||
|     lfs_off_t off1 = 0; | ||||
|     uint32_t crc1 = 0; | ||||
|  | ||||
| @@ -1454,23 +1452,12 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { | ||||
|             noff = lfs_min(noff, end - 2*sizeof(uint32_t)); | ||||
|         } | ||||
|  | ||||
|         // build crc tag | ||||
|         // clamp erase size to tag size, this gives us the full tag as potential | ||||
|         // to intentionally invalidate erase CRCs | ||||
|         const lfs_size_t esize = lfs_max( | ||||
|                 lfs->cfg->prog_size, sizeof(lfs_tag_t)); | ||||
|         lfs_tag_t etag = 0; | ||||
|         lfs_tag_t tag = LFS_MKTAG(LFS_TYPE_CRC + 2, 0x3ff, noff - off); | ||||
|         lfs_size_t size = 2*sizeof(uint32_t); | ||||
|         struct { | ||||
|             lfs_tag_t tag; | ||||
|             union { | ||||
|                 struct { | ||||
|                     uint32_t crc; | ||||
|                 } crc2; | ||||
|                 struct { | ||||
|                     struct lfs_estate estate; | ||||
|                     uint32_t crc; | ||||
|                 } crc3; | ||||
|             } u; | ||||
|         } data; | ||||
|  | ||||
|         // space for estate? also only emit on last commit in padding commits | ||||
|         if (noff <= lfs->cfg->block_size - esize) { | ||||
|             // first we get a tag-worth of bits, this is so we can | ||||
|             // tweak our current tag to force future writes to be | ||||
| @@ -1491,36 +1478,43 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             data.u.crc3.estate.size = esize; | ||||
|             data.u.crc3.estate.crc = ecrc; | ||||
|             lfs_estate_tole32(&data.u.crc3.estate); | ||||
|  | ||||
|             // indicate we include estate | ||||
|             tag |= LFS_MKTAG(1, 0, 0); | ||||
|             size += sizeof(struct lfs_estate); | ||||
|             struct lfs_estate estate = {.size = esize, .crc = ecrc}; | ||||
|             lfs_estate_tole32(&estate); | ||||
|             err = lfs_dir_commitattr(lfs, commit, | ||||
|                     LFS_MKTAG(LFS_TYPE_NPROGCRC, 0x3ff, sizeof(estate)), | ||||
|                     &estate); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         data.tag = lfs_tobe32(tag ^ commit->ptag); | ||||
|         commit->crc = lfs_crc(commit->crc, &data, size-sizeof(uint32_t)); | ||||
|         ((uint32_t*)&data)[size/sizeof(uint32_t) - 1] = lfs_tole32(commit->crc); | ||||
|         // build crc state | ||||
|         off = commit->off + sizeof(lfs_tag_t); | ||||
|         struct { | ||||
|             lfs_tag_t tag; | ||||
|             uint32_t crc; | ||||
|         } cstate; | ||||
|         lfs_tag_t ctag = LFS_MKTAG(LFS_TYPE_COMMITCRC, 0x3ff, noff-off); | ||||
|         cstate.tag = lfs_tobe32(ctag ^ commit->ptag); | ||||
|         commit->crc = lfs_crc(commit->crc, &cstate.tag, sizeof(cstate.tag)); | ||||
|         cstate.crc = lfs_tole32(commit->crc); | ||||
|  | ||||
|         int err = lfs_bd_prog(lfs, | ||||
|                 &lfs->pcache, &lfs->rcache, false, | ||||
|                 commit->block, commit->off, &data, size); | ||||
|                 commit->block, commit->off, &cstate, sizeof(cstate)); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // keep track of non-padding checksum to verify | ||||
|         if (off1 == 0) { | ||||
|             //off1 = commit->off + sizeof(uint32_t); | ||||
|             off1 = commit->off + size-sizeof(uint32_t); | ||||
|             off1 = off; | ||||
|             crc1 = commit->crc; | ||||
|         } | ||||
|  | ||||
|         commit->off += sizeof(tag)+lfs_tag_size(tag); | ||||
|         commit->off = noff; | ||||
|         // perturb valid bit? | ||||
|         commit->ptag = tag | (0x80000000 & ~lfs_frombe32(etag)); | ||||
|         commit->ptag = ctag | (0x80000000 & ~lfs_frombe32(etag)); | ||||
|         // reset crc for next commit | ||||
|         commit->crc = 0xffffffff; | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user