Blame source/vdo/kernel/verify.c

Packit Service cbade1
/*
Packit Service cbade1
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service cbade1
 *
Packit Service cbade1
 * This program is free software; you can redistribute it and/or
Packit Service cbade1
 * modify it under the terms of the GNU General Public License
Packit Service cbade1
 * as published by the Free Software Foundation; either version 2
Packit Service cbade1
 * of the License, or (at your option) any later version.
Packit Service cbade1
 * 
Packit Service cbade1
 * This program is distributed in the hope that it will be useful,
Packit Service cbade1
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service cbade1
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service cbade1
 * GNU General Public License for more details.
Packit Service cbade1
 * 
Packit Service cbade1
 * You should have received a copy of the GNU General Public License
Packit Service cbade1
 * along with this program; if not, write to the Free Software
Packit Service cbade1
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service cbade1
 * 02110-1301, USA. 
Packit Service cbade1
 *
Packit Service cbade1
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/kernel/verify.c#3 $
Packit Service cbade1
 */
Packit Service cbade1
Packit Service cbade1
#include "verify.h"
Packit Service cbade1
Packit Service cbade1
#include "logger.h"
Packit Service cbade1
Packit Service cbade1
#include "dataKVIO.h"
Packit Service cbade1
#include "numeric.h"
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Compare blocks of memory for equality.
Packit Service cbade1
 *
Packit Service cbade1
 * This assumes the blocks are likely to be large; it's not well
Packit Service cbade1
 * optimized for comparing just a few bytes.  This is desirable
Packit Service cbade1
 * because the Linux kernel memcmp() routine on x86 is not well
Packit Service cbade1
 * optimized for large blocks, and the performance penalty turns out
Packit Service cbade1
 * to be significant if you're doing lots of 4KB comparisons.
Packit Service cbade1
 *
Packit Service cbade1
 * @param pointerArgument1  first data block
Packit Service cbade1
 * @param pointerArgument2  second data block
Packit Service cbade1
 * @param length            length of the data block
Packit Service cbade1
 *
Packit Service cbade1
 * @return   true iff the two blocks are equal
Packit Service cbade1
 **/
Packit Service cbade1
__attribute__((warn_unused_result))
Packit Service cbade1
static bool memoryEqual(void   *pointerArgument1,
Packit Service cbade1
                        void   *pointerArgument2,
Packit Service cbade1
                        size_t  length)
Packit Service cbade1
{
Packit Service cbade1
  byte *pointer1 = pointerArgument1;
Packit Service cbade1
  byte *pointer2 = pointerArgument2;
Packit Service cbade1
  while (length >= sizeof(uint64_t)) {
Packit Service cbade1
    /*
Packit Service cbade1
     * GET_UNALIGNED is just for paranoia.  (1) On x86_64 it is
Packit Service cbade1
     * treated the same as an aligned access.  (2) In this use case,
Packit Service cbade1
     * one or both of the inputs will almost(?) always be aligned.
Packit Service cbade1
     */
Packit Service cbade1
    if (GET_UNALIGNED(uint64_t, pointer1)
Packit Service cbade1
        != GET_UNALIGNED(uint64_t, pointer2)) {
Packit Service cbade1
      return false;
Packit Service cbade1
    }
Packit Service cbade1
    pointer1 += sizeof(uint64_t);
Packit Service cbade1
    pointer2 += sizeof(uint64_t);
Packit Service cbade1
    length -= sizeof(uint64_t);
Packit Service cbade1
  }
Packit Service cbade1
  while (length > 0) {
Packit Service cbade1
    if (*pointer1 != *pointer2) {
Packit Service cbade1
      return false;
Packit Service cbade1
    }
Packit Service cbade1
    pointer1++;
Packit Service cbade1
    pointer2++;
Packit Service cbade1
    length--;
Packit Service cbade1
  }
Packit Service cbade1
  return true;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Verify the Albireo-provided deduplication advice, and invoke a
Packit Service cbade1
 * callback once the answer is available.
Packit Service cbade1
 *
Packit Service cbade1
 * After we've compared the stored data with the data to be written,
Packit Service cbade1
 * or after we've failed to be able to do so, the stored VIO callback
Packit Service cbade1
 * is queued to be run in the main (kvdoReqQ) thread.
Packit Service cbade1
 *
Packit Service cbade1
 * If the advice turns out to be stale and the deduplication session
Packit Service cbade1
 * is still active, submit a correction.  (Currently the correction
Packit Service cbade1
 * must be sent before the callback can be invoked, if the dedupe
Packit Service cbade1
 * session is still live.)
Packit Service cbade1
 *
Packit Service cbade1
 * @param item  The workitem from the queue
Packit Service cbade1
 **/
Packit Service cbade1
static void verifyDuplicationWork(KvdoWorkItem *item)
Packit Service cbade1
{
Packit Service cbade1
  DataKVIO *dataKVIO = workItemAsDataKVIO(item);
Packit Service cbade1
  dataKVIOAddTraceRecord(dataKVIO, THIS_LOCATION("$F;j=dedupe;cb=verify"));
Packit Service cbade1
Packit Service cbade1
  if (likely(memoryEqual(dataKVIO->dataBlock, dataKVIO->readBlock.data,
Packit Service cbade1
                         VDO_BLOCK_SIZE))) {
Packit Service cbade1
    // Leave dataKVIO->dataVIO.isDuplicate set to true.
Packit Service cbade1
  } else {
Packit Service cbade1
    dataKVIO->dataVIO.isDuplicate = false;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  kvdoEnqueueDataVIOCallback(dataKVIO);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Verify the Albireo-provided deduplication advice, and invoke a
Packit Service cbade1
 * callback once the answer is available.
Packit Service cbade1
 *
Packit Service cbade1
 * @param dataKVIO  The DataKVIO that we are looking to dedupe.
Packit Service cbade1
 **/
Packit Service cbade1
static void verifyReadBlockCallback(DataKVIO *dataKVIO)
Packit Service cbade1
{
Packit Service cbade1
  dataKVIOAddTraceRecord(dataKVIO, THIS_LOCATION(NULL));
Packit Service cbade1
  int err = dataKVIO->readBlock.status;
Packit Service cbade1
  if (unlikely(err != 0)) {
Packit Service cbade1
    logDebug("%s: err %d", __func__, err);
Packit Service cbade1
    dataKVIO->dataVIO.isDuplicate = false;
Packit Service cbade1
    kvdoEnqueueDataVIOCallback(dataKVIO);
Packit Service cbade1
    return;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  launchDataKVIOOnCPUQueue(dataKVIO, verifyDuplicationWork, NULL,
Packit Service cbade1
                           CPU_Q_ACTION_COMPRESS_BLOCK);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void kvdoVerifyDuplication(DataVIO *dataVIO)
Packit Service cbade1
{
Packit Service cbade1
  ASSERT_LOG_ONLY(dataVIO->isDuplicate, "advice to verify must be valid");
Packit Service cbade1
  ASSERT_LOG_ONLY(dataVIO->duplicate.state != MAPPING_STATE_UNMAPPED,
Packit Service cbade1
                  "advice to verify must not be a discard");
Packit Service cbade1
  ASSERT_LOG_ONLY(dataVIO->duplicate.pbn != ZERO_BLOCK,
Packit Service cbade1
                  "advice to verify must not point to the zero block");
Packit Service cbade1
  ASSERT_LOG_ONLY(!dataVIO->isZeroBlock,
Packit Service cbade1
                  "zeroed block should not have advice to verify");
Packit Service cbade1
Packit Service cbade1
  TraceLocation location
Packit Service cbade1
    = THIS_LOCATION("verifyDuplication;dup=update(verify);io=verify");
Packit Service cbade1
  dataVIOAddTraceRecord(dataVIO, location);
Packit Service cbade1
  kvdoReadBlock(dataVIO, dataVIO->duplicate.pbn, dataVIO->duplicate.state,
Packit Service cbade1
                BIO_Q_ACTION_VERIFY, verifyReadBlockCallback);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
bool kvdoCompareDataVIOs(DataVIO *first, DataVIO *second)
Packit Service cbade1
{
Packit Service cbade1
  dataVIOAddTraceRecord(second, THIS_LOCATION(NULL));
Packit Service cbade1
  DataKVIO *a = dataVIOAsDataKVIO(first);
Packit Service cbade1
  DataKVIO *b = dataVIOAsDataKVIO(second);
Packit Service cbade1
  return memoryEqual(a->dataBlock, b->dataBlock, VDO_BLOCK_SIZE);
Packit Service cbade1
}