1 /*****************************************************************************
2 
3 Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2019, MariaDB Corporation.
5 
6 This program is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free Software
8 Foundation; version 2 of the License.
9 
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17 
18 *****************************************************************************/
19 
20 /**************************************************//**
21 @file os/os0proc.cc
22 The interface to the operating system
23 process control primitives
24 
25 Created 9/30/1995 Heikki Tuuri
26 *******************************************************/
27 
28 #include "univ.i"
29 #ifdef HAVE_LINUX_LARGE_PAGES
30 # include "mysqld.h"
31 #endif
32 #include "my_valgrind.h"
33 
34 /* FreeBSD for example has only MAP_ANON, Linux has MAP_ANONYMOUS and
35 MAP_ANON but MAP_ANON is marked as deprecated */
36 #if defined(MAP_ANONYMOUS)
37 #define OS_MAP_ANON	MAP_ANONYMOUS
38 #elif defined(MAP_ANON)
39 #define OS_MAP_ANON	MAP_ANON
40 #endif
41 
42 /** The total amount of memory currently allocated from the operating
43 system with os_mem_alloc_large(). */
44 ulint	os_total_large_mem_allocated = 0;
45 
46 /** Converts the current process id to a number.
47 @return process id as a number */
48 ulint
os_proc_get_number(void)49 os_proc_get_number(void)
50 /*====================*/
51 {
52 #ifdef _WIN32
53 	return(static_cast<ulint>(GetCurrentProcessId()));
54 #else
55 	return(static_cast<ulint>(getpid()));
56 #endif
57 }
58 
59 /** Allocates large pages memory.
60 @param[in,out]	n	Number of bytes to allocate
61 @return allocated memory */
62 void*
os_mem_alloc_large(ulint * n)63 os_mem_alloc_large(
64 	ulint*	n)
65 {
66 	void*	ptr;
67 	ulint	size;
68 #ifdef HAVE_LINUX_LARGE_PAGES
69 	int shmid;
70 	struct shmid_ds buf;
71 
72 	if (!my_use_large_pages || !opt_large_page_size) {
73 		goto skip;
74 	}
75 
76 	/* Align block size to opt_large_page_size */
77 	ut_ad(ut_is_2pow(opt_large_page_size));
78 	size = ut_2pow_round(*n + opt_large_page_size - 1,
79 			     ulint(opt_large_page_size));
80 
81 	shmid = shmget(IPC_PRIVATE, (size_t) size, SHM_HUGETLB | SHM_R | SHM_W);
82 	if (shmid < 0) {
83 		ib::warn() << "Failed to allocate " << size
84 			<< " bytes. errno " << errno;
85 		ptr = NULL;
86 	} else {
87 		ptr = shmat(shmid, NULL, 0);
88 		if (ptr == (void*)-1) {
89 			ib::warn() << "Failed to attach shared memory segment,"
90 				" errno " << errno;
91 			ptr = NULL;
92 		}
93 
94 		/* Remove the shared memory segment so that it will be
95 		automatically freed after memory is detached or
96 		process exits */
97 		shmctl(shmid, IPC_RMID, &buf);
98 	}
99 
100 	if (ptr) {
101 		*n = size;
102 		my_atomic_addlint(
103 			&os_total_large_mem_allocated, size);
104 
105 		MEM_UNDEFINED(ptr, size);
106 		return(ptr);
107 	}
108 
109 	ib::warn() << "Using conventional memory pool";
110 skip:
111 #endif /* HAVE_LINUX_LARGE_PAGES */
112 
113 #ifdef _WIN32
114 	SYSTEM_INFO	system_info;
115 	GetSystemInfo(&system_info);
116 
117 	/* Align block size to system page size */
118 	ut_ad(ut_is_2pow(system_info.dwPageSize));
119 	size = *n = ut_2pow_round<ulint>(*n + (system_info.dwPageSize - 1),
120 					 system_info.dwPageSize);
121 	ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE,
122 			   PAGE_READWRITE);
123 	if (!ptr) {
124 		ib::info() << "VirtualAlloc(" << size << " bytes) failed;"
125 			" Windows error " << GetLastError();
126 	} else {
127 		my_atomic_addlint(
128 			&os_total_large_mem_allocated, size);
129 		MEM_UNDEFINED(ptr, size);
130 	}
131 #else
132 	size = getpagesize();
133 	/* Align block size to system page size */
134 	ut_ad(ut_is_2pow(size));
135 	size = *n = ut_2pow_round(*n + (size - 1), size);
136 	ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
137 		   MAP_PRIVATE | OS_MAP_ANON, -1, 0);
138 	if (UNIV_UNLIKELY(ptr == (void*) -1)) {
139 		ib::error() << "mmap(" << size << " bytes) failed;"
140 			" errno " << errno;
141 		ptr = NULL;
142 	} else {
143 		my_atomic_addlint(
144 			&os_total_large_mem_allocated, size);
145 		MEM_UNDEFINED(ptr, size);
146 	}
147 #endif
148 	return(ptr);
149 }
150 
151 /** Frees large pages memory.
152 @param[in]	ptr	pointer returned by os_mem_alloc_large()
153 @param[in]	size	size returned by os_mem_alloc_large() */
154 void
os_mem_free_large(void * ptr,ulint size)155 os_mem_free_large(
156 	void	*ptr,
157 	ulint	size)
158 {
159 	ut_a(os_total_large_mem_allocated >= size);
160 
161 #ifdef __SANITIZE_ADDRESS__
162 	// We could have manually poisoned that memory for ASAN.
163 	// And we must unpoison it by ourself as specified in documentation
164 	// for __asan_poison_memory_region() in sanitizer/asan_interface.h
165 	// munmap() doesn't do it for us automatically.
166 	MEM_MAKE_ADDRESSABLE(ptr, size);
167 #endif /* __SANITIZE_ADDRESS__ */
168 
169 #ifdef HAVE_LINUX_LARGE_PAGES
170 	if (my_use_large_pages && opt_large_page_size && !shmdt(ptr)) {
171 		my_atomic_addlint(
172 			&os_total_large_mem_allocated, -size);
173 		return;
174 	}
175 #endif /* HAVE_LINUX_LARGE_PAGES */
176 #ifdef _WIN32
177 	/* When RELEASE memory, the size parameter must be 0.
178 	Do not use MEM_RELEASE with MEM_DECOMMIT. */
179 	if (!VirtualFree(ptr, 0, MEM_RELEASE)) {
180 		ib::error() << "VirtualFree(" << ptr << ", " << size
181 			<< ") failed; Windows error " << GetLastError();
182 	} else {
183 		my_atomic_addlint(
184 			&os_total_large_mem_allocated, -lint(size));
185 	}
186 #elif !defined OS_MAP_ANON
187 	ut_free(ptr);
188 #else
189 # if defined(UNIV_SOLARIS)
190 	if (munmap(static_cast<caddr_t>(ptr), size)) {
191 # else
192 	if (munmap(ptr, size)) {
193 # endif /* UNIV_SOLARIS */
194 		ib::error() << "munmap(" << ptr << ", " << size << ") failed;"
195 			" errno " << errno;
196 	} else {
197 		my_atomic_addlint(
198 			&os_total_large_mem_allocated, -size);
199 	}
200 #endif
201 }
202