Split ENA reset routine into restore and destroy stages

For alignment with Linux driver and better handling ena_detach(), the
reset is now calling ena_device_restore() and ena_device_destroy().

The ena_device_destroy() is also being called on ena_detach(), so the
code will be more readable.

The watchdog is now being activated after reset only, if it was active
before.

There were added additional checks to ensure, that there is no race with
the link state change AENQ handler.

Submitted by:  Michal Krawczyk <mk@semihalf.com>
Obtained from: Semihalf
Sponsored by:  Amazon, Inc.
This commit is contained in:
Marcin Wojtas 2019-05-30 13:39:25 +00:00
parent fd43fd2af0
commit 32f63fa7f9
2 changed files with 164 additions and 96 deletions

View File

@ -2286,11 +2286,6 @@ ena_up(struct ena_adapter *adapter)
return (ENXIO);
}
if (unlikely(!ENA_FLAG_ISSET(ENA_FLAG_DEVICE_RUNNING, adapter))) {
device_printf(adapter->pdev, "device is not running!\n");
return (ENXIO);
}
if (!ENA_FLAG_ISSET(ENA_FLAG_DEV_UP, adapter)) {
device_printf(adapter->pdev, "device is going UP\n");
@ -4066,14 +4061,163 @@ ena_timer_service(void *data)
}
static void
ena_reset_task(void *arg, int pending)
ena_destroy_device(struct ena_adapter *adapter, bool graceful)
{
struct ena_com_dev_get_features_ctx get_feat_ctx;
struct ena_adapter *adapter = (struct ena_adapter *)arg;
if_t ifp = adapter->ifp;
struct ena_com_dev *ena_dev = adapter->ena_dev;
bool dev_up;
if (!ENA_FLAG_ISSET(ENA_FLAG_DEVICE_RUNNING, adapter))
return;
if_link_state_change(ifp, LINK_STATE_DOWN);
callout_drain(&adapter->timer_service);
dev_up = ENA_FLAG_ISSET(ENA_FLAG_DEV_UP, adapter);
if (dev_up)
ENA_FLAG_SET_ATOMIC(ENA_FLAG_DEV_UP_BEFORE_RESET, adapter);
else
ENA_FLAG_CLEAR_ATOMIC(ENA_FLAG_DEV_UP_BEFORE_RESET, adapter);
if (!graceful)
ena_com_set_admin_running_state(ena_dev, false);
if (ENA_FLAG_ISSET(ENA_FLAG_DEV_UP, adapter))
ena_down(adapter);
/*
* Stop the device from sending AENQ events (if the device was up, and
* the trigger reset was on, ena_down already performs device reset)
*/
if (!(ENA_FLAG_ISSET(ENA_FLAG_TRIGGER_RESET, adapter) && dev_up))
ena_com_dev_reset(adapter->ena_dev, adapter->reset_reason);
ena_free_mgmnt_irq(adapter);
ena_disable_msix(adapter);
ena_com_abort_admin_commands(ena_dev);
ena_com_wait_for_abort_completion(ena_dev);
ena_com_admin_destroy(ena_dev);
ena_com_mmio_reg_read_request_destroy(ena_dev);
adapter->reset_reason = ENA_REGS_RESET_NORMAL;
ENA_FLAG_CLEAR_ATOMIC(ENA_FLAG_TRIGGER_RESET, adapter);
ENA_FLAG_CLEAR_ATOMIC(ENA_FLAG_DEVICE_RUNNING, adapter);
}
static int
ena_device_validate_params(struct ena_adapter *adapter,
struct ena_com_dev_get_features_ctx *get_feat_ctx)
{
if (memcmp(get_feat_ctx->dev_attr.mac_addr, adapter->mac_addr,
ETHER_ADDR_LEN) != 0) {
device_printf(adapter->pdev,
"Error, mac address are different\n");
return (EINVAL);
}
if (get_feat_ctx->dev_attr.max_mtu < if_getmtu(adapter->ifp)) {
device_printf(adapter->pdev,
"Error, device max mtu is smaller than ifp MTU\n");
return (EINVAL);
}
return 0;
}
static int
ena_restore_device(struct ena_adapter *adapter)
{
struct ena_com_dev_get_features_ctx get_feat_ctx;
struct ena_com_dev *ena_dev = adapter->ena_dev;
if_t ifp = adapter->ifp;
device_t dev = adapter->pdev;
int wd_active;
int rc;
ENA_FLAG_SET_ATOMIC(ENA_FLAG_ONGOING_RESET, adapter);
rc = ena_device_init(adapter, dev, &get_feat_ctx, &wd_active);
if (rc != 0) {
device_printf(dev, "Cannot initialize device\n");
goto err;
}
/*
* Only enable WD if it was enabled before reset, so it won't override
* value set by the user by the sysctl.
*/
if (adapter->wd_active != 0)
adapter->wd_active = wd_active;
rc = ena_device_validate_params(adapter, &get_feat_ctx);
if (rc != 0) {
device_printf(dev, "Validation of device parameters failed\n");
goto err_device_destroy;
}
rc = ena_handle_updated_queues(adapter, &get_feat_ctx);
if (rc != 0)
goto err_device_destroy;
ENA_FLAG_CLEAR_ATOMIC(ENA_FLAG_ONGOING_RESET, adapter);
/* Make sure we don't have a race with AENQ Links state handler */
if (ENA_FLAG_ISSET(ENA_FLAG_LINK_UP, adapter))
if_link_state_change(ifp, LINK_STATE_UP);
rc = ena_enable_msix_and_set_admin_interrupts(adapter,
adapter->num_queues);
if (rc != 0) {
device_printf(dev, "Enable MSI-X failed\n");
goto err_device_destroy;
}
/* If the interface was up before the reset bring it up */
if (ENA_FLAG_ISSET(ENA_FLAG_DEV_UP_BEFORE_RESET, adapter)) {
rc = ena_up(adapter);
if (rc != 0) {
device_printf(dev, "Failed to create I/O queues\n");
goto err_disable_msix;
}
}
ENA_FLAG_SET_ATOMIC(ENA_FLAG_DEVICE_RUNNING, adapter);
callout_reset_sbt(&adapter->timer_service, SBT_1S, SBT_1S,
ena_timer_service, (void *)adapter, 0);
device_printf(dev,
"Device reset completed successfully, Driver info: %s\n", ena_version);
return (rc);
err_disable_msix:
ena_free_mgmnt_irq(adapter);
ena_disable_msix(adapter);
err_device_destroy:
ena_com_abort_admin_commands(ena_dev);
ena_com_wait_for_abort_completion(ena_dev);
ena_com_admin_destroy(ena_dev);
ena_com_dev_reset(ena_dev, ENA_REGS_RESET_DRIVER_INVALID_STATE);
ena_com_mmio_reg_read_request_destroy(ena_dev);
err:
ENA_FLAG_CLEAR_ATOMIC(ENA_FLAG_DEVICE_RUNNING, adapter);
ENA_FLAG_CLEAR_ATOMIC(ENA_FLAG_ONGOING_RESET, adapter);
device_printf(dev, "Reset attempt failed. Can not reset the device\n");
return (rc);
}
static void
ena_reset_task(void *arg, int pending)
{
struct ena_adapter *adapter = (struct ena_adapter *)arg;
if (unlikely(!ENA_FLAG_ISSET(ENA_FLAG_TRIGGER_RESET, adapter))) {
device_printf(adapter->pdev,
"device reset scheduled but trigger_reset is off\n");
@ -4081,72 +4225,8 @@ ena_reset_task(void *arg, int pending)
}
sx_xlock(&adapter->ioctl_sx);
callout_drain(&adapter->timer_service);
dev_up = ENA_FLAG_ISSET(ENA_FLAG_DEV_UP, adapter);
ena_com_set_admin_running_state(ena_dev, false);
ena_down(adapter);
ena_free_mgmnt_irq(adapter);
ena_disable_msix(adapter);
ena_com_abort_admin_commands(ena_dev);
ena_com_wait_for_abort_completion(ena_dev);
ena_com_admin_destroy(ena_dev);
ena_com_mmio_reg_read_request_destroy(ena_dev);
adapter->reset_reason = ENA_REGS_RESET_NORMAL;
ENA_FLAG_CLEAR_ATOMIC(ENA_FLAG_TRIGGER_RESET, adapter);
/* Finished destroy part. Restart the device */
rc = ena_device_init(adapter, adapter->pdev, &get_feat_ctx,
&adapter->wd_active);
if (unlikely(rc != 0)) {
device_printf(adapter->pdev,
"ENA device init failed! (err: %d)\n", rc);
goto err_dev_free;
}
rc = ena_handle_updated_queues(adapter, &get_feat_ctx);
if (unlikely(rc != 0))
goto err_dev_free;
rc = ena_enable_msix_and_set_admin_interrupts(adapter,
adapter->num_queues);
if (unlikely(rc != 0)) {
device_printf(adapter->pdev, "Enable MSI-X failed\n");
goto err_com_free;
}
/* If the interface was up before the reset bring it up */
if (dev_up) {
rc = ena_up(adapter);
if (unlikely(rc != 0)) {
device_printf(adapter->pdev,
"Failed to create I/O queues\n");
goto err_msix_free;
}
}
callout_reset_sbt(&adapter->timer_service, SBT_1S, SBT_1S,
ena_timer_service, (void *)adapter, 0);
sx_unlock(&adapter->ioctl_sx);
return;
err_msix_free:
ena_free_mgmnt_irq(adapter);
ena_disable_msix(adapter);
err_com_free:
ena_com_abort_admin_commands(ena_dev);
ena_com_wait_for_abort_completion(ena_dev);
ena_com_admin_destroy(ena_dev);
ena_com_mmio_reg_read_request_destroy(ena_dev);
ena_com_dev_reset(ena_dev, ENA_REGS_RESET_DRIVER_INVALID_STATE);
err_dev_free:
device_printf(adapter->pdev, "ENA reset failed!\n");
ENA_FLAG_CLEAR_ATOMIC(ENA_FLAG_DEVICE_RUNNING, adapter);
ena_destroy_device(adapter, false);
ena_restore_device(adapter);
sx_unlock(&adapter->ioctl_sx);
}
@ -4403,6 +4483,7 @@ ena_detach(device_t pdev)
sx_xlock(&adapter->ioctl_sx);
ena_down(adapter);
ena_destroy_device(adapter, true);
sx_unlock(&adapter->ioctl_sx);
ena_free_all_io_rings_resources(adapter);
@ -4412,9 +4493,6 @@ ena_detach(device_t pdev)
ena_free_counters((counter_u64_t *)&adapter->dev_stats,
sizeof(struct ena_stats_dev));
if (likely(ENA_FLAG_ISSET(ENA_FLAG_RSS_ACTIVE, adapter)))
ena_com_rss_destroy(ena_dev);
rc = ena_free_rx_dma_tag(adapter);
if (unlikely(rc != 0))
device_printf(adapter->pdev,
@ -4425,24 +4503,15 @@ ena_detach(device_t pdev)
device_printf(adapter->pdev,
"Unmapped TX DMA tag associations\n");
/* Reset the device only if the device is running. */
if (ENA_FLAG_ISSET(ENA_FLAG_DEVICE_RUNNING, adapter))
ena_com_dev_reset(ena_dev, adapter->reset_reason);
ena_com_delete_host_info(ena_dev);
ena_free_irqs(adapter);
ena_com_abort_admin_commands(ena_dev);
ena_com_wait_for_abort_completion(ena_dev);
ena_com_admin_destroy(ena_dev);
ena_com_mmio_reg_read_request_destroy(ena_dev);
ena_free_pci_resources(adapter);
if (likely(ENA_FLAG_ISSET(ENA_FLAG_RSS_ACTIVE, adapter)))
ena_com_rss_destroy(ena_dev);
ena_com_delete_host_info(ena_dev);
mtx_destroy(&adapter->global_mtx);
sx_destroy(&adapter->ioctl_sx);
@ -4480,15 +4549,13 @@ ena_update_on_link_change(void *adapter_data,
if (status != 0) {
device_printf(adapter->pdev, "link is UP\n");
if_link_state_change(ifp, LINK_STATE_UP);
ENA_FLAG_SET_ATOMIC(ENA_FLAG_LINK_UP, adapter);
} else if (status == 0) {
if (!ENA_FLAG_ISSET(ENA_FLAG_ONGOING_RESET, adapter))
if_link_state_change(ifp, LINK_STATE_UP);
} else {
device_printf(adapter->pdev, "link is DOWN\n");
if_link_state_change(ifp, LINK_STATE_DOWN);
ENA_FLAG_CLEAR_ATOMIC(ENA_FLAG_LINK_UP, adapter);
} else {
device_printf(adapter->pdev, "invalid value recvd\n");
BUG();
}
}

View File

@ -164,6 +164,7 @@ enum ena_flags_t {
ENA_FLAG_MSIX_ENABLED,
ENA_FLAG_TRIGGER_RESET,
ENA_FLAG_ONGOING_RESET,
ENA_FLAG_DEV_UP_BEFORE_RESET,
ENA_FLAG_RSS_ACTIVE,
ENA_FLAGS_NUMBER = ENA_FLAG_RSS_ACTIVE
};