下半部分的数据包流

Packet flow in bottom half

我正在阅读有关从 NIC 中断处理程序到用户的接收路径中的数据包流 space。

我想知道新分配的 skbuff 在 bottom half 上下文中保留到什么时候。

从 LDD 获取 snull_rx() 代码:

void snull_rx(struct net_device *dev, struct snull_packet *pkt)
{
    struct sk_buff *skb;
    struct snull_priv *priv = netdev_priv(dev);

    /*
     * The packet has been retrieved from the transmission
     * medium. Build an skb around it, so upper layers can handle it
     */
    skb = dev_alloc_skb(pkt->datalen + 2);
    if (!skb) {
        if (printk_ratelimit(  ))
            printk(KERN_NOTICE "snull rx: low on mem - packet dropped\n");
        priv->stats.rx_dropped++;
        goto out;
    }
    memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);

    /* Write metadata, and then pass to the receive level */
    skb->dev = dev;
    skb->protocol = eth_type_trans(skb, dev);
    skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
    priv->stats.rx_packets++;
    priv->stats.rx_bytes += pkt->datalen;
    netif_rx(skb);
  out:
    return;
}

那么在 netif_rx(skb) 之后 skb 会停留在下半部分吗?

谢谢。

上面的代码在硬件中断上下文中运行 netif_rx 将数据包排队并向内核发出信号以在 SOFTIRQ 上下文 (NET_RX)

上继续

编辑:我写了一篇博客 post 概述了整个 linux 网络堆栈(接收路径),提供了大量详细信息, take a look.

答案很复杂,但是是的,netfilter 代码在 softirq 上下文中运行。

流程的工作方式是这样的:

  1. 一个数据包到达,启用NAPI
  2. 软 irq 上下文中的 NAPI 内核线程 运行(/proc/softirqs 中的NET_RX_SOFTIRQ)从内存(NIC DMA 处理数据的地方)收集数据包。
  3. softirq 最多只能消耗其 budget 个数据包或数据包处理的时间限制。你可以找到这个代码 here.
  4. 这可以防止 softirq 消耗整个 CPU。
  5. 最终,函数__netif_receive_skb_core被调用。此函数的确切路径取决于驱动程序,但对于 e1000e,路径是:
    1. NAPI 软中断调用 e1000e_poll
    2. e1000e_poll 呼叫 e1000_clean_rx_irq
    3. e1000_clean_rx_irq 呼叫 e1000_receive_skb
    4. e1000_receive_skb 呼叫 napi_gro_receive
    5. napi_gro_receive 呼叫 napi_skb_finish
    6. napi_skb_finish 呼叫 netif_receive_skb
    7. netif_receive_skb 呼叫 netif_receive_skb
    8. netif_receive_skb 呼叫 __netif_receive_skb_core
  6. 根据您是否使用接收数据包控制,此处的代码路径会略有不同。
  7. 无论哪种情况,最终数据包都会被传送到协议层 here
  8. 如果我们将 IP 视为我们选择的协议,数据包将被传递到 ip_rcv,这也将 check netfilter
  9. 数据包继续通过每个协议栈,直到通过调用 sock_queue_rcv_skb 排队到套接字的接收缓冲区。例如,UDP 从名为 __udp_queue_rcv_skb.
  10. 的函数中执行 here
  11. 函数sock_queue_rcv_skb将数据排队到套接字接收缓冲区。您可以找到此代码 here.

一些注意事项:

  • 您可以通过更改 sysctl net.core.netdev_budget 为 NAPI 调整 budget。预算越高,排队到进程接收队列的数据包就越多,但是 CPU 用于 运行 用户进程的时间会更少。
  • 如果您的 NIC 支持多个 RX 队列,您可以在多个 CPU 之间分配传入数据包处理负载。
  • 如果您的 NIC 不支持多个 RX 队列,您可以使用 Receive Packet Steering 在多个 CPU 之间分配数据包处理负载。

如果您对这个过程有任何其他问题我可以回答,请告诉我。