2025-08-10 01:34:16 +02:00

760 lines
24 KiB
C

#ifndef FHGFSINODE_H_
#define FHGFSINODE_H_
#include <app/App.h>
#include <app/config/Config.h>
#include <common/Common.h>
#include <common/threading/AtomicInt64.h>
#include <common/threading/Mutex.h>
#include <common/threading/RWLock.h>
#include <common/storage/PathInfo.h>
#include <common/storage/EntryInfo.h>
#include <common/storage/StorageDefinitions.h>
#include <common/storage/StorageErrors.h>
#include <common/storage/Metadata.h>
#include <common/toolkit/Time.h>
#include <common/toolkit/HashTk.h>
#include <common/toolkit/MetadataTk.h>
#include <common/toolkit/tree/IntMapIter.h>
#include <filesystem/FhgfsInode.h>
#include <net/filesystem/RemotingIOInfo.h>
#include <toolkit/BitStore.h>
#include <os/OsCompat.h>
#include "FhgfsOpsSuper.h"
#include <linux/fs.h>
#include <linux/vfs.h>
#define BEEGFS_INODE_ROOT_INO 2 /* traditional root inode number */
#define BEEGFS_INODE_MAXRESERVED_INO BEEGFS_INODE_ROOT_INO
#define BEEGFS_INODE_FILEHANDLES_NUM 3 /* r, rw, w (see "enum FileHandleType") */
#define BEEGFS_INODE(inodePointer) ( (FhgfsInode*)inodePointer)
#define BEEGFS_VFSINODE(fhgfsInodePointer) ( (struct inode*)fhgfsInodePointer)
#define BEEGFS_INODE_FLAG_WRITE_ERROR 1 // there was a write error with this inode
// if set switches to sync writes to faster notify
// applications
#define BEEGFS_INODE_FLAG_PAGED 2 // the inode was written to from page functions
struct StripePattern; // forward declaration
struct FhgfsInodeFileHandle;
typedef struct FhgfsInodeFileHandle FhgfsInodeFileHandle;
enum FileHandleType;
typedef enum FileHandleType FileHandleType;
enum FileBufferType;
typedef enum FileBufferType FileBufferType;
struct CacheBuffer;
typedef struct CacheBuffer CacheBuffer;
struct FhgfsInode;
typedef struct FhgfsInode FhgfsInode;
struct FhgfsIsizeHints;
typedef struct FhgfsIsizeHints FhgfsIsizeHints;
enum FileHandleType // (note: used as array index for FhgfsInode::fileHandles)
{FileHandleType_READ=0, FileHandleType_RW=1, FileHandleType_WRITE=2};
enum FileBufferType
{FileBufferType_NONE=0, FileBufferType_READ=1, FileBufferType_WRITE=2};
struct CacheBuffer
{
char* buf; // the file contents cache buffer
//size_t bufLen; // length of buf (not needed, because we have bufUsageMaxLen)
size_t bufUsageLen; // how much of the buffer is already used up
size_t bufUsageMaxLen; // how much of the buffer may be used (is min(bufLen, chunk_end) )
loff_t fileOffset; // the file offset where this buffer starts, -1 for append
enum FileBufferType bufType;
};
/*
* Used to avoid isize invalid decreases in paged mode due to outdated server side i_size
*/
struct FhgfsIsizeHints
{
bool ignoreIsize; // caller knows that the i_size needs to be ignored
uint64_t timeBeforeRemoteStat; // time before doing a remote stat call
};
// kernel inode cache helpers
extern void FhgfsInode_initOnce(FhgfsInode* this);
extern void FhgfsInode_allocInit(FhgfsInode* this);
extern void FhgfsInode_destroyUninit(FhgfsInode* this);
// public extern
extern FhgfsOpsErr FhgfsInode_referenceHandle(FhgfsInode* this, struct dentry* dentry,
int openFlags, bool allowRWHandle, LookupIntentInfoOut* lookupInfo,
FileHandleType* outHandleType, uint32_t* outVersion);
extern FhgfsOpsErr FhgfsInode_releaseHandle(FhgfsInode* this, FileHandleType handleType,
struct dentry* dentry);
extern bool FhgfsInode_hasWriteHandle(FhgfsInode* this);
extern void FhgfsInode_getRefIOInfo(FhgfsInode* this, FileHandleType handleType,
unsigned accessFlags, RemotingIOInfo* outIOInfo);
extern void FhgfsInode_fileCacheExclusiveLock(FhgfsInode* this);
extern void FhgfsInode_fileCacheExclusiveUnlock(FhgfsInode* this);
extern int FhgfsInode_fileCacheExclusiveTryLock(FhgfsInode* this);
extern void FhgfsInode_fileCacheSharedLock(FhgfsInode* this);
extern void FhgfsInode_fileCacheSharedUnlock(FhgfsInode* this);
extern struct CacheBuffer* Fhgfsinode_getFileCacheBuffer(FhgfsInode* this);
extern bool FhgfsInode_removeRangeLockPID(FhgfsInode* this, int pid);
extern void FhgfsInode_addRangeLockPID(FhgfsInode* this, int pid);
extern uint64_t FhgfsInode_generateInodeID(struct super_block* sb, const char* entryID,
int entryIDLen);
extern bool FhgfsInode_isCacheValid(FhgfsInode* this, umode_t i_mode, Config* cfg);
// private extern
extern FileHandleType __FhgfsInode_openFlagsToHandleType(int openFlags);
extern FhgfsOpsErr __FhgfsInode_referenceTrunc(FhgfsInode* this, App* app, int openFlag,
struct dentry* dentry);
extern void __FhgfsInode_initOpenIOInfo(FhgfsInode* this, FhgfsInodeFileHandle* fileHandle,
unsigned accessFlags, PathInfo* pathInfo, RemotingIOInfo* outIOInfo);
// getters & setters
static inline const EntryInfo* FhgfsInode_getEntryInfo(FhgfsInode* this);
static inline PathInfo* FhgfsInode_getPathInfo(FhgfsInode* this);
static inline DirEntryType FhgfsInode_getDirEntryType(FhgfsInode* this);
static inline int FhgfsInode_getNumRangeLockPIDs(FhgfsInode* this);
static inline bool FhgfsInode_getIsFileOpen(FhgfsInode* this);
static inline bool FhgfsInode_getIsFileOpenByMultipleReaders(FhgfsInode* this);
static inline void FhgfsInode_setLastWriteBackOrIsizeWriteTime(FhgfsInode* this);
static inline uint64_t FhgfsInode_getLastWriteBackOrIsizeWriteTime(FhgfsInode* this);
static inline int FhgfsInode_getWriteBackCounter(FhgfsInode* this);
static inline void FhgfsInode_setWritePageError(FhgfsInode* this);
static inline int FhgfsInode_getHasWritePageError(FhgfsInode* this);
// inliners
static inline void Fhgfsinode_appendLock(FhgfsInode* this);
static inline void Fhgfsinode_appendUnlock(FhgfsInode* this);
static inline void _FhgfsInode_initRootEntryInfo(FhgfsInode* this);
static inline void FhgfsInode_entryInfoReadLock(FhgfsInode* this);
static inline void FhgfsInode_entryInfoReadUnlock(FhgfsInode* this);
static inline void FhgfsInode_entryInfoWriteLock(FhgfsInode* this);
static inline void FhgfsInode_entryInfoWriteUnlock(FhgfsInode* this);
static inline bool FhgfsInode_compareEntryID(FhgfsInode* this, const char* otherEntryID);
static inline void FhgfsInode_updateEntryInfoOnRenameUnlocked(FhgfsInode* this,
const EntryInfo* newParentInfo, const char* newEntryName);
static inline void FhgfsInode_updateEntryInfoUnlocked(FhgfsInode* this,
const EntryInfo* newEntryInfo);
static inline int FhgfsInode_getStripeCount(FhgfsInode* this);
static inline StripePattern* FhgfsInode_getStripePattern(FhgfsInode* this);
static inline void FhgfsInode_clearStripePattern(FhgfsInode* this);
static inline int FhgfsInode_handleTypeToOpenFlags(FileHandleType handleType);
static inline void FhgfsInode_invalidateCache(FhgfsInode* this);
static inline void FhgfsInode_incNumDirtyPages(FhgfsInode* this);
static inline void FhgfsInode_decNumDirtyPages(FhgfsInode* this);
static inline uint64_t FhgfsInode_getNumDirtyPages(FhgfsInode* this);
static inline void FhgfsInode_setNumDirtyPages(FhgfsInode* this, uint64_t value);
static inline bool FhgfsInode_getHasDirtyPages(FhgfsInode* this);
static inline void FhgfsInode_setParentNodeID(FhgfsInode* this, NumNodeID parentNodeID);
static inline NumNodeID FhgfsInode_getParentNodeID(FhgfsInode* this);
static inline void FhgfsInode_incWriteBackCounter(FhgfsInode* this);
static inline void FhgfsInode_decWriteBackCounter(FhgfsInode* this);
static inline void FhgfsInode_initIsizeHints(FhgfsInode* this, FhgfsIsizeHints* outISizeHints);
static inline void FhgfsInode_setNoIsizeDecrease(FhgfsInode* this);
static inline void FhgfsInode_unsetNoIsizeDecrease(FhgfsInode* this);
static inline int FhgfsInode_getNoIsizeDecrease(FhgfsInode* this);
static inline void FhgfsInode_setPageWriteFlag(FhgfsInode* this);
static inline void FhgfsInode_clearWritePageError(FhgfsInode* this);
static inline int FhgfsInode_getHasPageWriteFlag(FhgfsInode* this);
/**
* Represents an open file handle on the mds.
*/
struct FhgfsInodeFileHandle
{
const char* fileHandleID; // set when this handle is used for a remote file open
unsigned refCount; // number of references to this handle
bool needsAppendLockCleanup; // true if append lock release failed
AtomicInt maxUsedTargetIndex; /* zero-based target index in stripe pattern (-1 means "none").
this is the highest target index that was read/written, so higher targets don't need
fsync() or close(). */
BitStore firstWriteDone; /* one bit per storage target in stripe pattern; bit is set when we send
data to this target. */
};
/**
* Represents a linux file or directory inode with fhgfs extensions.
*/
struct FhgfsInode
{
struct inode vfs_inode; // linux vfs inode
EntryInfo entryInfo;
RWLock entryInfoLock;
NumNodeID parentNodeID; /* nodeID of the parent directory, should be moved to EntryInfo
* in the future. So far only used for NFS exports and only set for
* DirInodes in FhgfsOpsExport to reconnect dentries. */
PathInfo pathInfo;
Time dataCacheTime; /* last time we updated file contents (monotonic clock) or dir attribs;
protected by i_lock */
Mutex fileHandlesMutex;
FhgfsInodeFileHandle fileHandles[BEEGFS_INODE_FILEHANDLES_NUM]; // use FileHandleType as index
struct StripePattern* pattern; // initialized when file is opened; multiple readers allowed
Mutex rangeLockPIDsMutex;
AtomicInt numRangeLockPIDs; // modified only with mutex held, but readable without it
IntMap rangeLockPIDs; /* all PIDs (TGIDs) that used range locking and have this file open
(values are dummies) */
Mutex appendMutex; // for atomic writes with append-flag
AtomicInt appendFDsOpen;
struct CacheBuffer fileCacheBuffer; // current buffer cache entry (protected by fileCacheLock)
RWLock fileCacheLock; /* for sync'ed access to cacheEntry and page cache (required to ensure
order, e.g. when a new r/w-call comes in while we're about to disable caching) */
AtomicInt64 numPageCacheDirtyPages; // number of written (=> dirty) bytes since last flush
/* writeBackCounter and lastWriteBackEndOrIsizeWriteTime are hints for refreshInode to
* ingore the servers inode size */
AtomicInt writeBackCounter; // number of writeBack threads sending pages to the server
AtomicInt64 lastWriteBackEndOrIsizeWriteTime;
AtomicInt noRemoteIsizeDecrease; // if set remote attributes won't decrease the i_size
int flags; // protected by inode->i_lock
uint32_t fileVersion; /* protected by entryInfoLock (which would subsume a more granular lock).
used by native cache mode to track file contents change. */
uint32_t metaVersion; /* protected by entryInfoLock (which would subsume a more granular lock).
used as a way to invalidate the cache due to internal metadata changes
(like stripe pattern change) via lookup::revalidateIntent. */
atomic_t modified; // tracks whether the inode data has been written since its last full flush
atomic_t coRWInProg; //Coherent read/write in progress.
};
int FhgfsInode_getNumRangeLockPIDs(FhgfsInode* this)
{
return AtomicInt_read(&this->numRangeLockPIDs);
}
/**
* Test whether a given file inode is currently open.
*
* @return true if the file is currently open.
*/
bool FhgfsInode_getIsFileOpen(FhgfsInode* this)
{
bool retVal = false;
int i;
Mutex_lock(&this->fileHandlesMutex); // L O C K
for(i=0; i < BEEGFS_INODE_FILEHANDLES_NUM; i++)
{
if(this->fileHandles[i].refCount)
{ // we found an open file handle
retVal = true;
break;
}
}
Mutex_unlock(&this->fileHandlesMutex); // U N L O C K
return retVal;
}
/**
* Test whether a given file inode is currently open for reading by multiple readers.
*
* Note: This is specifically intended to give a hint for read-ahead disabling (because the current
* buffered mode logic could do read-ahead trashing with multiple concurrent readers on the same
* file). Since this is just a hint, we don't care about mutex locking here.
*
* @return true if the file is currently open by more than one reader (including read+write).
*/
bool FhgfsInode_getIsFileOpenByMultipleReaders(FhgfsInode* this)
{
unsigned numReaders = this->fileHandles[FileHandleType_READ].refCount +
this->fileHandles[FileHandleType_RW].refCount;
return (numReaders > 1);
}
/**
* Acquire the internal append mutex
*
* Note: remember to call _appendUnlock()!!
*/
void Fhgfsinode_appendLock(FhgfsInode* this)
{
Mutex_lock(&this->appendMutex);
}
void Fhgfsinode_appendUnlock(FhgfsInode* this)
{
Mutex_unlock(&this->appendMutex);
}
/**
* Get the EntryInfo of this inode
*
* Note: you will probably need to call _entryInfoReadLock() before using this (and don't forget to
* unlock afterwards).
*/
const EntryInfo* FhgfsInode_getEntryInfo(FhgfsInode* this)
{
return &this->entryInfo;
}
PathInfo* FhgfsInode_getPathInfo(FhgfsInode* this)
{
return &this->pathInfo;
}
/**
* Initialize the root inode. We can not do that at mount time as we allow mounts without a
* connection to the management server.
*/
void _FhgfsInode_initRootEntryInfo(FhgfsInode* this)
{
struct inode* vfs_inode = &this->vfs_inode;
struct super_block* sb = vfs_inode->i_sb;
bool hasRootInfo = FhgfsOps_getHasRootEntryInfo(sb);
// unlikely as it will happen only once
if (unlikely(!hasRootInfo) && (vfs_inode->i_ino == BEEGFS_INODE_ROOT_INO) )
{
/* root inode, EntryInfo not yet set during mount, as the meta server might be unknown.
* Set it now. */
App* app = FhgfsOps_getApp(sb);
RWLock_writeLock(&this->entryInfoLock); // Write-LOCK
hasRootInfo = FhgfsOps_getHasRootEntryInfo(sb); // verify under a lock
if (unlikely(hasRootInfo) )
{
// another thread updated it already, nothing to do
}
else
{
bool isSuccess;
EntryInfo_uninit(&this->entryInfo);
isSuccess = MetadataTk_getRootEntryInfoCopy(app, &this->entryInfo);
if (isSuccess)
FhgfsOps_setHasRootEntryInfo(sb, true);
}
RWLock_writeUnlock(&this->entryInfoLock); // Write-UNLOCK
}
}
/*
* Get an EntryInfo read-lock.
*
* Note: Must be taken on reading parentEntryID. Should not be taken if only entryID is read,
* as entryID is never updated.
* See FhgfsInode_updateEntryInfoUnlocked() and FhgfsInode_updateEntryInfoOnRenameUnlocked()
* Note: If the root inode is not initialized, it will be initialized by this method under
* a write-lock.
*/
void FhgfsInode_entryInfoReadLock(FhgfsInode* this)
{
_FhgfsInode_initRootEntryInfo(this); // NOTE: might temporarily writelock entryInfoLock
RWLock_readLock(&this->entryInfoLock); // Read-LOCK
}
void FhgfsInode_entryInfoReadUnlock(FhgfsInode* this)
{
RWLock_readUnlock(&this->entryInfoLock);
}
/**
* Note: Also might initialize the root-inode.
*/
void FhgfsInode_entryInfoWriteLock(FhgfsInode* this)
{
_FhgfsInode_initRootEntryInfo(this); // NOTE: might temporarily writelock entryInfoLock
RWLock_writeLock(&this->entryInfoLock); // Write-LOCK
}
void FhgfsInode_entryInfoWriteUnlock(FhgfsInode* this)
{
RWLock_writeUnlock(&this->entryInfoLock);
}
/**
* Compares given entryID with the entryID of this inode.
*
* Note: This is intended to be called from methods like __FhgfsOps_compareInodeID, which is
* called e.g. by ilookup5 with the inode_hash_lock held, so this method may not sleep
*
* @return true if given ID matches, false otherwise
*/
bool FhgfsInode_compareEntryID(FhgfsInode* this, const char* otherEntryID)
{
if(this->vfs_inode.i_ino == BEEGFS_INODE_ROOT_INO)
{ // root node => owner info is not stored in the inode
return !strcmp(otherEntryID, META_ROOTDIR_ID_STR);
}
else
{
const char* entryID;
entryID = this->entryInfo.entryID;
if (unlikely(!entryID) )
{
printk_fhgfs(KERN_WARNING, "Bug: entryID is a NULL pointer!\n");
return false;
}
return !strcmp(otherEntryID, entryID);
}
}
/**
* Return the DirEntryType of the given inode
*/
DirEntryType FhgfsInode_getDirEntryType(FhgfsInode* this)
{
return this->entryInfo.entryType;
}
/**
* Update EntryInfo on rename
*
* Note: newParentInfo and newName are not owned by this object!
*
* Note: FhgfsInode->entryInfoLock already needs to be write-locked by the caller.
*/
void FhgfsInode_updateEntryInfoOnRenameUnlocked(FhgfsInode* this, const EntryInfo* newParentInfo,
const char* newName)
{
EntryInfo_updateParentEntryID(&this->entryInfo, newParentInfo->entryID);
EntryInfo_updateFileName(&this->entryInfo, newName);
if (!DirEntryType_ISDIR(this->entryInfo.entryType) )
{ // only update the ownerNodeID if it is not a directory
this->entryInfo.owner = newParentInfo->owner;
if (this->entryInfo.owner.isGroup)
this->entryInfo.featureFlags |= ENTRYINFO_FEATURE_BUDDYMIRRORED;
else
this->entryInfo.featureFlags &= ~ENTRYINFO_FEATURE_BUDDYMIRRORED;
}
}
/**
* Update an existing EntryInfo with a new one
*
* Note: this->entryInfoLock already needs to be write-locked by the caller.
*/
void FhgfsInode_updateEntryInfoUnlocked(FhgfsInode* this, const EntryInfo* newEntryInfo)
{
EntryInfo_update(&this->entryInfo, newEntryInfo);
}
int FhgfsInode_getStripeCount(FhgfsInode* this)
{
return UInt16Vec_length(this->pattern->getStripeTargetIDs(this->pattern));
}
StripePattern* FhgfsInode_getStripePattern(FhgfsInode* this)
{
return this->pattern;
}
/**
* Clear the stripe pattern of an inode.
* Should only be called if inode->i_count is 1.
* After stripePattern is set to NULL, next operation should only be open.
* Nothing should access the pattern ptr until open loads a new pattern.
* Note: This is called with a spin_lock held.
*/
void FhgfsInode_clearStripePattern(FhgfsInode* this)
{
this->pattern=NULL;
}
/**
* Converts FileHandleType to OPENFILE_ACCESS_... fhgfs flags.
*
* Note: This is especially required when a handle was referenced with allowRWHandle, because then
* you don't know the OPENFILE_ACCESS_... flags.
*/
int FhgfsInode_handleTypeToOpenFlags(FileHandleType handleType)
{
switch(handleType)
{
case FileHandleType_WRITE:
{
return OPENFILE_ACCESS_WRITE;
} break;
case FileHandleType_RW:
{
return OPENFILE_ACCESS_READWRITE;
} break;
default:
{
return OPENFILE_ACCESS_READ;
} break;
}
}
/**
* Invalidate the cache of an inode
*/
void FhgfsInode_invalidateCache(FhgfsInode* this)
{
Time_setZero(&this->dataCacheTime);
}
void FhgfsInode_incNumDirtyPages(FhgfsInode* this)
{
AtomicInt64_inc(&this->numPageCacheDirtyPages);
}
void FhgfsInode_decNumDirtyPages(FhgfsInode* this)
{
AtomicInt64_dec(&this->numPageCacheDirtyPages);
}
uint64_t FhgfsInode_getNumDirtyPages(FhgfsInode* this)
{
return AtomicInt64_read(&this->numPageCacheDirtyPages);
}
void FhgfsInode_setNumDirtyPages(FhgfsInode* this, uint64_t value)
{
return AtomicInt64_set(&this->numPageCacheDirtyPages, value);
}
/**
* Test whether a given file inode is currently write-opened.
*
* @return true if the file is currently open.
*/
bool FhgfsInode_getHasDirtyPages(FhgfsInode* this)
{
return !!FhgfsInode_getNumDirtyPages(this);
}
void FhgfsInode_setParentNodeID(FhgfsInode* this, NumNodeID parentNodeID)
{
this->parentNodeID = parentNodeID;
}
NumNodeID FhgfsInode_getParentNodeID(FhgfsInode* this)
{
return this->parentNodeID;
}
void FhgfsInode_incWriteBackCounter(FhgfsInode* this)
{
AtomicInt_inc(&this->writeBackCounter);
}
void FhgfsInode_decWriteBackCounter(FhgfsInode* this)
{
AtomicInt_dec(&this->writeBackCounter);
}
/**
* @param this: Will be NULL on lookup calls
*/
int FhgfsInode_getWriteBackCounter(FhgfsInode* this)
{
if (!this)
return 0;
return AtomicInt_read(&this->writeBackCounter);
}
void FhgfsInode_setNoIsizeDecrease(FhgfsInode* this)
{
AtomicInt_set(&this->noRemoteIsizeDecrease, 1);
}
/**
* Unset noRemoteIsizeDecrease if appropriate
*/
void FhgfsInode_unsetNoIsizeDecrease(FhgfsInode* this)
{
struct address_space *mapping = this->vfs_inode.i_mapping;
if (!FhgfsInode_getNoIsizeDecrease(this) )
return; // not set, so no need to do anything
if (FhgfsInode_getWriteBackCounter(this) )
return; // still write-back threads left
// check if there are dirty, writeback or mmapped-write pages
if ( (FhgfsInode_getHasDirtyPages(this) ) ||
(mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) ) ||
(mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK) ) ||
(mapping_writably_mapped(mapping) ) )
{ // server did not receive all pages yet
return;
}
AtomicInt_set(&this->noRemoteIsizeDecrease, 0);
}
int FhgfsInode_getNoIsizeDecrease(FhgfsInode* this)
{
return AtomicInt_read(&this->noRemoteIsizeDecrease);
}
void FhgfsInode_setLastWriteBackOrIsizeWriteTime(FhgfsInode* this)
{
AtomicInt64_set(&this->lastWriteBackEndOrIsizeWriteTime, get_jiffies_64() );
}
uint64_t FhgfsInode_getLastWriteBackOrIsizeWriteTime(FhgfsInode* this)
{
return AtomicInt64_read(&this->lastWriteBackEndOrIsizeWriteTime);
}
/**
* Initialize iSizeHints
*
* @param this might be a NULL pointer
*
* Note: As optimization it will only read atomic values and jiffies if the inode refers to
* a regular file. As this is not known before doing lookup-stat, this might be a NULL
* pointer.
*/
void FhgfsInode_initIsizeHints(FhgfsInode* this, FhgfsIsizeHints* outISizeHints)
{
struct address_space *mapping;
if (this && !S_ISREG(this->vfs_inode.i_mode) )
return;
outISizeHints->timeBeforeRemoteStat = get_jiffies_64();
outISizeHints->ignoreIsize = false;
/* Check if there is a hint that we should ignore the remote isize */
if (this)
{
mapping = this->vfs_inode.i_mapping;
if ((FhgfsInode_getWriteBackCounter(this) || FhgfsInode_getHasDirtyPages(this) ||
(mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) ) ||
(mapping_tagged(mapping, PAGECACHE_TAG_WRITEBACK) ) ||
(mapping_writably_mapped(mapping) ) ) )
{
outISizeHints->ignoreIsize = true;
}
}
}
/**
* Set the page-write flag (there was a page write to this inode
*/
void FhgfsInode_setPageWriteFlag(FhgfsInode* this)
{
/* Note: This is called for each an every page (in paged mode, mmap, splice, ...)
* and therefore a very hot path and therefore needs to be optimized.
* Mostly the read will succeed if there are paged writes and the read will only fail
* for the first inode. And as reading values is cheaper than writing values, we first
* check if the flag is already set */
if (!FhgfsInode_getHasPageWriteFlag(this) )
this->flags |= BEEGFS_INODE_FLAG_PAGED;
}
/**
* Get the page-write flag
*
* Note: In order to avoid inode locking overhead, this is usually called without hold i_lock
* It is not perfectly reliable then, but at some point CPUs get synchronized and that
* is sufficient for us.
*/
int FhgfsInode_getHasPageWriteFlag(FhgfsInode* this)
{
return this->flags & BEEGFS_INODE_FLAG_PAGED;
}
/**
* There was an error writing pages of this inode
*/
void FhgfsInode_setWritePageError(FhgfsInode* this)
{
this->flags |= BEEGFS_INODE_FLAG_WRITE_ERROR;
}
/**
* Clear the write error
*/
void FhgfsInode_clearWritePageError(FhgfsInode* this)
{
this->flags &= ~(BEEGFS_INODE_FLAG_WRITE_ERROR);
}
int FhgfsInode_getHasWritePageError(FhgfsInode* this)
{
return this->flags & BEEGFS_INODE_FLAG_WRITE_ERROR;
}
#endif /* FHGFSINODE_H_ */