Commit 081f608e authored by Stephen Hemminger's avatar Stephen Hemminger Committed by David S. Miller

[NET]: sysfs support of network devices.

parent a3b18c71
......@@ -28,7 +28,7 @@
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/kobject.h>
#include <linux/device.h>
#include <asm/atomic.h>
#include <asm/cache.h>
......@@ -441,11 +441,22 @@ struct net_device
struct divert_blk *divert;
#endif /* CONFIG_NET_DIVERT */
/* generic object representation */
struct kobject kobj;
/* generic device structure used in constructing class */
struct device *dev;
/* class/net/name entry */
struct class_device class_dev;
/* statistics sub-directory */
struct kobject stats_kobj;
};
#define SET_MODULE_OWNER(dev) do { } while (0)
/* Set the sysfs physical device reference for the network logical device
* if set prior to registration will cause a symlink during initialization.
*/
#define SET_NETDEV_DEV(net, pdev) ((net)->dev = (pdev))
struct packet_type
{
......@@ -561,12 +572,12 @@ static inline void netif_stop_queue(struct net_device *dev)
set_bit(__LINK_STATE_XOFF, &dev->state);
}
static inline int netif_queue_stopped(struct net_device *dev)
static inline int netif_queue_stopped(const struct net_device *dev)
{
return test_bit(__LINK_STATE_XOFF, &dev->state);
}
static inline int netif_running(struct net_device *dev)
static inline int netif_running(const struct net_device *dev)
{
return test_bit(__LINK_STATE_START, &dev->state);
}
......@@ -606,7 +617,9 @@ extern int netif_rx(struct sk_buff *skb);
#define HAVE_NETIF_RECEIVE_SKB 1
extern int netif_receive_skb(struct sk_buff *skb);
extern int dev_ioctl(unsigned int cmd, void *);
extern unsigned dev_get_flags(const struct net_device *);
extern int dev_change_flags(struct net_device *, unsigned);
extern int dev_set_mtu(struct net_device *, int);
extern void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev);
extern void dev_init(void);
......@@ -642,7 +655,7 @@ static inline void dev_put(struct net_device *dev)
extern void linkwatch_fire_event(struct net_device *dev);
static inline int netif_carrier_ok(struct net_device *dev)
static inline int netif_carrier_ok(const struct net_device *dev)
{
return !test_bit(__LINK_STATE_NOCARRIER, &dev->state);
}
......
......@@ -10,7 +10,8 @@ obj-y += sysctl_net_core.o
endif
endif
obj-$(CONFIG_NET) += flow.o dev.o dev_mcast.o dst.o neighbour.o rtnetlink.o utils.o link_watch.o filter.o
obj-$(CONFIG_NET) += flow.o dev.o net-sysfs.o dev_mcast.o dst.o neighbour.o \
rtnetlink.o utils.o link_watch.o filter.o
obj-$(CONFIG_NETFILTER) += netfilter.o
obj-$(CONFIG_NET_DIVERT) += dv.o
......
......@@ -131,16 +131,6 @@ extern int plip_init(void);
NET_PROFILE_DEFINE(dev_queue_xmit)
NET_PROFILE_DEFINE(softnet_process)
const char *if_port_text[] = {
"unknown",
"BNC",
"10baseT",
"AUI",
"100baseT",
"100baseTX",
"100baseFX"
};
/*
* The list of packet types we will receive (as opposed to discard)
* and the routines to invoke.
......@@ -203,7 +193,9 @@ int netdev_fastroute;
int netdev_fastroute_obstacles;
#endif
static struct subsystem net_subsys;
extern int netdev_sysfs_init(void);
extern int netdev_register_sysfs(struct net_device *);
extern void netdev_unregister_sysfs(struct net_device *);
/*******************************************************************************
......@@ -2075,6 +2067,22 @@ void dev_set_allmulti(struct net_device *dev, int inc)
dev_mc_upload(dev);
}
unsigned dev_get_flags(const struct net_device *dev)
{
unsigned flags;
flags = (dev->flags & ~(IFF_PROMISC |
IFF_ALLMULTI |
IFF_RUNNING)) |
(dev->gflags & (IFF_PROMISC |
IFF_ALLMULTI));
if (netif_running(dev) && netif_carrier_ok(dev))
flags |= IFF_RUNNING;
return flags;
}
int dev_change_flags(struct net_device *dev, unsigned flags)
{
int ret;
......@@ -2137,6 +2145,32 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
return ret;
}
int dev_set_mtu(struct net_device *dev, int new_mtu)
{
int err;
if (new_mtu == dev->mtu)
return 0;
/* MTU must be positive. */
if (new_mtu < 0)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
err = 0;
if (dev->change_mtu)
err = dev->change_mtu(dev, new_mtu);
else
dev->mtu = new_mtu;
if (!err && dev->flags & IFF_UP)
notifier_call_chain(&netdev_chain,
NETDEV_CHANGEMTU, dev);
return err;
}
/*
* Perform the SIOCxIFxxx calls.
*/
......@@ -2150,13 +2184,7 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
switch (cmd) {
case SIOCGIFFLAGS: /* Get interface flags */
ifr->ifr_flags = (dev->flags & ~(IFF_PROMISC |
IFF_ALLMULTI |
IFF_RUNNING)) |
(dev->gflags & (IFF_PROMISC |
IFF_ALLMULTI));
if (netif_running(dev) && netif_carrier_ok(dev))
ifr->ifr_flags |= IFF_RUNNING;
ifr->ifr_flags = dev_get_flags(dev);
return 0;
case SIOCSIFFLAGS: /* Set interface flags */
......@@ -2176,27 +2204,7 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
return 0;
case SIOCSIFMTU: /* Set the MTU of a device */
if (ifr->ifr_mtu == dev->mtu)
return 0;
/*
* MTU must be positive.
*/
if (ifr->ifr_mtu < 0)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
err = 0;
if (dev->change_mtu)
err = dev->change_mtu(dev, ifr->ifr_mtu);
else
dev->mtu = ifr->ifr_mtu;
if (!err && dev->flags & IFF_UP)
notifier_call_chain(&netdev_chain,
NETDEV_CHANGEMTU, dev);
return err;
return dev_set_mtu(dev, ifr->ifr_mtu);
case SIOCGIFHWADDR:
memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr,
......@@ -2284,6 +2292,7 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
return -EEXIST;
memcpy(dev->name, ifr->ifr_newname, IFNAMSIZ);
dev->name[IFNAMSIZ - 1] = 0;
snprintf(dev->class_dev.class_id, BUS_ID_SIZE, dev->name);
notifier_call_chain(&netdev_chain,
NETDEV_CHANGENAME, dev);
return 0;
......@@ -2580,9 +2589,8 @@ int register_netdevice(struct net_device *dev)
if (d == dev || !strcmp(d->name, dev->name))
goto out_err;
}
snprintf(dev->kobj.name,KOBJ_NAME_LEN,dev->name);
kobj_set_kset_s(dev,net_subsys);
if ((ret = kobject_register(&dev->kobj)))
if ((ret = netdev_register_sysfs(dev)))
goto out_err;
/* Fix illegal SG+CSUM combinations. */
......@@ -2834,7 +2842,7 @@ int unregister_netdevice(struct net_device *dev)
free_divert_blk(dev);
kobject_unregister(&dev->kobj);
netdev_unregister_sysfs(dev);
spin_lock(&unregister_todo_lock);
dev->next = unregister_todo;
......@@ -2859,8 +2867,6 @@ extern void ip_auto_config(void);
extern void dv_init(void);
#endif /* CONFIG_NET_DIVERT */
static decl_subsys(net,NULL,NULL);
/*
* This is called single threaded during boot, so no need
......@@ -2876,7 +2882,8 @@ static int __init net_dev_init(void)
if (dev_proc_init())
goto out;
subsystem_register(&net_subsys);
if (netdev_sysfs_init())
goto out;
INIT_LIST_HEAD(&ptype_all);
for (i = 0; i < 16; i++)
......@@ -2950,7 +2957,8 @@ static int __init net_dev_init(void)
*/
netdev_boot_setup_check(dev);
if (dev->init && dev->init(dev)) {
if ( (dev->init && dev->init(dev)) ||
netdev_register_sysfs(dev) ) {
/*
* It failed to come up. It will be unhooked later.
* dev_alloc_name can now advance to next suitable
......
/*
* net-sysfs.c - network device class and attributes
*
* Copyright (c) 2003 Stephen Hemminber <shemminger@osdl.org>
*
*
* TODO:
* last_tx
* last_rx
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <net/sock.h>
#include <linux/rtnetlink.h>
const char *if_port_text[] = {
[IF_PORT_UNKNOWN] = "unknown",
[IF_PORT_10BASE2] = "BNC",
[IF_PORT_10BASET] = "10baseT",
[IF_PORT_AUI] = "AUI",
[IF_PORT_100BASET] = "100baseT",
[IF_PORT_100BASETX] = "100baseTX",
[IF_PORT_100BASEFX] = "100baseFX"
};
#define to_net_dev(class) container_of((class), struct net_device, class_dev)
/* generate a show function for simple field */
#define NETDEVICE_SHOW(field, format_string) \
static ssize_t show_##field(struct class_device *dev, char *buf) \
{ \
return sprintf(buf, format_string, to_net_dev(dev)->field); \
}
/* generate a store function for a field with locking */
#define NETDEVICE_STORE(field) \
static ssize_t \
store_##field(struct class_device *dev, const char *buf, size_t len) \
{ \
char *endp; \
long new = simple_strtol(buf, &endp, 16); \
\
if (endp == buf || new < 0) \
return -EINVAL; \
\
if (!capable(CAP_NET_ADMIN)) \
return -EPERM; \
\
rtnl_lock(); \
to_net_dev(dev)->field = new; \
rtnl_unlock(); \
return len; \
}
/* generate a read-only network device class attribute */
#define NETDEVICE_ATTR(field, format_string) \
NETDEVICE_SHOW(field, format_string) \
static CLASS_DEVICE_ATTR(field, S_IRUGO, show_##field, NULL) \
NETDEVICE_ATTR(addr_len, "%d\n");
NETDEVICE_ATTR(iflink, "%d\n");
NETDEVICE_ATTR(ifindex, "%d\n");
NETDEVICE_ATTR(features, "%#x\n");
NETDEVICE_ATTR(type, "%d\n");
/* TODO: only a few devices set this now should fix others. */
static ssize_t show_port(struct class_device *dev, char *buf)
{
unsigned char port = to_net_dev(dev)->if_port;
char *cp = buf;
cp += sprintf(cp, "%d", port);
if (port < ARRAY_SIZE(if_port_text))
cp += sprintf(cp, " (%s)", if_port_text[port]);
*cp++ ='\n';
return cp - buf;
}
static CLASS_DEVICE_ATTR(if_port, S_IRUGO, show_port, NULL);
static ssize_t format_addr(char *buf, const unsigned char *addr, int len)
{
int i;
char *cp = buf;
read_lock(&dev_base_lock);
for (i = 0; i < len; i++)
cp += sprintf(cp, "%02x%c", addr[i],
i == (len - 1) ? '\n' : ':');
read_unlock(&dev_base_lock);
return cp - buf;
}
static ssize_t show_address(struct class_device *dev, char *buf)
{
struct net_device *net = to_net_dev(dev);
return format_addr(buf, net->dev_addr, net->addr_len);
}
static ssize_t show_broadcast(struct class_device *dev, char *buf)
{
struct net_device *net = to_net_dev(dev);
return format_addr(buf, net->broadcast, net->addr_len);
}
static CLASS_DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
static CLASS_DEVICE_ATTR(broadcast, S_IRUGO, show_broadcast, NULL);
/* read-write attributes */
NETDEVICE_SHOW(mtu, "%d\n");
static ssize_t store_mtu(struct class_device *dev, const char *buf, size_t len)
{
char *endp;
int new_mtu;
int err;
new_mtu = simple_strtoul(buf, &endp, 10);
if (endp == buf)
return -EINVAL;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
rtnl_lock();
err = dev_set_mtu(to_net_dev(dev), new_mtu);
rtnl_unlock();
return err == 0 ? len : err;
}
static CLASS_DEVICE_ATTR(mtu, S_IRUGO | S_IWUSR, show_mtu, store_mtu);
NETDEVICE_SHOW(flags, "%#x\n");
static ssize_t store_flags(struct class_device *dev, const char *buf, size_t len)
{
unsigned long new_flags;
char *endp;
int err = 0;
new_flags = simple_strtoul(buf, &endp, 16);
if (endp == buf)
return -EINVAL;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
rtnl_lock();
err = dev_change_flags(to_net_dev(dev), new_flags);
rtnl_unlock();
return err ? err : len;
}
static CLASS_DEVICE_ATTR(flags, S_IRUGO | S_IWUSR, show_flags, store_flags);
NETDEVICE_SHOW(tx_queue_len, "%lu\n");
NETDEVICE_STORE(tx_queue_len);
static CLASS_DEVICE_ATTR(tx_queue_len, S_IRUGO | S_IWUSR, show_tx_queue_len,
store_tx_queue_len);
static struct class net_class = {
.name = "net",
};
static struct class_device_attribute *net_class_attributes[] = {
&class_device_attr_ifindex,
&class_device_attr_iflink,
&class_device_attr_addr_len,
&class_device_attr_tx_queue_len,
&class_device_attr_features,
&class_device_attr_mtu,
&class_device_attr_flags,
&class_device_attr_if_port,
&class_device_attr_type,
&class_device_attr_address,
&class_device_attr_broadcast,
NULL
};
struct netstat_fs_entry {
struct attribute attr;
ssize_t (*show)(const struct net_device_stats *, char *);
ssize_t (*store)(struct net_device_stats *, const char *, size_t);
};
static ssize_t net_device_stat_show(unsigned long var, char *buf)
{
return sprintf(buf, "%ld\n", var);
}
/* generate a read-only statistics attribute */
#define NETDEVICE_STAT(_NAME) \
static ssize_t show_stat_##_NAME(const struct net_device_stats *stats, \
char *buf) \
{ \
return net_device_stat_show(stats->_NAME, buf); \
} \
static struct netstat_fs_entry net_stat_##_NAME = { \
.attr = {.name = __stringify(_NAME), .mode = S_IRUGO }, \
.show = show_stat_##_NAME, \
}
NETDEVICE_STAT(rx_packets);
NETDEVICE_STAT(tx_packets);
NETDEVICE_STAT(rx_bytes);
NETDEVICE_STAT(tx_bytes);
NETDEVICE_STAT(rx_errors);
NETDEVICE_STAT(tx_errors);
NETDEVICE_STAT(rx_dropped);
NETDEVICE_STAT(tx_dropped);
NETDEVICE_STAT(multicast);
NETDEVICE_STAT(collisions);
NETDEVICE_STAT(rx_length_errors);
NETDEVICE_STAT(rx_over_errors);
NETDEVICE_STAT(rx_crc_errors);
NETDEVICE_STAT(rx_frame_errors);
NETDEVICE_STAT(rx_fifo_errors);
NETDEVICE_STAT(rx_missed_errors);
NETDEVICE_STAT(tx_aborted_errors);
NETDEVICE_STAT(tx_carrier_errors);
NETDEVICE_STAT(tx_fifo_errors);
NETDEVICE_STAT(tx_heartbeat_errors);
NETDEVICE_STAT(tx_window_errors);
NETDEVICE_STAT(rx_compressed);
NETDEVICE_STAT(tx_compressed);
static struct attribute *default_attrs[] = {
&net_stat_rx_packets.attr,
&net_stat_tx_packets.attr,
&net_stat_rx_bytes.attr,
&net_stat_tx_bytes.attr,
&net_stat_rx_errors.attr,
&net_stat_tx_errors.attr,
&net_stat_rx_dropped.attr,
&net_stat_tx_dropped.attr,
&net_stat_multicast.attr,
&net_stat_collisions.attr,
&net_stat_rx_length_errors.attr,
&net_stat_rx_over_errors.attr,
&net_stat_rx_crc_errors.attr,
&net_stat_rx_frame_errors.attr,
&net_stat_rx_fifo_errors.attr,
&net_stat_rx_missed_errors.attr,
&net_stat_tx_aborted_errors.attr,
&net_stat_tx_carrier_errors.attr,
&net_stat_tx_fifo_errors.attr,
&net_stat_tx_heartbeat_errors.attr,
&net_stat_tx_window_errors.attr,
&net_stat_rx_compressed.attr,
&net_stat_tx_compressed.attr,
NULL
};
static ssize_t
netstat_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct netstat_fs_entry *entry
= container_of(attr, struct netstat_fs_entry, attr);
struct class_device *class_dev
= container_of(kobj->parent, struct class_device, kobj);
struct net_device *dev
= to_net_dev(class_dev);
struct net_device_stats *stats
= dev->get_stats ? dev->get_stats(dev) : NULL;
if (stats && entry->show)
return entry->show(stats, buf);
return -EINVAL;
}
static struct sysfs_ops netstat_sysfs_ops = {
.show = netstat_attr_show,
};
static struct kobj_type netstat_ktype = {
.sysfs_ops = &netstat_sysfs_ops,
.default_attrs = default_attrs,
};
/* Create sysfs entries for network device. */
int netdev_register_sysfs(struct net_device *net)
{
struct class_device *class_dev = &(net->class_dev);
int i;
struct class_device_attribute *attr;
int ret;
memset(class_dev, 0, sizeof(struct class_device));
class_dev->class = &net_class;
class_dev->dev = net->dev;
class_dev->class_data = net;
snprintf(class_dev->class_id, BUS_ID_SIZE, net->name);
if ((ret = class_device_register(class_dev)))
goto out;
for (i = 0; (attr = net_class_attributes[i]); i++) {
if ((ret = class_device_create_file(class_dev, attr)))
goto out_unreg;
}
if (net->get_stats) {
struct kobject *k = &net->stats_kobj;
memset(k, 0, sizeof(*k));
k->parent = kobject_get(&class_dev->kobj);
if (!k->parent) {
ret = -EBUSY;
goto out_unreg;
}
snprintf(k->name, KOBJ_NAME_LEN, "%s", "statistics");
k->ktype = &netstat_ktype;
if((ret = kobject_register(k)))
goto out_unreg;
}
out:
return ret;
out_unreg:
printk(KERN_WARNING "%s: sysfs attribute registration failed %d\n",
net->name, ret);
class_device_unregister(class_dev);
goto out;
}
void netdev_unregister_sysfs(struct net_device *net)
{
if (net->get_stats)
kobject_del(&net->stats_kobj);
class_device_unregister(&net->class_dev);
}
int netdev_sysfs_init(void)
{
return class_register(&net_class);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment