1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <autoconf.h>
14#include <sync/recursive_mutex.h>
15#include <stddef.h>
16#include <assert.h>
17#include <limits.h>
18
19#include <sel4/sel4.h>
20#ifdef CONFIG_DEBUG_BUILD
21#include <sel4debug/debug.h>
22#endif
23
24static void *thread_id(void)
25{
26    return (void *)seL4_GetIPCBuffer();
27}
28
29int sync_recursive_mutex_init(sync_recursive_mutex_t *mutex, seL4_CPtr notification)
30{
31    if (mutex == NULL) {
32        ZF_LOGE("Mutex passed to sync_recursive_mutex_init is NULL");
33        return -1;
34    }
35#ifdef CONFIG_DEBUG_BUILD
36    /* Check the cap actually is a notification. */
37    assert(debug_cap_is_notification(notification));
38#endif
39
40    mutex->notification.cptr = notification;
41    mutex->owner = NULL;
42    mutex->held = 0;
43
44    /* Prime the endpoint. */
45    seL4_Signal(mutex->notification.cptr);
46    return 0;
47}
48
49int sync_recursive_mutex_lock(sync_recursive_mutex_t *mutex)
50{
51    if (mutex == NULL) {
52        ZF_LOGE("Mutex passed to sync_recursive_mutex_lock is NULL");
53        return -1;
54    }
55    if (thread_id() != mutex->owner) {
56        /* We don't already have the mutex. */
57        seL4_Wait(mutex->notification.cptr, NULL);
58        __atomic_thread_fence(__ATOMIC_ACQUIRE);
59        assert(mutex->owner == NULL);
60        mutex->owner = thread_id();
61        assert(mutex->held == 0);
62    }
63    if (mutex->held == UINT_MAX) {
64        /* We would overflow if we re-acquired the mutex. Note that we can only
65         * be in this branch if we already held the mutex before entering this
66         * function, so we don't need to release the mutex here.
67         */
68        return -1;
69    }
70    mutex->held++;
71    return 0;
72}
73
74int sync_recursive_mutex_unlock(sync_recursive_mutex_t *mutex)
75{
76    if (mutex == NULL) {
77        ZF_LOGE("Mutex passed to sync_recursive_mutex_lock is NULL");
78        return -1;
79    }
80    assert(mutex->owner == thread_id());
81    assert(mutex->held > 0);
82    mutex->held--;
83    if (mutex->held == 0) {
84        /* This was the outermost lock we held. Wake the next person up. */
85        __atomic_store_n(&mutex->owner, NULL, __ATOMIC_RELEASE);
86        seL4_Signal(mutex->notification.cptr);
87    }
88    return 0;
89}
90
91int sync_recursive_mutex_new(vka_t *vka, sync_recursive_mutex_t *mutex)
92{
93    int error = vka_alloc_notification(vka, &(mutex->notification));
94
95    if (error != 0) {
96        return error;
97    } else {
98        return sync_recursive_mutex_init(mutex, mutex->notification.cptr);
99    }
100}
101
102int sync_recursive_mutex_destroy(vka_t *vka, sync_recursive_mutex_t *mutex)
103{
104    vka_free_object(vka, &(mutex->notification));
105    return 0;
106}
107