On Sun, 2015-12-06 at 13:03 -0800, Eric Dumazet wrote: > But then when later we promote a skb->dst to a refctounted one > (skb_dst_force(), we might make sure we abort the operation if __refcnt > == 0 ( and DST_NOCACHE is in dst->flags) >
Minimum patch would be : diff --git a/include/net/dst.h b/include/net/dst.h index 1279f9b09791..b9a3239f4296 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -322,6 +322,24 @@ static inline void skb_dst_force(struct sk_buff *skb) } } +/** + * skb_dst_force_safe - makes sure skb dst is refcounted + * @skb: buffer + * + * If dst is not yet refcounted and not destroyed, grab a ref on it. + */ +static inline void skb_dst_force_safe(struct sk_buff *skb) +{ + if (skb_dst_is_noref(skb)) { + struct dst_entry *dst = skb_dst(skb); + + if (!atomic_inc_not_zero(&dst->__refcnt)) + dst = NULL; + + skb->_skb_refdst = (unsigned long)dst; + } +} + /** * __skb_tunnel_rx - prepare skb for rx reinsert diff --git a/include/net/sock.h b/include/net/sock.h index b1d475b5db68..6367d1112c3d 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -816,7 +816,7 @@ void sk_stream_write_space(struct sock *sk); static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb) { /* dont let skb dst not refcounted, we are going to leave rcu lock */ - skb_dst_force(skb); + skb_dst_force_safe(skb); if (!sk->sk_backlog.tail) sk->sk_backlog.head = skb; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index db003438aaf5..a72688a288f5 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1493,7 +1493,7 @@ bool tcp_prequeue(struct sock *sk, struct sk_buff *skb) if (likely(sk->sk_rx_dst)) skb_dst_drop(skb); else - skb_dst_force(skb); + skb_dst_force_safe(skb); __skb_queue_tail(&tp->ucopy.prequeue, skb); tp->ucopy.memory += skb->truesize; -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html