New upstream version 8.1.0

This commit is contained in:
geos_one
2025-08-10 01:34:16 +02:00
commit c891bb7105
4398 changed files with 838833 additions and 0 deletions

View 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);
}
}

View 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_ */

View 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);
}
}

View 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_

View 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_

View 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_

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

View 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_ */

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

View 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_*/

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

View 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_ */