本文主要是对Linux的QoS限速和OVS QoS限速使用的一些理解(不包含TC各个算法具体实现),如有错误,望能批评指正。

常用流控算法的理解


classless qdisc

  • [p or b]fifo,最简单的qdisc,纯粹的先进先出,只有一个参数limit设置队列长度,pfifo以报文个数为单位,bfifo以字节为单位。
  • pfifo_fast,系统标准的qdisc,他的队列分为三个band,band0最高,优先处理,band0有数据就不会处理band1,根据报文的TOS分配到三个band。
  • red,当带宽接近于规定的带宽时,系统会随机丢弃一些报文,非常适合高带宽应用。
  • sfq,按照session为流量进行排序,然后循环发送每个session的报文。
  • tbf,适用于把流速降低到某个值。
  • ingress,用于入流量。

classful qdisc

  • cbq,既有限制宽带的能力,也有带宽优先级管理的功能。根据报文离队频率和链路层的带宽计算空闲时间,通过调整空闲时间限制带宽。
  • htb,保证每个类型的带宽,但是也允许特定的类可以突破带宽上限,占用别的类的带宽。
  • prio,不能限制带宽,只能对流量优先级管理。
  • hfsc,为leaf class保证精确的带宽和分配延时,以及剩下带宽的公平,和htb不同的是,他通过丢包,让低延时的会话受益。

我们今天主要针对classless的ingress和classful的htb,前者是入口的流向限制,后者是出口的流量限制。

TC命令


TC命令入口的限速

命令思路

  • 首先删除网卡配置的ingress QDisc。
  • 为网卡创建一个基于ingress的QDisc。
  • 为该QDisc建立一个基于ip或者其他的过滤器filter,限制速度为多少。

实际命令

tc qdisc del dev <devname> handle ffff: ingress 2>/dev/null
tc qdisc add dev <devname> handle ffff: ingress
tc filter add dev <devname> parent ffff: protocol ip prio <priority> u32 match ip src <ipaddr> police rate <kbits_rate>kbit burst <kbits_burst>k mtu 65535 drop

TC命令的出口限速

方式1

命令思路

该方式的思路和格式类似上面入口的配置,这里不推荐这种方式,经过验证准确性并不好。没有体现出classful的优势。

  • 首先删除网卡配置的HTB QDisc。
  • 为网卡创建一个基于HTB的QDisc。
  • 为该QDisc建立一个基于ip或者其他的过滤器filter,限制速度为多少。

实际命令

tc qdisc del dev <devname> root 2>/dev/null
tc qdisc add dev <devname> root handle 1: htb
tc filter add dev <devname> parent 1: protocol ip prio <priority> u32 match ip src <ipaddr> police rate <kbits_rate>kbit burst <kbits_burst>k mtu 65535 drop

方式2

命令思路

  • 首先删除网卡配置的HTB QDisc。
  • 为网卡创建一个基于HTB的QDisc。
  • 为该QDisc建立一个子类的队列,并未该队列配置限速。
  • 建一个过滤器filter,将符合的报文送入上面配置的队列。

实际命令

tc qdisc del dev <devname> root 2>/dev/null
tc qdisc add dev <devname> root handle 1: htb
tc class add dev <devname> parent 1: classid 1:queue_id htb rate <kbits_rate>kbit ceil <kbits_rate>kbit burst <kbits_burst>k
tc filter add dev <devname> protocol ip parent 1: prio <priority> u32 match ip src <ipaddr> flowid 1:queue_id

方式3

命令思路

  • 首先删除网卡配置的HTB QDisc。
  • 为网卡创建一个基于HTB的QDisc。
  • 为该QDisc建立一个子类的队列,并未该队列配置限速。
  • 建一个过滤器filter,将iptables指定的报文导入上面队列中。

实际命令

tc qdisc del dev <devname> root 2>/dev/null
tc qdisc add dev <devname> root handle 1: htb
tc class add dev <devname> parent 1: classid 1:queue_id htb rate <kbits_rate>kbit ceil <kbits_rate>kbit burst <kbits_burst>k
tc filter add dev <devname> protocol all parent 1: prio <priority> handle 1 fw classid 1:queue_id

iptables配合的命令如下

iptables -t mangle -A POSTROUTING -d <ipaddr> -j MARK --set-mark queue_id

方式4

命令思路

  • 首先删除网卡配置的HTB QDisc。
  • 为网卡创建一个基于HTB的QDisc。
  • 为该QDisc建立一个子类的队列,并未该队列配置限速。
  • 建一个过滤器filter,将router指定的报文导入上面队列中。

实际命令

tc qdisc del dev <devname> root 2>/dev/null
tc qdisc add dev <devname> root handle 1: htb
tc class add dev <devname> parent 1: classid 1:queue_id htb rate <kbits_rate>kbit ceil <kbits_rate>kbit burst <kbits_burst>k
tc filter add dev <devname> protocol all parent 1: prio <priority> route to queue_id classid 1:queue_id

router配合的命令如下

ip route add <ipaddr> dev eth0 via <device_ip> realm queue_id

OVS的QOS


OVS配置命令

ovs-vsctl set interface <devname> ingress_policing_rate=<kbits_rate> ingress_policing_burst=<kbits_burst>
//配置ingress,这个地方只是限制整个端口的,目前不能细分,跟实现有关。他的filter是all。

ovs-vsctl set port <devname> qos=@qosname -- --id=@qosname create qos type=linux-htb queues=queue_id=@queue_name -- --id=@queue_name create queue other-config:max-rate=<bytes_rate>
//配置出口HTB类型的一个队列,需要ovs-ofctl配置将需要的报文导入该队列
ovs-ofctl add-flow brname in_port=portno,actions=set_queue:queue_id,normal
//最简单的倒流量的方式,可以在流表添加过滤条件。

OVS运行时对TC的配置

OVS启动的时候会调用如下代码对QoS进行配置。

ovs-vswitchd.c
main-->bridge_run-->bridge_reconfigure-->iface_configure_qos

iface_configure_qos

  • netdev_set_qos为接口创建qdisc。
  • 根据队列的queue数量,分别调用netdev_set_queue为队列创建class。
  • ofproto_port_set_queuesofproto_port设置queue的数据结构,为了和上面的class对应。
  • netdev_set_policing

netdev_set_qos-->netdev_linux_set_qos

  • tc_lookup_ovs_name根据OVS的qos算法找到对应的ops,之前各种类型的ovs的qos算法都已经注册,比较好找,轮询即可。
  • 如果是tc_ops_noop,直接调用对应的tc_install,即noop_tc_install,然后返回。
  • tc_query_qdisc主要创建qdisc,类似命令行tc qdisc add dev <dev> root handle 1: kind,如果已经创建直接返回。
  • 最早查询的ops和在Linux Kernel创建的ops比较是不是同一种,是的话调用ops->qdisc_set,即htb_qdisc_set,创建class,类似命令行tc class add dev dev_name parent 1: classid 1:fffe htb rate <min_rate>bps ceil <max_rate>bps burst 0
  • 如果不是同一种则调用tc_del_qdisc删除存在的qdisc,调用ops->tc_install,即htb_tc_install,创建qdisc和class。

htb_tc_install

  • htb_setup_qdisc__相对于命令行tc qdisc add dev <dev> root handle 1: htb default 1
  • htb_setup_class__相当于命令行tc class replace dev <dev> classid 1:fffe parent 1: htb rate <min_rate>bps ceil <max_rate>bps burst <burst>b prio <priority>

netdev_set_queue-->netdev_linux_set_queue

  • tc_query_qdisc主要创建qdisc,类似命令行tc qdisc add dev <dev> root handle 1: kind,如果已经创建直接返回。
  • 调用ops->class_set,即htb_class_set,类似命令行tc class replace dev <dev> classid 1:queue_id+1 parent 1:fffe htb rate <min_rate>bps ceil <max_rate>bps burst <burst>b prio <priority>

ofproto_port_set_queues不是特别了解这个操作的意义,可能是修改tos/dscp的

  • 将上面netdev_set_queue配置的queue的数据ofproto_port_queue设置到ofport中。

netdev_set_policing-->netdev_linux_set_policing

  • tc_add_del_ingress_qdisc首先删除ingress的qdisc,类似命令行tc qdisc del dev <devname> handle ffff: ingress
  • tc_add_del_ingress_qdisc接着创建ingress的qdisc,类似命令行tc qdisc add dev <devname> handle ffff: ingress
  • tc_add_policer配置限速速率,类似命令行tc filter add dev <devname> parent ffff: protocol all prio 49 basic police rate <kbits_rate>kbit burst <kbits_burst>k mtu 65535 drop

以上就是OVS配置QOS的流程,出口的时候使用HTB。

目前得出的结论就是OVSingress就是和上面TC命令介绍的方式一致,出口策略没有采用TC的filter命令,而是根据queue_id创建相应的class,而流表会使用set_queue的action设置queue。而ovs-ofctl中的set_queue action是设置的skb的priority,而在htb的htb_classify会根据skb->priority找到对应的class。

限速的实现点有以下几个

  • ingress限速:__netif_receive_skb-->__netif_receive_skb_core-->handle_ing(常用,配置了CONFIG_NET_CLS_ACT)。
  • egress限速:dev_queue_xmit-->__dev_xmit_skb

vhost网口处的限速调用为

  • ingress限速:vhost_worker-->handle_tx_kick-->handle_tx(sock->ops->sendmsg)-->tun_sendmsg-->tun_get_user(内部的tun_alloc_skb?)-->netif_rx_ni(netif_rx没看到多占cpu)-->do_softirq-->call_softirq-->__do_softirq-->net_rx_action-->process_backlog-->__netif_receive_skb-->__netif_receive_skb_core-->handle_ing
  • egress限速:ovs_vport_send-->netdev_send-->dev_queue_xmit-->__dev_xmit_skb
最后修改:2021 年 08 月 18 日
如果觉得我的文章对你有用,请随意赞赏