| From foo@baz Fri Apr 11 08:46:36 PDT 2014 |
| From: Hannes Frederic Sowa <hannes@stressinduktion.org> |
| Date: Mon, 31 Mar 2014 20:14:10 +0200 |
| Subject: ipv6: some ipv6 statistic counters failed to disable bh |
| |
| From: Hannes Frederic Sowa <hannes@stressinduktion.org> |
| |
| [ Upstream commit 43a43b6040165f7b40b5b489fe61a4cb7f8c4980 ] |
| |
| After commit c15b1ccadb323ea ("ipv6: move DAD and addrconf_verify |
| processing to workqueue") some counters are now updated in process context |
| and thus need to disable bh before doing so, otherwise deadlocks can |
| happen on 32-bit archs. Fabio Estevam noticed this while while mounting |
| a NFS volume on an ARM board. |
| |
| As a compensation for missing this I looked after the other *_STATS_BH |
| and found three other calls which need updating: |
| |
| 1) icmp6_send: ip6_fragment -> icmpv6_send -> icmp6_send (error handling) |
| 2) ip6_push_pending_frames: rawv6_sendmsg -> rawv6_push_pending_frames -> ... |
| (only in case of icmp protocol with raw sockets in error handling) |
| 3) ping6_v6_sendmsg (error handling) |
| |
| Fixes: c15b1ccadb323ea ("ipv6: move DAD and addrconf_verify processing to workqueue") |
| Reported-by: Fabio Estevam <festevam@gmail.com> |
| Tested-by: Fabio Estevam <fabio.estevam@freescale.com> |
| Cc: Eric Dumazet <eric.dumazet@gmail.com> |
| Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv6/icmp.c | 2 +- |
| net/ipv6/ip6_output.c | 4 ++-- |
| net/ipv6/mcast.c | 11 ++++++----- |
| 3 files changed, 9 insertions(+), 8 deletions(-) |
| |
| --- a/net/ipv6/icmp.c |
| +++ b/net/ipv6/icmp.c |
| @@ -508,7 +508,7 @@ static void icmp6_send(struct sk_buff *s |
| np->tclass, NULL, &fl6, (struct rt6_info *)dst, |
| MSG_DONTWAIT, np->dontfrag); |
| if (err) { |
| - ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS); |
| + ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS); |
| ip6_flush_pending_frames(sk); |
| } else { |
| err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, |
| --- a/net/ipv6/ip6_output.c |
| +++ b/net/ipv6/ip6_output.c |
| @@ -1554,8 +1554,8 @@ int ip6_push_pending_frames(struct sock |
| if (proto == IPPROTO_ICMPV6) { |
| struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); |
| |
| - ICMP6MSGOUT_INC_STATS_BH(net, idev, icmp6_hdr(skb)->icmp6_type); |
| - ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS); |
| + ICMP6MSGOUT_INC_STATS(net, idev, icmp6_hdr(skb)->icmp6_type); |
| + ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); |
| } |
| |
| err = ip6_local_out(skb); |
| --- a/net/ipv6/mcast.c |
| +++ b/net/ipv6/mcast.c |
| @@ -1439,11 +1439,12 @@ static void mld_sendpack(struct sk_buff |
| dst_output); |
| out: |
| if (!err) { |
| - ICMP6MSGOUT_INC_STATS_BH(net, idev, ICMPV6_MLD2_REPORT); |
| - ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS); |
| - IP6_UPD_PO_STATS_BH(net, idev, IPSTATS_MIB_OUTMCAST, payload_len); |
| - } else |
| - IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_OUTDISCARDS); |
| + ICMP6MSGOUT_INC_STATS(net, idev, ICMPV6_MLD2_REPORT); |
| + ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); |
| + IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, payload_len); |
| + } else { |
| + IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS); |
| + } |
| |
| rcu_read_unlock(); |
| return; |