简介
本文只讲述网卡的statistics数据注册和获取流程,典型的如:
/sys/class/net/eth0/statistics/rx_packets
/sys/class/net/eth0/statistics/tx_packets
/sys/class/net/eth0/statistics/rx_bytes
/sys/class/net/eth0/statistics/tx_bytes
/sys/class/net/eth0/statistics/rx_dropped
/sys/class/net/eth0/statistics/tx_dropped
或者通过# ifconfig eth0查看到的数据
设备节点的创建
注册
在网卡加入到kernel的时候都必须通过register_netdevice()进行注册,该函数通过调用netdev_register_kobject()函数来设置相关信息并注册对应的设备节点。
/* Create sysfs entries for network device. */
int netdev_register_kobject(struct net_device *ndev){
struct device *dev = &(ndev->dev);
const struct attribute_group **groups = ndev->sysfs_groups;
...
dev->groups = groups;
...
*groups++ = &netstat_group;
...
device_add(dev);
}
device_add()->device_add_attrs()->device_add_groups(dev->groups)->sysfs_create_groups()
netstat_group定义
#define NETSTAT_ENTRY(name) \
static ssize_t name##_show(struct device *d, \
struct device_attribute *attr, char *buf) \
{ \
return netstat_show(d, attr, buf, \
offsetof(struct rtnl_link_stats64, name)); \
} \
static DEVICE_ATTR_RO(name)
NETSTAT_ENTRY(rx_packets);
NETSTAT_ENTRY(tx_packets);
NETSTAT_ENTRY(rx_bytes);
NETSTAT_ENTRY(tx_bytes);
NETSTAT_ENTRY(rx_errors);
NETSTAT_ENTRY(tx_errors);
NETSTAT_ENTRY(rx_dropped);
NETSTAT_ENTRY(tx_dropped);
NETSTAT_ENTRY(multicast);
NETSTAT_ENTRY(collisions);
NETSTAT_ENTRY(rx_length_errors);
NETSTAT_ENTRY(rx_over_errors);
NETSTAT_ENTRY(rx_crc_errors);
NETSTAT_ENTRY(rx_frame_errors);
NETSTAT_ENTRY(rx_fifo_errors);
NETSTAT_ENTRY(rx_missed_errors);
NETSTAT_ENTRY(tx_aborted_errors);
NETSTAT_ENTRY(tx_carrier_errors);
NETSTAT_ENTRY(tx_fifo_errors);
NETSTAT_ENTRY(tx_heartbeat_errors);
NETSTAT_ENTRY(tx_window_errors);
NETSTAT_ENTRY(rx_compressed);
NETSTAT_ENTRY(tx_compressed);
NETSTAT_ENTRY(rx_nohandler);
static struct attribute *netstat_attrs[] = {
&dev_attr_rx_packets.attr,
&dev_attr_tx_packets.attr,
&dev_attr_rx_bytes.attr,
&dev_attr_tx_bytes.attr,
&dev_attr_rx_errors.attr,
&dev_attr_tx_errors.attr,
&dev_attr_rx_dropped.attr,
&dev_attr_tx_dropped.attr,
&dev_attr_multicast.attr,
&dev_attr_collisions.attr,
&dev_attr_rx_length_errors.attr,
&dev_attr_rx_over_errors.attr,
&dev_attr_rx_crc_errors.attr,
&dev_attr_rx_frame_errors.attr,
&dev_attr_rx_fifo_errors.attr,
&dev_attr_rx_missed_errors.attr,
&dev_attr_tx_aborted_errors.attr,
&dev_attr_tx_carrier_errors.attr,
&dev_attr_tx_fifo_errors.attr,
&dev_attr_tx_heartbeat_errors.attr,
&dev_attr_tx_window_errors.attr,
&dev_attr_rx_compressed.attr,
&dev_attr_tx_compressed.attr,
&dev_attr_rx_nohandler.attr,
NULL
};
static struct attribute_group netstat_group = {
.name = "statistics",
.attrs = netstat_attrs,
};
netstat_show函数
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
#define __ATTR_RO(_name) { \
.attr = { .name = __stringify(_name), .mode = S_IRUGO }, \
.show = _name##_show, \
}
#define DEVICE_ATTR_RO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev,
struct rtnl_link_stats64 *storage)
{
const struct net_device_ops *ops = dev->netdev_ops;
if (ops->ndo_get_stats64) {
memset(storage, 0, sizeof(*storage));
ops->ndo_get_stats64(dev, storage);
} else if (ops->ndo_get_stats) {
netdev_stats_to_stats64(storage, ops->ndo_get_stats(dev));
} else {
netdev_stats_to_stats64(storage, &dev->stats);
}
storage->rx_dropped += atomic_long_read(&dev->rx_dropped);
storage->tx_dropped += atomic_long_read(&dev->tx_dropped);
storage->rx_nohandler += atomic_long_read(&dev->rx_nohandler);
return storage;
}
static ssize_t netstat_show(const struct device *d,
struct device_attribute *attr, char *buf,
unsigned long offset)
{
struct net_device *dev = to_net_dev(d);
ssize_t ret = -EINVAL;
WARN_ON(offset > sizeof(struct rtnl_link_stats64) ||
offset % sizeof(u64) != 0);
read_lock(&dev_base_lock);
if (dev_isalive(dev)) {
struct rtnl_link_stats64 temp;
const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp);
ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *) stats) + offset));
}
read_unlock(&dev_base_lock);
return ret;
}
dev_get_stats
通过dev_get_stats()函数可以知道,如果网卡有注册ndo_get_stats64()函数或者ndo_get_stats()函数,则通过网卡驱动获取stats数据,否则直接通过dev->stats获取。
其他
net/core/dev.c
register_netdevice
net/core/net-sysfs.c
netdev_register_kobject()->
dev->groups = groups;
*groups++ = &netstat_group;
device_add()->device_add_attrs()->device_add_groups(dev->groups)->sysfs_create_groups()