xref: /openbmc/linux/net/phonet/pn_dev.c (revision c7d03a00)
1f8ff6028SRemi Denis-Courmont /*
2f8ff6028SRemi Denis-Courmont  * File: pn_dev.c
3f8ff6028SRemi Denis-Courmont  *
4f8ff6028SRemi Denis-Courmont  * Phonet network device
5f8ff6028SRemi Denis-Courmont  *
6f8ff6028SRemi Denis-Courmont  * Copyright (C) 2008 Nokia Corporation.
7f8ff6028SRemi Denis-Courmont  *
831fdc555SRémi Denis-Courmont  * Authors: Sakari Ailus <sakari.ailus@nokia.com>
931fdc555SRémi Denis-Courmont  *          Rémi Denis-Courmont
10f8ff6028SRemi Denis-Courmont  *
11f8ff6028SRemi Denis-Courmont  * This program is free software; you can redistribute it and/or
12f8ff6028SRemi Denis-Courmont  * modify it under the terms of the GNU General Public License
13f8ff6028SRemi Denis-Courmont  * version 2 as published by the Free Software Foundation.
14f8ff6028SRemi Denis-Courmont  *
15f8ff6028SRemi Denis-Courmont  * This program is distributed in the hope that it will be useful, but
16f8ff6028SRemi Denis-Courmont  * WITHOUT ANY WARRANTY; without even the implied warranty of
17f8ff6028SRemi Denis-Courmont  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18f8ff6028SRemi Denis-Courmont  * General Public License for more details.
19f8ff6028SRemi Denis-Courmont  *
20f8ff6028SRemi Denis-Courmont  * You should have received a copy of the GNU General Public License
21f8ff6028SRemi Denis-Courmont  * along with this program; if not, write to the Free Software
22f8ff6028SRemi Denis-Courmont  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23f8ff6028SRemi Denis-Courmont  * 02110-1301 USA
24f8ff6028SRemi Denis-Courmont  */
25f8ff6028SRemi Denis-Courmont 
26f8ff6028SRemi Denis-Courmont #include <linux/kernel.h>
27f8ff6028SRemi Denis-Courmont #include <linux/net.h>
285a0e3ad6STejun Heo #include <linux/slab.h>
29f8ff6028SRemi Denis-Courmont #include <linux/netdevice.h>
30f8ff6028SRemi Denis-Courmont #include <linux/phonet.h>
31421d20a3SDavid S. Miller #include <linux/proc_fs.h>
32f5bb1c55SRémi Denis-Courmont #include <linux/if_arp.h>
33f8ff6028SRemi Denis-Courmont #include <net/sock.h>
349a3b7a42Sremi.denis-courmont@nokia #include <net/netns/generic.h>
35f8ff6028SRemi Denis-Courmont #include <net/phonet/pn_dev.h>
36f8ff6028SRemi Denis-Courmont 
3755748ac0SRémi Denis-Courmont struct phonet_routes {
3888880135SRémi Denis-Courmont 	struct mutex		lock;
3979952bcaSFabian Frederick 	struct net_device __rcu	*table[64];
4055748ac0SRémi Denis-Courmont };
4155748ac0SRémi Denis-Courmont 
429a3b7a42Sremi.denis-courmont@nokia struct phonet_net {
439a3b7a42Sremi.denis-courmont@nokia 	struct phonet_device_list pndevs;
4455748ac0SRémi Denis-Courmont 	struct phonet_routes routes;
45f8ff6028SRemi Denis-Courmont };
46f8ff6028SRemi Denis-Courmont 
47c7d03a00SAlexey Dobriyan static unsigned int phonet_net_id __read_mostly;
489a3b7a42Sremi.denis-courmont@nokia 
490db3f0f4SJiri Pirko static struct phonet_net *phonet_pernet(struct net *net)
500db3f0f4SJiri Pirko {
510db3f0f4SJiri Pirko 	BUG_ON(!net);
520db3f0f4SJiri Pirko 
530db3f0f4SJiri Pirko 	return net_generic(net, phonet_net_id);
540db3f0f4SJiri Pirko }
550db3f0f4SJiri Pirko 
569a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *phonet_device_list(struct net *net)
579a3b7a42Sremi.denis-courmont@nokia {
580db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(net);
599a3b7a42Sremi.denis-courmont@nokia 	return &pnn->pndevs;
609a3b7a42Sremi.denis-courmont@nokia }
619a3b7a42Sremi.denis-courmont@nokia 
62f8ff6028SRemi Denis-Courmont /* Allocate new Phonet device. */
63f8ff6028SRemi Denis-Courmont static struct phonet_device *__phonet_device_alloc(struct net_device *dev)
64f8ff6028SRemi Denis-Courmont {
659a3b7a42Sremi.denis-courmont@nokia 	struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
66f8ff6028SRemi Denis-Courmont 	struct phonet_device *pnd = kmalloc(sizeof(*pnd), GFP_ATOMIC);
67f8ff6028SRemi Denis-Courmont 	if (pnd == NULL)
68f8ff6028SRemi Denis-Courmont 		return NULL;
69f8ff6028SRemi Denis-Courmont 	pnd->netdev = dev;
70f8ff6028SRemi Denis-Courmont 	bitmap_zero(pnd->addrs, 64);
71f8ff6028SRemi Denis-Courmont 
72eeb74a9dSRémi Denis-Courmont 	BUG_ON(!mutex_is_locked(&pndevs->lock));
73eeb74a9dSRémi Denis-Courmont 	list_add_rcu(&pnd->list, &pndevs->list);
74f8ff6028SRemi Denis-Courmont 	return pnd;
75f8ff6028SRemi Denis-Courmont }
76f8ff6028SRemi Denis-Courmont 
77f8ff6028SRemi Denis-Courmont static struct phonet_device *__phonet_get(struct net_device *dev)
78f8ff6028SRemi Denis-Courmont {
799a3b7a42Sremi.denis-courmont@nokia 	struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
80f8ff6028SRemi Denis-Courmont 	struct phonet_device *pnd;
81f8ff6028SRemi Denis-Courmont 
82eeb74a9dSRémi Denis-Courmont 	BUG_ON(!mutex_is_locked(&pndevs->lock));
839a3b7a42Sremi.denis-courmont@nokia 	list_for_each_entry(pnd, &pndevs->list, list) {
84f8ff6028SRemi Denis-Courmont 		if (pnd->netdev == dev)
85f8ff6028SRemi Denis-Courmont 			return pnd;
86f8ff6028SRemi Denis-Courmont 	}
87f8ff6028SRemi Denis-Courmont 	return NULL;
88f8ff6028SRemi Denis-Courmont }
89f8ff6028SRemi Denis-Courmont 
90eeb74a9dSRémi Denis-Courmont static struct phonet_device *__phonet_get_rcu(struct net_device *dev)
91eeb74a9dSRémi Denis-Courmont {
92eeb74a9dSRémi Denis-Courmont 	struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
93eeb74a9dSRémi Denis-Courmont 	struct phonet_device *pnd;
94eeb74a9dSRémi Denis-Courmont 
95eeb74a9dSRémi Denis-Courmont 	list_for_each_entry_rcu(pnd, &pndevs->list, list) {
96eeb74a9dSRémi Denis-Courmont 		if (pnd->netdev == dev)
97eeb74a9dSRémi Denis-Courmont 			return pnd;
98eeb74a9dSRémi Denis-Courmont 	}
99eeb74a9dSRémi Denis-Courmont 	return NULL;
100eeb74a9dSRémi Denis-Courmont }
101eeb74a9dSRémi Denis-Courmont 
1022be6fa4cSRémi Denis-Courmont static void phonet_device_destroy(struct net_device *dev)
103f8ff6028SRemi Denis-Courmont {
1042be6fa4cSRémi Denis-Courmont 	struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
1052be6fa4cSRémi Denis-Courmont 	struct phonet_device *pnd;
1062be6fa4cSRémi Denis-Courmont 
1072be6fa4cSRémi Denis-Courmont 	ASSERT_RTNL();
1082be6fa4cSRémi Denis-Courmont 
109eeb74a9dSRémi Denis-Courmont 	mutex_lock(&pndevs->lock);
1102be6fa4cSRémi Denis-Courmont 	pnd = __phonet_get(dev);
1112be6fa4cSRémi Denis-Courmont 	if (pnd)
112eeb74a9dSRémi Denis-Courmont 		list_del_rcu(&pnd->list);
113eeb74a9dSRémi Denis-Courmont 	mutex_unlock(&pndevs->lock);
1142be6fa4cSRémi Denis-Courmont 
1152be6fa4cSRémi Denis-Courmont 	if (pnd) {
1162be6fa4cSRémi Denis-Courmont 		u8 addr;
1172be6fa4cSRémi Denis-Courmont 
118a1ca14acSAkinobu Mita 		for_each_set_bit(addr, pnd->addrs, 64)
1192be6fa4cSRémi Denis-Courmont 			phonet_address_notify(RTM_DELADDR, dev, addr);
120f8ff6028SRemi Denis-Courmont 		kfree(pnd);
121f8ff6028SRemi Denis-Courmont 	}
1222be6fa4cSRémi Denis-Courmont }
123f8ff6028SRemi Denis-Courmont 
124f8ff6028SRemi Denis-Courmont struct net_device *phonet_device_get(struct net *net)
125f8ff6028SRemi Denis-Courmont {
1269a3b7a42Sremi.denis-courmont@nokia 	struct phonet_device_list *pndevs = phonet_device_list(net);
127f8ff6028SRemi Denis-Courmont 	struct phonet_device *pnd;
12859e57f44SEric Dumazet 	struct net_device *dev = NULL;
129f8ff6028SRemi Denis-Courmont 
130eeb74a9dSRémi Denis-Courmont 	rcu_read_lock();
131eeb74a9dSRémi Denis-Courmont 	list_for_each_entry_rcu(pnd, &pndevs->list, list) {
132f8ff6028SRemi Denis-Courmont 		dev = pnd->netdev;
133f8ff6028SRemi Denis-Courmont 		BUG_ON(!dev);
134f8ff6028SRemi Denis-Courmont 
1359a3b7a42Sremi.denis-courmont@nokia 		if ((dev->reg_state == NETREG_REGISTERED) &&
136f8ff6028SRemi Denis-Courmont 			((pnd->netdev->flags & IFF_UP)) == IFF_UP)
137f8ff6028SRemi Denis-Courmont 			break;
138f8ff6028SRemi Denis-Courmont 		dev = NULL;
139f8ff6028SRemi Denis-Courmont 	}
140f8ff6028SRemi Denis-Courmont 	if (dev)
141f8ff6028SRemi Denis-Courmont 		dev_hold(dev);
142eeb74a9dSRémi Denis-Courmont 	rcu_read_unlock();
143f8ff6028SRemi Denis-Courmont 	return dev;
144f8ff6028SRemi Denis-Courmont }
145f8ff6028SRemi Denis-Courmont 
146f8ff6028SRemi Denis-Courmont int phonet_address_add(struct net_device *dev, u8 addr)
147f8ff6028SRemi Denis-Courmont {
1489a3b7a42Sremi.denis-courmont@nokia 	struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
149f8ff6028SRemi Denis-Courmont 	struct phonet_device *pnd;
150f8ff6028SRemi Denis-Courmont 	int err = 0;
151f8ff6028SRemi Denis-Courmont 
152eeb74a9dSRémi Denis-Courmont 	mutex_lock(&pndevs->lock);
153f8ff6028SRemi Denis-Courmont 	/* Find or create Phonet-specific device data */
154f8ff6028SRemi Denis-Courmont 	pnd = __phonet_get(dev);
155f8ff6028SRemi Denis-Courmont 	if (pnd == NULL)
156f8ff6028SRemi Denis-Courmont 		pnd = __phonet_device_alloc(dev);
157f8ff6028SRemi Denis-Courmont 	if (unlikely(pnd == NULL))
158f8ff6028SRemi Denis-Courmont 		err = -ENOMEM;
159f8ff6028SRemi Denis-Courmont 	else if (test_and_set_bit(addr >> 2, pnd->addrs))
160f8ff6028SRemi Denis-Courmont 		err = -EEXIST;
161eeb74a9dSRémi Denis-Courmont 	mutex_unlock(&pndevs->lock);
162f8ff6028SRemi Denis-Courmont 	return err;
163f8ff6028SRemi Denis-Courmont }
164f8ff6028SRemi Denis-Courmont 
165f8ff6028SRemi Denis-Courmont int phonet_address_del(struct net_device *dev, u8 addr)
166f8ff6028SRemi Denis-Courmont {
1679a3b7a42Sremi.denis-courmont@nokia 	struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
168f8ff6028SRemi Denis-Courmont 	struct phonet_device *pnd;
169f8ff6028SRemi Denis-Courmont 	int err = 0;
170f8ff6028SRemi Denis-Courmont 
171eeb74a9dSRémi Denis-Courmont 	mutex_lock(&pndevs->lock);
172f8ff6028SRemi Denis-Courmont 	pnd = __phonet_get(dev);
173eeb74a9dSRémi Denis-Courmont 	if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) {
174f8ff6028SRemi Denis-Courmont 		err = -EADDRNOTAVAIL;
175eeb74a9dSRémi Denis-Courmont 		pnd = NULL;
176eeb74a9dSRémi Denis-Courmont 	} else if (bitmap_empty(pnd->addrs, 64))
177eeb74a9dSRémi Denis-Courmont 		list_del_rcu(&pnd->list);
178eeb74a9dSRémi Denis-Courmont 	else
179eeb74a9dSRémi Denis-Courmont 		pnd = NULL;
180eeb74a9dSRémi Denis-Courmont 	mutex_unlock(&pndevs->lock);
181eeb74a9dSRémi Denis-Courmont 
18288e7594aSJiri Pirko 	if (pnd)
1837e113a9cSLai Jiangshan 		kfree_rcu(pnd, rcu);
18488e7594aSJiri Pirko 
185f8ff6028SRemi Denis-Courmont 	return err;
186f8ff6028SRemi Denis-Courmont }
187f8ff6028SRemi Denis-Courmont 
188f8ff6028SRemi Denis-Courmont /* Gets a source address toward a destination, through a interface. */
18955748ac0SRémi Denis-Courmont u8 phonet_address_get(struct net_device *dev, u8 daddr)
190f8ff6028SRemi Denis-Courmont {
191f8ff6028SRemi Denis-Courmont 	struct phonet_device *pnd;
19255748ac0SRémi Denis-Courmont 	u8 saddr;
193f8ff6028SRemi Denis-Courmont 
194eeb74a9dSRémi Denis-Courmont 	rcu_read_lock();
195eeb74a9dSRémi Denis-Courmont 	pnd = __phonet_get_rcu(dev);
196f8ff6028SRemi Denis-Courmont 	if (pnd) {
197f8ff6028SRemi Denis-Courmont 		BUG_ON(bitmap_empty(pnd->addrs, 64));
198f8ff6028SRemi Denis-Courmont 
199f8ff6028SRemi Denis-Courmont 		/* Use same source address as destination, if possible */
20055748ac0SRémi Denis-Courmont 		if (test_bit(daddr >> 2, pnd->addrs))
20155748ac0SRémi Denis-Courmont 			saddr = daddr;
20255748ac0SRémi Denis-Courmont 		else
20355748ac0SRémi Denis-Courmont 			saddr = find_first_bit(pnd->addrs, 64) << 2;
204f8ff6028SRemi Denis-Courmont 	} else
20555748ac0SRémi Denis-Courmont 		saddr = PN_NO_ADDR;
206eeb74a9dSRémi Denis-Courmont 	rcu_read_unlock();
20755748ac0SRémi Denis-Courmont 
20855748ac0SRémi Denis-Courmont 	if (saddr == PN_NO_ADDR) {
20955748ac0SRémi Denis-Courmont 		/* Fallback to another device */
21055748ac0SRémi Denis-Courmont 		struct net_device *def_dev;
21155748ac0SRémi Denis-Courmont 
21255748ac0SRémi Denis-Courmont 		def_dev = phonet_device_get(dev_net(dev));
21355748ac0SRémi Denis-Courmont 		if (def_dev) {
21455748ac0SRémi Denis-Courmont 			if (def_dev != dev)
21555748ac0SRémi Denis-Courmont 				saddr = phonet_address_get(def_dev, daddr);
21655748ac0SRémi Denis-Courmont 			dev_put(def_dev);
21755748ac0SRémi Denis-Courmont 		}
21855748ac0SRémi Denis-Courmont 	}
21955748ac0SRémi Denis-Courmont 	return saddr;
220f8ff6028SRemi Denis-Courmont }
221f8ff6028SRemi Denis-Courmont 
22252404881SRémi Denis-Courmont int phonet_address_lookup(struct net *net, u8 addr)
223f8ff6028SRemi Denis-Courmont {
2249a3b7a42Sremi.denis-courmont@nokia 	struct phonet_device_list *pndevs = phonet_device_list(net);
225f8ff6028SRemi Denis-Courmont 	struct phonet_device *pnd;
2269a3b7a42Sremi.denis-courmont@nokia 	int err = -EADDRNOTAVAIL;
227f8ff6028SRemi Denis-Courmont 
228eeb74a9dSRémi Denis-Courmont 	rcu_read_lock();
229eeb74a9dSRémi Denis-Courmont 	list_for_each_entry_rcu(pnd, &pndevs->list, list) {
230f8ff6028SRemi Denis-Courmont 		/* Don't allow unregistering devices! */
231f8ff6028SRemi Denis-Courmont 		if ((pnd->netdev->reg_state != NETREG_REGISTERED) ||
232f8ff6028SRemi Denis-Courmont 				((pnd->netdev->flags & IFF_UP)) != IFF_UP)
233f8ff6028SRemi Denis-Courmont 			continue;
234f8ff6028SRemi Denis-Courmont 
235f8ff6028SRemi Denis-Courmont 		if (test_bit(addr >> 2, pnd->addrs)) {
2369a3b7a42Sremi.denis-courmont@nokia 			err = 0;
2379a3b7a42Sremi.denis-courmont@nokia 			goto found;
238f8ff6028SRemi Denis-Courmont 		}
239f8ff6028SRemi Denis-Courmont 	}
2409a3b7a42Sremi.denis-courmont@nokia found:
241eeb74a9dSRémi Denis-Courmont 	rcu_read_unlock();
2429a3b7a42Sremi.denis-courmont@nokia 	return err;
243f8ff6028SRemi Denis-Courmont }
244f8ff6028SRemi Denis-Courmont 
245f5bb1c55SRémi Denis-Courmont /* automatically configure a Phonet device, if supported */
246f5bb1c55SRémi Denis-Courmont static int phonet_device_autoconf(struct net_device *dev)
247f5bb1c55SRémi Denis-Courmont {
248f5bb1c55SRémi Denis-Courmont 	struct if_phonet_req req;
249f5bb1c55SRémi Denis-Courmont 	int ret;
250f5bb1c55SRémi Denis-Courmont 
251f5bb1c55SRémi Denis-Courmont 	if (!dev->netdev_ops->ndo_do_ioctl)
252f5bb1c55SRémi Denis-Courmont 		return -EOPNOTSUPP;
253f5bb1c55SRémi Denis-Courmont 
254f5bb1c55SRémi Denis-Courmont 	ret = dev->netdev_ops->ndo_do_ioctl(dev, (struct ifreq *)&req,
255f5bb1c55SRémi Denis-Courmont 						SIOCPNGAUTOCONF);
256f5bb1c55SRémi Denis-Courmont 	if (ret < 0)
257f5bb1c55SRémi Denis-Courmont 		return ret;
258b11b5165SRémi Denis-Courmont 
259b11b5165SRémi Denis-Courmont 	ASSERT_RTNL();
260b11b5165SRémi Denis-Courmont 	ret = phonet_address_add(dev, req.ifr_phonet_autoconf.device);
261b11b5165SRémi Denis-Courmont 	if (ret)
262b11b5165SRémi Denis-Courmont 		return ret;
263b11b5165SRémi Denis-Courmont 	phonet_address_notify(RTM_NEWADDR, dev,
264b11b5165SRémi Denis-Courmont 				req.ifr_phonet_autoconf.device);
265b11b5165SRémi Denis-Courmont 	return 0;
266f5bb1c55SRémi Denis-Courmont }
267f5bb1c55SRémi Denis-Courmont 
268f062f41dSRémi Denis-Courmont static void phonet_route_autodel(struct net_device *dev)
269f062f41dSRémi Denis-Courmont {
2700db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(dev_net(dev));
27195c96174SEric Dumazet 	unsigned int i;
272f062f41dSRémi Denis-Courmont 	DECLARE_BITMAP(deleted, 64);
273f062f41dSRémi Denis-Courmont 
274f062f41dSRémi Denis-Courmont 	/* Remove left-over Phonet routes */
275f062f41dSRémi Denis-Courmont 	bitmap_zero(deleted, 64);
27688880135SRémi Denis-Courmont 	mutex_lock(&pnn->routes.lock);
277f062f41dSRémi Denis-Courmont 	for (i = 0; i < 64; i++)
27879952bcaSFabian Frederick 		if (rcu_access_pointer(pnn->routes.table[i]) == dev) {
279a9b3cd7fSStephen Hemminger 			RCU_INIT_POINTER(pnn->routes.table[i], NULL);
280f062f41dSRémi Denis-Courmont 			set_bit(i, deleted);
28188880135SRémi Denis-Courmont 		}
28288880135SRémi Denis-Courmont 	mutex_unlock(&pnn->routes.lock);
28388880135SRémi Denis-Courmont 
28488880135SRémi Denis-Courmont 	if (bitmap_empty(deleted, 64))
28588880135SRémi Denis-Courmont 		return; /* short-circuit RCU */
28688880135SRémi Denis-Courmont 	synchronize_rcu();
2876a499b24SAkinobu Mita 	for_each_set_bit(i, deleted, 64) {
28888880135SRémi Denis-Courmont 		rtm_phonet_notify(RTM_DELROUTE, dev, i);
289f062f41dSRémi Denis-Courmont 		dev_put(dev);
290f062f41dSRémi Denis-Courmont 	}
291f062f41dSRémi Denis-Courmont }
292f062f41dSRémi Denis-Courmont 
293f8ff6028SRemi Denis-Courmont /* notify Phonet of device events */
294f8ff6028SRemi Denis-Courmont static int phonet_device_notify(struct notifier_block *me, unsigned long what,
295351638e7SJiri Pirko 				void *ptr)
296f8ff6028SRemi Denis-Courmont {
297351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
298f8ff6028SRemi Denis-Courmont 
299f5bb1c55SRémi Denis-Courmont 	switch (what) {
300f5bb1c55SRémi Denis-Courmont 	case NETDEV_REGISTER:
301f5bb1c55SRémi Denis-Courmont 		if (dev->type == ARPHRD_PHONET)
302f5bb1c55SRémi Denis-Courmont 			phonet_device_autoconf(dev);
303f5bb1c55SRémi Denis-Courmont 		break;
304f5bb1c55SRémi Denis-Courmont 	case NETDEV_UNREGISTER:
3052be6fa4cSRémi Denis-Courmont 		phonet_device_destroy(dev);
306f062f41dSRémi Denis-Courmont 		phonet_route_autodel(dev);
307f5bb1c55SRémi Denis-Courmont 		break;
308f5bb1c55SRémi Denis-Courmont 	}
309f8ff6028SRemi Denis-Courmont 	return 0;
310f8ff6028SRemi Denis-Courmont 
311f8ff6028SRemi Denis-Courmont }
312f8ff6028SRemi Denis-Courmont 
313f8ff6028SRemi Denis-Courmont static struct notifier_block phonet_device_notifier = {
314f8ff6028SRemi Denis-Courmont 	.notifier_call = phonet_device_notify,
315f8ff6028SRemi Denis-Courmont 	.priority = 0,
316f8ff6028SRemi Denis-Courmont };
317f8ff6028SRemi Denis-Courmont 
3189a3b7a42Sremi.denis-courmont@nokia /* Per-namespace Phonet devices handling */
3192c8c1e72SAlexey Dobriyan static int __net_init phonet_init_net(struct net *net)
3209a3b7a42Sremi.denis-courmont@nokia {
3210db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(net);
3229a3b7a42Sremi.denis-courmont@nokia 
323d4beaa66SGao feng 	if (!proc_create("phonet", 0, net->proc_net, &pn_sock_seq_fops))
324c1dc13e9SRémi Denis-Courmont 		return -ENOMEM;
325c1dc13e9SRémi Denis-Courmont 
3269a3b7a42Sremi.denis-courmont@nokia 	INIT_LIST_HEAD(&pnn->pndevs.list);
327eeb74a9dSRémi Denis-Courmont 	mutex_init(&pnn->pndevs.lock);
32888880135SRémi Denis-Courmont 	mutex_init(&pnn->routes.lock);
3299a3b7a42Sremi.denis-courmont@nokia 	return 0;
3309a3b7a42Sremi.denis-courmont@nokia }
3319a3b7a42Sremi.denis-courmont@nokia 
3322c8c1e72SAlexey Dobriyan static void __net_exit phonet_exit_net(struct net *net)
3339a3b7a42Sremi.denis-courmont@nokia {
334ece31ffdSGao feng 	remove_proc_entry("phonet", net->proc_net);
3359a3b7a42Sremi.denis-courmont@nokia }
3369a3b7a42Sremi.denis-courmont@nokia 
3379a3b7a42Sremi.denis-courmont@nokia static struct pernet_operations phonet_net_ops = {
3389a3b7a42Sremi.denis-courmont@nokia 	.init = phonet_init_net,
3399a3b7a42Sremi.denis-courmont@nokia 	.exit = phonet_exit_net,
340d2b3eb63SEric W. Biederman 	.id   = &phonet_net_id,
341d2b3eb63SEric W. Biederman 	.size = sizeof(struct phonet_net),
3429a3b7a42Sremi.denis-courmont@nokia };
3439a3b7a42Sremi.denis-courmont@nokia 
344f8ff6028SRemi Denis-Courmont /* Initialize Phonet devices list */
34576e02cf6Sremi.denis-courmont@nokia int __init phonet_device_init(void)
346f8ff6028SRemi Denis-Courmont {
34703478756SEric W. Biederman 	int err = register_pernet_subsys(&phonet_net_ops);
3489a3b7a42Sremi.denis-courmont@nokia 	if (err)
3499a3b7a42Sremi.denis-courmont@nokia 		return err;
350660f706dSremi.denis-courmont@nokia 
351d4beaa66SGao feng 	proc_create("pnresource", 0, init_net.proc_net, &pn_res_seq_fops);
352f8ff6028SRemi Denis-Courmont 	register_netdevice_notifier(&phonet_device_notifier);
353660f706dSremi.denis-courmont@nokia 	err = phonet_netlink_register();
354660f706dSremi.denis-courmont@nokia 	if (err)
355660f706dSremi.denis-courmont@nokia 		phonet_device_exit();
356660f706dSremi.denis-courmont@nokia 	return err;
357f8ff6028SRemi Denis-Courmont }
358f8ff6028SRemi Denis-Courmont 
359f8ff6028SRemi Denis-Courmont void phonet_device_exit(void)
360f8ff6028SRemi Denis-Courmont {
361f8ff6028SRemi Denis-Courmont 	rtnl_unregister_all(PF_PHONET);
3626530e0feSremi.denis-courmont@nokia 	unregister_netdevice_notifier(&phonet_device_notifier);
36303478756SEric W. Biederman 	unregister_pernet_subsys(&phonet_net_ops);
364ece31ffdSGao feng 	remove_proc_entry("pnresource", init_net.proc_net);
365f8ff6028SRemi Denis-Courmont }
36655748ac0SRémi Denis-Courmont 
36755748ac0SRémi Denis-Courmont int phonet_route_add(struct net_device *dev, u8 daddr)
36855748ac0SRémi Denis-Courmont {
3690db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(dev_net(dev));
37055748ac0SRémi Denis-Courmont 	struct phonet_routes *routes = &pnn->routes;
37155748ac0SRémi Denis-Courmont 	int err = -EEXIST;
37255748ac0SRémi Denis-Courmont 
37355748ac0SRémi Denis-Courmont 	daddr = daddr >> 2;
37488880135SRémi Denis-Courmont 	mutex_lock(&routes->lock);
37555748ac0SRémi Denis-Courmont 	if (routes->table[daddr] == NULL) {
376cf778b00SEric Dumazet 		rcu_assign_pointer(routes->table[daddr], dev);
37755748ac0SRémi Denis-Courmont 		dev_hold(dev);
37855748ac0SRémi Denis-Courmont 		err = 0;
37955748ac0SRémi Denis-Courmont 	}
38088880135SRémi Denis-Courmont 	mutex_unlock(&routes->lock);
38155748ac0SRémi Denis-Courmont 	return err;
38255748ac0SRémi Denis-Courmont }
38355748ac0SRémi Denis-Courmont 
38455748ac0SRémi Denis-Courmont int phonet_route_del(struct net_device *dev, u8 daddr)
38555748ac0SRémi Denis-Courmont {
3860db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(dev_net(dev));
38755748ac0SRémi Denis-Courmont 	struct phonet_routes *routes = &pnn->routes;
38855748ac0SRémi Denis-Courmont 
38955748ac0SRémi Denis-Courmont 	daddr = daddr >> 2;
39088880135SRémi Denis-Courmont 	mutex_lock(&routes->lock);
39179952bcaSFabian Frederick 	if (rcu_access_pointer(routes->table[daddr]) == dev)
392a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(routes->table[daddr], NULL);
39388880135SRémi Denis-Courmont 	else
39488880135SRémi Denis-Courmont 		dev = NULL;
39588880135SRémi Denis-Courmont 	mutex_unlock(&routes->lock);
39688880135SRémi Denis-Courmont 
39788880135SRémi Denis-Courmont 	if (!dev)
39888880135SRémi Denis-Courmont 		return -ENOENT;
39988880135SRémi Denis-Courmont 	synchronize_rcu();
40055748ac0SRémi Denis-Courmont 	dev_put(dev);
40188880135SRémi Denis-Courmont 	return 0;
40255748ac0SRémi Denis-Courmont }
40355748ac0SRémi Denis-Courmont 
404e67f88ddSEric Dumazet struct net_device *phonet_route_get_rcu(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 
41055748ac0SRémi Denis-Courmont 	daddr >>= 2;
41188880135SRémi Denis-Courmont 	dev = rcu_dereference(routes->table[daddr]);
41255748ac0SRémi Denis-Courmont 	return dev;
41355748ac0SRémi Denis-Courmont }
41455748ac0SRémi Denis-Courmont 
41555748ac0SRémi Denis-Courmont struct net_device *phonet_route_output(struct net *net, u8 daddr)
41655748ac0SRémi Denis-Courmont {
4170db3f0f4SJiri Pirko 	struct phonet_net *pnn = phonet_pernet(net);
41855748ac0SRémi Denis-Courmont 	struct phonet_routes *routes = &pnn->routes;
41955748ac0SRémi Denis-Courmont 	struct net_device *dev;
42055748ac0SRémi Denis-Courmont 
42188880135SRémi Denis-Courmont 	daddr >>= 2;
42288880135SRémi Denis-Courmont 	rcu_read_lock();
42388880135SRémi Denis-Courmont 	dev = rcu_dereference(routes->table[daddr]);
42455748ac0SRémi Denis-Courmont 	if (dev)
42555748ac0SRémi Denis-Courmont 		dev_hold(dev);
42688880135SRémi Denis-Courmont 	rcu_read_unlock();
42755748ac0SRémi Denis-Courmont 
42855748ac0SRémi Denis-Courmont 	if (!dev)
42955748ac0SRémi Denis-Courmont 		dev = phonet_device_get(net); /* Default route */
43055748ac0SRémi Denis-Courmont 	return dev;
43155748ac0SRémi Denis-Courmont }
432