|
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 |
}
|