Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Frank Schreuder <fschreuder@transip.nl>
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Nikolay Aleksandrov and committed by
David S. Miller
caaecdd3 5719b296

+7 -5
+5 -2
include/net/inet_frag.h
··· 21 21 * @INET_FRAG_FIRST_IN: first fragment has arrived 22 22 * @INET_FRAG_LAST_IN: final fragment has arrived 23 23 * @INET_FRAG_COMPLETE: frag queue has been processed and is due for destruction 24 - * @INET_FRAG_EVICTED: frag queue is being evicted 25 24 */ 26 25 enum { 27 26 INET_FRAG_FIRST_IN = BIT(0), 28 27 INET_FRAG_LAST_IN = BIT(1), 29 28 INET_FRAG_COMPLETE = BIT(2), 30 - INET_FRAG_EVICTED = BIT(3) 31 29 }; 32 30 33 31 /** ··· 123 125 { 124 126 if (atomic_dec_and_test(&q->refcnt)) 125 127 inet_frag_destroy(q, f); 128 + } 129 + 130 + static inline bool inet_frag_evicting(struct inet_frag_queue *q) 131 + { 132 + return !hlist_unhashed(&q->list_evictor); 126 133 } 127 134 128 135 /* Memory Tracking Functions. */
-1
net/ipv4/inet_fragment.c
··· 140 140 if (!del_timer(&fq->timer)) 141 141 continue; 142 142 143 - fq->flags |= INET_FRAG_EVICTED; 144 143 hlist_add_head(&fq->list_evictor, &expired); 145 144 ++evicted; 146 145 }
+1 -1
net/ipv4/ip_fragment.c
··· 202 202 ipq_kill(qp); 203 203 IP_INC_STATS_BH(net, IPSTATS_MIB_REASMFAILS); 204 204 205 - if (!(qp->q.flags & INET_FRAG_EVICTED)) { 205 + if (!inet_frag_evicting(&qp->q)) { 206 206 struct sk_buff *head = qp->q.fragments; 207 207 const struct iphdr *iph; 208 208 int err;
+1 -1
net/ipv6/reassembly.c
··· 144 144 145 145 IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS); 146 146 147 - if (fq->q.flags & INET_FRAG_EVICTED) 147 + if (inet_frag_evicting(&fq->q)) 148 148 goto out_rcu_unlock; 149 149 150 150 IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT);