net: add a noref bit on skb dst
Use low order bit of skb->_skb_dst to tell dst is not refcounted. Change _skb_dst to _skb_refdst to make sure all uses are catched. skb_dst() returns the dst, regardless of noref bit set or not, but with a lockdep check to make sure a noref dst is not given if current user is not rcu protected. New skb_dst_set_noref() helper to set an notrefcounted dst on a skb. (with lockdep check) skb_dst_drop() drops a reference only if skb dst was refcounted. skb_dst_force() helper is used to force a refcount on dst, when skb is queued and not anymore RCU protected. Use skb_dst_force() in __sk_add_backlog(), __dev_xmit_skb() if !IFF_XMIT_DST_RELEASE or skb enqueued on qdisc queue, in sock_queue_rcv_skb(), in __nf_queue(). Use skb_dst_force() in dev_requeue_skb(). Note: dst_use_noref() still dirties dst, we might transform it later to do one dirtying per jiffies. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
ebda37c27d
commit
7fee226ad2
@ -168,6 +168,12 @@ static inline void dst_use(struct dst_entry *dst, unsigned long time)
|
||||
dst->lastuse = time;
|
||||
}
|
||||
|
||||
static inline void dst_use_noref(struct dst_entry *dst, unsigned long time)
|
||||
{
|
||||
dst->__use++;
|
||||
dst->lastuse = time;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct dst_entry * dst_clone(struct dst_entry * dst)
|
||||
{
|
||||
@ -177,11 +183,47 @@ struct dst_entry * dst_clone(struct dst_entry * dst)
|
||||
}
|
||||
|
||||
extern void dst_release(struct dst_entry *dst);
|
||||
|
||||
static inline void refdst_drop(unsigned long refdst)
|
||||
{
|
||||
if (!(refdst & SKB_DST_NOREF))
|
||||
dst_release((struct dst_entry *)(refdst & SKB_DST_PTRMASK));
|
||||
}
|
||||
|
||||
/**
|
||||
* skb_dst_drop - drops skb dst
|
||||
* @skb: buffer
|
||||
*
|
||||
* Drops dst reference count if a reference was taken.
|
||||
*/
|
||||
static inline void skb_dst_drop(struct sk_buff *skb)
|
||||
{
|
||||
if (skb->_skb_dst)
|
||||
dst_release(skb_dst(skb));
|
||||
skb->_skb_dst = 0UL;
|
||||
if (skb->_skb_refdst) {
|
||||
refdst_drop(skb->_skb_refdst);
|
||||
skb->_skb_refdst = 0UL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void skb_dst_copy(struct sk_buff *nskb, const struct sk_buff *oskb)
|
||||
{
|
||||
nskb->_skb_refdst = oskb->_skb_refdst;
|
||||
if (!(nskb->_skb_refdst & SKB_DST_NOREF))
|
||||
dst_clone(skb_dst(nskb));
|
||||
}
|
||||
|
||||
/**
|
||||
* skb_dst_force - makes sure skb dst is refcounted
|
||||
* @skb: buffer
|
||||
*
|
||||
* If dst is not yet refcounted, let's do it
|
||||
*/
|
||||
static inline void skb_dst_force(struct sk_buff *skb)
|
||||
{
|
||||
if (skb_dst_is_noref(skb)) {
|
||||
WARN_ON(!rcu_read_lock_held());
|
||||
skb->_skb_refdst &= ~SKB_DST_NOREF;
|
||||
dst_clone(skb_dst(skb));
|
||||
}
|
||||
}
|
||||
|
||||
/* Children define the path of the packet through the
|
||||
|
Reference in New Issue
Block a user