Blame source/vdo/kernel/bio.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/bio.c#8 $
Packit Service cbade1
 */
Packit Service cbade1
Packit Service cbade1
#include "bio.h"
Packit Service cbade1
Packit Service cbade1
#include "logger.h"
Packit Service cbade1
#include "memoryAlloc.h"
Packit Service cbade1
#include "numeric.h"
Packit Service cbade1
Packit Service cbade1
#include "flush.h"
Packit Service cbade1
#include "recoveryJournal.h"
Packit Service cbade1
Packit Service cbade1
#include "bioIterator.h"
Packit Service cbade1
#include "ioSubmitter.h"
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Gets the raw buffer from a biovec.
Packit Service cbade1
 *
Packit Service cbade1
 * @param biovec  The biovec in question
Packit Service cbade1
 *
Packit Service cbade1
 * @return the buffer
Packit Service cbade1
 **/
Packit Service cbade1
static char *getBufferForBiovec(struct bio_vec *biovec)
Packit Service cbade1
{
Packit Service cbade1
  return (page_address(biovec->bv_page) + biovec->bv_offset);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void bioCopyDataIn(BIO *bio, char *dataPtr)
Packit Service cbade1
{
Packit Service cbade1
  struct bio_vec *biovec;
Packit Service cbade1
  for (BioIterator iter = createBioIterator(bio);
Packit Service cbade1
       (biovec = getNextBiovec(&iter)) != NULL;
Packit Service cbade1
       advanceBioIterator(&iter)) {
Packit Service cbade1
    memcpy(dataPtr, getBufferForBiovec(biovec), biovec->bv_len);
Packit Service cbade1
    dataPtr += biovec->bv_len;
Packit Service cbade1
  }
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void bioCopyDataOut(BIO *bio, char *dataPtr)
Packit Service cbade1
{
Packit Service cbade1
  struct bio_vec *biovec;
Packit Service cbade1
  for (BioIterator iter = createBioIterator(bio);
Packit Service cbade1
       (biovec = getNextBiovec(&iter)) != NULL;
Packit Service cbade1
       advanceBioIterator(&iter)) {
Packit Service cbade1
    memcpy(getBufferForBiovec(biovec), dataPtr, biovec->bv_len);
Packit Service cbade1
    flush_dcache_page(biovec->bv_page);
Packit Service cbade1
    dataPtr += biovec->bv_len;
Packit Service cbade1
  }
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void setBioOperation(BIO *bio, unsigned int operation)
Packit Service cbade1
{
Packit Service cbade1
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
Packit Service cbade1
  bio->bi_opf &= ~REQ_OP_MASK;
Packit Service cbade1
  bio->bi_opf |= operation;
Packit Service cbade1
#else
Packit Service cbade1
Packit Service cbade1
  unsigned int OPERATION_MASK = WRITE | REQ_DISCARD | REQ_FLUSH;
Packit Service cbade1
Packit Service cbade1
  // Clear the relevant bits
Packit Service cbade1
  bio->bi_rw &= ~OPERATION_MASK;
Packit Service cbade1
  // Set the operation we care about
Packit Service cbade1
  bio->bi_rw |= operation;
Packit Service cbade1
#endif
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void freeBio(BIO *bio, KernelLayer *layer)
Packit Service cbade1
{
Packit Service cbade1
  bio_put(bio);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void countBios(AtomicBioStats *bioStats, BIO *bio)
Packit Service cbade1
{
Packit Service cbade1
  if (isWriteBio(bio)) {
Packit Service cbade1
    atomic64_inc(&bioStats->write);
Packit Service cbade1
  } else {
Packit Service cbade1
    atomic64_inc(&bioStats->read);
Packit Service cbade1
  }
Packit Service cbade1
  if (isDiscardBio(bio)) {
Packit Service cbade1
    atomic64_inc(&bioStats->discard);
Packit Service cbade1
  }
Packit Service cbade1
  if (isFlushBio(bio)) {
Packit Service cbade1
    atomic64_inc(&bioStats->flush);
Packit Service cbade1
  }
Packit Service cbade1
  if (isFUABio(bio)) {
Packit Service cbade1
    atomic64_inc(&bioStats->fua);
Packit Service cbade1
  }
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * The function determines whether a buffer contains all zeroes.
Packit Service cbade1
 *
Packit Service cbade1
 * @param buffer  The buffer to check
Packit Service cbade1
 * @param length  The length of the buffer
Packit Service cbade1
 *
Packit Service cbade1
 * @return true is all zeroes, false otherwise
Packit Service cbade1
 **/
Packit Service cbade1
static inline bool isAllZeros(const char *buffer, unsigned int length)
Packit Service cbade1
{
Packit Service cbade1
  /*
Packit Service cbade1
   * Handle expected common case of even the first word being nonzero,
Packit Service cbade1
   * without getting into the more expensive (for one iteration) loop
Packit Service cbade1
   * below.
Packit Service cbade1
   */
Packit Service cbade1
  if (likely(length >= sizeof(uint64_t))) {
Packit Service cbade1
    if (GET_UNALIGNED(uint64_t, buffer) != 0) {
Packit Service cbade1
      return false;
Packit Service cbade1
    }
Packit Service cbade1
Packit Service cbade1
    unsigned int wordCount = length / sizeof(uint64_t);
Packit Service cbade1
Packit Service cbade1
    // Unroll to process 64 bytes at a time
Packit Service cbade1
    unsigned int chunkCount = wordCount / 8;
Packit Service cbade1
    while (chunkCount-- > 0) {
Packit Service cbade1
      uint64_t word0 = GET_UNALIGNED(uint64_t, buffer);
Packit Service cbade1
      uint64_t word1 = GET_UNALIGNED(uint64_t, buffer + 1 * sizeof(uint64_t));
Packit Service cbade1
      uint64_t word2 = GET_UNALIGNED(uint64_t, buffer + 2 * sizeof(uint64_t));
Packit Service cbade1
      uint64_t word3 = GET_UNALIGNED(uint64_t, buffer + 3 * sizeof(uint64_t));
Packit Service cbade1
      uint64_t word4 = GET_UNALIGNED(uint64_t, buffer + 4 * sizeof(uint64_t));
Packit Service cbade1
      uint64_t word5 = GET_UNALIGNED(uint64_t, buffer + 5 * sizeof(uint64_t));
Packit Service cbade1
      uint64_t word6 = GET_UNALIGNED(uint64_t, buffer + 6 * sizeof(uint64_t));
Packit Service cbade1
      uint64_t word7 = GET_UNALIGNED(uint64_t, buffer + 7 * sizeof(uint64_t));
Packit Service cbade1
      uint64_t or = (word0 | word1 | word2 | word3
Packit Service cbade1
                     | word4 | word5 | word6 | word7);
Packit Service cbade1
      // Prevent compiler from using 8*(cmp;jne).
Packit Service cbade1
      __asm__ __volatile__ ("" : : "g" (or));
Packit Service cbade1
      if (or != 0) {
Packit Service cbade1
        return false;
Packit Service cbade1
      }
Packit Service cbade1
      buffer += 8 * sizeof(uint64_t);
Packit Service cbade1
    }
Packit Service cbade1
    wordCount %= 8;
Packit Service cbade1
Packit Service cbade1
    // Unroll to process 8 bytes at a time.
Packit Service cbade1
    // (Is this still worthwhile?)
Packit Service cbade1
    while (wordCount-- > 0) {
Packit Service cbade1
      if (GET_UNALIGNED(uint64_t, buffer) != 0) {
Packit Service cbade1
        return false;
Packit Service cbade1
      }
Packit Service cbade1
      buffer += sizeof(uint64_t);
Packit Service cbade1
    }
Packit Service cbade1
    length %= sizeof(uint64_t);
Packit Service cbade1
    // Fall through to finish up anything left over.
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  while (length-- > 0) {
Packit Service cbade1
    if (*buffer++ != 0) {
Packit Service cbade1
      return false;
Packit Service cbade1
    }
Packit Service cbade1
  }
Packit Service cbade1
  return true;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
bool bioIsZeroData(BIO *bio)
Packit Service cbade1
{
Packit Service cbade1
  struct bio_vec *biovec;
Packit Service cbade1
  for (BioIterator iter = createBioIterator(bio);
Packit Service cbade1
       (biovec = getNextBiovec(&iter)) != NULL;
Packit Service cbade1
       advanceBioIterator(&iter)) {
Packit Service cbade1
    if (!isAllZeros(getBufferForBiovec(biovec), biovec->bv_len)) {
Packit Service cbade1
      return false;
Packit Service cbade1
    }
Packit Service cbade1
  }
Packit Service cbade1
  return true;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void bioZeroData(BIO *bio)
Packit Service cbade1
{
Packit Service cbade1
  zero_fill_bio(bio);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
static void setBioSize(BIO *bio, BlockSize bioSize)
Packit Service cbade1
{
Packit Service cbade1
#ifdef USE_BI_ITER
Packit Service cbade1
  bio->bi_iter.bi_size = bioSize;
Packit Service cbade1
#else
Packit Service cbade1
  bio->bi_size = bioSize;
Packit Service cbade1
#endif
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**
Packit Service cbade1
 * Initialize a bio.
Packit Service cbade1
 *
Packit Service cbade1
 * @param bio    The bio to initialize
Packit Service cbade1
 * @param layer  The layer to which it belongs.
Packit Service cbade1
 **/
Packit Service cbade1
static void initializeBio(BIO *bio, KernelLayer *layer)
Packit Service cbade1
{
Packit Service cbade1
  // Save off important info so it can be set back later
Packit Service cbade1
  unsigned short  vcnt = bio->bi_vcnt;
Packit Service cbade1
  void           *pvt  = bio->bi_private;
Packit Service cbade1
  bio_reset(bio);     // Memsets large portion of bio. Reset all needed fields.
Packit Service cbade1
  bio->bi_private      = pvt;
Packit Service cbade1
  bio->bi_vcnt         = vcnt;
Packit Service cbade1
  bio->bi_end_io       = completeAsyncBio;
Packit Service cbade1
  setBioSector(bio, (sector_t) -1);  // Sector will be set later on.
Packit Service cbade1
  setBioBlockDevice(bio, getKernelLayerBdev(layer));
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void resetBio(BIO *bio, KernelLayer *layer)
Packit Service cbade1
{
Packit Service cbade1
  initializeBio(bio, layer);
Packit Service cbade1
  setBioSize(bio, VDO_BLOCK_SIZE);
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
int allocateBio(KernelLayer *layer, unsigned int bvecCount, BIO **bioPtr)
Packit Service cbade1
{
Packit Service cbade1
  BIO *bio = bio_alloc_bioset(GFP_NOIO, bvecCount, layer->bioset);
Packit Service cbade1
  if (IS_ERR(bio)) {
Packit Service cbade1
    logError("bio allocation failure %ld", PTR_ERR(bio));
Packit Service cbade1
    return PTR_ERR(bio);
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  initializeBio(bio, layer);
Packit Service cbade1
Packit Service cbade1
  *bioPtr = bio;
Packit Service cbade1
  return VDO_SUCCESS;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
int createBio(KernelLayer *layer, char *data, BIO **bioPtr)
Packit Service cbade1
{
Packit Service cbade1
  BIO *bio = NULL;
Packit Service cbade1
  if (data == NULL) {
Packit Service cbade1
    int result = allocateBio(layer, 0, &bio;;
Packit Service cbade1
    if (result != VDO_SUCCESS) {
Packit Service cbade1
      return result;
Packit Service cbade1
    }
Packit Service cbade1
Packit Service cbade1
    *bioPtr = bio;
Packit Service cbade1
    return VDO_SUCCESS;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  unsigned int  len       = VDO_BLOCK_SIZE;
Packit Service cbade1
  unsigned long kaddr     = (unsigned long) data;
Packit Service cbade1
  unsigned long end       = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
Packit Service cbade1
  unsigned long start     = kaddr >> PAGE_SHIFT;
Packit Service cbade1
  const int     bvecCount = end - start;
Packit Service cbade1
Packit Service cbade1
  int result = allocateBio(layer, bvecCount, &bio;;
Packit Service cbade1
  if (result != VDO_SUCCESS) {
Packit Service cbade1
    return result;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  int offset = offset_in_page(kaddr);
Packit Service cbade1
  for (unsigned int i = 0; (i < bvecCount) && (len > 0); i++) {
Packit Service cbade1
    unsigned int bytes = PAGE_SIZE - offset;
Packit Service cbade1
    if (bytes > len) {
Packit Service cbade1
      bytes = len;
Packit Service cbade1
    }
Packit Service cbade1
Packit Service cbade1
  struct page *page
Packit Service cbade1
    = is_vmalloc_addr(data) ? vmalloc_to_page(data) : virt_to_page(data);
Packit Service cbade1
  int bytesAdded = bio_add_page(bio, page, bytes, offset);
Packit Service cbade1
  if (bytesAdded != bytes) {
Packit Service cbade1
    freeBio(bio, layer);
Packit Service cbade1
    return logErrorWithStringError(VDO_BIO_CREATION_FAILED,
Packit Service cbade1
                                   "Could only add %i bytes to bio",
Packit Service cbade1
                                   bytesAdded);
Packit Service cbade1
Packit Service cbade1
    }
Packit Service cbade1
Packit Service cbade1
    data   += bytes;
Packit Service cbade1
    len    -= bytes;
Packit Service cbade1
    offset  = 0;
Packit Service cbade1
  }
Packit Service cbade1
Packit Service cbade1
  *bioPtr = bio;
Packit Service cbade1
  return VDO_SUCCESS;
Packit Service cbade1
}
Packit Service cbade1
Packit Service cbade1
/**********************************************************************/
Packit Service cbade1
void prepareFlushBIO(BIO                 *bio,
Packit Service cbade1
                     void                *context,
Packit Service cbade1
                     struct block_device *device,
Packit Service cbade1
                     bio_end_io_t        *endIOCallback)
Packit Service cbade1
{
Packit Service cbade1
  clearBioOperationAndFlags(bio);
Packit Service cbade1
  /*
Packit Service cbade1
   * One would think we could use REQ_OP_FLUSH on new kernels, but some
Packit Service cbade1
   * layers of the stack don't recognize that as a flush. So do it
Packit Service cbade1
   * like blkdev_issue_flush() and make it a write+flush.
Packit Service cbade1
   */
Packit Service cbade1
  setBioOperationWrite(bio);
Packit Service cbade1
  setBioOperationFlagPreflush(bio);
Packit Service cbade1
  bio->bi_end_io  = endIOCallback;
Packit Service cbade1
  bio->bi_private = context;
Packit Service cbade1
  bio->bi_vcnt    = 0;
Packit Service cbade1
  setBioBlockDevice(bio, device);
Packit Service cbade1
  setBioSize(bio, 0);
Packit Service cbade1
  setBioSector(bio, 0);
Packit Service cbade1
}