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