Merge branch 'gve-add-rx-hw-timestamping-support'

Ziwei Xiao says:

====================
gve: Add Rx HW timestamping support

This patch series add the support of Rx HW timestamping, which sends
adminq commands periodically to the device for clock synchronization with
the NIC.

The ability to read the PHC from user space will be added in the
future patch series when adding the actual PTP support. For this patch
series, it's adding the initial ptp to utilize the ptp_schedule_worker
to schedule the work of syncing the NIC clock.
====================

Link: https://patch.msgid.link/20250614000754.164827-1-hramamurthy@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-06-16 15:27:27 -07:00
10 changed files with 396 additions and 17 deletions

View File

@@ -18,6 +18,7 @@ if NET_VENDOR_GOOGLE
config GVE
tristate "Google Virtual NIC (gVNIC) support"
depends on (PCI_MSI && (X86 || CPU_LITTLE_ENDIAN))
depends on PTP_1588_CLOCK_OPTIONAL
select PAGE_POOL
help
This driver supports Google Virtual NIC (gVNIC)"

View File

@@ -1,5 +1,7 @@
# Makefile for the Google virtual Ethernet (gve) driver
obj-$(CONFIG_GVE) += gve.o
gve-objs := gve_main.o gve_tx.o gve_tx_dqo.o gve_rx.o gve_rx_dqo.o gve_ethtool.o gve_adminq.o gve_utils.o gve_flow_rule.o \
gve-y := gve_main.o gve_tx.o gve_tx_dqo.o gve_rx.o gve_rx_dqo.o gve_ethtool.o gve_adminq.o gve_utils.o gve_flow_rule.o \
gve_buffer_mgmt_dqo.o
gve-$(CONFIG_PTP_1588_CLOCK) += gve_ptp.o

View File

@@ -11,7 +11,9 @@
#include <linux/dmapool.h>
#include <linux/ethtool_netlink.h>
#include <linux/netdevice.h>
#include <linux/net_tstamp.h>
#include <linux/pci.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/u64_stats_sync.h>
#include <net/page_pool/helpers.h>
#include <net/xdp.h>
@@ -750,6 +752,12 @@ struct gve_rss_config {
u32 *hash_lut;
};
struct gve_ptp {
struct ptp_clock_info info;
struct ptp_clock *clock;
struct gve_priv *priv;
};
struct gve_priv {
struct net_device *dev;
struct gve_tx_ring *tx; /* array of tx_cfg.num_queues */
@@ -813,6 +821,7 @@ struct gve_priv {
u32 adminq_set_driver_parameter_cnt;
u32 adminq_report_stats_cnt;
u32 adminq_report_link_speed_cnt;
u32 adminq_report_nic_timestamp_cnt;
u32 adminq_get_ptype_map_cnt;
u32 adminq_verify_driver_compatibility_cnt;
u32 adminq_query_flow_rules_cnt;
@@ -870,6 +879,14 @@ struct gve_priv {
u16 rss_lut_size;
bool cache_rss_config;
struct gve_rss_config rss_config;
/* True if the device supports reading the nic clock */
bool nic_timestamp_supported;
struct gve_ptp *ptp;
struct kernel_hwtstamp_config ts_config;
struct gve_nic_ts_report *nic_ts_report;
dma_addr_t nic_ts_report_bus;
u64 last_sync_nic_counter; /* Clock counter from last NIC TS report */
};
enum gve_service_task_flags_bit {
@@ -1249,6 +1266,24 @@ int gve_del_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd);
int gve_flow_rules_reset(struct gve_priv *priv);
/* RSS config */
int gve_init_rss_config(struct gve_priv *priv, u16 num_queues);
/* PTP and timestamping */
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
int gve_clock_nic_ts_read(struct gve_priv *priv);
int gve_init_clock(struct gve_priv *priv);
void gve_teardown_clock(struct gve_priv *priv);
#else /* CONFIG_PTP_1588_CLOCK */
static inline int gve_clock_nic_ts_read(struct gve_priv *priv)
{
return -EOPNOTSUPP;
}
static inline int gve_init_clock(struct gve_priv *priv)
{
return 0;
}
static inline void gve_teardown_clock(struct gve_priv *priv) { }
#endif /* CONFIG_PTP_1588_CLOCK */
/* report stats handling */
void gve_handle_report_stats(struct gve_priv *priv);
/* exported by ethtool.c */

View File

@@ -46,6 +46,7 @@ void gve_parse_device_option(struct gve_priv *priv,
struct gve_device_option_buffer_sizes **dev_op_buffer_sizes,
struct gve_device_option_flow_steering **dev_op_flow_steering,
struct gve_device_option_rss_config **dev_op_rss_config,
struct gve_device_option_nic_timestamp **dev_op_nic_timestamp,
struct gve_device_option_modify_ring **dev_op_modify_ring)
{
u32 req_feat_mask = be32_to_cpu(option->required_features_mask);
@@ -225,6 +226,23 @@ void gve_parse_device_option(struct gve_priv *priv,
"RSS config");
*dev_op_rss_config = (void *)(option + 1);
break;
case GVE_DEV_OPT_ID_NIC_TIMESTAMP:
if (option_length < sizeof(**dev_op_nic_timestamp) ||
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_NIC_TIMESTAMP) {
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
"Nic Timestamp",
(int)sizeof(**dev_op_nic_timestamp),
GVE_DEV_OPT_REQ_FEAT_MASK_NIC_TIMESTAMP,
option_length, req_feat_mask);
break;
}
if (option_length > sizeof(**dev_op_nic_timestamp))
dev_warn(&priv->pdev->dev,
GVE_DEVICE_OPTION_TOO_BIG_FMT,
"Nic Timestamp");
*dev_op_nic_timestamp = (void *)(option + 1);
break;
default:
/* If we don't recognize the option just continue
* without doing anything.
@@ -246,6 +264,7 @@ gve_process_device_options(struct gve_priv *priv,
struct gve_device_option_buffer_sizes **dev_op_buffer_sizes,
struct gve_device_option_flow_steering **dev_op_flow_steering,
struct gve_device_option_rss_config **dev_op_rss_config,
struct gve_device_option_nic_timestamp **dev_op_nic_timestamp,
struct gve_device_option_modify_ring **dev_op_modify_ring)
{
const int num_options = be16_to_cpu(descriptor->num_device_options);
@@ -269,6 +288,7 @@ gve_process_device_options(struct gve_priv *priv,
dev_op_dqo_rda, dev_op_jumbo_frames,
dev_op_dqo_qpl, dev_op_buffer_sizes,
dev_op_flow_steering, dev_op_rss_config,
dev_op_nic_timestamp,
dev_op_modify_ring);
dev_opt = next_opt;
}
@@ -306,6 +326,7 @@ int gve_adminq_alloc(struct device *dev, struct gve_priv *priv)
priv->adminq_set_driver_parameter_cnt = 0;
priv->adminq_report_stats_cnt = 0;
priv->adminq_report_link_speed_cnt = 0;
priv->adminq_report_nic_timestamp_cnt = 0;
priv->adminq_get_ptype_map_cnt = 0;
priv->adminq_query_flow_rules_cnt = 0;
priv->adminq_cfg_flow_rule_cnt = 0;
@@ -442,6 +463,8 @@ static int gve_adminq_kick_and_wait(struct gve_priv *priv)
int tail, head;
int i;
lockdep_assert_held(&priv->adminq_lock);
tail = ioread32be(&priv->reg_bar0->adminq_event_counter);
head = priv->adminq_prod_cnt;
@@ -467,9 +490,6 @@ static int gve_adminq_kick_and_wait(struct gve_priv *priv)
return 0;
}
/* This function is not threadsafe - the caller is responsible for any
* necessary locks.
*/
static int gve_adminq_issue_cmd(struct gve_priv *priv,
union gve_adminq_command *cmd_orig)
{
@@ -477,6 +497,8 @@ static int gve_adminq_issue_cmd(struct gve_priv *priv,
u32 opcode;
u32 tail;
lockdep_assert_held(&priv->adminq_lock);
tail = ioread32be(&priv->reg_bar0->adminq_event_counter);
// Check if next command will overflow the buffer.
@@ -544,6 +566,9 @@ static int gve_adminq_issue_cmd(struct gve_priv *priv,
case GVE_ADMINQ_REPORT_LINK_SPEED:
priv->adminq_report_link_speed_cnt++;
break;
case GVE_ADMINQ_REPORT_NIC_TIMESTAMP:
priv->adminq_report_nic_timestamp_cnt++;
break;
case GVE_ADMINQ_GET_PTYPE_MAP:
priv->adminq_get_ptype_map_cnt++;
break;
@@ -709,13 +734,19 @@ int gve_adminq_create_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_que
int err;
int i;
mutex_lock(&priv->adminq_lock);
for (i = start_id; i < start_id + num_queues; i++) {
err = gve_adminq_create_tx_queue(priv, i);
if (err)
return err;
goto out;
}
return gve_adminq_kick_and_wait(priv);
err = gve_adminq_kick_and_wait(priv);
out:
mutex_unlock(&priv->adminq_lock);
return err;
}
static void gve_adminq_get_create_rx_queue_cmd(struct gve_priv *priv,
@@ -788,13 +819,19 @@ int gve_adminq_create_rx_queues(struct gve_priv *priv, u32 num_queues)
int err;
int i;
mutex_lock(&priv->adminq_lock);
for (i = 0; i < num_queues; i++) {
err = gve_adminq_create_rx_queue(priv, i);
if (err)
return err;
goto out;
}
return gve_adminq_kick_and_wait(priv);
err = gve_adminq_kick_and_wait(priv);
out:
mutex_unlock(&priv->adminq_lock);
return err;
}
static int gve_adminq_destroy_tx_queue(struct gve_priv *priv, u32 queue_index)
@@ -820,13 +857,19 @@ int gve_adminq_destroy_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_qu
int err;
int i;
mutex_lock(&priv->adminq_lock);
for (i = start_id; i < start_id + num_queues; i++) {
err = gve_adminq_destroy_tx_queue(priv, i);
if (err)
return err;
goto out;
}
return gve_adminq_kick_and_wait(priv);
err = gve_adminq_kick_and_wait(priv);
out:
mutex_unlock(&priv->adminq_lock);
return err;
}
static void gve_adminq_make_destroy_rx_queue_cmd(union gve_adminq_command *cmd,
@@ -861,13 +904,19 @@ int gve_adminq_destroy_rx_queues(struct gve_priv *priv, u32 num_queues)
int err;
int i;
mutex_lock(&priv->adminq_lock);
for (i = 0; i < num_queues; i++) {
err = gve_adminq_destroy_rx_queue(priv, i);
if (err)
return err;
goto out;
}
return gve_adminq_kick_and_wait(priv);
err = gve_adminq_kick_and_wait(priv);
out:
mutex_unlock(&priv->adminq_lock);
return err;
}
static void gve_set_default_desc_cnt(struct gve_priv *priv,
@@ -904,6 +953,8 @@ static void gve_enable_supported_features(struct gve_priv *priv,
*dev_op_flow_steering,
const struct gve_device_option_rss_config
*dev_op_rss_config,
const struct gve_device_option_nic_timestamp
*dev_op_nic_timestamp,
const struct gve_device_option_modify_ring
*dev_op_modify_ring)
{
@@ -980,10 +1031,15 @@ static void gve_enable_supported_features(struct gve_priv *priv,
"RSS device option enabled with key size of %u, lut size of %u.\n",
priv->rss_key_size, priv->rss_lut_size);
}
if (dev_op_nic_timestamp &&
(supported_features_mask & GVE_SUP_NIC_TIMESTAMP_MASK))
priv->nic_timestamp_supported = true;
}
int gve_adminq_describe_device(struct gve_priv *priv)
{
struct gve_device_option_nic_timestamp *dev_op_nic_timestamp = NULL;
struct gve_device_option_flow_steering *dev_op_flow_steering = NULL;
struct gve_device_option_buffer_sizes *dev_op_buffer_sizes = NULL;
struct gve_device_option_jumbo_frames *dev_op_jumbo_frames = NULL;
@@ -1024,6 +1080,7 @@ int gve_adminq_describe_device(struct gve_priv *priv)
&dev_op_buffer_sizes,
&dev_op_flow_steering,
&dev_op_rss_config,
&dev_op_nic_timestamp,
&dev_op_modify_ring);
if (err)
goto free_device_descriptor;
@@ -1088,7 +1145,8 @@ int gve_adminq_describe_device(struct gve_priv *priv)
gve_enable_supported_features(priv, supported_features_mask,
dev_op_jumbo_frames, dev_op_dqo_qpl,
dev_op_buffer_sizes, dev_op_flow_steering,
dev_op_rss_config, dev_op_modify_ring);
dev_op_rss_config, dev_op_nic_timestamp,
dev_op_modify_ring);
free_device_descriptor:
dma_pool_free(priv->adminq_pool, descriptor, descriptor_bus);
@@ -1200,6 +1258,22 @@ int gve_adminq_report_link_speed(struct gve_priv *priv)
return err;
}
int gve_adminq_report_nic_ts(struct gve_priv *priv,
dma_addr_t nic_ts_report_addr)
{
union gve_adminq_command cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = cpu_to_be32(GVE_ADMINQ_REPORT_NIC_TIMESTAMP);
cmd.report_nic_ts = (struct gve_adminq_report_nic_ts) {
.nic_ts_report_len =
cpu_to_be64(sizeof(struct gve_nic_ts_report)),
.nic_ts_report_addr = cpu_to_be64(nic_ts_report_addr),
};
return gve_adminq_execute_cmd(priv, &cmd);
}
int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv,
struct gve_ptype_lut *ptype_lut)
{

View File

@@ -27,6 +27,7 @@ enum gve_adminq_opcodes {
GVE_ADMINQ_GET_PTYPE_MAP = 0xE,
GVE_ADMINQ_VERIFY_DRIVER_COMPATIBILITY = 0xF,
GVE_ADMINQ_QUERY_FLOW_RULES = 0x10,
GVE_ADMINQ_REPORT_NIC_TIMESTAMP = 0x11,
GVE_ADMINQ_QUERY_RSS = 0x12,
/* For commands that are larger than 56 bytes */
@@ -174,6 +175,12 @@ struct gve_device_option_rss_config {
static_assert(sizeof(struct gve_device_option_rss_config) == 8);
struct gve_device_option_nic_timestamp {
__be32 supported_features_mask;
};
static_assert(sizeof(struct gve_device_option_nic_timestamp) == 4);
/* Terminology:
*
* RDA - Raw DMA Addressing - Buffers associated with SKBs are directly DMA
@@ -192,6 +199,7 @@ enum gve_dev_opt_id {
GVE_DEV_OPT_ID_JUMBO_FRAMES = 0x8,
GVE_DEV_OPT_ID_BUFFER_SIZES = 0xa,
GVE_DEV_OPT_ID_FLOW_STEERING = 0xb,
GVE_DEV_OPT_ID_NIC_TIMESTAMP = 0xd,
GVE_DEV_OPT_ID_RSS_CONFIG = 0xe,
};
@@ -206,6 +214,7 @@ enum gve_dev_opt_req_feat_mask {
GVE_DEV_OPT_REQ_FEAT_MASK_MODIFY_RING = 0x0,
GVE_DEV_OPT_REQ_FEAT_MASK_FLOW_STEERING = 0x0,
GVE_DEV_OPT_REQ_FEAT_MASK_RSS_CONFIG = 0x0,
GVE_DEV_OPT_REQ_FEAT_MASK_NIC_TIMESTAMP = 0x0,
};
enum gve_sup_feature_mask {
@@ -214,6 +223,7 @@ enum gve_sup_feature_mask {
GVE_SUP_BUFFER_SIZES_MASK = 1 << 4,
GVE_SUP_FLOW_STEERING_MASK = 1 << 5,
GVE_SUP_RSS_CONFIG_MASK = 1 << 7,
GVE_SUP_NIC_TIMESTAMP_MASK = 1 << 8,
};
#define GVE_DEV_OPT_LEN_GQI_RAW_ADDRESSING 0x0
@@ -392,6 +402,21 @@ struct gve_adminq_report_link_speed {
static_assert(sizeof(struct gve_adminq_report_link_speed) == 8);
struct gve_adminq_report_nic_ts {
__be64 nic_ts_report_len;
__be64 nic_ts_report_addr;
};
static_assert(sizeof(struct gve_adminq_report_nic_ts) == 16);
struct gve_nic_ts_report {
__be64 nic_timestamp; /* NIC clock in nanoseconds */
__be64 reserved1;
__be64 reserved2;
__be64 reserved3;
__be64 reserved4;
};
struct stats {
__be32 stat_name;
__be32 queue_id;
@@ -585,6 +610,7 @@ union gve_adminq_command {
struct gve_adminq_query_flow_rules query_flow_rules;
struct gve_adminq_configure_rss configure_rss;
struct gve_adminq_query_rss query_rss;
struct gve_adminq_report_nic_ts report_nic_ts;
struct gve_adminq_extended_command extended_command;
};
};
@@ -624,6 +650,8 @@ int gve_adminq_reset_flow_rules(struct gve_priv *priv);
int gve_adminq_query_flow_rules(struct gve_priv *priv, u16 query_opcode, u32 starting_loc);
int gve_adminq_configure_rss(struct gve_priv *priv, struct ethtool_rxfh_param *rxfh);
int gve_adminq_query_rss_config(struct gve_priv *priv, struct ethtool_rxfh_param *rxfh);
int gve_adminq_report_nic_ts(struct gve_priv *priv,
dma_addr_t nic_ts_report_addr);
struct gve_ptype_lut;
int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv,

View File

@@ -247,7 +247,8 @@ struct gve_rx_compl_desc_dqo {
};
__le32 hash;
__le32 reserved6;
__le64 reserved7;
__le32 reserved7;
__le32 ts; /* timestamp in nanosecs */
} __packed;
static_assert(sizeof(struct gve_rx_compl_desc_dqo) == 32);

View File

@@ -76,7 +76,7 @@ static const char gve_gstrings_adminq_stats[][ETH_GSTRING_LEN] __nonstring_array
"adminq_dcfg_device_resources_cnt", "adminq_set_driver_parameter_cnt",
"adminq_report_stats_cnt", "adminq_report_link_speed_cnt", "adminq_get_ptype_map_cnt",
"adminq_query_flow_rules", "adminq_cfg_flow_rule", "adminq_cfg_rss_cnt",
"adminq_query_rss_cnt",
"adminq_query_rss_cnt", "adminq_report_nic_timestamp_cnt",
};
static const char gve_gstrings_priv_flags[][ETH_GSTRING_LEN] = {
@@ -456,6 +456,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
data[i++] = priv->adminq_cfg_flow_rule_cnt;
data[i++] = priv->adminq_cfg_rss_cnt;
data[i++] = priv->adminq_query_rss_cnt;
data[i++] = priv->adminq_report_nic_timestamp_cnt;
}
static void gve_get_channels(struct net_device *netdev,
@@ -922,6 +923,27 @@ static int gve_set_rxfh(struct net_device *netdev, struct ethtool_rxfh_param *rx
return 0;
}
static int gve_get_ts_info(struct net_device *netdev,
struct kernel_ethtool_ts_info *info)
{
struct gve_priv *priv = netdev_priv(netdev);
ethtool_op_get_ts_info(netdev, info);
if (priv->nic_timestamp_supported) {
info->so_timestamping |= SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
info->rx_filters |= BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_ALL);
if (priv->ptp)
info->phc_index = ptp_clock_index(priv->ptp->clock);
}
return 0;
}
const struct ethtool_ops gve_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS,
.supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT,
@@ -950,5 +972,5 @@ const struct ethtool_ops gve_ethtool_ops = {
.get_priv_flags = gve_get_priv_flags,
.set_priv_flags = gve_set_priv_flags,
.get_link_ksettings = gve_get_link_ksettings,
.get_ts_info = ethtool_op_get_ts_info,
.get_ts_info = gve_get_ts_info,
};

View File

@@ -619,9 +619,12 @@ static int gve_setup_device_resources(struct gve_priv *priv)
err = gve_alloc_counter_array(priv);
if (err)
goto abort_with_rss_config_cache;
err = gve_alloc_notify_blocks(priv);
err = gve_init_clock(priv);
if (err)
goto abort_with_counter;
err = gve_alloc_notify_blocks(priv);
if (err)
goto abort_with_clock;
err = gve_alloc_stats_report(priv);
if (err)
goto abort_with_ntfy_blocks;
@@ -674,6 +677,8 @@ static int gve_setup_device_resources(struct gve_priv *priv)
gve_free_stats_report(priv);
abort_with_ntfy_blocks:
gve_free_notify_blocks(priv);
abort_with_clock:
gve_teardown_clock(priv);
abort_with_counter:
gve_free_counter_array(priv);
abort_with_rss_config_cache:
@@ -722,6 +727,7 @@ static void gve_teardown_device_resources(struct gve_priv *priv)
gve_free_counter_array(priv);
gve_free_notify_blocks(priv);
gve_free_stats_report(priv);
gve_teardown_clock(priv);
gve_clear_device_resources_ok(priv);
}
@@ -2042,6 +2048,46 @@ static int gve_set_features(struct net_device *netdev,
return err;
}
static int gve_get_ts_config(struct net_device *dev,
struct kernel_hwtstamp_config *kernel_config)
{
struct gve_priv *priv = netdev_priv(dev);
*kernel_config = priv->ts_config;
return 0;
}
static int gve_set_ts_config(struct net_device *dev,
struct kernel_hwtstamp_config *kernel_config,
struct netlink_ext_ack *extack)
{
struct gve_priv *priv = netdev_priv(dev);
if (kernel_config->tx_type != HWTSTAMP_TX_OFF) {
NL_SET_ERR_MSG_MOD(extack, "TX timestamping is not supported");
return -ERANGE;
}
if (kernel_config->rx_filter != HWTSTAMP_FILTER_NONE) {
if (!priv->nic_ts_report) {
NL_SET_ERR_MSG_MOD(extack,
"RX timestamping is not supported");
kernel_config->rx_filter = HWTSTAMP_FILTER_NONE;
return -EOPNOTSUPP;
}
kernel_config->rx_filter = HWTSTAMP_FILTER_ALL;
gve_clock_nic_ts_read(priv);
ptp_schedule_worker(priv->ptp->clock, 0);
} else {
ptp_cancel_worker_sync(priv->ptp->clock);
}
priv->ts_config.rx_filter = kernel_config->rx_filter;
return 0;
}
static const struct net_device_ops gve_netdev_ops = {
.ndo_start_xmit = gve_start_xmit,
.ndo_features_check = gve_features_check,
@@ -2053,6 +2099,8 @@ static const struct net_device_ops gve_netdev_ops = {
.ndo_bpf = gve_xdp,
.ndo_xdp_xmit = gve_xdp_xmit,
.ndo_xsk_wakeup = gve_xsk_wakeup,
.ndo_hwtstamp_get = gve_get_ts_config,
.ndo_hwtstamp_set = gve_set_ts_config,
};
static void gve_handle_status(struct gve_priv *priv, u32 status)
@@ -2272,6 +2320,9 @@ static int gve_init_priv(struct gve_priv *priv, bool skip_describe_device)
priv->rx_coalesce_usecs = GVE_RX_IRQ_RATELIMIT_US_DQO;
}
priv->ts_config.tx_type = HWTSTAMP_TX_OFF;
priv->ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
setup_device:
gve_set_netdev_xdp_features(priv);
err = gve_setup_device_resources(priv);

View File

@@ -0,0 +1,139 @@
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Google virtual Ethernet (gve) driver
*
* Copyright (C) 2025 Google LLC
*/
#include "gve.h"
#include "gve_adminq.h"
/* Interval to schedule a nic timestamp calibration, 250ms. */
#define GVE_NIC_TS_SYNC_INTERVAL_MS 250
/* Read the nic timestamp from hardware via the admin queue. */
int gve_clock_nic_ts_read(struct gve_priv *priv)
{
u64 nic_raw;
int err;
err = gve_adminq_report_nic_ts(priv, priv->nic_ts_report_bus);
if (err)
return err;
nic_raw = be64_to_cpu(priv->nic_ts_report->nic_timestamp);
WRITE_ONCE(priv->last_sync_nic_counter, nic_raw);
return 0;
}
static long gve_ptp_do_aux_work(struct ptp_clock_info *info)
{
const struct gve_ptp *ptp = container_of(info, struct gve_ptp, info);
struct gve_priv *priv = ptp->priv;
int err;
if (gve_get_reset_in_progress(priv) || !gve_get_admin_queue_ok(priv))
goto out;
err = gve_clock_nic_ts_read(priv);
if (err && net_ratelimit())
dev_err(&priv->pdev->dev,
"%s read err %d\n", __func__, err);
out:
return msecs_to_jiffies(GVE_NIC_TS_SYNC_INTERVAL_MS);
}
static const struct ptp_clock_info gve_ptp_caps = {
.owner = THIS_MODULE,
.name = "gve clock",
.do_aux_work = gve_ptp_do_aux_work,
};
static int gve_ptp_init(struct gve_priv *priv)
{
struct gve_ptp *ptp;
int err;
if (!priv->nic_timestamp_supported) {
dev_dbg(&priv->pdev->dev, "Device does not support PTP\n");
return -EOPNOTSUPP;
}
priv->ptp = kzalloc(sizeof(*priv->ptp), GFP_KERNEL);
if (!priv->ptp)
return -ENOMEM;
ptp = priv->ptp;
ptp->info = gve_ptp_caps;
ptp->clock = ptp_clock_register(&ptp->info, &priv->pdev->dev);
if (IS_ERR(ptp->clock)) {
dev_err(&priv->pdev->dev, "PTP clock registration failed\n");
err = PTR_ERR(ptp->clock);
goto free_ptp;
}
ptp->priv = priv;
return 0;
free_ptp:
kfree(ptp);
priv->ptp = NULL;
return err;
}
static void gve_ptp_release(struct gve_priv *priv)
{
struct gve_ptp *ptp = priv->ptp;
if (!ptp)
return;
if (ptp->clock)
ptp_clock_unregister(ptp->clock);
kfree(ptp);
priv->ptp = NULL;
}
int gve_init_clock(struct gve_priv *priv)
{
int err;
if (!priv->nic_timestamp_supported)
return 0;
err = gve_ptp_init(priv);
if (err)
return err;
priv->nic_ts_report =
dma_alloc_coherent(&priv->pdev->dev,
sizeof(struct gve_nic_ts_report),
&priv->nic_ts_report_bus,
GFP_KERNEL);
if (!priv->nic_ts_report) {
dev_err(&priv->pdev->dev, "%s dma alloc error\n", __func__);
err = -ENOMEM;
goto release_ptp;
}
return 0;
release_ptp:
gve_ptp_release(priv);
return err;
}
void gve_teardown_clock(struct gve_priv *priv)
{
gve_ptp_release(priv);
if (priv->nic_ts_report) {
dma_free_coherent(&priv->pdev->dev,
sizeof(struct gve_nic_ts_report),
priv->nic_ts_report, priv->nic_ts_report_bus);
priv->nic_ts_report = NULL;
}
}

View File

@@ -437,6 +437,29 @@ static void gve_rx_skb_hash(struct sk_buff *skb,
skb_set_hash(skb, le32_to_cpu(compl_desc->hash), hash_type);
}
/* Expand the hardware timestamp to the full 64 bits of width, and add it to the
* skb.
*
* This algorithm works by using the passed hardware timestamp to generate a
* diff relative to the last read of the nic clock. This diff can be positive or
* negative, as it is possible that we have read the clock more recently than
* the hardware has received this packet. To detect this, we use the high bit of
* the diff, and assume that the read is more recent if the high bit is set. In
* this case we invert the process.
*
* Note that this means if the time delta between packet reception and the last
* clock read is greater than ~2 seconds, this will provide invalid results.
*/
static void gve_rx_skb_hwtstamp(struct gve_rx_ring *rx, u32 hwts)
{
u64 last_read = READ_ONCE(rx->gve->last_sync_nic_counter);
struct sk_buff *skb = rx->ctx.skb_head;
u32 low = (u32)last_read;
s32 diff = hwts - low;
skb_hwtstamps(skb)->hwtstamp = ns_to_ktime(last_read + diff);
}
static void gve_rx_free_skb(struct napi_struct *napi, struct gve_rx_ring *rx)
{
if (!rx->ctx.skb_head)
@@ -767,6 +790,9 @@ static int gve_rx_complete_skb(struct gve_rx_ring *rx, struct napi_struct *napi,
if (feat & NETIF_F_RXCSUM)
gve_rx_skb_csum(rx->ctx.skb_head, desc, ptype);
if (rx->gve->ts_config.rx_filter == HWTSTAMP_FILTER_ALL)
gve_rx_skb_hwtstamp(rx, le32_to_cpu(desc->ts));
/* RSC packets must set gso_size otherwise the TCP stack will complain
* that packets are larger than MTU.
*/