Skip to content

Commit

Permalink
net: Allow skb_recycle_check to be done in stages
Browse files Browse the repository at this point in the history
skb_recycle_check resets the skb if it's eligible for recycling.
However, there are times when a driver might want to optionally
manipulate the skb data with the skb before resetting the skb,
but after it has determined eligibility.  We do this by splitting the
eligibility check from the skb reset, creating two inline functions to
accomplish that task.

Signed-off-by: Andy Fleming <afleming@freescale.com>
Acked-by: David Daney <david.daney@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Andy Fleming authored and David S. Miller committed Oct 19, 2011
1 parent 1e5c22c commit 3d153a7
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 25 deletions.
21 changes: 21 additions & 0 deletions include/linux/skbuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ static inline struct sk_buff *alloc_skb_fclone(unsigned int size,
return __alloc_skb(size, priority, 1, NUMA_NO_NODE);
}

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

extern struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src);
Expand Down Expand Up @@ -2484,5 +2485,25 @@ static inline void skb_checksum_none_assert(struct sk_buff *skb)

bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off);

static inline bool skb_is_recycleable(struct sk_buff *skb, int skb_size)
{
if (irqs_disabled())
return false;

if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY)
return false;

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

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

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

return true;
}
#endif /* __KERNEL__ */
#endif /* _LINUX_SKBUFF_H */
51 changes: 26 additions & 25 deletions net/core/skbuff.c
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,30 @@ void consume_skb(struct sk_buff *skb)
}
EXPORT_SYMBOL(consume_skb);

/**
* skb_recycle - clean up an skb for reuse
* @skb: buffer
*
* Recycles the skb to be reused as a receive buffer. This
* function does any necessary reference count dropping, and
* cleans up the skbuff as if it just came from __alloc_skb().
*/
void skb_recycle(struct sk_buff *skb)
{
struct skb_shared_info *shinfo;

skb_release_head_state(skb);

shinfo = skb_shinfo(skb);
memset(shinfo, 0, offsetof(struct skb_shared_info, dataref));
atomic_set(&shinfo->dataref, 1);

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

/**
* skb_recycle_check - check if skb can be reused for receive
* @skb: buffer
Expand All @@ -498,33 +522,10 @@ EXPORT_SYMBOL(consume_skb);
*/
bool skb_recycle_check(struct sk_buff *skb, int skb_size)
{
struct skb_shared_info *shinfo;

if (irqs_disabled())
return false;

if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY)
return false;

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

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

if (skb_shared(skb) || skb_cloned(skb))
if (!skb_is_recycleable(skb, skb_size))
return false;

skb_release_head_state(skb);

shinfo = skb_shinfo(skb);
memset(shinfo, 0, offsetof(struct skb_shared_info, dataref));
atomic_set(&shinfo->dataref, 1);

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

return true;
}
Expand Down

0 comments on commit 3d153a7

Please sign in to comment.