英语

对内存映射地址的 I/O 写操作排序

在某些平台上,所谓的内存映射 I/O 是弱排序的。在这些平台上,驱动程序编写者有责任确保对设备上内存映射地址的 I/O 写操作按预期顺序到达。这通常是通过读取一个“安全”的设备或桥接寄存器来完成的,从而导致 I/O 芯片组在发布任何读取之前刷新对设备的待处理写入。驱动程序通常会在受自旋锁保护的关键代码段退出之前立即使用此技术。这将确保后续对 I/O 空间的写入仅在所有先前的写入之后到达(很像内存屏障操作 mb(),只是针对 I/O)。

一个来自假设设备驱动程序的更具体的例子

        ...
CPU A:  spin_lock_irqsave(&dev_lock, flags)
CPU A:  val = readl(my_status);
CPU A:  ...
CPU A:  writel(newval, ring_ptr);
CPU A:  spin_unlock_irqrestore(&dev_lock, flags)
        ...
CPU B:  spin_lock_irqsave(&dev_lock, flags)
CPU B:  val = readl(my_status);
CPU B:  ...
CPU B:  writel(newval2, ring_ptr);
CPU B:  spin_unlock_irqrestore(&dev_lock, flags)
        ...

在上面的例子中,设备可能在收到 newval 之前收到 newval2,这可能会导致问题。不过,修复它很容易

        ...
CPU A:  spin_lock_irqsave(&dev_lock, flags)
CPU A:  val = readl(my_status);
CPU A:  ...
CPU A:  writel(newval, ring_ptr);
CPU A:  (void)readl(safe_register); /* maybe a config register? */
CPU A:  spin_unlock_irqrestore(&dev_lock, flags)
        ...
CPU B:  spin_lock_irqsave(&dev_lock, flags)
CPU B:  val = readl(my_status);
CPU B:  ...
CPU B:  writel(newval2, ring_ptr);
CPU B:  (void)readl(safe_register); /* maybe a config register? */
CPU B:  spin_unlock_irqrestore(&dev_lock, flags)

在这里,从 safe_register 的读取将导致 I/O 芯片组在实际向芯片组发布读取之前刷新任何待处理的写入,从而防止可能的数据损坏。