From ed2725433844803f7ca4879d9c5dd27c297f75b7 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 27 Nov 2013 22:36:34 +0100 Subject: [PATCH] add irq save spinlocks and semaphores --- arch/x86/include/asm/irqflags.h | 105 ++++++++++++++++++++++++++++++++ include/eduos/semaphore.h | 20 +++--- include/eduos/semaphore_types.h | 4 +- include/eduos/spinlock.h | 87 ++++++++++++++++++++++++++ include/eduos/spinlock_types.h | 13 ++++ 5 files changed, 217 insertions(+), 12 deletions(-) create mode 100644 arch/x86/include/asm/irqflags.h diff --git a/arch/x86/include/asm/irqflags.h b/arch/x86/include/asm/irqflags.h new file mode 100644 index 0000000..8bef7fc --- /dev/null +++ b/arch/x86/include/asm/irqflags.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010, Stefan Lankes, RWTH Aachen University + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @author Stefan Lankes + * @file arch/x86/include/asm/irqflags.h + * @brief Functions related to IRQ configuration + * + * This file contains definitions of inline functions + * for enabling and disabling IRQ handling. + */ + +#ifndef __ARCH_IRQFLAGS_H__ +#define __ARCH_IRQFLAGS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Disable IRQs + * + * This inline function just clears out the interrupt bit + */ +inline static void irq_disable(void) { + asm volatile("cli" ::: "memory"); +} + +/** @brief Disable IRQs (nested) + * + * Disable IRQs when unsure if IRQs were enabled at all.\n + * This function together with irq_nested_enable can be used + * in situations when interrupts shouldn't be activated if they + * were not activated before calling this function. + * + * @return The set of flags which have been set until now + */ +inline static uint32_t irq_nested_disable(void) { + size_t flags; + asm volatile("pushf; cli; pop %0": "=r"(flags) : : "memory"); + if (flags & (1 << 9)) + return 1; + return 0; +} + +/** @brief Enable IRQs */ +inline static void irq_enable(void) { + asm volatile("sti" ::: "memory"); +} + +/** @brief Enable IRQs (nested) + * + * If called after calling irq_nested_disable, this function will + * not activate IRQs if they were not active before. + * + * @param flags Flags to set. Could be the old ones you got from irq_nested_disable. + */ +inline static void irq_nested_enable(uint8_t flags) { + if (flags) + irq_enable(); +} + +/** @brief Determines, if the interrupt flags (IF) is ser + * + * @return + * - 1 interrupt flag is set + * - 0 interrupt flag is cleared + */ +inline static uint8_t is_irq_enabled(void) +{ + size_t flags; + asm volatile("pushf; pop %0": "=r"(flags) : : "memory"); + if (flags & (1 << 9)) + return 1; + return 0; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/eduos/semaphore.h b/include/eduos/semaphore.h index b0f9e7d..08908c6 100644 --- a/include/eduos/semaphore.h +++ b/include/eduos/semaphore.h @@ -64,7 +64,7 @@ inline static int sem_init(sem_t* s, unsigned int v) { s->pos = 0; for(i=0; iqueue[i] = MAX_TASKS; - spinlock_init(&s->lock); + spinlock_irqsave_init(&s->lock); return 0; } @@ -78,7 +78,7 @@ inline static int sem_destroy(sem_t* s) { if (BUILTIN_EXPECT(!s, 0)) return -EINVAL; - spinlock_destroy(&s->lock); + spinlock_irqsave_destroy(&s->lock); return 0; } @@ -98,12 +98,12 @@ inline static int sem_trywait(sem_t* s) { if (BUILTIN_EXPECT(!s, 0)) return -EINVAL; - spinlock_lock(&s->lock); + spinlock_irqsave_lock(&s->lock); if (s->value > 0) { s->value--; ret = 0; } - spinlock_unlock(&s->lock); + spinlock_irqsave_unlock(&s->lock); return ret; } @@ -121,15 +121,15 @@ inline static int sem_wait(sem_t* s) { return -EINVAL; next_try1: - spinlock_lock(&s->lock); + spinlock_irqsave_lock(&s->lock); if (s->value > 0) { s->value--; - spinlock_unlock(&s->lock); + spinlock_irqsave_unlock(&s->lock); } else { s->queue[s->pos] = current_task->id; s->pos = (s->pos + 1) % MAX_TASKS; block_current_task(); - spinlock_unlock(&s->lock); + spinlock_irqsave_unlock(&s->lock); reschedule(); goto next_try1; } @@ -146,10 +146,10 @@ inline static int sem_post(sem_t* s) { if (BUILTIN_EXPECT(!s, 0)) return -EINVAL; - spinlock_lock(&s->lock); + spinlock_irqsave_lock(&s->lock); if (s->value > 0) { s->value++; - spinlock_unlock(&s->lock); + spinlock_irqsave_unlock(&s->lock); } else { unsigned int k, i; @@ -163,7 +163,7 @@ inline static int sem_post(sem_t* s) { } i = (i + 1) % MAX_TASKS; } - spinlock_unlock(&s->lock); + spinlock_irqsave_unlock(&s->lock); } return 0; diff --git a/include/eduos/semaphore_types.h b/include/eduos/semaphore_types.h index a8c0165..b18d7b2 100644 --- a/include/eduos/semaphore_types.h +++ b/include/eduos/semaphore_types.h @@ -49,11 +49,11 @@ typedef struct { /// Position in queue unsigned int pos; /// Access lock - spinlock_t lock; + spinlock_irqsave_t lock; } sem_t; /// Macro for initialization of semaphore -#define SEM_INIT(v) {v, {[0 ... MAX_TASKS-1] = MAX_TASKS}, 0, SPINLOCK_INIT} +#define SEM_INIT(v) {v, {[0 ... MAX_TASKS-1] = MAX_TASKS}, 0, SPINLOCK_IRQSAVE_INIT} #ifdef __cplusplus } diff --git a/include/eduos/spinlock.h b/include/eduos/spinlock.h index 878d0fe..e6b3ff0 100644 --- a/include/eduos/spinlock.h +++ b/include/eduos/spinlock.h @@ -40,6 +40,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -125,6 +126,92 @@ inline static int spinlock_unlock(spinlock_t* s) { return 0; } +/** @brief Initialization of a irqsave spinlock + * + * Initialize each irqsave spinlock before use! + * + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_irqsave_init(spinlock_irqsave_t* s) { + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + atomic_int32_set(&s->queue, 0); + atomic_int32_set(&s->dequeue, 1); + s->flags = 0; + s->counter = 0; + + return 0; +} + +/** @brief Destroy irqsave spinlock after use + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_irqsave_destroy(spinlock_irqsave_t* s) { + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + s->flags = 0; + s->counter = 0; + + return 0; +} + +/** @brief Unlock an irqsave spinlock on exit of critical section + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_irqsave_lock(spinlock_irqsave_t* s) { + int32_t ticket; + uint8_t flags; + + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + flags = irq_nested_disable(); + if (s->counter == 1) { + s->counter++; + return 0; + } + + ticket = atomic_int32_add(&s->queue, 1); + while (atomic_int32_read(&s->dequeue) != ticket) { + PAUSE; + } + + s->flags = flags; + s->counter = 1; + + return 0; +} + +/** @brief Unlock irqsave spinlock on exit of critical section and re-enable interrupts + * @return + * - 0 on success + * - -EINVAL (-22) on failure + */ +inline static int spinlock_irqsave_unlock(spinlock_irqsave_t* s) { + uint8_t flags; + + if (BUILTIN_EXPECT(!s, 0)) + return -EINVAL; + + s->counter--; + if (!s->counter) { + flags = s->flags; + s->flags = 0; + atomic_int32_inc(&s->dequeue); + irq_nested_enable(flags); + } + + return 0; +} + #ifdef __cplusplus } #endif diff --git a/include/eduos/spinlock_types.h b/include/eduos/spinlock_types.h index e8fc389..9ed902f 100644 --- a/include/eduos/spinlock_types.h +++ b/include/eduos/spinlock_types.h @@ -54,8 +54,21 @@ typedef struct spinlock { uint32_t counter; } spinlock_t; +typedef struct spinlock_irqsave { + /// Internal queue + atomic_int32_t queue; + /// Internal dequeue + atomic_int32_t dequeue; + /// Internal counter var + uint32_t counter; + /// Interrupt flag + uint8_t flags; +} spinlock_irqsave_t; + /// Macro for spinlock initialization #define SPINLOCK_INIT { ATOMIC_INIT(0), ATOMIC_INIT(1), MAX_TASKS, 0} +/// Macro for irqsave spinlock initialization +#define SPINLOCK_IRQSAVE_INIT { ATOMIC_INIT(0), ATOMIC_INIT(1), 0, 0} #ifdef __cplusplus }