/* mbuffer.c * * Author Karsten Keil * * Copyright 2007 by Karsten Keil * Copyright 2010 by Karsten Keil * * This code is free software; you can redistribute it and/or modify * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE * version 2.1 as published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU LESSER GENERAL PUBLIC LICENSE for more details. * */ #include #include #include #include #include "helper.h" #include "debug.h" static struct mqueue free_queue_l2, free_queue_l3; static int Max_Cache; #define MIN_CACHE 4 #define MB_TYP_L2 2 #define MB_TYP_L3 3 #ifdef MEMLEAK_DEBUG #include static struct Aqueue { struct lhead Alist; pthread_mutex_t lock; int len; } AllocQueue; static inline void Aqueue_init(struct Aqueue *q) { pthread_mutex_init(&q->lock, NULL); q->len = 0; q->Alist.prev = &q->Alist; q->Alist.next = &q->Alist; } static inline int Amqueue_len(struct Aqueue *q) { return q->len ; } static inline void __list_add(struct lhead *new, struct lhead *prev, struct lhead *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } static inline void __list_del(struct lhead * prev, struct lhead * next) { next->prev = prev; prev->next = next; } static inline void Aqueue_head(struct Aqueue *q, struct mbuffer *newm) { pthread_mutex_lock(&q->lock); q->len++; __list_add(&newm->Alist, &q->Alist, q->Alist.next); pthread_mutex_unlock(&q->lock); } static inline void Aqueue_tail(struct Aqueue *q, struct mbuffer *newm) { pthread_mutex_lock(&q->lock); q->len++; __list_add(&newm->Alist, q->Alist.prev, &q->Alist); pthread_mutex_unlock(&q->lock); } static inline struct mbuffer *Adequeue(struct Aqueue *q) { struct mbuffer *m; struct lhead *le; pthread_mutex_lock(&q->lock); if (q->len) { le = q->Alist.next; __list_del(le->prev, le->next); q->len--; m = container_of(le, struct mbuffer, Alist); } else m = NULL; pthread_mutex_unlock(&q->lock); return m; } static void Aqueue_remove(struct Aqueue *q, struct mbuffer *mb) { pthread_mutex_lock(&q->lock); __list_del(mb->Alist.prev, mb->Alist.next); q->len--; pthread_mutex_unlock(&q->lock); } #endif void init_mbuffer(int max_cache) { mqueue_init(&free_queue_l2); mqueue_init(&free_queue_l3); #ifdef MEMLEAK_DEBUG Aqueue_init(&AllocQueue); #endif if (max_cache < MIN_CACHE) max_cache = MIN_CACHE; Max_Cache = max_cache; } static void __mqueue_purge(struct mqueue *q) { struct mbuffer *mb; while ((mb = mdequeue(q))!=NULL) { if (mb->head) free(mb->head); if (mb->chead) free(mb->chead); free(mb); } } #ifdef MEMLEAK_DEBUG static struct mbuffer * _new_mbuffer(int typ, const char *file, int lineno, const char *func) { struct mbuffer *m; m = __mi_calloc(1, sizeof(struct mbuffer), file, lineno, func); if (!m) goto err; switch(typ) { case MB_TYP_L3: m->chead = malloc(MBUFFER_DATA_SIZE); if (!m->chead) { free(m); goto err; } m->cend = m->chead + MBUFFER_DATA_SIZE; m->ctail = m->chead; case MB_TYP_L2: m->head = malloc(MBUFFER_DATA_SIZE); if (!m->head) { if (m->chead) free(m->chead); free(m); goto err; } m->end = m->head + MBUFFER_DATA_SIZE; m->data = m->tail = m->head; m->h = (struct mISDNhead *) m->head; break; } return(m); err: eprint("%s: no mem for mbuffer\n", __FUNCTION__); return(NULL); } static struct mbuffer * _alloc_mbuffer(int typ, const char *file, int lineno, const char *func) { struct mbuffer *m; switch(typ) { case MB_TYP_L3: m = mdequeue(&free_queue_l3); break; case MB_TYP_L2: m = mdequeue(&free_queue_l2); break; default: eprint("%s: wrong type %d\n", __func__, typ); return NULL; } if (!m) m = _new_mbuffer(typ, file, lineno, func); else __mi_reuse(m, file, lineno, func); strncpy(m->d_fn, file, 79); m->d_ln = lineno; Aqueue_tail(&AllocQueue, m); return m; } struct mbuffer * __alloc_mbuffer(const char *file, int lineno, const char *func) { return _alloc_mbuffer(MB_TYP_L2, file, lineno, func); } void __free_mbuffer(struct mbuffer *m, const char *file, int lineno, const char *func) { if (!m) return; if (m->refcnt) { m->refcnt--; return; } Aqueue_remove(&AllocQueue, m); strncpy(m->d_fn, file, 79); m->d_ln = -lineno; if (m->list) { if (m->list == &free_queue_l3) dprint(DBGM_L3BUFFER, "%s l3 buffer %p already freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0)); else if (m->list == &free_queue_l2) dprint(DBGM_L3BUFFER, "%s l2 buffer %p already freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0)); else dprint(DBGM_L3BUFFER, "%s buffer %p still in list %p : %lx\n", __FUNCTION__, m, m->list, (unsigned long)__builtin_return_address(0)); return; } else dprint(DBGM_L3BUFFER, "%s buffer %p freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0)); if (m->chead) { if (free_queue_l3.len > Max_Cache) { free(m->chead); free(m->head); __mi_free(m, file, lineno, func); } else { memset(&m->l3, 0, sizeof(m->l3)); memset(&m->l3h, 0, sizeof(m->l3h)); m->data = m->tail = m->head; m->len = 0; m->ctail = m->chead; __mi_reuse(m, file, lineno, "IN_FREE_QUEUE"); mqueue_head(&free_queue_l3, m); } } else { if (free_queue_l2.len > Max_Cache) { free(m->head); __mi_free(m, file, lineno, func); } else { memset(&m->l3, 0, sizeof(m->l3)); memset(&m->l3h, 0, sizeof(m->l3h)); m->data = m->tail = m->head; m->len = 0; __mi_reuse(m, file, lineno, "IN_FREE_QUEUE"); mqueue_head(&free_queue_l2, m); } } } struct l3_msg * __alloc_l3_msg(const char *file, int lineno, const char *func) { struct mbuffer *m; m = _alloc_mbuffer(MB_TYP_L3, file, lineno, func); if (m) return &m->l3; else return NULL; } void __free_l3_msg(struct l3_msg *l3m, const char *file, int lineno, const char *func) { struct mbuffer *m; if (!l3m) return; m = container_of(l3m, struct mbuffer, l3); __free_mbuffer(m, file, lineno, func); } void cleanup_mbuffer(void) { struct mbuffer *m; int ql; __mqueue_purge(&free_queue_l2); __mqueue_purge(&free_queue_l3); ql = Amqueue_len(&AllocQueue); iprint("AllocQueue has %d lost mbuffer\n", ql); if (ql) { m = Adequeue(&AllocQueue); while (m) { wprint("Lost mbuffer allocated %s:%d typ=%s len=%d\n", m->d_fn, m->d_ln, m->chead ? "L3" : "L2", m->len); wprint(" H: prim=%s id=%d L3: prim=%s pid=%d\n", _mi_msg_type2str(m->h->prim), m->h->id, _mi_msg_type2str(m->l3.type), m->l3.pid); free_mbuffer(m); m = Adequeue(&AllocQueue); } } } #else static struct mbuffer * _new_mbuffer(int typ) { struct mbuffer *m; m = calloc(1, sizeof(struct mbuffer)); if (!m) goto err; switch(typ) { case MB_TYP_L3: m->chead = malloc(MBUFFER_DATA_SIZE); if (!m->chead) { free(m); goto err; } m->cend = m->chead + MBUFFER_DATA_SIZE; m->ctail = m->chead; /* FALLTHRU */ case MB_TYP_L2: m->head = malloc(MBUFFER_DATA_SIZE); if (!m->head) { if (m->chead) free(m->chead); free(m); goto err; } m->end = m->head + MBUFFER_DATA_SIZE; m->data = m->tail = m->head; m->h = (struct mISDNhead *) m->head; break; } return(m); err: eprint("%s: no mem for mbuffer\n", __FUNCTION__); return(NULL); } static struct mbuffer * _alloc_mbuffer(int typ) { struct mbuffer *m; switch(typ) { case MB_TYP_L3: m = mdequeue(&free_queue_l3); break; case MB_TYP_L2: m = mdequeue(&free_queue_l2); break; default: eprint("%s: wrong type %d\n", __func__, typ); return NULL; } if (!m) m = _new_mbuffer(typ); return m; } struct mbuffer * alloc_mbuffer(void) { return _alloc_mbuffer(MB_TYP_L2); } void free_mbuffer(struct mbuffer *m) { if (!m) return; if (m->refcnt) { m->refcnt--; return; } if (m->list) { if (m->list == &free_queue_l3) dprint(DBGM_L3BUFFER, "%s l3 buffer %p already freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0)); else if (m->list == &free_queue_l2) dprint(DBGM_L3BUFFER, "%s l2 buffer %p already freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0)); else dprint(DBGM_L3BUFFER, "%s buffer %p still in list %p : %lx\n", __FUNCTION__, m, m->list, (unsigned long)__builtin_return_address(0)); return; } else dprint(DBGM_L3BUFFER, "%s buffer %p freed: %lx\n", __FUNCTION__, m, (unsigned long)__builtin_return_address(0)); if (m->chead) { if (free_queue_l3.len > Max_Cache) { free(m->chead); free(m->head); free(m); } else { memset(&m->l3, 0, sizeof(m->l3)); memset(&m->l3h, 0, sizeof(m->l3h)); m->data = m->tail = m->head; m->len = 0; m->ctail = m->chead; mqueue_head(&free_queue_l3, m); } } else { if (free_queue_l2.len > Max_Cache) { free(m->head); free(m); } else { memset(&m->l3, 0, sizeof(m->l3)); memset(&m->l3h, 0, sizeof(m->l3h)); m->data = m->tail = m->head; m->len = 0; mqueue_head(&free_queue_l2, m); } } } struct l3_msg * alloc_l3_msg(void) { struct mbuffer *m; m = _alloc_mbuffer(MB_TYP_L3); if (m) return &m->l3; else return NULL; } void free_l3_msg(struct l3_msg *l3m) { struct mbuffer *m; if (!l3m) return; m = container_of(l3m, struct mbuffer, l3); free_mbuffer(m); } void cleanup_mbuffer(void) { __mqueue_purge(&free_queue_l2); __mqueue_purge(&free_queue_l3); } #endif void l3_msg_increment_refcnt(struct l3_msg *l3m) { struct mbuffer *m; m = container_of(l3m, struct mbuffer, l3); m->refcnt++; }