Skip to content

Commit 0d5b931

Browse files
edumazetdavem330
authored andcommitted
inet: frags: better deal with smp races
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: 648700f ("inet: frags: use rhashtables for reassembly units") Signed-off-by: Eric Dumazet <edumazet@google.com> Reported-by: 배석진 <soukjin.bae@samsung.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent e12c225 commit 0d5b931

File tree

1 file changed

+15
-14
lines changed

1 file changed

+15
-14
lines changed

net/ipv4/inet_fragment.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -178,21 +178,22 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf,
178178
}
179179

180180
static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf,
181-
void *arg)
181+
void *arg,
182+
struct inet_frag_queue **prev)
182183
{
183184
struct inet_frags *f = nf->f;
184185
struct inet_frag_queue *q;
185-
int err;
186186

187187
q = inet_frag_alloc(nf, f, arg);
188-
if (!q)
188+
if (!q) {
189+
*prev = ERR_PTR(-ENOMEM);
189190
return NULL;
190-
191+
}
191192
mod_timer(&q->timer, jiffies + nf->timeout);
192193

193-
err = rhashtable_insert_fast(&nf->rhashtable, &q->node,
194-
f->rhash_params);
195-
if (err < 0) {
194+
*prev = rhashtable_lookup_get_insert_key(&nf->rhashtable, &q->key,
195+
&q->node, f->rhash_params);
196+
if (*prev) {
196197
q->flags |= INET_FRAG_COMPLETE;
197198
inet_frag_kill(q);
198199
inet_frag_destroy(q);
@@ -204,22 +205,22 @@ static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf,
204205
/* TODO : call from rcu_read_lock() and no longer use refcount_inc_not_zero() */
205206
struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, void *key)
206207
{
207-
struct inet_frag_queue *fq;
208+
struct inet_frag_queue *fq = NULL, *prev;
208209

209210
if (!nf->high_thresh || frag_mem_limit(nf) > nf->high_thresh)
210211
return NULL;
211212

212213
rcu_read_lock();
213214

214-
fq = rhashtable_lookup(&nf->rhashtable, key, nf->f->rhash_params);
215-
if (fq) {
215+
prev = rhashtable_lookup(&nf->rhashtable, key, nf->f->rhash_params);
216+
if (!prev)
217+
fq = inet_frag_create(nf, key, &prev);
218+
if (prev && !IS_ERR(prev)) {
219+
fq = prev;
216220
if (!refcount_inc_not_zero(&fq->refcnt))
217221
fq = NULL;
218-
rcu_read_unlock();
219-
return fq;
220222
}
221223
rcu_read_unlock();
222-
223-
return inet_frag_create(nf, key);
224+
return fq;
224225
}
225226
EXPORT_SYMBOL(inet_frag_find);

0 commit comments

Comments
 (0)