Repository navigation
Conversation
…boot The freefile is created once, on the largest free block of the card and at most 4 GB (the largest FAT32 file), and reused as it is at every later boot. Its size is a whole number of superclusters plus one cluster, so once the logs have taken the superclusters one cluster is left: the card reads as full and the blackbox stops logging until the card is formatted, although the rest of a card larger than 4 GB and any log deleted from a PC are free. When the freefile is below 2 GB at boot, search the FAT for the largest free block as on the first boot, and if that is larger, free the old freefile's clusters and move the freefile there. Larger freefiles are reused without a search, as before.
…w it Logs take their clusters from the start of the freefile, so it only ever shrank until the next format, and one log could never grow past what was left of it at boot. On a card larger than 4 GB the clusters that follow the freefile are normally free, so at boot extend it over them, up to the largest file size, leaving AFATFS_FREEFILE_LEAVE_CLUSTERS for regular files as a new freefile does. Only a freefile that is still below 2 GB afterwards is moved to the largest free block. The new clusters are chained and flushed before the freefile's last cluster points at them, and the new size is saved after that, so a power cut leaves at worst unreferenced clusters, never a chain that runs into free ones.
After the skip the search went on reading the FAT sector of the freefile's first cluster while the cluster number had moved past it, so the entries it examined belonged to other clusters. It could only matter when those sectors were already cached, and the search at boot now runs with the freefile in place.
afatfs_FATFillWithPattern() counted a final FAT sector it only writes part of among the sectors to pre-erase. That sector has to be read first, and on an SD card on SPI the read ends the multiple-block write started with ACMD23 before it reaches that block; the SD specification leaves an unwritten pre-erased block undefined, erased or old. Its other entries, which can belong to the next file, could then be read back erased. A new freefile ends that way (whole superclusters plus one cluster), and so does every extension of it.
afatfs_getFreeBufferSpace() counted every unlocked sector that was empty or in sync, but afatfs_allocateCacheSector() never evicts one with a retain count: a contiguous file keeps its directory entry retained for as long as it is open, and the blackbox log and the free file are both contiguous. With a log open, two of the 8 sectors were reported as space the blackbox could write into, and could not be. Measured on a TBS Lucid H7 while the SD card held a write for 54 ms: afatfs_fwrite() refused bytes while this function still reported 1024 bytes free.
An SD card can hold a write for tens of milliseconds (the standard
allows 250). The blackbox keeps writing into the other cache sectors
meanwhile, and once none is left it drops what it writes. With 8
sectors, and 3 of them never free while a log is open, the cache held
about 30 ms of a 1 kHz log.
Measured on a TBS Lucid H7 whose card holds a write for 54 ms every
768 KB once it runs out of prepared blocks, 120 s logs:
sectors 1 kHz log (85 kB/s) 2 kHz log (170 kB/s)
rows lost rows lost
8 379 2178
12 8 1282
16 0 703
24 0 0
32 0 0 (also over a 20 minute log)
Now 32 sectors on H7 and 24 on F7, where RAM is plenty. On F4 with the
card on SPI the cache moves to CCM with 24 sectors: RAM is short there,
CCM mostly unused, and the CPU writes the card, so no DMA needs to
reach it. Elsewhere (F4 with SDIO, AT32) it stays at 8. A target can
set AFATFS_NUM_CACHE_SECTORS itself. Targets without an SD card do not
link the cache.
Walking the larger cache costs afatfs_poll() about 8 ns per sector on
the H7, in the scheduler's idle loop.
… card has room With the freefile refilled at boot, every power cycle starts with up to 4 GB for logs, but within one power cycle the freefile only shrinks: when it runs out, or a log reaches the largest FAT32 file, the blackbox stops for the rest of the flight although the card may have room. Behind USE_BLACKBOX_SDCARD_SPLIT, on by default on targets with more than 512 KB of flash, afatfs refills the freefile in the background once it is below 256 MB: it extends it over the free clusters that follow it, 16 MB at a time, and when nothing free follows it searches the FAT for the largest free block, and moves the freefile there once it has run out and the log that used it has been closed. Regular cluster allocations wait while clusters are being claimed, and a contiguous file can no longer grow past the largest file size. The blackbox closes a log that can't grow when afatfs can make room, instead of stopping, and the usual start path opens the next file once there is room, without waiting for a serial port before the header. The card only counts as full once no new file can get room either.
…a full card stops as before A log created while the freefile was being refilled could not grow, so the blackbox split it at once and, with no frame logged, deleted it. Deleting a contiguous file with no clusters ran the contiguous erase from cluster 0 up to the freefile. Truncating a file with no clusters now only saves its directory entry, and a split log's new file waits until the freefile has room for its first supercluster, as long as the refill can still bring some: when it can't, the file opens as without the option and the full card stops the log. When a regular cluster allocation finds no free cluster, the card is full for more than the freefile: the refill no longer clears that, and the blackbox stops as it did without the option.
…f retrying forever
…rectory entry each step A power cut while the freefile was regrown or moved at boot left whatever chain was in flight as a lost FAT chain: up to 4 GB of new chain, and up to 2 GB of the old freefile when moving it. Three cuts on a TBS Lucid H7 Wing left three lost chains of 650 MB on average. The new chain now grows in 256 MB blocks, each one chained, linked, saved and flushed before the next. A moved freefile gives its old clusters back the same way from its end: the smaller size is saved, then the chain is cut there, then the tail is freed. A cut strands at most one block either way.
… end and freeing from the front
…eature/blackbox-sd-split
The freefile holds only part of the card, at most 4 GiB, and with the regrow at boot later logs also reach the free clusters outside it. After mount, while no file is open, those clusters are counted one FAT sector at a time with a gap after each, so that terrain can still take the card while afatfs is idle, and the count is kept up to date by allocations and frees. MSP_SDCARD_SUMMARY gets the freefile plus that count appended as cardFreeSpaceKB, 0xFFFFFFFF until the count is done (or for good if a FAT sector cannot be read). Older configurators read the fields before it as they did.
…dy card The freefile's chain holds no free cluster, so the count no longer reads its FAT sectors: about a quarter of them on a 16 GB card, nearly all on cards of 4 GB or less. And a removed card's count stays in afatfs, so the summary sends it only while the card is ready.
…n top of iNavFlight#12156 Both build on iNavFlight#12056. The free space count and the freefile refill both run in the READY state, each under its own #ifdef.
Two ways iNavFlight#12060's refill upsets iNavFlight#12156's count of the card's free space: - The count pauses while files are open but not while the refill searches the FAT for the largest free block or claims clusters. One of the count's FAT sector reads then waits behind the refill's and passes the 1 s after which the count takes the sector as unreadable and gives up: in the SITL the count gave up at every boot where the refill searched, and the free space stayed unknown until the next one. The count now pauses while the refill is not idle. - The refill extends or moves the freefile with its own FAT writes, past afatfs_freeCountAdjust(), so the clusters it claimed stayed counted as free outside the freefile while also making it bigger. The count now starts over once the refill has saved the freefile, with its timing restarted as at mount, and runs again the next time no file is open. A count that gave up on an unreadable FAT sector stays given up.
|
ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing |
PR Summary by QodoKeep SD-card free-space counts accurate during freefile refills
AI Description
Diagram
High-Level Assessment
Files changed (10)
|
Code Review by Qodo
1.
|
|
RAM / Flash usage vs. base commit
See RAM/flash optimization guide for techniques to reduce usage. |
|
Test firmware build ready — commit Download firmware for PR #12158 251 targets built. Find your board's
|
…new log regularFull stayed set after the regular allocation that failed, so the room freed when blackbox deleted an empty log was never offered to the next one until the card was mounted again.
…ding to KiB With 512-byte clusters an odd freefile and an odd outside count each lost half a KiB.
The count read the whole FAT after every boot, from the realtime callbacks, and went on while armed whenever no file was open (a card used only for terrain, or before the log opens). It now starts at the first MSP_SDCARD_SUMMARY, which the Configurator sends from the Blackbox tab, and arming stops it; a stopped count resumes where it was at the next request.
After the free count the card's directories are read, one sector at a time with the count's gap, and MSP_SDCARD_SUMMARY gets three fields appended: - logTailsKB: each log takes whole superclusters from the freefile, and the rest of its last one stays taken without data until the log is deleted; - terrainKB: the terrain files (*.TER); - lostKB: what is taken outside the freefile but held by no file or directory, left by writes cut short. Any change to a directory entry makes the walk run again at the next request. A directory that can't be read, or nests deeper than 4, leaves the three unknown until the card changes.
…r-refill # Conflicts: # src/main/io/asyncfatfs/asyncfatfs.c
Builds on #12156 (the card's free space over MSP) and #12060 (an SD log continued in a new file), both on #12056: this branch merges the two and adds one commit, which is the change of its own.
It has #12156's later commits too: the count runs only when the Configurator asks for it, never armed, and the space taken outside the freefile is told apart (log ends, terrain data, lost space). They merge here without changes to this PR's own commit.
In #12156 I wrote that whichever of the two is merged second would need its refill to keep the count right, and that I would add it there. Here it is as its own PR instead, so that neither of the two has to wait for the other: it is what joins them. Testing it showed a second problem besides the one I expected.
afatfs_freeCountAdjust(). The clusters it claims stayed counted as free outside the freefile while also making the freefile bigger. Once the refill has saved the freefile's new size, the count now starts over and runs again the next time it is asked for with no file open, as after boot; until thenMSP_SDCARD_SUMMARYreports the free space as not counted yet. A count that gave up on a really unreadable FAT sector stays given up.The merge itself: the free space count and the refill both run in the READY state, each under its own
#ifdef.Tested in the SITL with
--sdcard.USE_BLACKBOX_SDCARD_SPLITis only defined for targets over 512 KB of flash, so for the test I defined it in the SITL target too. The card: 1 GB, 4 KB clusters, full but for two holes of 1.6 and 2.15 MB, so that the freefile (1 MB, in the larger hole) is used up by a short log and then moved to the other hole. Checked against the image's FAT read by an independent script, which gives 4264 KiB free at boot and 3236 KiB after the log and the move:Size against the merge of #12156 and #12060: +32 B of flash on KAKUTEF7, KAKUTEF7HDV and TBS_LUCID_H7_WING, +56 B on MATEKF405SE, nothing else; MATEKF722SE and NEUTRONRCF435WING unchanged. Unit tests 620/620.