Skip to content

Commit

Permalink
Merge branch 'skb_expand_head'
Browse files Browse the repository at this point in the history
Vasily Averin says:

====================
skbuff: introduce skb_expand_head()

currently if skb does not have enough headroom skb_realloc_headrom is called.
It is not optimal because it creates new skb.

this patch set introduces new helper skb_expand_head()
Unlike skb_realloc_headroom, it does not allocate a new skb if possible;
copies skb->sk on new skb when as needed and frees original skb in case of failures.

This helps to simplify ip[6]_finish_output2(), ip6_xmit() and few other
functions in vrf, ax25 and bpf.

There are few other cases where this helper can be used
but it requires an additional investigations.

v3 changes:
 - ax25 compilation warning fixed
 - v5.14-rc4 rebase
 - now it does not depend on non-committed pathces

v2 changes:
 - helper's name was changed to skb_expand_head
 - fixed few mistakes inside skb_expand_head():
    skb_set_owner_w should set sk on nskb
    kfree was replaced by kfree_skb()
    improved warning message
 - added minor refactoring in changed functions in vrf and bpf patches
 - removed kfree_skb() in ax25_rt_build_path caller ax25_ip_xmit
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Aug 3, 2021
2 parents fa97662 + a1e975e commit 07e1d6b
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 121 deletions.
21 changes: 7 additions & 14 deletions drivers/net/vrf.c
Original file line number Diff line number Diff line change
Expand Up @@ -857,30 +857,24 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s
unsigned int hh_len = LL_RESERVED_SPACE(dev);
struct neighbour *neigh;
bool is_v6gw = false;
int ret = -EINVAL;

nf_reset_ct(skb);

/* Be paranoid, rather than too clever. */
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
struct sk_buff *skb2;

skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
if (!skb2) {
ret = -ENOMEM;
goto err;
skb = skb_expand_head(skb, hh_len);
if (!skb) {
skb->dev->stats.tx_errors++;
return -ENOMEM;
}
if (skb->sk)
skb_set_owner_w(skb2, skb->sk);

consume_skb(skb);
skb = skb2;
}

rcu_read_lock_bh();

neigh = ip_neigh_for_gw(rt, skb, &is_v6gw);
if (!IS_ERR(neigh)) {
int ret;

sock_confirm_neigh(skb, neigh);
/* if crossing protocols, can not use the cached header */
ret = neigh_output(neigh, skb, is_v6gw);
Expand All @@ -889,9 +883,8 @@ static int vrf_finish_output(struct net *net, struct sock *sk, struct sk_buff *s
}

rcu_read_unlock_bh();
err:
vrf_tx_error(skb->dev, skb);
return ret;
return -EINVAL;
}

static int vrf_output(struct net *net, struct sock *sk, struct sk_buff *skb)
Expand Down
1 change: 1 addition & 0 deletions include/linux/skbuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,7 @@ static inline struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom,
int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask);
struct sk_buff *skb_realloc_headroom(struct sk_buff *skb,
unsigned int headroom);
struct sk_buff *skb_expand_head(struct sk_buff *skb, unsigned int headroom);
struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom,
int newtailroom, gfp_t priority);
int __must_check skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg,
Expand Down
4 changes: 1 addition & 3 deletions net/ax25/ax25_ip.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,8 @@ netdev_tx_t ax25_ip_xmit(struct sk_buff *skb)
skb_pull(skb, AX25_KISS_HEADER_LEN);

if (digipeat != NULL) {
if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) {
kfree_skb(skb);
if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL)
goto put;
}

skb = ourskb;
}
Expand Down
13 changes: 3 additions & 10 deletions net/ax25/ax25_out.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,6 @@ void ax25_kick(ax25_cb *ax25)

void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
{
struct sk_buff *skbn;
unsigned char *ptr;
int headroom;

Expand All @@ -336,18 +335,12 @@ void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)

headroom = ax25_addr_size(ax25->digipeat);

if (skb_headroom(skb) < headroom) {
if ((skbn = skb_realloc_headroom(skb, headroom)) == NULL) {
if (unlikely(skb_headroom(skb) < headroom)) {
skb = skb_expand_head(skb, headroom);
if (!skb) {
printk(KERN_CRIT "AX.25: ax25_transmit_buffer - out of memory\n");
kfree_skb(skb);
return;
}

if (skb->sk != NULL)
skb_set_owner_w(skbn, skb->sk);

consume_skb(skb);
skb = skbn;
}

ptr = skb_push(skb, headroom);
Expand Down
13 changes: 3 additions & 10 deletions net/ax25/ax25_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -441,24 +441,17 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src,
ax25_address *dest, ax25_digi *digi)
{
struct sk_buff *skbn;
unsigned char *bp;
int len;

len = digi->ndigi * AX25_ADDR_LEN;

if (skb_headroom(skb) < len) {
if ((skbn = skb_realloc_headroom(skb, len)) == NULL) {
if (unlikely(skb_headroom(skb) < len)) {
skb = skb_expand_head(skb, len);
if (!skb) {
printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n");
return NULL;
}

if (skb->sk != NULL)
skb_set_owner_w(skbn, skb->sk);

consume_skb(skb);

skb = skbn;
}

bp = skb_push(skb, len);
Expand Down
27 changes: 5 additions & 22 deletions net/core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -2180,17 +2180,9 @@ static int bpf_out_neigh_v6(struct net *net, struct sk_buff *skb,
skb->tstamp = 0;

if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
struct sk_buff *skb2;

skb2 = skb_realloc_headroom(skb, hh_len);
if (unlikely(!skb2)) {
kfree_skb(skb);
skb = skb_expand_head(skb, hh_len);
if (!skb)
return -ENOMEM;
}
if (skb->sk)
skb_set_owner_w(skb2, skb->sk);
consume_skb(skb);
skb = skb2;
}

rcu_read_lock_bh();
Expand All @@ -2214,8 +2206,7 @@ static int bpf_out_neigh_v6(struct net *net, struct sk_buff *skb,
}
rcu_read_unlock_bh();
if (dst)
IP6_INC_STATS(dev_net(dst->dev),
ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
out_drop:
kfree_skb(skb);
return -ENETDOWN;
Expand Down Expand Up @@ -2287,17 +2278,9 @@ static int bpf_out_neigh_v4(struct net *net, struct sk_buff *skb,
skb->tstamp = 0;

if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
struct sk_buff *skb2;

skb2 = skb_realloc_headroom(skb, hh_len);
if (unlikely(!skb2)) {
kfree_skb(skb);
skb = skb_expand_head(skb, hh_len);
if (!skb)
return -ENOMEM;
}
if (skb->sk)
skb_set_owner_w(skb2, skb->sk);
consume_skb(skb);
skb = skb2;
}

rcu_read_lock_bh();
Expand Down
42 changes: 42 additions & 0 deletions net/core/skbuff.c
Original file line number Diff line number Diff line change
Expand Up @@ -1789,6 +1789,48 @@ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom)
}
EXPORT_SYMBOL(skb_realloc_headroom);

/**
* skb_expand_head - reallocate header of &sk_buff
* @skb: buffer to reallocate
* @headroom: needed headroom
*
* Unlike skb_realloc_headroom, this one does not allocate a new skb
* if possible; copies skb->sk to new skb as needed
* and frees original skb in case of failures.
*
* It expect increased headroom and generates warning otherwise.
*/

struct sk_buff *skb_expand_head(struct sk_buff *skb, unsigned int headroom)
{
int delta = headroom - skb_headroom(skb);

if (WARN_ONCE(delta <= 0,
"%s is expecting an increase in the headroom", __func__))
return skb;

/* pskb_expand_head() might crash, if skb is shared */
if (skb_shared(skb)) {
struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);

if (likely(nskb)) {
if (skb->sk)
skb_set_owner_w(nskb, skb->sk);
consume_skb(skb);
} else {
kfree_skb(skb);
}
skb = nskb;
}
if (skb &&
pskb_expand_head(skb, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) {
kfree_skb(skb);
skb = NULL;
}
return skb;
}
EXPORT_SYMBOL(skb_expand_head);

/**
* skb_copy_expand - copy and expand sk_buff
* @skb: buffer to copy
Expand Down
13 changes: 2 additions & 11 deletions net/ipv4/ip_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,19 +198,10 @@ static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *s
} else if (rt->rt_type == RTN_BROADCAST)
IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTBCAST, skb->len);

/* Be paranoid, rather than too clever. */
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
struct sk_buff *skb2;

skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
if (!skb2) {
kfree_skb(skb);
skb = skb_expand_head(skb, hh_len);
if (!skb)
return -ENOMEM;
}
if (skb->sk)
skb_set_owner_w(skb2, skb->sk);
consume_skb(skb);
skb = skb2;
}

if (lwtunnel_xmit_redirect(dst->lwtstate)) {
Expand Down
Loading

0 comments on commit 07e1d6b

Please sign in to comment.