new_iface->proc_ptr = proc_entry;
create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry,
- read_proc_u64, &new_iface->tx_bytes);
+ read_proc_u64, &new_iface->totals[IFS_TX].bytes);
create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry,
- read_proc_u64, &new_iface->rx_bytes);
+ read_proc_u64, &new_iface->totals[IFS_RX].bytes);
create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry,
- read_proc_u64, &new_iface->tx_packets);
+ read_proc_u64, &new_iface->totals[IFS_TX].packets);
create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry,
- read_proc_u64, &new_iface->rx_packets);
+ read_proc_u64, &new_iface->totals[IFS_RX].packets);
create_proc_read_entry("active", proc_iface_perms, proc_entry,
read_proc_bool, &new_iface->active);
return new_iface;
}
+static void iface_check_stats_reset_and_adjust(struct net_device *net_dev,
+ struct iface_stat *iface)
+{
+ struct rtnl_link_stats64 dev_stats, *stats;
+ bool stats_rewound;
+
+ stats = dev_get_stats(net_dev, &dev_stats);
+ /* No empty packets */
+ stats_rewound =
+ (stats->rx_bytes < iface->last_known[IFS_RX].bytes)
+ || (stats->tx_bytes < iface->last_known[IFS_TX].bytes);
+
+ IF_DEBUG("qtaguid: %s(%s): iface=%p netdev=%p "
+ "bytes rx/tx=%llu/%llu "
+ "active=%d last_known=%d "
+ "stats_rewound=%d\n", __func__,
+ net_dev ? net_dev->name : "?",
+ iface, net_dev,
+ stats->rx_bytes, stats->tx_bytes,
+ iface->active, iface->last_known_valid, stats_rewound);
+
+ if (iface->active && iface->last_known_valid && stats_rewound) {
+ pr_warn_once("qtaguid: iface_stat: %s(%s): "
+ "iface reset its stats unexpectedly\n", __func__,
+ net_dev->name);
+
+ iface->totals[IFS_TX].bytes += iface->last_known[IFS_TX].bytes;
+ iface->totals[IFS_TX].packets +=
+ iface->last_known[IFS_TX].packets;
+ iface->totals[IFS_RX].bytes += iface->last_known[IFS_RX].bytes;
+ iface->totals[IFS_RX].packets +=
+ iface->last_known[IFS_RX].packets;
+ iface->last_known_valid = false;
+ IF_DEBUG("qtaguid: %s(%s): iface=%p "
+ "used last known bytes rx/tx=%llu/%llu\n", __func__,
+ iface->ifname, iface, iface->last_known[IFS_RX].bytes,
+ iface->last_known[IFS_TX].bytes);
+ }
+}
+
/*
* Create a new entry for tracking the specified interface.
* Do nothing if the entry already exists.
* Called when an interface is configured with a valid IP address.
*/
-void iface_stat_create(const struct net_device *net_dev,
- struct in_ifaddr *ifa)
+static void iface_stat_create(struct net_device *net_dev,
+ struct in_ifaddr *ifa)
{
struct in_device *in_dev = NULL;
const char *ifname;
if (entry != NULL) {
IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n",
ifname, entry);
+ iface_check_stats_reset_and_adjust(net_dev, entry);
if (ipv4_is_loopback(ipaddr)) {
entry->active = false;
IF_DEBUG("qtaguid: iface_stat: create(%s): "
in_dev_put(in_dev);
}
-void iface_stat_create_ipv6(const struct net_device *net_dev,
- struct inet6_ifaddr *ifa)
+static void iface_stat_create_ipv6(struct net_device *net_dev,
+ struct inet6_ifaddr *ifa)
{
struct in_device *in_dev;
const char *ifname;
if (entry != NULL) {
IF_DEBUG("qtaguid: iface_stat: create6(%s): entry=%p\n",
ifname, entry);
+ iface_check_stats_reset_and_adjust(net_dev, entry);
if (addr_type & IPV6_ADDR_LOOPBACK) {
entry->active = false;
IF_DEBUG("qtaguid: iface_stat: create6(%s): "
* does not exist (when a device was never configured with an IP address).
* Called when an device is being unregistered.
*/
-static void iface_stat_update(struct net_device *dev)
+static void iface_stat_update(struct net_device *dev, bool stash_only)
{
struct rtnl_link_stats64 dev_stats, *stats;
struct iface_stat *entry;
spin_unlock_bh(&iface_stat_list_lock);
return;
}
+
IF_DEBUG("qtaguid: iface_stat: update(%s): entry=%p\n",
dev->name, entry);
- if (entry->active) {
- entry->tx_bytes += stats->tx_bytes;
- entry->tx_packets += stats->tx_packets;
- entry->rx_bytes += stats->rx_bytes;
- entry->rx_packets += stats->rx_packets;
- entry->active = false;
+ if (!entry->active) {
+ IF_DEBUG("qtaguid: iface_stat: update(%s): already disabled\n",
+ dev->name);
+ spin_unlock_bh(&iface_stat_list_lock);
+ return;
+ }
+
+ if (stash_only) {
+ entry->last_known[IFS_TX].bytes = stats->tx_bytes;
+ entry->last_known[IFS_TX].packets = stats->tx_packets;
+ entry->last_known[IFS_RX].bytes = stats->rx_bytes;
+ entry->last_known[IFS_RX].packets = stats->rx_packets;
+ entry->last_known_valid = true;
IF_DEBUG("qtaguid: iface_stat: update(%s): "
- " disable tracking. rx/tx=%llu/%llu\n",
+ "dev stats stashed rx/tx=%llu/%llu\n",
dev->name, stats->rx_bytes, stats->tx_bytes);
- } else {
- IF_DEBUG("qtaguid: iface_stat: update(%s): disabled\n",
- dev->name);
+ spin_unlock_bh(&iface_stat_list_lock);
+ return;
}
+ entry->totals[IFS_TX].bytes += stats->tx_bytes;
+ entry->totals[IFS_TX].packets += stats->tx_packets;
+ entry->totals[IFS_RX].bytes += stats->rx_bytes;
+ entry->totals[IFS_RX].packets += stats->rx_packets;
+ /* We don't need the last_known[] anymore */
+ entry->last_known_valid = false;
+ entry->active = false;
+ IF_DEBUG("qtaguid: iface_stat: update(%s): "
+ "disable tracking. rx/tx=%llu/%llu\n",
+ dev->name, stats->rx_bytes, stats->tx_bytes);
spin_unlock_bh(&iface_stat_list_lock);
}
return NOTIFY_DONE;
IF_DEBUG("qtaguid: iface_stat: netdev_event(): "
- "ev=0x%lx netdev=%p->name=%s\n",
- event, dev, dev ? dev->name : "");
+ "ev=0x%lx/%s netdev=%p->name=%s\n",
+ event, netdev_evt_str(event), dev, dev ? dev->name : "");
switch (event) {
case NETDEV_UP:
iface_stat_create(dev, NULL);
+ atomic64_inc(&qtu_events.iface_events);
break;
case NETDEV_DOWN:
- iface_stat_update(dev);
+ case NETDEV_UNREGISTER:
+ iface_stat_update(dev, event == NETDEV_DOWN);
+ atomic64_inc(&qtu_events.iface_events);
break;
}
return NOTIFY_DONE;
return NOTIFY_DONE;
IF_DEBUG("qtaguid: iface_stat: inet6addr_event(): "
- "ev=0x%lx ifa=%p\n",
- event, ifa);
+ "ev=0x%lx/%s ifa=%p\n",
+ event, netdev_evt_str(event), ifa);
switch (event) {
case NETDEV_UP:
atomic64_inc(&qtu_events.iface_events);
break;
case NETDEV_DOWN:
+ case NETDEV_UNREGISTER:
BUG_ON(!ifa || !ifa->idev);
dev = (struct net_device *)ifa->idev->dev;
- iface_stat_update(dev);
+ iface_stat_update(dev, event == NETDEV_DOWN);
atomic64_inc(&qtu_events.iface_events);
break;
}
return NOTIFY_DONE;
IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): "
- "ev=0x%lx ifa=%p\n",
- event, ifa);
+ "ev=0x%lx/%s ifa=%p\n",
+ event, netdev_evt_str(event), ifa);
switch (event) {
case NETDEV_UP:
atomic64_inc(&qtu_events.iface_events);
break;
case NETDEV_DOWN:
+ case NETDEV_UNREGISTER:
BUG_ON(!ifa || !ifa->ifa_dev);
dev = ifa->ifa_dev->dev;
- iface_stat_update(dev);
+ iface_stat_update(dev, event == NETDEV_DOWN);
atomic64_inc(&qtu_events.iface_events);
break;
}
return kasprintf(GFP_ATOMIC, "iface_stat@%p{"
"list=list_head{...}, "
"ifname=%s, "
- "rx_bytes=%llu, "
- "rx_packets=%llu, "
- "tx_bytes=%llu, "
- "tx_packets=%llu, "
+ "total={rx={bytes=%llu, "
+ "packets=%llu}, "
+ "tx={bytes=%llu, "
+ "packets=%llu}}, "
+ "last_known_valid=%d, "
+ "last_known={rx={bytes=%llu, "
+ "packets=%llu}, "
+ "tx={bytes=%llu, "
+ "packets=%llu}}, "
"active=%d, "
"proc_ptr=%p, "
"tag_stat_tree=rb_root{...}}",
is,
is->ifname,
- is->rx_bytes,
- is->rx_packets,
- is->tx_bytes,
- is->tx_packets,
+ is->totals[IFS_RX].bytes,
+ is->totals[IFS_RX].packets,
+ is->totals[IFS_TX].bytes,
+ is->totals[IFS_TX].packets,
+ is->last_known_valid,
+ is->last_known[IFS_RX].bytes,
+ is->last_known[IFS_RX].packets,
+ is->last_known[IFS_TX].bytes,
+ is->last_known[IFS_TX].packets,
is->active,
is->proc_ptr);
}
str = "}";
CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str);
}
+
+/*------------------------------------------*/
+static const char * const netdev_event_strings[] = {
+ "netdev_unknown",
+ "NETDEV_UP",
+ "NETDEV_DOWN",
+ "NETDEV_REBOOT",
+ "NETDEV_CHANGE",
+ "NETDEV_REGISTER",
+ "NETDEV_UNREGISTER",
+ "NETDEV_CHANGEMTU",
+ "NETDEV_CHANGEADDR",
+ "NETDEV_GOING_DOWN",
+ "NETDEV_CHANGENAME",
+ "NETDEV_FEAT_CHANGE",
+ "NETDEV_BONDING_FAILOVER",
+ "NETDEV_PRE_UP",
+ "NETDEV_PRE_TYPE_CHANGE",
+ "NETDEV_POST_TYPE_CHANGE",
+ "NETDEV_POST_INIT",
+ "NETDEV_UNREGISTER_BATCH",
+ "NETDEV_RELEASE",
+ "NETDEV_NOTIFY_PEERS",
+ "NETDEV_JOIN",
+};
+
+const char *netdev_evt_str(int netdev_event)
+{
+ if (netdev_event < 0
+ || netdev_event >= ARRAY_SIZE(netdev_event_strings))
+ return "bad event num";
+ return netdev_event_strings[netdev_event];
+}