Re: [PATCH v10 3/4] tee: optee: support tracking system threads

Etienne CARRIERE - foss
2023-10-13T09:23:36Z

> From: Sumit Garg <sumit.garg@linaro.org>
> Sent: Friday, October 13, 2023 11:13 AM
>
> On Fri, 13 Oct 2023 at 14:09, Etienne CARRIERE - foss
> <etienne.carriere@foss.st.com> wrote:
> >
> > > From: Sumit Garg <sumit.garg@linaro.org>
> > > Sent: Friday, October 13, 2023 9:21 AM
> > >
> > > On Wed, 11 Oct 2023 at 12:41, Etienne CARRIERE - foss
> > > <etienne.carriere@foss.st.com> wrote:
> > > >
> > > > > From: Sumit Garg <sumit.garg@linaro.org>
> > > > > Sent: Friday, October 6, 2023 11:33 AM
> > > > >
> > > > > On Tue, 3 Oct 2023 at 19:36, Etienne Carriere
> > > > > <etienne.carriere@foss.st.com> wrote:
> > > > > >
> > > > > > Adds support in the OP-TEE driver to keep track of reserved system
> > > > > > threads. The logic allows one OP-TEE thread to be reserved to TEE system
> > > > > > sessions.
> > > > > >
> > > > > > The optee_cq_*() functions are updated to handle this if enabled,
> > > > > > that is when TEE describes how many thread context it supports
> > > > > > and when at least 1 session has registered as a system session
> > > > > > (using tee_client_system_session()).
> > > > > >
> > > > > > For sake of simplicity, initialization of call queue management
> > > > > > is factorized into new helper function optee_cq_init().
> > > > > >
> > > > > > The SMC ABI part of the driver enables this tracking, but the
> > > > > > FF-A ABI part does not.
> > > > > >
> > > > > >
> > > > > > Co-developed-by: Jens Wiklander <jens.wiklander@linaro.org>
> > > > > > Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
> > > > > > Co-developed-by: Sumit Garg <sumit.garg@linaro.org>
> > > > > > Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
> > > > > > Signed-off-by: Etienne Carriere <etienne.carriere@foss.st.com>
> > > > > > ---
> > > > > > Changes since v9:
> > > > > > - Add a reference counter for TEE system thread provisioning. We reserve
> > > > > > a TEE thread context for system session only when there is at least
> > > > > > 1 opened system session.
> > > > > > - Use 2 wait queue lists, normal_waiters and sys_waiter, as proposed in
> > > > > > patch v8. Using a single list can prevent a waiting system thread from
> > > > > > being resumed if the executing system thread wakes a normal waiter in
> > > > > > the list.
> > > > >
> > > > > How would that be possible? The system thread wakeup
> > > > > (free_thread_threshold = 0) is given priority over normal thread
> > > > > wakeup (free_thread_threshold = 1). I think a single queue list would
> > > > > be sufficient as demonstrated in v9.
> > > > >
> > > >
> > > > Hello Sumit,
> > > >
> > > > I think a system session can be trapped waiting when using a single queue list.
> > > > To have a chance to reach the TEE, a waiting thread must wait that a TEE thread comes out of the TEE and calls complete() on the waitqueue to wake next waiter.
> > > >
> > > > To illustrate, consider a 10 TEE threads configuration on TEE side (::total_thread_count=10 at init),
> > > > and several TEE clients in Linux OS, including 2 system sessions, from 2 consumer drivers (::sys_thread_req_count=2).
> > > >
> > > > Imagine the 9 normal threads and the 1 system thread are in use. (::free_thread_count=0),
> > > > Now comes the other system session: it goes to the waitqueue list.
> > > > Now comes a normal session invocation: it goes to the waitqueue list, 1st position.
> > > >
> > > > Now, TEE system thread returns to Linux:
> > > > It increments the counter, ::free_thread_count=1, and calls complete() for the waitequeue.
> > > > The 1st element in the waitqueue list is the last entered normal session invocation.
> > > > However, that waiter won't switch local boolean 'need_wait' to false because ::free_thread_count=1 and ::sys_thread_req_count!=0.
> > > > So no attempt to reach TEE and wake another waiter on return.
> > > > At that point there is a system session in the waitqueue list that could enter TEE (::free_thread_count=1) but is waiting someone returns from the TEE.
> > >
> > > I suppose the following loop tries to wake-up every waiter to give
> > > them a chance to enter OP-TEE. So with that system session would
> > > always be prefered over normal session, right?
> >
> > No, the below loop will wake only the 1st waiter it finds in the list that is
> > current waiting (completion_done() returns false). So if it finds a normal
> > session, it will only wake this one which, in turn, will not try to reach the
> > TEE from the while(need_wiat) loop in optee_cq_wait_init(), because there is
> > not enough free threads. Because it does not reach the TEE, it will not
> > it wake another waiter.
> >
>
> Okay I see your point, so how about the following change on top of v9.
> I still think having 2 queues is an overkill here.
>
> diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
> index df5fb5410b72..47f57054d9b7 100644
> --- a/drivers/tee/optee/call.c
> +++ b/drivers/tee/optee/call.c
> @@ -60,6 +60,7 @@ void optee_cq_wait_init(struct optee_call_queue *cq,
> */
> init_completion(&w->c);
> list_add_tail(&w->list_node, &cq->waiters);
> + w->sys_thread = sys_thread;
>
> ...
>
> @@ -83,6 +84,14 @@ static void optee_cq_complete_one(struct
> optee_call_queue *cq)
> {
> struct optee_call_waiter *w;
>
> + /* Try to wakeup system session capable threads first */
> + list_for_each_entry(w, &cq->waiters, list_node) {
> + if (!completion_done(&w->c) && w->sys_thread) {
> + complete(&w->c);
> + return;
> + }
> + }
> +

Indeed, looking for system sessions first in the list would address the issue.
I would test sys_thread firs: if (w->sys_thread && !completion_done(&w->c))

That said, is it better to have 2 lists or to have 1 list possibly scanned twice?
I'm fine with both ways.

etienne

> list_for_each_entry(w, &cq->waiters, list_node) {
> if (!completion_done(&w->c)) {
> complete(&w->c);
> diff --git a/drivers/tee/optee/optee_private.h
> b/drivers/tee/optee/optee_private.h
> index 6bb5cae09688..a7817ce9f90f 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -43,6 +43,7 @@ typedef void (optee_invoke_fn)(unsigned long,
> unsigned long, unsigned long,
> struct optee_call_waiter {
> struct list_head list_node;
> struct completion c;
> + bool sys_thread;
> };
>
> struct optee_call_queue {
>
> -Sumit
>
> > >
> > > static void optee_cq_complete_one(struct optee_call_queue *cq)
> > > {
> > > struct optee_call_waiter *w;
> > >
> > > list_for_each_entry(w, &cq->waiters, list_node) {
> > > if (!completion_done(&w->c)) {
> > > complete(&w->c);
> > > break;
> > > }
> > > }
> > > }
> > >
> > > -Sumit
> > >
> >
> > Note I've found a error in this patch v10, see below.
> >
> > BR,
> > Etienne
> >
> >
> > > >
> > > > With 2 lists, we first treat system sessions to overcome that.
> > > > Am I missing something?
> > > >
> > > > Best regards,
> > > > Etienne
> > > >
> > > > > -Sumit
> > > > >
(snip)