beegfs/client_module/source/toolkit/FhgfsChunkPageVec.h
2025-08-10 01:34:16 +02:00

430 lines
12 KiB
C

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