-
Notifications
You must be signed in to change notification settings - Fork 349
Module api convert of aria, asrc, and copier #10284
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
Module api convert of aria, asrc, and copier #10284
Conversation
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.
Pull Request Overview
This PR converts aria, asrc, copier, and crossover modules to use the new module API, replacing legacy memory allocation and blob handler functions with module-specific equivalents. This is part of a broader effort to modernize the SOF codebase following the merge of the module API infrastructure.
Key changes:
- Replace
rzalloc,rfree, andrballoccalls with module-awaremod_zalloc,mod_free, andmod_balloc - Update blob handlers from component-based to module-based implementations
- Remove error handling paths that manually free memory (now handled by module framework)
Reviewed Changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/include/module/crossover/crossover_common.h | Add module parameter to crossover utility functions |
| src/audio/multiband_drc/multiband_drc.c | Update crossover initialization call to pass module context |
| src/audio/crossover/crossover.c | Convert crossover module to use module API for memory and blob handling |
| src/audio/copier/ipcgtw_copier.h | Update IPC gateway copier creation function signature |
| src/audio/copier/host_copier.h | Update host copier creation function signature |
| src/audio/copier/copier_ipcgtw.c | Convert IPC gateway copier to module API |
| src/audio/copier/copier_host.c | Convert host copier to module API with improved error handling |
| src/audio/copier/copier.c | Convert main copier module to module API |
| src/audio/asrc/asrc_ipc4.c | Remove unused allocation header |
| src/audio/asrc/asrc_ipc3.c | Remove unused allocation header |
| src/audio/asrc/asrc_farrow.h | Update ASRC function signatures for module API |
| src/audio/asrc/asrc_farrow.c | Convert ASRC Farrow implementation to module API |
| src/audio/asrc/asrc.c | Convert ASRC module to module API |
| src/audio/aria/aria.c | Convert ARIA module to module API |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
331531a to
27a0659
Compare
|
Had to drop crossover module, as drc changes have a dependency to it. It will part of the next module conversions PR. |
lyakh
left a comment
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.
not 100% certain, but let's clarify when manual clean up is needed and when it isn't
|
|
||
| err_free_buf: | ||
| rfree(cd->buf); | ||
| mod_free(mod, cd->buf); |
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.
if .prepare() fails, a clean up will be performed, so all mod_alloc() memory will be freed automatically? Could you double-check and - if correct - adjust?
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 am unsure of the callback semantics. Does .prepare() failure trigger module unload? Can there be a successful .prepare() after a failed one? I was not sure so I wrote the code as it would be possible.
edit:
I've now studied how this works. The firmware does not automatically unload the module if a prepare fails. However, the Linux driver will eventually tear down the pipeline and that unloads all the modules. But for the FW to be inherently concistent, I do not think we can rely on this procedure, and we should free what ever resources we reserved in the prepare function.
| rfree(gtw_cfg); | ||
| error_cd: | ||
| rfree(cd); | ||
| return ret; |
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.
...but if .init() fails, no .free() is called?
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 initialization fails, then the module is unloaded. I think I tested that 😅. Its taken so long to get first comments to these commits, that its hard to remember any more. I think I have to run some tests again, but its hard now that I lost my development vehicle.
edit:
There is indeed a problem here. If the module specific init fails, then all the mod_alloc() allocated resources are left unfreed. Luckily the fix for this is a one liner: #10320
|
|
||
| e_ipcgtw: | ||
| rfree(ipcgtw_data); | ||
| return ret; |
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.
isn't this function also called as a part of .init() / .create()?
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.
Yes. Its called only from module init so I felt safe to omit the cleanup code.
Hi. May I know what is the main reason for dropping crossover rather than converting its API, multiple outputs perhaps? |
27a0659 to
9830a4e
Compare
@johnylin76 , Crossover, Multiband DRC, and DRC have interdependencies and to avoid build breakage between the commits they need to be converted at once. They are converted here: #10289 (The other module conversion PRs are #10292 , #10293 , #10294, #10295 and #10296) |
|
I'm not a fan of removing memory deallocation from the code. While I understand the idea of tracking memory allocated by a module to detect potential leaks and clean up unfreed memory, I don't think we should relieve modules from managing their own memory. This leads to discussions like: if function xxx returns an error, will the module be freed or not? And when our memory allocation strategy inevitably changes again in a year, are we going to reintroduce deallocation? |
@softwarecki It is optional, module can still call mod_alloc()/mod_free() operations as needed. The tracking has worked well on Linux as it simplified error handling code around memory resources since these are rarely validated code paths. It also allowed for easier Linux module code reviews as code complexity decreased. You are right though, it should still be up to the module developer on what they prefer - they can still use manual mod_free() if they want. Btw, I dont expect any module client level memory changes after this, the lower levels will become virtual regions at the pipeline/DP module level. See #10281 |
| if (buf_32) { | ||
| src_obj->ring_buffers32[ch] = NULL; | ||
| rfree(buf_32); | ||
| mod_free(mod, buf_32); |
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'd add a comment here saying, that this is needed because of the .reset() path
|
@lgirdwood We shouldn't forget that SOF is an embedded solution running on limited resources, including memory. Linux has far more memory available. For this reason, I would personally prefer this functionality to be configurable in KConfig. That way, we can choose whether we want to track allocations made by a module and, if so, whether to use it for automatic memory cleanup or just as a debug feature to detect leaks. Maybe we should simply split this into two stages: first, switch modules to use the new allocation mechanism, and once we see how it performs and know its memory overhead in practice, we can decide whether to keep it permanently, make it configurable, or eventually remove manual deallocation. In any case, I wouldn't remove memory deallocation from existing code. If we decide to bring it back in the future, it will require a lot of work. As for flexibility, if someone creates a new module assuming memory bookkeeping will be active and memory will free itself, that's their choice. |
|
The only thing this resource tracking does is that when a module is unloaded, whether it was due to an error or an unload request from the host side, all its resources are always freed. It is very useful in error handling situations and makes the code simpler and easier to manage. This is particularly true in init() functions where the module builds up the data structures it needs to do its job. This often leads to multi layered error handling code where memory leaks can easily happen. What remains is that the developer will still have to understand what runtime allocated resources are still needed within the lifetime of the module and what are not. This problem is not a new one and it is not caused by the resource tracking, it is just something that is not solved by it. However, the tracking still makes sure that no resource leak remains after the module is unloaded. But if this is too scary a jump to do now, I can put the the memory tracking code behind a config option, and keep the error branch resource freeing code, and free function code (I actually considered keeping the mod->free() contents my self, but decided to follow Linux devm model completely). I just want to know if this is what everybody wants, and can agree to, before I go through all the module implementations once more again. So is this the way to go @softwarecki & @lgirdwood ? edit: |
|
Thanks for the detailed explanation. A few concerns from the embedded side:
The resource tracking is exercised not only at unload, but also on allocation and on manual frees. Even if allocation is relatively fast, freeing a buffer requires scanning the list of all tracked allocations, which makes free O(n). That is a non-trivial runtime cost. Engineers will naturally lean on the convenience of automatic cleanup, but that convenience consumes extra RAM, CPU cycles, and energy. We already observe a noticeable CPU peak during pipeline startup; adding tracking overhead on the hot path will likely make this worse. Given this is an embedded system, I would prioritize performance and tight resource usage over convenience. Since some modules (dts, cadence, waves) already depend on tracking, perhaps they could continue using |
|
@softwarecki & @lgirdwood As it can be seen here, the amount of tracked resources is usually well below 10. But if we ever have a module instance that reserves hundreds of tracked resource units, it should be straight forward enough to store the cointainers into a Zephyr red-black-tree[3] and make mod_free() O(log(n)) scalable, but with the current usage that should not be necessary. Anyway, if this does not convince you, how about making the resource tracking per module configurable? E.g. there would be a function that should be called in module init before the first mod_alloc call (approriate safequards in place to avoid miss use) that turns the resource tracking on. Wihtout it the mod_alloc() and friends would not track resource usage and would only forward the calls to approriate backends. I could then turn the tracking on for the modules that currently need it, and rest would keep it off until we decide to turn the tracking on for them, either for good, or maybe just for debugging purposes. [1] #10339 |
@softwarecki in fact automated clean up at module destruction time gives us a chance of a significant optimisation - just drop the whole module heap without freeing every allocation in it. |
|
As I understand it, the shown usage is in containers, each holding 16 allocations. The log shows 42 containers in use - each is 392 bytes, so tracking costs us over 13 KB.
Seems like an acceptable solution to me.
@lyakh In that case, we don't need tracking at all - there's no risk of memory leaks. Thats yet another argument in favor of making this solution configurable. |
Each module instance allocatest (5 * sizeof(int) = ) 20 byte containers in 16 container chunks. One such allocation takes 20 * 16 + 2 * sizeof(int) = 328 bytes. The first chunk is enough for all the module instances in the above example. So tracking takes 328 bytes / module instance. In the above example there is 18 module instances so the consumed memory adds up to 18 * 328 = 5904 bytes, so bit under 6kb. (I think I should shrink the CONFIG_MODULE_MEMORY_API_CONTAINER_CHUNK_SIZE default to 12, so that a chunk would fit nicely to four cache-lines.)
Ok, I'll prepare a PR to make that change. |
9830a4e to
0aba1c6
Compare
|
These new versions of module conversions use module API to allocate the resources, but the code still has all the resource freeing code in place. |
Allocate all memory through module API mod_alloc() and friends. The one rballoc() call is converted to mod_balloc(). Signed-off-by: Jyri Sarha <jyri.sarha@linux.intel.com>
Allocate all memory, blob handlers, and fast_get() buffers through module API mod_alloc() and friends. Signed-off-by: Jyri Sarha <jyri.sarha@linux.intel.com>
Allocate all memory through module API mod_alloc() and friends. NOTE: copier_dai.c and copier_host.c still have their shared memory allocated through the old API. This is to be fixed once we have decided on how the shared memory allocations should work in user-space. Signed-off-by: Jyri Sarha <jyri.sarha@linux.intel.com>
The code within #if CONFIG_HOST_DMA_STREAM_SYNCHRONIZATION should free host_common resources in case of failure. The commit fixes the issue. Signed-off-by: Jyri Sarha <jyri.sarha@linux.intel.com>
0aba1c6 to
4abdf3d
Compare
|
Fixed microphone privacy related compile error from copier, and rebased. |
|
SOFCI TEST |
Now that #10223 is finally merged, here is the first module api conversions PR.
Modules converted in this PR are aria, asrc, and copier. In addition the PR contains an error branch memory leak fix I spotted while doing the conversion.
The full set of conversions, freshly rebased, is still here #10195.