+ rte_wmb();
+ /* Disable datapath on secondary process. */
+ hns3_mp_req_stop_rxtx(eth_dev);
+ rte_delay_ms(hw->tqps_num);
+
+ rte_spinlock_lock(&hw->lock);
+ if (hw->adapter_state == HNS3_NIC_STARTED ||
+ hw->adapter_state == HNS3_NIC_STOPPING) {
+ hns3vf_do_stop(hns);
+ hw->reset.mbuf_deferred_free = true;
+ } else
+ hw->reset.mbuf_deferred_free = false;
+
+ /*
+ * It is cumbersome for hardware to pick-and-choose entries for deletion
+ * from table space. Hence, for function reset software intervention is
+ * required to delete the entries.
+ */
+ if (rte_atomic16_read(&hw->reset.disable_cmd) == 0)
+ hns3vf_configure_all_mc_mac_addr(hns, true);
+ rte_spinlock_unlock(&hw->lock);
+
+ return 0;
+}
+
+static int
+hns3vf_start_service(struct hns3_adapter *hns)
+{
+ struct hns3_hw *hw = &hns->hw;
+ struct rte_eth_dev *eth_dev;
+
+ eth_dev = &rte_eth_devices[hw->data->port_id];
+ hns3_set_rxtx_function(eth_dev);
+ hns3_mp_req_start_rxtx(eth_dev);
+ if (hw->adapter_state == HNS3_NIC_STARTED)
+ hns3vf_service_handler(eth_dev);
+
+ return 0;
+}
+
+static int
+hns3vf_check_default_mac_change(struct hns3_hw *hw)
+{
+ char mac_str[RTE_ETHER_ADDR_FMT_SIZE];
+ struct rte_ether_addr *hw_mac;
+ int ret;
+
+ /*
+ * The hns3 PF ethdev driver in kernel support setting VF MAC address
+ * on the host by "ip link set ..." command. If the hns3 PF kernel
+ * ethdev driver sets the MAC address for VF device after the
+ * initialization of the related VF device, the PF driver will notify
+ * VF driver to reset VF device to make the new MAC address effective
+ * immediately. The hns3 VF PMD driver should check whether the MAC
+ * address has been changed by the PF kernel ethdev driver, if changed
+ * VF driver should configure hardware using the new MAC address in the
+ * recovering hardware configuration stage of the reset process.
+ */
+ ret = hns3vf_get_host_mac_addr(hw);
+ if (ret)
+ return ret;
+
+ hw_mac = (struct rte_ether_addr *)hw->mac.mac_addr;
+ ret = rte_is_zero_ether_addr(hw_mac);
+ if (ret) {
+ rte_ether_addr_copy(&hw->data->mac_addrs[0], hw_mac);
+ } else {
+ ret = rte_is_same_ether_addr(&hw->data->mac_addrs[0], hw_mac);
+ if (!ret) {
+ rte_ether_addr_copy(hw_mac, &hw->data->mac_addrs[0]);
+ rte_ether_format_addr(mac_str, RTE_ETHER_ADDR_FMT_SIZE,
+ &hw->data->mac_addrs[0]);
+ hns3_warn(hw, "Default MAC address has been changed to:"
+ " %s by the host PF kernel ethdev driver",
+ mac_str);
+ }
+ }
+
+ return 0;
+}
+
+static int
+hns3vf_restore_conf(struct hns3_adapter *hns)
+{
+ struct hns3_hw *hw = &hns->hw;
+ int ret;
+
+ ret = hns3vf_check_default_mac_change(hw);
+ if (ret)
+ return ret;
+
+ ret = hns3vf_configure_mac_addr(hns, false);
+ if (ret)
+ return ret;
+
+ ret = hns3vf_configure_all_mc_mac_addr(hns, false);
+ if (ret)
+ goto err_mc_mac;
+
+ ret = hns3vf_restore_promisc(hns);
+ if (ret)
+ goto err_vlan_table;
+
+ ret = hns3vf_restore_vlan_conf(hns);
+ if (ret)
+ goto err_vlan_table;
+
+ if (hw->adapter_state == HNS3_NIC_STARTED) {
+ ret = hns3vf_do_start(hns, false);
+ if (ret)
+ goto err_vlan_table;
+ hns3_info(hw, "hns3vf dev restart successful!");
+ } else if (hw->adapter_state == HNS3_NIC_STOPPING)
+ hw->adapter_state = HNS3_NIC_CONFIGURED;
+ return 0;
+
+err_vlan_table:
+ hns3vf_configure_all_mc_mac_addr(hns, true);
+err_mc_mac:
+ hns3vf_configure_mac_addr(hns, true);
+ return ret;
+}
+
+static enum hns3_reset_level
+hns3vf_get_reset_level(struct hns3_hw *hw, uint64_t *levels)
+{
+ enum hns3_reset_level reset_level;
+
+ /* return the highest priority reset level amongst all */
+ if (hns3_atomic_test_bit(HNS3_VF_RESET, levels))
+ reset_level = HNS3_VF_RESET;
+ else if (hns3_atomic_test_bit(HNS3_VF_FULL_RESET, levels))
+ reset_level = HNS3_VF_FULL_RESET;
+ else if (hns3_atomic_test_bit(HNS3_VF_PF_FUNC_RESET, levels))
+ reset_level = HNS3_VF_PF_FUNC_RESET;
+ else if (hns3_atomic_test_bit(HNS3_VF_FUNC_RESET, levels))
+ reset_level = HNS3_VF_FUNC_RESET;
+ else if (hns3_atomic_test_bit(HNS3_FLR_RESET, levels))
+ reset_level = HNS3_FLR_RESET;
+ else
+ reset_level = HNS3_NONE_RESET;
+
+ if (hw->reset.level != HNS3_NONE_RESET && reset_level < hw->reset.level)
+ return HNS3_NONE_RESET;
+
+ return reset_level;
+}
+
+static void
+hns3vf_reset_service(void *param)
+{
+ struct hns3_adapter *hns = (struct hns3_adapter *)param;
+ struct hns3_hw *hw = &hns->hw;
+ enum hns3_reset_level reset_level;
+ struct timeval tv_delta;
+ struct timeval tv_start;
+ struct timeval tv;
+ uint64_t msec;
+
+ /*
+ * The interrupt is not triggered within the delay time.
+ * The interrupt may have been lost. It is necessary to handle
+ * the interrupt to recover from the error.
+ */
+ if (rte_atomic16_read(&hns->hw.reset.schedule) == SCHEDULE_DEFERRED) {
+ rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_REQUESTED);
+ hns3_err(hw, "Handling interrupts in delayed tasks");
+ hns3vf_interrupt_handler(&rte_eth_devices[hw->data->port_id]);
+ reset_level = hns3vf_get_reset_level(hw, &hw->reset.pending);
+ if (reset_level == HNS3_NONE_RESET) {
+ hns3_err(hw, "No reset level is set, try global reset");
+ hns3_atomic_set_bit(HNS3_VF_RESET, &hw->reset.pending);
+ }
+ }
+ rte_atomic16_set(&hns->hw.reset.schedule, SCHEDULE_NONE);
+
+ /*
+ * Hardware reset has been notified, we now have to poll & check if
+ * hardware has actually completed the reset sequence.
+ */
+ reset_level = hns3vf_get_reset_level(hw, &hw->reset.pending);
+ if (reset_level != HNS3_NONE_RESET) {
+ gettimeofday(&tv_start, NULL);
+ hns3_reset_process(hns, reset_level);
+ gettimeofday(&tv, NULL);
+ timersub(&tv, &tv_start, &tv_delta);
+ msec = tv_delta.tv_sec * MSEC_PER_SEC +
+ tv_delta.tv_usec / USEC_PER_MSEC;
+ if (msec > HNS3_RESET_PROCESS_MS)
+ hns3_err(hw, "%d handle long time delta %" PRIx64
+ " ms time=%ld.%.6ld",
+ hw->reset.level, msec, tv.tv_sec, tv.tv_usec);
+ }
+}
+
+static int
+hns3vf_reinit_dev(struct hns3_adapter *hns)
+{
+ struct rte_eth_dev *eth_dev = &rte_eth_devices[hns->hw.data->port_id];
+ struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev);
+ struct hns3_hw *hw = &hns->hw;
+ int ret;
+
+ if (hw->reset.level == HNS3_VF_FULL_RESET) {
+ rte_intr_disable(&pci_dev->intr_handle);
+ hns3vf_set_bus_master(pci_dev, true);
+ }
+
+ /* Firmware command initialize */
+ ret = hns3_cmd_init(hw);
+ if (ret) {
+ hns3_err(hw, "Failed to init cmd: %d", ret);
+ return ret;
+ }
+
+ if (hw->reset.level == HNS3_VF_FULL_RESET) {
+ /*
+ * UIO enables msix by writing the pcie configuration space
+ * vfio_pci enables msix in rte_intr_enable.
+ */
+ if (pci_dev->kdrv == RTE_KDRV_IGB_UIO ||
+ pci_dev->kdrv == RTE_KDRV_UIO_GENERIC) {
+ if (hns3vf_enable_msix(pci_dev, true))
+ hns3_err(hw, "Failed to enable msix");
+ }
+
+ rte_intr_enable(&pci_dev->intr_handle);
+ }
+
+ ret = hns3_reset_all_queues(hns);
+ if (ret) {
+ hns3_err(hw, "Failed to reset all queues: %d", ret);
+ return ret;
+ }
+
+ ret = hns3vf_init_hardware(hns);
+ if (ret) {
+ hns3_err(hw, "Failed to init hardware: %d", ret);
+ return ret;
+ }
+