/* * Copyright (c) 2014, Linaro Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include "tee_mutex_wait.h" struct tee_mutex_wait { struct list_head link; struct completion comp; struct mutex mu; u32 wait_after; u32 key; }; /* * Compares two serial numbers using Serial Number Arithmetic * (https://www.ietf.org/rfc/rfc1982.txt). */ #define TICK_GT(t1, t2) \ (((t1) < (t2) && (t2) - (t1) > 0xFFFFFFFFu) || \ ((t1) > (t2) && (t1) - (t2) < 0xFFFFFFFFu)) static struct tee_mutex_wait *tee_mutex_wait_get(struct device *dev, struct tee_mutex_wait_private *priv, u32 key) { struct tee_mutex_wait *w; mutex_lock(&priv->mu); list_for_each_entry(w, &priv->db, link) if (w->key == key) goto out; w = kmalloc(sizeof(struct tee_mutex_wait), GFP_KERNEL); if (!w) { dev_err(dev, "kmalloc failed\n"); goto out; } init_completion(&w->comp); mutex_init(&w->mu); w->wait_after = 0; w->key = key; list_add_tail(&w->link, &priv->db); out: mutex_unlock(&priv->mu); return w; } static void tee_mutex_wait_delete_entry(struct tee_mutex_wait *w) { list_del(&w->link); mutex_destroy(&w->mu); kfree(w); } void tee_mutex_wait_delete(struct device *dev, struct tee_mutex_wait_private *priv, u32 key) { struct tee_mutex_wait *w; mutex_lock(&priv->mu); list_for_each_entry(w, &priv->db, link) { if (w->key == key) { tee_mutex_wait_delete_entry(w); break; } } mutex_unlock(&priv->mu); } EXPORT_SYMBOL(tee_mutex_wait_delete); void tee_mutex_wait_wakeup(struct device *dev, struct tee_mutex_wait_private *priv, u32 key, u32 wait_after) { struct tee_mutex_wait *w = tee_mutex_wait_get(dev, priv, key); if (!w) return; mutex_lock(&w->mu); w->wait_after = wait_after; mutex_unlock(&w->mu); complete(&w->comp); } EXPORT_SYMBOL(tee_mutex_wait_wakeup); void tee_mutex_wait_sleep(struct device *dev, struct tee_mutex_wait_private *priv, u32 key, u32 wait_tick) { struct tee_mutex_wait *w = tee_mutex_wait_get(dev, priv, key); u32 wait_after; if (!w) return; mutex_lock(&w->mu); wait_after = w->wait_after; mutex_unlock(&w->mu); if (TICK_GT(wait_tick, wait_after)) wait_for_completion_timeout(&w->comp, HZ); } EXPORT_SYMBOL(tee_mutex_wait_sleep); int tee_mutex_wait_init(struct tee_mutex_wait_private *priv) { mutex_init(&priv->mu); INIT_LIST_HEAD(&priv->db); return 0; } EXPORT_SYMBOL(tee_mutex_wait_init); void tee_mutex_wait_exit(struct tee_mutex_wait_private *priv) { /* * It's the callers responibility to ensure that no one is using * anything inside priv. */ mutex_destroy(&priv->mu); while (!list_empty(&priv->db)) { struct tee_mutex_wait *w = list_first_entry(&priv->db, struct tee_mutex_wait, link); tee_mutex_wait_delete_entry(w); } } EXPORT_SYMBOL(tee_mutex_wait_exit);