greiman / SdFat

Arduino FAT16/FAT32 exFAT Library
MIT License
1.08k stars 505 forks source link

SdFat32 Preallocation - Sector Boundary Question #402

Open RadekPudelko opened 2 years ago

RadekPudelko commented 2 years ago

Hello,

I am working on testing out sd card behavior with random power failure and have found that preallocating files is a good way to avoid file corruption, as long as I do not change the file size. By corruption, I mean that it is possible to open the file and operate on it via the SdFat library.

The issue I am dealing with now is with power loss mid write. The behavior I experience is that I either get some partial writes or garbage bytes when power is randomly lost, which is expected, considering that the card did not have a chance to fully write and that sectors must be deleted before they can be written.

This has lead me to this question of how are sectors aligned with file bytes? For instance, when I preallocate a file, are file positions 0, 512, 1024, etc aligned with sector boundaries, assuming sector size of 512? I am wondering what is going on under the hood of preallocate.

I ran some experiments and have found that I am able to limit the damage to the file if I limit my writes along these boundaries. For instance, for a preallocated file of size 1536 (3 sectors), If I write 512 bytes or less at position 512 and lose power mid write, only bytes 512-1024 are affected. However, if I write 512 bytes at position 511 and lose power, my affected bytes are between 0-1024, since my write spans 2 sectors.

My experiments seem to confirm my suspicion. If it is true, then I will be able to cache my writes in a way to avoid all issues with power loss mid write operation.

greiman commented 2 years ago

Modern cards are far more complex than the 512 byte sector model. Cards emulate 512 byte sectors with very large flash pages called Record Units. The RU size for class 10 or higher cards is 512KB. The card has large RAM buffers and manages programing flash using a manufacture dependent algorithm. The only sure way to insure a 512 byte sector is written is to call sync()/flush() after the write.

file.sync();

There will still be a period of possibly many milliseconds where the card is writing the RAM to the SD flash.

I suggest you download this document "SD Specifications Part 1 Physical Layer Specification Version 9.00 August 22, 2022" here.

Look at the area:

4.13.1.1 Allocation Unit (AU) The user Area is divided into units called "Allocation Unit (AU)" (Refer to Figure 4-47). Each card has its own fixed AU Size (SAU) and the maximum AU Size is defined depending on the card's capacity. The host should manage data areas with the unit of AU. If the first AUs in the card contain file system information then they should not be used for real time recording. An AV application should start recording from the first complete AU, to which only user data can be recorded. Note that this specification does not apply to the Protected Area.

4.13.1.2 Recording Unit (RU) Each AU is divided into units called "Recording Unit (RU)" (Refer to Figure 4-47). The unit of RU Size (SRU) is 16KBye. The RU Size is a multiple of 16KByte and shall not span across an AU boundary. Larger RU size may improve performance. The condition and requirement of the minimum RU Size is defined by Section 4.13.1.8.1. The number of RUs in an AU (NRU) is calculated from SAU/SRU.

4.13.1.3 Write Performance Figure 4-48 shows the typical data management of the card when the host writes RUs of an AU. When the host writes to a fragmented AU, the card prepares a new AU by copying the used RUs and writing the new RUs.. The location A is at the start of the AU boundary and location B is at the end of the AU boundary. From A to B, the host shall write data to free RUs contiguously and skip used RUs (shall not skip any free RU). The card may indicate busy to the host, so the host can wait, during the time the card controller is writing and moving data. The total write time from A to B can be calculated by summing up the write time of free RUs and the moving time of the used RUs. The number of used RUs (Nu) is available by counting it over one AU and number of free RUs is expressed by (NRU – Nu).