Commit 07e1d6b3 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'skb_expand_head'



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: default avatarDavid S. Miller <davem@davemloft.net>
parents fa976624 a1e975e1
Loading
Loading
Loading
Loading
+7 −14
Original line number Diff line number Diff line
@@ -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);
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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,
+1 −3
Original line number Diff line number Diff line
@@ -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;
	}
+3 −10
Original line number Diff line number Diff line
@@ -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;

@@ -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);
+3 −10
Original line number Diff line number Diff line
@@ -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);
Loading