Spinlock

XWOS spinlock

Overview

A spinlock is a lock introduced in multi-core systems to prevent multiple processors from accessing a common memory area (called a critical section) simultaneously. When one CPU acquires the spinlock and accesses the critical section, other CPUs can only spin waiting for the lock. Spinning refers to continuously looping and testing whether the lock has been released.

The spinlock is a lock mechanism designed specifically for SMP scenarios. In a single-core (UP) system, a spinlock is not needed. However, for the uniformity of software interfaces, a dummy spinlock is implemented, which is simply an encapsulation of disabling preemption, bottom half (BH), or interrupts.

A spinlock is also accompanied by other operations: memory barrier, disabling scheduler preemption, disabling bottom half (BH), disabling interrupts, etc.

Limitations

  • A spinlock contains memory barrier operations, which reduce CPU performance;
  • A spinlock does not distinguish between read and write operations.

Using Spinlock

Spinlock Initialization

A spinlock is implemented based on atomic operation instructions. The spinlock structure is very small, with its core data being a primitive data type that a CPU instruction can operate on, so dynamic creation and deletion methods are not provided. Users can initialize a spinlock via xwos_splk_init().

Multiple Locks

When using multiple spinlocks to protect critical sections, the locking and unlocking order must be consistent, otherwise it will cause deadlock.

Lock Modes

Protecting Critical Sections Between Thread Contexts

  • The critical section can be nested and is only safe for thread context.
        xwos_splk_lock(&lock1);
        /* Critical section 1 */
        xwos_splk_lock(&lock2);
        /* Critical section 2 */
        xwos_splk_unlock(&lock2);
        /* Critical section 1 */
        xwos_splk_unlock(&lock1);
  • Within the critical section, only preemption is disabled. This can be understood as: at the thread level, operations within the critical section are atomic.
  • Data within the critical section can only be accessed by thread code.
  • Preemption cannot occur within the critical section. Users must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI

Protecting Single Critical Sections Between Any Context

  • The critical section is safe for any context. However, the critical section must be single; nesting will cause the following error:
        xwos_splk_lock_cpuirq(&lock1);
        /* Critical section 1 */
        xwos_splk_lock_cpuirq(&lock2);
        /* Critical section 2 */
        xwos_splk_unlock_cpuirq(&lock2);
        /* Critical section 1: ERROR! Interrupt is enabled */
        xwos_splk_unlock_cpuirq(&lock1);
  • Within the critical section, not only preemption is disabled, but CPU interrupts are also disabled. This can be understood as: operations within the critical section are atomic, and data within the critical section can be accessed by any code.
  • Interrupts cannot occur within the critical section, and scheduling also cannot occur. Users still must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI

Protecting Nested Critical Sections Between Any Context

  • To solve the problem of nesting critical sections when interrupts are disabled, the CAPI for saving and restoring interrupt flags can be used.
        xwos_splk_lock_cpuirqsv(&lock1, &cpuirq1);
        /* Critical section 1 */
        xwos_splk_lock_cpuirqsv(&lock2, &cpuirq2);
        /* Critical section 2 */
        xwos_splk_unlock_cpuirqrs(&loc2, cpuirq2); /* Interrupt flag is still disabled when exiting critical section 2 */
        /* Critical section 1: No error of interrupt being enabled */
        xwos_splk_unlock_cpuirqrs(&loc1, cpuirq1);
  • Within the critical section, not only preemption is disabled, but CPU interrupts are also disabled. This can be understood as: operations within the critical section are atomic, and data within the critical section can be accessed by any code.
  • Interrupts cannot occur within the critical section, and scheduling also cannot occur. Users still must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI

Protecting Critical Sections Between Thread and Specified Interrupt (IRQ) Contexts

  • The critical section is safe for thread and specified interrupt (IRQ) contexts. However, the critical section must be single.
        xwos_splk_lock_irqs(&lock, irq_array, num);
        /* Critical section */
        xwos_splk_unlock_irqs(&lock, irq_array, num);
  • Within the critical section, only preemption and specified interrupts are disabled. This can be understood as: at the level of thread and specified interrupt functions, operations within the critical section are atomic, and data within the critical section can only be accessed by thread and specified peripheral interrupt (IRQ) contexts.
  • Specified interrupts cannot occur within the critical section, and scheduling also cannot occur. However, users still must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI
    • xwos_splk_lock_irqs() : Lock, disable preemption, disable partial interrupts (IRQ), enter critical section
    • xwos_splk_trylock_irqs() : Try to lock, disable preemption, disable partial interrupts (IRQ), try to enter critical section
    • xwos_splk_unlock_irqs() : Unlock, enable preemption, enable partial interrupts (IRQ), exit critical section

Protecting Nested Critical Sections Between Thread and Specified Interrupt (IRQ) Contexts

  • The critical section is safe for thread and specified interrupt (IRQ) contexts. The critical section can be nested.
        xwos_splk_lock_irqssv(&lock1, irq_array, flag1_array, num);
        /* Critical section 1 */
        xwos_splk_lock_irqssv(&lock2, irq_array, flag2_array, num);
        /* Critical section 2 */
        xwos_splk_unlock_irqsrs(&lock2, irq_array, flag2_array, num);
        /* Critical section 1 */
        xwos_splk_unlock_irqsrs(&lock1, irq_array, flag1_array, num);
  • Within the critical section, only preemption and specified interrupts are disabled. This can be understood as: at the level of thread and specified interrupt functions,
  • operations within the critical section are atomic, and data within the critical section can only be accessed by thread and specified peripheral interrupt (IRQ) contexts.
  • Specified interrupts cannot occur within the critical section, and scheduling also cannot occur. However, users still must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI
    • xwos_splk_lock_irqssv() : Lock, disable preemption, save partial interrupt (IRQ) flags and disable, enter critical section
    • xwos_splk_trylock_irqssv() : Try to lock, disable preemption, save partial interrupt (IRQ) flags and disable, try to enter critical section
    • xwos_splk_unlock_irqsrs() : Unlock, enable preemption, restore partial interrupts (IRQ), exit critical section

Protecting Critical Sections Between Thread and Bottom Half (BH) Contexts

  • The critical section is safe for thread and bottom half (BH) contexts. The critical section can be nested.
        xwos_splk_lock_bh(&lock1);
        /* Critical section 1 */
        xwos_splk_lock_bh(&lock2);
        /* Critical section 2 */
        xwos_splk_unlock_bh(&lock2);
        /* Critical section 1 */
        xwos_splk_unlock_bh(&lock1);
  • Within the critical section, only preemption and bottom half (BH) are disabled. This can be understood as: at the level of thread and bottom half, operations within the critical section are atomic, and data within the critical section can only be accessed by thread and bottom half (BH) contexts.
  • Scheduling cannot occur within the critical section. However, users still must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI

CAPI Reference