linux seqlock实现

概念

读者和写着之间通过一个无符号整形来同步, 每次写都递增该值, 读操作的前后读取该值来判断读期间是否被更新,如果值被更新则读无效

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
typedef struct seqcount {
unsigned sequence;
} seqcount_t;
typedef struct {
struct seqcount seqcount;
spinlock_t lock;
} seqlock_t;
#define __SEQLOCK_UNLOCKED(lockname) \
{ \
.seqcount = SEQCNT_ZERO(lockname), \
.lock = __SPIN_LOCK_UNLOCKED(lockname) \
}
#define seqlock_init(x) \
do { \
seqcount_init(&(x)->seqcount); \
spin_lock_init(&(x)->lock); \
} while (0)
#define DEFINE_SEQLOCK(x) \
seqlock_t x = __SEQLOCK_UNLOCKED(x)

通过spinlock在写者与写者之间同步。 在写的前后增加seqcount来表面写的过程,因此seqcount&0x1=1的时候为正在写的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static inline void write_seqlock(seqlock_t *sl)
{
spin_lock(&sl->lock);
write_seqcount_begin(&sl->seqcount);
}
static inline void write_seqcount_begin(seqcount_t *s)
{
raw_write_seqcount_begin(s);
}
static inline void raw_write_seqcount_begin(seqcount_t *s)
{
s->sequence++;
smp_wmb();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
static inline void write_sequnlock(seqlock_t *sl)
{
write_seqcount_end(&sl->seqcount);
spin_unlock(&sl->lock);
}
static inline void write_seqcount_end(seqcount_t *s)
{
raw_write_seqcount_end(s);
}
static inline void raw_write_seqcount_end(seqcount_t *s)
{
smp_wmb();
s->sequence++;
}

读取的时候先调用read_seqbegin查看是否正在写, 如果正在写则busy loop等待

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static inline unsigned read_seqbegin(const seqlock_t *sl)
{
return raw_read_seqcount_begin(&sl->seqcount);
}
static inline unsigned raw_read_seqcount_begin(const seqcount_t *s)
{
unsigned ret = __read_seqcount_begin(s);
smp_rmb();
return ret;
}
static inline unsigned __read_seqcount_begin(const seqcount_t *s)
{
unsigned ret;
repeat:
ret = READ_ONCE(s->sequence);
if (unlikely(ret & 1)) { //正在写
cpu_relax(); //等待很短的时候,体系相关,可能执行nop指令
goto repeat;
}
return ret;
}

判断本次读取是否有效

1
2
3
4
5
6
7
8
9
10
static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start)
{
return unlikely(s->sequence != start);
}
static inline int read_seqcount_retry(const seqcount_t *s, unsigned start)
{
smp_rmb();
return __read_seqcount_retry(s, start);
}

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
DEFINE_SEQLOCK(demo_seqlock)
//写者
write_seqlock(&demo_seqlock)
do_write()
write_sequnlock(&demo_seqlock)
//读者
unsigned start;
do {
start = read_seqbegin(&demo_seqlock);
do_read()
}while(read_seqcount_retry(&demo_seqlock, start))

适用场景

多读少写