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 47*c7d03a00SAlexey 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