Skip to content

Conversation

@keyonjie
Copy link
Contributor

pipeline: add negotiation before propagating params

For different connected pipelines, they are usually connected via a
component with >1 source or sink buffers, as most of those components
don't have ability to converse frame format or sample rate, we need add
a params negotiation mechanism between those pipelines.

If all buffers are iterated in the params propagation direction, no
negotiation needed, otherwise we need to iterate all other buffers in
the opposite direction, to make sure all connected buffers have chance
to take part in the negotiation.

The negotiation should happen before we can propagate the params
further, when doing negotiation in a branch/buffer, we should check:
If a branch is active, check if params is matched, return error to
reject the whole .params() requirement if not.
If a branch is inactive, force update the params to its calling buffer,
to make sure all branches are aligned on the format.

We are propagating the params to branched buffer, and the subsequent
component's .params() or .prepare() should be responsible to calibrate
the buffer params if needed. For example, a component who has different
channels buffers should explicitly configure the channels for its
branched buffers (the ones connected to another pipeline).

Copy link
Member

@lgirdwood lgirdwood left a comment

Choose a reason for hiding this comment

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

We are missing some documentation here for sof-docs on how this works. @kv2019i is creating a set of concrete pipeline rules and this should be included if approved. @kv2019i how are your pipeline rules coming along ?

Copy link
Member

Choose a reason for hiding this comment

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

Can you put the calling_buf in ctx

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, after think it more, @lgirdwood I think it's better to keep this calling_buf outside of the ctx, as the all the other ctx members are constant during the whole path walking, while this calling_buf is keep changing.

So better not to store it in the ctx otherwise it will be lost as the deeper layer will overwrite and break the upper layer value.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, what's the usage of calling_buf. Is it the current or next or previous source/sink buffer in the pipeline ? It sounds like we need to rename to better reflect the usage.

Copy link
Contributor Author

@keyonjie keyonjie Apr 20, 2020

Choose a reason for hiding this comment

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

Ok, what's the usage of calling_buf. Is it the current or next or previous source/sink buffer in the pipeline ? It sounds like we need to rename to better reflect the usage.

It is the buffer ahead of the 'current' component, it could be one of the current component's source/sink buffer in the pipeline, depends on what direction we are walking in. the calling_buf is used to check and make sure we are not iterating to the path which we have done to avoid dead loop, for example, in below path, we are walking .params() in this way: b1-> comp 1 -> params negotiation with b2, b3(b1 is excluded here for it is the calling_buf) done --> b3:

b3--------|
          v
b1 -> comp 1 --> b3
          ^
b2--------|

Copy link
Member

Choose a reason for hiding this comment

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

Ok, this is really then the "next_buf". To avoid re-walking the same components you should add a member to component or buffer that is a flag for "walked already"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, this is really then the "next_buf". To avoid re-walking the same components you should add a member to component or buffer that is a flag for "walked already"

I think it is not the "next", it should be the "previous"? adding a flag could be a choice, but then we might need to manage state of this flag (e.g. we need to do one more iteration to reset all of them once the walking is done, we even need to do this several times in one .params() of the pipeline, as you may know that the pipeline_comp_hw_params() is doing one more iteration before the real pipeline_comp_hw_params() is called).

Copy link
Member

Choose a reason for hiding this comment

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

DAPM uses a "walked" flag when traversing the graph. It's reset before every walk. Please do the same here, sinec the DAPM version also survives loops (and this PR wont)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can add this flag to buffer only (no need for component), but to reset before every walk(e.g. .params(), .prepare(), .trigger(), .reset(), .copy()), we need to add a full iteration (to all buffers belong to or connected to the pipeline under iterated) ahead respectively, is this what you expected @lgirdwood ?

@keyonjie
Copy link
Contributor Author

We are missing some documentation here for sof-docs on how this works. @kv2019i is creating a set of concrete pipeline rules and this should be included if approved. @kv2019i how are your pipeline rules coming along ?

Sure, I am preparing documentation on how this works, I can add it to sof-docs when @kv2019i's one is available.

Copy link

Choose a reason for hiding this comment

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

It runs in the ppl_data->p context, so use pipe_dbg(ppl_data->p, "....", ...) to include the pipeline id in the trace messages.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, I just followed the existed ones in pipeline_comp_params() and pipeline_comp_hw_params(), fwiw, let me change it to pipe_dbg.

Copy link

Choose a reason for hiding this comment

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

Use pipe_err()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure.

Copy link

Choose a reason for hiding this comment

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

Trace message does not explain what happened. I am also not sure whether this is real run-time error, expected scenario (the message should be a warning at most then), or a logic error (and assert() should be used here)?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@mmaka1 Asserts cannot be used as sole verification, because they can be disabled. Not yet, but once we will move to Zephyr we will be able to disable asserts. So if it's a logic error, we shouldn't have just assert but also this trace and return.

And actually... We shouldn't assert on parameters that are controlled by the Linux driver, but only internal values. Asserting on an external guarantee (something the Linux driver "guarantees" is never a good idea, because mismatched versions will occur).

Copy link

Choose a reason for hiding this comment

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

@paulstelian97 I assume that asserts may be compiled out of the production code, so they should be use to catch logic errors only (those coming from coding issues) during the code bringup and debug phase. Any data coming from the driver should be verified at run-time. However I asked about this particular case, afair the parameters in this flow are internally computed by the fw, so I asked to double check whether tracing run-time error makes sense in this case. Still not sure what response is expected when seeing this error message in the trace log.

Copy link
Collaborator

Choose a reason for hiding this comment

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

If we don't know for certain this is internally computed, assume it isn't. That's the thing.

Copy link
Contributor Author

@keyonjie keyonjie May 6, 2020

Choose a reason for hiding this comment

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

@mmaka1 @paulstelian97 Thanks for the comments.

I was not sure how the helper will be used and what params and buffer it could be passed in, so I treated it as run-time error here.

But after thought about it more now, as @mmaka1 stated, here what we tried to catch here is the code logic error(e.g. some calling to buffer_params_match() with NULL buffer or params) but not the run-time params errors from the Linux kernel side, the response should be halting the execution of the FW but not just returning an false when this error happen, so changing it to 'assert' handling looks good to me.

@keyonjie keyonjie force-pushed the topic/pipeline_refine branch from 82129fa to 16a9b5c Compare May 6, 2020 04:49
@keyonjie
Copy link
Contributor Author

keyonjie commented May 7, 2020

@paulstelian97 @mmaka1 @lgirdwood comments are addressed, please help to re-review, thanks.

@lgirdwood
Copy link
Member

@keyonjie is this aligned with the pipline params rules that are being written by @mmaka1 ?

@keyonjie
Copy link
Contributor Author

keyonjie commented May 8, 2020

@keyonjie is this aligned with the pipline params rules that are being written by @mmaka1 ?

I think so, @mmaka1 will work for more complicate cases on top of this.

Copy link
Collaborator

@bkokoszx bkokoszx left a comment

Choose a reason for hiding this comment

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

@keyonjie
We can see that our TestDemuxSynchronizedCapture48000Hz24b32b4ch are failing. Those tests are using following topology:

     pipe_plb
    +-------------------------------+
    | +------+   +------+   +-----+ |                                                 +------+
    | | Host |-->| Buff |-->| Dai |-------------------------------------------------->| SSPx |
    | +------+   +------+   +-----+ |                                                 +------+
    +-------------------------------+                                                    |
                                                                                         |
                                                                                         |
        pipe_cap                                                                         |
    +----------------------------------------------------------------------------+       |
    | +-------+   +------+   +-----+   +------+   +-------+   +------+   +-----+ |    +------+
    | | Host1 |<--| B1.0 |<--| Vol |<--| B1.1 |<--| DeMux |<--| B1.2 |<--| Dai |<-----| SSPx |
    | +-------+   +------+   +-----+   +------+   +-------+   +------+   +-----+ |    +------+
    +-------------------------------------------------|--------------------------+
                                                      |
        pipe_cap                                      |
    +-------------------------------------------+     |
    | +-------+   +------+   +-----+   +------+ |     |
    | | Host2 |<--| B2.0 |<--| Vol |<--| B2.1 |<------+
    | +-------+   +------+   +-----+   +------+ |
    +-------------------------------------------+

, where capture Host1 has 48 kHz, 16/16 frame_fmt, 4 channels stream and Host2 has 48 kHz, 16/16 frame_fmt, 8 channels.
It is failing on a captured streams verification. This happens, because we do not have B1.1 well configured. Firstly, we send pcm params to Host1 and configure B1.1 with 4 channels stream, next we send pcm params to Host2 and in pipeline_comp_params_neg() we overwrite ```B1.1`` with 8 channels, which results in a mismatch in channels parameter.
In my opinion, we should add condition ensuring that we wouldn't negotiate params between pipelines working in the same direction. Something like:

diff --git a/src/audio/pipeline.c b/src/audio/pipeline.c
index c381174b2..d7a8701eb 100644
--- a/src/audio/pipeline.c
+++ b/src/audio/pipeline.c
@@ -388,6 +388,10 @@ static int pipeline_comp_params_neg(struct comp_dev *current,
        pipe_dbg(ppl_data->p, "pipeline_comp_params_neg(), current->comp.id = %u, dir = %u",
                 dev_comp_id(current), dir);

+       if (current->state == COMP_STATE_PREPARE &&
+           current->direction == ppl_data->params->params.direction)
+               return err;
+
        /* return if current is running */
        if (current->state == COMP_STATE_ACTIVE) {
                if (buffer_params_match(calling_buf,

@keyonjie
Copy link
Contributor Author

@keyonjie
We can see that our TestDemuxSynchronizedCapture48000Hz24b32b4ch are failing. Those tests are using following topology:

     pipe_plb
    +-------------------------------+
    | +------+   +------+   +-----+ |                                                 +------+
    | | Host |-->| Buff |-->| Dai |-------------------------------------------------->| SSPx |
    | +------+   +------+   +-----+ |                                                 +------+
    +-------------------------------+                                                    |
                                                                                         |
                                                                                         |
        pipe_cap                                                                         |
    +----------------------------------------------------------------------------+       |
    | +-------+   +------+   +-----+   +------+   +-------+   +------+   +-----+ |    +------+
    | | Host1 |<--| B1.0 |<--| Vol |<--| B1.1 |<--| DeMux |<--| B1.2 |<--| Dai |<-----| SSPx |
    | +-------+   +------+   +-----+   +------+   +-------+   +------+   +-----+ |    +------+
    +-------------------------------------------------|--------------------------+
                                                      |
        pipe_cap                                      |
    +-------------------------------------------+     |
    | +-------+   +------+   +-----+   +------+ |     |
    | | Host2 |<--| B2.0 |<--| Vol |<--| B2.1 |<------+
    | +-------+   +------+   +-----+   +------+ |
    +-------------------------------------------+

, where capture Host1 has 48 kHz, 16/16 frame_fmt, 4 channels stream and Host2 has 48 kHz, 16/16 frame_fmt, 8 channels.
It is failing on a captured streams verification. This happens, because we do not have B1.1 well configured. Firstly, we send pcm params to Host1 and configure B1.1 with 4 channels stream, next we send pcm params to Host2 and in pipeline_comp_params_neg() we overwrite ```B1.1`` with 8 channels, which results in a mismatch in channels parameter.

Thanks for trying this, I think you params the 2nd stream before the 1st one is started, right, this do uncover a bug in the state check, can you try if below change help?

diff --git a/src/audio/pipeline.c b/src/audio/pipeline.c
index c381174b2..4f78bd619 100644
--- a/src/audio/pipeline.c
+++ b/src/audio/pipeline.c
@@ -388,8 +388,9 @@ static int pipeline_comp_params_neg(struct comp_dev *current,
        pipe_dbg(ppl_data->p, "pipeline_comp_params_neg(), current->comp.id = %u, dir = %u",
                 dev_comp_id(current), dir);
 
-       /* return if current is running */
-       if (current->state == COMP_STATE_ACTIVE) {
+       /* return if current is already configured */
+       if (current->state != COMP_STATE_INIT &&
+           current->state != COMP_STATE_READY) {
                if (buffer_params_match(calling_buf,
                                        &ppl_data->params->params))
                        return 0;

@keyonjie keyonjie force-pushed the topic/pipeline_refine branch from 16a9b5c to fe42edb Compare May 13, 2020 03:46
@keyonjie keyonjie requested a review from bkokoszx May 13, 2020 03:48
@keyonjie
Copy link
Contributor Author

@bkokoszx the new component state coverage is added.

@lgirdwood
Copy link
Member

@keyonjie @mmaka1 @kv2019i @lbetlej how can we check this PR is aligned with our new rules ?

@keyonjie
Copy link
Contributor Author

@keyonjie @mmaka1 @kv2019i @lbetlej how can we check this PR is aligned with our new rules ?

I would leave this to be answered from @mmaka1 who is designing the new rules.

Copy link
Member

Choose a reason for hiding this comment

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

TODO ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep.

Copy link
Member

Choose a reason for hiding this comment

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

"further" what ?, can you provide context.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

typo, should be the propagation "go further".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"further" what ?, can you provide context.

fixed.

@zrombel
Copy link

zrombel commented Jul 31, 2020

Due to recent PR3194 merge, please rebase for TGL Multicore tests to Pass on Internal CI.

Copy link
Member

@lgirdwood lgirdwood left a comment

Choose a reason for hiding this comment

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

@keyonjie ping

@keyonjie
Copy link
Contributor Author

@lgirdwood @mmaka1 @bkokoszx I am not sure if we still need this change for demux/smart_amp usage scenarios?

@lgirdwood
Copy link
Member

lgirdwood commented Aug 10, 2020

@keyonjie dont think so. Lets close, we can re-open if needed.

@lgirdwood
Copy link
Member

@keyonjie @bkokoszx do we need to reopen this ? Been speaking with @mwasko and it sounds like we have a dependency ?

@bkokoszx
Copy link
Collaborator

bkokoszx commented Sep 1, 2020

@lgirdwood
Yes, we need to reopen it in order to fix #3249 issue.

@lgirdwood lgirdwood reopened this Sep 1, 2020
@keyonjie keyonjie force-pushed the topic/pipeline_refine branch from 91d864d to 5a033f8 Compare September 2, 2020 10:11
@keyonjie
Copy link
Contributor Author

keyonjie commented Sep 2, 2020

@lgirdwood rebased to the latest base. How should we proceed for this now?

@keyonjie keyonjie force-pushed the topic/pipeline_refine branch from 5a033f8 to 5bec139 Compare September 7, 2020 09:15
Copy link
Contributor Author

@keyonjie keyonjie left a comment

Choose a reason for hiding this comment

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

@lgirdwood we still need this fix for components like demux which could have multiple inputs/outputs, just updated it and addressed what remained, please help to review.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"further" what ?, can you provide context.

fixed.

Copy link
Member

Choose a reason for hiding this comment

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

This comment does not align with the code below

Copy link
Member

Choose a reason for hiding this comment

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

Do you mean walk the new pipeline here. I think at this point the connected pipeline source buffer will have the correct params configured so no further action would be needed. This can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we stop here after the connected (but not paramed) pipeline source buffer is propagated.
We might need to propagate it forward in the future, but no this requirement for now.

OK, let me remove this comment.

Copy link
Member

Choose a reason for hiding this comment

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

It's still there.

@keyonjie keyonjie force-pushed the topic/pipeline_refine branch from 5bec139 to d779ae5 Compare September 8, 2020 07:25
@keyonjie keyonjie requested review from lgirdwood and removed request for tlauda September 8, 2020 07:27
Copy link
Member

@lgirdwood lgirdwood left a comment

Choose a reason for hiding this comment

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

Just minor.

Copy link
Member

Choose a reason for hiding this comment

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

It's still there.

Add a helper buffer_param_match() to check if the configured runtime
buffer frame format/rate/channels are matched to the param ones

Signed-off-by: Keyon Jie <yang.jie@linux.intel.com>
Add a flag 'walking' to the buffer struct, to indicate if the buffer is
just being traversed while walking through the graph, and don't go back
to the buffer which is already walked.

Signed-off-by: Keyon Jie <yang.jie@linux.intel.com>
For different connected pipelines, they are usually connected via a
component with >1 source or sink buffers, as most of those components
don't have ability to converse frame format or sample rate, we need add
a params negotiation mechanism between those pipelines.

If all buffers are iterated in the params propagation direction, no
negotiation needed, otherwise we need to iterate all other buffers in
the opposite direction, to make sure all connected buffers have chance
to take part in the negotiation.

The negotiation should happen before we can propagate the params
further, when doing negotiation in a branch/buffer, we should check:
If a branch is active, check if params is matched, return error to
reject the whole .params() requirement if not.
If a branch is inactive, force update the params to its calling buffer,
to make sure all branches are aligned on the format.

We are propagating the params to branched buffer, and the subsequent
component's .params() or .prepare() should be responsible to calibrate
the buffer params if needed. For example, a component who has different
channels buffers should explicitly configure the channels for its
branched buffers (the ones connected to another pipeline).

Signed-off-by: Keyon Jie <yang.jie@linux.intel.com>
@keyonjie keyonjie force-pushed the topic/pipeline_refine branch from d779ae5 to 49e9c72 Compare September 10, 2020 05:32
@keyonjie keyonjie requested a review from lgirdwood September 10, 2020 05:33
@lgirdwood lgirdwood merged commit 64a1657 into thesofproject:master Sep 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants