New upstream version 8.1.0
This commit is contained in:
278
client_module/source/toolkit/BitStore.c
Normal file
278
client_module/source/toolkit/BitStore.c
Normal file
@@ -0,0 +1,278 @@
|
||||
#include <common/toolkit/Serialization.h>
|
||||
#include "BitStore.h"
|
||||
|
||||
/**
|
||||
* Set or unset a bit at the given index.
|
||||
*
|
||||
* Note: The bit operations in here are atomic.
|
||||
*
|
||||
* @param isSet true to set or false to unset the corresponding bit.
|
||||
*/
|
||||
void BitStore_setBit(BitStore* this, unsigned bitIndex, bool isSet)
|
||||
{
|
||||
unsigned index = BitStore_getBitBlockIndex(bitIndex);
|
||||
unsigned indexInBitBlock = BitStore_getBitIndexInBitBlock(bitIndex);
|
||||
|
||||
if(unlikely(bitIndex >= this->numBits) )
|
||||
{
|
||||
BEEGFS_BUG_ON(true, "index out of bounds: bitIndex >= this->numBits");
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
if (isSet)
|
||||
set_bit(indexInBitBlock, &this->lowerBits);
|
||||
else
|
||||
clear_bit(indexInBitBlock, &this->lowerBits);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isSet)
|
||||
set_bit(indexInBitBlock, &this->higherBits[index - 1]);
|
||||
else
|
||||
clear_bit(indexInBitBlock, &this->higherBits[index - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* grow/shrink internal buffers when needed for new size.
|
||||
*
|
||||
* note: this method does not reset or copy bits, so consider the bits to be uninitialized after
|
||||
* calling this method. thus, you might want to call clearBits() afterwards to clear all bits.
|
||||
*
|
||||
* @param size number of bits
|
||||
*/
|
||||
void BitStore_setSize(BitStore* this, unsigned newSize)
|
||||
{
|
||||
unsigned oldBitBlockCount = BitStore_calculateBitBlockCount(this->numBits);
|
||||
unsigned newBitBlockCount = BitStore_calculateBitBlockCount(newSize);
|
||||
|
||||
if(newBitBlockCount != oldBitBlockCount)
|
||||
{
|
||||
SAFE_KFREE(this->higherBits);
|
||||
|
||||
if(newBitBlockCount > 1)
|
||||
{
|
||||
this->higherBits = (bitstore_store_type*)os_kmalloc(
|
||||
BITSTORE_BLOCK_SIZE * (newBitBlockCount - 1) );
|
||||
}
|
||||
|
||||
this->numBits = newBitBlockCount * BITSTORE_BLOCK_BIT_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* reset all bits to 0
|
||||
*/
|
||||
void BitStore_clearBits(BitStore* this)
|
||||
{
|
||||
this->lowerBits = BITSTORE_BLOCK_INIT_MASK;
|
||||
|
||||
if(this->higherBits)
|
||||
{
|
||||
unsigned bitBlockCount = BitStore_calculateBitBlockCount(this->numBits);
|
||||
|
||||
memset(this->higherBits, 0, (bitBlockCount-1) * BITSTORE_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* note: serialized format is always one or more full 64bit blocks to keep the format independent
|
||||
* of 32bit/64bit archs.
|
||||
* note: serialized format is 8 byte aligned.
|
||||
*/
|
||||
void BitStore_serialize(SerializeCtx* ctx, const BitStore* this)
|
||||
{
|
||||
unsigned index;
|
||||
unsigned blockCount = BitStore_calculateBitBlockCount(this->numBits);
|
||||
|
||||
// size
|
||||
Serialization_serializeUInt(ctx, this->numBits);
|
||||
|
||||
// padding for 8byte alignment
|
||||
Serialization_serializeUInt(ctx, 0);
|
||||
|
||||
{ // lowerBits
|
||||
if (BITSTORE_BLOCK_SIZE == sizeof(uint64_t) )
|
||||
Serialization_serializeUInt64(ctx, this->lowerBits);
|
||||
else
|
||||
if (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) )
|
||||
Serialization_serializeUInt(ctx, this->lowerBits);
|
||||
}
|
||||
|
||||
{ // higherBits
|
||||
for(index = 0; index < (blockCount - 1); index++)
|
||||
{
|
||||
if (BITSTORE_BLOCK_SIZE == sizeof(uint64_t) )
|
||||
Serialization_serializeUInt64(ctx, this->higherBits[index]);
|
||||
else
|
||||
if (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) )
|
||||
Serialization_serializeUInt(ctx, this->higherBits[index]);
|
||||
}
|
||||
}
|
||||
|
||||
// add padding to allow 64bit deserialize from 32bit serialize
|
||||
if( (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) ) && (blockCount & 1) )
|
||||
{ // odd number of 32bit blocks
|
||||
Serialization_serializeUInt(ctx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param outBitStoreStart deserialization buffer pointer for deserialize()
|
||||
*/
|
||||
bool BitStore_deserializePreprocess(DeserializeCtx* ctx, const char** outBitStoreStart)
|
||||
{
|
||||
unsigned padding;
|
||||
unsigned tmpSize = 0; // =0, because it is used later and otherwise gcc complains
|
||||
// with "may be uninitialized"
|
||||
unsigned blockCount;
|
||||
size_t blocksLen;
|
||||
|
||||
*outBitStoreStart = ctx->data;
|
||||
|
||||
// size
|
||||
if(unlikely(!Serialization_deserializeUInt(ctx, &tmpSize) ) )
|
||||
return false;
|
||||
|
||||
// padding for 8byte alignment
|
||||
if(unlikely(!Serialization_deserializeUInt(ctx, &padding) ) )
|
||||
return false;
|
||||
|
||||
// bit blocks
|
||||
|
||||
blockCount = BitStore_calculateBitBlockCount(tmpSize);
|
||||
|
||||
if(BITSTORE_BLOCK_SIZE == sizeof(uint64_t) )
|
||||
blocksLen = Serialization_serialLenUInt64() * blockCount;
|
||||
else
|
||||
if(BITSTORE_BLOCK_SIZE == sizeof(uint32_t) )
|
||||
{
|
||||
// add padding that allows 64bit deserialize from 32bit serialize
|
||||
if(blockCount & 1)
|
||||
blockCount++;
|
||||
|
||||
blocksLen = Serialization_serialLenUInt() * blockCount;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
// check buffer length
|
||||
if(unlikely(blocksLen > ctx->length) )
|
||||
return false;
|
||||
|
||||
ctx->data += blocksLen;
|
||||
ctx->length -= blocksLen;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BitStore_deserialize(BitStore* this, DeserializeCtx* ctx)
|
||||
{
|
||||
unsigned padding;
|
||||
unsigned blockCount;
|
||||
unsigned bitCount = 0;
|
||||
|
||||
// size
|
||||
// store the bit count in a temp variable because setSizeAndReset() below needs old count
|
||||
Serialization_deserializeUInt(ctx, &bitCount);
|
||||
|
||||
// padding for 8byte alignment
|
||||
Serialization_deserializeUInt(ctx, &padding);
|
||||
|
||||
// clear and alloc memory for the higher bits if needed
|
||||
|
||||
BitStore_setSize(this, bitCount);
|
||||
|
||||
{ // lowerBits
|
||||
if (BITSTORE_BLOCK_SIZE == sizeof(uint64_t) )
|
||||
Serialization_deserializeUInt64(ctx, (uint64_t*)&this->lowerBits);
|
||||
else
|
||||
if (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) )
|
||||
Serialization_deserializeUInt(ctx, (uint32_t*)&this->lowerBits);
|
||||
}
|
||||
|
||||
blockCount = BitStore_calculateBitBlockCount(this->numBits);
|
||||
|
||||
{ // higherBits
|
||||
unsigned index;
|
||||
|
||||
BEEGFS_BUG_ON_DEBUG( (blockCount > 1) && !this->higherBits, "Bug: higherBits==NULL");
|
||||
|
||||
for(index = 0; index < (blockCount - 1); index++)
|
||||
{
|
||||
bitstore_store_type* higherBits = &this->higherBits[index];
|
||||
|
||||
if (BITSTORE_BLOCK_SIZE == sizeof(uint64_t) )
|
||||
Serialization_deserializeUInt64(ctx, (uint64_t*)higherBits);
|
||||
else
|
||||
if (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) )
|
||||
Serialization_deserializeUInt(ctx, (uint32_t*)higherBits);
|
||||
}
|
||||
}
|
||||
|
||||
// add padding that allows 64bit deserialize from 32bit serialize
|
||||
if( (BITSTORE_BLOCK_SIZE == sizeof(uint32_t) ) && (blockCount & 1) )
|
||||
Serialization_deserializeUInt(ctx, &padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this store with values from given other store.
|
||||
*/
|
||||
void BitStore_copy(BitStore* this, BitStore* other)
|
||||
{
|
||||
BitStore_setSize(this, other->numBits);
|
||||
|
||||
this->lowerBits = other->lowerBits;
|
||||
|
||||
if(!this->higherBits)
|
||||
return;
|
||||
|
||||
// we have higherBits to copy
|
||||
{
|
||||
unsigned blockCount = BitStore_calculateBitBlockCount(this->numBits);
|
||||
|
||||
memcpy(this->higherBits, other->higherBits, (blockCount-1) * BITSTORE_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this store with values from given other store.
|
||||
*
|
||||
* To make this copy thread-safe, this method does not attempt any re-alloc of internal buffers,
|
||||
* so the copy will not be complete if the second store contains more blocks.
|
||||
*/
|
||||
void BitStore_copyThreadSafe(BitStore* this, const BitStore* other)
|
||||
{
|
||||
unsigned thisBlockCount;
|
||||
unsigned otherBlockCount;
|
||||
|
||||
this->lowerBits = other->lowerBits;
|
||||
|
||||
if(!this->higherBits)
|
||||
return;
|
||||
|
||||
// copy (or clear) our higherBits
|
||||
|
||||
thisBlockCount = BitStore_calculateBitBlockCount(this->numBits);
|
||||
otherBlockCount = BitStore_calculateBitBlockCount(other->numBits);
|
||||
|
||||
if(otherBlockCount > 1)
|
||||
{ // copy as much as we can from other's higherBits
|
||||
unsigned minCommonBlockCount = MIN(thisBlockCount, otherBlockCount);
|
||||
|
||||
memcpy(this->higherBits, other->higherBits, (minCommonBlockCount-1) * BITSTORE_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
// (this store must at least have one higherBits block, because higherBits!=NULL)
|
||||
if(thisBlockCount > otherBlockCount)
|
||||
{ // zero remaining higherBits of this store
|
||||
unsigned numBlocksDiff = thisBlockCount - otherBlockCount;
|
||||
|
||||
// ("otherBlockCount-1": -1 for lowerBits block)
|
||||
memset(&(this->higherBits[otherBlockCount-1]), 0, numBlocksDiff * BITSTORE_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
137
client_module/source/toolkit/BitStore.h
Normal file
137
client_module/source/toolkit/BitStore.h
Normal file
@@ -0,0 +1,137 @@
|
||||
#ifndef BITSTORE_H_
|
||||
#define BITSTORE_H_
|
||||
|
||||
#include <common/Common.h>
|
||||
#include <common/toolkit/SerializationTypes.h>
|
||||
|
||||
|
||||
typedef long unsigned int bitstore_store_type;
|
||||
|
||||
|
||||
#define BITSTORE_BLOCK_SIZE sizeof(bitstore_store_type) // size of a block in bytes
|
||||
#define BITSTORE_BLOCK_BIT_COUNT (BITSTORE_BLOCK_SIZE * 8) // size of a block in bits
|
||||
#define BITSTORE_BLOCK_INIT_MASK 0UL // mask to zero all bits of block
|
||||
|
||||
|
||||
struct BitStore;
|
||||
typedef struct BitStore BitStore;
|
||||
|
||||
|
||||
static inline void BitStore_init(BitStore* this, bool setZero);
|
||||
static inline void BitStore_initWithSizeAndReset(BitStore* this, unsigned size);
|
||||
static inline void BitStore_uninit(BitStore* this);
|
||||
|
||||
extern void BitStore_setBit(BitStore* this, unsigned bitIndex, bool isSet);
|
||||
|
||||
extern void BitStore_setSize(BitStore* this, unsigned newSize);
|
||||
extern void BitStore_clearBits(BitStore* this);
|
||||
|
||||
extern void BitStore_serialize(SerializeCtx* ctx, const BitStore* this);
|
||||
extern bool BitStore_deserializePreprocess(DeserializeCtx* ctx, const char** outBitStoreStart);
|
||||
extern void BitStore_deserialize(BitStore* this, DeserializeCtx* ctx);
|
||||
|
||||
extern void BitStore_copy(BitStore* this, BitStore* other);
|
||||
extern void BitStore_copyThreadSafe(BitStore* this, const BitStore* other);
|
||||
|
||||
// public inliners
|
||||
|
||||
static inline bool BitStore_getBit(const BitStore* this, unsigned bitIndex);
|
||||
|
||||
static inline unsigned BitStore_getBitBlockIndex(unsigned bitIndex);
|
||||
static inline unsigned BitStore_getBitIndexInBitBlock(unsigned bitIndex);
|
||||
static inline unsigned BitStore_calculateBitBlockCount(unsigned size);
|
||||
|
||||
|
||||
/**
|
||||
* A vector of bits.
|
||||
*/
|
||||
struct BitStore
|
||||
{
|
||||
unsigned numBits; // max number of bits that this bitstore can hold
|
||||
bitstore_store_type lowerBits; // used to avoid overhead of extra alloc for higherBits
|
||||
bitstore_store_type* higherBits; // array, which is alloc'ed only when needed
|
||||
};
|
||||
|
||||
|
||||
void BitStore_init(BitStore* this, bool setZero)
|
||||
{
|
||||
this->numBits = BITSTORE_BLOCK_BIT_COUNT;
|
||||
this->higherBits = NULL;
|
||||
|
||||
if (setZero)
|
||||
this->lowerBits = BITSTORE_BLOCK_INIT_MASK;
|
||||
}
|
||||
|
||||
void BitStore_initWithSizeAndReset(BitStore* this, unsigned size)
|
||||
{
|
||||
BitStore_init(this, false);
|
||||
BitStore_setSize(this, size);
|
||||
BitStore_clearBits(this);
|
||||
}
|
||||
|
||||
void BitStore_uninit(BitStore* this)
|
||||
{
|
||||
SAFE_KFREE_NOSET(this->higherBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether the bit at the given index is set or not.
|
||||
*
|
||||
* Note: The bit operations in here are atomic.
|
||||
*/
|
||||
bool BitStore_getBit(const BitStore* this, unsigned bitIndex)
|
||||
{
|
||||
unsigned index;
|
||||
unsigned indexInBitBlock;
|
||||
|
||||
if(unlikely(bitIndex >= this->numBits) )
|
||||
return false;
|
||||
|
||||
index = BitStore_getBitBlockIndex(bitIndex);
|
||||
indexInBitBlock = BitStore_getBitIndexInBitBlock(bitIndex);
|
||||
|
||||
if (index == 0)
|
||||
return test_bit(indexInBitBlock, &this->lowerBits);
|
||||
else
|
||||
return test_bit(indexInBitBlock, &this->higherBits[index - 1]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* returns the index of the bit block (array value) which contains the searched bit
|
||||
*/
|
||||
unsigned BitStore_getBitBlockIndex(unsigned bitIndex)
|
||||
{
|
||||
return (bitIndex / BITSTORE_BLOCK_BIT_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the index in a bit block for the searched bit
|
||||
*/
|
||||
unsigned BitStore_getBitIndexInBitBlock(unsigned bitIndex)
|
||||
{
|
||||
return (bitIndex % BITSTORE_BLOCK_BIT_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* calculates the needed bit block count for the given BitStore size
|
||||
*/
|
||||
unsigned BitStore_calculateBitBlockCount(unsigned size)
|
||||
{
|
||||
unsigned retVal;
|
||||
|
||||
if(size <= BITSTORE_BLOCK_BIT_COUNT)
|
||||
return 1; // only first block needed
|
||||
|
||||
|
||||
retVal = size / BITSTORE_BLOCK_BIT_COUNT;
|
||||
|
||||
// find out whether we use a partial block
|
||||
|
||||
if (size % BITSTORE_BLOCK_BIT_COUNT)
|
||||
retVal++; // we need another (partial) block
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
#endif /* BITSTORE_H_ */
|
||||
43
client_module/source/toolkit/FhgfsChunkPageVec.c
Normal file
43
client_module/source/toolkit/FhgfsChunkPageVec.c
Normal file
@@ -0,0 +1,43 @@
|
||||
#include <app/log/Logger.h>
|
||||
#include <app/config/Config.h>
|
||||
#include <app/App.h>
|
||||
#include <filesystem/FhgfsOpsPages.h>
|
||||
|
||||
#include "FhgfsChunkPageVec.h"
|
||||
|
||||
/**
|
||||
* Iterate over (remaining) pages on write and handle writeRes (might be an error)
|
||||
*
|
||||
* @param writeRes negative linux error code on error
|
||||
*/
|
||||
void FhgfsChunkPageVec_iterateAllHandleWritePages(FhgfsChunkPageVec* this, int writeRes)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
FhgfsPage* fhgfsPage = FhgfsChunkPageVec_iterateGetNextPage(this);
|
||||
if (!fhgfsPage)
|
||||
break;
|
||||
|
||||
FhgfsOpsPages_endWritePage(fhgfsPage->page, writeRes, this->inode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unmap, unlock and release all pages in the page list vector
|
||||
*/
|
||||
void FhgfsChunkPageVec_iterateAllHandleReadErr(FhgfsChunkPageVec* this)
|
||||
{
|
||||
FhgfsPage* fhgfsPage;
|
||||
|
||||
while (1)
|
||||
{
|
||||
fhgfsPage = FhgfsChunkPageVec_iterateGetNextPage(this);
|
||||
if (!fhgfsPage)
|
||||
break;
|
||||
|
||||
FhgfsPage_unmapUnlockReleaseFhgfsPage(fhgfsPage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
429
client_module/source/toolkit/FhgfsChunkPageVec.h
Normal file
429
client_module/source/toolkit/FhgfsChunkPageVec.h
Normal file
@@ -0,0 +1,429 @@
|
||||
#ifndef FHGFSCHUNKPAGEVEC_H_
|
||||
#define FHGFSCHUNKPAGEVEC_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include <os/OsCompat.h>
|
||||
#include "FhgfsPage.h"
|
||||
#include "FhgfsPageListVec.h"
|
||||
|
||||
#include <linux/writeback.h> // struct write_back control
|
||||
|
||||
|
||||
|
||||
struct FhgfsChunkPageVec;
|
||||
typedef struct FhgfsChunkPageVec FhgfsChunkPageVec;
|
||||
|
||||
|
||||
static inline FhgfsChunkPageVec* FhgfsChunkPageVec_create(App *app, struct inode* inode,
|
||||
struct kmem_cache* pageVecCache, mempool_t* pageVecPool, unsigned numChunkPages);
|
||||
static inline void FhgfsChunkPageVec_destroy(FhgfsChunkPageVec* this);
|
||||
|
||||
|
||||
static inline int FhgfsChunkPageVec_init(FhgfsChunkPageVec* this, App *app, struct inode* inode,
|
||||
struct kmem_cache* pageVecCache, mempool_t* pageVecPool, unsigned numChunkPages);
|
||||
static inline void FhgfsChunkPageVec_uninit(FhgfsChunkPageVec* this);
|
||||
|
||||
static inline int FhgfsChunkPageVec_addPageListVec(FhgfsChunkPageVec* this, mempool_t* pageVecPool);
|
||||
static inline FhgfsPageListVec* FhgfsChunkPageVec_getFirstPageListVec(FhgfsChunkPageVec* this);
|
||||
|
||||
|
||||
static inline bool FhgfsChunkPageVec_pushPage(FhgfsChunkPageVec* this, struct page* page,
|
||||
int usedPageLength);
|
||||
|
||||
static inline unsigned FhgfsChunkPageVec_getSize(FhgfsChunkPageVec* this);
|
||||
|
||||
static inline struct inode* FhgfsChunkPageVec_getInode(FhgfsChunkPageVec* this);
|
||||
|
||||
static inline loff_t FhgfsChunkPageVec_getFirstPageFileOffset(FhgfsChunkPageVec* this);
|
||||
|
||||
static inline FhgfsPage* FhgfsChunkPageVec_iterateGetNextPage(FhgfsChunkPageVec* this);
|
||||
static inline void FhgfsChunkPageVec_resetIterator(FhgfsChunkPageVec* this);
|
||||
|
||||
static inline size_t FhgfsChunkPageVec_getCurrentListVecIterIdx(FhgfsChunkPageVec* this);
|
||||
|
||||
static inline size_t FhgfsChunkPageVec_getCurrentListVecIterIdx(FhgfsChunkPageVec* this);
|
||||
|
||||
static inline size_t FhgfsChunkPageVec_getDataSize(FhgfsChunkPageVec* this);
|
||||
static inline size_t FhgfsChunkPageVec_getRemainingDataSize(FhgfsChunkPageVec* this);
|
||||
|
||||
static inline unsigned _FhgfsChunkPageVec_getChunkPageOffset(FhgfsChunkPageVec* this,
|
||||
pgoff_t index);
|
||||
|
||||
extern void FhgfsChunkPageVec_iterateAllHandleWritePages(FhgfsChunkPageVec* this, int writeRes);
|
||||
extern void FhgfsChunkPageVec_iterateAllHandleReadErr(FhgfsChunkPageVec* this);
|
||||
|
||||
|
||||
|
||||
struct FhgfsChunkPageVec
|
||||
{
|
||||
App* app;
|
||||
struct inode* inode;
|
||||
struct kmem_cache* pageListVecCache;
|
||||
mempool_t* pageVecPool; // only set to non-NULL if there was a pageVec pool allocation
|
||||
|
||||
unsigned numChunkPages;
|
||||
|
||||
unsigned size; // number of used elements of the vector */
|
||||
|
||||
struct list_head pageListVecHead; // list of FhgfsPageListVec entries
|
||||
FhgfsPageListVec* lastAllocListVec; /* last list entry that was allocated, new pages will be
|
||||
* added to this vector */
|
||||
FhgfsPageListVec* currentIterListVec; /* On iterating over the list vector this is the
|
||||
* current FhgfsPageListVec element */
|
||||
size_t currentVecIdx; // current vector index within currentIterListVec
|
||||
size_t maxPagesPerVec; // number of vector pages per FhgfsPageListVec
|
||||
size_t currentListVecIterIdx; /* index as if the current page would be in a large single
|
||||
* array. Note: For log and debugging only */
|
||||
|
||||
size_t lastPageUsedDataSize; // used bytes of the last page
|
||||
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
int numListVecs;
|
||||
#endif
|
||||
|
||||
loff_t firstPageFileOffset;
|
||||
unsigned firstPageChunkOffset;
|
||||
pgoff_t firstPageIdx;
|
||||
pgoff_t lastPageIdx;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
FhgfsChunkPageVec* FhgfsChunkPageVec_create(App* app, struct inode* inode,
|
||||
struct kmem_cache* pageVecCache, mempool_t* pageVecPool, unsigned numChunkPages)
|
||||
{
|
||||
int initErr;
|
||||
|
||||
FhgfsChunkPageVec* this = os_kmalloc(sizeof(*this) );
|
||||
if (unlikely(!this) )
|
||||
return NULL;
|
||||
|
||||
initErr = FhgfsChunkPageVec_init(this, app, inode, pageVecCache, pageVecPool, numChunkPages);
|
||||
if (unlikely(initErr) )
|
||||
{
|
||||
kfree(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
void FhgfsChunkPageVec_destroy(FhgfsChunkPageVec* this)
|
||||
{
|
||||
FhgfsChunkPageVec_uninit(this);
|
||||
|
||||
kfree(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initialize FhgfsChunkPageVec
|
||||
*
|
||||
* @return 0 on success, negative linux error code on error
|
||||
*/
|
||||
int FhgfsChunkPageVec_init(FhgfsChunkPageVec* this, App* app, struct inode* inode,
|
||||
struct kmem_cache* pageVecCache, mempool_t* pageVecPool, unsigned numChunkPages)
|
||||
{
|
||||
int listVecAddRes;
|
||||
|
||||
this->app = app;
|
||||
this->inode = inode;
|
||||
this->pageListVecCache = pageVecCache;
|
||||
this->pageVecPool = NULL; // first initialize to NULL, it will be set later if required
|
||||
|
||||
this->numChunkPages = numChunkPages;
|
||||
this->size = 0;
|
||||
this->firstPageChunkOffset = 0;
|
||||
|
||||
INIT_LIST_HEAD(&this->pageListVecHead);
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
this->numListVecs = 0;
|
||||
#endif
|
||||
|
||||
// add the first pageListVec
|
||||
listVecAddRes = FhgfsChunkPageVec_addPageListVec(this, pageVecPool);
|
||||
if (unlikely(listVecAddRes) )
|
||||
return listVecAddRes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Uninitialize the vector
|
||||
*/
|
||||
void FhgfsChunkPageVec_uninit(FhgfsChunkPageVec* this)
|
||||
{
|
||||
while (!list_empty(&this->pageListVecHead) )
|
||||
{
|
||||
FhgfsPageListVec* listVec = list_entry(this->pageListVecHead.prev, FhgfsPageListVec, list);
|
||||
|
||||
list_del(&listVec->list);
|
||||
|
||||
FhgfsPageListVec_destroy(listVec, this->pageListVecCache, this->pageVecPool);
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
this->numListVecs--;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
if (unlikely(this->numListVecs) )
|
||||
{
|
||||
Logger* log = App_getLogger(this->app);
|
||||
|
||||
Logger_logErrFormatted(log, __func__, "Bug: Failed to distroy all listVecs, remaining: %d",
|
||||
this->numListVecs);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a new pageListVec entry and add it to the list
|
||||
*
|
||||
* @param pageVecPool must be set from FhgfsChunkPageVec_init, but is NULL on adding
|
||||
* additional pageListVecs (secondary allocation)
|
||||
*/
|
||||
int FhgfsChunkPageVec_addPageListVec(FhgfsChunkPageVec* this, mempool_t* pageVecPool)
|
||||
{
|
||||
bool isPoolAlloc = false;
|
||||
|
||||
FhgfsPageListVec* pageListVec = FhgfsPageListVec_create(this->pageListVecCache, pageVecPool,
|
||||
&isPoolAlloc);
|
||||
if (unlikely(!pageListVec) )
|
||||
return -ENOMEM;
|
||||
|
||||
if (isPoolAlloc)
|
||||
this->pageVecPool = pageVecPool;
|
||||
|
||||
if (list_empty(&this->pageListVecHead) )
|
||||
{ // first entry
|
||||
this->currentIterListVec = pageListVec;
|
||||
this->currentVecIdx = 0;
|
||||
this->currentListVecIterIdx = 0;
|
||||
this->maxPagesPerVec = FhgfsPageListVec_getMaxPages(pageListVec);
|
||||
}
|
||||
|
||||
// add this listVec head to the list
|
||||
list_add_tail(&pageListVec->list, &this->pageListVecHead);
|
||||
|
||||
this->lastAllocListVec = pageListVec;
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
this->numListVecs++;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the first pageListVec
|
||||
*/
|
||||
FhgfsPageListVec* FhgfsChunkPageVec_getFirstPageListVec(FhgfsChunkPageVec* this)
|
||||
{
|
||||
if (list_empty(&this->pageListVecHead) )
|
||||
return NULL;
|
||||
|
||||
return list_first_entry(&this->pageListVecHead, FhgfsPageListVec, list);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append another page to the page list-vector
|
||||
*
|
||||
* Note: Returns false if the page-vec is full or a non-consecutive page is to be added
|
||||
*/
|
||||
bool FhgfsChunkPageVec_pushPage(FhgfsChunkPageVec* this, struct page* page,
|
||||
int usedPageLength)
|
||||
{
|
||||
const char* logContext = __func__;
|
||||
bool pushSuccess;
|
||||
|
||||
if ((this->size + this->firstPageChunkOffset) == this->numChunkPages)
|
||||
return false; // pageVec full
|
||||
|
||||
if ((this->size) && (this->lastPageIdx + 1 != page->index) )
|
||||
return false; // non-consecutive page
|
||||
|
||||
pushSuccess = FhgfsPageListVec_pushPage(this->lastAllocListVec, page,
|
||||
usedPageLength);
|
||||
|
||||
if (!pushSuccess)
|
||||
{
|
||||
/* NOTE: pageVecPool must be set to NULL in _addPageListVec here, to ensure the pool is not
|
||||
* used multiple times (2nd alloc might wait for the 1st one to complete and so might
|
||||
* never succeed). */
|
||||
int listVecAddRes = FhgfsChunkPageVec_addPageListVec(this, NULL);
|
||||
if (listVecAddRes)
|
||||
return false;
|
||||
|
||||
// now try again, should work now, if not we give up
|
||||
pushSuccess = FhgfsPageListVec_pushPage(this->lastAllocListVec, page, usedPageLength);
|
||||
if (!pushSuccess)
|
||||
{
|
||||
Logger* log = App_getLogger(this->app);
|
||||
|
||||
Logger_logErr(log, logContext, "Page adding failed.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->size)
|
||||
{
|
||||
this->firstPageFileOffset = page_offset(page);
|
||||
this->firstPageIdx = page->index;
|
||||
|
||||
this->firstPageChunkOffset = _FhgfsChunkPageVec_getChunkPageOffset(this, page->index);
|
||||
}
|
||||
|
||||
this->lastPageIdx = page->index;
|
||||
|
||||
this->size++;
|
||||
|
||||
this->lastPageUsedDataSize = usedPageLength;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the current vector Size
|
||||
*/
|
||||
unsigned FhgfsChunkPageVec_getSize(FhgfsChunkPageVec* this)
|
||||
{
|
||||
return this->size;
|
||||
}
|
||||
|
||||
struct inode* FhgfsChunkPageVec_getInode(FhgfsChunkPageVec* this)
|
||||
{
|
||||
return this->inode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the offset
|
||||
*/
|
||||
loff_t FhgfsChunkPageVec_getFirstPageFileOffset(FhgfsChunkPageVec* this)
|
||||
{
|
||||
return this->firstPageFileOffset;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the next page in the pageListVec.
|
||||
* On first call of this function it returns the first page.
|
||||
* If there are no more pages left it returns NULL and resets the counters.
|
||||
*/
|
||||
FhgfsPage* FhgfsChunkPageVec_iterateGetNextPage(FhgfsChunkPageVec* this)
|
||||
{
|
||||
FhgfsPage* fhgfsPage;
|
||||
|
||||
// printk(KERN_INFO "%s curVecIdx: %zu maxPagesPerVec: %zu size: %u\n",
|
||||
// __func__, this->currentVecIdx, this->maxPagesPerVec, this->size);
|
||||
|
||||
if (this->currentListVecIterIdx == this->size)
|
||||
return NULL; // end reached
|
||||
|
||||
if (this->currentVecIdx == this->maxPagesPerVec)
|
||||
{ // we reached the last page of this vector, take the next list element
|
||||
FhgfsPageListVec* nextListVec;
|
||||
|
||||
if (unlikely(list_is_last(&this->currentIterListVec->list, &this->pageListVecHead) ) )
|
||||
{ // last list element reached, reset and return NULL
|
||||
Logger* log = App_getLogger(this->app);
|
||||
|
||||
// actually must not happen, as we check above for this->currentVecIdx == this->size
|
||||
Logger_logErrFormatted(log, __func__,
|
||||
"Bug: Is last list element, but should have more pages: %zu/%u",
|
||||
this->currentVecIdx, this->size);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nextListVec = list_next_entry(this->currentIterListVec, list);
|
||||
|
||||
this->currentIterListVec = nextListVec;
|
||||
this->currentVecIdx = 0;
|
||||
}
|
||||
|
||||
fhgfsPage = FhgfsPageListVec_getFhgfsPage(this->currentIterListVec, this->currentVecIdx);
|
||||
|
||||
this->currentVecIdx++; // increase for the next round
|
||||
this->currentListVecIterIdx++;
|
||||
|
||||
return fhgfsPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the iterator, so that one may walk over all pages from the beginning again
|
||||
*/
|
||||
void FhgfsChunkPageVec_resetIterator(FhgfsChunkPageVec* this)
|
||||
{
|
||||
this->currentIterListVec = FhgfsChunkPageVec_getFirstPageListVec(this);
|
||||
this->currentVecIdx = 0;
|
||||
this->currentListVecIterIdx = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the curent overall vector index (as if we would have a single vector and not a list of
|
||||
* vectors.
|
||||
*/
|
||||
size_t FhgfsChunkPageVec_getCurrentListVecIterIdx(FhgfsChunkPageVec* this)
|
||||
{
|
||||
return this->currentListVecIterIdx;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the overall size of the chunk-vec-list
|
||||
*/
|
||||
size_t FhgfsChunkPageVec_getDataSize(FhgfsChunkPageVec* this)
|
||||
{
|
||||
return (this->size - 1) * PAGE_SIZE + this->lastPageUsedDataSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remaining size of the chunk-vec-list
|
||||
*/
|
||||
size_t FhgfsChunkPageVec_getRemainingDataSize(FhgfsChunkPageVec* this)
|
||||
{
|
||||
size_t remainingPages = this->size - this->currentListVecIterIdx;
|
||||
|
||||
return (remainingPages - 1) * PAGE_SIZE + this->lastPageUsedDataSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute the offset within a single chunk
|
||||
*/
|
||||
unsigned _FhgfsChunkPageVec_getChunkPageOffset(FhgfsChunkPageVec* this, pgoff_t index)
|
||||
{
|
||||
unsigned numChunkPages = this->numChunkPages;
|
||||
|
||||
/* note: "& (numChunkPages - 1) only works as "% numChunkPages" replacement,
|
||||
* because chunksize (and therefore also chunkPages) must be a power of two */
|
||||
|
||||
return index & (numChunkPages - 1);
|
||||
}
|
||||
|
||||
|
||||
#endif // FHGFSCHUNKPAGEVEC_H_
|
||||
77
client_module/source/toolkit/FhgfsPage.h
Normal file
77
client_module/source/toolkit/FhgfsPage.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef FHGFSPAGE_H_
|
||||
#define FHGFSPAGE_H_
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
|
||||
struct FhgfsPage;
|
||||
typedef struct FhgfsPage FhgfsPage;
|
||||
|
||||
|
||||
static inline void FhgfsPage_unmapUnlockReleasePage(struct page* page);
|
||||
static inline void FhgfsPage_unmapUnlockReleaseFhgfsPage(FhgfsPage* this);
|
||||
|
||||
static inline loff_t FhgfsPage_getFileOffset(FhgfsPage* this);
|
||||
static inline void FhgfsPage_zeroPage(FhgfsPage* this);
|
||||
static inline pgoff_t FhgfsPage_getPageIndex(FhgfsPage* this);
|
||||
|
||||
|
||||
struct FhgfsPage
|
||||
{
|
||||
struct page* page;
|
||||
void *data; // real page data, obtained by kmap(page)
|
||||
int length; // size of *used* data of this page
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Unmap, unlock and release a (kernel) page
|
||||
*/
|
||||
void FhgfsPage_unmapUnlockReleasePage(struct page* page)
|
||||
{
|
||||
kunmap(page);
|
||||
|
||||
unlock_page(page);
|
||||
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unmap, unlock and release an fhgfs page
|
||||
*/
|
||||
void FhgfsPage_unmapUnlockReleaseFhgfsPage(FhgfsPage* this)
|
||||
{
|
||||
FhgfsPage_unmapUnlockReleasePage(this->page);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the offset (within a file) of the page
|
||||
*/
|
||||
loff_t FhgfsPage_getFileOffset(FhgfsPage* this)
|
||||
{
|
||||
return page_offset(this->page); // offset within the file
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero the data of a page
|
||||
*/
|
||||
void FhgfsPage_zeroPage(FhgfsPage* this)
|
||||
{
|
||||
// zero_user_segment() would be optimal, but not available in older kernels
|
||||
// zero_user_segment(page, zeroOffset, BEEGFS_PAGE_SIZE);
|
||||
// BUT we can use our kmapped Fhgfs_Page directly for zeroing
|
||||
|
||||
memset(this->data, 0, PAGE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the page (in file) index of a page
|
||||
*/
|
||||
pgoff_t FhgfsPage_getPageIndex(FhgfsPage* this)
|
||||
{
|
||||
return this->page->index;
|
||||
}
|
||||
|
||||
#endif // FHGFSPAGE_H_
|
||||
159
client_module/source/toolkit/FhgfsPageListVec.h
Normal file
159
client_module/source/toolkit/FhgfsPageListVec.h
Normal file
@@ -0,0 +1,159 @@
|
||||
#ifndef FHGFSPAGELISTVEC_H_
|
||||
#define FHGFSPAGELISTVEC_H_
|
||||
|
||||
#include <linux/mempool.h>
|
||||
|
||||
#include "FhgfsPage.h"
|
||||
|
||||
// max pages in a single pageVec, but by using pageVecList's many of these may be used
|
||||
#ifdef BEEGFS_DEBUG
|
||||
#define BEEGFS_LIST_VEC_MAX_PAGES 16 // if debugging is enable we make sure lists are tested
|
||||
#else
|
||||
#define BEEGFS_LIST_VEC_MAX_PAGES 32
|
||||
#endif
|
||||
|
||||
struct FhgfsPageListVec;
|
||||
typedef struct FhgfsPageListVec FhgfsPageListVec;
|
||||
|
||||
|
||||
static inline FhgfsPageListVec* FhgfsPageListVec_create(struct kmem_cache* pageVecCache,
|
||||
mempool_t* pageListPool, bool* outIsPoolAlloc);
|
||||
static inline void FhgfsPageListVec_destroy(FhgfsPageListVec* this,
|
||||
struct kmem_cache* pageVecCache, mempool_t* pageVecPool);
|
||||
static inline void FhgfsPageListVec_init(FhgfsPageListVec* this);
|
||||
static inline void FhgfsPageListVec_uninit(FhgfsPageListVec* this);
|
||||
|
||||
static inline bool FhgfsPageListVec_pushPage(FhgfsPageListVec* this, struct page* page,
|
||||
int usedPageLength);
|
||||
|
||||
static inline size_t FhgfsPageListVec_getMaxPages(FhgfsPageListVec* this);
|
||||
|
||||
static inline FhgfsPage* FhgfsPageListVec_getFhgfsPage(FhgfsPageListVec* this, size_t index);
|
||||
|
||||
|
||||
|
||||
struct FhgfsPageListVec
|
||||
{
|
||||
FhgfsPage fhgfsPages[BEEGFS_LIST_VEC_MAX_PAGES];
|
||||
size_t usedPages; // number of used pages of this vector
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param pageVecPool is only set if this is the first allocation from FhgfsChunkPageVec, as
|
||||
* this allocation must not fail.
|
||||
* @param outIsPoolAlloc is supposed to be initialized to false
|
||||
*/
|
||||
FhgfsPageListVec* FhgfsPageListVec_create(struct kmem_cache* pageVecCache,
|
||||
mempool_t* pageVecPool, bool* outIsPoolAlloc)
|
||||
{
|
||||
FhgfsPageListVec* this;
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
// test cache alloc failures in debug mode
|
||||
if (jiffies & 1) // odd jiffies simulate an allocation error
|
||||
this = NULL;
|
||||
else
|
||||
this = (FhgfsPageListVec*) kmem_cache_alloc(pageVecCache, GFP_NOFS);
|
||||
#else
|
||||
this = (FhgfsPageListVec*) kmem_cache_alloc(pageVecCache, GFP_NOFS);
|
||||
#endif
|
||||
|
||||
if (unlikely(!this) )
|
||||
{
|
||||
if (pageVecPool)
|
||||
{
|
||||
this = mempool_alloc(pageVecPool, GFP_NOFS);
|
||||
if (unlikely(!this) )
|
||||
{
|
||||
printk_fhgfs(KERN_WARNING, "%s: mempool_alloc unexpectedly failed\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*outIsPoolAlloc = true;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FhgfsPageListVec_init(this);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
void FhgfsPageListVec_destroy(FhgfsPageListVec* this, struct kmem_cache* pageVecCache,
|
||||
mempool_t* pageVecPool)
|
||||
{
|
||||
FhgfsPageListVec_uninit(this);
|
||||
|
||||
if (pageVecPool)
|
||||
mempool_free(this, pageVecPool);
|
||||
else
|
||||
kmem_cache_free(pageVecCache, this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize FhgfsPageListVec
|
||||
*/
|
||||
void FhgfsPageListVec_init(FhgfsPageListVec* this)
|
||||
{
|
||||
this->usedPages = 0;
|
||||
INIT_LIST_HEAD(&this->list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninitialize FhgfsPageListVec
|
||||
*/
|
||||
void FhgfsPageListVec_uninit(FhgfsPageListVec* this)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add (append) a kernel page to this pageListVec
|
||||
*
|
||||
* @return false if the pageListVec is full, true if adding succeeded
|
||||
*/
|
||||
bool FhgfsPageListVec_pushPage(FhgfsPageListVec* this, struct page* page, int usedPageLength)
|
||||
{
|
||||
FhgfsPage* fhgfsPage;
|
||||
|
||||
if (this->usedPages == BEEGFS_LIST_VEC_MAX_PAGES)
|
||||
return false;
|
||||
|
||||
fhgfsPage = &this->fhgfsPages[this->usedPages];
|
||||
this->usedPages++;
|
||||
|
||||
fhgfsPage->page = page;
|
||||
fhgfsPage->data = kmap(page);
|
||||
fhgfsPage->length = usedPageLength;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of max pages of this vector
|
||||
*/
|
||||
size_t FhgfsPageListVec_getMaxPages(FhgfsPageListVec* this)
|
||||
{
|
||||
return BEEGFS_LIST_VEC_MAX_PAGES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the page at the given index
|
||||
*/
|
||||
FhgfsPage* FhgfsPageListVec_getFhgfsPage(FhgfsPageListVec* this, size_t index)
|
||||
{
|
||||
return &this->fhgfsPages[index];
|
||||
}
|
||||
|
||||
#endif // FHGFSPAGELISTVEC_H_
|
||||
13
client_module/source/toolkit/InodeRefStore.c
Normal file
13
client_module/source/toolkit/InodeRefStore.c
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "InodeRefStore.h"
|
||||
|
||||
|
||||
int __InodeRefStore_keyComparator(const void* key1, const void* key2)
|
||||
{
|
||||
if(key1 < key2)
|
||||
return -1;
|
||||
else
|
||||
if(key1 > key2)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
246
client_module/source/toolkit/InodeRefStore.h
Normal file
246
client_module/source/toolkit/InodeRefStore.h
Normal file
@@ -0,0 +1,246 @@
|
||||
#ifndef INODEREFSTORE_H_
|
||||
#define INODEREFSTORE_H_
|
||||
|
||||
#include <common/Common.h>
|
||||
#include <common/toolkit/tree/PointerRBTree.h>
|
||||
#include <common/toolkit/tree/PointerRBTreeIter.h>
|
||||
#include <common/threading/Mutex.h>
|
||||
#include <os/OsDeps.h>
|
||||
|
||||
|
||||
/*
|
||||
* This class saves references to inodes in a tree. It has special methods to make it suitable
|
||||
* for async cache flushes.
|
||||
*/
|
||||
|
||||
struct InodeRefStore;
|
||||
typedef struct InodeRefStore InodeRefStore;
|
||||
|
||||
static inline void InodeRefStore_init(InodeRefStore* this);
|
||||
static inline InodeRefStore* InodeRefStore_construct(void);
|
||||
static inline void InodeRefStore_uninit(InodeRefStore* this);
|
||||
static inline void InodeRefStore_destruct(InodeRefStore* this);
|
||||
|
||||
static inline void InodeRefStore_addAndReferenceInode(InodeRefStore* this, struct inode* inode);
|
||||
static inline void InodeRefStore_addOrPutInode(InodeRefStore* this, struct inode* inode);
|
||||
static inline struct inode* InodeRefStore_getAndRemoveFirstInode(InodeRefStore* this);
|
||||
static inline struct inode* InodeRefStore_getAndRemoveNextInode(InodeRefStore* this,
|
||||
struct inode* oldInode);
|
||||
static inline bool InodeRefStore_removeAndReleaseInode(InodeRefStore* this,
|
||||
struct inode* inode);
|
||||
|
||||
// getters & setters
|
||||
static inline size_t InodeRefStore_getSize(InodeRefStore* this);
|
||||
|
||||
|
||||
// static
|
||||
extern int __InodeRefStore_keyComparator(const void* key1, const void* key2);
|
||||
|
||||
|
||||
struct InodeRefStore
|
||||
{
|
||||
RBTree tree; // keys are inode pointers, values are unused (NULL)
|
||||
|
||||
Mutex mutex;
|
||||
};
|
||||
|
||||
|
||||
void InodeRefStore_init(InodeRefStore* this)
|
||||
{
|
||||
Mutex_init(&this->mutex);
|
||||
|
||||
PointerRBTree_init(&this->tree, __InodeRefStore_keyComparator);
|
||||
}
|
||||
|
||||
struct InodeRefStore* InodeRefStore_construct(void)
|
||||
{
|
||||
struct InodeRefStore* this = (InodeRefStore*)os_kmalloc(sizeof(*this) );
|
||||
|
||||
InodeRefStore_init(this);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void InodeRefStore_uninit(InodeRefStore* this)
|
||||
{
|
||||
RBTreeIter iter = PointerRBTree_begin(&this->tree);
|
||||
for( ; !PointerRBTreeIter_end(&iter); PointerRBTreeIter_next(&iter) )
|
||||
{
|
||||
struct inode* inode = PointerRBTreeIter_key(&iter);
|
||||
iput(inode);
|
||||
}
|
||||
|
||||
PointerRBTree_uninit(&this->tree);
|
||||
|
||||
Mutex_uninit(&this->mutex);
|
||||
}
|
||||
|
||||
void InodeRefStore_destruct(InodeRefStore* this)
|
||||
{
|
||||
InodeRefStore_uninit(this);
|
||||
|
||||
kfree(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given inode to the tree if not there yet.
|
||||
*
|
||||
* Note: The inode is referenced by calling ihold(). It must later be released by calling
|
||||
* iput().
|
||||
*/
|
||||
void InodeRefStore_addAndReferenceInode(InodeRefStore* this, struct inode* inode)
|
||||
{
|
||||
Mutex_lock(&this->mutex);
|
||||
|
||||
// only insert it, if not already within the three (mutex lock required)
|
||||
if(PointerRBTree_insert(&this->tree, inode, NULL) )
|
||||
{
|
||||
/* Only increase the counter once we know we had to insert it.
|
||||
* Also increase the counter within the lock to avoid racing with _removeAndRelease() */
|
||||
ihold(inode);
|
||||
}
|
||||
|
||||
Mutex_unlock(&this->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given inode to the tree or drops the reference if the inode already exists in the tree.
|
||||
*
|
||||
* Note: Usually, _addAndReferenceInode() should be used; this is only called if caller ensured
|
||||
* that there is an extra inode reference (igrab() ) for this inode.
|
||||
*/
|
||||
void InodeRefStore_addOrPutInode(InodeRefStore* this, struct inode* inode)
|
||||
{
|
||||
Mutex_lock(&this->mutex);
|
||||
|
||||
// only insert it, if not already within the three (mutex lock required)
|
||||
if(!PointerRBTree_insert(&this->tree, inode, NULL) )
|
||||
{
|
||||
/* Decrease ref counter if the inode already exists, because that means the caller who
|
||||
* added the inode already increased the ref count - and we only want to have one single
|
||||
* reference per inode in this store.
|
||||
* Also decrease the counter within the lock to avoid racing with _removeAndRelease() */
|
||||
iput(inode);
|
||||
}
|
||||
|
||||
Mutex_unlock(&this->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get first element in the store (for iteration).
|
||||
*
|
||||
* Note: If the inode was added by _addAndReferenceInode(), the caller will probably also want to
|
||||
* call iput() after work with the returned inode is complete.
|
||||
*
|
||||
* @return NULL if the store is empty
|
||||
*/
|
||||
struct inode* InodeRefStore_getAndRemoveFirstInode(InodeRefStore* this)
|
||||
{
|
||||
struct inode* firstInode = NULL;
|
||||
RBTreeIter iter;
|
||||
|
||||
Mutex_lock(&this->mutex);
|
||||
|
||||
iter = PointerRBTree_begin(&this->tree);
|
||||
if(!PointerRBTreeIter_end(&iter) )
|
||||
{
|
||||
firstInode = PointerRBTreeIter_key(&iter);
|
||||
|
||||
PointerRBTree_erase(&this->tree, firstInode);
|
||||
}
|
||||
|
||||
Mutex_unlock(&this->mutex);
|
||||
|
||||
return firstInode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next inode and remove it from the store. Typically used for iteration based on
|
||||
* _getAndRemoveFirstInode().
|
||||
*
|
||||
* Note: If the inode was added by _addAndReferenceInode(), the caller will probably also want to
|
||||
* call iput() after work with the returned inode is complete.
|
||||
*
|
||||
* @return NULL if no next inode exists in the store
|
||||
*/
|
||||
struct inode* InodeRefStore_getAndRemoveNextInode(InodeRefStore* this, struct inode* oldInode)
|
||||
{
|
||||
struct inode* nextInode = NULL;
|
||||
RBTreeIter iter;
|
||||
|
||||
Mutex_lock(&this->mutex);
|
||||
|
||||
iter = PointerRBTree_find(&this->tree, oldInode);
|
||||
if(!PointerRBTreeIter_end(&iter) )
|
||||
{ // fast path: oldInode still (or again) exists in the tree, so we can easily find next
|
||||
|
||||
// retrieve (and remove) next element
|
||||
PointerRBTreeIter_next(&iter);
|
||||
if(!PointerRBTreeIter_end(&iter) )
|
||||
{
|
||||
nextInode = PointerRBTreeIter_key(&iter);
|
||||
|
||||
PointerRBTree_erase(&this->tree, nextInode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // slow path: oldInode doesn't exist, so we need to insert it to find the next element
|
||||
|
||||
// temporarily insert oldInode to find next element
|
||||
PointerRBTree_insert(&this->tree, oldInode, NULL);
|
||||
iter = PointerRBTree_find(&this->tree, oldInode);
|
||||
|
||||
// retrieve (and remove) next element
|
||||
PointerRBTreeIter_next(&iter);
|
||||
if(!PointerRBTreeIter_end(&iter) )
|
||||
{
|
||||
nextInode = PointerRBTreeIter_key(&iter);
|
||||
|
||||
PointerRBTree_erase(&this->tree, nextInode);
|
||||
}
|
||||
|
||||
// remove temporarily inserted oldInode
|
||||
PointerRBTree_erase(&this->tree, oldInode);
|
||||
}
|
||||
|
||||
Mutex_unlock(&this->mutex);
|
||||
|
||||
return nextInode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove inode from store. Only if it existed in the store, we also drop an inode reference.
|
||||
*
|
||||
* @return false if no such element existed, true otherwise
|
||||
*/
|
||||
bool InodeRefStore_removeAndReleaseInode(InodeRefStore* this, struct inode* inode)
|
||||
{
|
||||
bool eraseRes;
|
||||
|
||||
Mutex_lock(&this->mutex);
|
||||
|
||||
eraseRes = PointerRBTree_erase(&this->tree, inode);
|
||||
if(eraseRes)
|
||||
iput(inode); // iput must be inside the mutex to avoid races with _addAndReference()
|
||||
|
||||
Mutex_unlock(&this->mutex);
|
||||
|
||||
return eraseRes;
|
||||
}
|
||||
|
||||
|
||||
size_t InodeRefStore_getSize(InodeRefStore* this)
|
||||
{
|
||||
size_t retVal;
|
||||
|
||||
Mutex_lock(&this->mutex);
|
||||
|
||||
retVal = PointerRBTree_length(&this->tree);
|
||||
|
||||
Mutex_unlock(&this->mutex);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
#endif /* INODEREFSTORE_H_ */
|
||||
309
client_module/source/toolkit/NoAllocBufferStore.c
Normal file
309
client_module/source/toolkit/NoAllocBufferStore.c
Normal file
@@ -0,0 +1,309 @@
|
||||
#include <toolkit/NoAllocBufferStore.h>
|
||||
#include <common/toolkit/tree/PointerRBTree.h>
|
||||
#include <common/toolkit/tree/PointerRBTreeIter.h>
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
static int __NoAllocBufferStore_PointerComparator(const void* ptr1, const void* ptr2);
|
||||
static void __NoAllocBufferStore_debugAddTask(NoAllocBufferStore* this);
|
||||
static void __NoAllocBufferStore_debugRemoveTask(NoAllocBufferStore* this);
|
||||
static void __NoAllocBufferStore_debugCheckTask(NoAllocBufferStore* this);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This BufferStore does not use any kind of memory allocation during its normal
|
||||
* operation. However, it does (de)allocate the requested number of buffers
|
||||
* during (de)initialization.
|
||||
*/
|
||||
|
||||
static bool __NoAllocBufferStore_initBuffers(NoAllocBufferStore* this);
|
||||
|
||||
|
||||
struct NoAllocBufferStore
|
||||
{
|
||||
char** bufArray;
|
||||
|
||||
size_t numBufs;
|
||||
size_t bufSize;
|
||||
|
||||
size_t numAvailable; // number of currently available buffers in the store
|
||||
|
||||
Mutex mutex;
|
||||
Condition newBufCond;
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
RBTree pidDebugTree; // store tasks that have taken a buffer
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
/**
|
||||
* Return suitable values for a tree
|
||||
*/
|
||||
static int __NoAllocBufferStore_PointerComparator(const void* ptr1, const void* ptr2)
|
||||
{
|
||||
if (ptr1 < ptr2)
|
||||
return -1;
|
||||
else if (ptr1 == ptr2)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
/**
|
||||
* @param numBufs number of buffers to be allocated, may be 0
|
||||
* @param bufSize size of each allocated buffer
|
||||
*/
|
||||
bool NoAllocBufferStore_init(NoAllocBufferStore* this, size_t numBufs, size_t bufSize)
|
||||
{
|
||||
this->numAvailable = 0;
|
||||
this->numBufs = numBufs;
|
||||
this->bufSize = bufSize;
|
||||
|
||||
Mutex_init(&this->mutex);
|
||||
Condition_init(&this->newBufCond);
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
PointerRBTree_init(&this->pidDebugTree, __NoAllocBufferStore_PointerComparator);
|
||||
#endif
|
||||
|
||||
if(!__NoAllocBufferStore_initBuffers(this) )
|
||||
{
|
||||
Mutex_uninit(&this->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
/**
|
||||
* Add the buffer allocator into the debug tree, including the call trace. That way we can easily
|
||||
* figure out two (forbidden) calls from the same thread. For debug builds only!
|
||||
*/
|
||||
static void __NoAllocBufferStore_debugAddTask(NoAllocBufferStore* this)
|
||||
{
|
||||
void * trace = os_saveStackTrace();
|
||||
PointerRBTree_insert(&this->pidDebugTree, (void *)(long)current->pid, trace);
|
||||
}
|
||||
#else
|
||||
#define __NoAllocBufferStore_debugAddTask(this)
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
/**
|
||||
* Tasks frees the buffer, so also remove it from the debug tree. For debug builds only!
|
||||
*/
|
||||
static void __NoAllocBufferStore_debugRemoveTask(NoAllocBufferStore* this)
|
||||
{
|
||||
void * currentPID = (void *)(long)current->pid;
|
||||
RBTreeIter iter = PointerRBTree_find(&this->pidDebugTree, currentPID);
|
||||
void *trace;
|
||||
|
||||
if (PointerRBTreeIter_end(&iter) == true)
|
||||
return; // Buffer was likely/hopefully added with NoAllocBufferStore_instantBuf()
|
||||
|
||||
trace = PointerRBTreeIter_value(&iter);
|
||||
os_freeStackTrace(trace);
|
||||
|
||||
PointerRBTree_erase(&this->pidDebugTree, currentPID);
|
||||
}
|
||||
#else
|
||||
#define __NoAllocBufferStore_debugRemoveTask(this)
|
||||
#endif
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
/**
|
||||
* Check if the debug tree already knows about the current pid and if so, print a debug message
|
||||
* to syslog to warn about the possible deadlock. NOTE: For debug builds only!
|
||||
*/
|
||||
static void __NoAllocBufferStore_debugCheckTask(NoAllocBufferStore* this)
|
||||
{
|
||||
RBTreeIter iter = PointerRBTree_find(&this->pidDebugTree, (void *)(long)current->pid);
|
||||
if (unlikely(PointerRBTreeIter_end(&iter) == false) )
|
||||
{
|
||||
void *trace = PointerRBTreeIter_value(&iter);
|
||||
printk_fhgfs(KERN_WARNING, "Thread called BufferStore two times - might deadlock!\n");
|
||||
dump_stack();
|
||||
printk_fhgfs(KERN_WARNING, "Call trace of the previous allocation:\n");
|
||||
os_printStackTrace(trace, 4);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define __NoAllocBufferStore_debugCheckTask(this)
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
|
||||
/**
|
||||
* @param numBufs number of buffers to be allocated
|
||||
* @param bufSize size of each allocated buffer
|
||||
*/
|
||||
struct NoAllocBufferStore* NoAllocBufferStore_construct(size_t numBufs, size_t bufSize)
|
||||
{
|
||||
struct NoAllocBufferStore* this = kmalloc(sizeof(*this), GFP_NOFS);
|
||||
|
||||
if(!this ||
|
||||
!NoAllocBufferStore_init(this, numBufs, bufSize) )
|
||||
{
|
||||
kfree(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void NoAllocBufferStore_uninit(NoAllocBufferStore* this)
|
||||
{
|
||||
// delete buffers
|
||||
size_t i;
|
||||
|
||||
for(i=0; i < this->numAvailable; i++)
|
||||
vfree(this->bufArray[i]);
|
||||
|
||||
// normal clean-up
|
||||
Mutex_uninit(&this->mutex);
|
||||
|
||||
SAFE_KFREE(this->bufArray);
|
||||
|
||||
#ifdef BEEGFS_DEBUG
|
||||
PointerRBTree_uninit(&this->pidDebugTree);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void NoAllocBufferStore_destruct(NoAllocBufferStore* this)
|
||||
{
|
||||
NoAllocBufferStore_uninit(this);
|
||||
|
||||
kfree(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a buffer from the store.
|
||||
* Waits if no buffer is immediately available.
|
||||
*
|
||||
* @return a valid buffer pointer
|
||||
*/
|
||||
char* NoAllocBufferStore_waitForBuf(NoAllocBufferStore* this)
|
||||
{
|
||||
char* buf;
|
||||
|
||||
Mutex_lock(&this->mutex);
|
||||
|
||||
__NoAllocBufferStore_debugCheckTask(this);
|
||||
|
||||
while(!this->numAvailable)
|
||||
Condition_wait(&this->newBufCond, &this->mutex);
|
||||
|
||||
buf = (this->bufArray)[this->numAvailable-1];
|
||||
(this->numAvailable)--;
|
||||
|
||||
__NoAllocBufferStore_debugAddTask(this);
|
||||
|
||||
Mutex_unlock(&this->mutex);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a buffer from the store.
|
||||
* Fails if no buffer is immediately available.
|
||||
*
|
||||
* @return buffer pointer if a buffer was immediately available, NULL otherwise
|
||||
*/
|
||||
char* NoAllocBufferStore_instantBuf(NoAllocBufferStore* this)
|
||||
{
|
||||
char* buf;
|
||||
|
||||
Mutex_lock(&this->mutex);
|
||||
|
||||
if(!this->numAvailable)
|
||||
buf = NULL;
|
||||
else
|
||||
{
|
||||
buf = (this->bufArray)[this->numAvailable-1];
|
||||
(this->numAvailable)--;
|
||||
|
||||
/* note: no _debugAddTask() here, because _instantBuf() is specifically intended to avoid
|
||||
deadlocks (so we would generate only a lot of false alarms with this). */
|
||||
}
|
||||
|
||||
Mutex_unlock(&this->mutex);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-add a buffer to the pool
|
||||
*/
|
||||
void NoAllocBufferStore_addBuf(NoAllocBufferStore* this, char* buf)
|
||||
{
|
||||
if (unlikely(buf == NULL) )
|
||||
{
|
||||
BEEGFS_BUG_ON(buf == NULL, "NULL buffer detected!");
|
||||
return; // If the caller had a real buffer, it will leak now!
|
||||
}
|
||||
|
||||
Mutex_lock(&this->mutex);
|
||||
|
||||
Condition_signal(&this->newBufCond);
|
||||
|
||||
(this->bufArray)[this->numAvailable] = buf;
|
||||
(this->numAvailable)++;
|
||||
|
||||
__NoAllocBufferStore_debugRemoveTask(this);
|
||||
|
||||
Mutex_unlock(&this->mutex);
|
||||
}
|
||||
|
||||
bool __NoAllocBufferStore_initBuffers(NoAllocBufferStore* this)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
this->bufArray = this->numBufs ? (char**)os_kzalloc(this->numBufs * sizeof(char*) ) : NULL;
|
||||
if (!this->bufArray && (this->numBufs != 0) ) // numBufs is 0 for pageIO buffers in buffered mode
|
||||
return false;
|
||||
|
||||
for(i = 0; i < this->numBufs; i++)
|
||||
{
|
||||
char* buf = vmalloc(this->bufSize);
|
||||
if(!buf)
|
||||
{
|
||||
printk_fhgfs(KERN_WARNING, "NoAllocBufferStore_initBuffers: vmalloc failed to alloc "
|
||||
"%lld bytes for buffer number %lld\n", (long long)this->bufSize, (long long)i);
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
(this->bufArray)[i] = buf;
|
||||
(this->numAvailable)++;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
for(i = 0; i < this->numBufs; i++)
|
||||
vfree(this->bufArray[i]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t NoAllocBufferStore_getNumAvailable(NoAllocBufferStore* this)
|
||||
{
|
||||
size_t numAvailable;
|
||||
|
||||
Mutex_lock(&this->mutex);
|
||||
|
||||
numAvailable = this->numAvailable;
|
||||
|
||||
Mutex_unlock(&this->mutex);
|
||||
|
||||
return numAvailable;
|
||||
}
|
||||
|
||||
size_t NoAllocBufferStore_getBufSize(NoAllocBufferStore* this)
|
||||
{
|
||||
return this->bufSize;
|
||||
}
|
||||
32
client_module/source/toolkit/NoAllocBufferStore.h
Normal file
32
client_module/source/toolkit/NoAllocBufferStore.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef NOALLOCBUFFERSTORE_H_
|
||||
#define NOALLOCBUFFERSTORE_H_
|
||||
|
||||
#include <common/threading/Mutex.h>
|
||||
#include <common/threading/Condition.h>
|
||||
#include <common/threading/Thread.h>
|
||||
#include <common/Common.h>
|
||||
|
||||
/**
|
||||
* This BufferStore does not use any kind of memory allocation during its normal
|
||||
* operation. However, it does (de)allocate the requested number of buffers
|
||||
* during (de)initialization.
|
||||
*/
|
||||
|
||||
struct NoAllocBufferStore;
|
||||
typedef struct NoAllocBufferStore NoAllocBufferStore;
|
||||
|
||||
extern __must_check bool NoAllocBufferStore_init(NoAllocBufferStore* this, size_t numBufs,
|
||||
size_t bufSize);
|
||||
extern NoAllocBufferStore* NoAllocBufferStore_construct(size_t numBufs, size_t bufSize);
|
||||
extern void NoAllocBufferStore_uninit(NoAllocBufferStore* this);
|
||||
extern void NoAllocBufferStore_destruct(NoAllocBufferStore* this);
|
||||
|
||||
extern char* NoAllocBufferStore_waitForBuf(NoAllocBufferStore* this);
|
||||
extern char* NoAllocBufferStore_instantBuf(NoAllocBufferStore* this);
|
||||
extern void NoAllocBufferStore_addBuf(NoAllocBufferStore* this, char* buf);
|
||||
|
||||
// getters & setters
|
||||
extern size_t NoAllocBufferStore_getNumAvailable(NoAllocBufferStore* this);
|
||||
extern size_t NoAllocBufferStore_getBufSize(NoAllocBufferStore* this);
|
||||
|
||||
#endif /*NOALLOCBUFFERSTORE_H_*/
|
||||
76
client_module/source/toolkit/StatFsCache.c
Normal file
76
client_module/source/toolkit/StatFsCache.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <app/App.h>
|
||||
#include <net/filesystem/FhgfsOpsRemoting.h>
|
||||
#include "StatFsCache.h"
|
||||
|
||||
|
||||
/**
|
||||
* If caching is enabled and cache is not expired, this will return the cached values. Otherwise
|
||||
* FhgfsOpsRemoting_statStoragePath() will be called for fresh values.
|
||||
*
|
||||
* @param outSizeTotal in bytes.
|
||||
* @param outSizeFree in bytes.
|
||||
* @return if remoting fails, the error code from FhgfsOpsRemoting_statStoragePath() will be
|
||||
* returned.
|
||||
*/
|
||||
FhgfsOpsErr StatFsCache_getFreeSpace(StatFsCache* this, int64_t* outSizeTotal,
|
||||
int64_t* outSizeFree)
|
||||
{
|
||||
Config* cfg = App_getConfig(this->app);
|
||||
unsigned tuneStatFsCacheSecs = Config_getTuneStatFsCacheSecs(cfg); // zero means disable caching
|
||||
|
||||
if(tuneStatFsCacheSecs)
|
||||
{ // caching enabled, test if cache values expired
|
||||
|
||||
RWLock_readLock(&this->rwlock); // L O C K (read)
|
||||
|
||||
if(!Time_getIsZero(&this->lastUpdateTime) && // zero time means cache values are uninitialized
|
||||
(Time_elapsedMS(&this->lastUpdateTime) <= (tuneStatFsCacheSecs*1000) ) ) // *1000 for MS
|
||||
{ // cache values are still valid
|
||||
*outSizeTotal = this->cachedSizeTotal;
|
||||
*outSizeFree = this->cachedSizeFree;
|
||||
|
||||
RWLock_readUnlock(&this->rwlock); // U N L O C K (read)
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
// cache values not valid
|
||||
|
||||
RWLock_readUnlock(&this->rwlock); // U N L O C K (read)
|
||||
}
|
||||
|
||||
// get fresh values from servers
|
||||
|
||||
{
|
||||
FhgfsOpsErr retVal = FhgfsOpsRemoting_statStoragePath(this->app, true,
|
||||
outSizeTotal, outSizeFree);
|
||||
|
||||
if(retVal != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
*outSizeTotal = 0;
|
||||
*outSizeFree = 0;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
// update cached values
|
||||
/* note: as we don't hold a write-lock during remoting (to allow parallelism), it's possible that
|
||||
multiple threads are racing with different values on the update below, but that's uncritical
|
||||
for free space info */
|
||||
|
||||
if(tuneStatFsCacheSecs)
|
||||
{
|
||||
RWLock_writeLock(&this->rwlock); // L O C K (write)
|
||||
|
||||
this->cachedSizeTotal = *outSizeTotal;
|
||||
this->cachedSizeFree = *outSizeFree;
|
||||
|
||||
Time_setToNow(&this->lastUpdateTime);
|
||||
|
||||
RWLock_writeUnlock(&this->rwlock); // U N L O C K (write)
|
||||
}
|
||||
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
67
client_module/source/toolkit/StatFsCache.h
Normal file
67
client_module/source/toolkit/StatFsCache.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef STATFSCACHE_H_
|
||||
#define STATFSCACHE_H_
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/threading/RWLock.h>
|
||||
#include <common/Common.h>
|
||||
#include <os/OsDeps.h>
|
||||
|
||||
|
||||
struct App; // forward declaration
|
||||
|
||||
struct StatFsCache;
|
||||
typedef struct StatFsCache StatFsCache;
|
||||
|
||||
|
||||
static inline void StatFsCache_init(StatFsCache* this, App* app);
|
||||
static inline StatFsCache* StatFsCache_construct(App* app);
|
||||
static inline void StatFsCache_destruct(StatFsCache* this);
|
||||
|
||||
FhgfsOpsErr StatFsCache_getFreeSpace(StatFsCache* this, int64_t* outSizeTotal,
|
||||
int64_t* outSizeFree);
|
||||
|
||||
|
||||
/**
|
||||
* This class provides a cache for free space information to avoid querying all storage targets
|
||||
* for each statfs() syscall.
|
||||
*/
|
||||
struct StatFsCache
|
||||
{
|
||||
App* app;
|
||||
|
||||
RWLock rwlock;
|
||||
|
||||
Time lastUpdateTime; // time when cache values were last updated
|
||||
|
||||
int64_t cachedSizeTotal; // in bytes
|
||||
int64_t cachedSizeFree; // in bytes
|
||||
};
|
||||
|
||||
|
||||
void StatFsCache_init(StatFsCache* this, App* app)
|
||||
{
|
||||
this->app = app;
|
||||
|
||||
RWLock_init(&this->rwlock);
|
||||
|
||||
Time_initZero(&this->lastUpdateTime);
|
||||
}
|
||||
|
||||
struct StatFsCache* StatFsCache_construct(App* app)
|
||||
{
|
||||
struct StatFsCache* this = (StatFsCache*)os_kmalloc(sizeof(*this) );
|
||||
|
||||
if(likely(this) )
|
||||
StatFsCache_init(this, app);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void StatFsCache_destruct(StatFsCache* this)
|
||||
{
|
||||
kfree(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* STATFSCACHE_H_ */
|
||||
Reference in New Issue
Block a user