PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/kernel-2.6/323-skb9-truesize.patch

http://wl500g.googlecode.com/
Patch | 158 lines | 137 code | 21 blank | 0 comment | 0 complexity | b716b379c43aa5002394fe445bcd1c65 MD5 | raw file
Possible License(s): GPL-2.0
  1. From 3d13008e7345fa7a79d8f6438150dc15d6ba6e9d
  2. From: Eric Dumazet
  3. Date: Tue, 21 Sep 2010 08:47:45 +0000
  4. Subject: [PATCH] ip: fix truesize mismatch in ip fragmentation
  5. Special care should be taken when slow path is hit in ip_fragment() :
  6. When walking through frags, we transfert truesize ownership from skb to
  7. frags. Then if we hit a slow_path condition, we must undo this or risk
  8. uncharging frags->truesize twice, and in the end, having negative socket
  9. sk_wmem_alloc counter, or even freeing socket sooner than expected.
  10. Many thanks to Nick Bowler, who provided a very clean bug report and
  11. test program.
  12. Thanks to Jarek for reviewing my first patch and providing a V2
  13. While Nick bisection pointed to commit 2b85a34e911 (net: No more
  14. expensive sock_hold()/sock_put() on each tx), underlying bug is older
  15. (2.6.12-rc5)
  16. A side effect is to extend work done in commit b2722b1c3a893e
  17. (ip_fragment: also adjust skb->truesize for packets not owned by a
  18. socket) to ipv6 as well.
  19. Reported-and-bisected-by: Nick Bowler <nbowler@elliptictech.com>
  20. Tested-by: Nick Bowler <nbowler@elliptictech.com>
  21. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
  22. CC: Jarek Poplawski <jarkao2@gmail.com>
  23. CC: Patrick McHardy <kaber@trash.net>
  24. Signed-off-by: David S. Miller <davem@davemloft.net>
  25. ---
  26. net/ipv4/ip_output.c | 19 +++++++++++++------
  27. net/ipv6/ip6_output.c | 18 +++++++++++++-----
  28. 2 files changed, 26 insertions(+), 11 deletions(-)
  29. diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
  30. --- a/net/ipv4/ip_output.c
  31. +++ b/net/ipv4/ip_output.c
  32. @@ -457,9 +457,8 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
  33. * we can switch to copy when see the first bad fragment.
  34. */
  35. if (skb_shinfo(skb)->frag_list) {
  36. - struct sk_buff *frag;
  37. + struct sk_buff *frag, *frag2;
  38. int first_len = skb_pagelen(skb);
  39. - int truesizes = 0;
  40. if (first_len - hlen > mtu ||
  41. ((first_len - hlen) & 7) ||
  42. @@ -472,11 +471,11 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
  43. if (frag->len > mtu ||
  44. ((frag->len & 7) && frag->next) ||
  45. skb_headroom(frag) < hlen)
  46. - goto slow_path;
  47. + goto slow_path_clean;
  48. /* Partially cloned skb? */
  49. if (skb_shared(frag))
  50. - goto slow_path;
  51. + goto slow_path_clean;
  52. BUG_ON(frag->sk);
  53. if (skb->sk) {
  54. @@ -484,7 +483,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
  55. frag->sk = skb->sk;
  56. frag->destructor = sock_wfree;
  57. }
  58. - truesizes += frag->truesize;
  59. + skb->truesize -= frag->truesize;
  60. }
  61. /* Everything is OK. Generate! */
  62. @@ -494,7 +493,6 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
  63. frag = skb_shinfo(skb)->frag_list;
  64. skb_shinfo(skb)->frag_list = NULL;
  65. skb->data_len = first_len - skb_headlen(skb);
  66. - skb->truesize -= truesizes;
  67. skb->len = first_len;
  68. iph->tot_len = htons(first_len);
  69. iph->frag_off = htons(IP_MF);
  70. @@ -546,6 +544,15 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
  71. }
  72. IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
  73. return err;
  74. +
  75. +slow_path_clean:
  76. + for (frag2 = skb_shinfo(skb)->frag_list; frag2; frag2 = frag2->next) {
  77. + if (frag2 == frag)
  78. + break;
  79. + frag2->sk = NULL;
  80. + frag2->destructor = NULL;
  81. + skb->truesize += frag2->truesize;
  82. + }
  83. }
  84. slow_path:
  85. diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
  86. --- a/net/ipv6/ip6_output.c
  87. +++ b/net/ipv6/ip6_output.c
  88. @@ -614,7 +614,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
  89. if (skb_shinfo(skb)->frag_list) {
  90. int first_len = skb_pagelen(skb);
  91. - int truesizes = 0;
  92. + struct sk_buff *frag2;
  93. if (first_len - hlen > mtu ||
  94. ((first_len - hlen) & 7) ||
  95. @@ -626,19 +626,19 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
  96. if (frag->len > mtu ||
  97. ((frag->len & 7) && frag->next) ||
  98. skb_headroom(frag) < hlen)
  99. - goto slow_path;
  100. + goto slow_path_clean;
  101. /* Partially cloned skb? */
  102. if (skb_shared(frag))
  103. - goto slow_path;
  104. + goto slow_path_clean;
  105. BUG_ON(frag->sk);
  106. if (skb->sk) {
  107. sock_hold(skb->sk);
  108. frag->sk = skb->sk;
  109. frag->destructor = sock_wfree;
  110. - truesizes += frag->truesize;
  111. }
  112. + skb->truesize -= frag->truesize;
  113. }
  114. err = 0;
  115. @@ -668,7 +668,6 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
  116. first_len = skb_pagelen(skb);
  117. skb->data_len = first_len - skb_headlen(skb);
  118. - skb->truesize -= truesizes;
  119. skb->len = first_len;
  120. ipv6_hdr(skb)->payload_len = htons(first_len -
  121. sizeof(struct ipv6hdr));
  122. @@ -728,6 +727,15 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
  123. IP6_INC_STATS(ip6_dst_idev(&rt->u.dst), IPSTATS_MIB_FRAGFAILS);
  124. dst_release(&rt->u.dst);
  125. return err;
  126. +
  127. +slow_path_clean:
  128. + for (frag2 = skb_shinfo(skb)->frag_list; frag2; frag2 = frag2->next) {
  129. + if (frag2 == frag)
  130. + break;
  131. + frag2->sk = NULL;
  132. + frag2->destructor = NULL;
  133. + skb->truesize += frag2->truesize;
  134. + }
  135. }
  136. slow_path:
  137. --