Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
3 : : * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
4 : : *
5 : : * This file is part of LVM2.
6 : : *
7 : : * This copyrighted material is made available to anyone wishing to use,
8 : : * modify, copy, or redistribute it subject to the terms and conditions
9 : : * of the GNU Lesser General Public License v.2.1.
10 : : *
11 : : * You should have received a copy of the GNU Lesser General Public License
12 : : * along with this program; if not, write to the Free Software Foundation,
13 : : * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 : : */
15 : :
16 : : #include "lib.h"
17 : : #include "memlock.h"
18 : : #include "defaults.h"
19 : : #include "config.h"
20 : : #include "toolcontext.h"
21 : :
22 : : #include <limits.h>
23 : : #include <fcntl.h>
24 : : #include <unistd.h>
25 : : #include <sys/mman.h>
26 : : #include <sys/time.h>
27 : : #include <sys/resource.h>
28 : :
29 : : #ifndef DEVMAPPER_SUPPORT
30 : :
31 : : void memlock_inc(struct cmd_context *cmd)
32 : : {
33 : : return;
34 : : }
35 : : void memlock_dec(struct cmd_context *cmd)
36 : : {
37 : : return;
38 : : }
39 : : int memlock(void)
40 : : {
41 : : return 0;
42 : : }
43 : : void memlock_init(struct cmd_context *cmd)
44 : : {
45 : : return;
46 : : }
47 : :
48 : : #else /* DEVMAPPER_SUPPORT */
49 : :
50 : : static size_t _size_stack;
51 : : static size_t _size_malloc_tmp;
52 : : static size_t _size_malloc = 2000000;
53 : :
54 : : static void *_malloc_mem = NULL;
55 : : static int _memlock_count = 0;
56 : : static int _memlock_count_daemon = 0;
57 : : static int _priority;
58 : : static int _default_priority;
59 : :
60 : : /* list of maps, that are unconditionaly ignored */
61 : : static const char * const _ignore_maps[] = {
62 : : "[vdso]",
63 : : "[vsyscall]",
64 : : };
65 : :
66 : : /* default blacklist for maps */
67 : : static const char * const _blacklist_maps[] = {
68 : : "locale/locale-archive",
69 : : "gconv/gconv-modules.cache",
70 : : "/libreadline.so.", /* not using readline during mlock */
71 : : "/libncurses.so.", /* not using readline during mlock */
72 : : "/libdl-", /* not using dlopen,dlsym during mlock */
73 : : /* "/libdevmapper-event.so" */
74 : : };
75 : :
76 : : typedef enum { LVM_MLOCK, LVM_MUNLOCK } lvmlock_t;
77 : :
78 : : static unsigned _use_mlockall;
79 : : static FILE *_mapsh;
80 : : static char _procselfmaps[PATH_MAX] = "";
81 : : #define SELF_MAPS "/self/maps"
82 : :
83 : : static size_t _mstats; /* statistic for maps locking */
84 : :
85 : 0 : static void _touch_memory(void *mem, size_t size)
86 : : {
87 : 0 : size_t pagesize = lvm_getpagesize();
88 : 0 : void *pos = mem;
89 : 0 : void *end = mem + size - sizeof(long);
90 : :
91 [ # # ]: 0 : while (pos < end) {
92 : 0 : *(long *) pos = 1;
93 : 0 : pos += pagesize;
94 : : }
95 : 0 : }
96 : :
97 : 0 : static void _allocate_memory(void)
98 : : {
99 : : void *stack_mem, *temp_malloc_mem;
100 : :
101 : 0 : if ((stack_mem = alloca(_size_stack)))
102 : 0 : _touch_memory(stack_mem, _size_stack);
103 : :
104 [ # # ]: 0 : if ((temp_malloc_mem = malloc(_size_malloc_tmp)))
105 : 0 : _touch_memory(temp_malloc_mem, _size_malloc_tmp);
106 : :
107 [ # # ]: 0 : if ((_malloc_mem = malloc(_size_malloc)))
108 : 0 : _touch_memory(_malloc_mem, _size_malloc);
109 : :
110 : 0 : free(temp_malloc_mem);
111 : 0 : }
112 : :
113 : 0 : static void _release_memory(void)
114 : : {
115 : 0 : free(_malloc_mem);
116 : 0 : }
117 : :
118 : : /*
119 : : * mlock/munlock memory areas from /proc/self/maps
120 : : * format described in kernel/Documentation/filesystem/proc.txt
121 : : */
122 : 0 : static int _maps_line(struct cmd_context *cmd, lvmlock_t lock,
123 : : const char* line, size_t* mstats)
124 : : {
125 : : const struct config_node *cn;
126 : : struct config_value *cv;
127 : : long from, to;
128 : : int pos, i;
129 : : char fr, fw, fx, fp;
130 : : size_t sz;
131 : :
132 [ # # ]: 0 : if (sscanf(line, "%lx-%lx %c%c%c%c%n",
133 : : &from, &to, &fr, &fw, &fx, &fp, &pos) != 6) {
134 : 0 : log_error("Failed to parse maps line: %s", line);
135 : 0 : return 0;
136 : : }
137 : :
138 : : /* Select readable maps */
139 [ # # ]: 0 : if (fr != 'r') {
140 : 0 : log_debug("mlock area unreadable '%s': Skipping.", line);
141 : 0 : return 1;
142 : : }
143 : :
144 : : /* always ignored areas */
145 [ # # ]: 0 : for (i = 0; i < sizeof(_ignore_maps) / sizeof(_ignore_maps[0]); ++i)
146 [ # # ]: 0 : if (strstr(line + pos, _ignore_maps[i])) {
147 : 0 : log_debug("mlock ignore filter '%s' matches '%s': Skipping.",
148 : : _ignore_maps[i], line);
149 : 0 : return 1;
150 : : }
151 : :
152 : 0 : sz = to - from;
153 [ # # ]: 0 : if (!(cn = find_config_tree_node(cmd, "activation/mlock_filter"))) {
154 : : /* If no blacklist configured, use an internal set */
155 [ # # ]: 0 : for (i = 0; i < sizeof(_blacklist_maps) / sizeof(_blacklist_maps[0]); ++i)
156 [ # # ]: 0 : if (strstr(line + pos, _blacklist_maps[i])) {
157 : 0 : log_debug("mlock default filter '%s' matches '%s': Skipping.",
158 : : _blacklist_maps[i], line);
159 : 0 : return 1;
160 : : }
161 : : } else {
162 [ # # ]: 0 : for (cv = cn->v; cv; cv = cv->next) {
163 [ # # ][ # # ]: 0 : if ((cv->type != CFG_STRING) || !cv->v.str[0])
164 : 0 : continue;
165 [ # # ]: 0 : if (strstr(line + pos, cv->v.str)) {
166 : 0 : log_debug("mlock_filter '%s' matches '%s': Skipping.",
167 : : cv->v.str, line);
168 : 0 : return 1;
169 : : }
170 : : }
171 : : }
172 : :
173 : 0 : *mstats += sz;
174 [ # # ]: 0 : log_debug("%s %10ldKiB %12lx - %12lx %c%c%c%c %s",
175 : : (lock == LVM_MLOCK) ? "mlock" : "munlock",
176 : : ((long)sz + 1023) / 1024, from, to, fr, fw, fx, fp, line + pos);
177 : :
178 [ # # ]: 0 : if (lock == LVM_MLOCK) {
179 [ # # ]: 0 : if (mlock((const void*)from, sz) < 0) {
180 : 0 : log_sys_error("mlock", line);
181 : 0 : return 0;
182 : : }
183 : : } else {
184 [ # # ]: 0 : if (munlock((const void*)from, sz) < 0) {
185 : 0 : log_sys_error("munlock", line);
186 : 0 : return 0;
187 : : }
188 : : }
189 : :
190 : 0 : return 1;
191 : : }
192 : :
193 : 0 : static int _memlock_maps(struct cmd_context *cmd, lvmlock_t lock, size_t *mstats)
194 : : {
195 : 0 : char *line = NULL;
196 : : size_t len;
197 : : ssize_t n;
198 : 0 : int ret = 1;
199 : :
200 [ # # ]: 0 : if (_use_mlockall) {
201 : : #ifdef MCL_CURRENT
202 [ # # ]: 0 : if (lock == LVM_MLOCK) {
203 [ # # ]: 0 : if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
204 : 0 : log_sys_error("mlockall", "");
205 : 0 : return 0;
206 : : }
207 : : } else {
208 [ # # ]: 0 : if (munlockall()) {
209 : 0 : log_sys_error("munlockall", "");
210 : 0 : return 0;
211 : : }
212 : : }
213 : 0 : return 1;
214 : : #else
215 : : return 0;
216 : : #endif
217 : : }
218 : :
219 : : /* Reset statistic counters */
220 : 0 : *mstats = 0;
221 : 0 : rewind(_mapsh);
222 : :
223 [ # # ]: 0 : while ((n = getline(&line, &len, _mapsh)) != -1) {
224 [ # # ]: 0 : line[n > 0 ? n - 1 : 0] = '\0'; /* remove \n */
225 [ # # ]: 0 : if (!_maps_line(cmd, lock, line, mstats))
226 : 0 : ret = 0;
227 : : }
228 : :
229 : 0 : free(line);
230 : :
231 [ # # ]: 0 : log_debug("%socked %ld bytes",
232 : : (lock == LVM_MLOCK) ? "L" : "Unl", (long)*mstats);
233 : :
234 : 0 : return ret;
235 : : }
236 : :
237 : : /* Stop memory getting swapped out */
238 : 0 : static void _lock_mem(struct cmd_context *cmd)
239 : : {
240 : 0 : _allocate_memory();
241 : :
242 : : /*
243 : : * For daemon we need to use mlockall()
244 : : * so even future adition of thread which may not even use lvm lib
245 : : * will not block memory locked thread
246 : : * Note: assuming _memlock_count_daemon is updated before _memlock_count
247 : : */
248 [ # # ]: 0 : _use_mlockall = _memlock_count_daemon ? 1 :
249 : 0 : find_config_tree_bool(cmd, "activation/use_mlockall", DEFAULT_USE_MLOCKALL);
250 : :
251 [ # # ]: 0 : if (!_use_mlockall) {
252 [ # # # # ]: 0 : if (!*_procselfmaps &&
253 : : dm_snprintf(_procselfmaps, sizeof(_procselfmaps),
254 : 0 : "%s" SELF_MAPS, cmd->proc_dir) < 0) {
255 : 0 : log_error("proc_dir too long");
256 : 0 : return;
257 : : }
258 : :
259 [ # # ]: 0 : if (!(_mapsh = fopen(_procselfmaps, "r"))) {
260 : 0 : log_sys_error("fopen", _procselfmaps);
261 : 0 : return;
262 : : }
263 : : }
264 : :
265 : 0 : log_very_verbose("Locking memory");
266 [ # # ]: 0 : if (!_memlock_maps(cmd, LVM_MLOCK, &_mstats))
267 : 0 : stack;
268 : :
269 : 0 : errno = 0;
270 [ # # # # ]: 0 : if (((_priority = getpriority(PRIO_PROCESS, 0)) == -1) && errno)
271 : 0 : log_sys_error("getpriority", "");
272 : : else
273 [ # # ]: 0 : if (setpriority(PRIO_PROCESS, 0, _default_priority))
274 : 0 : log_error("setpriority %d failed: %s",
275 : : _default_priority, strerror(errno));
276 : : }
277 : :
278 : 0 : static void _unlock_mem(struct cmd_context *cmd)
279 : : {
280 : : size_t unlock_mstats;
281 : :
282 : 0 : log_very_verbose("Unlocking memory");
283 : :
284 [ # # ]: 0 : if (!_memlock_maps(cmd, LVM_MUNLOCK, &unlock_mstats))
285 : 0 : stack;
286 : :
287 [ # # ]: 0 : if (!_use_mlockall) {
288 [ # # ]: 0 : if (fclose(_mapsh))
289 : 0 : log_sys_error("fclose", _procselfmaps);
290 : :
291 [ # # ]: 0 : if (_mstats < unlock_mstats)
292 : 0 : log_error(INTERNAL_ERROR "Maps lock %ld < unlock %ld",
293 : : (long)_mstats, (long)unlock_mstats);
294 : : }
295 : :
296 [ # # ]: 0 : if (setpriority(PRIO_PROCESS, 0, _priority))
297 : 0 : log_error("setpriority %u failed: %s", _priority,
298 : : strerror(errno));
299 : 0 : _release_memory();
300 : 0 : }
301 : :
302 : 0 : static void _lock_mem_if_needed(struct cmd_context *cmd)
303 : : {
304 [ # # ]: 0 : if ((_memlock_count + _memlock_count_daemon) == 1)
305 : 0 : _lock_mem(cmd);
306 : 0 : }
307 : :
308 : 0 : static void _unlock_mem_if_possible(struct cmd_context *cmd)
309 : : {
310 [ # # ]: 0 : if ((_memlock_count + _memlock_count_daemon) == 0)
311 : 0 : _unlock_mem(cmd);
312 : 0 : }
313 : :
314 : 0 : void memlock_inc(struct cmd_context *cmd)
315 : : {
316 : 0 : ++_memlock_count;
317 : 0 : _lock_mem_if_needed(cmd);
318 : 0 : log_debug("memlock_count inc to %d", _memlock_count);
319 : 0 : }
320 : :
321 : 0 : void memlock_dec(struct cmd_context *cmd)
322 : : {
323 [ # # ]: 0 : if (!_memlock_count)
324 : 0 : log_error(INTERNAL_ERROR "_memlock_count has dropped below 0.");
325 : 0 : --_memlock_count;
326 : 0 : _unlock_mem_if_possible(cmd);
327 : 0 : log_debug("memlock_count dec to %d", _memlock_count);
328 : 0 : }
329 : :
330 : : /*
331 : : * The memlock_*_daemon functions will force the mlockall() call that we need
332 : : * to stay in memory, but they will have no effect on device scans (unlike
333 : : * normal memlock_inc and memlock_dec). Memory is kept locked as long as either
334 : : * of memlock or memlock_daemon is in effect.
335 : : */
336 : :
337 : 0 : void memlock_inc_daemon(struct cmd_context *cmd)
338 : : {
339 : 0 : ++_memlock_count_daemon;
340 [ # # ][ # # ]: 0 : if (_memlock_count_daemon == 1 && _memlock_count > 0)
341 : 0 : log_error(INTERNAL_ERROR "_memlock_inc_daemon used after _memlock_inc.");
342 : 0 : _lock_mem_if_needed(cmd);
343 : 0 : log_debug("memlock_count_daemon inc to %d", _memlock_count_daemon);
344 : 0 : }
345 : :
346 : 0 : void memlock_dec_daemon(struct cmd_context *cmd)
347 : : {
348 [ # # ]: 0 : if (!_memlock_count_daemon)
349 : 0 : log_error(INTERNAL_ERROR "_memlock_count_daemon has dropped below 0.");
350 : 0 : --_memlock_count_daemon;
351 : 0 : _unlock_mem_if_possible(cmd);
352 : 0 : log_debug("memlock_count_daemon dec to %d", _memlock_count_daemon);
353 : 0 : }
354 : :
355 : : /*
356 : : * This disregards the daemon (dmeventd) locks, since we use memlock() to check
357 : : * whether it is safe to run a device scan, which would normally coincide with
358 : : * !memlock() -- but the daemon global memory lock breaks this assumption, so
359 : : * we do not take those into account here.
360 : : */
361 : 4 : int memlock(void)
362 : : {
363 : 4 : return _memlock_count;
364 : : }
365 : :
366 : 1 : void memlock_init(struct cmd_context *cmd)
367 : : {
368 : 1 : _size_stack = find_config_tree_int(cmd,
369 : : "activation/reserved_stack",
370 : : DEFAULT_RESERVED_STACK) * 1024;
371 : 1 : _size_malloc_tmp = find_config_tree_int(cmd,
372 : : "activation/reserved_memory",
373 : : DEFAULT_RESERVED_MEMORY) * 1024;
374 : 1 : _default_priority = find_config_tree_int(cmd,
375 : : "activation/process_priority",
376 : : DEFAULT_PROCESS_PRIORITY);
377 : 1 : }
378 : :
379 : : #endif
|