-
Notifications
You must be signed in to change notification settings - Fork 3k
fatfs: Add lower bound to block sizes #4843
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Stupid question: it seems that you are dupplicating code, why don't you update |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading the code, I realize that ssize
in disk_write
, disk_read
or the sector count ioctl represent the sector size. It is a good candidate for the introduction of a new member function get_sector_size
.
@@ -175,15 +175,23 @@ DSTATUS disk_initialize(BYTE pdrv) | |||
DRESULT disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count) | |||
{ | |||
debug_if(FFS_DBG, "disk_read(sector %d, count %d) on pdrv [%d]\n", sector, count, pdrv); | |||
bd_size_t ssize = _ffs[pdrv]->get_erase_size(); | |||
DWORD ssize = _ffs[pdrv]->get_erase_size(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use get_read_size
? Is it checked anywhere that get_erase_size
is a multiple of get_read_size
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The block device API requires that the erase size is a multiple of the program size, and the program size is a multiple of the read size:
https://github.com/ARMmbed/mbed-os/blob/master/features/filesystem/bd/BlockDevice.h#L112
https://github.com/ARMmbed/mbed-os/blob/master/features/filesystem/bd/BlockDevice.h#L105
Although it does look like we don't currently have any tests that cover that
int err = _ffs[pdrv]->read(buff, sector*ssize, count*ssize); | ||
return err ? RES_PARERR : RES_OK; | ||
} | ||
|
||
DRESULT disk_write(BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) | ||
{ | ||
debug_if(FFS_DBG, "disk_write(sector %d, count %d) on pdrv [%d]\n", sector, count, pdrv); | ||
bd_size_t ssize = _ffs[pdrv]->get_erase_size(); | ||
DWORD ssize = _ffs[pdrv]->get_erase_size(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be relevant to use a different ssize
acquired from get_program_size
for the program operation or ensure the erase size is a multiple of the program size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I think I see your point. 512 might not be a multiple of the read/write sizes. I'm thinking this should be provided as additional parameters to the FATFileSystem. I'm thinking I'll close this for now to revise.
@Nodraak, get_erase_size returns the minimum erase size of the underlying device. While the FATFileSystem doesn't support blocks less than 512 bytes, other filesystem might. For example, the SPIFFileSystem uses an erase size of 65536 bytes: And the LittleFileSystem could go as low as 64 bytes per block: @pan-, what would |
Closing for now (see #4843 (comment)) |
@geky Ahhh, I'm an idiot, I didn't notice static DWORD get_sector_size(BYTE pdrv) {
DWORD ssize = _ffs[pdrv]->get_erase_size()
if (ssize < 512) {
ssize = 512;
}
} |
@pan- Ah! that is a good point, I'll add it in |
Some block devices (for example I2C EEPROM) can be erased at the byte level. These aren't really block devices, but fall under the block device API. For these devices, the fat filesystem needs to use a lower bound on the block size. In this case we used 512 bytes is used since it is already a standard.
@geky What is the status of this? Janne is asking for this patch go into the next release (will be 5.5.6 now) but you closed the PR .... ?? |
@JanneKiiskila why the priority? I understand this is needed for #4899, but is there a seperate issue? @adbridge, this needed work and no one needed it, so I closed it to work on other things. I can reopen, although I still need to update it based on @pan-'s points. |
All SD-card corruption issues are a priority, they cause a massive havoc in CI if there's any potential for issues with corruption. |
Fair enough 👍 |
Although I do believe he is modifying the FATFileSystem to let the problematic block size through. By default the FATFileSystem should assert if the block size is too small. |
Not really, there seems to be no check to make sure the relations between the |
Ah, my bad, it looks like this check is only performed during format, not mount: |
Is open for review now |
ssize = 512; | ||
} | ||
|
||
MBED_ASSERT(ssize >= _MIN_SS && ssize <= _MAX_SS); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the assertions should be moved in the constructor, something like:
MBED_ASSERT(bd->get_program_size() == bd->get_read_size());
The erase size should then be checked by is_valid_erase
so the optimization kicks in when it's possible to do so and should return the real size instead of returning _block_size
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The block device needs to be initialized before get_erase_size
and friends can be called reliably. Currently the init is called from the chan layer during mount, but mount may also write to the device.
We could bring up the block device to check things before mounting, but since these are asserts anyways it's easier to just put them in the chan-disk layer.
The assert for is_valid_erase
should live in the block device driver.
I did add some other checks on program/read size though 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I forgot the fields are populated after the init()
. I'd still move the assertions in the init codepath since those don't really change and the read/write paths should be as fast as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The read/write paths are already bounded by the speed of the underlying block devices. In the read/program/erase funtions you will find similar asserts. If you are looking for speed can't you just compile in release mode? Release mode will disable these asserts, but they will still have been validated while developing.
26b018f
to
ca47d4f
Compare
@@ -160,6 +160,26 @@ void ff_memfree(void *p) | |||
} | |||
|
|||
// Implementation of diskio functions (see ChaN/diskio.h) | |||
static WORD disk_get_sector_size(BYTE pdrv) | |||
{ | |||
WORD ssize = _ffs[pdrv]->get_erase_size(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It probably makes sense to use get_read_size
here (the invariant here is that read size == wirte size, right?) since get_erase_size()
can assume whatever value the HW reports and the block driver will act accordingly.
The rounding to 512 isn't needed anymore since we found the erase size for that particular card isn't 128 but 64K, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For most of the other block devices an erase is required before programming (flash in particular). The SD card is the exception. If we program less than an erase block on NOR flash, the data will just become corrupted.
For the block device API, the erase
function makes a block available for programming. The get_erase_size
should return the smallest block size that can be rewritten. Since the FAT fs doesn't schedule erases seperately, it needs to erase a block during a write. So the sector size here is bounded to the underlying device's get_erase_size
.
The rounding to 512 is still useful for I2C EEPROM chips, which actually have get_erase_size
of 1 byte.
@LemonBoy Happy with all the responses and the code as it is now? |
The assertions are enough to not to accidentally corrupt the filesystem when the values are wrong and that's fine by me. |
/morph test-nightly |
Result: FAILUREYour command has finished executing! Here's what you wrote!
OutputBuild failed! |
/morph test-nightly |
Result: SUCCESSYour command has finished executing! Here's what you wrote!
OutputAll builds and test passed! |
what's going on with continuous-integration/jenkins/pr-head? |
@tommikas What's happening with the Jenkins over there? Can we get this PR tested? |
The build had passed but for some reason the notification hadn't reached github. Triggered a rebuild and it seems to be ok now. |
thanks! LGTM! |
Some block devices (for example I2C EEPROM) can be erased at the byte level. These aren't really block devices, but fall under the block device API. For these devices, the fat filesystem needs to use a lower bound on the block size. In this case we used 512 bytes is used since it is already a standard.