mirror of
https://github.com/fail0verflow/switch-linux.git
synced 2025-05-04 02:34:21 -04:00
hv_netvsc: add ethtool statistics for tx packet issues
Printing console messages is not helpful when system is out of memory; and can be disastrous with netconsole. Instead keep statistics of these anomalous conditions. Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
e3f74b841d
commit
4323b47cf8
2 changed files with 79 additions and 16 deletions
|
@ -652,6 +652,14 @@ struct netvsc_stats {
|
||||||
struct u64_stats_sync syncp;
|
struct u64_stats_sync syncp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct netvsc_ethtool_stats {
|
||||||
|
unsigned long tx_scattered;
|
||||||
|
unsigned long tx_no_memory;
|
||||||
|
unsigned long tx_no_space;
|
||||||
|
unsigned long tx_too_big;
|
||||||
|
unsigned long tx_busy;
|
||||||
|
};
|
||||||
|
|
||||||
struct netvsc_reconfig {
|
struct netvsc_reconfig {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
u32 event;
|
u32 event;
|
||||||
|
@ -681,6 +689,7 @@ struct net_device_context {
|
||||||
/* Ethtool settings */
|
/* Ethtool settings */
|
||||||
u8 duplex;
|
u8 duplex;
|
||||||
u32 speed;
|
u32 speed;
|
||||||
|
struct netvsc_ethtool_stats eth_stats;
|
||||||
|
|
||||||
/* the device is going away */
|
/* the device is going away */
|
||||||
bool start_remove;
|
bool start_remove;
|
||||||
|
|
|
@ -365,7 +365,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
|
||||||
u32 skb_length;
|
u32 skb_length;
|
||||||
struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT];
|
struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT];
|
||||||
struct hv_page_buffer *pb = page_buf;
|
struct hv_page_buffer *pb = page_buf;
|
||||||
struct netvsc_stats *tx_stats = this_cpu_ptr(net_device_ctx->tx_stats);
|
|
||||||
|
|
||||||
/* We will atmost need two pages to describe the rndis
|
/* We will atmost need two pages to describe the rndis
|
||||||
* header. We can only transmit MAX_PAGE_BUFFER_COUNT number
|
* header. We can only transmit MAX_PAGE_BUFFER_COUNT number
|
||||||
|
@ -377,17 +376,14 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
|
||||||
num_data_pgs = netvsc_get_slots(skb) + 2;
|
num_data_pgs = netvsc_get_slots(skb) + 2;
|
||||||
|
|
||||||
if (unlikely(num_data_pgs > MAX_PAGE_BUFFER_COUNT)) {
|
if (unlikely(num_data_pgs > MAX_PAGE_BUFFER_COUNT)) {
|
||||||
if (skb_linearize(skb)) {
|
++net_device_ctx->eth_stats.tx_scattered;
|
||||||
net_alert_ratelimited("failed to linearize skb\n");
|
|
||||||
ret = -ENOMEM;
|
if (skb_linearize(skb))
|
||||||
goto drop;
|
goto no_memory;
|
||||||
}
|
|
||||||
|
|
||||||
num_data_pgs = netvsc_get_slots(skb) + 2;
|
num_data_pgs = netvsc_get_slots(skb) + 2;
|
||||||
if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) {
|
if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) {
|
||||||
net_alert_ratelimited("packet too big: %u pages (%u bytes)\n",
|
++net_device_ctx->eth_stats.tx_too_big;
|
||||||
num_data_pgs, skb->len);
|
|
||||||
ret = -EFAULT;
|
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -398,11 +394,9 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
|
||||||
* structure.
|
* structure.
|
||||||
*/
|
*/
|
||||||
ret = skb_cow_head(skb, RNDIS_AND_PPI_SIZE);
|
ret = skb_cow_head(skb, RNDIS_AND_PPI_SIZE);
|
||||||
if (ret) {
|
if (ret)
|
||||||
netdev_err(net, "unable to alloc hv_netvsc_packet\n");
|
goto no_memory;
|
||||||
ret = -ENOMEM;
|
|
||||||
goto drop;
|
|
||||||
}
|
|
||||||
/* Use the skb control buffer for building up the packet */
|
/* Use the skb control buffer for building up the packet */
|
||||||
BUILD_BUG_ON(sizeof(struct hv_netvsc_packet) >
|
BUILD_BUG_ON(sizeof(struct hv_netvsc_packet) >
|
||||||
FIELD_SIZEOF(struct sk_buff, cb));
|
FIELD_SIZEOF(struct sk_buff, cb));
|
||||||
|
@ -518,7 +512,7 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
|
||||||
|
|
||||||
ret = skb_cow_head(skb, 0);
|
ret = skb_cow_head(skb, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto drop;
|
goto no_memory;
|
||||||
|
|
||||||
uh = udp_hdr(skb);
|
uh = udp_hdr(skb);
|
||||||
udp_len = ntohs(uh->len);
|
udp_len = ntohs(uh->len);
|
||||||
|
@ -545,20 +539,32 @@ do_send:
|
||||||
ret = netvsc_send(net_device_ctx->device_ctx, packet,
|
ret = netvsc_send(net_device_ctx->device_ctx, packet,
|
||||||
rndis_msg, &pb, skb);
|
rndis_msg, &pb, skb);
|
||||||
if (likely(ret == 0)) {
|
if (likely(ret == 0)) {
|
||||||
|
struct netvsc_stats *tx_stats = this_cpu_ptr(net_device_ctx->tx_stats);
|
||||||
|
|
||||||
u64_stats_update_begin(&tx_stats->syncp);
|
u64_stats_update_begin(&tx_stats->syncp);
|
||||||
tx_stats->packets++;
|
tx_stats->packets++;
|
||||||
tx_stats->bytes += skb_length;
|
tx_stats->bytes += skb_length;
|
||||||
u64_stats_update_end(&tx_stats->syncp);
|
u64_stats_update_end(&tx_stats->syncp);
|
||||||
return NETDEV_TX_OK;
|
return NETDEV_TX_OK;
|
||||||
}
|
}
|
||||||
if (ret == -EAGAIN)
|
|
||||||
|
if (ret == -EAGAIN) {
|
||||||
|
++net_device_ctx->eth_stats.tx_busy;
|
||||||
return NETDEV_TX_BUSY;
|
return NETDEV_TX_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == -ENOSPC)
|
||||||
|
++net_device_ctx->eth_stats.tx_no_space;
|
||||||
|
|
||||||
drop:
|
drop:
|
||||||
dev_kfree_skb_any(skb);
|
dev_kfree_skb_any(skb);
|
||||||
net->stats.tx_dropped++;
|
net->stats.tx_dropped++;
|
||||||
|
|
||||||
return NETDEV_TX_OK;
|
return NETDEV_TX_OK;
|
||||||
|
|
||||||
|
no_memory:
|
||||||
|
++net_device_ctx->eth_stats.tx_no_memory;
|
||||||
|
goto drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1015,6 +1021,51 @@ static int netvsc_set_mac_addr(struct net_device *ndev, void *p)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
char name[ETH_GSTRING_LEN];
|
||||||
|
u16 offset;
|
||||||
|
} netvsc_stats[] = {
|
||||||
|
{ "tx_scattered", offsetof(struct netvsc_ethtool_stats, tx_scattered) },
|
||||||
|
{ "tx_no_memory", offsetof(struct netvsc_ethtool_stats, tx_no_memory) },
|
||||||
|
{ "tx_no_space", offsetof(struct netvsc_ethtool_stats, tx_no_space) },
|
||||||
|
{ "tx_too_big", offsetof(struct netvsc_ethtool_stats, tx_too_big) },
|
||||||
|
{ "tx_busy", offsetof(struct netvsc_ethtool_stats, tx_busy) },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int netvsc_get_sset_count(struct net_device *dev, int string_set)
|
||||||
|
{
|
||||||
|
switch (string_set) {
|
||||||
|
case ETH_SS_STATS:
|
||||||
|
return ARRAY_SIZE(netvsc_stats);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netvsc_get_ethtool_stats(struct net_device *dev,
|
||||||
|
struct ethtool_stats *stats, u64 *data)
|
||||||
|
{
|
||||||
|
struct net_device_context *ndc = netdev_priv(dev);
|
||||||
|
const void *nds = &ndc->eth_stats;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(netvsc_stats); i++)
|
||||||
|
data[i] = *(unsigned long *)(nds + netvsc_stats[i].offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
switch (stringset) {
|
||||||
|
case ETH_SS_STATS:
|
||||||
|
for (i = 0; i < ARRAY_SIZE(netvsc_stats); i++)
|
||||||
|
memcpy(data + i * ETH_GSTRING_LEN,
|
||||||
|
netvsc_stats[i].name, ETH_GSTRING_LEN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||||
static void netvsc_poll_controller(struct net_device *net)
|
static void netvsc_poll_controller(struct net_device *net)
|
||||||
{
|
{
|
||||||
|
@ -1027,6 +1078,9 @@ static void netvsc_poll_controller(struct net_device *net)
|
||||||
static const struct ethtool_ops ethtool_ops = {
|
static const struct ethtool_ops ethtool_ops = {
|
||||||
.get_drvinfo = netvsc_get_drvinfo,
|
.get_drvinfo = netvsc_get_drvinfo,
|
||||||
.get_link = ethtool_op_get_link,
|
.get_link = ethtool_op_get_link,
|
||||||
|
.get_ethtool_stats = netvsc_get_ethtool_stats,
|
||||||
|
.get_sset_count = netvsc_get_sset_count,
|
||||||
|
.get_strings = netvsc_get_strings,
|
||||||
.get_channels = netvsc_get_channels,
|
.get_channels = netvsc_get_channels,
|
||||||
.set_channels = netvsc_set_channels,
|
.set_channels = netvsc_set_channels,
|
||||||
.get_ts_info = ethtool_op_get_ts_info,
|
.get_ts_info = ethtool_op_get_ts_info,
|
||||||
|
|
Loading…
Add table
Reference in a new issue