Register callbacks for RTE_ETH_EVENT_ERR_RECOVERING,
RTE_ETH_EVENT_RECOVERY_SUCCESS, and RTE_ETH_EVENT_RECOVERY_FAILED
events on the VF port to handle MANA service resets.

- On ERR_RECOVERING: switch data path to synthetic but keep the
  VF device attached in DPDK
- On RECOVERY_SUCCESS: switch data path back to VF
- On RECOVERY_FAILED: do full VF removal (same as INTR_RMV)
- Unregister all recovery callbacks during detach, removal, and
  close

This ensures that during a service reset (kernel suspend/resume
without PCI remove), netvsc keeps the VF attached and seamlessly
switches back to it after recovery, without requiring a PCI
hot-add event.

Signed-off-by: Long Li <[email protected]>
Signed-off-by: Wei Hu <[email protected]>
---
 drivers/net/netvsc/hn_vf.c | 106 +++++++++++++++++++++++++++++++++++++
 1 file changed, 106 insertions(+)

diff --git a/drivers/net/netvsc/hn_vf.c b/drivers/net/netvsc/hn_vf.c
index 1fcc65a712..366efb721f 100644
--- a/drivers/net/netvsc/hn_vf.c
+++ b/drivers/net/netvsc/hn_vf.c
@@ -50,6 +50,13 @@ static int hn_vf_match(const struct rte_eth_dev *dev)
 }
 
 
+static int hn_eth_recovering_callback(uint16_t port_id,
+       enum rte_eth_event_type event, void *cb_arg, void *out);
+static int hn_eth_recovery_success_callback(uint16_t port_id,
+       enum rte_eth_event_type event, void *cb_arg, void *out);
+static int hn_eth_recovery_failed_callback(uint16_t port_id,
+       enum rte_eth_event_type event, void *cb_arg, void *out);
+
 /*
  * Attach new PCI VF device and return the port_id
  */
@@ -111,6 +118,17 @@ static int hn_vf_attach(struct rte_eth_dev *dev, struct 
hn_data *hv)
                return ret;
        }
 
+       /* Register recovery event callbacks for service reset handling */
+       rte_eth_dev_callback_register(hv->vf_ctx.vf_port,
+                                     RTE_ETH_EVENT_ERR_RECOVERING,
+                                     hn_eth_recovering_callback, hv);
+       rte_eth_dev_callback_register(hv->vf_ctx.vf_port,
+                                     RTE_ETH_EVENT_RECOVERY_SUCCESS,
+                                     hn_eth_recovery_success_callback, hv);
+       rte_eth_dev_callback_register(hv->vf_ctx.vf_port,
+                                     RTE_ETH_EVENT_RECOVERY_FAILED,
+                                     hn_eth_recovery_failed_callback, hv);
+
        return 0;
 }
 
@@ -143,6 +161,12 @@ static void hn_remove_delayed(void *args)
                PMD_DRV_LOG(ERR,
                            "rte_eth_dev_callback_unregister failed ret=%d",
                            ret);
+       rte_eth_dev_callback_unregister(port_id, RTE_ETH_EVENT_ERR_RECOVERING,
+                                       hn_eth_recovering_callback, hv);
+       rte_eth_dev_callback_unregister(port_id, RTE_ETH_EVENT_RECOVERY_SUCCESS,
+                                       hn_eth_recovery_success_callback, hv);
+       rte_eth_dev_callback_unregister(port_id, RTE_ETH_EVENT_RECOVERY_FAILED,
+                                       hn_eth_recovery_failed_callback, hv);
 
        /* Detach and release port_id from system */
        ret = rte_eth_dev_stop(port_id);
@@ -187,6 +211,70 @@ int hn_eth_rmv_event_callback(uint16_t port_id,
        return 0;
 }
 
+/*
+ * Handle VF error recovery event from MANA PMD.
+ * Switch data path to synthetic but keep the VF attached.
+ */
+static int
+hn_eth_recovering_callback(uint16_t port_id,
+                          enum rte_eth_event_type event __rte_unused,
+                          void *cb_arg, void *out __rte_unused)
+{
+       struct hn_data *hv = cb_arg;
+
+       PMD_DRV_LOG(NOTICE, "VF port %u recovering from error", port_id);
+
+       rte_rwlock_write_lock(&hv->vf_lock);
+       hn_vf_remove_unlocked(hv);
+       rte_rwlock_write_unlock(&hv->vf_lock);
+
+       return 0;
+}
+
+/*
+ * Handle VF recovery success event from MANA PMD.
+ * Switch data path back to VF.
+ */
+static int
+hn_eth_recovery_success_callback(uint16_t port_id,
+                                enum rte_eth_event_type event __rte_unused,
+                                void *cb_arg, void *out __rte_unused)
+{
+       struct hn_data *hv = cb_arg;
+       int ret;
+
+       PMD_DRV_LOG(NOTICE, "VF port %u recovery succeeded", port_id);
+
+       rte_rwlock_write_lock(&hv->vf_lock);
+       if (hv->vf_ctx.vf_attached && !hv->vf_ctx.vf_vsc_switched) {
+               ret = hn_nvs_set_datapath(hv, NVS_DATAPATH_VF);
+               if (ret)
+                       PMD_DRV_LOG(ERR, "Failed to switch to VF after 
recovery");
+               else
+                       hv->vf_ctx.vf_vsc_switched = true;
+       }
+       rte_rwlock_write_unlock(&hv->vf_lock);
+
+       return 0;
+}
+
+/*
+ * Handle VF recovery failure event from MANA PMD.
+ * VF is unusable, do full removal.
+ */
+static int
+hn_eth_recovery_failed_callback(uint16_t port_id,
+                               enum rte_eth_event_type event __rte_unused,
+                               void *cb_arg, void *out __rte_unused)
+{
+       struct hn_data *hv = cb_arg;
+
+       PMD_DRV_LOG(NOTICE, "VF port %u recovery failed, removing", port_id);
+       rte_eal_alarm_set(1, hn_remove_delayed, hv);
+
+       return 0;
+}
+
 static int hn_setup_vf_queues(int port, struct rte_eth_dev *dev)
 {
        struct hn_rx_queue *rx_queue;
@@ -247,6 +335,12 @@ static void hn_vf_detach(struct hn_data *hv)
 
        rte_eth_dev_callback_unregister(port, RTE_ETH_EVENT_INTR_RMV,
                                        hn_eth_rmv_event_callback, hv);
+       rte_eth_dev_callback_unregister(port, RTE_ETH_EVENT_ERR_RECOVERING,
+                                       hn_eth_recovering_callback, hv);
+       rte_eth_dev_callback_unregister(port, RTE_ETH_EVENT_RECOVERY_SUCCESS,
+                                       hn_eth_recovery_success_callback, hv);
+       rte_eth_dev_callback_unregister(port, RTE_ETH_EVENT_RECOVERY_FAILED,
+                                       hn_eth_recovery_failed_callback, hv);
 
        if (rte_eth_dev_owner_unset(port, hv->owner.id) < 0)
                PMD_DRV_LOG(ERR, "Failed to unset owner for port %d", port);
@@ -630,6 +724,18 @@ int hn_vf_close(struct rte_eth_dev *dev)
                                                RTE_ETH_EVENT_INTR_RMV,
                                                hn_eth_rmv_event_callback,
                                                hv);
+               rte_eth_dev_callback_unregister(hv->vf_ctx.vf_port,
+                                               RTE_ETH_EVENT_ERR_RECOVERING,
+                                               hn_eth_recovering_callback,
+                                               hv);
+               rte_eth_dev_callback_unregister(hv->vf_ctx.vf_port,
+                                               RTE_ETH_EVENT_RECOVERY_SUCCESS,
+                                               
hn_eth_recovery_success_callback,
+                                               hv);
+               rte_eth_dev_callback_unregister(hv->vf_ctx.vf_port,
+                                               RTE_ETH_EVENT_RECOVERY_FAILED,
+                                               hn_eth_recovery_failed_callback,
+                                               hv);
                rte_eal_alarm_cancel(hn_remove_delayed, hv);
                ret = rte_eth_dev_close(hv->vf_ctx.vf_port);
                hv->vf_ctx.vf_attached = false;
-- 
2.34.1

Reply via email to