1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5 Copyright (C) 2013-2020 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * BAREOS memory pool routines.
24 *
25 * The idea behind these routines is that there will be pools of memory that
26 * are pre-allocated for quick access. The pools will have a fixed memory size
27 * on allocation but if need be, the size can be increased. This is particularly
28 * useful for filename buffers where 256 bytes should be sufficient in 99.99%
29 * of the cases, but when it isn't we want to be able to increase the size.
30 *
31 * A major advantage of the pool memory aside from the speed is that the buffer
32 * carries around its size, so to ensure that there is enough memory, simply
33 * call the CheckPoolMemorySize() with the desired size and it will adjust only
34 * if necessary.
35 *
36 * Kern E. Sibbald
37 */
38
39 #include "include/bareos.h"
40 #include "lib/util.h"
41
42 #define HEAD_SIZE BALIGN(sizeof(struct abufhead))
43
44 struct s_pool_ctl {
45 int32_t size; /* default size */
46 int32_t max_allocated; /* max allocated */
47 int32_t max_used; /* max buffers used */
48 int32_t in_use; /* number in use */
49 struct abufhead* free_buf; /* pointer to free buffers */
50 };
51
52 /*
53 * Bareos Name length plus extra
54 */
55 #define NLEN (MAX_NAME_LENGTH + 2)
56
57 /*
58 * Bareos Record length
59 */
60 #define RLEN 128
61
62 /* #define STRESS_TEST_POOL */
63
64 /*
65 * Define default Pool buffer sizes
66 */
67 #ifndef STRESS_TEST_POOL
68 static struct s_pool_ctl pool_ctl[] = {
69 {256, 256, 0, 0, NULL}, /* PM_NOPOOL no pooling */
70 {NLEN, NLEN, 0, 0, NULL}, /* PM_NAME Bareos name */
71 {256, 256, 0, 0, NULL}, /* PM_FNAME filename buffers */
72 {512, 512, 0, 0, NULL}, /* PM_MESSAGE message buffer */
73 {1024, 1024, 0, 0, NULL}, /* PM_EMSG error message buffer */
74 {4096, 4096, 0, 0, NULL}, /* PM_BSOCK message buffer */
75 {RLEN, RLEN, 0, 0, NULL} /* PM_RECORD message buffer */
76 };
77 #else
78 /*
79 * This is used ONLY when stress testing the code
80 */
81 static struct s_pool_ctl pool_ctl[] = {
82 {20, 20, 0, 0, NULL}, /* PM_NOPOOL no pooling */
83 {NLEN, NLEN, 0, 0, NULL}, /* PM_NAME Bareos name */
84 {20, 20, 0, 0, NULL}, /* PM_FNAME filename buffers */
85 {20, 20, 0, 0, NULL}, /* PM_MESSAGE message buffer */
86 {20, 20, 0, 0, NULL}, /* PM_EMSG error message buffer */
87 {20, 20, 0, 0, NULL} /* PM_BSOCK message buffer */
88 {RLEN, RLEN, 0, 0, NULL} /* PM_RECORD message buffer */
89 };
90 #endif
91
92 /*
93 * Memory allocation control structures and storage.
94 */
95 struct abufhead {
96 int32_t ablen; /* Buffer length in bytes */
97 int32_t pool; /* pool */
98 struct abufhead* next; /* pointer to next free buffer */
99 int32_t bnet_size; /* dummy for BnetSend() */
100 };
101
102 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
103
104
105 /*
106 * Special version of error reporting using a static buffer so we don't use
107 * the normal error reporting which uses dynamic memory e.g. recursivly calls
108 * these routines again leading to deadlocks.
109 */
MemPoolErrorMessage(const char * file,int line,const char * fmt,...)110 [[noreturn]] static void MemPoolErrorMessage(const char* file,
111 int line,
112 const char* fmt,
113 ...)
114 {
115 char buf[256];
116 va_list arg_ptr;
117 int len;
118
119 len = Bsnprintf(buf, sizeof(buf), _("%s: ABORTING due to ERROR in %s:%d\n"),
120 my_name, get_basename(file), line);
121
122 va_start(arg_ptr, fmt);
123 Bvsnprintf(buf + len, sizeof(buf) - len, (char*)fmt, arg_ptr);
124 va_end(arg_ptr);
125
126 DispatchMessage(NULL, M_ABORT, 0, buf);
127 abort();
128 }
129
GetPoolMemory(int pool)130 POOLMEM* GetPoolMemory(int pool)
131 {
132 struct abufhead* buf;
133
134 P(mutex);
135 if (pool_ctl[pool].free_buf) {
136 buf = pool_ctl[pool].free_buf;
137 pool_ctl[pool].free_buf = buf->next;
138 V(mutex);
139 return (POOLMEM*)((char*)buf + HEAD_SIZE);
140 }
141
142 if ((buf = (struct abufhead*)malloc(pool_ctl[pool].size + HEAD_SIZE))
143 == NULL) {
144 V(mutex);
145 MemPoolErrorMessage(__FILE__, __LINE__,
146 _("Out of memory requesting %d bytes\n"),
147 pool_ctl[pool].size);
148 return NULL;
149 }
150
151 buf->ablen = pool_ctl[pool].size;
152 buf->pool = pool;
153 buf->next = NULL;
154 pool_ctl[pool].in_use++;
155 pool_ctl[pool].max_used
156 = std::max(pool_ctl[pool].in_use, pool_ctl[pool].max_used);
157 V(mutex);
158 return (POOLMEM*)(((char*)buf) + HEAD_SIZE);
159 }
160
161 /* Get nonpool memory of size requested */
GetMemory(int32_t size)162 POOLMEM* GetMemory(int32_t size)
163 {
164 struct abufhead* buf;
165
166 if ((buf = (struct abufhead*)malloc(size + HEAD_SIZE)) == NULL) {
167 MemPoolErrorMessage(__FILE__, __LINE__,
168 _("Out of memory requesting %d bytes\n"), size);
169 return NULL;
170 }
171
172 buf->ablen = size;
173 buf->pool = 0;
174 buf->next = NULL;
175 P(mutex);
176 pool_ctl[0].in_use++;
177 pool_ctl[0].max_used = std::max(pool_ctl[0].in_use, pool_ctl[0].max_used);
178 V(mutex);
179 return (POOLMEM*)(((char*)buf) + HEAD_SIZE);
180 }
181
182 /* Return the size of a memory buffer */
SizeofPoolMemory(POOLMEM * obuf)183 int32_t SizeofPoolMemory(POOLMEM* obuf)
184 {
185 char* cp = (char*)obuf;
186
187 ASSERT(obuf);
188 cp -= HEAD_SIZE;
189 return ((struct abufhead*)cp)->ablen;
190 }
191
192 /* Realloc pool memory buffer */
ReallocPoolMemory(POOLMEM * obuf,int32_t size)193 POOLMEM* ReallocPoolMemory(POOLMEM* obuf, int32_t size)
194 {
195 ASSERT(obuf);
196 void* buf = realloc((char*)obuf - HEAD_SIZE, size + HEAD_SIZE);
197 if (buf == NULL) {
198 MemPoolErrorMessage(__FILE__, __LINE__,
199 _("Out of memory requesting %d bytes\n"), size);
200 return NULL;
201 }
202
203 ((struct abufhead*)buf)->ablen = size;
204 const int pool = ((struct abufhead*)buf)->pool;
205 P(mutex);
206 if (size > pool_ctl[pool].max_allocated) {
207 pool_ctl[pool].max_allocated = size;
208 }
209 V(mutex);
210 return (POOLMEM*)(((char*)buf) + HEAD_SIZE);
211 }
212
CheckPoolMemorySize(POOLMEM * obuf,int32_t size)213 POOLMEM* CheckPoolMemorySize(POOLMEM* obuf, int32_t size)
214 {
215 ASSERT(obuf);
216 if (size <= SizeofPoolMemory(obuf)) { return obuf; }
217 return ReallocPoolMemory(obuf, size);
218 }
219
220 /* Free a memory buffer */
FreePoolMemory(POOLMEM * obuf)221 void FreePoolMemory(POOLMEM* obuf)
222 {
223 ASSERT(obuf);
224 struct abufhead* buf = (struct abufhead*)((char*)obuf - HEAD_SIZE);
225
226 const int pool = buf->pool;
227
228 if (pool == 0) {
229 free((char*)buf); /* free nonpooled memory */
230 P(mutex);
231 pool_ctl[0].in_use--;
232 V(mutex);
233 return;
234 }
235 P(mutex);
236 struct abufhead* next;
237 for (next = pool_ctl[pool].free_buf; next; next = next->next) {
238 if (next == buf) { // attempt to free twice
239 V(mutex);
240 ASSERT(next != buf);
241 }
242 }
243 // otherwise link it to the free pool chain
244 pool_ctl[pool].in_use--;
245 buf->next = pool_ctl[pool].free_buf;
246 pool_ctl[pool].free_buf = buf;
247 V(mutex);
248 }
249
250 /*
251 * Clean up memory pool periodically
252 *
253 */
254 static time_t last_garbage_collection = 0;
255 const int garbage_interval = 24 * 60 * 60; /* garbage collect every 24 hours */
256
GarbageCollectMemoryPool()257 void GarbageCollectMemoryPool()
258 {
259 time_t now;
260
261 P(mutex);
262 if (last_garbage_collection == 0) {
263 last_garbage_collection = time(NULL);
264 V(mutex);
265 return;
266 }
267 now = time(NULL);
268 if (now >= last_garbage_collection + garbage_interval) {
269 last_garbage_collection = now;
270 V(mutex);
271 GarbageCollectMemory();
272 } else {
273 V(mutex);
274 }
275 }
276
277 /* Release all freed pooled memory */
CloseMemoryPool()278 void CloseMemoryPool()
279 {
280 P(mutex);
281 for (int i = 1; i <= PM_MAX; i++) {
282 abufhead* buf = pool_ctl[i].free_buf;
283 while (buf) {
284 abufhead* next = buf->next;
285 free(buf);
286 buf = next;
287 }
288 pool_ctl[i].free_buf = NULL;
289 }
290 V(mutex);
291
292 if (debug_level >= 1) { PrintMemoryPoolStats(); }
293 }
294
295 /*
296 * Garbage collect and trim memory if possible
297 * This should be called after all big memory usages if possible.
298 */
GarbageCollectMemory()299 void GarbageCollectMemory() { CloseMemoryPool(); /* release free chain */ }
300
pool_name(int pool)301 static const char* pool_name(int pool)
302 {
303 static char buf[30];
304 static const char* name[] = {"NoPool", "NAME ", "FNAME ", "MSG ",
305 "EMSG ", "BareosSocket ", "RECORD"};
306
307 if (pool >= 0 && pool <= PM_MAX) { return name[pool]; }
308 sprintf(buf, "%-6d", pool);
309
310 return buf;
311 }
312
313 /*
314 * Print staticstics on memory pool usage
315 */
PrintMemoryPoolStats()316 void PrintMemoryPoolStats()
317 {
318 Pmsg0(-1, "Pool Maxsize Maxused Inuse\n");
319 for (int i = 0; i <= PM_MAX; i++) {
320 Pmsg4(-1, "%5s %7d %7d %5d\n", pool_name(i), pool_ctl[i].max_allocated,
321 pool_ctl[i].max_used, pool_ctl[i].in_use);
322 }
323
324 Pmsg0(-1, "\n");
325 }
326
327 /*
328 * Concatenate a string (str) onto a pool memory buffer pm
329 * Returns: length of concatenated string
330 */
PmStrcat(POOLMEM * & pm,const char * str)331 int PmStrcat(POOLMEM*& pm, const char* str)
332 {
333 int pmlen = strlen(pm);
334 int len;
335
336 if (!str) str = "";
337
338 len = strlen(str) + 1;
339 pm = CheckPoolMemorySize(pm, pmlen + len);
340 memcpy(pm + pmlen, str, len);
341 return pmlen + len - 1;
342 }
343
PmStrcat(POOLMEM * & pm,PoolMem & str)344 int PmStrcat(POOLMEM*& pm, PoolMem& str)
345 {
346 int pmlen = strlen(pm);
347 int len = strlen(str.c_str()) + 1;
348
349 pm = CheckPoolMemorySize(pm, pmlen + len);
350 memcpy(pm + pmlen, str.c_str(), len);
351 return pmlen + len - 1;
352 }
353
PmStrcat(PoolMem & pm,const char * str)354 int PmStrcat(PoolMem& pm, const char* str)
355 {
356 int pmlen = strlen(pm.c_str());
357 int len;
358
359 if (!str) str = "";
360
361 len = strlen(str) + 1;
362 pm.check_size(pmlen + len);
363 memcpy(pm.c_str() + pmlen, str, len);
364 return pmlen + len - 1;
365 }
366
PmStrcat(PoolMem * & pm,const char * str)367 int PmStrcat(PoolMem*& pm, const char* str)
368 {
369 int pmlen = strlen(pm->c_str());
370 int len;
371
372 if (!str) str = "";
373
374 len = strlen(str) + 1;
375 pm->check_size(pmlen + len);
376 memcpy(pm->c_str() + pmlen, str, len);
377 return pmlen + len - 1;
378 }
379
380 /*
381 * Copy a string (str) into a pool memory buffer pm
382 * Returns: length of string copied
383 */
PmStrcpy(POOLMEM * & pm,const char * str)384 int PmStrcpy(POOLMEM*& pm, const char* str)
385 {
386 int len;
387
388 if (!str) str = "";
389
390 len = strlen(str) + 1;
391 pm = CheckPoolMemorySize(pm, len);
392 memcpy(pm, str, len);
393 return len - 1;
394 }
395
PmStrcpy(POOLMEM * & pm,PoolMem & str)396 int PmStrcpy(POOLMEM*& pm, PoolMem& str)
397 {
398 int len = strlen(str.c_str()) + 1;
399
400 pm = CheckPoolMemorySize(pm, len);
401 memcpy(pm, str.c_str(), len);
402 return len - 1;
403 }
404
PmStrcpy(PoolMem & pm,const char * str)405 int PmStrcpy(PoolMem& pm, const char* str)
406 {
407 int len;
408
409 if (!str) str = "";
410
411 len = strlen(str) + 1;
412 pm.check_size(len);
413 memcpy(pm.c_str(), str, len);
414 return len - 1;
415 }
416
PmStrcpy(PoolMem * & pm,const char * str)417 int PmStrcpy(PoolMem*& pm, const char* str)
418 {
419 int len;
420
421 if (!str) str = "";
422
423 len = strlen(str) + 1;
424 pm->check_size(len);
425 memcpy(pm->c_str(), str, len);
426 return len - 1;
427 }
428
429 /*
430 * Copy data into a pool memory buffer pm
431 * Returns: length of data copied
432 */
PmMemcpy(POOLMEM * & pm,const char * data,int32_t n)433 int PmMemcpy(POOLMEM*& pm, const char* data, int32_t n)
434 {
435 pm = CheckPoolMemorySize(pm, n);
436 memcpy(pm, data, n);
437 return n;
438 }
439
PmMemcpy(POOLMEM * & pm,PoolMem & data,int32_t n)440 int PmMemcpy(POOLMEM*& pm, PoolMem& data, int32_t n)
441 {
442 pm = CheckPoolMemorySize(pm, n);
443 memcpy(pm, data.c_str(), n);
444 return n;
445 }
446
PmMemcpy(PoolMem & pm,const char * data,int32_t n)447 int PmMemcpy(PoolMem& pm, const char* data, int32_t n)
448 {
449 pm.check_size(n);
450 memcpy(pm.c_str(), data, n);
451 return n;
452 }
453
PmMemcpy(PoolMem * & pm,const char * data,int32_t n)454 int PmMemcpy(PoolMem*& pm, const char* data, int32_t n)
455 {
456 pm->check_size(n);
457 memcpy(pm->c_str(), data, n);
458 return n;
459 }
460
461 /* ============== CLASS PoolMem ============== */
462
463 /*
464 * Return the size of a memory buffer
465 */
MaxSize()466 int32_t PoolMem::MaxSize()
467 {
468 int32_t size;
469 char* cp = mem;
470
471 cp -= HEAD_SIZE;
472 size = ((struct abufhead*)cp)->ablen;
473
474 return size;
475 }
476
ReallocPm(int32_t size)477 void PoolMem::ReallocPm(int32_t size)
478 {
479 char* cp = mem;
480 char* buf;
481 int pool;
482
483 P(mutex);
484 cp -= HEAD_SIZE;
485 buf = (char*)realloc(cp, size + HEAD_SIZE);
486 if (buf == NULL) {
487 V(mutex);
488 MemPoolErrorMessage(__FILE__, __LINE__,
489 _("Out of memory requesting %d bytes\n"), size);
490 return;
491 }
492
493 ((struct abufhead*)buf)->ablen = size;
494 pool = ((struct abufhead*)buf)->pool;
495 if (size > pool_ctl[pool].max_allocated) {
496 pool_ctl[pool].max_allocated = size;
497 }
498 mem = buf + HEAD_SIZE;
499 V(mutex);
500 }
501
strcat(PoolMem & str)502 int PoolMem::strcat(PoolMem& str) { return strcat(str.c_str()); }
503
strcat(const char * str)504 int PoolMem::strcat(const char* str)
505 {
506 int pmlen = strlen();
507 int len;
508
509 if (!str) str = "";
510
511 len = ::strlen(str) + 1;
512 check_size(pmlen + len);
513 memcpy(mem + pmlen, str, len);
514 return pmlen + len - 1;
515 }
516
strcpy(PoolMem & str)517 int PoolMem::strcpy(PoolMem& str) { return strcpy(str.c_str()); }
518
strcpy(const char * str)519 int PoolMem::strcpy(const char* str)
520 {
521 int len;
522
523 if (!str) str = "";
524
525 len = ::strlen(str) + 1;
526 check_size(len);
527 memcpy(mem, str, len);
528 return len - 1;
529 }
530
toLower()531 void PoolMem::toLower() { lcase(mem); }
532
bsprintf(const char * fmt,...)533 int PoolMem::bsprintf(const char* fmt, ...)
534 {
535 int len;
536 va_list arg_ptr;
537 va_start(arg_ptr, fmt);
538 len = Bvsprintf(fmt, arg_ptr);
539 va_end(arg_ptr);
540 return len;
541 }
542
Bvsprintf(const char * fmt,va_list arg_ptr)543 int PoolMem::Bvsprintf(const char* fmt, va_list arg_ptr)
544 {
545 int maxlen, len;
546 va_list ap;
547
548 again:
549 maxlen = MaxSize() - 1;
550 va_copy(ap, arg_ptr);
551 len = ::Bvsnprintf(mem, maxlen, fmt, ap);
552 va_end(ap);
553 if (len < 0 || len >= maxlen) {
554 ReallocPm(maxlen + maxlen / 2);
555 goto again;
556 }
557 return len;
558 }
559