aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/phonet/pn_dev.c59
1 files changed, 34 insertions, 25 deletions
diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c
index 6d64fda1afc9..3287f8f0b5cc 100644
--- a/net/phonet/pn_dev.c
+++ b/net/phonet/pn_dev.c
@@ -34,7 +34,7 @@
34#include <net/phonet/pn_dev.h> 34#include <net/phonet/pn_dev.h>
35 35
36struct phonet_routes { 36struct phonet_routes {
37 spinlock_t lock; 37 struct mutex lock;
38 struct net_device *table[64]; 38 struct net_device *table[64];
39}; 39};
40 40
@@ -248,17 +248,22 @@ static void phonet_route_autodel(struct net_device *dev)
248 248
249 /* Remove left-over Phonet routes */ 249 /* Remove left-over Phonet routes */
250 bitmap_zero(deleted, 64); 250 bitmap_zero(deleted, 64);
251 spin_lock_bh(&pnn->routes.lock); 251 mutex_lock(&pnn->routes.lock);
252 for (i = 0; i < 64; i++) 252 for (i = 0; i < 64; i++)
253 if (dev == pnn->routes.table[i]) { 253 if (dev == pnn->routes.table[i]) {
254 rcu_assign_pointer(pnn->routes.table[i], NULL);
254 set_bit(i, deleted); 255 set_bit(i, deleted);
255 pnn->routes.table[i] = NULL;
256 dev_put(dev);
257 } 256 }
258 spin_unlock_bh(&pnn->routes.lock); 257 mutex_unlock(&pnn->routes.lock);
258
259 if (bitmap_empty(deleted, 64))
260 return; /* short-circuit RCU */
261 synchronize_rcu();
259 for (i = find_first_bit(deleted, 64); i < 64; 262 for (i = find_first_bit(deleted, 64); i < 64;
260 i = find_next_bit(deleted, 64, i + 1)) 263 i = find_next_bit(deleted, 64, i + 1)) {
261 rtm_phonet_notify(RTM_DELROUTE, dev, i); 264 rtm_phonet_notify(RTM_DELROUTE, dev, i);
265 dev_put(dev);
266 }
262} 267}
263 268
264/* notify Phonet of device events */ 269/* notify Phonet of device events */
@@ -300,7 +305,7 @@ static int phonet_init_net(struct net *net)
300 305
301 INIT_LIST_HEAD(&pnn->pndevs.list); 306 INIT_LIST_HEAD(&pnn->pndevs.list);
302 spin_lock_init(&pnn->pndevs.lock); 307 spin_lock_init(&pnn->pndevs.lock);
303 spin_lock_init(&pnn->routes.lock); 308 mutex_init(&pnn->routes.lock);
304 net_assign_generic(net, phonet_net_id, pnn); 309 net_assign_generic(net, phonet_net_id, pnn);
305 return 0; 310 return 0;
306} 311}
@@ -361,13 +366,13 @@ int phonet_route_add(struct net_device *dev, u8 daddr)
361 int err = -EEXIST; 366 int err = -EEXIST;
362 367
363 daddr = daddr >> 2; 368 daddr = daddr >> 2;
364 spin_lock_bh(&routes->lock); 369 mutex_lock(&routes->lock);
365 if (routes->table[daddr] == NULL) { 370 if (routes->table[daddr] == NULL) {
366 routes->table[daddr] = dev; 371 rcu_assign_pointer(routes->table[daddr], dev);
367 dev_hold(dev); 372 dev_hold(dev);
368 err = 0; 373 err = 0;
369 } 374 }
370 spin_unlock_bh(&routes->lock); 375 mutex_unlock(&routes->lock);
371 return err; 376 return err;
372} 377}
373 378
@@ -375,17 +380,20 @@ int phonet_route_del(struct net_device *dev, u8 daddr)
375{ 380{
376 struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id); 381 struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id);
377 struct phonet_routes *routes = &pnn->routes; 382 struct phonet_routes *routes = &pnn->routes;
378 int err = -ENOENT;
379 383
380 daddr = daddr >> 2; 384 daddr = daddr >> 2;
381 spin_lock_bh(&routes->lock); 385 mutex_lock(&routes->lock);
382 if (dev == routes->table[daddr]) { 386 if (dev == routes->table[daddr])
383 routes->table[daddr] = NULL; 387 rcu_assign_pointer(routes->table[daddr], NULL);
384 dev_put(dev); 388 else
385 err = 0; 389 dev = NULL;
386 } 390 mutex_unlock(&routes->lock);
387 spin_unlock_bh(&routes->lock); 391
388 return err; 392 if (!dev)
393 return -ENOENT;
394 synchronize_rcu();
395 dev_put(dev);
396 return 0;
389} 397}
390 398
391struct net_device *phonet_route_get(struct net *net, u8 daddr) 399struct net_device *phonet_route_get(struct net *net, u8 daddr)
@@ -397,9 +405,9 @@ struct net_device *phonet_route_get(struct net *net, u8 daddr)
397 ASSERT_RTNL(); /* no need to hold the device */ 405 ASSERT_RTNL(); /* no need to hold the device */
398 406
399 daddr >>= 2; 407 daddr >>= 2;
400 spin_lock_bh(&routes->lock); 408 rcu_read_lock();
401 dev = routes->table[daddr]; 409 dev = rcu_dereference(routes->table[daddr]);
402 spin_unlock_bh(&routes->lock); 410 rcu_read_unlock();
403 return dev; 411 return dev;
404} 412}
405 413
@@ -409,11 +417,12 @@ struct net_device *phonet_route_output(struct net *net, u8 daddr)
409 struct phonet_routes *routes = &pnn->routes; 417 struct phonet_routes *routes = &pnn->routes;
410 struct net_device *dev; 418 struct net_device *dev;
411 419
412 spin_lock_bh(&routes->lock); 420 daddr >>= 2;
413 dev = routes->table[daddr >> 2]; 421 rcu_read_lock();
422 dev = rcu_dereference(routes->table[daddr]);
414 if (dev) 423 if (dev)
415 dev_hold(dev); 424 dev_hold(dev);
416 spin_unlock_bh(&routes->lock); 425 rcu_read_unlock();
417 426
418 if (!dev) 427 if (!dev)
419 dev = phonet_device_get(net); /* Default route */ 428 dev = phonet_device_get(net); /* Default route */