From edfb6a148ce62e5e19354a1dcd9a34e00815c2a1 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 23 Jan 2013 03:59:12 +0000 Subject: tuntap: reduce memory using of queues A MAX_TAP_QUEUES(1024) queues of tuntap device is always allocated unconditionally even userspace only requires a single queue device. This is unnecessary and will lead a very high order of page allocation when has a high possibility to fail. Solving this by creating a one queue net device when userspace only use one queue and also reduce MAX_TAP_QUEUES to DEFAULT_MAX_NUM_RSS_QUEUES which can guarantee the success of the allocation. Reported-by: Dirk Hohndel Cc: Eric Dumazet Cc: David Woodhouse Cc: Michael S. Tsirkin Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/tun.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers/net/tun.c') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index c81680dc10eb..8939d2117de2 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -109,11 +109,10 @@ struct tap_filter { unsigned char addr[FLT_EXACT_COUNT][ETH_ALEN]; }; -/* 1024 is probably a high enough limit: modern hypervisors seem to support on - * the order of 100-200 CPUs so this leaves us some breathing space if we want - * to match a queue per guest CPU. - */ -#define MAX_TAP_QUEUES 1024 +/* DEFAULT_MAX_NUM_RSS_QUEUES were choosed to let the rx/tx queues allocated for + * the netdevice to be fit in one page. So we can make sure the success of + * memory allocation. TODO: increase the limit. */ +#define MAX_TAP_QUEUES DEFAULT_MAX_NUM_RSS_QUEUES #define TUN_FLOW_EXPIRE (3 * HZ) @@ -1583,6 +1582,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) else { char *name; unsigned long flags = 0; + int queues = ifr->ifr_flags & IFF_MULTI_QUEUE ? + MAX_TAP_QUEUES : 1; if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; @@ -1606,8 +1607,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) name = ifr->ifr_name; dev = alloc_netdev_mqs(sizeof(struct tun_struct), name, - tun_setup, - MAX_TAP_QUEUES, MAX_TAP_QUEUES); + tun_setup, queues, queues); + if (!dev) return -ENOMEM; -- cgit v1.2.3 From b8732fb7f8920e2f6216d2b67faf8b0b0d67ca81 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 23 Jan 2013 03:59:13 +0000 Subject: tuntap: limit the number of flow caches We create new flow caches when a new flow is identified by tuntap, This may lead some issues: - userspace may produce a huge amount of short live flows to exhaust host memory - the unlimited number of flow caches may produce a long list which increase the time in the linear searching Solve this by introducing a limit of total number of flow caches. Cc: Stephen Hemminger Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/tun.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/net/tun.c') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 8939d2117de2..cc09b67c23bc 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -113,6 +113,7 @@ struct tap_filter { * the netdevice to be fit in one page. So we can make sure the success of * memory allocation. TODO: increase the limit. */ #define MAX_TAP_QUEUES DEFAULT_MAX_NUM_RSS_QUEUES +#define MAX_TAP_FLOWS 4096 #define TUN_FLOW_EXPIRE (3 * HZ) @@ -185,6 +186,7 @@ struct tun_struct { unsigned int numdisabled; struct list_head disabled; void *security; + u32 flow_count; }; static inline u32 tun_hashfn(u32 rxhash) @@ -218,6 +220,7 @@ static struct tun_flow_entry *tun_flow_create(struct tun_struct *tun, e->queue_index = queue_index; e->tun = tun; hlist_add_head_rcu(&e->hash_link, head); + ++tun->flow_count; } return e; } @@ -228,6 +231,7 @@ static void tun_flow_delete(struct tun_struct *tun, struct tun_flow_entry *e) e->rxhash, e->queue_index); hlist_del_rcu(&e->hash_link); kfree_rcu(e, rcu); + --tun->flow_count; } static void tun_flow_flush(struct tun_struct *tun) @@ -317,7 +321,8 @@ static void tun_flow_update(struct tun_struct *tun, u32 rxhash, e->updated = jiffies; } else { spin_lock_bh(&tun->lock); - if (!tun_flow_find(head, rxhash)) + if (!tun_flow_find(head, rxhash) && + tun->flow_count < MAX_TAP_FLOWS) tun_flow_create(tun, head, rxhash, queue_index); if (!timer_pending(&tun->flow_gc_timer)) -- cgit v1.2.3