mirror of
https://github.com/torvalds/linux.git
synced 2026-06-02 11:33:28 +02:00
netfilter: nf_conntrack: speed up reads from nf_conntrack proc file
Dumping all conntrack entries via proc interface can take hours due to
linear search to skip entries dumped so far in each cycle.
Apply same strategy used to speed up ipvs proc reading done in
commit 178883fd03 ("ipvs: speed up reads from ip_vs_conn proc file")
to nf_conntrack.
Note that the ctnetlink interface doesn't suffer from this problem, but
many scripts depend on the nf_conntrack proc interface.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
bfe7cfb65c
commit
5e4d107abd
|
|
@ -98,69 +98,87 @@ struct ct_iter_state {
|
|||
struct seq_net_private p;
|
||||
struct hlist_nulls_head *hash;
|
||||
unsigned int htable_size;
|
||||
unsigned int skip_elems;
|
||||
unsigned int bucket;
|
||||
u_int64_t time_now;
|
||||
};
|
||||
|
||||
static struct hlist_nulls_node *ct_get_first(struct seq_file *seq)
|
||||
static struct nf_conntrack_tuple_hash *ct_get_next(const struct net *net,
|
||||
struct ct_iter_state *st)
|
||||
{
|
||||
struct ct_iter_state *st = seq->private;
|
||||
struct nf_conntrack_tuple_hash *h;
|
||||
struct hlist_nulls_node *n;
|
||||
unsigned int i;
|
||||
|
||||
for (st->bucket = 0;
|
||||
st->bucket < st->htable_size;
|
||||
st->bucket++) {
|
||||
n = rcu_dereference(
|
||||
hlist_nulls_first_rcu(&st->hash[st->bucket]));
|
||||
if (!is_a_nulls(n))
|
||||
return n;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
for (i = st->bucket; i < st->htable_size; i++) {
|
||||
unsigned int skip = 0;
|
||||
|
||||
static struct hlist_nulls_node *ct_get_next(struct seq_file *seq,
|
||||
struct hlist_nulls_node *head)
|
||||
{
|
||||
struct ct_iter_state *st = seq->private;
|
||||
restart:
|
||||
hlist_nulls_for_each_entry_rcu(h, n, &st->hash[i], hnnode) {
|
||||
struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
|
||||
struct hlist_nulls_node *tmp = n;
|
||||
|
||||
head = rcu_dereference(hlist_nulls_next_rcu(head));
|
||||
while (is_a_nulls(head)) {
|
||||
if (likely(get_nulls_value(head) == st->bucket)) {
|
||||
if (++st->bucket >= st->htable_size)
|
||||
return NULL;
|
||||
if (!net_eq(net, nf_ct_net(ct)))
|
||||
continue;
|
||||
|
||||
if (++skip <= st->skip_elems)
|
||||
continue;
|
||||
|
||||
/* h should be returned, skip to nulls marker. */
|
||||
while (!is_a_nulls(tmp))
|
||||
tmp = rcu_dereference(hlist_nulls_next_rcu(tmp));
|
||||
|
||||
/* check if h is still linked to hash[i] */
|
||||
if (get_nulls_value(tmp) != i) {
|
||||
skip = 0;
|
||||
goto restart;
|
||||
}
|
||||
|
||||
st->skip_elems = skip;
|
||||
st->bucket = i;
|
||||
return h;
|
||||
}
|
||||
head = rcu_dereference(
|
||||
hlist_nulls_first_rcu(&st->hash[st->bucket]));
|
||||
|
||||
skip = 0;
|
||||
if (get_nulls_value(n) != i)
|
||||
goto restart;
|
||||
|
||||
st->skip_elems = 0;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
static struct hlist_nulls_node *ct_get_idx(struct seq_file *seq, loff_t pos)
|
||||
{
|
||||
struct hlist_nulls_node *head = ct_get_first(seq);
|
||||
|
||||
if (head)
|
||||
while (pos && (head = ct_get_next(seq, head)))
|
||||
pos--;
|
||||
return pos ? NULL : head;
|
||||
st->bucket = i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
__acquires(RCU)
|
||||
{
|
||||
struct ct_iter_state *st = seq->private;
|
||||
struct net *net = seq_file_net(seq);
|
||||
|
||||
st->time_now = ktime_get_real_ns();
|
||||
rcu_read_lock();
|
||||
|
||||
nf_conntrack_get_ht(&st->hash, &st->htable_size);
|
||||
return ct_get_idx(seq, *pos);
|
||||
|
||||
if (*pos == 0) {
|
||||
st->skip_elems = 0;
|
||||
st->bucket = 0;
|
||||
} else if (st->skip_elems) {
|
||||
/* resume from last dumped entry */
|
||||
st->skip_elems--;
|
||||
}
|
||||
|
||||
return ct_get_next(net, st);
|
||||
}
|
||||
|
||||
static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct ct_iter_state *st = s->private;
|
||||
struct net *net = seq_file_net(s);
|
||||
|
||||
(*pos)++;
|
||||
return ct_get_next(s, v);
|
||||
return ct_get_next(net, st);
|
||||
}
|
||||
|
||||
static void ct_seq_stop(struct seq_file *s, void *v)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user