Commit 513b49ca authored by Keefe LIU's avatar Keefe LIU Committed by Zhengchao Shao
Browse files

ipvlan: Introduce local xmit queue for l2e mode

hulk inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I8KDQE


CVE: NA

-------------------------------------------------

Consider two IPVlan devices are set up on the same master, when
they communicate with each other by TCP, the receive part is too
fast to make the send packets coalesced, so in this case, the
performace is not as good as we expect.

This patch introduces a local xmit queue for l2e mode, when the
packets are sent to the IPVlan devices of the same master, the
packets will be cloned and added to the local xmit queue, this
operation can make the send packets coalesced and improve the
TCP performace in this case.

Signed-off-by: default avatarKeefe LIU <liuqifa@huawei.com>
Signed-off-by: default avatarZhengchao Shao <shaozhengchao@huawei.com>
parent 53f51b3c
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -39,6 +39,11 @@

#define IPVLAN_QBACKLOG_LIMIT	1000

#if IS_ENABLED(CONFIG_IPVLAN_L2E)
extern int sysctl_ipvlan_loop_qlen;
extern int sysctl_ipvlan_loop_delay;
#endif

typedef enum {
	IPVL_IPV6 = 0,
	IPVL_ICMPV6,
@@ -70,6 +75,12 @@ struct ipvl_dev {
	netdev_features_t	sfeatures;
	u32			msg_enable;
	spinlock_t		addrs_lock;
#if IS_ENABLED(CONFIG_IPVLAN_L2E)
	int                     local_packets_cached;
	unsigned long           local_timeout;
	struct timer_list       local_free_timer;
	struct sk_buff_head     local_xmit_queue;
#endif
};

struct ipvl_addr {
+33 −1
Original line number Diff line number Diff line
@@ -766,9 +766,37 @@ static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev)
}

#if IS_ENABLED(CONFIG_IPVLAN_L2E)
static int ipvlan_l2e_local_xmit_event(struct ipvl_dev *ipvlan,
				       struct sk_buff **pskb)
{
	struct sk_buff *nskb, *tskb;

	while ((ipvlan->local_packets_cached >= sysctl_ipvlan_loop_qlen) &&
	       (tskb = skb_dequeue(&ipvlan->local_xmit_queue))) {
		ipvlan->local_packets_cached -= tskb->truesize;
		if (ipvlan->local_packets_cached < 0 ||
		    skb_queue_empty(&ipvlan->local_xmit_queue))
			ipvlan->local_packets_cached = 0;
		kfree_skb(tskb);
	}

	nskb = skb_clone(*pskb, GFP_ATOMIC);
	if (!nskb)
		return NET_XMIT_DROP;

	ipvlan->local_timeout = jiffies
				+ (sysctl_ipvlan_loop_delay * HZ) / 1000;
	mod_timer(&ipvlan->local_free_timer, ipvlan->local_timeout);
	skb_queue_tail(&ipvlan->local_xmit_queue, *pskb);
	ipvlan->local_packets_cached += (*pskb)->truesize;
	*pskb = nskb;

	return 0;
}

static int ipvlan_xmit_mode_l2e(struct sk_buff *skb, struct net_device *dev)
{
	const struct ipvl_dev *ipvlan = netdev_priv(dev);
	struct ipvl_dev *ipvlan = netdev_priv(dev);
	struct ethhdr *eth = eth_hdr(skb);
	struct ipvl_addr *addr;
	void *lyr3h;
@@ -785,6 +813,10 @@ static int ipvlan_xmit_mode_l2e(struct sk_buff *skb, struct net_device *dev)
					consume_skb(skb);
					return NET_XMIT_DROP;
				}

				if (unlikely(ipvlan_l2e_local_xmit_event(ipvlan,
									 &skb)))
					return NET_XMIT_DROP;
				return ipvlan_rcv_frame(addr, &skb, true);
			}
		}
+86 −3
Original line number Diff line number Diff line
@@ -7,9 +7,52 @@
#include "ipvlan.h"

#if IS_ENABLED(CONFIG_IPVLAN_L2E)
static int one = 1;
static int delay_max = 100;
/* set loop queue length from 0 to 10 big packets(65536) */
static int qlen_min;
static int qlen_max = 655360;

int sysctl_ipvlan_loop_qlen = 131072;
int sysctl_ipvlan_loop_delay = 10;
static int ipvlan_default_mode = IPVLAN_MODE_L3;
module_param(ipvlan_default_mode, int, 0400);
MODULE_PARM_DESC(ipvlan_default_mode, "set ipvlan default mode: 0 for l2, 1 for l3, 2 for l3s, 3 for l2e, others invalid now");
MODULE_PARM_DESC(ipvlan_default_mode, "set ipvlan default mode: 0 for l2, 1 for l3, 2 for l2e, 3 for l3s, others invalid now");

static struct ctl_table_header *ipvlan_table_hrd;
static struct ctl_table ipvlan_table[] = {
	{
		.procname	= "loop_delay",
		.data		= &sysctl_ipvlan_loop_delay,
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= &one,
		.extra2		= &delay_max,
	},
	{
		.procname	= "loop_qlen",
		.data		= &sysctl_ipvlan_loop_qlen,
		.maxlen		= sizeof(int),
		.mode		= 0644,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= &qlen_min,
		.extra2		= &qlen_max,
	},
	{ }
};

static int ipvlan_sysctl_init(void)
{
	ipvlan_table_hrd = register_net_sysctl(&init_net,
					       "net/ipvlan", ipvlan_table);
	return !ipvlan_table_hrd ? -ENOMEM : 0;
}

static void ipvlan_sysctl_exit(void)
{
	unregister_net_sysctl_table(ipvlan_table_hrd);
}
#endif

static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval,
@@ -168,6 +211,34 @@ static int ipvlan_init(struct net_device *dev)
	return 0;
}

#if IS_ENABLED(CONFIG_IPVLAN_L2E)
static void ipvlan_local_free_handler(struct timer_list *t)
{
	struct ipvl_dev *ipvlan = from_timer(ipvlan, t, local_free_timer);

	skb_queue_purge(&ipvlan->local_xmit_queue);
	ipvlan->local_packets_cached = 0;
}

static inline void ipvlan_local_init(struct net_device *dev)
{
	struct ipvl_dev *ipvlan = netdev_priv(dev);

	ipvlan->local_packets_cached = 0;
	skb_queue_head_init(&ipvlan->local_xmit_queue);
	timer_setup(&ipvlan->local_free_timer,
		    ipvlan_local_free_handler, 0);
}

static inline void ipvlan_local_uninit(struct net_device *dev)
{
	struct ipvl_dev *ipvlan = netdev_priv(dev);

	del_timer(&ipvlan->local_free_timer);
	skb_queue_purge(&ipvlan->local_xmit_queue);
}
#endif

static void ipvlan_uninit(struct net_device *dev)
{
	struct ipvl_dev *ipvlan = netdev_priv(dev);
@@ -193,6 +264,9 @@ static int ipvlan_open(struct net_device *dev)
	else
		dev->flags &= ~IFF_NOARP;

#if IS_ENABLED(CONFIG_IPVLAN_L2E)
	ipvlan_local_init(dev);
#endif
	rcu_read_lock();
	list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
		ipvlan_ht_addr_add(ipvlan, addr);
@@ -209,7 +283,9 @@ static int ipvlan_stop(struct net_device *dev)

	dev_uc_unsync(phy_dev, dev);
	dev_mc_unsync(phy_dev, dev);

#if IS_ENABLED(CONFIG_IPVLAN_L2E)
	ipvlan_local_uninit(dev);
#endif
	rcu_read_lock();
	list_for_each_entry_rcu(addr, &ipvlan->addrs, anode)
		ipvlan_ht_addr_del(addr);
@@ -1061,7 +1137,11 @@ static int __init ipvlan_init_module(void)
		ipvlan_l3s_cleanup();
		goto error;
	}

#if IS_ENABLED(CONFIG_IPVLAN_L2E)
	err = ipvlan_sysctl_init();
	if (err < 0)
		pr_err("ipvlan proc init failed, continue\n");
#endif
	return 0;
error:
	unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
@@ -1089,6 +1169,9 @@ static void __exit ipvlan_cleanup_module(void)
	unregister_inet6addr_validator_notifier(
	    &ipvlan_addr6_vtor_notifier_block);
#endif
#if IS_ENABLED(CONFIG_IPVLAN_L2E)
	ipvlan_sysctl_exit();
#endif
}

module_init(ipvlan_init_module);