Blame source/vdo/kernel/bufferPool.c

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
}