Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed flushing dirty data and compressed the cache size #1467

Merged
merged 1 commit into from Nov 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions configure.ac
Expand Up @@ -32,6 +32,7 @@ AC_PROG_CC
AC_CHECK_HEADERS([sys/xattr.h])
AC_CHECK_HEADERS([attr/xattr.h])
AC_CHECK_HEADERS([sys/extattr.h])
AC_CHECK_FUNCS([fallocate])

CXXFLAGS="$CXXFLAGS -Wall -fno-exceptions -D_FILE_OFFSET_BITS=64 -D_FORTIFY_SOURCE=2"

Expand Down
65 changes: 65 additions & 0 deletions src/fdcache_entity.cpp
Expand Up @@ -1601,6 +1601,71 @@ int FdEntity::UploadPendingMeta()
return result;
}

// [NOTE]
// For systems where the fallocate function cannot be detected, use a dummy function.
// ex. OSX
//
#ifndef HAVE_FALLOCATE
// We need the symbols defined in fallocate, so we define them here.
// The definitions are copied from linux/falloc.h, but if HAVE_FALLOCATE is undefined,
// these values can be anything.
//
#define FALLOC_FL_PUNCH_HOLE 0x02 /* de-allocates range */
#define FALLOC_FL_KEEP_SIZE 0x01

static int fallocate(int /*fd*/, int /*mode*/, off_t /*offset*/, off_t /*len*/)
{
errno = ENOSYS; // This is a bad idea, but the caller can handle it simply.
return -1;
}
#endif // HAVE_FALLOCATE

// [NOTE]
// This method punches an area(on cache file) that has no data at the time it is called.
// This is called to prevent the cache file from growing.
// However, this method uses the non-portable(Linux specific) system call fallocate().
// Also, depending on the file system, FALLOC_FL_PUNCH_HOLE mode may not work and HOLE
// will not open.(Filesystems for which this method works are ext4, btrfs, xfs, etc.)
//
bool FdEntity::PunchHole(off_t start, size_t size)
{
S3FS_PRN_DBG("[path=%s][fd=%d][offset=%lld][size=%zu]", path.c_str(), fd, static_cast<long long int>(start), size);

if(-1 == fd){
return false;
}
AutoLock auto_lock(&fdent_data_lock);

// get page list that have no data
fdpage_list_t nodata_pages;
if(!pagelist.GetNoDataPageLists(nodata_pages)){
S3FS_PRN_ERR("filed to get page list that have no data.");
return false;
}
if(nodata_pages.empty()){
S3FS_PRN_DBG("there is no page list that have no data, so nothing to do.");
return true;
}

// try to punch hole to file
for(fdpage_list_t::const_iterator iter = nodata_pages.begin(); iter != nodata_pages.end(); ++iter){
if(0 != fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, iter->offset, iter->bytes)){
if(ENOSYS == errno || EOPNOTSUPP == errno){
S3FS_PRN_ERR("filed to fallocate for punching hole to file with errno(%d), it maybe the fallocate function is not implemented in this kernel, or the file system does not support FALLOC_FL_PUNCH_HOLE.", errno);
}else{
S3FS_PRN_ERR("filed to fallocate for punching hole to file with errno(%d)", errno);
}
return false;
}
if(!pagelist.SetPageLoadedStatus(iter->offset, iter->bytes, PageList::PAGE_NOT_LOAD_MODIFIED)){
S3FS_PRN_ERR("succeed to punch HOLEs in the cache file, but failed to update the cache stat.");
return false;
}
S3FS_PRN_DBG("made a hole at [%lld - %lld bytes](into a boundary) of the cache file.", static_cast<long long int>(iter->offset), static_cast<long long int>(iter->bytes));
}
return true;
}

/*
* Local variables:
* tab-width: 4
Expand Down
1 change: 1 addition & 0 deletions src/fdcache_entity.h
Expand Up @@ -117,6 +117,7 @@ class FdEntity
ssize_t Write(const char* bytes, off_t start, size_t size);

bool ReserveDiskSpace(off_t size);
bool PunchHole(off_t start = 0, size_t size = 0);
};

typedef std::map<std::string, class FdEntity*> fdent_map_t; // key=path, value=FdEntity*
Expand Down
39 changes: 39 additions & 0 deletions src/fdcache_page.cpp
Expand Up @@ -689,6 +689,45 @@ bool PageList::GetPageListsForMultipartUpload(fdpage_list_t& dlpages, fdpage_lis
return true;
}

bool PageList::GetNoDataPageLists(fdpage_list_t& nodata_pages, off_t start, size_t size)
{
// compress before this processing
if(!Compress()){
return false;
}

// extract areas without data
fdpage_list_t tmp_pagelist;
off_t stop_pos = (0L == size ? -1 : (start + size));
for(fdpage_list_t::const_iterator iter = pages.begin(); iter != pages.end(); ++iter){
if((iter->offset + iter->bytes) < start){
continue;
}
if(-1 != stop_pos && stop_pos <= iter->offset){
break;
}
if(iter->modified){
continue;
}

fdpage tmppage;
tmppage.offset = std::max(iter->offset, start);
tmppage.bytes = (-1 != stop_pos ? iter->bytes : std::min(iter->bytes, (stop_pos - tmppage.offset)));
tmppage.loaded = iter->loaded;
tmppage.modified = iter->modified;

tmp_pagelist.push_back(tmppage);
}

if(tmp_pagelist.empty()){
nodata_pages.clear();
}else{
// compress
nodata_pages = compress_fdpage_list(tmp_pagelist);
}
return true;
}

off_t PageList::BytesModified() const
{
off_t total = 0;
Expand Down
1 change: 1 addition & 0 deletions src/fdcache_page.h
Expand Up @@ -109,6 +109,7 @@ class PageList
off_t GetTotalUnloadedPageSize(off_t start = 0, off_t size = 0) const; // size=0 is checking to end of list
int GetUnloadedPages(fdpage_list_t& unloaded_list, off_t start = 0, off_t size = 0) const; // size=0 is checking to end of list
bool GetPageListsForMultipartUpload(fdpage_list_t& dlpages, fdpage_list_t& mixuppages, off_t max_partsize);
bool GetNoDataPageLists(fdpage_list_t& nodata_pages, off_t start = 0, size_t size = 0);

off_t BytesModified() const;
bool IsModified() const;
Expand Down
11 changes: 8 additions & 3 deletions src/s3fs.cpp
Expand Up @@ -2285,10 +2285,15 @@ static int s3fs_write(const char* _path, const char* buf, size_t size, off_t off
}

if(max_dirty_data != -1 && ent->BytesModified() >= max_dirty_data){
if(0 != (res = ent->RowFlush(path, true))){
S3FS_PRN_ERR("could not upload file(%s): result=%zd", path, res);
int flushres;
if(0 != (flushres = ent->RowFlush(path, true))){
S3FS_PRN_ERR("could not upload file(%s): result=%d", path, flushres);
StatCache::getStatCacheData()->DelStat(path);
return res;
return -EIO;
}
// Punch a hole in the file to recover disk space.
if(!ent->PunchHole()){
S3FS_PRN_WARN("could not punching HOLEs to a cache file, but continue.");
}
}

Expand Down