Fork us on GitHub Follow us on Facebook Follow us on Twitter

Changeset 2b4a9f26 in mainline


Ignore:
Timestamp:
2010-05-20T19:30:18Z (11 years ago)
Author:
Martin Decky <martin@…>
Branches:
lfn, master
Children:
42bbbe2
Parents:
b10e6e31
Message:

Add interrupts-disabled spinlocks whose purpose is the semantic distinction between spinlocks acquired always with interrupts disabled and normal spinlocks.
The compile-time distinction is implemented by the means of the type system and basic code correctness properties can be checked by the debugging assertions during run-time (with CONFIG_DEBUG)

  • Correct bracketing of top-level interrupts-disabled spinlocks
  • Correctness of the use of nested interrupts-disabled spinlocks (interrupts must be actually disabled)
  • Basic consistency of places where the interrupt level is restored

The API encapsulates the physical manipulation with the interrupt levels and also two common locking patterns

  • Sequential unlocking of spinlock A before locking of spinlock B without restoring original interrupt level (irq_spinlock_pass())
  • Hand-over-hand locking of two locks in the correct locking order, e.g. if A precedes B, then locking of B before unlocking A (irq_spinlock_exchange())

A new HAL function interrupts_disabled() should be implemented in each port to support the run-time checks of correctly disabled interrupts while locking nested interrupts-disabled spinlocks
(only in debug builds, not used in non-debug builds).

Location:
kernel
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • kernel/arch/ia32/include/asm.h

    rb10e6e31 r2b4a9f26  
    3838
    3939#include <arch/pm.h>
     40#include <arch/cpu.h>
    4041#include <typedefs.h>
    4142#include <config.h>
     
    299300}
    300301
     302/** Check interrupts state.
     303 *
     304 * @return True if interrupts are disabled.
     305 *
     306 */
     307static inline bool interrupts_disabled(void)
     308{
     309        ipl_t v;
     310       
     311        asm volatile (
     312                "pushf\n"
     313                "popl %[v]\n"
     314                : [v] "=r" (v)
     315        );
     316       
     317        return ((v & EFLAGS_IF) == 0);
     318}
     319
    301320/** Write to MSR */
    302321static inline void write_msr(uint32_t msr, uint64_t value)
  • kernel/doc/synchronization

    rb10e6e31 r2b4a9f26  
    55                                 | spinlock_t |
    66                                 +------------+
     7                                       |
     8                                       +------------------------------+
     9                                                                      |
     10                      INTERRUPTS-DISABLED SPINNING LOCKS              |
     11       irq_spinlock_lock, irq_spinlock_trylock, irq_spinlock_unlock   |
     12                               +----------------+                     |
     13                               | irq_spinlock_t |<--------------------+
     14                               +----------------+
     15
     16
    717
    818                                   WAIT QUEUES
     
    1020                                   +---------+
    1121                                   | waitq_t |
    12                                    +---------+
    13                                   /           \
     22                                   +---------+
     23                                  /           \
    1424               SEMAPHORES        /             \  CONDITION VARIABLES
    1525 semaphore_down_timeout, semaphore_up    condvar_wait_timeout, condvar_signal
     
    1828            +--------------+                         +-----------+
    1929                   |                                       ^
    20                    |                                       |
     30                   |                                       |
    2131                   |                                +------+
    22                    V                               /           
     32                   V                               /
    2333                MUTEXES                           / READERS/WRITERS LOCKS
    2434    mutex_lock_timeout, mutex_unlock   rwlock_reader/writer_lock_timeout, rwlock_unlock
    2535              +---------+                       /        +----------+
    26               | mutex_t |------------------------------->| rwlock_t |
    27               +---------+                     /          +----------+
    28                    |                         /
    29                    +------------------------+
     36              | mutex_t |------------------------------->| rwlock_t |
     37              +---------+                     /          +----------+
     38                   |                         /
     39                   +------------------------+
  • kernel/generic/include/synch/spinlock.h

    rb10e6e31 r2b4a9f26  
    4949#ifdef CONFIG_DEBUG_SPINLOCK
    5050        const char *name;
    51 #endif
     51#endif /* CONFIG_DEBUG_SPINLOCK */
    5252} spinlock_t;
    5353
     
    6060
    6161/*
    62  * SPINLOCK_INITIALIZE is to be used for statically allocated spinlocks.
    63  * It declares and initializes the lock.
     62 * SPINLOCK_INITIALIZE and SPINLOCK_STATIC_INITIALIZE are to be used
     63 * for statically allocated spinlocks. They declare (either as global
     64 * or static) symbol and initialize the lock.
    6465 */
    6566#ifdef CONFIG_DEBUG_SPINLOCK
     
    7778        }
    7879
    79 #define spinlock_lock(lock)     spinlock_lock_debug((lock))
    80 #define spinlock_unlock(lock)   spinlock_unlock_debug((lock))
    81 
    82 #else
     80#define ASSERT_SPINLOCK(expr, lock) \
     81        ASSERT_VERBOSE(expr, (lock)->name)
     82
     83#define spinlock_lock(lock)    spinlock_lock_debug((lock))
     84#define spinlock_unlock(lock)  spinlock_unlock_debug((lock))
     85
     86#else /* CONFIG_DEBUG_SPINLOCK */
    8387
    8488#define SPINLOCK_INITIALIZE_NAME(lock_name, desc_name) \
     
    9296        }
    9397
    94 #define spinlock_lock(lock)     atomic_lock_arch(&(lock)->val)
    95 #define spinlock_unlock(lock)   spinlock_unlock_nondebug((lock))
    96 
    97 #endif
     98#define ASSERT_SPINLOCK(expr, lock) \
     99        ASSERT(expr)
     100
     101#define spinlock_lock(lock)    atomic_lock_arch(&(lock)->val)
     102#define spinlock_unlock(lock)  spinlock_unlock_nondebug((lock))
     103
     104#endif /* CONFIG_DEBUG_SPINLOCK */
    98105
    99106#define SPINLOCK_INITIALIZE(lock_name) \
     
    103110        SPINLOCK_STATIC_INITIALIZE_NAME(lock_name, #lock_name)
    104111
    105 extern void spinlock_initialize(spinlock_t *lock, const char *name);
    106 extern int spinlock_trylock(spinlock_t *lock);
    107 extern void spinlock_lock_debug(spinlock_t *lock);
    108 extern void spinlock_unlock_debug(spinlock_t *lock);
     112extern void spinlock_initialize(spinlock_t *, const char *);
     113extern int spinlock_trylock(spinlock_t *);
     114extern void spinlock_lock_debug(spinlock_t *);
     115extern void spinlock_unlock_debug(spinlock_t *);
    109116
    110117/** Unlock spinlock
     
    113120 *
    114121 * @param sl Pointer to spinlock_t structure.
     122 *
    115123 */
    116124static inline void spinlock_unlock_nondebug(spinlock_t *lock)
     
    141149        }
    142150
    143 #else
     151#else /* CONFIG_DEBUG_SPINLOCK */
    144152
    145153#define DEADLOCK_PROBE_INIT(pname)
    146154#define DEADLOCK_PROBE(pname, value)
    147155
    148 #endif
     156#endif /* CONFIG_DEBUG_SPINLOCK */
    149157
    150158#else /* CONFIG_SMP */
     
    160168#define SPINLOCK_INITIALIZE_NAME(name, desc_name)
    161169#define SPINLOCK_STATIC_INITIALIZE_NAME(name, desc_name)
     170
     171#define ASSERT_SPINLOCK(expr, lock)
    162172
    163173#define spinlock_initialize(lock, name)
     
    170180#define DEADLOCK_PROBE(pname, value)
    171181
     182#endif /* CONFIG_SMP */
     183
     184typedef struct {
     185        spinlock_t lock;  /**< Spinlock */
     186        bool guard;       /**< Flag whether ipl is valid */
     187        ipl_t ipl;        /**< Original interrupt level */
     188} irq_spinlock_t;
     189
     190#define IRQ_SPINLOCK_DECLARE(lock_name)  irq_spinlock_t lock_name
     191#define IRQ_SPINLOCK_EXTERN(lock_name)   extern irq_spinlock_t lock_name
     192
     193#define ASSERT_IRQ_SPINLOCK(expr, irq_lock) \
     194        ASSERT_SPINLOCK(expr, &((irq_lock)->lock))
     195
     196/*
     197 * IRQ_SPINLOCK_INITIALIZE and IRQ_SPINLOCK_STATIC_INITIALIZE are to be used
     198 * for statically allocated interrupts-disabled spinlocks. They declare (either
     199 * as global or static symbol) and initialize the lock.
     200 */
     201#ifdef CONFIG_DEBUG_SPINLOCK
     202
     203#define IRQ_SPINLOCK_INITIALIZE_NAME(lock_name, desc_name) \
     204        irq_spinlock_t lock_name = { \
     205                .lock = { \
     206                        .name = desc_name, \
     207                        .val = { 0 } \
     208                }, \
     209                .guard = false, \
     210                .ipl = 0 \
     211        }
     212
     213#define IRQ_SPINLOCK_STATIC_INITIALIZE_NAME(lock_name, desc_name) \
     214        static irq_spinlock_t lock_name = { \
     215                .lock = { \
     216                        .name = desc_name, \
     217                        .val = { 0 } \
     218                }, \
     219                .guard = false, \
     220                .ipl = 0 \
     221        }
     222
     223#else /* CONFIG_DEBUG_SPINLOCK */
     224
     225#define IRQ_SPINLOCK_INITIALIZE_NAME(lock_name, desc_name) \
     226        irq_spinlock_t lock_name = { \
     227                .lock = { \
     228                        .val = { 0 } \
     229                }, \
     230                .guard = false, \
     231                .ipl = 0 \
     232        }
     233
     234#define IRQ_SPINLOCK_STATIC_INITIALIZE_NAME(lock_name, desc_name) \
     235        static irq_spinlock_t lock_name = { \
     236                .lock = { \
     237                        .val = { 0 } \
     238                }, \
     239                .guard = false, \
     240                .ipl = 0 \
     241        }
     242
     243#endif /* CONFIG_DEBUG_SPINLOCK */
     244
     245#define IRQ_SPINLOCK_INITIALIZE(lock_name) \
     246        IRQ_SPINLOCK_INITIALIZE_NAME(lock_name, #lock_name)
     247
     248#define IRQ_SPINLOCK_STATIC_INITIALIZE(lock_name) \
     249        IRQ_SPINLOCK_STATIC_INITIALIZE_NAME(lock_name, #lock_name)
     250
     251/** Initialize interrupts-disabled spinlock
     252 *
     253 * @param lock IRQ spinlock to be initialized.
     254 * @param name IRQ spinlock name.
     255 *
     256 */
     257static inline void irq_spinlock_initialize(irq_spinlock_t *lock, const char *name)
     258{
     259        spinlock_initialize(&(lock->lock), name);
     260        lock->guard = false;
     261        lock->ipl = 0;
     262}
     263
     264/** Lock interrupts-disabled spinlock
     265 *
     266 * Lock a spinlock which requires disabled interrupts.
     267 *
     268 * @param lock    IRQ spinlock to be locked.
     269 * @param irq_dis If true, interrupts are actually disabled
     270 *                prior locking the spinlock. If false, interrupts
     271 *                are expected to be already disabled.
     272 *
     273 */
     274static inline void irq_spinlock_lock(irq_spinlock_t *lock, bool irq_dis)
     275{
     276        if (irq_dis) {
     277                ipl_t ipl = interrupts_disable();
     278                spinlock_lock(&(lock->lock));
     279               
     280                lock->guard = true;
     281                lock->ipl = ipl;
     282        } else {
     283                ASSERT_IRQ_SPINLOCK(interrupts_disabled(), lock);
     284               
     285                spinlock_lock(&(lock->lock));
     286                ASSERT_IRQ_SPINLOCK(!lock->guard, lock);
     287        }
     288}
     289
     290/** Unlock interrupts-disabled spinlock
     291 *
     292 * Unlock a spinlock which requires disabled interrupts.
     293 *
     294 * @param lock    IRQ spinlock to be unlocked.
     295 * @param irq_res If true, interrupts are restored to previously
     296 *                saved interrupt level.
     297 *
     298 */
     299static inline void irq_spinlock_unlock(irq_spinlock_t *lock, bool irq_res)
     300{
     301        ASSERT_IRQ_SPINLOCK(interrupts_disabled(), lock);
     302       
     303        if (irq_res) {
     304                ASSERT_IRQ_SPINLOCK(lock->guard, lock);
     305               
     306                lock->guard = false;
     307                ipl_t ipl = lock->ipl;
     308               
     309                spinlock_unlock(&(lock->lock));
     310                interrupts_restore(ipl);
     311        } else {
     312                ASSERT_IRQ_SPINLOCK(!lock->guard, lock);
     313                spinlock_unlock(&(lock->lock));
     314        }
     315}
     316
     317/** Lock interrupts-disabled spinlock
     318 *
     319 * Lock an interrupts-disabled spinlock conditionally. If the
     320 * spinlock is not available at the moment, signal failure.
     321 * Interrupts are expected to be already disabled.
     322 *
     323 * @param lock IRQ spinlock to be locked conditionally.
     324 *
     325 * @return Zero on failure, non-zero otherwise.
     326 *
     327 */
     328static inline int irq_spinlock_trylock(irq_spinlock_t *lock)
     329{
     330        ASSERT_IRQ_SPINLOCK(interrupts_disabled(), lock);
     331        int rc = spinlock_trylock(&(lock->lock));
     332       
     333        ASSERT_IRQ_SPINLOCK(!lock->guard, lock);
     334        return rc;
     335}
     336
     337/** Pass lock from one interrupts-disabled spinlock to another
     338 *
     339 * Pass lock from one IRQ spinlock to another IRQ spinlock
     340 * without enabling interrupts during the process.
     341 *
     342 * The first IRQ spinlock is supposed to be locked.
     343 *
     344 * @param unlock IRQ spinlock to be unlocked.
     345 * @param lock   IRQ spinlock to be locked.
     346 *
     347 */
     348static inline void irq_spinlock_pass(irq_spinlock_t *unlock,
     349    irq_spinlock_t *lock)
     350{
     351        ASSERT_IRQ_SPINLOCK(interrupts_disabled(), unlock);
     352       
     353        /* Pass guard from unlock to lock */
     354        bool guard = unlock->guard;
     355        ipl_t ipl = unlock->ipl;
     356        unlock->guard = false;
     357       
     358        spinlock_unlock(&(unlock->lock));
     359        spinlock_lock(&(lock->lock));
     360       
     361        ASSERT_IRQ_SPINLOCK(!lock->guard, lock);
     362       
     363        if (guard) {
     364                lock->guard = true;
     365                lock->ipl = ipl;
     366        }
     367}
     368
     369/** Hand-over-hand locking of interrupts-disabled spinlocks
     370 *
     371 * Implement hand-over-hand locking between two interrupts-disabled
     372 * spinlocks without enabling interrupts during the process.
     373 *
     374 * The first IRQ spinlock is supposed to be locked.
     375 *
     376 * @param unlock IRQ spinlock to be unlocked.
     377 * @param lock   IRQ spinlock to be locked.
     378 *
     379 */
     380static inline void irq_spinlock_exchange(irq_spinlock_t *unlock,
     381    irq_spinlock_t *lock)
     382{
     383        ASSERT_IRQ_SPINLOCK(interrupts_disabled(), unlock);
     384       
     385        spinlock_lock(&(lock->lock));
     386        ASSERT_IRQ_SPINLOCK(!lock->guard, lock);
     387       
     388        /* Pass guard from unlock to lock */
     389        if (unlock->guard) {
     390                lock->guard = true;
     391                lock->ipl = unlock->ipl;
     392                unlock->guard = false;
     393        }
     394       
     395        spinlock_unlock(&(unlock->lock));
     396}
     397
    172398#endif
    173399
    174 #endif
    175 
    176400/** @}
    177401 */
  • kernel/generic/src/synch/spinlock.c

    rb10e6e31 r2b4a9f26  
    128128void spinlock_unlock_debug(spinlock_t *lock)
    129129{
    130         ASSERT(atomic_get(&lock->val) != 0);
     130        ASSERT_SPINLOCK(atomic_get(&lock->val) != 0, lock);
    131131       
    132132        /*
     
    143143/** Lock spinlock conditionally
    144144 *
    145  * Lock spinlock conditionally.
    146  * If the spinlock is not available at the moment,
    147  * signal failure.
     145 * Lock spinlock conditionally. If the spinlock is not available
     146 * at the moment, signal failure.
    148147 *
    149148 * @param lock Pointer to spinlock_t structure.
Note: See TracChangeset for help on using the changeset viewer.