[PATCH v6 5/6] optee: separate notification functions
Sumit Garg
sumit.garg at linaro.org
Wed Oct 13 00:15:08 PDT 2021
On Wed, 6 Oct 2021 at 12:46, Jens Wiklander <jens.wiklander at linaro.org> wrote:
>
> Renames struct optee_wait_queue to struct optee_notif and all related
> functions to optee_notif_*().
>
> The implementation is changed to allow sending a notification from an
> atomic state, that is from the top half of an interrupt handler.
>
> Waiting for keys is currently only used when secure world is waiting for
> a mutex or condition variable. The old implementation could handle any
> 32-bit key while this new implementation is restricted to only 8 bits or
> the maximum value 255. A upper value is needed since a bitmap is
> allocated to allow an interrupt handler to only set a bit in case the
> waiter hasn't had the time yet to allocate and register a completion.
>
> The keys are currently only representing secure world threads which
> number usually are never even close to 255 so it should be safe for now.
> In future ABI updates the maximum value of the key will be communicated
> while the driver is initializing.
>
> Signed-off-by: Jens Wiklander <jens.wiklander at linaro.org>
> ---
> drivers/tee/optee/Makefile | 1 +
> drivers/tee/optee/core.c | 12 ++-
> drivers/tee/optee/notif.c | 125 ++++++++++++++++++++++++++++++
> drivers/tee/optee/optee_private.h | 19 +++--
> drivers/tee/optee/optee_rpc_cmd.h | 31 ++++----
> drivers/tee/optee/rpc.c | 73 ++---------------
> 6 files changed, 170 insertions(+), 91 deletions(-)
> create mode 100644 drivers/tee/optee/notif.c
>
Apart from minor nit below:
Reviewed-by: Sumit Garg <sumit.garg at linaro.org>
> diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
> index 3aa33ea9e6a6..df55e4ad5370 100644
> --- a/drivers/tee/optee/Makefile
> +++ b/drivers/tee/optee/Makefile
> @@ -2,6 +2,7 @@
> obj-$(CONFIG_OPTEE) += optee.o
> optee-objs += core.o
> optee-objs += call.o
> +optee-objs += notif.o
> optee-objs += rpc.o
> optee-objs += supp.o
> optee-objs += shm_pool.o
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 5ce13b099d7d..8531184f98f4 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -592,6 +592,7 @@ static int optee_remove(struct platform_device *pdev)
> */
> optee_disable_shm_cache(optee);
>
> + optee_notif_uninit(optee);
> /*
> * The two devices have to be unregistered before we can free the
> * other resources.
> @@ -602,7 +603,6 @@ static int optee_remove(struct platform_device *pdev)
> tee_shm_pool_free(optee->pool);
> if (optee->memremaped_shm)
> memunmap(optee->memremaped_shm);
> - optee_wait_queue_exit(&optee->wait_queue);
> optee_supp_uninit(&optee->supp);
> mutex_destroy(&optee->call_queue.mutex);
>
> @@ -712,11 +712,17 @@ static int optee_probe(struct platform_device *pdev)
>
> mutex_init(&optee->call_queue.mutex);
> INIT_LIST_HEAD(&optee->call_queue.waiters);
> - optee_wait_queue_init(&optee->wait_queue);
> optee_supp_init(&optee->supp);
> optee->memremaped_shm = memremaped_shm;
> optee->pool = pool;
>
> + platform_set_drvdata(pdev, optee);
> + rc = optee_notif_init(optee, 255);
nit: Can you use a macro here instead of a constant with a proper
comment similar to the one in commit description?
-Sumit
> + if (rc) {
> + optee_remove(pdev);
> + return rc;
> + }
> +
> /*
> * Ensure that there are no pre-existing shm objects before enabling
> * the shm cache so that there's no chance of receiving an invalid
> @@ -731,8 +737,6 @@ static int optee_probe(struct platform_device *pdev)
> if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
> pr_info("dynamic shared memory is enabled\n");
>
> - platform_set_drvdata(pdev, optee);
> -
> rc = optee_enumerate_devices(PTA_CMD_GET_DEVICES);
> if (rc) {
> optee_remove(pdev);
> diff --git a/drivers/tee/optee/notif.c b/drivers/tee/optee/notif.c
> new file mode 100644
> index 000000000000..a28fa03dcd0e
> --- /dev/null
> +++ b/drivers/tee/optee/notif.c
> @@ -0,0 +1,125 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2015-2021, Linaro Limited
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/arm-smccc.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/tee_drv.h>
> +#include "optee_private.h"
> +
> +struct notif_entry {
> + struct list_head link;
> + struct completion c;
> + u_int key;
> +};
> +
> +static bool have_key(struct optee *optee, u_int key)
> +{
> + struct notif_entry *entry;
> +
> + list_for_each_entry(entry, &optee->notif.db, link)
> + if (entry->key == key)
> + return true;
> +
> + return false;
> +}
> +
> +int optee_notif_wait(struct optee *optee, u_int key)
> +{
> + unsigned long flags;
> + struct notif_entry *entry;
> + int rc = 0;
> +
> + if (key > optee->notif.max_key)
> + return -EINVAL;
> +
> + entry = kmalloc(sizeof(*entry), GFP_KERNEL);
> + if (!entry)
> + return -ENOMEM;
> + init_completion(&entry->c);
> + entry->key = key;
> +
> + spin_lock_irqsave(&optee->notif.lock, flags);
> +
> + /*
> + * If the bit is already set it means that the key has already
> + * been posted and we must not wait.
> + */
> + if (test_bit(key, optee->notif.bitmap)) {
> + clear_bit(key, optee->notif.bitmap);
> + goto out;
> + }
> +
> + /*
> + * Check if someone is already waiting for this key. If there is
> + * it's a programming error.
> + */
> + if (have_key(optee, key)) {
> + rc = -EBUSY;
> + goto out;
> + }
> +
> + list_add_tail(&entry->link, &optee->notif.db);
> +
> + /*
> + * Unlock temporarily and wait for completion.
> + */
> + spin_unlock_irqrestore(&optee->notif.lock, flags);
> + wait_for_completion(&entry->c);
> + spin_lock_irqsave(&optee->notif.lock, flags);
> +
> + list_del(&entry->link);
> +out:
> + spin_unlock_irqrestore(&optee->notif.lock, flags);
> +
> + kfree(entry);
> +
> + return rc;
> +}
> +
> +int optee_notif_send(struct optee *optee, u_int key)
> +{
> + unsigned long flags;
> + struct notif_entry *entry;
> +
> + if (key > optee->notif.max_key)
> + return -EINVAL;
> +
> + spin_lock_irqsave(&optee->notif.lock, flags);
> +
> + list_for_each_entry(entry, &optee->notif.db, link)
> + if (entry->key == key) {
> + complete(&entry->c);
> + goto out;
> + }
> +
> + /* Only set the bit in case there where nobody waiting */
> + set_bit(key, optee->notif.bitmap);
> +out:
> + spin_unlock_irqrestore(&optee->notif.lock, flags);
> +
> + return 0;
> +}
> +
> +int optee_notif_init(struct optee *optee, u_int max_key)
> +{
> + spin_lock_init(&optee->notif.lock);
> + INIT_LIST_HEAD(&optee->notif.db);
> + optee->notif.bitmap = bitmap_zalloc(max_key, GFP_KERNEL);
> + if (!optee->notif.bitmap)
> + return -ENOMEM;
> +
> + optee->notif.max_key = max_key;
> +
> + return 0;
> +}
> +
> +void optee_notif_uninit(struct optee *optee)
> +{
> + kfree(optee->notif.bitmap);
> +}
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index dbdd367be156..76a16d9b6cf4 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -35,10 +35,12 @@ struct optee_call_queue {
> struct list_head waiters;
> };
>
> -struct optee_wait_queue {
> - /* Serializes access to this struct */
> - struct mutex mu;
> +struct optee_notif {
> + u_int max_key;
> + /* Serializes access to the elements below in this struct */
> + spinlock_t lock;
> struct list_head db;
> + u_long *bitmap;
> };
>
> /**
> @@ -72,8 +74,7 @@ struct optee_supp {
> * @teedev: client device
> * @invoke_fn: function to issue smc or hvc
> * @call_queue: queue of threads waiting to call @invoke_fn
> - * @wait_queue: queue of threads from secure world waiting for a
> - * secure world sync object
> + * @notif: notification synchronization struct
> * @supp: supplicant synchronization struct for RPC to supplicant
> * @pool: shared memory pool
> * @memremaped_shm virtual address of memory in shared memory pool
> @@ -88,7 +89,7 @@ struct optee {
> struct tee_device *teedev;
> optee_invoke_fn *invoke_fn;
> struct optee_call_queue call_queue;
> - struct optee_wait_queue wait_queue;
> + struct optee_notif notif;
> struct optee_supp supp;
> struct tee_shm_pool *pool;
> void *memremaped_shm;
> @@ -131,8 +132,10 @@ void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param,
> struct optee_call_ctx *call_ctx);
> void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx);
>
> -void optee_wait_queue_init(struct optee_wait_queue *wq);
> -void optee_wait_queue_exit(struct optee_wait_queue *wq);
> +int optee_notif_init(struct optee *optee, u_int max_key);
> +void optee_notif_uninit(struct optee *optee);
> +int optee_notif_wait(struct optee *optee, u_int key);
> +int optee_notif_send(struct optee *optee, u_int key);
>
> u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
> struct tee_param *param);
> diff --git a/drivers/tee/optee/optee_rpc_cmd.h b/drivers/tee/optee/optee_rpc_cmd.h
> index b8275140cef8..f3f06e0994a7 100644
> --- a/drivers/tee/optee/optee_rpc_cmd.h
> +++ b/drivers/tee/optee/optee_rpc_cmd.h
> @@ -28,24 +28,27 @@
> #define OPTEE_RPC_CMD_GET_TIME 3
>
> /*
> - * Wait queue primitive, helper for secure world to implement a wait queue.
> + * Notification from/to secure world.
> *
> - * If secure world needs to wait for a secure world mutex it issues a sleep
> - * request instead of spinning in secure world. Conversely is a wakeup
> - * request issued when a secure world mutex with a thread waiting thread is
> - * unlocked.
> + * If secure world needs to wait for something, for instance a mutex, it
> + * does a notification wait request instead of spinning in secure world.
> + * Conversely can a synchronous notification can be sent when a secure
> + * world mutex with a thread waiting thread is unlocked.
> *
> - * Waiting on a key
> - * [in] value[0].a OPTEE_RPC_WAIT_QUEUE_SLEEP
> - * [in] value[0].b Wait key
> + * This interface can also be used to wait for a asynchronous notification
> + * which instead is sent via a non-secure interrupt.
> *
> - * Waking up a key
> - * [in] value[0].a OPTEE_RPC_WAIT_QUEUE_WAKEUP
> - * [in] value[0].b Wakeup key
> + * Waiting on notification
> + * [in] value[0].a OPTEE_RPC_NOTIFICATION_WAIT
> + * [in] value[0].b notification value
> + *
> + * Sending a synchronous notification
> + * [in] value[0].a OPTEE_RPC_NOTIFICATION_SEND
> + * [in] value[0].b notification value
> */
> -#define OPTEE_RPC_CMD_WAIT_QUEUE 4
> -#define OPTEE_RPC_WAIT_QUEUE_SLEEP 0
> -#define OPTEE_RPC_WAIT_QUEUE_WAKEUP 1
> +#define OPTEE_RPC_CMD_NOTIFICATION 4
> +#define OPTEE_RPC_NOTIFICATION_WAIT 0
> +#define OPTEE_RPC_NOTIFICATION_SEND 1
>
> /*
> * Suspend execution
> diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c
> index efbaff7ad7e5..fa492655843a 100644
> --- a/drivers/tee/optee/rpc.c
> +++ b/drivers/tee/optee/rpc.c
> @@ -1,6 +1,6 @@
> // SPDX-License-Identifier: GPL-2.0-only
> /*
> - * Copyright (c) 2015-2016, Linaro Limited
> + * Copyright (c) 2015-2021, Linaro Limited
> */
>
> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> @@ -14,23 +14,6 @@
> #include "optee_smc.h"
> #include "optee_rpc_cmd.h"
>
> -struct wq_entry {
> - struct list_head link;
> - struct completion c;
> - u32 key;
> -};
> -
> -void optee_wait_queue_init(struct optee_wait_queue *priv)
> -{
> - mutex_init(&priv->mu);
> - INIT_LIST_HEAD(&priv->db);
> -}
> -
> -void optee_wait_queue_exit(struct optee_wait_queue *priv)
> -{
> - mutex_destroy(&priv->mu);
> -}
> -
> static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg)
> {
> struct timespec64 ts;
> @@ -143,48 +126,6 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx,
> }
> #endif
>
> -static struct wq_entry *wq_entry_get(struct optee_wait_queue *wq, u32 key)
> -{
> - struct wq_entry *w;
> -
> - mutex_lock(&wq->mu);
> -
> - list_for_each_entry(w, &wq->db, link)
> - if (w->key == key)
> - goto out;
> -
> - w = kmalloc(sizeof(*w), GFP_KERNEL);
> - if (w) {
> - init_completion(&w->c);
> - w->key = key;
> - list_add_tail(&w->link, &wq->db);
> - }
> -out:
> - mutex_unlock(&wq->mu);
> - return w;
> -}
> -
> -static void wq_sleep(struct optee_wait_queue *wq, u32 key)
> -{
> - struct wq_entry *w = wq_entry_get(wq, key);
> -
> - if (w) {
> - wait_for_completion(&w->c);
> - mutex_lock(&wq->mu);
> - list_del(&w->link);
> - mutex_unlock(&wq->mu);
> - kfree(w);
> - }
> -}
> -
> -static void wq_wakeup(struct optee_wait_queue *wq, u32 key)
> -{
> - struct wq_entry *w = wq_entry_get(wq, key);
> -
> - if (w)
> - complete(&w->c);
> -}
> -
> static void handle_rpc_func_cmd_wq(struct optee *optee,
> struct optee_msg_arg *arg)
> {
> @@ -196,11 +137,13 @@ static void handle_rpc_func_cmd_wq(struct optee *optee,
> goto bad;
>
> switch (arg->params[0].u.value.a) {
> - case OPTEE_RPC_WAIT_QUEUE_SLEEP:
> - wq_sleep(&optee->wait_queue, arg->params[0].u.value.b);
> + case OPTEE_RPC_NOTIFICATION_WAIT:
> + if (optee_notif_wait(optee, arg->params[0].u.value.b))
> + goto bad;
> break;
> - case OPTEE_RPC_WAIT_QUEUE_WAKEUP:
> - wq_wakeup(&optee->wait_queue, arg->params[0].u.value.b);
> + case OPTEE_RPC_NOTIFICATION_SEND:
> + if (optee_notif_send(optee, arg->params[0].u.value.b))
> + goto bad;
> break;
> default:
> goto bad;
> @@ -463,7 +406,7 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
> case OPTEE_RPC_CMD_GET_TIME:
> handle_rpc_func_cmd_get_time(arg);
> break;
> - case OPTEE_RPC_CMD_WAIT_QUEUE:
> + case OPTEE_RPC_CMD_NOTIFICATION:
> handle_rpc_func_cmd_wq(optee, arg);
> break;
> case OPTEE_RPC_CMD_SUSPEND:
> --
> 2.31.1
>
More information about the linux-arm-kernel
mailing list