/*
 * Copyright (c) 2020 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA. 
 *
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/bufferPool.h#1 $
 */
#ifndef BUFFERPOOL_H
#define BUFFERPOOL_H

/*
 * We need bug.h because in 3.10, kernel.h (indirectly) defines
 * ARRAY_SIZE as a macro which (indirectly and conditionally) uses
 * BUILD_BUG_ON_ZERO, which is defined in bug.h, which is *not*
 * included. In earlier versions like 3.2 it Just Worked.
 */
#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/types.h>

typedef struct bufferPool BufferPool;

typedef int BufferAllocateFunction(void *poolData, void **dataPtr);
typedef void BufferFreeFunction(void *poolData, void *data);
typedef void BufferDumpFunction(void *poolData, void *data);

/**
 * Creates a generic pool of buffer data. The elements in the pool are
 * allocated up front and placed on a free list, which manages the
 * reuse of the individual buffers in the pool.
 *
 * @param [in]  poolName         Name of the pool
 * @param [in]  size             The number of elements to create for this pool
 * @param [in]  allocateFunction The function to call to create the actual data
 *                               for each element
 * @param [in]  freeFunction     The function to call to free the actual data
 *                               for each element
 * @param [in]  dumpFunction     The function to call to dump the actual data
 *                               for each element into the log
 * @param [in]  poolData         A pointer to the pool's associated data
 * @param [out] poolPtr          A pointer to hold the pool that was created
 *
 * @return a success or error code
 */
int makeBufferPool(const char              *poolName,
                   unsigned int             size,
                   BufferAllocateFunction  *allocateFunction,
                   BufferFreeFunction      *freeFunction,
                   BufferDumpFunction      *dumpFunction,
                   void                    *poolData,
                   BufferPool             **poolPtr)
  __attribute__((warn_unused_result));

/**
 * Free a buffer pool and null out the reference to it. This will free
 * all the elements of the pool as well.
 *
 * @param [in]  poolPtr   The reference to the pool to free
 **/
void freeBufferPool(BufferPool **poolPtr);

/**
 * Dump a buffer pool to the log.
 *
 * @param [in] pool          The buffer pool to allocate from
 * @param [in] dumpElements  True for complete output, or false for a
 *                           one-line summary
 **/
void dumpBufferPool(BufferPool *pool, bool dumpElements);

/**
 * Acquires a free buffer from the free list of the pool and
 * returns it's associated data.
 *
 * @param [in]  pool      The buffer pool to allocate from
 * @param [out] dataPtr   A pointer to hold the buffer data
 *
 * @return a success or error code
 */
int allocBufferFromPool(BufferPool *pool, void **dataPtr)
  __attribute__((warn_unused_result));

/**
 * Returns a buffer to the free list of a pool
 *
 * @param [in] pool   The buffer pool to return the buffer to
 * @param [in] data   The buffer data to return
 */
void freeBufferToPool(BufferPool *pool, void *data);

/**
 * Returns a set of buffers to the free list of a pool
 *
 * @param [in] pool   The buffer pool to return the buffer to
 * @param [in] data   The buffer data to return
 * @param [in] count  Number of entries in the data array
 */
void freeBuffersToPool(BufferPool *pool, void **data, int count);

/**
 * Control structure for freeing (releasing back to the pool) pointers
 * in batches.
 *
 * Since the objects stored in a buffer pool are completely opaque,
 * some external data structure is needed to manage a collection of
 * them. This is a simple helper for doing that, since we're freeing
 * batches of objects in a couple different places. Within the pool
 * itself there's a pair of linked lists, but getting at them requires
 * the locking that we're trying to minimize.
 *
 * We collect pointers until the array is full or until there are no
 * more available, and we call freeBuffersToPool to release a batch
 * all at once.
 **/
typedef struct freeBufferPointers {
  BufferPool *pool;
  int         index;
  void       *pointers[30]; // size is arbitrary
} FreeBufferPointers;

/**
 * Initialize the control structure for batching buffer pointers to be
 * released to their pool.
 *
 * @param [out] fbp   The (caller-allocated) control structure
 * @param [in]  pool  The buffer pool to return objects to.
 **/
static inline void initFreeBufferPointers(FreeBufferPointers *fbp,
                                          BufferPool         *pool)
{
  fbp->index = 0;
  fbp->pool  = pool;
}

/**
 * Release any buffers left in the collection.
 *
 * @param [in]  fbp  The control structure
 **/
static inline void freeBufferPointers(FreeBufferPointers *fbp)
{
  freeBuffersToPool(fbp->pool, fbp->pointers, fbp->index);
  fbp->index = 0;
}

/**
 * Add another buffer pointer to the collection, and if we're full,
 * release the whole batch to the pool.
 *
 * @param [in]  fbp      The control structure
 * @param [in]  pointer  The buffer pointer to release
 **/
static inline void addFreeBufferPointer(FreeBufferPointers *fbp,
                                        void               *pointer)
{
  fbp->pointers[fbp->index] = pointer;
  fbp->index++;
  if (fbp->index == ARRAY_SIZE(fbp->pointers)) {
    freeBufferPointers(fbp);
  }
}

#endif /* BUFFERPOOL_H */
