HelenOS sources

root/kernel/generic/src/synch/irq_spinlock.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. owned_by_me
  2. not_owned_by_me
  3. claim
  4. unclaim
  5. owned_by_me
  6. not_owned_by_me
  7. claim
  8. unclaim
  9. irq_spinlock_initialize
  10. irq_spinlock_lock
  11. irq_spinlock_unlock
  12. irq_spinlock_trylock
  13. irq_spinlock_pass
  14. irq_spinlock_exchange
  15. irq_spinlock_locked

/*
 * Copyright (c) 2001-2004 Jakub Jermar
 * Copyright (c) 2023 Jiří Zárevúcky
 * 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.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

/** @addtogroup kernel_sync
 * @{
 */

/**
 * @file
 * @brief IRQ Spinlocks.
 */

#include <arch/asm.h>
#include <synch/spinlock.h>

#include <cpu.h>

#ifdef CONFIG_DEBUG_SPINLOCK

#define CPU_OWNER ((CPU == NULL) ? (cpu_t *) UINTPTR_MAX : CPU)

static inline bool owned_by_me(irq_spinlock_t *lock)
{
        return atomic_load_explicit(&lock->owner, memory_order_relaxed) == CPU_OWNER;
}

static inline bool not_owned_by_me(irq_spinlock_t *lock)
{
        return !owned_by_me(lock);
}

static inline void claim(irq_spinlock_t *lock)
{
        cpu_t *cpu = CPU_OWNER;
        atomic_store_explicit(&lock->owner, cpu, memory_order_relaxed);
        CURRENT->mutex_locks++;
}

static inline void unclaim(irq_spinlock_t *lock)
{
        CURRENT->mutex_locks--;
        atomic_store_explicit(&lock->owner, NULL, memory_order_relaxed);
}

#else

static inline bool owned_by_me(irq_spinlock_t *lock)
{
        return true;
}

static inline bool not_owned_by_me(irq_spinlock_t *lock)
{
        return true;
}

static inline void claim(irq_spinlock_t *lock)
{
}

static inline void unclaim(irq_spinlock_t *lock)
{
}

#endif

/** Initialize interrupts-disabled spinlock
 *
 * @param lock IRQ spinlock to be initialized.
 * @param name IRQ spinlock name.
 *
 */
void irq_spinlock_initialize(irq_spinlock_t *lock, const char *name)
{
        *lock = (irq_spinlock_t) IRQ_SPINLOCK_INITIALIZER(name);
}

/** Lock interrupts-disabled spinlock
 *
 * Lock a spinlock which requires disabled interrupts.
 *
 * @param lock    IRQ spinlock to be locked.
 * @param irq_dis If true, disables interrupts before locking the spinlock.
 *                If false, interrupts are expected to be already disabled.
 *
 */
void irq_spinlock_lock(irq_spinlock_t *lock, bool irq_dis)
{
        ASSERT_IRQ_SPINLOCK(not_owned_by_me(lock), lock);

        if (irq_dis) {
                ipl_t ipl = interrupts_disable();
                spinlock_lock(&(lock->lock));

                lock->guard = true;
                lock->ipl = ipl;
        } else {
                ASSERT_IRQ_SPINLOCK(interrupts_disabled(), lock);

                spinlock_lock(&(lock->lock));
                ASSERT_IRQ_SPINLOCK(!lock->guard, lock);
        }

        claim(lock);
}

/** Unlock interrupts-disabled spinlock
 *
 * Unlock a spinlock which requires disabled interrupts.
 *
 * @param lock    IRQ spinlock to be unlocked.
 * @param irq_res If true, interrupts are restored to previously
 *                saved interrupt level.
 *
 */
void irq_spinlock_unlock(irq_spinlock_t *lock, bool irq_res)
{
        ASSERT_IRQ_SPINLOCK(interrupts_disabled(), lock);
        ASSERT_IRQ_SPINLOCK(owned_by_me(lock), lock);

        unclaim(lock);

        if (irq_res) {
                ASSERT_IRQ_SPINLOCK(lock->guard, lock);

                lock->guard = false;
                ipl_t ipl = lock->ipl;

                spinlock_unlock(&(lock->lock));
                interrupts_restore(ipl);
        } else {
                ASSERT_IRQ_SPINLOCK(!lock->guard, lock);
                spinlock_unlock(&(lock->lock));
        }
}

/** Lock interrupts-disabled spinlock
 *
 * Lock an interrupts-disabled spinlock conditionally. If the
 * spinlock is not available at the moment, signal failure.
 * Interrupts are expected to be already disabled.
 *
 * @param lock IRQ spinlock to be locked conditionally.
 *
 * @return Zero on failure, non-zero otherwise.
 *
 */
bool irq_spinlock_trylock(irq_spinlock_t *lock)
{
        ASSERT_IRQ_SPINLOCK(interrupts_disabled(), lock);
        bool ret = spinlock_trylock(&(lock->lock));
        if (ret)
                claim(lock);

        ASSERT_IRQ_SPINLOCK((!ret) || (!lock->guard), lock);
        return ret;
}

/** Pass lock from one interrupts-disabled spinlock to another
 *
 * Pass lock from one IRQ spinlock to another IRQ spinlock
 * without enabling interrupts during the process.
 *
 * The first IRQ spinlock is supposed to be locked.
 *
 * @param unlock IRQ spinlock to be unlocked.
 * @param lock   IRQ spinlock to be locked.
 *
 */
void irq_spinlock_pass(irq_spinlock_t *unlock, irq_spinlock_t *lock)
{
        ASSERT_IRQ_SPINLOCK(interrupts_disabled(), unlock);
        ASSERT_IRQ_SPINLOCK(owned_by_me(unlock), unlock);
        ASSERT_IRQ_SPINLOCK(not_owned_by_me(lock), lock);

        /* Pass guard from unlock to lock */
        bool guard = unlock->guard;
        ipl_t ipl = unlock->ipl;
        unlock->guard = false;

        unclaim(unlock);

        spinlock_unlock(&(unlock->lock));
        spinlock_lock(&(lock->lock));

        claim(lock);

        ASSERT_IRQ_SPINLOCK(!lock->guard, lock);

        if (guard) {
                lock->guard = true;
                lock->ipl = ipl;
        }
}

/** Hand-over-hand locking of interrupts-disabled spinlocks
 *
 * Implement hand-over-hand locking between two interrupts-disabled
 * spinlocks without enabling interrupts during the process.
 *
 * The first IRQ spinlock is supposed to be locked.
 *
 * @param unlock IRQ spinlock to be unlocked.
 * @param lock   IRQ spinlock to be locked.
 *
 */
void irq_spinlock_exchange(irq_spinlock_t *unlock, irq_spinlock_t *lock)
{
        ASSERT_IRQ_SPINLOCK(interrupts_disabled(), unlock);
        ASSERT_IRQ_SPINLOCK(owned_by_me(unlock), unlock);
        ASSERT_IRQ_SPINLOCK(not_owned_by_me(lock), lock);

        spinlock_lock(&(lock->lock));
        ASSERT_IRQ_SPINLOCK(!lock->guard, lock);

        /* Pass guard from unlock to lock */
        if (unlock->guard) {
                lock->guard = true;
                lock->ipl = unlock->ipl;
                unlock->guard = false;
        }

        claim(lock);
        unclaim(unlock);

        spinlock_unlock(&(unlock->lock));
}

/** Find out whether the IRQ spinlock is currently locked.
 *
 * @param lock          IRQ spinlock.
 * @return              True if the IRQ spinlock is locked, false otherwise.
 */
bool irq_spinlock_locked(irq_spinlock_t *lock)
{
        return owned_by_me(lock) && spinlock_locked(&lock->lock);
}

/** @}
 */

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub