From patchwork Fri Sep 11 00:22:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Kozlyuk X-Patchwork-Id: 77255 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id C4C02A04B5; Fri, 11 Sep 2020 02:22:30 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id DC9301C0D2; Fri, 11 Sep 2020 02:22:19 +0200 (CEST) Received: from mail-lj1-f195.google.com (mail-lj1-f195.google.com [209.85.208.195]) by dpdk.org (Postfix) with ESMTP id 5ABFA1C0B9 for ; Fri, 11 Sep 2020 02:22:17 +0200 (CEST) Received: by mail-lj1-f195.google.com with SMTP id r24so10543237ljm.3 for ; Thu, 10 Sep 2020 17:22:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=yuu7Kw1EK66acJ0QzG8ewB4p6dl1GZgrMVfFFAY4gHg=; b=Ay+0WAvAxjrJrY5CcYgFESGPvj+dg/FehTt5XEtJ7VvPH9i7k0KNsOrYV34O+12ZVE urD1eboljKB2LieNJfS28l6x97D168yGaHgBQ7TTi+hveUsCpXEJi21brw42aqb3qslK qcN0raLzKmfvIaK+/mQMn+HH/8XyRRN3QHZ7hHoirndQQP4dBfULdW5+A7Z6Yi/Ckqhs dQv1Vj6meQJ6MML55n5r8SD2bUzjjOWVunfpAQVROTjUb7MSW9OVbyp0aDQhigi2gTU9 oSY0vjkyCH0VmNL7+2zNY/0bXoAlsHaTtHQcWoedeUfKpHz14hn56/ObZHaoa2d6+pVk zjSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yuu7Kw1EK66acJ0QzG8ewB4p6dl1GZgrMVfFFAY4gHg=; b=CzStsTU3+VHzJzIHw9dEQcKOabrrz2oM7hUrWGiOhrSfGLffvQ+4RT9E3G/ITlScgk wJfqglu/HHrDN5rlUvAm2qtA28JBhzXkzyKvKXQyPDQP6Gv5i+wVT6h0p+6KuWFWxwz3 PuXtrVIRrrqZ2IQSwZf8daGKkeYxTWcF+X1YT8k7hK8Tru3eXJBHht0b38ffUyvildhH uwVo+NxsB/sjdvA/4JrXG5GjW/kVjngPU0ioAJl1ntJfyurR3Ey1nbDQV0Jta6xD9+/A k1Ue2XnlxhSDLylykJMXwnd4/AODJWA4uVRq1NDvELgj4fVEOEx3QRRbhcosWyHGjZPy FLLw== X-Gm-Message-State: AOAM533u1M1NFb/IxUGPpzGGLQ81Xm1uqU7wt8tFiguoRTDN8kikWI39 pdXxOHrE8vIH9qa2DJ/UblXgfN43lTsLww== X-Google-Smtp-Source: ABdhPJyFCFyuxLFBqCIY+0GYQ5QTAXeoMMbQtXtVmg3V7PwAEwrtVYXjlMmy0MnTUXIYvQc2wsmtSA== X-Received: by 2002:a2e:b5d9:: with SMTP id g25mr5423882ljn.335.1599783736653; Thu, 10 Sep 2020 17:22:16 -0700 (PDT) Received: from localhost.localdomain (broadband-37-110-65-23.ip.moscow.rt.ru. [37.110.65.23]) by smtp.gmail.com with ESMTPSA id b16sm132765ljh.34.2020.09.10.17.22.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 10 Sep 2020 17:22:16 -0700 (PDT) From: Dmitry Kozlyuk To: dev@dpdk.org Cc: Dmitry Kozlyuk , Narcisa Ana Maria Vasile , Dmitry Malloy , Pallavi Kadam Date: Fri, 11 Sep 2020 03:22:07 +0300 Message-Id: <20200911002207.31813-3-dmitry.kozliuk@gmail.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: <20200911002207.31813-1-dmitry.kozliuk@gmail.com> References: <20200911002207.31813-1-dmitry.kozliuk@gmail.com> MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH 2/2] eal/windows: implement alarm API X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Implementation is based on waitable timers Win32 API. When timer is set, a callback and its argument are supplied to the OS, while timer handle is stored in EAL alarm list. When timer expires, OS wakes up the interrupt thread and runs the callback. Upon completion it removes the alarm. Waitable timers must be set from the thread their callback will run in, eal_intr_thread_schedule() provides a way to schedule asyncronuous code execution in the interrupt thread. Alarm module builds synchronous timer setup on top of it. Windows alarms are not a type of DPDK interrupt handle and do not interact with interrupt module beyond executing in the same thread. Signed-off-by: Dmitry Kozlyuk --- lib/librte_eal/rte_eal_exports.def | 2 + lib/librte_eal/windows/eal_alarm.c | 219 +++++++++++++++++++++++++++++ lib/librte_eal/windows/meson.build | 1 + 3 files changed, 222 insertions(+) create mode 100644 lib/librte_eal/windows/eal_alarm.c diff --git a/lib/librte_eal/rte_eal_exports.def b/lib/librte_eal/rte_eal_exports.def index 9baca0110..b6abb5ae1 100644 --- a/lib/librte_eal/rte_eal_exports.def +++ b/lib/librte_eal/rte_eal_exports.def @@ -14,6 +14,8 @@ EXPORTS rte_devargs_insert rte_devargs_next rte_devargs_remove + rte_eal_alarm_set + rte_eal_alarm_cancel rte_eal_get_configuration rte_eal_has_hugepages rte_eal_has_pci diff --git a/lib/librte_eal/windows/eal_alarm.c b/lib/librte_eal/windows/eal_alarm.c new file mode 100644 index 000000000..3b262793a --- /dev/null +++ b/lib/librte_eal/windows/eal_alarm.c @@ -0,0 +1,219 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2020 Dmitry Kozlyuk + */ + +#include +#include + +#include +#include + +#include + +#include "eal_windows.h" + +enum alarm_state { + ALARM_ARMED, + ALARM_TRIGGERED, + ALARM_CANCELLED +}; + +struct alarm_entry { + LIST_ENTRY(alarm_entry) next; + rte_eal_alarm_callback cb_fn; + void *cb_arg; + HANDLE timer; + atomic_uint state; +}; + +static LIST_HEAD(alarm_list, alarm_entry) alarm_list = LIST_HEAD_INITIALIZER(); + +static rte_spinlock_t alarm_lock = RTE_SPINLOCK_INITIALIZER; + +static int intr_thread_exec(void (*func)(void *arg), void *arg); + +static void +alarm_remove_unsafe(struct alarm_entry *ap) +{ + LIST_REMOVE(ap, next); + CloseHandle(ap->timer); + free(ap); +} + +static void +alarm_callback(void *arg, DWORD low __rte_unused, DWORD high __rte_unused) +{ + struct alarm_entry *ap = arg; + unsigned int state = ALARM_ARMED; + + if (!atomic_compare_exchange_strong( + &ap->state, &state, ALARM_TRIGGERED)) + return; + + ap->cb_fn(ap->cb_arg); + + rte_spinlock_lock(&alarm_lock); + alarm_remove_unsafe(ap); + rte_spinlock_unlock(&alarm_lock); +} + +struct alarm_task { + struct alarm_entry *entry; + LARGE_INTEGER deadline; + int ret; +}; + +static void +alarm_set(void *arg) +{ + struct alarm_task *task = arg; + + BOOL ret = SetWaitableTimer( + task->entry->timer, &task->deadline, + 0, alarm_callback, task->entry, FALSE); + task->ret = ret ? 0 : (-1); +} + +int +rte_eal_alarm_set(uint64_t us, rte_eal_alarm_callback cb_fn, void *cb_arg) +{ + struct alarm_entry *ap; + HANDLE timer; + FILETIME ft; + struct alarm_task task; + int ret; + + /* Calculate deadline ASAP, unit of measure = 100ns. */ + GetSystemTimePreciseAsFileTime(&ft); + task.deadline.LowPart = ft.dwLowDateTime; + task.deadline.HighPart = ft.dwHighDateTime; + task.deadline.QuadPart += 10 * us; + + ap = calloc(1, sizeof(*ap)); + if (ap == NULL) { + RTE_LOG(ERR, EAL, "Cannot allocate alarm entry\n"); + ret = -ENOMEM; + goto exit; + } + + timer = CreateWaitableTimer(NULL, FALSE, NULL); + if (timer == NULL) { + RTE_LOG_WIN32_ERR("CreateWaitableTimer()"); + ret = -EINVAL; + goto fail; + } + + ap->timer = timer; + ap->cb_fn = cb_fn; + ap->cb_arg = cb_arg; + task.entry = ap; + + /* Waitable timer must be set in the same thread that will + * do an alertable wait for the alarm to trigger, that is, + * in the interrupt thread. Setting can fail, so do it synchronously. + */ + ret = intr_thread_exec(alarm_set, &task); + if (ret < 0) { + RTE_LOG(ERR, EAL, "Cannot setup alarm in interrupt thread\n"); + goto fail; + } + + ret = task.ret; + if (ret < 0) + goto fail; + + rte_spinlock_lock(&alarm_lock); + LIST_INSERT_HEAD(&alarm_list, ap, next); + rte_spinlock_unlock(&alarm_lock); + + goto exit; + +fail: + if (timer != NULL) + CloseHandle(timer); + if (ap != NULL) + free(ap); + +exit: + rte_eal_trace_alarm_set(us, cb_fn, cb_arg, ret); + return ret; +} + +static bool +alarm_matches(const struct alarm_entry *ap, + rte_eal_alarm_callback cb_fn, void *cb_arg) +{ + bool any_arg = cb_arg == (void *)(-1); + return (ap->cb_fn == cb_fn) && (any_arg || ap->cb_arg == cb_arg); +} + +int +rte_eal_alarm_cancel(rte_eal_alarm_callback cb_fn, void *cb_arg) +{ + struct alarm_entry *ap; + unsigned int state; + int removed; + bool executing; + + removed = 0; + do { + executing = false; + + rte_spinlock_lock(&alarm_lock); + + LIST_FOREACH(ap, &alarm_list, next) { + if (!alarm_matches(ap, cb_fn, cb_arg)) + continue; + + state = ALARM_ARMED; + if (atomic_compare_exchange_strong( + &ap->state, &state, ALARM_CANCELLED)) { + alarm_remove_unsafe(ap); + removed++; + } else if (state == ALARM_TRIGGERED) + executing = true; + } + + rte_spinlock_unlock(&alarm_lock); + } while (executing); + + rte_eal_trace_alarm_cancel(cb_fn, cb_arg, removed); + return removed; +} + +struct intr_task { + void (*func)(void *arg); + void *arg; + rte_spinlock_t lock; /* unlocked at task completion */ +}; + +static void +intr_thread_entry(void *arg) +{ + struct intr_task *task = arg; + task->func(task->arg); + rte_spinlock_unlock(&task->lock); +} + +static int +intr_thread_exec(void (*func)(void *arg), void *arg) +{ + struct intr_task task; + int ret; + + task.func = func; + task.arg = arg; + rte_spinlock_init(&task.lock); + + /* Make timers more precise by synchronizing in userspace. */ + rte_spinlock_lock(&task.lock); + ret = eal_intr_thread_schedule(intr_thread_entry, &task); + if (ret < 0) { + RTE_LOG(ERR, EAL, "Cannot schedule task to interrupt thread\n"); + return -EINVAL; + } + + /* Wait for the task to complete. */ + rte_spinlock_lock(&task.lock); + return 0; +} diff --git a/lib/librte_eal/windows/meson.build b/lib/librte_eal/windows/meson.build index b690bc6b0..3b2faf29e 100644 --- a/lib/librte_eal/windows/meson.build +++ b/lib/librte_eal/windows/meson.build @@ -5,6 +5,7 @@ subdir('include') sources += files( 'eal.c', + 'eal_alarm.c', 'eal_debug.c', 'eal_file.c', 'eal_hugepages.c',