mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 00:10:09 +00:00
d265929930
Currently nf_conncount can trigger garbage collection (GC) at multiple places. Each GC process takes a spin_lock_bh to traverse the nf_conncount_list. We found that when testing port scanning use two parallel nmap, because the number of connection increase fast, the nf_conncount_count and its subsequent call to __nf_conncount_add take too much time, causing several CPU lockup. This happens when user set the conntrack limit to +20,000, because the larger the limit, the longer the list that GC has to traverse. The patch mitigate the performance issue by avoiding unnecessary GC with a timestamp. Whenever nf_conncount has done a GC, a timestamp is updated, and beforce the next time GC is triggered, we make sure it's more than a jiffies. By doin this we can greatly reduce the CPU cycles and avoid the softirq lockup. To reproduce it in OVS, $ ovs-appctl dpctl/ct-set-limits zone=1,limit=20000 $ ovs-appctl dpctl/ct-get-limits At another machine, runs two nmap $ nmap -p1- <IP> $ nmap -p1- <IP> Signed-off-by: William Tu <u9012063@gmail.com> Co-authored-by: Yifeng Sun <pkusunyifeng@gmail.com> Reported-by: Greg Rose <gvrose8192@gmail.com> Suggested-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
40 lines
1.2 KiB
C
40 lines
1.2 KiB
C
#ifndef _NF_CONNTRACK_COUNT_H
|
|
#define _NF_CONNTRACK_COUNT_H
|
|
|
|
#include <linux/list.h>
|
|
#include <linux/spinlock.h>
|
|
#include <net/netfilter/nf_conntrack_tuple.h>
|
|
#include <net/netfilter/nf_conntrack_zones.h>
|
|
|
|
struct nf_conncount_data;
|
|
|
|
struct nf_conncount_list {
|
|
spinlock_t list_lock;
|
|
u32 last_gc; /* jiffies at most recent gc */
|
|
struct list_head head; /* connections with the same filtering key */
|
|
unsigned int count; /* length of list */
|
|
};
|
|
|
|
struct nf_conncount_data *nf_conncount_init(struct net *net, unsigned int family,
|
|
unsigned int keylen);
|
|
void nf_conncount_destroy(struct net *net, unsigned int family,
|
|
struct nf_conncount_data *data);
|
|
|
|
unsigned int nf_conncount_count(struct net *net,
|
|
struct nf_conncount_data *data,
|
|
const u32 *key,
|
|
const struct nf_conntrack_tuple *tuple,
|
|
const struct nf_conntrack_zone *zone);
|
|
|
|
int nf_conncount_add(struct net *net, struct nf_conncount_list *list,
|
|
const struct nf_conntrack_tuple *tuple,
|
|
const struct nf_conntrack_zone *zone);
|
|
|
|
void nf_conncount_list_init(struct nf_conncount_list *list);
|
|
|
|
bool nf_conncount_gc_list(struct net *net,
|
|
struct nf_conncount_list *list);
|
|
|
|
void nf_conncount_cache_free(struct nf_conncount_list *list);
|
|
|
|
#endif
|