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