Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 242922a0 authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso
Browse files

netfilter: conntrack: simplify early_drop



We don't need to acquire the bucket lock during early drop, we can
use lockless traveral just like ____nf_conntrack_find.

The timer deletion serves as synchronization point, if another cpu
attempts to evict same entry, only one will succeed with timer deletion.

Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 8786a971
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -301,6 +301,7 @@ void nf_ct_tmpl_free(struct nf_conn *tmpl);

#define NF_CT_STAT_INC(net, count)	  __this_cpu_inc((net)->ct.stat->count)
#define NF_CT_STAT_INC_ATOMIC(net, count) this_cpu_inc((net)->ct.stat->count)
#define NF_CT_STAT_ADD_ATOMIC(net, count, v) this_cpu_add((net)->ct.stat->count, (v))

#define MODULE_ALIAS_NFCT_HELPER(helper) \
        MODULE_ALIAS("nfct-helper-" helper)
+47 −48
Original line number Diff line number Diff line
@@ -834,32 +834,15 @@ EXPORT_SYMBOL_GPL(nf_conntrack_tuple_taken);

/* There's a small race here where we may free a just-assured
   connection.  Too bad: we're in trouble anyway. */
static noinline int early_drop(struct net *net, unsigned int _hash)
static unsigned int early_drop_list(struct net *net,
				    struct hlist_nulls_head *head)
{
	/* Use oldest entry, which is roughly LRU */
	struct nf_conntrack_tuple_hash *h;
	struct nf_conn *tmp;
	struct hlist_nulls_node *n;
	unsigned int i, hash, sequence;
	struct nf_conn *ct = NULL;
	spinlock_t *lockp;
	bool ret = false;

	i = 0;
	unsigned int drops = 0;
	struct nf_conn *tmp;

	local_bh_disable();
restart:
	sequence = read_seqcount_begin(&nf_conntrack_generation);
	for (; i < NF_CT_EVICTION_RANGE; i++) {
		hash = scale_hash(_hash++);
		lockp = &nf_conntrack_locks[hash % CONNTRACK_LOCKS];
		nf_conntrack_lock(lockp);
		if (read_seqcount_retry(&nf_conntrack_generation, sequence)) {
			spin_unlock(lockp);
			goto restart;
		}
		hlist_nulls_for_each_entry_rcu(h, n, &nf_conntrack_hash[hash],
					       hnnode) {
	hlist_nulls_for_each_entry_rcu(h, n, head, hnnode) {
		tmp = nf_ct_tuplehash_to_ctrack(h);

		if (test_bit(IPS_ASSURED_BIT, &tmp->status) ||
@@ -867,34 +850,50 @@ static noinline int early_drop(struct net *net, unsigned int _hash)
		    nf_ct_is_dying(tmp))
			continue;

			if (atomic_inc_not_zero(&tmp->ct_general.use)) {
				ct = tmp;
				break;
			}
		if (!atomic_inc_not_zero(&tmp->ct_general.use))
			continue;

		/* kill only if still in same netns -- might have moved due to
		 * SLAB_DESTROY_BY_RCU rules.
		 *
		 * We steal the timer reference.  If that fails timer has
		 * already fired or someone else deleted it. Just drop ref
		 * and move to next entry.
		 */
		if (net_eq(nf_ct_net(tmp), net) &&
		    nf_ct_is_confirmed(tmp) &&
		    del_timer(&tmp->timeout) &&
		    nf_ct_delete(tmp, 0, 0))
			drops++;

		nf_ct_put(tmp);
	}

		spin_unlock(lockp);
		if (ct)
			break;
	return drops;
}

	local_bh_enable();
static noinline int early_drop(struct net *net, unsigned int _hash)
{
	unsigned int i;

	if (!ct)
		return false;
	for (i = 0; i < NF_CT_EVICTION_RANGE; i++) {
		struct hlist_nulls_head *ct_hash;
		unsigned hash, sequence, drops;

	/* kill only if in same netns -- might have moved due to
	 * SLAB_DESTROY_BY_RCU rules
	 */
	if (net_eq(nf_ct_net(ct), net) && del_timer(&ct->timeout)) {
		if (nf_ct_delete(ct, 0, 0)) {
			NF_CT_STAT_INC_ATOMIC(net, early_drop);
			ret = true;
		do {
			sequence = read_seqcount_begin(&nf_conntrack_generation);
			hash = scale_hash(_hash++);
			ct_hash = nf_conntrack_hash;
		} while (read_seqcount_retry(&nf_conntrack_generation, sequence));

		drops = early_drop_list(net, &ct_hash[hash]);
		if (drops) {
			NF_CT_STAT_ADD_ATOMIC(net, early_drop, drops);
			return true;
		}
	}

	nf_ct_put(ct);
	return ret;
	return false;
}

static struct nf_conn *