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