11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * net-sysfs.c - network device class and attributes 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 2003 Stephen Hemminger <shemminger@osdl.org> 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 71da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 81da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 91da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 101da177e4SLinus Torvalds */ 111da177e4SLinus Torvalds 124fc268d2SRandy Dunlap #include <linux/capability.h> 131da177e4SLinus Torvalds #include <linux/kernel.h> 141da177e4SLinus Torvalds #include <linux/netdevice.h> 151da177e4SLinus Torvalds #include <linux/if_arp.h> 165a0e3ad6STejun Heo #include <linux/slab.h> 17608b4b95SEric W. Biederman #include <linux/nsproxy.h> 181da177e4SLinus Torvalds #include <net/sock.h> 19608b4b95SEric W. Biederman #include <net/net_namespace.h> 201da177e4SLinus Torvalds #include <linux/rtnetlink.h> 211da177e4SLinus Torvalds #include <linux/wireless.h> 22fec5e652STom Herbert #include <linux/vmalloc.h> 238f1546caSJohannes Berg #include <net/wext.h> 241da177e4SLinus Torvalds 25342709efSPavel Emelyanov #include "net-sysfs.h" 26342709efSPavel Emelyanov 278b41d188SEric W. Biederman #ifdef CONFIG_SYSFS 281da177e4SLinus Torvalds static const char fmt_hex[] = "%#x\n"; 29d1102b59SDavid S. Miller static const char fmt_long_hex[] = "%#lx\n"; 301da177e4SLinus Torvalds static const char fmt_dec[] = "%d\n"; 311da177e4SLinus Torvalds static const char fmt_ulong[] = "%lu\n"; 32be1f3c2cSBen Hutchings static const char fmt_u64[] = "%llu\n"; 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds static inline int dev_isalive(const struct net_device *dev) 351da177e4SLinus Torvalds { 36fe9925b5SStephen Hemminger return dev->reg_state <= NETREG_REGISTERED; 371da177e4SLinus Torvalds } 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds /* use same locking rules as GIF* ioctl's */ 4043cb76d9SGreg Kroah-Hartman static ssize_t netdev_show(const struct device *dev, 4143cb76d9SGreg Kroah-Hartman struct device_attribute *attr, char *buf, 421da177e4SLinus Torvalds ssize_t (*format)(const struct net_device *, char *)) 431da177e4SLinus Torvalds { 4443cb76d9SGreg Kroah-Hartman struct net_device *net = to_net_dev(dev); 451da177e4SLinus Torvalds ssize_t ret = -EINVAL; 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds read_lock(&dev_base_lock); 481da177e4SLinus Torvalds if (dev_isalive(net)) 491da177e4SLinus Torvalds ret = (*format)(net, buf); 501da177e4SLinus Torvalds read_unlock(&dev_base_lock); 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds return ret; 531da177e4SLinus Torvalds } 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds /* generate a show function for simple field */ 561da177e4SLinus Torvalds #define NETDEVICE_SHOW(field, format_string) \ 571da177e4SLinus Torvalds static ssize_t format_##field(const struct net_device *net, char *buf) \ 581da177e4SLinus Torvalds { \ 591da177e4SLinus Torvalds return sprintf(buf, format_string, net->field); \ 601da177e4SLinus Torvalds } \ 6143cb76d9SGreg Kroah-Hartman static ssize_t show_##field(struct device *dev, \ 6243cb76d9SGreg Kroah-Hartman struct device_attribute *attr, char *buf) \ 631da177e4SLinus Torvalds { \ 6443cb76d9SGreg Kroah-Hartman return netdev_show(dev, attr, buf, format_##field); \ 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds /* use same locking and permission rules as SIF* ioctl's */ 6943cb76d9SGreg Kroah-Hartman static ssize_t netdev_store(struct device *dev, struct device_attribute *attr, 701da177e4SLinus Torvalds const char *buf, size_t len, 711da177e4SLinus Torvalds int (*set)(struct net_device *, unsigned long)) 721da177e4SLinus Torvalds { 731da177e4SLinus Torvalds struct net_device *net = to_net_dev(dev); 741da177e4SLinus Torvalds char *endp; 751da177e4SLinus Torvalds unsigned long new; 761da177e4SLinus Torvalds int ret = -EINVAL; 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 791da177e4SLinus Torvalds return -EPERM; 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds new = simple_strtoul(buf, &endp, 0); 821da177e4SLinus Torvalds if (endp == buf) 831da177e4SLinus Torvalds goto err; 841da177e4SLinus Torvalds 855a5990d3SStephen Hemminger if (!rtnl_trylock()) 86336ca57cSEric W. Biederman return restart_syscall(); 875a5990d3SStephen Hemminger 881da177e4SLinus Torvalds if (dev_isalive(net)) { 891da177e4SLinus Torvalds if ((ret = (*set)(net, new)) == 0) 901da177e4SLinus Torvalds ret = len; 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds rtnl_unlock(); 931da177e4SLinus Torvalds err: 941da177e4SLinus Torvalds return ret; 951da177e4SLinus Torvalds } 961da177e4SLinus Torvalds 979d29672cSDavid Woodhouse NETDEVICE_SHOW(dev_id, fmt_hex); 98c1f79426SStefan Assmann NETDEVICE_SHOW(addr_assign_type, fmt_dec); 99fd586bacSKay Sievers NETDEVICE_SHOW(addr_len, fmt_dec); 100fd586bacSKay Sievers NETDEVICE_SHOW(iflink, fmt_dec); 101fd586bacSKay Sievers NETDEVICE_SHOW(ifindex, fmt_dec); 10204ed3e74SMichał Mirosław NETDEVICE_SHOW(features, fmt_hex); 103fd586bacSKay Sievers NETDEVICE_SHOW(type, fmt_dec); 104b00055aaSStefan Rompf NETDEVICE_SHOW(link_mode, fmt_dec); 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds /* use same locking rules as GIFHWADDR ioctl's */ 10743cb76d9SGreg Kroah-Hartman static ssize_t show_address(struct device *dev, struct device_attribute *attr, 10843cb76d9SGreg Kroah-Hartman char *buf) 1091da177e4SLinus Torvalds { 1101da177e4SLinus Torvalds struct net_device *net = to_net_dev(dev); 1111da177e4SLinus Torvalds ssize_t ret = -EINVAL; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds read_lock(&dev_base_lock); 1141da177e4SLinus Torvalds if (dev_isalive(net)) 1157ffc49a6SMichael Chan ret = sysfs_format_mac(buf, net->dev_addr, net->addr_len); 1161da177e4SLinus Torvalds read_unlock(&dev_base_lock); 1171da177e4SLinus Torvalds return ret; 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds 12043cb76d9SGreg Kroah-Hartman static ssize_t show_broadcast(struct device *dev, 12143cb76d9SGreg Kroah-Hartman struct device_attribute *attr, char *buf) 1221da177e4SLinus Torvalds { 1231da177e4SLinus Torvalds struct net_device *net = to_net_dev(dev); 1241da177e4SLinus Torvalds if (dev_isalive(net)) 1257ffc49a6SMichael Chan return sysfs_format_mac(buf, net->broadcast, net->addr_len); 1261da177e4SLinus Torvalds return -EINVAL; 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds 12943cb76d9SGreg Kroah-Hartman static ssize_t show_carrier(struct device *dev, 13043cb76d9SGreg Kroah-Hartman struct device_attribute *attr, char *buf) 1311da177e4SLinus Torvalds { 1321da177e4SLinus Torvalds struct net_device *netdev = to_net_dev(dev); 1331da177e4SLinus Torvalds if (netif_running(netdev)) { 1341da177e4SLinus Torvalds return sprintf(buf, fmt_dec, !!netif_carrier_ok(netdev)); 1351da177e4SLinus Torvalds } 1361da177e4SLinus Torvalds return -EINVAL; 1371da177e4SLinus Torvalds } 1381da177e4SLinus Torvalds 139d519e17eSAndy Gospodarek static ssize_t show_speed(struct device *dev, 140d519e17eSAndy Gospodarek struct device_attribute *attr, char *buf) 141d519e17eSAndy Gospodarek { 142d519e17eSAndy Gospodarek struct net_device *netdev = to_net_dev(dev); 143d519e17eSAndy Gospodarek int ret = -EINVAL; 144d519e17eSAndy Gospodarek 145d519e17eSAndy Gospodarek if (!rtnl_trylock()) 146d519e17eSAndy Gospodarek return restart_syscall(); 147d519e17eSAndy Gospodarek 148ac5e3af9SEric Dumazet if (netif_running(netdev) && 149ac5e3af9SEric Dumazet netdev->ethtool_ops && 150ac5e3af9SEric Dumazet netdev->ethtool_ops->get_settings) { 151d519e17eSAndy Gospodarek struct ethtool_cmd cmd = { ETHTOOL_GSET }; 152d519e17eSAndy Gospodarek 153d519e17eSAndy Gospodarek if (!netdev->ethtool_ops->get_settings(netdev, &cmd)) 154d519e17eSAndy Gospodarek ret = sprintf(buf, fmt_dec, ethtool_cmd_speed(&cmd)); 155d519e17eSAndy Gospodarek } 156d519e17eSAndy Gospodarek rtnl_unlock(); 157d519e17eSAndy Gospodarek return ret; 158d519e17eSAndy Gospodarek } 159d519e17eSAndy Gospodarek 160d519e17eSAndy Gospodarek static ssize_t show_duplex(struct device *dev, 161d519e17eSAndy Gospodarek struct device_attribute *attr, char *buf) 162d519e17eSAndy Gospodarek { 163d519e17eSAndy Gospodarek struct net_device *netdev = to_net_dev(dev); 164d519e17eSAndy Gospodarek int ret = -EINVAL; 165d519e17eSAndy Gospodarek 166d519e17eSAndy Gospodarek if (!rtnl_trylock()) 167d519e17eSAndy Gospodarek return restart_syscall(); 168d519e17eSAndy Gospodarek 169ac5e3af9SEric Dumazet if (netif_running(netdev) && 170ac5e3af9SEric Dumazet netdev->ethtool_ops && 171ac5e3af9SEric Dumazet netdev->ethtool_ops->get_settings) { 172d519e17eSAndy Gospodarek struct ethtool_cmd cmd = { ETHTOOL_GSET }; 173d519e17eSAndy Gospodarek 174d519e17eSAndy Gospodarek if (!netdev->ethtool_ops->get_settings(netdev, &cmd)) 175d519e17eSAndy Gospodarek ret = sprintf(buf, "%s\n", cmd.duplex ? "full" : "half"); 176d519e17eSAndy Gospodarek } 177d519e17eSAndy Gospodarek rtnl_unlock(); 178d519e17eSAndy Gospodarek return ret; 179d519e17eSAndy Gospodarek } 180d519e17eSAndy Gospodarek 18143cb76d9SGreg Kroah-Hartman static ssize_t show_dormant(struct device *dev, 18243cb76d9SGreg Kroah-Hartman struct device_attribute *attr, char *buf) 183b00055aaSStefan Rompf { 184b00055aaSStefan Rompf struct net_device *netdev = to_net_dev(dev); 185b00055aaSStefan Rompf 186b00055aaSStefan Rompf if (netif_running(netdev)) 187b00055aaSStefan Rompf return sprintf(buf, fmt_dec, !!netif_dormant(netdev)); 188b00055aaSStefan Rompf 189b00055aaSStefan Rompf return -EINVAL; 190b00055aaSStefan Rompf } 191b00055aaSStefan Rompf 19236cbd3dcSJan Engelhardt static const char *const operstates[] = { 193b00055aaSStefan Rompf "unknown", 194b00055aaSStefan Rompf "notpresent", /* currently unused */ 195b00055aaSStefan Rompf "down", 196b00055aaSStefan Rompf "lowerlayerdown", 197b00055aaSStefan Rompf "testing", /* currently unused */ 198b00055aaSStefan Rompf "dormant", 199b00055aaSStefan Rompf "up" 200b00055aaSStefan Rompf }; 201b00055aaSStefan Rompf 20243cb76d9SGreg Kroah-Hartman static ssize_t show_operstate(struct device *dev, 20343cb76d9SGreg Kroah-Hartman struct device_attribute *attr, char *buf) 204b00055aaSStefan Rompf { 205b00055aaSStefan Rompf const struct net_device *netdev = to_net_dev(dev); 206b00055aaSStefan Rompf unsigned char operstate; 207b00055aaSStefan Rompf 208b00055aaSStefan Rompf read_lock(&dev_base_lock); 209b00055aaSStefan Rompf operstate = netdev->operstate; 210b00055aaSStefan Rompf if (!netif_running(netdev)) 211b00055aaSStefan Rompf operstate = IF_OPER_DOWN; 212b00055aaSStefan Rompf read_unlock(&dev_base_lock); 213b00055aaSStefan Rompf 214e3a5cd9eSAdrian Bunk if (operstate >= ARRAY_SIZE(operstates)) 215b00055aaSStefan Rompf return -EINVAL; /* should not happen */ 216b00055aaSStefan Rompf 217b00055aaSStefan Rompf return sprintf(buf, "%s\n", operstates[operstate]); 218b00055aaSStefan Rompf } 219b00055aaSStefan Rompf 2201da177e4SLinus Torvalds /* read-write attributes */ 2211da177e4SLinus Torvalds NETDEVICE_SHOW(mtu, fmt_dec); 2221da177e4SLinus Torvalds 2231da177e4SLinus Torvalds static int change_mtu(struct net_device *net, unsigned long new_mtu) 2241da177e4SLinus Torvalds { 2251da177e4SLinus Torvalds return dev_set_mtu(net, (int) new_mtu); 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds 22843cb76d9SGreg Kroah-Hartman static ssize_t store_mtu(struct device *dev, struct device_attribute *attr, 22943cb76d9SGreg Kroah-Hartman const char *buf, size_t len) 2301da177e4SLinus Torvalds { 23143cb76d9SGreg Kroah-Hartman return netdev_store(dev, attr, buf, len, change_mtu); 2321da177e4SLinus Torvalds } 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds NETDEVICE_SHOW(flags, fmt_hex); 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds static int change_flags(struct net_device *net, unsigned long new_flags) 2371da177e4SLinus Torvalds { 2381da177e4SLinus Torvalds return dev_change_flags(net, (unsigned) new_flags); 2391da177e4SLinus Torvalds } 2401da177e4SLinus Torvalds 24143cb76d9SGreg Kroah-Hartman static ssize_t store_flags(struct device *dev, struct device_attribute *attr, 24243cb76d9SGreg Kroah-Hartman const char *buf, size_t len) 2431da177e4SLinus Torvalds { 24443cb76d9SGreg Kroah-Hartman return netdev_store(dev, attr, buf, len, change_flags); 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds NETDEVICE_SHOW(tx_queue_len, fmt_ulong); 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds static int change_tx_queue_len(struct net_device *net, unsigned long new_len) 2501da177e4SLinus Torvalds { 2511da177e4SLinus Torvalds net->tx_queue_len = new_len; 2521da177e4SLinus Torvalds return 0; 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds 25543cb76d9SGreg Kroah-Hartman static ssize_t store_tx_queue_len(struct device *dev, 25643cb76d9SGreg Kroah-Hartman struct device_attribute *attr, 25743cb76d9SGreg Kroah-Hartman const char *buf, size_t len) 2581da177e4SLinus Torvalds { 25943cb76d9SGreg Kroah-Hartman return netdev_store(dev, attr, buf, len, change_tx_queue_len); 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds 2620b815a1aSStephen Hemminger static ssize_t store_ifalias(struct device *dev, struct device_attribute *attr, 2630b815a1aSStephen Hemminger const char *buf, size_t len) 2640b815a1aSStephen Hemminger { 2650b815a1aSStephen Hemminger struct net_device *netdev = to_net_dev(dev); 2660b815a1aSStephen Hemminger size_t count = len; 2670b815a1aSStephen Hemminger ssize_t ret; 2680b815a1aSStephen Hemminger 2690b815a1aSStephen Hemminger if (!capable(CAP_NET_ADMIN)) 2700b815a1aSStephen Hemminger return -EPERM; 2710b815a1aSStephen Hemminger 2720b815a1aSStephen Hemminger /* ignore trailing newline */ 2730b815a1aSStephen Hemminger if (len > 0 && buf[len - 1] == '\n') 2740b815a1aSStephen Hemminger --count; 2750b815a1aSStephen Hemminger 276336ca57cSEric W. Biederman if (!rtnl_trylock()) 277336ca57cSEric W. Biederman return restart_syscall(); 2780b815a1aSStephen Hemminger ret = dev_set_alias(netdev, buf, count); 2790b815a1aSStephen Hemminger rtnl_unlock(); 2800b815a1aSStephen Hemminger 2810b815a1aSStephen Hemminger return ret < 0 ? ret : len; 2820b815a1aSStephen Hemminger } 2830b815a1aSStephen Hemminger 2840b815a1aSStephen Hemminger static ssize_t show_ifalias(struct device *dev, 2850b815a1aSStephen Hemminger struct device_attribute *attr, char *buf) 2860b815a1aSStephen Hemminger { 2870b815a1aSStephen Hemminger const struct net_device *netdev = to_net_dev(dev); 2880b815a1aSStephen Hemminger ssize_t ret = 0; 2890b815a1aSStephen Hemminger 290336ca57cSEric W. Biederman if (!rtnl_trylock()) 291336ca57cSEric W. Biederman return restart_syscall(); 2920b815a1aSStephen Hemminger if (netdev->ifalias) 2930b815a1aSStephen Hemminger ret = sprintf(buf, "%s\n", netdev->ifalias); 2940b815a1aSStephen Hemminger rtnl_unlock(); 2950b815a1aSStephen Hemminger return ret; 2960b815a1aSStephen Hemminger } 2970b815a1aSStephen Hemminger 298a512b92bSVlad Dogaru NETDEVICE_SHOW(group, fmt_dec); 299a512b92bSVlad Dogaru 300a512b92bSVlad Dogaru static int change_group(struct net_device *net, unsigned long new_group) 301a512b92bSVlad Dogaru { 302a512b92bSVlad Dogaru dev_set_group(net, (int) new_group); 303a512b92bSVlad Dogaru return 0; 304a512b92bSVlad Dogaru } 305a512b92bSVlad Dogaru 306a512b92bSVlad Dogaru static ssize_t store_group(struct device *dev, struct device_attribute *attr, 307a512b92bSVlad Dogaru const char *buf, size_t len) 308a512b92bSVlad Dogaru { 309a512b92bSVlad Dogaru return netdev_store(dev, attr, buf, len, change_group); 310a512b92bSVlad Dogaru } 311a512b92bSVlad Dogaru 31243cb76d9SGreg Kroah-Hartman static struct device_attribute net_class_attributes[] = { 313c1f79426SStefan Assmann __ATTR(addr_assign_type, S_IRUGO, show_addr_assign_type, NULL), 314fd586bacSKay Sievers __ATTR(addr_len, S_IRUGO, show_addr_len, NULL), 3159d29672cSDavid Woodhouse __ATTR(dev_id, S_IRUGO, show_dev_id, NULL), 3160b815a1aSStephen Hemminger __ATTR(ifalias, S_IRUGO | S_IWUSR, show_ifalias, store_ifalias), 317fd586bacSKay Sievers __ATTR(iflink, S_IRUGO, show_iflink, NULL), 318fd586bacSKay Sievers __ATTR(ifindex, S_IRUGO, show_ifindex, NULL), 319fd586bacSKay Sievers __ATTR(features, S_IRUGO, show_features, NULL), 320fd586bacSKay Sievers __ATTR(type, S_IRUGO, show_type, NULL), 321b00055aaSStefan Rompf __ATTR(link_mode, S_IRUGO, show_link_mode, NULL), 322fd586bacSKay Sievers __ATTR(address, S_IRUGO, show_address, NULL), 323fd586bacSKay Sievers __ATTR(broadcast, S_IRUGO, show_broadcast, NULL), 324fd586bacSKay Sievers __ATTR(carrier, S_IRUGO, show_carrier, NULL), 325d519e17eSAndy Gospodarek __ATTR(speed, S_IRUGO, show_speed, NULL), 326d519e17eSAndy Gospodarek __ATTR(duplex, S_IRUGO, show_duplex, NULL), 327b00055aaSStefan Rompf __ATTR(dormant, S_IRUGO, show_dormant, NULL), 328b00055aaSStefan Rompf __ATTR(operstate, S_IRUGO, show_operstate, NULL), 329fd586bacSKay Sievers __ATTR(mtu, S_IRUGO | S_IWUSR, show_mtu, store_mtu), 330fd586bacSKay Sievers __ATTR(flags, S_IRUGO | S_IWUSR, show_flags, store_flags), 331fd586bacSKay Sievers __ATTR(tx_queue_len, S_IRUGO | S_IWUSR, show_tx_queue_len, 332fd586bacSKay Sievers store_tx_queue_len), 333b6644cb7SXiaotian Feng __ATTR(netdev_group, S_IRUGO | S_IWUSR, show_group, store_group), 334fd586bacSKay Sievers {} 3351da177e4SLinus Torvalds }; 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds /* Show a given an attribute in the statistics group */ 33843cb76d9SGreg Kroah-Hartman static ssize_t netstat_show(const struct device *d, 33943cb76d9SGreg Kroah-Hartman struct device_attribute *attr, char *buf, 3401da177e4SLinus Torvalds unsigned long offset) 3411da177e4SLinus Torvalds { 34243cb76d9SGreg Kroah-Hartman struct net_device *dev = to_net_dev(d); 3431da177e4SLinus Torvalds ssize_t ret = -EINVAL; 3441da177e4SLinus Torvalds 345be1f3c2cSBen Hutchings WARN_ON(offset > sizeof(struct rtnl_link_stats64) || 346be1f3c2cSBen Hutchings offset % sizeof(u64) != 0); 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds read_lock(&dev_base_lock); 34996e74088SPavel Emelyanov if (dev_isalive(dev)) { 35028172739SEric Dumazet struct rtnl_link_stats64 temp; 35128172739SEric Dumazet const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); 35228172739SEric Dumazet 353be1f3c2cSBen Hutchings ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *) stats) + offset)); 35496e74088SPavel Emelyanov } 3551da177e4SLinus Torvalds read_unlock(&dev_base_lock); 3561da177e4SLinus Torvalds return ret; 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds /* generate a read-only statistics attribute */ 3601da177e4SLinus Torvalds #define NETSTAT_ENTRY(name) \ 36143cb76d9SGreg Kroah-Hartman static ssize_t show_##name(struct device *d, \ 36243cb76d9SGreg Kroah-Hartman struct device_attribute *attr, char *buf) \ 3631da177e4SLinus Torvalds { \ 36443cb76d9SGreg Kroah-Hartman return netstat_show(d, attr, buf, \ 365be1f3c2cSBen Hutchings offsetof(struct rtnl_link_stats64, name)); \ 3661da177e4SLinus Torvalds } \ 36743cb76d9SGreg Kroah-Hartman static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds NETSTAT_ENTRY(rx_packets); 3701da177e4SLinus Torvalds NETSTAT_ENTRY(tx_packets); 3711da177e4SLinus Torvalds NETSTAT_ENTRY(rx_bytes); 3721da177e4SLinus Torvalds NETSTAT_ENTRY(tx_bytes); 3731da177e4SLinus Torvalds NETSTAT_ENTRY(rx_errors); 3741da177e4SLinus Torvalds NETSTAT_ENTRY(tx_errors); 3751da177e4SLinus Torvalds NETSTAT_ENTRY(rx_dropped); 3761da177e4SLinus Torvalds NETSTAT_ENTRY(tx_dropped); 3771da177e4SLinus Torvalds NETSTAT_ENTRY(multicast); 3781da177e4SLinus Torvalds NETSTAT_ENTRY(collisions); 3791da177e4SLinus Torvalds NETSTAT_ENTRY(rx_length_errors); 3801da177e4SLinus Torvalds NETSTAT_ENTRY(rx_over_errors); 3811da177e4SLinus Torvalds NETSTAT_ENTRY(rx_crc_errors); 3821da177e4SLinus Torvalds NETSTAT_ENTRY(rx_frame_errors); 3831da177e4SLinus Torvalds NETSTAT_ENTRY(rx_fifo_errors); 3841da177e4SLinus Torvalds NETSTAT_ENTRY(rx_missed_errors); 3851da177e4SLinus Torvalds NETSTAT_ENTRY(tx_aborted_errors); 3861da177e4SLinus Torvalds NETSTAT_ENTRY(tx_carrier_errors); 3871da177e4SLinus Torvalds NETSTAT_ENTRY(tx_fifo_errors); 3881da177e4SLinus Torvalds NETSTAT_ENTRY(tx_heartbeat_errors); 3891da177e4SLinus Torvalds NETSTAT_ENTRY(tx_window_errors); 3901da177e4SLinus Torvalds NETSTAT_ENTRY(rx_compressed); 3911da177e4SLinus Torvalds NETSTAT_ENTRY(tx_compressed); 3921da177e4SLinus Torvalds 3931da177e4SLinus Torvalds static struct attribute *netstat_attrs[] = { 39443cb76d9SGreg Kroah-Hartman &dev_attr_rx_packets.attr, 39543cb76d9SGreg Kroah-Hartman &dev_attr_tx_packets.attr, 39643cb76d9SGreg Kroah-Hartman &dev_attr_rx_bytes.attr, 39743cb76d9SGreg Kroah-Hartman &dev_attr_tx_bytes.attr, 39843cb76d9SGreg Kroah-Hartman &dev_attr_rx_errors.attr, 39943cb76d9SGreg Kroah-Hartman &dev_attr_tx_errors.attr, 40043cb76d9SGreg Kroah-Hartman &dev_attr_rx_dropped.attr, 40143cb76d9SGreg Kroah-Hartman &dev_attr_tx_dropped.attr, 40243cb76d9SGreg Kroah-Hartman &dev_attr_multicast.attr, 40343cb76d9SGreg Kroah-Hartman &dev_attr_collisions.attr, 40443cb76d9SGreg Kroah-Hartman &dev_attr_rx_length_errors.attr, 40543cb76d9SGreg Kroah-Hartman &dev_attr_rx_over_errors.attr, 40643cb76d9SGreg Kroah-Hartman &dev_attr_rx_crc_errors.attr, 40743cb76d9SGreg Kroah-Hartman &dev_attr_rx_frame_errors.attr, 40843cb76d9SGreg Kroah-Hartman &dev_attr_rx_fifo_errors.attr, 40943cb76d9SGreg Kroah-Hartman &dev_attr_rx_missed_errors.attr, 41043cb76d9SGreg Kroah-Hartman &dev_attr_tx_aborted_errors.attr, 41143cb76d9SGreg Kroah-Hartman &dev_attr_tx_carrier_errors.attr, 41243cb76d9SGreg Kroah-Hartman &dev_attr_tx_fifo_errors.attr, 41343cb76d9SGreg Kroah-Hartman &dev_attr_tx_heartbeat_errors.attr, 41443cb76d9SGreg Kroah-Hartman &dev_attr_tx_window_errors.attr, 41543cb76d9SGreg Kroah-Hartman &dev_attr_rx_compressed.attr, 41643cb76d9SGreg Kroah-Hartman &dev_attr_tx_compressed.attr, 4171da177e4SLinus Torvalds NULL 4181da177e4SLinus Torvalds }; 4191da177e4SLinus Torvalds 4201da177e4SLinus Torvalds 4211da177e4SLinus Torvalds static struct attribute_group netstat_group = { 4221da177e4SLinus Torvalds .name = "statistics", 4231da177e4SLinus Torvalds .attrs = netstat_attrs, 4241da177e4SLinus Torvalds }; 4251da177e4SLinus Torvalds 42622bb1be4SJohannes Berg #ifdef CONFIG_WIRELESS_EXT_SYSFS 4271da177e4SLinus Torvalds /* helper function that does all the locking etc for wireless stats */ 42843cb76d9SGreg Kroah-Hartman static ssize_t wireless_show(struct device *d, char *buf, 4291da177e4SLinus Torvalds ssize_t (*format)(const struct iw_statistics *, 4301da177e4SLinus Torvalds char *)) 4311da177e4SLinus Torvalds { 43243cb76d9SGreg Kroah-Hartman struct net_device *dev = to_net_dev(d); 4338f1546caSJohannes Berg const struct iw_statistics *iw; 4341da177e4SLinus Torvalds ssize_t ret = -EINVAL; 4351da177e4SLinus Torvalds 436b8afe641SEric W. Biederman if (!rtnl_trylock()) 437b8afe641SEric W. Biederman return restart_syscall(); 4386dd214b5SAndrey Borzenkov if (dev_isalive(dev)) { 4398f1546caSJohannes Berg iw = get_wireless_stats(dev); 4408f1546caSJohannes Berg if (iw) 4411da177e4SLinus Torvalds ret = (*format)(iw, buf); 4426dd214b5SAndrey Borzenkov } 443a160ee69SJohannes Berg rtnl_unlock(); 4441da177e4SLinus Torvalds 4451da177e4SLinus Torvalds return ret; 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds 4481da177e4SLinus Torvalds /* show function template for wireless fields */ 4491da177e4SLinus Torvalds #define WIRELESS_SHOW(name, field, format_string) \ 4501da177e4SLinus Torvalds static ssize_t format_iw_##name(const struct iw_statistics *iw, char *buf) \ 4511da177e4SLinus Torvalds { \ 4521da177e4SLinus Torvalds return sprintf(buf, format_string, iw->field); \ 4531da177e4SLinus Torvalds } \ 45443cb76d9SGreg Kroah-Hartman static ssize_t show_iw_##name(struct device *d, \ 45543cb76d9SGreg Kroah-Hartman struct device_attribute *attr, char *buf) \ 4561da177e4SLinus Torvalds { \ 45743cb76d9SGreg Kroah-Hartman return wireless_show(d, buf, format_iw_##name); \ 4581da177e4SLinus Torvalds } \ 45943cb76d9SGreg Kroah-Hartman static DEVICE_ATTR(name, S_IRUGO, show_iw_##name, NULL) 4601da177e4SLinus Torvalds 4611da177e4SLinus Torvalds WIRELESS_SHOW(status, status, fmt_hex); 4621da177e4SLinus Torvalds WIRELESS_SHOW(link, qual.qual, fmt_dec); 4631da177e4SLinus Torvalds WIRELESS_SHOW(level, qual.level, fmt_dec); 4641da177e4SLinus Torvalds WIRELESS_SHOW(noise, qual.noise, fmt_dec); 4651da177e4SLinus Torvalds WIRELESS_SHOW(nwid, discard.nwid, fmt_dec); 4661da177e4SLinus Torvalds WIRELESS_SHOW(crypt, discard.code, fmt_dec); 4671da177e4SLinus Torvalds WIRELESS_SHOW(fragment, discard.fragment, fmt_dec); 4681da177e4SLinus Torvalds WIRELESS_SHOW(misc, discard.misc, fmt_dec); 4691da177e4SLinus Torvalds WIRELESS_SHOW(retries, discard.retries, fmt_dec); 4701da177e4SLinus Torvalds WIRELESS_SHOW(beacon, miss.beacon, fmt_dec); 4711da177e4SLinus Torvalds 4721da177e4SLinus Torvalds static struct attribute *wireless_attrs[] = { 47343cb76d9SGreg Kroah-Hartman &dev_attr_status.attr, 47443cb76d9SGreg Kroah-Hartman &dev_attr_link.attr, 47543cb76d9SGreg Kroah-Hartman &dev_attr_level.attr, 47643cb76d9SGreg Kroah-Hartman &dev_attr_noise.attr, 47743cb76d9SGreg Kroah-Hartman &dev_attr_nwid.attr, 47843cb76d9SGreg Kroah-Hartman &dev_attr_crypt.attr, 47943cb76d9SGreg Kroah-Hartman &dev_attr_fragment.attr, 48043cb76d9SGreg Kroah-Hartman &dev_attr_retries.attr, 48143cb76d9SGreg Kroah-Hartman &dev_attr_misc.attr, 48243cb76d9SGreg Kroah-Hartman &dev_attr_beacon.attr, 4831da177e4SLinus Torvalds NULL 4841da177e4SLinus Torvalds }; 4851da177e4SLinus Torvalds 4861da177e4SLinus Torvalds static struct attribute_group wireless_group = { 4871da177e4SLinus Torvalds .name = "wireless", 4881da177e4SLinus Torvalds .attrs = wireless_attrs, 4891da177e4SLinus Torvalds }; 4901da177e4SLinus Torvalds #endif 491d6523ddfSEric W. Biederman #endif /* CONFIG_SYSFS */ 4921da177e4SLinus Torvalds 49330bde1f5SStephen Rothwell #ifdef CONFIG_RPS 4940a9627f2STom Herbert /* 4950a9627f2STom Herbert * RX queue sysfs structures and functions. 4960a9627f2STom Herbert */ 4970a9627f2STom Herbert struct rx_queue_attribute { 4980a9627f2STom Herbert struct attribute attr; 4990a9627f2STom Herbert ssize_t (*show)(struct netdev_rx_queue *queue, 5000a9627f2STom Herbert struct rx_queue_attribute *attr, char *buf); 5010a9627f2STom Herbert ssize_t (*store)(struct netdev_rx_queue *queue, 5020a9627f2STom Herbert struct rx_queue_attribute *attr, const char *buf, size_t len); 5030a9627f2STom Herbert }; 5040a9627f2STom Herbert #define to_rx_queue_attr(_attr) container_of(_attr, \ 5050a9627f2STom Herbert struct rx_queue_attribute, attr) 5060a9627f2STom Herbert 5070a9627f2STom Herbert #define to_rx_queue(obj) container_of(obj, struct netdev_rx_queue, kobj) 5080a9627f2STom Herbert 5090a9627f2STom Herbert static ssize_t rx_queue_attr_show(struct kobject *kobj, struct attribute *attr, 5100a9627f2STom Herbert char *buf) 5110a9627f2STom Herbert { 5120a9627f2STom Herbert struct rx_queue_attribute *attribute = to_rx_queue_attr(attr); 5130a9627f2STom Herbert struct netdev_rx_queue *queue = to_rx_queue(kobj); 5140a9627f2STom Herbert 5150a9627f2STom Herbert if (!attribute->show) 5160a9627f2STom Herbert return -EIO; 5170a9627f2STom Herbert 5180a9627f2STom Herbert return attribute->show(queue, attribute, buf); 5190a9627f2STom Herbert } 5200a9627f2STom Herbert 5210a9627f2STom Herbert static ssize_t rx_queue_attr_store(struct kobject *kobj, struct attribute *attr, 5220a9627f2STom Herbert const char *buf, size_t count) 5230a9627f2STom Herbert { 5240a9627f2STom Herbert struct rx_queue_attribute *attribute = to_rx_queue_attr(attr); 5250a9627f2STom Herbert struct netdev_rx_queue *queue = to_rx_queue(kobj); 5260a9627f2STom Herbert 5270a9627f2STom Herbert if (!attribute->store) 5280a9627f2STom Herbert return -EIO; 5290a9627f2STom Herbert 5300a9627f2STom Herbert return attribute->store(queue, attribute, buf, count); 5310a9627f2STom Herbert } 5320a9627f2STom Herbert 533fa50d645Sstephen hemminger static const struct sysfs_ops rx_queue_sysfs_ops = { 5340a9627f2STom Herbert .show = rx_queue_attr_show, 5350a9627f2STom Herbert .store = rx_queue_attr_store, 5360a9627f2STom Herbert }; 5370a9627f2STom Herbert 5380a9627f2STom Herbert static ssize_t show_rps_map(struct netdev_rx_queue *queue, 5390a9627f2STom Herbert struct rx_queue_attribute *attribute, char *buf) 5400a9627f2STom Herbert { 5410a9627f2STom Herbert struct rps_map *map; 5420a9627f2STom Herbert cpumask_var_t mask; 5430a9627f2STom Herbert size_t len = 0; 5440a9627f2STom Herbert int i; 5450a9627f2STom Herbert 5460a9627f2STom Herbert if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) 5470a9627f2STom Herbert return -ENOMEM; 5480a9627f2STom Herbert 5490a9627f2STom Herbert rcu_read_lock(); 5500a9627f2STom Herbert map = rcu_dereference(queue->rps_map); 5510a9627f2STom Herbert if (map) 5520a9627f2STom Herbert for (i = 0; i < map->len; i++) 5530a9627f2STom Herbert cpumask_set_cpu(map->cpus[i], mask); 5540a9627f2STom Herbert 5550a9627f2STom Herbert len += cpumask_scnprintf(buf + len, PAGE_SIZE, mask); 5560a9627f2STom Herbert if (PAGE_SIZE - len < 3) { 5570a9627f2STom Herbert rcu_read_unlock(); 5580a9627f2STom Herbert free_cpumask_var(mask); 5590a9627f2STom Herbert return -EINVAL; 5600a9627f2STom Herbert } 5610a9627f2STom Herbert rcu_read_unlock(); 5620a9627f2STom Herbert 5630a9627f2STom Herbert free_cpumask_var(mask); 5640a9627f2STom Herbert len += sprintf(buf + len, "\n"); 5650a9627f2STom Herbert return len; 5660a9627f2STom Herbert } 5670a9627f2STom Herbert 568f5acb907SEric Dumazet static ssize_t store_rps_map(struct netdev_rx_queue *queue, 5690a9627f2STom Herbert struct rx_queue_attribute *attribute, 5700a9627f2STom Herbert const char *buf, size_t len) 5710a9627f2STom Herbert { 5720a9627f2STom Herbert struct rps_map *old_map, *map; 5730a9627f2STom Herbert cpumask_var_t mask; 5740a9627f2STom Herbert int err, cpu, i; 5750a9627f2STom Herbert static DEFINE_SPINLOCK(rps_map_lock); 5760a9627f2STom Herbert 5770a9627f2STom Herbert if (!capable(CAP_NET_ADMIN)) 5780a9627f2STom Herbert return -EPERM; 5790a9627f2STom Herbert 5800a9627f2STom Herbert if (!alloc_cpumask_var(&mask, GFP_KERNEL)) 5810a9627f2STom Herbert return -ENOMEM; 5820a9627f2STom Herbert 5830a9627f2STom Herbert err = bitmap_parse(buf, len, cpumask_bits(mask), nr_cpumask_bits); 5840a9627f2STom Herbert if (err) { 5850a9627f2STom Herbert free_cpumask_var(mask); 5860a9627f2STom Herbert return err; 5870a9627f2STom Herbert } 5880a9627f2STom Herbert 5890a9627f2STom Herbert map = kzalloc(max_t(unsigned, 5900a9627f2STom Herbert RPS_MAP_SIZE(cpumask_weight(mask)), L1_CACHE_BYTES), 5910a9627f2STom Herbert GFP_KERNEL); 5920a9627f2STom Herbert if (!map) { 5930a9627f2STom Herbert free_cpumask_var(mask); 5940a9627f2STom Herbert return -ENOMEM; 5950a9627f2STom Herbert } 5960a9627f2STom Herbert 5970a9627f2STom Herbert i = 0; 5980a9627f2STom Herbert for_each_cpu_and(cpu, mask, cpu_online_mask) 5990a9627f2STom Herbert map->cpus[i++] = cpu; 6000a9627f2STom Herbert 6010a9627f2STom Herbert if (i) 6020a9627f2STom Herbert map->len = i; 6030a9627f2STom Herbert else { 6040a9627f2STom Herbert kfree(map); 6050a9627f2STom Herbert map = NULL; 6060a9627f2STom Herbert } 6070a9627f2STom Herbert 6080a9627f2STom Herbert spin_lock(&rps_map_lock); 6096e3f7fafSEric Dumazet old_map = rcu_dereference_protected(queue->rps_map, 6106e3f7fafSEric Dumazet lockdep_is_held(&rps_map_lock)); 6110a9627f2STom Herbert rcu_assign_pointer(queue->rps_map, map); 6120a9627f2STom Herbert spin_unlock(&rps_map_lock); 6130a9627f2STom Herbert 6140a9627f2STom Herbert if (old_map) 615f6f80238SLai Jiangshan kfree_rcu(old_map, rcu); 6160a9627f2STom Herbert 6170a9627f2STom Herbert free_cpumask_var(mask); 6180a9627f2STom Herbert return len; 6190a9627f2STom Herbert } 6200a9627f2STom Herbert 621fec5e652STom Herbert static ssize_t show_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue, 622fec5e652STom Herbert struct rx_queue_attribute *attr, 623fec5e652STom Herbert char *buf) 624fec5e652STom Herbert { 625fec5e652STom Herbert struct rps_dev_flow_table *flow_table; 626fec5e652STom Herbert unsigned int val = 0; 627fec5e652STom Herbert 628fec5e652STom Herbert rcu_read_lock(); 629fec5e652STom Herbert flow_table = rcu_dereference(queue->rps_flow_table); 630fec5e652STom Herbert if (flow_table) 631fec5e652STom Herbert val = flow_table->mask + 1; 632fec5e652STom Herbert rcu_read_unlock(); 633fec5e652STom Herbert 634fec5e652STom Herbert return sprintf(buf, "%u\n", val); 635fec5e652STom Herbert } 636fec5e652STom Herbert 637fec5e652STom Herbert static void rps_dev_flow_table_release_work(struct work_struct *work) 638fec5e652STom Herbert { 639fec5e652STom Herbert struct rps_dev_flow_table *table = container_of(work, 640fec5e652STom Herbert struct rps_dev_flow_table, free_work); 641fec5e652STom Herbert 642fec5e652STom Herbert vfree(table); 643fec5e652STom Herbert } 644fec5e652STom Herbert 645fec5e652STom Herbert static void rps_dev_flow_table_release(struct rcu_head *rcu) 646fec5e652STom Herbert { 647fec5e652STom Herbert struct rps_dev_flow_table *table = container_of(rcu, 648fec5e652STom Herbert struct rps_dev_flow_table, rcu); 649fec5e652STom Herbert 650fec5e652STom Herbert INIT_WORK(&table->free_work, rps_dev_flow_table_release_work); 651fec5e652STom Herbert schedule_work(&table->free_work); 652fec5e652STom Herbert } 653fec5e652STom Herbert 654f5acb907SEric Dumazet static ssize_t store_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue, 655fec5e652STom Herbert struct rx_queue_attribute *attr, 656fec5e652STom Herbert const char *buf, size_t len) 657fec5e652STom Herbert { 658fec5e652STom Herbert unsigned int count; 659fec5e652STom Herbert char *endp; 660fec5e652STom Herbert struct rps_dev_flow_table *table, *old_table; 661fec5e652STom Herbert static DEFINE_SPINLOCK(rps_dev_flow_lock); 662fec5e652STom Herbert 663fec5e652STom Herbert if (!capable(CAP_NET_ADMIN)) 664fec5e652STom Herbert return -EPERM; 665fec5e652STom Herbert 666fec5e652STom Herbert count = simple_strtoul(buf, &endp, 0); 667fec5e652STom Herbert if (endp == buf) 668fec5e652STom Herbert return -EINVAL; 669fec5e652STom Herbert 670fec5e652STom Herbert if (count) { 671fec5e652STom Herbert int i; 672fec5e652STom Herbert 673fec5e652STom Herbert if (count > 1<<30) { 674fec5e652STom Herbert /* Enforce a limit to prevent overflow */ 675fec5e652STom Herbert return -EINVAL; 676fec5e652STom Herbert } 677fec5e652STom Herbert count = roundup_pow_of_two(count); 678fec5e652STom Herbert table = vmalloc(RPS_DEV_FLOW_TABLE_SIZE(count)); 679fec5e652STom Herbert if (!table) 680fec5e652STom Herbert return -ENOMEM; 681fec5e652STom Herbert 682fec5e652STom Herbert table->mask = count - 1; 683fec5e652STom Herbert for (i = 0; i < count; i++) 684fec5e652STom Herbert table->flows[i].cpu = RPS_NO_CPU; 685fec5e652STom Herbert } else 686fec5e652STom Herbert table = NULL; 687fec5e652STom Herbert 688fec5e652STom Herbert spin_lock(&rps_dev_flow_lock); 6896e3f7fafSEric Dumazet old_table = rcu_dereference_protected(queue->rps_flow_table, 6906e3f7fafSEric Dumazet lockdep_is_held(&rps_dev_flow_lock)); 691fec5e652STom Herbert rcu_assign_pointer(queue->rps_flow_table, table); 692fec5e652STom Herbert spin_unlock(&rps_dev_flow_lock); 693fec5e652STom Herbert 694fec5e652STom Herbert if (old_table) 695fec5e652STom Herbert call_rcu(&old_table->rcu, rps_dev_flow_table_release); 696fec5e652STom Herbert 697fec5e652STom Herbert return len; 698fec5e652STom Herbert } 699fec5e652STom Herbert 7000a9627f2STom Herbert static struct rx_queue_attribute rps_cpus_attribute = 7010a9627f2STom Herbert __ATTR(rps_cpus, S_IRUGO | S_IWUSR, show_rps_map, store_rps_map); 7020a9627f2STom Herbert 703fec5e652STom Herbert 704fec5e652STom Herbert static struct rx_queue_attribute rps_dev_flow_table_cnt_attribute = 705fec5e652STom Herbert __ATTR(rps_flow_cnt, S_IRUGO | S_IWUSR, 706fec5e652STom Herbert show_rps_dev_flow_table_cnt, store_rps_dev_flow_table_cnt); 707fec5e652STom Herbert 7080a9627f2STom Herbert static struct attribute *rx_queue_default_attrs[] = { 7090a9627f2STom Herbert &rps_cpus_attribute.attr, 710fec5e652STom Herbert &rps_dev_flow_table_cnt_attribute.attr, 7110a9627f2STom Herbert NULL 7120a9627f2STom Herbert }; 7130a9627f2STom Herbert 7140a9627f2STom Herbert static void rx_queue_release(struct kobject *kobj) 7150a9627f2STom Herbert { 7160a9627f2STom Herbert struct netdev_rx_queue *queue = to_rx_queue(kobj); 7176e3f7fafSEric Dumazet struct rps_map *map; 7186e3f7fafSEric Dumazet struct rps_dev_flow_table *flow_table; 7190a9627f2STom Herbert 720fec5e652STom Herbert 7216e3f7fafSEric Dumazet map = rcu_dereference_raw(queue->rps_map); 7229ea19481SJohn Fastabend if (map) { 7239ea19481SJohn Fastabend RCU_INIT_POINTER(queue->rps_map, NULL); 724f6f80238SLai Jiangshan kfree_rcu(map, rcu); 7259ea19481SJohn Fastabend } 7266e3f7fafSEric Dumazet 7276e3f7fafSEric Dumazet flow_table = rcu_dereference_raw(queue->rps_flow_table); 7289ea19481SJohn Fastabend if (flow_table) { 7299ea19481SJohn Fastabend RCU_INIT_POINTER(queue->rps_flow_table, NULL); 7306e3f7fafSEric Dumazet call_rcu(&flow_table->rcu, rps_dev_flow_table_release); 7319ea19481SJohn Fastabend } 7320a9627f2STom Herbert 7339ea19481SJohn Fastabend memset(kobj, 0, sizeof(*kobj)); 734fe822240STom Herbert dev_put(queue->dev); 7350a9627f2STom Herbert } 7360a9627f2STom Herbert 7370a9627f2STom Herbert static struct kobj_type rx_queue_ktype = { 7380a9627f2STom Herbert .sysfs_ops = &rx_queue_sysfs_ops, 7390a9627f2STom Herbert .release = rx_queue_release, 7400a9627f2STom Herbert .default_attrs = rx_queue_default_attrs, 7410a9627f2STom Herbert }; 7420a9627f2STom Herbert 7430a9627f2STom Herbert static int rx_queue_add_kobject(struct net_device *net, int index) 7440a9627f2STom Herbert { 7450a9627f2STom Herbert struct netdev_rx_queue *queue = net->_rx + index; 7460a9627f2STom Herbert struct kobject *kobj = &queue->kobj; 7470a9627f2STom Herbert int error = 0; 7480a9627f2STom Herbert 7490a9627f2STom Herbert kobj->kset = net->queues_kset; 7500a9627f2STom Herbert error = kobject_init_and_add(kobj, &rx_queue_ktype, NULL, 7510a9627f2STom Herbert "rx-%u", index); 7520a9627f2STom Herbert if (error) { 7530a9627f2STom Herbert kobject_put(kobj); 7540a9627f2STom Herbert return error; 7550a9627f2STom Herbert } 7560a9627f2STom Herbert 7570a9627f2STom Herbert kobject_uevent(kobj, KOBJ_ADD); 758fe822240STom Herbert dev_hold(queue->dev); 7590a9627f2STom Herbert 7600a9627f2STom Herbert return error; 7610a9627f2STom Herbert } 762bf264145STom Herbert #endif /* CONFIG_RPS */ 7630a9627f2STom Herbert 76462fe0b40SBen Hutchings int 76562fe0b40SBen Hutchings net_rx_queue_update_kobjects(struct net_device *net, int old_num, int new_num) 7660a9627f2STom Herbert { 767bf264145STom Herbert #ifdef CONFIG_RPS 7680a9627f2STom Herbert int i; 7690a9627f2STom Herbert int error = 0; 7700a9627f2STom Herbert 77162fe0b40SBen Hutchings for (i = old_num; i < new_num; i++) { 7720a9627f2STom Herbert error = rx_queue_add_kobject(net, i); 77362fe0b40SBen Hutchings if (error) { 77462fe0b40SBen Hutchings new_num = old_num; 7750a9627f2STom Herbert break; 7760a9627f2STom Herbert } 77762fe0b40SBen Hutchings } 7780a9627f2STom Herbert 77962fe0b40SBen Hutchings while (--i >= new_num) 7800a9627f2STom Herbert kobject_put(&net->_rx[i].kobj); 7810a9627f2STom Herbert 7820a9627f2STom Herbert return error; 783bf264145STom Herbert #else 784bf264145STom Herbert return 0; 785bf264145STom Herbert #endif 7860a9627f2STom Herbert } 7870a9627f2STom Herbert 788bf264145STom Herbert #ifdef CONFIG_XPS 7891d24eb48STom Herbert /* 7901d24eb48STom Herbert * netdev_queue sysfs structures and functions. 7911d24eb48STom Herbert */ 7921d24eb48STom Herbert struct netdev_queue_attribute { 7931d24eb48STom Herbert struct attribute attr; 7941d24eb48STom Herbert ssize_t (*show)(struct netdev_queue *queue, 7951d24eb48STom Herbert struct netdev_queue_attribute *attr, char *buf); 7961d24eb48STom Herbert ssize_t (*store)(struct netdev_queue *queue, 7971d24eb48STom Herbert struct netdev_queue_attribute *attr, const char *buf, size_t len); 7981d24eb48STom Herbert }; 7991d24eb48STom Herbert #define to_netdev_queue_attr(_attr) container_of(_attr, \ 8001d24eb48STom Herbert struct netdev_queue_attribute, attr) 8011d24eb48STom Herbert 8021d24eb48STom Herbert #define to_netdev_queue(obj) container_of(obj, struct netdev_queue, kobj) 8031d24eb48STom Herbert 8041d24eb48STom Herbert static ssize_t netdev_queue_attr_show(struct kobject *kobj, 8051d24eb48STom Herbert struct attribute *attr, char *buf) 80662fe0b40SBen Hutchings { 8071d24eb48STom Herbert struct netdev_queue_attribute *attribute = to_netdev_queue_attr(attr); 8081d24eb48STom Herbert struct netdev_queue *queue = to_netdev_queue(kobj); 8091d24eb48STom Herbert 8101d24eb48STom Herbert if (!attribute->show) 8111d24eb48STom Herbert return -EIO; 8121d24eb48STom Herbert 8131d24eb48STom Herbert return attribute->show(queue, attribute, buf); 8141d24eb48STom Herbert } 8151d24eb48STom Herbert 8161d24eb48STom Herbert static ssize_t netdev_queue_attr_store(struct kobject *kobj, 8171d24eb48STom Herbert struct attribute *attr, 8181d24eb48STom Herbert const char *buf, size_t count) 8191d24eb48STom Herbert { 8201d24eb48STom Herbert struct netdev_queue_attribute *attribute = to_netdev_queue_attr(attr); 8211d24eb48STom Herbert struct netdev_queue *queue = to_netdev_queue(kobj); 8221d24eb48STom Herbert 8231d24eb48STom Herbert if (!attribute->store) 8241d24eb48STom Herbert return -EIO; 8251d24eb48STom Herbert 8261d24eb48STom Herbert return attribute->store(queue, attribute, buf, count); 8271d24eb48STom Herbert } 8281d24eb48STom Herbert 8291d24eb48STom Herbert static const struct sysfs_ops netdev_queue_sysfs_ops = { 8301d24eb48STom Herbert .show = netdev_queue_attr_show, 8311d24eb48STom Herbert .store = netdev_queue_attr_store, 8321d24eb48STom Herbert }; 8331d24eb48STom Herbert 8341d24eb48STom Herbert static inline unsigned int get_netdev_queue_index(struct netdev_queue *queue) 8351d24eb48STom Herbert { 8361d24eb48STom Herbert struct net_device *dev = queue->dev; 8371d24eb48STom Herbert int i; 8381d24eb48STom Herbert 8391d24eb48STom Herbert for (i = 0; i < dev->num_tx_queues; i++) 8401d24eb48STom Herbert if (queue == &dev->_tx[i]) 8411d24eb48STom Herbert break; 8421d24eb48STom Herbert 8431d24eb48STom Herbert BUG_ON(i >= dev->num_tx_queues); 8441d24eb48STom Herbert 8451d24eb48STom Herbert return i; 8461d24eb48STom Herbert } 8471d24eb48STom Herbert 8481d24eb48STom Herbert 8491d24eb48STom Herbert static ssize_t show_xps_map(struct netdev_queue *queue, 8501d24eb48STom Herbert struct netdev_queue_attribute *attribute, char *buf) 8511d24eb48STom Herbert { 8521d24eb48STom Herbert struct net_device *dev = queue->dev; 8531d24eb48STom Herbert struct xps_dev_maps *dev_maps; 8541d24eb48STom Herbert cpumask_var_t mask; 8551d24eb48STom Herbert unsigned long index; 8561d24eb48STom Herbert size_t len = 0; 8571d24eb48STom Herbert int i; 8581d24eb48STom Herbert 8591d24eb48STom Herbert if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) 8601d24eb48STom Herbert return -ENOMEM; 8611d24eb48STom Herbert 8621d24eb48STom Herbert index = get_netdev_queue_index(queue); 8631d24eb48STom Herbert 8641d24eb48STom Herbert rcu_read_lock(); 8651d24eb48STom Herbert dev_maps = rcu_dereference(dev->xps_maps); 8661d24eb48STom Herbert if (dev_maps) { 8671d24eb48STom Herbert for_each_possible_cpu(i) { 8681d24eb48STom Herbert struct xps_map *map = 8691d24eb48STom Herbert rcu_dereference(dev_maps->cpu_map[i]); 8701d24eb48STom Herbert if (map) { 8711d24eb48STom Herbert int j; 8721d24eb48STom Herbert for (j = 0; j < map->len; j++) { 8731d24eb48STom Herbert if (map->queues[j] == index) { 8741d24eb48STom Herbert cpumask_set_cpu(i, mask); 8751d24eb48STom Herbert break; 8761d24eb48STom Herbert } 8771d24eb48STom Herbert } 8781d24eb48STom Herbert } 8791d24eb48STom Herbert } 8801d24eb48STom Herbert } 8811d24eb48STom Herbert rcu_read_unlock(); 8821d24eb48STom Herbert 8831d24eb48STom Herbert len += cpumask_scnprintf(buf + len, PAGE_SIZE, mask); 8841d24eb48STom Herbert if (PAGE_SIZE - len < 3) { 8851d24eb48STom Herbert free_cpumask_var(mask); 8861d24eb48STom Herbert return -EINVAL; 8871d24eb48STom Herbert } 8881d24eb48STom Herbert 8891d24eb48STom Herbert free_cpumask_var(mask); 8901d24eb48STom Herbert len += sprintf(buf + len, "\n"); 8911d24eb48STom Herbert return len; 8921d24eb48STom Herbert } 8931d24eb48STom Herbert 8941d24eb48STom Herbert static DEFINE_MUTEX(xps_map_mutex); 895a4177869SEric Dumazet #define xmap_dereference(P) \ 896a4177869SEric Dumazet rcu_dereference_protected((P), lockdep_is_held(&xps_map_mutex)) 8971d24eb48STom Herbert 8981d24eb48STom Herbert static ssize_t store_xps_map(struct netdev_queue *queue, 8991d24eb48STom Herbert struct netdev_queue_attribute *attribute, 9001d24eb48STom Herbert const char *buf, size_t len) 9011d24eb48STom Herbert { 9021d24eb48STom Herbert struct net_device *dev = queue->dev; 9031d24eb48STom Herbert cpumask_var_t mask; 9041d24eb48STom Herbert int err, i, cpu, pos, map_len, alloc_len, need_set; 9051d24eb48STom Herbert unsigned long index; 9061d24eb48STom Herbert struct xps_map *map, *new_map; 9071d24eb48STom Herbert struct xps_dev_maps *dev_maps, *new_dev_maps; 9081d24eb48STom Herbert int nonempty = 0; 909f2cd2d3eSEric Dumazet int numa_node = -2; 9101d24eb48STom Herbert 9111d24eb48STom Herbert if (!capable(CAP_NET_ADMIN)) 9121d24eb48STom Herbert return -EPERM; 9131d24eb48STom Herbert 9141d24eb48STom Herbert if (!alloc_cpumask_var(&mask, GFP_KERNEL)) 9151d24eb48STom Herbert return -ENOMEM; 9161d24eb48STom Herbert 9171d24eb48STom Herbert index = get_netdev_queue_index(queue); 9181d24eb48STom Herbert 9191d24eb48STom Herbert err = bitmap_parse(buf, len, cpumask_bits(mask), nr_cpumask_bits); 9201d24eb48STom Herbert if (err) { 9211d24eb48STom Herbert free_cpumask_var(mask); 9221d24eb48STom Herbert return err; 9231d24eb48STom Herbert } 9241d24eb48STom Herbert 9251d24eb48STom Herbert new_dev_maps = kzalloc(max_t(unsigned, 9261d24eb48STom Herbert XPS_DEV_MAPS_SIZE, L1_CACHE_BYTES), GFP_KERNEL); 9271d24eb48STom Herbert if (!new_dev_maps) { 9281d24eb48STom Herbert free_cpumask_var(mask); 9291d24eb48STom Herbert return -ENOMEM; 9301d24eb48STom Herbert } 9311d24eb48STom Herbert 9321d24eb48STom Herbert mutex_lock(&xps_map_mutex); 9331d24eb48STom Herbert 934a4177869SEric Dumazet dev_maps = xmap_dereference(dev->xps_maps); 9351d24eb48STom Herbert 9361d24eb48STom Herbert for_each_possible_cpu(cpu) { 937a4177869SEric Dumazet map = dev_maps ? 938a4177869SEric Dumazet xmap_dereference(dev_maps->cpu_map[cpu]) : NULL; 939a4177869SEric Dumazet new_map = map; 9401d24eb48STom Herbert if (map) { 9411d24eb48STom Herbert for (pos = 0; pos < map->len; pos++) 9421d24eb48STom Herbert if (map->queues[pos] == index) 9431d24eb48STom Herbert break; 9441d24eb48STom Herbert map_len = map->len; 9451d24eb48STom Herbert alloc_len = map->alloc_len; 9461d24eb48STom Herbert } else 9471d24eb48STom Herbert pos = map_len = alloc_len = 0; 9481d24eb48STom Herbert 9491d24eb48STom Herbert need_set = cpu_isset(cpu, *mask) && cpu_online(cpu); 950f2cd2d3eSEric Dumazet #ifdef CONFIG_NUMA 951f2cd2d3eSEric Dumazet if (need_set) { 952f2cd2d3eSEric Dumazet if (numa_node == -2) 953f2cd2d3eSEric Dumazet numa_node = cpu_to_node(cpu); 954f2cd2d3eSEric Dumazet else if (numa_node != cpu_to_node(cpu)) 955f2cd2d3eSEric Dumazet numa_node = -1; 956f2cd2d3eSEric Dumazet } 957f2cd2d3eSEric Dumazet #endif 9581d24eb48STom Herbert if (need_set && pos >= map_len) { 9591d24eb48STom Herbert /* Need to add queue to this CPU's map */ 9601d24eb48STom Herbert if (map_len >= alloc_len) { 9611d24eb48STom Herbert alloc_len = alloc_len ? 9621d24eb48STom Herbert 2 * alloc_len : XPS_MIN_MAP_ALLOC; 963b02038a1SEric Dumazet new_map = kzalloc_node(XPS_MAP_SIZE(alloc_len), 964b02038a1SEric Dumazet GFP_KERNEL, 965b02038a1SEric Dumazet cpu_to_node(cpu)); 9661d24eb48STom Herbert if (!new_map) 9671d24eb48STom Herbert goto error; 9681d24eb48STom Herbert new_map->alloc_len = alloc_len; 9691d24eb48STom Herbert for (i = 0; i < map_len; i++) 9701d24eb48STom Herbert new_map->queues[i] = map->queues[i]; 9711d24eb48STom Herbert new_map->len = map_len; 9721d24eb48STom Herbert } 9731d24eb48STom Herbert new_map->queues[new_map->len++] = index; 9741d24eb48STom Herbert } else if (!need_set && pos < map_len) { 9751d24eb48STom Herbert /* Need to remove queue from this CPU's map */ 9761d24eb48STom Herbert if (map_len > 1) 9771d24eb48STom Herbert new_map->queues[pos] = 9781d24eb48STom Herbert new_map->queues[--new_map->len]; 9791d24eb48STom Herbert else 9801d24eb48STom Herbert new_map = NULL; 9811d24eb48STom Herbert } 982a4177869SEric Dumazet RCU_INIT_POINTER(new_dev_maps->cpu_map[cpu], new_map); 9831d24eb48STom Herbert } 9841d24eb48STom Herbert 9851d24eb48STom Herbert /* Cleanup old maps */ 9861d24eb48STom Herbert for_each_possible_cpu(cpu) { 987a4177869SEric Dumazet map = dev_maps ? 988a4177869SEric Dumazet xmap_dereference(dev_maps->cpu_map[cpu]) : NULL; 989a4177869SEric Dumazet if (map && xmap_dereference(new_dev_maps->cpu_map[cpu]) != map) 990edc86d8aSLai Jiangshan kfree_rcu(map, rcu); 9911d24eb48STom Herbert if (new_dev_maps->cpu_map[cpu]) 9921d24eb48STom Herbert nonempty = 1; 9931d24eb48STom Herbert } 9941d24eb48STom Herbert 9951d24eb48STom Herbert if (nonempty) 9961d24eb48STom Herbert rcu_assign_pointer(dev->xps_maps, new_dev_maps); 9971d24eb48STom Herbert else { 9981d24eb48STom Herbert kfree(new_dev_maps); 9991d24eb48STom Herbert rcu_assign_pointer(dev->xps_maps, NULL); 10001d24eb48STom Herbert } 10011d24eb48STom Herbert 10021d24eb48STom Herbert if (dev_maps) 1003*b55071ebSLai Jiangshan kfree_rcu(dev_maps, rcu); 10041d24eb48STom Herbert 1005b236da69SChangli Gao netdev_queue_numa_node_write(queue, (numa_node >= 0) ? numa_node : 1006b236da69SChangli Gao NUMA_NO_NODE); 1007f2cd2d3eSEric Dumazet 10081d24eb48STom Herbert mutex_unlock(&xps_map_mutex); 10091d24eb48STom Herbert 10101d24eb48STom Herbert free_cpumask_var(mask); 10111d24eb48STom Herbert return len; 10121d24eb48STom Herbert 10131d24eb48STom Herbert error: 10141d24eb48STom Herbert mutex_unlock(&xps_map_mutex); 10151d24eb48STom Herbert 10161d24eb48STom Herbert if (new_dev_maps) 10171d24eb48STom Herbert for_each_possible_cpu(i) 1018a4177869SEric Dumazet kfree(rcu_dereference_protected( 1019a4177869SEric Dumazet new_dev_maps->cpu_map[i], 1020a4177869SEric Dumazet 1)); 10211d24eb48STom Herbert kfree(new_dev_maps); 10221d24eb48STom Herbert free_cpumask_var(mask); 10231d24eb48STom Herbert return -ENOMEM; 10241d24eb48STom Herbert } 10251d24eb48STom Herbert 10261d24eb48STom Herbert static struct netdev_queue_attribute xps_cpus_attribute = 10271d24eb48STom Herbert __ATTR(xps_cpus, S_IRUGO | S_IWUSR, show_xps_map, store_xps_map); 10281d24eb48STom Herbert 10291d24eb48STom Herbert static struct attribute *netdev_queue_default_attrs[] = { 10301d24eb48STom Herbert &xps_cpus_attribute.attr, 10311d24eb48STom Herbert NULL 10321d24eb48STom Herbert }; 10331d24eb48STom Herbert 10341d24eb48STom Herbert static void netdev_queue_release(struct kobject *kobj) 10351d24eb48STom Herbert { 10361d24eb48STom Herbert struct netdev_queue *queue = to_netdev_queue(kobj); 10371d24eb48STom Herbert struct net_device *dev = queue->dev; 10381d24eb48STom Herbert struct xps_dev_maps *dev_maps; 10391d24eb48STom Herbert struct xps_map *map; 10401d24eb48STom Herbert unsigned long index; 10411d24eb48STom Herbert int i, pos, nonempty = 0; 10421d24eb48STom Herbert 10431d24eb48STom Herbert index = get_netdev_queue_index(queue); 10441d24eb48STom Herbert 10451d24eb48STom Herbert mutex_lock(&xps_map_mutex); 1046a4177869SEric Dumazet dev_maps = xmap_dereference(dev->xps_maps); 10471d24eb48STom Herbert 10481d24eb48STom Herbert if (dev_maps) { 10491d24eb48STom Herbert for_each_possible_cpu(i) { 1050a4177869SEric Dumazet map = xmap_dereference(dev_maps->cpu_map[i]); 10511d24eb48STom Herbert if (!map) 10521d24eb48STom Herbert continue; 10531d24eb48STom Herbert 10541d24eb48STom Herbert for (pos = 0; pos < map->len; pos++) 10551d24eb48STom Herbert if (map->queues[pos] == index) 10561d24eb48STom Herbert break; 10571d24eb48STom Herbert 10581d24eb48STom Herbert if (pos < map->len) { 10591d24eb48STom Herbert if (map->len > 1) 10601d24eb48STom Herbert map->queues[pos] = 10611d24eb48STom Herbert map->queues[--map->len]; 10621d24eb48STom Herbert else { 10631d24eb48STom Herbert RCU_INIT_POINTER(dev_maps->cpu_map[i], 10641d24eb48STom Herbert NULL); 1065edc86d8aSLai Jiangshan kfree_rcu(map, rcu); 10661d24eb48STom Herbert map = NULL; 10671d24eb48STom Herbert } 10681d24eb48STom Herbert } 10691d24eb48STom Herbert if (map) 10701d24eb48STom Herbert nonempty = 1; 10711d24eb48STom Herbert } 10721d24eb48STom Herbert 10731d24eb48STom Herbert if (!nonempty) { 10741d24eb48STom Herbert RCU_INIT_POINTER(dev->xps_maps, NULL); 1075*b55071ebSLai Jiangshan kfree_rcu(dev_maps, rcu); 10761d24eb48STom Herbert } 10771d24eb48STom Herbert } 10781d24eb48STom Herbert 10791d24eb48STom Herbert mutex_unlock(&xps_map_mutex); 10801d24eb48STom Herbert 10811d24eb48STom Herbert memset(kobj, 0, sizeof(*kobj)); 10821d24eb48STom Herbert dev_put(queue->dev); 10831d24eb48STom Herbert } 10841d24eb48STom Herbert 10851d24eb48STom Herbert static struct kobj_type netdev_queue_ktype = { 10861d24eb48STom Herbert .sysfs_ops = &netdev_queue_sysfs_ops, 10871d24eb48STom Herbert .release = netdev_queue_release, 10881d24eb48STom Herbert .default_attrs = netdev_queue_default_attrs, 10891d24eb48STom Herbert }; 10901d24eb48STom Herbert 10911d24eb48STom Herbert static int netdev_queue_add_kobject(struct net_device *net, int index) 10921d24eb48STom Herbert { 10931d24eb48STom Herbert struct netdev_queue *queue = net->_tx + index; 10941d24eb48STom Herbert struct kobject *kobj = &queue->kobj; 10951d24eb48STom Herbert int error = 0; 10961d24eb48STom Herbert 10971d24eb48STom Herbert kobj->kset = net->queues_kset; 10981d24eb48STom Herbert error = kobject_init_and_add(kobj, &netdev_queue_ktype, NULL, 10991d24eb48STom Herbert "tx-%u", index); 11001d24eb48STom Herbert if (error) { 11011d24eb48STom Herbert kobject_put(kobj); 11021d24eb48STom Herbert return error; 11031d24eb48STom Herbert } 11041d24eb48STom Herbert 11051d24eb48STom Herbert kobject_uevent(kobj, KOBJ_ADD); 11061d24eb48STom Herbert dev_hold(queue->dev); 11071d24eb48STom Herbert 11081d24eb48STom Herbert return error; 11091d24eb48STom Herbert } 1110bf264145STom Herbert #endif /* CONFIG_XPS */ 11111d24eb48STom Herbert 11121d24eb48STom Herbert int 11131d24eb48STom Herbert netdev_queue_update_kobjects(struct net_device *net, int old_num, int new_num) 11141d24eb48STom Herbert { 1115bf264145STom Herbert #ifdef CONFIG_XPS 11161d24eb48STom Herbert int i; 11171d24eb48STom Herbert int error = 0; 11181d24eb48STom Herbert 11191d24eb48STom Herbert for (i = old_num; i < new_num; i++) { 11201d24eb48STom Herbert error = netdev_queue_add_kobject(net, i); 11211d24eb48STom Herbert if (error) { 11221d24eb48STom Herbert new_num = old_num; 11231d24eb48STom Herbert break; 11241d24eb48STom Herbert } 11251d24eb48STom Herbert } 11261d24eb48STom Herbert 11271d24eb48STom Herbert while (--i >= new_num) 11281d24eb48STom Herbert kobject_put(&net->_tx[i].kobj); 11291d24eb48STom Herbert 11301d24eb48STom Herbert return error; 1131bf264145STom Herbert #else 1132bf264145STom Herbert return 0; 1133bf264145STom Herbert #endif 11341d24eb48STom Herbert } 11351d24eb48STom Herbert 11361d24eb48STom Herbert static int register_queue_kobjects(struct net_device *net) 11371d24eb48STom Herbert { 1138bf264145STom Herbert int error = 0, txq = 0, rxq = 0, real_rx = 0, real_tx = 0; 11391d24eb48STom Herbert 1140bf264145STom Herbert #if defined(CONFIG_RPS) || defined(CONFIG_XPS) 114162fe0b40SBen Hutchings net->queues_kset = kset_create_and_add("queues", 114262fe0b40SBen Hutchings NULL, &net->dev.kobj); 114362fe0b40SBen Hutchings if (!net->queues_kset) 114462fe0b40SBen Hutchings return -ENOMEM; 1145bf264145STom Herbert #endif 11461d24eb48STom Herbert 1147bf264145STom Herbert #ifdef CONFIG_RPS 1148bf264145STom Herbert real_rx = net->real_num_rx_queues; 1149bf264145STom Herbert #endif 1150bf264145STom Herbert real_tx = net->real_num_tx_queues; 1151bf264145STom Herbert 1152bf264145STom Herbert error = net_rx_queue_update_kobjects(net, 0, real_rx); 11531d24eb48STom Herbert if (error) 11541d24eb48STom Herbert goto error; 1155bf264145STom Herbert rxq = real_rx; 11561d24eb48STom Herbert 1157bf264145STom Herbert error = netdev_queue_update_kobjects(net, 0, real_tx); 11581d24eb48STom Herbert if (error) 11591d24eb48STom Herbert goto error; 1160bf264145STom Herbert txq = real_tx; 11611d24eb48STom Herbert 11621d24eb48STom Herbert return 0; 11631d24eb48STom Herbert 11641d24eb48STom Herbert error: 11651d24eb48STom Herbert netdev_queue_update_kobjects(net, txq, 0); 11661d24eb48STom Herbert net_rx_queue_update_kobjects(net, rxq, 0); 11671d24eb48STom Herbert return error; 116862fe0b40SBen Hutchings } 116962fe0b40SBen Hutchings 11701d24eb48STom Herbert static void remove_queue_kobjects(struct net_device *net) 11710a9627f2STom Herbert { 1172bf264145STom Herbert int real_rx = 0, real_tx = 0; 1173bf264145STom Herbert 1174bf264145STom Herbert #ifdef CONFIG_RPS 1175bf264145STom Herbert real_rx = net->real_num_rx_queues; 1176bf264145STom Herbert #endif 1177bf264145STom Herbert real_tx = net->real_num_tx_queues; 1178bf264145STom Herbert 1179bf264145STom Herbert net_rx_queue_update_kobjects(net, real_rx, 0); 1180bf264145STom Herbert netdev_queue_update_kobjects(net, real_tx, 0); 1181bf264145STom Herbert #if defined(CONFIG_RPS) || defined(CONFIG_XPS) 11820a9627f2STom Herbert kset_unregister(net->queues_kset); 1183bf264145STom Herbert #endif 11840a9627f2STom Herbert } 1185608b4b95SEric W. Biederman 1186608b4b95SEric W. Biederman static const void *net_current_ns(void) 1187608b4b95SEric W. Biederman { 1188608b4b95SEric W. Biederman return current->nsproxy->net_ns; 1189608b4b95SEric W. Biederman } 1190608b4b95SEric W. Biederman 1191608b4b95SEric W. Biederman static const void *net_initial_ns(void) 1192608b4b95SEric W. Biederman { 1193608b4b95SEric W. Biederman return &init_net; 1194608b4b95SEric W. Biederman } 1195608b4b95SEric W. Biederman 1196608b4b95SEric W. Biederman static const void *net_netlink_ns(struct sock *sk) 1197608b4b95SEric W. Biederman { 1198608b4b95SEric W. Biederman return sock_net(sk); 1199608b4b95SEric W. Biederman } 1200608b4b95SEric W. Biederman 120104600794SJohannes Berg struct kobj_ns_type_operations net_ns_type_operations = { 1202608b4b95SEric W. Biederman .type = KOBJ_NS_TYPE_NET, 1203608b4b95SEric W. Biederman .current_ns = net_current_ns, 1204608b4b95SEric W. Biederman .netlink_ns = net_netlink_ns, 1205608b4b95SEric W. Biederman .initial_ns = net_initial_ns, 1206608b4b95SEric W. Biederman }; 120704600794SJohannes Berg EXPORT_SYMBOL_GPL(net_ns_type_operations); 1208608b4b95SEric W. Biederman 1209608b4b95SEric W. Biederman static void net_kobj_ns_exit(struct net *net) 1210608b4b95SEric W. Biederman { 1211608b4b95SEric W. Biederman kobj_ns_exit(KOBJ_NS_TYPE_NET, net); 1212608b4b95SEric W. Biederman } 1213608b4b95SEric W. Biederman 1214d6523ddfSEric W. Biederman static struct pernet_operations kobj_net_ops = { 1215608b4b95SEric W. Biederman .exit = net_kobj_ns_exit, 1216608b4b95SEric W. Biederman }; 1217608b4b95SEric W. Biederman 12188b41d188SEric W. Biederman 12191da177e4SLinus Torvalds #ifdef CONFIG_HOTPLUG 12207eff2e7aSKay Sievers static int netdev_uevent(struct device *d, struct kobj_uevent_env *env) 12211da177e4SLinus Torvalds { 122243cb76d9SGreg Kroah-Hartman struct net_device *dev = to_net_dev(d); 12237eff2e7aSKay Sievers int retval; 12241da177e4SLinus Torvalds 1225312c004dSKay Sievers /* pass interface to uevent. */ 12267eff2e7aSKay Sievers retval = add_uevent_var(env, "INTERFACE=%s", dev->name); 1227bf62456eSEric Rannaud if (retval) 1228bf62456eSEric Rannaud goto exit; 12291da177e4SLinus Torvalds 1230ca2f37dbSJean Tourrilhes /* pass ifindex to uevent. 1231ca2f37dbSJean Tourrilhes * ifindex is useful as it won't change (interface name may change) 1232ca2f37dbSJean Tourrilhes * and is what RtNetlink uses natively. */ 12337eff2e7aSKay Sievers retval = add_uevent_var(env, "IFINDEX=%d", dev->ifindex); 1234ca2f37dbSJean Tourrilhes 1235bf62456eSEric Rannaud exit: 1236bf62456eSEric Rannaud return retval; 12371da177e4SLinus Torvalds } 12381da177e4SLinus Torvalds #endif 12391da177e4SLinus Torvalds 12401da177e4SLinus Torvalds /* 12411da177e4SLinus Torvalds * netdev_release -- destroy and free a dead device. 124243cb76d9SGreg Kroah-Hartman * Called when last reference to device kobject is gone. 12431da177e4SLinus Torvalds */ 124443cb76d9SGreg Kroah-Hartman static void netdev_release(struct device *d) 12451da177e4SLinus Torvalds { 124643cb76d9SGreg Kroah-Hartman struct net_device *dev = to_net_dev(d); 12471da177e4SLinus Torvalds 12481da177e4SLinus Torvalds BUG_ON(dev->reg_state != NETREG_RELEASED); 12491da177e4SLinus Torvalds 12500b815a1aSStephen Hemminger kfree(dev->ifalias); 12511da177e4SLinus Torvalds kfree((char *)dev - dev->padded); 12521da177e4SLinus Torvalds } 12531da177e4SLinus Torvalds 1254608b4b95SEric W. Biederman static const void *net_namespace(struct device *d) 1255608b4b95SEric W. Biederman { 1256608b4b95SEric W. Biederman struct net_device *dev; 1257608b4b95SEric W. Biederman dev = container_of(d, struct net_device, dev); 1258608b4b95SEric W. Biederman return dev_net(dev); 1259608b4b95SEric W. Biederman } 1260608b4b95SEric W. Biederman 12611da177e4SLinus Torvalds static struct class net_class = { 12621da177e4SLinus Torvalds .name = "net", 126343cb76d9SGreg Kroah-Hartman .dev_release = netdev_release, 12648b41d188SEric W. Biederman #ifdef CONFIG_SYSFS 126543cb76d9SGreg Kroah-Hartman .dev_attrs = net_class_attributes, 12668b41d188SEric W. Biederman #endif /* CONFIG_SYSFS */ 12671da177e4SLinus Torvalds #ifdef CONFIG_HOTPLUG 126843cb76d9SGreg Kroah-Hartman .dev_uevent = netdev_uevent, 12691da177e4SLinus Torvalds #endif 1270608b4b95SEric W. Biederman .ns_type = &net_ns_type_operations, 1271608b4b95SEric W. Biederman .namespace = net_namespace, 12721da177e4SLinus Torvalds }; 12731da177e4SLinus Torvalds 12749093bbb2SStephen Hemminger /* Delete sysfs entries but hold kobject reference until after all 12759093bbb2SStephen Hemminger * netdev references are gone. 12769093bbb2SStephen Hemminger */ 12778b41d188SEric W. Biederman void netdev_unregister_kobject(struct net_device * net) 12781da177e4SLinus Torvalds { 12799093bbb2SStephen Hemminger struct device *dev = &(net->dev); 12809093bbb2SStephen Hemminger 12819093bbb2SStephen Hemminger kobject_get(&dev->kobj); 12823891845eSEric W. Biederman 12831d24eb48STom Herbert remove_queue_kobjects(net); 12840a9627f2STom Herbert 12859093bbb2SStephen Hemminger device_del(dev); 12861da177e4SLinus Torvalds } 12871da177e4SLinus Torvalds 12881da177e4SLinus Torvalds /* Create sysfs entries for network device. */ 12898b41d188SEric W. Biederman int netdev_register_kobject(struct net_device *net) 12901da177e4SLinus Torvalds { 129143cb76d9SGreg Kroah-Hartman struct device *dev = &(net->dev); 1292a4dbd674SDavid Brownell const struct attribute_group **groups = net->sysfs_groups; 12930a9627f2STom Herbert int error = 0; 12941da177e4SLinus Torvalds 1295a1b3f594SEric W. Biederman device_initialize(dev); 129643cb76d9SGreg Kroah-Hartman dev->class = &net_class; 129743cb76d9SGreg Kroah-Hartman dev->platform_data = net; 129843cb76d9SGreg Kroah-Hartman dev->groups = groups; 12991da177e4SLinus Torvalds 1300a2205472SStephen Hemminger dev_set_name(dev, "%s", net->name); 13011da177e4SLinus Torvalds 13028b41d188SEric W. Biederman #ifdef CONFIG_SYSFS 13030c509a6cSEric W. Biederman /* Allow for a device specific group */ 13040c509a6cSEric W. Biederman if (*groups) 13050c509a6cSEric W. Biederman groups++; 13061da177e4SLinus Torvalds 13070c509a6cSEric W. Biederman *groups++ = &netstat_group; 130822bb1be4SJohannes Berg #ifdef CONFIG_WIRELESS_EXT_SYSFS 13093d23e349SJohannes Berg if (net->ieee80211_ptr) 1310fe9925b5SStephen Hemminger *groups++ = &wireless_group; 13113d23e349SJohannes Berg #ifdef CONFIG_WIRELESS_EXT 13123d23e349SJohannes Berg else if (net->wireless_handlers) 13133d23e349SJohannes Berg *groups++ = &wireless_group; 13143d23e349SJohannes Berg #endif 13151da177e4SLinus Torvalds #endif 13168b41d188SEric W. Biederman #endif /* CONFIG_SYSFS */ 13171da177e4SLinus Torvalds 13180a9627f2STom Herbert error = device_add(dev); 13190a9627f2STom Herbert if (error) 13200a9627f2STom Herbert return error; 13210a9627f2STom Herbert 13221d24eb48STom Herbert error = register_queue_kobjects(net); 13230a9627f2STom Herbert if (error) { 13240a9627f2STom Herbert device_del(dev); 13250a9627f2STom Herbert return error; 13260a9627f2STom Herbert } 13270a9627f2STom Herbert 13280a9627f2STom Herbert return error; 13291da177e4SLinus Torvalds } 13301da177e4SLinus Torvalds 1331b8a9787eSJay Vosburgh int netdev_class_create_file(struct class_attribute *class_attr) 1332b8a9787eSJay Vosburgh { 1333b8a9787eSJay Vosburgh return class_create_file(&net_class, class_attr); 1334b8a9787eSJay Vosburgh } 13359e34a5b5SEric Dumazet EXPORT_SYMBOL(netdev_class_create_file); 1336b8a9787eSJay Vosburgh 1337b8a9787eSJay Vosburgh void netdev_class_remove_file(struct class_attribute *class_attr) 1338b8a9787eSJay Vosburgh { 1339b8a9787eSJay Vosburgh class_remove_file(&net_class, class_attr); 1340b8a9787eSJay Vosburgh } 1341b8a9787eSJay Vosburgh EXPORT_SYMBOL(netdev_class_remove_file); 1342b8a9787eSJay Vosburgh 13438b41d188SEric W. Biederman int netdev_kobject_init(void) 13441da177e4SLinus Torvalds { 1345608b4b95SEric W. Biederman kobj_ns_type_register(&net_ns_type_operations); 1346d6523ddfSEric W. Biederman register_pernet_subsys(&kobj_net_ops); 13471da177e4SLinus Torvalds return class_register(&net_class); 13481da177e4SLinus Torvalds } 1349