|
@@ -149,11 +149,19 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
|
|
* is freed by GSO engine
|
|
* is freed by GSO engine
|
|
*/
|
|
*/
|
|
if (copy_destructor) {
|
|
if (copy_destructor) {
|
|
|
|
+ int delta;
|
|
|
|
+
|
|
swap(gso_skb->sk, skb->sk);
|
|
swap(gso_skb->sk, skb->sk);
|
|
swap(gso_skb->destructor, skb->destructor);
|
|
swap(gso_skb->destructor, skb->destructor);
|
|
sum_truesize += skb->truesize;
|
|
sum_truesize += skb->truesize;
|
|
- refcount_add(sum_truesize - gso_skb->truesize,
|
|
|
|
- &skb->sk->sk_wmem_alloc);
|
|
|
|
|
|
+ delta = sum_truesize - gso_skb->truesize;
|
|
|
|
+ /* In some pathological cases, delta can be negative.
|
|
|
|
+ * We need to either use refcount_add() or refcount_sub_and_test()
|
|
|
|
+ */
|
|
|
|
+ if (likely(delta >= 0))
|
|
|
|
+ refcount_add(delta, &skb->sk->sk_wmem_alloc);
|
|
|
|
+ else
|
|
|
|
+ WARN_ON_ONCE(refcount_sub_and_test(-delta, &skb->sk->sk_wmem_alloc));
|
|
}
|
|
}
|
|
|
|
|
|
delta = htonl(oldlen + (skb_tail_pointer(skb) -
|
|
delta = htonl(oldlen + (skb_tail_pointer(skb) -
|