nfnetlink_queue: use hash table to speed up entry finding.
Subsystems (2)
PATCH 3/3 nfnetlink_queue: use hash table to speed up entry finding.
use hash table to speed up entry finding.
If verdicts aren't received in order, list isn't efficient, and hash table is better.
----include/linux/netfilter/nfnetlink_queue.h | 1 net/netfilter/nfnetlink_queue.c | 118 ++++++++++++++++++++++++++----
2 files changed, 105 insertions(+), 14 deletions(-)
include/linux/netfilter/nfnetlink_queue.h +1 -0
@@ -83,6 +83,7 @@ enum nfqnl_attr_config {NFQA_CFG_CMD ,/* nfqnl_msg_config_cmd */ NFQA_CFG_PARAMS ,/* nfqnl_msg_config_params */ NFQA_CFG_QUEUE_MAXLEN ,/* __u32 */ +NFQA_CFG_QUEUE_HTBLSIZ ,/* __u32 */ __NFQA_CFG_MAX };#define NFQA_CFG_MAX (__NFQA_CFG_MAX-1)
net/netfilter/nfnetlink_queue.c +104 -14
207 lines not on this page · raw message
Quoted 6 lines · Changli Gao
use hash table to speed up entry finding.
If verdicts aren't received in order, list isn't efficient, and hash table is better.
Signed-off-by Changli Gao <[email protected]>
You might add in Changelog that this would be the first flex_array use in kernel.
net/netfilter/nfnetlink_queue.c instance_destroy(struct nfqnl_instance *inst) · line 189
Quoted 7 lines
net/netfilter/nfnetlink_queue.c +6
-0+static inline struct list_head *nfqnl_head_get (struct nfqnl_instance *queue, +unsigned int id) +{ +return flex_array_get (queue->fa, id % queue->queue_htblsiz); +} +
A divide is expensive on many arches, yet in 2010.
When a divide by always the same unsigned int is performed, its good to use a reciprocal divide.
See include/linux/reciprocal_div.h
I dont see flex_array use being useful if you preallocate all its slots.
flex_array_get()/fa_element_to_part_nr() are a monster if you ask me, with many divides. You could submit patches to flex_array to use reciprocal divide, and fa_element_to_part_nr() should be inlined, so that flex_array_get() becomes a leaf function.
vmalloc() is way faster in my opinion. If not, vmalloc() should be fixed.
ffffffff811b7174 <fa_element_to_part_nr>:
ffffffff811b7174: 55 push %rbp ffffffff811b7175: 48 63 3f movslq (%rdi),%rdi ffffffff811b7178: b8 00 10 00 00 mov $0x1000,%eax ffffffff811b717d: 31 d2 xor %edx,%edx ffffffff811b717f: 89 f6 mov %esi,%esi ffffffff811b7181: 48 89 e5 mov %rsp,%rbp ffffffff811b7184: c9 leaveq ffffffff811b7185: 48 f7 f7 div %rdi ffffffff811b7188: 31 d2 xor %edx,%edx ffffffff811b718a: 48 89 c1 mov %rax,%rcx ffffffff811b718d: 48 89 f0 mov %rsi,%rax ffffffff811b7190: 48 f7 f1 div %rcx ffffffff811b7193: c3 retq
ffffffff811b7194 <flex_array_get>:
ffffffff811b7194: 55 push %rbp ffffffff811b7195: 48 89 e5 mov %rsp,%rbp ffffffff811b7198: 41 54 push %r12 ffffffff811b719a: 41 89 f4 mov %esi,%r12d ffffffff811b719d: 53 push %rbx ffffffff811b719e: 48 89 fb mov %rdi,%rbx ffffffff811b71a1: e8 ce ff ff ff callq ffffffff811b7174 <fa_element_to_part_nr> ffffffff811b71a6: 8b 53 04 mov 0x4(%rbx),%edx ffffffff811b71a9: 41 39 d4 cmp %edx,%r12d ffffffff811b71ac: 73 35 jae ffffffff811b71e3 <flex_array_get+0x4f> ffffffff811b71ae: 8b 0b mov (%rbx),%ecx ffffffff811b71b0: 0f af d1 imul %ecx,%edx ffffffff811b71b3: 81 fa f8 0f 00 00 cmp $0xff8,%edx ffffffff811b71b9: 77 2f ja ffffffff811b71ea <flex_array_get+0x56> ffffffff811b71bb: 48 83 c3 08 add $0x8,%rbx ffffffff811b71bf: 48 63 f9 movslq %ecx,%rdi ffffffff811b71c2: b8 00 10 00 00 mov $0x1000,%eax ffffffff811b71c7: 31 d2 xor %edx,%edx ffffffff811b71c9: 48 f7 f7 div %rdi ffffffff811b71cc: 45 89 e4 mov %r12d,%r12d ffffffff811b71cf: 31 d2 xor %edx,%edx ffffffff811b71d1: 48 89 c6 mov %rax,%rsi ffffffff811b71d4: 4c 89 e0 mov %r12,%rax ffffffff811b71d7: 48 f7 f6 div %rsi ffffffff811b71da: 0f af ca imul %edx,%ecx ffffffff811b71dd: 48 8d 04 0b lea (%rbx,%rcx,1),%rax ffffffff811b71e1: eb 02 jmp ffffffff811b71e5 <flex_array_get+0x51> ffffffff811b71e3: 31 c0 xor %eax,%eax ffffffff811b71e5: 5b pop %rbx ffffffff811b71e6: 41 5c pop %r12 ffffffff811b71e8: c9 leaveq ffffffff811b71e9: c3 retq ffffffff811b71ea: 48 98 cltq ffffffff811b71ec: 48 8b 5c c3 08 mov 0x8(%rbx,%rax,8),%rbx ffffffff811b71f1: 48 85 db test %rbx,%rbx ffffffff811b71f4: 75 c9 jne ffffffff811b71bf <flex_array_get+0x2b> ffffffff811b71f6: eb eb jmp ffffffff811b71e3 <flex_array_get+0x4f>
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 4 lines · Changli Gao
use hash table to speed up entry finding.
If verdicts aren't received in order, list isn't efficient, and hash table is better.
Any what is the advantage of using flex arrays compared to a simple open code hash table?
Quoted 9 lines · Patrick McHardy
Quoted 4 lines · Changli Gao
use hash table to speed up entry finding.
If verdicts aren't received in order, list isn't efficient, and hash table is better.
Any what is the advantage of using flex arrays compared to a simple open code hash table?
From Documentation/flexible-arrays.txt
Large contiguous memory allocations can be unreliable in the Linux kernel. Kernel programmers will sometimes respond to this problem by allocating pages with vmalloc(). This solution not ideal, though. On 32-bit systems, memory from vmalloc() must be mapped into a relatively small address space; it's easy to run out. On SMP systems, the page table changes required by vmalloc() allocations can require expensive cross-processor interrupts on all CPUs. And, on all systems, use of space in the vmalloc() range increases pressure on the translation lookaside buffer (TLB), reducing the performance of the system.
User may create lots of queues, which have large hash tables. Flex array has better scalability than vmalloc() and kmalloc().
Signature · 6 lines
Regards, Changli Gao([email protected]) -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 23 lines · Changli Gao
Quoted 9 lines · Patrick McHardy
Quoted 4 lines · Changli Gao
use hash table to speed up entry finding.
If verdicts aren't received in order, list isn't efficient, and hash table is better.
Any what is the advantage of using flex arrays compared to a simple open code hash table?
From
Documentation/flexible-arrays.txtLarge contiguous memory allocations can be unreliable in the Linux kernel. Kernel programmers will sometimes respond to this problem by allocating pages with
vmalloc(). This solution not ideal, though. On 32-bit systems, memory fromvmalloc()must be mapped into a relatively small address space; it's easy to run out. On SMP systems, the page table changes required byvmalloc()allocations can require expensive cross-processor interrupts on all CPUs. And, on all systems, use of space in thevmalloc()range increases pressure on the translation lookaside buffer (TLB), reducing the performance of the system.
Quoted 3 lines
User may create lots of queues, which have large hash tables. Flex array has better scalability than
vmalloc()andkmalloc().
Thats theory. And for sparse arrays, that pro might be true. In your case, you prealloc all the array, using more ram than vmalloc...
In practice, x86 cpu can access vmalloced() data much faster then flex_array_managed data (take a look at all the expensive divides in flex code... :'( )
If vmalloc() happens to be slow, it can trivially be extended to use huge pages too. It will happen one day, when/if necessary.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 5 lines · Eric Dumazet
Le mardi 13 avril 2010 à 19:06 +0800, Changli Gao a écrit :
Thats theory. And for sparse arrays, that pro might be true. In your case, you prealloc all the array, using more ram than vmalloc...
Quoted 7 lines
In practice, x86 cpu can access
vmalloced()data much faster thenflex_array_manageddata (take a look at all the expensive divides in flex code... :'( )If
vmalloc()happens to be slow, it can trivially be extended to use huge pages too. It will happen one day, when/if necessary.
The current flex_array isn't optimized well. If it is well optimized, will it be faster than vmalloc()?
Signature · 6 lines
Regards, Changli Gao([email protected]) -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 4 lines · Eric Dumazet
Thats theory. And for sparse arrays, that pro might be true. In your case, you prealloc all the array, using more ram than vmalloc...
I prealloc all the arrays, because all of them are needed later in any way.
Signature · 6 lines
Regards, Changli Gao([email protected]) -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 8 lines · Changli Gao
Quoted 4 lines · Eric Dumazet
Thats theory. And for sparse arrays, that pro might be true. In your case, you prealloc all the array, using more ram than vmalloc...
I prealloc all the arrays, because all of them are needed later in any way.
Yes, that is why vmalloc() is perfect for this case. No extra memory for management, but one pointer for each page of memory.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 10 lines · Eric Dumazet
Quoted 6 lines · Changli Gao
Quoted 3 lines · Eric Dumazet
Thats theory. And for sparse arrays, that pro might be true. In your case, you prealloc all the array, using more ram than vmalloc...
I prealloc all the arrays, because all of them are needed later in any way.
Yes, that is why
vmalloc()is perfect for this case. No extra memory for management, but one pointer for each page of memory.
I agree, if it works for conntrack, it certainly also works for nfnetlink_queue.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 6 lines · Patrick McHardy
Quoted 2 lines
Yes, that is why
vmalloc()is perfect for this case. No extra memory for management, but one pointer for each page of memory.I agree, if it works for conntrack, it certainly also works for
nfnetlink_queue.
I need to allocate memory in atomic section, so vmalloc() can't be used. :(
does double check mechanism works?
Signature · 6 lines
Regards, Changli Gao([email protected]) -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 4 lines · Changli Gao
Quoted 3 lines · Patrick McHardy
Quoted 2 lines
Yes, that is why
vmalloc()is perfect for this case. No extra memory for management, but one pointer for each page of memory.
Quoted 8 lines
Quoted 3 lines
I agree, if it works for conntrack, it certainly also works for
nfnetlink_queue.I need to allocate memory in atomic section, so
vmalloc()can't be used. :(does double check mechanism works?
Allocate memory to setup a hash table in atomic section ?
Changli you can forget this right now, this is not serious.
We are not going to use this thing because your requirements are crazy.
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 11 lines · Eric Dumazet
Quoted 10 lines · Changli Gao
Quoted 6 lines · Patrick McHardy
Quoted 2 lines
Yes, that is why
vmalloc()is perfect for this case. No extra memory for management, but one pointer for each page of memory.I agree, if it works for conntrack, it certainly also works for
nfnetlink_queue.I need to allocate memory in atomic section, so
vmalloc()can't be used. :(
Quoted 9 lines
Quoted 2 lines
does double check mechanism works?
Allocate memory to setup a hash table in atomic section ?
Changli you can forget this right now, this is not serious.
We are not going to use this thing because your requirements are crazy.
OK, I'll keep it myself. :)
Signature · 6 lines
Regards, Changli Gao([email protected]) -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 8 lines · Changli Gao
Quoted 5 lines · Patrick McHardy
Quoted 2 lines
Yes, that is why
vmalloc()is perfect for this case. No extra memory for management, but one pointer for each page of memory.I agree, if it works for conntrack, it certainly also works for
nfnetlink_queue.I need to allocate memory in atomic section, so
vmalloc()can't be used. :(
Why?
Quoted 4 lines · Patrick McHardy
Quoted 3 lines · Changli Gao
Quoted 2 lines · Patrick McHardy
Quoted 2 lines
Yes, that is why
vmalloc()is perfect for this case. No extra memory for management, but one pointer for each page of memory.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
I agree, if it works for conntrack, it certainly also works for
nfnetlink_queue.I need to allocate memory in atomic section, so
vmalloc()can't be used. :(Why?
instance_create() is called in rcu read-side critical section, and the whole body of this function is protected by the spinlock instances_lock. All these make memory allocation for queue instances should be atomic.
Signature · 6 lines
Regards, Changli Gao([email protected]) -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 8 lines · Changli Gao
Quoted 7 lines · Patrick McHardy
Quoted 6 lines · Changli Gao
Quoted 5 lines · Patrick McHardy
Quoted 2 lines
Yes, that is why
vmalloc()is perfect for this case. No extra memory for management, but one pointer for each page of memory.I agree, if it works for conntrack, it certainly also works for
nfnetlink_queue.
Quoted 8 lines
Quoted 3 lines
Quoted 1 line
I need to allocate memory in atomic section, so
vmalloc()can't be used. :(Why?
instance_create()is called in rcu read-side critical section, and the whole body of this function is protected by the spinlockinstances_lock. All these make memory allocation for queue instances should be atomic.
That should be easily fixable. For the lookup we can add a reference counter so we don't need the rcu read side critical section. For creation the lock actually looks unnecessary since all nfnetlink handlers run under nfnl_mutex, so we can't have concurrent creation and removal of queuing instances. Well, we need it for list insertion to avoid races with the seq file handlers, but we don't need it before that.
Quoted 12 lines · Patrick McHardy
Quoted 11 lines · Changli Gao
Quoted 10 lines · Patrick McHardy
Quoted 7 lines · Changli Gao
Quoted 5 lines · Patrick McHardy
Quoted 2 lines
Yes, that is why
vmalloc()is perfect for this case. No extra memory for management, but one pointer for each page of memory.I agree, if it works for conntrack, it certainly also works for
nfnetlink_queue.I need to allocate memory in atomic section, so
vmalloc()can't be used. :(Why?
Quoted 7 lines
Quoted 4 lines
instance_create()is called in rcu read-side critical section, and the whole body of this function is protected by the spinlockinstances_lock. All these make memory allocation for queue instances should be atomic.That should be easily fixable. For the lookup we can add a reference counter so we don't need the rcu read side critical section.
Actually we don't even need that, since we're holding nfnl_mutex the instance can't go away.
Quoted 5 lines
For creation the lock actually looks unnecessary since all nfnetlink handlers run under
nfnl_mutex, so we can't have concurrent creation and removal of queuing instances. Well, we need it for list insertion to avoid races with the seq file handlers, but we don't need it before that.
Quoted 8 lines · Patrick McHardy
Quoted 3 lines · Patrick McHardy
That should be easily fixable. For the lookup we can add a reference counter so we don't need the rcu read side critical section.
Actually we don't even need that, since we're holding
nfnl_mutexthe instance can't go away.
I don't think so. a netlink queue instance maybe destroyed by nfqnl_rcv_nl_event(). And the function is called by netlink directly without nfnl_mutex.
Signature · 6 lines
Regards, Changli Gao([email protected]) -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 11 lines · Changli Gao
Quoted 6 lines · Patrick McHardy
Quoted 2 lines · Patrick McHardy
That should be easily fixable. For the lookup we can add a reference counter so we don't need the rcu read side critical section.
Actually we don't even need that, since we're holding
nfnl_mutexthe instance can't go away.I don't think so. a netlink queue instance maybe destroyed by
nfqnl_rcv_nl_event(). And the function is called by netlink directly withoutnfnl_mutex.
No its not:
static void nfnetlink_rcv(struct sk_buff *skb)
{
nfnl_lock();
netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
nfnl_unlock();
}Quoted 11 lines · Patrick McHardy
No its not:
static void
nfnetlink_rcv(struct sk_buff *skb)
{
nfnl_lock();
netlink_rcv_skb(skb,&nfnetlink_rcv_msg);
nfnl_unlock();
}
static int
nfqnl_rcv_nl_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct netlink_notify *n = ptr;
if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) {
int i;
/* destroy all instances for this pid */
spin_lock(&instances_lock);
for (i = 0; i < INSTANCE_BUCKETS; i++) {
struct hlist_node *tmp, *t2;
struct nfqnl_instance *inst;
struct hlist_head *head = &instance_table[i];
hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) {
if ((n->net == &init_net) &&
(n->pid == inst->peer_pid))
__instance_destroy(inst);
}
}
spin_unlock(&instances_lock);
}
return NOTIFY_DONE;
}static struct notifier_block nfqnl_rtnl_notifier = {
.notifier_call = nfqnl_rcv_nl_event,
};
...
netlink_register_notifier(&nfqnl_rtnl_notifier);
...
int netlink_register_notifier(struct notifier_block *nb)
{
return atomic_notifier_chain_register(&netlink_chain, nb);
}...
static int netlink_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct netlink_sock *nlk;
if (!sk)
return 0;
netlink_remove(sk);
sock_orphan(sk);
nlk = nlk_sk(sk);
/*
* OK. Socket is unlinked, any packets that arrive now
* will be purged.
*/
sock->sk = NULL;
wake_up_interruptible_all(&nlk->wait);
skb_queue_purge(&sk->sk_write_queue);
if (nlk->pid) {
struct netlink_notify n = {
.net = sock_net(sk),
.protocol = sk->sk_protocol,
.pid = nlk->pid,
};
atomic_notifier_call_chain(&netlink_chain,
NETLINK_URELEASE, &n);
}Signature · 6 lines
Regards, Changli Gao([email protected]) -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 27 lines · Changli Gao
static int
nfqnl_rcv_nl_event(structnotifier_block*this,
unsigned long event, void *ptr){ struct netlink_notify *n = ptr; if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) { int i; /* destroy all instances for this pid */ spin_lock(&instances_lock); for (i = 0; i < INSTANCE_BUCKETS; i++) { struct hlist_node *tmp, *t2; struct nfqnl_instance *inst; struct hlist_head *head = &instance_table[i]; hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) { if ((n->net == &init_net) && (n->pid == inst->peer_pid)) __instance_destroy(inst); } } spin_unlock(&instances_lock); } return NOTIFY_DONE; }
Quoted 4 lines
static struct
notifier_blocknfqnl_rtnl_notifier= {
.notifier_call =nfqnl_rcv_nl_event,
};
Ah, right. So call nfnl_lock() or convert the spinlock to a mutex.
Quoted 28 lines · Patrick McHardy
Quoted 27 lines · Changli Gao
static int
nfqnl_rcv_nl_event(struct notifier_block *this, unsigned long event, void *ptr){
structnetlink_notify*n = ptr;if (event ==
NETLINK_URELEASE&&n->protocol==NETLINK_NETFILTER) { int i;/* destroy all instances for this pid */
spin_lock(&instances_lock);
for (i = 0; i <INSTANCE_BUCKETS; i++) { structhlist_node*tmp, *t2; structnfqnl_instance*inst; structhlist_head*head = &instance_table[i];
hlist_for_each_entry_safe(inst, tmp, t2, head, hlist){ if ((n->net==&init_net) && (n->pid==inst->peer_pid))__instance_destroy(inst); }
}
spin_unlock(&instances_lock);
}
returnNOTIFY_DONE;
}
Quoted 8 lines
Quoted 4 lines
static struct
notifier_blocknfqnl_rtnl_notifier= { .notifier_call =nfqnl_rcv_nl_event, };Ah, right. So call
nfnl_lock()or convert the spinlock to a mutex.
We can't convert the spinlock to a mutex simply. The notifier chain is an atomic notifier chain.
Signature · 2 lines
Regards, Changli Gao([email protected])
Quoted 33 lines · Changli Gao
Quoted 32 lines · Patrick McHardy
Quoted 31 lines · Changli Gao
static int
nfqnl_rcv_nl_event(structnotifier_block*this,
unsigned long event, void *ptr){ struct netlink_notify *n = ptr; if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) { int i; /* destroy all instances for this pid */ spin_lock(&instances_lock); for (i = 0; i < INSTANCE_BUCKETS; i++) { struct hlist_node *tmp, *t2; struct nfqnl_instance *inst; struct hlist_head *head = &instance_table[i]; hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) { if ((n->net == &init_net) && (n->pid == inst->peer_pid)) __instance_destroy(inst); } } spin_unlock(&instances_lock); } return NOTIFY_DONE; }static struct
notifier_blocknfqnl_rtnl_notifier= {
.notifier_call =nfqnl_rcv_nl_event,
};
Quoted 6 lines
Quoted 3 lines
Ah, right. So call
nfnl_lock()or convert the spinlock to a mutex.We can't convert the spinlock to a mutex simply. The notifier chain is an atomic notifier chain.
Well, then use reference counting, redo the lookup, or whatever. Really, this isn't that hard.