open5gs/lib/core/ogs-pkbuf.c

494 lines
14 KiB
C

/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ogs-core.h"
#undef OGS_LOG_DOMAIN
#define OGS_LOG_DOMAIN __ogs_mem_domain
#if OGS_USE_TALLOC == 0
#define OGS_CLUSTER_128_SIZE 128
#define OGS_CLUSTER_256_SIZE 256
#define OGS_CLUSTER_512_SIZE 512
#define OGS_CLUSTER_1024_SIZE 1024
#define OGS_CLUSTER_2048_SIZE 2048
#define OGS_CLUSTER_8192_SIZE 8192
#define OGS_CLUSTER_32768_SIZE 32768
/*
*
* In lib/core/ogs-kqueue.c:69
* context->change_list = ogs_calloc(
* pollset->capacity, sizeof(struct kevent));
* 1. pollset->capacity : 1024*16
* 2. sizeof(struct kevent) : 64
* 3. sizeof(ogs_pkbuf_t *) is headroom in ogs_calloc()
*
* So, we use BIG_SIZE : 1024*(16*64=1024)*64+8
*/
#define OGS_CLUSTER_BIG_SIZE (1024*1024+sizeof(ogs_pkbuf_t *))
typedef uint8_t ogs_cluster_128_t[OGS_CLUSTER_128_SIZE];
typedef uint8_t ogs_cluster_256_t[OGS_CLUSTER_256_SIZE];
typedef uint8_t ogs_cluster_512_t[OGS_CLUSTER_512_SIZE];
typedef uint8_t ogs_cluster_1024_t[OGS_CLUSTER_1024_SIZE];
typedef uint8_t ogs_cluster_2048_t[OGS_CLUSTER_2048_SIZE];
typedef uint8_t ogs_cluster_8192_t[OGS_CLUSTER_8192_SIZE];
typedef uint8_t ogs_cluster_32768_t[OGS_CLUSTER_32768_SIZE];
typedef uint8_t ogs_cluster_big_t[OGS_CLUSTER_BIG_SIZE];
OGS_STATIC_ASSERT(sizeof(ogs_cluster_128_t) % sizeof(void *) == 0);
OGS_STATIC_ASSERT(sizeof(ogs_cluster_256_t) % sizeof(void *) == 0);
OGS_STATIC_ASSERT(sizeof(ogs_cluster_512_t) % sizeof(void *) == 0);
OGS_STATIC_ASSERT(sizeof(ogs_cluster_1024_t) % sizeof(void *) == 0);
OGS_STATIC_ASSERT(sizeof(ogs_cluster_2048_t) % sizeof(void *) == 0);
OGS_STATIC_ASSERT(sizeof(ogs_cluster_8192_t) % sizeof(void *) == 0);
OGS_STATIC_ASSERT(sizeof(ogs_cluster_32768_t) % sizeof(void *) == 0);
OGS_STATIC_ASSERT(sizeof(ogs_cluster_big_t) % sizeof(void *) == 0);
typedef struct ogs_pkbuf_pool_s {
OGS_POOL(pkbuf, ogs_pkbuf_t);
OGS_POOL(cluster, ogs_cluster_t);
OGS_POOL(cluster_128, ogs_cluster_128_t);
OGS_POOL(cluster_256, ogs_cluster_256_t);
OGS_POOL(cluster_512, ogs_cluster_512_t);
OGS_POOL(cluster_1024, ogs_cluster_1024_t);
OGS_POOL(cluster_2048, ogs_cluster_2048_t);
OGS_POOL(cluster_8192, ogs_cluster_8192_t);
OGS_POOL(cluster_32768, ogs_cluster_32768_t);
OGS_POOL(cluster_big, ogs_cluster_big_t);
ogs_thread_mutex_t mutex;
} ogs_pkbuf_pool_t;
static OGS_POOL(pkbuf_pool, ogs_pkbuf_pool_t);
static ogs_pkbuf_pool_t *default_pool = NULL;
static ogs_cluster_t *cluster_alloc(
ogs_pkbuf_pool_t *pool, unsigned int size);
static void cluster_free(ogs_pkbuf_pool_t *pool, ogs_cluster_t *cluster);
#endif
void *ogs_pkbuf_put_data(
ogs_pkbuf_t *pkbuf, const void *data, unsigned int len)
{
void *tmp = ogs_pkbuf_put(pkbuf, len);
memcpy(tmp, data, len);
return tmp;
}
void ogs_pkbuf_init(void)
{
#if OGS_USE_TALLOC == 0
ogs_pool_init(&pkbuf_pool, ogs_core()->pkbuf.pool);
#endif
}
void ogs_pkbuf_final(void)
{
#if OGS_USE_TALLOC == 0
ogs_pool_final(&pkbuf_pool);
#endif
}
void ogs_pkbuf_default_init(ogs_pkbuf_config_t *config)
{
#if OGS_USE_TALLOC == 0
ogs_assert(config);
memset(config, 0, sizeof *config);
config->cluster_128_pool = 65536;
config->cluster_256_pool = 16384;
config->cluster_512_pool = 4096;
config->cluster_1024_pool = 2048;
config->cluster_2048_pool = 1024;
config->cluster_8192_pool = 256;
config->cluster_32768_pool = 64;
config->cluster_big_pool = 8;
#endif
}
void ogs_pkbuf_default_create(ogs_pkbuf_config_t *config)
{
#if OGS_USE_TALLOC == 0
default_pool = ogs_pkbuf_pool_create(config);
#endif
}
void ogs_pkbuf_default_destroy(void)
{
#if OGS_USE_TALLOC == 0
ogs_pkbuf_pool_destroy(default_pool);
#endif
}
ogs_pkbuf_pool_t *ogs_pkbuf_pool_create(ogs_pkbuf_config_t *config)
{
ogs_pkbuf_pool_t *pool = NULL;
#if OGS_USE_TALLOC == 0
int tmp = 0;
ogs_assert(config);
ogs_pool_alloc(&pkbuf_pool, &pool);
ogs_assert(pool);
memset(pool, 0, sizeof *pool);
ogs_thread_mutex_init(&pool->mutex);
tmp = config->cluster_128_pool + config->cluster_256_pool +
config->cluster_512_pool + config->cluster_1024_pool +
config->cluster_2048_pool + config->cluster_8192_pool +
config->cluster_32768_pool + config->cluster_big_pool;
ogs_pool_init(&pool->pkbuf, tmp);
ogs_pool_init(&pool->cluster, tmp);
ogs_pool_init(&pool->cluster_128, config->cluster_128_pool);
ogs_pool_init(&pool->cluster_256, config->cluster_256_pool);
ogs_pool_init(&pool->cluster_512, config->cluster_512_pool);
ogs_pool_init(&pool->cluster_1024, config->cluster_1024_pool);
ogs_pool_init(&pool->cluster_2048, config->cluster_2048_pool);
ogs_pool_init(&pool->cluster_8192, config->cluster_8192_pool);
ogs_pool_init(&pool->cluster_32768, config->cluster_32768_pool);
ogs_pool_init(&pool->cluster_big, config->cluster_big_pool);
#endif
return pool;
}
#define ogs_pkbuf_pool_final(pool) do { \
if (((pool)->size != (pool)->avail)) { \
int i; \
ogs_error("%d in '%s[%d]' were not released.", \
(pool)->size - (pool)->avail, (pool)->name, (pool)->size); \
for (i = 0; i < (pool)->size; i++) { \
ogs_pkbuf_t *pkbuf = (pool)->index[i]; \
if (pkbuf) { \
ogs_log_print(OGS_LOG_ERROR, "SIZE[%d] is not freed. (%s)\n", \
pkbuf->len, pkbuf->file_line); \
} \
} \
} \
free((pool)->free); \
free((pool)->array); \
free((pool)->index); \
} while (0)
void ogs_pkbuf_pool_destroy(ogs_pkbuf_pool_t *pool)
{
#if OGS_USE_TALLOC == 0
ogs_assert(pool);
ogs_pkbuf_pool_final(&pool->pkbuf);
ogs_pool_final(&pool->cluster);
ogs_pool_final(&pool->cluster_128);
ogs_pool_final(&pool->cluster_256);
ogs_pool_final(&pool->cluster_512);
ogs_pool_final(&pool->cluster_1024);
ogs_pool_final(&pool->cluster_2048);
ogs_pool_final(&pool->cluster_8192);
ogs_pool_final(&pool->cluster_32768);
ogs_pool_final(&pool->cluster_big);
ogs_thread_mutex_destroy(&pool->mutex);
ogs_pool_free(&pkbuf_pool, pool);
#endif
}
ogs_pkbuf_t *ogs_pkbuf_alloc_debug(
ogs_pkbuf_pool_t *pool, unsigned int size, const char *file_line)
{
#if OGS_USE_TALLOC == 1
ogs_pkbuf_t *pkbuf = NULL;
pkbuf = ogs_talloc_zero_size(pool, sizeof(*pkbuf) + size, file_line);
if (!pkbuf) {
ogs_error("ogs_pkbuf_alloc() failed [size=%d]", size);
return NULL;
}
pkbuf->head = pkbuf->_data;
pkbuf->end = pkbuf->_data + size;
pkbuf->len = 0;
pkbuf->data = pkbuf->_data;
pkbuf->tail = pkbuf->_data;
pkbuf->file_line = file_line; /* For debug */
return pkbuf;
#else
ogs_pkbuf_t *pkbuf = NULL;
ogs_cluster_t *cluster = NULL;
if (pool == NULL)
pool = default_pool;
ogs_assert(pool);
ogs_thread_mutex_lock(&pool->mutex);
cluster = cluster_alloc(pool, size);
if (!cluster) {
ogs_error("ogs_pkbuf_alloc() failed [size=%d]", size);
ogs_thread_mutex_unlock(&pool->mutex);
return NULL;
}
ogs_pool_alloc(&pool->pkbuf, &pkbuf);
if (!pkbuf) {
ogs_error("ogs_pkbuf_alloc() failed [size=%d]", size);
ogs_thread_mutex_unlock(&pool->mutex);
return NULL;
}
memset(pkbuf, 0, sizeof(*pkbuf));
OGS_OBJECT_REF(cluster);
pkbuf->cluster = cluster;
pkbuf->len = 0;
pkbuf->data = cluster->buffer;
pkbuf->head = cluster->buffer;
pkbuf->tail = cluster->buffer;
pkbuf->end = cluster->buffer + size;
pkbuf->file_line = file_line; /* For debug */
pkbuf->pool = pool;
ogs_thread_mutex_unlock(&pool->mutex);
return pkbuf;
#endif
}
void ogs_pkbuf_free(ogs_pkbuf_t *pkbuf)
{
#if OGS_USE_TALLOC == 1
ogs_talloc_free(pkbuf, OGS_FILE_LINE);
#else
ogs_pkbuf_pool_t *pool = NULL;
ogs_cluster_t *cluster = NULL;
ogs_assert(pkbuf);
pool = pkbuf->pool;
ogs_assert(pool);
ogs_thread_mutex_lock(&pool->mutex);
cluster = pkbuf->cluster;
ogs_assert(cluster);
if (OGS_OBJECT_IS_REF(cluster))
OGS_OBJECT_UNREF(cluster);
else
cluster_free(pool, pkbuf->cluster);
ogs_pool_free(&pool->pkbuf, pkbuf);
ogs_thread_mutex_unlock(&pool->mutex);
#endif
}
ogs_pkbuf_t *ogs_pkbuf_copy_debug(ogs_pkbuf_t *pkbuf, const char *file_line)
{
#if OGS_USE_TALLOC == 1
ogs_pkbuf_t *newbuf;
#else
ogs_pkbuf_pool_t *pool = NULL;
ogs_pkbuf_t *newbuf = NULL;
#endif
int size = 0;
ogs_assert(pkbuf);
size = pkbuf->end - pkbuf->head;
if (size <= 0) {
ogs_error("Invalid argument[size=%d, head=%p, end=%p] in (%s)",
size, pkbuf->head, pkbuf->end, file_line);
return NULL;
}
#if OGS_USE_TALLOC == 1
newbuf = ogs_pkbuf_alloc_debug(NULL, size, file_line);
if (!newbuf) {
ogs_error("ogs_pkbuf_alloc() failed [size=%d]", size);
return NULL;
}
/* copy data */
memcpy(newbuf->_data, pkbuf->_data, size);
/* copy header */
newbuf->len = pkbuf->len;
newbuf->tail += pkbuf->tail - pkbuf->_data;
newbuf->data += pkbuf->data - pkbuf->_data;
return newbuf;
#else
pool = pkbuf->pool;
ogs_assert(pool);
ogs_thread_mutex_lock(&pool->mutex);
ogs_pool_alloc(&pool->pkbuf, &newbuf);
if (!newbuf) {
ogs_error("ogs_pkbuf_copy() failed [size=%d]", size);
ogs_thread_mutex_unlock(&pool->mutex);
return NULL;
}
ogs_assert(newbuf);
memcpy(newbuf, pkbuf, sizeof *pkbuf);
OGS_OBJECT_REF(newbuf->cluster);
ogs_thread_mutex_unlock(&pool->mutex);
#endif
return newbuf;
}
#if OGS_USE_TALLOC == 0
static ogs_cluster_t *cluster_alloc(
ogs_pkbuf_pool_t *pool, unsigned int size)
{
ogs_cluster_t *cluster = NULL;
void *buffer = NULL;
ogs_assert(pool);
ogs_pool_alloc(&pool->cluster, &cluster);
ogs_assert(cluster);
memset(cluster, 0, sizeof(*cluster));
if (size <= OGS_CLUSTER_128_SIZE) {
ogs_pool_alloc(&pool->cluster_128, (ogs_cluster_128_t**)&buffer);
if (!buffer) {
ogs_error("ogs_pool_alloc() failed");
return NULL;
}
cluster->size = OGS_CLUSTER_128_SIZE;
} else if (size <= OGS_CLUSTER_256_SIZE) {
ogs_pool_alloc(&pool->cluster_256, (ogs_cluster_256_t**)&buffer);
if (!buffer) {
ogs_error("ogs_pool_alloc() failed");
return NULL;
}
cluster->size = OGS_CLUSTER_256_SIZE;
} else if (size <= OGS_CLUSTER_512_SIZE) {
ogs_pool_alloc(&pool->cluster_512, (ogs_cluster_512_t**)&buffer);
if (!buffer) {
ogs_error("ogs_pool_alloc() failed");
return NULL;
}
cluster->size = OGS_CLUSTER_512_SIZE;
} else if (size <= OGS_CLUSTER_1024_SIZE) {
ogs_pool_alloc(&pool->cluster_1024, (ogs_cluster_1024_t**)&buffer);
if (!buffer) {
ogs_error("ogs_pool_alloc() failed");
return NULL;
}
cluster->size = OGS_CLUSTER_1024_SIZE;
} else if (size <= OGS_CLUSTER_2048_SIZE) {
ogs_pool_alloc(&pool->cluster_2048, (ogs_cluster_2048_t**)&buffer);
if (!buffer) {
ogs_error("ogs_pool_alloc() failed");
return NULL;
}
cluster->size = OGS_CLUSTER_2048_SIZE;
} else if (size <= OGS_CLUSTER_8192_SIZE) {
ogs_pool_alloc(&pool->cluster_8192, (ogs_cluster_8192_t**)&buffer);
if (!buffer) {
ogs_error("ogs_pool_alloc() failed");
return NULL;
}
cluster->size = OGS_CLUSTER_8192_SIZE;
} else if (size <= OGS_CLUSTER_32768_SIZE) {
ogs_pool_alloc(&pool->cluster_32768, (ogs_cluster_32768_t**)&buffer);
if (!buffer) {
ogs_error("ogs_pool_alloc() failed");
return NULL;
}
cluster->size = OGS_CLUSTER_32768_SIZE;
} else if (size <= OGS_CLUSTER_BIG_SIZE) {
ogs_pool_alloc(&pool->cluster_big, (ogs_cluster_big_t**)&buffer);
if (!buffer) {
ogs_error("ogs_pool_alloc() failed");
return NULL;
}
cluster->size = OGS_CLUSTER_BIG_SIZE;
} else {
ogs_fatal("invalid size = %d", size);
ogs_assert_if_reached();
}
cluster->buffer = buffer;
return cluster;
}
static void cluster_free(ogs_pkbuf_pool_t *pool, ogs_cluster_t *cluster)
{
ogs_assert(pool);
ogs_assert(cluster);
ogs_assert(cluster->buffer);
switch (cluster->size) {
case OGS_CLUSTER_128_SIZE:
ogs_pool_free(&pool->cluster_128, (ogs_cluster_128_t*)cluster->buffer);
break;
case OGS_CLUSTER_256_SIZE:
ogs_pool_free(&pool->cluster_256, (ogs_cluster_256_t*)cluster->buffer);
break;
case OGS_CLUSTER_512_SIZE:
ogs_pool_free(&pool->cluster_512, (ogs_cluster_512_t*)cluster->buffer);
break;
case OGS_CLUSTER_1024_SIZE:
ogs_pool_free(
&pool->cluster_1024, (ogs_cluster_1024_t*)cluster->buffer);
break;
case OGS_CLUSTER_2048_SIZE:
ogs_pool_free(
&pool->cluster_2048, (ogs_cluster_2048_t*)cluster->buffer);
break;
case OGS_CLUSTER_8192_SIZE:
ogs_pool_free(
&pool->cluster_8192, (ogs_cluster_8192_t*)cluster->buffer);
break;
case OGS_CLUSTER_32768_SIZE:
ogs_pool_free(
&pool->cluster_32768, (ogs_cluster_32768_t*)cluster->buffer);
break;
case OGS_CLUSTER_BIG_SIZE:
ogs_pool_free(&pool->cluster_big, (ogs_cluster_big_t*)cluster->buffer);
break;
default:
ogs_assert_if_reached();
}
ogs_pool_free(&pool->cluster, cluster);
}
#endif