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

Commit 59b830bf authored by Eric Dumazet's avatar Eric Dumazet Committed by Greg Kroah-Hartman
Browse files

inet: frags: better deal with smp races

[ Upstream commit 0d5b9311baf27bb545f187f12ecfd558220c607d ]

Multiple cpus might attempt to insert a new fragment in rhashtable,
if for example RPS is buggy, as reported by 배석진 in
https://patchwork.ozlabs.org/patch/994601/



We use rhashtable_lookup_get_insert_key() instead of
rhashtable_insert_fast() to let cpus losing the race
free their own inet_frag_queue and use the one that
was inserted by another cpu.

Fixes: 648700f76b03 ("inet: frags: use rhashtables for reassembly units")
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Reported-by: default avatar배석진 <soukjin.bae@samsung.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 039df755
Loading
Loading
Loading
Loading
+15 −13
Original line number Diff line number Diff line
@@ -180,21 +180,22 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf,
}

static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf,
						void *arg)
						void *arg,
						struct inet_frag_queue **prev)
{
	struct inet_frags *f = nf->f;
	struct inet_frag_queue *q;
	int err;

	q = inet_frag_alloc(nf, f, arg);
	if (!q)
	if (!q) {
		*prev = ERR_PTR(-ENOMEM);
		return NULL;

	}
	mod_timer(&q->timer, jiffies + nf->timeout);

	err = rhashtable_insert_fast(&nf->rhashtable, &q->node,
				     f->rhash_params);
	if (err < 0) {
	*prev = rhashtable_lookup_get_insert_key(&nf->rhashtable, &q->key,
						 &q->node, f->rhash_params);
	if (*prev) {
		q->flags |= INET_FRAG_COMPLETE;
		inet_frag_kill(q);
		inet_frag_destroy(q);
@@ -206,19 +207,20 @@ static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf,
/* TODO : call from rcu_read_lock() and no longer use refcount_inc_not_zero() */
struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, void *key)
{
	struct inet_frag_queue *fq;
	struct inet_frag_queue *fq = NULL, *prev;

	rcu_read_lock();

	fq = rhashtable_lookup(&nf->rhashtable, key, nf->f->rhash_params);
	if (fq) {
	prev = rhashtable_lookup(&nf->rhashtable, key, nf->f->rhash_params);
	if (!prev)
		fq = inet_frag_create(nf, key, &prev);
	if (prev && !IS_ERR(prev)) {
		fq = prev;
		if (!refcount_inc_not_zero(&fq->refcnt))
			fq = NULL;
		rcu_read_unlock();
		return fq;
	}
	rcu_read_unlock();

	return inet_frag_create(nf, key);
	return fq;
}
EXPORT_SYMBOL(inet_frag_find);