linux内核netdev_max_backlog设置在极端情况下导致openvswitch转发不通

近期在调试问题时,遇到了一个从配置上看openvswitch无任何问题,但却导致转发不通的问题,特此记录下说,说明在openvswitch在大规模部署时,仍需要有较多调优之处。

设想如下openstack场景,大量tenant router通过linux namespace即仿真vrouter访问外网,此namespace中通过openvswitch internal port(或veth,如果不追求转发性能)连接到另一公网namespace,此时如果公网namespace中接口数量过多,比如说2000个(比如openstack中的qg-*接口),将有可能面临转发不通的情况。

从openvswitch的角度,我们知道openvswitch在内核态存在一个datapath,负责生成内核流表,实现高性能转发,对于一个拥有上千个port的ovs bridge来说,比如对应于上面的公网namespace,收到一个arp请求报文,由于是广播,报文通守openvswitch在userspace命中默认的normal流表后将推送广播至其他所有成员端口的内核流表,此后arp报文广播复制逻辑将在内核datapath模块处执行,最终命中以下内核代码:

:::c
/*
* enqueue_to_backlog is called to queue an skb to a per CPU backlog
* queue (may be a remote CPU queue).
*/
static int enqueue_to_backlog(struct sk_buff *skb, int cpu, unsigned int *qtail)
{
    struct softnet_data *sd;
    unsigned long flags;

    sd = &per_cpu(softnet_data, cpu);

    local_irq_save(flags);

    rps_lock(sd);
    if (skb_queue_len(&sd->input_pkt_queue) <= netdev_max_backlog) {
        if (skb_queue_len(&sd->input_pkt_queue)) {
enqueue:
	        __skb_queue_tail(&sd->input_pkt_queue, skb);
		     input_queue_tail_incr_save(sd, qtail);
             rps_unlock(sd);
             local_irq_restore(flags);
             return NET_RX_SUCCESS;
		 }

         /* Schedule NAPI for backlog device
          * We can use non atomic operation since we own the queue lock
          */
         if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) {
             if (!rps_ipi_queued(sd))
                 ____napi_schedule(sd, &sd->backlog);
         }
         goto enqueue;
    }

    sd->dropped++;
    rps_unlock(sd);

    local_irq_restore(flags);

    atomic_long_inc(&skb->dev->rx_dropped);
    kfree_skb(skb);
    return NET_RX_DROP;
}

可以注意到在上面的代码中有一个对sd->input_pkt_queue的检查过程,而sd是每个cpu核心net rx soft irq用于维护待处理报文的队列,netdev_max_backlog则是内核所提供的一个配置选项,默认为1000。

在ovs datapath复制报文的过程中,将对上千个port进行遍历处理,这个处理过程中不会进行cpu释放的操作,也即将持续调用enqueue_to_backlog(openvswitch datapath最初调用的是netif_rx,但最终调到enqueue_to_backlog),那么默认的1000限制将很快达到,假如上述的qg-*所公用的外网网关碰巧位于1000个以后,那么很显然,某个vrouter namespace发过来的ARP请求则无法被发送给公网外网网关接口,导致vrouter所对应的内网无法访问外网,如果是在生产环境中,势必影响客户业务。

对于此问题的修改,有如下两种思路:

  • 直接提高netdev_max_backlog数量,根据可能存在的接口数量合理设置
  • 通过sdn控制器配置arp或动态回复arp请求

由于目前项目中所采用的方案,我们通过方案1进行了改造,较好的解决了此问题。

发表评论

您的电子邮箱地址不会被公开。