Annotation of libelwix/src/mem.c, revision 1.2
1.1 misho 1: /*************************************************************************
2: * (C) 2012 AITNET ltd - Sofia/Bulgaria - <misho@elwix.org>
3: * by Michael Pounov <misho@openbsd-bg.org>
4: *
5: * $Author: misho $
1.2 ! misho 6: * $Id: mem.c,v 1.1.1.1.6.1 2013/05/26 20:03:19 misho Exp $
1.1 misho 7: *
8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #include "global.h"
47:
48:
49: mpool_t *elwix_mpool;
50:
51:
52: /*
53: * mpool_init() - Init memory pool
54: *
55: * @maxmem = If !=0 set maximum memory quota
56: * return: =NULL error or !=NULL new allocated pool
57: */
58: mpool_t *
59: mpool_init(u_long maxmem)
60: {
61: mpool_t *mp;
62: register int i;
63:
64: mp = malloc(sizeof(mpool_t));
65: if (!mp) {
66: LOGERR;
67: return NULL;
68: } else
69: memset(mp, 0, sizeof(mpool_t));
70:
71: pthread_mutex_init(&mp->pool_mtx, NULL);
72:
73: mp->pool_quota.max = maxmem;
74:
75: mpool_lock(mp);
76: for (i = 0; i < MEM_BUCKETS; i++) {
77: TAILQ_INIT(&mp->pool_active[i]);
78: TAILQ_INIT(&mp->pool_inactive[i]);
79: }
80: mpool_unlock(mp);
81:
82: return mp;
83: }
84:
85: /*
86: * mpool_destroy() - Destroy memory pool
87: *
88: * @mp = Memory pool
89: * return: none
90: */
91: void
92: mpool_destroy(mpool_t ** __restrict mp)
93: {
94: struct tagAlloc *m;
95: register int i;
96:
97: if (!mp && !*mp)
98: return;
99:
100: mpool_lock(*mp);
101:
102: for (i = 0; i < MEM_BUCKETS; i++) {
103: while ((m = TAILQ_FIRST(&(*mp)->pool_active[i]))) {
104: TAILQ_REMOVE(&(*mp)->pool_active[i], m, alloc_node);
105: if (m->alloc_mem)
106: free(m->alloc_mem);
107: free(m);
108: }
109: while ((m = TAILQ_FIRST(&(*mp)->pool_inactive[i]))) {
110: TAILQ_REMOVE(&(*mp)->pool_inactive[i], m, alloc_node);
111: if (m->alloc_mem)
112: free(m->alloc_mem);
113: free(m);
114: }
115: }
116:
117: mpool_unlock(*mp);
118: pthread_mutex_destroy(&(*mp)->pool_mtx);
119:
120: free(*mp);
121: *mp = NULL;
122: }
123:
124: /* ----------------------------------------------------------- */
125:
126: static inline long
127: BucketIndex(u_int size)
128: {
129: register long b;
130:
131: if (!size--)
132: return 0; /* min bucket position in array */
133:
134: for (b = MEM_MIN_BUCKET; b < MEM_MAX_BUCKET; b++)
135: if (!(size >> b))
136: break;
137:
138: return b - MEM_MIN_BUCKET; /* convert to bucket array index */
139: }
140:
141: static inline struct tagAlloc *
142: pullInactive(mpool_t * __restrict mp, int idx)
143: {
144: struct tagAlloc *m = NULL;
145:
146: /* must be locked pool before use this function */
147: if ((m = TAILQ_FIRST(&mp->pool_inactive[idx]))) {
148: TAILQ_REMOVE(&mp->pool_inactive[idx], m, alloc_node);
149: /* statistics */
150: mp->pool_calls.cache--;
151: mp->pool_bytes.cache -= mem_size(m);
152:
153: /* clear name */
154: *m->alloc_name = 0;
155: }
156:
157: return m;
158: }
159:
160: /*
161: * mpool_malloc() - Memory allocation
162: *
163: * @mp = Memory pool
164: * @size = Size
165: * @memname = Optional memory block name
166: * return: NULL error or !=NULL ok allocated memory
167: */
168: void *
169: mpool_malloc(mpool_t * __restrict mp, u_int size, const char *memname)
170: {
171: struct tagAlloc *m;
172: int idx;
173: u_int align;
174:
175: if (!mp) {
176: elwix_SetErr(EINVAL, "Pool not specified");
177: return NULL;
178: }
179: if (size > MEM_ALLOC_MAX) {
180: elwix_SetErr(ENOMEM, "Memory size is too large");
181: return NULL;
182: } else
183: size = (size + 3) & ~3; /* must align to 4 because needed room for sentinels */
184:
185: idx = BucketIndex(size);
186:
187: mpool_lock(mp);
188:
189: /* get memory from cache if exists */
190: if (!(m = pullInactive(mp, idx))) {
191: /* quota */
192: if (mp->pool_quota.max &&
193: (mp->pool_quota.curr + size) > mp->pool_quota.max) {
194: elwix_SetErr(ENOMEM, "Max.allocate memory quota has been reached");
195: mpool_unlock(mp);
196: return NULL;
197: }
198:
199: m = malloc(sizeof(struct tagAlloc));
200: if (!m) {
201: LOGERR;
202: mpool_unlock(mp);
203: return NULL;
204: } else
205: memset(m, 0, sizeof(struct tagAlloc));
206: }
207:
208: if (memname)
209: strlcpy(m->alloc_name, memname, sizeof m->alloc_name);
210:
211: if (!m->alloc_mem) {
212: align = 1 << (idx + MEM_MIN_BUCKET);
213: m->alloc_mem = malloc(align + 12); /* +12 sentinel bytes */
214: if (!m->alloc_mem) {
215: LOGERR;
216: free(m);
217: mpool_unlock(mp);
218: return NULL;
219: } else { /* quota */
220: mp->pool_quota.curr += size;
221: memset(m->alloc_mem, 0, align + 12);
222: }
223: }
224:
225: m->alloc_mem[0] = size / sizeof(u_int);
226: m->alloc_mem[1] = MEM_MAGIC_START;
227: m->alloc_mem[2 + size / sizeof(u_int)] = MEM_MAGIC_STOP;
228: TAILQ_INSERT_HEAD(&mp->pool_active[idx], m, alloc_node);
229: /* statistics */
230: mp->pool_calls.alloc++;
231: mp->pool_bytes.alloc += size;
232:
233: mpool_unlock(mp);
234: return mem_data(m, void*);
235: }
236:
237: /*
238: * mpool_realloc() Reallocate memory block with new size
239: *
240: * @mp = Memory pool
241: * @data = Allocated memory data
242: * @newsize = New size of memory block
243: * @memname = Optional new memory block name
244: * return: NULL error or !=NULL new reallocated memory block
245: */
246: void *
247: mpool_realloc(mpool_t * __restrict mp, void * __restrict data, u_int newsize, const char *memname)
248: {
249: struct tagAlloc *m, *tmp;
250: int idx, oidx;
251: void *p;
252: u_int align, osize;
253:
254: /* if !data execute mpool_malloc() */
255: if (!data)
256: return mpool_malloc(mp, newsize, memname);
257:
258: if (!mp) {
259: elwix_SetErr(EINVAL, "Pool not specified");
260: return NULL;
261: }
262: /* check address range & sentinel */
263: if (MEM_BADADDR(data) || MEM_CORRUPT(data)) {
264: elwix_SetErr(EFAULT, "Corrupted memory address");
265: return NULL;
266: } else {
267: osize = ((u_int*)data)[-2] * sizeof(u_int);
268: oidx = BucketIndex(osize);
269: }
270: /* prepare new size */
271: if (newsize > MEM_ALLOC_MAX) {
272: elwix_SetErr(ENOMEM, "Memory size is too large");
273: return NULL;
274: } else {
275: newsize = (newsize + 3) & ~3; /* must align to 4 because needed room for sentinels */
276: idx = BucketIndex(newsize);
277: }
278:
279: mpool_lock(mp);
280:
281: /* quota */
282: if (mp->pool_quota.max &&
283: (mp->pool_quota.curr + ((u_long) newsize - osize)) > mp->pool_quota.max) {
284: elwix_SetErr(ENOMEM, "Max.allocate memory quota has been reached");
285: mpool_unlock(mp);
286: return NULL;
287: }
288:
289: /* find old memory block */
290: TAILQ_FOREACH_SAFE(m, &mp->pool_active[oidx], alloc_node, tmp) {
291: if (mem_data(m, void*) == data && mem_size(m) == osize) {
292: /* case in different buckets */
293: if (oidx != idx) {
294: TAILQ_REMOVE(&mp->pool_active[oidx], m, alloc_node);
295: /* statistics */
296: mp->pool_calls.alloc--;
297: }
298: mp->pool_bytes.alloc -= osize;
299: break;
300: }
301: }
302: /* memory block not found! */
303: if (!m) {
304: mpool_unlock(mp);
305: elwix_SetErr(EFAULT, "Memory block not found");
306: return NULL;
307: }
308:
309: /* try to reallocate memory block to new bucket */
310: if (oidx != idx) {
311: align = 1 << (idx + MEM_MIN_BUCKET);
312: p = realloc(m->alloc_mem, align + 12);
313: if (!p) {
314: LOGERR;
315:
316: /* restore to old bucket pulled memory block for reallocation */
317: TAILQ_INSERT_HEAD(&mp->pool_active[oidx], m, alloc_node);
318: /* statistics */
319: mp->pool_calls.alloc++;
320: mp->pool_bytes.alloc += osize;
321:
322: mpool_unlock(mp);
323: return NULL;
324: } else
325: m->alloc_mem = (u_int*) p;
326: }
327: /* quota */
328: mp->pool_quota.curr += (u_long) newsize - osize;
329:
330: m->alloc_mem[0] = newsize / sizeof(u_int);
331: m->alloc_mem[1] = MEM_MAGIC_START;
332: m->alloc_mem[2 + newsize / sizeof(u_int)] = MEM_MAGIC_STOP;
333:
334: if (oidx != idx) {
335: TAILQ_INSERT_HEAD(&mp->pool_active[idx], m, alloc_node);
336: /* statistics */
337: mp->pool_calls.alloc++;
338: }
339: mp->pool_bytes.alloc += newsize;
340:
341: if (memname)
342: strlcpy(m->alloc_name, memname, sizeof m->alloc_name);
343:
344: mpool_unlock(mp);
345: return mem_data(m, void*);
346: }
347:
348: /*
349: * mpool_purge() - Purge memory block cache and release resources
350: *
351: * @mp = Memory pool
352: * @atmost = Free at most in buckets
353: * return: -1 error or 0 ok
354: */
355: int
356: mpool_purge(mpool_t * __restrict mp, u_int atmost)
357: {
358: register int i, cx;
359: struct tagAlloc *m, *tmp;
360:
361: if (!mp) {
362: elwix_SetErr(EINVAL, "Pool not specified");
363: return -1;
364: }
365:
366: mpool_lock(mp);
367:
368: for (i = cx = 0; i < MEM_BUCKETS; cx = 0, i++) {
369: TAILQ_FOREACH_SAFE(m, &mp->pool_inactive[i], alloc_node, tmp) {
370: /* barrier for purge */
371: if (cx < atmost) {
372: cx++;
373: continue;
374: }
375:
376: TAILQ_REMOVE(&mp->pool_inactive[i], m, alloc_node);
377: /* statistics */
378: mp->pool_calls.cache--;
379: mp->pool_bytes.cache -= mem_size(m);
380:
381: mp->pool_calls.free++;
382: mp->pool_bytes.free += mem_size(m);
383: /* quota */
384: mp->pool_quota.curr -= mem_size(m);
385:
386: if (m->alloc_mem)
387: free(m->alloc_mem);
388: free(m);
389: }
390: }
391:
392: mpool_unlock(mp);
393: return 0;
394: }
395:
396: /*
397: * mpool_free() Free allocated memory with mpool_alloc()
398: *
399: * @mp = Memory pool
400: * @data = Allocated memory data
401: * @purge = if !=0 force release memory block
402: * return: <0 error or 0 ok released memory block
403: */
404: int
405: mpool_free(mpool_t * __restrict mp, void * __restrict data, int purge)
406: {
407: int idx;
408: struct tagAlloc *m, *tmp;
409:
410: assert(data);
411: if (!mp) {
412: elwix_SetErr(EINVAL, "Pool not specified");
413: return -1;
414: }
415: /* check address range & sentinel */
416: assert(!MEM_BADADDR(data) && !MEM_CORRUPT(data));
417: if (MEM_BADADDR(data) || MEM_CORRUPT(data)) {
418: elwix_SetErr(EFAULT, "Corrupted memory address");
419: return -2;
420: } else
421: idx = BucketIndex(((u_int*)data)[-2] * sizeof(u_int));
422:
423: mpool_lock(mp);
424: TAILQ_FOREACH_SAFE(m, &mp->pool_active[idx], alloc_node, tmp)
425: if (mem_data(m, void*) == data) {
426: TAILQ_REMOVE(&mp->pool_active[idx], m, alloc_node);
427: /* statistics */
428: mp->pool_calls.alloc--;
429: mp->pool_bytes.alloc -= mem_size(m);
430:
431: if (!purge) {
432: TAILQ_INSERT_HEAD(&mp->pool_inactive[idx], m, alloc_node);
433: /* statistics */
434: mp->pool_calls.cache++;
435: mp->pool_bytes.cache += mem_size(m);
436: } else {
437: /* statistics */
438: mp->pool_calls.free++;
439: mp->pool_bytes.free += mem_size(m);
440: /* quota */
441: mp->pool_quota.curr -= mem_size(m);
442:
443: if (m->alloc_mem)
444: free(m->alloc_mem);
445: free(m);
446: }
447: break;
448: }
449: mpool_unlock(mp);
450:
451: return 0;
452: }
453:
454: /*
455: * mpool_free2() Free allocated memory with mpool_alloc() by size and memory name
456: *
457: * @mp = Memory pool
458: * @size = Allocated memory data size
459: * @memname = Memory name
460: * @purge = if !=0 force release memory block
461: * return: <0 error or 0 ok released memory block
462: */
463: int
464: mpool_free2(mpool_t * __restrict mp, u_int size, const char *memname, int purge)
465: {
466: int idx;
467: struct tagAlloc *m, *tmp;
468:
469: if (!mp || !memname) {
470: elwix_SetErr(EINVAL, "Pool or memory name is not specified");
471: return -1;
472: } else
473: idx = BucketIndex(size);
474:
475: mpool_lock(mp);
476: TAILQ_FOREACH_SAFE(m, &mp->pool_active[idx], alloc_node, tmp)
477: if (!strcmp(m->alloc_name, memname)) {
478: TAILQ_REMOVE(&mp->pool_active[idx], m, alloc_node);
479: /* statistics */
480: mp->pool_calls.alloc--;
481: mp->pool_bytes.alloc -= mem_size(m);
482:
483: if (!purge) {
484: TAILQ_INSERT_HEAD(&mp->pool_inactive[idx], m, alloc_node);
485: /* statistics */
486: mp->pool_calls.cache++;
487: mp->pool_bytes.cache += mem_size(m);
488: } else {
489: /* statistics */
490: mp->pool_calls.free++;
491: mp->pool_bytes.free += mem_size(m);
492: /* quota */
493: mp->pool_quota.curr -= mem_size(m);
494:
495: if (m->alloc_mem)
496: free(m->alloc_mem);
497: free(m);
498: }
499: break;
500: }
501: mpool_unlock(mp);
502:
503: return 0;
504: }
505:
506: /*
507: * mpool_strdup() - String duplicate
508: *
509: * @mp = Memory pool
510: * @str = String
511: * @memname = Memory name
512: * return: NULL error or !=NULL new string
513: */
514: char *
515: mpool_strdup(mpool_t * __restrict mp, const char *str, const char *memname)
516: {
517: char *s = NULL;
518: u_int len;
519:
520: if (!mp) {
521: elwix_SetErr(EINVAL, "Pool not specified");
522: return NULL;
523: }
524: if (!str) {
525: elwix_SetErr(EINVAL, "String is NULL");
526: return NULL;
527: } else
528: len = strlen(str) + 1;
529:
530: s = mpool_malloc(mp, len, memname);
531: if (!s)
532: return NULL;
533: else
534: memcpy(s, str, len);
535:
536: return s;
537: }
538:
539: /*
540: * mpool_getmembynam() Find allocated memory block by size and memory name
541: *
542: * @mp = Memory pool
543: * @size = Memory size
544: * @memname = Memory name
545: * return: NULL error or not found and !=NULL allocated memory
546: */
1.2 ! misho 547: struct tagAlloc *
1.1 misho 548: mpool_getmembynam(mpool_t * __restrict mp, u_int size, const char *memname)
549: {
550: int idx;
551: struct tagAlloc *m = NULL;
552:
553: if (!mp || !memname)
554: return NULL;
555:
556: idx = BucketIndex(size);
557: TAILQ_FOREACH(m, &mp->pool_active[idx], alloc_node)
558: if (!strcmp(m->alloc_name, memname))
559: break;
560:
561: return mem_data(m, void*);
562: }
563:
564: /*
565: * mpool_getsizebyaddr() - Get size of allocated memory block by address
566: *
567: * @addr = allocated memory from mpool_malloc()
568: * return: usable size of allocated memory block
569: */
1.2 ! misho 570: u_int
1.1 misho 571: mpool_getsizebyaddr(void * __restrict data)
572: {
573: if (mpool_chkaddr(data))
574: return 0;
575:
576: return (((u_int*) data)[-2] * sizeof(u_int));
577: }
578:
579: /*
580: * mpool_chkaddr() - Check validity of given address
581: *
582: * @data = allocated memory from mpool_malloc()
583: * return: -1 bad address, 1 corrupted address or 0 ok
584: */
1.2 ! misho 585: int
1.1 misho 586: mpool_chkaddr(void * __restrict data)
587: {
588: /* check address range */
589: if (MEM_BADADDR(data))
590: return -1;
591: /* check sentinel */
592: if (MEM_CORRUPT(data))
593: return 1;
594: /* data address is ok! */
595: return 0;
596: }
597:
598: /*
599: * mpool_setquota() - Change maximum memory quota
600: *
601: * @mp = Memory pool
602: * @maxmem = New max quota size
603: * return: old maximum memory quota size
604: */
1.2 ! misho 605: u_long
1.1 misho 606: mpool_setquota(mpool_t * __restrict mp, u_long maxmem)
607: {
608: u_long ret;
609:
610: if (!mp)
611: return 0;
612:
613: ret = mp->pool_quota.max;
614: mp->pool_quota.max = maxmem;
615:
616: /* if new max quota is less then current allocated memory,
617: * try to purge memory cache blocks
618: */
619: if (mp->pool_quota.max < mp->pool_quota.curr)
620: mpool_purge(mp, 0);
621:
622: return ret;
623: }
624:
625: /*
626: * mpool_getquota() - Get memory quota
627: *
628: * @mp = Memory pool
629: * @currmem = Return current memory
630: * @maxmem = Return max quota size
631: * return: none
632: */
1.2 ! misho 633: void
1.1 misho 634: mpool_getquota(mpool_t * __restrict mp, u_long *currmem, u_long *maxmem)
635: {
636: if (!mp)
637: return;
638:
639: if (maxmem)
640: *maxmem = mp->pool_quota.max;
641: if (currmem)
642: *currmem = mp->pool_quota.curr;
643: }
644:
645: /* ----------------------------------------------------------- */
646:
647: /*
648: * mpool_statistics() - Dump statistics from memory pool buckets
649: *
650: * @mp = Memory pool
651: * @cb = Export statistics to callback
652: * return: none
653: */
654: void
655: mpool_statistics(mpool_t * __restrict mp, mpool_stat_cb cb)
656: {
657: struct tagAlloc *m;
658: register int i, act, inact;
659:
660: if (!mp || !cb)
661: return;
662:
663: for (i = act = inact = 0; i < MEM_BUCKETS; act = inact = 0, i++) {
664: TAILQ_FOREACH(m, &mp->pool_active[i], alloc_node)
665: act++;
666: TAILQ_FOREACH(m, &mp->pool_inactive[i], alloc_node)
667: inact++;
668:
669: cb(1 << (i + MEM_MIN_BUCKET), act, inact);
670: }
671: }
672:
673: /* ----------------------------------------------------------- */
674:
675: /*
676: * mpool_xmalloc() - malloc wrapper
677: *
678: * @size = Size
679: * return: NULL error or !=NULL ok allocated memory
680: */
681: void *
682: mpool_xmalloc(size_t size)
683: {
684: return mpool_malloc(elwix_mpool, size, elwix_Prog);
685: }
686:
687: /*
688: * mpool_xcalloc() - calloc wrapper
689: *
690: * @num = number of elements
691: * @size = Size of element
692: * return: NULL error or !=NULL ok allocated memory
693: */
694: void *
695: mpool_xcalloc(size_t num, size_t size)
696: {
697: return mpool_malloc(elwix_mpool, num * size, elwix_Prog);
698: }
699:
700: /*
701: * mpool_xrealloc() - realloc wrapper
702: *
703: * @data = Allocated memory data
704: * @newsize = New size of memory block
705: * return: NULL error or !=NULL new reallocated memory block
706: */
707: void *
708: mpool_xrealloc(void * __restrict data, size_t newsize)
709: {
710: return mpool_realloc(elwix_mpool, data, newsize, elwix_Prog);
711: }
712:
713: /*
714: * mpool_xfree() - free wrapper
715: *
716: * @data = Allocated memory data
717: * return: none
718: */
719: void
720: mpool_xfree(void * __restrict data)
721: {
722: mpool_free(elwix_mpool, data, 0);
723: }
724:
725: /*
726: * mpool_xstrdup() - strdup wrapper
727: *
728: * @str = string
729: * return: =NULL error or !=NULL new allocated string
730: */
731: char *
732: mpool_xstrdup(const char *str)
733: {
734: return mpool_strdup(elwix_mpool, str, elwix_Prog);
735: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>