linux通过tc控制网络QoS(2)

先来看下traffic control在内核里是如何实现的,首先内核在发送数据时,最终会调用到dev_queue_xmit,

    struct Qdisc *q

    if (q->enqueue) {
        rc = __dev_xmit_skb(skb, q, dev, txq);
        goto out;
    }   

如果q->enqueue函数不为空,此时就进入traffic control的逻辑,下面调用__dev_xmit_skb

static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
                 struct net_device *dev,
                 struct netdev_queue *txq)

函数会判断Qdisc->state状态,如果__QDISC_STATE_DEACTIVATED,那么free skb返回NET_XMIT_DROP。如果Qdisc->state状态为非__QDISC_STATE_RUNNING,同时Qdisc的标签有TCQ_F_CAN_BYPASS,那么直接把包发出去。否则调用qdisc_enqueue_skb把skb放到root Qdisc中,再调用qdisc_run

qdisc_run如果判断Qdisc->state为__QDISC_STATE_RUNNING,则调用__qdisc_run,

void __qdisc_run(struct Qdisc *q)
{
    unsigned long start_time = jiffies;

    while (qdisc_restart(q)) {
        /*
         * Postpone processing if
         * 1. another process needs the CPU;
         * 2. we've been doing it for too long.
         */
        if (need_resched() || jiffies != start_time) {
            __netif_schedule(q);
            break;
        }
    }

    clear_bit(__QDISC_STATE_RUNNING, &q->state);
}

__qdisc_run会循环调用qdisc_restart,直到消耗了一个jiffy或者该CPU需要被调度给其他进程(need_resched),此时调用__netif_reschedule把当前CPU的softnet_data->output_queue交给Qdisc->output_queue,并触发一个NET_TX_SOFTIRQ软中断

qdisc_restart函数如下:

static inline int qdisc_restart(struct Qdisc *q)
{
    struct netdev_queue *txq;
    struct net_device *dev;
    spinlock_t *root_lock;
    struct sk_buff *skb;

    /* Dequeue packet */
    skb = dequeue_skb(q);
    if (unlikely(!skb))
        return 0;

    root_lock = qdisc_lock(q);
    dev = qdisc_dev(q);
    txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));

    return sch_direct_xmit(skb, q, dev, txq, root_lock);
}

qdisc_restart从Qdisc头里取一个skb出来,调用qdisc_lock获取一个Qdisc根锁,然后调用netdev_get_tx_queue基于skb哈希获取netdev_queue队列,调用sch_direct_xmit直接把skb发送出去

sch_direct_xmit先调用qdisc_unlock释放Qdisc根锁,调用dev_hard_start_xmit通过驱动发送skb,然后判断返回值,如果是NETDEV_TX_OK,返回qdisc_qlen的Qdisc队列长度;如果是NETDEV_TX_LOCKED,此时出现了锁异常,这里不深入探讨了;如果返回了NETDEV_TX_BUSY,则调用dev_requeue_skb把skb重新入队列


traffic control还支持对入包进行控制,内核对于入包会调用netif_receive_skb,该函数会调用handle_ing。handle_ing首先判断skb->dev->rx_queue.qdisc是否是noop_qdisc,如果是noop_qdisc那么不会有任何QoS控制,否则调用ing_filter

static int ing_filter(struct sk_buff *skb)
{
    struct net_device *dev = skb->dev;
    u32 ttl = G_TC_RTTL(skb->tc_verd);
    struct netdev_queue *rxq;
    int result = TC_ACT_OK;
    struct Qdisc *q;
    
    if (MAX_RED_LOOP < ttl++) {
        printk(KERN_WARNING
               "Redir loop detected Dropping packet (%d->%d)\n",
               skb->iif, dev->ifindex);
        return TC_ACT_SHOT;
    }   

    skb->tc_verd = SET_TC_RTTL(skb->tc_verd, ttl);
    skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_INGRESS);
    rxq = &dev->rx_queue;    
    q = rxq->qdisc;
    if (q != &noop_qdisc) {
        spin_lock(qdisc_lock(q));
        if (likely(!test_bit(__QDISC_STATE_DEACTIVATED, &q->state)))
            result = qdisc_enqueue_root(skb, q);
        spin_unlock(qdisc_lock(q));
    }    
    return result;
}

ing_filter首先找出skb->dev->rx_queue.qdisc,如果不是noop_qdisc,同时状态为非__QDISC_STATE_DEACTIVATED,则调用qdisc_enqueue_root,把skb入队列


可以看出,如果网络设备支持traffic control,其驱动的收发函数必须要支持Qdisc的enqueue/dequeue,从测试结果和代码上看,xen netback就可以支持,而bridge就无法支持

对于xen netback而言,限制虚拟机出包QoS有两种方法,首先要知道,netfront发包到了netback,会触发netback的net_tx_action tasklet,里面会调用net_tx_submit,继而最终调用到netif_rx,还记得这个函数不,内核协议栈处理非NAPI的时候就是调用netif_rx来收包的。netif_rx会调用netif_receive_skb,里面会有一个和handle_ing函数,用来对ingress的包做QoS,这就是第一种方法,对netback做tc qdisc ingress规则来进行traffic control;

限制出包的第二钟方法前面也提过啦,就是在网桥上做mark神马的,然后在物理口针对mark做traffic control,由于这里是egress规则,因此可以用各种tc class来做。

那么netback上限制虚拟机入包呢?对于入包而言,首先从物理口进入网桥,然后从网桥进入netback,网桥把包交换到网络设备的过程中,会调用br_forward_finish,继而调用br_dev_queue_push_xmit通过dev_queue_xmit把包“发”出去。因此限制虚拟机入包QoS,只需要在netback上设置tc egress规则即可。由于netback是net_device设备的一个超集,因此支持dev_queue_xmit方法,也支持egress方向的qdisc规则


最后,来看下ingress QoS的相关设置,内核自带的ingress qdisc的功能是非常简单的,只支持最基本的rate limit,对于超出的流量一律drop,目前好的做法是通过一个虚拟网络设备把ingress流量redirect到这个设备上,再在这个虚拟设备设置traffic control规则

modprobe ifb

ip link set ifb0 up

ip link set ifb1 up

ifb设备是内核用来做traffic control redirect的,驱动加载之后,可以对该设备设置和egress相同的规则

tc qdisc add dev ifb0 root handle 1: htb default 100

tc class add dev ifb0 parent 1: classid 1:10 htb rate 100mbit ceil 100mbit

tc filter add dev ifb0 parent 1: protocol ip prio 1 u32 match ip src 0.0.0.0/0 flowid 1:10   #所有流量都match classid 1:10

最后还需要一条规则把peth0的入流量redirect到ifb0设备上

tc qdisc add dev peth0 ingress

tc filter add dev peth0 parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0



  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值