1 /*****************************************************************************
2
3 Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved.
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 "os0proc.h"
36 #ifdef UNIV_NONINL
37 #include "os0proc.ic"
38 #endif
39
40 #include "ut0mem.h"
41 #include "ut0byte.h"
42
43 /* Linux release version */
44 #if defined(UNIV_LINUX) && defined(_GNU_SOURCE)
45 #include <string.h> /* strverscmp() */
46 #include <sys/utsname.h> /* uname() */
47 #endif
48
49 #include "ha_prototypes.h"
50
51 /* FreeBSD for example has only MAP_ANON, Linux has MAP_ANONYMOUS and
52 MAP_ANON but MAP_ANON is marked as deprecated */
53 #if defined(MAP_ANONYMOUS)
54 #define OS_MAP_ANON MAP_ANONYMOUS
55 #elif defined(MAP_ANON)
56 #define OS_MAP_ANON MAP_ANON
57 #endif
58
59 /* Linux's MAP_POPULATE */
60 #if defined(MAP_POPULATE)
61 #define OS_MAP_POPULATE MAP_POPULATE
62 #else
63 #define OS_MAP_POPULATE 0
64 #endif
65
66 UNIV_INTERN ibool os_use_large_pages;
67 /* Large page size. This may be a boot-time option on some platforms */
68 UNIV_INTERN ulint os_large_page_size;
69
70
71 /****************************************************************//**
72 Retrieve and compare operating system release.
73 @return TRUE if the OS release is equal to, or later than release. */
74 UNIV_INTERN
75 bool
os_compare_release(const char * release MY_ATTRIBUTE ((unused)))76 os_compare_release(
77 /*===============*/
78 const char* release /*!< in: OS release */
79 MY_ATTRIBUTE((unused)))
80 {
81 #if defined(UNIV_LINUX) && defined(_GNU_SOURCE)
82 struct utsname name;
83 return uname(&name) == 0 && strverscmp(name.release, release) >= 0;
84 #else
85 return false;
86 #endif
87 }
88
89 /****************************************************************//**
90 Converts the current process id to a number. It is not guaranteed that the
91 number is unique. In Linux returns the 'process number' of the current
92 thread. That number is the same as one sees in 'top', for example. In Linux
93 the thread id is not the same as one sees in 'top'.
94 @return process id as a number */
95 UNIV_INTERN
96 ulint
os_proc_get_number(void)97 os_proc_get_number(void)
98 /*====================*/
99 {
100 #ifdef __WIN__
101 return((ulint)GetCurrentProcessId());
102 #else
103 return((ulint) getpid());
104 #endif
105 }
106
107 /****************************************************************//**
108 Allocates large pages memory.
109 @return allocated memory */
110 UNIV_INTERN
111 void*
os_mem_alloc_large(ulint * n,bool populate)112 os_mem_alloc_large(
113 /*===============*/
114 ulint* n, /*!< in/out: number of bytes */
115 bool populate) /*!< in: virtual page preallocation */
116 {
117 void* ptr;
118 ulint size;
119 #if defined HAVE_LARGE_PAGES && defined UNIV_LINUX
120 int shmid;
121 struct shmid_ds buf;
122
123 if (!os_use_large_pages || !os_large_page_size) {
124 goto skip;
125 }
126
127 /* Align block size to os_large_page_size */
128 ut_ad(ut_is_2pow(os_large_page_size));
129 size = ut_2pow_round(*n + (os_large_page_size - 1),
130 os_large_page_size);
131
132 shmid = shmget(IPC_PRIVATE, (size_t) size, SHM_HUGETLB | SHM_R | SHM_W);
133 if (shmid < 0) {
134 fprintf(stderr, "InnoDB: HugeTLB: Warning: Failed to allocate"
135 " %lu bytes. errno %d\n", size, errno);
136 ptr = NULL;
137 } else {
138 ptr = shmat(shmid, NULL, 0);
139 if (ptr == (void*)-1) {
140 fprintf(stderr, "InnoDB: HugeTLB: Warning: Failed to"
141 " attach shared memory segment, errno %d\n",
142 errno);
143 ptr = NULL;
144 }
145
146 /* Remove the shared memory segment so that it will be
147 automatically freed after memory is detached or
148 process exits */
149 shmctl(shmid, IPC_RMID, &buf);
150 }
151
152 if (ptr) {
153 *n = size;
154 os_fast_mutex_lock(&ut_list_mutex);
155 ut_total_allocated_memory += size;
156 os_fast_mutex_unlock(&ut_list_mutex);
157 UNIV_MEM_ALLOC(ptr, size);
158 return(ptr);
159 }
160
161 fprintf(stderr, "InnoDB HugeTLB: Warning: Using conventional"
162 " memory pool\n");
163 skip:
164 #endif /* HAVE_LARGE_PAGES && UNIV_LINUX */
165
166 #ifdef __WIN__
167 SYSTEM_INFO system_info;
168 GetSystemInfo(&system_info);
169
170 /* Align block size to system page size */
171 ut_ad(ut_is_2pow(system_info.dwPageSize));
172 /* system_info.dwPageSize is only 32-bit. Casting to ulint is required
173 on 64-bit Windows. */
174 size = *n = ut_2pow_round(*n + (system_info.dwPageSize - 1),
175 (ulint) system_info.dwPageSize);
176 ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE,
177 PAGE_READWRITE);
178 if (!ptr) {
179 fprintf(stderr, "InnoDB: VirtualAlloc(%lu bytes) failed;"
180 " Windows error %lu\n",
181 (ulong) size, (ulong) GetLastError());
182 } else {
183 os_fast_mutex_lock(&ut_list_mutex);
184 ut_total_allocated_memory += size;
185 os_fast_mutex_unlock(&ut_list_mutex);
186 UNIV_MEM_ALLOC(ptr, size);
187 }
188 #elif !defined OS_MAP_ANON
189 size = *n;
190 ptr = ut_malloc_low(size, TRUE, FALSE);
191 #else
192 # ifdef HAVE_GETPAGESIZE
193 size = getpagesize();
194 # else
195 size = UNIV_PAGE_SIZE;
196 # endif
197 /* Align block size to system page size */
198 ut_ad(ut_is_2pow(size));
199 size = *n = ut_2pow_round(*n + (size - 1), size);
200 ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
201 MAP_PRIVATE | OS_MAP_ANON |
202 (populate ? OS_MAP_POPULATE : 0), -1, 0);
203 if (UNIV_UNLIKELY(ptr == MAP_FAILED)) {
204 fprintf(stderr, "InnoDB: mmap(%lu bytes) failed;"
205 " errno %lu\n",
206 (ulong) size, (ulong) errno);
207 return NULL;
208 } else {
209 os_fast_mutex_lock(&ut_list_mutex);
210 ut_total_allocated_memory += size;
211 os_fast_mutex_unlock(&ut_list_mutex);
212 UNIV_MEM_ALLOC(ptr, size);
213 }
214 #endif
215
216 #if OS_MAP_ANON && OS_MAP_POPULATE
217 /* MAP_POPULATE is only supported for private mappings
218 since Linux 2.6.23. */
219 populate = populate && !os_compare_release("2.6.23");
220
221 if (populate) {
222 ib_logf(IB_LOG_LEVEL_WARN, "InnoDB: Warning: mmap(MAP_POPULATE) "
223 "is not supported for private mappings. "
224 "Forcing preallocation by faulting in pages.\n");
225 }
226 #endif
227
228 /* Initialize the entire buffer to force the allocation
229 of physical memory page frames. */
230 if (populate) {
231 memset(ptr, '\0', size);
232 }
233
234 return(ptr);
235 }
236
237 /****************************************************************//**
238 Frees large pages memory. */
239 UNIV_INTERN
240 void
os_mem_free_large(void * ptr,ulint size)241 os_mem_free_large(
242 /*==============*/
243 void *ptr, /*!< in: pointer returned by
244 os_mem_alloc_large() */
245 ulint size) /*!< in: size returned by
246 os_mem_alloc_large() */
247 {
248 os_fast_mutex_lock(&ut_list_mutex);
249 ut_a(ut_total_allocated_memory >= size);
250 os_fast_mutex_unlock(&ut_list_mutex);
251
252 #if defined HAVE_LARGE_PAGES && defined UNIV_LINUX
253 if (os_use_large_pages && os_large_page_size && !shmdt(ptr)) {
254 os_fast_mutex_lock(&ut_list_mutex);
255 ut_a(ut_total_allocated_memory >= size);
256 ut_total_allocated_memory -= size;
257 os_fast_mutex_unlock(&ut_list_mutex);
258 UNIV_MEM_FREE(ptr, size);
259 return;
260 }
261 #endif /* HAVE_LARGE_PAGES && UNIV_LINUX */
262 #ifdef __WIN__
263 /* When RELEASE memory, the size parameter must be 0.
264 Do not use MEM_RELEASE with MEM_DECOMMIT. */
265 if (!VirtualFree(ptr, 0, MEM_RELEASE)) {
266 fprintf(stderr, "InnoDB: VirtualFree(%p, %lu) failed;"
267 " Windows error %lu\n",
268 ptr, (ulong) size, (ulong) GetLastError());
269 } else {
270 os_fast_mutex_lock(&ut_list_mutex);
271 ut_a(ut_total_allocated_memory >= size);
272 ut_total_allocated_memory -= size;
273 os_fast_mutex_unlock(&ut_list_mutex);
274 UNIV_MEM_FREE(ptr, size);
275 }
276 #elif !defined OS_MAP_ANON
277 ut_free(ptr);
278 #else
279 # if defined(UNIV_SOLARIS)
280 if (munmap(static_cast<caddr_t>(ptr), size)) {
281 # else
282 if (munmap(ptr, size)) {
283 # endif /* UNIV_SOLARIS */
284 fprintf(stderr, "InnoDB: munmap(%p, %lu) failed;"
285 " errno %lu\n",
286 ptr, (ulong) size, (ulong) errno);
287 } else {
288 os_fast_mutex_lock(&ut_list_mutex);
289 ut_a(ut_total_allocated_memory >= size);
290 ut_total_allocated_memory -= size;
291 os_fast_mutex_unlock(&ut_list_mutex);
292 UNIV_MEM_FREE(ptr, size);
293 }
294 #endif
295 }
296