Linux kernel sync-seqlock

Source: Internet
Author: User

First, preface

Ordinary spin lock treats reader and writer equally, RW spin lock gives reader higher priority, then there is no mechanism to let writer priority lock? The answer is Seqlock. This article mainly describes the Linux kernel 4.0 in the Seqlock mechanism, first of all, seqlock work principle, if you want to scratch, then understand the concept of the East is OK, that is, the second chapter, of course, I still recommend common driver engineer to understand the Seqlock API , the third chapter gives a simple example of how this can be easily used in the drive (or in other kernel modules) using Seqlock. Detail is the devil, conceptual things need to think genius, not to say that the details of the implementation of the code is insignificant, if you want to enter the inner world of Seqlock, recommend reading the fourth chapter Seqlock code implementation, This chapter and CPU architecture related content we chose ARM64 (hehe ~ ~ to keep up with the pace of the Times). The last chapter is the reference material, if the description is not clear, you can refer to these classic documents, in countless sleepless nights, they give me the comfort of the heart, but also willing to bring happiness to the reader.

Second, the principle of work

1, overview

Seqlock This locking mechanism is prone to writer thread, that is, unless there are other writer thread into the critical section, otherwise it will be in the way, no matter how many reader thread can not block writer's footsteps. Writer thread so overbearing, reader swollen? For Seqlock,reader this side needs data access to detect whether there is a concurrent writer thread operation, if the concurrent writer detected, then read again. Through constant retry, until reader thread is in the critical section, no writer thread can be inserted. This design is not very fair to reader, especially if the writer thread load is heavier, reader thread may be retry multiple times, which can lead to a decrease in the performance of the reader thread side.

Summarize the characteristics of the Seqlock: The critical section allows only one writer thread to enter, in the absence of writer thread, reader thread can enter freely, that is, reader does not block reader. In the critical section, the writer thread can execute immediately without waiting if only reader thread is in the case.

2, writer thread of the operation

For writer thread, get the Seqlock operation as follows:

(1) acquires a lock (for example, Spin lock), which ensures that only one writer enters the critical section.

(2) Sequence counter plus one

The release Seqlock operation is as follows:

(1) Release the lock, allowing other writer thread to enter the critical section.

(2) Sequence counter plus one (note: not minus one oh, sequence counter is a constantly accumulating counter)

The above operation shows that if the critical section does not have any writer thread, then the sequence counter is even (sequence counter is initialized to 0), if the critical section has a writer thread (of course, there can only be one), Then sequence counter is odd.

3, Reader thread operation is as follows:

(1) Get the value of sequence counter, if it is even, you can enter the critical section, if it is odd, then wait for the writer to leave the critical section (sequence counter becomes even). The value of the sequence counter when entering the critical section we call the old sequence counter.

(2) Enter the critical section and read the data

(3) Get the value of sequence counter, if equals old sequence counter, explain everything OK, otherwise go back to step (1)

4, the application of the scene. Generally speaking, Seqlock is suitable for:

(1) Read operation is more frequent

(2) write operation is less, but the performance requirements are high, do not want to be blocked by reader thread (the reason why the write operation is less important to consider the performance of Read side)

(3) Data types are relatively simple, but data access cannot be protected by atomic manipulation. Let's give a simple example: suppose the data to be protected is a list, header--->a node--->b node--->c node--->null. As reader thread iterates through the list, the pointer to b node is assigned to the temporary variable x, when the interrupt occurs and reader thread is preempt (note that preemption is not forbidden for seqlock,reader). The writer thread that executes on the other CPU has plenty of time to release the memory of Node B (Note: The temporary variable x in reader thread also points to the RAM). When the read thread resumes execution and accesses the memory via the x pointer (for example, trying to find C node through next), the tragedy happened ...

Third, API example

In kernel, jiffies_64 saves the number of ticks since the start of the system, and access to that data (as well as other jiffies-related data) needs to hold Jiffies_lock this SEQ lock.

1, reader side code as follows:

U64 get_jiffies_64 (void)
{

do {
Seq = Read_seqbegin (&jiffies_lock);
ret = jiffies_64;
} while (Read_seqretry (&jiffies_lock, seq));
}

2, writer side code as follows:

static void Tick_do_update_jiffies64 (ktime_t now)
{
Write_seqlock (&jiffies_lock);

The critical section modifies related variables such as jiffies_64, and the specific code is slightly
Write_sequnlock (&jiffies_lock);
}

In contrast to the above code, any engineer can use a seqlock to protect his critical section than a gourd-drawn scoop. Of course, Seqlock's interface API is very rich and interested readers can read the Seqlock.h file themselves.

Iv. implementation of the Code

1, the definition of SEQ Lock

typedef struct {
struct Seqcount seqcount;----------sequence counter
spinlock_t lock;
} seqlock_t;

Seq Lock is actually spin lock + sequence counter.

2, Write_seqlock/write_sequnlock

static inline void Write_seqlock (seqlock_t *sl)
{
Spin_lock (&sl->lock);

sl->sequence++;
SMP_WMB ();
}

The only thing to note is SMP_WMB, a write memory barrier for SMP, which ensures that both the compiler and the CPU do not disrupt sequence counter memory access and the order in which critical section memory is accessed (critical section protection is dependent on sequence counter value, Therefore, the order cannot be disrupted). Write_sequnlock is very simple, leave it to everyone to see it.

3, Read_seqbegin

Static inline unsigned read_seqbegin (const seqlock_t *SL)
{
unsigned ret;

Repeat
ret = access_once (sl->sequence); ---To get a snapshot of Sequenc counter before entering the critical section
if (Unlikely (Ret & 1)) {----if it is odd, it indicates that there is a writer thread
Cpu_relax ();
Goto repeat; ---if there is writer, then do not enter the critical area, constantly polling sequenc counter
}

SMP_RMB (); ---Ensure the memory access order of the SEQUENC counter and critical sections
return ret;
}

If there is a writer thread,read_seqbegin function there will be a continuous polling sequenc counter, until it becomes even-numbered process, in this process, if not controlled, Then the performance of the overall system can be lost (performance here refers to power and speed). Therefore, in the polling process, there is a cpu_relax call, for ARM64, whose code is:

static inline void Cpu_relax (void)
{
ASM volatile ("yield"::: "Memory");
}

The yield instruction is used to inform the hardware system that the instructions executed on this CPU are polling operations, not so urgent, and if there is any resource conflict, this CPU can give control.

4, Read_seqretry

Static inline unsigned read_seqretry (const seqlock_t *SL, unsigned start)
{
SMP_RMB ();---Ensure the memory access order of the SEQUENC counter and critical sections
Return unlikely (sl->sequence! = start);
}

The start parameter is a snapshot of the Sequenc counter entering the critical section, which is more than the sequenc counter of the current exit critical section, if it is equal, stating that no writer has entered the interrupt reader thread, then you can leave the critical section happily.

There is one more interesting logical question: Why does Read_seqbegin want to make a parity judgment? Is it okay to put everything in the read_seqretry? In other words, why does the read_seqbegin have to wait until there is no writer thread to enter the critical section? In fact, there is a writer thread can also enter, anyway in the read_seqretry can be parity and equal judgment, so as to ensure the correctness of the logic. Of course, this is right, but there is a lack of performance, reader in the detection of a writer thread in the critical section, still put reader thread into, may lead to some additional overhead writer thread (cache Miss), so the best way to do this is to intercept in Read_seqbegin.

V. References

1. Understanding the Linux Kernel 3rd Edition

2, Linux Kernel development 3rd Edition

3, Perfbook (https://www.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.html)

Linux kernel sync-seqlock

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.