mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

In gve_adminq_issue_cmd(), return -EINVAL instead of 0 when an unknown admin queue command opcode is encountered. This prevents the function from silently succeeding on invalid input and prevents undefined behavior by ensuring the function fails gracefully when an unrecognized opcode is provided. These changes improve error handling. Signed-off-by: Alok Tiwari <alok.a.tiwari@oracle.com> Link: https://patch.msgid.link/20250616054504.1644770-2-alok.a.tiwari@oracle.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1604 lines
48 KiB
C
1604 lines
48 KiB
C
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
|
/* Google virtual Ethernet (gve) driver
|
|
*
|
|
* Copyright (C) 2015-2021 Google, Inc.
|
|
*/
|
|
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/pci.h>
|
|
#include "gve.h"
|
|
#include "gve_adminq.h"
|
|
#include "gve_register.h"
|
|
|
|
#define GVE_MAX_ADMINQ_RELEASE_CHECK 500
|
|
#define GVE_ADMINQ_SLEEP_LEN 20
|
|
#define GVE_MAX_ADMINQ_EVENT_COUNTER_CHECK 100
|
|
|
|
#define GVE_DEVICE_OPTION_ERROR_FMT "%s option error:\n" \
|
|
"Expected: length=%d, feature_mask=%x.\n" \
|
|
"Actual: length=%d, feature_mask=%x.\n"
|
|
|
|
#define GVE_DEVICE_OPTION_TOO_BIG_FMT "Length of %s option larger than expected. Possible older version of guest driver.\n"
|
|
|
|
static
|
|
struct gve_device_option *gve_get_next_option(struct gve_device_descriptor *descriptor,
|
|
struct gve_device_option *option)
|
|
{
|
|
void *option_end, *descriptor_end;
|
|
|
|
option_end = (void *)(option + 1) + be16_to_cpu(option->option_length);
|
|
descriptor_end = (void *)descriptor + be16_to_cpu(descriptor->total_length);
|
|
|
|
return option_end > descriptor_end ? NULL : (struct gve_device_option *)option_end;
|
|
}
|
|
|
|
#define GVE_DEVICE_OPTION_NO_MIN_RING_SIZE 8
|
|
|
|
static
|
|
void gve_parse_device_option(struct gve_priv *priv,
|
|
struct gve_device_descriptor *device_descriptor,
|
|
struct gve_device_option *option,
|
|
struct gve_device_option_gqi_rda **dev_op_gqi_rda,
|
|
struct gve_device_option_gqi_qpl **dev_op_gqi_qpl,
|
|
struct gve_device_option_dqo_rda **dev_op_dqo_rda,
|
|
struct gve_device_option_jumbo_frames **dev_op_jumbo_frames,
|
|
struct gve_device_option_dqo_qpl **dev_op_dqo_qpl,
|
|
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);
|
|
u16 option_length = be16_to_cpu(option->option_length);
|
|
u16 option_id = be16_to_cpu(option->option_id);
|
|
|
|
/* If the length or feature mask doesn't match, continue without
|
|
* enabling the feature.
|
|
*/
|
|
switch (option_id) {
|
|
case GVE_DEV_OPT_ID_GQI_RAW_ADDRESSING:
|
|
if (option_length != GVE_DEV_OPT_LEN_GQI_RAW_ADDRESSING ||
|
|
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_GQI_RAW_ADDRESSING) {
|
|
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
|
|
"Raw Addressing",
|
|
GVE_DEV_OPT_LEN_GQI_RAW_ADDRESSING,
|
|
GVE_DEV_OPT_REQ_FEAT_MASK_GQI_RAW_ADDRESSING,
|
|
option_length, req_feat_mask);
|
|
break;
|
|
}
|
|
|
|
dev_info(&priv->pdev->dev,
|
|
"Gqi raw addressing device option enabled.\n");
|
|
priv->queue_format = GVE_GQI_RDA_FORMAT;
|
|
break;
|
|
case GVE_DEV_OPT_ID_GQI_RDA:
|
|
if (option_length < sizeof(**dev_op_gqi_rda) ||
|
|
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_GQI_RDA) {
|
|
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
|
|
"GQI RDA", (int)sizeof(**dev_op_gqi_rda),
|
|
GVE_DEV_OPT_REQ_FEAT_MASK_GQI_RDA,
|
|
option_length, req_feat_mask);
|
|
break;
|
|
}
|
|
|
|
if (option_length > sizeof(**dev_op_gqi_rda)) {
|
|
dev_warn(&priv->pdev->dev,
|
|
GVE_DEVICE_OPTION_TOO_BIG_FMT, "GQI RDA");
|
|
}
|
|
*dev_op_gqi_rda = (void *)(option + 1);
|
|
break;
|
|
case GVE_DEV_OPT_ID_GQI_QPL:
|
|
if (option_length < sizeof(**dev_op_gqi_qpl) ||
|
|
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_GQI_QPL) {
|
|
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
|
|
"GQI QPL", (int)sizeof(**dev_op_gqi_qpl),
|
|
GVE_DEV_OPT_REQ_FEAT_MASK_GQI_QPL,
|
|
option_length, req_feat_mask);
|
|
break;
|
|
}
|
|
|
|
if (option_length > sizeof(**dev_op_gqi_qpl)) {
|
|
dev_warn(&priv->pdev->dev,
|
|
GVE_DEVICE_OPTION_TOO_BIG_FMT, "GQI QPL");
|
|
}
|
|
*dev_op_gqi_qpl = (void *)(option + 1);
|
|
break;
|
|
case GVE_DEV_OPT_ID_DQO_RDA:
|
|
if (option_length < sizeof(**dev_op_dqo_rda) ||
|
|
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_DQO_RDA) {
|
|
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
|
|
"DQO RDA", (int)sizeof(**dev_op_dqo_rda),
|
|
GVE_DEV_OPT_REQ_FEAT_MASK_DQO_RDA,
|
|
option_length, req_feat_mask);
|
|
break;
|
|
}
|
|
|
|
if (option_length > sizeof(**dev_op_dqo_rda)) {
|
|
dev_warn(&priv->pdev->dev,
|
|
GVE_DEVICE_OPTION_TOO_BIG_FMT, "DQO RDA");
|
|
}
|
|
*dev_op_dqo_rda = (void *)(option + 1);
|
|
break;
|
|
case GVE_DEV_OPT_ID_DQO_QPL:
|
|
if (option_length < sizeof(**dev_op_dqo_qpl) ||
|
|
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_DQO_QPL) {
|
|
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
|
|
"DQO QPL", (int)sizeof(**dev_op_dqo_qpl),
|
|
GVE_DEV_OPT_REQ_FEAT_MASK_DQO_QPL,
|
|
option_length, req_feat_mask);
|
|
break;
|
|
}
|
|
|
|
if (option_length > sizeof(**dev_op_dqo_qpl)) {
|
|
dev_warn(&priv->pdev->dev,
|
|
GVE_DEVICE_OPTION_TOO_BIG_FMT, "DQO QPL");
|
|
}
|
|
*dev_op_dqo_qpl = (void *)(option + 1);
|
|
break;
|
|
case GVE_DEV_OPT_ID_JUMBO_FRAMES:
|
|
if (option_length < sizeof(**dev_op_jumbo_frames) ||
|
|
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_JUMBO_FRAMES) {
|
|
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
|
|
"Jumbo Frames",
|
|
(int)sizeof(**dev_op_jumbo_frames),
|
|
GVE_DEV_OPT_REQ_FEAT_MASK_JUMBO_FRAMES,
|
|
option_length, req_feat_mask);
|
|
break;
|
|
}
|
|
|
|
if (option_length > sizeof(**dev_op_jumbo_frames)) {
|
|
dev_warn(&priv->pdev->dev,
|
|
GVE_DEVICE_OPTION_TOO_BIG_FMT,
|
|
"Jumbo Frames");
|
|
}
|
|
*dev_op_jumbo_frames = (void *)(option + 1);
|
|
break;
|
|
case GVE_DEV_OPT_ID_BUFFER_SIZES:
|
|
if (option_length < sizeof(**dev_op_buffer_sizes) ||
|
|
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_BUFFER_SIZES) {
|
|
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
|
|
"Buffer Sizes",
|
|
(int)sizeof(**dev_op_buffer_sizes),
|
|
GVE_DEV_OPT_REQ_FEAT_MASK_BUFFER_SIZES,
|
|
option_length, req_feat_mask);
|
|
break;
|
|
}
|
|
|
|
if (option_length > sizeof(**dev_op_buffer_sizes))
|
|
dev_warn(&priv->pdev->dev,
|
|
GVE_DEVICE_OPTION_TOO_BIG_FMT,
|
|
"Buffer Sizes");
|
|
*dev_op_buffer_sizes = (void *)(option + 1);
|
|
break;
|
|
case GVE_DEV_OPT_ID_MODIFY_RING:
|
|
if (option_length < GVE_DEVICE_OPTION_NO_MIN_RING_SIZE ||
|
|
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_MODIFY_RING) {
|
|
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
|
|
"Modify Ring", (int)sizeof(**dev_op_modify_ring),
|
|
GVE_DEV_OPT_REQ_FEAT_MASK_MODIFY_RING,
|
|
option_length, req_feat_mask);
|
|
break;
|
|
}
|
|
|
|
if (option_length > sizeof(**dev_op_modify_ring)) {
|
|
dev_warn(&priv->pdev->dev,
|
|
GVE_DEVICE_OPTION_TOO_BIG_FMT, "Modify Ring");
|
|
}
|
|
|
|
*dev_op_modify_ring = (void *)(option + 1);
|
|
|
|
/* device has not provided min ring size */
|
|
if (option_length == GVE_DEVICE_OPTION_NO_MIN_RING_SIZE)
|
|
priv->default_min_ring_size = true;
|
|
break;
|
|
case GVE_DEV_OPT_ID_FLOW_STEERING:
|
|
if (option_length < sizeof(**dev_op_flow_steering) ||
|
|
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_FLOW_STEERING) {
|
|
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
|
|
"Flow Steering",
|
|
(int)sizeof(**dev_op_flow_steering),
|
|
GVE_DEV_OPT_REQ_FEAT_MASK_FLOW_STEERING,
|
|
option_length, req_feat_mask);
|
|
break;
|
|
}
|
|
|
|
if (option_length > sizeof(**dev_op_flow_steering))
|
|
dev_warn(&priv->pdev->dev,
|
|
GVE_DEVICE_OPTION_TOO_BIG_FMT,
|
|
"Flow Steering");
|
|
*dev_op_flow_steering = (void *)(option + 1);
|
|
break;
|
|
case GVE_DEV_OPT_ID_RSS_CONFIG:
|
|
if (option_length < sizeof(**dev_op_rss_config) ||
|
|
req_feat_mask != GVE_DEV_OPT_REQ_FEAT_MASK_RSS_CONFIG) {
|
|
dev_warn(&priv->pdev->dev, GVE_DEVICE_OPTION_ERROR_FMT,
|
|
"RSS config",
|
|
(int)sizeof(**dev_op_rss_config),
|
|
GVE_DEV_OPT_REQ_FEAT_MASK_RSS_CONFIG,
|
|
option_length, req_feat_mask);
|
|
break;
|
|
}
|
|
|
|
if (option_length > sizeof(**dev_op_rss_config))
|
|
dev_warn(&priv->pdev->dev,
|
|
GVE_DEVICE_OPTION_TOO_BIG_FMT,
|
|
"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.
|
|
*/
|
|
dev_dbg(&priv->pdev->dev, "Unrecognized device option 0x%hx not enabled.\n",
|
|
option_id);
|
|
}
|
|
}
|
|
|
|
/* Process all device options for a given describe device call. */
|
|
static int
|
|
gve_process_device_options(struct gve_priv *priv,
|
|
struct gve_device_descriptor *descriptor,
|
|
struct gve_device_option_gqi_rda **dev_op_gqi_rda,
|
|
struct gve_device_option_gqi_qpl **dev_op_gqi_qpl,
|
|
struct gve_device_option_dqo_rda **dev_op_dqo_rda,
|
|
struct gve_device_option_jumbo_frames **dev_op_jumbo_frames,
|
|
struct gve_device_option_dqo_qpl **dev_op_dqo_qpl,
|
|
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);
|
|
struct gve_device_option *dev_opt;
|
|
int i;
|
|
|
|
/* The options struct directly follows the device descriptor. */
|
|
dev_opt = (void *)(descriptor + 1);
|
|
for (i = 0; i < num_options; i++) {
|
|
struct gve_device_option *next_opt;
|
|
|
|
next_opt = gve_get_next_option(descriptor, dev_opt);
|
|
if (!next_opt) {
|
|
dev_err(&priv->dev->dev,
|
|
"options exceed device_descriptor's total length.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
gve_parse_device_option(priv, descriptor, dev_opt,
|
|
dev_op_gqi_rda, dev_op_gqi_qpl,
|
|
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;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gve_adminq_alloc(struct device *dev, struct gve_priv *priv)
|
|
{
|
|
priv->adminq_pool = dma_pool_create("adminq_pool", dev,
|
|
GVE_ADMINQ_BUFFER_SIZE, 0, 0);
|
|
if (unlikely(!priv->adminq_pool))
|
|
return -ENOMEM;
|
|
priv->adminq = dma_pool_alloc(priv->adminq_pool, GFP_KERNEL,
|
|
&priv->adminq_bus_addr);
|
|
if (unlikely(!priv->adminq)) {
|
|
dma_pool_destroy(priv->adminq_pool);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
priv->adminq_mask =
|
|
(GVE_ADMINQ_BUFFER_SIZE / sizeof(union gve_adminq_command)) - 1;
|
|
priv->adminq_prod_cnt = 0;
|
|
priv->adminq_cmd_fail = 0;
|
|
priv->adminq_timeouts = 0;
|
|
priv->adminq_describe_device_cnt = 0;
|
|
priv->adminq_cfg_device_resources_cnt = 0;
|
|
priv->adminq_register_page_list_cnt = 0;
|
|
priv->adminq_unregister_page_list_cnt = 0;
|
|
priv->adminq_create_tx_queue_cnt = 0;
|
|
priv->adminq_create_rx_queue_cnt = 0;
|
|
priv->adminq_destroy_tx_queue_cnt = 0;
|
|
priv->adminq_destroy_rx_queue_cnt = 0;
|
|
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;
|
|
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;
|
|
priv->adminq_cfg_rss_cnt = 0;
|
|
priv->adminq_query_rss_cnt = 0;
|
|
|
|
/* Setup Admin queue with the device */
|
|
if (priv->pdev->revision < 0x1) {
|
|
iowrite32be(priv->adminq_bus_addr / PAGE_SIZE,
|
|
&priv->reg_bar0->adminq_pfn);
|
|
} else {
|
|
iowrite16be(GVE_ADMINQ_BUFFER_SIZE,
|
|
&priv->reg_bar0->adminq_length);
|
|
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
|
|
iowrite32be(priv->adminq_bus_addr >> 32,
|
|
&priv->reg_bar0->adminq_base_address_hi);
|
|
#endif
|
|
iowrite32be(priv->adminq_bus_addr,
|
|
&priv->reg_bar0->adminq_base_address_lo);
|
|
iowrite32be(GVE_DRIVER_STATUS_RUN_MASK, &priv->reg_bar0->driver_status);
|
|
}
|
|
mutex_init(&priv->adminq_lock);
|
|
gve_set_admin_queue_ok(priv);
|
|
return 0;
|
|
}
|
|
|
|
void gve_adminq_release(struct gve_priv *priv)
|
|
{
|
|
int i = 0;
|
|
|
|
/* Tell the device the adminq is leaving */
|
|
if (priv->pdev->revision < 0x1) {
|
|
iowrite32be(0x0, &priv->reg_bar0->adminq_pfn);
|
|
while (ioread32be(&priv->reg_bar0->adminq_pfn)) {
|
|
/* If this is reached the device is unrecoverable and still
|
|
* holding memory. Continue looping to avoid memory corruption,
|
|
* but WARN so it is visible what is going on.
|
|
*/
|
|
if (i == GVE_MAX_ADMINQ_RELEASE_CHECK)
|
|
WARN(1, "Unrecoverable platform error!");
|
|
i++;
|
|
msleep(GVE_ADMINQ_SLEEP_LEN);
|
|
}
|
|
} else {
|
|
iowrite32be(GVE_DRIVER_STATUS_RESET_MASK, &priv->reg_bar0->driver_status);
|
|
while (!(ioread32be(&priv->reg_bar0->device_status)
|
|
& GVE_DEVICE_STATUS_DEVICE_IS_RESET)) {
|
|
if (i == GVE_MAX_ADMINQ_RELEASE_CHECK)
|
|
WARN(1, "Unrecoverable platform error!");
|
|
i++;
|
|
msleep(GVE_ADMINQ_SLEEP_LEN);
|
|
}
|
|
}
|
|
gve_clear_device_rings_ok(priv);
|
|
gve_clear_device_resources_ok(priv);
|
|
gve_clear_admin_queue_ok(priv);
|
|
}
|
|
|
|
void gve_adminq_free(struct device *dev, struct gve_priv *priv)
|
|
{
|
|
if (!gve_get_admin_queue_ok(priv))
|
|
return;
|
|
gve_adminq_release(priv);
|
|
dma_pool_free(priv->adminq_pool, priv->adminq, priv->adminq_bus_addr);
|
|
dma_pool_destroy(priv->adminq_pool);
|
|
gve_clear_admin_queue_ok(priv);
|
|
}
|
|
|
|
static void gve_adminq_kick_cmd(struct gve_priv *priv, u32 prod_cnt)
|
|
{
|
|
iowrite32be(prod_cnt, &priv->reg_bar0->adminq_doorbell);
|
|
}
|
|
|
|
static bool gve_adminq_wait_for_cmd(struct gve_priv *priv, u32 prod_cnt)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < GVE_MAX_ADMINQ_EVENT_COUNTER_CHECK; i++) {
|
|
if (ioread32be(&priv->reg_bar0->adminq_event_counter)
|
|
== prod_cnt)
|
|
return true;
|
|
msleep(GVE_ADMINQ_SLEEP_LEN);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int gve_adminq_parse_err(struct gve_priv *priv, u32 status)
|
|
{
|
|
if (status != GVE_ADMINQ_COMMAND_PASSED &&
|
|
status != GVE_ADMINQ_COMMAND_UNSET) {
|
|
dev_err(&priv->pdev->dev, "AQ command failed with status %d\n", status);
|
|
priv->adminq_cmd_fail++;
|
|
}
|
|
switch (status) {
|
|
case GVE_ADMINQ_COMMAND_PASSED:
|
|
return 0;
|
|
case GVE_ADMINQ_COMMAND_UNSET:
|
|
dev_err(&priv->pdev->dev, "parse_aq_err: err and status both unset, this should not be possible.\n");
|
|
return -EINVAL;
|
|
case GVE_ADMINQ_COMMAND_ERROR_ABORTED:
|
|
case GVE_ADMINQ_COMMAND_ERROR_CANCELLED:
|
|
case GVE_ADMINQ_COMMAND_ERROR_DATALOSS:
|
|
case GVE_ADMINQ_COMMAND_ERROR_FAILED_PRECONDITION:
|
|
case GVE_ADMINQ_COMMAND_ERROR_UNAVAILABLE:
|
|
return -EAGAIN;
|
|
case GVE_ADMINQ_COMMAND_ERROR_ALREADY_EXISTS:
|
|
case GVE_ADMINQ_COMMAND_ERROR_INTERNAL_ERROR:
|
|
case GVE_ADMINQ_COMMAND_ERROR_INVALID_ARGUMENT:
|
|
case GVE_ADMINQ_COMMAND_ERROR_NOT_FOUND:
|
|
case GVE_ADMINQ_COMMAND_ERROR_OUT_OF_RANGE:
|
|
case GVE_ADMINQ_COMMAND_ERROR_UNKNOWN_ERROR:
|
|
return -EINVAL;
|
|
case GVE_ADMINQ_COMMAND_ERROR_DEADLINE_EXCEEDED:
|
|
return -ETIME;
|
|
case GVE_ADMINQ_COMMAND_ERROR_PERMISSION_DENIED:
|
|
case GVE_ADMINQ_COMMAND_ERROR_UNAUTHENTICATED:
|
|
return -EACCES;
|
|
case GVE_ADMINQ_COMMAND_ERROR_RESOURCE_EXHAUSTED:
|
|
return -ENOMEM;
|
|
case GVE_ADMINQ_COMMAND_ERROR_UNIMPLEMENTED:
|
|
return -EOPNOTSUPP;
|
|
default:
|
|
dev_err(&priv->pdev->dev, "parse_aq_err: unknown status code %d\n", status);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* Flushes all AQ commands currently queued and waits for them to complete.
|
|
* If there are failures, it will return the first error.
|
|
*/
|
|
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;
|
|
|
|
gve_adminq_kick_cmd(priv, head);
|
|
if (!gve_adminq_wait_for_cmd(priv, head)) {
|
|
dev_err(&priv->pdev->dev, "AQ commands timed out, need to reset AQ\n");
|
|
priv->adminq_timeouts++;
|
|
return -ENOTRECOVERABLE;
|
|
}
|
|
|
|
for (i = tail; i < head; i++) {
|
|
union gve_adminq_command *cmd;
|
|
u32 status, err;
|
|
|
|
cmd = &priv->adminq[i & priv->adminq_mask];
|
|
status = be32_to_cpu(READ_ONCE(cmd->status));
|
|
err = gve_adminq_parse_err(priv, status);
|
|
if (err)
|
|
// Return the first error if we failed.
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gve_adminq_issue_cmd(struct gve_priv *priv,
|
|
union gve_adminq_command *cmd_orig)
|
|
{
|
|
union gve_adminq_command *cmd;
|
|
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.
|
|
if (((priv->adminq_prod_cnt + 1) & priv->adminq_mask) ==
|
|
(tail & priv->adminq_mask)) {
|
|
int err;
|
|
|
|
// Flush existing commands to make room.
|
|
err = gve_adminq_kick_and_wait(priv);
|
|
if (err)
|
|
return err;
|
|
|
|
// Retry.
|
|
tail = ioread32be(&priv->reg_bar0->adminq_event_counter);
|
|
if (((priv->adminq_prod_cnt + 1) & priv->adminq_mask) ==
|
|
(tail & priv->adminq_mask)) {
|
|
// This should never happen. We just flushed the
|
|
// command queue so there should be enough space.
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
cmd = &priv->adminq[priv->adminq_prod_cnt & priv->adminq_mask];
|
|
priv->adminq_prod_cnt++;
|
|
|
|
memcpy(cmd, cmd_orig, sizeof(*cmd_orig));
|
|
opcode = be32_to_cpu(READ_ONCE(cmd->opcode));
|
|
if (opcode == GVE_ADMINQ_EXTENDED_COMMAND)
|
|
opcode = be32_to_cpu(cmd->extended_command.inner_opcode);
|
|
|
|
switch (opcode) {
|
|
case GVE_ADMINQ_DESCRIBE_DEVICE:
|
|
priv->adminq_describe_device_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_CONFIGURE_DEVICE_RESOURCES:
|
|
priv->adminq_cfg_device_resources_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_REGISTER_PAGE_LIST:
|
|
priv->adminq_register_page_list_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_UNREGISTER_PAGE_LIST:
|
|
priv->adminq_unregister_page_list_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_CREATE_TX_QUEUE:
|
|
priv->adminq_create_tx_queue_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_CREATE_RX_QUEUE:
|
|
priv->adminq_create_rx_queue_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_DESTROY_TX_QUEUE:
|
|
priv->adminq_destroy_tx_queue_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_DESTROY_RX_QUEUE:
|
|
priv->adminq_destroy_rx_queue_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_DECONFIGURE_DEVICE_RESOURCES:
|
|
priv->adminq_dcfg_device_resources_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_SET_DRIVER_PARAMETER:
|
|
priv->adminq_set_driver_parameter_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_REPORT_STATS:
|
|
priv->adminq_report_stats_cnt++;
|
|
break;
|
|
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;
|
|
case GVE_ADMINQ_VERIFY_DRIVER_COMPATIBILITY:
|
|
priv->adminq_verify_driver_compatibility_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_QUERY_FLOW_RULES:
|
|
priv->adminq_query_flow_rules_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_CONFIGURE_FLOW_RULE:
|
|
priv->adminq_cfg_flow_rule_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_CONFIGURE_RSS:
|
|
priv->adminq_cfg_rss_cnt++;
|
|
break;
|
|
case GVE_ADMINQ_QUERY_RSS:
|
|
priv->adminq_query_rss_cnt++;
|
|
break;
|
|
default:
|
|
dev_err(&priv->pdev->dev, "unknown AQ command opcode %d\n", opcode);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gve_adminq_execute_cmd(struct gve_priv *priv,
|
|
union gve_adminq_command *cmd_orig)
|
|
{
|
|
u32 tail, head;
|
|
int err;
|
|
|
|
mutex_lock(&priv->adminq_lock);
|
|
tail = ioread32be(&priv->reg_bar0->adminq_event_counter);
|
|
head = priv->adminq_prod_cnt;
|
|
if (tail != head) {
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
err = gve_adminq_issue_cmd(priv, cmd_orig);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = gve_adminq_kick_and_wait(priv);
|
|
|
|
out:
|
|
mutex_unlock(&priv->adminq_lock);
|
|
return err;
|
|
}
|
|
|
|
static int gve_adminq_execute_extended_cmd(struct gve_priv *priv, u32 opcode,
|
|
size_t cmd_size, void *cmd_orig)
|
|
{
|
|
union gve_adminq_command cmd;
|
|
dma_addr_t inner_cmd_bus;
|
|
void *inner_cmd;
|
|
int err;
|
|
|
|
inner_cmd = dma_alloc_coherent(&priv->pdev->dev, cmd_size,
|
|
&inner_cmd_bus, GFP_KERNEL);
|
|
if (!inner_cmd)
|
|
return -ENOMEM;
|
|
|
|
memcpy(inner_cmd, cmd_orig, cmd_size);
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_EXTENDED_COMMAND);
|
|
cmd.extended_command = (struct gve_adminq_extended_command) {
|
|
.inner_opcode = cpu_to_be32(opcode),
|
|
.inner_length = cpu_to_be32(cmd_size),
|
|
.inner_command_addr = cpu_to_be64(inner_cmd_bus),
|
|
};
|
|
|
|
err = gve_adminq_execute_cmd(priv, &cmd);
|
|
|
|
dma_free_coherent(&priv->pdev->dev, cmd_size, inner_cmd, inner_cmd_bus);
|
|
return err;
|
|
}
|
|
|
|
/* The device specifies that the management vector can either be the first irq
|
|
* or the last irq. ntfy_blk_msix_base_idx indicates the first irq assigned to
|
|
* the ntfy blks. If it is 0 then the management vector is last, if it is 1 then
|
|
* the management vector is first.
|
|
*
|
|
* gve arranges the msix vectors so that the management vector is last.
|
|
*/
|
|
#define GVE_NTFY_BLK_BASE_MSIX_IDX 0
|
|
int gve_adminq_configure_device_resources(struct gve_priv *priv,
|
|
dma_addr_t counter_array_bus_addr,
|
|
u32 num_counters,
|
|
dma_addr_t db_array_bus_addr,
|
|
u32 num_ntfy_blks)
|
|
{
|
|
union gve_adminq_command cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_CONFIGURE_DEVICE_RESOURCES);
|
|
cmd.configure_device_resources =
|
|
(struct gve_adminq_configure_device_resources) {
|
|
.counter_array = cpu_to_be64(counter_array_bus_addr),
|
|
.num_counters = cpu_to_be32(num_counters),
|
|
.irq_db_addr = cpu_to_be64(db_array_bus_addr),
|
|
.num_irq_dbs = cpu_to_be32(num_ntfy_blks),
|
|
.irq_db_stride = cpu_to_be32(sizeof(*priv->irq_db_indices)),
|
|
.ntfy_blk_msix_base_idx =
|
|
cpu_to_be32(GVE_NTFY_BLK_BASE_MSIX_IDX),
|
|
.queue_format = priv->queue_format,
|
|
};
|
|
|
|
return gve_adminq_execute_cmd(priv, &cmd);
|
|
}
|
|
|
|
int gve_adminq_deconfigure_device_resources(struct gve_priv *priv)
|
|
{
|
|
union gve_adminq_command cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_DECONFIGURE_DEVICE_RESOURCES);
|
|
|
|
return gve_adminq_execute_cmd(priv, &cmd);
|
|
}
|
|
|
|
static int gve_adminq_create_tx_queue(struct gve_priv *priv, u32 queue_index)
|
|
{
|
|
struct gve_tx_ring *tx = &priv->tx[queue_index];
|
|
union gve_adminq_command cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_CREATE_TX_QUEUE);
|
|
cmd.create_tx_queue = (struct gve_adminq_create_tx_queue) {
|
|
.queue_id = cpu_to_be32(queue_index),
|
|
.queue_resources_addr =
|
|
cpu_to_be64(tx->q_resources_bus),
|
|
.tx_ring_addr = cpu_to_be64(tx->bus),
|
|
.ntfy_id = cpu_to_be32(tx->ntfy_id),
|
|
.tx_ring_size = cpu_to_be16(priv->tx_desc_cnt),
|
|
};
|
|
|
|
if (gve_is_gqi(priv)) {
|
|
u32 qpl_id = priv->queue_format == GVE_GQI_RDA_FORMAT ?
|
|
GVE_RAW_ADDRESSING_QPL_ID : tx->tx_fifo.qpl->id;
|
|
|
|
cmd.create_tx_queue.queue_page_list_id = cpu_to_be32(qpl_id);
|
|
} else {
|
|
u32 qpl_id = 0;
|
|
|
|
if (priv->queue_format == GVE_DQO_RDA_FORMAT)
|
|
qpl_id = GVE_RAW_ADDRESSING_QPL_ID;
|
|
else
|
|
qpl_id = tx->dqo.qpl->id;
|
|
cmd.create_tx_queue.queue_page_list_id = cpu_to_be32(qpl_id);
|
|
cmd.create_tx_queue.tx_comp_ring_addr =
|
|
cpu_to_be64(tx->complq_bus_dqo);
|
|
cmd.create_tx_queue.tx_comp_ring_size =
|
|
cpu_to_be16(priv->tx_desc_cnt);
|
|
}
|
|
|
|
return gve_adminq_issue_cmd(priv, &cmd);
|
|
}
|
|
|
|
int gve_adminq_create_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues)
|
|
{
|
|
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)
|
|
goto out;
|
|
}
|
|
|
|
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,
|
|
union gve_adminq_command *cmd,
|
|
u32 queue_index)
|
|
{
|
|
struct gve_rx_ring *rx = &priv->rx[queue_index];
|
|
|
|
memset(cmd, 0, sizeof(*cmd));
|
|
cmd->opcode = cpu_to_be32(GVE_ADMINQ_CREATE_RX_QUEUE);
|
|
cmd->create_rx_queue = (struct gve_adminq_create_rx_queue) {
|
|
.queue_id = cpu_to_be32(queue_index),
|
|
.ntfy_id = cpu_to_be32(rx->ntfy_id),
|
|
.queue_resources_addr = cpu_to_be64(rx->q_resources_bus),
|
|
.rx_ring_size = cpu_to_be16(priv->rx_desc_cnt),
|
|
.packet_buffer_size = cpu_to_be16(rx->packet_buffer_size),
|
|
};
|
|
|
|
if (gve_is_gqi(priv)) {
|
|
u32 qpl_id = priv->queue_format == GVE_GQI_RDA_FORMAT ?
|
|
GVE_RAW_ADDRESSING_QPL_ID : rx->data.qpl->id;
|
|
|
|
cmd->create_rx_queue.rx_desc_ring_addr =
|
|
cpu_to_be64(rx->desc.bus);
|
|
cmd->create_rx_queue.rx_data_ring_addr =
|
|
cpu_to_be64(rx->data.data_bus);
|
|
cmd->create_rx_queue.index = cpu_to_be32(queue_index);
|
|
cmd->create_rx_queue.queue_page_list_id = cpu_to_be32(qpl_id);
|
|
} else {
|
|
u32 qpl_id = 0;
|
|
|
|
if (priv->queue_format == GVE_DQO_RDA_FORMAT)
|
|
qpl_id = GVE_RAW_ADDRESSING_QPL_ID;
|
|
else
|
|
qpl_id = rx->dqo.qpl->id;
|
|
cmd->create_rx_queue.queue_page_list_id = cpu_to_be32(qpl_id);
|
|
cmd->create_rx_queue.rx_desc_ring_addr =
|
|
cpu_to_be64(rx->dqo.complq.bus);
|
|
cmd->create_rx_queue.rx_data_ring_addr =
|
|
cpu_to_be64(rx->dqo.bufq.bus);
|
|
cmd->create_rx_queue.rx_buff_ring_size =
|
|
cpu_to_be16(priv->rx_desc_cnt);
|
|
cmd->create_rx_queue.enable_rsc =
|
|
!!(priv->dev->features & NETIF_F_LRO);
|
|
if (priv->header_split_enabled)
|
|
cmd->create_rx_queue.header_buffer_size =
|
|
cpu_to_be16(priv->header_buf_size);
|
|
}
|
|
}
|
|
|
|
static int gve_adminq_create_rx_queue(struct gve_priv *priv, u32 queue_index)
|
|
{
|
|
union gve_adminq_command cmd;
|
|
|
|
gve_adminq_get_create_rx_queue_cmd(priv, &cmd, queue_index);
|
|
return gve_adminq_issue_cmd(priv, &cmd);
|
|
}
|
|
|
|
/* Unlike gve_adminq_create_rx_queue, this actually rings the doorbell */
|
|
int gve_adminq_create_single_rx_queue(struct gve_priv *priv, u32 queue_index)
|
|
{
|
|
union gve_adminq_command cmd;
|
|
|
|
gve_adminq_get_create_rx_queue_cmd(priv, &cmd, queue_index);
|
|
return gve_adminq_execute_cmd(priv, &cmd);
|
|
}
|
|
|
|
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)
|
|
goto out;
|
|
}
|
|
|
|
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)
|
|
{
|
|
union gve_adminq_command cmd;
|
|
int err;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_DESTROY_TX_QUEUE);
|
|
cmd.destroy_tx_queue = (struct gve_adminq_destroy_tx_queue) {
|
|
.queue_id = cpu_to_be32(queue_index),
|
|
};
|
|
|
|
err = gve_adminq_issue_cmd(priv, &cmd);
|
|
if (err)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gve_adminq_destroy_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues)
|
|
{
|
|
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)
|
|
goto out;
|
|
}
|
|
|
|
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,
|
|
u32 queue_index)
|
|
{
|
|
memset(cmd, 0, sizeof(*cmd));
|
|
cmd->opcode = cpu_to_be32(GVE_ADMINQ_DESTROY_RX_QUEUE);
|
|
cmd->destroy_rx_queue = (struct gve_adminq_destroy_rx_queue) {
|
|
.queue_id = cpu_to_be32(queue_index),
|
|
};
|
|
}
|
|
|
|
static int gve_adminq_destroy_rx_queue(struct gve_priv *priv, u32 queue_index)
|
|
{
|
|
union gve_adminq_command cmd;
|
|
|
|
gve_adminq_make_destroy_rx_queue_cmd(&cmd, queue_index);
|
|
return gve_adminq_issue_cmd(priv, &cmd);
|
|
}
|
|
|
|
/* Unlike gve_adminq_destroy_rx_queue, this actually rings the doorbell */
|
|
int gve_adminq_destroy_single_rx_queue(struct gve_priv *priv, u32 queue_index)
|
|
{
|
|
union gve_adminq_command cmd;
|
|
|
|
gve_adminq_make_destroy_rx_queue_cmd(&cmd, queue_index);
|
|
return gve_adminq_execute_cmd(priv, &cmd);
|
|
}
|
|
|
|
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)
|
|
goto out;
|
|
}
|
|
|
|
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,
|
|
const struct gve_device_descriptor *descriptor)
|
|
{
|
|
priv->tx_desc_cnt = be16_to_cpu(descriptor->tx_queue_entries);
|
|
priv->rx_desc_cnt = be16_to_cpu(descriptor->rx_queue_entries);
|
|
|
|
/* set default ranges */
|
|
priv->max_tx_desc_cnt = priv->tx_desc_cnt;
|
|
priv->max_rx_desc_cnt = priv->rx_desc_cnt;
|
|
priv->min_tx_desc_cnt = priv->tx_desc_cnt;
|
|
priv->min_rx_desc_cnt = priv->rx_desc_cnt;
|
|
}
|
|
|
|
static void gve_set_default_rss_sizes(struct gve_priv *priv)
|
|
{
|
|
if (!gve_is_gqi(priv)) {
|
|
priv->rss_key_size = GVE_RSS_KEY_SIZE;
|
|
priv->rss_lut_size = GVE_RSS_INDIR_SIZE;
|
|
priv->cache_rss_config = true;
|
|
}
|
|
}
|
|
|
|
static void gve_enable_supported_features(struct gve_priv *priv,
|
|
u32 supported_features_mask,
|
|
const struct gve_device_option_jumbo_frames
|
|
*dev_op_jumbo_frames,
|
|
const struct gve_device_option_dqo_qpl
|
|
*dev_op_dqo_qpl,
|
|
const struct gve_device_option_buffer_sizes
|
|
*dev_op_buffer_sizes,
|
|
const struct gve_device_option_flow_steering
|
|
*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)
|
|
{
|
|
/* Before control reaches this point, the page-size-capped max MTU from
|
|
* the gve_device_descriptor field has already been stored in
|
|
* priv->dev->max_mtu. We overwrite it with the true max MTU below.
|
|
*/
|
|
if (dev_op_jumbo_frames &&
|
|
(supported_features_mask & GVE_SUP_JUMBO_FRAMES_MASK)) {
|
|
dev_info(&priv->pdev->dev,
|
|
"JUMBO FRAMES device option enabled.\n");
|
|
priv->dev->max_mtu = be16_to_cpu(dev_op_jumbo_frames->max_mtu);
|
|
}
|
|
|
|
/* Override pages for qpl for DQO-QPL */
|
|
if (dev_op_dqo_qpl) {
|
|
priv->tx_pages_per_qpl =
|
|
be16_to_cpu(dev_op_dqo_qpl->tx_pages_per_qpl);
|
|
if (priv->tx_pages_per_qpl == 0)
|
|
priv->tx_pages_per_qpl = DQO_QPL_DEFAULT_TX_PAGES;
|
|
}
|
|
|
|
if (dev_op_buffer_sizes &&
|
|
(supported_features_mask & GVE_SUP_BUFFER_SIZES_MASK)) {
|
|
priv->max_rx_buffer_size =
|
|
be16_to_cpu(dev_op_buffer_sizes->packet_buffer_size);
|
|
priv->header_buf_size =
|
|
be16_to_cpu(dev_op_buffer_sizes->header_buffer_size);
|
|
dev_info(&priv->pdev->dev,
|
|
"BUFFER SIZES device option enabled with max_rx_buffer_size of %u, header_buf_size of %u.\n",
|
|
priv->max_rx_buffer_size, priv->header_buf_size);
|
|
}
|
|
|
|
/* Read and store ring size ranges given by device */
|
|
if (dev_op_modify_ring &&
|
|
(supported_features_mask & GVE_SUP_MODIFY_RING_MASK)) {
|
|
priv->modify_ring_size_enabled = true;
|
|
|
|
/* max ring size for DQO QPL should not be overwritten because of device limit */
|
|
if (priv->queue_format != GVE_DQO_QPL_FORMAT) {
|
|
priv->max_rx_desc_cnt = be16_to_cpu(dev_op_modify_ring->max_rx_ring_size);
|
|
priv->max_tx_desc_cnt = be16_to_cpu(dev_op_modify_ring->max_tx_ring_size);
|
|
}
|
|
if (priv->default_min_ring_size) {
|
|
/* If device hasn't provided minimums, use default minimums */
|
|
priv->min_tx_desc_cnt = GVE_DEFAULT_MIN_TX_RING_SIZE;
|
|
priv->min_rx_desc_cnt = GVE_DEFAULT_MIN_RX_RING_SIZE;
|
|
} else {
|
|
priv->min_rx_desc_cnt = be16_to_cpu(dev_op_modify_ring->min_rx_ring_size);
|
|
priv->min_tx_desc_cnt = be16_to_cpu(dev_op_modify_ring->min_tx_ring_size);
|
|
}
|
|
}
|
|
|
|
if (dev_op_flow_steering &&
|
|
(supported_features_mask & GVE_SUP_FLOW_STEERING_MASK)) {
|
|
if (dev_op_flow_steering->max_flow_rules) {
|
|
priv->max_flow_rules =
|
|
be32_to_cpu(dev_op_flow_steering->max_flow_rules);
|
|
priv->dev->hw_features |= NETIF_F_NTUPLE;
|
|
dev_info(&priv->pdev->dev,
|
|
"FLOW STEERING device option enabled with max rule limit of %u.\n",
|
|
priv->max_flow_rules);
|
|
}
|
|
}
|
|
|
|
if (dev_op_rss_config &&
|
|
(supported_features_mask & GVE_SUP_RSS_CONFIG_MASK)) {
|
|
priv->rss_key_size =
|
|
be16_to_cpu(dev_op_rss_config->hash_key_size);
|
|
priv->rss_lut_size =
|
|
be16_to_cpu(dev_op_rss_config->hash_lut_size);
|
|
priv->cache_rss_config = false;
|
|
dev_dbg(&priv->pdev->dev,
|
|
"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;
|
|
struct gve_device_option_modify_ring *dev_op_modify_ring = NULL;
|
|
struct gve_device_option_rss_config *dev_op_rss_config = NULL;
|
|
struct gve_device_option_gqi_rda *dev_op_gqi_rda = NULL;
|
|
struct gve_device_option_gqi_qpl *dev_op_gqi_qpl = NULL;
|
|
struct gve_device_option_dqo_rda *dev_op_dqo_rda = NULL;
|
|
struct gve_device_option_dqo_qpl *dev_op_dqo_qpl = NULL;
|
|
struct gve_device_descriptor *descriptor;
|
|
u32 supported_features_mask = 0;
|
|
union gve_adminq_command cmd;
|
|
dma_addr_t descriptor_bus;
|
|
int err = 0;
|
|
u8 *mac;
|
|
u16 mtu;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
descriptor = dma_pool_alloc(priv->adminq_pool, GFP_KERNEL,
|
|
&descriptor_bus);
|
|
if (!descriptor)
|
|
return -ENOMEM;
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_DESCRIBE_DEVICE);
|
|
cmd.describe_device.device_descriptor_addr =
|
|
cpu_to_be64(descriptor_bus);
|
|
cmd.describe_device.device_descriptor_version =
|
|
cpu_to_be32(GVE_ADMINQ_DEVICE_DESCRIPTOR_VERSION);
|
|
cmd.describe_device.available_length =
|
|
cpu_to_be32(GVE_ADMINQ_BUFFER_SIZE);
|
|
|
|
err = gve_adminq_execute_cmd(priv, &cmd);
|
|
if (err)
|
|
goto free_device_descriptor;
|
|
|
|
err = gve_process_device_options(priv, descriptor, &dev_op_gqi_rda,
|
|
&dev_op_gqi_qpl, &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);
|
|
if (err)
|
|
goto free_device_descriptor;
|
|
|
|
/* If the GQI_RAW_ADDRESSING option is not enabled and the queue format
|
|
* is not set to GqiRda, choose the queue format in a priority order:
|
|
* DqoRda, DqoQpl, GqiRda, GqiQpl. Use GqiQpl as default.
|
|
*/
|
|
if (dev_op_dqo_rda) {
|
|
priv->queue_format = GVE_DQO_RDA_FORMAT;
|
|
dev_info(&priv->pdev->dev,
|
|
"Driver is running with DQO RDA queue format.\n");
|
|
supported_features_mask =
|
|
be32_to_cpu(dev_op_dqo_rda->supported_features_mask);
|
|
} else if (dev_op_dqo_qpl) {
|
|
priv->queue_format = GVE_DQO_QPL_FORMAT;
|
|
supported_features_mask =
|
|
be32_to_cpu(dev_op_dqo_qpl->supported_features_mask);
|
|
} else if (dev_op_gqi_rda) {
|
|
priv->queue_format = GVE_GQI_RDA_FORMAT;
|
|
dev_info(&priv->pdev->dev,
|
|
"Driver is running with GQI RDA queue format.\n");
|
|
supported_features_mask =
|
|
be32_to_cpu(dev_op_gqi_rda->supported_features_mask);
|
|
} else if (priv->queue_format == GVE_GQI_RDA_FORMAT) {
|
|
dev_info(&priv->pdev->dev,
|
|
"Driver is running with GQI RDA queue format.\n");
|
|
} else {
|
|
priv->queue_format = GVE_GQI_QPL_FORMAT;
|
|
if (dev_op_gqi_qpl)
|
|
supported_features_mask =
|
|
be32_to_cpu(dev_op_gqi_qpl->supported_features_mask);
|
|
dev_info(&priv->pdev->dev,
|
|
"Driver is running with GQI QPL queue format.\n");
|
|
}
|
|
|
|
/* set default descriptor counts */
|
|
gve_set_default_desc_cnt(priv, descriptor);
|
|
|
|
gve_set_default_rss_sizes(priv);
|
|
|
|
/* DQO supports LRO. */
|
|
if (!gve_is_gqi(priv))
|
|
priv->dev->hw_features |= NETIF_F_LRO;
|
|
|
|
priv->max_registered_pages =
|
|
be64_to_cpu(descriptor->max_registered_pages);
|
|
mtu = be16_to_cpu(descriptor->mtu);
|
|
if (mtu < ETH_MIN_MTU) {
|
|
dev_err(&priv->pdev->dev, "MTU %d below minimum MTU\n", mtu);
|
|
err = -EINVAL;
|
|
goto free_device_descriptor;
|
|
}
|
|
priv->dev->max_mtu = mtu;
|
|
priv->num_event_counters = be16_to_cpu(descriptor->counters);
|
|
eth_hw_addr_set(priv->dev, descriptor->mac);
|
|
mac = descriptor->mac;
|
|
dev_info(&priv->pdev->dev, "MAC addr: %pM\n", mac);
|
|
priv->tx_pages_per_qpl = be16_to_cpu(descriptor->tx_pages_per_qpl);
|
|
priv->default_num_queues = be16_to_cpu(descriptor->default_num_queues);
|
|
|
|
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_nic_timestamp,
|
|
dev_op_modify_ring);
|
|
|
|
free_device_descriptor:
|
|
dma_pool_free(priv->adminq_pool, descriptor, descriptor_bus);
|
|
return err;
|
|
}
|
|
|
|
int gve_adminq_register_page_list(struct gve_priv *priv,
|
|
struct gve_queue_page_list *qpl)
|
|
{
|
|
struct device *hdev = &priv->pdev->dev;
|
|
u32 num_entries = qpl->num_entries;
|
|
u32 size = num_entries * sizeof(qpl->page_buses[0]);
|
|
union gve_adminq_command cmd;
|
|
dma_addr_t page_list_bus;
|
|
__be64 *page_list;
|
|
int err;
|
|
int i;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
page_list = dma_alloc_coherent(hdev, size, &page_list_bus, GFP_KERNEL);
|
|
if (!page_list)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < num_entries; i++)
|
|
page_list[i] = cpu_to_be64(qpl->page_buses[i]);
|
|
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_REGISTER_PAGE_LIST);
|
|
cmd.reg_page_list = (struct gve_adminq_register_page_list) {
|
|
.page_list_id = cpu_to_be32(qpl->id),
|
|
.num_pages = cpu_to_be32(num_entries),
|
|
.page_address_list_addr = cpu_to_be64(page_list_bus),
|
|
.page_size = cpu_to_be64(PAGE_SIZE),
|
|
};
|
|
|
|
err = gve_adminq_execute_cmd(priv, &cmd);
|
|
dma_free_coherent(hdev, size, page_list, page_list_bus);
|
|
return err;
|
|
}
|
|
|
|
int gve_adminq_unregister_page_list(struct gve_priv *priv, u32 page_list_id)
|
|
{
|
|
union gve_adminq_command cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_UNREGISTER_PAGE_LIST);
|
|
cmd.unreg_page_list = (struct gve_adminq_unregister_page_list) {
|
|
.page_list_id = cpu_to_be32(page_list_id),
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
int gve_adminq_verify_driver_compatibility(struct gve_priv *priv,
|
|
u64 driver_info_len,
|
|
dma_addr_t driver_info_addr)
|
|
{
|
|
union gve_adminq_command cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_VERIFY_DRIVER_COMPATIBILITY);
|
|
cmd.verify_driver_compatibility = (struct gve_adminq_verify_driver_compatibility) {
|
|
.driver_info_len = cpu_to_be64(driver_info_len),
|
|
.driver_info_addr = cpu_to_be64(driver_info_addr),
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct gve_ptype_map *ptype_map;
|
|
union gve_adminq_command cmd;
|
|
dma_addr_t ptype_map_bus;
|
|
int err = 0;
|
|
int i;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
ptype_map = dma_alloc_coherent(&priv->pdev->dev, sizeof(*ptype_map),
|
|
&ptype_map_bus, GFP_KERNEL);
|
|
if (!ptype_map)
|
|
return -ENOMEM;
|
|
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_GET_PTYPE_MAP);
|
|
cmd.get_ptype_map = (struct gve_adminq_get_ptype_map) {
|
|
.ptype_map_len = cpu_to_be64(sizeof(*ptype_map)),
|
|
.ptype_map_addr = cpu_to_be64(ptype_map_bus),
|
|
};
|
|
|
|
err = gve_adminq_execute_cmd(priv, &cmd);
|
|
if (err)
|
|
goto err;
|
|
|
|
/* Populate ptype_lut. */
|
|
for (i = 0; i < GVE_NUM_PTYPES; i++) {
|
|
ptype_lut->ptypes[i].l3_type =
|
|
ptype_map->ptypes[i].l3_type;
|
|
ptype_lut->ptypes[i].l4_type =
|
|
ptype_map->ptypes[i].l4_type;
|
|
}
|
|
err:
|
|
dma_free_coherent(&priv->pdev->dev, sizeof(*ptype_map), ptype_map,
|
|
ptype_map_bus);
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
gve_adminq_configure_flow_rule(struct gve_priv *priv,
|
|
struct gve_adminq_configure_flow_rule *flow_rule_cmd)
|
|
{
|
|
int err = gve_adminq_execute_extended_cmd(priv,
|
|
GVE_ADMINQ_CONFIGURE_FLOW_RULE,
|
|
sizeof(struct gve_adminq_configure_flow_rule),
|
|
flow_rule_cmd);
|
|
|
|
if (err == -ETIME) {
|
|
dev_err(&priv->pdev->dev, "Timeout to configure the flow rule, trigger reset");
|
|
gve_reset(priv, true);
|
|
} else if (!err) {
|
|
priv->flow_rules_cache.rules_cache_synced = false;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int gve_adminq_add_flow_rule(struct gve_priv *priv, struct gve_adminq_flow_rule *rule, u32 loc)
|
|
{
|
|
struct gve_adminq_configure_flow_rule flow_rule_cmd = {
|
|
.opcode = cpu_to_be16(GVE_FLOW_RULE_CFG_ADD),
|
|
.location = cpu_to_be32(loc),
|
|
.rule = *rule,
|
|
};
|
|
|
|
return gve_adminq_configure_flow_rule(priv, &flow_rule_cmd);
|
|
}
|
|
|
|
int gve_adminq_del_flow_rule(struct gve_priv *priv, u32 loc)
|
|
{
|
|
struct gve_adminq_configure_flow_rule flow_rule_cmd = {
|
|
.opcode = cpu_to_be16(GVE_FLOW_RULE_CFG_DEL),
|
|
.location = cpu_to_be32(loc),
|
|
};
|
|
|
|
return gve_adminq_configure_flow_rule(priv, &flow_rule_cmd);
|
|
}
|
|
|
|
int gve_adminq_reset_flow_rules(struct gve_priv *priv)
|
|
{
|
|
struct gve_adminq_configure_flow_rule flow_rule_cmd = {
|
|
.opcode = cpu_to_be16(GVE_FLOW_RULE_CFG_RESET),
|
|
};
|
|
|
|
return gve_adminq_configure_flow_rule(priv, &flow_rule_cmd);
|
|
}
|
|
|
|
int gve_adminq_configure_rss(struct gve_priv *priv, struct ethtool_rxfh_param *rxfh)
|
|
{
|
|
const u32 *hash_lut_to_config = NULL;
|
|
const u8 *hash_key_to_config = NULL;
|
|
dma_addr_t lut_bus = 0, key_bus = 0;
|
|
union gve_adminq_command cmd;
|
|
__be32 *lut = NULL;
|
|
u8 hash_alg = 0;
|
|
u8 *key = NULL;
|
|
int err = 0;
|
|
u16 i;
|
|
|
|
switch (rxfh->hfunc) {
|
|
case ETH_RSS_HASH_NO_CHANGE:
|
|
fallthrough;
|
|
case ETH_RSS_HASH_TOP:
|
|
hash_alg = ETH_RSS_HASH_TOP;
|
|
break;
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
if (rxfh->indir) {
|
|
if (rxfh->indir_size != priv->rss_lut_size)
|
|
return -EINVAL;
|
|
|
|
hash_lut_to_config = rxfh->indir;
|
|
} else if (priv->cache_rss_config) {
|
|
hash_lut_to_config = priv->rss_config.hash_lut;
|
|
}
|
|
|
|
if (hash_lut_to_config) {
|
|
lut = dma_alloc_coherent(&priv->pdev->dev,
|
|
priv->rss_lut_size * sizeof(*lut),
|
|
&lut_bus, GFP_KERNEL);
|
|
if (!lut)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < priv->rss_lut_size; i++)
|
|
lut[i] = cpu_to_be32(hash_lut_to_config[i]);
|
|
}
|
|
|
|
if (rxfh->key) {
|
|
if (rxfh->key_size != priv->rss_key_size) {
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
hash_key_to_config = rxfh->key;
|
|
} else if (priv->cache_rss_config) {
|
|
hash_key_to_config = priv->rss_config.hash_key;
|
|
}
|
|
|
|
if (hash_key_to_config) {
|
|
key = dma_alloc_coherent(&priv->pdev->dev,
|
|
priv->rss_key_size,
|
|
&key_bus, GFP_KERNEL);
|
|
if (!key) {
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
memcpy(key, hash_key_to_config, priv->rss_key_size);
|
|
}
|
|
|
|
/* Zero-valued fields in the cmd.configure_rss instruct the device to
|
|
* not update those fields.
|
|
*/
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_CONFIGURE_RSS);
|
|
cmd.configure_rss = (struct gve_adminq_configure_rss) {
|
|
.hash_types = cpu_to_be16(BIT(GVE_RSS_HASH_TCPV4) |
|
|
BIT(GVE_RSS_HASH_UDPV4) |
|
|
BIT(GVE_RSS_HASH_TCPV6) |
|
|
BIT(GVE_RSS_HASH_UDPV6)),
|
|
.hash_alg = hash_alg,
|
|
.hash_key_size =
|
|
cpu_to_be16((key_bus) ? priv->rss_key_size : 0),
|
|
.hash_lut_size =
|
|
cpu_to_be16((lut_bus) ? priv->rss_lut_size : 0),
|
|
.hash_key_addr = cpu_to_be64(key_bus),
|
|
.hash_lut_addr = cpu_to_be64(lut_bus),
|
|
};
|
|
|
|
err = gve_adminq_execute_cmd(priv, &cmd);
|
|
|
|
out:
|
|
if (lut)
|
|
dma_free_coherent(&priv->pdev->dev,
|
|
priv->rss_lut_size * sizeof(*lut),
|
|
lut, lut_bus);
|
|
if (key)
|
|
dma_free_coherent(&priv->pdev->dev,
|
|
priv->rss_key_size, key, key_bus);
|
|
return err;
|
|
}
|
|
|
|
/* In the dma memory that the driver allocated for the device to query the flow rules, the device
|
|
* will first write it with a struct of gve_query_flow_rules_descriptor. Next to it, the device
|
|
* will write an array of rules or rule ids with the count that specified in the descriptor.
|
|
* For GVE_FLOW_RULE_QUERY_STATS, the device will only write the descriptor.
|
|
*/
|
|
static int gve_adminq_process_flow_rules_query(struct gve_priv *priv, u16 query_opcode,
|
|
struct gve_query_flow_rules_descriptor *descriptor)
|
|
{
|
|
struct gve_flow_rules_cache *flow_rules_cache = &priv->flow_rules_cache;
|
|
u32 num_queried_rules, total_memory_len, rule_info_len;
|
|
void *rule_info;
|
|
|
|
total_memory_len = be32_to_cpu(descriptor->total_length);
|
|
num_queried_rules = be32_to_cpu(descriptor->num_queried_rules);
|
|
rule_info = (void *)(descriptor + 1);
|
|
|
|
switch (query_opcode) {
|
|
case GVE_FLOW_RULE_QUERY_RULES:
|
|
rule_info_len = num_queried_rules * sizeof(*flow_rules_cache->rules_cache);
|
|
if (sizeof(*descriptor) + rule_info_len != total_memory_len) {
|
|
dev_err(&priv->dev->dev, "flow rules query is out of memory.\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy(flow_rules_cache->rules_cache, rule_info, rule_info_len);
|
|
flow_rules_cache->rules_cache_num = num_queried_rules;
|
|
break;
|
|
case GVE_FLOW_RULE_QUERY_IDS:
|
|
rule_info_len = num_queried_rules * sizeof(*flow_rules_cache->rule_ids_cache);
|
|
if (sizeof(*descriptor) + rule_info_len != total_memory_len) {
|
|
dev_err(&priv->dev->dev, "flow rule ids query is out of memory.\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy(flow_rules_cache->rule_ids_cache, rule_info, rule_info_len);
|
|
flow_rules_cache->rule_ids_cache_num = num_queried_rules;
|
|
break;
|
|
case GVE_FLOW_RULE_QUERY_STATS:
|
|
priv->num_flow_rules = be32_to_cpu(descriptor->num_flow_rules);
|
|
priv->max_flow_rules = be32_to_cpu(descriptor->max_flow_rules);
|
|
return 0;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gve_adminq_query_flow_rules(struct gve_priv *priv, u16 query_opcode, u32 starting_loc)
|
|
{
|
|
struct gve_query_flow_rules_descriptor *descriptor;
|
|
union gve_adminq_command cmd;
|
|
dma_addr_t descriptor_bus;
|
|
int err = 0;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
descriptor = dma_pool_alloc(priv->adminq_pool, GFP_KERNEL, &descriptor_bus);
|
|
if (!descriptor)
|
|
return -ENOMEM;
|
|
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_QUERY_FLOW_RULES);
|
|
cmd.query_flow_rules = (struct gve_adminq_query_flow_rules) {
|
|
.opcode = cpu_to_be16(query_opcode),
|
|
.starting_rule_id = cpu_to_be32(starting_loc),
|
|
.available_length = cpu_to_be64(GVE_ADMINQ_BUFFER_SIZE),
|
|
.rule_descriptor_addr = cpu_to_be64(descriptor_bus),
|
|
};
|
|
err = gve_adminq_execute_cmd(priv, &cmd);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = gve_adminq_process_flow_rules_query(priv, query_opcode, descriptor);
|
|
|
|
out:
|
|
dma_pool_free(priv->adminq_pool, descriptor, descriptor_bus);
|
|
return err;
|
|
}
|
|
|
|
static int gve_adminq_process_rss_query(struct gve_priv *priv,
|
|
struct gve_query_rss_descriptor *descriptor,
|
|
struct ethtool_rxfh_param *rxfh)
|
|
{
|
|
u32 total_memory_length;
|
|
u16 hash_lut_length;
|
|
void *rss_info_addr;
|
|
__be32 *lut;
|
|
u16 i;
|
|
|
|
total_memory_length = be32_to_cpu(descriptor->total_length);
|
|
hash_lut_length = priv->rss_lut_size * sizeof(*rxfh->indir);
|
|
|
|
if (sizeof(*descriptor) + priv->rss_key_size + hash_lut_length != total_memory_length) {
|
|
dev_err(&priv->dev->dev,
|
|
"rss query desc from device has invalid length parameter.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rxfh->hfunc = descriptor->hash_alg;
|
|
|
|
rss_info_addr = (void *)(descriptor + 1);
|
|
if (rxfh->key) {
|
|
rxfh->key_size = priv->rss_key_size;
|
|
memcpy(rxfh->key, rss_info_addr, priv->rss_key_size);
|
|
}
|
|
|
|
rss_info_addr += priv->rss_key_size;
|
|
lut = (__be32 *)rss_info_addr;
|
|
if (rxfh->indir) {
|
|
rxfh->indir_size = priv->rss_lut_size;
|
|
for (i = 0; i < priv->rss_lut_size; i++)
|
|
rxfh->indir[i] = be32_to_cpu(lut[i]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gve_adminq_query_rss_config(struct gve_priv *priv, struct ethtool_rxfh_param *rxfh)
|
|
{
|
|
struct gve_query_rss_descriptor *descriptor;
|
|
union gve_adminq_command cmd;
|
|
dma_addr_t descriptor_bus;
|
|
int err = 0;
|
|
|
|
descriptor = dma_pool_alloc(priv->adminq_pool, GFP_KERNEL, &descriptor_bus);
|
|
if (!descriptor)
|
|
return -ENOMEM;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.opcode = cpu_to_be32(GVE_ADMINQ_QUERY_RSS);
|
|
cmd.query_rss = (struct gve_adminq_query_rss) {
|
|
.available_length = cpu_to_be64(GVE_ADMINQ_BUFFER_SIZE),
|
|
.rss_descriptor_addr = cpu_to_be64(descriptor_bus),
|
|
};
|
|
err = gve_adminq_execute_cmd(priv, &cmd);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = gve_adminq_process_rss_query(priv, descriptor, rxfh);
|
|
|
|
out:
|
|
dma_pool_free(priv->adminq_pool, descriptor, descriptor_bus);
|
|
return err;
|
|
}
|