Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 30 additions & 28 deletions zephyr/lib/fast-get.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,40 +277,42 @@ void fast_put(struct k_heap *heap, const void *sram_ptr)
LOG_ERR("Put called to unknown address %p", sram_ptr);
goto out;
}

entry->refcount--;

if (!entry->refcount) {
#if CONFIG_USERSPACE
/* For large buffers, we need to:
* 1. Free the heap buffer FIRST (while partition still grants us access)
* 2. Then remove the partition (to prevent partition leaks)
* This order is critical - we must free while we still have access.
*/
if (entry->size > FAST_GET_MAX_COPY_SIZE) {
struct k_mem_partition part;
struct k_mem_domain *domain = entry->thread->mem_domain_info.mem_domain;
void *addr = entry->sram_ptr;

sof_heap_free(heap, addr);

part.start = (uintptr_t)addr;
part.size = ALIGN_UP(entry->size, CONFIG_MM_DRV_PAGE_SIZE);
part.attr = K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB;

int err = k_mem_domain_remove_partition(domain, &part);

if (err < 0)
LOG_WRN("partition removal failed err=%d", err);
LOG_DBG("freeing buffer %p", entry->sram_ptr);
sof_heap_free(heap, entry->sram_ptr);
}

} else
#endif /* CONFIG_USERSPACE */
{
/* Small buffers have no partitions, just free */
sof_heap_free(heap, entry->sram_ptr);
}
#if CONFIG_USERSPACE
/*
* For large buffers, each thread that called fast_get() has a partition
* in its memory domain. Each thread must remove its own partition here
* to prevent partition leaks.
*
* Order matters: free buffer first (needs partition for cache access),
* then remove partition.
*/
if (entry->size > FAST_GET_MAX_COPY_SIZE && entry->thread) {
struct k_mem_partition part = {
.start = (uintptr_t)entry->sram_ptr,
.size = ALIGN_UP(entry->size, CONFIG_MM_DRV_PAGE_SIZE),
.attr = K_MEM_PARTITION_P_RO_U_RO | XTENSA_MMU_CACHED_WB,
};
struct k_mem_domain *domain = k_current_get()->mem_domain_info.mem_domain;

LOG_DBG("removing partition %p size %#zx from thread %p",
(void *)part.start, part.size, k_current_get());
int err = k_mem_domain_remove_partition(domain, &part);

Comment on lines +302 to +308
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k_mem_domain_remove_partition() operates on a memory domain, which can be shared by multiple threads. Removing the partition from k_current_get()->mem_domain_info.mem_domain can therefore revoke access for other threads in the same domain that still hold references (entry->refcount > 0). To avoid use-after-revoke faults, this code should track granted partitions per-domain (and only remove when the last user in that domain releases), or otherwise enforce/document that each fast_get() user has a unique mem_domain.

Copilot uses AI. Check for mistakes.
if (err)
LOG_WRN("partition removal failed: %d", err);
}
#endif

if (!entry->refcount)
memset(entry, 0, sizeof(*entry));
}
out:
LOG_DBG("put %p, DRAM %p size %u refcnt %u", sram_ptr, entry ? entry->dram_ptr : 0,
entry ? entry->size : 0, entry ? entry->refcount : 0);
Expand Down
Loading