网卡 dma 屏障指令

我们看一个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);
}

如何使用屏障指令

原文地址:https://www.cnblogs.com/dream397/p/15665746.html