1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2020 Dmitry Kozlyuk
10 #include <rte_spinlock.h>
12 #include <rte_eal_trace.h>
14 #include "eal_windows.h"
23 LIST_ENTRY(alarm_entry) next;
24 rte_eal_alarm_callback cb_fn;
30 static LIST_HEAD(alarm_list, alarm_entry) alarm_list = LIST_HEAD_INITIALIZER();
32 static rte_spinlock_t alarm_lock = RTE_SPINLOCK_INITIALIZER;
34 static int intr_thread_exec_sync(void (*func)(void *arg), void *arg);
37 alarm_remove_unsafe(struct alarm_entry *ap)
39 LIST_REMOVE(ap, next);
40 CloseHandle(ap->timer);
45 alarm_callback(void *arg, DWORD low __rte_unused, DWORD high __rte_unused)
47 struct alarm_entry *ap = arg;
48 unsigned int state = ALARM_ARMED;
50 if (!atomic_compare_exchange_strong(
51 &ap->state, &state, ALARM_TRIGGERED))
54 ap->cb_fn(ap->cb_arg);
56 rte_spinlock_lock(&alarm_lock);
57 alarm_remove_unsafe(ap);
58 rte_spinlock_unlock(&alarm_lock);
62 alarm_set(struct alarm_entry *entry, LARGE_INTEGER deadline)
64 BOOL ret = SetWaitableTimer(
65 entry->timer, &deadline, 0, alarm_callback, entry, FALSE);
67 RTE_LOG_WIN32_ERR("SetWaitableTimer");
74 struct alarm_entry *entry;
75 LARGE_INTEGER deadline;
80 alarm_task_exec(void *arg)
82 struct alarm_task *task = arg;
83 task->ret = alarm_set(task->entry, task->deadline);
87 rte_eal_alarm_set(uint64_t us, rte_eal_alarm_callback cb_fn, void *cb_arg)
89 struct alarm_entry *ap;
92 LARGE_INTEGER deadline;
96 RTE_LOG(ERR, EAL, "NULL callback\n");
101 /* Calculate deadline ASAP, unit of measure = 100ns. */
102 GetSystemTimePreciseAsFileTime(&ft);
103 deadline.LowPart = ft.dwLowDateTime;
104 deadline.HighPart = ft.dwHighDateTime;
105 deadline.QuadPart += 10 * us;
107 ap = calloc(1, sizeof(*ap));
109 RTE_LOG(ERR, EAL, "Cannot allocate alarm entry\n");
114 timer = CreateWaitableTimer(NULL, FALSE, NULL);
116 RTE_LOG_WIN32_ERR("CreateWaitableTimer()");
125 /* Waitable timer must be set in the same thread that will
126 * do an alertable wait for the alarm to trigger, that is,
127 * in the interrupt thread.
129 if (rte_thread_is_intr()) {
130 /* Directly schedule callback execution. */
131 ret = alarm_set(ap, deadline);
133 RTE_LOG(ERR, EAL, "Cannot setup alarm\n");
137 /* Dispatch a task to set alarm into the interrupt thread.
138 * Execute it synchronously, because it can fail.
140 struct alarm_task task = {
142 .deadline = deadline,
145 ret = intr_thread_exec_sync(alarm_task_exec, &task);
147 RTE_LOG(ERR, EAL, "Cannot setup alarm in interrupt thread\n");
156 rte_spinlock_lock(&alarm_lock);
157 LIST_INSERT_HEAD(&alarm_list, ap, next);
158 rte_spinlock_unlock(&alarm_lock);
169 rte_eal_trace_alarm_set(us, cb_fn, cb_arg, ret);
174 alarm_matches(const struct alarm_entry *ap,
175 rte_eal_alarm_callback cb_fn, void *cb_arg)
177 bool any_arg = cb_arg == (void *)(-1);
178 return (ap->cb_fn == cb_fn) && (any_arg || ap->cb_arg == cb_arg);
182 rte_eal_alarm_cancel(rte_eal_alarm_callback cb_fn, void *cb_arg)
184 struct alarm_entry *ap;
192 RTE_LOG(ERR, EAL, "NULL callback\n");
199 rte_spinlock_lock(&alarm_lock);
201 LIST_FOREACH(ap, &alarm_list, next) {
202 if (!alarm_matches(ap, cb_fn, cb_arg))
206 if (atomic_compare_exchange_strong(
207 &ap->state, &state, ALARM_CANCELLED)) {
208 alarm_remove_unsafe(ap);
210 } else if (state == ALARM_TRIGGERED)
214 rte_spinlock_unlock(&alarm_lock);
217 rte_eal_trace_alarm_cancel(cb_fn, cb_arg, removed);
222 void (*func)(void *arg);
224 rte_spinlock_t lock; /* unlocked at task completion */
228 intr_thread_entry(void *arg)
230 struct intr_task *task = arg;
231 task->func(task->arg);
232 rte_spinlock_unlock(&task->lock);
236 intr_thread_exec_sync(void (*func)(void *arg), void *arg)
238 struct intr_task task;
243 rte_spinlock_init(&task.lock);
245 /* Make timers more precise by synchronizing in userspace. */
246 rte_spinlock_lock(&task.lock);
247 ret = eal_intr_thread_schedule(intr_thread_entry, &task);
249 RTE_LOG(ERR, EAL, "Cannot schedule task to interrupt thread\n");
253 /* Wait for the task to complete. */
254 rte_spinlock_lock(&task.lock);