diff --git a/include/nuttx/irq.h b/include/nuttx/irq.h index a4bb107a238..8f4ed3824f4 100644 --- a/include/nuttx/irq.h +++ b/include/nuttx/irq.h @@ -222,6 +222,65 @@ void leave_critical_section(irqstate_t flags); # define leave_critical_section(f) up_irq_restore(f) #endif +/**************************************************************************** + * Name: spin_lock_irqsave + * + * Description: + * If SMP and SPINLOCK_IRQ are enabled: + * Disable local interrupts and take the global spinlock (g_irq_spin) + * if the call counter (g_irq_spin_count[cpu]) equals to 0. Then the + * counter on the CPU is increment to allow nested call. + * + * NOTE: This API is very simple to protect data (e.g. H/W register + * or internal data structure) in SMP mode. But do not use this API + * with kernel APIs which suspend a caller thread. (e.g. nxsem_wait) + * + * If SMP and SPINLOCK_IRQ are not enabled: + * This function is equivalent to enter_critical_section(). + * + * Input Parameters: + * None + * + * Returned Value: + * An opaque, architecture-specific value that represents the state of + * the interrupts prior to the call to spin_lock_irqsave(); + * + ****************************************************************************/ + +#if defined (CONFIG_SMP) && defined (CONFIG_SPINLOCK_IRQ) +irqstate_t spin_lock_irqsave(void); +#else +# define spin_lock_irqsave(f) enter_critical_section(f) +#endif + +/**************************************************************************** + * Name: spin_unlock_irqrestore + * + * Description: + * If SMP and SPINLOCK_IRQ are enabled: + * Decrement the call counter (g_irq_spin_count[cpu]) and if it + * decrements to zero then release the spinlock (g_irq_spin) and + * restore the interrupt state as it was prior to the previous call to + * spin_lock_irqsave(). + * + * If SMP and SPINLOCK_IRQ are not enabled: + * This function is equivalent to leave_critical_section(). + * + * Input Parameters: + * flags - The architecture-specific value that represents the state of + * the interrupts prior to the call to spin_lock_irqsave(); + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined (CONFIG_SMP) && defined (CONFIG_SPINLOCK_IRQ) +void spin_unlock_irqrestore(irqstate_t flags); +#else +# define spin_unlock_irqrestore(f) leave_critical_section(f) +#endif + #undef EXTERN #ifdef __cplusplus } diff --git a/sched/Kconfig b/sched/Kconfig index b2ff0bba452..b5a46fe3743 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -249,6 +249,13 @@ config SPINLOCK Enables suppport for spinlocks. Spinlocks are current used only for SMP suppport. +config SPINLOCK_IRQ + bool "Support Spinlocks with IRQ control" + default n + ---help--- + Enables suppport for spinlocks with IRQ control. This feature can be + used to protect data in SMP mode. + config SMP bool "Symmetric Multi-Processing (SMP)" default n diff --git a/sched/irq/Make.defs b/sched/irq/Make.defs index 9b5b264d666..72636e3ea20 100644 --- a/sched/irq/Make.defs +++ b/sched/irq/Make.defs @@ -37,6 +37,9 @@ CSRCS += irq_initialize.c irq_attach.c irq_dispatch.c irq_unexpectedisr.c ifeq ($(CONFIG_SMP),y) CSRCS += irq_csection.c +ifeq ($(CONFIG_SPINLOCK_IRQ),y) +CSRCS += irq_spinlock.c +endif else ifeq ($(CONFIG_SCHED_INSTRUMENTATION_CSECTION),y) CSRCS += irq_csection.c endif diff --git a/sched/irq/irq_spinlock.c b/sched/irq/irq_spinlock.c new file mode 100644 index 00000000000..a6f1ee97d18 --- /dev/null +++ b/sched/irq/irq_spinlock.c @@ -0,0 +1,140 @@ +/**************************************************************************** + * sched/irq/irq_spinlock.c + * + * Copyright (C) 2017 Sony Corporation. All rights reserved. + * Author: Masayuki Ishikawa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name NuttX 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 + * COPYRIGHT OWNER 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +#include "sched/sched.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Used for access control */ + +static volatile spinlock_t g_irq_spin SP_SECTION = SP_UNLOCKED; + +/* Handles nested calls to spin_lock_irqsave and spin_unlock_irqrestore */ + +static volatile uint8_t g_irq_spin_count[CONFIG_SMP_NCPUS]; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: spin_lock_irqsave + * + * Description: + * If SMP and SPINLOCK_IRQ are enabled: + * Disable local interrupts and take the global spinlock (g_irq_spin) + * if the call counter (g_irq_spin_count[cpu]) equals to 0. Then the + * counter on the CPU is increment to allow nested call. + * + * NOTE: This API is very simple to protect data (e.g. H/W register + * or internal data structure) in SMP mode. But do not use this API + * with kernel APIs which suspend a caller thread. (e.g. nxsem_wait) + * + * If SMP and SPINLOCK_IRQ are not enabled: + * This function is equivalent to enter_critical_section(). + * + * Input Parameters: + * None + * + * Returned Value: + * An opaque, architecture-specific value that represents the state of + * the interrupts prior to the call to spin_lock_irqsave(); + * + ****************************************************************************/ + +irqstate_t spin_lock_irqsave(void) +{ + irqstate_t ret; + ret = up_irq_save(); + + int me = this_cpu(); + if (0 == g_irq_spin_count[me]) + { + spin_lock(&g_irq_spin); + } + + g_irq_spin_count[me]++; + ASSERT(0 != g_irq_spin_count[me]); + return ret; +} + +/**************************************************************************** + * Name: spin_unlock_irqrestore + * + * Description: + * If SMP and SPINLOCK_IRQ are enabled: + * Decrement the call counter (g_irq_spin_count[cpu]) and if it + * decrements to zero then release the spinlock (g_irq_spin) and + * restore the interrupt state as it was prior to the previous call to + * spin_lock_irqsave(). + * + * If SMP and SPINLOCK_IRQ are not enabled: + * This function is equivalent to leave_critical_section(). + * + * Input Parameters: + * flags - The architecture-specific value that represents the state of + * the interrupts prior to the call to spin_lock_irqsave(); + * + * Returned Value: + * None + * + ****************************************************************************/ + +void spin_unlock_irqrestore(irqstate_t flags) +{ + int me = this_cpu(); + + ASSERT(0 < g_irq_spin_count[me]); + g_irq_spin_count[me]--; + + if (0 == g_irq_spin_count[me]) + { + spin_unlock(&g_irq_spin); + } + + up_irq_restore(flags); +}