Skip to contentSkip to conversation

nfnetlink_queue: use hash table to speed up entry finding.

PATCH In review Changli Gao 3 participants 22 messages Apr 9, 2010 – Apr 16, 2010

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

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
index 2455fe5..77b1566 100644
@@ -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

#1 lore raw
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.

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

#2 lore raw
In reply to Changli Gao
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?

#3 lore raw
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
#4 lore raw
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.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.

Quoted 3 lines

User may create lots of queues, which have large hash tables. Flex array has better scalability than vmalloc() and kmalloc().

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

#5 lore raw
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 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.

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
#6 lore raw
In reply to Eric Dumazet
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
#7 lore raw
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

#8 lore raw
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

#9 lore raw
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
#10 lore raw
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

#11 lore raw
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
#12 lore raw
In reply to Changli Gao
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?

#13 lore raw
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
#14 lore raw
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 spinlock instances_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.

#15 lore raw
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 spinlock instances_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.

#16 lore raw
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_mutex the 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
#17 lore raw
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_mutex the 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.

No its not:

static void nfnetlink_rcv(struct sk_buff *skb)

{
	nfnl_lock();
	netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
	nfnl_unlock();
}
#18 lore raw
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
#19 lore raw
Quoted 27 lines · Changli Gao

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;
}
Quoted 4 lines

static struct notifier_block nfqnl_rtnl_notifier = {
.notifier_call = nfqnl_rcv_nl_event,
};

Ah, right. So call nfnl_lock() or convert the spinlock to a mutex.

#20 lore raw
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) {
        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 8 lines
Quoted 4 lines

static struct notifier_block nfqnl_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])
#21 lore raw
Quoted 33 lines · Changli Gao
Quoted 32 lines · Patrick McHardy
Quoted 31 lines · Changli Gao

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,
};

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.