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

Commit caaecdd3 authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by David S. Miller
Browse files

inet: frags: remove INET_FRAG_EVICTED and use list_evictor for the test



We can simply remove the INET_FRAG_EVICTED flag to avoid all the flags
race conditions with the evictor and use a participation test for the
evictor list, when we're at that point (after inet_frag_kill) in the
timer there're 2 possible cases:

1. The evictor added the entry to its evictor list while the timer was
waiting for the chainlock
or
2. The timer unchained the entry and the evictor won't see it

In both cases we should be able to see list_evictor correctly due
to the sync on the chainlock.

Joint work with Florian Westphal.

Tested-by: default avatarFrank Schreuder <fschreuder@transip.nl>
Signed-off-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5719b296
Loading
Loading
Loading
Loading
+5 −2
Original line number Original line Diff line number Diff line
@@ -21,13 +21,11 @@ struct netns_frags {
 * @INET_FRAG_FIRST_IN: first fragment has arrived
 * @INET_FRAG_FIRST_IN: first fragment has arrived
 * @INET_FRAG_LAST_IN: final fragment has arrived
 * @INET_FRAG_LAST_IN: final fragment has arrived
 * @INET_FRAG_COMPLETE: frag queue has been processed and is due for destruction
 * @INET_FRAG_COMPLETE: frag queue has been processed and is due for destruction
 * @INET_FRAG_EVICTED: frag queue is being evicted
 */
 */
enum {
enum {
	INET_FRAG_FIRST_IN	= BIT(0),
	INET_FRAG_FIRST_IN	= BIT(0),
	INET_FRAG_LAST_IN	= BIT(1),
	INET_FRAG_LAST_IN	= BIT(1),
	INET_FRAG_COMPLETE	= BIT(2),
	INET_FRAG_COMPLETE	= BIT(2),
	INET_FRAG_EVICTED	= BIT(3)
};
};


/**
/**
@@ -127,6 +125,11 @@ static inline void inet_frag_put(struct inet_frag_queue *q, struct inet_frags *f
		inet_frag_destroy(q, f);
		inet_frag_destroy(q, f);
}
}


static inline bool inet_frag_evicting(struct inet_frag_queue *q)
{
	return !hlist_unhashed(&q->list_evictor);
}

/* Memory Tracking Functions. */
/* Memory Tracking Functions. */


/* The default percpu_counter batch size is not big enough to scale to
/* The default percpu_counter batch size is not big enough to scale to
+0 −1
Original line number Original line Diff line number Diff line
@@ -140,7 +140,6 @@ inet_evict_bucket(struct inet_frags *f, struct inet_frag_bucket *hb)
		if (!del_timer(&fq->timer))
		if (!del_timer(&fq->timer))
			continue;
			continue;


		fq->flags |= INET_FRAG_EVICTED;
		hlist_add_head(&fq->list_evictor, &expired);
		hlist_add_head(&fq->list_evictor, &expired);
		++evicted;
		++evicted;
	}
	}
+1 −1
Original line number Original line Diff line number Diff line
@@ -202,7 +202,7 @@ static void ip_expire(unsigned long arg)
	ipq_kill(qp);
	ipq_kill(qp);
	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS);
	IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS);


	if (!(qp->q.flags & INET_FRAG_EVICTED)) {
	if (!inet_frag_evicting(&qp->q)) {
		struct sk_buff *head = qp->q.fragments;
		struct sk_buff *head = qp->q.fragments;
		const struct iphdr *iph;
		const struct iphdr *iph;
		int err;
		int err;
+1 −1
Original line number Original line Diff line number Diff line
@@ -144,7 +144,7 @@ void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq,


	IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
	IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);


	if (fq->q.flags & INET_FRAG_EVICTED)
	if (inet_frag_evicting(&fq->q))
		goto out_rcu_unlock;
		goto out_rcu_unlock;


	IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT);
	IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT);