From 9366674416a3a86e14d086a8d7d3cd0f014804f3 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 18 Mar 2024 02:15:46 -0500 Subject: [PATCH] Replaced separate BLOCKSIZE/BLOCKCOUNT attrs with single GEOMETRY attr This saves a bit of rbyd overhead, since these almost always come together. Perhaps more interesting, it carves out space for storing mroot-anchor redundancy information. This uses the lowest two bits of the GEOMETRY tag to indicate how many redundant blocks belong to the mroot-anchor: LFSR_TAG_GEOMETRY 0x0008 v--- ---- ---- 1-rr This solves a bit of a hole in our redundancy encoding. The plan is for this info to be stored in the lowest two bits of every pointer, but the mroot-anchor doesn't really have a pointer. Though this is just future plans. Right now the redundancy information is unused. Current implementations should use the GEOMETRY tag 0x0009, which you may notice implied redundancy level-1. This matches our current 2-block per mdir default. Geometry attr encoding: .---+---+---+---. tag (0x0008+r): 1 be16 2 bytes |x0008+r| 0 |siz| weight (0): 1 leb128 1 byte +---+---+---+---+ size: 1 leb128 1 byte | block_size | block_size: 1 leb128 <=4 bytes +---+- -+- -+- -+- -. | block_count | block_count: 1 leb128 <=5 bytes '---+- -+- -+- -+- -' total: <=13 bytes Code changes: code stack before: 34092 2880 after: 34040 (-0.2%) 2880 (+0.0%) --- lfs.c | 118 ++++++++++++++++++++++++++++---------------- scripts/dbgbmap.py | 7 ++- scripts/dbgbtree.py | 10 ++-- scripts/dbglfs.py | 41 ++++++--------- scripts/dbgmtree.py | 10 ++-- scripts/dbgrbyd.py | 10 ++-- 6 files changed, 105 insertions(+), 91 deletions(-) diff --git a/lfs.c b/lfs.c index 2f7a17e3..1209c053 100644 --- a/lfs.c +++ b/lfs.c @@ -721,10 +721,9 @@ enum lfsr_tag { LFSR_TAG_RCOMPAT = 0x0005, LFSR_TAG_WCOMPAT = 0x0006, LFSR_TAG_OCOMPAT = 0x0007, - LFSR_TAG_BLOCKSIZE = 0x0008, - LFSR_TAG_BLOCKCOUNT = 0x0009, - LFSR_TAG_NAMELIMIT = 0x000a, - LFSR_TAG_SIZELIMIT = 0x000b, + LFSR_TAG_GEOMETRY = 0x0009, + LFSR_TAG_NAMELIMIT = 0x000c, + LFSR_TAG_SIZELIMIT = 0x000d, // global-state tags LFSR_TAG_GDELTA = 0x0100, @@ -7884,6 +7883,59 @@ static int lfsr_data_readwcompat(lfs_t *lfs, lfsr_data_t *data, } +// disk geometry +// +// note these are stored minus 1 to avoid overflow issues +typedef struct lfsr_geometry { + lfs_off_t block_size; + lfs_off_t block_count; +} lfsr_geometry_t; + +// geometry encoding +// .---+- -+- -+- -. +// | block_size | blocksize: <=4 bytes +// +---+- -+- -+- -+- -. +// | block_count | blockcount: <=5 bytes +// '---+- -+- -+- -+- -' total: <=9 bytes +#define LFSR_GEOMETRY_DSIZE (4+5) + +#define LFSR_DATA_FROMGEOMETRY(_geometry) \ + lfsr_data_fromgeometry(_geometry, (uint8_t[LFSR_GEOMETRY_DSIZE]){0}) + +static lfsr_data_t lfsr_data_fromgeometry(const lfsr_geometry_t *geometry, + uint8_t buffer[static LFSR_GEOMETRY_DSIZE]) { + lfs_ssize_t d = 0; + lfs_ssize_t d_ = lfs_toleb128(geometry->block_size-1, &buffer[d], 4); + LFS_ASSERT(d_ >= 0); + d += d_; + + d_ = lfs_toleb128(geometry->block_count-1, &buffer[d], 5); + LFS_ASSERT(d_ >= 0); + d += d_; + + return LFSR_DATA_BUF(buffer, d); +} + +static int lfsr_data_readgeometry(lfs_t *lfs, lfsr_data_t *data, + lfsr_geometry_t *geometry) { + int err = lfsr_data_readlleb128(lfs, data, + (uint32_t*)&geometry->block_size); + if (err) { + return err; + } + + err = lfsr_data_readleb128(lfs, data, + (uint32_t*)&geometry->block_count); + if (err) { + return err; + } + + geometry->block_size += 1; + geometry->block_count += 1; + return 0; +} + + /// Filesystem init functions /// static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg); @@ -7993,52 +8045,33 @@ static int lfsr_mountmroot(lfs_t *lfs, const lfsr_mdir_t *mroot) { // we don't bother to check for any ocompatflags, we would just // ignore these anyways - // check block size - err = lfsr_mdir_lookup(lfs, mroot, LFSR_TAG_BLOCKSIZE, + // check the on-disk geometry + err = lfsr_mdir_lookup(lfs, mroot, LFSR_TAG_GEOMETRY, &data); - if (err && err != LFS_ERR_NOENT) { + if (err) { + if (err == LFS_ERR_NOENT) { + LFS_ERROR("No geometry found"); + return LFS_ERR_INVAL; + } return err; } - uint32_t block_size = 0; - if (err != LFS_ERR_NOENT) { - err = lfsr_data_readleb128(lfs, &data, &block_size); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - if (err == LFS_ERR_CORRUPT) { - block_size = -1; - } + lfsr_geometry_t geometry; + err = lfsr_data_readgeometry(lfs, &data, &geometry); + if (err) { + return err; } - if (block_size != lfs->cfg->block_size-1) { + if (geometry.block_size != lfs->cfg->block_size) { LFS_ERROR("Incompatible block size %"PRId32" (!= %"PRId32")", - block_size+1, + geometry.block_size, lfs->cfg->block_size); return LFS_ERR_INVAL; } - // check block count - err = lfsr_mdir_lookup(lfs, mroot, LFSR_TAG_BLOCKCOUNT, - &data); - if (err && err != LFS_ERR_NOENT) { - return err; - } - - uint32_t block_count = 0; - if (err != LFS_ERR_NOENT) { - err = lfsr_data_readleb128(lfs, &data, &block_count); - if (err && err != LFS_ERR_CORRUPT) { - return err; - } - if (err == LFS_ERR_CORRUPT) { - block_count = -1; - } - } - - if (block_count != lfs->cfg->block_count-1) { + if (geometry.block_count != lfs->cfg->block_count) { LFS_ERROR("Incompatible block count %"PRId32" (!= %"PRId32")", - block_count+1, + geometry.block_count, lfs->cfg->block_count); return LFS_ERR_INVAL; } @@ -8280,11 +8313,10 @@ static int lfsr_formatinited(lfs_t *lfs) { LFSR_TAG_RCOMPAT, 0, LFSR_DATA_FROMRCOMPAT(LFSR_RCOMPAT_COMPAT)), LFSR_ATTR( - LFSR_TAG_BLOCKSIZE, 0, - LFSR_DATA_LEB128(lfs->cfg->block_size-1)), - LFSR_ATTR( - LFSR_TAG_BLOCKCOUNT, 0, - LFSR_DATA_LEB128(lfs->cfg->block_count-1)), + LFSR_TAG_GEOMETRY, 0, + LFSR_DATA_FROMGEOMETRY((&(lfsr_geometry_t){ + lfs->cfg->block_size, + lfs->cfg->block_count}))), LFSR_ATTR( LFSR_TAG_NAMELIMIT, 0, LFSR_DATA_LEB128(lfs->name_limit)), diff --git a/scripts/dbgbmap.py b/scripts/dbgbmap.py index 2dcb0ffe..91474f31 100755 --- a/scripts/dbgbmap.py +++ b/scripts/dbgbmap.py @@ -17,10 +17,9 @@ TAG_VERSION = 0x0004 TAG_RCOMPAT = 0x0005 TAG_WCOMPAT = 0x0006 TAG_OCOMPAT = 0x0007 -TAG_BLOCKSIZE = 0x0008 -TAG_BLOCKCOUNT = 0x0009 -TAG_NAMELIMIT = 0x000a -TAG_SIZELIMIT = 0x000b +TAG_GEOMETRY = 0x0009 +TAG_NAMELIMIT = 0x000c +TAG_SIZELIMIT = 0x000d TAG_GDELTA = 0x0100 TAG_GRMDELTA = 0x0100 TAG_NAME = 0x0200 diff --git a/scripts/dbgbtree.py b/scripts/dbgbtree.py index 276109d6..0a901dbb 100755 --- a/scripts/dbgbtree.py +++ b/scripts/dbgbtree.py @@ -15,10 +15,9 @@ TAG_VERSION = 0x0004 TAG_RCOMPAT = 0x0005 TAG_WCOMPAT = 0x0006 TAG_OCOMPAT = 0x0007 -TAG_BLOCKSIZE = 0x0008 -TAG_BLOCKCOUNT = 0x0009 -TAG_NAMELIMIT = 0x000a -TAG_SIZELIMIT = 0x000b +TAG_GEOMETRY = 0x0009 +TAG_NAMELIMIT = 0x000c +TAG_SIZELIMIT = 0x000d TAG_GDELTA = 0x0100 TAG_GRMDELTA = 0x0100 TAG_NAME = 0x0200 @@ -168,8 +167,7 @@ def tagrepr(tag, w, size, off=None): else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT - else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE - else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT + else 'geometry' if (tag & 0xfff) == TAG_GEOMETRY else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT else 'config 0x%02x' % (tag & 0xff), diff --git a/scripts/dbglfs.py b/scripts/dbglfs.py index d2dba779..83fe87a5 100755 --- a/scripts/dbglfs.py +++ b/scripts/dbglfs.py @@ -16,10 +16,9 @@ TAG_VERSION = 0x0004 TAG_RCOMPAT = 0x0005 TAG_WCOMPAT = 0x0006 TAG_OCOMPAT = 0x0007 -TAG_BLOCKSIZE = 0x0008 -TAG_BLOCKCOUNT = 0x0009 -TAG_NAMELIMIT = 0x000a -TAG_SIZELIMIT = 0x000b +TAG_GEOMETRY = 0x0009 +TAG_NAMELIMIT = 0x000c +TAG_SIZELIMIT = 0x000d TAG_GDELTA = 0x0100 TAG_GRMDELTA = 0x0100 TAG_NAME = 0x0200 @@ -199,8 +198,7 @@ def tagrepr(tag, w, size, off=None): else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT - else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE - else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT + else 'geometry' if (tag & 0xfff) == TAG_GEOMETRY else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT else 'config 0x%02x' % (tag & 0xff), @@ -1055,22 +1053,15 @@ class Config: return None @ft.cached_property - def block_size(self): - if TAG_BLOCKSIZE in self.config: - _, data = self.config[TAG_BLOCKSIZE] - block_size, _ = fromleb128(data) - return block_size + def geometry(self): + if TAG_GEOMETRY in self.config: + _, data = self.config[TAG_GEOMETRY] + d = 0 + block_size, d_ = fromleb128(data[d:]); d += d_ + block_count, d_ = fromleb128(data[d:]); d += d_ + return (block_size+1, block_count+1) else: - return None - - @ft.cached_property - def block_count(self): - if TAG_BLOCKCOUNT in self.config: - _, data = self.config[TAG_BLOCKCOUNT] - block_count, _ = fromleb128(data) - return block_count - else: - return None + return (None, None) @ft.cached_property def name_limit(self): @@ -1107,10 +1098,8 @@ class Config: elif tag == TAG_OCOMPAT: return 'ocompat 0x%s' % ''.join( '%x' % f for f in reversed(self.ocompat)) - elif tag == TAG_BLOCKSIZE: - return 'blocksize %d' % self.block_size - elif tag == TAG_BLOCKCOUNT: - return 'blockcount %d' % self.block_count + elif tag == TAG_GEOMETRY: + return 'geometry %dx%d' % self.geometry elif tag == TAG_SIZELIMIT: return 'sizelimit %d' % self.size_limit elif tag == TAG_NAMELIMIT: @@ -1865,7 +1854,7 @@ def main(disk, mroots=None, *, print('littlefs v%s.%s %dx%d %s, rev %d, weight %d.%d' % ( config.version[0] if config.version[0] is not None else '?', config.version[1] if config.version[1] is not None else '?', - (config.block_size or -1)+1, (config.block_count or -1)+1, + (config.geometry[0] or 0), (config.geometry[1] or 0), mroot.addr(), mroot.rev, bweight//mleaf_weight, 1*mleaf_weight)) # dynamically size the id field diff --git a/scripts/dbgmtree.py b/scripts/dbgmtree.py index c4d03245..807ed6c6 100755 --- a/scripts/dbgmtree.py +++ b/scripts/dbgmtree.py @@ -15,10 +15,9 @@ TAG_VERSION = 0x0004 TAG_RCOMPAT = 0x0005 TAG_WCOMPAT = 0x0006 TAG_OCOMPAT = 0x0007 -TAG_BLOCKSIZE = 0x0008 -TAG_BLOCKCOUNT = 0x0009 -TAG_NAMELIMIT = 0x000a -TAG_SIZELIMIT = 0x000b +TAG_GEOMETRY = 0x0009 +TAG_NAMELIMIT = 0x000c +TAG_SIZELIMIT = 0x000d TAG_GDELTA = 0x0100 TAG_GRMDELTA = 0x0100 TAG_NAME = 0x0200 @@ -183,8 +182,7 @@ def tagrepr(tag, w, size, off=None): else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT - else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE - else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT + else 'geometry' if (tag & 0xfff) == TAG_GEOMETRY else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT else 'config 0x%02x' % (tag & 0xff), diff --git a/scripts/dbgrbyd.py b/scripts/dbgrbyd.py index ae0e5c0e..50beb0b4 100755 --- a/scripts/dbgrbyd.py +++ b/scripts/dbgrbyd.py @@ -24,10 +24,9 @@ TAG_VERSION = 0x0004 TAG_RCOMPAT = 0x0005 TAG_WCOMPAT = 0x0006 TAG_OCOMPAT = 0x0007 -TAG_BLOCKSIZE = 0x0008 -TAG_BLOCKCOUNT = 0x0009 -TAG_NAMELIMIT = 0x000a -TAG_SIZELIMIT = 0x000b +TAG_GEOMETRY = 0x0009 +TAG_NAMELIMIT = 0x000c +TAG_SIZELIMIT = 0x000d TAG_GDELTA = 0x0100 TAG_GRMDELTA = 0x0100 TAG_NAME = 0x0200 @@ -170,8 +169,7 @@ def tagrepr(tag, w, size, off=None): else 'rcompat' if (tag & 0xfff) == TAG_RCOMPAT else 'wcompat' if (tag & 0xfff) == TAG_WCOMPAT else 'ocompat' if (tag & 0xfff) == TAG_OCOMPAT - else 'blocksize' if (tag & 0xfff) == TAG_BLOCKSIZE - else 'blockcount' if (tag & 0xfff) == TAG_BLOCKCOUNT + else 'geometry' if (tag & 0xfff) == TAG_GEOMETRY else 'sizelimit' if (tag & 0xfff) == TAG_SIZELIMIT else 'namelimit' if (tag & 0xfff) == TAG_NAMELIMIT else 'config 0x%02x' % (tag & 0xff),