vlan: Precise RX stats accounting
With multi queue devices, its possible that several cpus call vlan RX routines simultaneously for the same vlan device. We update RX stats counter without any locking, so we can get slightly wrong counters. One possible fix is to use percpu counters, to get precise accounting and also get guarantee of no cache line ping pongs between cpus. Note: this adds 16 bytes (32 bytes on 64bit arches) of percpu data per vlan device. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d83345adf9
commit
9793241fe9
|
@ -16,6 +16,21 @@ struct vlan_priority_tci_mapping {
|
||||||
struct vlan_priority_tci_mapping *next;
|
struct vlan_priority_tci_mapping *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct vlan_rx_stats - VLAN percpu rx stats
|
||||||
|
* @rx_packets: number of received packets
|
||||||
|
* @rx_bytes: number of received bytes
|
||||||
|
* @multicast: number of received multicast packets
|
||||||
|
* @rx_errors: number of errors
|
||||||
|
*/
|
||||||
|
struct vlan_rx_stats {
|
||||||
|
unsigned long rx_packets;
|
||||||
|
unsigned long rx_bytes;
|
||||||
|
unsigned long multicast;
|
||||||
|
unsigned long rx_errors;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct vlan_dev_info - VLAN private device data
|
* struct vlan_dev_info - VLAN private device data
|
||||||
* @nr_ingress_mappings: number of ingress priority mappings
|
* @nr_ingress_mappings: number of ingress priority mappings
|
||||||
|
@ -29,6 +44,7 @@ struct vlan_priority_tci_mapping {
|
||||||
* @dent: proc dir entry
|
* @dent: proc dir entry
|
||||||
* @cnt_inc_headroom_on_tx: statistic - number of skb expansions on TX
|
* @cnt_inc_headroom_on_tx: statistic - number of skb expansions on TX
|
||||||
* @cnt_encap_on_xmit: statistic - number of skb encapsulations on TX
|
* @cnt_encap_on_xmit: statistic - number of skb encapsulations on TX
|
||||||
|
* @vlan_rx_stats: ptr to percpu rx stats
|
||||||
*/
|
*/
|
||||||
struct vlan_dev_info {
|
struct vlan_dev_info {
|
||||||
unsigned int nr_ingress_mappings;
|
unsigned int nr_ingress_mappings;
|
||||||
|
@ -45,6 +61,7 @@ struct vlan_dev_info {
|
||||||
struct proc_dir_entry *dent;
|
struct proc_dir_entry *dent;
|
||||||
unsigned long cnt_inc_headroom_on_tx;
|
unsigned long cnt_inc_headroom_on_tx;
|
||||||
unsigned long cnt_encap_on_xmit;
|
unsigned long cnt_encap_on_xmit;
|
||||||
|
struct vlan_rx_stats *vlan_rx_stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev)
|
static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev)
|
||||||
|
|
|
@ -31,7 +31,7 @@ EXPORT_SYMBOL(__vlan_hwaccel_rx);
|
||||||
int vlan_hwaccel_do_receive(struct sk_buff *skb)
|
int vlan_hwaccel_do_receive(struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct net_device *dev = skb->dev;
|
struct net_device *dev = skb->dev;
|
||||||
struct net_device_stats *stats;
|
struct vlan_rx_stats *rx_stats;
|
||||||
|
|
||||||
skb->dev = vlan_dev_info(dev)->real_dev;
|
skb->dev = vlan_dev_info(dev)->real_dev;
|
||||||
netif_nit_deliver(skb);
|
netif_nit_deliver(skb);
|
||||||
|
@ -40,15 +40,17 @@ int vlan_hwaccel_do_receive(struct sk_buff *skb)
|
||||||
skb->priority = vlan_get_ingress_priority(dev, skb->vlan_tci);
|
skb->priority = vlan_get_ingress_priority(dev, skb->vlan_tci);
|
||||||
skb->vlan_tci = 0;
|
skb->vlan_tci = 0;
|
||||||
|
|
||||||
stats = &dev->stats;
|
rx_stats = per_cpu_ptr(vlan_dev_info(dev)->vlan_rx_stats,
|
||||||
stats->rx_packets++;
|
smp_processor_id());
|
||||||
stats->rx_bytes += skb->len;
|
|
||||||
|
rx_stats->rx_packets++;
|
||||||
|
rx_stats->rx_bytes += skb->len;
|
||||||
|
|
||||||
switch (skb->pkt_type) {
|
switch (skb->pkt_type) {
|
||||||
case PACKET_BROADCAST:
|
case PACKET_BROADCAST:
|
||||||
break;
|
break;
|
||||||
case PACKET_MULTICAST:
|
case PACKET_MULTICAST:
|
||||||
stats->multicast++;
|
rx_stats->multicast++;
|
||||||
break;
|
break;
|
||||||
case PACKET_OTHERHOST:
|
case PACKET_OTHERHOST:
|
||||||
/* Our lower layer thinks this is not local, let's make sure.
|
/* Our lower layer thinks this is not local, let's make sure.
|
||||||
|
|
|
@ -140,7 +140,7 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
|
||||||
struct packet_type *ptype, struct net_device *orig_dev)
|
struct packet_type *ptype, struct net_device *orig_dev)
|
||||||
{
|
{
|
||||||
struct vlan_hdr *vhdr;
|
struct vlan_hdr *vhdr;
|
||||||
struct net_device_stats *stats;
|
struct vlan_rx_stats *rx_stats;
|
||||||
u16 vlan_id;
|
u16 vlan_id;
|
||||||
u16 vlan_tci;
|
u16 vlan_tci;
|
||||||
|
|
||||||
|
@ -163,9 +163,10 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
stats = &skb->dev->stats;
|
rx_stats = per_cpu_ptr(vlan_dev_info(dev)->vlan_rx_stats,
|
||||||
stats->rx_packets++;
|
smp_processor_id());
|
||||||
stats->rx_bytes += skb->len;
|
rx_stats->rx_packets++;
|
||||||
|
rx_stats->rx_bytes += skb->len;
|
||||||
|
|
||||||
skb_pull_rcsum(skb, VLAN_HLEN);
|
skb_pull_rcsum(skb, VLAN_HLEN);
|
||||||
|
|
||||||
|
@ -180,7 +181,7 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PACKET_MULTICAST:
|
case PACKET_MULTICAST:
|
||||||
stats->multicast++;
|
rx_stats->multicast++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PACKET_OTHERHOST:
|
case PACKET_OTHERHOST:
|
||||||
|
@ -200,7 +201,7 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
|
||||||
|
|
||||||
skb = vlan_check_reorder_header(skb);
|
skb = vlan_check_reorder_header(skb);
|
||||||
if (!skb) {
|
if (!skb) {
|
||||||
stats->rx_errors++;
|
rx_stats->rx_errors++;
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -731,6 +732,11 @@ static int vlan_dev_init(struct net_device *dev)
|
||||||
subclass = 1;
|
subclass = 1;
|
||||||
|
|
||||||
vlan_dev_set_lockdep_class(dev, subclass);
|
vlan_dev_set_lockdep_class(dev, subclass);
|
||||||
|
|
||||||
|
vlan_dev_info(dev)->vlan_rx_stats = alloc_percpu(struct vlan_rx_stats);
|
||||||
|
if (!vlan_dev_info(dev)->vlan_rx_stats)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,6 +746,8 @@ static void vlan_dev_uninit(struct net_device *dev)
|
||||||
struct vlan_dev_info *vlan = vlan_dev_info(dev);
|
struct vlan_dev_info *vlan = vlan_dev_info(dev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
free_percpu(vlan->vlan_rx_stats);
|
||||||
|
vlan->vlan_rx_stats = NULL;
|
||||||
for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) {
|
for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) {
|
||||||
while ((pm = vlan->egress_priority_map[i]) != NULL) {
|
while ((pm = vlan->egress_priority_map[i]) != NULL) {
|
||||||
vlan->egress_priority_map[i] = pm->next;
|
vlan->egress_priority_map[i] = pm->next;
|
||||||
|
@ -775,6 +783,31 @@ static u32 vlan_ethtool_get_flags(struct net_device *dev)
|
||||||
return dev_ethtool_get_flags(vlan->real_dev);
|
return dev_ethtool_get_flags(vlan->real_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct net_device_stats *vlan_dev_get_stats(struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct net_device_stats *stats = &dev->stats;
|
||||||
|
|
||||||
|
dev_txq_stats_fold(dev, stats);
|
||||||
|
|
||||||
|
if (vlan_dev_info(dev)->vlan_rx_stats) {
|
||||||
|
struct vlan_rx_stats *p, rx = {0};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for_each_possible_cpu(i) {
|
||||||
|
p = per_cpu_ptr(vlan_dev_info(dev)->vlan_rx_stats, i);
|
||||||
|
rx.rx_packets += p->rx_packets;
|
||||||
|
rx.rx_bytes += p->rx_bytes;
|
||||||
|
rx.rx_errors += p->rx_errors;
|
||||||
|
rx.multicast += p->multicast;
|
||||||
|
}
|
||||||
|
stats->rx_packets = rx.rx_packets;
|
||||||
|
stats->rx_bytes = rx.rx_bytes;
|
||||||
|
stats->rx_errors = rx.rx_errors;
|
||||||
|
stats->multicast = rx.multicast;
|
||||||
|
}
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct ethtool_ops vlan_ethtool_ops = {
|
static const struct ethtool_ops vlan_ethtool_ops = {
|
||||||
.get_settings = vlan_ethtool_get_settings,
|
.get_settings = vlan_ethtool_get_settings,
|
||||||
.get_drvinfo = vlan_ethtool_get_drvinfo,
|
.get_drvinfo = vlan_ethtool_get_drvinfo,
|
||||||
|
@ -797,6 +830,7 @@ static const struct net_device_ops vlan_netdev_ops = {
|
||||||
.ndo_change_rx_flags = vlan_dev_change_rx_flags,
|
.ndo_change_rx_flags = vlan_dev_change_rx_flags,
|
||||||
.ndo_do_ioctl = vlan_dev_ioctl,
|
.ndo_do_ioctl = vlan_dev_ioctl,
|
||||||
.ndo_neigh_setup = vlan_dev_neigh_setup,
|
.ndo_neigh_setup = vlan_dev_neigh_setup,
|
||||||
|
.ndo_get_stats = vlan_dev_get_stats,
|
||||||
#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
|
#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
|
||||||
.ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup,
|
.ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup,
|
||||||
.ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done,
|
.ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done,
|
||||||
|
@ -820,6 +854,7 @@ static const struct net_device_ops vlan_netdev_accel_ops = {
|
||||||
.ndo_change_rx_flags = vlan_dev_change_rx_flags,
|
.ndo_change_rx_flags = vlan_dev_change_rx_flags,
|
||||||
.ndo_do_ioctl = vlan_dev_ioctl,
|
.ndo_do_ioctl = vlan_dev_ioctl,
|
||||||
.ndo_neigh_setup = vlan_dev_neigh_setup,
|
.ndo_neigh_setup = vlan_dev_neigh_setup,
|
||||||
|
.ndo_get_stats = vlan_dev_get_stats,
|
||||||
#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
|
#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
|
||||||
.ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup,
|
.ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup,
|
||||||
.ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done,
|
.ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done,
|
||||||
|
|
Loading…
Reference in a new issue