暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

一起撸linux内核35-记内核网络调试学到的dql

囧囧妹 2023-02-04
532

点击上方蓝字【囧囧妹】一起学习,一起成长!

最近调试板卡遇到内核网络出现与pc机偶现无法ping通的问题,在解决过程中学到了dql的一些知识,mark一下。

DQL (Dynamic Queue Limits) 是以太网的一种动态队列限制机制,它可以动态地调整队列长度,以提高网络性能。DQL使用算法估算网络流量速率并动态地调整队列长度,以确保网络更加高效,同时避免了过高的延迟和丢包情况。在以太网中,DQL是在协议栈和网卡驱动之间进行协调的,以确保最佳性能。

在 Linux 内核中,dql(Dynamic Queue Limits)是一种队列调度机制,用于控制网络设备队列的大小。dql是一种可以与生产者/消费者类型队列配合使用的机制。这样的队列具有以下一般特性:

    1. 对象排队到一个用对象数量指定的限制。
    2. 周期性地执行完成过程,以退出已消耗的对象。
    3. 当达到限制时,将出现饥饿,所有排队的数据实际上已经被消耗,但完成处理尚未运行,因此无法排队新数据。
    4. 最小化排队数据的数量是可取的。


dql的目标是计算限制为防止饥饿所需的最小对象数。

dql的主要功能是:dql_queued-当对象排队时调用以记录对象数量。dql_avail-基于对象限制和已经排队的对象数量,返回有多少对象可以排队。dql_completed-在完成时调用以指示从队列中退出了多少对象。

dql不实现任何dql数据结构的锁保护,更高层的调用应提供此锁保护。dql_queued应该被串行化,以防止并发执行函数;这对于dql_completed也是一样的。然而,dql_queued和dlq_completed可以并发执行(即可以由各种锁保护)

dql_completed 函数是 dql 机制中的一个重要部分,它用于更新队列状态,以反映实际发送数据包的数量。

具体实现如下:

1. 计算发送数据包的数量:dql_completed 函数首先需要知道已经发送了多少数据包。这可以通过计算队列的入队数和出队数的差来获得。

2. 更新队列状态:根据实际发送数据包的数量,更新队列的当前状态。这可以通过更新队列的统计数据,例如队列的最大长度和平均长度等。

3. 更新队列限制:dql_completed 函数还可以根据队列的当前状态,更新队列的大小限制。这通常是通过检查队列长度是否超过允许的最大值,然后对其进行调整。

4. 调整队列优先级:最后,dql_completed 函数可以根据队列的当前状态,调整队列的优先级。例如,如果队列的长度较小,则可以增加队列的优先级,以加快数据包的处理速度。


dql_init函数具体实现:

dql_init是一个初始化函数,它用来初始化以太网队列调度算法(DQL)的数据结构。参数&queue->dql是一个指向以太网队列调度数据结构的指针,HZ是一个定义在内核空间的常量,表示一秒中的节拍数,一般情况下它的值为100。这个函数通过设置以太网队列调度算法相关的变量,为以太网队列调度算法的使用做准备。

#define UINT_MAX (~0U)

UINT_MAX 是一个整型常量,它代表了无符号整型数的最大值。它的定义是 ~0U,这里 ~ 是按位取反运算符,0U 表示无符号整型 0。按位取反 0 后得到的数是全 1,也就是所有位都是 1。因此,UINT_MAX 就代表了无符号整型数的最大值。

#define DQL_MAX_OBJECT (UINT_MAX 16)

DQL_MAX_OBJECT定义了最大的对象数量。具体实现是将整型最大值(UINT_MAX)除以16。

#define DQL_MAX_LIMIT ((UINT_MAX 2) - DQL_MAX_OBJECT)

DQL_MAX_LIMIT定义了队列的最大限制,即对象的最大数量。它的具体实现是,先将整型最大值(UINT_MAX)除以2,再减去DQL_MAX_OBJECT定义的最大对象数量。

DQL_MAX_LIMIT 的值为:((~0U) / 2) - (UINT_MAX / 16)

因为:UINT_MAX = ~0U

所以:DQL_MAX_LIMIT = ((~0U) / 2) - (UINT_MAX / 16) = ((~0U) / 2) - ((~0U) / 16) = (2^32 - 1) / 2 - (2^32 - 1) / 16 = (2^32 - 1) / 2 - (2^32 - 1) / 16 = (2^31) - 1 - (2^28 - 1) = 2147483584


在Linux内核中,dql调整的函数是netdev_tx_completed_queue。这个函数用于调整内核队列的长度,以获得预期的带宽。它可以根据应用程序的反馈更新队列长度,以保证预期的带宽。

netdev_tx_completed_queue函数具体实现:
    static inline void netdev_tx_completed_queue(struct netdev_queue *dev_queue,
    unsigned int pkts, unsigned int bytes)
    {
    struct Qdisc *q = dev_queue->qdisc;


    if (q->tx_queue_len) {
    __netif_tx_lock_bh(dev_queue);
    __netif_tx_update_budget(dev_queue, pkts, bytes);
    __netif_tx_unlock_bh(dev_queue);
    }
    }

    这个函数的主要功能是调用 __netif_tx_update_budget 函数更新预取的带宽,并通过锁保证更新的原子性。其中 dev_queue 是一个网络设备的发送队列, pkts 表示已完成的数据包数量, bytes 表示已完成的数据包字节数。

    觉得不错,点击“分享”,“赞”,“在看”传播给更多热爱嵌入式的小伙伴吧!

    文章转载自囧囧妹,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论