Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2020 Intel Corporation
3 : : */
4 : :
5 : : #include <stdalign.h>
6 : :
7 : : #include <eal_export.h>
8 : : #include <rte_common.h>
9 : : #include <rte_lcore.h>
10 : : #include <rte_lcore_var.h>
11 : : #include <rte_rtm.h>
12 : : #include <rte_spinlock.h>
13 : :
14 : : #include "rte_power_intrinsics.h"
15 : :
16 : : /*
17 : : * Per-lcore structure holding current status of C0.2 sleeps.
18 : : */
19 : : struct power_wait_status {
20 : : rte_spinlock_t lock;
21 : : volatile void *monitor_addr; /**< NULL if not currently sleeping */
22 : : };
23 : :
24 : : RTE_LCORE_VAR_HANDLE(struct power_wait_status, wait_status);
25 : :
26 : : static void
27 : : init_wait_status(void)
28 : : {
29 [ # # # # : 0 : if (wait_status != NULL)
# # ]
30 : : return;
31 : 0 : RTE_LCORE_VAR_ALLOC(wait_status);
32 : : }
33 : :
34 : : /*
35 : : * This function uses UMONITOR/UMWAIT instructions and will enter C0.2 state.
36 : : * For more information about usage of these instructions, please refer to
37 : : * Intel(R) 64 and IA-32 Architectures Software Developer's Manual.
38 : : */
39 : 0 : static void intel_umonitor(volatile void *addr)
40 : : {
41 : : #if defined(RTE_TOOLCHAIN_MSVC) || defined(__WAITPKG__)
42 : : /* cast away "volatile" when using the intrinsic */
43 : : _umonitor((void *)(uintptr_t)addr);
44 : : #else
45 : : /*
46 : : * we're using raw byte codes for compiler versions which
47 : : * don't support this instruction natively.
48 : : */
49 : 0 : asm volatile(".byte 0xf3, 0x0f, 0xae, 0xf7;"
50 : : :
51 : : : "D"(addr));
52 : : #endif
53 : 0 : }
54 : :
55 : 0 : static void intel_umwait(const uint64_t timeout)
56 : : {
57 : : #if defined(RTE_TOOLCHAIN_MSVC) || defined(__WAITPKG__)
58 : : _umwait(0, timeout);
59 : : #else
60 : 0 : const uint32_t tsc_l = (uint32_t)timeout;
61 : 0 : const uint32_t tsc_h = (uint32_t)(timeout >> 32);
62 : :
63 : 0 : asm volatile(".byte 0xf2, 0x0f, 0xae, 0xf7;"
64 : : : /* ignore rflags */
65 : : : "D"(0), /* enter C0.2 */
66 : : "a"(tsc_l), "d"(tsc_h));
67 : : #endif
68 : 0 : }
69 : :
70 : : /*
71 : : * This function uses MONITORX/MWAITX instructions and will enter C1 state.
72 : : * For more information about usage of these instructions, please refer to
73 : : * AMD64 Architecture Programmer’s Manual.
74 : : */
75 : 0 : static void amd_monitorx(volatile void *addr)
76 : : {
77 : : #if defined(RTE_TOOLCHAIN_MSVC) || defined(__MWAITX__)
78 : : /* cast away "volatile" when using the intrinsic */
79 : : _mm_monitorx((void *)(uintptr_t)addr, 0, 0);
80 : : #else
81 : 0 : asm volatile(".byte 0x0f, 0x01, 0xfa;"
82 : : :
83 : : : "a"(addr),
84 : : "c"(0), /* no extensions */
85 : : "d"(0)); /* no hints */
86 : : #endif
87 : 0 : }
88 : :
89 : 0 : static void amd_mwaitx(const uint64_t timeout)
90 : : {
91 : : RTE_SET_USED(timeout);
92 : : #if defined(RTE_TOOLCHAIN_MSVC) || defined(__MWAITX__)
93 : : _mm_mwaitx(0, 0, 0);
94 : : #else
95 : 0 : asm volatile(".byte 0x0f, 0x01, 0xfb;"
96 : : : /* ignore rflags */
97 : : : "a"(0), /* enter C1 */
98 : : "c"(0)); /* no time-out */
99 : : #endif
100 : 0 : }
101 : :
102 : : static alignas(RTE_CACHE_LINE_SIZE) struct {
103 : : void (*mmonitor)(volatile void *addr);
104 : : void (*mwait)(const uint64_t timeout);
105 : : } power_monitor_ops;
106 : :
107 : : static inline void
108 : : __umwait_wakeup(volatile void *addr)
109 : : {
110 : : uint64_t val;
111 : :
112 : : /* trigger a write but don't change the value */
113 : 0 : val = rte_atomic_load_explicit((volatile __rte_atomic uint64_t *)addr,
114 : : rte_memory_order_relaxed);
115 : 0 : rte_atomic_compare_exchange_strong_explicit((volatile __rte_atomic uint64_t *)addr,
116 : : &val, val, rte_memory_order_relaxed, rte_memory_order_relaxed);
117 : 0 : }
118 : :
119 : : static bool wait_supported;
120 : : static bool wait_multi_supported;
121 : : static bool monitor_supported;
122 : :
123 : : static inline uint64_t
124 : : __get_umwait_val(const volatile void *p, const uint8_t sz)
125 : : {
126 : 0 : switch (sz) {
127 : 0 : case sizeof(uint8_t):
128 : 0 : return *(const volatile uint8_t *)p;
129 : 0 : case sizeof(uint16_t):
130 : 0 : return *(const volatile uint16_t *)p;
131 : 0 : case sizeof(uint32_t):
132 : 0 : return *(const volatile uint32_t *)p;
133 : 0 : case sizeof(uint64_t):
134 : 0 : return *(const volatile uint64_t *)p;
135 : : default:
136 : : /* shouldn't happen */
137 : : RTE_ASSERT(0);
138 : : return 0;
139 : : }
140 : : }
141 : :
142 : : static inline int
143 : : __check_val_size(const uint8_t sz)
144 : : {
145 : 0 : switch (sz) {
146 : : case sizeof(uint8_t): /* fall-through */
147 : : case sizeof(uint16_t): /* fall-through */
148 : : case sizeof(uint32_t): /* fall-through */
149 : : case sizeof(uint64_t): /* fall-through */
150 : : return 0;
151 : : default:
152 : : /* unexpected size */
153 : : return -1;
154 : : }
155 : : }
156 : :
157 : : /**
158 : : * This function uses UMONITOR/UMWAIT instructions and will enter C0.2 state.
159 : : * For more information about usage of these instructions, please refer to
160 : : * Intel(R) 64 and IA-32 Architectures Software Developer's Manual.
161 : : */
162 : : RTE_EXPORT_SYMBOL(rte_power_monitor)
163 : : int
164 [ # # ]: 0 : rte_power_monitor(const struct rte_power_monitor_cond *pmc,
165 : : const uint64_t tsc_timestamp)
166 : : {
167 : : const unsigned int lcore_id = rte_lcore_id();
168 : : struct power_wait_status *s;
169 : : uint64_t cur_value;
170 : :
171 : : /* prevent user from running this instruction if it's not supported */
172 [ # # ]: 0 : if (!monitor_supported)
173 : : return -ENOTSUP;
174 : :
175 : : /* prevent non-EAL thread from using this API */
176 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE)
177 : : return -EINVAL;
178 : :
179 [ # # ]: 0 : if (pmc == NULL)
180 : : return -EINVAL;
181 : :
182 [ # # ]: 0 : if (__check_val_size(pmc->size) < 0)
183 : : return -EINVAL;
184 : :
185 [ # # ]: 0 : if (pmc->fn == NULL)
186 : : return -EINVAL;
187 : :
188 : : init_wait_status();
189 : 0 : s = RTE_LCORE_VAR_LCORE(lcore_id, wait_status);
190 : :
191 : : /* update sleep address */
192 : 0 : rte_spinlock_lock(&s->lock);
193 : 0 : s->monitor_addr = pmc->addr;
194 : :
195 : : /* set address for memory monitor */
196 : 0 : power_monitor_ops.mmonitor(pmc->addr);
197 : :
198 : : /* now that we've put this address into monitor, we can unlock */
199 : : rte_spinlock_unlock(&s->lock);
200 : :
201 [ # # # # : 0 : cur_value = __get_umwait_val(pmc->addr, pmc->size);
# ]
202 : :
203 : : /* check if callback indicates we should abort */
204 [ # # ]: 0 : if (pmc->fn(cur_value, pmc->opaque) != 0)
205 : 0 : goto end;
206 : :
207 : : /* execute mwait */
208 : 0 : power_monitor_ops.mwait(tsc_timestamp);
209 : :
210 : 0 : end:
211 : : /* erase sleep address */
212 : : rte_spinlock_lock(&s->lock);
213 : 0 : s->monitor_addr = NULL;
214 : : rte_spinlock_unlock(&s->lock);
215 : :
216 : 0 : return 0;
217 : : }
218 : :
219 : : /**
220 : : * This function uses TPAUSE instruction and will enter C0.2 state. For more
221 : : * information about usage of this instruction, please refer to Intel(R) 64 and
222 : : * IA-32 Architectures Software Developer's Manual.
223 : : */
224 : : RTE_EXPORT_SYMBOL(rte_power_pause)
225 : : int
226 : 0 : rte_power_pause(const uint64_t tsc_timestamp)
227 : : {
228 : : /* prevent user from running this instruction if it's not supported */
229 [ # # ]: 0 : if (!wait_supported)
230 : : return -ENOTSUP;
231 : :
232 : : /* execute TPAUSE */
233 : : #if defined(RTE_TOOLCHAIN_MSVC) || defined(__WAITPKG__)
234 : : _tpause(0, tsc_timestamp);
235 : : #else
236 : 0 : const uint32_t tsc_l = (uint32_t)tsc_timestamp;
237 : 0 : const uint32_t tsc_h = (uint32_t)(tsc_timestamp >> 32);
238 : :
239 : 0 : asm volatile(".byte 0x66, 0x0f, 0xae, 0xf7;"
240 : : : /* ignore rflags */
241 : : : "D"(0), /* enter C0.2 */
242 : : "a"(tsc_l), "d"(tsc_h));
243 : : #endif
244 : :
245 : 0 : return 0;
246 : : }
247 : :
248 : 252 : RTE_INIT(rte_power_intrinsics_init) {
249 : : struct rte_cpu_intrinsics i;
250 : :
251 : 252 : rte_cpu_get_intrinsics_support(&i);
252 : :
253 [ - + ]: 252 : if (i.power_monitor && i.power_pause)
254 : 0 : wait_supported = 1;
255 [ - + ]: 252 : if (i.power_monitor_multi)
256 : 0 : wait_multi_supported = 1;
257 [ - + ]: 252 : if (i.power_monitor)
258 : 0 : monitor_supported = 1;
259 : :
260 [ - + ]: 252 : if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_MONITORX)) {
261 : 0 : power_monitor_ops.mmonitor = &amd_monitorx;
262 : 0 : power_monitor_ops.mwait = &amd_mwaitx;
263 : : } else {
264 : 252 : power_monitor_ops.mmonitor = &intel_umonitor;
265 : 252 : power_monitor_ops.mwait = &intel_umwait;
266 : : }
267 : 252 : }
268 : :
269 : : RTE_EXPORT_SYMBOL(rte_power_monitor_wakeup)
270 : : int
271 : 0 : rte_power_monitor_wakeup(const unsigned int lcore_id)
272 : : {
273 : : struct power_wait_status *s;
274 : :
275 : : /* prevent user from running this instruction if it's not supported */
276 [ # # ]: 0 : if (!monitor_supported)
277 : : return -ENOTSUP;
278 : :
279 : : /* prevent buffer overrun */
280 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE)
281 : : return -EINVAL;
282 : :
283 : : init_wait_status();
284 : 0 : s = RTE_LCORE_VAR_LCORE(lcore_id, wait_status);
285 : :
286 : : /*
287 : : * There is a race condition between sleep, wakeup and locking, but we
288 : : * don't need to handle it.
289 : : *
290 : : * Possible situations:
291 : : *
292 : : * 1. T1 locks, sets address, unlocks
293 : : * 2. T2 locks, triggers wakeup, unlocks
294 : : * 3. T1 sleeps
295 : : *
296 : : * In this case, because T1 has already set the address for monitoring,
297 : : * we will wake up immediately even if T2 triggers wakeup before T1
298 : : * goes to sleep.
299 : : *
300 : : * 1. T1 locks, sets address, unlocks, goes to sleep, and wakes up
301 : : * 2. T2 locks, triggers wakeup, and unlocks
302 : : * 3. T1 locks, erases address, and unlocks
303 : : *
304 : : * In this case, since we've already woken up, the "wakeup" was
305 : : * unneeded, and since T1 is still waiting on T2 releasing the lock, the
306 : : * wakeup address is still valid so it's perfectly safe to write it.
307 : : *
308 : : * For multi-monitor case, the act of locking will in itself trigger the
309 : : * wakeup, so no additional writes necessary.
310 : : */
311 : 0 : rte_spinlock_lock(&s->lock);
312 [ # # ]: 0 : if (s->monitor_addr != NULL)
313 : : __umwait_wakeup(s->monitor_addr);
314 : : rte_spinlock_unlock(&s->lock);
315 : :
316 : 0 : return 0;
317 : : }
318 : :
319 : : RTE_EXPORT_SYMBOL(rte_power_monitor_multi)
320 : : int
321 : 0 : rte_power_monitor_multi(const struct rte_power_monitor_cond pmc[],
322 : : const uint32_t num, const uint64_t tsc_timestamp)
323 : : {
324 : : struct power_wait_status *s;
325 : : uint32_t i, rc;
326 : :
327 : : /* check if supported */
328 [ # # ]: 0 : if (!wait_multi_supported)
329 : : return -ENOTSUP;
330 : :
331 [ # # ]: 0 : if (pmc == NULL || num == 0)
332 : : return -EINVAL;
333 : :
334 : : init_wait_status();
335 : 0 : s = RTE_LCORE_VAR(wait_status);
336 : :
337 : : /* we are already inside transaction region, return */
338 [ # # ]: 0 : if (rte_xtest() != 0)
339 : : return 0;
340 : :
341 : : /* start new transaction region */
342 : : rc = rte_xbegin();
343 : :
344 : : /* transaction abort, possible write to one of wait addresses */
345 [ # # ]: 0 : if (rc != RTE_XBEGIN_STARTED)
346 : : return 0;
347 : :
348 : : /*
349 : : * the mere act of reading the lock status here adds the lock to
350 : : * the read set. This means that when we trigger a wakeup from another
351 : : * thread, even if we don't have a defined wakeup address and thus don't
352 : : * actually cause any writes, the act of locking our lock will itself
353 : : * trigger the wakeup and abort the transaction.
354 : : */
355 : : rte_spinlock_is_locked(&s->lock);
356 : :
357 : : /*
358 : : * add all addresses to wait on into transaction read-set and check if
359 : : * any of wakeup conditions are already met.
360 : : */
361 : : rc = 0;
362 [ # # ]: 0 : for (i = 0; i < num; i++) {
363 : 0 : const struct rte_power_monitor_cond *c = &pmc[i];
364 : :
365 : : /* cannot be NULL */
366 [ # # ]: 0 : if (c->fn == NULL) {
367 : : rc = -EINVAL;
368 : : break;
369 : : }
370 : :
371 [ # # # # : 0 : const uint64_t val = __get_umwait_val(c->addr, c->size);
# ]
372 : :
373 : : /* abort if callback indicates that we need to stop */
374 [ # # ]: 0 : if (c->fn(val, c->opaque) != 0)
375 : : break;
376 : : }
377 : :
378 : : /* none of the conditions were met, sleep until timeout */
379 [ # # ]: 0 : if (i == num)
380 : 0 : rte_power_pause(tsc_timestamp);
381 : :
382 : : /* end transaction region */
383 : : rte_xend();
384 : :
385 : 0 : return rc;
386 : : }
|