Blame source/vdo/kernel/instanceNumber.c

Packit Service d40955
/*
Packit Service d40955
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service d40955
 *
Packit Service d40955
 * This program is free software; you can redistribute it and/or
Packit Service d40955
 * modify it under the terms of the GNU General Public License
Packit Service d40955
 * as published by the Free Software Foundation; either version 2
Packit Service d40955
 * of the License, or (at your option) any later version.
Packit Service d40955
 * 
Packit Service d40955
 * This program is distributed in the hope that it will be useful,
Packit Service d40955
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service d40955
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service d40955
 * GNU General Public License for more details.
Packit Service d40955
 * 
Packit Service d40955
 * You should have received a copy of the GNU General Public License
Packit Service d40955
 * along with this program; if not, write to the Free Software
Packit Service d40955
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service d40955
 * 02110-1301, USA. 
Packit Service d40955
 *
Packit Service d40955
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/instanceNumber.c#1 $
Packit Service d40955
 */
Packit Service d40955
Packit Service d40955
#include "instanceNumber.h"
Packit Service d40955
Packit Service d40955
#include <linux/bitops.h>
Packit Service d40955
#include <linux/mutex.h>
Packit Service d40955
Packit Service d40955
#include "memoryAlloc.h"
Packit Service d40955
#include "numUtils.h"
Packit Service d40955
#include "permassert.h"
Packit Service d40955
Packit Service d40955
/*
Packit Service d40955
 * Track in-use instance numbers using a flat bit array.
Packit Service d40955
 *
Packit Service d40955
 * O(n) run time isn't ideal, but if we have 1000 VDO devices in use
Packit Service d40955
 * simultaneously we still only need to scan 16 words, so it's not
Packit Service d40955
 * likely to be a big deal compared to other resource usage.
Packit Service d40955
 */
Packit Service d40955
Packit Service d40955
enum {
Packit Service d40955
  /**
Packit Service d40955
   * This minimum size for the bit array creates a numbering space of 0-999,
Packit Service d40955
   * which allows successive starts of the same volume to have different
Packit Service d40955
   * instance numbers in any reasonably-sized test. Changing instances on
Packit Service d40955
   * restart allows vdoMonReport to detect that the ephemeral stats have reset
Packit Service d40955
   * to zero.
Packit Service d40955
   **/
Packit Service d40955
  BIT_COUNT_MINIMUM   = 1000,
Packit Service d40955
  /** Grow the bit array by this many bits when needed */
Packit Service d40955
  BIT_COUNT_INCREMENT = 100,
Packit Service d40955
};
Packit Service d40955
Packit Service d40955
static struct mutex   instanceNumberLock;
Packit Service d40955
static unsigned int   bitCount;
Packit Service d40955
static unsigned long *words;
Packit Service d40955
static unsigned int   instanceCount;
Packit Service d40955
static unsigned int   nextInstance;
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Return the number of bytes needed to store a bit array of the specified
Packit Service d40955
 * capacity in an array of unsigned longs.
Packit Service d40955
 *
Packit Service d40955
 * @param bitCount  The number of bits the array must hold
Packit Service d40955
 *
Packit Service d40955
 * @return the number of bytes needed for the array reperesentation
Packit Service d40955
 **/
Packit Service d40955
static size_t getBitArraySize(unsigned int bitCount)
Packit Service d40955
{
Packit Service d40955
  // Round up to a multiple of the word size and convert to a byte count.
Packit Service d40955
  return (computeBucketCount(bitCount, BITS_PER_LONG) * sizeof(unsigned long));
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Re-allocate the bitmap word array so there will more instance numbers that
Packit Service d40955
 * can be allocated. Since the array is initially NULL, this also initializes
Packit Service d40955
 * the array the first time we allocate an instance number.
Packit Service d40955
 *
Packit Service d40955
 * @return UDS_SUCCESS or an error code from the allocation
Packit Service d40955
 **/
Packit Service d40955
static int growBitArray(void)
Packit Service d40955
{
Packit Service d40955
  unsigned int newCount = maxUInt(bitCount + BIT_COUNT_INCREMENT,
Packit Service d40955
                                  BIT_COUNT_MINIMUM);
Packit Service d40955
  unsigned long *newWords;
Packit Service d40955
  int result = reallocateMemory(words,
Packit Service d40955
                                getBitArraySize(bitCount),
Packit Service d40955
                                getBitArraySize(newCount),
Packit Service d40955
                                "instance number bit array",
Packit Service d40955
                                &newWords);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  bitCount = newCount;
Packit Service d40955
  words    = newWords;
Packit Service d40955
  return UDS_SUCCESS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
static int allocateKVDOInstanceLocked(unsigned int *instancePtr)
Packit Service d40955
{
Packit Service d40955
  // If there are no unallocated instances, grow the bit array.
Packit Service d40955
  if (instanceCount >= bitCount) {
Packit Service d40955
    int result = growBitArray();
Packit Service d40955
    if (result != UDS_SUCCESS) {
Packit Service d40955
      return result;
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // There must be a zero bit somewhere now. Find it, starting just after the
Packit Service d40955
  // last instance allocated.
Packit Service d40955
  unsigned int instance = find_next_zero_bit(words, bitCount, nextInstance);
Packit Service d40955
  if (instance >= bitCount) {
Packit Service d40955
    // Nothing free after nextInstance, so wrap around to instance zero.
Packit Service d40955
    instance = find_first_zero_bit(words, bitCount);
Packit Service d40955
    int result = ASSERT(instance < bitCount, "impossibly, no zero bit found");
Packit Service d40955
    if (result != UDS_SUCCESS) {
Packit Service d40955
      return result;
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  __set_bit(instance, words);
Packit Service d40955
  instanceCount += 1;
Packit Service d40955
  nextInstance = instance + 1;
Packit Service d40955
  *instancePtr = instance;
Packit Service d40955
  return UDS_SUCCESS;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
int allocateKVDOInstance(unsigned int *instancePtr)
Packit Service d40955
{
Packit Service d40955
  mutex_lock(&instanceNumberLock);
Packit Service d40955
  int result = allocateKVDOInstanceLocked(instancePtr);
Packit Service d40955
  mutex_unlock(&instanceNumberLock);
Packit Service d40955
  return result;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void releaseKVDOInstance(unsigned int instance)
Packit Service d40955
{
Packit Service d40955
  mutex_lock(&instanceNumberLock);
Packit Service d40955
  if (instance >= bitCount) {
Packit Service d40955
    ASSERT_LOG_ONLY(false, "instance number %u must be less than bit count %u",
Packit Service d40955
                    instance, bitCount);
Packit Service d40955
  } else if (test_bit(instance, words) == 0) {
Packit Service d40955
    ASSERT_LOG_ONLY(false, "instance number %u must be allocated", instance);
Packit Service d40955
  } else {
Packit Service d40955
    __clear_bit(instance, words);
Packit Service d40955
    instanceCount -= 1;
Packit Service d40955
  }
Packit Service d40955
  mutex_unlock(&instanceNumberLock);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void initializeInstanceNumberTracking(void)
Packit Service d40955
{
Packit Service d40955
  mutex_init(&instanceNumberLock);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**********************************************************************/
Packit Service d40955
void cleanUpInstanceNumberTracking(void)
Packit Service d40955
{
Packit Service d40955
  ASSERT_LOG_ONLY(instanceCount == 0,
Packit Service d40955
                  "should have no instance numbers still in use, but have %u",
Packit Service d40955
                  instanceCount);
Packit Service d40955
  FREE(words);
Packit Service d40955
  words = NULL;
Packit Service d40955
  bitCount = 0;
Packit Service d40955
  instanceCount = 0;
Packit Service d40955
  nextInstance = 0;
Packit Service d40955
  mutex_destroy(&instanceNumberLock);
Packit Service d40955
}