summaryrefslogtreecommitdiff
path: root/kernel/mutex.c
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2016-12-17 17:48:47 -0500
committerAnas Nashif <anas.nashif@intel.com>2016-12-19 14:59:35 -0500
commitd687a95611bbd737fa56ba0a99a3e65255715cac (patch)
tree7f472cc8d694f95b3dfdd5fcdcd03e824f5a43ef /kernel/mutex.c
parent9463dc0b8f0ae9916b410cb9e5f9ce8e758cc5ad (diff)
kernel: move kernel code to kernel/ directly
Also remove mentions of unified kernel in various places in the kernel, samples and documentation. Change-Id: Ice43bc73badbe7e14bae40fd6f2a302f6528a77d Signed-off-by: Anas Nashif <anas.nashif@intel.com>
Diffstat (limited to 'kernel/mutex.c')
-rw-r--r--kernel/mutex.c265
1 files changed, 265 insertions, 0 deletions
diff --git a/kernel/mutex.c b/kernel/mutex.c
new file mode 100644
index 000000000..ddf702520
--- /dev/null
+++ b/kernel/mutex.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2016 Wind River Systems, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file @brief mutex kernel services
+ *
+ * This module contains routines for handling mutex locking and unlocking.
+ *
+ * Mutexes implement a priority inheritance algorithm that boosts the priority
+ * level of the owning thread to match the priority level of the highest
+ * priority thread waiting on the mutex.
+ *
+ * Each mutex that contributes to priority inheritance must be released in the
+ * reverse order in which is was acquired. Furthermore each subsequent mutex
+ * that contributes to raising the owning thread's priority level must be
+ * acquired at a point after the most recent "bumping" of the priority level.
+ *
+ * For example, if thread A has two mutexes contributing to the raising of its
+ * priority level, the second mutex M2 must be acquired by thread A after
+ * thread A's priority level was bumped due to owning the first mutex M1.
+ * When releasing the mutex, thread A must release M2 before it releases M1.
+ * Failure to follow this nested model may result in threads running at
+ * unexpected priority levels (too high, or too low).
+ */
+
+#include <kernel.h>
+#include <kernel_structs.h>
+#include <toolchain.h>
+#include <sections.h>
+#include <wait_q.h>
+#include <misc/dlist.h>
+#include <debug/object_tracing_common.h>
+#include <errno.h>
+#include <init.h>
+
+#ifdef CONFIG_OBJECT_MONITOR
+#define RECORD_STATE_CHANGE(mutex) \
+ do { (mutex)->num_lock_state_changes++; } while ((0))
+#define RECORD_CONFLICT(mutex) \
+ do { (mutex)->num_conflicts++; } while ((0))
+#else
+#define RECORD_STATE_CHANGE(mutex) do { } while ((0))
+#define RECORD_CONFLICT(mutex) do { } while ((0))
+#endif
+
+#ifdef CONFIG_OBJECT_MONITOR
+#define INIT_OBJECT_MONITOR(mutex) do { \
+ mutex->num_lock_state_changes = 0; \
+ mutex->num_conflicts = 0; \
+ } while ((0))
+#else
+#define INIT_OBJECT_MONITOR(mutex) do { } while ((0))
+#endif
+
+extern struct k_mutex _k_mutex_list_start[];
+extern struct k_mutex _k_mutex_list_end[];
+
+struct k_mutex *_trace_list_k_mutex;
+
+#ifdef CONFIG_DEBUG_TRACING_KERNEL_OBJECTS
+
+/*
+ * Complete initialization of statically defined mutexes.
+ */
+static int init_mutex_module(struct device *dev)
+{
+ ARG_UNUSED(dev);
+
+ struct k_mutex *mutex;
+
+ for (mutex = _k_mutex_list_start; mutex < _k_mutex_list_end; mutex++) {
+ SYS_TRACING_OBJ_INIT(k_mutex, mutex);
+ }
+ return 0;
+}
+
+SYS_INIT(init_mutex_module, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
+
+#endif /* CONFIG_DEBUG_TRACING_KERNEL_OBJECTS */
+
+void k_mutex_init(struct k_mutex *mutex)
+{
+ mutex->owner = NULL;
+ mutex->lock_count = 0;
+
+ /* initialized upon first use */
+ /* mutex->owner_orig_prio = 0; */
+
+ sys_dlist_init(&mutex->wait_q);
+
+ SYS_TRACING_OBJ_INIT(k_mutex, mutex);
+ INIT_OBJECT_MONITOR(mutex);
+}
+
+static int new_prio_for_inheritance(int target, int limit)
+{
+ int new_prio = _is_prio_higher(target, limit) ? target : limit;
+
+ new_prio = _get_new_prio_with_ceiling(new_prio);
+
+ return new_prio;
+}
+
+static void adjust_owner_prio(struct k_mutex *mutex, int new_prio)
+{
+ if (mutex->owner->base.prio != new_prio) {
+
+ K_DEBUG("%p (ready (y/n): %c) prio changed to %d (was %d)\n",
+ mutex->owner, _is_thread_ready(mutex->owner) ?
+ 'y' : 'n',
+ new_prio, mutex->owner->base.prio);
+
+ _thread_priority_set(mutex->owner, new_prio);
+ }
+}
+
+int k_mutex_lock(struct k_mutex *mutex, int32_t timeout)
+{
+ int new_prio, key;
+
+ _sched_lock();
+
+ if (likely(mutex->lock_count == 0 || mutex->owner == _current)) {
+
+ RECORD_STATE_CHANGE();
+
+ mutex->owner_orig_prio = mutex->lock_count == 0 ?
+ _current->base.prio :
+ mutex->owner_orig_prio;
+
+ mutex->lock_count++;
+ mutex->owner = _current;
+
+ K_DEBUG("%p took mutex %p, count: %d, orig prio: %d\n",
+ _current, mutex, mutex->lock_count,
+ mutex->owner_orig_prio);
+
+ k_sched_unlock();
+
+ return 0;
+ }
+
+ RECORD_CONFLICT();
+
+ if (unlikely(timeout == K_NO_WAIT)) {
+ k_sched_unlock();
+ return -EBUSY;
+ }
+
+#if 0
+ if (_is_prio_higher(_current->prio, mutex->owner->prio)) {
+ new_prio = _current->prio;
+ }
+ new_prio = _get_new_prio_with_ceiling(new_prio);
+#endif
+ new_prio = new_prio_for_inheritance(_current->base.prio,
+ mutex->owner->base.prio);
+
+ key = irq_lock();
+
+ K_DEBUG("adjusting prio up on mutex %p\n", mutex);
+
+ adjust_owner_prio(mutex, new_prio);
+
+ _pend_current_thread(&mutex->wait_q, timeout);
+
+ int got_mutex = _Swap(key);
+
+ K_DEBUG("on mutex %p got_mutex value: %d\n", mutex, got_mutex);
+
+ K_DEBUG("%p got mutex %p (y/n): %c\n", _current, mutex,
+ got_mutex ? 'y' : 'n');
+
+ if (got_mutex == 0) {
+ k_sched_unlock();
+ return 0;
+ }
+
+ /* timed out */
+
+ K_DEBUG("%p timeout on mutex %p\n", _current, mutex);
+
+ struct k_thread *waiter =
+ (struct k_thread *)sys_dlist_peek_head(&mutex->wait_q);
+
+ new_prio = mutex->owner_orig_prio;
+ new_prio = waiter ? new_prio_for_inheritance(waiter->base.prio,
+ new_prio) : new_prio;
+
+ K_DEBUG("adjusting prio down on mutex %p\n", mutex);
+
+ key = irq_lock();
+ adjust_owner_prio(mutex, new_prio);
+ irq_unlock(key);
+
+ k_sched_unlock();
+
+ return -EAGAIN;
+}
+
+void k_mutex_unlock(struct k_mutex *mutex)
+{
+ int key;
+
+ __ASSERT(mutex->lock_count > 0, "");
+ __ASSERT(mutex->owner == _current, "");
+
+ _sched_lock();
+
+ RECORD_STATE_CHANGE();
+
+ mutex->lock_count--;
+
+ K_DEBUG("mutex %p lock_count: %d\n", mutex, mutex->lock_count);
+
+ if (mutex->lock_count != 0) {
+ k_sched_unlock();
+ return;
+ }
+
+ key = irq_lock();
+
+ adjust_owner_prio(mutex, mutex->owner_orig_prio);
+
+ struct k_thread *new_owner = _unpend_first_thread(&mutex->wait_q);
+
+ K_DEBUG("new owner of mutex %p: %p (prio: %d)\n",
+ mutex, new_owner, new_owner ? new_owner->base.prio : -1000);
+
+ if (new_owner) {
+ _abort_thread_timeout(new_owner);
+ _ready_thread(new_owner);
+
+ irq_unlock(key);
+
+ _set_thread_return_value(new_owner, 0);
+
+ /*
+ * new owner is already of higher or equal prio than first
+ * waiter since the wait queue is priority-based: no need to
+ * ajust its priority
+ */
+ mutex->owner = new_owner;
+ mutex->lock_count++;
+ mutex->owner_orig_prio = new_owner->base.prio;
+ } else {
+ irq_unlock(key);
+ mutex->owner = NULL;
+ }
+
+ k_sched_unlock();
+}