xref: /openbmc/linux/net/phonet/pn_dev.c (revision 1160dfa1)
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