458 lines
9.6 KiB
C
458 lines
9.6 KiB
C
/* mbuffer.c
|
|
*
|
|
* Author Karsten Keil <kkeil@linux-pingi.de>
|
|
*
|
|
* Copyright 2007 by Karsten Keil <kkeil@novell.com>
|
|
* Copyright 2010 by Karsten Keil <kkeil@linux-pingi.de>
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <mISDN/mbuffer.h>
|
|
#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 <mISDN/q931.h>
|
|
|
|
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++;
|
|
}
|
|
|