Skip to content

Commit bb7488c

Browse files
edumazetgregkh
authored andcommitted
inet: frags: save a pair of atomic operations in reassembly
[ Upstream commit ca0359d ] As mentioned in commit 648700f ("inet: frags: use rhashtables for reassembly units"): A followup patch will even remove the refcount hold/release left from prior implementation and save a couple of atomic operations. This patch implements this idea, seven years later. Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com> Link: https://patch.msgid.link/20250312082250.1803501-5-edumazet@google.com Signed-off-by: Paolo Abeni <pabeni@redhat.com> Stable-dep-of: 653d7dd ("inet: frags: publish queues before arming timer") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 32fa602 commit bb7488c

5 files changed

Lines changed: 24 additions & 18 deletions

File tree

net/ieee802154/6lowpan/reassembly.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,17 +304,20 @@ int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type)
304304
goto err;
305305
}
306306

307+
rcu_read_lock();
307308
fq = fq_find(net, cb, &hdr.source, &hdr.dest);
308309
if (fq != NULL) {
309-
int ret, refs = 1;
310+
int ret, refs = 0;
310311

311312
spin_lock(&fq->q.lock);
312313
ret = lowpan_frag_queue(fq, skb, frag_type, &refs);
313314
spin_unlock(&fq->q.lock);
314315

316+
rcu_read_unlock();
315317
inet_frag_putn(&fq->q, refs);
316318
return ret;
317319
}
320+
rcu_read_unlock();
318321

319322
err:
320323
kfree_skb(skb);

net/ipv4/inet_fragment.c

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,8 @@ static struct inet_frag_queue *inet_frag_alloc(struct fqdir *fqdir,
375375

376376
timer_setup(&q->timer, f->frag_expire, 0);
377377
spin_lock_init(&q->lock);
378-
refcount_set(&q->refcnt, 3);
378+
/* One reference for the timer, one for the hash table. */
379+
refcount_set(&q->refcnt, 2);
379380

380381
return q;
381382
}
@@ -397,7 +398,11 @@ static struct inet_frag_queue *inet_frag_create(struct fqdir *fqdir,
397398
*prev = rhashtable_lookup_get_insert_key(&fqdir->rhashtable, &q->key,
398399
&q->node, f->rhash_params);
399400
if (*prev) {
400-
int refs = 2;
401+
/* We could not insert in the hash table,
402+
* we need to cancel what inet_frag_alloc()
403+
* anticipated.
404+
*/
405+
int refs = 1;
401406

402407
q->flags |= INET_FRAG_COMPLETE;
403408
inet_frag_kill(q, &refs);
@@ -407,7 +412,6 @@ static struct inet_frag_queue *inet_frag_create(struct fqdir *fqdir,
407412
return q;
408413
}
409414

410-
/* TODO : call from rcu_read_lock() and no longer use refcount_inc_not_zero() */
411415
struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key)
412416
{
413417
/* This pairs with WRITE_ONCE() in fqdir_pre_exit(). */
@@ -417,17 +421,11 @@ struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key)
417421
if (!high_thresh || frag_mem_limit(fqdir) > high_thresh)
418422
return NULL;
419423

420-
rcu_read_lock();
421-
422424
prev = rhashtable_lookup(&fqdir->rhashtable, key, fqdir->f->rhash_params);
423425
if (!prev)
424426
fq = inet_frag_create(fqdir, key, &prev);
425-
if (!IS_ERR_OR_NULL(prev)) {
427+
if (!IS_ERR_OR_NULL(prev))
426428
fq = prev;
427-
if (!refcount_inc_not_zero(&fq->refcnt))
428-
fq = NULL;
429-
}
430-
rcu_read_unlock();
431429
return fq;
432430
}
433431
EXPORT_SYMBOL(inet_frag_find);

net/ipv4/ip_fragment.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,18 +477,21 @@ int ip_defrag(struct net *net, struct sk_buff *skb, u32 user)
477477
__IP_INC_STATS(net, IPSTATS_MIB_REASMREQDS);
478478

479479
/* Lookup (or create) queue header */
480+
rcu_read_lock();
480481
qp = ip_find(net, ip_hdr(skb), user, vif);
481482
if (qp) {
482-
int ret, refs = 1;
483+
int ret, refs = 0;
483484

484485
spin_lock(&qp->q.lock);
485486

486487
ret = ip_frag_queue(qp, skb, &refs);
487488

488489
spin_unlock(&qp->q.lock);
490+
rcu_read_unlock();
489491
inet_frag_putn(&qp->q, refs);
490492
return ret;
491493
}
494+
rcu_read_unlock();
492495

493496
__IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS);
494497
kfree_skb(skb);

net/ipv6/netfilter/nf_conntrack_reasm.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
452452
struct frag_hdr *fhdr;
453453
struct frag_queue *fq;
454454
struct ipv6hdr *hdr;
455-
int refs = 1;
455+
int refs = 0;
456456
u8 prevhdr;
457457

458458
/* Jumbo payload inhibits frag. header */
@@ -479,9 +479,11 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
479479
hdr = ipv6_hdr(skb);
480480
fhdr = (struct frag_hdr *)skb_transport_header(skb);
481481

482+
rcu_read_lock();
482483
fq = fq_find(net, fhdr->identification, user, hdr,
483484
skb->dev ? skb->dev->ifindex : 0);
484485
if (fq == NULL) {
486+
rcu_read_unlock();
485487
pr_debug("Can't find and can't create new queue\n");
486488
return -ENOMEM;
487489
}
@@ -495,6 +497,7 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
495497
}
496498

497499
spin_unlock_bh(&fq->q.lock);
500+
rcu_read_unlock();
498501
inet_frag_putn(&fq->q, refs);
499502
return ret;
500503
}

net/ipv6/reassembly.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb,
305305
skb_postpush_rcsum(skb, skb_network_header(skb),
306306
skb_network_header_len(skb));
307307

308-
rcu_read_lock();
309308
__IP6_INC_STATS(net, __in6_dev_stats_get(dev, skb), IPSTATS_MIB_REASMOKS);
310-
rcu_read_unlock();
311309
fq->q.rb_fragments = RB_ROOT;
312310
fq->q.fragments_tail = NULL;
313311
fq->q.last_run_head = NULL;
@@ -319,9 +317,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb,
319317
out_oom:
320318
net_dbg_ratelimited("ip6_frag_reasm: no memory for reassembly\n");
321319
out_fail:
322-
rcu_read_lock();
323320
__IP6_INC_STATS(net, __in6_dev_stats_get(dev, skb), IPSTATS_MIB_REASMFAILS);
324-
rcu_read_unlock();
325321
inet_frag_kill(&fq->q, refs);
326322
return -1;
327323
}
@@ -379,10 +375,11 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
379375
}
380376

381377
iif = skb->dev ? skb->dev->ifindex : 0;
378+
rcu_read_lock();
382379
fq = fq_find(net, fhdr->identification, hdr, iif);
383380
if (fq) {
384381
u32 prob_offset = 0;
385-
int ret, refs = 1;
382+
int ret, refs = 0;
386383

387384
spin_lock(&fq->q.lock);
388385

@@ -391,6 +388,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
391388
&prob_offset, &refs);
392389

393390
spin_unlock(&fq->q.lock);
391+
rcu_read_unlock();
394392
inet_frag_putn(&fq->q, refs);
395393
if (prob_offset) {
396394
__IP6_INC_STATS(net, __in6_dev_get_safely(skb->dev),
@@ -400,6 +398,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
400398
}
401399
return ret;
402400
}
401+
rcu_read_unlock();
403402

404403
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMFAILS);
405404
kfree_skb(skb);

0 commit comments

Comments
 (0)