Blame view
net/phonet/pn_dev.c
9.89 KB
f8ff60283 Phonet: network d... |
1 2 3 4 5 6 7 |
/* * File: pn_dev.c * * Phonet network device * * Copyright (C) 2008 Nokia Corporation. * |
31fdc5553 net: remove my fu... |
8 9 |
* Authors: Sakari Ailus <sakari.ailus@nokia.com> * Rémi Denis-Courmont |
f8ff60283 Phonet: network d... |
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include <linux/kernel.h> #include <linux/net.h> |
5a0e3ad6a include cleanup: ... |
28 |
#include <linux/slab.h> |
f8ff60283 Phonet: network d... |
29 30 |
#include <linux/netdevice.h> #include <linux/phonet.h> |
421d20a3d phonet: Fix build. |
31 |
#include <linux/proc_fs.h> |
f5bb1c558 Phonet: back-end ... |
32 |
#include <linux/if_arp.h> |
f8ff60283 Phonet: network d... |
33 |
#include <net/sock.h> |
9a3b7a42b Phonet: use per-n... |
34 |
#include <net/netns/generic.h> |
f8ff60283 Phonet: network d... |
35 |
#include <net/phonet/pn_dev.h> |
55748ac04 Phonet: routing t... |
36 |
struct phonet_routes { |
888801357 Phonet: convert r... |
37 |
struct mutex lock; |
55748ac04 Phonet: routing t... |
38 39 |
struct net_device *table[64]; }; |
9a3b7a42b Phonet: use per-n... |
40 41 |
struct phonet_net { struct phonet_device_list pndevs; |
55748ac04 Phonet: routing t... |
42 |
struct phonet_routes routes; |
f8ff60283 Phonet: network d... |
43 |
}; |
3c490a87f Phonet: phonet_ne... |
44 |
static int phonet_net_id __read_mostly; |
9a3b7a42b Phonet: use per-n... |
45 |
|
0db3f0f49 phonet: use phone... |
46 47 48 49 50 51 |
static struct phonet_net *phonet_pernet(struct net *net) { BUG_ON(!net); return net_generic(net, phonet_net_id); } |
9a3b7a42b Phonet: use per-n... |
52 53 |
struct phonet_device_list *phonet_device_list(struct net *net) { |
0db3f0f49 phonet: use phone... |
54 |
struct phonet_net *pnn = phonet_pernet(net); |
9a3b7a42b Phonet: use per-n... |
55 56 |
return &pnn->pndevs; } |
f8ff60283 Phonet: network d... |
57 58 59 |
/* Allocate new Phonet device. */ static struct phonet_device *__phonet_device_alloc(struct net_device *dev) { |
9a3b7a42b Phonet: use per-n... |
60 |
struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); |
f8ff60283 Phonet: network d... |
61 62 63 64 65 |
struct phonet_device *pnd = kmalloc(sizeof(*pnd), GFP_ATOMIC); if (pnd == NULL) return NULL; pnd->netdev = dev; bitmap_zero(pnd->addrs, 64); |
eeb74a9d4 Phonet: convert d... |
66 67 |
BUG_ON(!mutex_is_locked(&pndevs->lock)); list_add_rcu(&pnd->list, &pndevs->list); |
f8ff60283 Phonet: network d... |
68 69 70 71 72 |
return pnd; } static struct phonet_device *__phonet_get(struct net_device *dev) { |
9a3b7a42b Phonet: use per-n... |
73 |
struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); |
f8ff60283 Phonet: network d... |
74 |
struct phonet_device *pnd; |
eeb74a9d4 Phonet: convert d... |
75 |
BUG_ON(!mutex_is_locked(&pndevs->lock)); |
9a3b7a42b Phonet: use per-n... |
76 |
list_for_each_entry(pnd, &pndevs->list, list) { |
f8ff60283 Phonet: network d... |
77 78 79 80 81 |
if (pnd->netdev == dev) return pnd; } return NULL; } |
eeb74a9d4 Phonet: convert d... |
82 83 84 85 86 87 88 89 90 91 92 |
static struct phonet_device *__phonet_get_rcu(struct net_device *dev) { struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); struct phonet_device *pnd; list_for_each_entry_rcu(pnd, &pndevs->list, list) { if (pnd->netdev == dev) return pnd; } return NULL; } |
2be6fa4c7 Phonet: generate ... |
93 |
static void phonet_device_destroy(struct net_device *dev) |
f8ff60283 Phonet: network d... |
94 |
{ |
2be6fa4c7 Phonet: generate ... |
95 96 97 98 |
struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); struct phonet_device *pnd; ASSERT_RTNL(); |
eeb74a9d4 Phonet: convert d... |
99 |
mutex_lock(&pndevs->lock); |
2be6fa4c7 Phonet: generate ... |
100 101 |
pnd = __phonet_get(dev); if (pnd) |
eeb74a9d4 Phonet: convert d... |
102 103 |
list_del_rcu(&pnd->list); mutex_unlock(&pndevs->lock); |
2be6fa4c7 Phonet: generate ... |
104 105 106 |
if (pnd) { u8 addr; |
a1ca14ac5 phonet: use for_e... |
107 |
for_each_set_bit(addr, pnd->addrs, 64) |
2be6fa4c7 Phonet: generate ... |
108 109 110 |
phonet_address_notify(RTM_DELADDR, dev, addr); kfree(pnd); } |
f8ff60283 Phonet: network d... |
111 112 113 114 |
} struct net_device *phonet_device_get(struct net *net) { |
9a3b7a42b Phonet: use per-n... |
115 |
struct phonet_device_list *pndevs = phonet_device_list(net); |
f8ff60283 Phonet: network d... |
116 |
struct phonet_device *pnd; |
59e57f441 phonet: phonet_de... |
117 |
struct net_device *dev = NULL; |
f8ff60283 Phonet: network d... |
118 |
|
eeb74a9d4 Phonet: convert d... |
119 120 |
rcu_read_lock(); list_for_each_entry_rcu(pnd, &pndevs->list, list) { |
f8ff60283 Phonet: network d... |
121 122 |
dev = pnd->netdev; BUG_ON(!dev); |
9a3b7a42b Phonet: use per-n... |
123 |
if ((dev->reg_state == NETREG_REGISTERED) && |
f8ff60283 Phonet: network d... |
124 125 126 127 128 129 |
((pnd->netdev->flags & IFF_UP)) == IFF_UP) break; dev = NULL; } if (dev) dev_hold(dev); |
eeb74a9d4 Phonet: convert d... |
130 |
rcu_read_unlock(); |
f8ff60283 Phonet: network d... |
131 132 133 134 135 |
return dev; } int phonet_address_add(struct net_device *dev, u8 addr) { |
9a3b7a42b Phonet: use per-n... |
136 |
struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); |
f8ff60283 Phonet: network d... |
137 138 |
struct phonet_device *pnd; int err = 0; |
eeb74a9d4 Phonet: convert d... |
139 |
mutex_lock(&pndevs->lock); |
f8ff60283 Phonet: network d... |
140 141 142 143 144 145 146 147 |
/* Find or create Phonet-specific device data */ pnd = __phonet_get(dev); if (pnd == NULL) pnd = __phonet_device_alloc(dev); if (unlikely(pnd == NULL)) err = -ENOMEM; else if (test_and_set_bit(addr >> 2, pnd->addrs)) err = -EEXIST; |
eeb74a9d4 Phonet: convert d... |
148 |
mutex_unlock(&pndevs->lock); |
f8ff60283 Phonet: network d... |
149 150 151 152 153 |
return err; } int phonet_address_del(struct net_device *dev, u8 addr) { |
9a3b7a42b Phonet: use per-n... |
154 |
struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); |
f8ff60283 Phonet: network d... |
155 156 |
struct phonet_device *pnd; int err = 0; |
eeb74a9d4 Phonet: convert d... |
157 |
mutex_lock(&pndevs->lock); |
f8ff60283 Phonet: network d... |
158 |
pnd = __phonet_get(dev); |
eeb74a9d4 Phonet: convert d... |
159 |
if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) { |
f8ff60283 Phonet: network d... |
160 |
err = -EADDRNOTAVAIL; |
eeb74a9d4 Phonet: convert d... |
161 162 163 164 165 166 |
pnd = NULL; } else if (bitmap_empty(pnd->addrs, 64)) list_del_rcu(&pnd->list); else pnd = NULL; mutex_unlock(&pndevs->lock); |
88e7594a9 phonet: use call_... |
167 |
if (pnd) |
7e113a9c7 net,rcu: convert ... |
168 |
kfree_rcu(pnd, rcu); |
88e7594a9 phonet: use call_... |
169 |
|
f8ff60283 Phonet: network d... |
170 171 172 173 |
return err; } /* Gets a source address toward a destination, through a interface. */ |
55748ac04 Phonet: routing t... |
174 |
u8 phonet_address_get(struct net_device *dev, u8 daddr) |
f8ff60283 Phonet: network d... |
175 176 |
{ struct phonet_device *pnd; |
55748ac04 Phonet: routing t... |
177 |
u8 saddr; |
f8ff60283 Phonet: network d... |
178 |
|
eeb74a9d4 Phonet: convert d... |
179 180 |
rcu_read_lock(); pnd = __phonet_get_rcu(dev); |
f8ff60283 Phonet: network d... |
181 182 183 184 |
if (pnd) { BUG_ON(bitmap_empty(pnd->addrs, 64)); /* Use same source address as destination, if possible */ |
55748ac04 Phonet: routing t... |
185 186 187 188 |
if (test_bit(daddr >> 2, pnd->addrs)) saddr = daddr; else saddr = find_first_bit(pnd->addrs, 64) << 2; |
f8ff60283 Phonet: network d... |
189 |
} else |
55748ac04 Phonet: routing t... |
190 |
saddr = PN_NO_ADDR; |
eeb74a9d4 Phonet: convert d... |
191 |
rcu_read_unlock(); |
55748ac04 Phonet: routing t... |
192 193 194 195 196 197 198 199 200 201 202 203 204 |
if (saddr == PN_NO_ADDR) { /* Fallback to another device */ struct net_device *def_dev; def_dev = phonet_device_get(dev_net(dev)); if (def_dev) { if (def_dev != dev) saddr = phonet_address_get(def_dev, daddr); dev_put(def_dev); } } return saddr; |
f8ff60283 Phonet: network d... |
205 |
} |
524048819 Phonet: basic net... |
206 |
int phonet_address_lookup(struct net *net, u8 addr) |
f8ff60283 Phonet: network d... |
207 |
{ |
9a3b7a42b Phonet: use per-n... |
208 |
struct phonet_device_list *pndevs = phonet_device_list(net); |
f8ff60283 Phonet: network d... |
209 |
struct phonet_device *pnd; |
9a3b7a42b Phonet: use per-n... |
210 |
int err = -EADDRNOTAVAIL; |
f8ff60283 Phonet: network d... |
211 |
|
eeb74a9d4 Phonet: convert d... |
212 213 |
rcu_read_lock(); list_for_each_entry_rcu(pnd, &pndevs->list, list) { |
f8ff60283 Phonet: network d... |
214 215 216 217 218 219 |
/* Don't allow unregistering devices! */ if ((pnd->netdev->reg_state != NETREG_REGISTERED) || ((pnd->netdev->flags & IFF_UP)) != IFF_UP) continue; if (test_bit(addr >> 2, pnd->addrs)) { |
9a3b7a42b Phonet: use per-n... |
220 221 |
err = 0; goto found; |
f8ff60283 Phonet: network d... |
222 223 |
} } |
9a3b7a42b Phonet: use per-n... |
224 |
found: |
eeb74a9d4 Phonet: convert d... |
225 |
rcu_read_unlock(); |
9a3b7a42b Phonet: use per-n... |
226 |
return err; |
f8ff60283 Phonet: network d... |
227 |
} |
f5bb1c558 Phonet: back-end ... |
228 229 230 231 232 233 234 235 236 237 238 239 240 |
/* automatically configure a Phonet device, if supported */ static int phonet_device_autoconf(struct net_device *dev) { struct if_phonet_req req; int ret; if (!dev->netdev_ops->ndo_do_ioctl) return -EOPNOTSUPP; ret = dev->netdev_ops->ndo_do_ioctl(dev, (struct ifreq *)&req, SIOCPNGAUTOCONF); if (ret < 0) return ret; |
b11b5165a Phonet: Netlink e... |
241 242 243 244 245 246 247 248 |
ASSERT_RTNL(); ret = phonet_address_add(dev, req.ifr_phonet_autoconf.device); if (ret) return ret; phonet_address_notify(RTM_NEWADDR, dev, req.ifr_phonet_autoconf.device); return 0; |
f5bb1c558 Phonet: back-end ... |
249 |
} |
f062f41d0 Phonet: routing t... |
250 251 |
static void phonet_route_autodel(struct net_device *dev) { |
0db3f0f49 phonet: use phone... |
252 |
struct phonet_net *pnn = phonet_pernet(dev_net(dev)); |
95c961747 net: cleanup unsi... |
253 |
unsigned int i; |
f062f41d0 Phonet: routing t... |
254 255 256 257 |
DECLARE_BITMAP(deleted, 64); /* Remove left-over Phonet routes */ bitmap_zero(deleted, 64); |
888801357 Phonet: convert r... |
258 |
mutex_lock(&pnn->routes.lock); |
f062f41d0 Phonet: routing t... |
259 260 |
for (i = 0; i < 64; i++) if (dev == pnn->routes.table[i]) { |
a9b3cd7f3 rcu: convert uses... |
261 |
RCU_INIT_POINTER(pnn->routes.table[i], NULL); |
f062f41d0 Phonet: routing t... |
262 |
set_bit(i, deleted); |
f062f41d0 Phonet: routing t... |
263 |
} |
888801357 Phonet: convert r... |
264 265 266 267 268 |
mutex_unlock(&pnn->routes.lock); if (bitmap_empty(deleted, 64)) return; /* short-circuit RCU */ synchronize_rcu(); |
6a499b242 phonet: use for_e... |
269 |
for_each_set_bit(i, deleted, 64) { |
f062f41d0 Phonet: routing t... |
270 |
rtm_phonet_notify(RTM_DELROUTE, dev, i); |
888801357 Phonet: convert r... |
271 272 |
dev_put(dev); } |
f062f41d0 Phonet: routing t... |
273 |
} |
f8ff60283 Phonet: network d... |
274 275 |
/* notify Phonet of device events */ static int phonet_device_notify(struct notifier_block *me, unsigned long what, |
351638e7d net: pass info st... |
276 |
void *ptr) |
f8ff60283 Phonet: network d... |
277 |
{ |
351638e7d net: pass info st... |
278 |
struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
f8ff60283 Phonet: network d... |
279 |
|
f5bb1c558 Phonet: back-end ... |
280 281 282 283 284 285 |
switch (what) { case NETDEV_REGISTER: if (dev->type == ARPHRD_PHONET) phonet_device_autoconf(dev); break; case NETDEV_UNREGISTER: |
2be6fa4c7 Phonet: generate ... |
286 |
phonet_device_destroy(dev); |
f062f41d0 Phonet: routing t... |
287 |
phonet_route_autodel(dev); |
f5bb1c558 Phonet: back-end ... |
288 289 |
break; } |
f8ff60283 Phonet: network d... |
290 291 292 293 294 295 296 297 |
return 0; } static struct notifier_block phonet_device_notifier = { .notifier_call = phonet_device_notify, .priority = 0, }; |
9a3b7a42b Phonet: use per-n... |
298 |
/* Per-namespace Phonet devices handling */ |
2c8c1e729 net: spread __net... |
299 |
static int __net_init phonet_init_net(struct net *net) |
9a3b7a42b Phonet: use per-n... |
300 |
{ |
0db3f0f49 phonet: use phone... |
301 |
struct phonet_net *pnn = phonet_pernet(net); |
9a3b7a42b Phonet: use per-n... |
302 |
|
d4beaa66a net: proc: change... |
303 |
if (!proc_create("phonet", 0, net->proc_net, &pn_sock_seq_fops)) |
c1dc13e9d Phonet: sockets l... |
304 |
return -ENOMEM; |
c1dc13e9d Phonet: sockets l... |
305 |
|
9a3b7a42b Phonet: use per-n... |
306 |
INIT_LIST_HEAD(&pnn->pndevs.list); |
eeb74a9d4 Phonet: convert d... |
307 |
mutex_init(&pnn->pndevs.lock); |
888801357 Phonet: convert r... |
308 |
mutex_init(&pnn->routes.lock); |
9a3b7a42b Phonet: use per-n... |
309 310 |
return 0; } |
2c8c1e729 net: spread __net... |
311 |
static void __net_exit phonet_exit_net(struct net *net) |
9a3b7a42b Phonet: use per-n... |
312 |
{ |
ece31ffd5 net: proc: change... |
313 |
remove_proc_entry("phonet", net->proc_net); |
9a3b7a42b Phonet: use per-n... |
314 315 316 317 318 |
} static struct pernet_operations phonet_net_ops = { .init = phonet_init_net, .exit = phonet_exit_net, |
d2b3eb630 net: Simplify pho... |
319 320 |
.id = &phonet_net_id, .size = sizeof(struct phonet_net), |
9a3b7a42b Phonet: use per-n... |
321 |
}; |
f8ff60283 Phonet: network d... |
322 |
/* Initialize Phonet devices list */ |
76e02cf69 Phonet: allow pho... |
323 |
int __init phonet_device_init(void) |
f8ff60283 Phonet: network d... |
324 |
{ |
03478756b phonet: Sort out ... |
325 |
int err = register_pernet_subsys(&phonet_net_ops); |
9a3b7a42b Phonet: use per-n... |
326 327 |
if (err) return err; |
660f706d9 Phonet: handle rt... |
328 |
|
d4beaa66a net: proc: change... |
329 |
proc_create("pnresource", 0, init_net.proc_net, &pn_res_seq_fops); |
f8ff60283 Phonet: network d... |
330 |
register_netdevice_notifier(&phonet_device_notifier); |
660f706d9 Phonet: handle rt... |
331 332 333 334 |
err = phonet_netlink_register(); if (err) phonet_device_exit(); return err; |
f8ff60283 Phonet: network d... |
335 336 337 338 |
} void phonet_device_exit(void) { |
f8ff60283 Phonet: network d... |
339 |
rtnl_unregister_all(PF_PHONET); |
6530e0fee Phonet: remove us... |
340 |
unregister_netdevice_notifier(&phonet_device_notifier); |
03478756b phonet: Sort out ... |
341 |
unregister_pernet_subsys(&phonet_net_ops); |
ece31ffd5 net: proc: change... |
342 |
remove_proc_entry("pnresource", init_net.proc_net); |
f8ff60283 Phonet: network d... |
343 |
} |
55748ac04 Phonet: routing t... |
344 345 346 |
int phonet_route_add(struct net_device *dev, u8 daddr) { |
0db3f0f49 phonet: use phone... |
347 |
struct phonet_net *pnn = phonet_pernet(dev_net(dev)); |
55748ac04 Phonet: routing t... |
348 349 350 351 |
struct phonet_routes *routes = &pnn->routes; int err = -EEXIST; daddr = daddr >> 2; |
888801357 Phonet: convert r... |
352 |
mutex_lock(&routes->lock); |
55748ac04 Phonet: routing t... |
353 |
if (routes->table[daddr] == NULL) { |
cf778b00e net: reintroduce ... |
354 |
rcu_assign_pointer(routes->table[daddr], dev); |
55748ac04 Phonet: routing t... |
355 356 357 |
dev_hold(dev); err = 0; } |
888801357 Phonet: convert r... |
358 |
mutex_unlock(&routes->lock); |
55748ac04 Phonet: routing t... |
359 360 361 362 363 |
return err; } int phonet_route_del(struct net_device *dev, u8 daddr) { |
0db3f0f49 phonet: use phone... |
364 |
struct phonet_net *pnn = phonet_pernet(dev_net(dev)); |
55748ac04 Phonet: routing t... |
365 |
struct phonet_routes *routes = &pnn->routes; |
55748ac04 Phonet: routing t... |
366 367 |
daddr = daddr >> 2; |
888801357 Phonet: convert r... |
368 369 |
mutex_lock(&routes->lock); if (dev == routes->table[daddr]) |
a9b3cd7f3 rcu: convert uses... |
370 |
RCU_INIT_POINTER(routes->table[daddr], NULL); |
888801357 Phonet: convert r... |
371 372 373 374 375 376 377 378 379 |
else dev = NULL; mutex_unlock(&routes->lock); if (!dev) return -ENOENT; synchronize_rcu(); dev_put(dev); return 0; |
55748ac04 Phonet: routing t... |
380 |
} |
e67f88dd1 net: dont hold rt... |
381 |
struct net_device *phonet_route_get_rcu(struct net *net, u8 daddr) |
55748ac04 Phonet: routing t... |
382 |
{ |
0db3f0f49 phonet: use phone... |
383 |
struct phonet_net *pnn = phonet_pernet(net); |
55748ac04 Phonet: routing t... |
384 385 |
struct phonet_routes *routes = &pnn->routes; struct net_device *dev; |
55748ac04 Phonet: routing t... |
386 |
daddr >>= 2; |
888801357 Phonet: convert r... |
387 |
dev = rcu_dereference(routes->table[daddr]); |
55748ac04 Phonet: routing t... |
388 389 390 391 392 |
return dev; } struct net_device *phonet_route_output(struct net *net, u8 daddr) { |
0db3f0f49 phonet: use phone... |
393 |
struct phonet_net *pnn = phonet_pernet(net); |
55748ac04 Phonet: routing t... |
394 395 |
struct phonet_routes *routes = &pnn->routes; struct net_device *dev; |
888801357 Phonet: convert r... |
396 397 398 |
daddr >>= 2; rcu_read_lock(); dev = rcu_dereference(routes->table[daddr]); |
55748ac04 Phonet: routing t... |
399 400 |
if (dev) dev_hold(dev); |
888801357 Phonet: convert r... |
401 |
rcu_read_unlock(); |
55748ac04 Phonet: routing t... |
402 403 404 405 406 |
if (!dev) dev = phonet_device_get(net); /* Default route */ return dev; } |