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