18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * File: pn_dev.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Phonet network device 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Authors: Sakari Ailus <sakari.ailus@nokia.com> 108c2ecf20Sopenharmony_ci * Rémi Denis-Courmont 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/net.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/phonet.h> 188c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 198c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 208c2ecf20Sopenharmony_ci#include <net/sock.h> 218c2ecf20Sopenharmony_ci#include <net/netns/generic.h> 228c2ecf20Sopenharmony_ci#include <net/phonet/pn_dev.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct phonet_routes { 258c2ecf20Sopenharmony_ci struct mutex lock; 268c2ecf20Sopenharmony_ci struct net_device __rcu *table[64]; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct phonet_net { 308c2ecf20Sopenharmony_ci struct phonet_device_list pndevs; 318c2ecf20Sopenharmony_ci struct phonet_routes routes; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic unsigned int phonet_net_id __read_mostly; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic struct phonet_net *phonet_pernet(struct net *net) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci return net_generic(net, phonet_net_id); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct phonet_device_list *phonet_device_list(struct net *net) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct phonet_net *pnn = phonet_pernet(net); 448c2ecf20Sopenharmony_ci return &pnn->pndevs; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Allocate new Phonet device. */ 488c2ecf20Sopenharmony_cistatic struct phonet_device *__phonet_device_alloc(struct net_device *dev) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 518c2ecf20Sopenharmony_ci struct phonet_device *pnd = kmalloc(sizeof(*pnd), GFP_ATOMIC); 528c2ecf20Sopenharmony_ci if (pnd == NULL) 538c2ecf20Sopenharmony_ci return NULL; 548c2ecf20Sopenharmony_ci pnd->netdev = dev; 558c2ecf20Sopenharmony_ci bitmap_zero(pnd->addrs, 64); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci BUG_ON(!mutex_is_locked(&pndevs->lock)); 588c2ecf20Sopenharmony_ci list_add_rcu(&pnd->list, &pndevs->list); 598c2ecf20Sopenharmony_ci return pnd; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct phonet_device *__phonet_get(struct net_device *dev) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 658c2ecf20Sopenharmony_ci struct phonet_device *pnd; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci BUG_ON(!mutex_is_locked(&pndevs->lock)); 688c2ecf20Sopenharmony_ci list_for_each_entry(pnd, &pndevs->list, list) { 698c2ecf20Sopenharmony_ci if (pnd->netdev == dev) 708c2ecf20Sopenharmony_ci return pnd; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci return NULL; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic struct phonet_device *__phonet_get_rcu(struct net_device *dev) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 788c2ecf20Sopenharmony_ci struct phonet_device *pnd; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci list_for_each_entry_rcu(pnd, &pndevs->list, list) { 818c2ecf20Sopenharmony_ci if (pnd->netdev == dev) 828c2ecf20Sopenharmony_ci return pnd; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci return NULL; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void phonet_device_destroy(struct net_device *dev) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 908c2ecf20Sopenharmony_ci struct phonet_device *pnd; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ASSERT_RTNL(); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci mutex_lock(&pndevs->lock); 958c2ecf20Sopenharmony_ci pnd = __phonet_get(dev); 968c2ecf20Sopenharmony_ci if (pnd) 978c2ecf20Sopenharmony_ci list_del_rcu(&pnd->list); 988c2ecf20Sopenharmony_ci mutex_unlock(&pndevs->lock); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (pnd) { 1018c2ecf20Sopenharmony_ci u8 addr; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci for_each_set_bit(addr, pnd->addrs, 64) 1048c2ecf20Sopenharmony_ci phonet_address_notify(RTM_DELADDR, dev, addr); 1058c2ecf20Sopenharmony_ci kfree(pnd); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistruct net_device *phonet_device_get(struct net *net) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct phonet_device_list *pndevs = phonet_device_list(net); 1128c2ecf20Sopenharmony_ci struct phonet_device *pnd; 1138c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci rcu_read_lock(); 1168c2ecf20Sopenharmony_ci list_for_each_entry_rcu(pnd, &pndevs->list, list) { 1178c2ecf20Sopenharmony_ci dev = pnd->netdev; 1188c2ecf20Sopenharmony_ci BUG_ON(!dev); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if ((dev->reg_state == NETREG_REGISTERED) && 1218c2ecf20Sopenharmony_ci ((pnd->netdev->flags & IFF_UP)) == IFF_UP) 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci dev = NULL; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci if (dev) 1268c2ecf20Sopenharmony_ci dev_hold(dev); 1278c2ecf20Sopenharmony_ci rcu_read_unlock(); 1288c2ecf20Sopenharmony_ci return dev; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciint phonet_address_add(struct net_device *dev, u8 addr) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 1348c2ecf20Sopenharmony_ci struct phonet_device *pnd; 1358c2ecf20Sopenharmony_ci int err = 0; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci mutex_lock(&pndevs->lock); 1388c2ecf20Sopenharmony_ci /* Find or create Phonet-specific device data */ 1398c2ecf20Sopenharmony_ci pnd = __phonet_get(dev); 1408c2ecf20Sopenharmony_ci if (pnd == NULL) 1418c2ecf20Sopenharmony_ci pnd = __phonet_device_alloc(dev); 1428c2ecf20Sopenharmony_ci if (unlikely(pnd == NULL)) 1438c2ecf20Sopenharmony_ci err = -ENOMEM; 1448c2ecf20Sopenharmony_ci else if (test_and_set_bit(addr >> 2, pnd->addrs)) 1458c2ecf20Sopenharmony_ci err = -EEXIST; 1468c2ecf20Sopenharmony_ci mutex_unlock(&pndevs->lock); 1478c2ecf20Sopenharmony_ci return err; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ciint phonet_address_del(struct net_device *dev, u8 addr) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 1538c2ecf20Sopenharmony_ci struct phonet_device *pnd; 1548c2ecf20Sopenharmony_ci int err = 0; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci mutex_lock(&pndevs->lock); 1578c2ecf20Sopenharmony_ci pnd = __phonet_get(dev); 1588c2ecf20Sopenharmony_ci if (!pnd || !test_and_clear_bit(addr >> 2, pnd->addrs)) { 1598c2ecf20Sopenharmony_ci err = -EADDRNOTAVAIL; 1608c2ecf20Sopenharmony_ci pnd = NULL; 1618c2ecf20Sopenharmony_ci } else if (bitmap_empty(pnd->addrs, 64)) 1628c2ecf20Sopenharmony_ci list_del_rcu(&pnd->list); 1638c2ecf20Sopenharmony_ci else 1648c2ecf20Sopenharmony_ci pnd = NULL; 1658c2ecf20Sopenharmony_ci mutex_unlock(&pndevs->lock); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (pnd) 1688c2ecf20Sopenharmony_ci kfree_rcu(pnd, rcu); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return err; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* Gets a source address toward a destination, through a interface. */ 1748c2ecf20Sopenharmony_ciu8 phonet_address_get(struct net_device *dev, u8 daddr) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct phonet_device *pnd; 1778c2ecf20Sopenharmony_ci u8 saddr; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci rcu_read_lock(); 1808c2ecf20Sopenharmony_ci pnd = __phonet_get_rcu(dev); 1818c2ecf20Sopenharmony_ci if (pnd) { 1828c2ecf20Sopenharmony_ci BUG_ON(bitmap_empty(pnd->addrs, 64)); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Use same source address as destination, if possible */ 1858c2ecf20Sopenharmony_ci if (test_bit(daddr >> 2, pnd->addrs)) 1868c2ecf20Sopenharmony_ci saddr = daddr; 1878c2ecf20Sopenharmony_ci else 1888c2ecf20Sopenharmony_ci saddr = find_first_bit(pnd->addrs, 64) << 2; 1898c2ecf20Sopenharmony_ci } else 1908c2ecf20Sopenharmony_ci saddr = PN_NO_ADDR; 1918c2ecf20Sopenharmony_ci rcu_read_unlock(); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (saddr == PN_NO_ADDR) { 1948c2ecf20Sopenharmony_ci /* Fallback to another device */ 1958c2ecf20Sopenharmony_ci struct net_device *def_dev; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci def_dev = phonet_device_get(dev_net(dev)); 1988c2ecf20Sopenharmony_ci if (def_dev) { 1998c2ecf20Sopenharmony_ci if (def_dev != dev) 2008c2ecf20Sopenharmony_ci saddr = phonet_address_get(def_dev, daddr); 2018c2ecf20Sopenharmony_ci dev_put(def_dev); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci return saddr; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ciint phonet_address_lookup(struct net *net, u8 addr) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct phonet_device_list *pndevs = phonet_device_list(net); 2108c2ecf20Sopenharmony_ci struct phonet_device *pnd; 2118c2ecf20Sopenharmony_ci int err = -EADDRNOTAVAIL; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci rcu_read_lock(); 2148c2ecf20Sopenharmony_ci list_for_each_entry_rcu(pnd, &pndevs->list, list) { 2158c2ecf20Sopenharmony_ci /* Don't allow unregistering devices! */ 2168c2ecf20Sopenharmony_ci if ((pnd->netdev->reg_state != NETREG_REGISTERED) || 2178c2ecf20Sopenharmony_ci ((pnd->netdev->flags & IFF_UP)) != IFF_UP) 2188c2ecf20Sopenharmony_ci continue; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (test_bit(addr >> 2, pnd->addrs)) { 2218c2ecf20Sopenharmony_ci err = 0; 2228c2ecf20Sopenharmony_ci goto found; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_cifound: 2268c2ecf20Sopenharmony_ci rcu_read_unlock(); 2278c2ecf20Sopenharmony_ci return err; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* automatically configure a Phonet device, if supported */ 2318c2ecf20Sopenharmony_cistatic int phonet_device_autoconf(struct net_device *dev) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct if_phonet_req req; 2348c2ecf20Sopenharmony_ci int ret; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (!dev->netdev_ops->ndo_do_ioctl) 2378c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci ret = dev->netdev_ops->ndo_do_ioctl(dev, (struct ifreq *)&req, 2408c2ecf20Sopenharmony_ci SIOCPNGAUTOCONF); 2418c2ecf20Sopenharmony_ci if (ret < 0) 2428c2ecf20Sopenharmony_ci return ret; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci ASSERT_RTNL(); 2458c2ecf20Sopenharmony_ci ret = phonet_address_add(dev, req.ifr_phonet_autoconf.device); 2468c2ecf20Sopenharmony_ci if (ret) 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci phonet_address_notify(RTM_NEWADDR, dev, 2498c2ecf20Sopenharmony_ci req.ifr_phonet_autoconf.device); 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic void phonet_route_autodel(struct net_device *dev) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct phonet_net *pnn = phonet_pernet(dev_net(dev)); 2568c2ecf20Sopenharmony_ci unsigned int i; 2578c2ecf20Sopenharmony_ci DECLARE_BITMAP(deleted, 64); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* Remove left-over Phonet routes */ 2608c2ecf20Sopenharmony_ci bitmap_zero(deleted, 64); 2618c2ecf20Sopenharmony_ci mutex_lock(&pnn->routes.lock); 2628c2ecf20Sopenharmony_ci for (i = 0; i < 64; i++) 2638c2ecf20Sopenharmony_ci if (rcu_access_pointer(pnn->routes.table[i]) == dev) { 2648c2ecf20Sopenharmony_ci RCU_INIT_POINTER(pnn->routes.table[i], NULL); 2658c2ecf20Sopenharmony_ci set_bit(i, deleted); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci mutex_unlock(&pnn->routes.lock); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (bitmap_empty(deleted, 64)) 2708c2ecf20Sopenharmony_ci return; /* short-circuit RCU */ 2718c2ecf20Sopenharmony_ci synchronize_rcu(); 2728c2ecf20Sopenharmony_ci for_each_set_bit(i, deleted, 64) { 2738c2ecf20Sopenharmony_ci rtm_phonet_notify(RTM_DELROUTE, dev, i); 2748c2ecf20Sopenharmony_ci dev_put(dev); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/* notify Phonet of device events */ 2798c2ecf20Sopenharmony_cistatic int phonet_device_notify(struct notifier_block *me, unsigned long what, 2808c2ecf20Sopenharmony_ci void *ptr) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci switch (what) { 2858c2ecf20Sopenharmony_ci case NETDEV_REGISTER: 2868c2ecf20Sopenharmony_ci if (dev->type == ARPHRD_PHONET) 2878c2ecf20Sopenharmony_ci phonet_device_autoconf(dev); 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 2908c2ecf20Sopenharmony_ci phonet_device_destroy(dev); 2918c2ecf20Sopenharmony_ci phonet_route_autodel(dev); 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic struct notifier_block phonet_device_notifier = { 2998c2ecf20Sopenharmony_ci .notifier_call = phonet_device_notify, 3008c2ecf20Sopenharmony_ci .priority = 0, 3018c2ecf20Sopenharmony_ci}; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/* Per-namespace Phonet devices handling */ 3048c2ecf20Sopenharmony_cistatic int __net_init phonet_init_net(struct net *net) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct phonet_net *pnn = phonet_pernet(net); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (!proc_create_net("phonet", 0, net->proc_net, &pn_sock_seq_ops, 3098c2ecf20Sopenharmony_ci sizeof(struct seq_net_private))) 3108c2ecf20Sopenharmony_ci return -ENOMEM; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pnn->pndevs.list); 3138c2ecf20Sopenharmony_ci mutex_init(&pnn->pndevs.lock); 3148c2ecf20Sopenharmony_ci mutex_init(&pnn->routes.lock); 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic void __net_exit phonet_exit_net(struct net *net) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct phonet_net *pnn = phonet_pernet(net); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci remove_proc_entry("phonet", net->proc_net); 3238c2ecf20Sopenharmony_ci WARN_ON_ONCE(!list_empty(&pnn->pndevs.list)); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic struct pernet_operations phonet_net_ops = { 3278c2ecf20Sopenharmony_ci .init = phonet_init_net, 3288c2ecf20Sopenharmony_ci .exit = phonet_exit_net, 3298c2ecf20Sopenharmony_ci .id = &phonet_net_id, 3308c2ecf20Sopenharmony_ci .size = sizeof(struct phonet_net), 3318c2ecf20Sopenharmony_ci}; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* Initialize Phonet devices list */ 3348c2ecf20Sopenharmony_ciint __init phonet_device_init(void) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci int err = register_pernet_subsys(&phonet_net_ops); 3378c2ecf20Sopenharmony_ci if (err) 3388c2ecf20Sopenharmony_ci return err; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci proc_create_net("pnresource", 0, init_net.proc_net, &pn_res_seq_ops, 3418c2ecf20Sopenharmony_ci sizeof(struct seq_net_private)); 3428c2ecf20Sopenharmony_ci register_netdevice_notifier(&phonet_device_notifier); 3438c2ecf20Sopenharmony_ci err = phonet_netlink_register(); 3448c2ecf20Sopenharmony_ci if (err) 3458c2ecf20Sopenharmony_ci phonet_device_exit(); 3468c2ecf20Sopenharmony_ci return err; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_civoid phonet_device_exit(void) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci rtnl_unregister_all(PF_PHONET); 3528c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&phonet_device_notifier); 3538c2ecf20Sopenharmony_ci unregister_pernet_subsys(&phonet_net_ops); 3548c2ecf20Sopenharmony_ci remove_proc_entry("pnresource", init_net.proc_net); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ciint phonet_route_add(struct net_device *dev, u8 daddr) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct phonet_net *pnn = phonet_pernet(dev_net(dev)); 3608c2ecf20Sopenharmony_ci struct phonet_routes *routes = &pnn->routes; 3618c2ecf20Sopenharmony_ci int err = -EEXIST; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci daddr = daddr >> 2; 3648c2ecf20Sopenharmony_ci mutex_lock(&routes->lock); 3658c2ecf20Sopenharmony_ci if (routes->table[daddr] == NULL) { 3668c2ecf20Sopenharmony_ci rcu_assign_pointer(routes->table[daddr], dev); 3678c2ecf20Sopenharmony_ci dev_hold(dev); 3688c2ecf20Sopenharmony_ci err = 0; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci mutex_unlock(&routes->lock); 3718c2ecf20Sopenharmony_ci return err; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ciint phonet_route_del(struct net_device *dev, u8 daddr) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct phonet_net *pnn = phonet_pernet(dev_net(dev)); 3778c2ecf20Sopenharmony_ci struct phonet_routes *routes = &pnn->routes; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci daddr = daddr >> 2; 3808c2ecf20Sopenharmony_ci mutex_lock(&routes->lock); 3818c2ecf20Sopenharmony_ci if (rcu_access_pointer(routes->table[daddr]) == dev) 3828c2ecf20Sopenharmony_ci RCU_INIT_POINTER(routes->table[daddr], NULL); 3838c2ecf20Sopenharmony_ci else 3848c2ecf20Sopenharmony_ci dev = NULL; 3858c2ecf20Sopenharmony_ci mutex_unlock(&routes->lock); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!dev) 3888c2ecf20Sopenharmony_ci return -ENOENT; 3898c2ecf20Sopenharmony_ci synchronize_rcu(); 3908c2ecf20Sopenharmony_ci dev_put(dev); 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistruct net_device *phonet_route_get_rcu(struct net *net, u8 daddr) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct phonet_net *pnn = phonet_pernet(net); 3978c2ecf20Sopenharmony_ci struct phonet_routes *routes = &pnn->routes; 3988c2ecf20Sopenharmony_ci struct net_device *dev; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci daddr >>= 2; 4018c2ecf20Sopenharmony_ci dev = rcu_dereference(routes->table[daddr]); 4028c2ecf20Sopenharmony_ci return dev; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistruct net_device *phonet_route_output(struct net *net, u8 daddr) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct phonet_net *pnn = phonet_pernet(net); 4088c2ecf20Sopenharmony_ci struct phonet_routes *routes = &pnn->routes; 4098c2ecf20Sopenharmony_ci struct net_device *dev; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci daddr >>= 2; 4128c2ecf20Sopenharmony_ci rcu_read_lock(); 4138c2ecf20Sopenharmony_ci dev = rcu_dereference(routes->table[daddr]); 4148c2ecf20Sopenharmony_ci if (dev) 4158c2ecf20Sopenharmony_ci dev_hold(dev); 4168c2ecf20Sopenharmony_ci rcu_read_unlock(); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (!dev) 4198c2ecf20Sopenharmony_ci dev = phonet_device_get(net); /* Default route */ 4208c2ecf20Sopenharmony_ci return dev; 4218c2ecf20Sopenharmony_ci} 422