Skip to content

Commit 8c873e2

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: core: free hooks with call_rcu
Giuseppe Scrivano says: "SELinux, if enabled, registers for each new network namespace 6 netfilter hooks." Cost for this is high. With synchronize_net() removed: "The net benefit on an SMP machine with two cores is that creating a new network namespace takes -40% of the original time." This patch replaces synchronize_net+kvfree with call_rcu(). We store rcu_head at the tail of a structure that has no fixed layout, i.e. we cannot use offsetof() to compute the start of the original allocation. Thus store this information right after the rcu head. We could simplify this by just placing the rcu_head at the start of struct nf_hook_entries. However, this structure is used in packet processing hotpath, so only place what is needed for that at the beginning of the struct. Reported-by: Giuseppe Scrivano <gscrivan@redhat.com> Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 26888df commit 8c873e2

File tree

2 files changed

+43
-10
lines changed

2 files changed

+43
-10
lines changed

include/linux/netfilter.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,28 @@ struct nf_hook_entry {
7777
void *priv;
7878
};
7979

80+
struct nf_hook_entries_rcu_head {
81+
struct rcu_head head;
82+
void *allocation;
83+
};
84+
8085
struct nf_hook_entries {
8186
u16 num_hook_entries;
8287
/* padding */
8388
struct nf_hook_entry hooks[];
8489

85-
/* trailer: pointers to original orig_ops of each hook.
86-
*
87-
* This is not part of struct nf_hook_entry since its only
88-
* needed in slow path (hook register/unregister).
90+
/* trailer: pointers to original orig_ops of each hook,
91+
* followed by rcu_head and scratch space used for freeing
92+
* the structure via call_rcu.
8993
*
94+
* This is not part of struct nf_hook_entry since its only
95+
* needed in slow path (hook register/unregister):
9096
* const struct nf_hook_ops *orig_ops[]
97+
*
98+
* For the same reason, we store this at end -- its
99+
* only needed when a hook is deleted, not during
100+
* packet path processing:
101+
* struct nf_hook_entries_rcu_head head
91102
*/
92103
};
93104

net/netfilter/core.c

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ static struct nf_hook_entries *allocate_hook_entries_size(u16 num)
7474
struct nf_hook_entries *e;
7575
size_t alloc = sizeof(*e) +
7676
sizeof(struct nf_hook_entry) * num +
77-
sizeof(struct nf_hook_ops *) * num;
77+
sizeof(struct nf_hook_ops *) * num +
78+
sizeof(struct nf_hook_entries_rcu_head);
7879

7980
if (num == 0)
8081
return NULL;
@@ -85,6 +86,30 @@ static struct nf_hook_entries *allocate_hook_entries_size(u16 num)
8586
return e;
8687
}
8788

89+
static void __nf_hook_entries_free(struct rcu_head *h)
90+
{
91+
struct nf_hook_entries_rcu_head *head;
92+
93+
head = container_of(h, struct nf_hook_entries_rcu_head, head);
94+
kvfree(head->allocation);
95+
}
96+
97+
static void nf_hook_entries_free(struct nf_hook_entries *e)
98+
{
99+
struct nf_hook_entries_rcu_head *head;
100+
struct nf_hook_ops **ops;
101+
unsigned int num;
102+
103+
if (!e)
104+
return;
105+
106+
num = e->num_hook_entries;
107+
ops = nf_hook_entries_get_hook_ops(e);
108+
head = (void *)&ops[num];
109+
head->allocation = e;
110+
call_rcu(&head->head, __nf_hook_entries_free);
111+
}
112+
88113
static unsigned int accept_all(void *priv,
89114
struct sk_buff *skb,
90115
const struct nf_hook_state *state)
@@ -291,9 +316,8 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
291316
#ifdef HAVE_JUMP_LABEL
292317
static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]);
293318
#endif
294-
synchronize_net();
295319
BUG_ON(p == new_hooks);
296-
kvfree(p);
320+
nf_hook_entries_free(p);
297321
return 0;
298322
}
299323
EXPORT_SYMBOL(nf_register_net_hook);
@@ -361,10 +385,8 @@ void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
361385
if (!p)
362386
return;
363387

364-
synchronize_net();
365-
366388
nf_queue_nf_hook_drop(net);
367-
kvfree(p);
389+
nf_hook_entries_free(p);
368390
}
369391
EXPORT_SYMBOL(nf_unregister_net_hook);
370392

0 commit comments

Comments
 (0)