Skip to content
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

Persist DMA buffer in sdmmc_read_sectors/sdmmc_write_sectors (IDFGH-12770) #13749

Open
ducalex opened this issue May 5, 2024 · 7 comments
Open
Labels
Status: Opened Issue is new Type: Feature Request Feature request for IDF

Comments

@ducalex
Copy link

ducalex commented May 5, 2024

Is your feature request related to a problem?

When internal memory runs out, SD card reads and writes start returning ESP_ERR_NO_MEM because this line fails:

void* tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA);

In theory SPIRAM_MALLOC_RESERVE_INTERNAL should ensure that memory is always available but in practice it doesn't always work.

There are likely also performance implications to allocating/deallocating all the time (in some cases).

Having no DMA-capable memory left is obviously an undesirable situation in general, but it can happen and when it does we might still need SD card access.

Describe the solution you'd like.

My understanding is that block_size is at most 512 bytes: out_csd->sector_size = MIN(read_bl_size, 512);.

Therefore there is no reason to not keep the buffer around after it's first allocated for future use. In fact with that size it could be allocated preemptively when the SD card is initialized, or even placed in static BSS. Because if it's needed once, it will very likely be needed again!

Describe alternatives you've considered.

My current solution is this:

  1. Reserve a modest DMA-capable memory block on start
  2. Wrap sdmmc_host_t's do_transaction() to detect errors
  3. If an error occurs, free the reserved block and try the transaction again
  4. If the transaction succeeds, try to grab the block back

But this is far from ideal and doesn't always work...

Additional context.

#6596 suggested the same but was closed because of the emphasis on performance. I think the emphasis should be on stability improvements if this change is introduced.

@ducalex ducalex added the Type: Feature Request Feature request for IDF label May 5, 2024
@espressif-bot espressif-bot added the Status: Opened Issue is new label May 5, 2024
@github-actions github-actions bot changed the title Persist DMA buffer in sdmmc_read_sectors/sdmmc_write_sectors Persist DMA buffer in sdmmc_read_sectors/sdmmc_write_sectors (IDFGH-12770) May 5, 2024
@huming2207
Copy link
Contributor

huming2207 commented May 6, 2024

My understanding is that block_size is at most 512 bytes

Hmm I think that's not true? There are quite a lot of those big SD cards that comes with 1024 or even 4096 bytes sector.

Even some SD NAND chips for industrial purposes uses 1024 bytes sector. I've definitely seen a 16GB one doing that but I can't remember the part number

@rrtandler
Copy link
Collaborator

Hi @ducalex,
From what you have described so far, it looks like the rest of application is using DMA capable memory space so heavily, that there isn't even 512B block left at certain moment, when it comes to SD card access. I think keeping the buffer for SD allocated statically aka "forever" would just expose another place in the code, where the DMA capable memory won't be available when needed. Could you check the rest of your code for source of potential leak / fragmentation of DMA capable RAM ?

@ducalex
Copy link
Author

ducalex commented May 6, 2024

Hmm I think that's not true? There are quite a lot of those big SD cards that comes with 1024 or even 4096 bytes sector.

That is what I thought at first, but when I looked up the actual code in esp-idf, the only assignment I've found was the line I've quoted. Which would result in 512 or less.

Perhaps I missed another code path somewhere?

@huming2207
Copy link
Contributor

That is what I thought at first, but when I looked up the actual code in esp-idf, the only assignment I've found was the line I've quoted. Which would result in 512 or less.

Ah crap, yea I look down the code and indeed I can find them here:

I think this might be a bug?? Maybe it supposed to be MAX() instead? Otherwise any newer/larger SD cards won't be read/write properly.

Meanwhile, I believe these sort of DMA buffers shouldn't be statically allocated, because we do have use cases where two or more SD cards are needed (that shares with the same SPI but different CS). We have PSRAM here so we have sufficient RAM for heap allocations anyway.

@ducalex
Copy link
Author

ducalex commented May 9, 2024

I agree that it shouldn't be static, it was hyperbole on my part! I also agree that running out of DMA-capable memory indicates bigger problems.

However I maintain that it would be beneficial to keep the buffer after it's been used for the first time, because it will undoubtedly be needed again so that space is already pseudo-reserved. Making it explicit is simply more efficient and improves resilience.

That being said, my proposal hinges on the fact that the buffer is small, 1KB or less. If there is a bug and the sector size can in fact be 4KB or even more then yes, it is obviously unreasonable to keep that around!

@huming2207
Copy link
Contributor

However I maintain that it would be beneficial to keep the buffer after it's been used for the first time, because it will undoubtedly be needed again so that space is already pseudo-reserved. Making it explicit is simply more efficient and improves resilience.

Hmm yep, we do need to allocate at initialisation and free on deinit, not on every write op.

I will submit a pull request first to fix the MIN() issue, that will definitely become a big troublemaker on our firmware. Then I will see if I can work out a way to fix this heap allocation.

@igrr
Copy link
Member

igrr commented May 10, 2024

Thanks for the idea @ducalex! I agree that avoiding repeated allocations/deallocations could help with reliability.

Recently we have added a new dma_aligned_buffer member to sdmmc_host_t, which has a similar purpose: it provides a DMA-capable buffer which can be reused between operations. It's kind of misplaced in sdmmc_host_t, actually, as the usage occurs in the sdmmc component (protocol layer), not in the host driver, but that's something we have to refactor...
I think we could reuse this dma_aligned_buffer for read/write commands as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Opened Issue is new Type: Feature Request Feature request for IDF
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants