littlefs-project / littlefs

A little fail-safe filesystem designed for microcontrollers
BSD 3-Clause "New" or "Revised" License
4.9k stars 771 forks source link

Fix sync issue where data writes could appear before metadata writes #948

Closed geky closed 4 months ago

geky commented 4 months ago

Long story short we aren't calling sync correctly in littlefs. This fixes that.

Some forms of storage, mainly anything with an FTL, eMMC, SD, etc, do not guarantee a strict write order for writes to different blocks. In theory this is what bd sync is for, to tell the bd when it is important for the writes to be ordered.

Currently, littlefs calls bd sync after committing metadata. This is useful as it ensures that user code can rely on lfs_file_sync for ordering external side-effects.

But this is insufficient for handling storage with out-of-order writes.

Consider the simple case of a file with one data block:

  1. lfs_file_write(blablabla) => writes data into a new data block

  2. lfs_file_sync() => commits metadata to point to the new data block

But with out-of-order writes, the bd is free to reorder things such that the metadata is updated before the data is written. If we lose power, that would be bad.

The solution to this is to call bd sync twice: Once before we commit the metadata to tell the bd that these writes must be ordered, and once after we commit the metadata to allow ordering with user code.

As a small optimization, we only call bd sync if the current file is not inlined and has actually been modified (LFS_F_DIRTY). It's possible for inlined files to be interleaved with writes to other files.


This PR also adds LFS_EMUBD_POWERLOSS_OOO to lfs_emubd, which tells lfs_emubd to try to break any order-dependent code on powerloss. This should prevent a regression in the future.

Found by @MFaehling and @alex31 Related issues: https://github.com/littlefs-project/littlefs/issues/822, https://github.com/littlefs-project/littlefs/issues/936

geky-bot commented 4 months ago
Tests passed ✓, Code: 17024 B (+0.3%), Stack: 1432 B (+0.0%), Structs: 812 B (+0.0%) | | Code | Stack | Structs | | Coverage | |:--|-----:|------:|--------:|:--|---------:| | Default | 17024 B (+0.3%) | 1432 B (+0.0%) | 812 B (+0.0%) | Lines | 2391/2570 lines (-0.0%) | | Readonly | 6186 B (+0.0%) | 448 B (+0.0%) | 812 B (+0.0%) | Branches | 1242/1580 branches (-0.0%) | | Threadsafe | 17888 B (+0.3%) | 1432 B (+0.0%) | 820 B (+0.0%) | | **Benchmarks** | | Multiversion | 17088 B (+0.3%) | 1432 B (+0.0%) | 816 B (+0.0%) | Readed | 29369693876 B (+0.0%) | | Migrate | 18720 B (+0.3%) | 1736 B (+0.0%) | 816 B (+0.0%) | Proged | 1482874766 B (+0.0%) | | Error-asserts | 17704 B (+0.2%) | 1424 B (+0.0%) | 812 B (+0.0%) | Erased | 1568888832 B (+0.0%) |