aboutsummaryrefslogtreecommitdiffstats
path: root/net/phonet/pn_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/phonet/pn_dev.c')
-rw-r--r--net/phonet/pn_dev.c219
1 files changed, 181 insertions, 38 deletions
diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c
index 5f42f30dd168..bc4a33bf2d3d 100644
--- a/net/phonet/pn_dev.c
+++ b/net/phonet/pn_dev.c
@@ -33,11 +33,17 @@
33#include <net/netns/generic.h> 33#include <net/netns/generic.h>
34#include <net/phonet/pn_dev.h> 34#include <net/phonet/pn_dev.h>
35 35
36struct phonet_routes {
37 struct mutex lock;
38 struct net_device *table[64];
39};
40
36struct phonet_net { 41struct phonet_net {
37 struct phonet_device_list pndevs; 42 struct phonet_device_list pndevs;
43 struct phonet_routes routes;
38}; 44};
39 45
40int phonet_net_id; 46int phonet_net_id __read_mostly;
41 47
42struct phonet_device_list *phonet_device_list(struct net *net) 48struct phonet_device_list *phonet_device_list(struct net *net)
43{ 49{
@@ -55,7 +61,8 @@ static struct phonet_device *__phonet_device_alloc(struct net_device *dev)
55 pnd->netdev = dev; 61 pnd->netdev = dev;
56 bitmap_zero(pnd->addrs, 64); 62 bitmap_zero(pnd->addrs, 64);
57 63
58 list_add(&pnd->list, &pndevs->list); 64 BUG_ON(!mutex_is_locked(&pndevs->lock));
65 list_add_rcu(&pnd->list, &pndevs->list);
59 return pnd; 66 return pnd;
60} 67}
61 68
@@ -64,6 +71,7 @@ static struct phonet_device *__phonet_get(struct net_device *dev)
64 struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 71 struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
65 struct phonet_device *pnd; 72 struct phonet_device *pnd;
66 73
74 BUG_ON(!mutex_is_locked(&pndevs->lock));
67 list_for_each_entry(pnd, &pndevs->list, list) { 75 list_for_each_entry(pnd, &pndevs->list, list) {
68 if (pnd->netdev == dev) 76 if (pnd->netdev == dev)
69 return pnd; 77 return pnd;
@@ -71,6 +79,18 @@ static struct phonet_device *__phonet_get(struct net_device *dev)
71 return NULL; 79 return NULL;
72} 80}
73 81
82static struct phonet_device *__phonet_get_rcu(struct net_device *dev)
83{
84 struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
85 struct phonet_device *pnd;
86
87 list_for_each_entry_rcu(pnd, &pndevs->list, list) {
88 if (pnd->netdev == dev)
89 return pnd;
90 }
91 return NULL;
92}
93
74static void phonet_device_destroy(struct net_device *dev) 94static void phonet_device_destroy(struct net_device *dev)
75{ 95{
76 struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 96 struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
@@ -78,11 +98,11 @@ static void phonet_device_destroy(struct net_device *dev)
78 98
79 ASSERT_RTNL(); 99 ASSERT_RTNL();
80 100
81 spin_lock_bh(&pndevs->lock); 101 mutex_lock(&pndevs->lock);
82 pnd = __phonet_get(dev); 102 pnd = __phonet_get(dev);
83 if (pnd) 103 if (pnd)
84 list_del(&pnd->list); 104 list_del_rcu(&pnd->list);
85 spin_unlock_bh(&pndevs->lock); 105 mutex_unlock(&pndevs->lock);
86 106
87 if (pnd) { 107 if (pnd) {
88 u8 addr; 108 u8 addr;
@@ -100,8 +120,8 @@ struct net_device *phonet_device_get(struct net *net)
100 struct phonet_device *pnd; 120 struct phonet_device *pnd;
101 struct net_device *dev = NULL; 121 struct net_device *dev = NULL;
102 122
103 spin_lock_bh(&pndevs->lock); 123 rcu_read_lock();
104 list_for_each_entry(pnd, &pndevs->list, list) { 124 list_for_each_entry_rcu(pnd, &pndevs->list, list) {
105 dev = pnd->netdev; 125 dev = pnd->netdev;
106 BUG_ON(!dev); 126 BUG_ON(!dev);
107 127
@@ -112,7 +132,7 @@ struct net_device *phonet_device_get(struct net *net)
112 } 132 }
113 if (dev) 133 if (dev)
114 dev_hold(dev); 134 dev_hold(dev);
115 spin_unlock_bh(&pndevs->lock); 135 rcu_read_unlock();
116 return dev; 136 return dev;
117} 137}
118 138
@@ -122,7 +142,7 @@ int phonet_address_add(struct net_device *dev, u8 addr)
122 struct phonet_device *pnd; 142 struct phonet_device *pnd;
123 int err = 0; 143 int err = 0;
124 144
125 spin_lock_bh(&pndevs->lock); 145 mutex_lock(&pndevs->lock);
126 /* Find or create Phonet-specific device data */ 146 /* Find or create Phonet-specific device data */
127 pnd = __phonet_get(dev); 147 pnd = __phonet_get(dev);
128 if (pnd == NULL) 148 if (pnd == NULL)
@@ -131,7 +151,7 @@ int phonet_address_add(struct net_device *dev, u8 addr)
131 err = -ENOMEM; 151 err = -ENOMEM;
132 else if (test_and_set_bit(addr >> 2, pnd->addrs)) 152 else if (test_and_set_bit(addr >> 2, pnd->addrs))
133 err = -EEXIST; 153 err = -EEXIST;
134 spin_unlock_bh(&pndevs->lock); 154 mutex_unlock(&pndevs->lock);
135 return err; 155 return err;
136} 156}
137 157
@@ -141,36 +161,56 @@ int phonet_address_del(struct net_device *dev, u8 addr)
141 struct phonet_device *pnd; 161 struct phonet_device *pnd;
142 int err = 0; 162 int err = 0;
143 163
144 spin_lock_bh(&pndevs->lock); 164 mutex_lock(&pndevs->lock);
145 pnd = __phonet_get(dev); 165 pnd = __phonet_get(dev);
146 if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) 166 if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) {
147 err = -EADDRNOTAVAIL; 167 err = -EADDRNOTAVAIL;
148 else if (bitmap_empty(pnd->addrs, 64)) { 168 pnd = NULL;
149 list_del(&pnd->list); 169 } else if (bitmap_empty(pnd->addrs, 64))
170 list_del_rcu(&pnd->list);
171 else
172 pnd = NULL;
173 mutex_unlock(&pndevs->lock);
174
175 if (pnd) {
176 synchronize_rcu();
150 kfree(pnd); 177 kfree(pnd);
151 } 178 }
152 spin_unlock_bh(&pndevs->lock);
153 return err; 179 return err;
154} 180}
155 181
156/* Gets a source address toward a destination, through a interface. */ 182/* Gets a source address toward a destination, through a interface. */
157u8 phonet_address_get(struct net_device *dev, u8 addr) 183u8 phonet_address_get(struct net_device *dev, u8 daddr)
158{ 184{
159 struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
160 struct phonet_device *pnd; 185 struct phonet_device *pnd;
186 u8 saddr;
161 187
162 spin_lock_bh(&pndevs->lock); 188 rcu_read_lock();
163 pnd = __phonet_get(dev); 189 pnd = __phonet_get_rcu(dev);
164 if (pnd) { 190 if (pnd) {
165 BUG_ON(bitmap_empty(pnd->addrs, 64)); 191 BUG_ON(bitmap_empty(pnd->addrs, 64));
166 192
167 /* Use same source address as destination, if possible */ 193 /* Use same source address as destination, if possible */
168 if (!test_bit(addr >> 2, pnd->addrs)) 194 if (test_bit(daddr >> 2, pnd->addrs))
169 addr = find_first_bit(pnd->addrs, 64) << 2; 195 saddr = daddr;
196 else
197 saddr = find_first_bit(pnd->addrs, 64) << 2;
170 } else 198 } else
171 addr = PN_NO_ADDR; 199 saddr = PN_NO_ADDR;
172 spin_unlock_bh(&pndevs->lock); 200 rcu_read_unlock();
173 return addr; 201
202 if (saddr == PN_NO_ADDR) {
203 /* Fallback to another device */
204 struct net_device *def_dev;
205
206 def_dev = phonet_device_get(dev_net(dev));
207 if (def_dev) {
208 if (def_dev != dev)
209 saddr = phonet_address_get(def_dev, daddr);
210 dev_put(def_dev);
211 }
212 }
213 return saddr;
174} 214}
175 215
176int phonet_address_lookup(struct net *net, u8 addr) 216int phonet_address_lookup(struct net *net, u8 addr)
@@ -179,8 +219,8 @@ int phonet_address_lookup(struct net *net, u8 addr)
179 struct phonet_device *pnd; 219 struct phonet_device *pnd;
180 int err = -EADDRNOTAVAIL; 220 int err = -EADDRNOTAVAIL;
181 221
182 spin_lock_bh(&pndevs->lock); 222 rcu_read_lock();
183 list_for_each_entry(pnd, &pndevs->list, list) { 223 list_for_each_entry_rcu(pnd, &pndevs->list, list) {
184 /* Don't allow unregistering devices! */ 224 /* Don't allow unregistering devices! */
185 if ((pnd->netdev->reg_state != NETREG_REGISTERED) || 225 if ((pnd->netdev->reg_state != NETREG_REGISTERED) ||
186 ((pnd->netdev->flags & IFF_UP)) != IFF_UP) 226 ((pnd->netdev->flags & IFF_UP)) != IFF_UP)
@@ -192,7 +232,7 @@ int phonet_address_lookup(struct net *net, u8 addr)
192 } 232 }
193 } 233 }
194found: 234found:
195 spin_unlock_bh(&pndevs->lock); 235 rcu_read_unlock();
196 return err; 236 return err;
197} 237}
198 238
@@ -219,6 +259,32 @@ static int phonet_device_autoconf(struct net_device *dev)
219 return 0; 259 return 0;
220} 260}
221 261
262static void phonet_route_autodel(struct net_device *dev)
263{
264 struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id);
265 unsigned i;
266 DECLARE_BITMAP(deleted, 64);
267
268 /* Remove left-over Phonet routes */
269 bitmap_zero(deleted, 64);
270 mutex_lock(&pnn->routes.lock);
271 for (i = 0; i < 64; i++)
272 if (dev == pnn->routes.table[i]) {
273 rcu_assign_pointer(pnn->routes.table[i], NULL);
274 set_bit(i, deleted);
275 }
276 mutex_unlock(&pnn->routes.lock);
277
278 if (bitmap_empty(deleted, 64))
279 return; /* short-circuit RCU */
280 synchronize_rcu();
281 for (i = find_first_bit(deleted, 64); i < 64;
282 i = find_next_bit(deleted, 64, i + 1)) {
283 rtm_phonet_notify(RTM_DELROUTE, dev, i);
284 dev_put(dev);
285 }
286}
287
222/* notify Phonet of device events */ 288/* notify Phonet of device events */
223static int phonet_device_notify(struct notifier_block *me, unsigned long what, 289static int phonet_device_notify(struct notifier_block *me, unsigned long what,
224 void *arg) 290 void *arg)
@@ -232,6 +298,7 @@ static int phonet_device_notify(struct notifier_block *me, unsigned long what,
232 break; 298 break;
233 case NETDEV_UNREGISTER: 299 case NETDEV_UNREGISTER:
234 phonet_device_destroy(dev); 300 phonet_device_destroy(dev);
301 phonet_route_autodel(dev);
235 break; 302 break;
236 } 303 }
237 return 0; 304 return 0;
@@ -246,18 +313,14 @@ static struct notifier_block phonet_device_notifier = {
246/* Per-namespace Phonet devices handling */ 313/* Per-namespace Phonet devices handling */
247static int phonet_init_net(struct net *net) 314static int phonet_init_net(struct net *net)
248{ 315{
249 struct phonet_net *pnn = kmalloc(sizeof(*pnn), GFP_KERNEL); 316 struct phonet_net *pnn = net_generic(net, phonet_net_id);
250 if (!pnn)
251 return -ENOMEM;
252 317
253 if (!proc_net_fops_create(net, "phonet", 0, &pn_sock_seq_fops)) { 318 if (!proc_net_fops_create(net, "phonet", 0, &pn_sock_seq_fops))
254 kfree(pnn);
255 return -ENOMEM; 319 return -ENOMEM;
256 }
257 320
258 INIT_LIST_HEAD(&pnn->pndevs.list); 321 INIT_LIST_HEAD(&pnn->pndevs.list);
259 spin_lock_init(&pnn->pndevs.lock); 322 mutex_init(&pnn->pndevs.lock);
260 net_assign_generic(net, phonet_net_id, pnn); 323 mutex_init(&pnn->routes.lock);
261 return 0; 324 return 0;
262} 325}
263 326
@@ -265,25 +328,35 @@ static void phonet_exit_net(struct net *net)
265{ 328{
266 struct phonet_net *pnn = net_generic(net, phonet_net_id); 329 struct phonet_net *pnn = net_generic(net, phonet_net_id);
267 struct net_device *dev; 330 struct net_device *dev;
331 unsigned i;
268 332
269 rtnl_lock(); 333 rtnl_lock();
270 for_each_netdev(net, dev) 334 for_each_netdev(net, dev)
271 phonet_device_destroy(dev); 335 phonet_device_destroy(dev);
336
337 for (i = 0; i < 64; i++) {
338 dev = pnn->routes.table[i];
339 if (dev) {
340 rtm_phonet_notify(RTM_DELROUTE, dev, i);
341 dev_put(dev);
342 }
343 }
272 rtnl_unlock(); 344 rtnl_unlock();
273 345
274 proc_net_remove(net, "phonet"); 346 proc_net_remove(net, "phonet");
275 kfree(pnn);
276} 347}
277 348
278static struct pernet_operations phonet_net_ops = { 349static struct pernet_operations phonet_net_ops = {
279 .init = phonet_init_net, 350 .init = phonet_init_net,
280 .exit = phonet_exit_net, 351 .exit = phonet_exit_net,
352 .id = &phonet_net_id,
353 .size = sizeof(struct phonet_net),
281}; 354};
282 355
283/* Initialize Phonet devices list */ 356/* Initialize Phonet devices list */
284int __init phonet_device_init(void) 357int __init phonet_device_init(void)
285{ 358{
286 int err = register_pernet_gen_device(&phonet_net_id, &phonet_net_ops); 359 int err = register_pernet_device(&phonet_net_ops);
287 if (err) 360 if (err)
288 return err; 361 return err;
289 362
@@ -298,5 +371,75 @@ void phonet_device_exit(void)
298{ 371{
299 rtnl_unregister_all(PF_PHONET); 372 rtnl_unregister_all(PF_PHONET);
300 unregister_netdevice_notifier(&phonet_device_notifier); 373 unregister_netdevice_notifier(&phonet_device_notifier);
301 unregister_pernet_gen_device(phonet_net_id, &phonet_net_ops); 374 unregister_pernet_device(&phonet_net_ops);
375}
376
377int phonet_route_add(struct net_device *dev, u8 daddr)
378{
379 struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id);
380 struct phonet_routes *routes = &pnn->routes;
381 int err = -EEXIST;
382
383 daddr = daddr >> 2;
384 mutex_lock(&routes->lock);
385 if (routes->table[daddr] == NULL) {
386 rcu_assign_pointer(routes->table[daddr], dev);
387 dev_hold(dev);
388 err = 0;
389 }
390 mutex_unlock(&routes->lock);
391 return err;
392}
393
394int phonet_route_del(struct net_device *dev, u8 daddr)
395{
396 struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id);
397 struct phonet_routes *routes = &pnn->routes;
398
399 daddr = daddr >> 2;
400 mutex_lock(&routes->lock);
401 if (dev == routes->table[daddr])
402 rcu_assign_pointer(routes->table[daddr], NULL);
403 else
404 dev = NULL;
405 mutex_unlock(&routes->lock);
406
407 if (!dev)
408 return -ENOENT;
409 synchronize_rcu();
410 dev_put(dev);
411 return 0;
412}
413
414struct net_device *phonet_route_get(struct net *net, u8 daddr)
415{
416 struct phonet_net *pnn = net_generic(net, phonet_net_id);
417 struct phonet_routes *routes = &pnn->routes;
418 struct net_device *dev;
419
420 ASSERT_RTNL(); /* no need to hold the device */
421
422 daddr >>= 2;
423 rcu_read_lock();
424 dev = rcu_dereference(routes->table[daddr]);
425 rcu_read_unlock();
426 return dev;
427}
428
429struct net_device *phonet_route_output(struct net *net, u8 daddr)
430{
431 struct phonet_net *pnn = net_generic(net, phonet_net_id);
432 struct phonet_routes *routes = &pnn->routes;
433 struct net_device *dev;
434
435 daddr >>= 2;
436 rcu_read_lock();
437 dev = rcu_dereference(routes->table[daddr]);
438 if (dev)
439 dev_hold(dev);
440 rcu_read_unlock();
441
442 if (!dev)
443 dev = phonet_device_get(net); /* Default route */
444 return dev;
302} 445}