Skip to content

Commit bad43ca

Browse files
edumazetdavem330
authored andcommitted
net: introduce skb_try_coalesce()
Move tcp_try_coalesce() protocol independent part to skb_try_coalesce(). skb_try_coalesce() can be used in IPv4 defrag and IPv6 reassembly, to build optimized skbs (less sk_buff, and possibly less 'headers') skb_try_coalesce() is zero copy, unless the copy can fit in destination header (its a rare case) kfree_skb_partial() is also moved to net/core/skbuff.c and exported, because IPv6 will need it in patch (ipv6: use skb coalescing in reassembly). Signed-off-by: Eric Dumazet <[email protected]> Cc: Alexander Duyck <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3dde259 commit bad43ca

File tree

3 files changed

+94
-64
lines changed

3 files changed

+94
-64
lines changed

include/linux/skbuff.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,11 @@ extern void kfree_skb(struct sk_buff *skb);
562562
extern void consume_skb(struct sk_buff *skb);
563563
extern void __kfree_skb(struct sk_buff *skb);
564564
extern struct kmem_cache *skbuff_head_cache;
565+
566+
extern void kfree_skb_partial(struct sk_buff *skb, bool head_stolen);
567+
extern bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
568+
bool *fragstolen, int *delta_truesize);
569+
565570
extern struct sk_buff *__alloc_skb(unsigned int size,
566571
gfp_t priority, int fclone, int node);
567572
extern struct sk_buff *build_skb(void *data, unsigned int frag_size);

net/core/skbuff.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3346,3 +3346,89 @@ void __skb_warn_lro_forwarding(const struct sk_buff *skb)
33463346
skb->dev->name);
33473347
}
33483348
EXPORT_SYMBOL(__skb_warn_lro_forwarding);
3349+
3350+
void kfree_skb_partial(struct sk_buff *skb, bool head_stolen)
3351+
{
3352+
if (head_stolen)
3353+
kmem_cache_free(skbuff_head_cache, skb);
3354+
else
3355+
__kfree_skb(skb);
3356+
}
3357+
EXPORT_SYMBOL(kfree_skb_partial);
3358+
3359+
/**
3360+
* skb_try_coalesce - try to merge skb to prior one
3361+
* @to: prior buffer
3362+
* @from: buffer to add
3363+
* @fragstolen: pointer to boolean
3364+
*
3365+
*/
3366+
bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
3367+
bool *fragstolen, int *delta_truesize)
3368+
{
3369+
int i, delta, len = from->len;
3370+
3371+
*fragstolen = false;
3372+
3373+
if (skb_cloned(to))
3374+
return false;
3375+
3376+
if (len <= skb_tailroom(to)) {
3377+
BUG_ON(skb_copy_bits(from, 0, skb_put(to, len), len));
3378+
*delta_truesize = 0;
3379+
return true;
3380+
}
3381+
3382+
if (skb_has_frag_list(to) || skb_has_frag_list(from))
3383+
return false;
3384+
3385+
if (skb_headlen(from) != 0) {
3386+
struct page *page;
3387+
unsigned int offset;
3388+
3389+
if (skb_shinfo(to)->nr_frags +
3390+
skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS)
3391+
return false;
3392+
3393+
if (skb_head_is_locked(from))
3394+
return false;
3395+
3396+
delta = from->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff));
3397+
3398+
page = virt_to_head_page(from->head);
3399+
offset = from->data - (unsigned char *)page_address(page);
3400+
3401+
skb_fill_page_desc(to, skb_shinfo(to)->nr_frags,
3402+
page, offset, skb_headlen(from));
3403+
*fragstolen = true;
3404+
} else {
3405+
if (skb_shinfo(to)->nr_frags +
3406+
skb_shinfo(from)->nr_frags > MAX_SKB_FRAGS)
3407+
return false;
3408+
3409+
delta = from->truesize -
3410+
SKB_TRUESIZE(skb_end_pointer(from) - from->head);
3411+
}
3412+
3413+
WARN_ON_ONCE(delta < len);
3414+
3415+
memcpy(skb_shinfo(to)->frags + skb_shinfo(to)->nr_frags,
3416+
skb_shinfo(from)->frags,
3417+
skb_shinfo(from)->nr_frags * sizeof(skb_frag_t));
3418+
skb_shinfo(to)->nr_frags += skb_shinfo(from)->nr_frags;
3419+
3420+
if (!skb_cloned(from))
3421+
skb_shinfo(from)->nr_frags = 0;
3422+
3423+
/* if the skb is cloned this does nothing since we set nr_frags to 0 */
3424+
for (i = 0; i < skb_shinfo(from)->nr_frags; i++)
3425+
skb_frag_ref(from, i);
3426+
3427+
to->truesize += delta;
3428+
to->len += len;
3429+
to->data_len += len;
3430+
3431+
*delta_truesize = delta;
3432+
return true;
3433+
}
3434+
EXPORT_SYMBOL(skb_try_coalesce);

net/ipv4/tcp_input.c

Lines changed: 3 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4549,84 +4549,23 @@ static bool tcp_try_coalesce(struct sock *sk,
45494549
struct sk_buff *from,
45504550
bool *fragstolen)
45514551
{
4552-
int i, delta, len = from->len;
4552+
int delta;
45534553

45544554
*fragstolen = false;
45554555

4556-
if (tcp_hdr(from)->fin || skb_cloned(to))
4556+
if (tcp_hdr(from)->fin)
45574557
return false;
4558-
4559-
if (len <= skb_tailroom(to)) {
4560-
BUG_ON(skb_copy_bits(from, 0, skb_put(to, len), len));
4561-
goto merge;
4562-
}
4563-
4564-
if (skb_has_frag_list(to) || skb_has_frag_list(from))
4558+
if (!skb_try_coalesce(to, from, fragstolen, &delta))
45654559
return false;
45664560

4567-
if (skb_headlen(from) != 0) {
4568-
struct page *page;
4569-
unsigned int offset;
4570-
4571-
if (skb_shinfo(to)->nr_frags +
4572-
skb_shinfo(from)->nr_frags >= MAX_SKB_FRAGS)
4573-
return false;
4574-
4575-
if (skb_head_is_locked(from))
4576-
return false;
4577-
4578-
delta = from->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff));
4579-
4580-
page = virt_to_head_page(from->head);
4581-
offset = from->data - (unsigned char *)page_address(page);
4582-
4583-
skb_fill_page_desc(to, skb_shinfo(to)->nr_frags,
4584-
page, offset, skb_headlen(from));
4585-
*fragstolen = true;
4586-
} else {
4587-
if (skb_shinfo(to)->nr_frags +
4588-
skb_shinfo(from)->nr_frags > MAX_SKB_FRAGS)
4589-
return false;
4590-
4591-
delta = from->truesize -
4592-
SKB_TRUESIZE(skb_end_pointer(from) - from->head);
4593-
}
4594-
4595-
WARN_ON_ONCE(delta < len);
4596-
4597-
memcpy(skb_shinfo(to)->frags + skb_shinfo(to)->nr_frags,
4598-
skb_shinfo(from)->frags,
4599-
skb_shinfo(from)->nr_frags * sizeof(skb_frag_t));
4600-
skb_shinfo(to)->nr_frags += skb_shinfo(from)->nr_frags;
4601-
4602-
if (!skb_cloned(from))
4603-
skb_shinfo(from)->nr_frags = 0;
4604-
4605-
/* if the skb is cloned this does nothing since we set nr_frags to 0 */
4606-
for (i = 0; i < skb_shinfo(from)->nr_frags; i++)
4607-
skb_frag_ref(from, i);
4608-
4609-
to->truesize += delta;
46104561
atomic_add(delta, &sk->sk_rmem_alloc);
46114562
sk_mem_charge(sk, delta);
4612-
to->len += len;
4613-
to->data_len += len;
4614-
4615-
merge:
46164563
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPRCVCOALESCE);
46174564
TCP_SKB_CB(to)->end_seq = TCP_SKB_CB(from)->end_seq;
46184565
TCP_SKB_CB(to)->ack_seq = TCP_SKB_CB(from)->ack_seq;
46194566
return true;
46204567
}
46214568

4622-
static void kfree_skb_partial(struct sk_buff *skb, bool head_stolen)
4623-
{
4624-
if (head_stolen)
4625-
kmem_cache_free(skbuff_head_cache, skb);
4626-
else
4627-
__kfree_skb(skb);
4628-
}
4629-
46304569
static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
46314570
{
46324571
struct tcp_sock *tp = tcp_sk(sk);

0 commit comments

Comments
 (0)