gve: Enable Link Speed Reporting in the driver.
authorDavid Awogbemila <awogbemila@google.com>
Fri, 11 Sep 2020 17:38:51 +0000 (10:38 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 11 Sep 2020 21:31:54 +0000 (14:31 -0700)
This change allows the driver to report the device link speed
when the ethtool command:
ethtool <nic name>
is run.
Getting the link speed is done via a new admin queue command:
ReportLinkSpeed.

Reviewed-by: Yangchun Fu <yangchun@google.com>
Signed-off-by: David Awogbemila <awogbemila@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/google/gve/gve.h
drivers/net/ethernet/google/gve/gve_adminq.c
drivers/net/ethernet/google/gve/gve_adminq.h
drivers/net/ethernet/google/gve/gve_ethtool.c

index ebb770f..f5c8022 100644 (file)
@@ -232,6 +232,7 @@ struct gve_priv {
        u32 adminq_dcfg_device_resources_cnt;
        u32 adminq_set_driver_parameter_cnt;
        u32 adminq_report_stats_cnt;
+       u32 adminq_report_link_speed_cnt;
 
        /* Global stats */
        u32 interface_up_cnt; /* count of times interface turned up since last reset */
@@ -254,6 +255,8 @@ struct gve_priv {
        unsigned long stats_report_timer_period;
        struct timer_list stats_report_timer;
 
+       /* Gvnic device link speed from hypervisor. */
+       u64 link_speed;
 };
 
 enum gve_service_task_flags_bit {
index 6f5ccd5..24ae6a2 100644 (file)
@@ -36,6 +36,7 @@ int gve_adminq_alloc(struct device *dev, struct gve_priv *priv)
        priv->adminq_dcfg_device_resources_cnt = 0;
        priv->adminq_set_driver_parameter_cnt = 0;
        priv->adminq_report_stats_cnt = 0;
+       priv->adminq_report_link_speed_cnt = 0;
 
        /* Setup Admin queue with the device */
        iowrite32be(priv->adminq_bus_addr / PAGE_SIZE,
@@ -238,6 +239,9 @@ static int gve_adminq_issue_cmd(struct gve_priv *priv,
        case GVE_ADMINQ_REPORT_STATS:
                priv->adminq_report_stats_cnt++;
                break;
+       case GVE_ADMINQ_REPORT_LINK_SPEED:
+               priv->adminq_report_link_speed_cnt++;
+               break;
        default:
                dev_err(&priv->pdev->dev, "unknown AQ command opcode %d\n", opcode);
        }
@@ -595,3 +599,30 @@ int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len,
 
        return gve_adminq_execute_cmd(priv, &cmd);
 }
+
+int gve_adminq_report_link_speed(struct gve_priv *priv)
+{
+       union gve_adminq_command gvnic_cmd;
+       dma_addr_t link_speed_region_bus;
+       __be64 *link_speed_region;
+       int err;
+
+       link_speed_region =
+               dma_alloc_coherent(&priv->pdev->dev, sizeof(*link_speed_region),
+                                  &link_speed_region_bus, GFP_KERNEL);
+
+       if (!link_speed_region)
+               return -ENOMEM;
+
+       memset(&gvnic_cmd, 0, sizeof(gvnic_cmd));
+       gvnic_cmd.opcode = cpu_to_be32(GVE_ADMINQ_REPORT_LINK_SPEED);
+       gvnic_cmd.report_link_speed.link_speed_address =
+               cpu_to_be64(link_speed_region_bus);
+
+       err = gve_adminq_execute_cmd(priv, &gvnic_cmd);
+
+       priv->link_speed = be64_to_cpu(*link_speed_region);
+       dma_free_coherent(&priv->pdev->dev, sizeof(*link_speed_region), link_speed_region,
+                         link_speed_region_bus);
+       return err;
+}
index 784830f..281de83 100644 (file)
@@ -22,6 +22,7 @@ enum gve_adminq_opcodes {
        GVE_ADMINQ_DECONFIGURE_DEVICE_RESOURCES = 0x9,
        GVE_ADMINQ_SET_DRIVER_PARAMETER         = 0xB,
        GVE_ADMINQ_REPORT_STATS                 = 0xC,
+       GVE_ADMINQ_REPORT_LINK_SPEED    = 0xD
 };
 
 /* Admin queue status codes */
@@ -181,6 +182,12 @@ struct gve_adminq_report_stats {
 
 static_assert(sizeof(struct gve_adminq_report_stats) == 24);
 
+struct gve_adminq_report_link_speed {
+       __be64 link_speed_address;
+};
+
+static_assert(sizeof(struct gve_adminq_report_link_speed) == 8);
+
 struct stats {
        __be32 stat_name;
        __be32 queue_id;
@@ -228,6 +235,7 @@ union gve_adminq_command {
                        struct gve_adminq_unregister_page_list unreg_page_list;
                        struct gve_adminq_set_driver_parameter set_driver_param;
                        struct gve_adminq_report_stats report_stats;
+                       struct gve_adminq_report_link_speed report_link_speed;
                };
        };
        u8 reserved[64];
@@ -255,4 +263,5 @@ 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_report_stats(struct gve_priv *priv, u64 stats_report_len,
                            dma_addr_t stats_report_addr, u64 interval);
+int gve_adminq_report_link_speed(struct gve_priv *priv);
 #endif /* _GVE_ADMINQ_H */
index c5bb884..7b44769 100644 (file)
@@ -59,7 +59,7 @@ static const char gve_gstrings_adminq_stats[][ETH_GSTRING_LEN] = {
        "adminq_create_tx_queue_cnt", "adminq_create_rx_queue_cnt",
        "adminq_destroy_tx_queue_cnt", "adminq_destroy_rx_queue_cnt",
        "adminq_dcfg_device_resources_cnt", "adminq_set_driver_parameter_cnt",
-       "adminq_report_stats_cnt",
+       "adminq_report_stats_cnt", "adminq_report_link_speed_cnt"
 };
 
 static const char gve_gstrings_priv_flags[][ETH_GSTRING_LEN] = {
@@ -355,6 +355,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
        data[i++] = priv->adminq_dcfg_device_resources_cnt;
        data[i++] = priv->adminq_set_driver_parameter_cnt;
        data[i++] = priv->adminq_report_stats_cnt;
+       data[i++] = priv->adminq_report_link_speed_cnt;
 }
 
 static void gve_get_channels(struct net_device *netdev,
@@ -505,6 +506,16 @@ static int gve_set_priv_flags(struct net_device *netdev, u32 flags)
        return 0;
 }
 
+static int gve_get_link_ksettings(struct net_device *netdev,
+                                 struct ethtool_link_ksettings *cmd)
+{
+       struct gve_priv *priv = netdev_priv(netdev);
+       int err = gve_adminq_report_link_speed(priv);
+
+       cmd->base.speed = priv->link_speed;
+       return err;
+}
+
 const struct ethtool_ops gve_ethtool_ops = {
        .get_drvinfo = gve_get_drvinfo,
        .get_strings = gve_get_strings,
@@ -521,4 +532,5 @@ const struct ethtool_ops gve_ethtool_ops = {
        .set_tunable = gve_set_tunable,
        .get_priv_flags = gve_get_priv_flags,
        .set_priv_flags = gve_set_priv_flags,
+       .get_link_ksettings = gve_get_link_ksettings
 };