Blame vdo/kernel/bufferPool.c

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