git: aea2edcc00a1 - stable/13 - ena: Move ena_copy_eni_metrics into separate task
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 26 Jul 2022 19:30:55 UTC
The branch stable/13 has been updated by mw:
URL: https://cgit.FreeBSD.org/src/commit/?id=aea2edcc00a1e94e99334f6fe7dec6d472ecb632
commit aea2edcc00a1e94e99334f6fe7dec6d472ecb632
Author: Dawid Gorecki <dgr@semihalf.com>
AuthorDate: 2022-06-10 09:18:10 +0000
Commit: Marcin Wojtas <mw@FreeBSD.org>
CommitDate: 2022-07-26 19:30:15 +0000
ena: Move ena_copy_eni_metrics into separate task
Copying ENI metrics was done in callout context, this caused the driver
to panic when sample_interval was set to a value other than 0, as the
admin queue call which was executed could sleep while waiting on
a condition variable. Taskqueue, unlike callout, allows for sleeping, so
moving the function to a separate taskqueue fixes the problem.
ena_timer_service is still responsible for scheduling the taskqueue.
Stop draining the callout during ena_up/ena_down. This was done to
prevent a race between ena_up/down and ena_copy_eni_metrics admin queue
calls. Since ena_metrics_task is protected by ENA_LOCK there is no
possibility of a race between ena_up/down and ena_metrics_task.
Remove a comment about locking in ena_timer_service. With ENI metrics
in a separate task this comment became obsolete.
Obtained from: Semihalf
MFC after: 2 weeks
Sponsored by: Amazon, Inc.
(cherry picked from commit b899a02ad7330cae3c9bb08ad7975601dc3b9551)
---
sys/dev/ena/ena.c | 57 ++++++++++++++++++++++---------------------------------
sys/dev/ena/ena.h | 2 ++
2 files changed, 25 insertions(+), 34 deletions(-)
diff --git a/sys/dev/ena/ena.c b/sys/dev/ena/ena.c
index 343f2f9626cd..6a3956ddd181 100644
--- a/sys/dev/ena/ena.c
+++ b/sys/dev/ena/ena.c
@@ -2104,13 +2104,6 @@ ena_up(struct ena_adapter *adapter)
ena_log(adapter->pdev, INFO, "device is going UP\n");
- /*
- * ena_timer_service can use functions, which write to the admin queue.
- * Those calls are not protected by ENA_LOCK, and because of that, the
- * timer should be stopped when bringing the device up or down.
- */
- ENA_TIMER_DRAIN(adapter);
-
/* setup interrupts for IO queues */
rc = ena_setup_io_intr(adapter);
if (unlikely(rc != 0)) {
@@ -2157,8 +2150,6 @@ ena_up(struct ena_adapter *adapter)
ena_unmask_all_io_irqs(adapter);
- ENA_TIMER_RESET(adapter);
-
return (0);
err_up_complete:
@@ -2168,8 +2159,6 @@ err_up_complete:
err_create_queues_with_backoff:
ena_free_io_irq(adapter);
error:
- ENA_TIMER_RESET(adapter);
-
return (rc);
}
@@ -2474,9 +2463,6 @@ ena_down(struct ena_adapter *adapter)
if (!ENA_FLAG_ISSET(ENA_FLAG_DEV_UP, adapter))
return;
- /* Drain timer service to avoid admin queue race condition. */
- ENA_TIMER_DRAIN(adapter);
-
ena_log(adapter->pdev, INFO, "device is going DOWN\n");
ENA_FLAG_CLEAR_ATOMIC(ENA_FLAG_DEV_UP, adapter);
@@ -2501,8 +2487,6 @@ ena_down(struct ena_adapter *adapter)
ena_free_all_rx_resources(adapter);
counter_u64_add(adapter->dev_stats.interface_down, 1);
-
- ENA_TIMER_RESET(adapter);
}
static uint32_t
@@ -3275,24 +3259,7 @@ ena_timer_service(void *data)
if ((adapter->eni_metrics_sample_interval != 0) &&
(++adapter->eni_metrics_sample_interval_cnt >=
adapter->eni_metrics_sample_interval)) {
- /*
- * There is no race with other admin queue calls, as:
- * - Timer service runs after attach function ends, so all
- * configuration calls to the admin queue are finished.
- * - Timer service is temporarily stopped when bringing
- * the interface up or down.
- * - After interface is up, the driver doesn't use (at least
- * for now) other functions writing to the admin queue.
- *
- * It may change in the future, so in that situation, the lock
- * will be needed. ENA_LOCK_*() cannot be used for that purpose,
- * as callout ena_timer_service is protected by them. It could
- * lead to the deadlock if callout_drain() would hold the lock
- * before ena_copy_eni_metrics() was executed. It's advised to
- * use separate lock in that situation which will be used only
- * for the admin queue.
- */
- (void)ena_copy_eni_metrics(adapter);
+ taskqueue_enqueue(adapter->metrics_tq, &adapter->metrics_task);
adapter->eni_metrics_sample_interval_cnt = 0;
}
@@ -3499,6 +3466,16 @@ err:
return (rc);
}
+static void
+ena_metrics_task(void *arg, int pending)
+{
+ struct ena_adapter *adapter = (struct ena_adapter *)arg;
+
+ ENA_LOCK_LOCK();
+ (void)ena_copy_eni_metrics(adapter);
+ ENA_LOCK_UNLOCK();
+}
+
static void
ena_reset_task(void *arg, int pending)
{
@@ -3719,6 +3696,13 @@ ena_attach(device_t pdev)
taskqueue_start_threads(&adapter->reset_tq, 1, PI_NET,
"%s rstq", device_get_nameunit(adapter->pdev));
+ /* Initialize metrics task queue */
+ TASK_INIT(&adapter->metrics_task, 0, ena_metrics_task, adapter);
+ adapter->metrics_tq = taskqueue_create("ena_metrics_enqueue",
+ M_WAITOK | M_ZERO, taskqueue_thread_enqueue, &adapter->metrics_tq);
+ taskqueue_start_threads(&adapter->metrics_tq, 1, PI_NET,
+ "%s metricsq", device_get_nameunit(adapter->pdev));
+
/* Initialize statistics */
ena_alloc_counters((counter_u64_t *)&adapter->dev_stats,
sizeof(struct ena_stats_dev));
@@ -3797,6 +3781,11 @@ ena_detach(device_t pdev)
ENA_TIMER_DRAIN(adapter);
ENA_LOCK_UNLOCK();
+ /* Release metrics task */
+ while (taskqueue_cancel(adapter->metrics_tq, &adapter->metrics_task, NULL))
+ taskqueue_drain(adapter->metrics_tq, &adapter->metrics_task);
+ taskqueue_free(adapter->metrics_tq);
+
/* Release reset task */
while (taskqueue_cancel(adapter->reset_tq, &adapter->reset_task, NULL))
taskqueue_drain(adapter->reset_tq, &adapter->reset_task);
diff --git a/sys/dev/ena/ena.h b/sys/dev/ena/ena.h
index 56e14bd800ff..43fbaad17b78 100644
--- a/sys/dev/ena/ena.h
+++ b/sys/dev/ena/ena.h
@@ -470,6 +470,8 @@ struct ena_adapter {
uint32_t next_monitored_tx_qid;
struct task reset_task;
struct taskqueue *reset_tq;
+ struct task metrics_task;
+ struct taskqueue *metrics_tq;
int wd_active;
sbintime_t keep_alive_timeout;
sbintime_t missing_tx_timeout;