2022-08-14 04:28:05 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* mm/mempool/mempool_multiple.c
|
|
|
|
*
|
|
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
|
|
* this work for additional information regarding copyright ownership. The
|
|
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
|
|
* "License"); you may not use this file except in compliance with the
|
|
|
|
* License. You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Included Files
|
|
|
|
****************************************************************************/
|
|
|
|
|
2022-12-22 14:18:09 +01:00
|
|
|
#include <strings.h>
|
2023-05-09 10:03:24 +02:00
|
|
|
#include <syslog.h>
|
2023-02-01 14:41:12 +01:00
|
|
|
#include <sys/param.h>
|
|
|
|
|
2022-12-22 14:18:09 +01:00
|
|
|
#include <nuttx/mutex.h>
|
2022-08-14 04:28:05 +02:00
|
|
|
#include <nuttx/kmalloc.h>
|
|
|
|
#include <nuttx/mm/mempool.h>
|
|
|
|
|
2023-03-10 01:20:48 +01:00
|
|
|
#include <assert.h>
|
|
|
|
|
2022-08-14 04:28:05 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Pre-processor Definitions
|
|
|
|
****************************************************************************/
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
#undef ALIGN_UP
|
|
|
|
#define ALIGN_UP(x, a) ((((size_t)x) + ((a) - 1)) & (~((a) - 1)))
|
|
|
|
#undef ALIGN_DOWN
|
|
|
|
#define ALIGN_DOWN(x, a) ((size_t)(x) & (~((a) - 1)))
|
2022-08-14 04:28:05 +02:00
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* Private Types
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
struct mpool_dict_s
|
2022-11-26 16:44:38 +01:00
|
|
|
{
|
2022-11-23 15:08:13 +01:00
|
|
|
FAR struct mempool_s *pool; /* Record pool when expanding */
|
|
|
|
FAR void *addr; /* Record expand memary address */
|
|
|
|
size_t size; /* Record expand memary size */
|
|
|
|
};
|
2022-11-26 16:44:38 +01:00
|
|
|
|
2023-05-26 04:42:50 +02:00
|
|
|
struct mpool_chunk_s
|
|
|
|
{
|
|
|
|
sq_entry_t entry;
|
|
|
|
FAR void *start;
|
|
|
|
FAR void *next;
|
|
|
|
FAR void *end;
|
|
|
|
size_t used;
|
|
|
|
};
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
struct mempool_multiple_s
|
|
|
|
{
|
2023-06-02 10:12:27 +02:00
|
|
|
FAR struct mempool_s *pools; /* The memory pool array */
|
|
|
|
size_t npools; /* The number of memory pool array elements */
|
|
|
|
size_t expandsize; /* The number not will use it to init erery
|
|
|
|
* pool expandsize
|
|
|
|
*/
|
|
|
|
size_t minpoolsize; /* The number is align for each memory pool */
|
|
|
|
FAR void *arg; /* This pointer is used to store the user's
|
|
|
|
* private data
|
|
|
|
*/
|
|
|
|
mempool_multiple_alloc_t alloc; /* The alloc function for mempool */
|
|
|
|
mempool_multiple_alloc_size_t alloc_size; /* Get the address size of the
|
|
|
|
* alloc function
|
|
|
|
*/
|
|
|
|
mempool_multiple_free_t free; /* The free function for mempool */
|
|
|
|
size_t alloced; /* Total size of alloc */
|
2022-11-26 16:44:38 +01:00
|
|
|
|
|
|
|
/* This delta describes the relationship between the block size of each
|
|
|
|
* mempool in multiple mempool by user initialized. It is automatically
|
|
|
|
* detected by the mempool_multiple_init function. If the delta is not
|
|
|
|
* equal to 0, the block size of the pool in the multiple mempool is an
|
|
|
|
* arithmetic progressions, otherwise it is an increasing progressions.
|
|
|
|
*/
|
|
|
|
|
2023-06-02 10:12:27 +02:00
|
|
|
size_t delta;
|
2022-11-23 15:08:13 +01:00
|
|
|
|
|
|
|
/* It is used to record the information recorded by the mempool during
|
|
|
|
* expansion, and find the mempool by adding an index
|
|
|
|
*/
|
|
|
|
|
2023-06-19 05:35:28 +02:00
|
|
|
rmutex_t lock;
|
2023-06-02 10:12:27 +02:00
|
|
|
sq_queue_t chunk_queue;
|
|
|
|
size_t chunk_size;
|
|
|
|
size_t dict_used;
|
|
|
|
size_t dict_col_num_log2;
|
|
|
|
size_t dict_row_num;
|
|
|
|
FAR struct mpool_dict_s **dict;
|
2022-11-26 16:44:38 +01:00
|
|
|
};
|
|
|
|
|
2022-08-14 04:28:05 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Private Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
2023-01-22 19:52:28 +01:00
|
|
|
static inline FAR struct mempool_s *
|
2022-08-14 04:28:05 +02:00
|
|
|
mempool_multiple_find(FAR struct mempool_multiple_s *mpool, size_t size)
|
|
|
|
{
|
2022-11-26 16:44:38 +01:00
|
|
|
size_t right;
|
2022-09-16 10:55:09 +02:00
|
|
|
size_t left = 0;
|
|
|
|
size_t mid;
|
2022-08-14 04:28:05 +02:00
|
|
|
|
2022-11-26 16:44:38 +01:00
|
|
|
if (mpool == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
right = mpool->npools;
|
2022-10-27 15:35:40 +02:00
|
|
|
if (mpool->delta != 0)
|
|
|
|
{
|
|
|
|
left = mpool->pools[0].blocksize;
|
2022-11-23 15:08:13 +01:00
|
|
|
if (left >= size)
|
|
|
|
{
|
|
|
|
return &mpool->pools[0];
|
|
|
|
}
|
|
|
|
|
2022-10-27 15:35:40 +02:00
|
|
|
mid = (size - left + mpool->delta - 1) / mpool->delta;
|
|
|
|
return mid < right ? &mpool->pools[mid] : NULL;
|
|
|
|
}
|
|
|
|
|
2022-09-16 10:55:09 +02:00
|
|
|
while (left < right)
|
2022-08-14 04:28:05 +02:00
|
|
|
{
|
2022-09-16 10:55:09 +02:00
|
|
|
mid = (left + right) >> 1;
|
2022-10-28 16:42:14 +02:00
|
|
|
if (mpool->pools[mid].blocksize > size)
|
2022-08-14 04:28:05 +02:00
|
|
|
{
|
2022-09-16 10:55:09 +02:00
|
|
|
right = mid;
|
2022-08-14 04:28:05 +02:00
|
|
|
}
|
2022-09-16 10:55:09 +02:00
|
|
|
else
|
2022-08-14 04:28:05 +02:00
|
|
|
{
|
2022-09-16 10:55:09 +02:00
|
|
|
left = mid + 1;
|
2022-08-14 04:28:05 +02:00
|
|
|
}
|
|
|
|
}
|
2022-09-16 10:55:09 +02:00
|
|
|
|
|
|
|
if (left == mpool->npools)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return &mpool->pools[left];
|
2022-08-14 04:28:05 +02:00
|
|
|
}
|
|
|
|
|
2023-05-26 04:42:50 +02:00
|
|
|
static FAR void *
|
|
|
|
mempool_multiple_alloc_chunk(FAR struct mempool_multiple_s *mpool,
|
|
|
|
size_t align, size_t size)
|
|
|
|
{
|
|
|
|
FAR struct mpool_chunk_s *chunk;
|
|
|
|
FAR char *tmp;
|
|
|
|
FAR void *ret;
|
|
|
|
|
|
|
|
if (mpool->chunk_size < mpool->expandsize)
|
|
|
|
{
|
2023-06-02 10:12:27 +02:00
|
|
|
ret = mpool->alloc(mpool->arg, align, size);
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
mpool->alloced += mpool->alloc_size(mpool->arg, ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2023-05-26 04:42:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
chunk = (FAR struct mpool_chunk_s *)sq_peek(&mpool->chunk_queue);
|
|
|
|
if (chunk == NULL)
|
|
|
|
{
|
|
|
|
retry:
|
|
|
|
tmp = mpool->alloc(mpool->arg, mpool->expandsize,
|
|
|
|
mpool->chunk_size +
|
|
|
|
sizeof(struct mpool_chunk_s));
|
|
|
|
|
|
|
|
if (tmp == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-06-02 10:12:27 +02:00
|
|
|
mpool->alloced += mpool->alloc_size(mpool->arg, tmp);
|
2023-05-26 04:42:50 +02:00
|
|
|
chunk = (FAR struct mpool_chunk_s *)(tmp + mpool->chunk_size);
|
|
|
|
chunk->end = tmp + mpool->chunk_size;
|
|
|
|
chunk->start = tmp;
|
|
|
|
chunk->next = tmp;
|
|
|
|
chunk->used = 0;
|
|
|
|
sq_addfirst(&chunk->entry, &mpool->chunk_queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = (FAR void *)ALIGN_UP(chunk->next, align);
|
2023-07-13 16:21:47 +02:00
|
|
|
if ((uintptr_t)chunk->end - (uintptr_t)ret < size)
|
2023-05-26 04:42:50 +02:00
|
|
|
{
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
chunk->used++;
|
2023-07-13 16:21:47 +02:00
|
|
|
chunk->next = (FAR char *)ret + size;
|
2023-05-26 04:42:50 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mempool_multiple_free_chunk(FAR struct mempool_multiple_s *mpool,
|
|
|
|
FAR void *ptr)
|
|
|
|
{
|
|
|
|
FAR struct mpool_chunk_s *chunk;
|
|
|
|
FAR sq_entry_t *entry;
|
|
|
|
|
|
|
|
if (mpool->chunk_size < mpool->expandsize)
|
|
|
|
{
|
|
|
|
mpool->free(mpool->arg, ptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-06-19 05:35:28 +02:00
|
|
|
nxrmutex_lock(&mpool->lock);
|
2023-05-26 04:42:50 +02:00
|
|
|
sq_for_every(&mpool->chunk_queue, entry)
|
|
|
|
{
|
|
|
|
chunk = (FAR struct mpool_chunk_s *)entry;
|
|
|
|
if (ptr >= chunk->start && ptr < chunk->next)
|
|
|
|
{
|
|
|
|
if (--chunk->used == 0)
|
|
|
|
{
|
|
|
|
sq_rem(&chunk->entry, &mpool->chunk_queue);
|
|
|
|
mpool->free(mpool->arg, chunk->start);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-19 05:35:28 +02:00
|
|
|
nxrmutex_unlock(&mpool->lock);
|
2023-05-26 04:42:50 +02:00
|
|
|
}
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
static FAR void *mempool_multiple_alloc_callback(FAR struct mempool_s *pool,
|
|
|
|
size_t size)
|
|
|
|
{
|
|
|
|
FAR struct mempool_multiple_s *mpool = pool->priv;
|
|
|
|
FAR void *ret;
|
2022-12-22 14:18:09 +01:00
|
|
|
size_t row;
|
|
|
|
size_t col;
|
2022-11-23 15:08:13 +01:00
|
|
|
|
2023-06-19 05:35:28 +02:00
|
|
|
nxrmutex_lock(&mpool->lock);
|
2023-05-26 04:42:50 +02:00
|
|
|
ret = mempool_multiple_alloc_chunk(mpool, mpool->expandsize,
|
|
|
|
mpool->minpoolsize + size);
|
2022-11-23 15:08:13 +01:00
|
|
|
if (ret == NULL)
|
|
|
|
{
|
2023-06-19 05:35:28 +02:00
|
|
|
nxrmutex_unlock(&mpool->lock);
|
2022-11-23 15:08:13 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-12-22 14:18:09 +01:00
|
|
|
row = mpool->dict_used >> mpool->dict_col_num_log2;
|
|
|
|
|
2023-11-24 16:56:47 +01:00
|
|
|
/* There is no new pointer address to store the dictionaries */
|
2022-12-22 14:18:09 +01:00
|
|
|
|
|
|
|
DEBUGASSERT(mpool->dict_row_num > row);
|
|
|
|
|
|
|
|
col = mpool->dict_used - (row << mpool->dict_col_num_log2);
|
|
|
|
|
|
|
|
if (mpool->dict[row] == NULL)
|
|
|
|
{
|
2023-05-26 04:42:50 +02:00
|
|
|
mpool->dict[row] =
|
|
|
|
mempool_multiple_alloc_chunk(mpool, sizeof(uintptr_t),
|
|
|
|
(1 << mpool->dict_col_num_log2)
|
|
|
|
* sizeof(struct mpool_dict_s));
|
2022-12-22 14:18:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
mpool->dict[row][col].pool = pool;
|
|
|
|
mpool->dict[row][col].addr = ret;
|
|
|
|
mpool->dict[row][col].size = mpool->minpoolsize + size;
|
2022-11-23 15:08:13 +01:00
|
|
|
*(FAR size_t *)ret = mpool->dict_used++;
|
2023-06-19 05:35:28 +02:00
|
|
|
nxrmutex_unlock(&mpool->lock);
|
2022-11-23 15:08:13 +01:00
|
|
|
return (FAR char *)ret + mpool->minpoolsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mempool_multiple_free_callback(FAR struct mempool_s *pool,
|
|
|
|
FAR void *addr)
|
|
|
|
{
|
|
|
|
FAR struct mempool_multiple_s *mpool = pool->priv;
|
|
|
|
|
2023-05-26 04:42:50 +02:00
|
|
|
mempool_multiple_free_chunk(mpool,
|
|
|
|
(FAR char *)addr - mpool->minpoolsize);
|
2022-11-23 15:08:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: mempool_multiple_get_dict
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Obtain the dict through mpool and blk
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mpool - The handle of the multiple memory pool to be used.
|
|
|
|
* blk - The pointer of memory block.
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* Address of the dict to be used or NULL is not find.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
static FAR struct mpool_dict_s *
|
|
|
|
mempool_multiple_get_dict(FAR struct mempool_multiple_s *mpool,
|
|
|
|
FAR void *blk)
|
|
|
|
{
|
|
|
|
FAR void *addr;
|
|
|
|
size_t index;
|
2022-12-22 14:18:09 +01:00
|
|
|
size_t row;
|
|
|
|
size_t col;
|
2022-11-23 15:08:13 +01:00
|
|
|
|
|
|
|
if (mpool == NULL || blk == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr = (FAR void *)ALIGN_DOWN(blk, mpool->expandsize);
|
|
|
|
|
|
|
|
index = *(FAR size_t *)addr;
|
|
|
|
if (index >= mpool->dict_used)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-12-22 14:18:09 +01:00
|
|
|
row = index >> mpool->dict_col_num_log2;
|
|
|
|
col = index - (row << mpool->dict_col_num_log2);
|
|
|
|
if (mpool->dict[row] == NULL ||
|
|
|
|
mpool->dict[row][col].addr != addr ||
|
2023-01-18 11:36:48 +01:00
|
|
|
(FAR char *)blk - (FAR char *)addr >= mpool->dict[row][col].size)
|
2022-11-23 15:08:13 +01:00
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-12-22 14:18:09 +01:00
|
|
|
return &mpool->dict[row][col];
|
2022-11-23 15:08:13 +01:00
|
|
|
}
|
|
|
|
|
2022-08-14 04:28:05 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Public Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: mempool_multiple_init
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Initialize multiple memory pool, each element represents a memory pool.
|
|
|
|
* The user needs to specify the initialization information of each mempool
|
2022-10-28 16:42:14 +02:00
|
|
|
* in the array, including blocksize, initialsize, expandsize,
|
|
|
|
* interruptsize, wait. These mempool will be initialized by mempool_init.
|
|
|
|
* The name of all mempool are "name".
|
2022-08-14 04:28:05 +02:00
|
|
|
*
|
2022-10-27 15:35:40 +02:00
|
|
|
* This function will initialize the member delta by detecting the
|
|
|
|
* relationship between the each block size of mempool in multiple mempool.
|
|
|
|
*
|
2022-08-14 04:28:05 +02:00
|
|
|
* Input Parameters:
|
2022-12-22 14:18:09 +01:00
|
|
|
* name - The name of memory pool.
|
|
|
|
* poolsize - The block size array for pools in multiples pool.
|
|
|
|
* npools - How many pools in multiples pool.
|
|
|
|
* alloc - The alloc memory function for multiples pool.
|
2023-06-02 10:12:27 +02:00
|
|
|
* alloc_size - Get the address size of the alloc function.
|
2022-12-22 14:18:09 +01:00
|
|
|
* free - The free memory function for multiples pool.
|
2023-11-24 16:56:47 +01:00
|
|
|
* arg - The alloc & free memory functions used arg.
|
2023-05-26 04:42:50 +02:00
|
|
|
* chunksize - The multiples pool chunk size.
|
2023-11-24 16:56:47 +01:00
|
|
|
* expandsize - The expand memory for all pools in multiples pool.
|
|
|
|
* dict_expendsize - The expand size for multiple dictionaries.
|
2022-08-14 04:28:05 +02:00
|
|
|
* Returned Value:
|
2022-11-26 16:44:38 +01:00
|
|
|
* Return an initialized multiple pool pointer on success,
|
|
|
|
* otherwise NULL is returned.
|
2022-08-14 04:28:05 +02:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2022-11-26 16:44:38 +01:00
|
|
|
FAR struct mempool_multiple_s *
|
|
|
|
mempool_multiple_init(FAR const char *name,
|
|
|
|
FAR size_t *poolsize, size_t npools,
|
|
|
|
mempool_multiple_alloc_t alloc,
|
2023-06-02 10:12:27 +02:00
|
|
|
mempool_multiple_alloc_size_t alloc_size,
|
2023-05-26 04:42:50 +02:00
|
|
|
mempool_multiple_free_t free, FAR void *arg,
|
|
|
|
size_t chunksize, size_t expandsize,
|
2023-04-18 05:18:43 +02:00
|
|
|
size_t dict_expendsize)
|
2022-08-14 04:28:05 +02:00
|
|
|
{
|
2022-11-26 16:44:38 +01:00
|
|
|
FAR struct mempool_multiple_s *mpool;
|
|
|
|
FAR struct mempool_s *pools;
|
2022-11-23 15:08:13 +01:00
|
|
|
size_t maxpoolszie;
|
|
|
|
size_t minpoolsize;
|
2022-11-26 16:44:38 +01:00
|
|
|
int ret;
|
|
|
|
int i;
|
2022-08-14 04:28:05 +02:00
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
if (expandsize & (expandsize - 1))
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
maxpoolszie = poolsize[0];
|
|
|
|
minpoolsize = poolsize[0];
|
|
|
|
for (i = 0; i < npools; i++)
|
|
|
|
{
|
|
|
|
if (maxpoolszie < poolsize[i])
|
|
|
|
{
|
|
|
|
maxpoolszie = poolsize[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (minpoolsize > poolsize[i])
|
|
|
|
{
|
|
|
|
minpoolsize = poolsize[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-26 16:44:38 +01:00
|
|
|
mpool = alloc(arg, sizeof(uintptr_t), sizeof(struct mempool_multiple_s));
|
|
|
|
if (mpool == NULL)
|
2022-10-27 15:35:40 +02:00
|
|
|
{
|
2022-11-26 16:44:38 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2022-10-27 15:35:40 +02:00
|
|
|
|
2023-06-02 10:12:27 +02:00
|
|
|
mpool->alloc_size = alloc_size;
|
2023-05-26 04:42:50 +02:00
|
|
|
mpool->expandsize = expandsize;
|
|
|
|
mpool->chunk_size = chunksize;
|
|
|
|
mpool->alloc = alloc;
|
|
|
|
mpool->free = free;
|
|
|
|
mpool->arg = arg;
|
2023-06-02 10:12:27 +02:00
|
|
|
mpool->alloced = alloc_size(arg, mpool);
|
2023-05-26 04:42:50 +02:00
|
|
|
sq_init(&mpool->chunk_queue);
|
|
|
|
pools = mempool_multiple_alloc_chunk(mpool, sizeof(uintptr_t),
|
|
|
|
npools * sizeof(struct mempool_s));
|
2022-11-26 16:44:38 +01:00
|
|
|
if (pools == NULL)
|
|
|
|
{
|
|
|
|
goto err_with_mpool;
|
2022-10-27 15:35:40 +02:00
|
|
|
}
|
|
|
|
|
2022-11-26 16:44:38 +01:00
|
|
|
mpool->pools = pools;
|
|
|
|
mpool->npools = npools;
|
2022-11-23 15:08:13 +01:00
|
|
|
mpool->minpoolsize = minpoolsize;
|
2023-05-18 06:17:46 +02:00
|
|
|
mpool->delta = 0;
|
2022-11-26 16:44:38 +01:00
|
|
|
|
|
|
|
for (i = 0; i < npools; i++)
|
2022-08-14 04:28:05 +02:00
|
|
|
{
|
2022-11-26 16:44:38 +01:00
|
|
|
pools[i].blocksize = poolsize[i];
|
2022-11-23 15:08:13 +01:00
|
|
|
pools[i].expandsize = expandsize - mpool->minpoolsize;
|
2022-11-26 16:44:38 +01:00
|
|
|
pools[i].initialsize = 0;
|
|
|
|
pools[i].interruptsize = 0;
|
2022-11-23 15:08:13 +01:00
|
|
|
pools[i].priv = mpool;
|
|
|
|
pools[i].alloc = mempool_multiple_alloc_callback;
|
|
|
|
pools[i].free = mempool_multiple_free_callback;
|
2022-11-26 16:44:38 +01:00
|
|
|
ret = mempool_init(pools + i, name);
|
2022-08-14 04:28:05 +02:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
2022-11-26 16:44:38 +01:00
|
|
|
goto err_with_pools;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i + 1 != npools)
|
|
|
|
{
|
2023-05-18 06:17:46 +02:00
|
|
|
size_t delta = poolsize[i + 1] - poolsize[i];
|
2022-11-26 16:44:38 +01:00
|
|
|
|
|
|
|
if (i == 0)
|
|
|
|
{
|
|
|
|
mpool->delta = delta;
|
|
|
|
}
|
|
|
|
else if (delta != mpool->delta)
|
|
|
|
{
|
|
|
|
mpool->delta = 0;
|
|
|
|
}
|
2022-08-14 04:28:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
mpool->dict_used = 0;
|
2022-12-22 14:18:09 +01:00
|
|
|
mpool->dict_col_num_log2 = fls(dict_expendsize /
|
|
|
|
sizeof(struct mpool_dict_s));
|
|
|
|
|
2023-05-26 04:42:50 +02:00
|
|
|
mpool->dict_row_num = dict_expendsize / sizeof(FAR struct mpool_dict_s *);
|
|
|
|
mpool->dict = mempool_multiple_alloc_chunk(
|
|
|
|
mpool, sizeof(FAR struct mpool_dict_s *),
|
|
|
|
sizeof(FAR struct mpool_dict_s *) * mpool->dict_row_num);
|
2022-11-23 15:08:13 +01:00
|
|
|
if (mpool->dict == NULL)
|
|
|
|
{
|
|
|
|
goto err_with_pools;
|
|
|
|
}
|
|
|
|
|
2022-12-22 14:18:09 +01:00
|
|
|
memset(mpool->dict, 0,
|
2023-05-26 04:42:50 +02:00
|
|
|
mpool->dict_row_num * sizeof(FAR struct mpool_dict_s *));
|
2023-06-19 05:35:28 +02:00
|
|
|
nxrmutex_init(&mpool->lock);
|
2022-12-22 14:18:09 +01:00
|
|
|
|
2022-11-26 16:44:38 +01:00
|
|
|
return mpool;
|
|
|
|
|
|
|
|
err_with_pools:
|
2022-11-23 15:08:13 +01:00
|
|
|
while (--i >= 0)
|
|
|
|
{
|
|
|
|
mempool_deinit(pools + i);
|
|
|
|
}
|
|
|
|
|
2023-05-26 04:42:50 +02:00
|
|
|
mempool_multiple_free_chunk(mpool, pools);
|
2022-11-26 16:44:38 +01:00
|
|
|
err_with_mpool:
|
|
|
|
free(arg, mpool);
|
|
|
|
return NULL;
|
2022-08-14 04:28:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: mempool_multiple_alloc
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Allocate an block from specific multiple memory pool.
|
|
|
|
* If the mempool of the corresponding size doesn't have free block,
|
|
|
|
* it will continue to alloc memory for a larger memory pool until last
|
|
|
|
* mempool in multiple mempools.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mpool - The handle of multiple memory pool to be used.
|
|
|
|
* size - The size of alloc blk.
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* The pointer to the allocated block on success; NULL on any failure.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
FAR void *mempool_multiple_alloc(FAR struct mempool_multiple_s *mpool,
|
|
|
|
size_t size)
|
|
|
|
{
|
2022-11-23 15:08:13 +01:00
|
|
|
FAR struct mempool_s *end;
|
2022-08-14 04:28:05 +02:00
|
|
|
FAR struct mempool_s *pool;
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
pool = mempool_multiple_find(mpool, size);
|
2022-09-16 10:55:09 +02:00
|
|
|
if (pool == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
end = mpool->pools + mpool->npools;
|
2022-09-09 10:01:49 +02:00
|
|
|
do
|
2022-08-14 04:28:05 +02:00
|
|
|
{
|
2022-09-09 10:01:49 +02:00
|
|
|
FAR void *blk = mempool_alloc(pool);
|
2022-11-23 15:08:13 +01:00
|
|
|
|
|
|
|
if (blk)
|
2022-08-14 04:28:05 +02:00
|
|
|
{
|
2022-11-23 15:08:13 +01:00
|
|
|
return blk;
|
2022-08-14 04:28:05 +02:00
|
|
|
}
|
|
|
|
}
|
2022-09-16 09:35:08 +02:00
|
|
|
while (++pool < end);
|
2022-08-14 04:28:05 +02:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-09-16 09:35:08 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: mempool_multiple_realloc
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Change the size of the block memory pointed to by oldblk to size bytes.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mpool - The handle of multiple memory pool to be used.
|
|
|
|
* oldblk - The pointer to change the size of the block memory.
|
|
|
|
* size - The size of alloc blk.
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* The pointer to the allocated block on success; NULL on any failure.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
FAR void *mempool_multiple_realloc(FAR struct mempool_multiple_s *mpool,
|
|
|
|
FAR void *oldblk, size_t size)
|
|
|
|
{
|
2022-11-23 15:08:13 +01:00
|
|
|
FAR struct mpool_dict_s *dict;
|
2022-09-16 09:35:08 +02:00
|
|
|
FAR void *blk;
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
if (oldblk == NULL)
|
|
|
|
{
|
|
|
|
return mempool_multiple_alloc(mpool, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
dict = mempool_multiple_get_dict(mpool, oldblk);
|
|
|
|
if (dict == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-09-16 09:35:08 +02:00
|
|
|
blk = mempool_multiple_alloc(mpool, size);
|
|
|
|
if (blk != NULL && oldblk != NULL)
|
|
|
|
{
|
2022-11-23 15:08:13 +01:00
|
|
|
size = MIN(size, dict->pool->blocksize);
|
2022-10-31 08:09:47 +01:00
|
|
|
memcpy(blk, oldblk, size);
|
2022-09-16 09:35:08 +02:00
|
|
|
mempool_multiple_free(mpool, oldblk);
|
|
|
|
}
|
|
|
|
|
|
|
|
return blk;
|
|
|
|
}
|
|
|
|
|
2022-08-14 04:28:05 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: mempool_multiple_free
|
|
|
|
*
|
|
|
|
* Description:
|
2023-11-24 16:56:47 +01:00
|
|
|
* Release a memory block to the multiple memory pool. The blk must have
|
2022-08-14 04:28:05 +02:00
|
|
|
* been returned by a previous call to mempool_multiple_alloc.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mpool - The handle of multiple memory pool to be used.
|
|
|
|
* blk - The pointer of memory block.
|
2022-11-23 15:08:13 +01:00
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* Zero on success; Negative number on any failure.
|
|
|
|
*
|
2022-08-14 04:28:05 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
int mempool_multiple_free(FAR struct mempool_multiple_s *mpool,
|
|
|
|
FAR void *blk)
|
2022-08-14 04:28:05 +02:00
|
|
|
{
|
2022-11-23 15:08:13 +01:00
|
|
|
FAR struct mpool_dict_s *dict;
|
2022-08-14 04:28:05 +02:00
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
dict = mempool_multiple_get_dict(mpool, blk);
|
|
|
|
if (dict == NULL)
|
2022-10-31 08:09:47 +01:00
|
|
|
{
|
2022-11-23 15:08:13 +01:00
|
|
|
return -EINVAL;
|
2022-10-31 08:09:47 +01:00
|
|
|
}
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
blk = (FAR char *)blk - (((FAR char *)blk -
|
|
|
|
((FAR char *)dict->addr + mpool->minpoolsize)) %
|
2023-02-01 13:00:25 +01:00
|
|
|
MEMPOOL_REALBLOCKSIZE(dict->pool));
|
2022-11-23 15:08:13 +01:00
|
|
|
mempool_free(dict->pool, blk);
|
|
|
|
return 0;
|
2022-08-14 04:28:05 +02:00
|
|
|
}
|
|
|
|
|
2022-09-16 09:35:08 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: mempool_multiple_alloc_size
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Get size of memory block from multiple memory.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
2022-11-24 06:46:53 +01:00
|
|
|
* mpool - The handle of multiple memory pool to be used.
|
2022-09-16 09:35:08 +02:00
|
|
|
* blk - The pointer of memory block.
|
|
|
|
*
|
|
|
|
* Returned Value:
|
2022-11-23 15:08:13 +01:00
|
|
|
* The size of memory block on success. Negative number on any failure.
|
2022-09-16 09:35:08 +02:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
ssize_t mempool_multiple_alloc_size(FAR struct mempool_multiple_s *mpool,
|
|
|
|
FAR void *blk)
|
2022-09-16 09:35:08 +02:00
|
|
|
{
|
2022-11-23 15:08:13 +01:00
|
|
|
FAR struct mpool_dict_s *dict;
|
2022-09-16 09:35:08 +02:00
|
|
|
|
|
|
|
DEBUGASSERT(blk != NULL);
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
dict = mempool_multiple_get_dict(mpool, blk);
|
|
|
|
if (dict == NULL)
|
2022-10-31 08:09:47 +01:00
|
|
|
{
|
2022-11-23 15:08:13 +01:00
|
|
|
return -EINVAL;
|
2022-10-31 08:09:47 +01:00
|
|
|
}
|
2022-11-23 15:08:13 +01:00
|
|
|
|
|
|
|
return dict->pool->blocksize;
|
2022-10-31 08:09:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: mempool_multiple_memalign
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* This function requests more than enough space from malloc, finds a
|
|
|
|
* region within that chunk that meets the alignment request.
|
|
|
|
*
|
|
|
|
* The alignment argument must be a power of two.
|
|
|
|
*
|
|
|
|
* The memalign is special to multiple mempool because multiple mempool
|
|
|
|
* doesn't support split and shrink chunk operate. So When you alloc a
|
|
|
|
* memory block and find an aligned address in this block, you need to
|
|
|
|
* occupy 8 bytes before the address to save the address of the padding
|
|
|
|
* size and pool to ensure correct use in realloc and free operations.
|
|
|
|
* So we will use bit1 in the previous address of the address to represent
|
|
|
|
* that it is applied by memalign.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mpool - The handle of multiple memory pool to be used.
|
|
|
|
* alignment - The alignment request of memory block.
|
|
|
|
* size - The size of alloc blk.
|
|
|
|
*
|
|
|
|
* Returned Value:
|
|
|
|
* The size of memory block.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
FAR void *mempool_multiple_memalign(FAR struct mempool_multiple_s *mpool,
|
|
|
|
size_t alignment, size_t size)
|
|
|
|
{
|
2022-11-23 15:08:13 +01:00
|
|
|
FAR struct mempool_s *end;
|
2022-10-31 08:09:47 +01:00
|
|
|
FAR struct mempool_s *pool;
|
|
|
|
|
|
|
|
DEBUGASSERT((alignment & (alignment - 1)) == 0);
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
pool = mempool_multiple_find(mpool, size + alignment);
|
2022-10-31 08:09:47 +01:00
|
|
|
if (pool == NULL)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-11-23 15:08:13 +01:00
|
|
|
end = mpool->pools + mpool->npools;
|
2022-10-31 08:09:47 +01:00
|
|
|
do
|
|
|
|
{
|
|
|
|
FAR char *blk = mempool_alloc(pool);
|
|
|
|
if (blk != NULL)
|
|
|
|
{
|
2022-11-23 15:08:13 +01:00
|
|
|
return (FAR void *)ALIGN_UP(blk, alignment);
|
2022-10-31 08:09:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
while (++pool < end);
|
|
|
|
|
|
|
|
return NULL;
|
2022-09-16 09:35:08 +02:00
|
|
|
}
|
|
|
|
|
2023-05-09 10:03:24 +02:00
|
|
|
/****************************************************************************
|
2023-05-12 13:39:27 +02:00
|
|
|
* Name: mempool_multiple_foreach
|
2023-05-09 10:03:24 +02:00
|
|
|
****************************************************************************/
|
|
|
|
|
2023-05-12 13:39:27 +02:00
|
|
|
void mempool_multiple_foreach(FAR struct mempool_multiple_s *mpool,
|
|
|
|
mempool_multiple_foreach_t handle,
|
|
|
|
FAR void *arg)
|
2023-05-09 10:03:24 +02:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
for (i = 0; i < mpool->npools; i++)
|
|
|
|
{
|
2023-05-12 13:39:27 +02:00
|
|
|
handle(mpool->pools + i, arg);
|
2023-05-09 10:03:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-02 10:12:27 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: mempool_multiple_mallinfo
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
struct mallinfo
|
|
|
|
mempool_multiple_mallinfo(FAR struct mempool_multiple_s *mpool)
|
|
|
|
{
|
|
|
|
struct mallinfo info;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
memset(&info, 0, sizeof(struct mallinfo));
|
|
|
|
|
2023-06-19 05:35:28 +02:00
|
|
|
nxrmutex_lock(&mpool->lock);
|
2023-06-02 10:12:27 +02:00
|
|
|
info.arena = mpool->alloced;
|
|
|
|
|
|
|
|
if (mpool->chunk_size >= mpool->expandsize)
|
|
|
|
{
|
|
|
|
FAR struct mpool_chunk_s *chunk;
|
|
|
|
|
|
|
|
chunk = (FAR struct mpool_chunk_s *)sq_peek(&mpool->chunk_queue);
|
2023-07-13 16:21:47 +02:00
|
|
|
info.fordblks += (uintptr_t)chunk->end - (uintptr_t)chunk->next;
|
2023-06-02 10:12:27 +02:00
|
|
|
}
|
|
|
|
|
2023-06-19 05:35:28 +02:00
|
|
|
nxrmutex_unlock(&mpool->lock);
|
2023-06-02 10:12:27 +02:00
|
|
|
|
|
|
|
for (i = 0; i < mpool->npools; i++)
|
|
|
|
{
|
|
|
|
struct mempoolinfo_s poolinfo;
|
|
|
|
|
|
|
|
mempool_info(mpool->pools + i, &poolinfo);
|
|
|
|
info.fordblks += (poolinfo.ordblks + poolinfo.iordblks)
|
|
|
|
* poolinfo.sizeblks;
|
|
|
|
info.ordblks += poolinfo.ordblks + poolinfo.iordblks;
|
|
|
|
info.aordblks += poolinfo.aordblks;
|
|
|
|
if (info.mxordblk < poolinfo.sizeblks)
|
|
|
|
{
|
|
|
|
info.mxordblk = poolinfo.sizeblks;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
info.uordblks += mpool->alloced - info.fordblks;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2022-11-06 16:27:44 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: mempool_multiple_info_task
|
|
|
|
****************************************************************************/
|
|
|
|
|
2023-05-28 13:30:37 +02:00
|
|
|
struct mallinfo_task
|
2023-05-05 10:52:30 +02:00
|
|
|
mempool_multiple_info_task(FAR struct mempool_multiple_s *mpool,
|
2023-05-28 13:23:56 +02:00
|
|
|
FAR const struct malltask *task)
|
2022-11-06 16:27:44 +01:00
|
|
|
{
|
2023-05-05 10:52:30 +02:00
|
|
|
int i;
|
2023-05-28 13:30:37 +02:00
|
|
|
struct mallinfo_task info;
|
|
|
|
struct mallinfo_task ret =
|
2023-05-05 10:52:30 +02:00
|
|
|
{
|
|
|
|
0, 0
|
|
|
|
};
|
2022-11-06 16:27:44 +01:00
|
|
|
|
|
|
|
for (i = 0; i < mpool->npools; i++)
|
|
|
|
{
|
2023-05-28 13:23:56 +02:00
|
|
|
info = mempool_info_task(mpool->pools + i, task);
|
2023-05-05 10:52:30 +02:00
|
|
|
ret.aordblks += info.aordblks;
|
|
|
|
ret.uordblks += info.uordblks;
|
2022-11-06 16:27:44 +01:00
|
|
|
}
|
2023-05-05 10:52:30 +02:00
|
|
|
|
|
|
|
return ret;
|
2022-11-06 16:27:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Name: mempool_multiple_memdump
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* mempool_multiple_memdump returns a memory info about specified pid of
|
|
|
|
* task/thread. if pid equals -1, this function will dump all allocated
|
|
|
|
* node and output backtrace for every allocated node for this multiple
|
|
|
|
* mempool, if pid equals -2, this function will dump all free node for
|
|
|
|
* this multiple mempool, and if pid is greater than or equal to 0, will
|
|
|
|
* dump pid allocated node and output backtrace.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mpool - The handle of multiple memory pool to be used.
|
2023-05-05 10:52:30 +02:00
|
|
|
* dump - The information of what need dump.
|
2022-11-06 16:27:44 +01:00
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
void mempool_multiple_memdump(FAR struct mempool_multiple_s *mpool,
|
2023-05-05 10:52:30 +02:00
|
|
|
FAR const struct mm_memdump_s *dump)
|
2022-11-06 16:27:44 +01:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < mpool->npools; i++)
|
|
|
|
{
|
2023-05-05 10:52:30 +02:00
|
|
|
mempool_memdump(mpool->pools + i, dump);
|
2022-11-06 16:27:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-14 04:28:05 +02:00
|
|
|
/****************************************************************************
|
|
|
|
* Name: mempool_multiple_deinit
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* Deallocate multiple memory pool.
|
|
|
|
*
|
|
|
|
* Input Parameters:
|
|
|
|
* mpool - The handle of multiple memory pool to be used.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2022-11-25 17:22:12 +01:00
|
|
|
void mempool_multiple_deinit(FAR struct mempool_multiple_s *mpool)
|
2022-08-14 04:28:05 +02:00
|
|
|
{
|
2022-12-26 07:08:00 +01:00
|
|
|
size_t i;
|
2022-08-14 04:28:05 +02:00
|
|
|
|
2022-09-09 10:01:49 +02:00
|
|
|
DEBUGASSERT(mpool != NULL);
|
2022-08-14 04:28:05 +02:00
|
|
|
|
|
|
|
for (i = 0; i < mpool->npools; i++)
|
|
|
|
{
|
2022-11-25 17:22:12 +01:00
|
|
|
DEBUGVERIFY(mempool_deinit(mpool->pools + i));
|
2022-08-14 04:28:05 +02:00
|
|
|
}
|
2022-11-26 16:44:38 +01:00
|
|
|
|
2022-12-22 14:18:09 +01:00
|
|
|
for (i = 0; i < mpool->dict_row_num; i++)
|
|
|
|
{
|
|
|
|
if (mpool->dict[i] != NULL)
|
|
|
|
{
|
2023-05-26 04:42:50 +02:00
|
|
|
mempool_multiple_free_chunk(mpool, mpool->dict[i]);
|
2022-12-22 14:18:09 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-26 04:42:50 +02:00
|
|
|
mempool_multiple_free_chunk(mpool, mpool->dict);
|
|
|
|
mempool_multiple_free_chunk(mpool, mpool->pools);
|
2023-06-19 05:35:28 +02:00
|
|
|
nxrmutex_destroy(&mpool->lock);
|
2023-05-26 04:42:50 +02:00
|
|
|
mpool->free(mpool, mpool);
|
2022-08-14 04:28:05 +02:00
|
|
|
}
|