Blob Blame History Raw
/*
 * 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/uds-releases/jasper/src/uds/udsMain.c#12 $
 */

#include "uds.h"

#include "config.h"
#include "geometry.h"
#include "indexLayout.h"
#include "indexRouter.h"
#include "indexSession.h"
#include "loadType.h"
#include "logger.h"
#include "memoryAlloc.h"

const UdsMemoryConfigSize UDS_MEMORY_CONFIG_MAX   = 1024;
const UdsMemoryConfigSize UDS_MEMORY_CONFIG_256MB = (UdsMemoryConfigSize) -256;
const UdsMemoryConfigSize UDS_MEMORY_CONFIG_512MB = (UdsMemoryConfigSize) -512;
const UdsMemoryConfigSize UDS_MEMORY_CONFIG_768MB = (UdsMemoryConfigSize) -768;

/*
 * ===========================================================================
 * UDS system management
 * ===========================================================================
 */

/**********************************************************************/
int udsInitializeConfiguration(UdsConfiguration    *userConfig,
                               UdsMemoryConfigSize  memGB)
{
  if (userConfig == NULL) {
    return logErrorWithStringError(UDS_CONF_PTR_REQUIRED,
                                   "received a NULL config pointer");
  }

  /* Set the configuration parameters that change with memory size.  If you
   * change these values, you should also:
   *
   * Change Configuration_x1, which tests these values and expects to see them
   *
   * Bump the index configuration version number.  This bump ensures that
   * the test infrastructure will be forced to test the new configuration.
   */

  unsigned int chaptersPerVolume, recordPagesPerChapter;
  if (memGB == UDS_MEMORY_CONFIG_256MB) {
    chaptersPerVolume     = DEFAULT_CHAPTERS_PER_VOLUME;
    recordPagesPerChapter = SMALL_RECORD_PAGES_PER_CHAPTER;
  } else if (memGB == UDS_MEMORY_CONFIG_512MB) {
    chaptersPerVolume     = DEFAULT_CHAPTERS_PER_VOLUME;
    recordPagesPerChapter = 2 * SMALL_RECORD_PAGES_PER_CHAPTER;
  } else if (memGB == UDS_MEMORY_CONFIG_768MB) {
    chaptersPerVolume     = DEFAULT_CHAPTERS_PER_VOLUME;
    recordPagesPerChapter = 3 * SMALL_RECORD_PAGES_PER_CHAPTER;
  } else if (memGB == 1) {
    chaptersPerVolume     = DEFAULT_CHAPTERS_PER_VOLUME;
    recordPagesPerChapter = DEFAULT_RECORD_PAGES_PER_CHAPTER;
  } else if ((memGB > 1) && (memGB <= UDS_MEMORY_CONFIG_MAX)) {
    chaptersPerVolume     = memGB * DEFAULT_CHAPTERS_PER_VOLUME;
    recordPagesPerChapter = DEFAULT_RECORD_PAGES_PER_CHAPTER;
  } else {
    return UDS_INVALID_MEMORY_SIZE;
  }

  int result = ALLOCATE(1, struct udsConfiguration, "udsConfiguration",
                        userConfig);
  if (result != UDS_SUCCESS) {
    return result;
  }

  (*userConfig)->recordPagesPerChapter   = recordPagesPerChapter;
  (*userConfig)->chaptersPerVolume       = chaptersPerVolume;
  (*userConfig)->sparseChaptersPerVolume = DEFAULT_SPARSE_CHAPTERS_PER_VOLUME;
  (*userConfig)->cacheChapters           = DEFAULT_CACHE_CHAPTERS;
  (*userConfig)->checkpointFrequency     = DEFAULT_CHECKPOINT_FREQUENCY;
  (*userConfig)->masterIndexMeanDelta    = DEFAULT_MASTER_INDEX_MEAN_DELTA;
  (*userConfig)->bytesPerPage            = DEFAULT_BYTES_PER_PAGE;
  (*userConfig)->sparseSampleRate        = DEFAULT_SPARSE_SAMPLE_RATE;
  (*userConfig)->nonce                   = 0;
  return UDS_SUCCESS;
}

/**********************************************************************/
void udsConfigurationSetSparse(UdsConfiguration userConfig, bool sparse)
{
  bool prevSparse = (userConfig->sparseChaptersPerVolume != 0);
  if (sparse == prevSparse) {
    // nothing to do
    return;
  }

  unsigned int prevChaptersPerVolume = userConfig->chaptersPerVolume;
  if (sparse) {
    // Index 10TB with 4K blocks, 95% sparse, fit in dense (1TB) footprint
    userConfig->chaptersPerVolume = 10 * prevChaptersPerVolume;
    userConfig->sparseChaptersPerVolume = 9 * prevChaptersPerVolume
      + prevChaptersPerVolume / 2;
    userConfig->sparseSampleRate = 32;
  } else {
    userConfig->chaptersPerVolume = prevChaptersPerVolume / 10;
    userConfig->sparseChaptersPerVolume = 0;
    userConfig->sparseSampleRate = 0;
  }
}

/**********************************************************************/
bool udsConfigurationGetSparse(UdsConfiguration userConfig)
{
  return userConfig->sparseChaptersPerVolume > 0;
}

/**********************************************************************/
void udsConfigurationSetNonce(UdsConfiguration userConfig, UdsNonce nonce)
{
  userConfig->nonce = nonce;
}

/**********************************************************************/
UdsNonce udsConfigurationGetNonce(UdsConfiguration userConfig)
{
  return userConfig->nonce;
}

/**********************************************************************/
unsigned int udsConfigurationGetMemory(UdsConfiguration userConfig)
{
  enum {
    CHAPTERS = DEFAULT_CHAPTERS_PER_VOLUME,
    SMALL_PAGES = CHAPTERS * SMALL_RECORD_PAGES_PER_CHAPTER,
    LARGE_PAGES = CHAPTERS * DEFAULT_RECORD_PAGES_PER_CHAPTER
  };
  unsigned int pages = (userConfig->chaptersPerVolume
                        * userConfig->recordPagesPerChapter);
  if (userConfig->sparseChaptersPerVolume != 0) {
    pages /= 10;
  }
  switch (pages) {
  case SMALL_PAGES:     return UDS_MEMORY_CONFIG_256MB;
  case 2 * SMALL_PAGES: return UDS_MEMORY_CONFIG_512MB;
  case 3 * SMALL_PAGES: return UDS_MEMORY_CONFIG_768MB;
  default:              return pages / LARGE_PAGES;
  }
}

/**********************************************************************/
unsigned int
udsConfigurationGetChaptersPerVolume(UdsConfiguration userConfig)
{
  return userConfig->chaptersPerVolume;
}

/**********************************************************************/
void udsFreeConfiguration(UdsConfiguration userConfig)
{
  FREE(userConfig);
}

/**********************************************************************/
int udsCreateIndexSession(struct uds_index_session **session)
{
  if (session == NULL) {
    return UDS_NO_INDEXSESSION;
  }

  struct uds_index_session *indexSession = NULL;
  int result = makeEmptyIndexSession(&indexSession);
  if (result != UDS_SUCCESS) {
    return result;
  }

  *session = indexSession;
  return UDS_SUCCESS;
}

/**********************************************************************/
static
int initializeIndexSessionWithLayout(struct uds_index_session    *indexSession,
                                     IndexLayout                 *layout,
                                     const struct uds_parameters *userParams,
                                     LoadType                     loadType)
{
  int result = ((loadType == LOAD_CREATE)
                ? writeIndexConfig(layout, &indexSession->userConfig)
                : verifyIndexConfig(layout, &indexSession->userConfig));
  if (result != UDS_SUCCESS) {
    return result;
  }

  Configuration *indexConfig;
  result = makeConfiguration(&indexSession->userConfig, &indexConfig);
  if (result != UDS_SUCCESS) {
    logErrorWithStringError(result, "Failed to allocate config");
    return result;
  }

  // Zero the stats for the new index. 
  memset(&indexSession->stats, 0, sizeof(indexSession->stats));

  result = makeIndexRouter(layout, indexConfig, userParams, loadType,
                           &indexSession->loadContext, enterCallbackStage,
                           &indexSession->router);
  freeConfiguration(indexConfig);
  if (result != UDS_SUCCESS) {
    logErrorWithStringError(result, "Failed to make router");
    return result;
  }

  logUdsConfiguration(&indexSession->userConfig);
  return UDS_SUCCESS;
}

/**********************************************************************/
static int initializeIndexSession(struct uds_index_session    *indexSession,
                                  const char                  *name,
                                  const struct uds_parameters *userParams,
                                  LoadType                     loadType)
{
  IndexLayout *layout;
  int result = makeIndexLayout(name, loadType == LOAD_CREATE,
                               &indexSession->userConfig, &layout);
  if (result != UDS_SUCCESS) {
    return result;
  }

  result = initializeIndexSessionWithLayout(indexSession, layout, userParams,
                                            loadType);
  putIndexLayout(&layout);
  return result;
}

/**********************************************************************/
int udsOpenIndex(UdsOpenIndexType             openType,
                 const char                  *name,
                 const struct uds_parameters *userParams,
                 UdsConfiguration             userConfig,
                 struct uds_index_session    *session)
{
  if (name == NULL) {
    return UDS_INDEX_NAME_REQUIRED;
  }
  if (userConfig == NULL) {
    return UDS_CONF_REQUIRED;
  }
  if (session == NULL) {
    return UDS_NO_INDEXSESSION;
  }

  int result = startLoadingIndexSession(session);
  if (result != UDS_SUCCESS) {
    return result;
  }

  session->userConfig = *userConfig;

  // Map the external openType to the internal loadType
  LoadType loadType =   openType == UDS_CREATE     ? LOAD_CREATE
                      : openType == UDS_NO_REBUILD ? LOAD_LOAD
                      :                              LOAD_REBUILD;
  logNotice("%s: %s", getLoadType(loadType), name);

  result = initializeIndexSession(session, name, userParams, loadType);
  if (result != UDS_SUCCESS) {
    logErrorWithStringError(result, "Failed %s", getLoadType(loadType));
    saveAndFreeIndex(session);
  }

  finishLoadingIndexSession(session, result);
  return sansUnrecoverable(result);
}

/**********************************************************************/
const char *udsGetVersion(void)
{
#ifdef UDS_VERSION
  return UDS_VERSION;
#else
  return "internal version";
#endif
}

/**********************************************************************/
const char *udsStringError(int errnum, char *buf, size_t buflen)
{
  if (buf == NULL) {
    return NULL;
  }

  return stringError(errnum, buf, buflen);
}