| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/14 02:37 | flow | patch-triage |
3mArgs:null Results: {
"EnableConfigs": [
"NET_VENDOR_GOOGLE",
"GVE"
],
"FocusSymbols": [
"gve_reset",
"gve_add_flow_rule",
"gve_del_flow_rule",
"gve_adminq_init",
"gve_adminq_get_device_properties",
"gve_adminq_verify_driver_compatibility",
"gve_adminq_create_queues",
"gve_adminq_destroy_queues",
"gve_adminq_setup_mgmt_irq",
"gve_adminq_request_db_info",
"gve_adminq_set_num_ntfy_blks",
"gve_adminq_map_db_bar",
"gve_adminq_report_link_status",
"gve_adminq_get_ptype_map_dqo",
"gve_adminq_describe_device"
],
"KMSANReasoning": "The patch series refactors the initialization and teardown sequences of the `gve` driver, introducing `struct gve_device_info` to hold device properties and `struct gve_ctrl_ops` for control plane operations.\n\n1. No uninitialized memory is sent to user space or the device. All structures sent to the device (e.g., `struct gve_driver_info`, `union gve_adminq_command`) are either explicitly zeroed using `memset` or allocated using `dma_alloc_coherent` (which guarantees zeroed memory) before being populated.\n2. The patch does not introduce any new `copy_to_user`, `put_user`, netlink attributes, or ioctl output arguments.\n3. The refactored code does not introduce conditional logic or branching that depends on potentially uninitialized variables.\n4. The changes primarily involve reorganizing existing code and adding error handling, which do not expose uninitialized memory risks.\n\nAny potential bugs introduced by these changes (such as out-of-bounds accesses, use-after-free, or locking issues) would be caught by standard KASAN and LOCKDEP. A dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false,
"Reasoning": "The patch refactors the control plane operations of the Google Virtual Ethernet (gve) driver. It introduces `struct gve_ctrl_ops` and `struct gve_device_info` to abstract control plane operations and device information. This involves significant changes to driver initialization, teardown, reset, and admin queue operations. The gve driver is reachable in virtualized environments like GCE, which is a primary target for syzkaller. Therefore, these functional changes are worth fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/14 02:37 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit ef1a8b6afc61cfca9d272da37e710ba20b159c77\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Aug 14 02:37:30 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h\nindex c280ff35ee771..3a50e7f7a2e7a 100644\n--- a/drivers/net/ethernet/google/gve/gve.h\n+++ b/drivers/net/ethernet/google/gve/gve.h\n@@ -16,6 +16,8 @@\n #include \u003clinux/timer.h\u003e\n #include \u003clinux/ptp_clock_kernel.h\u003e\n #include \u003clinux/u64_stats_sync.h\u003e\n+#include \u003clinux/utsname.h\u003e\n+#include \u003clinux/version.h\u003e\n #include \u003cnet/page_pool/helpers.h\u003e\n #include \u003cnet/xdp.h\u003e\n \n@@ -674,6 +676,7 @@ struct gve_notify_block {\n \tstruct gve_tx_ring *tx; /* tx rings on this block */\n \tstruct gve_rx_ring *rx; /* rx rings on this block */\n \tu32 irq;\n+\tbool irq_requested;\n };\n \n /* Tracks allowed and current rx queue settings */\n@@ -797,6 +800,79 @@ struct gve_ptp {\n \tstruct gve_priv *priv;\n };\n \n+struct gve_device_info {\n+\tenum gve_queue_format queue_format;\n+\tu16 default_tx_queues;\n+\tu16 default_rx_queues;\n+\tu16 max_tx_queues;\n+\tu16 max_rx_queues;\n+\tu16 default_tx_ring_size;\n+\tu16 default_rx_ring_size;\n+\tu16 max_tx_ring_size;\n+\tu16 max_rx_ring_size;\n+\tu16 min_tx_ring_size;\n+\tu16 min_rx_ring_size;\n+\tu16 max_mtu;\n+\tu8 mac[ETH_ALEN];\n+\tu16 max_rx_buffer_size;\n+\tu16 header_buf_size;\n+\tu32 max_flow_rules;\n+\tu16 rss_key_size;\n+\tu16 rss_lut_size;\n+\tu16 tx_pages_per_qpl;\n+\tu16 num_event_counters;\n+\tu64 max_registered_pages;\n+\tbool default_min_ring_size;\n+\tbool nic_timestamp_supported;\n+\tbool modify_ring_size_enabled;\n+\tbool cache_rss_config;\n+};\n+\n+/**\n+ * struct gve_ctrl_ops - Control plane operations structure\n+ * @map_db_bar: Maps the doorbell BAR for the device and store in @priv.\n+ * @unmap_db_bar: Unmaps the doorbell BAR previously mapped by @map_db_bar.\n+ * @set_num_queues: Sets default and max TX/RX queues into allocation\n+ * structures stored in @priv to be used during initialization.\n+ * @set_num_ntfy_blks: Sets no. of vectors into @priv to be used during\n+ * initialization.\n+ * @request_db_info: Request and store doorbell information into @priv\n+ * @release_db_resources: Release device hold on DMA memory holding doorbell\n+ *\t\t\t info (AdminQ only)\n+ * @setup_mgmt_irq: Setup control plane IRQ\n+ * @teardown_mgmt_irq: Teardown control plane IRQ\n+ * @get_ptype_map: Learn packet type map from device and store it in @priv\n+ * @configure_rss: Set up default RSS configuration\n+ * @setup_stats_report: Set up DMA region for stats report (AdminQ only)\n+ * @reset_flow_rules: Flush all flow rules from device\n+ * @create_queues: Sends commands to the device to create TX/RX queues.\n+ * @destroy_queues: Sends commands to the device to destroy TX/RX queues.\n+ * @report_link_status: Set link status into @priv-\u003elink_up\n+ * @report_link_speed: Set link speed into @priv-\u003elink_speed\n+ */\n+struct gve_ctrl_ops {\n+\tint (*map_db_bar)(struct gve_priv *priv);\n+\tvoid (*unmap_db_bar)(struct gve_priv *priv);\n+\tvoid (*set_num_queues)(struct gve_priv *priv);\n+\tint (*set_num_ntfy_blks)(struct gve_priv *priv);\n+\tint (*request_db_info)(struct gve_priv *priv);\n+\tvoid (*release_db_resources)(struct gve_priv *priv);\n+\tint (*setup_mgmt_irq)(struct gve_priv *priv);\n+\tvoid (*teardown_mgmt_irq)(struct gve_priv *priv);\n+\tint (*get_ptype_map)(struct gve_priv *priv);\n+\tint (*configure_rss)(struct gve_priv *priv,\n+\t\t\t struct ethtool_rxfh_param *param);\n+\tint (*setup_stats_report)(struct gve_priv *priv,\n+\t\t\t\t u64 stats_report_len,\n+\t\t\t\t dma_addr_t stats_report_addr,\n+\t\t\t\t u64 interval_ms); /* AQ-specific */\n+\tint (*reset_flow_rules)(struct gve_priv *priv);\n+\tint (*create_queues)(struct gve_priv *priv);\n+\tint (*destroy_queues)(struct gve_priv *priv);\n+\tint (*report_link_status)(struct gve_priv *priv);\n+\tint (*report_link_speed)(struct gve_priv *priv);\n+};\n+\n struct gve_priv {\n \tstruct net_device *dev;\n \tstruct gve_tx_ring *tx; /* array of tx_cfg.num_queues */\n@@ -896,8 +972,10 @@ struct gve_priv {\n \n \t/* Gvnic device link speed from hypervisor. */\n \tu64 link_speed;\n+\tbool link_up;\n \tbool up_before_suspend; /* True if dev was up before suspend */\n \n+\tbool mgmt_irq_requested;\n \tstruct gve_ptype_lut *ptype_lut_dqo;\n \n \t/* Must be a power of two. */\n@@ -929,6 +1007,8 @@ struct gve_priv {\n \tstruct gve_nic_ts_report *nic_ts_report;\n \tdma_addr_t nic_ts_report_bus;\n \tu64 last_sync_nic_counter; /* Clock counter from last NIC TS report */\n+\tstruct gve_device_info device_info;\n+\tconst struct gve_ctrl_ops *ctrl_ops;\n };\n \n enum gve_service_task_flags_bit {\n@@ -1104,6 +1184,11 @@ static inline u32 gve_rx_idx_to_ntfy(struct gve_priv *priv, u32 queue_idx)\n \treturn (priv-\u003enum_ntfy_blks / 2) + queue_idx;\n }\n \n+static inline u32 gve_ntfy_to_msix_idx(struct gve_priv *priv, u32 ntfy_blk_idx)\n+{\n+\treturn ntfy_blk_idx;\n+}\n+\n static inline bool gve_is_qpl(struct gve_priv *priv)\n {\n \treturn priv-\u003equeue_format == GVE_GQI_QPL_FORMAT ||\n@@ -1207,6 +1292,8 @@ static inline bool gve_is_clock_enabled(struct gve_priv *priv)\n \treturn priv-\u003enic_ts_report;\n }\n \n+void gve_adminq_write_version(u8 __iomem *driver_version_register);\n+\n /* gqi napi handler defined in gve_main.c */\n int gve_napi_poll(struct napi_struct *napi, int budget);\n \n@@ -1300,7 +1387,7 @@ struct page_pool *gve_rx_create_page_pool(struct gve_priv *priv,\n \n /* Reset */\n void gve_schedule_reset(struct gve_priv *priv);\n-int gve_reset(struct gve_priv *priv, bool attempt_teardown);\n+int gve_reset(struct gve_priv *priv, bool skip_queue_setup);\n void gve_get_curr_alloc_cfgs(struct gve_priv *priv,\n \t\t\t struct gve_tx_alloc_rings_cfg *tx_alloc_cfg,\n \t\t\t struct gve_rx_alloc_rings_cfg *rx_alloc_cfg);\ndiff --git a/drivers/net/ethernet/google/gve/gve_adminq.c b/drivers/net/ethernet/google/gve/gve_adminq.c\nindex 08587bf40ed4a..bbf431794d02b 100644\n--- a/drivers/net/ethernet/google/gve/gve_adminq.c\n+++ b/drivers/net/ethernet/google/gve/gve_adminq.c\n@@ -70,7 +70,7 @@ void gve_parse_device_option(struct gve_priv *priv,\n \n \t\tdev_info(\u0026priv-\u003epdev-\u003edev,\n \t\t\t \"Gqi raw addressing device option enabled.\\n\");\n-\t\tpriv-\u003equeue_format = GVE_GQI_RDA_FORMAT;\n+\t\tpriv-\u003edevice_info.queue_format = GVE_GQI_RDA_FORMAT;\n \t\tbreak;\n \tcase GVE_DEV_OPT_ID_GQI_RDA:\n \t\tif (option_length \u003c sizeof(**dev_op_gqi_rda) ||\n@@ -190,7 +190,7 @@ void gve_parse_device_option(struct gve_priv *priv,\n \n \t\t/* device has not provided min ring size */\n \t\tif (option_length == GVE_DEVICE_OPTION_NO_MIN_RING_SIZE)\n-\t\t\tpriv-\u003edefault_min_ring_size = true;\n+\t\t\tpriv-\u003edevice_info.default_min_ring_size = true;\n \t\tbreak;\n \tcase GVE_DEV_OPT_ID_FLOW_STEERING:\n \t\tif (option_length \u003c sizeof(**dev_op_flow_steering) ||\n@@ -296,8 +296,10 @@ gve_process_device_options(struct gve_priv *priv,\n \treturn 0;\n }\n \n-int gve_adminq_alloc(struct device *dev, struct gve_priv *priv)\n+static int gve_adminq_alloc(struct gve_priv *priv)\n {\n+\tstruct device *dev = \u0026priv-\u003epdev-\u003edev;\n+\n \tpriv-\u003eadminq_pool = dma_pool_create(\"adminq_pool\", dev,\n \t\t\t\t\t GVE_ADMINQ_BUFFER_SIZE, 0, 0);\n \tif (unlikely(!priv-\u003eadminq_pool))\n@@ -353,7 +355,15 @@ int gve_adminq_alloc(struct device *dev, struct gve_priv *priv)\n \treturn 0;\n }\n \n-void gve_adminq_release(struct gve_priv *priv)\n+int gve_adminq_init(struct gve_priv *priv)\n+{\n+\tstruct gve_registers __iomem *reg_bar = priv-\u003ereg_bar0;\n+\n+\tgve_adminq_write_version(\u0026reg_bar-\u003edriver_version);\n+\treturn gve_adminq_alloc(priv);\n+}\n+\n+static void gve_adminq_release(struct gve_priv *priv)\n {\n \tint i = 0;\n \n@@ -382,10 +392,9 @@ void gve_adminq_release(struct gve_priv *priv)\n \t}\n \tgve_clear_device_rings_ok(priv);\n \tgve_clear_device_resources_ok(priv);\n-\tgve_clear_admin_queue_ok(priv);\n }\n \n-void gve_adminq_free(struct device *dev, struct gve_priv *priv)\n+void gve_adminq_free(struct gve_priv *priv)\n {\n \tif (!gve_get_admin_queue_ok(priv))\n \t\treturn;\n@@ -920,26 +929,40 @@ int gve_adminq_destroy_rx_queues(struct gve_priv *priv, u32 num_queues)\n \treturn err;\n }\n \n-static void gve_set_default_desc_cnt(struct gve_priv *priv,\n-\t\t\tconst struct gve_device_descriptor *descriptor)\n+static void gve_set_default_rss_sizes(struct gve_priv *priv)\n {\n-\tpriv-\u003etx_desc_cnt = be16_to_cpu(descriptor-\u003etx_queue_entries);\n-\tpriv-\u003erx_desc_cnt = be16_to_cpu(descriptor-\u003erx_queue_entries);\n+\tstruct gve_device_info *device_info = \u0026priv-\u003edevice_info;\n \n-\t/* set default ranges */\n-\tpriv-\u003emax_tx_desc_cnt = priv-\u003etx_desc_cnt;\n-\tpriv-\u003emax_rx_desc_cnt = priv-\u003erx_desc_cnt;\n-\tpriv-\u003emin_tx_desc_cnt = priv-\u003etx_desc_cnt;\n-\tpriv-\u003emin_rx_desc_cnt = priv-\u003erx_desc_cnt;\n+\tif (device_info-\u003equeue_format == GVE_DQO_RDA_FORMAT ||\n+\t device_info-\u003equeue_format == GVE_DQO_QPL_FORMAT) {\n+\t\tdevice_info-\u003erss_key_size = GVE_RSS_KEY_SIZE;\n+\t\tdevice_info-\u003erss_lut_size = GVE_RSS_INDIR_SIZE;\n+\t\tdevice_info-\u003ecache_rss_config = true;\n+\t}\n }\n \n-static void gve_set_default_rss_sizes(struct gve_priv *priv)\n+int gve_adminq_destroy_queues(struct gve_priv *priv)\n {\n-\tif (!gve_is_gqi(priv)) {\n-\t\tpriv-\u003erss_key_size = GVE_RSS_KEY_SIZE;\n-\t\tpriv-\u003erss_lut_size = GVE_RSS_INDIR_SIZE;\n-\t\tpriv-\u003ecache_rss_config = true;\n+\tint num_tx_queues = gve_num_tx_queues(priv);\n+\tint err;\n+\n+\terr = gve_adminq_destroy_tx_queues(priv, 0, num_tx_queues);\n+\tif (err) {\n+\t\tnetif_err(priv, drv, priv-\u003edev,\n+\t\t\t \"failed to destroy tx queues\\n\");\n+\t\t/* This failure will trigger a reset - no need to clean up */\n+\t\treturn err;\n \t}\n+\tnetif_dbg(priv, drv, priv-\u003edev, \"destroyed tx queues\\n\");\n+\terr = gve_adminq_destroy_rx_queues(priv, priv-\u003erx_cfg.num_queues);\n+\tif (err) {\n+\t\tnetif_err(priv, drv, priv-\u003edev,\n+\t\t\t \"failed to destroy rx queues\\n\");\n+\t\t/* This failure will trigger a reset - no need to clean up */\n+\t\treturn err;\n+\t}\n+\tnetif_dbg(priv, drv, priv-\u003edev, \"destroyed rx queues\\n\");\n+\treturn err;\n }\n \n static void gve_enable_supported_features(struct gve_priv *priv,\n@@ -959,77 +982,105 @@ static void gve_enable_supported_features(struct gve_priv *priv,\n \t\t\t\t\t const struct gve_device_option_modify_ring\n \t\t\t\t\t *dev_op_modify_ring)\n {\n+\tstruct gve_device_info *info = \u0026priv-\u003edevice_info;\n+\n \t/* Before control reaches this point, the page-size-capped max MTU from\n \t * the gve_device_descriptor field has already been stored in\n-\t * priv-\u003edev-\u003emax_mtu. We overwrite it with the true max MTU below.\n+\t * device_info-\u003emax_mtu. We overwrite it with the true max MTU below.\n \t */\n \tif (dev_op_jumbo_frames \u0026\u0026\n \t (supported_features_mask \u0026 GVE_SUP_JUMBO_FRAMES_MASK)) {\n \t\tdev_info(\u0026priv-\u003epdev-\u003edev,\n \t\t\t \"JUMBO FRAMES device option enabled.\\n\");\n-\t\tpriv-\u003edev-\u003emax_mtu = be16_to_cpu(dev_op_jumbo_frames-\u003emax_mtu);\n+\t\tinfo-\u003emax_mtu = be16_to_cpu(dev_op_jumbo_frames-\u003emax_mtu);\n \t}\n \n \tif (dev_op_buffer_sizes \u0026\u0026\n \t (supported_features_mask \u0026 GVE_SUP_BUFFER_SIZES_MASK)) {\n-\t\tpriv-\u003emax_rx_buffer_size =\n+\t\tinfo-\u003emax_rx_buffer_size =\n \t\t\tbe16_to_cpu(dev_op_buffer_sizes-\u003epacket_buffer_size);\n-\t\tpriv-\u003eheader_buf_size =\n+\t\tinfo-\u003eheader_buf_size =\n \t\t\tbe16_to_cpu(dev_op_buffer_sizes-\u003eheader_buffer_size);\n \t\tdev_info(\u0026priv-\u003epdev-\u003edev,\n \t\t\t \"BUFFER SIZES device option enabled with max_rx_buffer_size of %u, header_buf_size of %u.\\n\",\n-\t\t\t priv-\u003emax_rx_buffer_size, priv-\u003eheader_buf_size);\n-\t\tif (gve_is_dqo(priv) \u0026\u0026\n-\t\t priv-\u003emax_rx_buffer_size \u003e GVE_DEFAULT_RX_BUFFER_SIZE)\n-\t\t\tpriv-\u003erx_cfg.packet_buffer_size =\n-\t\t\t\tpriv-\u003emax_rx_buffer_size;\n+\t\t\t info-\u003emax_rx_buffer_size, info-\u003eheader_buf_size);\n \t}\n \n \t/* Read and store ring size ranges given by device */\n \tif (dev_op_modify_ring \u0026\u0026\n \t (supported_features_mask \u0026 GVE_SUP_MODIFY_RING_MASK)) {\n-\t\tpriv-\u003emodify_ring_size_enabled = true;\n-\t\tpriv-\u003emax_rx_desc_cnt =\n+\t\tinfo-\u003emodify_ring_size_enabled = true;\n+\t\tinfo-\u003emax_rx_ring_size =\n \t\t\tbe16_to_cpu(dev_op_modify_ring-\u003emax_rx_ring_size);\n-\t\tpriv-\u003emax_tx_desc_cnt =\n+\t\tinfo-\u003emax_tx_ring_size =\n \t\t\tbe16_to_cpu(dev_op_modify_ring-\u003emax_tx_ring_size);\n-\t\tif (priv-\u003edefault_min_ring_size) {\n+\t\tif (info-\u003edefault_min_ring_size) {\n \t\t\t/* If device hasn't provided minimums, use default minimums */\n-\t\t\tpriv-\u003emin_tx_desc_cnt = GVE_DEFAULT_MIN_TX_RING_SIZE;\n-\t\t\tpriv-\u003emin_rx_desc_cnt = GVE_DEFAULT_MIN_RX_RING_SIZE;\n+\t\t\tinfo-\u003emin_tx_ring_size = GVE_DEFAULT_MIN_TX_RING_SIZE;\n+\t\t\tinfo-\u003emin_rx_ring_size = GVE_DEFAULT_MIN_RX_RING_SIZE;\n \t\t} else {\n-\t\t\tpriv-\u003emin_rx_desc_cnt = be16_to_cpu(dev_op_modify_ring-\u003emin_rx_ring_size);\n-\t\t\tpriv-\u003emin_tx_desc_cnt = be16_to_cpu(dev_op_modify_ring-\u003emin_tx_ring_size);\n+\t\t\tinfo-\u003emin_rx_ring_size =\n+\t\t\t\tbe16_to_cpu(dev_op_modify_ring-\u003emin_rx_ring_size);\n+\t\t\tinfo-\u003emin_tx_ring_size =\n+\t\t\t\tbe16_to_cpu(dev_op_modify_ring-\u003emin_tx_ring_size);\n \t\t}\n \t}\n \n \tif (dev_op_flow_steering \u0026\u0026\n \t (supported_features_mask \u0026 GVE_SUP_FLOW_STEERING_MASK)) {\n \t\tif (dev_op_flow_steering-\u003emax_flow_rules) {\n-\t\t\tpriv-\u003emax_flow_rules =\n+\t\t\tinfo-\u003emax_flow_rules =\n \t\t\t\tbe32_to_cpu(dev_op_flow_steering-\u003emax_flow_rules);\n-\t\t\tpriv-\u003edev-\u003ehw_features |= NETIF_F_NTUPLE;\n \t\t\tdev_info(\u0026priv-\u003epdev-\u003edev,\n \t\t\t\t \"FLOW STEERING device option enabled with max rule limit of %u.\\n\",\n-\t\t\t\t priv-\u003emax_flow_rules);\n+\t\t\t\t info-\u003emax_flow_rules);\n \t\t}\n \t}\n \n \tif (dev_op_rss_config \u0026\u0026\n \t (supported_features_mask \u0026 GVE_SUP_RSS_CONFIG_MASK)) {\n-\t\tpriv-\u003erss_key_size =\n+\t\tinfo-\u003erss_key_size =\n \t\t\tbe16_to_cpu(dev_op_rss_config-\u003ehash_key_size);\n-\t\tpriv-\u003erss_lut_size =\n+\t\tinfo-\u003erss_lut_size =\n \t\t\tbe16_to_cpu(dev_op_rss_config-\u003ehash_lut_size);\n-\t\tpriv-\u003ecache_rss_config = false;\n+\t\tinfo-\u003ecache_rss_config = false;\n \t\tdev_dbg(\u0026priv-\u003epdev-\u003edev,\n \t\t\t\"RSS device option enabled with key size of %u, lut size of %u.\\n\",\n-\t\t\tpriv-\u003erss_key_size, priv-\u003erss_lut_size);\n+\t\t\tinfo-\u003erss_key_size, info-\u003erss_lut_size);\n \t}\n \n \tif (dev_op_nic_timestamp \u0026\u0026\n \t (supported_features_mask \u0026 GVE_SUP_NIC_TIMESTAMP_MASK))\n-\t\tpriv-\u003enic_timestamp_supported = true;\n+\t\tinfo-\u003enic_timestamp_supported = true;\n+}\n+\n+static void gve_fill_device_info(struct gve_priv *priv,\n+\t\t\t\t struct gve_device_descriptor *descriptor)\n+{\n+\tstruct gve_device_info *device_info = \u0026priv-\u003edevice_info;\n+\tu16 default_num_queues;\n+\n+\tdevice_info-\u003etx_pages_per_qpl =\n+\t\t\t\tbe16_to_cpu(descriptor-\u003etx_pages_per_qpl);\n+\tdevice_info-\u003emax_registered_pages =\n+\t\t\t\tbe64_to_cpu(descriptor-\u003emax_registered_pages);\n+\tdevice_info-\u003enum_event_counters = be16_to_cpu(descriptor-\u003ecounters);\n+\tether_addr_copy(device_info-\u003emac, descriptor-\u003emac);\n+\tdevice_info-\u003emax_mtu = be16_to_cpu(descriptor-\u003emtu);\n+\n+\tdefault_num_queues = be16_to_cpu(descriptor-\u003edefault_num_queues);\n+\tdevice_info-\u003edefault_tx_queues = default_num_queues;\n+\tdevice_info-\u003edefault_rx_queues = default_num_queues;\n+\tdevice_info-\u003edefault_tx_ring_size =\n+\t\t\t\tbe16_to_cpu(descriptor-\u003etx_queue_entries);\n+\tdevice_info-\u003edefault_rx_ring_size =\n+\t\t\t\tbe16_to_cpu(descriptor-\u003erx_queue_entries);\n+\n+\t/* set default ranges */\n+\tdevice_info-\u003emax_tx_ring_size = device_info-\u003edefault_tx_ring_size;\n+\tdevice_info-\u003emax_rx_ring_size = device_info-\u003edefault_rx_ring_size;\n+\tdevice_info-\u003emin_tx_ring_size = device_info-\u003edefault_tx_ring_size;\n+\tdevice_info-\u003emin_rx_ring_size = device_info-\u003edefault_rx_ring_size;\n }\n \n int gve_adminq_describe_device(struct gve_priv *priv)\n@@ -1040,6 +1091,7 @@ int gve_adminq_describe_device(struct gve_priv *priv)\n \tstruct gve_device_option_jumbo_frames *dev_op_jumbo_frames = NULL;\n \tstruct gve_device_option_modify_ring *dev_op_modify_ring = NULL;\n \tstruct gve_device_option_rss_config *dev_op_rss_config = NULL;\n+\tstruct gve_device_info *device_info = \u0026priv-\u003edevice_info;\n \tstruct gve_device_option_gqi_rda *dev_op_gqi_rda = NULL;\n \tstruct gve_device_option_gqi_qpl *dev_op_gqi_qpl = NULL;\n \tstruct gve_device_option_dqo_rda *dev_op_dqo_rda = NULL;\n@@ -1049,8 +1101,6 @@ int gve_adminq_describe_device(struct gve_priv *priv)\n \tunion gve_adminq_command cmd;\n \tdma_addr_t descriptor_bus;\n \tint err = 0;\n-\tu8 *mac;\n-\tu16 mtu;\n \n \tmemset(\u0026cmd, 0, sizeof(cmd));\n \tdescriptor = dma_pool_alloc(priv-\u003eadminq_pool, GFP_KERNEL,\n@@ -1085,26 +1135,26 @@ int gve_adminq_describe_device(struct gve_priv *priv)\n \t * DqoRda, DqoQpl, GqiRda, GqiQpl. Use GqiQpl as default.\n \t */\n \tif (dev_op_dqo_rda) {\n-\t\tpriv-\u003equeue_format = GVE_DQO_RDA_FORMAT;\n+\t\tdevice_info-\u003equeue_format = GVE_DQO_RDA_FORMAT;\n \t\tdev_info(\u0026priv-\u003epdev-\u003edev,\n \t\t\t \"Driver is running with DQO RDA queue format.\\n\");\n \t\tsupported_features_mask =\n \t\t\tbe32_to_cpu(dev_op_dqo_rda-\u003esupported_features_mask);\n \t} else if (dev_op_dqo_qpl) {\n-\t\tpriv-\u003equeue_format = GVE_DQO_QPL_FORMAT;\n+\t\tdevice_info-\u003equeue_format = GVE_DQO_QPL_FORMAT;\n \t\tsupported_features_mask =\n \t\t\tbe32_to_cpu(dev_op_dqo_qpl-\u003esupported_features_mask);\n \t} else if (dev_op_gqi_rda) {\n-\t\tpriv-\u003equeue_format = GVE_GQI_RDA_FORMAT;\n+\t\tdevice_info-\u003equeue_format = GVE_GQI_RDA_FORMAT;\n \t\tdev_info(\u0026priv-\u003epdev-\u003edev,\n \t\t\t \"Driver is running with GQI RDA queue format.\\n\");\n \t\tsupported_features_mask =\n \t\t\tbe32_to_cpu(dev_op_gqi_rda-\u003esupported_features_mask);\n-\t} else if (priv-\u003equeue_format == GVE_GQI_RDA_FORMAT) {\n+\t} else if (device_info-\u003equeue_format == GVE_GQI_RDA_FORMAT) {\n \t\tdev_info(\u0026priv-\u003epdev-\u003edev,\n \t\t\t \"Driver is running with GQI RDA queue format.\\n\");\n \t} else {\n-\t\tpriv-\u003equeue_format = GVE_GQI_QPL_FORMAT;\n+\t\tdevice_info-\u003equeue_format = GVE_GQI_QPL_FORMAT;\n \t\tif (dev_op_gqi_qpl)\n \t\t\tsupported_features_mask =\n \t\t\t\tbe32_to_cpu(dev_op_gqi_qpl-\u003esupported_features_mask);\n@@ -1112,35 +1162,9 @@ int gve_adminq_describe_device(struct gve_priv *priv)\n \t\t\t \"Driver is running with GQI QPL queue format.\\n\");\n \t}\n \n-\t/* set default descriptor counts */\n-\tgve_set_default_desc_cnt(priv, descriptor);\n-\n+\tgve_fill_device_info(priv, descriptor);\n \tgve_set_default_rss_sizes(priv);\n \n-\t/* DQO supports HW-GRO and UDP_GSO */\n-\tif (gve_is_dqo(priv)) {\n-\t\tu64 additional_features = NETIF_F_GRO_HW | NETIF_F_GSO_UDP_L4;\n-\n-\t\tpriv-\u003edev-\u003ehw_features |= additional_features;\n-\t\tpriv-\u003edev-\u003efeatures |= additional_features;\n-\t}\n-\n-\tpriv-\u003emax_registered_pages =\n-\t\t\t\tbe64_to_cpu(descriptor-\u003emax_registered_pages);\n-\tmtu = be16_to_cpu(descriptor-\u003emtu);\n-\tif (mtu \u003c ETH_MIN_MTU) {\n-\t\tdev_err(\u0026priv-\u003epdev-\u003edev, \"MTU %d below minimum MTU\\n\", mtu);\n-\t\terr = -EINVAL;\n-\t\tgoto free_device_descriptor;\n-\t}\n-\tpriv-\u003edev-\u003emax_mtu = mtu;\n-\tpriv-\u003enum_event_counters = be16_to_cpu(descriptor-\u003ecounters);\n-\teth_hw_addr_set(priv-\u003edev, descriptor-\u003emac);\n-\tmac = descriptor-\u003emac;\n-\tdev_info(\u0026priv-\u003epdev-\u003edev, \"MAC addr: %pM\\n\", mac);\n-\tpriv-\u003etx_pages_per_qpl = be16_to_cpu(descriptor-\u003etx_pages_per_qpl);\n-\tpriv-\u003edefault_num_queues = be16_to_cpu(descriptor-\u003edefault_num_queues);\n-\n \tgve_enable_supported_features(priv, supported_features_mask,\n \t\t\t\t dev_op_jumbo_frames, dev_op_dqo_qpl,\n \t\t\t\t dev_op_buffer_sizes, dev_op_flow_steering,\n@@ -1152,6 +1176,27 @@ int gve_adminq_describe_device(struct gve_priv *priv)\n \treturn err;\n }\n \n+int __maybe_unused gve_adminq_get_device_properties(struct gve_priv *priv)\n+{\n+\tint err;\n+\n+\terr = gve_adminq_verify_driver_compatibility(priv);\n+\tif (err) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Could not verify driver compatibility: err=%d\\n\", err);\n+\t\treturn err;\n+\t}\n+\n+\t/* Get the initial information we need from the device */\n+\terr = gve_adminq_describe_device(priv);\n+\tif (err) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Could not get device information: err=%d\\n\", err);\n+\t\treturn err;\n+\t}\n+\treturn 0;\n+}\n+\n int gve_adminq_register_page_list(struct gve_priv *priv,\n \t\t\t\t struct gve_queue_page_list *qpl)\n {\n@@ -1214,20 +1259,53 @@ int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len,\n \treturn gve_adminq_execute_cmd(priv, \u0026cmd);\n }\n \n-int gve_adminq_verify_driver_compatibility(struct gve_priv *priv,\n-\t\t\t\t\t u64 driver_info_len,\n-\t\t\t\t\t dma_addr_t driver_info_addr)\n+int gve_adminq_verify_driver_compatibility(struct gve_priv *priv)\n {\n+\tstruct gve_driver_info *driver_info;\n \tunion gve_adminq_command cmd;\n+\tdma_addr_t driver_info_bus;\n+\tint err;\n+\n+\tdriver_info = dma_alloc_coherent(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\t\t sizeof(struct gve_driver_info),\n+\t\t\t\t\t \u0026driver_info_bus, GFP_KERNEL);\n+\tif (!driver_info)\n+\t\treturn -ENOMEM;\n+\n+\t*driver_info = (struct gve_driver_info) {\n+\t\t.os_type = 1, /* Linux */\n+\t\t.os_version_major = cpu_to_be32(LINUX_VERSION_MAJOR),\n+\t\t.os_version_minor = cpu_to_be32(LINUX_VERSION_SUBLEVEL),\n+\t\t.os_version_sub = cpu_to_be32(LINUX_VERSION_PATCHLEVEL),\n+\t\t.driver_capability_flags = {\n+\t\t\tcpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS1),\n+\t\t\tcpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS2),\n+\t\t\tcpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS3),\n+\t\t\tcpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS4),\n+\t\t},\n+\t};\n+\tstrscpy(driver_info-\u003eos_version_str1, utsname()-\u003erelease,\n+\t\tsizeof(driver_info-\u003eos_version_str1));\n+\tstrscpy(driver_info-\u003eos_version_str2, utsname()-\u003eversion,\n+\t\tsizeof(driver_info-\u003eos_version_str2));\n \n \tmemset(\u0026cmd, 0, sizeof(cmd));\n \tcmd.opcode = cpu_to_be32(GVE_ADMINQ_VERIFY_DRIVER_COMPATIBILITY);\n \tcmd.verify_driver_compatibility = (struct gve_adminq_verify_driver_compatibility) {\n-\t\t.driver_info_len = cpu_to_be64(driver_info_len),\n-\t\t.driver_info_addr = cpu_to_be64(driver_info_addr),\n+\t\t.driver_info_len = cpu_to_be64(sizeof(struct gve_driver_info)),\n+\t\t.driver_info_addr = cpu_to_be64(driver_info_bus),\n \t};\n \n-\treturn gve_adminq_execute_cmd(priv, \u0026cmd);\n+\terr = gve_adminq_execute_cmd(priv, \u0026cmd);\n+\n+\t/* It's ok if the device doesn't support this */\n+\tif (err == -EOPNOTSUPP)\n+\t\terr = 0;\n+\n+\tdma_free_coherent(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t sizeof(struct gve_driver_info),\n+\t\t\t driver_info, driver_info_bus);\n+\treturn err;\n }\n \n int gve_adminq_report_link_speed(struct gve_priv *priv)\n@@ -1273,8 +1351,7 @@ int gve_adminq_report_nic_ts(struct gve_priv *priv,\n \treturn gve_adminq_execute_cmd(priv, \u0026cmd);\n }\n \n-int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv,\n-\t\t\t\t struct gve_ptype_lut *ptype_lut)\n+int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv)\n {\n \tstruct gve_ptype_map *ptype_map;\n \tunion gve_adminq_command cmd;\n@@ -1300,9 +1377,9 @@ int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv,\n \n \t/* Populate ptype_lut. */\n \tfor (i = 0; i \u003c GVE_NUM_PTYPES; i++) {\n-\t\tptype_lut-\u003eptypes[i].l3_type =\n+\t\tpriv-\u003eptype_lut_dqo-\u003eptypes[i].l3_type =\n \t\t\tptype_map-\u003eptypes[i].l3_type;\n-\t\tptype_lut-\u003eptypes[i].l4_type =\n+\t\tpriv-\u003eptype_lut_dqo-\u003eptypes[i].l4_type =\n \t\t\tptype_map-\u003eptypes[i].l4_type;\n \t}\n err:\n@@ -1320,12 +1397,8 @@ gve_adminq_configure_flow_rule(struct gve_priv *priv,\n \t\t\tsizeof(struct gve_adminq_configure_flow_rule),\n \t\t\tflow_rule_cmd);\n \n-\tif (err == -ETIME) {\n-\t\tdev_err(\u0026priv-\u003epdev-\u003edev, \"Timeout to configure the flow rule, trigger reset\");\n-\t\tgve_reset(priv, true);\n-\t} else if (!err) {\n+\tif (!err)\n \t\tpriv-\u003eflow_rules_cache.rules_cache_synced = false;\n-\t}\n \n \treturn err;\n }\n@@ -1600,3 +1673,174 @@ int gve_adminq_query_rss_config(struct gve_priv *priv, struct ethtool_rxfh_param\n \tdma_pool_free(priv-\u003eadminq_pool, descriptor, descriptor_bus);\n \treturn err;\n }\n+\n+int gve_adminq_set_num_ntfy_blks(struct gve_priv *priv)\n+{\n+\tint num_ntfy;\n+\n+\tnum_ntfy = pci_msix_vec_count(priv-\u003epdev);\n+\tif (num_ntfy \u003c= 0) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"could not count MSI-x vectors: err=%d\\n\", num_ntfy);\n+\t\treturn num_ntfy;\n+\t} else if (num_ntfy \u003c GVE_MIN_MSIX) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev, \"gve needs at least %d MSI-x vectors, but only has %d\\n\",\n+\t\t\tGVE_MIN_MSIX, num_ntfy);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* gvnic has one Notification Block per MSI-x vector, except for the\n+\t * management vector\n+\t */\n+\tpriv-\u003enum_ntfy_blks = (num_ntfy - 1) \u0026 ~0x1;\n+\tpriv-\u003emgmt_msix_idx = priv-\u003enum_ntfy_blks;\n+\n+\treturn 0;\n+}\n+\n+void gve_adminq_set_num_queues(struct gve_priv *priv)\n+{\n+\tpriv-\u003etx_cfg.max_queues =\n+\t\tmin_t(int, priv-\u003etx_cfg.max_queues, priv-\u003enum_ntfy_blks / 2);\n+\tpriv-\u003erx_cfg.max_queues =\n+\t\tmin_t(int, priv-\u003erx_cfg.max_queues, priv-\u003enum_ntfy_blks / 2);\n+\n+\tpriv-\u003etx_cfg.num_queues = priv-\u003etx_cfg.max_queues;\n+\tpriv-\u003erx_cfg.num_queues = priv-\u003erx_cfg.max_queues;\n+\tif (priv-\u003edefault_num_queues \u003e 0) {\n+\t\tpriv-\u003etx_cfg.num_queues = min_t(int, priv-\u003edefault_num_queues,\n+\t\t\t\t\t\tpriv-\u003etx_cfg.num_queues);\n+\t\tpriv-\u003erx_cfg.num_queues = min_t(int, priv-\u003edefault_num_queues,\n+\t\t\t\t\t\tpriv-\u003erx_cfg.num_queues);\n+\t}\n+}\n+\n+int gve_adminq_map_db_bar(struct gve_priv *priv)\n+{\n+\tstruct pci_dev *pdev = priv-\u003epdev;\n+\tvoid __iomem *db_bar;\n+\n+\tdb_bar = pci_iomap(pdev, GVE_DOORBELL_BAR, 0);\n+\tif (!db_bar) {\n+\t\tdev_err(\u0026pdev-\u003edev, \"Failed to map doorbell bar!\\n\");\n+\t\treturn -ENOMEM;\n+\t}\n+\tpriv-\u003edb_bar2 = db_bar;\n+\treturn 0;\n+}\n+\n+void gve_adminq_unmap_db_bar(struct gve_priv *priv)\n+{\n+\tstruct pci_dev *pdev = priv-\u003epdev;\n+\n+\tpci_iounmap(pdev, priv-\u003edb_bar2);\n+}\n+\n+int gve_adminq_request_db_info(struct gve_priv *priv)\n+{\n+\tint err;\n+\tint i;\n+\n+\terr = gve_adminq_configure_device_resources(priv,\n+\t\t\t\t\t\t priv-\u003ecounter_array_bus,\n+\t\t\t\t\t\t priv-\u003enum_event_counters,\n+\t\t\t\t\t\t priv-\u003eirq_db_indices_bus,\n+\t\t\t\t\t\t priv-\u003enum_ntfy_blks);\n+\tif (unlikely(err)) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"could not setup device_resources: err=%d\\n\", err);\n+\t\treturn -ENXIO;\n+\t}\n+\n+\tfor (i = 0; i \u003c priv-\u003enum_ntfy_blks; i++)\n+\t\tpriv-\u003entfy_blocks[i].irq_db_index =\n+\t\t\t\u0026priv-\u003eirq_db_indices[i].index;\n+\treturn 0;\n+}\n+\n+void gve_adminq_release_db_resources(struct gve_priv *priv)\n+{\n+\tint err;\n+\n+\t/* Log error in deconfigure device, but don't fail. This is only ever\n+\t * called as a reset is about to be triggered, so it would be redundant\n+\t * to trigger a reset.\n+\t */\n+\terr = gve_adminq_deconfigure_device_resources(priv);\n+\tif (err)\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Could not deconfigure device resources: err=%d\\n\",\n+\t\t\terr);\n+}\n+\n+static irqreturn_t gve_mgmnt_intr(int irq, void *arg)\n+{\n+\tstruct gve_priv *priv = arg;\n+\n+\t/* Device resources are not okay; consider the interrupt spurious. */\n+\tif (!gve_get_device_resources_ok(priv))\n+\t\treturn IRQ_NONE;\n+\n+\tqueue_work(priv-\u003egve_wq, \u0026priv-\u003eservice_task);\n+\treturn IRQ_HANDLED;\n+}\n+\n+int gve_adminq_setup_mgmt_irq(struct gve_priv *priv)\n+{\n+\tint err;\n+\n+\tsnprintf(priv-\u003emgmt_msix_name, sizeof(priv-\u003emgmt_msix_name),\n+\t\t \"gve-mgmnt@pci:%s\", pci_name(priv-\u003epdev));\n+\terr = request_irq(priv-\u003emsix_vectors[priv-\u003emgmt_msix_idx].vector,\n+\t\t\t gve_mgmnt_intr, 0, priv-\u003emgmt_msix_name, priv);\n+\tif (err)\n+\t\treturn err;\n+\n+\tpriv-\u003emgmt_irq_requested = true;\n+\n+\treturn 0;\n+}\n+\n+void gve_adminq_teardown_mgmt_irq(struct gve_priv *priv)\n+{\n+\tif (priv-\u003emgmt_irq_requested) {\n+\t\tfree_irq(priv-\u003emsix_vectors[priv-\u003emgmt_msix_idx].vector, priv);\n+\t\tpriv-\u003emgmt_irq_requested = false;\n+\t}\n+}\n+\n+int gve_adminq_create_queues(struct gve_priv *priv)\n+{\n+\tint num_tx_queues = gve_num_tx_queues(priv);\n+\tint err;\n+\n+\terr = gve_adminq_create_tx_queues(priv, 0, num_tx_queues);\n+\tif (err) {\n+\t\tnetif_err(priv, drv, priv-\u003edev, \"failed to create %d tx queues\\n\",\n+\t\t\t num_tx_queues);\n+\t\treturn err;\n+\t}\n+\tnetif_dbg(priv, drv, priv-\u003edev, \"created %d tx queues\\n\",\n+\t\t num_tx_queues);\n+\n+\terr = gve_adminq_create_rx_queues(priv, priv-\u003erx_cfg.num_queues);\n+\tif (err) {\n+\t\tnetif_err(priv, drv, priv-\u003edev, \"failed to create %d rx queues\\n\",\n+\t\t\t priv-\u003erx_cfg.num_queues);\n+\t\treturn err;\n+\t}\n+\tnetif_dbg(priv, drv, priv-\u003edev, \"created %d rx queues\\n\",\n+\t\t priv-\u003erx_cfg.num_queues);\n+\n+\treturn err;\n+}\n+\n+int gve_adminq_report_link_status(struct gve_priv *priv)\n+{\n+\tu32 status;\n+\n+\tstatus = ioread32be(\u0026priv-\u003ereg_bar0-\u003edevice_status);\n+\tWRITE_ONCE(priv-\u003elink_up,\n+\t\t !!(GVE_DEVICE_STATUS_LINK_STATUS_MASK \u0026 status));\n+\treturn 0;\n+}\ndiff --git a/drivers/net/ethernet/google/gve/gve_adminq.h b/drivers/net/ethernet/google/gve/gve_adminq.h\nindex 22a74b6aa17ea..8daac5fd685ab 100644\n--- a/drivers/net/ethernet/google/gve/gve_adminq.h\n+++ b/drivers/net/ethernet/google/gve/gve_adminq.h\n@@ -619,9 +619,8 @@ union gve_adminq_command {\n \n static_assert(sizeof(union gve_adminq_command) == 64);\n \n-int gve_adminq_alloc(struct device *dev, struct gve_priv *priv);\n-void gve_adminq_free(struct device *dev, struct gve_priv *priv);\n-void gve_adminq_release(struct gve_priv *priv);\n+int gve_adminq_init(struct gve_priv *priv);\n+void gve_adminq_free(struct gve_priv *priv);\n int gve_adminq_describe_device(struct gve_priv *priv);\n int gve_adminq_configure_device_resources(struct gve_priv *priv,\n \t\t\t\t\t dma_addr_t counter_array_bus_addr,\n@@ -629,6 +628,8 @@ int gve_adminq_configure_device_resources(struct gve_priv *priv,\n \t\t\t\t\t dma_addr_t db_array_bus_addr,\n \t\t\t\t\t u32 num_ntfy_blks);\n int gve_adminq_deconfigure_device_resources(struct gve_priv *priv);\n+int gve_adminq_create_queues(struct gve_priv *priv);\n+int gve_adminq_destroy_queues(struct gve_priv *priv);\n int gve_adminq_create_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues);\n int gve_adminq_destroy_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues);\n int gve_adminq_create_single_rx_queue(struct gve_priv *priv, u32 queue_index);\n@@ -640,9 +641,9 @@ int gve_adminq_register_page_list(struct gve_priv *priv,\n int gve_adminq_unregister_page_list(struct gve_priv *priv, u32 page_list_id);\n int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len,\n \t\t\t dma_addr_t stats_report_addr, u64 interval);\n-int gve_adminq_verify_driver_compatibility(struct gve_priv *priv,\n-\t\t\t\t\t u64 driver_info_len,\n-\t\t\t\t\t dma_addr_t driver_info_addr);\n+int gve_adminq_verify_driver_compatibility(struct gve_priv *priv);\n+int gve_adminq_get_device_properties(struct gve_priv *priv);\n+int gve_adminq_report_link_status(struct gve_priv *priv);\n int gve_adminq_report_link_speed(struct gve_priv *priv);\n int gve_adminq_add_flow_rule(struct gve_priv *priv, struct gve_adminq_flow_rule *rule, u32 loc);\n int gve_adminq_del_flow_rule(struct gve_priv *priv, u32 loc);\n@@ -654,7 +655,13 @@ int gve_adminq_report_nic_ts(struct gve_priv *priv,\n \t\t\t dma_addr_t nic_ts_report_addr);\n \n struct gve_ptype_lut;\n-int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv,\n-\t\t\t\t struct gve_ptype_lut *ptype_lut);\n-\n+int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv);\n+int gve_adminq_set_num_ntfy_blks(struct gve_priv *priv);\n+void gve_adminq_set_num_queues(struct gve_priv *priv);\n+int gve_adminq_map_db_bar(struct gve_priv *priv);\n+void gve_adminq_unmap_db_bar(struct gve_priv *priv);\n+int gve_adminq_request_db_info(struct gve_priv *priv);\n+void gve_adminq_release_db_resources(struct gve_priv *priv);\n+int gve_adminq_setup_mgmt_irq(struct gve_priv *priv);\n+void gve_adminq_teardown_mgmt_irq(struct gve_priv *priv);\n #endif /* _GVE_ADMINQ_H */\ndiff --git a/drivers/net/ethernet/google/gve/gve_dqo.h b/drivers/net/ethernet/google/gve/gve_dqo.h\nindex 5871f773f0c70..ce402020dff6a 100644\n--- a/drivers/net/ethernet/google/gve/gve_dqo.h\n+++ b/drivers/net/ethernet/google/gve/gve_dqo.h\n@@ -66,7 +66,7 @@ void gve_rx_write_doorbell_dqo(const struct gve_priv *priv, int queue_idx);\n void gve_xdp_tx_flush_dqo(struct gve_priv *priv, u32 xdp_qid);\n \n static inline void\n-gve_tx_put_doorbell_dqo(const struct gve_priv *priv,\n+gve_tx_put_doorbell_dqo(struct gve_priv *priv,\n \t\t\tconst struct gve_queue_resources *q_resources, u32 val)\n {\n \tu64 index;\ndiff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c\nindex 8199738ba979e..a3220c6514382 100644\n--- a/drivers/net/ethernet/google/gve/gve_ethtool.c\n+++ b/drivers/net/ethernet/google/gve/gve_ethtool.c\n@@ -651,7 +651,7 @@ static int gve_user_reset(struct net_device *netdev, u32 *flags)\n \n \tif (*flags == ETH_RESET_ALL) {\n \t\t*flags = 0;\n-\t\treturn gve_reset(priv, true);\n+\t\treturn gve_reset(priv, false);\n \t}\n \n \treturn -EOPNOTSUPP;\n@@ -749,12 +749,11 @@ static int gve_get_link_ksettings(struct net_device *netdev,\n \tint err = 0;\n \n \tif (priv-\u003elink_speed == 0)\n-\t\terr = gve_adminq_report_link_speed(priv);\n+\t\terr = priv-\u003ectrl_ops-\u003ereport_link_speed(priv);\n \n \tcmd-\u003ebase.speed = priv-\u003elink_speed;\n \n \tcmd-\u003ebase.duplex = DUPLEX_FULL;\n-\n \treturn err;\n }\n \ndiff --git a/drivers/net/ethernet/google/gve/gve_flow_rule.c b/drivers/net/ethernet/google/gve/gve_flow_rule.c\nindex 2c80cda28ef30..fae552f4ad6fb 100644\n--- a/drivers/net/ethernet/google/gve/gve_flow_rule.c\n+++ b/drivers/net/ethernet/google/gve/gve_flow_rule.c\n@@ -278,6 +278,11 @@ int gve_add_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd)\n \t\tgoto out;\n \n \terr = gve_adminq_add_flow_rule(priv, rule, fsp-\u003elocation);\n+\tif (err == -ETIME) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Timeout to add flow rule, trigger reset.\");\n+\t\tgve_reset(priv, false);\n+\t}\n \n out:\n \tkvfree(rule);\n@@ -290,9 +295,17 @@ int gve_add_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd)\n int gve_del_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd)\n {\n \tstruct ethtool_rx_flow_spec *fsp = (struct ethtool_rx_flow_spec *)\u0026cmd-\u003efs;\n+\tint err;\n \n \tif (!priv-\u003emax_flow_rules)\n \t\treturn -EOPNOTSUPP;\n \n-\treturn gve_adminq_del_flow_rule(priv, fsp-\u003elocation);\n+\terr = gve_adminq_del_flow_rule(priv, fsp-\u003elocation);\n+\tif (err == -ETIME) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Timeout to delete flow rule, trigger reset.\");\n+\t\tgve_reset(priv, false);\n+\t}\n+\n+\treturn err;\n }\ndiff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c\nindex e4d78ae52dafe..800e1104d01b1 100644\n--- a/drivers/net/ethernet/google/gve/gve_main.c\n+++ b/drivers/net/ethernet/google/gve/gve_main.c\n@@ -17,7 +17,6 @@\n #include \u003clinux/sched.h\u003e\n #include \u003clinux/timer.h\u003e\n #include \u003clinux/workqueue.h\u003e\n-#include \u003clinux/utsname.h\u003e\n #include \u003clinux/version.h\u003e\n #include \u003cnet/netdev_queues.h\u003e\n #include \u003cnet/sch_generic.h\u003e\n@@ -41,49 +40,6 @@ char gve_driver_name[] = \"gve\";\n const char gve_version_str[] = GVE_VERSION;\n static const char gve_version_prefix[] = GVE_VERSION_PREFIX;\n \n-static int gve_verify_driver_compatibility(struct gve_priv *priv)\n-{\n-\tint err;\n-\tstruct gve_driver_info *driver_info;\n-\tdma_addr_t driver_info_bus;\n-\n-\tdriver_info = dma_alloc_coherent(\u0026priv-\u003epdev-\u003edev,\n-\t\t\t\t\t sizeof(struct gve_driver_info),\n-\t\t\t\t\t \u0026driver_info_bus, GFP_KERNEL);\n-\tif (!driver_info)\n-\t\treturn -ENOMEM;\n-\n-\t*driver_info = (struct gve_driver_info) {\n-\t\t.os_type = 1, /* Linux */\n-\t\t.os_version_major = cpu_to_be32(LINUX_VERSION_MAJOR),\n-\t\t.os_version_minor = cpu_to_be32(LINUX_VERSION_SUBLEVEL),\n-\t\t.os_version_sub = cpu_to_be32(LINUX_VERSION_PATCHLEVEL),\n-\t\t.driver_capability_flags = {\n-\t\t\tcpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS1),\n-\t\t\tcpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS2),\n-\t\t\tcpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS3),\n-\t\t\tcpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS4),\n-\t\t},\n-\t};\n-\tstrscpy(driver_info-\u003eos_version_str1, utsname()-\u003erelease,\n-\t\tsizeof(driver_info-\u003eos_version_str1));\n-\tstrscpy(driver_info-\u003eos_version_str2, utsname()-\u003eversion,\n-\t\tsizeof(driver_info-\u003eos_version_str2));\n-\n-\terr = gve_adminq_verify_driver_compatibility(priv,\n-\t\t\t\t\t\t sizeof(struct gve_driver_info),\n-\t\t\t\t\t\t driver_info_bus);\n-\n-\t/* It's ok if the device doesn't support this */\n-\tif (err == -EOPNOTSUPP)\n-\t\terr = 0;\n-\n-\tdma_free_coherent(\u0026priv-\u003epdev-\u003edev,\n-\t\t\t sizeof(struct gve_driver_info),\n-\t\t\t driver_info, driver_info_bus);\n-\treturn err;\n-}\n-\n static netdev_features_t gve_features_check(struct sk_buff *skb,\n \t\t\t\t\t struct net_device *dev,\n \t\t\t\t\t netdev_features_t features)\n@@ -248,6 +204,30 @@ static void gve_free_counter_array(struct gve_priv *priv)\n \tpriv-\u003ecounter_array = NULL;\n }\n \n+static int gve_alloc_irq_db_indices(struct gve_priv *priv)\n+{\n+\tpriv-\u003eirq_db_indices =\n+\t\tdma_alloc_coherent(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\t priv-\u003enum_ntfy_blks *\n+\t\t\t\t sizeof(*priv-\u003eirq_db_indices),\n+\t\t\t\t \u0026priv-\u003eirq_db_indices_bus, GFP_KERNEL);\n+\tif (!priv-\u003eirq_db_indices)\n+\t\treturn -ENOMEM;\n+\n+\treturn 0;\n+}\n+\n+static void gve_free_irq_db_indices(struct gve_priv *priv)\n+{\n+\tif (!priv-\u003eirq_db_indices)\n+\t\treturn;\n+\n+\tdma_free_coherent(\u0026priv-\u003epdev-\u003edev, priv-\u003enum_ntfy_blks *\n+\t\t\t sizeof(*priv-\u003eirq_db_indices),\n+\t\t\t priv-\u003eirq_db_indices, priv-\u003eirq_db_indices_bus);\n+\tpriv-\u003eirq_db_indices = NULL;\n+}\n+\n /* NIC requests to report stats */\n static void gve_stats_report_task(struct work_struct *work)\n {\n@@ -311,14 +291,6 @@ static void gve_free_stats_report(struct gve_priv *priv)\n \tpriv-\u003estats_report = NULL;\n }\n \n-static irqreturn_t gve_mgmnt_intr(int irq, void *arg)\n-{\n-\tstruct gve_priv *priv = arg;\n-\n-\tqueue_work(priv-\u003egve_wq, \u0026priv-\u003eservice_task);\n-\treturn IRQ_HANDLED;\n-}\n-\n static irqreturn_t gve_intr(int irq, void *arg)\n {\n \tstruct gve_notify_block *block = arg;\n@@ -469,6 +441,15 @@ int gve_napi_poll_dqo(struct napi_struct *napi, int budget)\n \treturn work_done;\n }\n \n+static void gve_free_notify_blocks(struct gve_priv *priv)\n+{\n+\tpci_disable_msix(priv-\u003epdev);\n+\tkvfree(priv-\u003entfy_blocks);\n+\tpriv-\u003entfy_blocks = NULL;\n+\tkvfree(priv-\u003emsix_vectors);\n+\tpriv-\u003emsix_vectors = NULL;\n+}\n+\n static const struct cpumask *gve_get_node_mask(struct gve_priv *priv)\n {\n \tif (priv-\u003enuma_node == NUMA_NO_NODE)\n@@ -480,11 +461,9 @@ static const struct cpumask *gve_get_node_mask(struct gve_priv *priv)\n static int gve_alloc_notify_blocks(struct gve_priv *priv)\n {\n \tint num_vecs_requested = priv-\u003enum_ntfy_blks + 1;\n-\tconst struct cpumask *node_mask;\n-\tunsigned int cur_cpu;\n \tint vecs_enabled;\n-\tint i, j;\n \tint err;\n+\tint i;\n \n \tpriv-\u003emsix_vectors = kvzalloc_objs(*priv-\u003emsix_vectors,\n \t\t\t\t\t num_vecs_requested);\n@@ -498,7 +477,7 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)\n \t\tdev_err(\u0026priv-\u003epdev-\u003edev, \"Could not enable min msix %d/%d\\n\",\n \t\t\tGVE_MIN_MSIX, vecs_enabled);\n \t\terr = vecs_enabled;\n-\t\tgoto abort_with_msix_vectors;\n+\t\tgoto abort;\n \t}\n \tif (vecs_enabled != num_vecs_requested) {\n \t\tint new_num_ntfy_blks = (vecs_enabled - 1) \u0026 ~0x1;\n@@ -521,38 +500,65 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)\n \t\t\tpriv-\u003erx_cfg.num_queues = priv-\u003erx_cfg.max_queues;\n \t}\n \n-\t/* Setup Management Vector - the last vector */\n-\tsnprintf(priv-\u003emgmt_msix_name, sizeof(priv-\u003emgmt_msix_name), \"gve-mgmnt@pci:%s\",\n-\t\t pci_name(priv-\u003epdev));\n-\terr = request_irq(priv-\u003emsix_vectors[priv-\u003emgmt_msix_idx].vector,\n-\t\t\t gve_mgmnt_intr, 0, priv-\u003emgmt_msix_name, priv);\n-\tif (err) {\n-\t\tdev_err(\u0026priv-\u003epdev-\u003edev, \"Did not receive management vector.\\n\");\n-\t\tgoto abort_with_msix_enabled;\n-\t}\n-\tpriv-\u003eirq_db_indices =\n-\t\tdma_alloc_coherent(\u0026priv-\u003epdev-\u003edev,\n-\t\t\t\t priv-\u003enum_ntfy_blks *\n-\t\t\t\t sizeof(*priv-\u003eirq_db_indices),\n-\t\t\t\t \u0026priv-\u003eirq_db_indices_bus, GFP_KERNEL);\n-\tif (!priv-\u003eirq_db_indices) {\n-\t\terr = -ENOMEM;\n-\t\tgoto abort_with_mgmt_vector;\n-\t}\n-\n \tpriv-\u003entfy_blocks = kvzalloc(priv-\u003enum_ntfy_blks *\n \t\t\t\t sizeof(*priv-\u003entfy_blocks), GFP_KERNEL);\n \tif (!priv-\u003entfy_blocks) {\n \t\terr = -ENOMEM;\n-\t\tgoto abort_with_irq_db_indices;\n+\t\tgoto abort;\n+\t}\n+\n+\treturn 0;\n+abort:\n+\tgve_free_notify_blocks(priv);\n+\treturn err;\n+}\n+\n+static void gve_teardown_notify_blocks(struct gve_priv *priv)\n+{\n+\tconst struct gve_ctrl_ops *ops = priv-\u003ectrl_ops;\n+\tint i;\n+\n+\tif (!priv-\u003entfy_blocks)\n+\t\treturn;\n+\n+\tfor (i = 0; i \u003c priv-\u003enum_ntfy_blks; i++) {\n+\t\tstruct gve_notify_block *block = \u0026priv-\u003entfy_blocks[i];\n+\t\tint msix_idx = gve_ntfy_to_msix_idx(priv, i);\n+\n+\t\tif (!block-\u003eirq_requested)\n+\t\t\tcontinue;\n+\n+\t\tirq_set_affinity_hint(priv-\u003emsix_vectors[msix_idx].vector,\n+\t\t\t\t NULL);\n+\t\tfree_irq(priv-\u003emsix_vectors[msix_idx].vector, block);\n+\t\tblock-\u003eirq = 0;\n+\t\tblock-\u003eirq_requested = false;\n+\t}\n+\n+\tops-\u003eteardown_mgmt_irq(priv);\n+}\n+\n+static int gve_setup_notify_blocks(struct gve_priv *priv)\n+{\n+\tconst struct gve_ctrl_ops *ops = priv-\u003ectrl_ops;\n+\tconst struct cpumask *node_mask;\n+\tunsigned int cur_cpu;\n+\tint i;\n+\tint err;\n+\n+\t/* Setup Management Vector */\n+\terr = ops-\u003esetup_mgmt_irq(priv);\n+\tif (err) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Did not receive management vector.\\n\");\n+\t\treturn err;\n \t}\n \n-\t/* Setup the other blocks - the first n-1 vectors */\n \tnode_mask = gve_get_node_mask(priv);\n \tcur_cpu = cpumask_first(node_mask);\n \tfor (i = 0; i \u003c priv-\u003enum_ntfy_blks; i++) {\n \t\tstruct gve_notify_block *block = \u0026priv-\u003entfy_blocks[i];\n-\t\tint msix_idx = i;\n+\t\tint msix_idx = gve_ntfy_to_msix_idx(priv, i);\n \n \t\tsnprintf(block-\u003ename, sizeof(block-\u003ename), \"gve-ntfy-blk%d@pci:%s\",\n \t\t\t i, pci_name(priv-\u003epdev));\n@@ -562,13 +568,13 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)\n \t\t\t\t IRQF_NO_AUTOEN, block-\u003ename, block);\n \t\tif (err) {\n \t\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n-\t\t\t\t\"Failed to receive msix vector %d\\n\", i);\n-\t\t\tgoto abort_with_some_ntfy_blocks;\n+\t\t\t\t\"Failed to receive msix vector %d\\n\", msix_idx);\n+\t\t\tgoto abort;\n \t\t}\n \t\tblock-\u003eirq = priv-\u003emsix_vectors[msix_idx].vector;\n+\t\tblock-\u003eirq_requested = true;\n \t\tirq_set_affinity_and_hint(block-\u003eirq,\n \t\t\t\t\t cpumask_of(cur_cpu));\n-\t\tblock-\u003eirq_db_index = \u0026priv-\u003eirq_db_indices[i].index;\n \n \t\tcur_cpu = cpumask_next(cur_cpu, node_mask);\n \t\t/* Wrap once CPUs in the node have been exhausted, or when\n@@ -579,63 +585,29 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)\n \t\t\tcur_cpu = cpumask_first(node_mask);\n \t}\n \treturn 0;\n-abort_with_some_ntfy_blocks:\n-\tfor (j = 0; j \u003c i; j++) {\n-\t\tstruct gve_notify_block *block = \u0026priv-\u003entfy_blocks[j];\n-\t\tint msix_idx = j;\n \n-\t\tirq_set_affinity_hint(priv-\u003emsix_vectors[msix_idx].vector,\n-\t\t\t\t NULL);\n-\t\tfree_irq(priv-\u003emsix_vectors[msix_idx].vector, block);\n-\t\tblock-\u003eirq = 0;\n-\t}\n-\tkvfree(priv-\u003entfy_blocks);\n-\tpriv-\u003entfy_blocks = NULL;\n-abort_with_irq_db_indices:\n-\tdma_free_coherent(\u0026priv-\u003epdev-\u003edev, priv-\u003enum_ntfy_blks *\n-\t\t\t sizeof(*priv-\u003eirq_db_indices),\n-\t\t\t priv-\u003eirq_db_indices, priv-\u003eirq_db_indices_bus);\n-\tpriv-\u003eirq_db_indices = NULL;\n-abort_with_mgmt_vector:\n-\tfree_irq(priv-\u003emsix_vectors[priv-\u003emgmt_msix_idx].vector, priv);\n-abort_with_msix_enabled:\n-\tpci_disable_msix(priv-\u003epdev);\n-abort_with_msix_vectors:\n-\tkvfree(priv-\u003emsix_vectors);\n-\tpriv-\u003emsix_vectors = NULL;\n+abort:\n+\tgve_teardown_notify_blocks(priv);\n \treturn err;\n }\n \n-static void gve_free_notify_blocks(struct gve_priv *priv)\n+static void gve_free_control_plane_resources(struct gve_priv *priv)\n {\n-\tint i;\n-\n-\tif (!priv-\u003emsix_vectors)\n-\t\treturn;\n+\tbitmap_free(priv-\u003exsk_pools);\n+\tpriv-\u003exsk_pools = NULL;\n \n-\t/* Free the irqs */\n-\tfor (i = 0; i \u003c priv-\u003enum_ntfy_blks; i++) {\n-\t\tstruct gve_notify_block *block = \u0026priv-\u003entfy_blocks[i];\n-\t\tint msix_idx = i;\n+\tkvfree(priv-\u003eptype_lut_dqo);\n+\tpriv-\u003eptype_lut_dqo = NULL;\n \n-\t\tirq_set_affinity_hint(priv-\u003emsix_vectors[msix_idx].vector,\n-\t\t\t\t NULL);\n-\t\tfree_irq(priv-\u003emsix_vectors[msix_idx].vector, block);\n-\t\tblock-\u003eirq = 0;\n-\t}\n-\tfree_irq(priv-\u003emsix_vectors[priv-\u003emgmt_msix_idx].vector, priv);\n-\tkvfree(priv-\u003entfy_blocks);\n-\tpriv-\u003entfy_blocks = NULL;\n-\tdma_free_coherent(\u0026priv-\u003epdev-\u003edev, priv-\u003enum_ntfy_blks *\n-\t\t\t sizeof(*priv-\u003eirq_db_indices),\n-\t\t\t priv-\u003eirq_db_indices, priv-\u003eirq_db_indices_bus);\n-\tpriv-\u003eirq_db_indices = NULL;\n-\tpci_disable_msix(priv-\u003epdev);\n-\tkvfree(priv-\u003emsix_vectors);\n-\tpriv-\u003emsix_vectors = NULL;\n+\tgve_free_irq_db_indices(priv);\n+\tgve_free_counter_array(priv);\n+\tgve_free_notify_blocks(priv);\n+\tgve_free_stats_report(priv);\n+\tgve_free_rss_config_cache(priv);\n+\tgve_free_flow_rule_caches(priv);\n }\n \n-static int gve_setup_device_resources(struct gve_priv *priv)\n+static int gve_alloc_control_plane_resources(struct gve_priv *priv)\n {\n \tint err;\n \n@@ -644,39 +616,58 @@ static int gve_setup_device_resources(struct gve_priv *priv)\n \t\treturn err;\n \terr = gve_alloc_rss_config_cache(priv);\n \tif (err)\n-\t\tgoto abort_with_flow_rule_caches;\n-\terr = gve_alloc_counter_array(priv);\n-\tif (err)\n-\t\tgoto abort_with_rss_config_cache;\n+\t\tgoto abort;\n \terr = gve_alloc_notify_blocks(priv);\n \tif (err)\n-\t\tgoto abort_with_counter;\n+\t\tgoto abort;\n \terr = gve_alloc_stats_report(priv);\n \tif (err)\n-\t\tgoto abort_with_ntfy_blocks;\n-\terr = gve_adminq_configure_device_resources(priv,\n-\t\t\t\t\t\t priv-\u003ecounter_array_bus,\n-\t\t\t\t\t\t priv-\u003enum_event_counters,\n-\t\t\t\t\t\t priv-\u003eirq_db_indices_bus,\n-\t\t\t\t\t\t priv-\u003enum_ntfy_blks);\n-\tif (unlikely(err)) {\n-\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n-\t\t\t\"could not setup device_resources: err=%d\\n\", err);\n-\t\terr = -ENXIO;\n-\t\tgoto abort_with_stats_report;\n-\t}\n+\t\tgoto abort;\n+\terr = gve_alloc_counter_array(priv);\n+\tif (err)\n+\t\tgoto abort;\n+\terr = gve_alloc_irq_db_indices(priv);\n+\tif (err)\n+\t\tgoto abort;\n \n \tif (!gve_is_gqi(priv)) {\n-\t\tpriv-\u003eptype_lut_dqo = kvzalloc_obj(*priv-\u003eptype_lut_dqo);\n+\t\tpriv-\u003eptype_lut_dqo = kvzalloc_obj(*priv-\u003eptype_lut_dqo,\n+\t\t\t\t\t\t GFP_KERNEL);\n \t\tif (!priv-\u003eptype_lut_dqo) {\n \t\t\terr = -ENOMEM;\n-\t\t\tgoto abort_with_stats_report;\n+\t\t\tgoto abort;\n \t\t}\n-\t\terr = gve_adminq_get_ptype_map_dqo(priv, priv-\u003eptype_lut_dqo);\n+\t}\n+\n+\tpriv-\u003exsk_pools = bitmap_zalloc(priv-\u003erx_cfg.max_queues, GFP_KERNEL);\n+\tif (!priv-\u003exsk_pools) {\n+\t\terr = -ENOMEM;\n+\t\tgoto abort;\n+\t}\n+\n+\treturn 0;\n+abort:\n+\tgve_free_control_plane_resources(priv);\n+\treturn err;\n+}\n+\n+static int gve_setup_control_plane_resources(struct gve_priv *priv)\n+{\n+\tconst struct gve_ctrl_ops *ops = priv-\u003ectrl_ops;\n+\tint err;\n+\n+\terr = ops-\u003erequest_db_info(priv);\n+\tif (err) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev, \"Failed to get db info\");\n+\t\treturn err;\n+\t}\n+\n+\tif (!gve_is_gqi(priv)) {\n+\t\terr = ops-\u003eget_ptype_map(priv);\n \t\tif (err) {\n \t\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n \t\t\t\t\"Failed to get ptype map: err=%d\\n\", err);\n-\t\t\tgoto abort_with_ptype_lut;\n+\t\t\tgoto release_db_resources;\n \t\t}\n \t}\n \n@@ -691,79 +682,76 @@ static int gve_setup_device_resources(struct gve_priv *priv)\n \terr = gve_init_rss_config(priv, priv-\u003erx_cfg.num_queues);\n \tif (err) {\n \t\tdev_err(\u0026priv-\u003epdev-\u003edev, \"Failed to init RSS config\");\n-\t\tgoto abort_with_clock;\n+\t\tgoto teardown_clock;\n \t}\n \n-\terr = gve_adminq_report_stats(priv, priv-\u003estats_report_len,\n+\terr = ops-\u003esetup_stats_report(priv, priv-\u003estats_report_len,\n \t\t\t\t priv-\u003estats_report_bus,\n \t\t\t\t GVE_STATS_REPORT_TIMER_PERIOD);\n \tif (err)\n \t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n \t\t\t\"Failed to report stats: err=%d\\n\", err);\n+\n \tgve_set_device_resources_ok(priv);\n \treturn 0;\n \n-abort_with_clock:\n+teardown_clock:\n \tgve_teardown_clock(priv);\n-abort_with_ptype_lut:\n-\tkvfree(priv-\u003eptype_lut_dqo);\n-\tpriv-\u003eptype_lut_dqo = NULL;\n-abort_with_stats_report:\n-\tgve_free_stats_report(priv);\n-abort_with_ntfy_blocks:\n-\tgve_free_notify_blocks(priv);\n-abort_with_counter:\n-\tgve_free_counter_array(priv);\n-abort_with_rss_config_cache:\n-\tgve_free_rss_config_cache(priv);\n-abort_with_flow_rule_caches:\n-\tgve_free_flow_rule_caches(priv);\n-\n+release_db_resources:\n+\tops-\u003erelease_db_resources(priv);\n \treturn err;\n }\n \n-static void gve_trigger_reset(struct gve_priv *priv);\n-\n-static void gve_teardown_device_resources(struct gve_priv *priv)\n+/**\n+ * gve_teardown_control_plane_resources() - Request the device to release any\n+ * shared allocated resources.\n+ *\n+ * @priv: Pointer to the GVE private device data structure.\n+ *\n+ * If any part of the teardown step fails, the failure is documented, but is\n+ * otherwise ignored. It is expected that a device reset is triggered\n+ * immediately after tearing down device resources, which would clear any\n+ * lingering state on the device.\n+ */\n+static void gve_teardown_control_plane_resources(struct gve_priv *priv)\n {\n+\tconst struct gve_ctrl_ops *ops = priv-\u003ectrl_ops;\n \tint err;\n \n \t/* Tell device its resources are being freed */\n \tif (gve_get_device_resources_ok(priv)) {\n \t\terr = gve_flow_rules_reset(priv);\n-\t\tif (err) {\n+\t\tif (err)\n \t\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n \t\t\t\t\"Failed to reset flow rules: err=%d\\n\", err);\n-\t\t\tgve_trigger_reset(priv);\n-\t\t}\n \t\t/* detach the stats report */\n-\t\terr = gve_adminq_report_stats(priv, 0, 0x0, GVE_STATS_REPORT_TIMER_PERIOD);\n-\t\tif (err) {\n+\t\terr = ops-\u003esetup_stats_report(priv, 0, 0x0,\n+\t\t\t\t\t GVE_STATS_REPORT_TIMER_PERIOD);\n+\t\tif (err)\n \t\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n \t\t\t\t\"Failed to detach stats report: err=%d\\n\", err);\n-\t\t\tgve_trigger_reset(priv);\n-\t\t}\n-\t\terr = gve_adminq_deconfigure_device_resources(priv);\n-\t\tif (err) {\n-\t\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n-\t\t\t\t\"Could not deconfigure device resources: err=%d\\n\",\n-\t\t\t\terr);\n-\t\t\tgve_trigger_reset(priv);\n-\t\t}\n+\t\tgve_teardown_clock(priv);\n+\t\tops-\u003erelease_db_resources(priv);\n \t}\n \n-\tkvfree(priv-\u003eptype_lut_dqo);\n-\tpriv-\u003eptype_lut_dqo = NULL;\n-\n-\tgve_free_flow_rule_caches(priv);\n-\tgve_free_rss_config_cache(priv);\n-\tgve_free_counter_array(priv);\n-\tgve_free_notify_blocks(priv);\n-\tgve_free_stats_report(priv);\n-\tgve_teardown_clock(priv);\n \tgve_clear_device_resources_ok(priv);\n }\n \n+static void gve_teardown_device(struct gve_priv *priv)\n+{\n+\tgve_teardown_notify_blocks(priv);\n+\tgve_teardown_control_plane_resources(priv);\n+\tgve_adminq_free(priv);\n+\t/*\n+\t * Free any resources shared with the device only after we have a\n+\t * guarantee that the device will not try to access such resources.\n+\t * Device commands in gve_teardown_control_plane_resources can fail, in\n+\t * which case, device resources won't be relinquished until\n+\t * gve_adminq_free is called to trigger a device reset.\n+\t */\n+\tgve_free_control_plane_resources(priv);\n+}\n+\n static int gve_unregister_qpl(struct gve_priv *priv,\n \t\t\t struct gve_queue_page_list *qpl)\n {\n@@ -886,33 +874,18 @@ static int gve_unregister_qpls(struct gve_priv *priv)\n \n static int gve_create_rings(struct gve_priv *priv)\n {\n-\tint num_tx_queues = gve_num_tx_queues(priv);\n \tint err;\n \tint i;\n \n-\terr = gve_adminq_create_tx_queues(priv, 0, num_tx_queues);\n-\tif (err) {\n-\t\tnetif_err(priv, drv, priv-\u003edev, \"failed to create %d tx queues\\n\",\n-\t\t\t num_tx_queues);\n-\t\t/* This failure will trigger a reset - no need to clean\n-\t\t * up\n-\t\t */\n-\t\treturn err;\n-\t}\n-\tnetif_dbg(priv, drv, priv-\u003edev, \"created %d tx queues\\n\",\n-\t\t num_tx_queues);\n-\n-\terr = gve_adminq_create_rx_queues(priv, priv-\u003erx_cfg.num_queues);\n+\terr = priv-\u003ectrl_ops-\u003ecreate_queues(priv);\n \tif (err) {\n-\t\tnetif_err(priv, drv, priv-\u003edev, \"failed to create %d rx queues\\n\",\n-\t\t\t priv-\u003erx_cfg.num_queues);\n+\t\tnetif_err(priv, drv, priv-\u003edev,\n+\t\t\t \"failed to create queues\\n\");\n \t\t/* This failure will trigger a reset - no need to clean\n \t\t * up\n \t\t */\n \t\treturn err;\n \t}\n-\tnetif_dbg(priv, drv, priv-\u003edev, \"created %d rx queues\\n\",\n-\t\t priv-\u003erx_cfg.num_queues);\n \n \tif (gve_is_gqi(priv)) {\n \t\t/* Rx data ring has been prefilled with packet buffers at queue\n@@ -1067,26 +1040,12 @@ static int gve_queues_mem_alloc(struct gve_priv *priv,\n \n static int gve_destroy_rings(struct gve_priv *priv)\n {\n-\tint num_tx_queues = gve_num_tx_queues(priv);\n \tint err;\n \n-\terr = gve_adminq_destroy_tx_queues(priv, 0, num_tx_queues);\n-\tif (err) {\n-\t\tnetif_err(priv, drv, priv-\u003edev,\n-\t\t\t \"failed to destroy tx queues\\n\");\n-\t\t/* This failure will trigger a reset - no need to clean up */\n-\t\treturn err;\n-\t}\n-\tnetif_dbg(priv, drv, priv-\u003edev, \"destroyed tx queues\\n\");\n-\terr = gve_adminq_destroy_rx_queues(priv, priv-\u003erx_cfg.num_queues);\n-\tif (err) {\n-\t\tnetif_err(priv, drv, priv-\u003edev,\n-\t\t\t \"failed to destroy rx queues\\n\");\n-\t\t/* This failure will trigger a reset - no need to clean up */\n-\t\treturn err;\n-\t}\n-\tnetif_dbg(priv, drv, priv-\u003edev, \"destroyed rx queues\\n\");\n-\treturn 0;\n+\terr = priv-\u003ectrl_ops-\u003edestroy_queues(priv);\n+\n+\t/* A failure will trigger a reset - no need to clean up */\n+\treturn err;\n }\n \n static void gve_queues_mem_free(struct gve_priv *priv,\n@@ -1202,8 +1161,6 @@ void gve_schedule_reset(struct gve_priv *priv)\n \tqueue_work(priv-\u003egve_wq, \u0026priv-\u003eservice_task);\n }\n \n-static void gve_reset_and_teardown(struct gve_priv *priv, bool was_up);\n-static int gve_reset_recovery(struct gve_priv *priv, bool was_up);\n static void gve_turndown(struct gve_priv *priv);\n static void gve_turnup(struct gve_priv *priv);\n \n@@ -1314,11 +1271,12 @@ static int gve_reg_xdp_info(struct gve_priv *priv, struct net_device *dev)\n \treturn err;\n }\n \n-\n static void gve_drain_page_cache(struct gve_priv *priv)\n {\n \tint i;\n \n+\tif (!priv-\u003erx)\n+\t\treturn;\n \tfor (i = 0; i \u003c priv-\u003erx_cfg.num_queues; i++)\n \t\tpage_frag_cache_drain(\u0026priv-\u003erx[i].page_cache);\n }\n@@ -1461,10 +1419,11 @@ static int gve_queues_start(struct gve_priv *priv,\n reset:\n \tif (gve_get_reset_in_progress(priv))\n \t\tgoto stop_and_free_rings;\n-\tgve_reset_and_teardown(priv, true);\n-\t/* if this fails there is nothing we can do so just ignore the return */\n-\tgve_reset_recovery(priv, false);\n-\t/* return the original error */\n+\n+\t/* Attempt to reset. If reset is successful, gve_queues_start was\n+\t * successful.\n+\t */\n+\terr = gve_reset(priv, false);\n \treturn err;\n stop_and_free_rings:\n \tgve_tx_stop_rings(priv, gve_num_tx_queues(priv));\n@@ -1480,6 +1439,12 @@ static int gve_open(struct net_device *dev)\n \tstruct gve_priv *priv = netdev_priv(dev);\n \tint err;\n \n+\tif (!gve_get_device_resources_ok(priv)) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Attempting to open netdev without resources. Device must be reset.\");\n+\t\treturn -ENODEV;\n+\t}\n+\n \tgve_get_curr_alloc_cfgs(priv, \u0026tx_alloc_cfg, \u0026rx_alloc_cfg);\n \n \terr = gve_queues_mem_alloc(priv, \u0026tx_alloc_cfg, \u0026rx_alloc_cfg);\n@@ -1496,41 +1461,18 @@ static int gve_open(struct net_device *dev)\n \treturn 0;\n }\n \n-static int gve_queues_stop(struct gve_priv *priv)\n+static void gve_queues_stop(struct gve_priv *priv)\n {\n-\tint err;\n-\n-\tnetif_carrier_off(priv-\u003edev);\n-\tif (gve_get_device_rings_ok(priv)) {\n-\t\tgve_turndown(priv);\n-\t\tgve_drain_page_cache(priv);\n-\t\terr = gve_destroy_rings(priv);\n-\t\tif (err)\n-\t\t\tgoto err;\n-\t\terr = gve_unregister_qpls(priv);\n-\t\tif (err)\n-\t\t\tgoto err;\n-\t\tgve_clear_device_rings_ok(priv);\n-\t}\n-\ttimer_delete_sync(\u0026priv-\u003estats_report_timer);\n+\tgve_turndown(priv);\n \n \tgve_unreg_xdp_info(priv);\n+\tgve_drain_page_cache(priv);\n+\n+\ttimer_delete_sync(\u0026priv-\u003estats_report_timer);\n+\tcancel_work_sync(\u0026priv-\u003estats_report_task);\n \n \tgve_tx_stop_rings(priv, gve_num_tx_queues(priv));\n \tgve_rx_stop_rings(priv, priv-\u003erx_cfg.num_queues);\n-\n-\tpriv-\u003einterface_down_cnt++;\n-\treturn 0;\n-\n-err:\n-\t/* This must have been called from a reset due to the rtnl lock\n-\t * so just return at this point.\n-\t */\n-\tif (gve_get_reset_in_progress(priv))\n-\t\treturn err;\n-\t/* Otherwise reset before returning */\n-\tgve_reset_and_teardown(priv, true);\n-\treturn gve_reset_recovery(priv, false);\n }\n \n static int gve_close(struct net_device *dev)\n@@ -1538,23 +1480,42 @@ static int gve_close(struct net_device *dev)\n \tstruct gve_priv *priv = netdev_priv(dev);\n \tint err;\n \n-\terr = gve_queues_stop(priv);\n-\tif (err)\n-\t\treturn err;\n+\tgve_queues_stop(priv);\n+\n+\t/* Surrender to reset if the queue destroying adminq cmds fail. Reset\n+\t * will not re-enable the interface.\n+\t */\n+\tif (gve_get_device_rings_ok(priv)) {\n+\t\tgve_clear_device_rings_ok(priv);\n+\t\terr = gve_destroy_rings(priv);\n+\t\tif (err)\n+\t\t\tgoto reset;\n+\t\terr = gve_unregister_qpls(priv);\n+\t\tif (err)\n+\t\t\tgoto reset;\n+\t}\n \n \tgve_queues_mem_remove(priv);\n+\tpriv-\u003einterface_down_cnt++;\n \treturn 0;\n+\n+reset:\n+\terr = gve_reset(priv, true);\n+\treturn err;\n }\n \n-static void gve_handle_link_status(struct gve_priv *priv, bool link_status)\n+static void gve_handle_link_status(struct gve_priv *priv)\n {\n+\tbool link_up;\n+\n \tif (!gve_get_napi_enabled(priv))\n \t\treturn;\n \n-\tif (link_status == netif_carrier_ok(priv-\u003edev))\n+\tlink_up = READ_ONCE(priv-\u003elink_up);\n+\tif (link_up == netif_carrier_ok(priv-\u003edev))\n \t\treturn;\n \n-\tif (link_status) {\n+\tif (link_up) {\n \t\tnetdev_info(priv-\u003edev, \"Device link is up.\\n\");\n \t\tnetif_carrier_on(priv-\u003edev);\n \t} else {\n@@ -1581,7 +1542,6 @@ static int gve_set_xdp(struct gve_priv *priv, struct bpf_prog *prog,\n {\n \tstruct bpf_prog *old_prog;\n \tint err = 0;\n-\tu32 status;\n \n \told_prog = READ_ONCE(priv-\u003exdp_prog);\n \tif (!netif_running(priv-\u003edev)) {\n@@ -1608,8 +1568,8 @@ static int gve_set_xdp(struct gve_priv *priv, struct bpf_prog *prog,\n \t\tbpf_prog_put(old_prog);\n \n out:\n-\tstatus = ioread32be(\u0026priv-\u003ereg_bar0-\u003edevice_status);\n-\tgve_handle_link_status(priv, GVE_DEVICE_STATUS_LINK_STATUS_MASK \u0026 status);\n+\tpriv-\u003ectrl_ops-\u003ereport_link_status(priv);\n+\tgve_handle_link_status(priv);\n \treturn err;\n }\n \n@@ -1824,6 +1784,7 @@ static int gve_xdp(struct net_device *dev, struct netdev_bpf *xdp)\n \n int gve_init_rss_config(struct gve_priv *priv, u16 num_queues)\n {\n+\tconst struct gve_ctrl_ops *ops = priv-\u003ectrl_ops;\n \tstruct gve_rss_config *rss_config = \u0026priv-\u003erss_config;\n \tstruct ethtool_rxfh_param rxfh = {0};\n \tu16 i;\n@@ -1839,15 +1800,17 @@ int gve_init_rss_config(struct gve_priv *priv, u16 num_queues)\n \n \trxfh.hfunc = ETH_RSS_HASH_TOP;\n \n-\treturn gve_adminq_configure_rss(priv, \u0026rxfh);\n+\treturn ops-\u003econfigure_rss(priv, \u0026rxfh);\n }\n \n int gve_flow_rules_reset(struct gve_priv *priv)\n {\n+\tconst struct gve_ctrl_ops *ops = priv-\u003ectrl_ops;\n+\n \tif (!priv-\u003emax_flow_rules)\n \t\treturn 0;\n \n-\treturn gve_adminq_reset_flow_rules(priv);\n+\treturn ops-\u003ereset_flow_rules(priv);\n }\n \n int gve_adjust_config(struct gve_priv *priv,\n@@ -2039,11 +2002,11 @@ static void gve_turnup(struct gve_priv *priv)\n \n static void gve_turnup_and_check_status(struct gve_priv *priv)\n {\n-\tu32 status;\n+\tconst struct gve_ctrl_ops *ops = priv-\u003ectrl_ops;\n \n \tgve_turnup(priv);\n-\tstatus = ioread32be(\u0026priv-\u003ereg_bar0-\u003edevice_status);\n-\tgve_handle_link_status(priv, GVE_DEVICE_STATUS_LINK_STATUS_MASK \u0026 status);\n+\tops-\u003ereport_link_status(priv);\n+\tgve_handle_link_status(priv);\n }\n \n static struct gve_notify_block *gve_get_tx_notify_block(struct gve_priv *priv,\n@@ -2367,12 +2330,14 @@ static void gve_service_task(struct work_struct *work)\n {\n \tstruct gve_priv *priv = container_of(work, struct gve_priv,\n \t\t\t\t\t service_task);\n+\tconst struct gve_ctrl_ops *ops = priv-\u003ectrl_ops;\n \tu32 status = ioread32be(\u0026priv-\u003ereg_bar0-\u003edevice_status);\n \n \tgve_handle_status(priv, status);\n \n \tgve_handle_reset(priv);\n-\tgve_handle_link_status(priv, GVE_DEVICE_STATUS_LINK_STATUS_MASK \u0026 status);\n+\tops-\u003ereport_link_status(priv);\n+\tgve_handle_link_status(priv);\n }\n \n static void gve_set_netdev_xdp_features(struct gve_priv *priv)\n@@ -2398,188 +2363,249 @@ static const struct xdp_metadata_ops gve_xdp_metadata_ops = {\n \t.xmo_rx_timestamp\t= gve_xdp_rx_timestamp,\n };\n \n-static int gve_init_priv(struct gve_priv *priv, bool skip_describe_device)\n+static void gve_set_desc_cnt(struct gve_priv *priv)\n {\n-\tint num_ntfy;\n-\tint err;\n+\tstruct gve_device_info *device_info = \u0026priv-\u003edevice_info;\n \n-\t/* Set up the adminq */\n-\terr = gve_adminq_alloc(\u0026priv-\u003epdev-\u003edev, priv);\n-\tif (err) {\n-\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n-\t\t\t\"Failed to alloc admin queue: err=%d\\n\", err);\n-\t\treturn err;\n-\t}\n+\tpriv-\u003etx_desc_cnt = device_info-\u003edefault_tx_ring_size;\n+\tpriv-\u003erx_desc_cnt = device_info-\u003edefault_rx_ring_size;\n+\tpriv-\u003emax_tx_desc_cnt = device_info-\u003emax_tx_ring_size;\n+\tpriv-\u003emax_rx_desc_cnt = device_info-\u003emax_rx_ring_size;\n+\tpriv-\u003emin_tx_desc_cnt = device_info-\u003emin_tx_ring_size;\n+\tpriv-\u003emin_rx_desc_cnt = device_info-\u003emin_rx_ring_size;\n+}\n \n-\terr = gve_verify_driver_compatibility(priv);\n-\tif (err) {\n-\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n-\t\t\t\"Could not verify driver compatibility: err=%d\\n\", err);\n-\t\tgoto err;\n-\t}\n+static void gve_set_queue_properties(struct gve_priv *priv)\n+{\n+\tstruct gve_device_info *device_info = \u0026priv-\u003edevice_info;\n \n-\tpriv-\u003enum_registered_pages = 0;\n+\tgve_set_desc_cnt(priv);\n+\tpriv-\u003emax_registered_pages = device_info-\u003emax_registered_pages;\n+\tpriv-\u003etx_pages_per_qpl = device_info-\u003etx_pages_per_qpl;\n+}\n \n-\tif (skip_describe_device)\n-\t\tgoto setup_device;\n+static int gve_set_mtu(struct gve_priv *priv)\n+{\n+\tstruct gve_device_info *device_info = \u0026priv-\u003edevice_info;\n+\tu16 mtu;\n \n-\tpriv-\u003equeue_format = GVE_QUEUE_FORMAT_UNSPECIFIED;\n-\t/* Get the initial information we need from the device */\n-\terr = gve_adminq_describe_device(priv);\n-\tif (err) {\n-\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n-\t\t\t\"Could not get device information: err=%d\\n\", err);\n-\t\tgoto err;\n-\t}\n-\tpriv-\u003edev-\u003emtu = priv-\u003edev-\u003emax_mtu;\n-\tnum_ntfy = pci_msix_vec_count(priv-\u003epdev);\n-\tif (num_ntfy \u003c= 0) {\n-\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n-\t\t\t\"could not count MSI-x vectors: err=%d\\n\", num_ntfy);\n-\t\terr = num_ntfy;\n-\t\tgoto err;\n-\t} else if (num_ntfy \u003c GVE_MIN_MSIX) {\n-\t\tdev_err(\u0026priv-\u003epdev-\u003edev, \"gve needs at least %d MSI-x vectors, but only has %d\\n\",\n-\t\t\tGVE_MIN_MSIX, num_ntfy);\n-\t\terr = -EINVAL;\n-\t\tgoto err;\n+\tmtu = device_info-\u003emax_mtu;\n+\tif (mtu \u003c ETH_MIN_MTU) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev, \"MTU %d below minimum MTU\\n\", mtu);\n+\t\treturn -EINVAL;\n \t}\n+\tpriv-\u003edev-\u003emax_mtu = mtu;\n+\tpriv-\u003edev-\u003emtu = mtu;\n+\n+\treturn 0;\n+}\n+\n+static void gve_set_mac(struct gve_priv *priv)\n+{\n+\tstruct gve_device_info *device_info = \u0026priv-\u003edevice_info;\n+\tu8 *mac;\n+\n+\tmac = device_info-\u003emac;\n+\teth_hw_addr_set(priv-\u003edev, mac);\n+\tdev_info(\u0026priv-\u003epdev-\u003edev, \"MAC addr: %pM\\n\", mac);\n+}\n+\n+static void gve_set_buf_sizes(struct gve_priv *priv)\n+{\n+\tstruct gve_device_info *device_info = \u0026priv-\u003edevice_info;\n+\n+\tif (device_info-\u003emax_rx_buffer_size \u003e priv-\u003emax_rx_buffer_size)\n+\t\tpriv-\u003emax_rx_buffer_size = device_info-\u003emax_rx_buffer_size;\n \n-\t/* Big TCP is only supported on DQO */\n+\tif (gve_is_dqo(priv) \u0026\u0026\n+\t priv-\u003emax_rx_buffer_size \u003e GVE_DEFAULT_RX_BUFFER_SIZE)\n+\t\tpriv-\u003erx_cfg.packet_buffer_size = priv-\u003emax_rx_buffer_size;\n+\n+\tif (device_info-\u003eheader_buf_size)\n+\t\tpriv-\u003eheader_buf_size = device_info-\u003eheader_buf_size;\n+}\n+\n+static int gve_setup_device(struct gve_priv *priv)\n+{\n+\tint err;\n+\n+\tpriv-\u003enum_registered_pages = 0;\n+\n+\tgve_set_netdev_xdp_features(priv);\n \tif (!gve_is_gqi(priv))\n-\t\tnetif_set_tso_max_size(priv-\u003edev, GVE_DQO_TX_MAX);\n+\t\tpriv-\u003edev-\u003exdp_metadata_ops = \u0026gve_xdp_metadata_ops;\n \n-\tpriv-\u003erx_copybreak = GVE_DEFAULT_RX_COPYBREAK;\n-\t/* gvnic has one Notification Block per MSI-x vector, except for the\n-\t * management vector\n-\t */\n-\tpriv-\u003enum_ntfy_blks = (num_ntfy - 1) \u0026 ~0x1;\n-\tpriv-\u003emgmt_msix_idx = priv-\u003enum_ntfy_blks;\n-\tpriv-\u003enuma_node = dev_to_node(\u0026priv-\u003epdev-\u003edev);\n+\terr = gve_alloc_control_plane_resources(priv);\n+\tif (err)\n+\t\treturn err;\n+\n+\terr = gve_setup_control_plane_resources(priv);\n+\tif (err)\n+\t\treturn err;\n+\n+\terr = gve_setup_notify_blocks(priv);\n+\tif (err)\n+\t\treturn err;\n+\treturn 0;\n+}\n \n-\tpriv-\u003etx_cfg.max_queues =\n-\t\tmin_t(int, priv-\u003etx_cfg.max_queues, priv-\u003enum_ntfy_blks / 2);\n-\tpriv-\u003erx_cfg.max_queues =\n-\t\tmin_t(int, priv-\u003erx_cfg.max_queues, priv-\u003enum_ntfy_blks / 2);\n+static const struct gve_ctrl_ops gve_adminq_ops = {\n+\t.map_db_bar\t\t= gve_adminq_map_db_bar,\n+\t.unmap_db_bar\t\t= gve_adminq_unmap_db_bar,\n+\t.set_num_queues\t\t= gve_adminq_set_num_queues,\n+\t.set_num_ntfy_blks\t= gve_adminq_set_num_ntfy_blks,\n+\t.get_ptype_map\t\t= gve_adminq_get_ptype_map_dqo,\n+\t.reset_flow_rules\t= gve_adminq_reset_flow_rules,\n+\t.setup_stats_report\t= gve_adminq_report_stats,\n+\t.configure_rss\t\t= gve_adminq_configure_rss,\n+\t.request_db_info\t= gve_adminq_request_db_info,\n+\t.release_db_resources\t= gve_adminq_release_db_resources,\n+\t.setup_mgmt_irq\t\t= gve_adminq_setup_mgmt_irq,\n+\t.teardown_mgmt_irq\t= gve_adminq_teardown_mgmt_irq,\n+\t.create_queues\t\t= gve_adminq_create_queues,\n+\t.destroy_queues\t\t= gve_adminq_destroy_queues,\n+\t.report_link_status\t= gve_adminq_report_link_status,\n+\t.report_link_speed\t= gve_adminq_report_link_speed,\n+};\n+\n+static int gve_init_priv(struct gve_priv *priv)\n+{\n+\tstruct gve_device_info *device_info = \u0026priv-\u003edevice_info;\n+\tint err;\n \n-\tpriv-\u003etx_cfg.num_queues = priv-\u003etx_cfg.max_queues;\n-\tpriv-\u003erx_cfg.num_queues = priv-\u003erx_cfg.max_queues;\n-\tif (priv-\u003edefault_num_queues \u003e 0) {\n-\t\tpriv-\u003etx_cfg.num_queues = min_t(int, priv-\u003edefault_num_queues,\n-\t\t\t\t\t\tpriv-\u003etx_cfg.num_queues);\n-\t\tpriv-\u003erx_cfg.num_queues = min_t(int, priv-\u003edefault_num_queues,\n-\t\t\t\t\t\tpriv-\u003erx_cfg.num_queues);\n+\tpriv-\u003equeue_format = priv-\u003edevice_info.queue_format;\n+\n+\terr = priv-\u003ectrl_ops-\u003eset_num_ntfy_blks(priv);\n+\tif (err) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Could not setup notify blocks: err=%d\\n\", err);\n+\t\treturn err;\n \t}\n-\tpriv-\u003etx_cfg.num_xdp_queues = 0;\n \n+\tpriv-\u003ectrl_ops-\u003eset_num_queues(priv);\n \tdev_info(\u0026priv-\u003epdev-\u003edev, \"TX queues %d, RX queues %d\\n\",\n \t\t priv-\u003etx_cfg.num_queues, priv-\u003erx_cfg.num_queues);\n \tdev_info(\u0026priv-\u003epdev-\u003edev, \"Max TX queues %d, Max RX queues %d\\n\",\n \t\t priv-\u003etx_cfg.max_queues, priv-\u003erx_cfg.max_queues);\n \n-\tif (!gve_is_gqi(priv)) {\n+\tif (gve_is_dqo(priv)) {\n+\t\t/* DQO supports HW-GRO and UDP_GSO */\n+\t\tu64 additional_features = NETIF_F_GRO_HW | NETIF_F_GSO_UDP_L4;\n+\n+\t\tpriv-\u003edev-\u003ehw_features |= additional_features;\n+\t\tpriv-\u003edev-\u003efeatures |= additional_features;\n+\n \t\tpriv-\u003etx_coalesce_usecs = GVE_TX_IRQ_RATELIMIT_US_DQO;\n \t\tpriv-\u003erx_coalesce_usecs = GVE_RX_IRQ_RATELIMIT_US_DQO;\n-\t}\n-\n-\tpriv-\u003ets_config.tx_type = HWTSTAMP_TX_OFF;\n-\tpriv-\u003ets_config.rx_filter = HWTSTAMP_FILTER_NONE;\n \n-setup_device:\n-\tpriv-\u003exsk_pools = bitmap_zalloc(priv-\u003erx_cfg.max_queues, GFP_KERNEL);\n-\tif (!priv-\u003exsk_pools) {\n-\t\terr = -ENOMEM;\n-\t\tgoto err;\n+\t\t/* Big TCP is only supported on DQO */\n+\t\tnetif_set_tso_max_size(priv-\u003edev, GVE_DQO_TX_MAX);\n \t}\n \n-\tgve_set_netdev_xdp_features(priv);\n-\tif (!gve_is_gqi(priv))\n-\t\tpriv-\u003edev-\u003exdp_metadata_ops = \u0026gve_xdp_metadata_ops;\n+\tif (gve_set_mtu(priv))\n+\t\treturn -EINVAL;\n \n-\terr = gve_setup_device_resources(priv);\n-\tif (err)\n-\t\tgoto err_free_xsk_bitmap;\n+\tpriv-\u003enum_event_counters = device_info-\u003enum_event_counters;\n \n-\treturn 0;\n+\tgve_set_mac(priv);\n \n-err_free_xsk_bitmap:\n-\tbitmap_free(priv-\u003exsk_pools);\n-\tpriv-\u003exsk_pools = NULL;\n-err:\n-\tgve_adminq_free(\u0026priv-\u003epdev-\u003edev, priv);\n-\treturn err;\n-}\n+\tgve_set_queue_properties(priv);\n+\tpriv-\u003emodify_ring_size_enabled = device_info-\u003emodify_ring_size_enabled;\n \n-static void gve_teardown_priv_resources(struct gve_priv *priv)\n-{\n-\tgve_teardown_device_resources(priv);\n-\tgve_adminq_free(\u0026priv-\u003epdev-\u003edev, priv);\n-\tbitmap_free(priv-\u003exsk_pools);\n-\tpriv-\u003exsk_pools = NULL;\n-}\n+\tgve_set_buf_sizes(priv);\n \n-static void gve_trigger_reset(struct gve_priv *priv)\n-{\n-\t/* Reset the device by releasing the AQ */\n-\tgve_adminq_release(priv);\n-}\n+\tpriv-\u003emax_flow_rules = device_info-\u003emax_flow_rules;\n+\tif (priv-\u003emax_flow_rules)\n+\t\tpriv-\u003edev-\u003ehw_features |= NETIF_F_NTUPLE;\n \n-static void gve_reset_and_teardown(struct gve_priv *priv, bool was_up)\n-{\n-\tgve_trigger_reset(priv);\n-\t/* With the reset having already happened, close cannot fail */\n-\tif (was_up)\n-\t\tgve_close(priv-\u003edev);\n-\tgve_teardown_priv_resources(priv);\n+\tpriv-\u003erss_key_size = device_info-\u003erss_key_size;\n+\tpriv-\u003erss_lut_size = device_info-\u003erss_lut_size;\n+\tpriv-\u003ecache_rss_config = device_info-\u003ecache_rss_config;\n+\n+\tpriv-\u003enuma_node = dev_to_node(\u0026priv-\u003epdev-\u003edev);\n+\tpriv-\u003etx_cfg.num_xdp_queues = 0;\n+\tpriv-\u003erx_copybreak = GVE_DEFAULT_RX_COPYBREAK;\n+\tpriv-\u003ets_config.tx_type = HWTSTAMP_TX_OFF;\n+\tpriv-\u003ets_config.rx_filter = HWTSTAMP_FILTER_NONE;\n+\tpriv-\u003enic_timestamp_supported = device_info-\u003enic_timestamp_supported;\n+\treturn 0;\n }\n \n-static int gve_reset_recovery(struct gve_priv *priv, bool was_up)\n+static int gve_recover(struct gve_priv *priv, bool setup_queues)\n {\n+\tbool service_task_enabled = false;\n \tint err;\n \n-\terr = gve_init_priv(priv, true);\n+\terr = gve_adminq_init(priv);\n+\tif (err) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Failed to alloc admin queue: err=%d\\n\", err);\n+\t\tgoto teardown_device;\n+\t}\n+\n+\terr = gve_adminq_verify_driver_compatibility(priv);\n+\tif (err) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Could not verify driver compatibility: err=%d\\n\", err);\n+\t\tgoto teardown_device;\n+\t}\n+\n+\terr = gve_setup_device(priv);\n \tif (err)\n-\t\tgoto err;\n-\tif (was_up) {\n+\t\tgoto teardown_device;\n+\n+\t/* In the case of cascading failures, the disable count could be greater\n+\t * than 1. The service task must be enabled before starting queues.\n+\t */\n+\twhile (!service_task_enabled)\n+\t\tservice_task_enabled = enable_work(\u0026priv-\u003eservice_task);\n+\n+\tif (setup_queues) {\n \t\terr = gve_open(priv-\u003edev);\n \t\tif (err)\n-\t\t\tgoto err;\n+\t\t\tgoto disable_work;\n \t}\n+\n \treturn 0;\n-err:\n-\tdev_err(\u0026priv-\u003epdev-\u003edev, \"Reset failed! !!! DISABLING ALL QUEUES !!!\\n\");\n-\tgve_turndown(priv);\n+\n+disable_work:\n+\tdisable_work(\u0026priv-\u003eservice_task);\n+teardown_device:\n+\tdev_err(\u0026priv-\u003epdev-\u003edev, \"Recover failed! !!! DISABLING ALL QUEUES !!!\\n\");\n+\tgve_teardown_device(priv);\n \treturn err;\n }\n \n-int gve_reset(struct gve_priv *priv, bool attempt_teardown)\n+int gve_reset(struct gve_priv *priv, bool skip_queue_setup)\n {\n \tbool was_up = netif_running(priv-\u003edev);\n \tint err;\n \n+\tif (gve_get_reset_in_progress(priv))\n+\t\treturn 0;\n+\n \tdev_info(\u0026priv-\u003epdev-\u003edev, \"Performing reset\\n\");\n \tgve_clear_do_reset(priv);\n \tgve_set_reset_in_progress(priv);\n-\t/* If we aren't attempting to teardown normally, just go turndown and\n-\t * reset right away.\n-\t */\n-\tif (!attempt_teardown) {\n-\t\tgve_turndown(priv);\n-\t\tgve_reset_and_teardown(priv, was_up);\n-\t} else {\n-\t\t/* Otherwise attempt to close normally */\n-\t\tif (was_up) {\n-\t\t\terr = gve_close(priv-\u003edev);\n-\t\t\t/* If that fails reset as we did above */\n-\t\t\tif (err)\n-\t\t\t\tgve_reset_and_teardown(priv, was_up);\n+\n+\tif (was_up) {\n+\t\tgve_queues_stop(priv);\n+\t\tif (gve_get_device_rings_ok(priv)) {\n+\t\t\tgve_clear_device_rings_ok(priv);\n+\t\t\tgve_destroy_rings(priv);\n+\t\t\tgve_unregister_qpls(priv);\n \t\t}\n-\t\t/* Clean up any remaining resources */\n-\t\tgve_teardown_priv_resources(priv);\n \t}\n \n-\t/* Set it all back up */\n-\terr = gve_reset_recovery(priv, was_up);\n+\tdisable_work(\u0026priv-\u003eservice_task);\n+\tgve_teardown_device(priv);\n+\tgve_queues_mem_remove(priv);\n+\n+\terr = gve_recover(priv, was_up \u0026\u0026 !skip_queue_setup);\n+\tif (err)\n+\t\tdev_info(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t \"Failed to recover in reset: %d\\n\", err);\n+\n \tgve_clear_reset_in_progress(priv);\n \tpriv-\u003ereset_cnt++;\n \tpriv-\u003einterface_up_cnt = 0;\n@@ -2588,7 +2614,7 @@ int gve_reset(struct gve_priv *priv, bool attempt_teardown)\n \treturn err;\n }\n \n-static void gve_write_version(u8 __iomem *driver_version_register)\n+void gve_adminq_write_version(u8 __iomem *driver_version_register)\n {\n \tconst char *c = gve_version_prefix;\n \n@@ -2794,7 +2820,6 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)\n {\n \tint max_tx_queues, max_rx_queues;\n \tstruct net_device *dev;\n-\t__be32 __iomem *db_bar;\n \tstruct gve_registers __iomem *reg_bar;\n \tstruct gve_priv *priv;\n \tint err;\n@@ -2822,14 +2847,6 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)\n \t\tgoto abort_with_pci_region;\n \t}\n \n-\tdb_bar = pci_iomap(pdev, GVE_DOORBELL_BAR, 0);\n-\tif (!db_bar) {\n-\t\tdev_err(\u0026pdev-\u003edev, \"Failed to map doorbell bar!\\n\");\n-\t\terr = -ENOMEM;\n-\t\tgoto abort_with_reg_bar;\n-\t}\n-\n-\tgve_write_version(\u0026reg_bar-\u003edriver_version);\n \t/* Get max queues to alloc etherdev */\n \tmax_tx_queues = ioread32be(\u0026reg_bar-\u003emax_tx_queues);\n \tmax_rx_queues = ioread32be(\u0026reg_bar-\u003emax_rx_queues);\n@@ -2838,7 +2855,7 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)\n \tif (!dev) {\n \t\tdev_err(\u0026pdev-\u003edev, \"could not allocate netdev\\n\");\n \t\terr = -ENOMEM;\n-\t\tgoto abort_with_db_bar;\n+\t\tgoto abort_with_reg_bar;\n \t}\n \tSET_NETDEV_DEV(dev, \u0026pdev-\u003edev);\n \tpci_set_drvdata(pdev, dev);\n@@ -2870,35 +2887,65 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)\n \tpriv-\u003epdev = pdev;\n \tpriv-\u003emsg_enable = DEFAULT_MSG_LEVEL;\n \tpriv-\u003ereg_bar0 = reg_bar;\n-\tpriv-\u003edb_bar2 = db_bar;\n \tpriv-\u003eservice_task_flags = 0x0;\n \tpriv-\u003estate_flags = 0x0;\n \tpriv-\u003eethtool_flags = 0x0;\n \tpriv-\u003erx_cfg.packet_buffer_size = GVE_DEFAULT_RX_BUFFER_SIZE;\n \tpriv-\u003emax_rx_buffer_size = GVE_DEFAULT_RX_BUFFER_SIZE;\n \n+\terr = gve_adminq_init(priv);\n+\tif (err) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Failed to alloc admin queue: err=%d\\n\", err);\n+\t\tgoto abort_with_netdev;\n+\t}\n+\n+\tpriv-\u003edevice_info.queue_format = GVE_QUEUE_FORMAT_UNSPECIFIED;\n+\terr = gve_adminq_get_device_properties(priv);\n+\tif (err) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Could not get device information: err=%d\\n\", err);\n+\t\tgoto abort_with_adminq;\n+\t}\n+\n+\t/* Set adminq ctrl ops */\n+\tpriv-\u003ectrl_ops = \u0026gve_adminq_ops;\n+\n+\terr = priv-\u003ectrl_ops-\u003emap_db_bar(priv);\n+\tif (err) {\n+\t\terr = -ENOMEM;\n+\t\tgoto abort_with_adminq;\n+\t}\n+\n \tgve_set_probe_in_progress(priv);\n \tpriv-\u003egve_wq = alloc_ordered_workqueue(\"gve\", 0);\n \tif (!priv-\u003egve_wq) {\n \t\tdev_err(\u0026pdev-\u003edev, \"Could not allocate workqueue\");\n \t\terr = -ENOMEM;\n-\t\tgoto abort_with_netdev;\n+\t\tgoto abort_with_unmap_db_bar;\n \t}\n \tINIT_WORK(\u0026priv-\u003eservice_task, gve_service_task);\n \tINIT_WORK(\u0026priv-\u003estats_report_task, gve_stats_report_task);\n \tpriv-\u003etx_cfg.max_queues = max_tx_queues;\n \tpriv-\u003erx_cfg.max_queues = max_rx_queues;\n \n-\terr = gve_init_priv(priv, false);\n+\terr = gve_init_priv(priv);\n \tif (err)\n \t\tgoto abort_with_wq;\n \n+\terr = gve_setup_device(priv);\n+\tif (err) {\n+\t\tdev_err(\u0026priv-\u003epdev-\u003edev,\n+\t\t\t\"Could not setup device: err=%d\\n\", err);\n+\t\tgoto abort_teardown_device;\n+\t}\n+\n \tif (!gve_is_gqi(priv) \u0026\u0026 !gve_is_qpl(priv))\n \t\tdev-\u003enetmem_tx = NETMEM_TX_DMA;\n \n \terr = register_netdev(dev);\n \tif (err)\n-\t\tgoto abort_with_gve_init;\n+\t\tgoto abort_teardown_device;\n \n \tdev_info(\u0026pdev-\u003edev, \"GVE version %s\\n\", gve_version_str);\n \tdev_info(\u0026pdev-\u003edev, \"GVE queue format %d\\n\", (int)priv-\u003equeue_format);\n@@ -2906,18 +2953,22 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)\n \tqueue_work(priv-\u003egve_wq, \u0026priv-\u003eservice_task);\n \treturn 0;\n \n-abort_with_gve_init:\n-\tgve_teardown_priv_resources(priv);\n+abort_teardown_device:\n+\tdisable_work(\u0026priv-\u003estats_report_task);\n+\tgve_teardown_device(priv);\n \n abort_with_wq:\n \tdestroy_workqueue(priv-\u003egve_wq);\n \n+abort_with_unmap_db_bar:\n+\tpriv-\u003ectrl_ops-\u003eunmap_db_bar(priv);\n+\n+abort_with_adminq:\n+\tgve_adminq_free(priv);\n+\n abort_with_netdev:\n \tfree_netdev(dev);\n \n-abort_with_db_bar:\n-\tpci_iounmap(pdev, db_bar);\n-\n abort_with_reg_bar:\n \tpci_iounmap(pdev, reg_bar);\n \n@@ -2933,14 +2984,14 @@ static void gve_remove(struct pci_dev *pdev)\n {\n \tstruct net_device *netdev = pci_get_drvdata(pdev);\n \tstruct gve_priv *priv = netdev_priv(netdev);\n-\t__be32 __iomem *db_bar = priv-\u003edb_bar2;\n \tvoid __iomem *reg_bar = priv-\u003ereg_bar0;\n \n \tunregister_netdev(netdev);\n-\tgve_teardown_priv_resources(priv);\n+\tdisable_work_sync(\u0026priv-\u003eservice_task);\n+\tgve_teardown_device(priv);\n \tdestroy_workqueue(priv-\u003egve_wq);\n+\tpriv-\u003ectrl_ops-\u003eunmap_db_bar(priv);\n \tfree_netdev(netdev);\n-\tpci_iounmap(pdev, db_bar);\n \tpci_iounmap(pdev, reg_bar);\n \tpci_release_regions(pdev);\n \tpci_disable_device(pdev);\n@@ -2953,16 +3004,13 @@ static void gve_shutdown(struct pci_dev *pdev)\n \tbool was_up = netif_running(priv-\u003edev);\n \n \tnetif_device_detach(netdev);\n+\tdisable_work_sync(\u0026priv-\u003eservice_task);\n \n \trtnl_lock();\n \tnetdev_lock(netdev);\n-\tif (was_up \u0026\u0026 gve_close(priv-\u003edev)) {\n-\t\t/* If the dev was up, attempt to close, if close fails, reset */\n-\t\tgve_reset_and_teardown(priv, was_up);\n-\t} else {\n-\t\t/* If the dev wasn't up or close worked, finish tearing down */\n-\t\tgve_teardown_priv_resources(priv);\n-\t}\n+\tif (was_up)\n+\t\tgve_close(priv-\u003edev);\n+\tgve_teardown_device(priv);\n \tnetdev_unlock(netdev);\n \trtnl_unlock();\n }\n@@ -2974,16 +3022,14 @@ static int gve_suspend(struct device *dev)\n \tstruct gve_priv *priv = netdev_priv(netdev);\n \tbool was_up = netif_running(priv-\u003edev);\n \n+\tdisable_work_sync(\u0026priv-\u003eservice_task);\n+\n \tpriv-\u003esuspend_cnt++;\n \trtnl_lock();\n \tnetdev_lock(netdev);\n-\tif (was_up \u0026\u0026 gve_close(priv-\u003edev)) {\n-\t\t/* If the dev was up, attempt to close, if close fails, reset */\n-\t\tgve_reset_and_teardown(priv, was_up);\n-\t} else {\n-\t\t/* If the dev wasn't up or close worked, finish tearing down */\n-\t\tgve_teardown_priv_resources(priv);\n-\t}\n+\tif (was_up)\n+\t\tgve_close(priv-\u003edev);\n+\tgve_teardown_device(priv);\n \tpriv-\u003eup_before_suspend = was_up;\n \tnetdev_unlock(netdev);\n \trtnl_unlock();\n@@ -3000,7 +3046,7 @@ static int gve_resume(struct device *dev)\n \tpriv-\u003eresume_cnt++;\n \trtnl_lock();\n \tnetdev_lock(netdev);\n-\terr = gve_reset_recovery(priv, priv-\u003eup_before_suspend);\n+\terr = gve_recover(priv, priv-\u003eup_before_suspend);\n \tnetdev_unlock(netdev);\n \trtnl_unlock();\n \treturn err;\n"
} |
| 2/1 | 2026/08/14 02:37 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"NET_VENDOR_GOOGLE",
"GVE"
],
"FocusSymbols": [
"gve_reset",
"gve_add_flow_rule",
"gve_del_flow_rule",
"gve_adminq_init",
"gve_adminq_get_device_properties",
"gve_adminq_verify_driver_compatibility",
"gve_adminq_create_queues",
"gve_adminq_destroy_queues",
"gve_adminq_setup_mgmt_irq",
"gve_adminq_request_db_info",
"gve_adminq_set_num_ntfy_blks",
"gve_adminq_map_db_bar",
"gve_adminq_report_link_status",
"gve_adminq_get_ptype_map_dqo",
"gve_adminq_describe_device"
],
"Reasoning": "The patch refactors the control plane operations of the Google Virtual Ethernet (gve) driver. It introduces `struct gve_ctrl_ops` and `struct gve_device_info` to abstract control plane operations and device information. This involves significant changes to driver initialization, teardown, reset, and admin queue operations. The gve driver is reachable in virtualized environments like GCE, which is a primary target for syzkaller. Therefore, these functional changes are worth fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit ef1a8b6afc61cfca9d272da37e710ba20b159c77
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 14 02:37:30 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h
index c280ff35ee771..3a50e7f7a2e7a 100644
--- a/drivers/net/ethernet/google/gve/gve.h
+++ b/drivers/net/ethernet/google/gve/gve.h
@@ -16,6 +16,8 @@
#include <linux/timer.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/u64_stats_sync.h>
+#include <linux/utsname.h>
+#include <linux/version.h>
#include <net/page_pool/helpers.h>
#include <net/xdp.h>
@@ -674,6 +676,7 @@ struct gve_notify_block {
struct gve_tx_ring *tx; /* tx rings on this block */
struct gve_rx_ring *rx; /* rx rings on this block */
u32 irq;
+ bool irq_requested;
};
/* Tracks allowed and current rx queue settings */
@@ -797,6 +800,79 @@ struct gve_ptp {
struct gve_priv *priv;
};
+struct gve_device_info {
+ enum gve_queue_format queue_format;
+ u16 default_tx_queues;
+ u16 default_rx_queues;
+ u16 max_tx_queues;
+ u16 max_rx_queues;
+ u16 default_tx_ring_size;
+ u16 default_rx_ring_size;
+ u16 max_tx_ring_size;
+ u16 max_rx_ring_size;
+ u16 min_tx_ring_size;
+ u16 min_rx_ring_size;
+ u16 max_mtu;
+ u8 mac[ETH_ALEN];
+ u16 max_rx_buffer_size;
+ u16 header_buf_size;
+ u32 max_flow_rules;
+ u16 rss_key_size;
+ u16 rss_lut_size;
+ u16 tx_pages_per_qpl;
+ u16 num_event_counters;
+ u64 max_registered_pages;
+ bool default_min_ring_size;
+ bool nic_timestamp_supported;
+ bool modify_ring_size_enabled;
+ bool cache_rss_config;
+};
+
+/**
+ * struct gve_ctrl_ops - Control plane operations structure
+ * @map_db_bar: Maps the doorbell BAR for the device and store in @priv.
+ * @unmap_db_bar: Unmaps the doorbell BAR previously mapped by @map_db_bar.
+ * @set_num_queues: Sets default and max TX/RX queues into allocation
+ * structures stored in @priv to be used during initialization.
+ * @set_num_ntfy_blks: Sets no. of vectors into @priv to be used during
+ * initialization.
+ * @request_db_info: Request and store doorbell information into @priv
+ * @release_db_resources: Release device hold on DMA memory holding doorbell
+ * info (AdminQ only)
+ * @setup_mgmt_irq: Setup control plane IRQ
+ * @teardown_mgmt_irq: Teardown control plane IRQ
+ * @get_ptype_map: Learn packet type map from device and store it in @priv
+ * @configure_rss: Set up default RSS configuration
+ * @setup_stats_report: Set up DMA region for stats report (AdminQ only)
+ * @reset_flow_rules: Flush all flow rules from device
+ * @create_queues: Sends commands to the device to create TX/RX queues.
+ * @destroy_queues: Sends commands to the device to destroy TX/RX queues.
+ * @report_link_status: Set link status into @priv->link_up
+ * @report_link_speed: Set link speed into @priv->link_speed
+ */
+struct gve_ctrl_ops {
+ int (*map_db_bar)(struct gve_priv *priv);
+ void (*unmap_db_bar)(struct gve_priv *priv);
+ void (*set_num_queues)(struct gve_priv *priv);
+ int (*set_num_ntfy_blks)(struct gve_priv *priv);
+ int (*request_db_info)(struct gve_priv *priv);
+ void (*release_db_resources)(struct gve_priv *priv);
+ int (*setup_mgmt_irq)(struct gve_priv *priv);
+ void (*teardown_mgmt_irq)(struct gve_priv *priv);
+ int (*get_ptype_map)(struct gve_priv *priv);
+ int (*configure_rss)(struct gve_priv *priv,
+ struct ethtool_rxfh_param *param);
+ int (*setup_stats_report)(struct gve_priv *priv,
+ u64 stats_report_len,
+ dma_addr_t stats_report_addr,
+ u64 interval_ms); /* AQ-specific */
+ int (*reset_flow_rules)(struct gve_priv *priv);
+ int (*create_queues)(struct gve_priv *priv);
+ int (*destroy_queues)(struct gve_priv *priv);
+ int (*report_link_status)(struct gve_priv *priv);
+ int (*report_link_speed)(struct gve_priv *priv);
+};
+
struct gve_priv {
struct net_device *dev;
struct gve_tx_ring *tx; /* array of tx_cfg.num_queues */
@@ -896,8 +972,10 @@ struct gve_priv {
/* Gvnic device link speed from hypervisor. */
u64 link_speed;
+ bool link_up;
bool up_before_suspend; /* True if dev was up before suspend */
+ bool mgmt_irq_requested;
struct gve_ptype_lut *ptype_lut_dqo;
/* Must be a power of two. */
@@ -929,6 +1007,8 @@ struct gve_priv {
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 */
+ struct gve_device_info device_info;
+ const struct gve_ctrl_ops *ctrl_ops;
};
enum gve_service_task_flags_bit {
@@ -1104,6 +1184,11 @@ static inline u32 gve_rx_idx_to_ntfy(struct gve_priv *priv, u32 queue_idx)
return (priv->num_ntfy_blks / 2) + queue_idx;
}
+static inline u32 gve_ntfy_to_msix_idx(struct gve_priv *priv, u32 ntfy_blk_idx)
+{
+ return ntfy_blk_idx;
+}
+
static inline bool gve_is_qpl(struct gve_priv *priv)
{
return priv->queue_format == GVE_GQI_QPL_FORMAT ||
@@ -1207,6 +1292,8 @@ static inline bool gve_is_clock_enabled(struct gve_priv *priv)
return priv->nic_ts_report;
}
+void gve_adminq_write_version(u8 __iomem *driver_version_register);
+
/* gqi napi handler defined in gve_main.c */
int gve_napi_poll(struct napi_struct *napi, int budget);
@@ -1300,7 +1387,7 @@ struct page_pool *gve_rx_create_page_pool(struct gve_priv *priv,
/* Reset */
void gve_schedule_reset(struct gve_priv *priv);
-int gve_reset(struct gve_priv *priv, bool attempt_teardown);
+int gve_reset(struct gve_priv *priv, bool skip_queue_setup);
void gve_get_curr_alloc_cfgs(struct gve_priv *priv,
struct gve_tx_alloc_rings_cfg *tx_alloc_cfg,
struct gve_rx_alloc_rings_cfg *rx_alloc_cfg);
diff --git a/drivers/net/ethernet/google/gve/gve_adminq.c b/drivers/net/ethernet/google/gve/gve_adminq.c
index 08587bf40ed4a..bbf431794d02b 100644
--- a/drivers/net/ethernet/google/gve/gve_adminq.c
+++ b/drivers/net/ethernet/google/gve/gve_adminq.c
@@ -70,7 +70,7 @@ void gve_parse_device_option(struct gve_priv *priv,
dev_info(&priv->pdev->dev,
"Gqi raw addressing device option enabled.\n");
- priv->queue_format = GVE_GQI_RDA_FORMAT;
+ priv->device_info.queue_format = GVE_GQI_RDA_FORMAT;
break;
case GVE_DEV_OPT_ID_GQI_RDA:
if (option_length < sizeof(**dev_op_gqi_rda) ||
@@ -190,7 +190,7 @@ void gve_parse_device_option(struct gve_priv *priv,
/* device has not provided min ring size */
if (option_length == GVE_DEVICE_OPTION_NO_MIN_RING_SIZE)
- priv->default_min_ring_size = true;
+ priv->device_info.default_min_ring_size = true;
break;
case GVE_DEV_OPT_ID_FLOW_STEERING:
if (option_length < sizeof(**dev_op_flow_steering) ||
@@ -296,8 +296,10 @@ gve_process_device_options(struct gve_priv *priv,
return 0;
}
-int gve_adminq_alloc(struct device *dev, struct gve_priv *priv)
+static int gve_adminq_alloc(struct gve_priv *priv)
{
+ struct device *dev = &priv->pdev->dev;
+
priv->adminq_pool = dma_pool_create("adminq_pool", dev,
GVE_ADMINQ_BUFFER_SIZE, 0, 0);
if (unlikely(!priv->adminq_pool))
@@ -353,7 +355,15 @@ int gve_adminq_alloc(struct device *dev, struct gve_priv *priv)
return 0;
}
-void gve_adminq_release(struct gve_priv *priv)
+int gve_adminq_init(struct gve_priv *priv)
+{
+ struct gve_registers __iomem *reg_bar = priv->reg_bar0;
+
+ gve_adminq_write_version(®_bar->driver_version);
+ return gve_adminq_alloc(priv);
+}
+
+static void gve_adminq_release(struct gve_priv *priv)
{
int i = 0;
@@ -382,10 +392,9 @@ void gve_adminq_release(struct gve_priv *priv)
}
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)
+void gve_adminq_free(struct gve_priv *priv)
{
if (!gve_get_admin_queue_ok(priv))
return;
@@ -920,26 +929,40 @@ int gve_adminq_destroy_rx_queues(struct gve_priv *priv, u32 num_queues)
return err;
}
-static void gve_set_default_desc_cnt(struct gve_priv *priv,
- const struct gve_device_descriptor *descriptor)
+static void gve_set_default_rss_sizes(struct gve_priv *priv)
{
- priv->tx_desc_cnt = be16_to_cpu(descriptor->tx_queue_entries);
- priv->rx_desc_cnt = be16_to_cpu(descriptor->rx_queue_entries);
+ struct gve_device_info *device_info = &priv->device_info;
- /* 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;
+ if (device_info->queue_format == GVE_DQO_RDA_FORMAT ||
+ device_info->queue_format == GVE_DQO_QPL_FORMAT) {
+ device_info->rss_key_size = GVE_RSS_KEY_SIZE;
+ device_info->rss_lut_size = GVE_RSS_INDIR_SIZE;
+ device_info->cache_rss_config = true;
+ }
}
-static void gve_set_default_rss_sizes(struct gve_priv *priv)
+int gve_adminq_destroy_queues(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;
+ int num_tx_queues = gve_num_tx_queues(priv);
+ int err;
+
+ err = gve_adminq_destroy_tx_queues(priv, 0, num_tx_queues);
+ if (err) {
+ netif_err(priv, drv, priv->dev,
+ "failed to destroy tx queues\n");
+ /* This failure will trigger a reset - no need to clean up */
+ return err;
}
+ netif_dbg(priv, drv, priv->dev, "destroyed tx queues\n");
+ err = gve_adminq_destroy_rx_queues(priv, priv->rx_cfg.num_queues);
+ if (err) {
+ netif_err(priv, drv, priv->dev,
+ "failed to destroy rx queues\n");
+ /* This failure will trigger a reset - no need to clean up */
+ return err;
+ }
+ netif_dbg(priv, drv, priv->dev, "destroyed rx queues\n");
+ return err;
}
static void gve_enable_supported_features(struct gve_priv *priv,
@@ -959,77 +982,105 @@ static void gve_enable_supported_features(struct gve_priv *priv,
const struct gve_device_option_modify_ring
*dev_op_modify_ring)
{
+ struct gve_device_info *info = &priv->device_info;
+
/* 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.
+ * device_info->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);
+ info->max_mtu = be16_to_cpu(dev_op_jumbo_frames->max_mtu);
}
if (dev_op_buffer_sizes &&
(supported_features_mask & GVE_SUP_BUFFER_SIZES_MASK)) {
- priv->max_rx_buffer_size =
+ info->max_rx_buffer_size =
be16_to_cpu(dev_op_buffer_sizes->packet_buffer_size);
- priv->header_buf_size =
+ info->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);
- if (gve_is_dqo(priv) &&
- priv->max_rx_buffer_size > GVE_DEFAULT_RX_BUFFER_SIZE)
- priv->rx_cfg.packet_buffer_size =
- priv->max_rx_buffer_size;
+ info->max_rx_buffer_size, info->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;
- priv->max_rx_desc_cnt =
+ info->modify_ring_size_enabled = true;
+ info->max_rx_ring_size =
be16_to_cpu(dev_op_modify_ring->max_rx_ring_size);
- priv->max_tx_desc_cnt =
+ info->max_tx_ring_size =
be16_to_cpu(dev_op_modify_ring->max_tx_ring_size);
- if (priv->default_min_ring_size) {
+ if (info->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;
+ info->min_tx_ring_size = GVE_DEFAULT_MIN_TX_RING_SIZE;
+ info->min_rx_ring_size = 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);
+ info->min_rx_ring_size =
+ be16_to_cpu(dev_op_modify_ring->min_rx_ring_size);
+ info->min_tx_ring_size =
+ 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 =
+ info->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);
+ info->max_flow_rules);
}
}
if (dev_op_rss_config &&
(supported_features_mask & GVE_SUP_RSS_CONFIG_MASK)) {
- priv->rss_key_size =
+ info->rss_key_size =
be16_to_cpu(dev_op_rss_config->hash_key_size);
- priv->rss_lut_size =
+ info->rss_lut_size =
be16_to_cpu(dev_op_rss_config->hash_lut_size);
- priv->cache_rss_config = false;
+ info->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);
+ info->rss_key_size, info->rss_lut_size);
}
if (dev_op_nic_timestamp &&
(supported_features_mask & GVE_SUP_NIC_TIMESTAMP_MASK))
- priv->nic_timestamp_supported = true;
+ info->nic_timestamp_supported = true;
+}
+
+static void gve_fill_device_info(struct gve_priv *priv,
+ struct gve_device_descriptor *descriptor)
+{
+ struct gve_device_info *device_info = &priv->device_info;
+ u16 default_num_queues;
+
+ device_info->tx_pages_per_qpl =
+ be16_to_cpu(descriptor->tx_pages_per_qpl);
+ device_info->max_registered_pages =
+ be64_to_cpu(descriptor->max_registered_pages);
+ device_info->num_event_counters = be16_to_cpu(descriptor->counters);
+ ether_addr_copy(device_info->mac, descriptor->mac);
+ device_info->max_mtu = be16_to_cpu(descriptor->mtu);
+
+ default_num_queues = be16_to_cpu(descriptor->default_num_queues);
+ device_info->default_tx_queues = default_num_queues;
+ device_info->default_rx_queues = default_num_queues;
+ device_info->default_tx_ring_size =
+ be16_to_cpu(descriptor->tx_queue_entries);
+ device_info->default_rx_ring_size =
+ be16_to_cpu(descriptor->rx_queue_entries);
+
+ /* set default ranges */
+ device_info->max_tx_ring_size = device_info->default_tx_ring_size;
+ device_info->max_rx_ring_size = device_info->default_rx_ring_size;
+ device_info->min_tx_ring_size = device_info->default_tx_ring_size;
+ device_info->min_rx_ring_size = device_info->default_rx_ring_size;
}
int gve_adminq_describe_device(struct gve_priv *priv)
@@ -1040,6 +1091,7 @@ int gve_adminq_describe_device(struct gve_priv *priv)
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_info *device_info = &priv->device_info;
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;
@@ -1049,8 +1101,6 @@ int gve_adminq_describe_device(struct gve_priv *priv)
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,
@@ -1085,26 +1135,26 @@ int gve_adminq_describe_device(struct gve_priv *priv)
* DqoRda, DqoQpl, GqiRda, GqiQpl. Use GqiQpl as default.
*/
if (dev_op_dqo_rda) {
- priv->queue_format = GVE_DQO_RDA_FORMAT;
+ device_info->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;
+ device_info->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;
+ device_info->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) {
+ } else if (device_info->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;
+ device_info->queue_format = GVE_GQI_QPL_FORMAT;
if (dev_op_gqi_qpl)
supported_features_mask =
be32_to_cpu(dev_op_gqi_qpl->supported_features_mask);
@@ -1112,35 +1162,9 @@ int gve_adminq_describe_device(struct gve_priv *priv)
"Driver is running with GQI QPL queue format.\n");
}
- /* set default descriptor counts */
- gve_set_default_desc_cnt(priv, descriptor);
-
+ gve_fill_device_info(priv, descriptor);
gve_set_default_rss_sizes(priv);
- /* DQO supports HW-GRO and UDP_GSO */
- if (gve_is_dqo(priv)) {
- u64 additional_features = NETIF_F_GRO_HW | NETIF_F_GSO_UDP_L4;
-
- priv->dev->hw_features |= additional_features;
- priv->dev->features |= additional_features;
- }
-
- 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,
@@ -1152,6 +1176,27 @@ int gve_adminq_describe_device(struct gve_priv *priv)
return err;
}
+int __maybe_unused gve_adminq_get_device_properties(struct gve_priv *priv)
+{
+ int err;
+
+ err = gve_adminq_verify_driver_compatibility(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not verify driver compatibility: err=%d\n", err);
+ return err;
+ }
+
+ /* Get the initial information we need from the device */
+ err = gve_adminq_describe_device(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not get device information: err=%d\n", err);
+ return err;
+ }
+ return 0;
+}
+
int gve_adminq_register_page_list(struct gve_priv *priv,
struct gve_queue_page_list *qpl)
{
@@ -1214,20 +1259,53 @@ int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len,
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)
+int gve_adminq_verify_driver_compatibility(struct gve_priv *priv)
{
+ struct gve_driver_info *driver_info;
union gve_adminq_command cmd;
+ dma_addr_t driver_info_bus;
+ int err;
+
+ driver_info = dma_alloc_coherent(&priv->pdev->dev,
+ sizeof(struct gve_driver_info),
+ &driver_info_bus, GFP_KERNEL);
+ if (!driver_info)
+ return -ENOMEM;
+
+ *driver_info = (struct gve_driver_info) {
+ .os_type = 1, /* Linux */
+ .os_version_major = cpu_to_be32(LINUX_VERSION_MAJOR),
+ .os_version_minor = cpu_to_be32(LINUX_VERSION_SUBLEVEL),
+ .os_version_sub = cpu_to_be32(LINUX_VERSION_PATCHLEVEL),
+ .driver_capability_flags = {
+ cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS1),
+ cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS2),
+ cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS3),
+ cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS4),
+ },
+ };
+ strscpy(driver_info->os_version_str1, utsname()->release,
+ sizeof(driver_info->os_version_str1));
+ strscpy(driver_info->os_version_str2, utsname()->version,
+ sizeof(driver_info->os_version_str2));
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),
+ .driver_info_len = cpu_to_be64(sizeof(struct gve_driver_info)),
+ .driver_info_addr = cpu_to_be64(driver_info_bus),
};
- return gve_adminq_execute_cmd(priv, &cmd);
+ err = gve_adminq_execute_cmd(priv, &cmd);
+
+ /* It's ok if the device doesn't support this */
+ if (err == -EOPNOTSUPP)
+ err = 0;
+
+ dma_free_coherent(&priv->pdev->dev,
+ sizeof(struct gve_driver_info),
+ driver_info, driver_info_bus);
+ return err;
}
int gve_adminq_report_link_speed(struct gve_priv *priv)
@@ -1273,8 +1351,7 @@ int gve_adminq_report_nic_ts(struct gve_priv *priv,
return gve_adminq_execute_cmd(priv, &cmd);
}
-int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv,
- struct gve_ptype_lut *ptype_lut)
+int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv)
{
struct gve_ptype_map *ptype_map;
union gve_adminq_command cmd;
@@ -1300,9 +1377,9 @@ int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv,
/* Populate ptype_lut. */
for (i = 0; i < GVE_NUM_PTYPES; i++) {
- ptype_lut->ptypes[i].l3_type =
+ priv->ptype_lut_dqo->ptypes[i].l3_type =
ptype_map->ptypes[i].l3_type;
- ptype_lut->ptypes[i].l4_type =
+ priv->ptype_lut_dqo->ptypes[i].l4_type =
ptype_map->ptypes[i].l4_type;
}
err:
@@ -1320,12 +1397,8 @@ gve_adminq_configure_flow_rule(struct gve_priv *priv,
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) {
+ if (!err)
priv->flow_rules_cache.rules_cache_synced = false;
- }
return err;
}
@@ -1600,3 +1673,174 @@ int gve_adminq_query_rss_config(struct gve_priv *priv, struct ethtool_rxfh_param
dma_pool_free(priv->adminq_pool, descriptor, descriptor_bus);
return err;
}
+
+int gve_adminq_set_num_ntfy_blks(struct gve_priv *priv)
+{
+ int num_ntfy;
+
+ num_ntfy = pci_msix_vec_count(priv->pdev);
+ if (num_ntfy <= 0) {
+ dev_err(&priv->pdev->dev,
+ "could not count MSI-x vectors: err=%d\n", num_ntfy);
+ return num_ntfy;
+ } else if (num_ntfy < GVE_MIN_MSIX) {
+ dev_err(&priv->pdev->dev, "gve needs at least %d MSI-x vectors, but only has %d\n",
+ GVE_MIN_MSIX, num_ntfy);
+ return -EINVAL;
+ }
+
+ /* gvnic has one Notification Block per MSI-x vector, except for the
+ * management vector
+ */
+ priv->num_ntfy_blks = (num_ntfy - 1) & ~0x1;
+ priv->mgmt_msix_idx = priv->num_ntfy_blks;
+
+ return 0;
+}
+
+void gve_adminq_set_num_queues(struct gve_priv *priv)
+{
+ priv->tx_cfg.max_queues =
+ min_t(int, priv->tx_cfg.max_queues, priv->num_ntfy_blks / 2);
+ priv->rx_cfg.max_queues =
+ min_t(int, priv->rx_cfg.max_queues, priv->num_ntfy_blks / 2);
+
+ priv->tx_cfg.num_queues = priv->tx_cfg.max_queues;
+ priv->rx_cfg.num_queues = priv->rx_cfg.max_queues;
+ if (priv->default_num_queues > 0) {
+ priv->tx_cfg.num_queues = min_t(int, priv->default_num_queues,
+ priv->tx_cfg.num_queues);
+ priv->rx_cfg.num_queues = min_t(int, priv->default_num_queues,
+ priv->rx_cfg.num_queues);
+ }
+}
+
+int gve_adminq_map_db_bar(struct gve_priv *priv)
+{
+ struct pci_dev *pdev = priv->pdev;
+ void __iomem *db_bar;
+
+ db_bar = pci_iomap(pdev, GVE_DOORBELL_BAR, 0);
+ if (!db_bar) {
+ dev_err(&pdev->dev, "Failed to map doorbell bar!\n");
+ return -ENOMEM;
+ }
+ priv->db_bar2 = db_bar;
+ return 0;
+}
+
+void gve_adminq_unmap_db_bar(struct gve_priv *priv)
+{
+ struct pci_dev *pdev = priv->pdev;
+
+ pci_iounmap(pdev, priv->db_bar2);
+}
+
+int gve_adminq_request_db_info(struct gve_priv *priv)
+{
+ int err;
+ int i;
+
+ err = gve_adminq_configure_device_resources(priv,
+ priv->counter_array_bus,
+ priv->num_event_counters,
+ priv->irq_db_indices_bus,
+ priv->num_ntfy_blks);
+ if (unlikely(err)) {
+ dev_err(&priv->pdev->dev,
+ "could not setup device_resources: err=%d\n", err);
+ return -ENXIO;
+ }
+
+ for (i = 0; i < priv->num_ntfy_blks; i++)
+ priv->ntfy_blocks[i].irq_db_index =
+ &priv->irq_db_indices[i].index;
+ return 0;
+}
+
+void gve_adminq_release_db_resources(struct gve_priv *priv)
+{
+ int err;
+
+ /* Log error in deconfigure device, but don't fail. This is only ever
+ * called as a reset is about to be triggered, so it would be redundant
+ * to trigger a reset.
+ */
+ err = gve_adminq_deconfigure_device_resources(priv);
+ if (err)
+ dev_err(&priv->pdev->dev,
+ "Could not deconfigure device resources: err=%d\n",
+ err);
+}
+
+static irqreturn_t gve_mgmnt_intr(int irq, void *arg)
+{
+ struct gve_priv *priv = arg;
+
+ /* Device resources are not okay; consider the interrupt spurious. */
+ if (!gve_get_device_resources_ok(priv))
+ return IRQ_NONE;
+
+ queue_work(priv->gve_wq, &priv->service_task);
+ return IRQ_HANDLED;
+}
+
+int gve_adminq_setup_mgmt_irq(struct gve_priv *priv)
+{
+ int err;
+
+ snprintf(priv->mgmt_msix_name, sizeof(priv->mgmt_msix_name),
+ "gve-mgmnt@pci:%s", pci_name(priv->pdev));
+ err = request_irq(priv->msix_vectors[priv->mgmt_msix_idx].vector,
+ gve_mgmnt_intr, 0, priv->mgmt_msix_name, priv);
+ if (err)
+ return err;
+
+ priv->mgmt_irq_requested = true;
+
+ return 0;
+}
+
+void gve_adminq_teardown_mgmt_irq(struct gve_priv *priv)
+{
+ if (priv->mgmt_irq_requested) {
+ free_irq(priv->msix_vectors[priv->mgmt_msix_idx].vector, priv);
+ priv->mgmt_irq_requested = false;
+ }
+}
+
+int gve_adminq_create_queues(struct gve_priv *priv)
+{
+ int num_tx_queues = gve_num_tx_queues(priv);
+ int err;
+
+ err = gve_adminq_create_tx_queues(priv, 0, num_tx_queues);
+ if (err) {
+ netif_err(priv, drv, priv->dev, "failed to create %d tx queues\n",
+ num_tx_queues);
+ return err;
+ }
+ netif_dbg(priv, drv, priv->dev, "created %d tx queues\n",
+ num_tx_queues);
+
+ err = gve_adminq_create_rx_queues(priv, priv->rx_cfg.num_queues);
+ if (err) {
+ netif_err(priv, drv, priv->dev, "failed to create %d rx queues\n",
+ priv->rx_cfg.num_queues);
+ return err;
+ }
+ netif_dbg(priv, drv, priv->dev, "created %d rx queues\n",
+ priv->rx_cfg.num_queues);
+
+ return err;
+}
+
+int gve_adminq_report_link_status(struct gve_priv *priv)
+{
+ u32 status;
+
+ status = ioread32be(&priv->reg_bar0->device_status);
+ WRITE_ONCE(priv->link_up,
+ !!(GVE_DEVICE_STATUS_LINK_STATUS_MASK & status));
+ return 0;
+}
diff --git a/drivers/net/ethernet/google/gve/gve_adminq.h b/drivers/net/ethernet/google/gve/gve_adminq.h
index 22a74b6aa17ea..8daac5fd685ab 100644
--- a/drivers/net/ethernet/google/gve/gve_adminq.h
+++ b/drivers/net/ethernet/google/gve/gve_adminq.h
@@ -619,9 +619,8 @@ union gve_adminq_command {
static_assert(sizeof(union gve_adminq_command) == 64);
-int gve_adminq_alloc(struct device *dev, struct gve_priv *priv);
-void gve_adminq_free(struct device *dev, struct gve_priv *priv);
-void gve_adminq_release(struct gve_priv *priv);
+int gve_adminq_init(struct gve_priv *priv);
+void gve_adminq_free(struct gve_priv *priv);
int gve_adminq_describe_device(struct gve_priv *priv);
int gve_adminq_configure_device_resources(struct gve_priv *priv,
dma_addr_t counter_array_bus_addr,
@@ -629,6 +628,8 @@ int gve_adminq_configure_device_resources(struct gve_priv *priv,
dma_addr_t db_array_bus_addr,
u32 num_ntfy_blks);
int gve_adminq_deconfigure_device_resources(struct gve_priv *priv);
+int gve_adminq_create_queues(struct gve_priv *priv);
+int gve_adminq_destroy_queues(struct gve_priv *priv);
int gve_adminq_create_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues);
int gve_adminq_destroy_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues);
int gve_adminq_create_single_rx_queue(struct gve_priv *priv, u32 queue_index);
@@ -640,9 +641,9 @@ int gve_adminq_register_page_list(struct gve_priv *priv,
int gve_adminq_unregister_page_list(struct gve_priv *priv, u32 page_list_id);
int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len,
dma_addr_t stats_report_addr, u64 interval);
-int gve_adminq_verify_driver_compatibility(struct gve_priv *priv,
- u64 driver_info_len,
- dma_addr_t driver_info_addr);
+int gve_adminq_verify_driver_compatibility(struct gve_priv *priv);
+int gve_adminq_get_device_properties(struct gve_priv *priv);
+int gve_adminq_report_link_status(struct gve_priv *priv);
int gve_adminq_report_link_speed(struct gve_priv *priv);
int gve_adminq_add_flow_rule(struct gve_priv *priv, struct gve_adminq_flow_rule *rule, u32 loc);
int gve_adminq_del_flow_rule(struct gve_priv *priv, u32 loc);
@@ -654,7 +655,13 @@ 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,
- struct gve_ptype_lut *ptype_lut);
-
+int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv);
+int gve_adminq_set_num_ntfy_blks(struct gve_priv *priv);
+void gve_adminq_set_num_queues(struct gve_priv *priv);
+int gve_adminq_map_db_bar(struct gve_priv *priv);
+void gve_adminq_unmap_db_bar(struct gve_priv *priv);
+int gve_adminq_request_db_info(struct gve_priv *priv);
+void gve_adminq_release_db_resources(struct gve_priv *priv);
+int gve_adminq_setup_mgmt_irq(struct gve_priv *priv);
+void gve_adminq_teardown_mgmt_irq(struct gve_priv *priv);
#endif /* _GVE_ADMINQ_H */
diff --git a/drivers/net/ethernet/google/gve/gve_dqo.h b/drivers/net/ethernet/google/gve/gve_dqo.h
index 5871f773f0c70..ce402020dff6a 100644
--- a/drivers/net/ethernet/google/gve/gve_dqo.h
+++ b/drivers/net/ethernet/google/gve/gve_dqo.h
@@ -66,7 +66,7 @@ void gve_rx_write_doorbell_dqo(const struct gve_priv *priv, int queue_idx);
void gve_xdp_tx_flush_dqo(struct gve_priv *priv, u32 xdp_qid);
static inline void
-gve_tx_put_doorbell_dqo(const struct gve_priv *priv,
+gve_tx_put_doorbell_dqo(struct gve_priv *priv,
const struct gve_queue_resources *q_resources, u32 val)
{
u64 index;
diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c
index 8199738ba979e..a3220c6514382 100644
--- a/drivers/net/ethernet/google/gve/gve_ethtool.c
+++ b/drivers/net/ethernet/google/gve/gve_ethtool.c
@@ -651,7 +651,7 @@ static int gve_user_reset(struct net_device *netdev, u32 *flags)
if (*flags == ETH_RESET_ALL) {
*flags = 0;
- return gve_reset(priv, true);
+ return gve_reset(priv, false);
}
return -EOPNOTSUPP;
@@ -749,12 +749,11 @@ static int gve_get_link_ksettings(struct net_device *netdev,
int err = 0;
if (priv->link_speed == 0)
- err = gve_adminq_report_link_speed(priv);
+ err = priv->ctrl_ops->report_link_speed(priv);
cmd->base.speed = priv->link_speed;
cmd->base.duplex = DUPLEX_FULL;
-
return err;
}
diff --git a/drivers/net/ethernet/google/gve/gve_flow_rule.c b/drivers/net/ethernet/google/gve/gve_flow_rule.c
index 2c80cda28ef30..fae552f4ad6fb 100644
--- a/drivers/net/ethernet/google/gve/gve_flow_rule.c
+++ b/drivers/net/ethernet/google/gve/gve_flow_rule.c
@@ -278,6 +278,11 @@ int gve_add_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd)
goto out;
err = gve_adminq_add_flow_rule(priv, rule, fsp->location);
+ if (err == -ETIME) {
+ dev_err(&priv->pdev->dev,
+ "Timeout to add flow rule, trigger reset.");
+ gve_reset(priv, false);
+ }
out:
kvfree(rule);
@@ -290,9 +295,17 @@ int gve_add_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd)
int gve_del_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
+ int err;
if (!priv->max_flow_rules)
return -EOPNOTSUPP;
- return gve_adminq_del_flow_rule(priv, fsp->location);
+ err = gve_adminq_del_flow_rule(priv, fsp->location);
+ if (err == -ETIME) {
+ dev_err(&priv->pdev->dev,
+ "Timeout to delete flow rule, trigger reset.");
+ gve_reset(priv, false);
+ }
+
+ return err;
}
diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c
index e4d78ae52dafe..800e1104d01b1 100644
--- a/drivers/net/ethernet/google/gve/gve_main.c
+++ b/drivers/net/ethernet/google/gve/gve_main.c
@@ -17,7 +17,6 @@
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
-#include <linux/utsname.h>
#include <linux/version.h>
#include <net/netdev_queues.h>
#include <net/sch_generic.h>
@@ -41,49 +40,6 @@ char gve_driver_name[] = "gve";
const char gve_version_str[] = GVE_VERSION;
static const char gve_version_prefix[] = GVE_VERSION_PREFIX;
-static int gve_verify_driver_compatibility(struct gve_priv *priv)
-{
- int err;
- struct gve_driver_info *driver_info;
- dma_addr_t driver_info_bus;
-
- driver_info = dma_alloc_coherent(&priv->pdev->dev,
- sizeof(struct gve_driver_info),
- &driver_info_bus, GFP_KERNEL);
- if (!driver_info)
- return -ENOMEM;
-
- *driver_info = (struct gve_driver_info) {
- .os_type = 1, /* Linux */
- .os_version_major = cpu_to_be32(LINUX_VERSION_MAJOR),
- .os_version_minor = cpu_to_be32(LINUX_VERSION_SUBLEVEL),
- .os_version_sub = cpu_to_be32(LINUX_VERSION_PATCHLEVEL),
- .driver_capability_flags = {
- cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS1),
- cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS2),
- cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS3),
- cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS4),
- },
- };
- strscpy(driver_info->os_version_str1, utsname()->release,
- sizeof(driver_info->os_version_str1));
- strscpy(driver_info->os_version_str2, utsname()->version,
- sizeof(driver_info->os_version_str2));
-
- err = gve_adminq_verify_driver_compatibility(priv,
- sizeof(struct gve_driver_info),
- driver_info_bus);
-
- /* It's ok if the device doesn't support this */
- if (err == -EOPNOTSUPP)
- err = 0;
-
- dma_free_coherent(&priv->pdev->dev,
- sizeof(struct gve_driver_info),
- driver_info, driver_info_bus);
- return err;
-}
-
static netdev_features_t gve_features_check(struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features)
@@ -248,6 +204,30 @@ static void gve_free_counter_array(struct gve_priv *priv)
priv->counter_array = NULL;
}
+static int gve_alloc_irq_db_indices(struct gve_priv *priv)
+{
+ priv->irq_db_indices =
+ dma_alloc_coherent(&priv->pdev->dev,
+ priv->num_ntfy_blks *
+ sizeof(*priv->irq_db_indices),
+ &priv->irq_db_indices_bus, GFP_KERNEL);
+ if (!priv->irq_db_indices)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void gve_free_irq_db_indices(struct gve_priv *priv)
+{
+ if (!priv->irq_db_indices)
+ return;
+
+ dma_free_coherent(&priv->pdev->dev, priv->num_ntfy_blks *
+ sizeof(*priv->irq_db_indices),
+ priv->irq_db_indices, priv->irq_db_indices_bus);
+ priv->irq_db_indices = NULL;
+}
+
/* NIC requests to report stats */
static void gve_stats_report_task(struct work_struct *work)
{
@@ -311,14 +291,6 @@ static void gve_free_stats_report(struct gve_priv *priv)
priv->stats_report = NULL;
}
-static irqreturn_t gve_mgmnt_intr(int irq, void *arg)
-{
- struct gve_priv *priv = arg;
-
- queue_work(priv->gve_wq, &priv->service_task);
- return IRQ_HANDLED;
-}
-
static irqreturn_t gve_intr(int irq, void *arg)
{
struct gve_notify_block *block = arg;
@@ -469,6 +441,15 @@ int gve_napi_poll_dqo(struct napi_struct *napi, int budget)
return work_done;
}
+static void gve_free_notify_blocks(struct gve_priv *priv)
+{
+ pci_disable_msix(priv->pdev);
+ kvfree(priv->ntfy_blocks);
+ priv->ntfy_blocks = NULL;
+ kvfree(priv->msix_vectors);
+ priv->msix_vectors = NULL;
+}
+
static const struct cpumask *gve_get_node_mask(struct gve_priv *priv)
{
if (priv->numa_node == NUMA_NO_NODE)
@@ -480,11 +461,9 @@ static const struct cpumask *gve_get_node_mask(struct gve_priv *priv)
static int gve_alloc_notify_blocks(struct gve_priv *priv)
{
int num_vecs_requested = priv->num_ntfy_blks + 1;
- const struct cpumask *node_mask;
- unsigned int cur_cpu;
int vecs_enabled;
- int i, j;
int err;
+ int i;
priv->msix_vectors = kvzalloc_objs(*priv->msix_vectors,
num_vecs_requested);
@@ -498,7 +477,7 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)
dev_err(&priv->pdev->dev, "Could not enable min msix %d/%d\n",
GVE_MIN_MSIX, vecs_enabled);
err = vecs_enabled;
- goto abort_with_msix_vectors;
+ goto abort;
}
if (vecs_enabled != num_vecs_requested) {
int new_num_ntfy_blks = (vecs_enabled - 1) & ~0x1;
@@ -521,38 +500,65 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)
priv->rx_cfg.num_queues = priv->rx_cfg.max_queues;
}
- /* Setup Management Vector - the last vector */
- snprintf(priv->mgmt_msix_name, sizeof(priv->mgmt_msix_name), "gve-mgmnt@pci:%s",
- pci_name(priv->pdev));
- err = request_irq(priv->msix_vectors[priv->mgmt_msix_idx].vector,
- gve_mgmnt_intr, 0, priv->mgmt_msix_name, priv);
- if (err) {
- dev_err(&priv->pdev->dev, "Did not receive management vector.\n");
- goto abort_with_msix_enabled;
- }
- priv->irq_db_indices =
- dma_alloc_coherent(&priv->pdev->dev,
- priv->num_ntfy_blks *
- sizeof(*priv->irq_db_indices),
- &priv->irq_db_indices_bus, GFP_KERNEL);
- if (!priv->irq_db_indices) {
- err = -ENOMEM;
- goto abort_with_mgmt_vector;
- }
-
priv->ntfy_blocks = kvzalloc(priv->num_ntfy_blks *
sizeof(*priv->ntfy_blocks), GFP_KERNEL);
if (!priv->ntfy_blocks) {
err = -ENOMEM;
- goto abort_with_irq_db_indices;
+ goto abort;
+ }
+
+ return 0;
+abort:
+ gve_free_notify_blocks(priv);
+ return err;
+}
+
+static void gve_teardown_notify_blocks(struct gve_priv *priv)
+{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
+ int i;
+
+ if (!priv->ntfy_blocks)
+ return;
+
+ for (i = 0; i < priv->num_ntfy_blks; i++) {
+ struct gve_notify_block *block = &priv->ntfy_blocks[i];
+ int msix_idx = gve_ntfy_to_msix_idx(priv, i);
+
+ if (!block->irq_requested)
+ continue;
+
+ irq_set_affinity_hint(priv->msix_vectors[msix_idx].vector,
+ NULL);
+ free_irq(priv->msix_vectors[msix_idx].vector, block);
+ block->irq = 0;
+ block->irq_requested = false;
+ }
+
+ ops->teardown_mgmt_irq(priv);
+}
+
+static int gve_setup_notify_blocks(struct gve_priv *priv)
+{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
+ const struct cpumask *node_mask;
+ unsigned int cur_cpu;
+ int i;
+ int err;
+
+ /* Setup Management Vector */
+ err = ops->setup_mgmt_irq(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Did not receive management vector.\n");
+ return err;
}
- /* Setup the other blocks - the first n-1 vectors */
node_mask = gve_get_node_mask(priv);
cur_cpu = cpumask_first(node_mask);
for (i = 0; i < priv->num_ntfy_blks; i++) {
struct gve_notify_block *block = &priv->ntfy_blocks[i];
- int msix_idx = i;
+ int msix_idx = gve_ntfy_to_msix_idx(priv, i);
snprintf(block->name, sizeof(block->name), "gve-ntfy-blk%d@pci:%s",
i, pci_name(priv->pdev));
@@ -562,13 +568,13 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)
IRQF_NO_AUTOEN, block->name, block);
if (err) {
dev_err(&priv->pdev->dev,
- "Failed to receive msix vector %d\n", i);
- goto abort_with_some_ntfy_blocks;
+ "Failed to receive msix vector %d\n", msix_idx);
+ goto abort;
}
block->irq = priv->msix_vectors[msix_idx].vector;
+ block->irq_requested = true;
irq_set_affinity_and_hint(block->irq,
cpumask_of(cur_cpu));
- block->irq_db_index = &priv->irq_db_indices[i].index;
cur_cpu = cpumask_next(cur_cpu, node_mask);
/* Wrap once CPUs in the node have been exhausted, or when
@@ -579,63 +585,29 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)
cur_cpu = cpumask_first(node_mask);
}
return 0;
-abort_with_some_ntfy_blocks:
- for (j = 0; j < i; j++) {
- struct gve_notify_block *block = &priv->ntfy_blocks[j];
- int msix_idx = j;
- irq_set_affinity_hint(priv->msix_vectors[msix_idx].vector,
- NULL);
- free_irq(priv->msix_vectors[msix_idx].vector, block);
- block->irq = 0;
- }
- kvfree(priv->ntfy_blocks);
- priv->ntfy_blocks = NULL;
-abort_with_irq_db_indices:
- dma_free_coherent(&priv->pdev->dev, priv->num_ntfy_blks *
- sizeof(*priv->irq_db_indices),
- priv->irq_db_indices, priv->irq_db_indices_bus);
- priv->irq_db_indices = NULL;
-abort_with_mgmt_vector:
- free_irq(priv->msix_vectors[priv->mgmt_msix_idx].vector, priv);
-abort_with_msix_enabled:
- pci_disable_msix(priv->pdev);
-abort_with_msix_vectors:
- kvfree(priv->msix_vectors);
- priv->msix_vectors = NULL;
+abort:
+ gve_teardown_notify_blocks(priv);
return err;
}
-static void gve_free_notify_blocks(struct gve_priv *priv)
+static void gve_free_control_plane_resources(struct gve_priv *priv)
{
- int i;
-
- if (!priv->msix_vectors)
- return;
+ bitmap_free(priv->xsk_pools);
+ priv->xsk_pools = NULL;
- /* Free the irqs */
- for (i = 0; i < priv->num_ntfy_blks; i++) {
- struct gve_notify_block *block = &priv->ntfy_blocks[i];
- int msix_idx = i;
+ kvfree(priv->ptype_lut_dqo);
+ priv->ptype_lut_dqo = NULL;
- irq_set_affinity_hint(priv->msix_vectors[msix_idx].vector,
- NULL);
- free_irq(priv->msix_vectors[msix_idx].vector, block);
- block->irq = 0;
- }
- free_irq(priv->msix_vectors[priv->mgmt_msix_idx].vector, priv);
- kvfree(priv->ntfy_blocks);
- priv->ntfy_blocks = NULL;
- dma_free_coherent(&priv->pdev->dev, priv->num_ntfy_blks *
- sizeof(*priv->irq_db_indices),
- priv->irq_db_indices, priv->irq_db_indices_bus);
- priv->irq_db_indices = NULL;
- pci_disable_msix(priv->pdev);
- kvfree(priv->msix_vectors);
- priv->msix_vectors = NULL;
+ gve_free_irq_db_indices(priv);
+ gve_free_counter_array(priv);
+ gve_free_notify_blocks(priv);
+ gve_free_stats_report(priv);
+ gve_free_rss_config_cache(priv);
+ gve_free_flow_rule_caches(priv);
}
-static int gve_setup_device_resources(struct gve_priv *priv)
+static int gve_alloc_control_plane_resources(struct gve_priv *priv)
{
int err;
@@ -644,39 +616,58 @@ static int gve_setup_device_resources(struct gve_priv *priv)
return err;
err = gve_alloc_rss_config_cache(priv);
if (err)
- goto abort_with_flow_rule_caches;
- err = gve_alloc_counter_array(priv);
- if (err)
- goto abort_with_rss_config_cache;
+ goto abort;
err = gve_alloc_notify_blocks(priv);
if (err)
- goto abort_with_counter;
+ goto abort;
err = gve_alloc_stats_report(priv);
if (err)
- goto abort_with_ntfy_blocks;
- err = gve_adminq_configure_device_resources(priv,
- priv->counter_array_bus,
- priv->num_event_counters,
- priv->irq_db_indices_bus,
- priv->num_ntfy_blks);
- if (unlikely(err)) {
- dev_err(&priv->pdev->dev,
- "could not setup device_resources: err=%d\n", err);
- err = -ENXIO;
- goto abort_with_stats_report;
- }
+ goto abort;
+ err = gve_alloc_counter_array(priv);
+ if (err)
+ goto abort;
+ err = gve_alloc_irq_db_indices(priv);
+ if (err)
+ goto abort;
if (!gve_is_gqi(priv)) {
- priv->ptype_lut_dqo = kvzalloc_obj(*priv->ptype_lut_dqo);
+ priv->ptype_lut_dqo = kvzalloc_obj(*priv->ptype_lut_dqo,
+ GFP_KERNEL);
if (!priv->ptype_lut_dqo) {
err = -ENOMEM;
- goto abort_with_stats_report;
+ goto abort;
}
- err = gve_adminq_get_ptype_map_dqo(priv, priv->ptype_lut_dqo);
+ }
+
+ priv->xsk_pools = bitmap_zalloc(priv->rx_cfg.max_queues, GFP_KERNEL);
+ if (!priv->xsk_pools) {
+ err = -ENOMEM;
+ goto abort;
+ }
+
+ return 0;
+abort:
+ gve_free_control_plane_resources(priv);
+ return err;
+}
+
+static int gve_setup_control_plane_resources(struct gve_priv *priv)
+{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
+ int err;
+
+ err = ops->request_db_info(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev, "Failed to get db info");
+ return err;
+ }
+
+ if (!gve_is_gqi(priv)) {
+ err = ops->get_ptype_map(priv);
if (err) {
dev_err(&priv->pdev->dev,
"Failed to get ptype map: err=%d\n", err);
- goto abort_with_ptype_lut;
+ goto release_db_resources;
}
}
@@ -691,79 +682,76 @@ static int gve_setup_device_resources(struct gve_priv *priv)
err = gve_init_rss_config(priv, priv->rx_cfg.num_queues);
if (err) {
dev_err(&priv->pdev->dev, "Failed to init RSS config");
- goto abort_with_clock;
+ goto teardown_clock;
}
- err = gve_adminq_report_stats(priv, priv->stats_report_len,
+ err = ops->setup_stats_report(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);
return 0;
-abort_with_clock:
+teardown_clock:
gve_teardown_clock(priv);
-abort_with_ptype_lut:
- kvfree(priv->ptype_lut_dqo);
- priv->ptype_lut_dqo = NULL;
-abort_with_stats_report:
- gve_free_stats_report(priv);
-abort_with_ntfy_blocks:
- gve_free_notify_blocks(priv);
-abort_with_counter:
- gve_free_counter_array(priv);
-abort_with_rss_config_cache:
- gve_free_rss_config_cache(priv);
-abort_with_flow_rule_caches:
- gve_free_flow_rule_caches(priv);
-
+release_db_resources:
+ ops->release_db_resources(priv);
return err;
}
-static void gve_trigger_reset(struct gve_priv *priv);
-
-static void gve_teardown_device_resources(struct gve_priv *priv)
+/**
+ * gve_teardown_control_plane_resources() - Request the device to release any
+ * shared allocated resources.
+ *
+ * @priv: Pointer to the GVE private device data structure.
+ *
+ * If any part of the teardown step fails, the failure is documented, but is
+ * otherwise ignored. It is expected that a device reset is triggered
+ * immediately after tearing down device resources, which would clear any
+ * lingering state on the device.
+ */
+static void gve_teardown_control_plane_resources(struct gve_priv *priv)
{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
int err;
/* Tell device its resources are being freed */
if (gve_get_device_resources_ok(priv)) {
err = gve_flow_rules_reset(priv);
- if (err) {
+ if (err)
dev_err(&priv->pdev->dev,
"Failed to reset flow rules: err=%d\n", err);
- gve_trigger_reset(priv);
- }
/* detach the stats report */
- err = gve_adminq_report_stats(priv, 0, 0x0, GVE_STATS_REPORT_TIMER_PERIOD);
- if (err) {
+ err = ops->setup_stats_report(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);
- if (err) {
- dev_err(&priv->pdev->dev,
- "Could not deconfigure device resources: err=%d\n",
- err);
- gve_trigger_reset(priv);
- }
+ gve_teardown_clock(priv);
+ ops->release_db_resources(priv);
}
- kvfree(priv->ptype_lut_dqo);
- priv->ptype_lut_dqo = NULL;
-
- gve_free_flow_rule_caches(priv);
- gve_free_rss_config_cache(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);
}
+static void gve_teardown_device(struct gve_priv *priv)
+{
+ gve_teardown_notify_blocks(priv);
+ gve_teardown_control_plane_resources(priv);
+ gve_adminq_free(priv);
+ /*
+ * Free any resources shared with the device only after we have a
+ * guarantee that the device will not try to access such resources.
+ * Device commands in gve_teardown_control_plane_resources can fail, in
+ * which case, device resources won't be relinquished until
+ * gve_adminq_free is called to trigger a device reset.
+ */
+ gve_free_control_plane_resources(priv);
+}
+
static int gve_unregister_qpl(struct gve_priv *priv,
struct gve_queue_page_list *qpl)
{
@@ -886,33 +874,18 @@ static int gve_unregister_qpls(struct gve_priv *priv)
static int gve_create_rings(struct gve_priv *priv)
{
- int num_tx_queues = gve_num_tx_queues(priv);
int err;
int i;
- err = gve_adminq_create_tx_queues(priv, 0, num_tx_queues);
- if (err) {
- netif_err(priv, drv, priv->dev, "failed to create %d tx queues\n",
- num_tx_queues);
- /* This failure will trigger a reset - no need to clean
- * up
- */
- return err;
- }
- netif_dbg(priv, drv, priv->dev, "created %d tx queues\n",
- num_tx_queues);
-
- err = gve_adminq_create_rx_queues(priv, priv->rx_cfg.num_queues);
+ err = priv->ctrl_ops->create_queues(priv);
if (err) {
- netif_err(priv, drv, priv->dev, "failed to create %d rx queues\n",
- priv->rx_cfg.num_queues);
+ netif_err(priv, drv, priv->dev,
+ "failed to create queues\n");
/* This failure will trigger a reset - no need to clean
* up
*/
return err;
}
- netif_dbg(priv, drv, priv->dev, "created %d rx queues\n",
- priv->rx_cfg.num_queues);
if (gve_is_gqi(priv)) {
/* Rx data ring has been prefilled with packet buffers at queue
@@ -1067,26 +1040,12 @@ static int gve_queues_mem_alloc(struct gve_priv *priv,
static int gve_destroy_rings(struct gve_priv *priv)
{
- int num_tx_queues = gve_num_tx_queues(priv);
int err;
- err = gve_adminq_destroy_tx_queues(priv, 0, num_tx_queues);
- if (err) {
- netif_err(priv, drv, priv->dev,
- "failed to destroy tx queues\n");
- /* This failure will trigger a reset - no need to clean up */
- return err;
- }
- netif_dbg(priv, drv, priv->dev, "destroyed tx queues\n");
- err = gve_adminq_destroy_rx_queues(priv, priv->rx_cfg.num_queues);
- if (err) {
- netif_err(priv, drv, priv->dev,
- "failed to destroy rx queues\n");
- /* This failure will trigger a reset - no need to clean up */
- return err;
- }
- netif_dbg(priv, drv, priv->dev, "destroyed rx queues\n");
- return 0;
+ err = priv->ctrl_ops->destroy_queues(priv);
+
+ /* A failure will trigger a reset - no need to clean up */
+ return err;
}
static void gve_queues_mem_free(struct gve_priv *priv,
@@ -1202,8 +1161,6 @@ void gve_schedule_reset(struct gve_priv *priv)
queue_work(priv->gve_wq, &priv->service_task);
}
-static void gve_reset_and_teardown(struct gve_priv *priv, bool was_up);
-static int gve_reset_recovery(struct gve_priv *priv, bool was_up);
static void gve_turndown(struct gve_priv *priv);
static void gve_turnup(struct gve_priv *priv);
@@ -1314,11 +1271,12 @@ static int gve_reg_xdp_info(struct gve_priv *priv, struct net_device *dev)
return err;
}
-
static void gve_drain_page_cache(struct gve_priv *priv)
{
int i;
+ if (!priv->rx)
+ return;
for (i = 0; i < priv->rx_cfg.num_queues; i++)
page_frag_cache_drain(&priv->rx[i].page_cache);
}
@@ -1461,10 +1419,11 @@ static int gve_queues_start(struct gve_priv *priv,
reset:
if (gve_get_reset_in_progress(priv))
goto stop_and_free_rings;
- gve_reset_and_teardown(priv, true);
- /* if this fails there is nothing we can do so just ignore the return */
- gve_reset_recovery(priv, false);
- /* return the original error */
+
+ /* Attempt to reset. If reset is successful, gve_queues_start was
+ * successful.
+ */
+ err = gve_reset(priv, false);
return err;
stop_and_free_rings:
gve_tx_stop_rings(priv, gve_num_tx_queues(priv));
@@ -1480,6 +1439,12 @@ static int gve_open(struct net_device *dev)
struct gve_priv *priv = netdev_priv(dev);
int err;
+ if (!gve_get_device_resources_ok(priv)) {
+ dev_err(&priv->pdev->dev,
+ "Attempting to open netdev without resources. Device must be reset.");
+ return -ENODEV;
+ }
+
gve_get_curr_alloc_cfgs(priv, &tx_alloc_cfg, &rx_alloc_cfg);
err = gve_queues_mem_alloc(priv, &tx_alloc_cfg, &rx_alloc_cfg);
@@ -1496,41 +1461,18 @@ static int gve_open(struct net_device *dev)
return 0;
}
-static int gve_queues_stop(struct gve_priv *priv)
+static void gve_queues_stop(struct gve_priv *priv)
{
- int err;
-
- netif_carrier_off(priv->dev);
- if (gve_get_device_rings_ok(priv)) {
- gve_turndown(priv);
- gve_drain_page_cache(priv);
- err = gve_destroy_rings(priv);
- if (err)
- goto err;
- err = gve_unregister_qpls(priv);
- if (err)
- goto err;
- gve_clear_device_rings_ok(priv);
- }
- timer_delete_sync(&priv->stats_report_timer);
+ gve_turndown(priv);
gve_unreg_xdp_info(priv);
+ gve_drain_page_cache(priv);
+
+ timer_delete_sync(&priv->stats_report_timer);
+ cancel_work_sync(&priv->stats_report_task);
gve_tx_stop_rings(priv, gve_num_tx_queues(priv));
gve_rx_stop_rings(priv, priv->rx_cfg.num_queues);
-
- priv->interface_down_cnt++;
- return 0;
-
-err:
- /* This must have been called from a reset due to the rtnl lock
- * so just return at this point.
- */
- if (gve_get_reset_in_progress(priv))
- return err;
- /* Otherwise reset before returning */
- gve_reset_and_teardown(priv, true);
- return gve_reset_recovery(priv, false);
}
static int gve_close(struct net_device *dev)
@@ -1538,23 +1480,42 @@ static int gve_close(struct net_device *dev)
struct gve_priv *priv = netdev_priv(dev);
int err;
- err = gve_queues_stop(priv);
- if (err)
- return err;
+ gve_queues_stop(priv);
+
+ /* Surrender to reset if the queue destroying adminq cmds fail. Reset
+ * will not re-enable the interface.
+ */
+ if (gve_get_device_rings_ok(priv)) {
+ gve_clear_device_rings_ok(priv);
+ err = gve_destroy_rings(priv);
+ if (err)
+ goto reset;
+ err = gve_unregister_qpls(priv);
+ if (err)
+ goto reset;
+ }
gve_queues_mem_remove(priv);
+ priv->interface_down_cnt++;
return 0;
+
+reset:
+ err = gve_reset(priv, true);
+ return err;
}
-static void gve_handle_link_status(struct gve_priv *priv, bool link_status)
+static void gve_handle_link_status(struct gve_priv *priv)
{
+ bool link_up;
+
if (!gve_get_napi_enabled(priv))
return;
- if (link_status == netif_carrier_ok(priv->dev))
+ link_up = READ_ONCE(priv->link_up);
+ if (link_up == netif_carrier_ok(priv->dev))
return;
- if (link_status) {
+ if (link_up) {
netdev_info(priv->dev, "Device link is up.\n");
netif_carrier_on(priv->dev);
} else {
@@ -1581,7 +1542,6 @@ static int gve_set_xdp(struct gve_priv *priv, struct bpf_prog *prog,
{
struct bpf_prog *old_prog;
int err = 0;
- u32 status;
old_prog = READ_ONCE(priv->xdp_prog);
if (!netif_running(priv->dev)) {
@@ -1608,8 +1568,8 @@ static int gve_set_xdp(struct gve_priv *priv, struct bpf_prog *prog,
bpf_prog_put(old_prog);
out:
- status = ioread32be(&priv->reg_bar0->device_status);
- gve_handle_link_status(priv, GVE_DEVICE_STATUS_LINK_STATUS_MASK & status);
+ priv->ctrl_ops->report_link_status(priv);
+ gve_handle_link_status(priv);
return err;
}
@@ -1824,6 +1784,7 @@ static int gve_xdp(struct net_device *dev, struct netdev_bpf *xdp)
int gve_init_rss_config(struct gve_priv *priv, u16 num_queues)
{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
struct gve_rss_config *rss_config = &priv->rss_config;
struct ethtool_rxfh_param rxfh = {0};
u16 i;
@@ -1839,15 +1800,17 @@ int gve_init_rss_config(struct gve_priv *priv, u16 num_queues)
rxfh.hfunc = ETH_RSS_HASH_TOP;
- return gve_adminq_configure_rss(priv, &rxfh);
+ return ops->configure_rss(priv, &rxfh);
}
int gve_flow_rules_reset(struct gve_priv *priv)
{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
+
if (!priv->max_flow_rules)
return 0;
- return gve_adminq_reset_flow_rules(priv);
+ return ops->reset_flow_rules(priv);
}
int gve_adjust_config(struct gve_priv *priv,
@@ -2039,11 +2002,11 @@ static void gve_turnup(struct gve_priv *priv)
static void gve_turnup_and_check_status(struct gve_priv *priv)
{
- u32 status;
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
gve_turnup(priv);
- status = ioread32be(&priv->reg_bar0->device_status);
- gve_handle_link_status(priv, GVE_DEVICE_STATUS_LINK_STATUS_MASK & status);
+ ops->report_link_status(priv);
+ gve_handle_link_status(priv);
}
static struct gve_notify_block *gve_get_tx_notify_block(struct gve_priv *priv,
@@ -2367,12 +2330,14 @@ static void gve_service_task(struct work_struct *work)
{
struct gve_priv *priv = container_of(work, struct gve_priv,
service_task);
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
u32 status = ioread32be(&priv->reg_bar0->device_status);
gve_handle_status(priv, status);
gve_handle_reset(priv);
- gve_handle_link_status(priv, GVE_DEVICE_STATUS_LINK_STATUS_MASK & status);
+ ops->report_link_status(priv);
+ gve_handle_link_status(priv);
}
static void gve_set_netdev_xdp_features(struct gve_priv *priv)
@@ -2398,188 +2363,249 @@ static const struct xdp_metadata_ops gve_xdp_metadata_ops = {
.xmo_rx_timestamp = gve_xdp_rx_timestamp,
};
-static int gve_init_priv(struct gve_priv *priv, bool skip_describe_device)
+static void gve_set_desc_cnt(struct gve_priv *priv)
{
- int num_ntfy;
- int err;
+ struct gve_device_info *device_info = &priv->device_info;
- /* Set up the adminq */
- err = gve_adminq_alloc(&priv->pdev->dev, priv);
- if (err) {
- dev_err(&priv->pdev->dev,
- "Failed to alloc admin queue: err=%d\n", err);
- return err;
- }
+ priv->tx_desc_cnt = device_info->default_tx_ring_size;
+ priv->rx_desc_cnt = device_info->default_rx_ring_size;
+ priv->max_tx_desc_cnt = device_info->max_tx_ring_size;
+ priv->max_rx_desc_cnt = device_info->max_rx_ring_size;
+ priv->min_tx_desc_cnt = device_info->min_tx_ring_size;
+ priv->min_rx_desc_cnt = device_info->min_rx_ring_size;
+}
- err = gve_verify_driver_compatibility(priv);
- if (err) {
- dev_err(&priv->pdev->dev,
- "Could not verify driver compatibility: err=%d\n", err);
- goto err;
- }
+static void gve_set_queue_properties(struct gve_priv *priv)
+{
+ struct gve_device_info *device_info = &priv->device_info;
- priv->num_registered_pages = 0;
+ gve_set_desc_cnt(priv);
+ priv->max_registered_pages = device_info->max_registered_pages;
+ priv->tx_pages_per_qpl = device_info->tx_pages_per_qpl;
+}
- if (skip_describe_device)
- goto setup_device;
+static int gve_set_mtu(struct gve_priv *priv)
+{
+ struct gve_device_info *device_info = &priv->device_info;
+ u16 mtu;
- priv->queue_format = GVE_QUEUE_FORMAT_UNSPECIFIED;
- /* Get the initial information we need from the device */
- err = gve_adminq_describe_device(priv);
- if (err) {
- dev_err(&priv->pdev->dev,
- "Could not get device information: err=%d\n", err);
- goto err;
- }
- priv->dev->mtu = priv->dev->max_mtu;
- num_ntfy = pci_msix_vec_count(priv->pdev);
- if (num_ntfy <= 0) {
- dev_err(&priv->pdev->dev,
- "could not count MSI-x vectors: err=%d\n", num_ntfy);
- err = num_ntfy;
- goto err;
- } else if (num_ntfy < GVE_MIN_MSIX) {
- dev_err(&priv->pdev->dev, "gve needs at least %d MSI-x vectors, but only has %d\n",
- GVE_MIN_MSIX, num_ntfy);
- err = -EINVAL;
- goto err;
+ mtu = device_info->max_mtu;
+ if (mtu < ETH_MIN_MTU) {
+ dev_err(&priv->pdev->dev, "MTU %d below minimum MTU\n", mtu);
+ return -EINVAL;
}
+ priv->dev->max_mtu = mtu;
+ priv->dev->mtu = mtu;
+
+ return 0;
+}
+
+static void gve_set_mac(struct gve_priv *priv)
+{
+ struct gve_device_info *device_info = &priv->device_info;
+ u8 *mac;
+
+ mac = device_info->mac;
+ eth_hw_addr_set(priv->dev, mac);
+ dev_info(&priv->pdev->dev, "MAC addr: %pM\n", mac);
+}
+
+static void gve_set_buf_sizes(struct gve_priv *priv)
+{
+ struct gve_device_info *device_info = &priv->device_info;
+
+ if (device_info->max_rx_buffer_size > priv->max_rx_buffer_size)
+ priv->max_rx_buffer_size = device_info->max_rx_buffer_size;
- /* Big TCP is only supported on DQO */
+ if (gve_is_dqo(priv) &&
+ priv->max_rx_buffer_size > GVE_DEFAULT_RX_BUFFER_SIZE)
+ priv->rx_cfg.packet_buffer_size = priv->max_rx_buffer_size;
+
+ if (device_info->header_buf_size)
+ priv->header_buf_size = device_info->header_buf_size;
+}
+
+static int gve_setup_device(struct gve_priv *priv)
+{
+ int err;
+
+ priv->num_registered_pages = 0;
+
+ gve_set_netdev_xdp_features(priv);
if (!gve_is_gqi(priv))
- netif_set_tso_max_size(priv->dev, GVE_DQO_TX_MAX);
+ priv->dev->xdp_metadata_ops = &gve_xdp_metadata_ops;
- priv->rx_copybreak = GVE_DEFAULT_RX_COPYBREAK;
- /* gvnic has one Notification Block per MSI-x vector, except for the
- * management vector
- */
- priv->num_ntfy_blks = (num_ntfy - 1) & ~0x1;
- priv->mgmt_msix_idx = priv->num_ntfy_blks;
- priv->numa_node = dev_to_node(&priv->pdev->dev);
+ err = gve_alloc_control_plane_resources(priv);
+ if (err)
+ return err;
+
+ err = gve_setup_control_plane_resources(priv);
+ if (err)
+ return err;
+
+ err = gve_setup_notify_blocks(priv);
+ if (err)
+ return err;
+ return 0;
+}
- priv->tx_cfg.max_queues =
- min_t(int, priv->tx_cfg.max_queues, priv->num_ntfy_blks / 2);
- priv->rx_cfg.max_queues =
- min_t(int, priv->rx_cfg.max_queues, priv->num_ntfy_blks / 2);
+static const struct gve_ctrl_ops gve_adminq_ops = {
+ .map_db_bar = gve_adminq_map_db_bar,
+ .unmap_db_bar = gve_adminq_unmap_db_bar,
+ .set_num_queues = gve_adminq_set_num_queues,
+ .set_num_ntfy_blks = gve_adminq_set_num_ntfy_blks,
+ .get_ptype_map = gve_adminq_get_ptype_map_dqo,
+ .reset_flow_rules = gve_adminq_reset_flow_rules,
+ .setup_stats_report = gve_adminq_report_stats,
+ .configure_rss = gve_adminq_configure_rss,
+ .request_db_info = gve_adminq_request_db_info,
+ .release_db_resources = gve_adminq_release_db_resources,
+ .setup_mgmt_irq = gve_adminq_setup_mgmt_irq,
+ .teardown_mgmt_irq = gve_adminq_teardown_mgmt_irq,
+ .create_queues = gve_adminq_create_queues,
+ .destroy_queues = gve_adminq_destroy_queues,
+ .report_link_status = gve_adminq_report_link_status,
+ .report_link_speed = gve_adminq_report_link_speed,
+};
+
+static int gve_init_priv(struct gve_priv *priv)
+{
+ struct gve_device_info *device_info = &priv->device_info;
+ int err;
- priv->tx_cfg.num_queues = priv->tx_cfg.max_queues;
- priv->rx_cfg.num_queues = priv->rx_cfg.max_queues;
- if (priv->default_num_queues > 0) {
- priv->tx_cfg.num_queues = min_t(int, priv->default_num_queues,
- priv->tx_cfg.num_queues);
- priv->rx_cfg.num_queues = min_t(int, priv->default_num_queues,
- priv->rx_cfg.num_queues);
+ priv->queue_format = priv->device_info.queue_format;
+
+ err = priv->ctrl_ops->set_num_ntfy_blks(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not setup notify blocks: err=%d\n", err);
+ return err;
}
- priv->tx_cfg.num_xdp_queues = 0;
+ priv->ctrl_ops->set_num_queues(priv);
dev_info(&priv->pdev->dev, "TX queues %d, RX queues %d\n",
priv->tx_cfg.num_queues, priv->rx_cfg.num_queues);
dev_info(&priv->pdev->dev, "Max TX queues %d, Max RX queues %d\n",
priv->tx_cfg.max_queues, priv->rx_cfg.max_queues);
- if (!gve_is_gqi(priv)) {
+ if (gve_is_dqo(priv)) {
+ /* DQO supports HW-GRO and UDP_GSO */
+ u64 additional_features = NETIF_F_GRO_HW | NETIF_F_GSO_UDP_L4;
+
+ priv->dev->hw_features |= additional_features;
+ priv->dev->features |= additional_features;
+
priv->tx_coalesce_usecs = GVE_TX_IRQ_RATELIMIT_US_DQO;
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:
- priv->xsk_pools = bitmap_zalloc(priv->rx_cfg.max_queues, GFP_KERNEL);
- if (!priv->xsk_pools) {
- err = -ENOMEM;
- goto err;
+ /* Big TCP is only supported on DQO */
+ netif_set_tso_max_size(priv->dev, GVE_DQO_TX_MAX);
}
- gve_set_netdev_xdp_features(priv);
- if (!gve_is_gqi(priv))
- priv->dev->xdp_metadata_ops = &gve_xdp_metadata_ops;
+ if (gve_set_mtu(priv))
+ return -EINVAL;
- err = gve_setup_device_resources(priv);
- if (err)
- goto err_free_xsk_bitmap;
+ priv->num_event_counters = device_info->num_event_counters;
- return 0;
+ gve_set_mac(priv);
-err_free_xsk_bitmap:
- bitmap_free(priv->xsk_pools);
- priv->xsk_pools = NULL;
-err:
- gve_adminq_free(&priv->pdev->dev, priv);
- return err;
-}
+ gve_set_queue_properties(priv);
+ priv->modify_ring_size_enabled = device_info->modify_ring_size_enabled;
-static void gve_teardown_priv_resources(struct gve_priv *priv)
-{
- gve_teardown_device_resources(priv);
- gve_adminq_free(&priv->pdev->dev, priv);
- bitmap_free(priv->xsk_pools);
- priv->xsk_pools = NULL;
-}
+ gve_set_buf_sizes(priv);
-static void gve_trigger_reset(struct gve_priv *priv)
-{
- /* Reset the device by releasing the AQ */
- gve_adminq_release(priv);
-}
+ priv->max_flow_rules = device_info->max_flow_rules;
+ if (priv->max_flow_rules)
+ priv->dev->hw_features |= NETIF_F_NTUPLE;
-static void gve_reset_and_teardown(struct gve_priv *priv, bool was_up)
-{
- gve_trigger_reset(priv);
- /* With the reset having already happened, close cannot fail */
- if (was_up)
- gve_close(priv->dev);
- gve_teardown_priv_resources(priv);
+ priv->rss_key_size = device_info->rss_key_size;
+ priv->rss_lut_size = device_info->rss_lut_size;
+ priv->cache_rss_config = device_info->cache_rss_config;
+
+ priv->numa_node = dev_to_node(&priv->pdev->dev);
+ priv->tx_cfg.num_xdp_queues = 0;
+ priv->rx_copybreak = GVE_DEFAULT_RX_COPYBREAK;
+ priv->ts_config.tx_type = HWTSTAMP_TX_OFF;
+ priv->ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
+ priv->nic_timestamp_supported = device_info->nic_timestamp_supported;
+ return 0;
}
-static int gve_reset_recovery(struct gve_priv *priv, bool was_up)
+static int gve_recover(struct gve_priv *priv, bool setup_queues)
{
+ bool service_task_enabled = false;
int err;
- err = gve_init_priv(priv, true);
+ err = gve_adminq_init(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Failed to alloc admin queue: err=%d\n", err);
+ goto teardown_device;
+ }
+
+ err = gve_adminq_verify_driver_compatibility(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not verify driver compatibility: err=%d\n", err);
+ goto teardown_device;
+ }
+
+ err = gve_setup_device(priv);
if (err)
- goto err;
- if (was_up) {
+ goto teardown_device;
+
+ /* In the case of cascading failures, the disable count could be greater
+ * than 1. The service task must be enabled before starting queues.
+ */
+ while (!service_task_enabled)
+ service_task_enabled = enable_work(&priv->service_task);
+
+ if (setup_queues) {
err = gve_open(priv->dev);
if (err)
- goto err;
+ goto disable_work;
}
+
return 0;
-err:
- dev_err(&priv->pdev->dev, "Reset failed! !!! DISABLING ALL QUEUES !!!\n");
- gve_turndown(priv);
+
+disable_work:
+ disable_work(&priv->service_task);
+teardown_device:
+ dev_err(&priv->pdev->dev, "Recover failed! !!! DISABLING ALL QUEUES !!!\n");
+ gve_teardown_device(priv);
return err;
}
-int gve_reset(struct gve_priv *priv, bool attempt_teardown)
+int gve_reset(struct gve_priv *priv, bool skip_queue_setup)
{
bool was_up = netif_running(priv->dev);
int err;
+ if (gve_get_reset_in_progress(priv))
+ return 0;
+
dev_info(&priv->pdev->dev, "Performing reset\n");
gve_clear_do_reset(priv);
gve_set_reset_in_progress(priv);
- /* If we aren't attempting to teardown normally, just go turndown and
- * reset right away.
- */
- if (!attempt_teardown) {
- gve_turndown(priv);
- gve_reset_and_teardown(priv, was_up);
- } else {
- /* Otherwise attempt to close normally */
- if (was_up) {
- err = gve_close(priv->dev);
- /* If that fails reset as we did above */
- if (err)
- gve_reset_and_teardown(priv, was_up);
+
+ if (was_up) {
+ gve_queues_stop(priv);
+ if (gve_get_device_rings_ok(priv)) {
+ gve_clear_device_rings_ok(priv);
+ gve_destroy_rings(priv);
+ gve_unregister_qpls(priv);
}
- /* Clean up any remaining resources */
- gve_teardown_priv_resources(priv);
}
- /* Set it all back up */
- err = gve_reset_recovery(priv, was_up);
+ disable_work(&priv->service_task);
+ gve_teardown_device(priv);
+ gve_queues_mem_remove(priv);
+
+ err = gve_recover(priv, was_up && !skip_queue_setup);
+ if (err)
+ dev_info(&priv->pdev->dev,
+ "Failed to recover in reset: %d\n", err);
+
gve_clear_reset_in_progress(priv);
priv->reset_cnt++;
priv->interface_up_cnt = 0;
@@ -2588,7 +2614,7 @@ int gve_reset(struct gve_priv *priv, bool attempt_teardown)
return err;
}
-static void gve_write_version(u8 __iomem *driver_version_register)
+void gve_adminq_write_version(u8 __iomem *driver_version_register)
{
const char *c = gve_version_prefix;
@@ -2794,7 +2820,6 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int max_tx_queues, max_rx_queues;
struct net_device *dev;
- __be32 __iomem *db_bar;
struct gve_registers __iomem *reg_bar;
struct gve_priv *priv;
int err;
@@ -2822,14 +2847,6 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto abort_with_pci_region;
}
- db_bar = pci_iomap(pdev, GVE_DOORBELL_BAR, 0);
- if (!db_bar) {
- dev_err(&pdev->dev, "Failed to map doorbell bar!\n");
- err = -ENOMEM;
- goto abort_with_reg_bar;
- }
-
- gve_write_version(®_bar->driver_version);
/* Get max queues to alloc etherdev */
max_tx_queues = ioread32be(®_bar->max_tx_queues);
max_rx_queues = ioread32be(®_bar->max_rx_queues);
@@ -2838,7 +2855,7 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!dev) {
dev_err(&pdev->dev, "could not allocate netdev\n");
err = -ENOMEM;
- goto abort_with_db_bar;
+ goto abort_with_reg_bar;
}
SET_NETDEV_DEV(dev, &pdev->dev);
pci_set_drvdata(pdev, dev);
@@ -2870,35 +2887,65 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
priv->pdev = pdev;
priv->msg_enable = DEFAULT_MSG_LEVEL;
priv->reg_bar0 = reg_bar;
- priv->db_bar2 = db_bar;
priv->service_task_flags = 0x0;
priv->state_flags = 0x0;
priv->ethtool_flags = 0x0;
priv->rx_cfg.packet_buffer_size = GVE_DEFAULT_RX_BUFFER_SIZE;
priv->max_rx_buffer_size = GVE_DEFAULT_RX_BUFFER_SIZE;
+ err = gve_adminq_init(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Failed to alloc admin queue: err=%d\n", err);
+ goto abort_with_netdev;
+ }
+
+ priv->device_info.queue_format = GVE_QUEUE_FORMAT_UNSPECIFIED;
+ err = gve_adminq_get_device_properties(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not get device information: err=%d\n", err);
+ goto abort_with_adminq;
+ }
+
+ /* Set adminq ctrl ops */
+ priv->ctrl_ops = &gve_adminq_ops;
+
+ err = priv->ctrl_ops->map_db_bar(priv);
+ if (err) {
+ err = -ENOMEM;
+ goto abort_with_adminq;
+ }
+
gve_set_probe_in_progress(priv);
priv->gve_wq = alloc_ordered_workqueue("gve", 0);
if (!priv->gve_wq) {
dev_err(&pdev->dev, "Could not allocate workqueue");
err = -ENOMEM;
- goto abort_with_netdev;
+ goto abort_with_unmap_db_bar;
}
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->rx_cfg.max_queues = max_rx_queues;
- err = gve_init_priv(priv, false);
+ err = gve_init_priv(priv);
if (err)
goto abort_with_wq;
+ err = gve_setup_device(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not setup device: err=%d\n", err);
+ goto abort_teardown_device;
+ }
+
if (!gve_is_gqi(priv) && !gve_is_qpl(priv))
dev->netmem_tx = NETMEM_TX_DMA;
err = register_netdev(dev);
if (err)
- goto abort_with_gve_init;
+ goto abort_teardown_device;
dev_info(&pdev->dev, "GVE version %s\n", gve_version_str);
dev_info(&pdev->dev, "GVE queue format %d\n", (int)priv->queue_format);
@@ -2906,18 +2953,22 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
queue_work(priv->gve_wq, &priv->service_task);
return 0;
-abort_with_gve_init:
- gve_teardown_priv_resources(priv);
+abort_teardown_device:
+ disable_work(&priv->stats_report_task);
+ gve_teardown_device(priv);
abort_with_wq:
destroy_workqueue(priv->gve_wq);
+abort_with_unmap_db_bar:
+ priv->ctrl_ops->unmap_db_bar(priv);
+
+abort_with_adminq:
+ gve_adminq_free(priv);
+
abort_with_netdev:
free_netdev(dev);
-abort_with_db_bar:
- pci_iounmap(pdev, db_bar);
-
abort_with_reg_bar:
pci_iounmap(pdev, reg_bar);
@@ -2933,14 +2984,14 @@ static void gve_remove(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct gve_priv *priv = netdev_priv(netdev);
- __be32 __iomem *db_bar = priv->db_bar2;
void __iomem *reg_bar = priv->reg_bar0;
unregister_netdev(netdev);
- gve_teardown_priv_resources(priv);
+ disable_work_sync(&priv->service_task);
+ gve_teardown_device(priv);
destroy_workqueue(priv->gve_wq);
+ priv->ctrl_ops->unmap_db_bar(priv);
free_netdev(netdev);
- pci_iounmap(pdev, db_bar);
pci_iounmap(pdev, reg_bar);
pci_release_regions(pdev);
pci_disable_device(pdev);
@@ -2953,16 +3004,13 @@ static void gve_shutdown(struct pci_dev *pdev)
bool was_up = netif_running(priv->dev);
netif_device_detach(netdev);
+ disable_work_sync(&priv->service_task);
rtnl_lock();
netdev_lock(netdev);
- if (was_up && gve_close(priv->dev)) {
- /* If the dev was up, attempt to close, if close fails, reset */
- gve_reset_and_teardown(priv, was_up);
- } else {
- /* If the dev wasn't up or close worked, finish tearing down */
- gve_teardown_priv_resources(priv);
- }
+ if (was_up)
+ gve_close(priv->dev);
+ gve_teardown_device(priv);
netdev_unlock(netdev);
rtnl_unlock();
}
@@ -2974,16 +3022,14 @@ static int gve_suspend(struct device *dev)
struct gve_priv *priv = netdev_priv(netdev);
bool was_up = netif_running(priv->dev);
+ disable_work_sync(&priv->service_task);
+
priv->suspend_cnt++;
rtnl_lock();
netdev_lock(netdev);
- if (was_up && gve_close(priv->dev)) {
- /* If the dev was up, attempt to close, if close fails, reset */
- gve_reset_and_teardown(priv, was_up);
- } else {
- /* If the dev wasn't up or close worked, finish tearing down */
- gve_teardown_priv_resources(priv);
- }
+ if (was_up)
+ gve_close(priv->dev);
+ gve_teardown_device(priv);
priv->up_before_suspend = was_up;
netdev_unlock(netdev);
rtnl_unlock();
@@ -3000,7 +3046,7 @@ static int gve_resume(struct device *dev)
priv->resume_cnt++;
rtnl_lock();
netdev_lock(netdev);
- err = gve_reset_recovery(priv, priv->up_before_suspend);
+ err = gve_recover(priv, priv->up_before_suspend);
netdev_unlock(netdev);
rtnl_unlock();
return err;
|
| 3/2 | 2026/08/14 02:37 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 33760 output: 34 thoughts: 3233 Thoughts: **Fuzzing Strategy for GVE Driver Refactoring** Alright, let's break this down systematically. We've got a patch for the Google Virtual Ethernet (GVE) driver, primarily targeting control plane refactoring. My initial thought is to confirm reachability, and it's clear this driver *is* absolutely reachable in GCE (Google Cloud Engine), and heavily used via syzkaller, which sets up the base assumption. Since GVE is core to GCE, any code paths should be fuzzed. `WorthFuzzing` should equal `true`. The core of the patch involves restructuring the control plane, introducing `gve_ctrl_ops` and `gve_device_info`, which suggests a good focus for fuzzing, specifically around how the control operations are handled. I need to dig into the actual functions being refactored. I see changes across a few files: `gve_adminq.c`, `gve_main.c`, `gve_ethtool.c`, and `gve_flow_rule.c`. That gives me a first set of code to examine. The major function calls listed are the main areas of interest. - `gve_reset` - This is a prime candidate. Device reset is always a good area to hit. - `gve_open`, `gve_close` - Device open/close sequences. Standard, but critical, entry points. - `gve_probe`, `gve_remove` - Driver probe and remove operations - essential for module lifecycle, so high priority. - `gve_suspend`, `gve_resume` - Power management functions, likely complex, and good targets. - `gve_set_xdp` - This might be a hot target for BPF, so should get focus. - `gve_add_flow_rule`, `gve_del_flow_rule` - These are related to ethtool. Flow rules often involve complex state transitions, so interesting. - `gve_user_reset` - Called in `gve_ethtool.c` via `ETH_RESET_ALL`. I'm checking function calls inside each file. Let's see how they interact. I see `gve_user_reset`, which in turn calls `gve_reset`. Interesting. `gve_add_flow_rule` and `gve_del_flow_rule` are accessed via ethtool, and are interesting. `gve_set_xdp` is also a potential target. I'm checking `gve_reset`, since it's used multiple times. `gve_probe` is a crucial entry point for initializing the driver. I'm checking `gve_adminq_verify_driver_compatibility` `gve_adminq_get_device_properties` is used in `gve_probe`. `gve_adminq_init`, `gve_adminq_create_queues`, `gve_adminq_destroy_queues`, `gve_adminq_setup_mgmt_irq`, `gve_adminq_teardown_mgmt_irq`, `gve_adminq_request_db_info`, `gve_adminq_release_db_resources`, `gve_adminq_map_db_bar`, `gve_adminq_unmap_db_bar`, `gve_adminq_set_num_ntfy_blks`, `gve_adminq_set_num_queues`, `gve_adminq_report_link_status`, `gve_adminq_get_ptype_map_dqo`, `gve_adminq_describe_device`, `gve_adminq_alloc`, `gve_adminq_free`, `gve_adminq_write_version` are all related to adminq. I should also check `gve_recover`. Now I am checking `gve_setup_device`, `gve_teardown_device`, `gve_setup_control_plane_resources`, `gve_teardown_control_plane_resources`, `gve_alloc_control_plane_resources`, `gve_free_control_plane_resources`, `gve_setup_notify_blocks`, `gve_teardown_notify_blocks`, `gve_alloc_notify_blocks`, `gve_free_notify_blocks`, `gve_init_priv` - these all look like setup/teardown code, so are interesting for fuzzing. I'll check `gve |
| 4/2 | 2026/08/14 02:38 | tool | grepper |
0mArgs:{
"Expression": "config GVE",
"PathPrefix": "drivers/net/ethernet/google/Kconfig"
}Results: {
"Output": "drivers/net/ethernet/google/Kconfig=16=if NET_VENDOR_GOOGLE\ndrivers/net/ethernet/google/Kconfig-17-\ndrivers/net/ethernet/google/Kconfig:18:config GVE\ndrivers/net/ethernet/google/Kconfig-19-\ttristate \"Google Virtual NIC (gVNIC) support\"\n"
} |
| 5/2 | 2026/08/14 02:38 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 38152 output: 37 thoughts: 24 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Kconfig Dependency Deep Dive** Okay, so I need to figure out the dependencies related to Google Virtual Ethernet, specifically the `GVE` driver within the kernel configuration. My instinct is to head straight to the source, the `Kconfig` file. Specifically, I should check `drivers/net/ethernet/google/Kconfig`. That's where all the configuration options for this family of drivers should be defined, including any conditional dependencies. I'm looking for anything that impacts whether or not the `GVE` option is visible, selectable, or even built. This could be hardware requirements, other driver prerequisites, platform specific configurations, or kernel feature flags. I expect to be drilling down on all of these to give me a robust understanding. |
| 6/2 | 2026/08/14 02:38 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/google/Kconfig",
"FirstLine": 1,
"LineCount": 30
}Results: {
"Contents": " 1:\t#\n 2:\t# Google network device configuration\n 3:\t#\n 4:\t\n 5:\tconfig NET_VENDOR_GOOGLE\n 6:\t\tbool \"Google Devices\"\n 7:\t\tdefault y\n 8:\t\thelp\n 9:\t\t If you have a network (Ethernet) device belonging to this class, say Y.\n 10:\t\n 11:\t\t Note that the answer to this question doesn't directly affect the\n 12:\t\t kernel: saying N will just cause the configurator to skip all\n 13:\t\t the questions about Google devices. If you say Y, you will be asked\n 14:\t\t for your specific device in the following questions.\n 15:\t\n 16:\tif NET_VENDOR_GOOGLE\n 17:\t\n 18:\tconfig GVE\n 19:\t\ttristate \"Google Virtual NIC (gVNIC) support\"\n 20:\t\tdepends on (PCI_MSI \u0026\u0026 (X86 || CPU_LITTLE_ENDIAN))\n 21:\t\tdepends on PTP_1588_CLOCK_OPTIONAL\n 22:\t\tselect PAGE_POOL\n 23:\t\thelp\n 24:\t\t This driver supports Google Virtual NIC (gVNIC)\"\n 25:\t\n 26:\t\t To compile this driver as a module, choose M here.\n 27:\t\t The module will be called gve.\n 28:\t\n 29:\tendif #NET_VENDOR_GOOGLE\n"
} |
| 7/2 | 2026/08/14 02:38 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 38761 output: 341 thoughts: 3136 Thoughts: **Fuzzing Strategy for GVE Driver Refactoring** Alright, let's break this down for a fuzzing campaign. My initial assessment is that this patch, given its refactoring of the Google Virtual Ethernet (GVE) driver's control plane – the introduction of `struct gve_ctrl_ops` and `struct gve_device_info` – represents a *high-value target*. We're talking about fundamental changes to driver initialization, teardown, reset, and admin queue operations, which are prime areas for potential vulnerabilities, and since this is a virtualized driver likely used in GCE, it is an accessible target. First, I need to ensure the correct configuration. It appears that `NET_VENDOR_GOOGLE` and `GVE` are the key Kconfig options to enable. Standard practice suggests also enabling `PCI_MSI` and `PTP_1588_CLOCK_OPTIONAL`, but these are likely already defaults. Now, let's identify the specific functions that warrant focused fuzzing. We have several files modified: `gve_adminq.c`, `gve_main.c`, `gve_ethtool.c`, and `gve_flow_rule.c`. The driver itself is, of course, `GVE`. The most direct approach is to pinpoint the exposed functions. I've gone through the code, focusing on the ones that are not declared `static` and that are likely entry points to driver functionality. Here's my candidate list: * `gve_reset`: This is crucial for handling device resets, and could expose a state machine bug. * `gve_add_flow_rule` and `gve_del_flow_rule`: Flow rule management functions. These are critical as improper handling of flow rules is a known attack surface. * `gve_adminq_init`, `gve_adminq_get_device_properties`, `gve_adminq_verify_driver_compatibility`, `gve_adminq_create_queues`, `gve_adminq_destroy_queues`, `gve_adminq_setup_mgmt_irq`, `gve_adminq_request_db_info`, `gve_adminq_set_num_ntfy_blks`, `gve_adminq_map_db_bar`, `gve_adminq_report_link_status`, `gve_adminq_get_ptype_map_dqo`, and `gve_adminq_describe_device`. These are all highly useful for fuzzing, and they seem to have very high potential. These represent the core of the driver's operational flow and are likely entry points for control plane operations. Now, let's summarize: * **Configs**: `NET_VENDOR_GOOGLE`, `GVE` * **Focus Symbols**: `gve_reset`, `gve_add_flow_rule`, `gve_del_flow_rule`, `gve_adminq_init`, `gve_adminq_get_device_properties`, `gve_adminq_verify_driver_compatibility`, `gve_adminq_create_queues`, `gve_adminq_destroy_queues`, `gve_adminq_setup_mgmt_irq`, `gve_adminq_request_db_info`, `gve_adminq_set_num_ntfy_blks`, `gve_adminq_map_db_bar`, `gve_adminq_report_link_status`, `gve_adminq_get_ptype_map_dqo`, `gve_adminq_describe_device` With this list, I am ready to set up the fuzzing campaign and get to work. |
| 8/2 | 2026/08/14 02:38 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"NET_VENDOR_GOOGLE",
"GVE"
],
"FocusSymbols": [
"gve_reset",
"gve_add_flow_rule",
"gve_del_flow_rule",
"gve_adminq_init",
"gve_adminq_get_device_properties",
"gve_adminq_verify_driver_compatibility",
"gve_adminq_create_queues",
"gve_adminq_destroy_queues",
"gve_adminq_setup_mgmt_irq",
"gve_adminq_request_db_info",
"gve_adminq_set_num_ntfy_blks",
"gve_adminq_map_db_bar",
"gve_adminq_report_link_status",
"gve_adminq_get_ptype_map_dqo",
"gve_adminq_describe_device"
],
"Reasoning": "The patch refactors the control plane operations of the Google Virtual Ethernet (gve) driver. It introduces `struct gve_ctrl_ops` and `struct gve_device_info` to abstract control plane operations and device information. This involves significant changes to driver initialization, teardown, reset, and admin queue operations. The gve driver is reachable in virtualized environments like GCE, which is a primary target for syzkaller. Therefore, these functional changes are worth fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"NET_VENDOR_GOOGLE",
"GVE"
],
"FocusSymbols": [
"gve_reset",
"gve_add_flow_rule",
"gve_del_flow_rule",
"gve_adminq_init",
"gve_adminq_get_device_properties",
"gve_adminq_verify_driver_compatibility",
"gve_adminq_create_queues",
"gve_adminq_destroy_queues",
"gve_adminq_setup_mgmt_irq",
"gve_adminq_request_db_info",
"gve_adminq_set_num_ntfy_blks",
"gve_adminq_map_db_bar",
"gve_adminq_report_link_status",
"gve_adminq_get_ptype_map_dqo",
"gve_adminq_describe_device"
],
"Reasoning": "The patch refactors the control plane operations of the Google Virtual Ethernet (gve) driver. It introduces `struct gve_ctrl_ops` and `struct gve_device_info` to abstract control plane operations and device information. This involves significant changes to driver initialization, teardown, reset, and admin queue operations. The gve driver is reachable in virtualized environments like GCE, which is a primary target for syzkaller. Therefore, these functional changes are worth fuzzing.",
"WorthFuzzing": true
} |
| 9/1 | 2026/08/14 02:38 | action | If |
2mArgs:{
"WorthFuzzing": true
}Results: null |
| 10/2 | 2026/08/14 02:38 | agent | kmsan-evaluator |
2mModel:best-expensive Args: null Results: {
"KMSANReasoning": "The patch series refactors the initialization and teardown sequences of the `gve` driver, introducing `struct gve_device_info` to hold device properties and `struct gve_ctrl_ops` for control plane operations.\n\n1. No uninitialized memory is sent to user space or the device. All structures sent to the device (e.g., `struct gve_driver_info`, `union gve_adminq_command`) are either explicitly zeroed using `memset` or allocated using `dma_alloc_coherent` (which guarantees zeroed memory) before being populated.\n2. The patch does not introduce any new `copy_to_user`, `put_user`, netlink attributes, or ioctl output arguments.\n3. The refactored code does not introduce conditional logic or branching that depends on potentially uninitialized variables.\n4. The changes primarily involve reorganizing existing code and adding error handling, which do not expose uninitialized memory risks.\n\nAny potential bugs introduced by these changes (such as out-of-bounds accesses, use-after-free, or locking issues) would be caught by standard KASAN and LOCKDEP. A dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: For your convenience, here is the diff of the changes:
commit ef1a8b6afc61cfca9d272da37e710ba20b159c77
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 14 02:37:30 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h
index c280ff35ee771..3a50e7f7a2e7a 100644
--- a/drivers/net/ethernet/google/gve/gve.h
+++ b/drivers/net/ethernet/google/gve/gve.h
@@ -16,6 +16,8 @@
#include <linux/timer.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/u64_stats_sync.h>
+#include <linux/utsname.h>
+#include <linux/version.h>
#include <net/page_pool/helpers.h>
#include <net/xdp.h>
@@ -674,6 +676,7 @@ struct gve_notify_block {
struct gve_tx_ring *tx; /* tx rings on this block */
struct gve_rx_ring *rx; /* rx rings on this block */
u32 irq;
+ bool irq_requested;
};
/* Tracks allowed and current rx queue settings */
@@ -797,6 +800,79 @@ struct gve_ptp {
struct gve_priv *priv;
};
+struct gve_device_info {
+ enum gve_queue_format queue_format;
+ u16 default_tx_queues;
+ u16 default_rx_queues;
+ u16 max_tx_queues;
+ u16 max_rx_queues;
+ u16 default_tx_ring_size;
+ u16 default_rx_ring_size;
+ u16 max_tx_ring_size;
+ u16 max_rx_ring_size;
+ u16 min_tx_ring_size;
+ u16 min_rx_ring_size;
+ u16 max_mtu;
+ u8 mac[ETH_ALEN];
+ u16 max_rx_buffer_size;
+ u16 header_buf_size;
+ u32 max_flow_rules;
+ u16 rss_key_size;
+ u16 rss_lut_size;
+ u16 tx_pages_per_qpl;
+ u16 num_event_counters;
+ u64 max_registered_pages;
+ bool default_min_ring_size;
+ bool nic_timestamp_supported;
+ bool modify_ring_size_enabled;
+ bool cache_rss_config;
+};
+
+/**
+ * struct gve_ctrl_ops - Control plane operations structure
+ * @map_db_bar: Maps the doorbell BAR for the device and store in @priv.
+ * @unmap_db_bar: Unmaps the doorbell BAR previously mapped by @map_db_bar.
+ * @set_num_queues: Sets default and max TX/RX queues into allocation
+ * structures stored in @priv to be used during initialization.
+ * @set_num_ntfy_blks: Sets no. of vectors into @priv to be used during
+ * initialization.
+ * @request_db_info: Request and store doorbell information into @priv
+ * @release_db_resources: Release device hold on DMA memory holding doorbell
+ * info (AdminQ only)
+ * @setup_mgmt_irq: Setup control plane IRQ
+ * @teardown_mgmt_irq: Teardown control plane IRQ
+ * @get_ptype_map: Learn packet type map from device and store it in @priv
+ * @configure_rss: Set up default RSS configuration
+ * @setup_stats_report: Set up DMA region for stats report (AdminQ only)
+ * @reset_flow_rules: Flush all flow rules from device
+ * @create_queues: Sends commands to the device to create TX/RX queues.
+ * @destroy_queues: Sends commands to the device to destroy TX/RX queues.
+ * @report_link_status: Set link status into @priv->link_up
+ * @report_link_speed: Set link speed into @priv->link_speed
+ */
+struct gve_ctrl_ops {
+ int (*map_db_bar)(struct gve_priv *priv);
+ void (*unmap_db_bar)(struct gve_priv *priv);
+ void (*set_num_queues)(struct gve_priv *priv);
+ int (*set_num_ntfy_blks)(struct gve_priv *priv);
+ int (*request_db_info)(struct gve_priv *priv);
+ void (*release_db_resources)(struct gve_priv *priv);
+ int (*setup_mgmt_irq)(struct gve_priv *priv);
+ void (*teardown_mgmt_irq)(struct gve_priv *priv);
+ int (*get_ptype_map)(struct gve_priv *priv);
+ int (*configure_rss)(struct gve_priv *priv,
+ struct ethtool_rxfh_param *param);
+ int (*setup_stats_report)(struct gve_priv *priv,
+ u64 stats_report_len,
+ dma_addr_t stats_report_addr,
+ u64 interval_ms); /* AQ-specific */
+ int (*reset_flow_rules)(struct gve_priv *priv);
+ int (*create_queues)(struct gve_priv *priv);
+ int (*destroy_queues)(struct gve_priv *priv);
+ int (*report_link_status)(struct gve_priv *priv);
+ int (*report_link_speed)(struct gve_priv *priv);
+};
+
struct gve_priv {
struct net_device *dev;
struct gve_tx_ring *tx; /* array of tx_cfg.num_queues */
@@ -896,8 +972,10 @@ struct gve_priv {
/* Gvnic device link speed from hypervisor. */
u64 link_speed;
+ bool link_up;
bool up_before_suspend; /* True if dev was up before suspend */
+ bool mgmt_irq_requested;
struct gve_ptype_lut *ptype_lut_dqo;
/* Must be a power of two. */
@@ -929,6 +1007,8 @@ struct gve_priv {
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 */
+ struct gve_device_info device_info;
+ const struct gve_ctrl_ops *ctrl_ops;
};
enum gve_service_task_flags_bit {
@@ -1104,6 +1184,11 @@ static inline u32 gve_rx_idx_to_ntfy(struct gve_priv *priv, u32 queue_idx)
return (priv->num_ntfy_blks / 2) + queue_idx;
}
+static inline u32 gve_ntfy_to_msix_idx(struct gve_priv *priv, u32 ntfy_blk_idx)
+{
+ return ntfy_blk_idx;
+}
+
static inline bool gve_is_qpl(struct gve_priv *priv)
{
return priv->queue_format == GVE_GQI_QPL_FORMAT ||
@@ -1207,6 +1292,8 @@ static inline bool gve_is_clock_enabled(struct gve_priv *priv)
return priv->nic_ts_report;
}
+void gve_adminq_write_version(u8 __iomem *driver_version_register);
+
/* gqi napi handler defined in gve_main.c */
int gve_napi_poll(struct napi_struct *napi, int budget);
@@ -1300,7 +1387,7 @@ struct page_pool *gve_rx_create_page_pool(struct gve_priv *priv,
/* Reset */
void gve_schedule_reset(struct gve_priv *priv);
-int gve_reset(struct gve_priv *priv, bool attempt_teardown);
+int gve_reset(struct gve_priv *priv, bool skip_queue_setup);
void gve_get_curr_alloc_cfgs(struct gve_priv *priv,
struct gve_tx_alloc_rings_cfg *tx_alloc_cfg,
struct gve_rx_alloc_rings_cfg *rx_alloc_cfg);
diff --git a/drivers/net/ethernet/google/gve/gve_adminq.c b/drivers/net/ethernet/google/gve/gve_adminq.c
index 08587bf40ed4a..bbf431794d02b 100644
--- a/drivers/net/ethernet/google/gve/gve_adminq.c
+++ b/drivers/net/ethernet/google/gve/gve_adminq.c
@@ -70,7 +70,7 @@ void gve_parse_device_option(struct gve_priv *priv,
dev_info(&priv->pdev->dev,
"Gqi raw addressing device option enabled.\n");
- priv->queue_format = GVE_GQI_RDA_FORMAT;
+ priv->device_info.queue_format = GVE_GQI_RDA_FORMAT;
break;
case GVE_DEV_OPT_ID_GQI_RDA:
if (option_length < sizeof(**dev_op_gqi_rda) ||
@@ -190,7 +190,7 @@ void gve_parse_device_option(struct gve_priv *priv,
/* device has not provided min ring size */
if (option_length == GVE_DEVICE_OPTION_NO_MIN_RING_SIZE)
- priv->default_min_ring_size = true;
+ priv->device_info.default_min_ring_size = true;
break;
case GVE_DEV_OPT_ID_FLOW_STEERING:
if (option_length < sizeof(**dev_op_flow_steering) ||
@@ -296,8 +296,10 @@ gve_process_device_options(struct gve_priv *priv,
return 0;
}
-int gve_adminq_alloc(struct device *dev, struct gve_priv *priv)
+static int gve_adminq_alloc(struct gve_priv *priv)
{
+ struct device *dev = &priv->pdev->dev;
+
priv->adminq_pool = dma_pool_create("adminq_pool", dev,
GVE_ADMINQ_BUFFER_SIZE, 0, 0);
if (unlikely(!priv->adminq_pool))
@@ -353,7 +355,15 @@ int gve_adminq_alloc(struct device *dev, struct gve_priv *priv)
return 0;
}
-void gve_adminq_release(struct gve_priv *priv)
+int gve_adminq_init(struct gve_priv *priv)
+{
+ struct gve_registers __iomem *reg_bar = priv->reg_bar0;
+
+ gve_adminq_write_version(®_bar->driver_version);
+ return gve_adminq_alloc(priv);
+}
+
+static void gve_adminq_release(struct gve_priv *priv)
{
int i = 0;
@@ -382,10 +392,9 @@ void gve_adminq_release(struct gve_priv *priv)
}
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)
+void gve_adminq_free(struct gve_priv *priv)
{
if (!gve_get_admin_queue_ok(priv))
return;
@@ -920,26 +929,40 @@ int gve_adminq_destroy_rx_queues(struct gve_priv *priv, u32 num_queues)
return err;
}
-static void gve_set_default_desc_cnt(struct gve_priv *priv,
- const struct gve_device_descriptor *descriptor)
+static void gve_set_default_rss_sizes(struct gve_priv *priv)
{
- priv->tx_desc_cnt = be16_to_cpu(descriptor->tx_queue_entries);
- priv->rx_desc_cnt = be16_to_cpu(descriptor->rx_queue_entries);
+ struct gve_device_info *device_info = &priv->device_info;
- /* 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;
+ if (device_info->queue_format == GVE_DQO_RDA_FORMAT ||
+ device_info->queue_format == GVE_DQO_QPL_FORMAT) {
+ device_info->rss_key_size = GVE_RSS_KEY_SIZE;
+ device_info->rss_lut_size = GVE_RSS_INDIR_SIZE;
+ device_info->cache_rss_config = true;
+ }
}
-static void gve_set_default_rss_sizes(struct gve_priv *priv)
+int gve_adminq_destroy_queues(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;
+ int num_tx_queues = gve_num_tx_queues(priv);
+ int err;
+
+ err = gve_adminq_destroy_tx_queues(priv, 0, num_tx_queues);
+ if (err) {
+ netif_err(priv, drv, priv->dev,
+ "failed to destroy tx queues\n");
+ /* This failure will trigger a reset - no need to clean up */
+ return err;
}
+ netif_dbg(priv, drv, priv->dev, "destroyed tx queues\n");
+ err = gve_adminq_destroy_rx_queues(priv, priv->rx_cfg.num_queues);
+ if (err) {
+ netif_err(priv, drv, priv->dev,
+ "failed to destroy rx queues\n");
+ /* This failure will trigger a reset - no need to clean up */
+ return err;
+ }
+ netif_dbg(priv, drv, priv->dev, "destroyed rx queues\n");
+ return err;
}
static void gve_enable_supported_features(struct gve_priv *priv,
@@ -959,77 +982,105 @@ static void gve_enable_supported_features(struct gve_priv *priv,
const struct gve_device_option_modify_ring
*dev_op_modify_ring)
{
+ struct gve_device_info *info = &priv->device_info;
+
/* 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.
+ * device_info->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);
+ info->max_mtu = be16_to_cpu(dev_op_jumbo_frames->max_mtu);
}
if (dev_op_buffer_sizes &&
(supported_features_mask & GVE_SUP_BUFFER_SIZES_MASK)) {
- priv->max_rx_buffer_size =
+ info->max_rx_buffer_size =
be16_to_cpu(dev_op_buffer_sizes->packet_buffer_size);
- priv->header_buf_size =
+ info->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);
- if (gve_is_dqo(priv) &&
- priv->max_rx_buffer_size > GVE_DEFAULT_RX_BUFFER_SIZE)
- priv->rx_cfg.packet_buffer_size =
- priv->max_rx_buffer_size;
+ info->max_rx_buffer_size, info->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;
- priv->max_rx_desc_cnt =
+ info->modify_ring_size_enabled = true;
+ info->max_rx_ring_size =
be16_to_cpu(dev_op_modify_ring->max_rx_ring_size);
- priv->max_tx_desc_cnt =
+ info->max_tx_ring_size =
be16_to_cpu(dev_op_modify_ring->max_tx_ring_size);
- if (priv->default_min_ring_size) {
+ if (info->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;
+ info->min_tx_ring_size = GVE_DEFAULT_MIN_TX_RING_SIZE;
+ info->min_rx_ring_size = 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);
+ info->min_rx_ring_size =
+ be16_to_cpu(dev_op_modify_ring->min_rx_ring_size);
+ info->min_tx_ring_size =
+ 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 =
+ info->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);
+ info->max_flow_rules);
}
}
if (dev_op_rss_config &&
(supported_features_mask & GVE_SUP_RSS_CONFIG_MASK)) {
- priv->rss_key_size =
+ info->rss_key_size =
be16_to_cpu(dev_op_rss_config->hash_key_size);
- priv->rss_lut_size =
+ info->rss_lut_size =
be16_to_cpu(dev_op_rss_config->hash_lut_size);
- priv->cache_rss_config = false;
+ info->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);
+ info->rss_key_size, info->rss_lut_size);
}
if (dev_op_nic_timestamp &&
(supported_features_mask & GVE_SUP_NIC_TIMESTAMP_MASK))
- priv->nic_timestamp_supported = true;
+ info->nic_timestamp_supported = true;
+}
+
+static void gve_fill_device_info(struct gve_priv *priv,
+ struct gve_device_descriptor *descriptor)
+{
+ struct gve_device_info *device_info = &priv->device_info;
+ u16 default_num_queues;
+
+ device_info->tx_pages_per_qpl =
+ be16_to_cpu(descriptor->tx_pages_per_qpl);
+ device_info->max_registered_pages =
+ be64_to_cpu(descriptor->max_registered_pages);
+ device_info->num_event_counters = be16_to_cpu(descriptor->counters);
+ ether_addr_copy(device_info->mac, descriptor->mac);
+ device_info->max_mtu = be16_to_cpu(descriptor->mtu);
+
+ default_num_queues = be16_to_cpu(descriptor->default_num_queues);
+ device_info->default_tx_queues = default_num_queues;
+ device_info->default_rx_queues = default_num_queues;
+ device_info->default_tx_ring_size =
+ be16_to_cpu(descriptor->tx_queue_entries);
+ device_info->default_rx_ring_size =
+ be16_to_cpu(descriptor->rx_queue_entries);
+
+ /* set default ranges */
+ device_info->max_tx_ring_size = device_info->default_tx_ring_size;
+ device_info->max_rx_ring_size = device_info->default_rx_ring_size;
+ device_info->min_tx_ring_size = device_info->default_tx_ring_size;
+ device_info->min_rx_ring_size = device_info->default_rx_ring_size;
}
int gve_adminq_describe_device(struct gve_priv *priv)
@@ -1040,6 +1091,7 @@ int gve_adminq_describe_device(struct gve_priv *priv)
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_info *device_info = &priv->device_info;
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;
@@ -1049,8 +1101,6 @@ int gve_adminq_describe_device(struct gve_priv *priv)
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,
@@ -1085,26 +1135,26 @@ int gve_adminq_describe_device(struct gve_priv *priv)
* DqoRda, DqoQpl, GqiRda, GqiQpl. Use GqiQpl as default.
*/
if (dev_op_dqo_rda) {
- priv->queue_format = GVE_DQO_RDA_FORMAT;
+ device_info->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;
+ device_info->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;
+ device_info->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) {
+ } else if (device_info->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;
+ device_info->queue_format = GVE_GQI_QPL_FORMAT;
if (dev_op_gqi_qpl)
supported_features_mask =
be32_to_cpu(dev_op_gqi_qpl->supported_features_mask);
@@ -1112,35 +1162,9 @@ int gve_adminq_describe_device(struct gve_priv *priv)
"Driver is running with GQI QPL queue format.\n");
}
- /* set default descriptor counts */
- gve_set_default_desc_cnt(priv, descriptor);
-
+ gve_fill_device_info(priv, descriptor);
gve_set_default_rss_sizes(priv);
- /* DQO supports HW-GRO and UDP_GSO */
- if (gve_is_dqo(priv)) {
- u64 additional_features = NETIF_F_GRO_HW | NETIF_F_GSO_UDP_L4;
-
- priv->dev->hw_features |= additional_features;
- priv->dev->features |= additional_features;
- }
-
- 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,
@@ -1152,6 +1176,27 @@ int gve_adminq_describe_device(struct gve_priv *priv)
return err;
}
+int __maybe_unused gve_adminq_get_device_properties(struct gve_priv *priv)
+{
+ int err;
+
+ err = gve_adminq_verify_driver_compatibility(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not verify driver compatibility: err=%d\n", err);
+ return err;
+ }
+
+ /* Get the initial information we need from the device */
+ err = gve_adminq_describe_device(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not get device information: err=%d\n", err);
+ return err;
+ }
+ return 0;
+}
+
int gve_adminq_register_page_list(struct gve_priv *priv,
struct gve_queue_page_list *qpl)
{
@@ -1214,20 +1259,53 @@ int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len,
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)
+int gve_adminq_verify_driver_compatibility(struct gve_priv *priv)
{
+ struct gve_driver_info *driver_info;
union gve_adminq_command cmd;
+ dma_addr_t driver_info_bus;
+ int err;
+
+ driver_info = dma_alloc_coherent(&priv->pdev->dev,
+ sizeof(struct gve_driver_info),
+ &driver_info_bus, GFP_KERNEL);
+ if (!driver_info)
+ return -ENOMEM;
+
+ *driver_info = (struct gve_driver_info) {
+ .os_type = 1, /* Linux */
+ .os_version_major = cpu_to_be32(LINUX_VERSION_MAJOR),
+ .os_version_minor = cpu_to_be32(LINUX_VERSION_SUBLEVEL),
+ .os_version_sub = cpu_to_be32(LINUX_VERSION_PATCHLEVEL),
+ .driver_capability_flags = {
+ cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS1),
+ cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS2),
+ cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS3),
+ cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS4),
+ },
+ };
+ strscpy(driver_info->os_version_str1, utsname()->release,
+ sizeof(driver_info->os_version_str1));
+ strscpy(driver_info->os_version_str2, utsname()->version,
+ sizeof(driver_info->os_version_str2));
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),
+ .driver_info_len = cpu_to_be64(sizeof(struct gve_driver_info)),
+ .driver_info_addr = cpu_to_be64(driver_info_bus),
};
- return gve_adminq_execute_cmd(priv, &cmd);
+ err = gve_adminq_execute_cmd(priv, &cmd);
+
+ /* It's ok if the device doesn't support this */
+ if (err == -EOPNOTSUPP)
+ err = 0;
+
+ dma_free_coherent(&priv->pdev->dev,
+ sizeof(struct gve_driver_info),
+ driver_info, driver_info_bus);
+ return err;
}
int gve_adminq_report_link_speed(struct gve_priv *priv)
@@ -1273,8 +1351,7 @@ int gve_adminq_report_nic_ts(struct gve_priv *priv,
return gve_adminq_execute_cmd(priv, &cmd);
}
-int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv,
- struct gve_ptype_lut *ptype_lut)
+int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv)
{
struct gve_ptype_map *ptype_map;
union gve_adminq_command cmd;
@@ -1300,9 +1377,9 @@ int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv,
/* Populate ptype_lut. */
for (i = 0; i < GVE_NUM_PTYPES; i++) {
- ptype_lut->ptypes[i].l3_type =
+ priv->ptype_lut_dqo->ptypes[i].l3_type =
ptype_map->ptypes[i].l3_type;
- ptype_lut->ptypes[i].l4_type =
+ priv->ptype_lut_dqo->ptypes[i].l4_type =
ptype_map->ptypes[i].l4_type;
}
err:
@@ -1320,12 +1397,8 @@ gve_adminq_configure_flow_rule(struct gve_priv *priv,
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) {
+ if (!err)
priv->flow_rules_cache.rules_cache_synced = false;
- }
return err;
}
@@ -1600,3 +1673,174 @@ int gve_adminq_query_rss_config(struct gve_priv *priv, struct ethtool_rxfh_param
dma_pool_free(priv->adminq_pool, descriptor, descriptor_bus);
return err;
}
+
+int gve_adminq_set_num_ntfy_blks(struct gve_priv *priv)
+{
+ int num_ntfy;
+
+ num_ntfy = pci_msix_vec_count(priv->pdev);
+ if (num_ntfy <= 0) {
+ dev_err(&priv->pdev->dev,
+ "could not count MSI-x vectors: err=%d\n", num_ntfy);
+ return num_ntfy;
+ } else if (num_ntfy < GVE_MIN_MSIX) {
+ dev_err(&priv->pdev->dev, "gve needs at least %d MSI-x vectors, but only has %d\n",
+ GVE_MIN_MSIX, num_ntfy);
+ return -EINVAL;
+ }
+
+ /* gvnic has one Notification Block per MSI-x vector, except for the
+ * management vector
+ */
+ priv->num_ntfy_blks = (num_ntfy - 1) & ~0x1;
+ priv->mgmt_msix_idx = priv->num_ntfy_blks;
+
+ return 0;
+}
+
+void gve_adminq_set_num_queues(struct gve_priv *priv)
+{
+ priv->tx_cfg.max_queues =
+ min_t(int, priv->tx_cfg.max_queues, priv->num_ntfy_blks / 2);
+ priv->rx_cfg.max_queues =
+ min_t(int, priv->rx_cfg.max_queues, priv->num_ntfy_blks / 2);
+
+ priv->tx_cfg.num_queues = priv->tx_cfg.max_queues;
+ priv->rx_cfg.num_queues = priv->rx_cfg.max_queues;
+ if (priv->default_num_queues > 0) {
+ priv->tx_cfg.num_queues = min_t(int, priv->default_num_queues,
+ priv->tx_cfg.num_queues);
+ priv->rx_cfg.num_queues = min_t(int, priv->default_num_queues,
+ priv->rx_cfg.num_queues);
+ }
+}
+
+int gve_adminq_map_db_bar(struct gve_priv *priv)
+{
+ struct pci_dev *pdev = priv->pdev;
+ void __iomem *db_bar;
+
+ db_bar = pci_iomap(pdev, GVE_DOORBELL_BAR, 0);
+ if (!db_bar) {
+ dev_err(&pdev->dev, "Failed to map doorbell bar!\n");
+ return -ENOMEM;
+ }
+ priv->db_bar2 = db_bar;
+ return 0;
+}
+
+void gve_adminq_unmap_db_bar(struct gve_priv *priv)
+{
+ struct pci_dev *pdev = priv->pdev;
+
+ pci_iounmap(pdev, priv->db_bar2);
+}
+
+int gve_adminq_request_db_info(struct gve_priv *priv)
+{
+ int err;
+ int i;
+
+ err = gve_adminq_configure_device_resources(priv,
+ priv->counter_array_bus,
+ priv->num_event_counters,
+ priv->irq_db_indices_bus,
+ priv->num_ntfy_blks);
+ if (unlikely(err)) {
+ dev_err(&priv->pdev->dev,
+ "could not setup device_resources: err=%d\n", err);
+ return -ENXIO;
+ }
+
+ for (i = 0; i < priv->num_ntfy_blks; i++)
+ priv->ntfy_blocks[i].irq_db_index =
+ &priv->irq_db_indices[i].index;
+ return 0;
+}
+
+void gve_adminq_release_db_resources(struct gve_priv *priv)
+{
+ int err;
+
+ /* Log error in deconfigure device, but don't fail. This is only ever
+ * called as a reset is about to be triggered, so it would be redundant
+ * to trigger a reset.
+ */
+ err = gve_adminq_deconfigure_device_resources(priv);
+ if (err)
+ dev_err(&priv->pdev->dev,
+ "Could not deconfigure device resources: err=%d\n",
+ err);
+}
+
+static irqreturn_t gve_mgmnt_intr(int irq, void *arg)
+{
+ struct gve_priv *priv = arg;
+
+ /* Device resources are not okay; consider the interrupt spurious. */
+ if (!gve_get_device_resources_ok(priv))
+ return IRQ_NONE;
+
+ queue_work(priv->gve_wq, &priv->service_task);
+ return IRQ_HANDLED;
+}
+
+int gve_adminq_setup_mgmt_irq(struct gve_priv *priv)
+{
+ int err;
+
+ snprintf(priv->mgmt_msix_name, sizeof(priv->mgmt_msix_name),
+ "gve-mgmnt@pci:%s", pci_name(priv->pdev));
+ err = request_irq(priv->msix_vectors[priv->mgmt_msix_idx].vector,
+ gve_mgmnt_intr, 0, priv->mgmt_msix_name, priv);
+ if (err)
+ return err;
+
+ priv->mgmt_irq_requested = true;
+
+ return 0;
+}
+
+void gve_adminq_teardown_mgmt_irq(struct gve_priv *priv)
+{
+ if (priv->mgmt_irq_requested) {
+ free_irq(priv->msix_vectors[priv->mgmt_msix_idx].vector, priv);
+ priv->mgmt_irq_requested = false;
+ }
+}
+
+int gve_adminq_create_queues(struct gve_priv *priv)
+{
+ int num_tx_queues = gve_num_tx_queues(priv);
+ int err;
+
+ err = gve_adminq_create_tx_queues(priv, 0, num_tx_queues);
+ if (err) {
+ netif_err(priv, drv, priv->dev, "failed to create %d tx queues\n",
+ num_tx_queues);
+ return err;
+ }
+ netif_dbg(priv, drv, priv->dev, "created %d tx queues\n",
+ num_tx_queues);
+
+ err = gve_adminq_create_rx_queues(priv, priv->rx_cfg.num_queues);
+ if (err) {
+ netif_err(priv, drv, priv->dev, "failed to create %d rx queues\n",
+ priv->rx_cfg.num_queues);
+ return err;
+ }
+ netif_dbg(priv, drv, priv->dev, "created %d rx queues\n",
+ priv->rx_cfg.num_queues);
+
+ return err;
+}
+
+int gve_adminq_report_link_status(struct gve_priv *priv)
+{
+ u32 status;
+
+ status = ioread32be(&priv->reg_bar0->device_status);
+ WRITE_ONCE(priv->link_up,
+ !!(GVE_DEVICE_STATUS_LINK_STATUS_MASK & status));
+ return 0;
+}
diff --git a/drivers/net/ethernet/google/gve/gve_adminq.h b/drivers/net/ethernet/google/gve/gve_adminq.h
index 22a74b6aa17ea..8daac5fd685ab 100644
--- a/drivers/net/ethernet/google/gve/gve_adminq.h
+++ b/drivers/net/ethernet/google/gve/gve_adminq.h
@@ -619,9 +619,8 @@ union gve_adminq_command {
static_assert(sizeof(union gve_adminq_command) == 64);
-int gve_adminq_alloc(struct device *dev, struct gve_priv *priv);
-void gve_adminq_free(struct device *dev, struct gve_priv *priv);
-void gve_adminq_release(struct gve_priv *priv);
+int gve_adminq_init(struct gve_priv *priv);
+void gve_adminq_free(struct gve_priv *priv);
int gve_adminq_describe_device(struct gve_priv *priv);
int gve_adminq_configure_device_resources(struct gve_priv *priv,
dma_addr_t counter_array_bus_addr,
@@ -629,6 +628,8 @@ int gve_adminq_configure_device_resources(struct gve_priv *priv,
dma_addr_t db_array_bus_addr,
u32 num_ntfy_blks);
int gve_adminq_deconfigure_device_resources(struct gve_priv *priv);
+int gve_adminq_create_queues(struct gve_priv *priv);
+int gve_adminq_destroy_queues(struct gve_priv *priv);
int gve_adminq_create_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues);
int gve_adminq_destroy_tx_queues(struct gve_priv *priv, u32 start_id, u32 num_queues);
int gve_adminq_create_single_rx_queue(struct gve_priv *priv, u32 queue_index);
@@ -640,9 +641,9 @@ int gve_adminq_register_page_list(struct gve_priv *priv,
int gve_adminq_unregister_page_list(struct gve_priv *priv, u32 page_list_id);
int gve_adminq_report_stats(struct gve_priv *priv, u64 stats_report_len,
dma_addr_t stats_report_addr, u64 interval);
-int gve_adminq_verify_driver_compatibility(struct gve_priv *priv,
- u64 driver_info_len,
- dma_addr_t driver_info_addr);
+int gve_adminq_verify_driver_compatibility(struct gve_priv *priv);
+int gve_adminq_get_device_properties(struct gve_priv *priv);
+int gve_adminq_report_link_status(struct gve_priv *priv);
int gve_adminq_report_link_speed(struct gve_priv *priv);
int gve_adminq_add_flow_rule(struct gve_priv *priv, struct gve_adminq_flow_rule *rule, u32 loc);
int gve_adminq_del_flow_rule(struct gve_priv *priv, u32 loc);
@@ -654,7 +655,13 @@ 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,
- struct gve_ptype_lut *ptype_lut);
-
+int gve_adminq_get_ptype_map_dqo(struct gve_priv *priv);
+int gve_adminq_set_num_ntfy_blks(struct gve_priv *priv);
+void gve_adminq_set_num_queues(struct gve_priv *priv);
+int gve_adminq_map_db_bar(struct gve_priv *priv);
+void gve_adminq_unmap_db_bar(struct gve_priv *priv);
+int gve_adminq_request_db_info(struct gve_priv *priv);
+void gve_adminq_release_db_resources(struct gve_priv *priv);
+int gve_adminq_setup_mgmt_irq(struct gve_priv *priv);
+void gve_adminq_teardown_mgmt_irq(struct gve_priv *priv);
#endif /* _GVE_ADMINQ_H */
diff --git a/drivers/net/ethernet/google/gve/gve_dqo.h b/drivers/net/ethernet/google/gve/gve_dqo.h
index 5871f773f0c70..ce402020dff6a 100644
--- a/drivers/net/ethernet/google/gve/gve_dqo.h
+++ b/drivers/net/ethernet/google/gve/gve_dqo.h
@@ -66,7 +66,7 @@ void gve_rx_write_doorbell_dqo(const struct gve_priv *priv, int queue_idx);
void gve_xdp_tx_flush_dqo(struct gve_priv *priv, u32 xdp_qid);
static inline void
-gve_tx_put_doorbell_dqo(const struct gve_priv *priv,
+gve_tx_put_doorbell_dqo(struct gve_priv *priv,
const struct gve_queue_resources *q_resources, u32 val)
{
u64 index;
diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c
index 8199738ba979e..a3220c6514382 100644
--- a/drivers/net/ethernet/google/gve/gve_ethtool.c
+++ b/drivers/net/ethernet/google/gve/gve_ethtool.c
@@ -651,7 +651,7 @@ static int gve_user_reset(struct net_device *netdev, u32 *flags)
if (*flags == ETH_RESET_ALL) {
*flags = 0;
- return gve_reset(priv, true);
+ return gve_reset(priv, false);
}
return -EOPNOTSUPP;
@@ -749,12 +749,11 @@ static int gve_get_link_ksettings(struct net_device *netdev,
int err = 0;
if (priv->link_speed == 0)
- err = gve_adminq_report_link_speed(priv);
+ err = priv->ctrl_ops->report_link_speed(priv);
cmd->base.speed = priv->link_speed;
cmd->base.duplex = DUPLEX_FULL;
-
return err;
}
diff --git a/drivers/net/ethernet/google/gve/gve_flow_rule.c b/drivers/net/ethernet/google/gve/gve_flow_rule.c
index 2c80cda28ef30..fae552f4ad6fb 100644
--- a/drivers/net/ethernet/google/gve/gve_flow_rule.c
+++ b/drivers/net/ethernet/google/gve/gve_flow_rule.c
@@ -278,6 +278,11 @@ int gve_add_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd)
goto out;
err = gve_adminq_add_flow_rule(priv, rule, fsp->location);
+ if (err == -ETIME) {
+ dev_err(&priv->pdev->dev,
+ "Timeout to add flow rule, trigger reset.");
+ gve_reset(priv, false);
+ }
out:
kvfree(rule);
@@ -290,9 +295,17 @@ int gve_add_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd)
int gve_del_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd)
{
struct ethtool_rx_flow_spec *fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
+ int err;
if (!priv->max_flow_rules)
return -EOPNOTSUPP;
- return gve_adminq_del_flow_rule(priv, fsp->location);
+ err = gve_adminq_del_flow_rule(priv, fsp->location);
+ if (err == -ETIME) {
+ dev_err(&priv->pdev->dev,
+ "Timeout to delete flow rule, trigger reset.");
+ gve_reset(priv, false);
+ }
+
+ return err;
}
diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c
index e4d78ae52dafe..800e1104d01b1 100644
--- a/drivers/net/ethernet/google/gve/gve_main.c
+++ b/drivers/net/ethernet/google/gve/gve_main.c
@@ -17,7 +17,6 @@
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
-#include <linux/utsname.h>
#include <linux/version.h>
#include <net/netdev_queues.h>
#include <net/sch_generic.h>
@@ -41,49 +40,6 @@ char gve_driver_name[] = "gve";
const char gve_version_str[] = GVE_VERSION;
static const char gve_version_prefix[] = GVE_VERSION_PREFIX;
-static int gve_verify_driver_compatibility(struct gve_priv *priv)
-{
- int err;
- struct gve_driver_info *driver_info;
- dma_addr_t driver_info_bus;
-
- driver_info = dma_alloc_coherent(&priv->pdev->dev,
- sizeof(struct gve_driver_info),
- &driver_info_bus, GFP_KERNEL);
- if (!driver_info)
- return -ENOMEM;
-
- *driver_info = (struct gve_driver_info) {
- .os_type = 1, /* Linux */
- .os_version_major = cpu_to_be32(LINUX_VERSION_MAJOR),
- .os_version_minor = cpu_to_be32(LINUX_VERSION_SUBLEVEL),
- .os_version_sub = cpu_to_be32(LINUX_VERSION_PATCHLEVEL),
- .driver_capability_flags = {
- cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS1),
- cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS2),
- cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS3),
- cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS4),
- },
- };
- strscpy(driver_info->os_version_str1, utsname()->release,
- sizeof(driver_info->os_version_str1));
- strscpy(driver_info->os_version_str2, utsname()->version,
- sizeof(driver_info->os_version_str2));
-
- err = gve_adminq_verify_driver_compatibility(priv,
- sizeof(struct gve_driver_info),
- driver_info_bus);
-
- /* It's ok if the device doesn't support this */
- if (err == -EOPNOTSUPP)
- err = 0;
-
- dma_free_coherent(&priv->pdev->dev,
- sizeof(struct gve_driver_info),
- driver_info, driver_info_bus);
- return err;
-}
-
static netdev_features_t gve_features_check(struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features)
@@ -248,6 +204,30 @@ static void gve_free_counter_array(struct gve_priv *priv)
priv->counter_array = NULL;
}
+static int gve_alloc_irq_db_indices(struct gve_priv *priv)
+{
+ priv->irq_db_indices =
+ dma_alloc_coherent(&priv->pdev->dev,
+ priv->num_ntfy_blks *
+ sizeof(*priv->irq_db_indices),
+ &priv->irq_db_indices_bus, GFP_KERNEL);
+ if (!priv->irq_db_indices)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void gve_free_irq_db_indices(struct gve_priv *priv)
+{
+ if (!priv->irq_db_indices)
+ return;
+
+ dma_free_coherent(&priv->pdev->dev, priv->num_ntfy_blks *
+ sizeof(*priv->irq_db_indices),
+ priv->irq_db_indices, priv->irq_db_indices_bus);
+ priv->irq_db_indices = NULL;
+}
+
/* NIC requests to report stats */
static void gve_stats_report_task(struct work_struct *work)
{
@@ -311,14 +291,6 @@ static void gve_free_stats_report(struct gve_priv *priv)
priv->stats_report = NULL;
}
-static irqreturn_t gve_mgmnt_intr(int irq, void *arg)
-{
- struct gve_priv *priv = arg;
-
- queue_work(priv->gve_wq, &priv->service_task);
- return IRQ_HANDLED;
-}
-
static irqreturn_t gve_intr(int irq, void *arg)
{
struct gve_notify_block *block = arg;
@@ -469,6 +441,15 @@ int gve_napi_poll_dqo(struct napi_struct *napi, int budget)
return work_done;
}
+static void gve_free_notify_blocks(struct gve_priv *priv)
+{
+ pci_disable_msix(priv->pdev);
+ kvfree(priv->ntfy_blocks);
+ priv->ntfy_blocks = NULL;
+ kvfree(priv->msix_vectors);
+ priv->msix_vectors = NULL;
+}
+
static const struct cpumask *gve_get_node_mask(struct gve_priv *priv)
{
if (priv->numa_node == NUMA_NO_NODE)
@@ -480,11 +461,9 @@ static const struct cpumask *gve_get_node_mask(struct gve_priv *priv)
static int gve_alloc_notify_blocks(struct gve_priv *priv)
{
int num_vecs_requested = priv->num_ntfy_blks + 1;
- const struct cpumask *node_mask;
- unsigned int cur_cpu;
int vecs_enabled;
- int i, j;
int err;
+ int i;
priv->msix_vectors = kvzalloc_objs(*priv->msix_vectors,
num_vecs_requested);
@@ -498,7 +477,7 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)
dev_err(&priv->pdev->dev, "Could not enable min msix %d/%d\n",
GVE_MIN_MSIX, vecs_enabled);
err = vecs_enabled;
- goto abort_with_msix_vectors;
+ goto abort;
}
if (vecs_enabled != num_vecs_requested) {
int new_num_ntfy_blks = (vecs_enabled - 1) & ~0x1;
@@ -521,38 +500,65 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)
priv->rx_cfg.num_queues = priv->rx_cfg.max_queues;
}
- /* Setup Management Vector - the last vector */
- snprintf(priv->mgmt_msix_name, sizeof(priv->mgmt_msix_name), "gve-mgmnt@pci:%s",
- pci_name(priv->pdev));
- err = request_irq(priv->msix_vectors[priv->mgmt_msix_idx].vector,
- gve_mgmnt_intr, 0, priv->mgmt_msix_name, priv);
- if (err) {
- dev_err(&priv->pdev->dev, "Did not receive management vector.\n");
- goto abort_with_msix_enabled;
- }
- priv->irq_db_indices =
- dma_alloc_coherent(&priv->pdev->dev,
- priv->num_ntfy_blks *
- sizeof(*priv->irq_db_indices),
- &priv->irq_db_indices_bus, GFP_KERNEL);
- if (!priv->irq_db_indices) {
- err = -ENOMEM;
- goto abort_with_mgmt_vector;
- }
-
priv->ntfy_blocks = kvzalloc(priv->num_ntfy_blks *
sizeof(*priv->ntfy_blocks), GFP_KERNEL);
if (!priv->ntfy_blocks) {
err = -ENOMEM;
- goto abort_with_irq_db_indices;
+ goto abort;
+ }
+
+ return 0;
+abort:
+ gve_free_notify_blocks(priv);
+ return err;
+}
+
+static void gve_teardown_notify_blocks(struct gve_priv *priv)
+{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
+ int i;
+
+ if (!priv->ntfy_blocks)
+ return;
+
+ for (i = 0; i < priv->num_ntfy_blks; i++) {
+ struct gve_notify_block *block = &priv->ntfy_blocks[i];
+ int msix_idx = gve_ntfy_to_msix_idx(priv, i);
+
+ if (!block->irq_requested)
+ continue;
+
+ irq_set_affinity_hint(priv->msix_vectors[msix_idx].vector,
+ NULL);
+ free_irq(priv->msix_vectors[msix_idx].vector, block);
+ block->irq = 0;
+ block->irq_requested = false;
+ }
+
+ ops->teardown_mgmt_irq(priv);
+}
+
+static int gve_setup_notify_blocks(struct gve_priv *priv)
+{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
+ const struct cpumask *node_mask;
+ unsigned int cur_cpu;
+ int i;
+ int err;
+
+ /* Setup Management Vector */
+ err = ops->setup_mgmt_irq(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Did not receive management vector.\n");
+ return err;
}
- /* Setup the other blocks - the first n-1 vectors */
node_mask = gve_get_node_mask(priv);
cur_cpu = cpumask_first(node_mask);
for (i = 0; i < priv->num_ntfy_blks; i++) {
struct gve_notify_block *block = &priv->ntfy_blocks[i];
- int msix_idx = i;
+ int msix_idx = gve_ntfy_to_msix_idx(priv, i);
snprintf(block->name, sizeof(block->name), "gve-ntfy-blk%d@pci:%s",
i, pci_name(priv->pdev));
@@ -562,13 +568,13 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)
IRQF_NO_AUTOEN, block->name, block);
if (err) {
dev_err(&priv->pdev->dev,
- "Failed to receive msix vector %d\n", i);
- goto abort_with_some_ntfy_blocks;
+ "Failed to receive msix vector %d\n", msix_idx);
+ goto abort;
}
block->irq = priv->msix_vectors[msix_idx].vector;
+ block->irq_requested = true;
irq_set_affinity_and_hint(block->irq,
cpumask_of(cur_cpu));
- block->irq_db_index = &priv->irq_db_indices[i].index;
cur_cpu = cpumask_next(cur_cpu, node_mask);
/* Wrap once CPUs in the node have been exhausted, or when
@@ -579,63 +585,29 @@ static int gve_alloc_notify_blocks(struct gve_priv *priv)
cur_cpu = cpumask_first(node_mask);
}
return 0;
-abort_with_some_ntfy_blocks:
- for (j = 0; j < i; j++) {
- struct gve_notify_block *block = &priv->ntfy_blocks[j];
- int msix_idx = j;
- irq_set_affinity_hint(priv->msix_vectors[msix_idx].vector,
- NULL);
- free_irq(priv->msix_vectors[msix_idx].vector, block);
- block->irq = 0;
- }
- kvfree(priv->ntfy_blocks);
- priv->ntfy_blocks = NULL;
-abort_with_irq_db_indices:
- dma_free_coherent(&priv->pdev->dev, priv->num_ntfy_blks *
- sizeof(*priv->irq_db_indices),
- priv->irq_db_indices, priv->irq_db_indices_bus);
- priv->irq_db_indices = NULL;
-abort_with_mgmt_vector:
- free_irq(priv->msix_vectors[priv->mgmt_msix_idx].vector, priv);
-abort_with_msix_enabled:
- pci_disable_msix(priv->pdev);
-abort_with_msix_vectors:
- kvfree(priv->msix_vectors);
- priv->msix_vectors = NULL;
+abort:
+ gve_teardown_notify_blocks(priv);
return err;
}
-static void gve_free_notify_blocks(struct gve_priv *priv)
+static void gve_free_control_plane_resources(struct gve_priv *priv)
{
- int i;
-
- if (!priv->msix_vectors)
- return;
+ bitmap_free(priv->xsk_pools);
+ priv->xsk_pools = NULL;
- /* Free the irqs */
- for (i = 0; i < priv->num_ntfy_blks; i++) {
- struct gve_notify_block *block = &priv->ntfy_blocks[i];
- int msix_idx = i;
+ kvfree(priv->ptype_lut_dqo);
+ priv->ptype_lut_dqo = NULL;
- irq_set_affinity_hint(priv->msix_vectors[msix_idx].vector,
- NULL);
- free_irq(priv->msix_vectors[msix_idx].vector, block);
- block->irq = 0;
- }
- free_irq(priv->msix_vectors[priv->mgmt_msix_idx].vector, priv);
- kvfree(priv->ntfy_blocks);
- priv->ntfy_blocks = NULL;
- dma_free_coherent(&priv->pdev->dev, priv->num_ntfy_blks *
- sizeof(*priv->irq_db_indices),
- priv->irq_db_indices, priv->irq_db_indices_bus);
- priv->irq_db_indices = NULL;
- pci_disable_msix(priv->pdev);
- kvfree(priv->msix_vectors);
- priv->msix_vectors = NULL;
+ gve_free_irq_db_indices(priv);
+ gve_free_counter_array(priv);
+ gve_free_notify_blocks(priv);
+ gve_free_stats_report(priv);
+ gve_free_rss_config_cache(priv);
+ gve_free_flow_rule_caches(priv);
}
-static int gve_setup_device_resources(struct gve_priv *priv)
+static int gve_alloc_control_plane_resources(struct gve_priv *priv)
{
int err;
@@ -644,39 +616,58 @@ static int gve_setup_device_resources(struct gve_priv *priv)
return err;
err = gve_alloc_rss_config_cache(priv);
if (err)
- goto abort_with_flow_rule_caches;
- err = gve_alloc_counter_array(priv);
- if (err)
- goto abort_with_rss_config_cache;
+ goto abort;
err = gve_alloc_notify_blocks(priv);
if (err)
- goto abort_with_counter;
+ goto abort;
err = gve_alloc_stats_report(priv);
if (err)
- goto abort_with_ntfy_blocks;
- err = gve_adminq_configure_device_resources(priv,
- priv->counter_array_bus,
- priv->num_event_counters,
- priv->irq_db_indices_bus,
- priv->num_ntfy_blks);
- if (unlikely(err)) {
- dev_err(&priv->pdev->dev,
- "could not setup device_resources: err=%d\n", err);
- err = -ENXIO;
- goto abort_with_stats_report;
- }
+ goto abort;
+ err = gve_alloc_counter_array(priv);
+ if (err)
+ goto abort;
+ err = gve_alloc_irq_db_indices(priv);
+ if (err)
+ goto abort;
if (!gve_is_gqi(priv)) {
- priv->ptype_lut_dqo = kvzalloc_obj(*priv->ptype_lut_dqo);
+ priv->ptype_lut_dqo = kvzalloc_obj(*priv->ptype_lut_dqo,
+ GFP_KERNEL);
if (!priv->ptype_lut_dqo) {
err = -ENOMEM;
- goto abort_with_stats_report;
+ goto abort;
}
- err = gve_adminq_get_ptype_map_dqo(priv, priv->ptype_lut_dqo);
+ }
+
+ priv->xsk_pools = bitmap_zalloc(priv->rx_cfg.max_queues, GFP_KERNEL);
+ if (!priv->xsk_pools) {
+ err = -ENOMEM;
+ goto abort;
+ }
+
+ return 0;
+abort:
+ gve_free_control_plane_resources(priv);
+ return err;
+}
+
+static int gve_setup_control_plane_resources(struct gve_priv *priv)
+{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
+ int err;
+
+ err = ops->request_db_info(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev, "Failed to get db info");
+ return err;
+ }
+
+ if (!gve_is_gqi(priv)) {
+ err = ops->get_ptype_map(priv);
if (err) {
dev_err(&priv->pdev->dev,
"Failed to get ptype map: err=%d\n", err);
- goto abort_with_ptype_lut;
+ goto release_db_resources;
}
}
@@ -691,79 +682,76 @@ static int gve_setup_device_resources(struct gve_priv *priv)
err = gve_init_rss_config(priv, priv->rx_cfg.num_queues);
if (err) {
dev_err(&priv->pdev->dev, "Failed to init RSS config");
- goto abort_with_clock;
+ goto teardown_clock;
}
- err = gve_adminq_report_stats(priv, priv->stats_report_len,
+ err = ops->setup_stats_report(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);
return 0;
-abort_with_clock:
+teardown_clock:
gve_teardown_clock(priv);
-abort_with_ptype_lut:
- kvfree(priv->ptype_lut_dqo);
- priv->ptype_lut_dqo = NULL;
-abort_with_stats_report:
- gve_free_stats_report(priv);
-abort_with_ntfy_blocks:
- gve_free_notify_blocks(priv);
-abort_with_counter:
- gve_free_counter_array(priv);
-abort_with_rss_config_cache:
- gve_free_rss_config_cache(priv);
-abort_with_flow_rule_caches:
- gve_free_flow_rule_caches(priv);
-
+release_db_resources:
+ ops->release_db_resources(priv);
return err;
}
-static void gve_trigger_reset(struct gve_priv *priv);
-
-static void gve_teardown_device_resources(struct gve_priv *priv)
+/**
+ * gve_teardown_control_plane_resources() - Request the device to release any
+ * shared allocated resources.
+ *
+ * @priv: Pointer to the GVE private device data structure.
+ *
+ * If any part of the teardown step fails, the failure is documented, but is
+ * otherwise ignored. It is expected that a device reset is triggered
+ * immediately after tearing down device resources, which would clear any
+ * lingering state on the device.
+ */
+static void gve_teardown_control_plane_resources(struct gve_priv *priv)
{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
int err;
/* Tell device its resources are being freed */
if (gve_get_device_resources_ok(priv)) {
err = gve_flow_rules_reset(priv);
- if (err) {
+ if (err)
dev_err(&priv->pdev->dev,
"Failed to reset flow rules: err=%d\n", err);
- gve_trigger_reset(priv);
- }
/* detach the stats report */
- err = gve_adminq_report_stats(priv, 0, 0x0, GVE_STATS_REPORT_TIMER_PERIOD);
- if (err) {
+ err = ops->setup_stats_report(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);
- if (err) {
- dev_err(&priv->pdev->dev,
- "Could not deconfigure device resources: err=%d\n",
- err);
- gve_trigger_reset(priv);
- }
+ gve_teardown_clock(priv);
+ ops->release_db_resources(priv);
}
- kvfree(priv->ptype_lut_dqo);
- priv->ptype_lut_dqo = NULL;
-
- gve_free_flow_rule_caches(priv);
- gve_free_rss_config_cache(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);
}
+static void gve_teardown_device(struct gve_priv *priv)
+{
+ gve_teardown_notify_blocks(priv);
+ gve_teardown_control_plane_resources(priv);
+ gve_adminq_free(priv);
+ /*
+ * Free any resources shared with the device only after we have a
+ * guarantee that the device will not try to access such resources.
+ * Device commands in gve_teardown_control_plane_resources can fail, in
+ * which case, device resources won't be relinquished until
+ * gve_adminq_free is called to trigger a device reset.
+ */
+ gve_free_control_plane_resources(priv);
+}
+
static int gve_unregister_qpl(struct gve_priv *priv,
struct gve_queue_page_list *qpl)
{
@@ -886,33 +874,18 @@ static int gve_unregister_qpls(struct gve_priv *priv)
static int gve_create_rings(struct gve_priv *priv)
{
- int num_tx_queues = gve_num_tx_queues(priv);
int err;
int i;
- err = gve_adminq_create_tx_queues(priv, 0, num_tx_queues);
- if (err) {
- netif_err(priv, drv, priv->dev, "failed to create %d tx queues\n",
- num_tx_queues);
- /* This failure will trigger a reset - no need to clean
- * up
- */
- return err;
- }
- netif_dbg(priv, drv, priv->dev, "created %d tx queues\n",
- num_tx_queues);
-
- err = gve_adminq_create_rx_queues(priv, priv->rx_cfg.num_queues);
+ err = priv->ctrl_ops->create_queues(priv);
if (err) {
- netif_err(priv, drv, priv->dev, "failed to create %d rx queues\n",
- priv->rx_cfg.num_queues);
+ netif_err(priv, drv, priv->dev,
+ "failed to create queues\n");
/* This failure will trigger a reset - no need to clean
* up
*/
return err;
}
- netif_dbg(priv, drv, priv->dev, "created %d rx queues\n",
- priv->rx_cfg.num_queues);
if (gve_is_gqi(priv)) {
/* Rx data ring has been prefilled with packet buffers at queue
@@ -1067,26 +1040,12 @@ static int gve_queues_mem_alloc(struct gve_priv *priv,
static int gve_destroy_rings(struct gve_priv *priv)
{
- int num_tx_queues = gve_num_tx_queues(priv);
int err;
- err = gve_adminq_destroy_tx_queues(priv, 0, num_tx_queues);
- if (err) {
- netif_err(priv, drv, priv->dev,
- "failed to destroy tx queues\n");
- /* This failure will trigger a reset - no need to clean up */
- return err;
- }
- netif_dbg(priv, drv, priv->dev, "destroyed tx queues\n");
- err = gve_adminq_destroy_rx_queues(priv, priv->rx_cfg.num_queues);
- if (err) {
- netif_err(priv, drv, priv->dev,
- "failed to destroy rx queues\n");
- /* This failure will trigger a reset - no need to clean up */
- return err;
- }
- netif_dbg(priv, drv, priv->dev, "destroyed rx queues\n");
- return 0;
+ err = priv->ctrl_ops->destroy_queues(priv);
+
+ /* A failure will trigger a reset - no need to clean up */
+ return err;
}
static void gve_queues_mem_free(struct gve_priv *priv,
@@ -1202,8 +1161,6 @@ void gve_schedule_reset(struct gve_priv *priv)
queue_work(priv->gve_wq, &priv->service_task);
}
-static void gve_reset_and_teardown(struct gve_priv *priv, bool was_up);
-static int gve_reset_recovery(struct gve_priv *priv, bool was_up);
static void gve_turndown(struct gve_priv *priv);
static void gve_turnup(struct gve_priv *priv);
@@ -1314,11 +1271,12 @@ static int gve_reg_xdp_info(struct gve_priv *priv, struct net_device *dev)
return err;
}
-
static void gve_drain_page_cache(struct gve_priv *priv)
{
int i;
+ if (!priv->rx)
+ return;
for (i = 0; i < priv->rx_cfg.num_queues; i++)
page_frag_cache_drain(&priv->rx[i].page_cache);
}
@@ -1461,10 +1419,11 @@ static int gve_queues_start(struct gve_priv *priv,
reset:
if (gve_get_reset_in_progress(priv))
goto stop_and_free_rings;
- gve_reset_and_teardown(priv, true);
- /* if this fails there is nothing we can do so just ignore the return */
- gve_reset_recovery(priv, false);
- /* return the original error */
+
+ /* Attempt to reset. If reset is successful, gve_queues_start was
+ * successful.
+ */
+ err = gve_reset(priv, false);
return err;
stop_and_free_rings:
gve_tx_stop_rings(priv, gve_num_tx_queues(priv));
@@ -1480,6 +1439,12 @@ static int gve_open(struct net_device *dev)
struct gve_priv *priv = netdev_priv(dev);
int err;
+ if (!gve_get_device_resources_ok(priv)) {
+ dev_err(&priv->pdev->dev,
+ "Attempting to open netdev without resources. Device must be reset.");
+ return -ENODEV;
+ }
+
gve_get_curr_alloc_cfgs(priv, &tx_alloc_cfg, &rx_alloc_cfg);
err = gve_queues_mem_alloc(priv, &tx_alloc_cfg, &rx_alloc_cfg);
@@ -1496,41 +1461,18 @@ static int gve_open(struct net_device *dev)
return 0;
}
-static int gve_queues_stop(struct gve_priv *priv)
+static void gve_queues_stop(struct gve_priv *priv)
{
- int err;
-
- netif_carrier_off(priv->dev);
- if (gve_get_device_rings_ok(priv)) {
- gve_turndown(priv);
- gve_drain_page_cache(priv);
- err = gve_destroy_rings(priv);
- if (err)
- goto err;
- err = gve_unregister_qpls(priv);
- if (err)
- goto err;
- gve_clear_device_rings_ok(priv);
- }
- timer_delete_sync(&priv->stats_report_timer);
+ gve_turndown(priv);
gve_unreg_xdp_info(priv);
+ gve_drain_page_cache(priv);
+
+ timer_delete_sync(&priv->stats_report_timer);
+ cancel_work_sync(&priv->stats_report_task);
gve_tx_stop_rings(priv, gve_num_tx_queues(priv));
gve_rx_stop_rings(priv, priv->rx_cfg.num_queues);
-
- priv->interface_down_cnt++;
- return 0;
-
-err:
- /* This must have been called from a reset due to the rtnl lock
- * so just return at this point.
- */
- if (gve_get_reset_in_progress(priv))
- return err;
- /* Otherwise reset before returning */
- gve_reset_and_teardown(priv, true);
- return gve_reset_recovery(priv, false);
}
static int gve_close(struct net_device *dev)
@@ -1538,23 +1480,42 @@ static int gve_close(struct net_device *dev)
struct gve_priv *priv = netdev_priv(dev);
int err;
- err = gve_queues_stop(priv);
- if (err)
- return err;
+ gve_queues_stop(priv);
+
+ /* Surrender to reset if the queue destroying adminq cmds fail. Reset
+ * will not re-enable the interface.
+ */
+ if (gve_get_device_rings_ok(priv)) {
+ gve_clear_device_rings_ok(priv);
+ err = gve_destroy_rings(priv);
+ if (err)
+ goto reset;
+ err = gve_unregister_qpls(priv);
+ if (err)
+ goto reset;
+ }
gve_queues_mem_remove(priv);
+ priv->interface_down_cnt++;
return 0;
+
+reset:
+ err = gve_reset(priv, true);
+ return err;
}
-static void gve_handle_link_status(struct gve_priv *priv, bool link_status)
+static void gve_handle_link_status(struct gve_priv *priv)
{
+ bool link_up;
+
if (!gve_get_napi_enabled(priv))
return;
- if (link_status == netif_carrier_ok(priv->dev))
+ link_up = READ_ONCE(priv->link_up);
+ if (link_up == netif_carrier_ok(priv->dev))
return;
- if (link_status) {
+ if (link_up) {
netdev_info(priv->dev, "Device link is up.\n");
netif_carrier_on(priv->dev);
} else {
@@ -1581,7 +1542,6 @@ static int gve_set_xdp(struct gve_priv *priv, struct bpf_prog *prog,
{
struct bpf_prog *old_prog;
int err = 0;
- u32 status;
old_prog = READ_ONCE(priv->xdp_prog);
if (!netif_running(priv->dev)) {
@@ -1608,8 +1568,8 @@ static int gve_set_xdp(struct gve_priv *priv, struct bpf_prog *prog,
bpf_prog_put(old_prog);
out:
- status = ioread32be(&priv->reg_bar0->device_status);
- gve_handle_link_status(priv, GVE_DEVICE_STATUS_LINK_STATUS_MASK & status);
+ priv->ctrl_ops->report_link_status(priv);
+ gve_handle_link_status(priv);
return err;
}
@@ -1824,6 +1784,7 @@ static int gve_xdp(struct net_device *dev, struct netdev_bpf *xdp)
int gve_init_rss_config(struct gve_priv *priv, u16 num_queues)
{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
struct gve_rss_config *rss_config = &priv->rss_config;
struct ethtool_rxfh_param rxfh = {0};
u16 i;
@@ -1839,15 +1800,17 @@ int gve_init_rss_config(struct gve_priv *priv, u16 num_queues)
rxfh.hfunc = ETH_RSS_HASH_TOP;
- return gve_adminq_configure_rss(priv, &rxfh);
+ return ops->configure_rss(priv, &rxfh);
}
int gve_flow_rules_reset(struct gve_priv *priv)
{
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
+
if (!priv->max_flow_rules)
return 0;
- return gve_adminq_reset_flow_rules(priv);
+ return ops->reset_flow_rules(priv);
}
int gve_adjust_config(struct gve_priv *priv,
@@ -2039,11 +2002,11 @@ static void gve_turnup(struct gve_priv *priv)
static void gve_turnup_and_check_status(struct gve_priv *priv)
{
- u32 status;
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
gve_turnup(priv);
- status = ioread32be(&priv->reg_bar0->device_status);
- gve_handle_link_status(priv, GVE_DEVICE_STATUS_LINK_STATUS_MASK & status);
+ ops->report_link_status(priv);
+ gve_handle_link_status(priv);
}
static struct gve_notify_block *gve_get_tx_notify_block(struct gve_priv *priv,
@@ -2367,12 +2330,14 @@ static void gve_service_task(struct work_struct *work)
{
struct gve_priv *priv = container_of(work, struct gve_priv,
service_task);
+ const struct gve_ctrl_ops *ops = priv->ctrl_ops;
u32 status = ioread32be(&priv->reg_bar0->device_status);
gve_handle_status(priv, status);
gve_handle_reset(priv);
- gve_handle_link_status(priv, GVE_DEVICE_STATUS_LINK_STATUS_MASK & status);
+ ops->report_link_status(priv);
+ gve_handle_link_status(priv);
}
static void gve_set_netdev_xdp_features(struct gve_priv *priv)
@@ -2398,188 +2363,249 @@ static const struct xdp_metadata_ops gve_xdp_metadata_ops = {
.xmo_rx_timestamp = gve_xdp_rx_timestamp,
};
-static int gve_init_priv(struct gve_priv *priv, bool skip_describe_device)
+static void gve_set_desc_cnt(struct gve_priv *priv)
{
- int num_ntfy;
- int err;
+ struct gve_device_info *device_info = &priv->device_info;
- /* Set up the adminq */
- err = gve_adminq_alloc(&priv->pdev->dev, priv);
- if (err) {
- dev_err(&priv->pdev->dev,
- "Failed to alloc admin queue: err=%d\n", err);
- return err;
- }
+ priv->tx_desc_cnt = device_info->default_tx_ring_size;
+ priv->rx_desc_cnt = device_info->default_rx_ring_size;
+ priv->max_tx_desc_cnt = device_info->max_tx_ring_size;
+ priv->max_rx_desc_cnt = device_info->max_rx_ring_size;
+ priv->min_tx_desc_cnt = device_info->min_tx_ring_size;
+ priv->min_rx_desc_cnt = device_info->min_rx_ring_size;
+}
- err = gve_verify_driver_compatibility(priv);
- if (err) {
- dev_err(&priv->pdev->dev,
- "Could not verify driver compatibility: err=%d\n", err);
- goto err;
- }
+static void gve_set_queue_properties(struct gve_priv *priv)
+{
+ struct gve_device_info *device_info = &priv->device_info;
- priv->num_registered_pages = 0;
+ gve_set_desc_cnt(priv);
+ priv->max_registered_pages = device_info->max_registered_pages;
+ priv->tx_pages_per_qpl = device_info->tx_pages_per_qpl;
+}
- if (skip_describe_device)
- goto setup_device;
+static int gve_set_mtu(struct gve_priv *priv)
+{
+ struct gve_device_info *device_info = &priv->device_info;
+ u16 mtu;
- priv->queue_format = GVE_QUEUE_FORMAT_UNSPECIFIED;
- /* Get the initial information we need from the device */
- err = gve_adminq_describe_device(priv);
- if (err) {
- dev_err(&priv->pdev->dev,
- "Could not get device information: err=%d\n", err);
- goto err;
- }
- priv->dev->mtu = priv->dev->max_mtu;
- num_ntfy = pci_msix_vec_count(priv->pdev);
- if (num_ntfy <= 0) {
- dev_err(&priv->pdev->dev,
- "could not count MSI-x vectors: err=%d\n", num_ntfy);
- err = num_ntfy;
- goto err;
- } else if (num_ntfy < GVE_MIN_MSIX) {
- dev_err(&priv->pdev->dev, "gve needs at least %d MSI-x vectors, but only has %d\n",
- GVE_MIN_MSIX, num_ntfy);
- err = -EINVAL;
- goto err;
+ mtu = device_info->max_mtu;
+ if (mtu < ETH_MIN_MTU) {
+ dev_err(&priv->pdev->dev, "MTU %d below minimum MTU\n", mtu);
+ return -EINVAL;
}
+ priv->dev->max_mtu = mtu;
+ priv->dev->mtu = mtu;
+
+ return 0;
+}
+
+static void gve_set_mac(struct gve_priv *priv)
+{
+ struct gve_device_info *device_info = &priv->device_info;
+ u8 *mac;
+
+ mac = device_info->mac;
+ eth_hw_addr_set(priv->dev, mac);
+ dev_info(&priv->pdev->dev, "MAC addr: %pM\n", mac);
+}
+
+static void gve_set_buf_sizes(struct gve_priv *priv)
+{
+ struct gve_device_info *device_info = &priv->device_info;
+
+ if (device_info->max_rx_buffer_size > priv->max_rx_buffer_size)
+ priv->max_rx_buffer_size = device_info->max_rx_buffer_size;
- /* Big TCP is only supported on DQO */
+ if (gve_is_dqo(priv) &&
+ priv->max_rx_buffer_size > GVE_DEFAULT_RX_BUFFER_SIZE)
+ priv->rx_cfg.packet_buffer_size = priv->max_rx_buffer_size;
+
+ if (device_info->header_buf_size)
+ priv->header_buf_size = device_info->header_buf_size;
+}
+
+static int gve_setup_device(struct gve_priv *priv)
+{
+ int err;
+
+ priv->num_registered_pages = 0;
+
+ gve_set_netdev_xdp_features(priv);
if (!gve_is_gqi(priv))
- netif_set_tso_max_size(priv->dev, GVE_DQO_TX_MAX);
+ priv->dev->xdp_metadata_ops = &gve_xdp_metadata_ops;
- priv->rx_copybreak = GVE_DEFAULT_RX_COPYBREAK;
- /* gvnic has one Notification Block per MSI-x vector, except for the
- * management vector
- */
- priv->num_ntfy_blks = (num_ntfy - 1) & ~0x1;
- priv->mgmt_msix_idx = priv->num_ntfy_blks;
- priv->numa_node = dev_to_node(&priv->pdev->dev);
+ err = gve_alloc_control_plane_resources(priv);
+ if (err)
+ return err;
+
+ err = gve_setup_control_plane_resources(priv);
+ if (err)
+ return err;
+
+ err = gve_setup_notify_blocks(priv);
+ if (err)
+ return err;
+ return 0;
+}
- priv->tx_cfg.max_queues =
- min_t(int, priv->tx_cfg.max_queues, priv->num_ntfy_blks / 2);
- priv->rx_cfg.max_queues =
- min_t(int, priv->rx_cfg.max_queues, priv->num_ntfy_blks / 2);
+static const struct gve_ctrl_ops gve_adminq_ops = {
+ .map_db_bar = gve_adminq_map_db_bar,
+ .unmap_db_bar = gve_adminq_unmap_db_bar,
+ .set_num_queues = gve_adminq_set_num_queues,
+ .set_num_ntfy_blks = gve_adminq_set_num_ntfy_blks,
+ .get_ptype_map = gve_adminq_get_ptype_map_dqo,
+ .reset_flow_rules = gve_adminq_reset_flow_rules,
+ .setup_stats_report = gve_adminq_report_stats,
+ .configure_rss = gve_adminq_configure_rss,
+ .request_db_info = gve_adminq_request_db_info,
+ .release_db_resources = gve_adminq_release_db_resources,
+ .setup_mgmt_irq = gve_adminq_setup_mgmt_irq,
+ .teardown_mgmt_irq = gve_adminq_teardown_mgmt_irq,
+ .create_queues = gve_adminq_create_queues,
+ .destroy_queues = gve_adminq_destroy_queues,
+ .report_link_status = gve_adminq_report_link_status,
+ .report_link_speed = gve_adminq_report_link_speed,
+};
+
+static int gve_init_priv(struct gve_priv *priv)
+{
+ struct gve_device_info *device_info = &priv->device_info;
+ int err;
- priv->tx_cfg.num_queues = priv->tx_cfg.max_queues;
- priv->rx_cfg.num_queues = priv->rx_cfg.max_queues;
- if (priv->default_num_queues > 0) {
- priv->tx_cfg.num_queues = min_t(int, priv->default_num_queues,
- priv->tx_cfg.num_queues);
- priv->rx_cfg.num_queues = min_t(int, priv->default_num_queues,
- priv->rx_cfg.num_queues);
+ priv->queue_format = priv->device_info.queue_format;
+
+ err = priv->ctrl_ops->set_num_ntfy_blks(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not setup notify blocks: err=%d\n", err);
+ return err;
}
- priv->tx_cfg.num_xdp_queues = 0;
+ priv->ctrl_ops->set_num_queues(priv);
dev_info(&priv->pdev->dev, "TX queues %d, RX queues %d\n",
priv->tx_cfg.num_queues, priv->rx_cfg.num_queues);
dev_info(&priv->pdev->dev, "Max TX queues %d, Max RX queues %d\n",
priv->tx_cfg.max_queues, priv->rx_cfg.max_queues);
- if (!gve_is_gqi(priv)) {
+ if (gve_is_dqo(priv)) {
+ /* DQO supports HW-GRO and UDP_GSO */
+ u64 additional_features = NETIF_F_GRO_HW | NETIF_F_GSO_UDP_L4;
+
+ priv->dev->hw_features |= additional_features;
+ priv->dev->features |= additional_features;
+
priv->tx_coalesce_usecs = GVE_TX_IRQ_RATELIMIT_US_DQO;
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:
- priv->xsk_pools = bitmap_zalloc(priv->rx_cfg.max_queues, GFP_KERNEL);
- if (!priv->xsk_pools) {
- err = -ENOMEM;
- goto err;
+ /* Big TCP is only supported on DQO */
+ netif_set_tso_max_size(priv->dev, GVE_DQO_TX_MAX);
}
- gve_set_netdev_xdp_features(priv);
- if (!gve_is_gqi(priv))
- priv->dev->xdp_metadata_ops = &gve_xdp_metadata_ops;
+ if (gve_set_mtu(priv))
+ return -EINVAL;
- err = gve_setup_device_resources(priv);
- if (err)
- goto err_free_xsk_bitmap;
+ priv->num_event_counters = device_info->num_event_counters;
- return 0;
+ gve_set_mac(priv);
-err_free_xsk_bitmap:
- bitmap_free(priv->xsk_pools);
- priv->xsk_pools = NULL;
-err:
- gve_adminq_free(&priv->pdev->dev, priv);
- return err;
-}
+ gve_set_queue_properties(priv);
+ priv->modify_ring_size_enabled = device_info->modify_ring_size_enabled;
-static void gve_teardown_priv_resources(struct gve_priv *priv)
-{
- gve_teardown_device_resources(priv);
- gve_adminq_free(&priv->pdev->dev, priv);
- bitmap_free(priv->xsk_pools);
- priv->xsk_pools = NULL;
-}
+ gve_set_buf_sizes(priv);
-static void gve_trigger_reset(struct gve_priv *priv)
-{
- /* Reset the device by releasing the AQ */
- gve_adminq_release(priv);
-}
+ priv->max_flow_rules = device_info->max_flow_rules;
+ if (priv->max_flow_rules)
+ priv->dev->hw_features |= NETIF_F_NTUPLE;
-static void gve_reset_and_teardown(struct gve_priv *priv, bool was_up)
-{
- gve_trigger_reset(priv);
- /* With the reset having already happened, close cannot fail */
- if (was_up)
- gve_close(priv->dev);
- gve_teardown_priv_resources(priv);
+ priv->rss_key_size = device_info->rss_key_size;
+ priv->rss_lut_size = device_info->rss_lut_size;
+ priv->cache_rss_config = device_info->cache_rss_config;
+
+ priv->numa_node = dev_to_node(&priv->pdev->dev);
+ priv->tx_cfg.num_xdp_queues = 0;
+ priv->rx_copybreak = GVE_DEFAULT_RX_COPYBREAK;
+ priv->ts_config.tx_type = HWTSTAMP_TX_OFF;
+ priv->ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
+ priv->nic_timestamp_supported = device_info->nic_timestamp_supported;
+ return 0;
}
-static int gve_reset_recovery(struct gve_priv *priv, bool was_up)
+static int gve_recover(struct gve_priv *priv, bool setup_queues)
{
+ bool service_task_enabled = false;
int err;
- err = gve_init_priv(priv, true);
+ err = gve_adminq_init(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Failed to alloc admin queue: err=%d\n", err);
+ goto teardown_device;
+ }
+
+ err = gve_adminq_verify_driver_compatibility(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not verify driver compatibility: err=%d\n", err);
+ goto teardown_device;
+ }
+
+ err = gve_setup_device(priv);
if (err)
- goto err;
- if (was_up) {
+ goto teardown_device;
+
+ /* In the case of cascading failures, the disable count could be greater
+ * than 1. The service task must be enabled before starting queues.
+ */
+ while (!service_task_enabled)
+ service_task_enabled = enable_work(&priv->service_task);
+
+ if (setup_queues) {
err = gve_open(priv->dev);
if (err)
- goto err;
+ goto disable_work;
}
+
return 0;
-err:
- dev_err(&priv->pdev->dev, "Reset failed! !!! DISABLING ALL QUEUES !!!\n");
- gve_turndown(priv);
+
+disable_work:
+ disable_work(&priv->service_task);
+teardown_device:
+ dev_err(&priv->pdev->dev, "Recover failed! !!! DISABLING ALL QUEUES !!!\n");
+ gve_teardown_device(priv);
return err;
}
-int gve_reset(struct gve_priv *priv, bool attempt_teardown)
+int gve_reset(struct gve_priv *priv, bool skip_queue_setup)
{
bool was_up = netif_running(priv->dev);
int err;
+ if (gve_get_reset_in_progress(priv))
+ return 0;
+
dev_info(&priv->pdev->dev, "Performing reset\n");
gve_clear_do_reset(priv);
gve_set_reset_in_progress(priv);
- /* If we aren't attempting to teardown normally, just go turndown and
- * reset right away.
- */
- if (!attempt_teardown) {
- gve_turndown(priv);
- gve_reset_and_teardown(priv, was_up);
- } else {
- /* Otherwise attempt to close normally */
- if (was_up) {
- err = gve_close(priv->dev);
- /* If that fails reset as we did above */
- if (err)
- gve_reset_and_teardown(priv, was_up);
+
+ if (was_up) {
+ gve_queues_stop(priv);
+ if (gve_get_device_rings_ok(priv)) {
+ gve_clear_device_rings_ok(priv);
+ gve_destroy_rings(priv);
+ gve_unregister_qpls(priv);
}
- /* Clean up any remaining resources */
- gve_teardown_priv_resources(priv);
}
- /* Set it all back up */
- err = gve_reset_recovery(priv, was_up);
+ disable_work(&priv->service_task);
+ gve_teardown_device(priv);
+ gve_queues_mem_remove(priv);
+
+ err = gve_recover(priv, was_up && !skip_queue_setup);
+ if (err)
+ dev_info(&priv->pdev->dev,
+ "Failed to recover in reset: %d\n", err);
+
gve_clear_reset_in_progress(priv);
priv->reset_cnt++;
priv->interface_up_cnt = 0;
@@ -2588,7 +2614,7 @@ int gve_reset(struct gve_priv *priv, bool attempt_teardown)
return err;
}
-static void gve_write_version(u8 __iomem *driver_version_register)
+void gve_adminq_write_version(u8 __iomem *driver_version_register)
{
const char *c = gve_version_prefix;
@@ -2794,7 +2820,6 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int max_tx_queues, max_rx_queues;
struct net_device *dev;
- __be32 __iomem *db_bar;
struct gve_registers __iomem *reg_bar;
struct gve_priv *priv;
int err;
@@ -2822,14 +2847,6 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto abort_with_pci_region;
}
- db_bar = pci_iomap(pdev, GVE_DOORBELL_BAR, 0);
- if (!db_bar) {
- dev_err(&pdev->dev, "Failed to map doorbell bar!\n");
- err = -ENOMEM;
- goto abort_with_reg_bar;
- }
-
- gve_write_version(®_bar->driver_version);
/* Get max queues to alloc etherdev */
max_tx_queues = ioread32be(®_bar->max_tx_queues);
max_rx_queues = ioread32be(®_bar->max_rx_queues);
@@ -2838,7 +2855,7 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (!dev) {
dev_err(&pdev->dev, "could not allocate netdev\n");
err = -ENOMEM;
- goto abort_with_db_bar;
+ goto abort_with_reg_bar;
}
SET_NETDEV_DEV(dev, &pdev->dev);
pci_set_drvdata(pdev, dev);
@@ -2870,35 +2887,65 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
priv->pdev = pdev;
priv->msg_enable = DEFAULT_MSG_LEVEL;
priv->reg_bar0 = reg_bar;
- priv->db_bar2 = db_bar;
priv->service_task_flags = 0x0;
priv->state_flags = 0x0;
priv->ethtool_flags = 0x0;
priv->rx_cfg.packet_buffer_size = GVE_DEFAULT_RX_BUFFER_SIZE;
priv->max_rx_buffer_size = GVE_DEFAULT_RX_BUFFER_SIZE;
+ err = gve_adminq_init(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Failed to alloc admin queue: err=%d\n", err);
+ goto abort_with_netdev;
+ }
+
+ priv->device_info.queue_format = GVE_QUEUE_FORMAT_UNSPECIFIED;
+ err = gve_adminq_get_device_properties(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not get device information: err=%d\n", err);
+ goto abort_with_adminq;
+ }
+
+ /* Set adminq ctrl ops */
+ priv->ctrl_ops = &gve_adminq_ops;
+
+ err = priv->ctrl_ops->map_db_bar(priv);
+ if (err) {
+ err = -ENOMEM;
+ goto abort_with_adminq;
+ }
+
gve_set_probe_in_progress(priv);
priv->gve_wq = alloc_ordered_workqueue("gve", 0);
if (!priv->gve_wq) {
dev_err(&pdev->dev, "Could not allocate workqueue");
err = -ENOMEM;
- goto abort_with_netdev;
+ goto abort_with_unmap_db_bar;
}
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->rx_cfg.max_queues = max_rx_queues;
- err = gve_init_priv(priv, false);
+ err = gve_init_priv(priv);
if (err)
goto abort_with_wq;
+ err = gve_setup_device(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev,
+ "Could not setup device: err=%d\n", err);
+ goto abort_teardown_device;
+ }
+
if (!gve_is_gqi(priv) && !gve_is_qpl(priv))
dev->netmem_tx = NETMEM_TX_DMA;
err = register_netdev(dev);
if (err)
- goto abort_with_gve_init;
+ goto abort_teardown_device;
dev_info(&pdev->dev, "GVE version %s\n", gve_version_str);
dev_info(&pdev->dev, "GVE queue format %d\n", (int)priv->queue_format);
@@ -2906,18 +2953,22 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
queue_work(priv->gve_wq, &priv->service_task);
return 0;
-abort_with_gve_init:
- gve_teardown_priv_resources(priv);
+abort_teardown_device:
+ disable_work(&priv->stats_report_task);
+ gve_teardown_device(priv);
abort_with_wq:
destroy_workqueue(priv->gve_wq);
+abort_with_unmap_db_bar:
+ priv->ctrl_ops->unmap_db_bar(priv);
+
+abort_with_adminq:
+ gve_adminq_free(priv);
+
abort_with_netdev:
free_netdev(dev);
-abort_with_db_bar:
- pci_iounmap(pdev, db_bar);
-
abort_with_reg_bar:
pci_iounmap(pdev, reg_bar);
@@ -2933,14 +2984,14 @@ static void gve_remove(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct gve_priv *priv = netdev_priv(netdev);
- __be32 __iomem *db_bar = priv->db_bar2;
void __iomem *reg_bar = priv->reg_bar0;
unregister_netdev(netdev);
- gve_teardown_priv_resources(priv);
+ disable_work_sync(&priv->service_task);
+ gve_teardown_device(priv);
destroy_workqueue(priv->gve_wq);
+ priv->ctrl_ops->unmap_db_bar(priv);
free_netdev(netdev);
- pci_iounmap(pdev, db_bar);
pci_iounmap(pdev, reg_bar);
pci_release_regions(pdev);
pci_disable_device(pdev);
@@ -2953,16 +3004,13 @@ static void gve_shutdown(struct pci_dev *pdev)
bool was_up = netif_running(priv->dev);
netif_device_detach(netdev);
+ disable_work_sync(&priv->service_task);
rtnl_lock();
netdev_lock(netdev);
- if (was_up && gve_close(priv->dev)) {
- /* If the dev was up, attempt to close, if close fails, reset */
- gve_reset_and_teardown(priv, was_up);
- } else {
- /* If the dev wasn't up or close worked, finish tearing down */
- gve_teardown_priv_resources(priv);
- }
+ if (was_up)
+ gve_close(priv->dev);
+ gve_teardown_device(priv);
netdev_unlock(netdev);
rtnl_unlock();
}
@@ -2974,16 +3022,14 @@ static int gve_suspend(struct device *dev)
struct gve_priv *priv = netdev_priv(netdev);
bool was_up = netif_running(priv->dev);
+ disable_work_sync(&priv->service_task);
+
priv->suspend_cnt++;
rtnl_lock();
netdev_lock(netdev);
- if (was_up && gve_close(priv->dev)) {
- /* If the dev was up, attempt to close, if close fails, reset */
- gve_reset_and_teardown(priv, was_up);
- } else {
- /* If the dev wasn't up or close worked, finish tearing down */
- gve_teardown_priv_resources(priv);
- }
+ if (was_up)
+ gve_close(priv->dev);
+ gve_teardown_device(priv);
priv->up_before_suspend = was_up;
netdev_unlock(netdev);
rtnl_unlock();
@@ -3000,7 +3046,7 @@ static int gve_resume(struct device *dev)
priv->resume_cnt++;
rtnl_lock();
netdev_lock(netdev);
- err = gve_reset_recovery(priv, priv->up_before_suspend);
+ err = gve_recover(priv, priv->up_before_suspend);
netdev_unlock(netdev);
rtnl_unlock();
return err;
|
| 11/3 | 2026/08/14 02:38 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 33757 output: 22 thoughts: 676 Thoughts: **KMSAN Risk Assessment for gve Driver Patch Series**
Okay, so I need to dig into this `gve` driver patch series and specifically identify potential UNINITIALIZED MEMORY risks that KMSAN might pick up on. The changes refactor initialization and teardown, shifting a lot of code into these new `gve_ctrl_ops` and `gve_device_info` structures. The goal here seems to be to centralize device properties and control plane operations. No problem there, seems like a good refactoring pattern.
The critical thing, obviously, is to assess *how* the driver now initializes memory and whether this new structure causes any problems.
So, `gve_device_info` is being added to `gve_priv`, along with `gve_ctrl_ops`. Then the code moves on to look at `gve_adminq_verify_driver_compatibility`. And, hang on... it's allocating `struct gve_driver_info` using `dma_alloc_coherent`. This is immediately interesting because `struct gve_driver_info` is then sent *to the device*. That's where we need to focus.
Now I need to go through this code:
```c
driver_info = dma_alloc_coherent(&priv->pdev->dev,
sizeof(struct gve_driver_info),
&driver_info_bus, GFP_KERNEL);
if (!driver_info)
return -ENOMEM;
*driver_info = (struct gve_driver_info) {
.os_type = 1, /* Linux */
.os_version_major = cpu_to_be32(LINUX_VERSION_MAJOR),
.os_version_minor = cpu_to_be32(LINUX_VERSION_SUBLEVEL),
.os_version_sub = cpu_to_be32(LINUX_VERSION_PATCHLEVEL),
.driver_capability_flags = {
cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS1),
cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS2),
cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS3),
cpu_to_be64(GVE_DRIVER_CAPABILITY_FLAGS4),
},
};
strscpy(driver_info->os_version_str1, utsname()->release,
sizeof(driver_info->os_version_str1));
strscpy(driver_info->os_version_str2, utsname()->version,
sizeof(driver_info->os_version_str2));
```
The key is this initialization. It appears to be using a compound literal assignment. So, in C, all uninitialized fields and padding are zeroed out when you use this format: `(struct gve_driver_info){...}`? Hold on... I need to double-check that. C99 says that padding bytes aren't *guaranteed* to be zeroed in structure assignment. Damn, that's what I needed to check! Now, I need to go look at the definition of `struct gve_driver_info` to assess the risk, because this is the information sent to the hardware.
|
| 12/3 | 2026/08/14 02:38 | tool | grepper |
0mArgs:{
"Expression": "struct gve_driver_info \\{"
}Results: {
"Output": "drivers/net/ethernet/google/gve/gve_adminq.h=266=static_assert(sizeof(struct gve_adminq_extended_command) == 16);\ndrivers/net/ethernet/google/gve/gve_adminq.h-267-\ndrivers/net/ethernet/google/gve/gve_adminq.h:268:struct gve_driver_info {\ndrivers/net/ethernet/google/gve/gve_adminq.h-269-\tu8 os_type;\t/* 0x01 = Linux */\n"
} |
| 13/3 | 2026/08/14 02:38 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 35348 output: 47 thoughts: 17 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into the `gve_driver_info` Definition** Alright, let's get down to brass tacks. I need to fully understand the structure of `struct gve_driver_info`. It's crucial for properly interacting with the GVE (Generic Virtual Ethernet) driver. I need to be sure I know every single element, its purpose, and how it relates to the overall driver framework. I'm thinking I'll start by looking at the header file directly, that should give me a solid base. Let's see what's in there. I'm expecting fields related to device identification, potentially some function pointers for driver callbacks, and maybe configuration parameters for the virtual network interface. It's important to grasp the data layout precisely so that it's possible to write efficient and correct code for device interaction. I need to have a concrete mental model of this struct before I proceed. This could save me a lot of headaches down the line. |
| 14/3 | 2026/08/14 02:38 | tool | grepper |
0mArgs:{
"Expression": "struct gve_driver_info \\{",
"PathPrefix": "drivers/net/ethernet/google/gve/gve_adminq.h"
}Results: {
"Output": "drivers/net/ethernet/google/gve/gve_adminq.h=266=static_assert(sizeof(struct gve_adminq_extended_command) == 16);\ndrivers/net/ethernet/google/gve/gve_adminq.h-267-\ndrivers/net/ethernet/google/gve/gve_adminq.h:268:struct gve_driver_info {\ndrivers/net/ethernet/google/gve/gve_adminq.h-269-\tu8 os_type;\t/* 0x01 = Linux */\n"
} |
| 15/3 | 2026/08/14 02:38 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 35792 output: 47 thoughts: 34 Thoughts: Here's my take on the thought process, as requested: **Diving into the GVE Admin Queue Implementation** Okay, time to dig into the `gve_adminq.h` file. I need to get a better handle on the admin queue mechanism within the GVE driver. I'm going to jump straight to around line 268 – that's often a good spot to find a function declaration or a key data structure related to the core operational flow. Let's see what interesting tidbits are hiding there. I'm hoping to get a quick overview of how the admin queue is structured and what the main elements are within this portion of the code. This should provide some solid context. |
| 16/3 | 2026/08/14 02:38 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/google/gve/gve_adminq.h",
"FirstLine": 268,
"LineCount": 30
}Results: {
"Contents": " 268:\tstruct gve_driver_info {\n 269:\t\tu8 os_type;\t/* 0x01 = Linux */\n 270:\t\tu8 driver_major;\n 271:\t\tu8 driver_minor;\n 272:\t\tu8 driver_sub;\n 273:\t\t__be32 os_version_major;\n 274:\t\t__be32 os_version_minor;\n 275:\t\t__be32 os_version_sub;\n 276:\t\t__be64 driver_capability_flags[4];\n 277:\t\tu8 os_version_str1[GVE_VERSION_STR_LEN];\n 278:\t\tu8 os_version_str2[GVE_VERSION_STR_LEN];\n 279:\t};\n 280:\t\n 281:\tstruct gve_adminq_verify_driver_compatibility {\n 282:\t\t__be64 driver_info_len;\n 283:\t\t__be64 driver_info_addr;\n 284:\t};\n 285:\t\n 286:\tstatic_assert(sizeof(struct gve_adminq_verify_driver_compatibility) == 16);\n 287:\t\n 288:\tstruct gve_adminq_configure_device_resources {\n 289:\t\t__be64 counter_array;\n 290:\t\t__be64 irq_db_addr;\n 291:\t\t__be32 num_counters;\n 292:\t\t__be32 num_irq_dbs;\n 293:\t\t__be32 irq_db_stride;\n 294:\t\t__be32 ntfy_blk_msix_base_idx;\n 295:\t\tu8 queue_format;\n 296:\t\tu8 padding[7];\n 297:\t};\n"
} |
| 17/3 | 2026/08/14 02:38 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 36518 output: 22 thoughts: 14 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Checking `GVE_VERSION_STR_LEN`** Okay, so I'm jumping in here to take a look at the code and the first thing I need to verify is the definition of `GVE_VERSION_STR_LEN`. This is crucial. I need to make sure I understand the bounds it's setting for the version string, and that it's consistent across the different modules I'm familiar with that use it. A common gotcha is an improperly sized buffer leading to overflows, so my immediate thought is to confirm the size is adequate for the expected version string format. I'm going to quickly check if the constant accounts for a null terminator, because if it doesn't, that's an immediate red flag and something I'd need to flag immediately. If this is a defined constant, I'll trace its usage to make sure it is not overwritten elsewhere. Depending on what I find, this may be something that requires a small refactor to make it safer, or at the very least, a thorough review. I need to confirm assumptions about the length and implications of `GVE_VERSION_STR_LEN`, and where it is defined, before I can confidently move forward. |
| 18/3 | 2026/08/14 02:38 | tool | grepper |
0mArgs:{
"Expression": "GVE_VERSION_STR_LEN"
}Results: {
"Output": "drivers/net/ethernet/google/gve/gve_adminq.h=220=enum gve_sup_feature_mask {\n--\ndrivers/net/ethernet/google/gve/gve_adminq.h-230-\ndrivers/net/ethernet/google/gve/gve_adminq.h:231:#define GVE_VERSION_STR_LEN 128\ndrivers/net/ethernet/google/gve/gve_adminq.h-232-\n--\ndrivers/net/ethernet/google/gve/gve_adminq.h=268=struct gve_driver_info {\n--\ndrivers/net/ethernet/google/gve/gve_adminq.h-276-\t__be64 driver_capability_flags[4];\ndrivers/net/ethernet/google/gve/gve_adminq.h:277:\tu8 os_version_str1[GVE_VERSION_STR_LEN];\ndrivers/net/ethernet/google/gve/gve_adminq.h:278:\tu8 os_version_str2[GVE_VERSION_STR_LEN];\ndrivers/net/ethernet/google/gve/gve_adminq.h-279-};\n"
} |
| 19/3 | 2026/08/14 02:39 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 37150 output: 49 thoughts: 467 Thoughts: **Analyzing the `gve_driver_info` Structure and Memory Allocation** Okay, so I'm looking at this `gve_driver_info` struct definition. Let's break it down: `os_type`, `driver_major`, `driver_minor`, `driver_sub` are all single-byte fields. Then there's the OS version stuff: `os_version_major`, `os_version_minor`, `os_version_sub`, all 32-bit big-endian integers (`__be32`). After that, we have `driver_capability_flags` as an array of four 64-bit integers (`__be64`). And finally, two large strings, `os_version_str1` and `os_version_str2`, each 128 bytes long. Let me calculate the size. 1+1+1+1+4+4+4 + (4*8) + 128 + 128 = 304 bytes. And the offsets: - `os_type` starts at 0. - `driver_major` starts at 1. - `driver_minor` starts at 2. - `driver_sub` starts at 3. - `os_version_major` starts at 4. - `os_version_minor` starts at 8. - `os_version_sub` starts at 12. - `driver_capability_flags` starts at 16 (32 bytes total). - `os_version_str1` starts at 48. - `os_version_str2` starts at 176. So it's indeed 304 bytes, with no padding which is expected. Now, a related question: Does `dma_alloc_coherent` zero the memory it allocates? I'm pretty sure it does *not* inherently guarantee zeroed memory *unless* you pass `__GFP_ZERO`. But wait, I recall that Linux may have a default behavior for `dma_alloc_coherent` where the allocation is zeroed. *Let me double-check that*. It appears `dma_alloc_coherent` **does** return zeroed memory, it zeroes the memory by default! Confirmed: `dma_alloc_coherent` *is* defined to return zeroed memory. I'll keep this behavior in mind for the future. I want to verify this memory allocation, to confirm it. |
| 20/3 | 2026/08/14 02:39 | tool | grepper |
0mArgs:{
"Expression": "dma_alloc_coherent.*GFP_KERNEL",
"PathPrefix": "drivers/net/ethernet/google/gve/gve_adminq.c"
}Results: {
"Output": "drivers/net/ethernet/google/gve/gve_adminq.c=1200=int gve_adminq_register_page_list(struct gve_priv *priv,\n--\ndrivers/net/ethernet/google/gve/gve_adminq.c-1212-\tmemset(\u0026cmd, 0, sizeof(cmd));\ndrivers/net/ethernet/google/gve/gve_adminq.c:1213:\tpage_list = dma_alloc_coherent(hdev, size, \u0026page_list_bus, GFP_KERNEL);\ndrivers/net/ethernet/google/gve/gve_adminq.c-1214-\tif (!page_list)\n"
} |
| 21/3 | 2026/08/14 02:39 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 38397 output: 45 thoughts: 4841 Thoughts: **Analysis of DMA Allocation and Initialization in GVE Driver**
Okay, so I've been digging into this GVE driver code, specifically focusing on how memory is allocated and initialized, and whether there are any potential vulnerabilities related to uninitialized data being sent to the device or user space.
The primary concern, as I see it, revolves around the `dma_alloc_coherent` call and compound literals. The patch introduces this block where a `struct gve_driver_info` is allocated using `dma_alloc_coherent`:
```c
driver_info = dma_alloc_coherent(&priv->pdev->dev,
sizeof(struct gve_driver_info),
&driver_info_bus, GFP_KERNEL);
```
My initial thought was, if `dma_alloc_coherent` zeroes the memory (which it does), then the subsequent compound literal assignment `*driver_info = (struct gve_driver_info) { ... };` should overwrite the memory safely.
I immediately checked whether that compound literal might leave some uninitialized members, because the C standard says that any explicitly uninitialized members are zeroed, as though static storage duration. This is important to ensure data consistency and prevent potential information leaks.
I then did a deep dive into the structure's layout, confirming that it's naturally aligned. I carefully calculated the size of the `struct gve_driver_info` which contains several fundamental data types and two large character arrays to see if any padding bytes would exist. I came to the conclusion, through direct calculation, that there is no padding at all. This means every byte of the allocated memory will be overwritten by the compound literal or zeroed by the `dma_alloc_coherent` call.
After confirming the absence of padding, I thought of another thing - that while the memory is allocated and initialized by `dma_alloc_coherent`, then a compound literal on the stack could still have uninitialized padding bytes, if there were any, potentially leading to a problem. But, since there is no padding, this is not an issue.
The rest of the code is also safe, because the allocated structures from the driver, such as `descriptor`, which comes from a call to the device itself. Some internal structs such as `priv->ntfy_blocks`, `priv->xsk_pools` and `priv->irq_db_indices`, are allocated and zeroed out with functions like `kvzalloc` and `bitmap_zalloc`. The code generally follows good practices, reading data from the device when necessary.
Overall, the patch, and the relevant code, does not appear to have any obvious vulnerabilities related to uninitialized memory. The use of `dma_alloc_coherent`, compound literals, and zeroed allocations, coupled with the absence of padding, should make this code safe from memory leaks to either the device or user space. The refactoring doesn't change the underlying initialization logic. It primarily reorganizes the initialization and teardown sequences, which seem correct.
|
| 22/3 | 2026/08/14 02:39 | tool | grepper |
0mArgs:{
"Expression": "alloc_etherdev_mqs",
"PathPrefix": "drivers/net/ethernet/google/gve/gve_main.c"
}Results: {
"Output": "drivers/net/ethernet/google/gve/gve_main.c=2819=static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent)\n--\ndrivers/net/ethernet/google/gve/gve_main.c-2853-\t/* Alloc and setup the netdev and priv */\ndrivers/net/ethernet/google/gve/gve_main.c:2854:\tdev = alloc_etherdev_mqs(sizeof(*priv), max_tx_queues, max_rx_queues);\ndrivers/net/ethernet/google/gve/gve_main.c-2855-\tif (!dev) {\n"
} |
| 23/3 | 2026/08/14 02:39 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 44107 output: 48 thoughts: 1913 Thoughts: **Uninitialized Memory Review: A Thorough Examination** Okay, so I've been tracing the memory initialization within this driver, and I need to be absolutely certain we're not leaking any uninitialized data to the device or user space. Let's start with the confirmed areas. `alloc_etherdev_mqs` zeroes the memory; so, `device_info` is initialized from the get-go. Now, let's dissect `gve_adminq_verify_driver_compatibility`. I see `dma_alloc_coherent` is employed here, which, as analyzed previously, ensures the memory is zeroed. The compound literal assignment that follows also zeroes any uninitialized fields. Given that there's no padding within `struct gve_driver_info`, I'm confident there's no uninitialized data being sent. Next, I need to methodically check for any other structures being transmitted to either user space or the device. In `gve_ethtool.c`, I examined `gve_get_link_ksettings`. It's reading `priv->link_speed` and setting `cmd->base.speed`, so I'm not worried about uninitialized data here. Moving to `gve_flow_rule.c`, I peeked at `gve_add_flow_rule`. The function deals with flow rules and error handling and doesn't reveal any transfer of uninitialized memory. Now, let's explore `gve_adminq.c`. I looked at `gve_adminq_init`, which includes writing a version string to a register. This also doesn't present any issues. I verified this via `gve_adminq_write_version`, which, essentially, involves writing character-by-character, meaning there's no risk of sending anything uninitialized. Next, I inspected `gve_adminq_describe_device`, which uses `dma_pool_alloc` to allocate `descriptor`. This data is intended to receive data from the device, which makes me suspect that this might be an area for further investigation. I then followed this by inspecting `gve_adminq_execute_cmd`. This writes a `cmd` to the admin queue, and the important thing here is that `cmd` is a `union gve_adminq_command`. I see that `memset(&cmd, 0, sizeof(cmd))` is called before the write, which zeros `cmd`. This indicates the contents will be zeroed. Finally, I checked `gve_adminq_report_stats`. A similar pattern emerges here. `cmd` is zeroed initially, and `cmd.report_stats` is subsequently assigned. This is where I initially had a slight pause. My concern was the potential for the compound literal assignment to leave uninitialized memory in the union, potentially leaking stale data. However, after careful consideration, I'm confident that this is not the case. The `memset` zeroes the entire union, and the compound literal then populates the relevant fields within `report_stats`. Given that there's no padding in `struct gve_adminq_report_stats`, the assignment is safe. The `cmd.opcode` is also safe because it is written to the union before the `report_stats`. |
| 24/3 | 2026/08/14 02:40 | tool | grepper |
0mArgs:{
"Expression": "union gve_adminq_command \\{",
"PathPrefix": "drivers/net/ethernet/google/gve/gve_adminq.h"
}Results: {
"Output": "drivers/net/ethernet/google/gve/gve_adminq.h=588=static_assert(sizeof(struct gve_adminq_query_rss) == 16);\ndrivers/net/ethernet/google/gve/gve_adminq.h-589-\ndrivers/net/ethernet/google/gve/gve_adminq.h:590:union gve_adminq_command {\ndrivers/net/ethernet/google/gve/gve_adminq.h-591-\tstruct {\n"
} |
| 25/3 | 2026/08/14 02:40 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 46914 output: 47 thoughts: 14 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing `union gve_adminq_command`** Alright, let's dive into this `union gve_adminq_command`. My immediate thought is, given the naming convention, this is likely related to the Google Virtual Ethernet (GVE) interface's administrative queue. It's a `union`, which tells me we're dealing with a space-efficient way to represent a set of possibly mutually exclusive commands or data structures. I need to understand the different command types this union encapsulates. What are the various fields defined within it? Each member likely represents a different administrative operation or request being submitted to the GVE interface. I need to determine the specific command IDs or flags that will be used to differentiate between the structures in the `union`. I expect these command types will involve setup, teardown, resource allocation, status querying, or perhaps even error handling. I'll need to figure out the relationship to the underlying GVE hardware and the driver's interaction with that hardware, and also to see whether some commands are specific to the host or the virtual machine. I'm going to look for any common patterns in how they are implemented, if it has any overlapping fields, how the union handles alignment and potential size considerations, to figure out where any performance impacts might be. Ultimately, the goal is to fully understand how these administrative commands are defined and managed within the GVE ecosystem. |
| 26/3 | 2026/08/14 02:40 | tool | read-file |
0mArgs:{
"File": "drivers/net/ethernet/google/gve/gve_adminq.h",
"FirstLine": 590,
"LineCount": 30
}Results: {
"Contents": " 590:\tunion gve_adminq_command {\n 591:\t\tstruct {\n 592:\t\t\t__be32 opcode;\n 593:\t\t\t__be32 status;\n 594:\t\t\tunion {\n 595:\t\t\t\tstruct gve_adminq_configure_device_resources\n 596:\t\t\t\t\t\t\tconfigure_device_resources;\n 597:\t\t\t\tstruct gve_adminq_create_tx_queue create_tx_queue;\n 598:\t\t\t\tstruct gve_adminq_create_rx_queue create_rx_queue;\n 599:\t\t\t\tstruct gve_adminq_destroy_tx_queue destroy_tx_queue;\n 600:\t\t\t\tstruct gve_adminq_destroy_rx_queue destroy_rx_queue;\n 601:\t\t\t\tstruct gve_adminq_describe_device describe_device;\n 602:\t\t\t\tstruct gve_adminq_register_page_list reg_page_list;\n 603:\t\t\t\tstruct gve_adminq_unregister_page_list unreg_page_list;\n 604:\t\t\t\tstruct gve_adminq_set_driver_parameter set_driver_param;\n 605:\t\t\t\tstruct gve_adminq_report_stats report_stats;\n 606:\t\t\t\tstruct gve_adminq_report_link_speed report_link_speed;\n 607:\t\t\t\tstruct gve_adminq_get_ptype_map get_ptype_map;\n 608:\t\t\t\tstruct gve_adminq_verify_driver_compatibility\n 609:\t\t\t\t\t\t\tverify_driver_compatibility;\n 610:\t\t\t\tstruct gve_adminq_query_flow_rules query_flow_rules;\n 611:\t\t\t\tstruct gve_adminq_configure_rss configure_rss;\n 612:\t\t\t\tstruct gve_adminq_query_rss query_rss;\n 613:\t\t\t\tstruct gve_adminq_report_nic_ts report_nic_ts;\n 614:\t\t\t\tstruct gve_adminq_extended_command extended_command;\n 615:\t\t\t};\n 616:\t\t};\n 617:\t\tu8 reserved[64];\n 618:\t};\n 619:\t\n"
} |
| 27/3 | 2026/08/14 02:40 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 47847 output: 282 thoughts: 3738 Thoughts: **Analysis of the GVE Driver Patch for Uninitialized Memory Risks** Okay, let's break this down. My initial concern was, as always, about uninitialized memory being exposed either through the driver to userspace, or to the device. The review focuses on potential vulnerabilities that KMSAN (Kernel Memory Sanitizer) might detect. I went through the relevant functions systematically, starting with the initial command setup. The anonymous structures and unions are fine, they're handled correctly with `memset` or are allocated via DMA. I traced through several functions, like `gve_adminq_report_stats`, `gve_adminq_verify_driver_compatibility`, `gve_adminq_report_link_speed`, `gve_adminq_report_nic_ts`, `gve_adminq_get_ptype_map_dqo`, `gve_adminq_set_num_ntfy_blks`, etc. Each of these either zeros the command struct or ensures that memory is allocated and initialized before being sent to the device. The same approach to `gve_adminq_map_db_bar`, `gve_adminq_request_db_info`, and `gve_adminq_setup_mgmt_irq` all seems to be following well-defined allocation and initialization patterns. This refactoring is primarily about making the initialization and teardown phases easier to understand and manage. The code moves device-specific information into the `gve_device_info` structure and control operations into `gve_ctrl_ops`. There is no change to any structures that are sent to user space (no `copy_to_user`, no netlink attribute or ioctl output changes). Everything sent to the device is either handled with `memset` to ensure zero-initialization or is allocated through `dma_alloc_coherent` where memory is zeroed out by default, and then is fully initialized. No risk here. This patch doesn't introduce any new structures sent to user space or the device that could contain uninitialized memory, nor are there any conditional logic paths or checks based on potentially uninitialized variables. Therefore, given the nature of the changes – essentially a refactoring of existing, already working code with good initialization patterns – and the absence of any new data paths to user space or the device that could introduce an issue, I'm confident in assessing this patch does not expose any new uninitialized memory risks that KMSAN *specifically* would catch. Standard KASAN, LOCKDEP and other standard kernel bug detectors should be able to catch any potential issues. `NeedsKMSAN=false` is appropriate, the reasoning is solid. |
| 28/3 | 2026/08/14 02:40 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series refactors the initialization and teardown sequences of the `gve` driver, introducing `struct gve_device_info` to hold device properties and `struct gve_ctrl_ops` for control plane operations.\n\n1. No uninitialized memory is sent to user space or the device. All structures sent to the device (e.g., `struct gve_driver_info`, `union gve_adminq_command`) are either explicitly zeroed using `memset` or allocated using `dma_alloc_coherent` (which guarantees zeroed memory) before being populated.\n2. The patch does not introduce any new `copy_to_user`, `put_user`, netlink attributes, or ioctl output arguments.\n3. The refactored code does not introduce conditional logic or branching that depends on potentially uninitialized variables.\n4. The changes primarily involve reorganizing existing code and adding error handling, which do not expose uninitialized memory risks.\n\nAny potential bugs introduced by these changes (such as out-of-bounds accesses, use-after-free, or locking issues) would be caught by standard KASAN and LOCKDEP. A dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series refactors the initialization and teardown sequences of the `gve` driver, introducing `struct gve_device_info` to hold device properties and `struct gve_ctrl_ops` for control plane operations.\n\n1. No uninitialized memory is sent to user space or the device. All structures sent to the device (e.g., `struct gve_driver_info`, `union gve_adminq_command`) are either explicitly zeroed using `memset` or allocated using `dma_alloc_coherent` (which guarantees zeroed memory) before being populated.\n2. The patch does not introduce any new `copy_to_user`, `put_user`, netlink attributes, or ioctl output arguments.\n3. The refactored code does not introduce conditional logic or branching that depends on potentially uninitialized variables.\n4. The changes primarily involve reorganizing existing code and adding error handling, which do not expose uninitialized memory risks.\n\nAny potential bugs introduced by these changes (such as out-of-bounds accesses, use-after-free, or locking issues) would be caught by standard KASAN and LOCKDEP. A dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|