12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f8ff6028SRemi Denis-Courmont /*
3f8ff6028SRemi Denis-Courmont * File: pn_dev.c
4f8ff6028SRemi Denis-Courmont *
5f8ff6028SRemi Denis-Courmont * Phonet network device
6f8ff6028SRemi Denis-Courmont *
7f8ff6028SRemi Denis-Courmont * Copyright (C) 2008 Nokia Corporation.
8f8ff6028SRemi Denis-Courmont *
931fdc555SRémi Denis-Courmont * Authors: Sakari Ailus <sakari.ailus@nokia.com>
1031fdc555SRémi Denis-Courmont * Rémi Denis-Courmont
11f8ff6028SRemi Denis-Courmont */
12f8ff6028SRemi Denis-Courmont
13f8ff6028SRemi Denis-Courmont #include <linux/kernel.h>
14f8ff6028SRemi Denis-Courmont #include <linux/net.h>
155a0e3ad6STejun Heo #include <linux/slab.h>
16f8ff6028SRemi Denis-Courmont #include <linux/netdevice.h>
17f8ff6028SRemi Denis-Courmont #include <linux/phonet.h>
18421d20a3SDavid S. Miller #include <linux/proc_fs.h>
19f5bb1c55SRémi Denis-Courmont #include <linux/if_arp.h>
20f8ff6028SRemi Denis-Courmont #include <net/sock.h>
219a3b7a42Sremi.denis-courmont@nokia #include <net/netns/generic.h>
22f8ff6028SRemi Denis-Courmont #include <net/phonet/pn_dev.h>
23f8ff6028SRemi Denis-Courmont
2455748ac0SRémi Denis-Courmont struct phonet_routes {
2588880135SRémi Denis-Courmont struct mutex lock;
2679952bcaSFabian Frederick struct net_device __rcu *table[64];
2755748ac0SRémi Denis-Courmont };
2855748ac0SRémi Denis-Courmont
299a3b7a42Sremi.denis-courmont@nokia struct phonet_net {
309a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list pndevs;
3155748ac0SRémi Denis-Courmont struct phonet_routes routes;
32f8ff6028SRemi Denis-Courmont };
33f8ff6028SRemi Denis-Courmont
34c7d03a00SAlexey Dobriyan static unsigned int phonet_net_id __read_mostly;
359a3b7a42Sremi.denis-courmont@nokia
phonet_pernet(struct net * net)360db3f0f4SJiri Pirko static struct phonet_net *phonet_pernet(struct net *net)
370db3f0f4SJiri Pirko {
380db3f0f4SJiri Pirko return net_generic(net, phonet_net_id);
390db3f0f4SJiri Pirko }
400db3f0f4SJiri Pirko
phonet_device_list(struct net * net)419a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *phonet_device_list(struct net *net)
429a3b7a42Sremi.denis-courmont@nokia {
430db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(net);
449a3b7a42Sremi.denis-courmont@nokia return &pnn->pndevs;
459a3b7a42Sremi.denis-courmont@nokia }
469a3b7a42Sremi.denis-courmont@nokia
47f8ff6028SRemi Denis-Courmont /* Allocate new Phonet device. */
__phonet_device_alloc(struct net_device * dev)48f8ff6028SRemi Denis-Courmont static struct phonet_device *__phonet_device_alloc(struct net_device *dev)
49f8ff6028SRemi Denis-Courmont {
509a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
51f8ff6028SRemi Denis-Courmont struct phonet_device *pnd = kmalloc(sizeof(*pnd), GFP_ATOMIC);
52f8ff6028SRemi Denis-Courmont if (pnd == NULL)
53f8ff6028SRemi Denis-Courmont return NULL;
54f8ff6028SRemi Denis-Courmont pnd->netdev = dev;
55f8ff6028SRemi Denis-Courmont bitmap_zero(pnd->addrs, 64);
56f8ff6028SRemi Denis-Courmont
57eeb74a9dSRémi Denis-Courmont BUG_ON(!mutex_is_locked(&pndevs->lock));
58eeb74a9dSRémi Denis-Courmont list_add_rcu(&pnd->list, &pndevs->list);
59f8ff6028SRemi Denis-Courmont return pnd;
60f8ff6028SRemi Denis-Courmont }
61f8ff6028SRemi Denis-Courmont
__phonet_get(struct net_device * dev)62f8ff6028SRemi Denis-Courmont static struct phonet_device *__phonet_get(struct net_device *dev)
63f8ff6028SRemi Denis-Courmont {
649a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
65f8ff6028SRemi Denis-Courmont struct phonet_device *pnd;
66f8ff6028SRemi Denis-Courmont
67eeb74a9dSRémi Denis-Courmont BUG_ON(!mutex_is_locked(&pndevs->lock));
689a3b7a42Sremi.denis-courmont@nokia list_for_each_entry(pnd, &pndevs->list, list) {
69f8ff6028SRemi Denis-Courmont if (pnd->netdev == dev)
70f8ff6028SRemi Denis-Courmont return pnd;
71f8ff6028SRemi Denis-Courmont }
72f8ff6028SRemi Denis-Courmont return NULL;
73f8ff6028SRemi Denis-Courmont }
74f8ff6028SRemi Denis-Courmont
__phonet_get_rcu(struct net_device * dev)75eeb74a9dSRémi Denis-Courmont static struct phonet_device *__phonet_get_rcu(struct net_device *dev)
76eeb74a9dSRémi Denis-Courmont {
77eeb74a9dSRémi Denis-Courmont struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
78eeb74a9dSRémi Denis-Courmont struct phonet_device *pnd;
79eeb74a9dSRémi Denis-Courmont
80eeb74a9dSRémi Denis-Courmont list_for_each_entry_rcu(pnd, &pndevs->list, list) {
81eeb74a9dSRémi Denis-Courmont if (pnd->netdev == dev)
82eeb74a9dSRémi Denis-Courmont return pnd;
83eeb74a9dSRémi Denis-Courmont }
84eeb74a9dSRémi Denis-Courmont return NULL;
85eeb74a9dSRémi Denis-Courmont }
86eeb74a9dSRémi Denis-Courmont
phonet_device_destroy(struct net_device * dev)872be6fa4cSRémi Denis-Courmont static void phonet_device_destroy(struct net_device *dev)
88f8ff6028SRemi Denis-Courmont {
892be6fa4cSRémi Denis-Courmont struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
902be6fa4cSRémi Denis-Courmont struct phonet_device *pnd;
912be6fa4cSRémi Denis-Courmont
922be6fa4cSRémi Denis-Courmont ASSERT_RTNL();
932be6fa4cSRémi Denis-Courmont
94eeb74a9dSRémi Denis-Courmont mutex_lock(&pndevs->lock);
952be6fa4cSRémi Denis-Courmont pnd = __phonet_get(dev);
962be6fa4cSRémi Denis-Courmont if (pnd)
97eeb74a9dSRémi Denis-Courmont list_del_rcu(&pnd->list);
98eeb74a9dSRémi Denis-Courmont mutex_unlock(&pndevs->lock);
992be6fa4cSRémi Denis-Courmont
1002be6fa4cSRémi Denis-Courmont if (pnd) {
1012be6fa4cSRémi Denis-Courmont u8 addr;
1022be6fa4cSRémi Denis-Courmont
103a1ca14acSAkinobu Mita for_each_set_bit(addr, pnd->addrs, 64)
1042be6fa4cSRémi Denis-Courmont phonet_address_notify(RTM_DELADDR, dev, addr);
105f8ff6028SRemi Denis-Courmont kfree(pnd);
106f8ff6028SRemi Denis-Courmont }
1072be6fa4cSRémi Denis-Courmont }
108f8ff6028SRemi Denis-Courmont
phonet_device_get(struct net * net)109f8ff6028SRemi Denis-Courmont struct net_device *phonet_device_get(struct net *net)
110f8ff6028SRemi Denis-Courmont {
1119a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(net);
112f8ff6028SRemi Denis-Courmont struct phonet_device *pnd;
11359e57f44SEric Dumazet struct net_device *dev = NULL;
114f8ff6028SRemi Denis-Courmont
115eeb74a9dSRémi Denis-Courmont rcu_read_lock();
116eeb74a9dSRémi Denis-Courmont list_for_each_entry_rcu(pnd, &pndevs->list, list) {
117f8ff6028SRemi Denis-Courmont dev = pnd->netdev;
118f8ff6028SRemi Denis-Courmont BUG_ON(!dev);
119f8ff6028SRemi Denis-Courmont
1209a3b7a42Sremi.denis-courmont@nokia if ((dev->reg_state == NETREG_REGISTERED) &&
121f8ff6028SRemi Denis-Courmont ((pnd->netdev->flags & IFF_UP)) == IFF_UP)
122f8ff6028SRemi Denis-Courmont break;
123f8ff6028SRemi Denis-Courmont dev = NULL;
124f8ff6028SRemi Denis-Courmont }
125f8ff6028SRemi Denis-Courmont dev_hold(dev);
126eeb74a9dSRémi Denis-Courmont rcu_read_unlock();
127f8ff6028SRemi Denis-Courmont return dev;
128f8ff6028SRemi Denis-Courmont }
129f8ff6028SRemi Denis-Courmont
phonet_address_add(struct net_device * dev,u8 addr)130f8ff6028SRemi Denis-Courmont int phonet_address_add(struct net_device *dev, u8 addr)
131f8ff6028SRemi Denis-Courmont {
1329a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
133f8ff6028SRemi Denis-Courmont struct phonet_device *pnd;
134f8ff6028SRemi Denis-Courmont int err = 0;
135f8ff6028SRemi Denis-Courmont
136eeb74a9dSRémi Denis-Courmont mutex_lock(&pndevs->lock);
137f8ff6028SRemi Denis-Courmont /* Find or create Phonet-specific device data */
138f8ff6028SRemi Denis-Courmont pnd = __phonet_get(dev);
139f8ff6028SRemi Denis-Courmont if (pnd == NULL)
140f8ff6028SRemi Denis-Courmont pnd = __phonet_device_alloc(dev);
141f8ff6028SRemi Denis-Courmont if (unlikely(pnd == NULL))
142f8ff6028SRemi Denis-Courmont err = -ENOMEM;
143f8ff6028SRemi Denis-Courmont else if (test_and_set_bit(addr >> 2, pnd->addrs))
144f8ff6028SRemi Denis-Courmont err = -EEXIST;
145eeb74a9dSRémi Denis-Courmont mutex_unlock(&pndevs->lock);
146f8ff6028SRemi Denis-Courmont return err;
147f8ff6028SRemi Denis-Courmont }
148f8ff6028SRemi Denis-Courmont
phonet_address_del(struct net_device * dev,u8 addr)149f8ff6028SRemi Denis-Courmont int phonet_address_del(struct net_device *dev, u8 addr)
150f8ff6028SRemi Denis-Courmont {
1519a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
152f8ff6028SRemi Denis-Courmont struct phonet_device *pnd;
153f8ff6028SRemi Denis-Courmont int err = 0;
154f8ff6028SRemi Denis-Courmont
155eeb74a9dSRémi Denis-Courmont mutex_lock(&pndevs->lock);
156f8ff6028SRemi Denis-Courmont pnd = __phonet_get(dev);
157eeb74a9dSRémi Denis-Courmont if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) {
158f8ff6028SRemi Denis-Courmont err = -EADDRNOTAVAIL;
159eeb74a9dSRémi Denis-Courmont pnd = NULL;
160eeb74a9dSRémi Denis-Courmont } else if (bitmap_empty(pnd->addrs, 64))
161eeb74a9dSRémi Denis-Courmont list_del_rcu(&pnd->list);
162eeb74a9dSRémi Denis-Courmont else
163eeb74a9dSRémi Denis-Courmont pnd = NULL;
164eeb74a9dSRémi Denis-Courmont mutex_unlock(&pndevs->lock);
165eeb74a9dSRémi Denis-Courmont
16688e7594aSJiri Pirko if (pnd)
1677e113a9cSLai Jiangshan kfree_rcu(pnd, rcu);
16888e7594aSJiri Pirko
169f8ff6028SRemi Denis-Courmont return err;
170f8ff6028SRemi Denis-Courmont }
171f8ff6028SRemi Denis-Courmont
172f8ff6028SRemi Denis-Courmont /* Gets a source address toward a destination, through a interface. */
phonet_address_get(struct net_device * dev,u8 daddr)17355748ac0SRémi Denis-Courmont u8 phonet_address_get(struct net_device *dev, u8 daddr)
174f8ff6028SRemi Denis-Courmont {
175f8ff6028SRemi Denis-Courmont struct phonet_device *pnd;
17655748ac0SRémi Denis-Courmont u8 saddr;
177f8ff6028SRemi Denis-Courmont
178eeb74a9dSRémi Denis-Courmont rcu_read_lock();
179eeb74a9dSRémi Denis-Courmont pnd = __phonet_get_rcu(dev);
180f8ff6028SRemi Denis-Courmont if (pnd) {
181f8ff6028SRemi Denis-Courmont BUG_ON(bitmap_empty(pnd->addrs, 64));
182f8ff6028SRemi Denis-Courmont
183f8ff6028SRemi Denis-Courmont /* Use same source address as destination, if possible */
18455748ac0SRémi Denis-Courmont if (test_bit(daddr >> 2, pnd->addrs))
18555748ac0SRémi Denis-Courmont saddr = daddr;
18655748ac0SRémi Denis-Courmont else
18755748ac0SRémi Denis-Courmont saddr = find_first_bit(pnd->addrs, 64) << 2;
188f8ff6028SRemi Denis-Courmont } else
18955748ac0SRémi Denis-Courmont saddr = PN_NO_ADDR;
190eeb74a9dSRémi Denis-Courmont rcu_read_unlock();
19155748ac0SRémi Denis-Courmont
19255748ac0SRémi Denis-Courmont if (saddr == PN_NO_ADDR) {
19355748ac0SRémi Denis-Courmont /* Fallback to another device */
19455748ac0SRémi Denis-Courmont struct net_device *def_dev;
19555748ac0SRémi Denis-Courmont
19655748ac0SRémi Denis-Courmont def_dev = phonet_device_get(dev_net(dev));
19755748ac0SRémi Denis-Courmont if (def_dev) {
19855748ac0SRémi Denis-Courmont if (def_dev != dev)
19955748ac0SRémi Denis-Courmont saddr = phonet_address_get(def_dev, daddr);
20055748ac0SRémi Denis-Courmont dev_put(def_dev);
20155748ac0SRémi Denis-Courmont }
20255748ac0SRémi Denis-Courmont }
20355748ac0SRémi Denis-Courmont return saddr;
204f8ff6028SRemi Denis-Courmont }
205f8ff6028SRemi Denis-Courmont
phonet_address_lookup(struct net * net,u8 addr)20652404881SRémi Denis-Courmont int phonet_address_lookup(struct net *net, u8 addr)
207f8ff6028SRemi Denis-Courmont {
2089a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(net);
209f8ff6028SRemi Denis-Courmont struct phonet_device *pnd;
2109a3b7a42Sremi.denis-courmont@nokia int err = -EADDRNOTAVAIL;
211f8ff6028SRemi Denis-Courmont
212eeb74a9dSRémi Denis-Courmont rcu_read_lock();
213eeb74a9dSRémi Denis-Courmont list_for_each_entry_rcu(pnd, &pndevs->list, list) {
214f8ff6028SRemi Denis-Courmont /* Don't allow unregistering devices! */
215f8ff6028SRemi Denis-Courmont if ((pnd->netdev->reg_state != NETREG_REGISTERED) ||
216f8ff6028SRemi Denis-Courmont ((pnd->netdev->flags & IFF_UP)) != IFF_UP)
217f8ff6028SRemi Denis-Courmont continue;
218f8ff6028SRemi Denis-Courmont
219f8ff6028SRemi Denis-Courmont if (test_bit(addr >> 2, pnd->addrs)) {
2209a3b7a42Sremi.denis-courmont@nokia err = 0;
2219a3b7a42Sremi.denis-courmont@nokia goto found;
222f8ff6028SRemi Denis-Courmont }
223f8ff6028SRemi Denis-Courmont }
2249a3b7a42Sremi.denis-courmont@nokia found:
225eeb74a9dSRémi Denis-Courmont rcu_read_unlock();
2269a3b7a42Sremi.denis-courmont@nokia return err;
227f8ff6028SRemi Denis-Courmont }
228f8ff6028SRemi Denis-Courmont
229f5bb1c55SRémi Denis-Courmont /* automatically configure a Phonet device, if supported */
phonet_device_autoconf(struct net_device * dev)230f5bb1c55SRémi Denis-Courmont static int phonet_device_autoconf(struct net_device *dev)
231f5bb1c55SRémi Denis-Courmont {
232f5bb1c55SRémi Denis-Courmont struct if_phonet_req req;
233f5bb1c55SRémi Denis-Courmont int ret;
234f5bb1c55SRémi Denis-Courmont
235*4747c1a8SArnd Bergmann if (!dev->netdev_ops->ndo_siocdevprivate)
236f5bb1c55SRémi Denis-Courmont return -EOPNOTSUPP;
237f5bb1c55SRémi Denis-Courmont
238*4747c1a8SArnd Bergmann ret = dev->netdev_ops->ndo_siocdevprivate(dev, (struct ifreq *)&req,
239*4747c1a8SArnd Bergmann NULL, SIOCPNGAUTOCONF);
240f5bb1c55SRémi Denis-Courmont if (ret < 0)
241f5bb1c55SRémi Denis-Courmont return ret;
242b11b5165SRémi Denis-Courmont
243b11b5165SRémi Denis-Courmont ASSERT_RTNL();
244b11b5165SRémi Denis-Courmont ret = phonet_address_add(dev, req.ifr_phonet_autoconf.device);
245b11b5165SRémi Denis-Courmont if (ret)
246b11b5165SRémi Denis-Courmont return ret;
247b11b5165SRémi Denis-Courmont phonet_address_notify(RTM_NEWADDR, dev,
248b11b5165SRémi Denis-Courmont req.ifr_phonet_autoconf.device);
249b11b5165SRémi Denis-Courmont return 0;
250f5bb1c55SRémi Denis-Courmont }
251f5bb1c55SRémi Denis-Courmont
phonet_route_autodel(struct net_device * dev)252f062f41dSRémi Denis-Courmont static void phonet_route_autodel(struct net_device *dev)
253f062f41dSRémi Denis-Courmont {
2540db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(dev_net(dev));
25595c96174SEric Dumazet unsigned int i;
256f062f41dSRémi Denis-Courmont DECLARE_BITMAP(deleted, 64);
257f062f41dSRémi Denis-Courmont
258f062f41dSRémi Denis-Courmont /* Remove left-over Phonet routes */
259f062f41dSRémi Denis-Courmont bitmap_zero(deleted, 64);
26088880135SRémi Denis-Courmont mutex_lock(&pnn->routes.lock);
261f062f41dSRémi Denis-Courmont for (i = 0; i < 64; i++)
26279952bcaSFabian Frederick if (rcu_access_pointer(pnn->routes.table[i]) == dev) {
263a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(pnn->routes.table[i], NULL);
264f062f41dSRémi Denis-Courmont set_bit(i, deleted);
26588880135SRémi Denis-Courmont }
26688880135SRémi Denis-Courmont mutex_unlock(&pnn->routes.lock);
26788880135SRémi Denis-Courmont
26888880135SRémi Denis-Courmont if (bitmap_empty(deleted, 64))
26988880135SRémi Denis-Courmont return; /* short-circuit RCU */
27088880135SRémi Denis-Courmont synchronize_rcu();
2716a499b24SAkinobu Mita for_each_set_bit(i, deleted, 64) {
27288880135SRémi Denis-Courmont rtm_phonet_notify(RTM_DELROUTE, dev, i);
273f062f41dSRémi Denis-Courmont dev_put(dev);
274f062f41dSRémi Denis-Courmont }
275f062f41dSRémi Denis-Courmont }
276f062f41dSRémi Denis-Courmont
277f8ff6028SRemi Denis-Courmont /* notify Phonet of device events */
phonet_device_notify(struct notifier_block * me,unsigned long what,void * ptr)278f8ff6028SRemi Denis-Courmont static int phonet_device_notify(struct notifier_block *me, unsigned long what,
279351638e7SJiri Pirko void *ptr)
280f8ff6028SRemi Denis-Courmont {
281351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr);
282f8ff6028SRemi Denis-Courmont
283f5bb1c55SRémi Denis-Courmont switch (what) {
284f5bb1c55SRémi Denis-Courmont case NETDEV_REGISTER:
285f5bb1c55SRémi Denis-Courmont if (dev->type == ARPHRD_PHONET)
286f5bb1c55SRémi Denis-Courmont phonet_device_autoconf(dev);
287f5bb1c55SRémi Denis-Courmont break;
288f5bb1c55SRémi Denis-Courmont case NETDEV_UNREGISTER:
2892be6fa4cSRémi Denis-Courmont phonet_device_destroy(dev);
290f062f41dSRémi Denis-Courmont phonet_route_autodel(dev);
291f5bb1c55SRémi Denis-Courmont break;
292f5bb1c55SRémi Denis-Courmont }
293f8ff6028SRemi Denis-Courmont return 0;
294f8ff6028SRemi Denis-Courmont
295f8ff6028SRemi Denis-Courmont }
296f8ff6028SRemi Denis-Courmont
297f8ff6028SRemi Denis-Courmont static struct notifier_block phonet_device_notifier = {
298f8ff6028SRemi Denis-Courmont .notifier_call = phonet_device_notify,
299f8ff6028SRemi Denis-Courmont .priority = 0,
300f8ff6028SRemi Denis-Courmont };
301f8ff6028SRemi Denis-Courmont
3029a3b7a42Sremi.denis-courmont@nokia /* Per-namespace Phonet devices handling */
phonet_init_net(struct net * net)3032c8c1e72SAlexey Dobriyan static int __net_init phonet_init_net(struct net *net)
3049a3b7a42Sremi.denis-courmont@nokia {
3050db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(net);
3069a3b7a42Sremi.denis-courmont@nokia
307c3506372SChristoph Hellwig if (!proc_create_net("phonet", 0, net->proc_net, &pn_sock_seq_ops,
308c3506372SChristoph Hellwig sizeof(struct seq_net_private)))
309c1dc13e9SRémi Denis-Courmont return -ENOMEM;
310c1dc13e9SRémi Denis-Courmont
3119a3b7a42Sremi.denis-courmont@nokia INIT_LIST_HEAD(&pnn->pndevs.list);
312eeb74a9dSRémi Denis-Courmont mutex_init(&pnn->pndevs.lock);
31388880135SRémi Denis-Courmont mutex_init(&pnn->routes.lock);
3149a3b7a42Sremi.denis-courmont@nokia return 0;
3159a3b7a42Sremi.denis-courmont@nokia }
3169a3b7a42Sremi.denis-courmont@nokia
phonet_exit_net(struct net * net)3172c8c1e72SAlexey Dobriyan static void __net_exit phonet_exit_net(struct net *net)
3189a3b7a42Sremi.denis-courmont@nokia {
319ae61e8cdSVasily Averin struct phonet_net *pnn = phonet_pernet(net);
320ae61e8cdSVasily Averin
321ece31ffdSGao feng remove_proc_entry("phonet", net->proc_net);
322ae61e8cdSVasily Averin WARN_ON_ONCE(!list_empty(&pnn->pndevs.list));
3239a3b7a42Sremi.denis-courmont@nokia }
3249a3b7a42Sremi.denis-courmont@nokia
3259a3b7a42Sremi.denis-courmont@nokia static struct pernet_operations phonet_net_ops = {
3269a3b7a42Sremi.denis-courmont@nokia .init = phonet_init_net,
3279a3b7a42Sremi.denis-courmont@nokia .exit = phonet_exit_net,
328d2b3eb63SEric W. Biederman .id = &phonet_net_id,
329d2b3eb63SEric W. Biederman .size = sizeof(struct phonet_net),
3309a3b7a42Sremi.denis-courmont@nokia };
3319a3b7a42Sremi.denis-courmont@nokia
332f8ff6028SRemi Denis-Courmont /* Initialize Phonet devices list */
phonet_device_init(void)33376e02cf6Sremi.denis-courmont@nokia int __init phonet_device_init(void)
334f8ff6028SRemi Denis-Courmont {
33503478756SEric W. Biederman int err = register_pernet_subsys(&phonet_net_ops);
3369a3b7a42Sremi.denis-courmont@nokia if (err)
3379a3b7a42Sremi.denis-courmont@nokia return err;
338660f706dSremi.denis-courmont@nokia
339c3506372SChristoph Hellwig proc_create_net("pnresource", 0, init_net.proc_net, &pn_res_seq_ops,
340c3506372SChristoph Hellwig sizeof(struct seq_net_private));
341f8ff6028SRemi Denis-Courmont register_netdevice_notifier(&phonet_device_notifier);
342660f706dSremi.denis-courmont@nokia err = phonet_netlink_register();
343660f706dSremi.denis-courmont@nokia if (err)
344660f706dSremi.denis-courmont@nokia phonet_device_exit();
345660f706dSremi.denis-courmont@nokia return err;
346f8ff6028SRemi Denis-Courmont }
347f8ff6028SRemi Denis-Courmont
phonet_device_exit(void)348f8ff6028SRemi Denis-Courmont void phonet_device_exit(void)
349f8ff6028SRemi Denis-Courmont {
350f8ff6028SRemi Denis-Courmont rtnl_unregister_all(PF_PHONET);
3516530e0feSremi.denis-courmont@nokia unregister_netdevice_notifier(&phonet_device_notifier);
35203478756SEric W. Biederman unregister_pernet_subsys(&phonet_net_ops);
353ece31ffdSGao feng remove_proc_entry("pnresource", init_net.proc_net);
354f8ff6028SRemi Denis-Courmont }
35555748ac0SRémi Denis-Courmont
phonet_route_add(struct net_device * dev,u8 daddr)35655748ac0SRémi Denis-Courmont int phonet_route_add(struct net_device *dev, u8 daddr)
35755748ac0SRémi Denis-Courmont {
3580db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(dev_net(dev));
35955748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes;
36055748ac0SRémi Denis-Courmont int err = -EEXIST;
36155748ac0SRémi Denis-Courmont
36255748ac0SRémi Denis-Courmont daddr = daddr >> 2;
36388880135SRémi Denis-Courmont mutex_lock(&routes->lock);
36455748ac0SRémi Denis-Courmont if (routes->table[daddr] == NULL) {
365cf778b00SEric Dumazet rcu_assign_pointer(routes->table[daddr], dev);
36655748ac0SRémi Denis-Courmont dev_hold(dev);
36755748ac0SRémi Denis-Courmont err = 0;
36855748ac0SRémi Denis-Courmont }
36988880135SRémi Denis-Courmont mutex_unlock(&routes->lock);
37055748ac0SRémi Denis-Courmont return err;
37155748ac0SRémi Denis-Courmont }
37255748ac0SRémi Denis-Courmont
phonet_route_del(struct net_device * dev,u8 daddr)37355748ac0SRémi Denis-Courmont int phonet_route_del(struct net_device *dev, u8 daddr)
37455748ac0SRémi Denis-Courmont {
3750db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(dev_net(dev));
37655748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes;
37755748ac0SRémi Denis-Courmont
37855748ac0SRémi Denis-Courmont daddr = daddr >> 2;
37988880135SRémi Denis-Courmont mutex_lock(&routes->lock);
38079952bcaSFabian Frederick if (rcu_access_pointer(routes->table[daddr]) == dev)
381a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(routes->table[daddr], NULL);
38288880135SRémi Denis-Courmont else
38388880135SRémi Denis-Courmont dev = NULL;
38488880135SRémi Denis-Courmont mutex_unlock(&routes->lock);
38588880135SRémi Denis-Courmont
38688880135SRémi Denis-Courmont if (!dev)
38788880135SRémi Denis-Courmont return -ENOENT;
38888880135SRémi Denis-Courmont synchronize_rcu();
38955748ac0SRémi Denis-Courmont dev_put(dev);
39088880135SRémi Denis-Courmont return 0;
39155748ac0SRémi Denis-Courmont }
39255748ac0SRémi Denis-Courmont
phonet_route_get_rcu(struct net * net,u8 daddr)393e67f88ddSEric Dumazet struct net_device *phonet_route_get_rcu(struct net *net, u8 daddr)
39455748ac0SRémi Denis-Courmont {
3950db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(net);
39655748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes;
39755748ac0SRémi Denis-Courmont struct net_device *dev;
39855748ac0SRémi Denis-Courmont
39955748ac0SRémi Denis-Courmont daddr >>= 2;
40088880135SRémi Denis-Courmont dev = rcu_dereference(routes->table[daddr]);
40155748ac0SRémi Denis-Courmont return dev;
40255748ac0SRémi Denis-Courmont }
40355748ac0SRémi Denis-Courmont
phonet_route_output(struct net * net,u8 daddr)40455748ac0SRémi Denis-Courmont struct net_device *phonet_route_output(struct net *net, u8 daddr)
40555748ac0SRémi Denis-Courmont {
4060db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(net);
40755748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes;
40855748ac0SRémi Denis-Courmont struct net_device *dev;
40955748ac0SRémi Denis-Courmont
41088880135SRémi Denis-Courmont daddr >>= 2;
41188880135SRémi Denis-Courmont rcu_read_lock();
41288880135SRémi Denis-Courmont dev = rcu_dereference(routes->table[daddr]);
41355748ac0SRémi Denis-Courmont dev_hold(dev);
41488880135SRémi Denis-Courmont rcu_read_unlock();
41555748ac0SRémi Denis-Courmont
41655748ac0SRémi Denis-Courmont if (!dev)
41755748ac0SRémi Denis-Courmont dev = phonet_device_get(net); /* Default route */
41855748ac0SRémi Denis-Courmont return dev;
41955748ac0SRémi Denis-Courmont }
420