aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet
diff options
context:
space:
mode:
authorMitsuhiro Kimura <mitsuhiro.kimura.kc@renesas.com>2014-11-27 20:04:15 -0500
committerDavid S. Miller <davem@davemloft.net>2014-11-30 00:16:54 -0500
commit7fa2955ff70ce4532f144d26b8a087095f9c9ffc (patch)
tree53e7d463d553014241de7aeb0d1965e6e71b35e0 /drivers/net/ethernet
parent28603d13997e2ef47f18589cc9a44553aad49c86 (diff)
sh_eth: Fix sleeping function called from invalid context
This resolves the following bug which can be reproduced by building the kernel with CONFIG_DEBUG_ATOMIC_SLEEP=y and reading network statistics while the network interface is down. e.g.: ifconfig eth0 down cat /sys/class/net/eth0/statistics/tx_errors ---- [ 1238.161349] BUG: sleeping function called from invalid context at drivers/base/power/runtime.c:952 [ 1238.188279] in_atomic(): 1, irqs_disabled(): 0, pid: 1388, name: cat [ 1238.207425] CPU: 0 PID: 1388 Comm: cat Not tainted 3.10.31-ltsi-00046-gefa0b46 #1087 [ 1238.230737] Backtrace: [ 1238.238123] [<c0012e64>] (dump_backtrace+0x0/0x10c) from [<c0013000>] (show_stack+0x18/0x1c) [ 1238.263499] r6:000003b8 r5:c06160c0 r4:c0669e00 r3:00404000 [ 1238.280583] [<c0012fe8>] (show_stack+0x0/0x1c) from [<c04515a4>] (dump_stack+0x20/0x28) [ 1238.304631] [<c0451584>] (dump_stack+0x0/0x28) from [<c004970c>] (__might_sleep+0xf8/0x118) [ 1238.329734] [<c0049614>] (__might_sleep+0x0/0x118) from [<c02465ac>] (__pm_runtime_resume+0x38/0x90) [ 1238.357170] r7:d616f000 r6:c049c458 r5:00000004 r4:d6a17210 [ 1238.374251] [<c0246574>] (__pm_runtime_resume+0x0/0x90) from [<c029b1c4>] (sh_eth_get_stats+0x44/0x280) [ 1238.402468] r7:d616f000 r6:c049c458 r5:d5c21000 r4:d5c21000 [ 1238.419552] [<c029b180>] (sh_eth_get_stats+0x0/0x280) from [<c03ae39c>] (dev_get_stats+0x54/0x88) [ 1238.446204] r5:d5c21000 r4:d5ed7e08 [ 1238.456980] [<c03ae348>] (dev_get_stats+0x0/0x88) from [<c03c677c>] (netstat_show.isra.15+0x54/0x9c) [ 1238.484413] r6:d5c21000 r5:d5c21238 r4:00000028 r3:00000001 [ 1238.501495] [<c03c6728>] (netstat_show.isra.15+0x0/0x9c) from [<c03c69b8>] (show_tx_errors+0x18/0x1c) [ 1238.529196] r7:d5f945d8 r6:d5f945c0 r5:c049716c r4:c0650e7c [ 1238.546279] [<c03c69a0>] (show_tx_errors+0x0/0x1c) from [<c023963c>] (dev_attr_show+0x24/0x50) [ 1238.572157] [<c0239618>] (dev_attr_show+0x0/0x50) from [<c010c148>] (sysfs_read_file+0xb0/0x140) [ 1238.598554] r5:c049716c r4:d5c21240 [ 1238.609326] [<c010c098>] (sysfs_read_file+0x0/0x140) from [<c00b9ee4>] (vfs_read+0xb0/0x13c) [ 1238.634679] [<c00b9e34>] (vfs_read+0x0/0x13c) from [<c00ba0ac>] (SyS_read+0x44/0x74) [ 1238.657944] r8:bef45bf0 r7:00000000 r6:d6ac0600 r5:00000000 r4:00000000 [ 1238.678172] [<c00ba068>] (SyS_read+0x0/0x74) from [<c000eec0>] (ret_fast_syscall+0x0/0x30) ---- Signed-off-by: Mitsuhiro Kimura <mitsuhiro.kimura.kc@renesas.com> Signed-off-by: Yoshihiro Kaneko <ykaneko0929@gmail.com> Signed-off-by: Simon Horman <horms+renesas@verge.net.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c64
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.h1
2 files changed, 36 insertions, 29 deletions
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index f9e30b87ecd5..b5db6b3f939f 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -2036,6 +2036,8 @@ static int sh_eth_open(struct net_device *ndev)
2036 if (ret) 2036 if (ret)
2037 goto out_free_irq; 2037 goto out_free_irq;
2038 2038
2039 mdp->is_opened = 1;
2040
2039 return ret; 2041 return ret;
2040 2042
2041out_free_irq: 2043out_free_irq:
@@ -2125,6 +2127,36 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
2125 return NETDEV_TX_OK; 2127 return NETDEV_TX_OK;
2126} 2128}
2127 2129
2130static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
2131{
2132 struct sh_eth_private *mdp = netdev_priv(ndev);
2133
2134 if (sh_eth_is_rz_fast_ether(mdp))
2135 return &ndev->stats;
2136
2137 if (!mdp->is_opened)
2138 return &ndev->stats;
2139
2140 ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR);
2141 sh_eth_write(ndev, 0, TROCR); /* (write clear) */
2142 ndev->stats.collisions += sh_eth_read(ndev, CDCR);
2143 sh_eth_write(ndev, 0, CDCR); /* (write clear) */
2144 ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR);
2145 sh_eth_write(ndev, 0, LCCR); /* (write clear) */
2146
2147 if (sh_eth_is_gether(mdp)) {
2148 ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR);
2149 sh_eth_write(ndev, 0, CERCR); /* (write clear) */
2150 ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR);
2151 sh_eth_write(ndev, 0, CEECR); /* (write clear) */
2152 } else {
2153 ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR);
2154 sh_eth_write(ndev, 0, CNDCR); /* (write clear) */
2155 }
2156
2157 return &ndev->stats;
2158}
2159
2128/* device close function */ 2160/* device close function */
2129static int sh_eth_close(struct net_device *ndev) 2161static int sh_eth_close(struct net_device *ndev)
2130{ 2162{
@@ -2139,6 +2171,7 @@ static int sh_eth_close(struct net_device *ndev)
2139 sh_eth_write(ndev, 0, EDTRR); 2171 sh_eth_write(ndev, 0, EDTRR);
2140 sh_eth_write(ndev, 0, EDRRR); 2172 sh_eth_write(ndev, 0, EDRRR);
2141 2173
2174 sh_eth_get_stats(ndev);
2142 /* PHY Disconnect */ 2175 /* PHY Disconnect */
2143 if (mdp->phydev) { 2176 if (mdp->phydev) {
2144 phy_stop(mdp->phydev); 2177 phy_stop(mdp->phydev);
@@ -2157,36 +2190,9 @@ static int sh_eth_close(struct net_device *ndev)
2157 2190
2158 pm_runtime_put_sync(&mdp->pdev->dev); 2191 pm_runtime_put_sync(&mdp->pdev->dev);
2159 2192
2160 return 0; 2193 mdp->is_opened = 0;
2161}
2162
2163static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
2164{
2165 struct sh_eth_private *mdp = netdev_priv(ndev);
2166
2167 if (sh_eth_is_rz_fast_ether(mdp))
2168 return &ndev->stats;
2169
2170 pm_runtime_get_sync(&mdp->pdev->dev);
2171
2172 ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR);
2173 sh_eth_write(ndev, 0, TROCR); /* (write clear) */
2174 ndev->stats.collisions += sh_eth_read(ndev, CDCR);
2175 sh_eth_write(ndev, 0, CDCR); /* (write clear) */
2176 ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR);
2177 sh_eth_write(ndev, 0, LCCR); /* (write clear) */
2178 if (sh_eth_is_gether(mdp)) {
2179 ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR);
2180 sh_eth_write(ndev, 0, CERCR); /* (write clear) */
2181 ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR);
2182 sh_eth_write(ndev, 0, CEECR); /* (write clear) */
2183 } else {
2184 ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR);
2185 sh_eth_write(ndev, 0, CNDCR); /* (write clear) */
2186 }
2187 pm_runtime_put_sync(&mdp->pdev->dev);
2188 2194
2189 return &ndev->stats; 2195 return 0;
2190} 2196}
2191 2197
2192/* ioctl to device function */ 2198/* ioctl to device function */
diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index 9fa93321d512..22301bf9c21d 100644
--- a/drivers/net/ethernet/renesas/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -522,6 +522,7 @@ struct sh_eth_private {
522 522
523 unsigned no_ether_link:1; 523 unsigned no_ether_link:1;
524 unsigned ether_link_active_low:1; 524 unsigned ether_link_active_low:1;
525 unsigned is_opened:1;
525}; 526};
526 527
527static inline void sh_eth_soft_swap(char *src, int len) 528static inline void sh_eth_soft_swap(char *src, int len)