|
Packit Service |
75d76b |
/*
|
|
Packit Service |
75d76b |
* Copyright (c) 2020 Red Hat, Inc.
|
|
Packit Service |
75d76b |
*
|
|
Packit Service |
75d76b |
* This program is free software; you can redistribute it and/or
|
|
Packit Service |
75d76b |
* modify it under the terms of the GNU General Public License
|
|
Packit Service |
75d76b |
* as published by the Free Software Foundation; either version 2
|
|
Packit Service |
75d76b |
* of the License, or (at your option) any later version.
|
|
Packit Service |
75d76b |
*
|
|
Packit Service |
75d76b |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
75d76b |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
75d76b |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
75d76b |
* GNU General Public License for more details.
|
|
Packit Service |
75d76b |
*
|
|
Packit Service |
75d76b |
* You should have received a copy of the GNU General Public License
|
|
Packit Service |
75d76b |
* along with this program; if not, write to the Free Software
|
|
Packit Service |
75d76b |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit Service |
75d76b |
* 02110-1301, USA.
|
|
Packit Service |
75d76b |
*
|
|
Packit Service |
75d76b |
* $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/bufferPool.c#1 $
|
|
Packit Service |
75d76b |
*/
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
#include "bufferPool.h"
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
#include <linux/delay.h>
|
|
Packit Service |
75d76b |
#include <linux/sort.h>
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
#include "logger.h"
|
|
Packit Service |
75d76b |
#include "memoryAlloc.h"
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
#include "statusCodes.h"
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
/*
|
|
Packit Service |
75d76b |
* For list nodes on the free-object list, the data field describes
|
|
Packit Service |
75d76b |
* the object available for reuse.
|
|
Packit Service |
75d76b |
*
|
|
Packit Service |
75d76b |
* For nodes on the "spare" list, the data field is meaningless;
|
|
Packit Service |
75d76b |
* they're just nodes available for use when we need to add an object
|
|
Packit Service |
75d76b |
* pointer to the freeObjectList.
|
|
Packit Service |
75d76b |
*
|
|
Packit Service |
75d76b |
* These are both "free lists", in a sense; don't get confused!
|
|
Packit Service |
75d76b |
*/
|
|
Packit Service |
75d76b |
typedef struct {
|
|
Packit Service |
75d76b |
struct list_head list; // links in current list
|
|
Packit Service |
75d76b |
void *data; // element data, if on free list
|
|
Packit Service |
75d76b |
} BufferElement;
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
struct bufferPool {
|
|
Packit Service |
75d76b |
const char *name; // Pool name
|
|
Packit Service |
75d76b |
void *data; // Associated pool data
|
|
Packit Service |
75d76b |
spinlock_t lock; // Locks this object
|
|
Packit Service |
75d76b |
unsigned int size; // Total number of buffers
|
|
Packit Service |
75d76b |
struct list_head freeObjectList; // List of free buffers
|
|
Packit Service |
75d76b |
struct list_head spareListNodes; // Unused list nodes
|
|
Packit Service |
75d76b |
unsigned int numBusy; // Number of buffers in use
|
|
Packit Service |
75d76b |
unsigned int maxBusy; // Maximum value of the above
|
|
Packit Service |
75d76b |
BufferAllocateFunction *alloc; // Allocate function for buffer data
|
|
Packit Service |
75d76b |
BufferFreeFunction *free; // Free function for buffer data
|
|
Packit Service |
75d76b |
BufferDumpFunction *dump; // Dump function for buffer data
|
|
Packit Service |
75d76b |
BufferElement *bhead; // Array of BufferElement structures
|
|
Packit Service |
75d76b |
void **objects;
|
|
Packit Service |
75d76b |
};
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
/*************************************************************************/
|
|
Packit Service |
75d76b |
int makeBufferPool(const char *poolName,
|
|
Packit Service |
75d76b |
unsigned int size,
|
|
Packit Service |
75d76b |
BufferAllocateFunction *allocateFunction,
|
|
Packit Service |
75d76b |
BufferFreeFunction *freeFunction,
|
|
Packit Service |
75d76b |
BufferDumpFunction *dumpFunction,
|
|
Packit Service |
75d76b |
void *poolData,
|
|
Packit Service |
75d76b |
BufferPool **poolPtr)
|
|
Packit Service |
75d76b |
{
|
|
Packit Service |
75d76b |
BufferPool *pool;
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
int result = ALLOCATE(1, BufferPool, "buffer pool", &pool);
|
|
Packit Service |
75d76b |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
75d76b |
logError("buffer pool allocation failure %d", result);
|
|
Packit Service |
75d76b |
return result;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
result = ALLOCATE(size, BufferElement, "buffer pool elements", &pool->bhead);
|
|
Packit Service |
75d76b |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
75d76b |
logError("buffer element array allocation failure %d", result);
|
|
Packit Service |
75d76b |
freeBufferPool(&pool);
|
|
Packit Service |
75d76b |
return result;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
result = ALLOCATE(size, void *, "object pointers", &pool->objects);
|
|
Packit Service |
75d76b |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
75d76b |
logError("buffer object array allocation failure %d", result);
|
|
Packit Service |
75d76b |
freeBufferPool(&pool);
|
|
Packit Service |
75d76b |
return result;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
pool->name = poolName;
|
|
Packit Service |
75d76b |
pool->alloc = allocateFunction;
|
|
Packit Service |
75d76b |
pool->free = freeFunction;
|
|
Packit Service |
75d76b |
pool->dump = dumpFunction;
|
|
Packit Service |
75d76b |
pool->data = poolData;
|
|
Packit Service |
75d76b |
pool->size = size;
|
|
Packit Service |
75d76b |
spin_lock_init(&pool->lock);
|
|
Packit Service |
75d76b |
INIT_LIST_HEAD(&pool->freeObjectList);
|
|
Packit Service |
75d76b |
INIT_LIST_HEAD(&pool->spareListNodes);
|
|
Packit Service |
75d76b |
BufferElement *bh = pool->bhead;
|
|
Packit Service |
75d76b |
for (int i = 0; i < pool->size; i++) {
|
|
Packit Service |
75d76b |
result = pool->alloc(pool->data, &bh->data);
|
|
Packit Service |
75d76b |
if (result != VDO_SUCCESS) {
|
|
Packit Service |
75d76b |
logError("verify buffer data allocation failure %d", result);
|
|
Packit Service |
75d76b |
freeBufferPool(&pool);
|
|
Packit Service |
75d76b |
return result;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
pool->objects[i] = bh->data;
|
|
Packit Service |
75d76b |
list_add(&bh->list, &pool->freeObjectList);
|
|
Packit Service |
75d76b |
bh++;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
pool->numBusy = pool->maxBusy = 0;
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
*poolPtr = pool;
|
|
Packit Service |
75d76b |
return VDO_SUCCESS;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
/*************************************************************************/
|
|
Packit Service |
75d76b |
void freeBufferPool(BufferPool **poolPtr)
|
|
Packit Service |
75d76b |
{
|
|
Packit Service |
75d76b |
BufferPool *pool = *poolPtr;
|
|
Packit Service |
75d76b |
if (pool == NULL) {
|
|
Packit Service |
75d76b |
return;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
ASSERT_LOG_ONLY((pool->numBusy == 0), "freeing busy buffer pool, numBusy=%d",
|
|
Packit Service |
75d76b |
pool->numBusy);
|
|
Packit Service |
75d76b |
if (pool->objects != NULL) {
|
|
Packit Service |
75d76b |
for (int i = 0; i < pool->size; i++) {
|
|
Packit Service |
75d76b |
if (pool->objects[i] != NULL) {
|
|
Packit Service |
75d76b |
pool->free(pool->data, pool->objects[i]);
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
FREE(pool->objects);
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
FREE(pool->bhead);
|
|
Packit Service |
75d76b |
FREE(pool);
|
|
Packit Service |
75d76b |
*poolPtr = NULL;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
/*************************************************************************/
|
|
Packit Service |
75d76b |
static bool inFreeList(BufferPool *pool, void *data)
|
|
Packit Service |
75d76b |
{
|
|
Packit Service |
75d76b |
struct list_head *node;
|
|
Packit Service |
75d76b |
list_for_each(node, &pool->freeObjectList) {
|
|
Packit Service |
75d76b |
if (container_of(node, BufferElement, list)->data == data) {
|
|
Packit Service |
75d76b |
return true;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
return false;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
/*************************************************************************/
|
|
Packit Service |
75d76b |
void dumpBufferPool(BufferPool *pool, bool dumpElements)
|
|
Packit Service |
75d76b |
{
|
|
Packit Service |
75d76b |
// In order that syslog can empty its buffer, sleep after 35 elements for
|
|
Packit Service |
75d76b |
// 4ms (till the second clock tick). These numbers chosen in October
|
|
Packit Service |
75d76b |
// 2012 running on an lfarm.
|
|
Packit Service |
75d76b |
enum { ELEMENTS_PER_BATCH = 35 };
|
|
Packit Service |
75d76b |
enum { SLEEP_FOR_SYSLOG = 4 };
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
if (pool == NULL) {
|
|
Packit Service |
75d76b |
return;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
spin_lock(&pool->lock);
|
|
Packit Service |
75d76b |
logInfo("%s: %u of %u busy (max %u)", pool->name, pool->numBusy, pool->size,
|
|
Packit Service |
75d76b |
pool->maxBusy);
|
|
Packit Service |
75d76b |
if (dumpElements && (pool->dump != NULL)) {
|
|
Packit Service |
75d76b |
int dumped = 0;
|
|
Packit Service |
75d76b |
for (int i = 0; i < pool->size; i++) {
|
|
Packit Service |
75d76b |
if (!inFreeList(pool, pool->objects[i])) {
|
|
Packit Service |
75d76b |
pool->dump(pool->data, pool->objects[i]);
|
|
Packit Service |
75d76b |
if (++dumped >= ELEMENTS_PER_BATCH) {
|
|
Packit Service |
75d76b |
spin_unlock(&pool->lock);
|
|
Packit Service |
75d76b |
dumped = 0;
|
|
Packit Service |
75d76b |
msleep(SLEEP_FOR_SYSLOG);
|
|
Packit Service |
75d76b |
spin_lock(&pool->lock);
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
spin_unlock(&pool->lock);
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
/*************************************************************************/
|
|
Packit Service |
75d76b |
int allocBufferFromPool(BufferPool *pool, void **dataPtr)
|
|
Packit Service |
75d76b |
{
|
|
Packit Service |
75d76b |
if (pool == NULL) {
|
|
Packit Service |
75d76b |
return UDS_INVALID_ARGUMENT;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
spin_lock(&pool->lock);
|
|
Packit Service |
75d76b |
if (unlikely(list_empty(&pool->freeObjectList))) {
|
|
Packit Service |
75d76b |
spin_unlock(&pool->lock);
|
|
Packit Service |
75d76b |
logDebug("no free buffers");
|
|
Packit Service |
75d76b |
return -ENOMEM;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
BufferElement *bh = list_first_entry(&pool->freeObjectList, BufferElement,
|
|
Packit Service |
75d76b |
list);
|
|
Packit Service |
75d76b |
list_move(&bh->list, &pool->spareListNodes);
|
|
Packit Service |
75d76b |
pool->numBusy++;
|
|
Packit Service |
75d76b |
if (pool->numBusy > pool->maxBusy) {
|
|
Packit Service |
75d76b |
pool->maxBusy = pool->numBusy;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
*dataPtr = bh->data;
|
|
Packit Service |
75d76b |
spin_unlock(&pool->lock);
|
|
Packit Service |
75d76b |
return VDO_SUCCESS;
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
/*************************************************************************/
|
|
Packit Service |
75d76b |
static bool freeBufferToPoolInternal(BufferPool *pool, void *data)
|
|
Packit Service |
75d76b |
{
|
|
Packit Service |
75d76b |
if (unlikely(list_empty(&pool->spareListNodes))) {
|
|
Packit Service |
75d76b |
return false;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
BufferElement *bh = list_first_entry(&pool->spareListNodes, BufferElement,
|
|
Packit Service |
75d76b |
list);
|
|
Packit Service |
75d76b |
list_move(&bh->list, &pool->freeObjectList);
|
|
Packit Service |
75d76b |
bh->data = data;
|
|
Packit Service |
75d76b |
pool->numBusy--;
|
|
Packit Service |
75d76b |
return true;
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
/*************************************************************************/
|
|
Packit Service |
75d76b |
void freeBufferToPool(BufferPool *pool, void *data)
|
|
Packit Service |
75d76b |
{
|
|
Packit Service |
75d76b |
spin_lock(&pool->lock);
|
|
Packit Service |
75d76b |
bool success = freeBufferToPoolInternal(pool, data);
|
|
Packit Service |
75d76b |
spin_unlock(&pool->lock);
|
|
Packit Service |
75d76b |
if (!success) {
|
|
Packit Service |
75d76b |
logDebug("trying to add to free list when already full");
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
|
|
Packit Service |
75d76b |
/*************************************************************************/
|
|
Packit Service |
75d76b |
void freeBuffersToPool(BufferPool *pool, void **data, int count)
|
|
Packit Service |
75d76b |
{
|
|
Packit Service |
75d76b |
spin_lock(&pool->lock);
|
|
Packit Service |
75d76b |
bool success = true;
|
|
Packit Service |
75d76b |
for (int i = 0; (i < count) && success; i++) {
|
|
Packit Service |
75d76b |
success = freeBufferToPoolInternal(pool, data[i]);
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
spin_unlock(&pool->lock);
|
|
Packit Service |
75d76b |
if (!success) {
|
|
Packit Service |
75d76b |
logDebug("trying to add to free list when already full");
|
|
Packit Service |
75d76b |
}
|
|
Packit Service |
75d76b |
}
|