Blame source/vdo/kernel/udsIndex.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
 * 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/udsIndex.c#16 $
Packit Service d40955
 */
Packit Service d40955
Packit Service d40955
#include "udsIndex.h"
Packit Service d40955
Packit Service d40955
#include "logger.h"
Packit Service d40955
#include "memoryAlloc.h"
Packit Service d40955
#include "murmur/MurmurHash3.h"
Packit Service d40955
#include "numeric.h"
Packit Service d40955
#include "stringUtils.h"
Packit Service d40955
#include "uds-block.h"
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
Packit Service d40955
typedef struct udsAttribute {
Packit Service d40955
  struct attribute attr;
Packit Service d40955
  const char *(*showString)(DedupeIndex *);
Packit Service d40955
} UDSAttribute;
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
Packit Service d40955
enum { UDS_Q_ACTION };
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
Packit Service d40955
// These are the values in the atomic dedupeContext.requestState field
Packit Service d40955
enum {
Packit Service d40955
  // The UdsRequest object is not in use.
Packit Service d40955
  UR_IDLE = 0,
Packit Service d40955
  // The UdsRequest object is in use, and VDO is waiting for the result.
Packit Service d40955
  UR_BUSY = 1,
Packit Service d40955
  // The UdsRequest object is in use, but has timed out.
Packit Service d40955
  UR_TIMED_OUT = 2,
Packit Service d40955
};
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
Packit Service d40955
typedef enum {
Packit Service d40955
  // The UDS index is closed
Packit Service d40955
  IS_CLOSED = 0,
Packit Service d40955
  // The UDS index session is opening or closing
Packit Service d40955
  IS_CHANGING = 1,
Packit Service d40955
  // The UDS index is open.  There is a UDS index session.
Packit Service d40955
  IS_OPENED = 2,
Packit Service d40955
} IndexState;
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
Packit Service d40955
typedef struct udsIndex {
Packit Service d40955
  DedupeIndex        common;
Packit Service d40955
  struct kobject     dedupeObject;
Packit Service d40955
  RegisteredThread   allocatingThread;
Packit Service d40955
  char              *indexName;
Packit Service d40955
  UdsConfiguration   configuration;
Packit Service d40955
  struct uds_parameters udsParams;
Packit Service d40955
  struct uds_index_session *indexSession;
Packit Service d40955
  atomic_t           active;
Packit Service d40955
  // This spinlock protects the state fields and the starting of dedupe
Packit Service d40955
  // requests.
Packit Service d40955
  spinlock_t         stateLock;
Packit Service d40955
  KvdoWorkItem       workItem;    // protected by stateLock
Packit Service d40955
  KvdoWorkQueue     *udsQueue;    // protected by stateLock
Packit Service d40955
  unsigned int       maximum;     // protected by stateLock
Packit Service d40955
  IndexState         indexState;  // protected by stateLock
Packit Service d40955
  IndexState         indexTarget; // protected by stateLock
Packit Service d40955
  bool               changing;    // protected by stateLock
Packit Service d40955
  bool               createFlag;  // protected by stateLock
Packit Service d40955
  bool               dedupeFlag;  // protected by stateLock
Packit Service d40955
  bool               deduping;    // protected by stateLock
Packit Service d40955
  bool               errorFlag;   // protected by stateLock
Packit Service d40955
  bool               suspended;   // protected by stateLock
Packit Service d40955
  // This spinlock protects the pending list, the pending flag in each KVIO,
Packit Service d40955
  // and the timeout list.
Packit Service d40955
  spinlock_t         pendingLock;
Packit Service d40955
  struct list_head   pendingHead;  // protected by pendingLock
Packit Service d40955
  struct timer_list  pendingTimer; // protected by pendingLock
Packit Service d40955
  bool               startedTimer; // protected by pendingLock
Packit Service d40955
} UDSIndex;
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
Packit Service d40955
// Version 1:  user space albireo index (limited to 32 bytes)
Packit Service d40955
// Version 2:  kernel space albireo index (limited to 16 bytes)
Packit Service d40955
enum {
Packit Service d40955
  UDS_ADVICE_VERSION = 2,
Packit Service d40955
  // version byte + state byte + 64-bit little-endian PBN
Packit Service d40955
  UDS_ADVICE_SIZE    = 1 + 1 + sizeof(uint64_t),
Packit Service d40955
};
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
Packit Service d40955
  // We want to ensure that there is only one copy of the following constants.
Packit Service d40955
static const char *CLOSED    = "closed";
Packit Service d40955
static const char *CLOSING   = "closing";
Packit Service d40955
static const char *ERROR     = "error";
Packit Service d40955
static const char *OFFLINE   = "offline";
Packit Service d40955
static const char *ONLINE    = "online";
Packit Service d40955
static const char *OPENING   = "opening";
Packit Service d40955
static const char *SUSPENDED = "suspended";
Packit Service d40955
static const char *UNKNOWN   = "unknown";
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static const char *indexStateToString(UDSIndex *index, IndexState state)
Packit Service d40955
{
Packit Service d40955
  if (index->suspended) {
Packit Service d40955
    return SUSPENDED;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  switch (state) {
Packit Service d40955
  case IS_CLOSED:
Packit Service d40955
    // Closed.  The errorFlag tells if it is because of an error.
Packit Service d40955
    return index->errorFlag ? ERROR : CLOSED;
Packit Service d40955
  case IS_CHANGING:
Packit Service d40955
    // The indexTarget tells if we are opening or closing the index.
Packit Service d40955
    return index->indexTarget == IS_OPENED ? OPENING : CLOSING;
Packit Service d40955
  case IS_OPENED:
Packit Service d40955
    // Opened.  The dedupeFlag tells if we are online or offline.
Packit Service d40955
    return index->dedupeFlag ? ONLINE : OFFLINE;
Packit Service d40955
  default:
Packit Service d40955
    return UNKNOWN;
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Encode VDO duplicate advice into the newMetadata field of a UDS request.
Packit Service d40955
 *
Packit Service d40955
 * @param request  The UDS request to receive the encoding
Packit Service d40955
 * @param advice   The advice to encode
Packit Service d40955
 **/
Packit Service d40955
static void encodeUDSAdvice(UdsRequest *request, DataLocation advice)
Packit Service d40955
{
Packit Service d40955
  size_t offset = 0;
Packit Service d40955
  struct udsChunkData *encoding = &request->newMetadata;
Packit Service d40955
  encoding->data[offset++] = UDS_ADVICE_VERSION;
Packit Service d40955
  encoding->data[offset++] = advice.state;
Packit Service d40955
  encodeUInt64LE(encoding->data, &offset, advice.pbn);
Packit Service d40955
  BUG_ON(offset != UDS_ADVICE_SIZE);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/**
Packit Service d40955
 * Decode VDO duplicate advice from the oldMetadata field of a UDS request.
Packit Service d40955
 *
Packit Service d40955
 * @param request  The UDS request containing the encoding
Packit Service d40955
 * @param advice   The DataLocation to receive the decoded advice
Packit Service d40955
 *
Packit Service d40955
 * @return true if valid advice was found and decoded
Packit Service d40955
 **/
Packit Service d40955
static bool decodeUDSAdvice(const UdsRequest *request, DataLocation *advice)
Packit Service d40955
{
Packit Service d40955
  if ((request->status != UDS_SUCCESS) || !request->found) {
Packit Service d40955
    return false;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  size_t offset = 0;
Packit Service d40955
  const struct udsChunkData *encoding = &request->oldMetadata;
Packit Service d40955
  byte version = encoding->data[offset++];
Packit Service d40955
  if (version != UDS_ADVICE_VERSION) {
Packit Service d40955
    logError("invalid UDS advice version code %u", version);
Packit Service d40955
    return false;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  advice->state = encoding->data[offset++];
Packit Service d40955
  decodeUInt64LE(encoding->data, &offset, &advice->pbn);
Packit Service d40955
  BUG_ON(offset != UDS_ADVICE_SIZE);
Packit Service d40955
  return true;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void finishIndexOperation(UdsRequest *udsRequest)
Packit Service d40955
{
Packit Service d40955
  DataKVIO *dataKVIO = container_of(udsRequest, DataKVIO,
Packit Service d40955
                                    dedupeContext.udsRequest);
Packit Service d40955
  DedupeContext *dedupeContext = &dataKVIO->dedupeContext;
Packit Service d40955
  if (compareAndSwap32(&dedupeContext->requestState, UR_BUSY, UR_IDLE)) {
Packit Service d40955
    KVIO *kvio = dataKVIOAsKVIO(dataKVIO);
Packit Service d40955
    UDSIndex *index = container_of(kvio->layer->dedupeIndex, UDSIndex, common);
Packit Service d40955
Packit Service d40955
    spin_lock_bh(&index->pendingLock);
Packit Service d40955
    if (dedupeContext->isPending) {
Packit Service d40955
      list_del(&dedupeContext->pendingList);
Packit Service d40955
      dedupeContext->isPending = false;
Packit Service d40955
    }
Packit Service d40955
    spin_unlock_bh(&index->pendingLock);
Packit Service d40955
Packit Service d40955
    dedupeContext->status = udsRequest->status;
Packit Service d40955
    if ((udsRequest->type == UDS_POST) || (udsRequest->type == UDS_QUERY)) {
Packit Service d40955
      DataLocation advice;
Packit Service d40955
      if (decodeUDSAdvice(udsRequest, &advice)) {
Packit Service d40955
        setDedupeAdvice(dedupeContext, &advice);
Packit Service d40955
      } else {
Packit Service d40955
        setDedupeAdvice(dedupeContext, NULL);
Packit Service d40955
      }
Packit Service d40955
    }
Packit Service d40955
    invokeDedupeCallback(dataKVIO);
Packit Service d40955
    atomic_dec(&index->active);
Packit Service d40955
  } else {
Packit Service d40955
    compareAndSwap32(&dedupeContext->requestState, UR_TIMED_OUT, UR_IDLE);
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void startExpirationTimer(UDSIndex *index, DataKVIO *dataKVIO)
Packit Service d40955
{
Packit Service d40955
  if (!index->startedTimer) {
Packit Service d40955
    index->startedTimer = true;
Packit Service d40955
    mod_timer(&index->pendingTimer,
Packit Service d40955
              getAlbireoTimeout(dataKVIO->dedupeContext.submissionTime));
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void startIndexOperation(KvdoWorkItem *item)
Packit Service d40955
{
Packit Service d40955
  KVIO *kvio = workItemAsKVIO(item);
Packit Service d40955
  DataKVIO *dataKVIO = kvioAsDataKVIO(kvio);
Packit Service d40955
  UDSIndex *index = container_of(kvio->layer->dedupeIndex, UDSIndex, common);
Packit Service d40955
  DedupeContext *dedupeContext = &dataKVIO->dedupeContext;
Packit Service d40955
Packit Service d40955
  spin_lock_bh(&index->pendingLock);
Packit Service d40955
  list_add_tail(&dedupeContext->pendingList, &index->pendingHead);
Packit Service d40955
  dedupeContext->isPending = true;
Packit Service d40955
  startExpirationTimer(index, dataKVIO);
Packit Service d40955
  spin_unlock_bh(&index->pendingLock);
Packit Service d40955
Packit Service d40955
  UdsRequest *udsRequest = &dedupeContext->udsRequest;
Packit Service d40955
  int status = udsStartChunkOperation(udsRequest);
Packit Service d40955
  if (status != UDS_SUCCESS) {
Packit Service d40955
    udsRequest->status = status;
Packit Service d40955
    finishIndexOperation(udsRequest);
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
Packit Service d40955
static void timeoutIndexOperations(struct timer_list *t)
Packit Service d40955
#else
Packit Service d40955
static void timeoutIndexOperations(unsigned long arg)
Packit Service d40955
#endif
Packit Service d40955
{
Packit Service d40955
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
Packit Service d40955
  UDSIndex *index = from_timer(index, t, pendingTimer);
Packit Service d40955
#else
Packit Service d40955
  UDSIndex *index = (UDSIndex *) arg;
Packit Service d40955
#endif
Packit Service d40955
  LIST_HEAD(expiredHead);
Packit Service d40955
  uint64_t timeoutJiffies = msecs_to_jiffies(albireoTimeoutInterval);
Packit Service d40955
  unsigned long earliestSubmissionAllowed = jiffies - timeoutJiffies;
Packit Service d40955
  spin_lock_bh(&index->pendingLock);
Packit Service d40955
  index->startedTimer = false;
Packit Service d40955
  while (!list_empty(&index->pendingHead)) {
Packit Service d40955
    DataKVIO *dataKVIO = list_first_entry(&index->pendingHead, DataKVIO,
Packit Service d40955
                                          dedupeContext.pendingList);
Packit Service d40955
    DedupeContext *dedupeContext = &dataKVIO->dedupeContext;
Packit Service d40955
    if (earliestSubmissionAllowed <= dedupeContext->submissionTime) {
Packit Service d40955
      startExpirationTimer(index, dataKVIO);
Packit Service d40955
      break;
Packit Service d40955
    }
Packit Service d40955
    list_del(&dedupeContext->pendingList);
Packit Service d40955
    dedupeContext->isPending = false;
Packit Service d40955
    list_add_tail(&dedupeContext->pendingList, &expiredHead);
Packit Service d40955
  }
Packit Service d40955
  spin_unlock_bh(&index->pendingLock);
Packit Service d40955
  while (!list_empty(&expiredHead)) {
Packit Service d40955
    DataKVIO *dataKVIO = list_first_entry(&expiredHead, DataKVIO,
Packit Service d40955
                                          dedupeContext.pendingList);
Packit Service d40955
    DedupeContext *dedupeContext = &dataKVIO->dedupeContext;
Packit Service d40955
    list_del(&dedupeContext->pendingList);
Packit Service d40955
    if (compareAndSwap32(&dedupeContext->requestState,
Packit Service d40955
                         UR_BUSY, UR_TIMED_OUT)) {
Packit Service d40955
      dedupeContext->status = ETIMEDOUT;
Packit Service d40955
      invokeDedupeCallback(dataKVIO);
Packit Service d40955
      atomic_dec(&index->active);
Packit Service d40955
      kvdoReportDedupeTimeout(dataKVIOAsKVIO(dataKVIO)->layer, 1);
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void enqueueIndexOperation(DataKVIO        *dataKVIO,
Packit Service d40955
                                  UdsCallbackType  operation)
Packit Service d40955
{
Packit Service d40955
  KVIO *kvio = dataKVIOAsKVIO(dataKVIO);
Packit Service d40955
  DedupeContext *dedupeContext = &dataKVIO->dedupeContext;
Packit Service d40955
  UDSIndex *index = container_of(kvio->layer->dedupeIndex, UDSIndex, common);
Packit Service d40955
  dedupeContext->status         = UDS_SUCCESS;
Packit Service d40955
  dedupeContext->submissionTime = jiffies;
Packit Service d40955
  if (compareAndSwap32(&dedupeContext->requestState, UR_IDLE, UR_BUSY)) {
Packit Service d40955
    UdsRequest *udsRequest = &dataKVIO->dedupeContext.udsRequest;
Packit Service d40955
    udsRequest->chunkName = *dedupeContext->chunkName;
Packit Service d40955
    udsRequest->callback  = finishIndexOperation;
Packit Service d40955
    udsRequest->session   = index->indexSession;
Packit Service d40955
    udsRequest->type      = operation;
Packit Service d40955
    udsRequest->update    = true;
Packit Service d40955
    if ((operation == UDS_POST) || (operation == UDS_UPDATE)) {
Packit Service d40955
      encodeUDSAdvice(udsRequest, getDedupeAdvice(dedupeContext));
Packit Service d40955
    }
Packit Service d40955
Packit Service d40955
    setupWorkItem(&kvio->enqueueable.workItem, startIndexOperation, NULL,
Packit Service d40955
                  UDS_Q_ACTION);
Packit Service d40955
Packit Service d40955
    spin_lock(&index->stateLock);
Packit Service d40955
    if (index->deduping) {
Packit Service d40955
      enqueueWorkQueue(index->udsQueue, &kvio->enqueueable.workItem);
Packit Service d40955
      unsigned int active = atomic_inc_return(&index->active);
Packit Service d40955
      if (active > index->maximum) {
Packit Service d40955
        index->maximum = active;
Packit Service d40955
      }
Packit Service d40955
      kvio = NULL;
Packit Service d40955
    } else {
Packit Service d40955
      atomicStore32(&dedupeContext->requestState, UR_IDLE);
Packit Service d40955
    }
Packit Service d40955
    spin_unlock(&index->stateLock);
Packit Service d40955
  } else {
Packit Service d40955
    // A previous user of the KVIO had a dedupe timeout
Packit Service d40955
    // and its request is still outstanding.
Packit Service d40955
    atomic64_inc(&kvio->layer->dedupeContextBusy);
Packit Service d40955
  }
Packit Service d40955
  if (kvio != NULL) {
Packit Service d40955
    invokeDedupeCallback(dataKVIO);
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void closeIndex(UDSIndex *index)
Packit Service d40955
{
Packit Service d40955
  // Change the index state so that getIndexStatistics will not try to
Packit Service d40955
  // use the index session we are closing.
Packit Service d40955
  index->indexState = IS_CHANGING;
Packit Service d40955
  spin_unlock(&index->stateLock);
Packit Service d40955
  int result = udsCloseIndex(index->indexSession);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    logErrorWithStringError(result, "Error closing index %s",
Packit Service d40955
                            index->indexName);
Packit Service d40955
  }
Packit Service d40955
  spin_lock(&index->stateLock);
Packit Service d40955
  index->indexState = IS_CLOSED;
Packit Service d40955
  index->errorFlag |= result != UDS_SUCCESS;
Packit Service d40955
  // ASSERTION: We leave in IS_CLOSED state.
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void openIndex(UDSIndex *index)
Packit Service d40955
{
Packit Service d40955
  // ASSERTION: We enter in IS_CLOSED state.
Packit Service d40955
  bool createFlag = index->createFlag;
Packit Service d40955
  index->createFlag = false;
Packit Service d40955
  // Change the index state so that the it will be reported to the outside
Packit Service d40955
  // world as "opening".
Packit Service d40955
  index->indexState = IS_CHANGING;
Packit Service d40955
  index->errorFlag = false;
Packit Service d40955
  // Open the index session, while not holding the stateLock
Packit Service d40955
  spin_unlock(&index->stateLock);
Packit Service d40955
Packit Service d40955
  int result = udsOpenIndex(createFlag ? UDS_CREATE : UDS_LOAD,
Packit Service d40955
                            index->indexName, &index->udsParams,
Packit Service d40955
                            index->configuration, index->indexSession);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    logErrorWithStringError(result, "Error opening index %s",
Packit Service d40955
                            index->indexName);
Packit Service d40955
  }
Packit Service d40955
  spin_lock(&index->stateLock);
Packit Service d40955
  if (!createFlag) {
Packit Service d40955
    switch (result) {
Packit Service d40955
    case UDS_CORRUPT_COMPONENT:
Packit Service d40955
    case UDS_NO_INDEX:
Packit Service d40955
      // Either there is no index, or there is no way we can recover the index.
Packit Service d40955
      // We will be called again and try to create a new index.
Packit Service d40955
      index->indexState = IS_CLOSED;
Packit Service d40955
      index->createFlag = true;
Packit Service d40955
      return;
Packit Service d40955
    default:
Packit Service d40955
      break;
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
  if (result == UDS_SUCCESS) {
Packit Service d40955
    index->indexState  = IS_OPENED;
Packit Service d40955
  } else {
Packit Service d40955
    index->indexState  = IS_CLOSED;
Packit Service d40955
    index->indexTarget = IS_CLOSED;
Packit Service d40955
    index->errorFlag   = true;
Packit Service d40955
    spin_unlock(&index->stateLock);
Packit Service d40955
    logInfo("Setting UDS index target state to error");
Packit Service d40955
    spin_lock(&index->stateLock);
Packit Service d40955
  }
Packit Service d40955
  // ASSERTION: On success, we leave in IS_OPEN state.
Packit Service d40955
  // ASSERTION: On failure, we leave in IS_CLOSED state.
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void changeDedupeState(KvdoWorkItem *item)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(item, UDSIndex, workItem);
Packit Service d40955
  spin_lock(&index->stateLock);
Packit Service d40955
  // Loop until the index is in the target state and the create flag is
Packit Service d40955
  // clear.
Packit Service d40955
  while (!index->suspended &&
Packit Service d40955
         ((index->indexState != index->indexTarget) ||
Packit Service d40955
          index->createFlag)) {
Packit Service d40955
    if (index->indexState == IS_OPENED) {
Packit Service d40955
      closeIndex(index);
Packit Service d40955
    } else {
Packit Service d40955
      openIndex(index);
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
  index->changing = false;
Packit Service d40955
  index->deduping = index->dedupeFlag && (index->indexState == IS_OPENED);
Packit Service d40955
  spin_unlock(&index->stateLock);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void launchDedupeStateChange(UDSIndex *index)
Packit Service d40955
{
Packit Service d40955
  // ASSERTION: We enter with the state_lock held.
Packit Service d40955
  if (index->changing || index->suspended) {
Packit Service d40955
    // Either a change is already in progress, or changes are
Packit Service d40955
    // not allowed.
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  if (index->createFlag ||
Packit Service d40955
      (index->indexState != index->indexTarget)) {
Packit Service d40955
    index->changing = true;
Packit Service d40955
    index->deduping = false;
Packit Service d40955
    setupWorkItem(&index->workItem,
Packit Service d40955
                  changeDedupeState,
Packit Service d40955
                  NULL,
Packit Service d40955
                  UDS_Q_ACTION);
Packit Service d40955
    enqueueWorkQueue(index->udsQueue, &index->workItem);
Packit Service d40955
    return;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  // Online vs. offline changes happen immediately
Packit Service d40955
  index->deduping = (index->dedupeFlag && !index->suspended &&
Packit Service d40955
                     (index->indexState == IS_OPENED));
Packit Service d40955
Packit Service d40955
  // ASSERTION: We exit with the state_lock held.
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void setTargetState(UDSIndex   *index,
Packit Service d40955
                           IndexState  target,
Packit Service d40955
                           bool        changeDedupe,
Packit Service d40955
                           bool        dedupe,
Packit Service d40955
                           bool        setCreate)
Packit Service d40955
{
Packit Service d40955
  spin_lock(&index->stateLock);
Packit Service d40955
  const char *oldState = indexStateToString(index, index->indexTarget);
Packit Service d40955
  if (changeDedupe) {
Packit Service d40955
    index->dedupeFlag = dedupe;
Packit Service d40955
  }
Packit Service d40955
  if (setCreate) {
Packit Service d40955
    index->createFlag = true;
Packit Service d40955
  }
Packit Service d40955
  index->indexTarget = target;
Packit Service d40955
  launchDedupeStateChange(index);
Packit Service d40955
  const char *newState = indexStateToString(index, index->indexTarget);
Packit Service d40955
  spin_unlock(&index->stateLock);
Packit Service d40955
  if (oldState != newState) {
Packit Service d40955
    logInfo("Setting UDS index target state to %s", newState);
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void suspendUDSIndex(DedupeIndex *dedupeIndex, bool saveFlag)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(dedupeIndex, UDSIndex, common);
Packit Service d40955
  spin_lock(&index->stateLock);
Packit Service d40955
  index->suspended = true;
Packit Service d40955
  IndexState indexState = index->indexState;
Packit Service d40955
  spin_unlock(&index->stateLock);
Packit Service d40955
  if (indexState != IS_CLOSED) {
Packit Service d40955
    int result = udsSuspendIndexSession(index->indexSession, saveFlag);
Packit Service d40955
    if (result != UDS_SUCCESS) {
Packit Service d40955
      logErrorWithStringError(result, "Error suspending dedupe index");
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void resumeUDSIndex(DedupeIndex *dedupeIndex)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(dedupeIndex, UDSIndex, common);
Packit Service d40955
  int result = udsResumeIndexSession(index->indexSession);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    logErrorWithStringError(result, "Error resuming dedupe index");
Packit Service d40955
  }
Packit Service d40955
  spin_lock(&index->stateLock);
Packit Service d40955
  index->suspended = false;
Packit Service d40955
  launchDedupeStateChange(index);
Packit Service d40955
  spin_unlock(&index->stateLock);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void dumpUDSIndex(DedupeIndex *dedupeIndex, bool showQueue)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(dedupeIndex, UDSIndex, common);
Packit Service d40955
  spin_lock(&index->stateLock);
Packit Service d40955
  const char *state = indexStateToString(index, index->indexState);
Packit Service d40955
  const char *target = (index->changing
Packit Service d40955
                        ? indexStateToString(index, index->indexTarget)
Packit Service d40955
                        : NULL);
Packit Service d40955
  spin_unlock(&index->stateLock);
Packit Service d40955
  logInfo("UDS index: state: %s", state);
Packit Service d40955
  if (target != NULL) {
Packit Service d40955
    logInfo("UDS index: changing to state: %s", target);
Packit Service d40955
  }
Packit Service d40955
  if (showQueue) {
Packit Service d40955
    dumpWorkQueue(index->udsQueue);
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void finishUDSIndex(DedupeIndex *dedupeIndex)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(dedupeIndex, UDSIndex, common);
Packit Service d40955
  setTargetState(index, IS_CLOSED, false, false, false);
Packit Service d40955
  udsDestroyIndexSession(index->indexSession);
Packit Service d40955
  finishWorkQueue(index->udsQueue);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void freeUDSIndex(DedupeIndex *dedupeIndex)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(dedupeIndex, UDSIndex, common);
Packit Service d40955
  freeWorkQueue(&index->udsQueue);
Packit Service d40955
  spin_lock_bh(&index->pendingLock);
Packit Service d40955
  if (index->startedTimer) {
Packit Service d40955
    del_timer_sync(&index->pendingTimer);
Packit Service d40955
  }
Packit Service d40955
  spin_unlock_bh(&index->pendingLock);
Packit Service d40955
  kobject_put(&index->dedupeObject);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static const char *getUDSStateName(DedupeIndex *dedupeIndex)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(dedupeIndex, UDSIndex, common);
Packit Service d40955
  spin_lock(&index->stateLock);
Packit Service d40955
  const char *state = indexStateToString(index, index->indexState);
Packit Service d40955
  spin_unlock(&index->stateLock);
Packit Service d40955
  return state;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void getUDSStatistics(DedupeIndex *dedupeIndex, IndexStatistics *stats)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(dedupeIndex, UDSIndex, common);
Packit Service d40955
  spin_lock(&index->stateLock);
Packit Service d40955
  IndexState      indexState   = index->indexState;
Packit Service d40955
  stats->maxDedupeQueries      = index->maximum;
Packit Service d40955
  spin_unlock(&index->stateLock);
Packit Service d40955
  stats->currDedupeQueries     = atomic_read(&index->active);
Packit Service d40955
  if (indexState == IS_OPENED) {
Packit Service d40955
    UdsIndexStats indexStats;
Packit Service d40955
    int result = udsGetIndexStats(index->indexSession, &indexStats);
Packit Service d40955
    if (result == UDS_SUCCESS) {
Packit Service d40955
      stats->entriesIndexed = indexStats.entriesIndexed;
Packit Service d40955
    } else {
Packit Service d40955
      logErrorWithStringError(result, "Error reading index stats");
Packit Service d40955
    }
Packit Service d40955
    UdsContextStats contextStats;
Packit Service d40955
    result = udsGetIndexSessionStats(index->indexSession, &contextStats);
Packit Service d40955
    if (result == UDS_SUCCESS) {
Packit Service d40955
      stats->postsFound      = contextStats.postsFound;
Packit Service d40955
      stats->postsNotFound   = contextStats.postsNotFound;
Packit Service d40955
      stats->queriesFound    = contextStats.queriesFound;
Packit Service d40955
      stats->queriesNotFound = contextStats.queriesNotFound;
Packit Service d40955
      stats->updatesFound    = contextStats.updatesFound;
Packit Service d40955
      stats->updatesNotFound = contextStats.updatesNotFound;
Packit Service d40955
    } else {
Packit Service d40955
      logErrorWithStringError(result, "Error reading context stats");
Packit Service d40955
    }
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static int processMessage(DedupeIndex *dedupeIndex, const char *name)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(dedupeIndex, UDSIndex, common);
Packit Service d40955
  if (strcasecmp(name, "index-close") == 0) {
Packit Service d40955
    setTargetState(index, IS_CLOSED, false, false, false);
Packit Service d40955
    return 0;
Packit Service d40955
  } else if (strcasecmp(name, "index-create") == 0) {
Packit Service d40955
    setTargetState(index, IS_OPENED, false, false, true);
Packit Service d40955
    return 0;
Packit Service d40955
  } else if (strcasecmp(name, "index-disable") == 0) {
Packit Service d40955
    setTargetState(index, IS_OPENED, true, false, false);
Packit Service d40955
    return 0;
Packit Service d40955
  } else if (strcasecmp(name, "index-enable") == 0) {
Packit Service d40955
    setTargetState(index, IS_OPENED, true, true, false);
Packit Service d40955
    return 0;
Packit Service d40955
  }
Packit Service d40955
  return -EINVAL;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void udsPost(DataKVIO *dataKVIO)
Packit Service d40955
{
Packit Service d40955
  enqueueIndexOperation(dataKVIO, UDS_POST);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void udsQuery(DataKVIO *dataKVIO)
Packit Service d40955
{
Packit Service d40955
  enqueueIndexOperation(dataKVIO, UDS_QUERY);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void startUDSIndex(DedupeIndex *dedupeIndex, bool createFlag)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(dedupeIndex, UDSIndex, common);
Packit Service d40955
  setTargetState(index, IS_OPENED, true, true, createFlag);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void stopUDSIndex(DedupeIndex *dedupeIndex)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(dedupeIndex, UDSIndex, common);
Packit Service d40955
  setTargetState(index, IS_CLOSED, false, false, false);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void udsUpdate(DataKVIO *dataKVIO)
Packit Service d40955
{
Packit Service d40955
  enqueueIndexOperation(dataKVIO, UDS_UPDATE);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void dedupeKobjRelease(struct kobject *kobj)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index = container_of(kobj, UDSIndex, dedupeObject);
Packit Service d40955
  udsFreeConfiguration(index->configuration);
Packit Service d40955
  FREE(index->indexName);
Packit Service d40955
  FREE(index);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static ssize_t dedupeStatusShow(struct kobject   *kobj,
Packit Service d40955
                                struct attribute *attr,
Packit Service d40955
                                char             *buf)
Packit Service d40955
{
Packit Service d40955
  UDSAttribute *ua = container_of(attr, UDSAttribute, attr);
Packit Service d40955
  UDSIndex *index = container_of(kobj, UDSIndex, dedupeObject);
Packit Service d40955
  if (ua->showString != NULL) {
Packit Service d40955
    return sprintf(buf, "%s\n", ua->showString(&index->common));
Packit Service d40955
  } else {
Packit Service d40955
    return -EINVAL;
Packit Service d40955
  }
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static ssize_t dedupeStatusStore(struct kobject   *kobj,
Packit Service d40955
                                 struct attribute *attr,
Packit Service d40955
                                 const char       *buf,
Packit Service d40955
                                 size_t            length)
Packit Service d40955
{
Packit Service d40955
  return -EINVAL;
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
Packit Service d40955
static struct sysfs_ops dedupeSysfsOps = {
Packit Service d40955
  .show  = dedupeStatusShow,
Packit Service d40955
  .store = dedupeStatusStore,
Packit Service d40955
};
Packit Service d40955
Packit Service d40955
static UDSAttribute dedupeStatusAttribute = {
Packit Service d40955
  .attr = {.name = "status", .mode = 0444, },
Packit Service d40955
  .showString = getUDSStateName,
Packit Service d40955
};
Packit Service d40955
Packit Service d40955
static struct attribute *dedupeAttributes[] = {
Packit Service d40955
  &dedupeStatusAttribute.attr,
Packit Service d40955
  NULL,
Packit Service d40955
};
Packit Service d40955
Packit Service d40955
static struct kobj_type dedupeKobjType = {
Packit Service d40955
  .release       = dedupeKobjRelease,
Packit Service d40955
  .sysfs_ops     = &dedupeSysfsOps,
Packit Service d40955
  .default_attrs = dedupeAttributes,
Packit Service d40955
};
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void startUDSQueue(void *ptr)
Packit Service d40955
{
Packit Service d40955
  /*
Packit Service d40955
   * Allow the UDS dedupe worker thread to do memory allocations.  It will
Packit Service d40955
   * only do allocations during the UDS calls that open or close an index,
Packit Service d40955
   * but those allocations can safely sleep while reserving a large amount
Packit Service d40955
   * of memory.  We could use an allocationsAllowed boolean (like the base
Packit Service d40955
   * threads do), but it would be an unnecessary embellishment.
Packit Service d40955
   */
Packit Service d40955
  UDSIndex *index = ptr;
Packit Service d40955
  registerAllocatingThread(&index->allocatingThread, NULL);
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
static void finishUDSQueue(void *ptr)
Packit Service d40955
{
Packit Service d40955
  unregisterAllocatingThread();
Packit Service d40955
}
Packit Service d40955
Packit Service d40955
/*****************************************************************************/
Packit Service d40955
int makeUDSIndex(KernelLayer *layer, DedupeIndex **indexPtr)
Packit Service d40955
{
Packit Service d40955
  UDSIndex *index;
Packit Service d40955
  int result = ALLOCATE(1, UDSIndex, "UDS index data", &index);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  result = allocSprintf("index name", &index->indexName,
Packit Service d40955
                        "dev=%s offset=4096 size=%llu",
Packit Service d40955
                        layer->deviceConfig->parentDeviceName,
Packit Service d40955
                        getIndexRegionSize(layer->geometry) * VDO_BLOCK_SIZE);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    logError("Creating index name failed (%d)", result);
Packit Service d40955
    FREE(index);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  index->udsParams = (struct uds_parameters) UDS_PARAMETERS_INITIALIZER;
Packit Service d40955
  indexConfigToUdsParameters(&layer->geometry.indexConfig, &index->udsParams);
Packit Service d40955
  result = indexConfigToUdsConfiguration(&layer->geometry.indexConfig,
Packit Service d40955
                                         &index->configuration);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    FREE(index->indexName);
Packit Service d40955
    FREE(index);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
  udsConfigurationSetNonce(index->configuration,
Packit Service d40955
                           (UdsNonce) layer->geometry.nonce);
Packit Service d40955
Packit Service d40955
  result = udsCreateIndexSession(&index->indexSession);
Packit Service d40955
  if (result != UDS_SUCCESS) {
Packit Service d40955
    udsFreeConfiguration(index->configuration);
Packit Service d40955
    FREE(index->indexName);
Packit Service d40955
    FREE(index);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  static const KvdoWorkQueueType udsQueueType = {
Packit Service d40955
    .start        = startUDSQueue,
Packit Service d40955
    .finish       = finishUDSQueue,
Packit Service d40955
    .actionTable  = {
Packit Service d40955
      { .name = "uds_action", .code = UDS_Q_ACTION, .priority = 0 },
Packit Service d40955
    },
Packit Service d40955
  };
Packit Service d40955
  result = makeWorkQueue(layer->threadNamePrefix, "dedupeQ",
Packit Service d40955
                         &layer->wqDirectory, layer, index, &udsQueueType, 1,
Packit Service d40955
                         &index->udsQueue);
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    logError("UDS index queue initialization failed (%d)", result);
Packit Service d40955
    udsDestroyIndexSession(index->indexSession);
Packit Service d40955
    udsFreeConfiguration(index->configuration);
Packit Service d40955
    FREE(index->indexName);
Packit Service d40955
    FREE(index);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  kobject_init(&index->dedupeObject, &dedupeKobjType);
Packit Service d40955
  result = kobject_add(&index->dedupeObject, &layer->kobj, "dedupe");
Packit Service d40955
  if (result != VDO_SUCCESS) {
Packit Service d40955
    freeWorkQueue(&index->udsQueue);
Packit Service d40955
    udsDestroyIndexSession(index->indexSession);
Packit Service d40955
    udsFreeConfiguration(index->configuration);
Packit Service d40955
    FREE(index->indexName);
Packit Service d40955
    FREE(index);
Packit Service d40955
    return result;
Packit Service d40955
  }
Packit Service d40955
Packit Service d40955
  index->common.dump                      = dumpUDSIndex;
Packit Service d40955
  index->common.free                      = freeUDSIndex;
Packit Service d40955
  index->common.getDedupeStateName        = getUDSStateName;
Packit Service d40955
  index->common.getStatistics             = getUDSStatistics;
Packit Service d40955
  index->common.message                   = processMessage;
Packit Service d40955
  index->common.post                      = udsPost;
Packit Service d40955
  index->common.query                     = udsQuery;
Packit Service d40955
  index->common.resume                    = resumeUDSIndex;
Packit Service d40955
  index->common.start                     = startUDSIndex;
Packit Service d40955
  index->common.stop                      = stopUDSIndex;
Packit Service d40955
  index->common.suspend                   = suspendUDSIndex;
Packit Service d40955
  index->common.finish                    = finishUDSIndex;
Packit Service d40955
  index->common.update                    = udsUpdate;
Packit Service d40955
Packit Service d40955
  INIT_LIST_HEAD(&index->pendingHead);
Packit Service d40955
  spin_lock_init(&index->pendingLock);
Packit Service d40955
  spin_lock_init(&index->stateLock);
Packit Service d40955
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
Packit Service d40955
  timer_setup(&index->pendingTimer, timeoutIndexOperations, 0);
Packit Service d40955
#else
Packit Service d40955
  setup_timer(&index->pendingTimer, timeoutIndexOperations,
Packit Service d40955
              (unsigned long) index);
Packit Service d40955
#endif
Packit Service d40955
Packit Service d40955
  *indexPtr = &index->common;
Packit Service d40955
  return VDO_SUCCESS;
Packit Service d40955
}