我们看一个wmb()和rmb()的使用例子。我们需要到设备驱动中寻找,就顺便选一个我也不清楚的网卡驱动吧(drivers/net/8139too.c)。
static netdev_tx_t rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) { /* * Writing to TxStatus triggers a DMA transfer of the data * copied to tp->tx_buf[entry] above. Use a memory barrier * to make sure that the device sees the updated data. */ wmb(); RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), tp->tx_flag | max(len, (unsigned int)ETH_ZLEN)); }
从这里的注释我们其实可以看出RTL_W32_F()操作应该是发起一次设备DMA操作(通过写硬件寄存器实现)。wmb()的作用是保证写入DMA发起操作命令之前写入内存的数据都已经写入内存,保证DMA操作时可以看见最新的数据。简单思考就是保证寄存器操作必须是最后发生的。我们继续找个rmb()的例子(drivers/net/bnx2.c)。
static int bnx2_rx_int(struct bnx2 *bp, struct bnx2_napi *bnapi, int budget) { sw_cons = rxr->rx_cons; sw_prod = rxr->rx_prod; /* Memory barrier necessary as speculative reads of the rx * buffer can be ahead of the index in the status block */ rmb(); while (sw_cons != hw_cons) { }
这里的rmb()就是为了防止while循环里面的load操作在rmb()之前发生。也就说这里是有顺序要求的。设备方面的例子就不多说了,毕竟不是我们关注的重点。
static void macb_tx_interrupt(struct macb_queue *queue) { unsigned int tail; unsigned int head; u32 status; struct macb *bp = queue->bp; u16 queue_index = queue - bp->queues; status = macb_readl(bp, TSR); macb_writel(bp, TSR, status); if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) queue_writel(queue, ISR, MACB_BIT(TCOMP)); netdev_vdbg(bp->dev, "macb_tx_interrupt status = 0x%03lx\n", (unsigned long)status); head = queue->tx_head; for (tail = queue->tx_tail; tail != head; tail++) { struct macb_tx_skb *tx_skb; struct sk_buff *skb; struct macb_dma_desc *desc; u32 ctrl; desc = macb_tx_desc(queue, tail); /* Make hw descriptor updates visible to CPU */ rmb(); ctrl = desc->ctrl; /* TX_USED bit is only set by hardware on the very first buffer * descriptor of the transmitted frame. */ if (!(ctrl & MACB_BIT(TX_USED))) break; /* Process all buffers of the current transmitted frame */ for (;; tail++) { tx_skb = macb_tx_skb(queue, tail); skb = tx_skb->skb; /* First, update TX stats if needed */ if (skb) { netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n", macb_tx_ring_wrap(tail), skb->data); bp->stats.tx_packets++; bp->stats.tx_bytes += skb->len; } /* Now we can safely release resources */ macb_tx_unmap(bp, tx_skb); /* skb is set only for the last buffer of the frame. * WARNING: at this point skb has been freed by * macb_tx_unmap(). */ if (skb) break; } } queue->tx_tail = tail; if (__netif_subqueue_stopped(bp->dev, queue_index) && CIRC_CNT(queue->tx_head, queue->tx_tail, TX_RING_SIZE) <= MACB_TX_WAKEUP_THRESH) netif_wake_subqueue(bp->dev, queue_index); }