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