xref: /openbmc/linux/net/phonet/pn_dev.c (revision 4747c1a8)
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 
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 
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. */
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 
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 
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 
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 
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 	if (dev)
126f8ff6028SRemi Denis-Courmont 		dev_hold(dev);
127eeb74a9dSRémi Denis-Courmont 	rcu_read_unlock();
128f8ff6028SRemi Denis-Courmont 	return dev;
129f8ff6028SRemi Denis-Courmont }
130f8ff6028SRemi Denis-Courmont 
131f8ff6028SRemi Denis-Courmont int phonet_address_add(struct net_device *dev, u8 addr)
132f8ff6028SRemi Denis-Courmont {
1339a3b7a42Sremi.denis-courmont@nokia 	struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
134f8ff6028SRemi Denis-Courmont 	struct phonet_device *pnd;
135f8ff6028SRemi Denis-Courmont 	int err = 0;
136f8ff6028SRemi Denis-Courmont 
137eeb74a9dSRémi Denis-Courmont 	mutex_lock(&pndevs->lock);
138f8ff6028SRemi Denis-Courmont 	/* Find or create Phonet-specific device data */
139f8ff6028SRemi Denis-Courmont 	pnd = __phonet_get(dev);
140f8ff6028SRemi Denis-Courmont 	if (pnd == NULL)
141f8ff6028SRemi Denis-Courmont 		pnd = __phonet_device_alloc(dev);
142f8ff6028SRemi Denis-Courmont 	if (unlikely(pnd == NULL))
143f8ff6028SRemi Denis-Courmont 		err = -ENOMEM;
144f8ff6028SRemi Denis-Courmont 	else if (test_and_set_bit(addr >> 2, pnd->addrs))
145f8ff6028SRemi Denis-Courmont 		err = -EEXIST;
146eeb74a9dSRémi Denis-Courmont 	mutex_unlock(&pndevs->lock);
147f8ff6028SRemi Denis-Courmont 	return err;
148f8ff6028SRemi Denis-Courmont }
149f8ff6028SRemi Denis-Courmont 
150f8ff6028SRemi Denis-Courmont int phonet_address_del(struct net_device *dev, u8 addr)
151f8ff6028SRemi Denis-Courmont {
1529a3b7a42Sremi.denis-courmont@nokia 	struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
153f8ff6028SRemi Denis-Courmont 	struct phonet_device *pnd;
154f8ff6028SRemi Denis-Courmont 	int err = 0;
155f8ff6028SRemi Denis-Courmont 
156eeb74a9dSRémi Denis-Courmont 	mutex_lock(&pndevs->lock);
157f8ff6028SRemi Denis-Courmont 	pnd = __phonet_get(dev);
158eeb74a9dSRémi Denis-Courmont 	if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) {
159f8ff6028SRemi Denis-Courmont 		err = -EADDRNOTAVAIL;
160eeb74a9dSRémi Denis-Courmont 		pnd = NULL;
161eeb74a9dSRémi Denis-Courmont 	} else if (bitmap_empty(pnd->addrs, 64))
162eeb74a9dSRémi Denis-Courmont 		list_del_rcu(&pnd->list);
163eeb74a9dSRémi Denis-Courmont 	else
164eeb74a9dSRémi Denis-Courmont 		pnd = NULL;
165eeb74a9dSRémi Denis-Courmont 	mutex_unlock(&pndevs->lock);
166eeb74a9dSRémi Denis-Courmont 
16788e7594aSJiri Pirko 	if (pnd)
1687e113a9cSLai Jiangshan 		kfree_rcu(pnd, rcu);
16988e7594aSJiri Pirko 
170f8ff6028SRemi Denis-Courmont 	return err;
171f8ff6028SRemi Denis-Courmont }
172f8ff6028SRemi Denis-Courmont 
173f8ff6028SRemi Denis-Courmont /* Gets a source address toward a destination, through a interface. */
17455748ac0SRémi Denis-Courmont u8 phonet_address_get(struct net_device *dev, u8 daddr)
175f8ff6028SRemi Denis-Courmont {
176f8ff6028SRemi Denis-Courmont 	struct phonet_device *pnd;
17755748ac0SRémi Denis-Courmont 	u8 saddr;
178f8ff6028SRemi Denis-Courmont 
179eeb74a9dSRémi Denis-Courmont 	rcu_read_lock();
180eeb74a9dSRémi Denis-Courmont 	pnd = __phonet_get_rcu(dev);
181f8ff6028SRemi Denis-Courmont 	if (pnd) {
182f8ff6028SRemi Denis-Courmont 		BUG_ON(bitmap_empty(pnd->addrs, 64));
183f8ff6028SRemi Denis-Courmont 
184f8ff6028SRemi Denis-Courmont 		/* Use same source address as destination, if possible */
18555748ac0SRémi Denis-Courmont 		if (test_bit(daddr >> 2, pnd->addrs))
18655748ac0SRémi Denis-Courmont 			saddr = daddr;
18755748ac0SRémi Denis-Courmont 		else
18855748ac0SRémi Denis-Courmont 			saddr = find_first_bit(pnd->addrs, 64) << 2;
189f8ff6028SRemi Denis-Courmont 	} else
19055748ac0SRémi Denis-Courmont 		saddr = PN_NO_ADDR;
191eeb74a9dSRémi Denis-Courmont 	rcu_read_unlock();
19255748ac0SRémi Denis-Courmont 
19355748ac0SRémi Denis-Courmont 	if (saddr == PN_NO_ADDR) {
19455748ac0SRémi Denis-Courmont 		/* Fallback to another device */
19555748ac0SRémi Denis-Courmont 		struct net_device *def_dev;
19655748ac0SRémi Denis-Courmont 
19755748ac0SRémi Denis-Courmont 		def_dev = phonet_device_get(dev_net(dev));
19855748ac0SRémi Denis-Courmont 		if (def_dev) {
19955748ac0SRémi Denis-Courmont 			if (def_dev != dev)
20055748ac0SRémi Denis-Courmont 				saddr = phonet_address_get(def_dev, daddr);
20155748ac0SRémi Denis-Courmont 			dev_put(def_dev);
20255748ac0SRémi Denis-Courmont 		}
20355748ac0SRémi Denis-Courmont 	}
20455748ac0SRémi Denis-Courmont 	return saddr;
205f8ff6028SRemi Denis-Courmont }
206f8ff6028SRemi Denis-Courmont 
20752404881SRémi Denis-Courmont int phonet_address_lookup(struct net *net, u8 addr)
208f8ff6028SRemi Denis-Courmont {
2099a3b7a42Sremi.denis-courmont@nokia 	struct phonet_device_list *pndevs = phonet_device_list(net);
210f8ff6028SRemi Denis-Courmont 	struct phonet_device *pnd;
2119a3b7a42Sremi.denis-courmont@nokia 	int err = -EADDRNOTAVAIL;
212f8ff6028SRemi Denis-Courmont 
213eeb74a9dSRémi Denis-Courmont 	rcu_read_lock();
214eeb74a9dSRémi Denis-Courmont 	list_for_each_entry_rcu(pnd, &pndevs->list, list) {
215f8ff6028SRemi Denis-Courmont 		/* Don't allow unregistering devices! */
216f8ff6028SRemi Denis-Courmont 		if ((pnd->netdev->reg_state != NETREG_REGISTERED) ||
217f8ff6028SRemi Denis-Courmont 				((pnd->netdev->flags & IFF_UP)) != IFF_UP)
218f8ff6028SRemi Denis-Courmont 			continue;
219f8ff6028SRemi Denis-Courmont 
220f8ff6028SRemi Denis-Courmont 		if (test_bit(addr >> 2, pnd->addrs)) {
2219a3b7a42Sremi.denis-courmont@nokia 			err = 0;
2229a3b7a42Sremi.denis-courmont@nokia 			goto found;
223f8ff6028SRemi Denis-Courmont 		}
224f8ff6028SRemi Denis-Courmont 	}
2259a3b7a42Sremi.denis-courmont@nokia found:
226eeb74a9dSRémi Denis-Courmont 	rcu_read_unlock();
2279a3b7a42Sremi.denis-courmont@nokia 	return err;
228f8ff6028SRemi Denis-Courmont }
229f8ff6028SRemi Denis-Courmont 
230f5bb1c55SRémi Denis-Courmont /* automatically configure a Phonet device, if supported */
231f5bb1c55SRémi Denis-Courmont static int phonet_device_autoconf(struct net_device *dev)
232f5bb1c55SRémi Denis-Courmont {
233f5bb1c55SRémi Denis-Courmont 	struct if_phonet_req req;
234f5bb1c55SRémi Denis-Courmont 	int ret;
235f5bb1c55SRémi Denis-Courmont 
236*4747c1a8SArnd Bergmann 	if (!dev->netdev_ops->ndo_siocdevprivate)
237f5bb1c55SRémi Denis-Courmont 		return -EOPNOTSUPP;
238f5bb1c55SRémi Denis-Courmont 
239*4747c1a8SArnd Bergmann 	ret = dev->netdev_ops->ndo_siocdevprivate(dev, (struct ifreq *)&req,
240*4747c1a8SArnd Bergmann 						  NULL, SIOCPNGAUTOCONF);
241f5bb1c55SRémi Denis-Courmont 	if (ret < 0)
242f5bb1c55SRémi Denis-Courmont 		return ret;
243b11b5165SRémi Denis-Courmont 
244b11b5165SRémi Denis-Courmont 	ASSERT_RTNL();
245b11b5165SRémi Denis-Courmont 	ret = phonet_address_add(dev, req.ifr_phonet_autoconf.device);
246b11b5165SRémi Denis-Courmont 	if (ret)
247b11b5165SRémi Denis-Courmont 		return ret;
248b11b5165SRémi Denis-Courmont 	phonet_address_notify(RTM_NEWADDR, dev,
249b11b5165SRémi Denis-Courmont 				req.ifr_phonet_autoconf.device);
250b11b5165SRémi Denis-Courmont 	return 0;
251f5bb1c55SRémi Denis-Courmont }
252f5bb1c55SRémi Denis-Courmont 
253f062f41dSRémi Denis-Courmont static void phonet_route_autodel(struct net_device *dev)
254f062f41dSRémi Denis-Courmont {
2550db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(dev_net(dev));
25695c96174SEric Dumazet 	unsigned int i;
257f062f41dSRémi Denis-Courmont 	DECLARE_BITMAP(deleted, 64);
258f062f41dSRémi Denis-Courmont 
259f062f41dSRémi Denis-Courmont 	/* Remove left-over Phonet routes */
260f062f41dSRémi Denis-Courmont 	bitmap_zero(deleted, 64);
26188880135SRémi Denis-Courmont 	mutex_lock(&pnn->routes.lock);
262f062f41dSRémi Denis-Courmont 	for (i = 0; i < 64; i++)
26379952bcaSFabian Frederick 		if (rcu_access_pointer(pnn->routes.table[i]) == dev) {
264a9b3cd7fSStephen Hemminger 			RCU_INIT_POINTER(pnn->routes.table[i], NULL);
265f062f41dSRémi Denis-Courmont 			set_bit(i, deleted);
26688880135SRémi Denis-Courmont 		}
26788880135SRémi Denis-Courmont 	mutex_unlock(&pnn->routes.lock);
26888880135SRémi Denis-Courmont 
26988880135SRémi Denis-Courmont 	if (bitmap_empty(deleted, 64))
27088880135SRémi Denis-Courmont 		return; /* short-circuit RCU */
27188880135SRémi Denis-Courmont 	synchronize_rcu();
2726a499b24SAkinobu Mita 	for_each_set_bit(i, deleted, 64) {
27388880135SRémi Denis-Courmont 		rtm_phonet_notify(RTM_DELROUTE, dev, i);
274f062f41dSRémi Denis-Courmont 		dev_put(dev);
275f062f41dSRémi Denis-Courmont 	}
276f062f41dSRémi Denis-Courmont }
277f062f41dSRémi Denis-Courmont 
278f8ff6028SRemi Denis-Courmont /* notify Phonet of device events */
279f8ff6028SRemi Denis-Courmont static int phonet_device_notify(struct notifier_block *me, unsigned long what,
280351638e7SJiri Pirko 				void *ptr)
281f8ff6028SRemi Denis-Courmont {
282351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
283f8ff6028SRemi Denis-Courmont 
284f5bb1c55SRémi Denis-Courmont 	switch (what) {
285f5bb1c55SRémi Denis-Courmont 	case NETDEV_REGISTER:
286f5bb1c55SRémi Denis-Courmont 		if (dev->type == ARPHRD_PHONET)
287f5bb1c55SRémi Denis-Courmont 			phonet_device_autoconf(dev);
288f5bb1c55SRémi Denis-Courmont 		break;
289f5bb1c55SRémi Denis-Courmont 	case NETDEV_UNREGISTER:
2902be6fa4cSRémi Denis-Courmont 		phonet_device_destroy(dev);
291f062f41dSRémi Denis-Courmont 		phonet_route_autodel(dev);
292f5bb1c55SRémi Denis-Courmont 		break;
293f5bb1c55SRémi Denis-Courmont 	}
294f8ff6028SRemi Denis-Courmont 	return 0;
295f8ff6028SRemi Denis-Courmont 
296f8ff6028SRemi Denis-Courmont }
297f8ff6028SRemi Denis-Courmont 
298f8ff6028SRemi Denis-Courmont static struct notifier_block phonet_device_notifier = {
299f8ff6028SRemi Denis-Courmont 	.notifier_call = phonet_device_notify,
300f8ff6028SRemi Denis-Courmont 	.priority = 0,
301f8ff6028SRemi Denis-Courmont };
302f8ff6028SRemi Denis-Courmont 
3039a3b7a42Sremi.denis-courmont@nokia /* Per-namespace Phonet devices handling */
3042c8c1e72SAlexey Dobriyan static int __net_init phonet_init_net(struct net *net)
3059a3b7a42Sremi.denis-courmont@nokia {
3060db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(net);
3079a3b7a42Sremi.denis-courmont@nokia 
308c3506372SChristoph Hellwig 	if (!proc_create_net("phonet", 0, net->proc_net, &pn_sock_seq_ops,
309c3506372SChristoph Hellwig 			sizeof(struct seq_net_private)))
310c1dc13e9SRémi Denis-Courmont 		return -ENOMEM;
311c1dc13e9SRémi Denis-Courmont 
3129a3b7a42Sremi.denis-courmont@nokia 	INIT_LIST_HEAD(&pnn->pndevs.list);
313eeb74a9dSRémi Denis-Courmont 	mutex_init(&pnn->pndevs.lock);
31488880135SRémi Denis-Courmont 	mutex_init(&pnn->routes.lock);
3159a3b7a42Sremi.denis-courmont@nokia 	return 0;
3169a3b7a42Sremi.denis-courmont@nokia }
3179a3b7a42Sremi.denis-courmont@nokia 
3182c8c1e72SAlexey Dobriyan static void __net_exit phonet_exit_net(struct net *net)
3199a3b7a42Sremi.denis-courmont@nokia {
320ae61e8cdSVasily Averin 	struct phonet_net *pnn = phonet_pernet(net);
321ae61e8cdSVasily Averin 
322ece31ffdSGao feng 	remove_proc_entry("phonet", net->proc_net);
323ae61e8cdSVasily Averin 	WARN_ON_ONCE(!list_empty(&pnn->pndevs.list));
3249a3b7a42Sremi.denis-courmont@nokia }
3259a3b7a42Sremi.denis-courmont@nokia 
3269a3b7a42Sremi.denis-courmont@nokia static struct pernet_operations phonet_net_ops = {
3279a3b7a42Sremi.denis-courmont@nokia 	.init = phonet_init_net,
3289a3b7a42Sremi.denis-courmont@nokia 	.exit = phonet_exit_net,
329d2b3eb63SEric W. Biederman 	.id   = &phonet_net_id,
330d2b3eb63SEric W. Biederman 	.size = sizeof(struct phonet_net),
3319a3b7a42Sremi.denis-courmont@nokia };
3329a3b7a42Sremi.denis-courmont@nokia 
333f8ff6028SRemi Denis-Courmont /* Initialize Phonet devices list */
33476e02cf6Sremi.denis-courmont@nokia int __init phonet_device_init(void)
335f8ff6028SRemi Denis-Courmont {
33603478756SEric W. Biederman 	int err = register_pernet_subsys(&phonet_net_ops);
3379a3b7a42Sremi.denis-courmont@nokia 	if (err)
3389a3b7a42Sremi.denis-courmont@nokia 		return err;
339660f706dSremi.denis-courmont@nokia 
340c3506372SChristoph Hellwig 	proc_create_net("pnresource", 0, init_net.proc_net, &pn_res_seq_ops,
341c3506372SChristoph Hellwig 			sizeof(struct seq_net_private));
342f8ff6028SRemi Denis-Courmont 	register_netdevice_notifier(&phonet_device_notifier);
343660f706dSremi.denis-courmont@nokia 	err = phonet_netlink_register();
344660f706dSremi.denis-courmont@nokia 	if (err)
345660f706dSremi.denis-courmont@nokia 		phonet_device_exit();
346660f706dSremi.denis-courmont@nokia 	return err;
347f8ff6028SRemi Denis-Courmont }
348f8ff6028SRemi Denis-Courmont 
349f8ff6028SRemi Denis-Courmont void phonet_device_exit(void)
350f8ff6028SRemi Denis-Courmont {
351f8ff6028SRemi Denis-Courmont 	rtnl_unregister_all(PF_PHONET);
3526530e0feSremi.denis-courmont@nokia 	unregister_netdevice_notifier(&phonet_device_notifier);
35303478756SEric W. Biederman 	unregister_pernet_subsys(&phonet_net_ops);
354ece31ffdSGao feng 	remove_proc_entry("pnresource", init_net.proc_net);
355f8ff6028SRemi Denis-Courmont }
35655748ac0SRémi Denis-Courmont 
35755748ac0SRémi Denis-Courmont int phonet_route_add(struct net_device *dev, u8 daddr)
35855748ac0SRémi Denis-Courmont {
3590db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(dev_net(dev));
36055748ac0SRémi Denis-Courmont 	struct phonet_routes *routes = &pnn->routes;
36155748ac0SRémi Denis-Courmont 	int err = -EEXIST;
36255748ac0SRémi Denis-Courmont 
36355748ac0SRémi Denis-Courmont 	daddr = daddr >> 2;
36488880135SRémi Denis-Courmont 	mutex_lock(&routes->lock);
36555748ac0SRémi Denis-Courmont 	if (routes->table[daddr] == NULL) {
366cf778b00SEric Dumazet 		rcu_assign_pointer(routes->table[daddr], dev);
36755748ac0SRémi Denis-Courmont 		dev_hold(dev);
36855748ac0SRémi Denis-Courmont 		err = 0;
36955748ac0SRémi Denis-Courmont 	}
37088880135SRémi Denis-Courmont 	mutex_unlock(&routes->lock);
37155748ac0SRémi Denis-Courmont 	return err;
37255748ac0SRémi Denis-Courmont }
37355748ac0SRémi Denis-Courmont 
37455748ac0SRémi Denis-Courmont int phonet_route_del(struct net_device *dev, u8 daddr)
37555748ac0SRémi Denis-Courmont {
3760db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(dev_net(dev));
37755748ac0SRémi Denis-Courmont 	struct phonet_routes *routes = &pnn->routes;
37855748ac0SRémi Denis-Courmont 
37955748ac0SRémi Denis-Courmont 	daddr = daddr >> 2;
38088880135SRémi Denis-Courmont 	mutex_lock(&routes->lock);
38179952bcaSFabian Frederick 	if (rcu_access_pointer(routes->table[daddr]) == dev)
382a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(routes->table[daddr], NULL);
38388880135SRémi Denis-Courmont 	else
38488880135SRémi Denis-Courmont 		dev = NULL;
38588880135SRémi Denis-Courmont 	mutex_unlock(&routes->lock);
38688880135SRémi Denis-Courmont 
38788880135SRémi Denis-Courmont 	if (!dev)
38888880135SRémi Denis-Courmont 		return -ENOENT;
38988880135SRémi Denis-Courmont 	synchronize_rcu();
39055748ac0SRémi Denis-Courmont 	dev_put(dev);
39188880135SRémi Denis-Courmont 	return 0;
39255748ac0SRémi Denis-Courmont }
39355748ac0SRémi Denis-Courmont 
394e67f88ddSEric Dumazet struct net_device *phonet_route_get_rcu(struct net *net, u8 daddr)
39555748ac0SRémi Denis-Courmont {
3960db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(net);
39755748ac0SRémi Denis-Courmont 	struct phonet_routes *routes = &pnn->routes;
39855748ac0SRémi Denis-Courmont 	struct net_device *dev;
39955748ac0SRémi Denis-Courmont 
40055748ac0SRémi Denis-Courmont 	daddr >>= 2;
40188880135SRémi Denis-Courmont 	dev = rcu_dereference(routes->table[daddr]);
40255748ac0SRémi Denis-Courmont 	return dev;
40355748ac0SRémi Denis-Courmont }
40455748ac0SRémi Denis-Courmont 
40555748ac0SRémi Denis-Courmont struct net_device *phonet_route_output(struct net *net, u8 daddr)
40655748ac0SRémi Denis-Courmont {
4070db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(net);
40855748ac0SRémi Denis-Courmont 	struct phonet_routes *routes = &pnn->routes;
40955748ac0SRémi Denis-Courmont 	struct net_device *dev;
41055748ac0SRémi Denis-Courmont 
41188880135SRémi Denis-Courmont 	daddr >>= 2;
41288880135SRémi Denis-Courmont 	rcu_read_lock();
41388880135SRémi Denis-Courmont 	dev = rcu_dereference(routes->table[daddr]);
41455748ac0SRémi Denis-Courmont 	if (dev)
41555748ac0SRémi Denis-Courmont 		dev_hold(dev);
41688880135SRémi Denis-Courmont 	rcu_read_unlock();
41755748ac0SRémi Denis-Courmont 
41855748ac0SRémi Denis-Courmont 	if (!dev)
41955748ac0SRémi Denis-Courmont 		dev = phonet_device_get(net); /* Default route */
42055748ac0SRémi Denis-Courmont 	return dev;
42155748ac0SRémi Denis-Courmont }
422