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 * 8f8ff6028SRemi Denis-Courmont * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> 9f8ff6028SRemi Denis-Courmont * Original author: Sakari Ailus <sakari.ailus@nokia.com> 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; 3955748ac0SRémi Denis-Courmont struct net_device *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 47f99189b1SEric Dumazet 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 16588e7594aSJiri Pirko static void phonet_device_rcu_free(struct rcu_head *head) 16688e7594aSJiri Pirko { 16788e7594aSJiri Pirko struct phonet_device *pnd; 16888e7594aSJiri Pirko 16988e7594aSJiri Pirko pnd = container_of(head, struct phonet_device, rcu); 17088e7594aSJiri Pirko kfree(pnd); 17188e7594aSJiri Pirko } 17288e7594aSJiri Pirko 173f8ff6028SRemi Denis-Courmont int phonet_address_del(struct net_device *dev, u8 addr) 174f8ff6028SRemi Denis-Courmont { 1759a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 176f8ff6028SRemi Denis-Courmont struct phonet_device *pnd; 177f8ff6028SRemi Denis-Courmont int err = 0; 178f8ff6028SRemi Denis-Courmont 179eeb74a9dSRémi Denis-Courmont mutex_lock(&pndevs->lock); 180f8ff6028SRemi Denis-Courmont pnd = __phonet_get(dev); 181eeb74a9dSRémi Denis-Courmont if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) { 182f8ff6028SRemi Denis-Courmont err = -EADDRNOTAVAIL; 183eeb74a9dSRémi Denis-Courmont pnd = NULL; 184eeb74a9dSRémi Denis-Courmont } else if (bitmap_empty(pnd->addrs, 64)) 185eeb74a9dSRémi Denis-Courmont list_del_rcu(&pnd->list); 186eeb74a9dSRémi Denis-Courmont else 187eeb74a9dSRémi Denis-Courmont pnd = NULL; 188eeb74a9dSRémi Denis-Courmont mutex_unlock(&pndevs->lock); 189eeb74a9dSRémi Denis-Courmont 19088e7594aSJiri Pirko if (pnd) 19188e7594aSJiri Pirko call_rcu(&pnd->rcu, phonet_device_rcu_free); 19288e7594aSJiri Pirko 193f8ff6028SRemi Denis-Courmont return err; 194f8ff6028SRemi Denis-Courmont } 195f8ff6028SRemi Denis-Courmont 196f8ff6028SRemi Denis-Courmont /* Gets a source address toward a destination, through a interface. */ 19755748ac0SRémi Denis-Courmont u8 phonet_address_get(struct net_device *dev, u8 daddr) 198f8ff6028SRemi Denis-Courmont { 199f8ff6028SRemi Denis-Courmont struct phonet_device *pnd; 20055748ac0SRémi Denis-Courmont u8 saddr; 201f8ff6028SRemi Denis-Courmont 202eeb74a9dSRémi Denis-Courmont rcu_read_lock(); 203eeb74a9dSRémi Denis-Courmont pnd = __phonet_get_rcu(dev); 204f8ff6028SRemi Denis-Courmont if (pnd) { 205f8ff6028SRemi Denis-Courmont BUG_ON(bitmap_empty(pnd->addrs, 64)); 206f8ff6028SRemi Denis-Courmont 207f8ff6028SRemi Denis-Courmont /* Use same source address as destination, if possible */ 20855748ac0SRémi Denis-Courmont if (test_bit(daddr >> 2, pnd->addrs)) 20955748ac0SRémi Denis-Courmont saddr = daddr; 21055748ac0SRémi Denis-Courmont else 21155748ac0SRémi Denis-Courmont saddr = find_first_bit(pnd->addrs, 64) << 2; 212f8ff6028SRemi Denis-Courmont } else 21355748ac0SRémi Denis-Courmont saddr = PN_NO_ADDR; 214eeb74a9dSRémi Denis-Courmont rcu_read_unlock(); 21555748ac0SRémi Denis-Courmont 21655748ac0SRémi Denis-Courmont if (saddr == PN_NO_ADDR) { 21755748ac0SRémi Denis-Courmont /* Fallback to another device */ 21855748ac0SRémi Denis-Courmont struct net_device *def_dev; 21955748ac0SRémi Denis-Courmont 22055748ac0SRémi Denis-Courmont def_dev = phonet_device_get(dev_net(dev)); 22155748ac0SRémi Denis-Courmont if (def_dev) { 22255748ac0SRémi Denis-Courmont if (def_dev != dev) 22355748ac0SRémi Denis-Courmont saddr = phonet_address_get(def_dev, daddr); 22455748ac0SRémi Denis-Courmont dev_put(def_dev); 22555748ac0SRémi Denis-Courmont } 22655748ac0SRémi Denis-Courmont } 22755748ac0SRémi Denis-Courmont return saddr; 228f8ff6028SRemi Denis-Courmont } 229f8ff6028SRemi Denis-Courmont 23052404881SRémi Denis-Courmont int phonet_address_lookup(struct net *net, u8 addr) 231f8ff6028SRemi Denis-Courmont { 2329a3b7a42Sremi.denis-courmont@nokia struct phonet_device_list *pndevs = phonet_device_list(net); 233f8ff6028SRemi Denis-Courmont struct phonet_device *pnd; 2349a3b7a42Sremi.denis-courmont@nokia int err = -EADDRNOTAVAIL; 235f8ff6028SRemi Denis-Courmont 236eeb74a9dSRémi Denis-Courmont rcu_read_lock(); 237eeb74a9dSRémi Denis-Courmont list_for_each_entry_rcu(pnd, &pndevs->list, list) { 238f8ff6028SRemi Denis-Courmont /* Don't allow unregistering devices! */ 239f8ff6028SRemi Denis-Courmont if ((pnd->netdev->reg_state != NETREG_REGISTERED) || 240f8ff6028SRemi Denis-Courmont ((pnd->netdev->flags & IFF_UP)) != IFF_UP) 241f8ff6028SRemi Denis-Courmont continue; 242f8ff6028SRemi Denis-Courmont 243f8ff6028SRemi Denis-Courmont if (test_bit(addr >> 2, pnd->addrs)) { 2449a3b7a42Sremi.denis-courmont@nokia err = 0; 2459a3b7a42Sremi.denis-courmont@nokia goto found; 246f8ff6028SRemi Denis-Courmont } 247f8ff6028SRemi Denis-Courmont } 2489a3b7a42Sremi.denis-courmont@nokia found: 249eeb74a9dSRémi Denis-Courmont rcu_read_unlock(); 2509a3b7a42Sremi.denis-courmont@nokia return err; 251f8ff6028SRemi Denis-Courmont } 252f8ff6028SRemi Denis-Courmont 253f5bb1c55SRémi Denis-Courmont /* automatically configure a Phonet device, if supported */ 254f5bb1c55SRémi Denis-Courmont static int phonet_device_autoconf(struct net_device *dev) 255f5bb1c55SRémi Denis-Courmont { 256f5bb1c55SRémi Denis-Courmont struct if_phonet_req req; 257f5bb1c55SRémi Denis-Courmont int ret; 258f5bb1c55SRémi Denis-Courmont 259f5bb1c55SRémi Denis-Courmont if (!dev->netdev_ops->ndo_do_ioctl) 260f5bb1c55SRémi Denis-Courmont return -EOPNOTSUPP; 261f5bb1c55SRémi Denis-Courmont 262f5bb1c55SRémi Denis-Courmont ret = dev->netdev_ops->ndo_do_ioctl(dev, (struct ifreq *)&req, 263f5bb1c55SRémi Denis-Courmont SIOCPNGAUTOCONF); 264f5bb1c55SRémi Denis-Courmont if (ret < 0) 265f5bb1c55SRémi Denis-Courmont return ret; 266b11b5165SRémi Denis-Courmont 267b11b5165SRémi Denis-Courmont ASSERT_RTNL(); 268b11b5165SRémi Denis-Courmont ret = phonet_address_add(dev, req.ifr_phonet_autoconf.device); 269b11b5165SRémi Denis-Courmont if (ret) 270b11b5165SRémi Denis-Courmont return ret; 271b11b5165SRémi Denis-Courmont phonet_address_notify(RTM_NEWADDR, dev, 272b11b5165SRémi Denis-Courmont req.ifr_phonet_autoconf.device); 273b11b5165SRémi Denis-Courmont return 0; 274f5bb1c55SRémi Denis-Courmont } 275f5bb1c55SRémi Denis-Courmont 276f062f41dSRémi Denis-Courmont static void phonet_route_autodel(struct net_device *dev) 277f062f41dSRémi Denis-Courmont { 2780db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(dev_net(dev)); 279f062f41dSRémi Denis-Courmont unsigned i; 280f062f41dSRémi Denis-Courmont DECLARE_BITMAP(deleted, 64); 281f062f41dSRémi Denis-Courmont 282f062f41dSRémi Denis-Courmont /* Remove left-over Phonet routes */ 283f062f41dSRémi Denis-Courmont bitmap_zero(deleted, 64); 28488880135SRémi Denis-Courmont mutex_lock(&pnn->routes.lock); 285f062f41dSRémi Denis-Courmont for (i = 0; i < 64; i++) 286f062f41dSRémi Denis-Courmont if (dev == pnn->routes.table[i]) { 28788880135SRémi Denis-Courmont rcu_assign_pointer(pnn->routes.table[i], NULL); 288f062f41dSRémi Denis-Courmont set_bit(i, deleted); 28988880135SRémi Denis-Courmont } 29088880135SRémi Denis-Courmont mutex_unlock(&pnn->routes.lock); 29188880135SRémi Denis-Courmont 29288880135SRémi Denis-Courmont if (bitmap_empty(deleted, 64)) 29388880135SRémi Denis-Courmont return; /* short-circuit RCU */ 29488880135SRémi Denis-Courmont synchronize_rcu(); 295*6a499b24SAkinobu Mita for_each_set_bit(i, deleted, 64) { 29688880135SRémi Denis-Courmont rtm_phonet_notify(RTM_DELROUTE, dev, i); 297f062f41dSRémi Denis-Courmont dev_put(dev); 298f062f41dSRémi Denis-Courmont } 299f062f41dSRémi Denis-Courmont } 300f062f41dSRémi Denis-Courmont 301f8ff6028SRemi Denis-Courmont /* notify Phonet of device events */ 302f8ff6028SRemi Denis-Courmont static int phonet_device_notify(struct notifier_block *me, unsigned long what, 303f8ff6028SRemi Denis-Courmont void *arg) 304f8ff6028SRemi Denis-Courmont { 305f8ff6028SRemi Denis-Courmont struct net_device *dev = arg; 306f8ff6028SRemi Denis-Courmont 307f5bb1c55SRémi Denis-Courmont switch (what) { 308f5bb1c55SRémi Denis-Courmont case NETDEV_REGISTER: 309f5bb1c55SRémi Denis-Courmont if (dev->type == ARPHRD_PHONET) 310f5bb1c55SRémi Denis-Courmont phonet_device_autoconf(dev); 311f5bb1c55SRémi Denis-Courmont break; 312f5bb1c55SRémi Denis-Courmont case NETDEV_UNREGISTER: 3132be6fa4cSRémi Denis-Courmont phonet_device_destroy(dev); 314f062f41dSRémi Denis-Courmont phonet_route_autodel(dev); 315f5bb1c55SRémi Denis-Courmont break; 316f5bb1c55SRémi Denis-Courmont } 317f8ff6028SRemi Denis-Courmont return 0; 318f8ff6028SRemi Denis-Courmont 319f8ff6028SRemi Denis-Courmont } 320f8ff6028SRemi Denis-Courmont 321f8ff6028SRemi Denis-Courmont static struct notifier_block phonet_device_notifier = { 322f8ff6028SRemi Denis-Courmont .notifier_call = phonet_device_notify, 323f8ff6028SRemi Denis-Courmont .priority = 0, 324f8ff6028SRemi Denis-Courmont }; 325f8ff6028SRemi Denis-Courmont 3269a3b7a42Sremi.denis-courmont@nokia /* Per-namespace Phonet devices handling */ 3272c8c1e72SAlexey Dobriyan static int __net_init phonet_init_net(struct net *net) 3289a3b7a42Sremi.denis-courmont@nokia { 3290db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(net); 3309a3b7a42Sremi.denis-courmont@nokia 331d2b3eb63SEric W. Biederman if (!proc_net_fops_create(net, "phonet", 0, &pn_sock_seq_fops)) 332c1dc13e9SRémi Denis-Courmont return -ENOMEM; 333c1dc13e9SRémi Denis-Courmont 3349a3b7a42Sremi.denis-courmont@nokia INIT_LIST_HEAD(&pnn->pndevs.list); 335eeb74a9dSRémi Denis-Courmont mutex_init(&pnn->pndevs.lock); 33688880135SRémi Denis-Courmont mutex_init(&pnn->routes.lock); 3379a3b7a42Sremi.denis-courmont@nokia return 0; 3389a3b7a42Sremi.denis-courmont@nokia } 3399a3b7a42Sremi.denis-courmont@nokia 3402c8c1e72SAlexey Dobriyan static void __net_exit phonet_exit_net(struct net *net) 3419a3b7a42Sremi.denis-courmont@nokia { 3420db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(net); 3432be6fa4cSRémi Denis-Courmont struct net_device *dev; 344f062f41dSRémi Denis-Courmont unsigned i; 3459a3b7a42Sremi.denis-courmont@nokia 3462be6fa4cSRémi Denis-Courmont rtnl_lock(); 3472be6fa4cSRémi Denis-Courmont for_each_netdev(net, dev) 3482be6fa4cSRémi Denis-Courmont phonet_device_destroy(dev); 349f062f41dSRémi Denis-Courmont 350f062f41dSRémi Denis-Courmont for (i = 0; i < 64; i++) { 351f062f41dSRémi Denis-Courmont dev = pnn->routes.table[i]; 352f062f41dSRémi Denis-Courmont if (dev) { 353f062f41dSRémi Denis-Courmont rtm_phonet_notify(RTM_DELROUTE, dev, i); 354f062f41dSRémi Denis-Courmont dev_put(dev); 355f062f41dSRémi Denis-Courmont } 356f062f41dSRémi Denis-Courmont } 3572be6fa4cSRémi Denis-Courmont rtnl_unlock(); 358c1dc13e9SRémi Denis-Courmont 359c1dc13e9SRémi Denis-Courmont proc_net_remove(net, "phonet"); 3609a3b7a42Sremi.denis-courmont@nokia } 3619a3b7a42Sremi.denis-courmont@nokia 3629a3b7a42Sremi.denis-courmont@nokia static struct pernet_operations phonet_net_ops = { 3639a3b7a42Sremi.denis-courmont@nokia .init = phonet_init_net, 3649a3b7a42Sremi.denis-courmont@nokia .exit = phonet_exit_net, 365d2b3eb63SEric W. Biederman .id = &phonet_net_id, 366d2b3eb63SEric W. Biederman .size = sizeof(struct phonet_net), 3679a3b7a42Sremi.denis-courmont@nokia }; 3689a3b7a42Sremi.denis-courmont@nokia 369f8ff6028SRemi Denis-Courmont /* Initialize Phonet devices list */ 37076e02cf6Sremi.denis-courmont@nokia int __init phonet_device_init(void) 371f8ff6028SRemi Denis-Courmont { 372d2b3eb63SEric W. Biederman int err = register_pernet_device(&phonet_net_ops); 3739a3b7a42Sremi.denis-courmont@nokia if (err) 3749a3b7a42Sremi.denis-courmont@nokia return err; 375660f706dSremi.denis-courmont@nokia 376f8ff6028SRemi Denis-Courmont register_netdevice_notifier(&phonet_device_notifier); 377660f706dSremi.denis-courmont@nokia err = phonet_netlink_register(); 378660f706dSremi.denis-courmont@nokia if (err) 379660f706dSremi.denis-courmont@nokia phonet_device_exit(); 380660f706dSremi.denis-courmont@nokia return err; 381f8ff6028SRemi Denis-Courmont } 382f8ff6028SRemi Denis-Courmont 383f8ff6028SRemi Denis-Courmont void phonet_device_exit(void) 384f8ff6028SRemi Denis-Courmont { 385f8ff6028SRemi Denis-Courmont rtnl_unregister_all(PF_PHONET); 3866530e0feSremi.denis-courmont@nokia unregister_netdevice_notifier(&phonet_device_notifier); 387d2b3eb63SEric W. Biederman unregister_pernet_device(&phonet_net_ops); 388f8ff6028SRemi Denis-Courmont } 38955748ac0SRémi Denis-Courmont 39055748ac0SRémi Denis-Courmont int phonet_route_add(struct net_device *dev, u8 daddr) 39155748ac0SRémi Denis-Courmont { 3920db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(dev_net(dev)); 39355748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes; 39455748ac0SRémi Denis-Courmont int err = -EEXIST; 39555748ac0SRémi Denis-Courmont 39655748ac0SRémi Denis-Courmont daddr = daddr >> 2; 39788880135SRémi Denis-Courmont mutex_lock(&routes->lock); 39855748ac0SRémi Denis-Courmont if (routes->table[daddr] == NULL) { 39988880135SRémi Denis-Courmont rcu_assign_pointer(routes->table[daddr], dev); 40055748ac0SRémi Denis-Courmont dev_hold(dev); 40155748ac0SRémi Denis-Courmont err = 0; 40255748ac0SRémi Denis-Courmont } 40388880135SRémi Denis-Courmont mutex_unlock(&routes->lock); 40455748ac0SRémi Denis-Courmont return err; 40555748ac0SRémi Denis-Courmont } 40655748ac0SRémi Denis-Courmont 40755748ac0SRémi Denis-Courmont int phonet_route_del(struct net_device *dev, u8 daddr) 40855748ac0SRémi Denis-Courmont { 4090db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(dev_net(dev)); 41055748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes; 41155748ac0SRémi Denis-Courmont 41255748ac0SRémi Denis-Courmont daddr = daddr >> 2; 41388880135SRémi Denis-Courmont mutex_lock(&routes->lock); 41488880135SRémi Denis-Courmont if (dev == routes->table[daddr]) 41588880135SRémi Denis-Courmont rcu_assign_pointer(routes->table[daddr], NULL); 41688880135SRémi Denis-Courmont else 41788880135SRémi Denis-Courmont dev = NULL; 41888880135SRémi Denis-Courmont mutex_unlock(&routes->lock); 41988880135SRémi Denis-Courmont 42088880135SRémi Denis-Courmont if (!dev) 42188880135SRémi Denis-Courmont return -ENOENT; 42288880135SRémi Denis-Courmont synchronize_rcu(); 42355748ac0SRémi Denis-Courmont dev_put(dev); 42488880135SRémi Denis-Courmont return 0; 42555748ac0SRémi Denis-Courmont } 42655748ac0SRémi Denis-Courmont 42755748ac0SRémi Denis-Courmont struct net_device *phonet_route_get(struct net *net, u8 daddr) 42855748ac0SRémi Denis-Courmont { 4290db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(net); 43055748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes; 43155748ac0SRémi Denis-Courmont struct net_device *dev; 43255748ac0SRémi Denis-Courmont 43355748ac0SRémi Denis-Courmont ASSERT_RTNL(); /* no need to hold the device */ 43455748ac0SRémi Denis-Courmont 43555748ac0SRémi Denis-Courmont daddr >>= 2; 43688880135SRémi Denis-Courmont rcu_read_lock(); 43788880135SRémi Denis-Courmont dev = rcu_dereference(routes->table[daddr]); 43888880135SRémi Denis-Courmont rcu_read_unlock(); 43955748ac0SRémi Denis-Courmont return dev; 44055748ac0SRémi Denis-Courmont } 44155748ac0SRémi Denis-Courmont 44255748ac0SRémi Denis-Courmont struct net_device *phonet_route_output(struct net *net, u8 daddr) 44355748ac0SRémi Denis-Courmont { 4440db3f0f4SJiri Pirko struct phonet_net *pnn = phonet_pernet(net); 44555748ac0SRémi Denis-Courmont struct phonet_routes *routes = &pnn->routes; 44655748ac0SRémi Denis-Courmont struct net_device *dev; 44755748ac0SRémi Denis-Courmont 44888880135SRémi Denis-Courmont daddr >>= 2; 44988880135SRémi Denis-Courmont rcu_read_lock(); 45088880135SRémi Denis-Courmont dev = rcu_dereference(routes->table[daddr]); 45155748ac0SRémi Denis-Courmont if (dev) 45255748ac0SRémi Denis-Courmont dev_hold(dev); 45388880135SRémi Denis-Courmont rcu_read_unlock(); 45455748ac0SRémi Denis-Courmont 45555748ac0SRémi Denis-Courmont if (!dev) 45655748ac0SRémi Denis-Courmont dev = phonet_device_get(net); /* Default route */ 45755748ac0SRémi Denis-Courmont return dev; 45855748ac0SRémi Denis-Courmont } 459