1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2020 Dmitry Kozlyuk
9 #include <rte_spinlock.h>
11 #include <rte_eal_trace.h>
13 #include "eal_windows.h"
22 LIST_ENTRY(alarm_entry) next;
23 rte_eal_alarm_callback cb_fn;
29 static LIST_HEAD(alarm_list, alarm_entry) alarm_list = LIST_HEAD_INITIALIZER();
31 static rte_spinlock_t alarm_lock = RTE_SPINLOCK_INITIALIZER;
33 static int intr_thread_exec(void (*func)(void *arg), void *arg);
36 alarm_remove_unsafe(struct alarm_entry *ap)
38 LIST_REMOVE(ap, next);
39 CloseHandle(ap->timer);
44 alarm_callback(void *arg, DWORD low __rte_unused, DWORD high __rte_unused)
46 struct alarm_entry *ap = arg;
47 unsigned int state = ALARM_ARMED;
49 if (!atomic_compare_exchange_strong(
50 &ap->state, &state, ALARM_TRIGGERED))
53 ap->cb_fn(ap->cb_arg);
55 rte_spinlock_lock(&alarm_lock);
56 alarm_remove_unsafe(ap);
57 rte_spinlock_unlock(&alarm_lock);
61 struct alarm_entry *entry;
62 LARGE_INTEGER deadline;
69 struct alarm_task *task = arg;
71 BOOL ret = SetWaitableTimer(
72 task->entry->timer, &task->deadline,
73 0, alarm_callback, task->entry, FALSE);
74 task->ret = ret ? 0 : (-1);
78 rte_eal_alarm_set(uint64_t us, rte_eal_alarm_callback cb_fn, void *cb_arg)
80 struct alarm_entry *ap;
83 struct alarm_task task;
86 /* Calculate deadline ASAP, unit of measure = 100ns. */
87 GetSystemTimePreciseAsFileTime(&ft);
88 task.deadline.LowPart = ft.dwLowDateTime;
89 task.deadline.HighPart = ft.dwHighDateTime;
90 task.deadline.QuadPart += 10 * us;
92 ap = calloc(1, sizeof(*ap));
94 RTE_LOG(ERR, EAL, "Cannot allocate alarm entry\n");
99 timer = CreateWaitableTimer(NULL, FALSE, NULL);
101 RTE_LOG_WIN32_ERR("CreateWaitableTimer()");
111 /* Waitable timer must be set in the same thread that will
112 * do an alertable wait for the alarm to trigger, that is,
113 * in the interrupt thread. Setting can fail, so do it synchronously.
115 ret = intr_thread_exec(alarm_set, &task);
117 RTE_LOG(ERR, EAL, "Cannot setup alarm in interrupt thread\n");
125 rte_spinlock_lock(&alarm_lock);
126 LIST_INSERT_HEAD(&alarm_list, ap, next);
127 rte_spinlock_unlock(&alarm_lock);
138 rte_eal_trace_alarm_set(us, cb_fn, cb_arg, ret);
143 alarm_matches(const struct alarm_entry *ap,
144 rte_eal_alarm_callback cb_fn, void *cb_arg)
146 bool any_arg = cb_arg == (void *)(-1);
147 return (ap->cb_fn == cb_fn) && (any_arg || ap->cb_arg == cb_arg);
151 rte_eal_alarm_cancel(rte_eal_alarm_callback cb_fn, void *cb_arg)
153 struct alarm_entry *ap;
162 rte_spinlock_lock(&alarm_lock);
164 LIST_FOREACH(ap, &alarm_list, next) {
165 if (!alarm_matches(ap, cb_fn, cb_arg))
169 if (atomic_compare_exchange_strong(
170 &ap->state, &state, ALARM_CANCELLED)) {
171 alarm_remove_unsafe(ap);
173 } else if (state == ALARM_TRIGGERED)
177 rte_spinlock_unlock(&alarm_lock);
180 rte_eal_trace_alarm_cancel(cb_fn, cb_arg, removed);
185 void (*func)(void *arg);
187 rte_spinlock_t lock; /* unlocked at task completion */
191 intr_thread_entry(void *arg)
193 struct intr_task *task = arg;
194 task->func(task->arg);
195 rte_spinlock_unlock(&task->lock);
199 intr_thread_exec(void (*func)(void *arg), void *arg)
201 struct intr_task task;
206 rte_spinlock_init(&task.lock);
208 /* Make timers more precise by synchronizing in userspace. */
209 rte_spinlock_lock(&task.lock);
210 ret = eal_intr_thread_schedule(intr_thread_entry, &task);
212 RTE_LOG(ERR, EAL, "Cannot schedule task to interrupt thread\n");
216 /* Wait for the task to complete. */
217 rte_spinlock_lock(&task.lock);