在NDIS驱动程序中,我需要将一些数据写入共享内存,然后通知HW获取这些数据。对共享内存的写入受NDIS自旋锁保护。在写入到共享存储器和通知HW数据已被写入之间可能存在竞争。
自旋锁是否作为一种隐含的记忆屏障来阻止这场比赛?或者应该显式地添加内存屏障?
NdisAcquireSpinLock();
writeDataToSharedMem();
NdisReleaseSpinLock();
// MemoryBarrier(); // Is an explicit memory barrier needed?
NdisWriteRegisterUlong(); // Notify the HW that data was written发布于 2021-11-08 17:04:01
NDIS自旋锁为处理器之间的同步提供了完整的内存屏障。这个障碍实际上等同于您的代码所暗示的MemoryBarrier()例程。如果你仅仅是多线程的话,你给出的代码例子就是足够的同步。
(实际上,NdisWriteRegisterUlong()还提供了内存屏障,因此在您的代码示例中存在冗余内存屏障。如果在释放自旋锁和访问寄存器之间没有其他代码,那么可以通过切换到WRITE_REGISTER_NOFENCE_ULONG来消除其中一个内存障碍。但稍后,我们将在其中添加一些代码,因此该障碍将变得有意义。)
但是,由于您要在处理器和设备之间共享数据,理论上您还需要一个原语来同步设备对内存的访问。KeFlushIoBuffers()就是这样的原始人。请注意,一些DMA例程(BuildScatterGatherList等)将在内部刷新缓冲区,因此您不必总是直接调用KeFlushIoBuffers()。但是,如果您只是将命令写入环形缓冲区,而没有在每次写入之后调用任何DMA例程,那么DMA不可能知道何时代表您刷新IO缓冲区。在这种情况下,您必须自己冲洗它。
沉迷于思想实验可能会有所帮助:如果您(以某种方式)要找到一个单处理器系统,则可以将MemoryBarrier()实现为无操作。但是,您仍然需要其他东西来在处理器和连接PCIe的网络适配器之间进行同步。KeFlushIoBuffers()是其他的东西吗?
当PCIe总线是高速缓存一致时,KeFlushIoBuffers()是无操作的,例如,因为它在体系结构上要求在大多数x86和x64平台上。因此,如果您只在x64上测试代码,那么实际上很容易忘记包含IO刷新。这就是为什么你可能不会看到其他驱动程序像理论上应该的那样使用这个例程。
顺便提一下,我建议使用NT同步原语(KeAcquireSpinLock等),而不是NDIS原语。NDIS锁被设计为操作系统抽象层,以便可以为Windows 9x、Windows CE、OS/2和Windows NT重新编译相同的源代码。如今,大多数开发人员只需要担心NT,就可以安全地直接使用NT例程。NDIS例程不会增加价值,如果您只使用NT例程,那么您的代码将被更多的人理解。
您的最终代码可能如下所示:
KeAcquireSpinLock(&command_buffer_lock);
write_to_command_buffer();
KeReleaseSpinLock(&command_buffer_lock);
KeFlushIoBuffers(command_buffer_mdl, FALSE, TRUE);
WRITE_REGISTER_ULONG(&bar->command_buffer_doorbell);如果没有其他处理器竞相访问共享内存,则根本不需要自旋锁!例如,如果您仅从专用线程访问共享内存,则可以简单地将代码更新为:
write_to_command_buffer();
KeFlushIoBuffers(command_buffer_mdl, FALSE, TRUE);
WRITE_REGISTER_ULONG(&bar->command_buffer_doorbell);https://stackoverflow.com/questions/69871126
复制相似问题