sfq codel fq codel,这三种有什么三种不一样的人生讲章

新手园地& & & 硬件问题Linux系统管理Linux网络问题Linux环境编程Linux桌面系统国产LinuxBSD& & & BSD文档中心AIX& & & 新手入门& & & AIX文档中心& & & 资源下载& & & Power高级应用& & & IBM存储AS400Solaris& & & Solaris文档中心HP-UX& & & HP文档中心SCO UNIX& & & SCO文档中心互操作专区IRIXTru64 UNIXMac OS X门户网站运维集群和高可用服务器应用监控和防护虚拟化技术架构设计行业应用和管理服务器及硬件技术& & & 服务器资源下载云计算& & & 云计算文档中心& & & 云计算业界& & & 云计算资源下载存储备份& & & 存储文档中心& & & 存储业界& & & 存储资源下载& & & Symantec技术交流区安全技术网络技术& & & 网络技术文档中心C/C++& & & GUI编程& & & Functional编程内核源码& & & 内核问题移动开发& & & 移动开发技术资料ShellPerlJava& & & Java文档中心PHP& & & php文档中心Python& & & Python文档中心RubyCPU与编译器嵌入式开发驱动开发Web开发VoIP开发技术MySQL& & & MySQL文档中心SybaseOraclePostgreSQLDB2Informix数据仓库与数据挖掘NoSQL技术IT业界新闻与评论IT职业生涯& & & 猎头招聘IT图书与评论& & & CU技术图书大系& & & Linux书友会二手交易下载共享Linux文档专区IT培训与认证& & & 培训交流& & & 认证培训清茶斋投资理财运动地带快乐数码摄影& & & 摄影器材& & & 摄影比赛专区IT爱车族旅游天下站务交流版主会议室博客SNS站务交流区CU活动专区& & & Power活动专区& & & 拍卖交流区频道交流区
稍有积蓄, 积分 256, 距离下一级还需 244 积分
论坛徽章:2
本帖最后由 polejo 于
09:17 编辑
net sched一般都是用HTB+SFQ,看到OpenWRT里面现在默认的流控算法已经是 HFSC + FQ_CoDel,加了延迟性方面的优化。
老外还有篇文章对&&&&,看来对codel和fq_codel十分推崇。
在板子上试了下,FQ_CoDel。
#& && && && && && && && &&&1
#& && && && && && && && && &|
#& &---------------------------------------
#& &|& && && && && && && &&&|& && && && && && && &|
#& &1:1& && && && && && &1:2& && && && && &&&1:3
#& &------------& && & 12& && && && && && &13
#& &|& && && && &&&|& && &
#& &1:11& && & 1:12& &
#& &111& && &&&112
让包走handle111,发现叶子用FQ_CoDel时,虽然可以预期限速,但内存基本被它占完了,这对嵌入式肯定不可忍啊。叶子换成SFQ就好好的。
哪位用过这个FQ_CoDel的,是我用错了还是怎地?怎么测试这么差。
&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp
稍有积蓄, 积分 256, 距离下一级还需 244 积分
论坛徽章:2
=================================
大富大贵, 积分 18198, 距离下一级还需 1802 积分
论坛徽章:15
谢谢提供的链接,了解了bufferbloat问题,帮忙顶一下。Linux 网络堆栈的排队机制 - 博客频道 - CSDN.NET
钱国正的专栏
业精于勤荒于嬉,行成于思毁于随
分类:KnowHow
在任何网络堆栈或设备中,数据包的队列都是非常重要。这些队列使得不在同一时刻加载的模块能够相互通信,并且能提高网络性能,同时也会间接影响到网络延时的长短。本文章通过阐述IP数据包在Linux网络中的排队机制,来解释两个问题:
下面这张图(和它的变形)将会在文中不断的出现,用以说明具体的概念。
驱动队列(环形缓存区)
驱动队列位于IP数据栈和网卡之间。驱动队列使用先进先出算法,并通过环形缓存区实现—可以暂时把环形缓存区当做一个固定大小的缓存器。这个队列中不含任何来自包(分组)的数据,直接参与排队的是描述符(descriptor)。这些描述符指向 “内核套接字缓存”(socket kernel buffers,简写为SKBs),SKB中含有在整个内核处理过程中都要使用的数据包。
进入驱动队列的数据,来自IP数据栈,在IP数据栈里所有的IP数据包都要进行排队。这些数据包可以从本地获得,当某个网卡在网络中充当路由器时,数据包也可以从网卡上接收,找到路由后再发出去。从IP数据栈中进入驱动队列的数据包,先由硬件使之出列,再通过数据总线发送到网卡上,以进行传输。
驱动队列的用处在于,只要系统有数据需要传输时,数据能够马上被传送到网卡进行及时传输。大致意思就是,驱动队列给了IP数据栈一个排队的地方,通过硬件来对数据进行不同时的排队。实现这个功能的另一种做法是,只要当物理传输媒介准备好传输数据时,网卡便马上向IP数据栈申请数据。但是因为对IP数据栈的数据申请,不可能马上得到相应,所以这种办法会浪费掉大量宝贵的传输资源,使吞吐量相应地降低。还有另一种正好相反的办法—在IP数据栈准备好要传输的数据包后,进行等待,直到物理传输媒介做好传输数据的准备为止。但是这种做法同样也不理想,因为在等待时IP数据栈被闲置,没有办法做别的工作。
栈中的超大数据包
多数网卡都有最大传输单位(MTU),用来表示能够被物理媒介传输的最大帧数。以太网的默认MTU为1500字节,也有一些支持Jumbo Frames的以太网MTU能够达到9000多字节的。在IP数据栈中,MTU同时也是数据包传输的极限大小。比如,有个应用需要向TCP接口传送2000字节的数据,这时,IP数据栈就必须创建两个数据包来传送它,因为单个MTU小于2000字节。所以在进行较大数据的传输时,MTU如果相对较小,那么大量数据包就会被创建出来,并且它们都要在物理媒介上传输到驱动队列中。
为了避免因为MTU大小限制而出现的大量数据包,Linux内核对传输大小进行了多项优化:TCP段装卸(TCP segmentation offload,简称TSO),UDP碎片装卸(UDP&fragmentation offload,简称UFO)和类型化段装卸(generic segmentation offload ,简称GSO)。这些优化办法,使得IP数据栈能够创建比MTU更大的数据包。对于IPv4来说,优化后能够创建出最大含65536的数据包,并且这些数据包和MTU大小的数据包一样能够进入驱动队列排队。在使用TSO和UFO优化时,由网卡将较大的数据包拆分成能够传输的小数据包。对于没有该硬件拆分功能的网卡,GSO优化能够通过软件来实现相同的功能,在数据包进入驱动队列前迅速完成数据包拆分。
我在前面提过,驱动队列中能包含描述符的数量是一定的(但描述符可以指向不同大小的数据包)。所以,TSO,UFO和GSO等优化措施将数据包增大,也不完全是件好事,因为这些优化也会使驱动队列中进行排队的字节数增大了许多。图像3是一个与图像2的对比图。
虽然接下来我要将重点放在传输路径(transmit path)上了,但是这里还是要再强调一下,Linux在数据接收端同样有类似TSO、UFO和GSO的优化措施。这些接收端优化措施同样也能将每个数据包的大小限制增大。具体来说,类型接收装卸(generic receive offload,简称GRO)使网卡能够将接收到的若干数据包合并成一个大数据包后,再传给IP数据栈。在传送数据包时,GRO能将原始数据包重组,使之符合IP数据包首尾连接的属性。GRO同样也会带来副作用:较大的数据包在传送时,可能会被拆分成了若干较小的数据包,这时,就会有多个数据包在同一数据流中同时进行排队。较大的数据包如果发生了这样的“微拆分”(micro
burst),会对数据流之间的延时产生不利影响。
饿死和延时
虽然设置驱动队列—即在IP数据栈和硬件网卡间排队,非常便利,但这样做也带来“饿死和延时”的问题。
当网卡开始从驱动队列中取数据包时,如果恰好这时驱动队列为空队列,那么硬件其实就失去了一次传输数据的机会,也就将系统的吞吐量降低了。我们把这种情况叫做“饿死”(starvation)。需要注意的是,如果驱动队列为空,而此时系统又没有数据需要传输时,则不能称为“饿死”—-这是系统的正常情况。如何避免“饿死”是一个很复杂的问题,因为IP数据栈将数据包传入驱动队列的过程,和硬件网卡从驱动队列中取数据包的过程常常不是同时发生的。更加糟糕的是,这两个过程间的间隔时间很不确定,常常随着系统负载和网络接口物理介质等外部环境而变化。比如说,在一个非常繁忙的系统中,IP数据栈就很少有机会能把数据包加入到驱动队列缓存中,此时,很可能在驱动队列对更多数据包排队前,网卡就已经从驱动队列中取数据了。因此,如果驱动队列能变得更大的话,出现“饿死”的几率就会得到减小,并且系统吞吐量会相应提高。
虽然较大的队列能够保证高吞吐量,但是队列变大的同时,大量的延时情况也会出现。
在图像4中,单个带宽较大的TCP段几乎把驱动队列占满,我们把它称为“块(阻碍)交通流”(bulk traffic flow)(蓝色部分)。在最后进行排队的,是来自VoIP或游戏的“交互数据流”(黄色部分)。像VoIP或游戏一类的交互式应用,一般会在固定的时间间隔到达时,发送较小的数据包。这对延时是非常敏感的。并且这时,传输带宽较大的数据,会使包传送率(packet rate)增高而且会产生更大的数据包。较高的包传送率会很快占满队列缓存,进而阻碍交互性数据包的传输。为了进一步说明这种情况,我们先做出如下假设:
127个数据传输完毕时,交互数据包才能进行传输。在以上假设下,将所有127个块数据包传输完毕共需要(127 * 12,000) / 5,000,000 = 0.304 秒 (以每ping计算延时则为304 毫秒 )。这样的延时完全无法满足交互式应用的需求,并且这个时间中还没有包含完成所有传输所需的时间—因为我们只计算了完成127个块数据包传输的时间而已。之前我曾提到过,在驱动队列中,数据包(分组)的大小在TSO等优化下能够超过1500bytes。所以这也让延时问题变得更严重了。
由超过规定大小的缓存而引起的较大延时,也被称为Bufferbloat。在Controlling Queue&Delay 和the Bufferbloat中对这个问题有更详细的阐述。
综上所述,为驱动队列选择正确的大小是一个Goldilocks问题—不能定的太大因为会有延时,也不能定的太小因为吞吐量会降低。
字节队列限制(BQL)
字节队列限制(BQL)是最近在linux内核(& 3.3.0)中出现的新特性,它能为驱动队列自动分配合适的大小以解决前面提到过的问题。BQL机制在将数据包进行排队时,会自动计算当前系统下,能够避免饿死所需的最小驱动队列缓存大小,再决定是否对数据包进行排队。如前文所述,进行排队的数据越少,对数据包的最大延时也越小。
需要注意的是,驱动队列的实际大小并没有被BQL改变。BQL只是限制了当前时刻能够进行排队的数据多少(以字节计算)而已。任何超过大小限制的那一部分数据,都会被BQL阻挡在驱动队列之外。BQL机制会在以下两个事件发生时启动:
下面是简化后的BQL算法。LIMIT指的是BQL计算出来的限制值。
After adding packets to the queue
the number of queued bytes is over the current LIMIT value then
&&&&&&&&disable
the queueing of more data to the driver queue
注意,进行排队的数据大小可以超过LIMIT,因为数据在进行LIMIT检查以前,就已经排队了。因为非常大的字节,能够通过TSO、UFO和GSO优化,一次性进行排队,所以就造成了进行排队数据过大的问题。如果你更加重视延时,也许你会将这些优化特性去除掉。文章后面有介绍去除的办法。
BQL机制的第二阶段,在硬件完成传输了以后启动。
When the hardware has completed sending a batch of packets
(Referred to as the end of an interval)
the hardware was starved in the interval
&&&&&&&&increase
if the hardware was busy during the entire interval (not starved) and there are bytes to transmit
&&&&&&&&decrease
LIMIT by the number of bytes not transmitted in the interval
the number of queued bytes is less than LIMIT
&&&&&&&&enable
the queueing of more data to the buffer
如代码所示,BQL主要是在测试系统此时是否出现了饿死。如果出现了饿死,则LIMIT会增加,以使更多数据能够进去队列进行排队。如果系统在测试时间内一直都十分繁忙,而且仍有字节在等着传入队列中,则此时队列太大了,当前系统不需要这么大的队列,所以LIMIT会减小,以控制延时。
下面举一个实例,来帮助大家理解BQL是如何控制用于排队的数据大小的。在我的其中一个服务器上,默认的驱动队列大小是256个描述符。因为以太网MTU大小为1500bytes,所以此时驱动队列能对256 * 1,500 = 384,000 bytes进行排队(如果使用TSO、GSO则排队字节会更多)。但是,BQL此时计算出的LIMIT值为3012bytes。所以,BQL大大限制了能够进入队列的数据大小。
有关BQL非常有趣的一点,能够从B—byte 这个字母看出来。跟驱动队列的大小和其他数据包队列的大小单位不同,BQL以byte(字节)为单位进行操作。这是因为字节的数目,与其在物理介质上传输所需时间,有非常直接的关系。而数据包和描述符的数目与该时间则关系不大,因为数据包和描述符的大小都是不一样的。
BQL通过将排队数据的大小进行限制,使之保持在能避免饿死出现的最小值,来减少网络延时。BQL还有一个重要的特性,它能使原本在驱动队列中进行排队(使用FIFO算法排队)的数据包,转移到“排队准则”(queueing discipline (QDisc))上来进行排队。QDisc能够实现比FIFO复杂得多的排队算法策略。下一小节将重点介绍Linux的QDisc机制。
排队准则(QDisc)
我们已经了解到,驱动队列只是简单的先入先出队列,它不能将来自不同数据流的包区分开来。这样的设计能使网卡驱动软件变得小巧并且有更高的效率。需要注意的是,一些更加先进的以太网和无线网卡可以支持多种相互独立的传送队列,但是它们实际上都是非常类似的先进先出队列而已。系统中更高层负责在其中选择一种队列进行使用。
夹在IP数据栈和驱动队列中间的,是排队准则(QDisc)层(见Figure1),它负责实现Linux内核中的(数据传输)交通管理功能,比如交通分类,优先级别和速率协商。QDisc层的配置比起其他层要更加困难一些。要了解QDisc层,必须了解三个非常重要的概念:排队规则,类和过滤器。
排队准则(QDisc)是Linux对交通队列的抽象,但是比“先入先出”要复杂一些。通过网络接口,QDisc能独立地进行复杂的队列管理,而不需要改变IP数据栈或者网卡驱动的运行模式。每个网络接口都会默认被设为pfifo_fast QDisc ,pfifo_fast QDisc在TOS的基础上,实现了简单的三带优先机制(three band prioritization scheme)。虽然是默认机制,但是这并不代表它是最好的机制,因为pfifo_fast QDisc的队列深度很大(在下面txqueuelen中会有解释),并且它也不能区分不同数据流。
第二个和QDisc紧密相关的概念是类。单个QDisc能通过实现不同类,对数据中的不同部分进行不同处理。例如,层级表示桶(the Hierarchical Token Bucket (HTB))QDisc,能使用户配置500Kbps 和 300Kbps的类,并且控制数据使之进入用户希望其进入的类中。并不是所有QDisc都实现这种功能,我们一般把能够这样分类的QDisc称为——可分类的QDiscs。
过滤器(也叫分类器)是一种分类的机制,用来将数据分类到特定QDisc或类中。过滤器的种类很多,它们的复杂度也都不相同。u32 是其中最普通也最容易使用的一种。现在还没有针对u32的文档,但是你可以在这里找到一个例子one of my QoS scripts。
在LARTC HOWTO and the tc man pages,你可以了解到更多关于QDiscs、类和过滤器的细节。
传输层和排队准则间的缓存
你可能已经注意到了,在原来的图例中,排队准则层之上就没有数据包的队列了。这意味着网络数据栈要么直接把数据包放入排队准则中,要么把数据包推回它的上一层(比如套接字缓存的情况),如果这时队列已满的话。接下来一个很明显的问题是,当栈有很多数据包要发送的时候该怎么办?当TCP的拥塞窗口很大,或者某个应用正在快速发送UDP数据包时,就会出现要发送很多数据包的情况。对于只有一个队列的QDisc来说,类似问题在图例4中就已经出现过了。这里的问题是,一个带宽很大或者包速率很大的数据流会把队列里的所有空间占满,造成数据包丢失和延时的问题。更糟的是,这样很可能会使另一个缓存产生,进而产生一个静止队列(standing
queue),造成更严重的延时并使TCP的RTT和拥塞窗口的计算出现问题。由于Linux默认使用仅有一个队列的pfifo_fast QDisc(因为大多数数据流的TOS=0),所以这样的问题非常常见。
Linux3.6.0()出现后,Linux内核增加了TCP小队列(TCP small queue)的机制,用于解决该问题。TCP小队列对每个TCP数据流中,能够同时参与排队的字节数做出了限制。这样会产生意想不到的作用,能使内核将数据推回原先的应用,使得应用能更有效地优先处理写入套接字的请求。目前(),除TCP之外的其他传输协议的单个数据流,还是有可能阻塞QDsic层的。
另一种能部分解决传输层阻塞的办法,是使用有许多队列的QDsic层,最好是对每一个数据流都能有一个队列。SFQ和fq_codel的QDsic都能够很好地解决这个问题,使每个数据流都分到一个队列。
如何控制Linux中的队列长度
对于以太网设备,可以用ethtool命令来控制队列长度。ethtool提供底层的接口数据,并能控制IP数据栈和驱动的各种特性。
-g 能够将驱动队列的参数展示出来:
href=&/members/alpha/& rel=&nofollow&&@alpha&/a& net-next]# ethtool -g eth0
parameters for eth0:
RX:&&&&&&&
Mini:&&& 0
Jumbo:&&& 0
TX:&&&&&&&
hardware settings:
RX:&&&&&&&
Mini:&&& 0
Jumbo:&&& 0
TX:&&&&&&&
从上面可以看出,网卡驱动默认在传输队列中有256个描述符。前面我们提到过,为了免缓存丢包等情况,应该减小驱动队列的大小,这同时也能减小延时。引入BQL后,就没有必要再去调整驱动队列的大小了(下文中有配置BQL的介绍)。
ethtool也能用于调整优化特性,如TSO、UFO和GSO。-k 指令能展示出现在卸货(offload)状态,-K能调整这些状态。
href=&/members/alpha/& rel=&nofollow&&@alpha&/a& ~]$ ethtool -k eth0
parameters for eth0:
rx-checksumming:
tx-checksumming:
scatter-gather:
tcp-segmentation-offload:
udp-fragmentation-offload:
generic-segmentation-offload:
generic-receive-offload:
large-receive-offload:
rx-vlan-offload:
tx-vlan-offload:
ntuple-filters:
receive-hashing:
因为TSO, GSO, UFO 和 GRO大大增加了能够参与排队的字节数,如果相比于吞吐量你更在意延时的话,你就应该禁用这些优化。一般来说禁用这些优化,不会使你感到CPU和吞吐量收到了影响,除非你的系统要处理的数据率很大。
字节队列限制(BQL)
BQL算法能够自我适应,所以应该没什么必要去经常调整它。但是,如果你很关注最大在低码率上的最大延时的话,可能你会想要比现有LIMIT更大的上限值。在/sys 的目录中可以找到配置BQL的文件,目录的具体地址取决于网卡的位置和名字。在我的服务器上,eth0的目录是:
/sys/devices/pci0:00:14.0/net/eth0/queues/tx-0/byte_queue_limits
里面的文件有:
为能够参与排队的字节数设置一个上限,改一下limit_max 文件就行了:
&3000& & limit_max
什么是txqueuelen?
在前面我们已经提到了减小网卡传输队列大小的作用。当前的队列大小能够通过ip和ifconfig命令获得。但是令人困惑地是,两个命令得到的队列长度不同:
href=&/members/alpha/& rel=&nofollow&&@alpha&/a& ~]$ ifconfig eth0
Link encap:Ethernet& HWaddr 00:18:F3:51:44:10&
&&&&&&&&&&inet
addr:69.41.199.58& Bcast:69.41.199.63& Mask:255.255.255.248
&&&&&&&&&&inet6
addr: fe80::218:f3ff:fe51:4410/64 Scope:Link
&&&&&&&&&&UP
BROADCAST RUNNING MULTICAST& MTU:1500& Metric:1
&&&&&&&&&&RX
packets:435033 errors:0 dropped:0 overruns:0 frame:0
&&&&&&&&&&TX
packets:429919 errors:0 dropped:0 overruns:0 carrier:0
&&&&&&&&&&collisions:0
txqueuelen:1000
&&&&&&&&&&RX
bytes:.6 MiB)& TX bytes: (126.0 MiB)
&&&&&&&&&&Interrupt:23
href=&/members/alpha/& rel=&nofollow&&@alpha&/a& ~]$ ip link
lo:& mtu 16436 qdisc noqueue state UNKNOWN
&&&&link/loopback
00:00:00:00:00:00 brd 00:00:00:00:00:00
eth0:& mtu 1500 qdisc pfifo_fast state UP qlen 1000
&&&&link/ether
00:18:f3:51:44:10 brd ff:ff:ff:ff:ff:ff
默认的传输队列长度为1000包,在低带宽下这已经是很大的缓存了。
有趣的是,这个变量到底控制着什么东西呢?我以前也不知道,于是花了很多时间阅读内核代码。就我所知,txqueuelen只是丢某些排队准则的默认队列长度。这些排队准则有:
再来看图例1,txqueuelen参数控制着上面准则的队列大小。对于大多数其中的队列,tc命令行中的limit参数,都超过了txqueuelen的默认大小。总之,如果你不使用上面的队列准则,或者队列长度超过了exqueuelen大小,txqueuelen都是没有意义的。
再说一句,还有一点使我非常困惑,ifconfig命令显示的是如mac地址一类的底层网络接口细节,但是txqueuelen显示的却是更高层的QDisc层的信息。如果ifconfig显示驱动队列的大小可能会更合理些。
通过ip或者ifconfig命令能够控制传输队列的长度:
href=&/members/alpha/& rel=&nofollow&&@alpha&/a& dan]# ip link set txqueuelen 500 dev eth0
注意,ip命令使用 ‘txqueuelen’,但在显示接口细节时使用的是‘qlen’。
前面介绍过,Linux 内核有很多QDsic,每个QDisc有自己的包队列排队方法。在这篇文章里描述清楚如何配置这些QDisc的细节是不可能的。想要了解所有细节,可以参考man page(man tc)。在‘man tc qdisc-name’ (ex: ‘man tc htb’ or ‘man tc fq_codel’)中,能够找到每个QDisc的细节。LARTC也能找到许多有用的资源,但是缺少一些特性的资料。
下面是一些使用的tc命令的小技巧:
TCP 小队列
每个套接字TCP队列的大小限制,可以通过下面的/proc文件来查看和控制:
/proc/sys/net/ipv4/tcp_limit_output_bytes
我觉得一般情况下都不需要修改这个文件。
你控制不了的超大队列
不幸的是,并不是所有影响网络性能超大队列都能够被我们控制。很常见的是,问题往往出在服务提供商提供的装置和其他配套的设备(比如DSL或者电缆调制器)上。当服务提供商的装置本身有问题时,就没什么办法了,因为你不可能控制向你传输的数据。但是,在逆向上,你可以控制数据流使之比链路率(link rate)稍微低些。这会让装置中的队列不会有几对数据包出现。很多家庭路由器都有链路率限制的功能,你能够通过它来控制你的数据流低于链路率。如果你将Linux Box作为路由器,控制数据流同时也会使内核的排队机制运行地更加高效。你能找到很多在线的tc脚本,比如the
one I use with&some related performance results。
在每个使用分组交换的网络中,数据包缓存的排队机制都是非常必要的。控制这些缓存的包大小是至关重要的,能直接影响到网络延时等问题。虽然静态的缓存大小分配对于降低延时也很有效,但是更好的方法还是用更加智能的方法动态控制包的大小。实现动态控制的最好方法是使用动态机制,比如BQL和active queue management(AQM)技术,比如Codel。本文描述了数据包时如何在Linux的网络栈中进行排队的,并且介绍了相关特性的配置方法,给出了降低延时的建议。
&&– 对网络排队机制作和Codel算法出了很好的介绍,
&– Controlling Queue
Delay 一文的视频版本
&– 早先介绍数据流阻塞的文章
&&– 可能是目前为止最好的tc命令使用文档,只是有些过时,没有包含fq_codel等特性。
感谢Kevin Mason, Simon Barber, Lucas Fontes and Rami Rosen对文章所作出的帮助。
qianguozheng
排名:第1405名
Pernal Info
(5)(13)(1)(13)(49)(47)(4)(25)(19)(61)(7)(59)(7)(13)(1)(4)
Where are you[PATCH v13 net-next] codel: Controlled Delay AQM
[][][][][][]
Subject: [PATCH v13 net-next] codel: Controlled Delay AQM
From: Eric Dumazet &&
Date: Thu, 10 May :25 +0200
Cc: Kathleen Nichols &&, Van Jacobson &&,
, bloat &&,
netdev &&, Tom Herbert &&,
Yuchung Cheng &&, Matt Mathis &&,
Stephen Hemminger &&
In-reply-to: &.12504.27.camel@edumazet-glaptop&
From: Eric Dumazet &edumazet@xxxxxxxxxx&
An implementation of CoDel AQM, from Kathleen Nichols and Van Jacobson.
This AQM main input is no longer queue size in bytes or packets, but the
delay packets stay in (FIFO) queue.
As we don't have infinite memory, we still can drop packets in enqueue()
in case of massive load, but mean of CoDel is to drop packets in
dequeue(), using a control law based on two simple parameters :
target : target sojourn time (default 5ms)
interval : width of moving time window (default 100ms)
Based on initial work from Dave Taht.
Refactored to help future codel inclusion as a plugin for other linux
qdisc (FQ_CODEL, ...), like RED.
include/net/codel.h contains codel algorithm as close as possible than
Kathleen reference.
net/sched/sch_codel.c contains the linux qdisc specific glue.
Separate structures permit a memory efficient implementation of fq_codel
(to be sent as a separate work) : Each flow has its own struct
codel_vars.
timestamps are taken at enqueue() time with 1024 ns precision, allowing
a range of 2199 seconds in queue, and 100Gb links support. iproute2 uses
usec as base unit.
Selected packets are dropped, unless ECN is enabled and packets can get
ECN mark instead.
Tested from 2Mb to 10Gb speeds with no particular problems, on ixgbe and
tg3 drivers (BQL enabled).
Usage: tc qdisc ... codel [ limit PACKETS ] [ target TIME ]
[ interval TIME ] [ ecn ]
qdisc codel 10: parent 1:1 limit 2000p target 3.0ms interval 60.0ms ecn
bytes 8815805 pkt (dropped 0, overlimits 0 requeues 0)
rate 202365Kbit 16708pps backlog p requeues 0
count 116 lastcount 98 ldelay 4.3ms dropping drop_next 816us
maxpacket 1514 ecn_mark 84399 drop_overlimit 0
CoDel must be seen as a base module, and should be used keeping in mind
there is still a FIFO queue. So a typical setup will probably need a
hierarchy of several qdiscs and packet classifiers to be able to meet
whatever constraints a user might have.
One possible example would be to use fq_codel, which combines Fair
Queueing and CoDel, in replacement of sfq / sfq_red.
Signed-off-by: Eric Dumazet &edumazet@xxxxxxxxxx&
Signed-off-by: Dave Taht &dave.taht@xxxxxxxxxxxxxxx&
Cc: Kathleen Nichols &nichols@xxxxxxxxxxx&
Cc: Van Jacobson &van@xxxxxxxxxxx&
Cc: Tom Herbert &therbert@xxxxxxxxxx&
Cc: Matt Mathis &mattmathis@xxxxxxxxxx&
Cc: Yuchung Cheng &ycheng@xxxxxxxxxx&
Cc: Stephen Hemminger &shemminger@xxxxxxxxxx&
include/linux/pkt_sched.h |
include/net/codel.h
334 ++++++++++++++++++++++++++++++++++++
net/sched/Kconfig
net/sched/Makefile
net/sched/sch_codel.c
275 +++++++++++++++++++++++++++++
5 files changed, 647 insertions(+)
diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index ffe975c..cde56c2 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -655,4 +655,30 @@ struct tc_qfq_stats {
+/* CODEL */
+ TCA_CODEL_UNSPEC,
+ TCA_CODEL_TARGET,
+ TCA_CODEL_LIMIT,
+ TCA_CODEL_INTERVAL,
+ TCA_CODEL_ECN,
+ __TCA_CODEL_MAX
+#define TCA_CODEL_MAX (__TCA_CODEL_MAX - 1)
+struct tc_codel_xstats {
/* largest packet we've seen so far */
/* how many drops we've done since the last time we
* entered dropping state
/* count at entry to dropping state */
/* in-queue delay seen by most recently dequeued packet */
+ __s32 drop_ /* time to drop next packet */
+ __u32 drop_ /* number of time max qdisc packet limit was hit */
+ __u32 ecn_
/* number of packets we ECN marked instead of dropped */
/* are we in dropping state ? */
diff --git a/include/net/codel.h b/include/net/codel.h
new file mode 100644
index 0000000..ecafb0b
--- /dev/null
+++ b/include/net/codel.h
@@ -0,0 +1,334 @@
+#ifndef __NET_SCHED_CODEL_H
+#define __NET_SCHED_CODEL_H
+ * Codel - The Controlled-Delay Active Queue Management algorithm
Copyright (C)
Kathleen Nichols &nichols@xxxxxxxxxxx&
Copyright (C)
Van Jacobson &van@xxxxxxxxxxx&
Copyright (C) 2012 Michael D. Taht &dave.taht@xxxxxxxxxxxxxxx&
Copyright (C) 2012 Eric Dumazet &edumazet@xxxxxxxxxx&
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
notice, this list of conditions, and the following disclaimer,
without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License (&GPL&) version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * &AS IS& AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+#include &linux/types.h&
+#include &linux/ktime.h&
+#include &linux/skbuff.h&
+#include &net/pkt_sched.h&
+#include &net/inet_ecn.h&
+/* Controlling Queue Delay (CoDel) algorithm
+ * =========================================
+ * Source : Kathleen Nichols and Van Jacobson
+ * Implemented on linux by Dave Taht and Eric Dumazet
+ * CoDel uses a 1024 nsec clock, encoded in u32
+ * This gives a range of 2199 seconds, because of signed compares
+typedef u32 codel_time_t;
+typedef s32 codel_tdiff_t;
+#define CODEL_SHIFT 10
+#define MS2TIME(a) ((a * NSEC_PER_MSEC) && CODEL_SHIFT)
+static inline codel_time_t codel_get_time(void)
+ u64 ns = ktime_to_ns(ktime_get());
+ return ns && CODEL_SHIFT;
+#define codel_time_after(a, b)
((s32)(a) - (s32)(b) & 0)
+#define codel_time_after_eq(a, b) ((s32)(a) - (s32)(b) &= 0)
+#define codel_time_before(a, b)
((s32)(a) - (s32)(b) & 0)
+#define codel_time_before_eq(a, b) ((s32)(a) - (s32)(b) &= 0)
+/* Qdiscs using codel plugin must use codel_skb_cb in their own cb[] */
+struct codel_skb_cb {
+ codel_time_t enqueue_
+static struct codel_skb_cb *get_codel_cb(const struct sk_buff *skb)
+ qdisc_cb_private_validate(skb, sizeof(struct codel_skb_cb));
+ return (struct codel_skb_cb *)qdisc_skb_cb(skb)-&
+static codel_time_t codel_get_enqueue_time(const struct sk_buff *skb)
+ return get_codel_cb(skb)-&enqueue_
+static void codel_set_enqueue_time(struct sk_buff *skb)
+ get_codel_cb(skb)-&enqueue_time = codel_get_time();
+static inline u32 codel_time_to_us(codel_time_t val)
+ u64 valns = ((u64)val && CODEL_SHIFT);
+ do_div(valns, NSEC_PER_USEC);
+ return (u32)
+ * struct codel_params - contains codel parameters
+ * @target: target queue size (in time units)
+ * @interval: width of moving time window
+ * @ecn: is Explicit Congestion Notification enabled
+struct codel_params {
+ codel_time_t
+ codel_time_t
+ * struct codel_vars - contains codel variables
+ * @count:
how many drops we've done since the last time we
entered dropping state
+ * @lastcount:
count at entry to dropping state
+ * @dropping:
set to true if in dropping state
+ * @first_above_time: when we went (or will go) continuously above target
for interval
+ * @drop_next:
time to drop next packet, or when we dropped last
+ * @ldelay:
sojourn time of last dequeued packet
+struct codel_vars {
+ codel_time_t first_above_
+ codel_time_t drop_
+ codel_time_t
+ * struct codel_stats - contains codel shared variables and stats
+ * @maxpacket: largest packet we've seen so far
+ * @drop_count: temp count of dropped packets in dequeue()
+ * ecn_mark: number of packets we ECN marked instead of dropping
+struct codel_stats {
+static void codel_params_init(struct codel_params *params)
+ params-&interval = MS2TIME(100);
+ params-&target = MS2TIME(5);
+ params-&ecn =
+static void codel_vars_init(struct codel_vars *vars)
+ vars-&drop_next = 0;
+ vars-&first_above_time = 0;
+ vars-&dropping = /* exit dropping state */
+ vars-&count = 0;
+ vars-&lastcount = 0;
+static void codel_stats_init(struct codel_stats *stats)
+ stats-&maxpacket = 256;
+/* return interval/sqrt(x) with good precision
+ * relies on int_sqrt(unsigned long x) kernel implementation
+static u32 codel_inv_sqrt(u32 _interval, u32 _x)
+ u64 interval = _
+ unsigned long x = _x;
+ /* Scale operands for max precision */
+#if BITS_PER_LONG == 64
+ x &&= 32; /* On 64bit arches, we can prescale x by 32bits */
+ interval &&= 16;
+ while (x & (1UL && (BITS_PER_LONG - 2))) {
interval &&= 1;
+ do_div(interval, int_sqrt(x));
+ return (u32)
+static codel_time_t codel_control_law(codel_time_t t,
codel_time_t interval,
u32 count)
+ return t + codel_inv_sqrt(interval, count);
+static bool codel_should_drop(struct sk_buff *skb,
unsigned int *backlog,
struct codel_vars *vars,
struct codel_params *params,
struct codel_stats *stats,
codel_time_t now)
+ bool ok_to_
+ if (!skb) {
vars-&first_above_time = 0;
+ vars-&ldelay = now - codel_get_enqueue_time(skb);
+ *backlog -= qdisc_pkt_len(skb);
+ if (unlikely(qdisc_pkt_len(skb) & stats-&maxpacket))
stats-&maxpacket = qdisc_pkt_len(skb);
+ if (codel_time_before(vars-&ldelay, params-&target) ||
*backlog &= stats-&maxpacket) {
/* went below - stay below for at least interval */
vars-&first_above_time = 0;
+ ok_to_drop =
+ if (vars-&first_above_time == 0) {
/* just went above from below. If we stay above
* for at least interval we'll say it's ok to drop
vars-&first_above_time = now + params-&
+ } else if (codel_time_after(now, vars-&first_above_time)) {
ok_to_drop =
+ return ok_to_
+typedef struct sk_buff * (*codel_skb_dequeue_t)(struct codel_vars *vars,
struct Qdisc *sch);
+static struct sk_buff *codel_dequeue(struct Qdisc *sch,
struct codel_params *params,
struct codel_vars *vars,
struct codel_stats *stats,
codel_skb_dequeue_t dequeue_func,
u32 *backlog)
+ struct sk_buff *skb = dequeue_func(vars, sch);
+ codel_time_
+ if (!skb) {
vars-&dropping =
+ now = codel_get_time();
+ drop = codel_should_drop(skb, backlog, vars, params, stats, now);
+ if (vars-&dropping) {
if (!drop) {
/* sojourn time below target - leave dropping state */
vars-&dropping =
} else if (codel_time_after_eq(now, vars-&drop_next)) {
/* It's time for the next drop. Drop the current
* packet and dequeue the next. The dequeue might
* take us out of dropping state.
* If not, schedule the next drop.
* A large backlog might result in drop rates so high
* that the next drop should happen now,
* hence the while loop.
while (vars-&dropping &&
codel_time_after_eq(now, vars-&drop_next)) {
if (++vars-&count == 0) /* avoid zero divides */
vars-&count = ~0U;
if (params-&ecn && INET_ECN_set_ce(skb)) {
stats-&ecn_mark++;
vars-&drop_next =
codel_control_law(vars-&drop_next,
params-&interval,
vars-&count);
qdisc_drop(skb, sch);
stats-&drop_count++;
skb = dequeue_func(vars, sch);
if (!codel_should_drop(skb, backlog,
vars, params, stats, now)) {
/* leave dropping state */
vars-&dropping =
/* and schedule the next drop */
vars-&drop_next =
codel_control_law(vars-&drop_next,
params-&interval,
vars-&count);
+ } else if (drop) {
if (params-&ecn && INET_ECN_set_ce(skb)) {
stats-&ecn_mark++;
qdisc_drop(skb, sch);
stats-&drop_count++;
skb = dequeue_func(vars, sch);
drop = codel_should_drop(skb, backlog, vars, params,
stats, now);
vars-&dropping =
* if min went above target close to when we last went below it
* assume that the drop rate that controlled the queue on the
* last cycle is a good starting point to control it now.
if (codel_time_before(now - vars-&drop_next,
16 * params-&interval)) {
vars-&count = (vars-&count - vars-&lastcount) | 1;
vars-&count = 1;
vars-&lastcount = vars-&
vars-&drop_next = codel_control_law(now, params-&interval,
vars-&count);
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 75b58f8..fadd252 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -250,6 +250,17 @@ config NET_SCH_QFQ
If unsure, say N.
+config NET_SCH_CODEL
+ tristate &Controlled Delay AQM (CODEL)&
Say Y here if you want to use the Controlled Delay (CODEL)
packet scheduling algorithm.
To compile this driver as a module, choose M here: the module
will be called sch_codel.
If unsure, say N.
config NET_SCH_INGRESS
tristate &Ingress Qdisc&
depends on NET_CLS_ACT
diff --git a/net/sched/Makefile b/net/sched/Makefile
index 8cdf4e2..30fab03 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_NET_SCH_PLUG) += sch_plug.o
obj-$(CONFIG_NET_SCH_MQPRIO) += sch_mqprio.o
obj-$(CONFIG_NET_SCH_CHOKE) += sch_choke.o
obj-$(CONFIG_NET_SCH_QFQ) += sch_qfq.o
+obj-$(CONFIG_NET_SCH_CODEL) += sch_codel.o
obj-$(CONFIG_NET_CLS_U32) += cls_u32.o
obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o
diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c
new file mode 100644
index 0000000..a96e95a
--- /dev/null
+++ b/net/sched/sch_codel.c
@@ -0,0 +1,275 @@
+ * Codel - The Controlled-Delay Active Queue Management algorithm
Copyright (C)
Kathleen Nichols &nichols@xxxxxxxxxxx&
Copyright (C)
Van Jacobson &van@xxxxxxxxxxx&
Implemented on linux by :
Copyright (C) 2012 Michael D. Taht &dave.taht@xxxxxxxxxxxxxxx&
Copyright (C) 2012 Eric Dumazet &edumazet@xxxxxxxxxx&
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
notice, this list of conditions, and the following disclaimer,
without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License (&GPL&) version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * &AS IS& AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+#include &linux/module.h&
+#include &linux/slab.h&
+#include &linux/types.h&
+#include &linux/kernel.h&
+#include &linux/errno.h&
+#include &linux/skbuff.h&
+#include &net/pkt_sched.h&
+#include &net/codel.h&
+#define DEFAULT_CODEL_LIMIT 1000
+struct codel_sched_data {
+ struct codel_params
+ struct codel_vars
+ struct codel_stats
+/* This is the specific function called from codel_dequeue()
+ * to dequeue a packet from queue. Note: backlog is handled in
+ * codel, we dont need to reduce it here.
+static struct sk_buff *dequeue(struct codel_vars *vars, struct Qdisc *sch)
+ struct sk_buff *skb = __skb_dequeue(&sch-&q);
+ prefetch(&skb-&end); /* we'll need skb_shinfo() */
+static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch)
+ struct codel_sched_data *q = qdisc_priv(sch);
+ struct sk_buff *
+ skb = codel_dequeue(sch, &q-&params, &q-&vars, &q-&stats,
dequeue, &sch-&qstats.backlog);
+ /* We cant call qdisc_tree_decrease_qlen() if our qlen is 0,
* or HTB crashes. Defer it for next round.
+ if (q-&stats.drop_count && sch-&q.qlen) {
qdisc_tree_decrease_qlen(sch, q-&stats.drop_count);
q-&stats.drop_count = 0;
+ if (skb)
qdisc_bstats_update(sch, skb);
+static int codel_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch)
+ struct codel_sched_data *q;
+ if (likely(qdisc_qlen(sch) & sch-&limit)) {
codel_set_enqueue_time(skb);
return qdisc_enqueue_tail(skb, sch);
+ q = qdisc_priv(sch);
+ q-&drop_overlimit++;
+ return qdisc_drop(skb, sch);
+static const struct nla_policy codel_policy[TCA_CODEL_MAX + 1] = {
+ [TCA_CODEL_TARGET] = { .type = NLA_U32 },
+ [TCA_CODEL_LIMIT] = { .type = NLA_U32 },
+ [TCA_CODEL_INTERVAL] = { .type = NLA_U32 },
+ [TCA_CODEL_ECN]
= { .type = NLA_U32 },
+static int codel_change(struct Qdisc *sch, struct nlattr *opt)
+ struct codel_sched_data *q = qdisc_priv(sch);
+ struct nlattr *tb[TCA_CODEL_MAX + 1];
+ if (!opt)
return -EINVAL;
+ err = nla_parse_nested(tb, TCA_CODEL_MAX, opt, codel_policy);
+ if (err & 0)
+ sch_tree_lock(sch);
+ if (tb[TCA_CODEL_TARGET]) {
u32 target = nla_get_u32(tb[TCA_CODEL_TARGET]);
q-&params.target = ((u64)target * NSEC_PER_USEC) && CODEL_SHIFT;
+ if (tb[TCA_CODEL_INTERVAL]) {
u32 interval = nla_get_u32(tb[TCA_CODEL_INTERVAL]);
q-&params.interval = ((u64)interval * NSEC_PER_USEC) && CODEL_SHIFT;
+ if (tb[TCA_CODEL_LIMIT])
sch-&limit = nla_get_u32(tb[TCA_CODEL_LIMIT]);
+ if (tb[TCA_CODEL_ECN])
q-&params.ecn = !!nla_get_u32(tb[TCA_CODEL_ECN]);
+ qlen = sch-&q.
+ while (sch-&q.qlen & sch-&limit) {
struct sk_buff *skb = __skb_dequeue(&sch-&q);
sch-&qstats.backlog -= qdisc_pkt_len(skb);
qdisc_drop(skb, sch);
+ qdisc_tree_decrease_qlen(sch, qlen - sch-&q.qlen);
+ sch_tree_unlock(sch);
+ return 0;
+static int codel_init(struct Qdisc *sch, struct nlattr *opt)
+ struct codel_sched_data *q = qdisc_priv(sch);
+ sch-&limit = DEFAULT_CODEL_LIMIT;
+ codel_params_init(&q-&params);
+ codel_vars_init(&q-&vars);
+ codel_stats_init(&q-&stats);
+ if (opt) {
int err = codel_change(sch, opt);
+ if (sch-&limit &= 1)
sch-&flags |= TCQ_F_CAN_BYPASS;
sch-&flags &= ~TCQ_F_CAN_BYPASS;
+ return 0;
+static int codel_dump(struct Qdisc *sch, struct sk_buff *skb)
+ struct codel_sched_data *q = qdisc_priv(sch);
+ struct nlattr *
+ opts = nla_nest_start(skb, TCA_OPTIONS);
+ if (opts == NULL)
goto nla_put_
+ if (nla_put_u32(skb, TCA_CODEL_TARGET,
codel_time_to_us(q-&params.target)) ||
nla_put_u32(skb, TCA_CODEL_LIMIT,
sch-&limit) ||
nla_put_u32(skb, TCA_CODEL_INTERVAL,
codel_time_to_us(q-&params.interval)) ||
nla_put_u32(skb, TCA_CODEL_ECN,
q-&params.ecn))
goto nla_put_
+ return nla_nest_end(skb, opts);
+nla_put_failure:
+ nla_nest_cancel(skb, opts);
+ return -1;
+static int codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
+ const struct codel_sched_data *q = qdisc_priv(sch);
+ struct tc_codel_xstats st = {
.maxpacket = q-&stats.maxpacket,
= q-&vars.count,
.lastcount = q-&vars.lastcount,
.drop_overlimit = q-&drop_overlimit,
= codel_time_to_us(q-&vars.ldelay),
.dropping = q-&vars.dropping,
.ecn_mark = q-&stats.ecn_mark,
+ if (q-&vars.dropping) {
codel_tdiff_t delta = q-&vars.drop_next - codel_get_time();
if (delta &= 0)
st.drop_next = codel_time_to_us(delta);
st.drop_next = -codel_time_to_us(-delta);
+ return gnet_stats_copy_app(d, &st, sizeof(st));
+static void codel_reset(struct Qdisc *sch)
+ struct codel_sched_data *q = qdisc_priv(sch);
+ qdisc_reset_queue(sch);
+ codel_vars_init(&q-&vars);
+static struct Qdisc_ops codel_qdisc_ops __read_mostly = {
= &codel&,
+ .priv_size = sizeof(struct codel_sched_data),
+ .enqueue = codel_qdisc_enqueue,
+ .dequeue = codel_qdisc_dequeue,
= qdisc_peek_dequeued,
= codel_init,
= codel_reset,
= codel_change,
= codel_dump,
+ .dump_stats = codel_dump_stats,
= THIS_MODULE,
+static int __init codel_module_init(void)
+ return register_qdisc(&codel_qdisc_ops);
+static void __exit codel_module_exit(void)
+ unregister_qdisc(&codel_qdisc_ops);
+module_init(codel_module_init)
+module_exit(codel_module_exit)
+MODULE_DESCRIPTION(&Controlled Delay queue discipline&);
+MODULE_AUTHOR(&Dave Taht&);
+MODULE_AUTHOR(&Eric Dumazet&);
+MODULE_LICENSE(&Dual BSD/GPL&);
To unsubscribe from this list: send the line &unsubscribe netdev& in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at
Follow-Ups:
From: David Miller
Prev by Date:
Next by Date:
Previous by thread:
Next by thread:
Index(es):

我要回帖

更多关于 fq codel 的文章

 

随机推荐