1 /*****************************************************************************
2
3 Copyright (c) 1995, 2021, Oracle and/or its affiliates.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation. The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License, version 2.0, 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 Street, Suite 500, Boston, MA 02110-1335 USA
24
25 *****************************************************************************/
26
27 /**************************************************//**
28 @file os/os0proc.cc
29 The interface to the operating system
30 process control primitives
31
32 Created 9/30/1995 Heikki Tuuri
33 *******************************************************/
34
35 #include "ha_prototypes.h"
36
37 #include "os0proc.h"
38 #ifdef UNIV_NONINL
39 #include "os0proc.ic"
40 #endif
41
42 #include "srv0srv.h"
43 #include "ut0mem.h"
44 #include "ut0byte.h"
45
46 /* Linux release version */
47 #if defined(UNIV_LINUX) && defined(_GNU_SOURCE)
48 #include <string.h> /* strverscmp() */
49 #include <sys/utsname.h> /* uname() */
50 #endif
51
52 /* FreeBSD for example has only MAP_ANON, Linux has MAP_ANONYMOUS and
53 MAP_ANON but MAP_ANON is marked as deprecated */
54 #if defined(MAP_ANONYMOUS)
55 #define OS_MAP_ANON MAP_ANONYMOUS
56 #elif defined(MAP_ANON)
57 #define OS_MAP_ANON MAP_ANON
58 #endif
59
60 /* Linux's MAP_POPULATE */
61 #if defined(MAP_POPULATE)
62 #define OS_MAP_POPULATE MAP_POPULATE
63 #else
64 #define OS_MAP_POPULATE 0
65 #endif
66
67 /** The total amount of memory currently allocated from the operating
68 system with os_mem_alloc_large(). */
69 ulint os_total_large_mem_allocated = 0;
70
71 /** Whether to use large pages in the buffer pool */
72 my_bool os_use_large_pages;
73
74 /** Large page size. This may be a boot-time option on some platforms */
75 ulint os_large_page_size;
76
77
78 /****************************************************************//**
79 Retrieve and compare operating system release.
80 @return TRUE if the OS release is equal to, or later than release. */
81 bool
os_compare_release(const char * release MY_ATTRIBUTE ((unused)))82 os_compare_release(
83 /*===============*/
84 const char* release /*!< in: OS release */
85 MY_ATTRIBUTE((unused)))
86 {
87 #if defined(UNIV_LINUX) && defined(_GNU_SOURCE)
88 struct utsname name;
89 return uname(&name) == 0 && strverscmp(name.release, release) >= 0;
90 #else
91 return 0;
92 #endif
93 }
94
95 /** Converts the current process id to a number.
96 @return process id as a number */
97 ulint
os_proc_get_number(void)98 os_proc_get_number(void)
99 /*====================*/
100 {
101 #ifdef _WIN32
102 return(static_cast<ulint>(GetCurrentProcessId()));
103 #else
104 return(static_cast<ulint>(getpid()));
105 #endif
106 }
107
108 /** Allocates large pages memory.
109 @param[in,out] n Number of bytes to allocate
110 @return allocated memory */
111 void*
os_mem_alloc_large(ulint * n,bool populate)112 os_mem_alloc_large(
113 ulint* n,
114 bool populate)
115 {
116 void* ptr;
117 ulint size;
118 #if defined HAVE_LINUX_LARGE_PAGES && defined UNIV_LINUX
119 int shmid;
120 struct shmid_ds buf;
121
122 if (!os_use_large_pages || !os_large_page_size) {
123 goto skip;
124 }
125
126 /* Align block size to os_large_page_size */
127 ut_ad(ut_is_2pow(os_large_page_size));
128 size = ut_2pow_round(*n + (os_large_page_size - 1),
129 os_large_page_size);
130
131 shmid = shmget(IPC_PRIVATE, (size_t) size, SHM_HUGETLB | SHM_R | SHM_W);
132 if (shmid < 0) {
133 ib::warn() << "Failed to allocate " << size
134 << " bytes. errno " << errno;
135 ptr = NULL;
136 } else {
137 ptr = shmat(shmid, NULL, 0);
138 if (ptr == (void*)-1) {
139 ib::warn() << "Failed to attach shared memory segment,"
140 " errno " << errno;
141 ptr = NULL;
142 }
143
144 /* Remove the shared memory segment so that it will be
145 automatically freed after memory is detached or
146 process exits */
147 shmctl(shmid, IPC_RMID, &buf);
148 }
149
150 if (ptr) {
151 *n = size;
152 os_atomic_increment_ulint(
153 &os_total_large_mem_allocated, size);
154
155 UNIV_MEM_ALLOC(ptr, size);
156 return(ptr);
157 }
158
159 ib::warn() << "Using conventional memory pool";
160 skip:
161 #endif /* HAVE_LINUX_LARGE_PAGES && UNIV_LINUX */
162
163 #ifdef _WIN32
164 SYSTEM_INFO system_info;
165 GetSystemInfo(&system_info);
166
167 /* Align block size to system page size */
168 ut_ad(ut_is_2pow(system_info.dwPageSize));
169 /* system_info.dwPageSize is only 32-bit. Casting to ulint is required
170 on 64-bit Windows. */
171 size = *n = ut_2pow_round(*n + (system_info.dwPageSize - 1),
172 (ulint) system_info.dwPageSize);
173 ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE,
174 PAGE_READWRITE);
175 if (!ptr) {
176 ib::info() << "VirtualAlloc(" << size << " bytes) failed;"
177 " Windows error " << GetLastError();
178 } else {
179 os_atomic_increment_ulint(
180 &os_total_large_mem_allocated, size);
181 UNIV_MEM_ALLOC(ptr, size);
182 }
183 #else
184 size = getpagesize();
185 /* Align block size to system page size */
186 ut_ad(ut_is_2pow(size));
187 size = *n = ut_2pow_round(*n + (size - 1), size);
188 ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
189 MAP_PRIVATE | OS_MAP_ANON |
190 (populate ? OS_MAP_POPULATE : 0), -1, 0);
191 if (UNIV_UNLIKELY(ptr == (void*) -1)) {
192 ib::error() << "mmap(" << size << " bytes) failed;"
193 " errno " << errno;
194 return NULL;
195 } else {
196 os_atomic_increment_ulint(
197 &os_total_large_mem_allocated, size);
198 UNIV_MEM_ALLOC(ptr, size);
199 }
200 #endif
201
202 #if OS_MAP_ANON && OS_MAP_POPULATE
203 /* MAP_POPULATE is only supported for private mappings
204 since Linux 2.6.23. */
205 populate = populate && !os_compare_release("2.6.23");
206
207 if (populate) {
208 ib::warn() << "mmap(MAP_POPULATE) is not supported for private mappings. "
209 "Forcing preallocation by faulting in pages.";
210 }
211 #endif
212
213 /* Initialize the entire buffer to force the allocation
214 of physical memory page frames. */
215 if (populate) {
216 memset(ptr, '\0', size);
217 }
218
219 return(ptr);
220 }
221
222 /** Frees large pages memory.
223 @param[in] ptr pointer returned by os_mem_alloc_large()
224 @param[in] size size returned by os_mem_alloc_large() */
225 void
os_mem_free_large(void * ptr,ulint size)226 os_mem_free_large(
227 void *ptr,
228 ulint size)
229 {
230 ut_a(os_total_large_mem_allocated >= size);
231
232 #if defined HAVE_LINUX_LARGE_PAGES && defined UNIV_LINUX
233 if (os_use_large_pages && os_large_page_size && !shmdt(ptr)) {
234 os_atomic_decrement_ulint(
235 &os_total_large_mem_allocated, size);
236 UNIV_MEM_FREE(ptr, size);
237 return;
238 }
239 #endif /* HAVE_LINUX_LARGE_PAGES && UNIV_LINUX */
240 #ifdef _WIN32
241 /* When RELEASE memory, the size parameter must be 0.
242 Do not use MEM_RELEASE with MEM_DECOMMIT. */
243 if (!VirtualFree(ptr, 0, MEM_RELEASE)) {
244 ib::error() << "VirtualFree(" << ptr << ", " << size
245 << ") failed; Windows error " << GetLastError();
246 } else {
247 os_atomic_decrement_ulint(
248 &os_total_large_mem_allocated, size);
249 UNIV_MEM_FREE(ptr, size);
250 }
251 #elif !defined OS_MAP_ANON
252 ut_free(ptr);
253 #else
254 # if defined(UNIV_SOLARIS)
255 if (munmap(static_cast<caddr_t>(ptr), size)) {
256 # else
257 if (munmap(ptr, size)) {
258 # endif /* UNIV_SOLARIS */
259 ib::error() << "munmap(" << ptr << ", " << size << ") failed;"
260 " errno " << errno;
261 } else {
262 os_atomic_decrement_ulint(
263 &os_total_large_mem_allocated, size);
264 UNIV_MEM_FREE(ptr, size);
265 }
266 #endif
267 }
268