Commit 24aeb56f authored by Kuo Zhao's avatar Kuo Zhao Committed by David S. Miller

gve: Add Gvnic stats AQ command and ethtool show/set-priv-flags.

This adds functionality to report driver stats to Hypervisor.
(Users may want to turn this feature off as a matter of privacy
so a "report-stats" flag is added as an ethtool priv option.
It is also disabled by default.)
The hypervisor would trigger a stats report in case "too many"
packets dropped; the stats would be useful in debugging stuck
queues.
A "stats_report_trigger_cnt" stat is added to count the number of times
the hypervisor attempts to trigger stats report.

A timer is also added so that when report-stats is enabled, stat are
updated once every 20 seconds.
Reviewed-by: default avatarYangchun Fu <yangchun@google.com>
Signed-off-by: default avatarKuo Zhao <kuozhao@google.com>
Signed-off-by: default avatarDavid Awogbemila <awogbemila@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0d5775d3
...@@ -27,6 +27,13 @@ ...@@ -27,6 +27,13 @@
/* 1 for management, 1 for rx, 1 for tx */ /* 1 for management, 1 for rx, 1 for tx */
#define GVE_MIN_MSIX 3 #define GVE_MIN_MSIX 3
/* Numbers of gve tx/rx stats in stats report. */
#define GVE_TX_STATS_REPORT_NUM 5
#define GVE_RX_STATS_REPORT_NUM 2
/* Interval to schedule a stats report update, 20000ms. */
#define GVE_STATS_REPORT_TIMER_PERIOD 20000
/* Each slot in the desc ring has a 1:1 mapping to a slot in the data ring */ /* Each slot in the desc ring has a 1:1 mapping to a slot in the data ring */
struct gve_rx_desc_queue { struct gve_rx_desc_queue {
struct gve_rx_desc *desc_ring; /* the descriptor ring */ struct gve_rx_desc *desc_ring; /* the descriptor ring */
...@@ -220,6 +227,7 @@ struct gve_priv { ...@@ -220,6 +227,7 @@ struct gve_priv {
u32 adminq_destroy_rx_queue_cnt; u32 adminq_destroy_rx_queue_cnt;
u32 adminq_dcfg_device_resources_cnt; u32 adminq_dcfg_device_resources_cnt;
u32 adminq_set_driver_parameter_cnt; u32 adminq_set_driver_parameter_cnt;
u32 adminq_report_stats_cnt;
/* Global stats */ /* Global stats */
u32 interface_up_cnt; /* count of times interface turned up since last reset */ u32 interface_up_cnt; /* count of times interface turned up since last reset */
...@@ -227,24 +235,39 @@ struct gve_priv { ...@@ -227,24 +235,39 @@ struct gve_priv {
u32 reset_cnt; /* count of reset */ u32 reset_cnt; /* count of reset */
u32 page_alloc_fail; /* count of page alloc fails */ u32 page_alloc_fail; /* count of page alloc fails */
u32 dma_mapping_error; /* count of dma mapping errors */ u32 dma_mapping_error; /* count of dma mapping errors */
u32 stats_report_trigger_cnt; /* count of device-requested stats-reports since last reset */
struct workqueue_struct *gve_wq; struct workqueue_struct *gve_wq;
struct work_struct service_task; struct work_struct service_task;
struct work_struct stats_report_task;
unsigned long service_task_flags; unsigned long service_task_flags;
unsigned long state_flags; unsigned long state_flags;
struct gve_stats_report *stats_report;
u64 stats_report_len;
dma_addr_t stats_report_bus; /* dma address for the stats report */
unsigned long ethtool_flags;
unsigned long stats_report_timer_period;
struct timer_list stats_report_timer;
}; };
enum gve_service_task_flags { enum gve_service_task_flags_bit {
GVE_PRIV_FLAGS_DO_RESET = BIT(1), GVE_PRIV_FLAGS_DO_RESET = 1,
GVE_PRIV_FLAGS_RESET_IN_PROGRESS = BIT(2), GVE_PRIV_FLAGS_RESET_IN_PROGRESS = 2,
GVE_PRIV_FLAGS_PROBE_IN_PROGRESS = BIT(3), GVE_PRIV_FLAGS_PROBE_IN_PROGRESS = 3,
GVE_PRIV_FLAGS_DO_REPORT_STATS = 4,
}; };
enum gve_state_flags { enum gve_state_flags_bit {
GVE_PRIV_FLAGS_ADMIN_QUEUE_OK = BIT(1), GVE_PRIV_FLAGS_ADMIN_QUEUE_OK = 1,
GVE_PRIV_FLAGS_DEVICE_RESOURCES_OK = BIT(2), GVE_PRIV_FLAGS_DEVICE_RESOURCES_OK = 2,
GVE_PRIV_FLAGS_DEVICE_RINGS_OK = BIT(3), GVE_PRIV_FLAGS_DEVICE_RINGS_OK = 3,
GVE_PRIV_FLAGS_NAPI_ENABLED = BIT(4), GVE_PRIV_FLAGS_NAPI_ENABLED = 4,
};
enum gve_ethtool_flags_bit {
GVE_PRIV_FLAGS_REPORT_STATS = 0,
}; };
static inline bool gve_get_do_reset(struct gve_priv *priv) static inline bool gve_get_do_reset(struct gve_priv *priv)
...@@ -294,6 +317,22 @@ static inline void gve_clear_probe_in_progress(struct gve_priv *priv) ...@@ -294,6 +317,22 @@ static inline void gve_clear_probe_in_progress(struct gve_priv *priv)
clear_bit(GVE_PRIV_FLAGS_PROBE_IN_PROGRESS, &priv->service_task_flags); clear_bit(GVE_PRIV_FLAGS_PROBE_IN_PROGRESS, &priv->service_task_flags);
} }
static inline bool gve_get_do_report_stats(struct gve_priv *priv)
{
return test_bit(GVE_PRIV_FLAGS_DO_REPORT_STATS,
&priv->service_task_flags);
}
static inline void gve_set_do_report_stats(struct gve_priv *priv)
{
set_bit(GVE_PRIV_FLAGS_DO_REPORT_STATS, &priv->service_task_flags);
}
static inline void gve_clear_do_report_stats(struct gve_priv *priv)
{
clear_bit(GVE_PRIV_FLAGS_DO_REPORT_STATS, &priv->service_task_flags);
}
static inline bool gve_get_admin_queue_ok(struct gve_priv *priv) static inline bool gve_get_admin_queue_ok(struct gve_priv *priv)
{ {
return test_bit(GVE_PRIV_FLAGS_ADMIN_QUEUE_OK, &priv->state_flags); return test_bit(GVE_PRIV_FLAGS_ADMIN_QUEUE_OK, &priv->state_flags);
...@@ -354,6 +393,16 @@ static inline void gve_clear_napi_enabled(struct gve_priv *priv) ...@@ -354,6 +393,16 @@ static inline void gve_clear_napi_enabled(struct gve_priv *priv)
clear_bit(GVE_PRIV_FLAGS_NAPI_ENABLED, &priv->state_flags); clear_bit(GVE_PRIV_FLAGS_NAPI_ENABLED, &priv->state_flags);
} }
static inline bool gve_get_report_stats(struct gve_priv *priv)
{
return test_bit(GVE_PRIV_FLAGS_REPORT_STATS, &priv->ethtool_flags);
}
static inline void gve_clear_report_stats(struct gve_priv *priv)
{
clear_bit(GVE_PRIV_FLAGS_REPORT_STATS, &priv->ethtool_flags);
}
/* Returns the address of the ntfy_blocks irq doorbell /* Returns the address of the ntfy_blocks irq doorbell
*/ */
static inline __be32 __iomem *gve_irq_doorbell(struct gve_priv *priv, static inline __be32 __iomem *gve_irq_doorbell(struct gve_priv *priv,
...@@ -476,6 +525,8 @@ int gve_reset(struct gve_priv *priv, bool attempt_teardown); ...@@ -476,6 +525,8 @@ int gve_reset(struct gve_priv *priv, bool attempt_teardown);
int gve_adjust_queues(struct gve_priv *priv, int gve_adjust_queues(struct gve_priv *priv,
struct gve_queue_config new_rx_config, struct gve_queue_config new_rx_config,
struct gve_queue_config new_tx_config); struct gve_queue_config new_tx_config);
/* report stats handling */
void gve_handle_report_stats(struct gve_priv *priv);
/* exported by ethtool.c */ /* exported by ethtool.c */
extern const struct ethtool_ops gve_ethtool_ops; extern const struct ethtool_ops gve_ethtool_ops;
/* needed by ethtool */ /* needed by ethtool */
......
...@@ -35,6 +35,7 @@ int gve_adminq_alloc(struct device *dev, struct gve_priv *priv) ...@@ -35,6 +35,7 @@ int gve_adminq_alloc(struct device *dev, struct gve_priv *priv)
priv->adminq_destroy_rx_queue_cnt = 0; priv->adminq_destroy_rx_queue_cnt = 0;
priv->adminq_dcfg_device_resources_cnt = 0; priv->adminq_dcfg_device_resources_cnt = 0;
priv->adminq_set_driver_parameter_cnt = 0; priv->adminq_set_driver_parameter_cnt = 0;
priv->adminq_report_stats_cnt = 0;
/* Setup Admin queue with the device */ /* Setup Admin queue with the device */
iowrite32be(priv->adminq_bus_addr / PAGE_SIZE, iowrite32be(priv->adminq_bus_addr / PAGE_SIZE,
...@@ -183,6 +184,9 @@ int gve_adminq_execute_cmd(struct gve_priv *priv, ...@@ -183,6 +184,9 @@ int gve_adminq_execute_cmd(struct gve_priv *priv,
case GVE_ADMINQ_SET_DRIVER_PARAMETER: case GVE_ADMINQ_SET_DRIVER_PARAMETER:
priv->adminq_set_driver_parameter_cnt++; priv->adminq_set_driver_parameter_cnt++;
break; break;
case GVE_ADMINQ_REPORT_STATS:
priv->adminq_report_stats_cnt++;
break;
default: default:
dev_err(&priv->pdev->dev, "unknown AQ command opcode %d\n", opcode); dev_err(&priv->pdev->dev, "unknown AQ command opcode %d\n", opcode);
} }
...@@ -433,3 +437,19 @@ int gve_adminq_set_mtu(struct gve_priv *priv, u64 mtu) ...@@ -433,3 +437,19 @@ int gve_adminq_set_mtu(struct gve_priv *priv, u64 mtu)
return gve_adminq_execute_cmd(priv, &cmd); return gve_adminq_execute_cmd(priv, &cmd);
} }
int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len,
dma_addr_t stats_report_addr, u64 interval)
{
union gve_adminq_command cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = cpu_to_be32(GVE_ADMINQ_REPORT_STATS);
cmd.report_stats = (struct gve_adminq_report_stats) {
.stats_report_len = cpu_to_be64(stats_report_len),
.stats_report_addr = cpu_to_be64(stats_report_addr),
.interval = cpu_to_be64(interval),
};
return gve_adminq_execute_cmd(priv, &cmd);
}
...@@ -21,6 +21,7 @@ enum gve_adminq_opcodes { ...@@ -21,6 +21,7 @@ enum gve_adminq_opcodes {
GVE_ADMINQ_DESTROY_RX_QUEUE = 0x8, GVE_ADMINQ_DESTROY_RX_QUEUE = 0x8,
GVE_ADMINQ_DECONFIGURE_DEVICE_RESOURCES = 0x9, GVE_ADMINQ_DECONFIGURE_DEVICE_RESOURCES = 0x9,
GVE_ADMINQ_SET_DRIVER_PARAMETER = 0xB, GVE_ADMINQ_SET_DRIVER_PARAMETER = 0xB,
GVE_ADMINQ_REPORT_STATS = 0xC,
}; };
/* Admin queue status codes */ /* Admin queue status codes */
...@@ -172,6 +173,40 @@ struct gve_adminq_set_driver_parameter { ...@@ -172,6 +173,40 @@ struct gve_adminq_set_driver_parameter {
static_assert(sizeof(struct gve_adminq_set_driver_parameter) == 16); static_assert(sizeof(struct gve_adminq_set_driver_parameter) == 16);
struct gve_adminq_report_stats {
__be64 stats_report_len;
__be64 stats_report_addr;
__be64 interval;
};
static_assert(sizeof(struct gve_adminq_report_stats) == 24);
struct stats {
__be32 stat_name;
__be32 queue_id;
__be64 value;
};
static_assert(sizeof(struct stats) == 16);
struct gve_stats_report {
__be64 written_count;
struct stats stats[0];
};
static_assert(sizeof(struct gve_stats_report) == 8);
enum gve_stat_names {
// stats from gve
TX_WAKE_CNT = 1,
TX_STOP_CNT = 2,
TX_FRAMES_SENT = 3,
TX_BYTES_SENT = 4,
TX_LAST_COMPLETION_PROCESSED = 5,
RX_NEXT_EXPECTED_SEQUENCE = 6,
RX_BUFFERS_POSTED = 7,
};
union gve_adminq_command { union gve_adminq_command {
struct { struct {
__be32 opcode; __be32 opcode;
...@@ -187,6 +222,7 @@ union gve_adminq_command { ...@@ -187,6 +222,7 @@ union gve_adminq_command {
struct gve_adminq_register_page_list reg_page_list; struct gve_adminq_register_page_list reg_page_list;
struct gve_adminq_unregister_page_list unreg_page_list; struct gve_adminq_unregister_page_list unreg_page_list;
struct gve_adminq_set_driver_parameter set_driver_param; struct gve_adminq_set_driver_parameter set_driver_param;
struct gve_adminq_report_stats report_stats;
}; };
}; };
u8 reserved[64]; u8 reserved[64];
...@@ -214,4 +250,6 @@ int gve_adminq_register_page_list(struct gve_priv *priv, ...@@ -214,4 +250,6 @@ int gve_adminq_register_page_list(struct gve_priv *priv,
struct gve_queue_page_list *qpl); struct gve_queue_page_list *qpl);
int gve_adminq_unregister_page_list(struct gve_priv *priv, u32 page_list_id); int gve_adminq_unregister_page_list(struct gve_priv *priv, u32 page_list_id);
int gve_adminq_set_mtu(struct gve_priv *priv, u64 mtu); int gve_adminq_set_mtu(struct gve_priv *priv, u64 mtu);
int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len,
dma_addr_t stats_report_addr, u64 interval);
#endif /* _GVE_ADMINQ_H */ #endif /* _GVE_ADMINQ_H */
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include "gve.h" #include "gve.h"
#include "gve_adminq.h"
static void gve_get_drvinfo(struct net_device *netdev, static void gve_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info) struct ethtool_drvinfo *info)
...@@ -36,7 +37,7 @@ static const char gve_gstrings_main_stats[][ETH_GSTRING_LEN] = { ...@@ -36,7 +37,7 @@ static const char gve_gstrings_main_stats[][ETH_GSTRING_LEN] = {
"rx_dropped", "tx_dropped", "tx_timeouts", "rx_dropped", "tx_dropped", "tx_timeouts",
"rx_skb_alloc_fail", "rx_buf_alloc_fail", "rx_desc_err_dropped_pkt", "rx_skb_alloc_fail", "rx_buf_alloc_fail", "rx_desc_err_dropped_pkt",
"interface_up_cnt", "interface_down_cnt", "reset_cnt", "interface_up_cnt", "interface_down_cnt", "reset_cnt",
"page_alloc_fail", "dma_mapping_error", "page_alloc_fail", "dma_mapping_error", "stats_report_trigger_cnt",
}; };
static const char gve_gstrings_rx_stats[][ETH_GSTRING_LEN] = { static const char gve_gstrings_rx_stats[][ETH_GSTRING_LEN] = {
...@@ -56,12 +57,18 @@ static const char gve_gstrings_adminq_stats[][ETH_GSTRING_LEN] = { ...@@ -56,12 +57,18 @@ static const char gve_gstrings_adminq_stats[][ETH_GSTRING_LEN] = {
"adminq_create_tx_queue_cnt", "adminq_create_rx_queue_cnt", "adminq_create_tx_queue_cnt", "adminq_create_rx_queue_cnt",
"adminq_destroy_tx_queue_cnt", "adminq_destroy_rx_queue_cnt", "adminq_destroy_tx_queue_cnt", "adminq_destroy_rx_queue_cnt",
"adminq_dcfg_device_resources_cnt", "adminq_set_driver_parameter_cnt", "adminq_dcfg_device_resources_cnt", "adminq_set_driver_parameter_cnt",
"adminq_report_stats_cnt",
};
static const char gve_gstrings_priv_flags[][ETH_GSTRING_LEN] = {
"report-stats",
}; };
#define GVE_MAIN_STATS_LEN ARRAY_SIZE(gve_gstrings_main_stats) #define GVE_MAIN_STATS_LEN ARRAY_SIZE(gve_gstrings_main_stats)
#define GVE_ADMINQ_STATS_LEN ARRAY_SIZE(gve_gstrings_adminq_stats) #define GVE_ADMINQ_STATS_LEN ARRAY_SIZE(gve_gstrings_adminq_stats)
#define NUM_GVE_TX_CNTS ARRAY_SIZE(gve_gstrings_tx_stats) #define NUM_GVE_TX_CNTS ARRAY_SIZE(gve_gstrings_tx_stats)
#define NUM_GVE_RX_CNTS ARRAY_SIZE(gve_gstrings_rx_stats) #define NUM_GVE_RX_CNTS ARRAY_SIZE(gve_gstrings_rx_stats)
#define GVE_PRIV_FLAGS_STR_LEN ARRAY_SIZE(gve_gstrings_priv_flags)
static void gve_get_strings(struct net_device *netdev, u32 stringset, u8 *data) static void gve_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
{ {
...@@ -69,28 +76,42 @@ static void gve_get_strings(struct net_device *netdev, u32 stringset, u8 *data) ...@@ -69,28 +76,42 @@ static void gve_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
char *s = (char *)data; char *s = (char *)data;
int i, j; int i, j;
if (stringset != ETH_SS_STATS) switch (stringset) {
return; case ETH_SS_STATS:
memcpy(s, *gve_gstrings_main_stats,
memcpy(s, *gve_gstrings_main_stats, sizeof(gve_gstrings_main_stats));
sizeof(gve_gstrings_main_stats)); s += sizeof(gve_gstrings_main_stats);
s += sizeof(gve_gstrings_main_stats);
for (i = 0; i < priv->rx_cfg.num_queues; i++) { for (i = 0; i < priv->rx_cfg.num_queues; i++) {
for (j = 0; j < NUM_GVE_RX_CNTS; j++) { for (j = 0; j < NUM_GVE_RX_CNTS; j++) {
snprintf(s, ETH_GSTRING_LEN, gve_gstrings_rx_stats[j], i); snprintf(s, ETH_GSTRING_LEN,
s += ETH_GSTRING_LEN; gve_gstrings_rx_stats[j], i);
s += ETH_GSTRING_LEN;
}
} }
}
for (i = 0; i < priv->tx_cfg.num_queues; i++) { for (i = 0; i < priv->tx_cfg.num_queues; i++) {
for (j = 0; j < NUM_GVE_TX_CNTS; j++) { for (j = 0; j < NUM_GVE_TX_CNTS; j++) {
snprintf(s, ETH_GSTRING_LEN, gve_gstrings_tx_stats[j], i); snprintf(s, ETH_GSTRING_LEN,
s += ETH_GSTRING_LEN; gve_gstrings_tx_stats[j], i);
s += ETH_GSTRING_LEN;
}
} }
}
memcpy(s, *gve_gstrings_adminq_stats, memcpy(s, *gve_gstrings_adminq_stats,
sizeof(gve_gstrings_adminq_stats)); sizeof(gve_gstrings_adminq_stats));
s += sizeof(gve_gstrings_adminq_stats); s += sizeof(gve_gstrings_adminq_stats);
break;
case ETH_SS_PRIV_FLAGS:
memcpy(s, *gve_gstrings_priv_flags,
sizeof(gve_gstrings_priv_flags));
s += sizeof(gve_gstrings_priv_flags);
break;
default:
break;
}
} }
static int gve_get_sset_count(struct net_device *netdev, int sset) static int gve_get_sset_count(struct net_device *netdev, int sset)
...@@ -102,6 +123,8 @@ static int gve_get_sset_count(struct net_device *netdev, int sset) ...@@ -102,6 +123,8 @@ static int gve_get_sset_count(struct net_device *netdev, int sset)
return GVE_MAIN_STATS_LEN + GVE_ADMINQ_STATS_LEN + return GVE_MAIN_STATS_LEN + GVE_ADMINQ_STATS_LEN +
(priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS) + (priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS) +
(priv->tx_cfg.num_queues * NUM_GVE_TX_CNTS); (priv->tx_cfg.num_queues * NUM_GVE_TX_CNTS);
case ETH_SS_PRIV_FLAGS:
return GVE_PRIV_FLAGS_STR_LEN;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -182,6 +205,7 @@ gve_get_ethtool_stats(struct net_device *netdev, ...@@ -182,6 +205,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
data[i++] = priv->reset_cnt; data[i++] = priv->reset_cnt;
data[i++] = priv->page_alloc_fail; data[i++] = priv->page_alloc_fail;
data[i++] = priv->dma_mapping_error; data[i++] = priv->dma_mapping_error;
data[i++] = priv->stats_report_trigger_cnt;
i = GVE_MAIN_STATS_LEN; i = GVE_MAIN_STATS_LEN;
/* walk RX rings */ /* walk RX rings */
...@@ -248,6 +272,7 @@ gve_get_ethtool_stats(struct net_device *netdev, ...@@ -248,6 +272,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
data[i++] = priv->adminq_destroy_rx_queue_cnt; data[i++] = priv->adminq_destroy_rx_queue_cnt;
data[i++] = priv->adminq_dcfg_device_resources_cnt; data[i++] = priv->adminq_dcfg_device_resources_cnt;
data[i++] = priv->adminq_set_driver_parameter_cnt; data[i++] = priv->adminq_set_driver_parameter_cnt;
data[i++] = priv->adminq_report_stats_cnt;
} }
static void gve_get_channels(struct net_device *netdev, static void gve_get_channels(struct net_device *netdev,
...@@ -352,6 +377,52 @@ static int gve_set_tunable(struct net_device *netdev, ...@@ -352,6 +377,52 @@ static int gve_set_tunable(struct net_device *netdev,
} }
} }
static u32 gve_get_priv_flags(struct net_device *netdev)
{
struct gve_priv *priv = netdev_priv(netdev);
u32 ret_flags = 0;
/* Only 1 flag exists currently: report-stats (BIT(O)), so set that flag. */
if (priv->ethtool_flags & BIT(0))
ret_flags |= BIT(0);
return ret_flags;
}
static int gve_set_priv_flags(struct net_device *netdev, u32 flags)
{
struct gve_priv *priv = netdev_priv(netdev);
u64 ori_flags, new_flags;
ori_flags = READ_ONCE(priv->ethtool_flags);
new_flags = ori_flags;
/* Only one priv flag exists: report-stats (BIT(0))*/
if (flags & BIT(0))
new_flags |= BIT(0);
else
new_flags &= ~(BIT(0));
priv->ethtool_flags = new_flags;
/* start report-stats timer when user turns report stats on. */
if (flags & BIT(0)) {
mod_timer(&priv->stats_report_timer,
round_jiffies(jiffies +
msecs_to_jiffies(priv->stats_report_timer_period)));
}
/* Zero off gve stats when report-stats turned off and */
/* delete report stats timer. */
if (!(flags & BIT(0)) && (ori_flags & BIT(0))) {
int tx_stats_num = GVE_TX_STATS_REPORT_NUM *
priv->tx_cfg.num_queues;
int rx_stats_num = GVE_RX_STATS_REPORT_NUM *
priv->rx_cfg.num_queues;
memset(priv->stats_report->stats, 0, (tx_stats_num + rx_stats_num) *
sizeof(struct stats));
del_timer_sync(&priv->stats_report_timer);
}
return 0;
}
const struct ethtool_ops gve_ethtool_ops = { const struct ethtool_ops gve_ethtool_ops = {
.get_drvinfo = gve_get_drvinfo, .get_drvinfo = gve_get_drvinfo,
.get_strings = gve_get_strings, .get_strings = gve_get_strings,
...@@ -366,4 +437,6 @@ const struct ethtool_ops gve_ethtool_ops = { ...@@ -366,4 +437,6 @@ const struct ethtool_ops gve_ethtool_ops = {
.reset = gve_user_reset, .reset = gve_user_reset,
.get_tunable = gve_get_tunable, .get_tunable = gve_get_tunable,
.set_tunable = gve_set_tunable, .set_tunable = gve_set_tunable,
.get_priv_flags = gve_get_priv_flags,
.set_priv_flags = gve_set_priv_flags,
}; };
...@@ -78,6 +78,66 @@ static void gve_free_counter_array(struct gve_priv *priv) ...@@ -78,6 +78,66 @@ static void gve_free_counter_array(struct gve_priv *priv)
priv->counter_array = NULL; priv->counter_array = NULL;
} }
/* NIC requests to report stats */
static void gve_stats_report_task(struct work_struct *work)
{
struct gve_priv *priv = container_of(work, struct gve_priv,
stats_report_task);
if (gve_get_do_report_stats(priv)) {
gve_handle_report_stats(priv);
gve_clear_do_report_stats(priv);
}
}
static void gve_stats_report_schedule(struct gve_priv *priv)
{
if (!gve_get_probe_in_progress(priv) &&
!gve_get_reset_in_progress(priv)) {
gve_set_do_report_stats(priv);
queue_work(priv->gve_wq, &priv->stats_report_task);
}
}
static void gve_stats_report_timer(struct timer_list *t)
{
struct gve_priv *priv = from_timer(priv, t, stats_report_timer);
mod_timer(&priv->stats_report_timer,
round_jiffies(jiffies +
msecs_to_jiffies(priv->stats_report_timer_period)));
gve_stats_report_schedule(priv);
}
static int gve_alloc_stats_report(struct gve_priv *priv)
{
int tx_stats_num, rx_stats_num;
tx_stats_num = (GVE_TX_STATS_REPORT_NUM) *
priv->tx_cfg.num_queues;
rx_stats_num = (GVE_RX_STATS_REPORT_NUM) *
priv->rx_cfg.num_queues;
priv->stats_report_len = sizeof(struct gve_stats_report) +
(tx_stats_num + rx_stats_num) *
sizeof(struct stats);
priv->stats_report =
dma_alloc_coherent(&priv->pdev->dev, priv->stats_report_len,
&priv->stats_report_bus, GFP_KERNEL);
if (!priv->stats_report)
return -ENOMEM;
/* Set up timer for the report-stats task */
timer_setup(&priv->stats_report_timer, gve_stats_report_timer, 0);
priv->stats_report_timer_period = GVE_STATS_REPORT_TIMER_PERIOD;
return 0;
}
static void gve_free_stats_report(struct gve_priv *priv)
{
del_timer_sync(&priv->stats_report_timer);
dma_free_coherent(&priv->pdev->dev, priv->stats_report_len,
priv->stats_report, priv->stats_report_bus);
priv->stats_report = NULL;
}
static irqreturn_t gve_mgmnt_intr(int irq, void *arg) static irqreturn_t gve_mgmnt_intr(int irq, void *arg)
{ {
struct gve_priv *priv = arg; struct gve_priv *priv = arg;
...@@ -270,6 +330,9 @@ static int gve_setup_device_resources(struct gve_priv *priv) ...@@ -270,6 +330,9 @@ static int gve_setup_device_resources(struct gve_priv *priv)
err = gve_alloc_notify_blocks(priv); err = gve_alloc_notify_blocks(priv);
if (err) if (err)
goto abort_with_counter; goto abort_with_counter;
err = gve_alloc_stats_report(priv);
if (err)
goto abort_with_ntfy_blocks;
err = gve_adminq_configure_device_resources(priv, err = gve_adminq_configure_device_resources(priv,
priv->counter_array_bus, priv->counter_array_bus,
priv->num_event_counters, priv->num_event_counters,
...@@ -279,10 +342,18 @@ static int gve_setup_device_resources(struct gve_priv *priv) ...@@ -279,10 +342,18 @@ static int gve_setup_device_resources(struct gve_priv *priv)
dev_err(&priv->pdev->dev, dev_err(&priv->pdev->dev,
"could not setup device_resources: err=%d\n", err); "could not setup device_resources: err=%d\n", err);
err = -ENXIO; err = -ENXIO;
goto abort_with_ntfy_blocks; goto abort_with_stats_report;
} }
err = gve_adminq_report_stats(priv, priv->stats_report_len,
priv->stats_report_bus,
GVE_STATS_REPORT_TIMER_PERIOD);
if (err)
dev_err(&priv->pdev->dev,
"Failed to report stats: err=%d\n", err);
gve_set_device_resources_ok(priv); gve_set_device_resources_ok(priv);
return 0; return 0;
abort_with_stats_report:
gve_free_stats_report(priv);
abort_with_ntfy_blocks: abort_with_ntfy_blocks:
gve_free_notify_blocks(priv); gve_free_notify_blocks(priv);
abort_with_counter: abort_with_counter:
...@@ -298,6 +369,13 @@ static void gve_teardown_device_resources(struct gve_priv *priv) ...@@ -298,6 +369,13 @@ static void gve_teardown_device_resources(struct gve_priv *priv)
/* Tell device its resources are being freed */ /* Tell device its resources are being freed */
if (gve_get_device_resources_ok(priv)) { if (gve_get_device_resources_ok(priv)) {
/* detach the stats report */
err = gve_adminq_report_stats(priv, 0, 0x0, GVE_STATS_REPORT_TIMER_PERIOD);
if (err) {
dev_err(&priv->pdev->dev,
"Failed to detach stats report: err=%d\n", err);
gve_trigger_reset(priv);
}
err = gve_adminq_deconfigure_device_resources(priv); err = gve_adminq_deconfigure_device_resources(priv);
if (err) { if (err) {
dev_err(&priv->pdev->dev, dev_err(&priv->pdev->dev,
...@@ -308,6 +386,7 @@ static void gve_teardown_device_resources(struct gve_priv *priv) ...@@ -308,6 +386,7 @@ static void gve_teardown_device_resources(struct gve_priv *priv)
} }
gve_free_counter_array(priv); gve_free_counter_array(priv);
gve_free_notify_blocks(priv); gve_free_notify_blocks(priv);
gve_free_stats_report(priv);
gve_clear_device_resources_ok(priv); gve_clear_device_resources_ok(priv);
} }
...@@ -699,6 +778,11 @@ static int gve_open(struct net_device *dev) ...@@ -699,6 +778,11 @@ static int gve_open(struct net_device *dev)
goto reset; goto reset;
gve_set_device_rings_ok(priv); gve_set_device_rings_ok(priv);
if (gve_get_report_stats(priv))
mod_timer(&priv->stats_report_timer,
round_jiffies(jiffies +
msecs_to_jiffies(priv->stats_report_timer_period)));
gve_turnup(priv); gve_turnup(priv);
netif_carrier_on(dev); netif_carrier_on(dev);
priv->interface_up_cnt++; priv->interface_up_cnt++;
...@@ -740,6 +824,7 @@ static int gve_close(struct net_device *dev) ...@@ -740,6 +824,7 @@ static int gve_close(struct net_device *dev)
goto err; goto err;
gve_clear_device_rings_ok(priv); gve_clear_device_rings_ok(priv);
} }
del_timer_sync(&priv->stats_report_timer);
gve_free_rings(priv); gve_free_rings(priv);
gve_free_qpls(priv); gve_free_qpls(priv);
...@@ -823,6 +908,7 @@ static void gve_turndown(struct gve_priv *priv) ...@@ -823,6 +908,7 @@ static void gve_turndown(struct gve_priv *priv)
netif_tx_disable(priv->dev); netif_tx_disable(priv->dev);
gve_clear_napi_enabled(priv); gve_clear_napi_enabled(priv);
gve_clear_report_stats(priv);
} }
static void gve_turnup(struct gve_priv *priv) static void gve_turnup(struct gve_priv *priv)
...@@ -873,6 +959,10 @@ static void gve_handle_status(struct gve_priv *priv, u32 status) ...@@ -873,6 +959,10 @@ static void gve_handle_status(struct gve_priv *priv, u32 status)
dev_info(&priv->pdev->dev, "Device requested reset.\n"); dev_info(&priv->pdev->dev, "Device requested reset.\n");
gve_set_do_reset(priv); gve_set_do_reset(priv);
} }
if (GVE_DEVICE_STATUS_REPORT_STATS_MASK & status) {
priv->stats_report_trigger_cnt++;
gve_set_do_report_stats(priv);
}
} }
static void gve_handle_reset(struct gve_priv *priv) static void gve_handle_reset(struct gve_priv *priv)
...@@ -891,7 +981,68 @@ static void gve_handle_reset(struct gve_priv *priv) ...@@ -891,7 +981,68 @@ static void gve_handle_reset(struct gve_priv *priv)
} }
} }
/* Handle NIC status register changes and reset requests */ void gve_handle_report_stats(struct gve_priv *priv)
{
int idx, stats_idx = 0, tx_bytes;
unsigned int start = 0;
struct stats *stats = priv->stats_report->stats;
if (!gve_get_report_stats(priv))
return;
be64_add_cpu(&priv->stats_report->written_count, 1);
/* tx stats */
if (priv->tx) {
for (idx = 0; idx < priv->tx_cfg.num_queues; idx++) {
do {
start = u64_stats_fetch_begin(&priv->tx[idx].statss);
tx_bytes = priv->tx[idx].bytes_done;
} while (u64_stats_fetch_retry(&priv->tx[idx].statss, start));
stats[stats_idx++] = (struct stats) {
.stat_name = cpu_to_be32(TX_WAKE_CNT),
.value = cpu_to_be64(priv->tx[idx].wake_queue),
.queue_id = cpu_to_be32(idx),
};
stats[stats_idx++] = (struct stats) {
.stat_name = cpu_to_be32(TX_STOP_CNT),
.value = cpu_to_be64(priv->tx[idx].stop_queue),
.queue_id = cpu_to_be32(idx),
};
stats[stats_idx++] = (struct stats) {
.stat_name = cpu_to_be32(TX_FRAMES_SENT),
.value = cpu_to_be64(priv->tx[idx].req),
.queue_id = cpu_to_be32(idx),
};
stats[stats_idx++] = (struct stats) {
.stat_name = cpu_to_be32(TX_BYTES_SENT),
.value = cpu_to_be64(tx_bytes),
.queue_id = cpu_to_be32(idx),
};
stats[stats_idx++] = (struct stats) {
.stat_name = cpu_to_be32(TX_LAST_COMPLETION_PROCESSED),
.value = cpu_to_be64(priv->tx[idx].done),
.queue_id = cpu_to_be32(idx),
};
}
}
/* rx stats */
if (priv->rx) {
for (idx = 0; idx < priv->rx_cfg.num_queues; idx++) {
stats[stats_idx++] = (struct stats) {
.stat_name = cpu_to_be32(RX_NEXT_EXPECTED_SEQUENCE),
.value = cpu_to_be64(priv->rx[idx].desc.seqno),
.queue_id = cpu_to_be32(idx),
};
stats[stats_idx++] = (struct stats) {
.stat_name = cpu_to_be32(RX_BUFFERS_POSTED),
.value = cpu_to_be64(priv->rx[0].fill_cnt),
.queue_id = cpu_to_be32(idx),
};
}
}
}
/* Handle NIC status register changes, reset requests and report stats */
static void gve_service_task(struct work_struct *work) static void gve_service_task(struct work_struct *work)
{ {
struct gve_priv *priv = container_of(work, struct gve_priv, struct gve_priv *priv = container_of(work, struct gve_priv,
...@@ -1056,6 +1207,7 @@ int gve_reset(struct gve_priv *priv, bool attempt_teardown) ...@@ -1056,6 +1207,7 @@ int gve_reset(struct gve_priv *priv, bool attempt_teardown)
priv->reset_cnt++; priv->reset_cnt++;
priv->interface_up_cnt = 0; priv->interface_up_cnt = 0;
priv->interface_down_cnt = 0; priv->interface_down_cnt = 0;
priv->stats_report_trigger_cnt = 0;
return err; return err;
} }
...@@ -1158,6 +1310,7 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -1158,6 +1310,7 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
priv->db_bar2 = db_bar; priv->db_bar2 = db_bar;
priv->service_task_flags = 0x0; priv->service_task_flags = 0x0;
priv->state_flags = 0x0; priv->state_flags = 0x0;
priv->ethtool_flags = 0x0;
gve_set_probe_in_progress(priv); gve_set_probe_in_progress(priv);
priv->gve_wq = alloc_ordered_workqueue("gve", 0); priv->gve_wq = alloc_ordered_workqueue("gve", 0);
...@@ -1167,6 +1320,7 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -1167,6 +1320,7 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto abort_with_netdev; goto abort_with_netdev;
} }
INIT_WORK(&priv->service_task, gve_service_task); INIT_WORK(&priv->service_task, gve_service_task);
INIT_WORK(&priv->stats_report_task, gve_stats_report_task);
priv->tx_cfg.max_queues = max_tx_queues; priv->tx_cfg.max_queues = max_tx_queues;
priv->rx_cfg.max_queues = max_rx_queues; priv->rx_cfg.max_queues = max_rx_queues;
......
...@@ -23,5 +23,6 @@ struct gve_registers { ...@@ -23,5 +23,6 @@ struct gve_registers {
enum gve_device_status_flags { enum gve_device_status_flags {
GVE_DEVICE_STATUS_RESET_MASK = BIT(1), GVE_DEVICE_STATUS_RESET_MASK = BIT(1),
GVE_DEVICE_STATUS_LINK_STATUS_MASK = BIT(2), GVE_DEVICE_STATUS_LINK_STATUS_MASK = BIT(2),
GVE_DEVICE_STATUS_REPORT_STATS_MASK = BIT(3),
}; };
#endif /* _GVE_REGISTER_H_ */ #endif /* _GVE_REGISTER_H_ */
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