blob: 7f0d6ad2e342191752fd8eb92eae9de6f83851c3 [file] [log] [blame]
Damjan Marion522e4862016-03-04 12:44:14 +01001/*
2 * Copyright (c) 2016 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef included_clib_cpu_h
17#define included_clib_cpu_h
18
Damjan Marion0a78fa12019-01-19 23:45:36 +010019#include <sys/syscall.h>
Damjan Marion1c80e832016-05-11 23:07:18 +020020#include <vppinfra/format.h>
21
22/*
23 * multiarchitecture support. Adding new entry will produce
24 * new graph node function variant optimized for specific cpu
25 * microarchitecture.
26 * Order is important for runtime selection, as 1st match wins...
27 */
28
29#if __x86_64__ && CLIB_DEBUG == 0
30#define foreach_march_variant(macro, x) \
31 macro(avx2, x, "arch=core-avx2")
32#else
33#define foreach_march_variant(macro, x)
34#endif
35
36
Damjan Marion5ad75f52018-11-29 14:40:30 +010037#if __GNUC__ > 4 && !__clang__ && CLIB_DEBUG == 0
Damjan Marion24223172018-05-28 16:22:14 +020038#define CLIB_CPU_OPTIMIZED __attribute__ ((optimize ("O3")))
Damjan Marion1c80e832016-05-11 23:07:18 +020039#else
40#define CLIB_CPU_OPTIMIZED
41#endif
42
43
44#define CLIB_MULTIARCH_ARCH_CHECK(arch, fn, tgt) \
45 if (clib_cpu_supports_ ## arch()) \
46 return & fn ## _ ##arch;
47
48#define CLIB_MULTIARCH_SELECT_FN(fn,...) \
49 __VA_ARGS__ void * fn ## _multiarch_select(void) \
50{ \
51 foreach_march_variant(CLIB_MULTIARCH_ARCH_CHECK, fn) \
52 return & fn; \
53}
54
Damjan Marion812b32d2018-05-28 21:26:47 +020055#ifdef CLIB_MARCH_VARIANT
Damjan Marion04f3db32017-11-10 21:55:45 +010056#define __CLIB_MULTIARCH_FN(a,b) a##_##b
57#define _CLIB_MULTIARCH_FN(a,b) __CLIB_MULTIARCH_FN(a,b)
Damjan Marion812b32d2018-05-28 21:26:47 +020058#define CLIB_MULTIARCH_FN(fn) _CLIB_MULTIARCH_FN(fn,CLIB_MARCH_VARIANT)
Damjan Marion04f3db32017-11-10 21:55:45 +010059#else
60#define CLIB_MULTIARCH_FN(fn) fn
61#endif
Damjan Marion1c80e832016-05-11 23:07:18 +020062
Damjan Marion812b32d2018-05-28 21:26:47 +020063#define CLIB_MARCH_SFX CLIB_MULTIARCH_FN
64
Damjan Marion910d3692019-01-21 11:48:34 +010065typedef struct _clib_march_fn_registration
66{
67 void *function;
68 int priority;
69 struct _clib_march_fn_registration *next;
70 char *name;
71} clib_march_fn_registration;
72
73static_always_inline void *
74clib_march_select_fn_ptr (clib_march_fn_registration * r)
75{
76 void *rv = 0;
77 int last_prio = -1;
78
79 while (r)
80 {
81 if (last_prio < r->priority)
82 {
83 last_prio = r->priority;
84 rv = r->function;
85 }
86 r = r->next;
87 }
88 return rv;
89}
90
91#define CLIB_MARCH_FN_POINTER(fn) \
92 clib_march_select_fn_ptr (fn##_march_fn_registrations);
93
94#define _CLIB_MARCH_FN_REGISTRATION(fn) \
95static clib_march_fn_registration \
96CLIB_MARCH_SFX(fn##_march_fn_registration) = \
97{ \
98 .name = CLIB_MARCH_VARIANT_STR \
99}; \
100\
101static void __clib_constructor \
102fn##_march_register () \
103{ \
104 clib_march_fn_registration *r; \
105 r = & CLIB_MARCH_SFX (fn##_march_fn_registration); \
106 r->priority = CLIB_MARCH_FN_PRIORITY(); \
107 r->next = fn##_march_fn_registrations; \
108 r->function = CLIB_MARCH_SFX (fn); \
109 fn##_march_fn_registrations = r; \
110}
111
112#ifdef CLIB_MARCH_VARIANT
113#define CLIB_MARCH_FN_REGISTRATION(fn) \
114extern clib_march_fn_registration *fn##_march_fn_registrations; \
115_CLIB_MARCH_FN_REGISTRATION(fn)
116#else
117#define CLIB_MARCH_FN_REGISTRATION(fn) \
118clib_march_fn_registration *fn##_march_fn_registrations = 0; \
119_CLIB_MARCH_FN_REGISTRATION(fn)
120#endif
Damjan Marion1c80e832016-05-11 23:07:18 +0200121#define foreach_x86_64_flags \
122_ (sse3, 1, ecx, 0) \
123_ (ssse3, 1, ecx, 9) \
124_ (sse41, 1, ecx, 19) \
125_ (sse42, 1, ecx, 20) \
126_ (avx, 1, ecx, 28) \
127_ (avx2, 7, ebx, 5) \
128_ (avx512f, 7, ebx, 16) \
Gabriel Ganne73cb0062017-12-05 14:26:33 +0100129_ (x86_aes, 1, ecx, 25) \
Damjan Marionc0e939b2016-11-12 11:50:01 +0100130_ (sha, 7, ebx, 29) \
131_ (invariant_tsc, 0x80000007, edx, 8)
Damjan Marion1c80e832016-05-11 23:07:18 +0200132
Gabriel Ganne73cb0062017-12-05 14:26:33 +0100133
134#define foreach_aarch64_flags \
135_ (fp, 0) \
136_ (asimd, 1) \
137_ (evtstrm, 2) \
138_ (aarch64_aes, 3) \
139_ (pmull, 4) \
140_ (sha1, 5) \
141_ (sha2, 6) \
142_ (crc32, 7) \
143_ (atomics, 8) \
144_ (fphp, 9) \
145_ (asimdhp, 10) \
146_ (cpuid, 11) \
147_ (asimdrdm, 12) \
148_ (jscvt, 13) \
149_ (fcma, 14) \
150_ (lrcpc, 15) \
151_ (dcpop, 16) \
152_ (sha3, 17) \
153_ (sm3, 18) \
154_ (sm4, 19) \
155_ (asimddp, 20) \
156_ (sha512, 21) \
157_ (sve, 22)
158
Damjan Marion0a78fa12019-01-19 23:45:36 +0100159static inline u32
Damjan Marionee721412019-01-27 17:54:11 +0100160clib_get_current_cpu_id ()
Damjan Marion0a78fa12019-01-19 23:45:36 +0100161{
162 unsigned cpu, node;
163 syscall (__NR_getcpu, &cpu, &node, 0);
164 return cpu;
165}
166
167static inline u32
168clib_get_current_numa_node ()
169{
170 unsigned cpu, node;
171 syscall (__NR_getcpu, &cpu, &node, 0);
172 return node;
173}
174
Christophe Fontaine33e81952016-12-19 14:41:52 +0100175#if defined(__x86_64__)
176#include "cpuid.h"
177
Damjan Marion1c80e832016-05-11 23:07:18 +0200178static inline int
Dave Barachc3799992016-08-15 11:12:27 -0400179clib_get_cpuid (const u32 lev, u32 * eax, u32 * ebx, u32 * ecx, u32 * edx)
Damjan Marion1c80e832016-05-11 23:07:18 +0200180{
181 if ((u32) __get_cpuid_max (0x80000000 & lev, 0) < lev)
182 return 0;
183 if (lev == 7)
Dave Barachc3799992016-08-15 11:12:27 -0400184 __cpuid_count (lev, 0, *eax, *ebx, *ecx, *edx);
Damjan Marion1c80e832016-05-11 23:07:18 +0200185 else
Dave Barachc3799992016-08-15 11:12:27 -0400186 __cpuid (lev, *eax, *ebx, *ecx, *edx);
Damjan Marion1c80e832016-05-11 23:07:18 +0200187 return 1;
188}
189
190
191#define _(flag, func, reg, bit) \
192static inline int \
193clib_cpu_supports_ ## flag() \
194{ \
195 u32 __attribute__((unused)) eax, ebx = 0, ecx = 0, edx = 0; \
196 clib_get_cpuid (func, &eax, &ebx, &ecx, &edx); \
197 \
198 return ((reg & (1 << bit)) != 0); \
199}
Dave Barachc3799992016-08-15 11:12:27 -0400200foreach_x86_64_flags
Damjan Marion1c80e832016-05-11 23:07:18 +0200201#undef _
Gabriel Ganne73cb0062017-12-05 14:26:33 +0100202#else /* __x86_64__ */
Christophe Fontaine33e81952016-12-19 14:41:52 +0100203
204#define _(flag, func, reg, bit) \
205static inline int clib_cpu_supports_ ## flag() { return 0; }
206foreach_x86_64_flags
207#undef _
Gabriel Ganne73cb0062017-12-05 14:26:33 +0100208#endif /* __x86_64__ */
209#if defined(__aarch64__)
210#include <sys/auxv.h>
211#define _(flag, bit) \
212static inline int \
213clib_cpu_supports_ ## flag() \
214{ \
215 unsigned long hwcap = getauxval(AT_HWCAP); \
216 return (hwcap & (1 << bit)); \
217}
218 foreach_aarch64_flags
219#undef _
220#else /* ! __x86_64__ && !__aarch64__ */
221#define _(flag, bit) \
222static inline int clib_cpu_supports_ ## flag() { return 0; }
223 foreach_aarch64_flags
224#undef _
225#endif /* __x86_64__, __aarch64__ */
226/*
227 * aes is the only feature with the same name in both flag lists
228 * handle this by prefixing it with the arch name, and handling it
229 * with the custom function below
230 */
231 static inline int
232clib_cpu_supports_aes ()
233{
234#if defined (__aarch64__)
235 return clib_cpu_supports_x86_aes ();
236#elif defined (__aarch64__)
237 return clib_cpu_supports_aarch64_aes ();
238#else
239 return 0;
Christophe Fontaine33e81952016-12-19 14:41:52 +0100240#endif
Gabriel Ganne73cb0062017-12-05 14:26:33 +0100241}
242
Damjan Marion812b32d2018-05-28 21:26:47 +0200243static inline int
244clib_cpu_march_priority_avx512 ()
245{
246 if (clib_cpu_supports_avx512f ())
247 return 20;
248 return -1;
249}
250
251static inline int
252clib_cpu_march_priority_avx2 ()
253{
254 if (clib_cpu_supports_avx2 ())
255 return 10;
256 return -1;
257}
258
Lijian Zhang2e237212018-09-10 17:13:56 +0800259static inline u32
260clib_cpu_implementer ()
261{
262 char buf[128];
263 static u32 implementer = -1;
264
265 if (-1 != implementer)
266 return implementer;
267
268 FILE *fp = fopen ("/proc/cpuinfo", "r");
269 if (!fp)
270 return implementer;
271
272 while (!feof (fp))
273 {
274 if (!fgets (buf, sizeof (buf), fp))
275 break;
276 buf[127] = '\0';
277 if (strstr (buf, "CPU implementer"))
278 implementer = (u32) strtol (memchr (buf, ':', 128) + 2, NULL, 0);
279 if (-1 != implementer)
280 break;
281 }
282 fclose (fp);
283
284 return implementer;
285}
286
287static inline u32
288clib_cpu_part ()
289{
290 char buf[128];
291 static u32 part = -1;
292
293 if (-1 != part)
294 return part;
295
296 FILE *fp = fopen ("/proc/cpuinfo", "r");
297 if (!fp)
298 return part;
299
300 while (!feof (fp))
301 {
302 if (!fgets (buf, sizeof (buf), fp))
303 break;
304 buf[127] = '\0';
305 if (strstr (buf, "CPU part"))
306 part = (u32) strtol (memchr (buf, ':', 128) + 2, NULL, 0);
307 if (-1 != part)
308 break;
309 }
310 fclose (fp);
311
312 return part;
313}
314
315#define AARCH64_CPU_IMPLEMENTER_THUNERDERX2 0x43
316#define AARCH64_CPU_PART_THUNERDERX2 0x0af
317#define AARCH64_CPU_IMPLEMENTER_QDF24XX 0x51
318#define AARCH64_CPU_PART_QDF24XX 0xc00
319#define AARCH64_CPU_IMPLEMENTER_CORTEXA72 0x41
320#define AARCH64_CPU_PART_CORTEXA72 0xd08
321
322static inline int
323clib_cpu_march_priority_thunderx2t99 ()
324{
325 if ((AARCH64_CPU_IMPLEMENTER_THUNERDERX2 == clib_cpu_implementer ()) &&
326 (AARCH64_CPU_PART_THUNERDERX2 == clib_cpu_part ()))
327 return 20;
328 return -1;
329}
330
331static inline int
332clib_cpu_march_priority_qdf24xx ()
333{
334 if ((AARCH64_CPU_IMPLEMENTER_QDF24XX == clib_cpu_implementer ()) &&
335 (AARCH64_CPU_PART_QDF24XX == clib_cpu_part ()))
336 return 20;
337 return -1;
338}
339
340static inline int
341clib_cpu_march_priority_cortexa72 ()
342{
343 if ((AARCH64_CPU_IMPLEMENTER_CORTEXA72 == clib_cpu_implementer ()) &&
344 (AARCH64_CPU_PART_CORTEXA72 == clib_cpu_part ()))
345 return 10;
346 return -1;
347}
348
Damjan Marion812b32d2018-05-28 21:26:47 +0200349#ifdef CLIB_MARCH_VARIANT
350#define CLIB_MARCH_FN_PRIORITY() CLIB_MARCH_SFX(clib_cpu_march_priority)()
351#else
352#define CLIB_MARCH_FN_PRIORITY() 0
353#endif
Gabriel Ganne73cb0062017-12-05 14:26:33 +0100354#endif /* included_clib_cpu_h */
355
Florin Coras983cc7d2018-09-18 23:11:55 -0700356#define CLIB_MARCH_FN_CONSTRUCTOR(fn) \
357static void __clib_constructor \
358CLIB_MARCH_SFX(fn ## _march_constructor) (void) \
359{ \
360 if (CLIB_MARCH_FN_PRIORITY() > fn ## _selected_priority) \
361 { \
362 fn ## _selected = & CLIB_MARCH_SFX (fn ## _ma); \
363 fn ## _selected_priority = CLIB_MARCH_FN_PRIORITY(); \
364 } \
365} \
366
367#ifndef CLIB_MARCH_VARIANT
368#define CLIB_MARCH_FN(fn, rtype, _args...) \
369 static rtype CLIB_CPU_OPTIMIZED CLIB_MARCH_SFX (fn ## _ma)(_args); \
370 rtype (*fn ## _selected) (_args) = & CLIB_MARCH_SFX (fn ## _ma); \
371 int fn ## _selected_priority = 0; \
372 static inline rtype CLIB_CPU_OPTIMIZED \
373 CLIB_MARCH_SFX (fn ## _ma)(_args)
374#else
375#define CLIB_MARCH_FN(fn, rtype, _args...) \
376 static rtype CLIB_CPU_OPTIMIZED CLIB_MARCH_SFX (fn ## _ma)(_args); \
377 extern int (*fn ## _selected) (_args); \
378 extern int fn ## _selected_priority; \
379 CLIB_MARCH_FN_CONSTRUCTOR (fn) \
380 static rtype CLIB_CPU_OPTIMIZED CLIB_MARCH_SFX (fn ## _ma)(_args)
381#endif
382
383#define CLIB_MARCH_FN_SELECT(fn) (* fn ## _selected)
384
Gabriel Ganne73cb0062017-12-05 14:26:33 +0100385format_function_t format_cpu_uarch;
Damjan Marion522e4862016-03-04 12:44:14 +0100386format_function_t format_cpu_model_name;
Damjan Marion1c80e832016-05-11 23:07:18 +0200387format_function_t format_cpu_flags;
Damjan Marion522e4862016-03-04 12:44:14 +0100388
Dave Barachc3799992016-08-15 11:12:27 -0400389/*
390 * fd.io coding-style-patch-verification: ON
391 *
392 * Local Variables:
393 * eval: (c-set-style "gnu")
394 * End:
395 */