From ae2644eb88d54cef315a55476bc925a1c1bca1a9 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 4 Jan 2024 22:56:25 -0600 Subject: [PATCH] Added LFS_O_SYNC, for implicit syncs during file writes The motivation for this comes from the observation that many users call sync on every file write. Much more than I expected. I think one reason is in embedded systems it's common to just write structs to disk, either the whole file or to a log. O_SYNC exists in POSIX/Lunix/etc, so it makes sense to provide in littlefs. In theory it's just one extra function call, and may even save in total application cost (though we don't measure this) by reducing the number of function calls at the application-level. --- Unfortunately in-practice turned out to be quite a bit different than in-theory... The main culprit being the improved guarantees around error atomicity... The ideal guarantee is that if there is an error during a write, the entire write operation is reverted. Combining this with O_SYNC means we need to hold a copy of the origin file state all thwe way through our sync call. This got a bit messy... The annoying part isn't even the functionality! Our system of tracking btree/bshrub snapshots is quite robust! The problems were entirely with: 1. Figuring out how the heck to avoid clobbering the old file buffer state. 2. Figuring out how the internal APIs should work while passing around a bunch of staging state. For 1., fortunately, thanks to bypassing writes, and some careful pointer manipulation, we can void buffer clobbing. And for 2. just some internal API work was needed. Internally all syncs end up in lfsr_ftree_sync, though this feels a bit clumsy since the functionality is not really ftree related... Unfortunately, all of this added up to quite a bit more code cost than I had hoped. In theory, adding some sort of LFS_CERAMIC/LFS_GLASS modes that relax error atomicity for code size could help with most of this? But it needs some thought: code stack before: 33324 3072 after: 33544 (+0.7%) 3072 (+0.0%) --- lfs.c | 243 ++++++++++++++++++++++++++--------------- lfs.h | 3 +- tests/test_fmulti.toml | 132 +++++++++++++++------- 3 files changed, 248 insertions(+), 130 deletions(-) diff --git a/lfs.c b/lfs.c index b61c2dfd..fdbedd8a 100644 --- a/lfs.c +++ b/lfs.c @@ -9241,6 +9241,10 @@ static inline bool lfsr_o_isappend(uint32_t flags) { return flags & LFS_O_APPEND; } +static inline bool lfsr_o_issync(uint32_t flags) { + return flags & LFS_O_SYNC; +} + static inline bool lfsr_o_isdesync(uint32_t flags) { return flags & LFS_O_DESYNC; } @@ -10454,6 +10458,74 @@ static int lfsr_ftree_flush(lfs_t *lfs, lfsr_ftree_t *ftree, return 0; } +static int lfsr_ftree_sync(lfs_t *lfs, lfsr_ftree_t *ftree, bool unflushed, + lfs_off_t buffer_pos, const uint8_t *buffer, lfs_size_t buffer_size) { + // note because of small-file caching and our current write + // strategy, we never actually end up with only a direct data + // or bptr + // + // this is convenient because bptrs are a bit annoying to commit + LFS_ASSERT(!lfsr_ftree_isbsprout(ftree)); + LFS_ASSERT(!lfsr_ftree_isbleaf(ftree)); + // small files should start as zero, const prop should optimize this out + LFS_ASSERT(!unflushed || buffer_pos == 0); + // small files/ftree should be exclusive here + LFS_ASSERT(!unflushed || lfsr_ftree_size(ftree) == 0); + // small files must be inlined entirely in our buffer + LFS_ASSERT(!unflushed + || (buffer_size <= lfs->cfg->cache_size + && buffer_size <= lfs->cfg->inline_size + && buffer_size <= lfs->cfg->fragment_size)); + + // commit our file's metadata + uint8_t buf[LFSR_BTREE_DSIZE]; + int err = lfsr_mdir_commit(lfs, &ftree->mdir, LFSR_ATTRS( + (unflushed && buffer_size == 0) + ? LFSR_ATTR(ftree->mdir.mid, + WIDE(RM(STRUCT)), 0, + NULL()) + : (unflushed) + ? LFSR_ATTR(ftree->mdir.mid, + WIDE(DATA), 0, + BUF(buffer, buffer_size)) + : (lfsr_ftree_isbshrub(ftree)) + ? LFSR_ATTR(ftree->mdir.mid, + WIDE(SHRUBTRUNK), 0, + SHRUBTRUNK(&ftree->u.bshrub)) + : LFSR_ATTR(ftree->mdir.mid, + WIDE(BTREE), 0, + FROMBTREE(&ftree->u.btree, buf)))); + if (err) { + return err; + } + + // update other file handles + for (lfsr_openedmdir_t *opened = lfs->opened[ + LFS_TYPE_REG-LFS_TYPE_REG]; + opened; + opened = opened->next) { + lfsr_file_t *file_ = (lfsr_file_t*)opened; + if (file_->ftree.mdir.mid == ftree->mdir.mid + // don't double update + && &file_->ftree != ftree + // don't update desynced file handles + && !lfsr_o_isdesync(file_->flags)) { + if (unflushed) { + file_->size = buffer_size; + } else { + file_->size = lfsr_ftree_size(ftree); + } + file_->ftree.u = ftree->u; + file_->buffer_pos = buffer_pos; + LFS_ASSERT(buffer_size <= lfs->cfg->cache_size); + memcpy(file_->buffer, buffer, buffer_size); + file_->buffer_size = buffer_size; + } + } + + return 0; +} + // our high-level file operations // needed in lfsr_file_read @@ -10567,9 +10639,9 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file, } // copy state so we can recover from errors - lfs_off_t pos_ = file->pos; bool unflushed_ = lfsr_f_isunflushed(file->flags); lfs_off_t buffer_pos_ = file->buffer_pos; + const uint8_t *buffer_ = file->buffer; lfs_size_t buffer_size_ = file->buffer_size; lfsr_ftree_t ftree_ = file->ftree; // add to tracked mdirs @@ -10580,24 +10652,25 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file, lfs_alloc_ckpoint(lfs); // update pos if we are appending + lfs_off_t pos__ = file->pos; if (lfsr_o_isappend(file->flags)) { - pos_ = file->size; + pos__ = file->size; } // if we're a small file, we may need to append zeros - if (pos_ > file->size - && pos_ <= lfs->cfg->cache_size - && pos_ <= lfs->cfg->inline_size - && pos_ <= lfs->cfg->fragment_size) { + if (pos__ > file->size + && pos__ <= lfs->cfg->cache_size + && pos__ <= lfs->cfg->inline_size + && pos__ <= lfs->cfg->fragment_size) { LFS_ASSERT(unflushed_); LFS_ASSERT(file->size == buffer_size_); memset(&file->buffer[buffer_size_], 0, - pos_ - buffer_size_); - buffer_size_ = pos_; + pos__ - buffer_size_); + buffer_size_ = pos__; } - const uint8_t *buffer_ = buffer; + const uint8_t *buffer__ = buffer; while (size > 0) { // bypass buffer? // @@ -10607,7 +10680,7 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file, // if (!unflushed_ && size >= lfs->cfg->cache_size) { err = lfsr_ftree_flush(lfs, &ftree_, - pos_, buffer_, size); + pos__, buffer__, size); if (err) { goto failed; } @@ -10616,14 +10689,12 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file, // // note we need to clear the buffer anyways to avoid any // out-of-date data - memcpy(file->buffer, - &buffer_[size - lfs->cfg->cache_size], - lfs->cfg->cache_size); - buffer_pos_ = pos_ + size - lfs->cfg->cache_size; + buffer_pos_ = pos__ + size - lfs->cfg->cache_size; + buffer_ = &buffer__[size - lfs->cfg->cache_size]; buffer_size_ = lfs->cfg->cache_size; - pos_ += size; - buffer_ += size; + pos__ += size; + buffer__ += size; size -= size; continue; } @@ -10639,57 +10710,101 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file, // buffer once, and flush at most twice. // if (!unflushed_ - || (pos_ >= buffer_pos_ - && pos_ <= buffer_pos_ + buffer_size_ - && pos_ < buffer_pos_ + lfs->cfg->cache_size)) { + || (pos__ >= buffer_pos_ + && pos__ <= buffer_pos_ + buffer_size_ + && pos__ < buffer_pos_ + lfs->cfg->cache_size)) { // unused buffer? we can move it where we need it + lfs_size_t d; if (!unflushed_) { - buffer_pos_ = pos_; - buffer_size_ = 0; + d = lfs_min32( + size, + lfs->cfg->cache_size); + buffer_pos_ = pos__; + buffer_ = buffer__; + buffer_size_ = d; + } else { + d = lfs_min32( + size, + lfs->cfg->cache_size - (pos__ - buffer_pos_)); + LFS_ASSERT(file->buffer == buffer_); + memcpy(&file->buffer[pos__ - buffer_pos_], buffer__, d); + buffer_size_ = lfs_max32( + buffer_size_, + pos__+d - buffer_pos_); } - lfs_size_t d = lfs_min32( - size, - lfs->cfg->cache_size - (pos_ - buffer_pos_)); - memcpy(&file->buffer[pos_ - buffer_pos_], buffer_, d); - buffer_size_ = lfs_max32( - buffer_size_, - pos_+d - buffer_pos_); - unflushed_ = true; - pos_ += d; - buffer_ += d; + pos__ += d; + buffer__ += d; size -= d; continue; } // flush our buffer so the above can't fail err = lfsr_ftree_flush(lfs, &ftree_, - buffer_pos_, file->buffer, buffer_size_); + buffer_pos_, buffer_, buffer_size_); if (err) { goto failed; } unflushed_ = false; } + // sync if requested + lfs_off_t size__ = lfs_max32(file->size, pos__); + bool unsynced_ = true; + if (lfsr_o_issync(file->flags)) { + // syncing requires a flush for non-small files + if (unflushed_ && !( + size__ <= lfs->cfg->cache_size + && size__ <= lfs->cfg->inline_size + && size__ <= lfs->cfg->fragment_size)) { + // flush + err = lfsr_ftree_flush(lfs, &ftree_, + buffer_pos_, buffer_, buffer_size_); + if (err) { + goto failed; + } + unflushed_ = false; + } + + // sync + err = lfsr_ftree_sync(lfs, &ftree_, unflushed_, + buffer_pos_, buffer_, buffer_size_); + if (err) { + goto failed; + } + + // mark as in-sync + unsynced_ = false; + file->flags &= ~LFS_O_DESYNC; + } + // remove from tracked mdirs lfsr_mdir_removeopened(lfs, LFS_TYPE_REG, (lfsr_openedmdir_t*)&ftree_); // mark as unflushed and unsynced, update file, and return amount written lfs_size_t written; if (lfsr_o_isappend(file->flags)) { - written = pos_ - file->size; + written = pos__ - file->size; } else { - written = pos_ - file->pos; + written = pos__ - file->pos; + } + if (unsynced_) { + file->flags |= LFS_F_UNSYNCED; + } else { + file->flags &= ~LFS_F_UNSYNCED; } - file->flags |= LFS_F_UNSYNCED; if (unflushed_) { file->flags |= LFS_F_UNFLUSHED; } else { file->flags &= ~LFS_F_UNFLUSHED; } - file->pos = pos_; - file->size = lfs_max32(file->size, pos_); + file->pos = pos__; + file->size = size__; file->buffer_pos = buffer_pos_; + if (buffer_ != file->buffer) { + LFS_ASSERT(buffer_size_ <= lfs->cfg->cache_size); + memcpy(file->buffer, buffer_, buffer_size_); + } file->buffer_size = buffer_size_; file->ftree.u = ftree_.u; return written; @@ -10799,66 +10914,20 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { // checkpoint the allocator again lfs_alloc_ckpoint(lfs); - // note because of small-file caching and our current write - // strategy, we never actually end up with only a direct data - // or bptr - // - // this is convenient because bpts are a bit annoying to commit - LFS_ASSERT(!lfsr_ftree_isbsprout(&file->ftree)); - LFS_ASSERT(!lfsr_ftree_isbleaf(&file->ftree)); // if this invariant breaks something has gone horribly wrong LFS_ASSERT(file->size == lfs_max32( file->buffer_pos + file->buffer_size, lfsr_ftree_size(&file->ftree))); - // small files must be inlined entirely in our buffer - LFS_ASSERT(!(file->size <= lfs->cfg->cache_size - && file->size <= lfs->cfg->inline_size - && file->size <= lfs->cfg->fragment_size) - || file->size == file->buffer_size); // commit our file's metadata - uint8_t buf[LFSR_BTREE_DSIZE]; - err = lfsr_mdir_commit(lfs, &file->ftree.mdir, LFSR_ATTRS( - (file->size == 0) - ? LFSR_ATTR(file->ftree.mdir.mid, - WIDE(RM(STRUCT)), 0, - NULL()) - : (file->size <= lfs->cfg->cache_size - && file->size <= lfs->cfg->inline_size - && file->size <= lfs->cfg->fragment_size) - ? LFSR_ATTR(file->ftree.mdir.mid, - WIDE(DATA), 0, - BUF(file->buffer, file->size)) - : (lfsr_ftree_isbshrub(&file->ftree)) - ? LFSR_ATTR(file->ftree.mdir.mid, - WIDE(SHRUBTRUNK), 0, - SHRUBTRUNK(&file->ftree.u.bshrub)) - : LFSR_ATTR(file->ftree.mdir.mid, - WIDE(BTREE), 0, - FROMBTREE(&file->ftree.u.btree, buf)))); + err = lfsr_ftree_sync(lfs, &file->ftree, lfsr_f_isunflushed(file->flags), + file->buffer_pos, file->buffer, file->buffer_size); if (err) { goto failed; } - // mark as synced - file->flags &= ~LFS_F_UNSYNCED & ~LFS_O_DESYNC; - // update other file handles - for (lfsr_openedmdir_t *opened = lfs->opened[ - LFS_TYPE_REG-LFS_TYPE_REG]; - opened; - opened = opened->next) { - lfsr_file_t *file_ = (lfsr_file_t*)opened; - if (file_->ftree.mdir.mid == file->ftree.mdir.mid - // don't update desynced file handles - && !lfsr_o_isdesync(file_->flags)) { - file_->size = file->size; - file_->ftree.u = file->ftree.u; - file_->buffer_pos = file->buffer_pos; - memcpy(file_->buffer, file->buffer, file->buffer_size); - file_->buffer_size = file->buffer_size; - } - } - + // mark as in-sync + file->flags &= ~LFS_O_SYNC & ~LFS_O_DESYNC; return 0; failed:; diff --git a/lfs.h b/lfs.h index 803f38e6..eeec862b 100644 --- a/lfs.h +++ b/lfs.h @@ -162,7 +162,8 @@ enum lfs_open_flags { LFS_O_EXCL = 0x0200, // Fail if a file already exists LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size LFS_O_APPEND = 0x0800, // Move to end of file on every write - LFS_O_DESYNC = 0x1000, // Do not sync or recieve file updates + LFS_O_SYNC = 0x1000, // Sync metadata on every write + LFS_O_DESYNC = 0x2000, // Do not sync or recieve file updates #endif // internally used flags diff --git a/tests/test_fmulti.toml b/tests/test_fmulti.toml index 97cfebef..31aec911 100644 --- a/tests/test_fmulti.toml +++ b/tests/test_fmulti.toml @@ -116,7 +116,8 @@ code = ''' defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SIZE = [ '0', 'CACHE_SIZE/2', @@ -151,7 +152,8 @@ code = ''' // write 1 handle, read R handles in parallel lfsr_file_t writer; lfsr_file_t readers[R]; - lfsr_file_open(&lfs, &writer, "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &writer, "jello", + LFS_O_WRONLY | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; for (lfs_size_t r = 0; r < R; r++) { lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0; } @@ -162,7 +164,7 @@ code = ''' } lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK; memcpy(&after[i], wbuf, CHUNK); - if (SYNC != 0) { + if (SYNC == 1) { lfsr_file_sync(&lfs, &writer) => 0; } @@ -195,7 +197,8 @@ code = ''' defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ @@ -232,7 +235,8 @@ code = ''' // write 1 handle, read R handles in parallel lfsr_file_t writer; lfsr_file_t readers[R]; - lfsr_file_open(&lfs, &writer, "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &writer, "jello", + LFS_O_WRONLY | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; for (lfs_size_t r = 0; r < R; r++) { lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0; } @@ -249,7 +253,7 @@ code = ''' } lfsr_file_write(&lfs, &writer, wbuf, size) => size; memcpy(&after[off], wbuf, size); - if (SYNC != 0) { + if (SYNC == 1) { lfsr_file_sync(&lfs, &writer) => 0; } @@ -289,7 +293,8 @@ code = ''' defines.W = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SIZE = [ '0', 'CACHE_SIZE/2', @@ -324,7 +329,8 @@ code = ''' // write W handles in parallel lfsr_file_t writers[W]; for (lfs_size_t w = 0; w < W; w++) { - lfsr_file_open(&lfs, &writers[w], "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &writers[w], "jello", + LFS_O_WRONLY | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t w = 0; w < W; w++) { @@ -338,7 +344,9 @@ code = ''' memcpy(&after[i], wbuf, CHUNK); } } else { - lfsr_file_sync(&lfs, &writers[w]) => 0; + if (SYNC == 1) { + lfsr_file_sync(&lfs, &writers[w]) => 0; + } memcpy(&after[i], wbuf, CHUNK); } } @@ -361,7 +369,8 @@ code = ''' defines.W = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ @@ -398,7 +407,8 @@ code = ''' // write W files in parallel lfsr_file_t writers[W]; for (lfs_size_t w = 0; w < W; w++) { - lfsr_file_open(&lfs, &writers[w], "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &writers[w], "jello", + LFS_O_WRONLY | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t w = 0; w < W; w++) { @@ -418,7 +428,9 @@ code = ''' memcpy(&after[off], wbuf, size); } } else { - lfsr_file_sync(&lfs, &writers[w]) => 0; + if (SYNC == 1) { + lfsr_file_sync(&lfs, &writers[w]) => 0; + } memcpy(&after[off], wbuf, size); } } @@ -443,7 +455,8 @@ defines.W = 4 defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SIZE = [ '0', 'CACHE_SIZE/2', @@ -479,7 +492,8 @@ code = ''' lfsr_file_t writers[W]; lfsr_file_t readers[R]; for (lfs_size_t w = 0; w < W; w++) { - lfsr_file_open(&lfs, &writers[w], "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &writers[w], "jello", + LFS_O_WRONLY | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t r = 0; r < R; r++) { lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0; @@ -496,7 +510,9 @@ code = ''' memcpy(&after[i], wbuf, CHUNK); } } else { - lfsr_file_sync(&lfs, &writers[w]) => 0; + if (SYNC == 1) { + lfsr_file_sync(&lfs, &writers[w]) => 0; + } memcpy(&after[i], wbuf, CHUNK); } } @@ -532,7 +548,8 @@ defines.W = 4 defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ @@ -570,7 +587,8 @@ code = ''' lfsr_file_t writers[W]; lfsr_file_t readers[R]; for (lfs_size_t w = 0; w < W; w++) { - lfsr_file_open(&lfs, &writers[w], "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &writers[w], "jello", + LFS_O_WRONLY | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t r = 0; r < R; r++) { lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0; @@ -593,7 +611,9 @@ code = ''' memcpy(&after[off], wbuf, size); } } else { - lfsr_file_sync(&lfs, &writers[w]) => 0; + if (SYNC == 1) { + lfsr_file_sync(&lfs, &writers[w]) => 0; + } memcpy(&after[off], wbuf, size); } } @@ -635,7 +655,8 @@ code = ''' defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SIZE = [ '0', 'CACHE_SIZE/2', @@ -674,7 +695,8 @@ code = ''' // write RW rdwrs in parallel lfsr_file_t rdwrs[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { - lfsr_file_open(&lfs, &rdwrs[rw], "jello", LFS_O_RDWR) => 0; + lfsr_file_open(&lfs, &rdwrs[rw], "jello", + LFS_O_RDWR | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t rw = 0; rw < RW; rw++) { @@ -690,7 +712,9 @@ code = ''' memcpy(&after[i], wbuf, CHUNK); } } else { - lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + if (SYNC == 1) { + lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + } memcpy(&after[i], wbuf, CHUNK); } } @@ -723,7 +747,8 @@ code = ''' defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ @@ -764,7 +789,8 @@ code = ''' // write RW rdwrs in parallel lfsr_file_t rdwrs[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { - lfsr_file_open(&lfs, &rdwrs[rw], "jello", LFS_O_RDWR) => 0; + lfsr_file_open(&lfs, &rdwrs[rw], "jello", + LFS_O_RDWR | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t rw = 0; rw < RW; rw++) { @@ -785,7 +811,9 @@ code = ''' memcpy(&after[off], wbuf, size); } } else { - lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + if (SYNC == 1) { + lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + } memcpy(&after[off], wbuf, size); } } @@ -827,7 +855,8 @@ code = ''' defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SIZE = [ '0', 'CACHE_SIZE/2', @@ -863,7 +892,9 @@ code = ''' lfsr_file_t writer; lfsr_file_t readers[R]; lfsr_file_open(&lfs, &writer, "jello", - LFS_O_WRONLY | LFS_O_DESYNC) => 0; + LFS_O_WRONLY + | LFS_O_DESYNC + | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; for (lfs_size_t r = 0; r < R; r++) { lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0; } @@ -874,7 +905,7 @@ code = ''' } lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK; memcpy(&after[i], wbuf, CHUNK); - if (SYNC != 0) { + if (SYNC == 1) { lfsr_file_sync(&lfs, &writer) => 0; } @@ -911,7 +942,8 @@ code = ''' defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ @@ -949,7 +981,9 @@ code = ''' lfsr_file_t writer; lfsr_file_t readers[R]; lfsr_file_open(&lfs, &writer, "jello", - LFS_O_WRONLY | LFS_O_DESYNC) => 0; + LFS_O_WRONLY + | LFS_O_DESYNC + | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; for (lfs_size_t r = 0; r < R; r++) { lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0; } @@ -966,7 +1000,7 @@ code = ''' } lfsr_file_write(&lfs, &writer, wbuf, size) => size; memcpy(&after[off], wbuf, size); - if (SYNC != 0) { + if (SYNC == 1) { lfsr_file_sync(&lfs, &writer) => 0; } @@ -1010,7 +1044,8 @@ code = ''' defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SIZE = [ '0', 'CACHE_SIZE/2', @@ -1045,7 +1080,8 @@ code = ''' // write 1 handle, read R handles in parallel lfsr_file_t writer; lfsr_file_t readers[R]; - lfsr_file_open(&lfs, &writer, "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &writer, "jello", + LFS_O_WRONLY | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; for (lfs_size_t r = 0; r < R; r++) { lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0; @@ -1057,7 +1093,7 @@ code = ''' } lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK; memcpy(&after[i], wbuf, CHUNK); - if (SYNC != 0) { + if (SYNC == 1) { lfsr_file_sync(&lfs, &writer) => 0; } @@ -1086,7 +1122,8 @@ code = ''' defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ @@ -1123,7 +1160,8 @@ code = ''' // write 1 handle, read R handles in parallel lfsr_file_t writer; lfsr_file_t readers[R]; - lfsr_file_open(&lfs, &writer, "jello", LFS_O_WRONLY) => 0; + lfsr_file_open(&lfs, &writer, "jello", + LFS_O_WRONLY | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; for (lfs_size_t r = 0; r < R; r++) { lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY | LFS_O_DESYNC) => 0; @@ -1141,7 +1179,7 @@ code = ''' } lfsr_file_write(&lfs, &writer, wbuf, size) => size; memcpy(&after[off], wbuf, size); - if (SYNC != 0) { + if (SYNC == 1) { lfsr_file_sync(&lfs, &writer) => 0; } @@ -1177,7 +1215,8 @@ code = ''' defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SIZE = [ '0', 'CACHE_SIZE/2', @@ -1217,7 +1256,9 @@ code = ''' lfsr_file_t rdwrs[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { lfsr_file_open(&lfs, &rdwrs[rw], "jello", - LFS_O_RDWR | LFS_O_DESYNC) => 0; + LFS_O_RDWR + | LFS_O_DESYNC + | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t rw = 0; rw < RW; rw++) { @@ -1233,7 +1274,9 @@ code = ''' memcpy(&after[i], wbuf, CHUNK); } } else { - lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + if (SYNC == 1) { + lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + } if (i == 0) { memcpy(after, between[rw], SIZE); } else { @@ -1274,7 +1317,8 @@ code = ''' defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync -defines.SYNC = [0, 1] +# 2 => sync via LFS_O_SYNC +defines.SYNC = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ @@ -1316,7 +1360,9 @@ code = ''' lfsr_file_t rdwrs[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { lfsr_file_open(&lfs, &rdwrs[rw], "jello", - LFS_O_RDWR | LFS_O_DESYNC) => 0; + LFS_O_RDWR + | LFS_O_DESYNC + | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t rw = 0; rw < RW; rw++) { @@ -1337,7 +1383,9 @@ code = ''' memcpy(&after[off], wbuf, size); } } else { - lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + if (SYNC == 1) { + lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + } if (i == 0) { memcpy(after, between[rw], SIZE); } else {