Linux公平队列FQ配置
时间:2023-08-02 07:07:01
FQ (Fair Queue)是类别(classless)报文调度器主要用于本地流量。设计控制每个流的发送节奏(pacing)。FQ完成流的区分,可以完成TCP层层所需的发送节奏。所有属于套接口的报纸都被认为是流动的(flow)。可用于非本地生成的报纸(如路由器设备)sk_buff结构成员hash作为区分流的备选。
可以使用应用程序setsockopt调用系统的选项SO_MAX_PACING_RATE指定最大的pacing速率。FQ调度器可以在发送报纸之间增加延迟TCP栈所需的发送速率。
轮询列出队列Round-Robin这样做。留了一种特殊的方式。FIFO队列用于高优先级高优先级:TC_PRIO_CONTROL,用于IGMP,IPv6多播,STP等协议报文),这个队列的报文总是优先发送。
TCP pacing有空闲时间流动是有益的,因为拥堵的窗口是允许的TCP栈在队列中缓存大量报纸,进入空闲时,由pacing发送避免了TCP业余时间后重新进入慢启动的问题。BDP大流量和发送大量数据的应用(如视频流)有不利影响。
1. FQ配置
配置如下:
$ sudo tc qdisc add dev ens38 root fq $ $ sudo tc -s qdisc show dev ens38 qdisc fq 8001: root refcnt 2 limit 10000p flow_limit 100p buckets 1024 orphan_mask 1023 quantum 3028 initial_quantum 15140 low_rate_threshold 550Kbit refill_delay 40.0ms Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 0 flows (0 inactive, 0 throttled) 0 gc, 0 highprio, 0 throttled
FQ参数:
limit : 真实队列长度的硬限值。当达到这个限值时,新的报纸将被丢弃。如果降低此值,当满足新值时,报纸将被丢弃。默认情况下,此值为:1万。
flow_limit : 每流最大报文数量的硬性限值。默认为:100。
quantum : 每次轮询队列的信用值(credit),例如,每次都可以排队的字节数量。这个值的设置意味着下一个流等待服务的时间更长,默认为两倍MTU值。
refill_delay : 默认情况下,非活动流重新分配信用值的时间间隔为40ms。
low_rate_threshold : 当pacing当速率低于此值时,在发送报纸之间增加延迟。
initial_quantum : 信用值的初始发送(credit),例如,当一个新的流刚被排队处理时,可以处理的字节数量。特别允许此选项IW10而设置(TCP初始窗口值已调整为10)。默认值为10倍的接口MTU值,对于标准的以太网为15140。
maxrate : 流量的最大发送率。默认情况下,速度没有限制。此外,应用程序还可以通过设置接口选项SO_MAX_PACING_RATE设定最大速率,但真正生效的速率是两者中的较小值。
buckets : 流查找使用的hash表的大小。为了进行高效的冲突检测,每一个bucket桶给红黑树结构(red-black tree)。默认值为:1024。
[no]pacing : 流的pacing默认开启功能控制。
orphan_mask : 对于不属于套接口的报文,或TCP未完成连接的报纸(套接口状态为TCPF_LISTEN或者TCPF_NEW_SYN_RECV),FQ将保存在报文中skb结构成员hash的值进行mask操作(使用orphan_mask),这将导致这些报纸不能侵占一切bucket,实现预防DDOS攻击效果。默认值为1023(16)x3FF),这意味着不会为此类报纸分配超过1024数量的流结构。
ce_threshold : 对于报纸的正常发送时间戳,如果超过此阈值间隔,则设置所有尚未发送的报纸ECN标记ECE(ECN Congestion Experienced)。这对于类似DCTCP拥塞控制算法更有用,需要在队列占用率较低的情况下进行标记。
2. 内核FQ初始化
函数fq_init实现FQ从以下可以看出初始化FQ每个参数的默认值。bucket选项对应的参数fq_trees_log,保存的为bucket数量取2对数,对应1024,去2对数后为10。
static int fq_init(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) { struct fq_sched_data *q = qdisc_priv(sch); sch->limit = 10000; q->flow_plimit = 100; q->quantum = 2 * psched_mtu(qdisc_dev(sch)); q->initial_quantum = 10 * psched_mtu(qdisc_dev(sch)); q->flow_refill_delay = msecs_to_jiffies(40); q->flow_max_rate = ~0UL; q->time_next_delayed_flow = ~0ULL; q->rate_enable = 1; q->new_flows.first = NULL; q->old_flows.first = NULL; q->delayed = RB_ROOT; q->fq_root = NULL; q->fq_trees_log = ilog2(1024); q->orphan_mask = 1024 - 1; q->low_rate_threshold = 550000 / 8; /* Default ce_threshold of 4294 seconds */ q->ce_threshold = (u64)NSEC_PER_USEC * ~0U; qdisc_watchdog_init_clockid(&q->watchdog, sch, CLOCK_MONOTONIC); if (opt) err = fq_change(sch, opt, extack); else err = fq_resize(sch, q->fq_trees_log); return err;
内核初始化low_rate_threshold参数单位为bit,换算为byte的话为:
550000 / 8 = (550000 / 8) * 8 = 550000bytes = 550kbytes。
参数ce_threshold默认值等于: ~0U,内核变量q->ce_threshold中保存的为纳秒值:(NSEC_PER_USEC * ~0U),转换为秒:4294s。
1000L * 0xFFFF FFFF = 4294967295000 ns ≈ 4294s
tc命令在显示ce_threshold转换后的微秒值(即:0xFFFF FFFF),,对于默认值0xFFFF FFFF,tc不显示。应该没有超过正常发送时间这么长时间未发送的报纸。
static int fq_dump(struct Qdisc *sch, struct sk_buff *skb) { struct fq_sched_data *q = qdisc_priv(sch); u64 ce_threshold = q->ce_threshold; opts = nla_nest_start(skb, TCA_OPTIONS); if (opts == NULL) goto nla_put_failure; /* TCA_FQ_FLOW_DEFAULT_RATE is not used anymore */ do_div(ce_threshold, NSEC_PER_USEC);
内核版本 5.0