Seqlock

XWOS seqlock

Overview

A seqlock is a lock that improves upon the spinlock. It primarily distinguishes between read and write operations. A seqlock contains a spinlock and a sequence value. The seqlock divides critical sections into three types:

  • Exclusive write: Any write operation on the seqlock critical section is exclusive. Each write operation first locks the spinlock, then increments the sequence value, and increments the sequence value again when leaving the critical section. In other words, the sequence value increments twice, so the number of increments is always even.
  • Non-exclusive read: If multiple CPUs perform read-only operations, they can simultaneously enter the non-exclusive read critical section. CPUs entering the non-exclusive read critical section do not need to acquire the spinlock but must first check whether the sequence value is even and record the sequence value at that time. When exiting the read critical section, they need to read the sequence value again and compare it with the previously recorded value:
    • If equal, the read result is valid;
    • If not equal, it means another CPU performed a write operation during the read, and this read operation is invalid.
  • Exclusive read: If you want the read critical section not to be invalidated by write operations, you can use the exclusive read mode. Exclusive read will exclude exclusive write and exclusive read operations on other CPUs, but will not exclude non-exclusive reads; other CPUs can still enter the non-exclusive read critical section.

Limitations

A seqlock has a flaw: when a writer writes data as a null pointer, it might cause a non-exclusive reader to dereference a null pointer and crash.

Using Seqlock

Seqlock Initialization

Like spinlocks, the seqlock structure is very small and does not provide dynamic creation and deletion methods. Users can initialize a seqlock via xwos_sqlk_init().

Multiple Locks

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

Non-exclusive Read Critical Section

If multiple CPUs perform read-only operations, they can simultaneously enter the non-exclusive read critical section. CPUs entering the non-exclusive read critical section do not need to acquire the spinlock, but must first check whether the sequence value is even and record the sequence value at that time. This operation can be done via xwos_sqlk_rd_begin().

When exiting the read critical section, the sequence value must be read again and compared with the previously recorded value. This operation can be done via xwos_sqlk_rd_retry().

  • If equal, the read result is valid;
  • If not equal, it means another CPU performed a write operation during the read, and this read operation is invalid.
        do {
                seq = xwos_sqlk_rd_begin(&lock); /* Get sequence value before entering critical section */
                /* Non-exclusive read critical section */
        } while (xwos_sqlk_rd_retry(&lock, seq)); /* Check if sequence value has changed */

Users can also read the sequence value via xwos_sqlk_get_seq() and compare it themselves.

Exclusive Read Critical Section

If you want the read critical section not to be invalidated by write operations, you can use the exclusive read mode. Exclusive read will exclude exclusive write and exclusive read operations on other CPUs, but will not exclude non-exclusive reads; other CPUs can still enter the non-exclusive read critical section.

Protecting Read Critical Sections Between Thread Contexts

  • The critical section can be nested and is only safe for thread context.
        xwos_splk_rdex_lock(&lock1);
        /* Exclusive read critical section 1 */
        xwos_splk_rdex_lock(&lock2);
        /* Exclusive read critical section 2 */
        xwos_splk_rdex_unlock(&lock2);
        /* Exclusive read critical section 1 */
        xwos_splk_rdex_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, and data within the critical section can only be accessed by thread context.
  • Scheduling cannot occur within the critical section. Users must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI

Protecting Read 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_rdex_lock_cpuirq(&lock1);
        /* Exclusive read critical section 1 */
        xwos_splk_rdex_lock_cpuirq(&lock2);
        /* Exclusive read critical section 2 */
        xwos_splk_rdex_unlock_cpuirq(&lock2);
        /* Exclusive read critical section 1: ERROR! Interrupt is enabled */
        xwos_splk_rdex_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 context.
  • Interrupts cannot occur within the critical section, and scheduling is also impossible. However, users still must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI

Protecting Nested Read 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_rdex_lock_cpuirqsv(&lock1, &cpuirq1);
        /* Exclusive read critical section 1 */
        xwos_splk_rdex_lock_cpuirqsv(&lock2, &cpuirq2);
        /* Exclusive read critical section 2 */
        xwos_splk_rdex_unlock_cpuirqrs(&loc2, cpuirq2);
        /* Exclusive read critical section 1 */
        xwos_splk_rdex_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 only be accessed by thread context.
  • Interrupts cannot occur within the critical section, and scheduling is also impossible. However, users still must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI

Protecting Exclusive Read 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_rdex_lock_irqs(&lock, irq_array, num);
        /* Exclusive read critical section */
        xwos_splk_rdex_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

Protecting Nested Read 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_rdex_lock_irqssv(&lock1, irq_array, flag1_array, num);
        /* Exclusive read critical section 1 */
        xwos_splk_rdex_lock_irqssv(&lock2, irq_array, flag2_array, num);
        /* Exclusive read critical section 2 */
        xwos_splk_rdex_unlock_irqsrs(&lock2, irq_array, flag2_array, num);
        /* Exclusive read critical section 1 */
        xwos_splk_rdex_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

Protecting Read 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_rdex_lock_bh(&lock1);
        /* Exclusive read critical section 1 */
        xwos_splk_rdex_lock_bh(&lock2);
        /* Exclusive read critical section 2 */
        xwos_splk_rdex_unlock_bh(&lock2);
        /* Exclusive read critical section 1 */
        xwos_splk_rdex_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

Read-Write Critical Section

Any read-write operation on the seqlock critical section is exclusive. Each time entering the critical section, the spinlock is first locked, then the sequence value is incremented, and it is incremented again when leaving the critical section. In other words, the sequence value increments twice, so the number of increments is always even.

Protecting Read-Write Critical Sections Between Thread Contexts

  • The critical section is only safe for thread context. The critical section can be nested.
        xwos_splk_wr_lock(&lock1);
        /* Critical section 1 */
        xwos_splk_wr_lock(&lock2);
        /* Critical section 2 */
        xwos_splk_wr_unlock(&lock2);
        /* Critical section 1 */
        xwos_splk_wr_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, and data within the critical section can only be accessed by thread context.
  • Scheduling cannot occur within the critical section. Users must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI

Protecting Read-Write 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_wr_lock_cpuirq(&lock1);
        /* Critical section 1 */
        xwos_splk_wr_lock_cpuirq(&lock2);
        /* Critical section 2 */
        xwos_splk_wr_unlock_cpuirq(&lock2);
        /* Critical section 1: ERROR! Interrupt is enabled */
        xwos_splk_wr_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 context.
  • Interrupts cannot occur within the critical section, and scheduling is also impossible. However, users still must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI

Protecting Nested Read-Write 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_wr_lock_cpuirqsv(&lock1, &cpuirq1);
        /* Critical section 1 */
        xwos_splk_wr_lock_cpuirqsv(&lock2, &cpuirq2);
        /* Critical section 2 */
        xwos_splk_wr_unlock_cpuirqrs(&loc2, cpuirq2);
        /* Critical section 1 */
        xwos_splk_wr_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 only be accessed by thread context.
  • Interrupts cannot occur within the critical section, and scheduling is also impossible. However, users still must not use CAPI that cause sleep and blocking inside the critical section.
  • CAPI

Protecting Read-Write 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_wr_lock_irqs(&lock, irq_array, num);
        /* Critical section */
        xwos_splk_wr_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

Protecting Nested Read-Write 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_wr_lock_irqssv(&lock1, irq_array, flag1_array, num);
        /* Critical section 1 */
        xwos_splk_wr_lock_irqssv(&lock2, irq_array, flag2_array, num);
        /* Critical section 2 */
        xwos_splk_wr_unlock_irqsrs(&lock2, irq_array, flag2_array, num);
        /* Critical section 1 */
        xwos_splk_wr_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

Protecting Read-Write 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_wr_lock_bh(&lock);
        /* Critical section 1 */
        xwos_splk_wr_lock_bh(&lock2);
        /* Critical section 1 */
        xwos_splk_wr_unlock_bh(&lock2);
        /* Critical section 1 */
        xwos_splk_wr_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