Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 04a4bb55 authored by Lennert Buytenhek's avatar Lennert Buytenhek Committed by David S. Miller
Browse files

net: add skb_recycle_check() to enable netdriver skb recycling



This patch adds skb_recycle_check(), which can be used by a network
driver after transmitting an skb to check whether this skb can be
recycled as a receive buffer.

skb_recycle_check() checks that the skb is not shared or cloned, and
that it is linear and its head portion large enough (as determined by
the driver) to be recycled as a receive buffer.  If these conditions
are met, it does any necessary reference count dropping and cleans
up the skbuff as if it just came from __alloc_skb().

Signed-off-by: default avatarLennert Buytenhek <buytenh@marvell.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 788df732
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -383,6 +383,8 @@ static inline struct sk_buff *alloc_skb_fclone(unsigned int size,
	return __alloc_skb(size, priority, 1, -1);
}

extern int skb_recycle_check(struct sk_buff *skb, int skb_size);

extern struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src);
extern struct sk_buff *skb_clone(struct sk_buff *skb,
				 gfp_t priority);
+39 −2
Original line number Diff line number Diff line
@@ -363,8 +363,7 @@ static void kfree_skbmem(struct sk_buff *skb)
	}
}

/* Free everything but the sk_buff shell. */
static void skb_release_all(struct sk_buff *skb)
static void skb_release_head_state(struct sk_buff *skb)
{
	dst_release(skb->dst);
#ifdef CONFIG_XFRM
@@ -388,6 +387,12 @@ static void skb_release_all(struct sk_buff *skb)
	skb->tc_verd = 0;
#endif
#endif
}

/* Free everything but the sk_buff shell. */
static void skb_release_all(struct sk_buff *skb)
{
	skb_release_head_state(skb);
	skb_release_data(skb);
}

@@ -424,6 +429,38 @@ void kfree_skb(struct sk_buff *skb)
	__kfree_skb(skb);
}

int skb_recycle_check(struct sk_buff *skb, int skb_size)
{
	struct skb_shared_info *shinfo;

	if (skb_is_nonlinear(skb) || skb->fclone != SKB_FCLONE_UNAVAILABLE)
		return 0;

	skb_size = SKB_DATA_ALIGN(skb_size + NET_SKB_PAD);
	if (skb_end_pointer(skb) - skb->head < skb_size)
		return 0;

	if (skb_shared(skb) || skb_cloned(skb))
		return 0;

	skb_release_head_state(skb);
	shinfo = skb_shinfo(skb);
	atomic_set(&shinfo->dataref, 1);
	shinfo->nr_frags = 0;
	shinfo->gso_size = 0;
	shinfo->gso_segs = 0;
	shinfo->gso_type = 0;
	shinfo->ip6_frag_id = 0;
	shinfo->frag_list = NULL;

	memset(skb, 0, offsetof(struct sk_buff, tail));
	skb_reset_tail_pointer(skb);
	skb->data = skb->head + NET_SKB_PAD;

	return 1;
}
EXPORT_SYMBOL(skb_recycle_check);

static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
{
	new->tstamp		= old->tstamp;