网卡驱动:stmmac DMA发送流程
阅读原文时间:2021年04月21日阅读:20

1. 设置DAM buffer&descriptor,并启动DMA发送

在stmmac_xmit设置buffe r& descriptor,如下片段:

     if (likely(!is_jumbo)) {
        bool last_segment = (nfrags == 0);
        des = dma_map_single(priv->device, skb->data,
                     nopaged_len, DMA_TO_DEVICE);
        if (dma_mapping_error(priv->device, des))
            goto dma_map_err;
        tx_q->tx_skbuff_dma[first_entry].buf = des;    //在接收中断中,用于dma_unmap_single
        if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00))
            first->des0 = cpu_to_le32(des);
        else
            first->des2 = cpu_to_le32(des);
        tx_q->tx_skbuff_dma[first_entry].len = nopaged_len;
        tx_q->tx_skbuff_dma[first_entry].last_segment = last_segment;
        if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
                 priv->hwts_tx_en)) {
            /* declare that device is doing timestamping */
            skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
            priv->hw->desc->enable_tx_timestamp(first);
        }
        /* Prepare the first descriptor setting the OWN bit too */
        priv->hw->desc->prepare_tx_desc(first, 1, nopaged_len,
                        csum_insertion, priv->mode, 1,
                        last_segment, skb->len);
        /* The own bit must be the latest setting done when prepare the
         * descriptor and then barrier is needed to make sure that
         * all is coherent before granting the DMA engine.
         */
        dma_wmb();
    }

    if (priv->synopsys_id < DWMAC_CORE_4_00)
        priv->hw->dma->enable_dma_transmission(priv->ioaddr);
    else {
        tx_q->tx_tail_addr = tx_q->dma_tx_phy + (tx_q->cur_tx * sizeof(*desc));
        priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, tx_q->tx_tail_addr,
                           queue);
    }

1)进行DMA映射,得到映射地址des

des = dma_map_single(priv->device, skb->data, nopaged_len, DMA_TO_DEVICE);   

2)将映射后的DMA地址填充到DMA描述符的buffer1,即des0

 first->des0 = cpu_to_le32(des);

3)填充描述符其他信息,最重要的是设置OWN bit,让DMA拥有该描述符

 priv->hw->desc->prepare_tx_desc

4)设置尾指针,启动DMA发送

priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, tx_q->tx_tail_addr, queue);

5)接下来DMA会自动读取descriptor,根据descriptor读取buffer,然后将buffer数据送往queue。

在stmmac_xmit函数中还有对skb 分段的处理,过程与上述类似。

如果有skb frags,则从第二个descriptor开始,用skb_shinfo(skb)->frags[i]进行dma映射,再填充描述符的buffer。循环该过程直到所有的skb frag被处理完毕。

如果skb没有分段,则只会处理第一个descriptor,即上述步骤1-4。

注:kernel配置 CONFIG_BQL=y

2. 发送中断处理:

2.1. DMA发送完数据后产生中断,调用 stmmac_interrupt服务程序

2.2. stmmac_interrupt 通过调用 stmmac_dma_interrupt 处理DMA相关中断(包括发送和接收)。在stmmac_dma_interrupt中,通过NAPI机制触发软中断,调用stmmac_poll处理相关事件。

2.3. stmmac_poll调用stmmac_tx_clean回收资源,以及queue操作

1)通过dma_unmap_single解除dma映射

dma_unmap_single(priv->device, tx_q->tx_skbuff_dma[entry].buf, tx_q->tx_skbuff_dma[entry].len, DMA_TO_DEVICE);

2)释放skb

dev_consume_skb_any(skb);

3)清空descriptor:des2和des3, 将其赋值0

release_tx_desc()

4)设置queue的状态 ,如果queue是停止状态(__QUEUE_STATE_STACK_XOFF 或 __QUEUE_STATE_DRV_XOFF被置位),但是现在已经满足开启条件,则唤醒queue。   

在stmmac_xmit发送时如果停止了queue,现在如果条件满足,会重新恢复queue。

    netdev_tx_completed_queue(netdev_get_tx_queue(priv->dev, queue),
                  pkts_compl, bytes_compl);

    if (unlikely(netif_tx_queue_stopped(netdev_get_tx_queue(priv->dev,
                                queue))) &&
        stmmac_tx_avail(priv, queue) > STMMAC_TX_THRESH) {
        netif_dbg(priv, tx_done, priv->dev,
              "%s: restart transmit\n", __func__);
        netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, queue));
    }

在netdev_tx_completed_queue中,test_and_clear_bit(__QUEUE_STATE_STACK_XOFF, &dev_queue->state),如果test_and_clear_bit返回真,调用 netif_schedule_queue(q)重新开始调度queue

在netif_tx_wake_queue中,test_and_clear_bit(__QUEUE_STATE_DRV_XOFF, &dev_queue->state),如果test_and_clear_bit返回真,调用 __netif_schedule(q)重新开始调度queue

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章