gve: Add a jumbo-frame device option.
authorShailend Chand <shailend@google.com>
Sun, 24 Oct 2021 18:42:38 +0000 (11:42 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 25 Oct 2021 13:13:12 +0000 (14:13 +0100)
A widely deployed driver has a bug that will cause the driver not
to load when a max_mtu > 2048 is present in the device descriptor.

To avoid this bug while still enabling jumbo frames, we present a lower
max_mtu in the device descriptor and pass the actual max_mtu in
a separate device option.

The driver supports 2 different queue formats. To enable features
on one queue format, but not the other, a supported_features mask
was added to the device options in the device descriptor.

Signed-off-by: Shailend Chand <shailend@google.com>
Signed-off-by: Jeroen de Borst <jeroendb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/google/gve/gve_adminq.c
drivers/net/ethernet/google/gve/gve_adminq.h

index 3dfda6d..83ae56c 100644 (file)
@@ -38,7 +38,8 @@ void gve_parse_device_option(struct gve_priv *priv,
                             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_dqo_rda **dev_op_dqo_rda,
+                            struct gve_device_option_jumbo_frames **dev_op_jumbo_frames)
 {
        u32 req_feat_mask = be32_to_cpu(option->required_features_mask);
        u16 option_length = be16_to_cpu(option->option_length);
@@ -111,6 +112,24 @@ void gve_parse_device_option(struct gve_priv *priv,
                }
                *dev_op_dqo_rda = (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;
        default:
                /* If we don't recognize the option just continue
                 * without doing anything.
@@ -126,7 +145,8 @@ 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_dqo_rda **dev_op_dqo_rda,
+                          struct gve_device_option_jumbo_frames **dev_op_jumbo_frames)
 {
        const int num_options = be16_to_cpu(descriptor->num_device_options);
        struct gve_device_option *dev_opt;
@@ -146,7 +166,7 @@ gve_process_device_options(struct gve_priv *priv,
 
                gve_parse_device_option(priv, descriptor, dev_opt,
                                        dev_op_gqi_rda, dev_op_gqi_qpl,
-                                       dev_op_dqo_rda);
+                                       dev_op_dqo_rda, dev_op_jumbo_frames);
                dev_opt = next_opt;
        }
 
@@ -661,12 +681,31 @@ gve_set_desc_cnt_dqo(struct gve_priv *priv,
        return 0;
 }
 
+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)
+{
+       /* 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);
+       }
+}
+
 int gve_adminq_describe_device(struct gve_priv *priv)
 {
+       struct gve_device_option_jumbo_frames *dev_op_jumbo_frames = 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_descriptor *descriptor;
+       u32 supported_features_mask = 0;
        union gve_adminq_command cmd;
        dma_addr_t descriptor_bus;
        int err = 0;
@@ -690,7 +729,8 @@ int gve_adminq_describe_device(struct gve_priv *priv)
                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_gqi_qpl, &dev_op_dqo_rda,
+                                        &dev_op_jumbo_frames);
        if (err)
                goto free_device_descriptor;
 
@@ -705,12 +745,19 @@ int gve_adminq_describe_device(struct gve_priv *priv)
                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_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 {
                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");
        }
@@ -747,6 +794,9 @@ int gve_adminq_describe_device(struct gve_priv *priv)
        }
        priv->default_num_queues = be16_to_cpu(descriptor->default_num_queues);
 
+       gve_enable_supported_features(priv, supported_features_mask,
+                                     dev_op_jumbo_frames);
+
 free_device_descriptor:
        dma_free_coherent(&priv->pdev->dev, PAGE_SIZE, descriptor,
                          descriptor_bus);
index 3953f6f..83c0b40 100644 (file)
@@ -108,6 +108,14 @@ struct gve_device_option_dqo_rda {
 
 static_assert(sizeof(struct gve_device_option_dqo_rda) == 8);
 
+struct gve_device_option_jumbo_frames {
+       __be32 supported_features_mask;
+       __be16 max_mtu;
+       u8 padding[2];
+};
+
+static_assert(sizeof(struct gve_device_option_jumbo_frames) == 8);
+
 /* Terminology:
  *
  * RDA - Raw DMA Addressing - Buffers associated with SKBs are directly DMA
@@ -121,6 +129,7 @@ enum gve_dev_opt_id {
        GVE_DEV_OPT_ID_GQI_RDA = 0x2,
        GVE_DEV_OPT_ID_GQI_QPL = 0x3,
        GVE_DEV_OPT_ID_DQO_RDA = 0x4,
+       GVE_DEV_OPT_ID_JUMBO_FRAMES = 0x8,
 };
 
 enum gve_dev_opt_req_feat_mask {
@@ -128,6 +137,11 @@ enum gve_dev_opt_req_feat_mask {
        GVE_DEV_OPT_REQ_FEAT_MASK_GQI_RDA = 0x0,
        GVE_DEV_OPT_REQ_FEAT_MASK_GQI_QPL = 0x0,
        GVE_DEV_OPT_REQ_FEAT_MASK_DQO_RDA = 0x0,
+       GVE_DEV_OPT_REQ_FEAT_MASK_JUMBO_FRAMES = 0x0,
+};
+
+enum gve_sup_feature_mask {
+       GVE_SUP_JUMBO_FRAMES_MASK = 1 << 2,
 };
 
 #define GVE_DEV_OPT_LEN_GQI_RAW_ADDRESSING 0x0