4.9.37内核中kfifo为何在spin_lock_irqsave下仍需smp_wmb?
自旋锁和内存屏障的作用完全不同,不能互相替代,核心原因如下:
自旋锁只保证互斥访问,不约束内存操作顺序
自旋锁spin_lock_irqsave/spin_unlock_irqrestore的作用是阻止其他CPU进入临界区,确保同一时间只有一个CPU修改fifo的状态和数据,但它管不了CPU自身的指令重排序。比如CPU可能会把更新fifo->in索引的操作(在kfifo_in里)提前到memcpy写数据的操作之前执行——因为这两个操作没有数据依赖,CPU为了性能会做这类重排。如果发生这种情况,其他CPU解锁后看到fifo->in变大了,去读fifo数据时,实际数据还没写完,就会读到脏数据。缓存一致性不保证操作顺序的可见性
即使解锁后缓存会同步到其他CPU,但指令重排序已经让fifo->in的更新先于数据写入被刷到共享缓存。smp_wmb()是写内存屏障,它强制CPU把之前所有的写操作(也就是两次memcpy)全部完成并刷到缓存,之后再执行后续的写操作(更新fifo->in),从硬件层面确保了操作的顺序。kfifo的读写逻辑依赖严格的顺序约束
kfifo的读端是通过比较in和out的差值来判断可用数据量的。如果in先被更新,读端会认为有新数据可读,但此时数据还没完成复制,读出来的内容就是错误的。smp_wmb()的存在就是为了确保数据写入完成这个操作,在**fifo->in索引更新**之前被所有CPU可见,从根本上避免这种时序问题。
代码里的注释也直接点明了这个屏障的作用:make sure that the data in the fifo is up to date before incrementing the fifo->in index counter。
内容的提问来源于stack exchange,提问作者lan muming

