Skip to content
Merged
Show file tree
Hide file tree
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
17 changes: 16 additions & 1 deletion src/audio/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,15 @@ void comp_update_buffer_consume(struct comp_buffer __sparse_cache *buffer, uint3
(char *)audio_stream_get_addr(&buffer->stream)));
}

/*
* Locking: must be called with interrupts disabled! Serialized IPCs protect us
* from racing attach / detach calls, but the scheduler can interrupt the IPC
* thread and begin using the buffer for streaming. FIXME: this is still a
* problem with different cores.
*/
void buffer_attach(struct comp_buffer *buffer, struct list_item *head, int dir)
{
struct list_item *list = buffer_comp_list(buffer, dir);
struct list_item __sparse_cache *needs_sync;
bool further_buffers_exist;

Expand All @@ -302,11 +309,17 @@ void buffer_attach(struct comp_buffer *buffer, struct list_item *head, int dir)
if (further_buffers_exist)
dcache_writeback_region(needs_sync, sizeof(struct list_item));
/* The cache line can be prefetched here, invalidate it after prepending */
list_item_prepend(buffer_comp_list(buffer, dir), head);
list_item_prepend(list, head);
if (further_buffers_exist)
dcache_invalidate_region(needs_sync, sizeof(struct list_item));
/* no dirty cache lines exist for this buffer yet, no need to write back */
dcache_invalidate_region(uncache_to_cache(list), sizeof(*list));
Comment thread
btian1 marked this conversation as resolved.
}

/*
* Locking: must be called with interrupts disabled! See buffer_attach() above
* for details
*/
void buffer_detach(struct comp_buffer *buffer, struct list_item *head, int dir)
{
struct list_item __sparse_cache *needs_sync_prev, *needs_sync_next;
Expand All @@ -329,8 +342,10 @@ void buffer_detach(struct comp_buffer *buffer, struct list_item *head, int dir)
dcache_writeback_region(needs_sync_next, sizeof(struct list_item));
if (buffers_before_exist)
dcache_writeback_region(needs_sync_prev, sizeof(struct list_item));
dcache_writeback_region(uncache_to_cache(buf_list), sizeof(*buf_list));
/* buffers before or after can be prefetched here */
list_item_del(buf_list);
dcache_invalidate_region(uncache_to_cache(buf_list), sizeof(*buf_list));
if (buffers_after_exist)
dcache_invalidate_region(needs_sync_next, sizeof(struct list_item));
if (buffers_before_exist)
Expand Down
11 changes: 11 additions & 0 deletions src/audio/module_adapter/module_adapter.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <sof/common.h>
#include <sof/platform.h>
#include <sof/ut.h>
#include <rtos/interrupt.h>
#include <limits.h>
#include <stdint.h>

Expand Down Expand Up @@ -357,13 +358,17 @@ int module_adapter_prepare(struct comp_dev *dev)
for (i = 0; i < mod->num_output_buffers; i++) {
struct comp_buffer *buffer = buffer_alloc(buff_size, SOF_MEM_CAPS_RAM,
0, PLATFORM_DCACHE_ALIGN);
uint32_t flags;

if (!buffer) {
comp_err(dev, "module_adapter_prepare(): failed to allocate local buffer");
ret = -ENOMEM;
goto free;
}

irq_local_disable(flags);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

when turn off irq, means some or one of irq have negative impact with buffer_attach.
this part of change can make buffer_attach more secure, just not sure it is must or not.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this is needed because we're processing an IPC here and we might get interrupted by the scheduler, already running a pipeline to which we're attaching an additional source or sink.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This does feel out-of-place in module_adapter.c level code. Given both IPC and pipeline are run in regular threads, we should be able to use plain mutex'es for exclusive access here. This would make it also userspace compatible for future-proofing this code.

But maybe beyond this PR, especially given pipeline_connect() already does the IRQ enable/disable dance, so the same should be done here to be safe.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This does feel out-of-place in module_adapter.c level code. Given both IPC and pipeline are run in regular threads, we should be able to use plain mutex'es for exclusive access here. This would make it also userspace compatible for future-proofing this code.

@kv2019i maybe mutexes would work too, but would take context switching:

  1. binding of an additional pipeline is taking place
  2. a timer interrupt arrives
  3. the copying is scheduled
  4. since the LL scheduler has a higher priority than the IPC thread we switch to copying
  5. we try to take the mutex but it's held by the IPC thread
  6. the copying thread is suspended and the IPC thread is scheduled (priority inversion)
  7. the IPC thread finishes binding and releases the mutex
  8. we switch back to the copying thread

So, this should work, but would involve a bunch of switching. If the protected area is small enough, locking interrupts should be simple enough. But if we shouldn't use priority calls, then we can use a mutex

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@lyakh Ack. Let's revisit this when we introduce user-space, this is anyways not the only place where we have explicit irq control in the SOF audio code.

buffer_attach(buffer, &mod->sink_buffer_list, PPL_DIR_UPSTREAM);
irq_local_enable(flags);

buffer_c = buffer_acquire(buffer);
buffer_set_params(buffer_c, mod->stream_params, BUFFER_UPDATE_FORCE);
Expand Down Expand Up @@ -398,8 +403,11 @@ int module_adapter_prepare(struct comp_dev *dev)
list_for_item_safe(blist, _blist, &mod->sink_buffer_list) {
struct comp_buffer *buffer = container_of(blist, struct comp_buffer,
sink_list);
uint32_t flags;

irq_local_disable(flags);
buffer_detach(buffer, &mod->sink_buffer_list, PPL_DIR_UPSTREAM);
irq_local_enable(flags);
buffer_free(buffer);
}

Expand Down Expand Up @@ -1185,8 +1193,11 @@ void module_adapter_free(struct comp_dev *dev)
list_for_item_safe(blist, _blist, &mod->sink_buffer_list) {
struct comp_buffer *buffer = container_of(blist, struct comp_buffer,
sink_list);
uint32_t flags;

irq_local_disable(flags);
buffer_detach(buffer, &mod->sink_buffer_list, PPL_DIR_UPSTREAM);
irq_local_enable(flags);
buffer_free(buffer);
}

Expand Down