1 /*****************************************************************************
2 
3 Copyright (c) 2015, 2018, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8 
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15 
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 
25 *****************************************************************************/
26 
27 /** @file include/os0numa.h
28  NUMA API wrapper over various operating system specific APIs.
29 
30  The os_numa*() functions in this file mimic the numa*() Linux API that is
31  documented in numa(3). They take the same arguments, have the same return
32  type and behave in the same way. There are two purposes behind this:
33  1. Have zero learning curve for developers already familiar with the Linux API.
34  2. Linux's numa*() functions are documented in more detail than ours
35     os_numa*(). Should any doubt arise about the behavior, the Linux docs should
36     be referred.
37 
38  Created Jul 16, 2015 Vasil Dimov
39  *******************************************************/
40 
41 #ifndef os0numa_h
42 #define os0numa_h
43 
44 #include "univ.i"
45 
46 #ifdef HAVE_LIBNUMA
47 #include <numa.h>
48 #endif /* HAVE_LIBNUMA */
49 
50 #ifdef HAVE_SCHED_GETCPU
51 #include <utmpx.h>
52 #endif /* HAVE_SCHED_GETCPU */
53 
54 #ifdef _WIN32
55 
56 /* https://msdn.microsoft.com/en-us/library/windows/desktop/dd405494(v=vs.85).aspx
57  */
58 #define _WIN32_WINNT 0x0601
59 #include <WinBase.h>
60 
61 #define HAVE_WINNUMA
62 
63 #endif
64 
65 /** Check if NUMA is available. This function must be called before any
66 other os_numa_*() functions and it must return != -1, otherwise the behavior
67 of the rest of the functions is undefined.
68 @return != -1 if available. */
os_numa_available()69 inline int os_numa_available() {
70 #if defined(HAVE_LIBNUMA)
71   return (numa_available());
72 #elif defined(HAVE_WINNUMA)
73   /* See this page for a description of the NUMA Windows API:
74   "NUMA Support"
75   https://msdn.microsoft.com/en-us/library/windows/desktop/aa363804(v=vs.85).aspx
76   */
77   ULONG highest_node;
78 
79   if (!GetNumaHighestNodeNumber(&highest_node)) {
80     return (-1);
81   }
82 
83   if (highest_node > 0) {
84     return (1);
85   } else {
86     return (-1);
87   }
88 #else
89   return (-1);
90 #endif /* HAVE_LIBNUMA */
91 }
92 
93 /** Get the number of CPUs in the system, including disabled ones.
94 @return number of CPUs */
os_numa_num_configured_cpus()95 inline int os_numa_num_configured_cpus() {
96 #if defined(HAVE_LIBNUMA)
97   return (numa_num_configured_cpus());
98 #elif defined(HAVE_WINNUMA)
99   SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buf;
100   DWORD buf_bytes = 0;
101 
102   if (GetLogicalProcessorInformationEx(RelationGroup, NULL, &buf_bytes)) {
103     /* GetLogicalProcessorInformationEx() unexpectedly succeeded. */
104     return (1);
105   }
106 
107   if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
108     /* GetLogicalProcessorInformationEx() failed with unexpected
109     error code. */
110     return (1);
111   }
112 
113   /* Now 'buf_bytes' contains the necessary size of buf (in bytes!). */
114 
115   buf = reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *>(
116       LocalAlloc(LMEM_FIXED, buf_bytes));
117 
118   if (buf == NULL) {
119     return (1);
120   }
121 
122   if (!GetLogicalProcessorInformationEx(RelationGroup, buf, &buf_bytes)) {
123     /* GetLogicalProcessorInformationEx() unexpectedly failed. */
124     LocalFree(buf);
125     return (1);
126   }
127 
128   int n_cpus = 0;
129   SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buf_orig = buf;
130 
131   /* Maybe this loop will iterate just once, but this is not mentioned
132   explicitly anywhere in the GetLogicalProcessorInformationEx()
133   documentation (when the first argument is RelationGroup). If we are
134   sure that it will iterate just once, then this code could be
135   simplified. */
136   for (DWORD offset = 0; offset < buf_bytes;) {
137     for (WORD i = 0; i < buf->Group.ActiveGroupCount; i++) {
138       n_cpus += buf->Group.GroupInfo[i].ActiveProcessorCount;
139     }
140 
141     offset += buf->Size;
142     buf = reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *>(
143         reinterpret_cast<char *>(buf) + buf->Size);
144   }
145 
146   LocalFree(buf_orig);
147 
148   return (n_cpus);
149 #else
150   /* Consider
151   boost::thread::hardware_concurrency() or
152   std::thread::hardware_concurrency() (C++11) */
153   ut_error;
154   return (-1);
155 #endif
156 }
157 
158 /** Get the NUMA node of a given CPU.
159 @param[in]	cpu	CPU whose NUMA node to return, must be obtained
160 using os_getcpu().
161 @return NUMA node id */
os_numa_node_of_cpu(int cpu)162 inline int os_numa_node_of_cpu(int cpu) {
163 #if defined(HAVE_LIBNUMA)
164   return (numa_node_of_cpu(cpu));
165 #elif defined(HAVE_WINNUMA)
166   PROCESSOR_NUMBER p;
167   USHORT node;
168 
169   p.Group = cpu >> 6;
170   p.Number = cpu & 63;
171 
172   if (GetNumaProcessorNodeEx(&p, &node)) {
173     return (static_cast<int>(node));
174   } else {
175     return (0);
176   }
177 #else
178   ut_error;
179   return (-1);
180 #endif
181 }
182 
183 /** Allocate a memory on a given NUMA node.
184 @param[in]	size	number of bytes to allocate
185 @param[in]	node	NUMA node on which to allocate the memory
186 @return pointer to allocated memory or NULL if allocation failed */
os_numa_alloc_onnode(size_t size,int node)187 inline void *os_numa_alloc_onnode(size_t size, int node) {
188 #if defined(HAVE_LIBNUMA)
189   return (numa_alloc_onnode(size, node));
190 #elif defined(HAVE_WINNUMA)
191   return (VirtualAllocExNuma(GetCurrentProcess(), NULL, size,
192                              MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, node));
193 #else
194   ut_error;
195   return (NULL);
196 #endif
197 }
198 
199 /** Free a memory allocated by os_numa_alloc_onnode().
200 @param[in]	ptr	pointer to memory to free
201 @param[in]	size	size of the memory */
os_numa_free(void * ptr,size_t size)202 inline void os_numa_free(void *ptr, size_t size) {
203 #if defined(HAVE_LIBNUMA)
204   numa_free(ptr, size);
205 #elif defined(HAVE_WINNUMA)
206   VirtualFreeEx(GetCurrentProcess(), ptr, 0, MEM_DECOMMIT | MEM_RELEASE);
207 #else
208   ut_error;
209 #endif
210 }
211 
212 #if defined(HAVE_SCHED_GETCPU) || defined(HAVE_WINNUMA)
213 
214 #define HAVE_OS_GETCPU
215 
216 /** Get the number of the CPU that executes the current thread now.
217 @return CPU number */
os_getcpu()218 inline int os_getcpu() {
219 #if defined(HAVE_SCHED_GETCPU)
220   return (sched_getcpu());
221 #elif defined(HAVE_WINNUMA)
222   PROCESSOR_NUMBER p;
223 
224   GetCurrentProcessorNumberEx(&p);
225 
226   return (static_cast<int>(p.Group << 6 | p.Number));
227 #endif
228 }
229 #endif /* HAVE_SCHED_GETCPU || HAVE_WINNUMA */
230 
231 #endif /* os0numa_h */
232