aboutsummaryrefslogtreecommitdiffstats
path: root/net/core/netpoll.c
diff options
context:
space:
mode:
authorNeil Horman <nhorman@tuxdriver.com>2013-02-05 03:05:43 -0500
committerDavid S. Miller <davem@davemloft.net>2013-02-06 15:45:03 -0500
commitca99ca14c95ae49fb4c9cd3abf5f84d11a7e8a61 (patch)
tree889b44125a7d9471424fc58537ba7e19aec20a2c /net/core/netpoll.c
parentf458c647ea5cdd956f8055a4178072be460559f1 (diff)
netpoll: protect napi_poll and poll_controller during dev_[open|close]
Ivan Vercera was recently backporting commit 9c13cb8bb477a83b9a3c9e5a5478a4e21294a760 to a RHEL kernel, and I noticed that, while this patch protects the tg3 driver from having its ndo_poll_controller routine called during device initalization, it does nothing for the driver during shutdown. I.e. it would be entirely possible to have the ndo_poll_controller method (or subsequently the ndo_poll) routine called for a driver in the netpoll path on CPU A while in parallel on CPU B, the ndo_close or ndo_open routine could be called. Given that the two latter routines tend to initizlize and free many data structures that the former two rely on, the result can easily be data corruption or various other crashes. Furthermore, it seems that this is potentially a problem with all net drivers that support netpoll, and so this should ideally be fixed in a common path. As Ben H Pointed out to me, we can't preform dev_open/dev_close in atomic context, so I've come up with this solution. We can use a mutex to sleep in open/close paths and just do a mutex_trylock in the napi poll path and abandon the poll attempt if we're locked, as we'll just retry the poll on the next send anyway. I've tested this here by flooding netconsole with messages on a system whos nic driver I modfied to periodically return NETDEV_TX_BUSY, so that the netpoll tx workqueue would be forced to send frames and poll the device. While this was going on I rapidly ifdown/up'ed the interface and watched for any problems. I've not found any. Signed-off-by: Neil Horman <nhorman@tuxdriver.com> CC: Ivan Vecera <ivecera@redhat.com> CC: "David S. Miller" <davem@davemloft.net> CC: Ben Hutchings <bhutchings@solarflare.com> CC: Francois Romieu <romieu@fr.zoreil.com> CC: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/netpoll.c')
-rw-r--r--net/core/netpoll.c40
1 files changed, 40 insertions, 0 deletions
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 331ccb90f915..edcd9ad95304 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -47,6 +47,8 @@ static struct sk_buff_head skb_pool;
47 47
48static atomic_t trapped; 48static atomic_t trapped;
49 49
50static struct srcu_struct netpoll_srcu;
51
50#define USEC_PER_POLL 50 52#define USEC_PER_POLL 50
51#define NETPOLL_RX_ENABLED 1 53#define NETPOLL_RX_ENABLED 1
52#define NETPOLL_RX_DROP 2 54#define NETPOLL_RX_DROP 2
@@ -199,6 +201,13 @@ static void netpoll_poll_dev(struct net_device *dev)
199 const struct net_device_ops *ops; 201 const struct net_device_ops *ops;
200 struct netpoll_info *ni = rcu_dereference_bh(dev->npinfo); 202 struct netpoll_info *ni = rcu_dereference_bh(dev->npinfo);
201 203
204 /* Don't do any rx activity if the dev_lock mutex is held
205 * the dev_open/close paths use this to block netpoll activity
206 * while changing device state
207 */
208 if (!mutex_trylock(&dev->npinfo->dev_lock))
209 return;
210
202 if (!dev || !netif_running(dev)) 211 if (!dev || !netif_running(dev))
203 return; 212 return;
204 213
@@ -211,6 +220,8 @@ static void netpoll_poll_dev(struct net_device *dev)
211 220
212 poll_napi(dev); 221 poll_napi(dev);
213 222
223 mutex_unlock(&dev->npinfo->dev_lock);
224
214 if (dev->flags & IFF_SLAVE) { 225 if (dev->flags & IFF_SLAVE) {
215 if (ni) { 226 if (ni) {
216 struct net_device *bond_dev; 227 struct net_device *bond_dev;
@@ -231,6 +242,31 @@ static void netpoll_poll_dev(struct net_device *dev)
231 zap_completion_queue(); 242 zap_completion_queue();
232} 243}
233 244
245int netpoll_rx_disable(struct net_device *dev)
246{
247 struct netpoll_info *ni;
248 int idx;
249 might_sleep();
250 idx = srcu_read_lock(&netpoll_srcu);
251 ni = srcu_dereference(dev->npinfo, &netpoll_srcu);
252 if (ni)
253 mutex_lock(&ni->dev_lock);
254 srcu_read_unlock(&netpoll_srcu, idx);
255 return 0;
256}
257EXPORT_SYMBOL(netpoll_rx_disable);
258
259void netpoll_rx_enable(struct net_device *dev)
260{
261 struct netpoll_info *ni;
262 rcu_read_lock();
263 ni = rcu_dereference(dev->npinfo);
264 if (ni)
265 mutex_unlock(&ni->dev_lock);
266 rcu_read_unlock();
267}
268EXPORT_SYMBOL(netpoll_rx_enable);
269
234static void refill_skbs(void) 270static void refill_skbs(void)
235{ 271{
236 struct sk_buff *skb; 272 struct sk_buff *skb;
@@ -1004,6 +1040,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp)
1004 INIT_LIST_HEAD(&npinfo->rx_np); 1040 INIT_LIST_HEAD(&npinfo->rx_np);
1005 1041
1006 spin_lock_init(&npinfo->rx_lock); 1042 spin_lock_init(&npinfo->rx_lock);
1043 mutex_init(&npinfo->dev_lock);
1007 skb_queue_head_init(&npinfo->neigh_tx); 1044 skb_queue_head_init(&npinfo->neigh_tx);
1008 skb_queue_head_init(&npinfo->txq); 1045 skb_queue_head_init(&npinfo->txq);
1009 INIT_DELAYED_WORK(&npinfo->tx_work, queue_process); 1046 INIT_DELAYED_WORK(&npinfo->tx_work, queue_process);
@@ -1169,6 +1206,7 @@ EXPORT_SYMBOL(netpoll_setup);
1169static int __init netpoll_init(void) 1206static int __init netpoll_init(void)
1170{ 1207{
1171 skb_queue_head_init(&skb_pool); 1208 skb_queue_head_init(&skb_pool);
1209 init_srcu_struct(&netpoll_srcu);
1172 return 0; 1210 return 0;
1173} 1211}
1174core_initcall(netpoll_init); 1212core_initcall(netpoll_init);
@@ -1208,6 +1246,8 @@ void __netpoll_cleanup(struct netpoll *np)
1208 spin_unlock_irqrestore(&npinfo->rx_lock, flags); 1246 spin_unlock_irqrestore(&npinfo->rx_lock, flags);
1209 } 1247 }
1210 1248
1249 synchronize_srcu(&netpoll_srcu);
1250
1211 if (atomic_dec_and_test(&npinfo->refcnt)) { 1251 if (atomic_dec_and_test(&npinfo->refcnt)) {
1212 const struct net_device_ops *ops; 1252 const struct net_device_ops *ops;
1213 1253