Skip to content

Commit 5e4d107

Browse files
Florian Westphalummakynes
authored andcommitted
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 178883f ("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>
1 parent bfe7cfb commit 5e4d107

File tree

1 file changed

+53
-35
lines changed

1 file changed

+53
-35
lines changed

net/netfilter/nf_conntrack_standalone.c

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -98,69 +98,87 @@ struct ct_iter_state {
9898
struct seq_net_private p;
9999
struct hlist_nulls_head *hash;
100100
unsigned int htable_size;
101+
unsigned int skip_elems;
101102
unsigned int bucket;
102103
u_int64_t time_now;
103104
};
104105

105-
static struct hlist_nulls_node *ct_get_first(struct seq_file *seq)
106+
static struct nf_conntrack_tuple_hash *ct_get_next(const struct net *net,
107+
struct ct_iter_state *st)
106108
{
107-
struct ct_iter_state *st = seq->private;
109+
struct nf_conntrack_tuple_hash *h;
108110
struct hlist_nulls_node *n;
111+
unsigned int i;
109112

110-
for (st->bucket = 0;
111-
st->bucket < st->htable_size;
112-
st->bucket++) {
113-
n = rcu_dereference(
114-
hlist_nulls_first_rcu(&st->hash[st->bucket]));
115-
if (!is_a_nulls(n))
116-
return n;
117-
}
118-
return NULL;
119-
}
113+
for (i = st->bucket; i < st->htable_size; i++) {
114+
unsigned int skip = 0;
120115

121-
static struct hlist_nulls_node *ct_get_next(struct seq_file *seq,
122-
struct hlist_nulls_node *head)
123-
{
124-
struct ct_iter_state *st = seq->private;
116+
restart:
117+
hlist_nulls_for_each_entry_rcu(h, n, &st->hash[i], hnnode) {
118+
struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
119+
struct hlist_nulls_node *tmp = n;
125120

126-
head = rcu_dereference(hlist_nulls_next_rcu(head));
127-
while (is_a_nulls(head)) {
128-
if (likely(get_nulls_value(head) == st->bucket)) {
129-
if (++st->bucket >= st->htable_size)
130-
return NULL;
121+
if (!net_eq(net, nf_ct_net(ct)))
122+
continue;
123+
124+
if (++skip <= st->skip_elems)
125+
continue;
126+
127+
/* h should be returned, skip to nulls marker. */
128+
while (!is_a_nulls(tmp))
129+
tmp = rcu_dereference(hlist_nulls_next_rcu(tmp));
130+
131+
/* check if h is still linked to hash[i] */
132+
if (get_nulls_value(tmp) != i) {
133+
skip = 0;
134+
goto restart;
135+
}
136+
137+
st->skip_elems = skip;
138+
st->bucket = i;
139+
return h;
131140
}
132-
head = rcu_dereference(
133-
hlist_nulls_first_rcu(&st->hash[st->bucket]));
134-
}
135-
return head;
136-
}
137141

138-
static struct hlist_nulls_node *ct_get_idx(struct seq_file *seq, loff_t pos)
139-
{
140-
struct hlist_nulls_node *head = ct_get_first(seq);
142+
skip = 0;
143+
if (get_nulls_value(n) != i)
144+
goto restart;
145+
146+
st->skip_elems = 0;
147+
}
141148

142-
if (head)
143-
while (pos && (head = ct_get_next(seq, head)))
144-
pos--;
145-
return pos ? NULL : head;
149+
st->bucket = i;
150+
return NULL;
146151
}
147152

148153
static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
149154
__acquires(RCU)
150155
{
151156
struct ct_iter_state *st = seq->private;
157+
struct net *net = seq_file_net(seq);
152158

153159
st->time_now = ktime_get_real_ns();
154160
rcu_read_lock();
155161

156162
nf_conntrack_get_ht(&st->hash, &st->htable_size);
157-
return ct_get_idx(seq, *pos);
163+
164+
if (*pos == 0) {
165+
st->skip_elems = 0;
166+
st->bucket = 0;
167+
} else if (st->skip_elems) {
168+
/* resume from last dumped entry */
169+
st->skip_elems--;
170+
}
171+
172+
return ct_get_next(net, st);
158173
}
159174

160175
static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
161176
{
177+
struct ct_iter_state *st = s->private;
178+
struct net *net = seq_file_net(s);
179+
162180
(*pos)++;
163-
return ct_get_next(s, v);
181+
return ct_get_next(net, st);
164182
}
165183

166184
static void ct_seq_stop(struct seq_file *s, void *v)

0 commit comments

Comments
 (0)