From 2a65d8d804d1475167107aed698b61141e89c2c7 Mon Sep 17 00:00:00 2001 From: Lanzheng Liu Date: Tue, 21 Nov 2023 19:08:07 +0800 Subject: [PATCH] new cache implementation Co-authored-by: zhuangbowei.zbw Signed-off-by: Lanzheng Liu --- src/image_file.cpp | 65 +- src/image_file.h | 2 +- src/image_service.cpp | 14 +- src/overlaybd/cache/CMakeLists.txt | 6 +- src/overlaybd/cache/cache.cpp | 126 ++-- src/overlaybd/cache/cache.h | 83 +-- src/overlaybd/cache/cached_fs.cpp | 446 ++++++++++++++ src/overlaybd/cache/forwardcfs.h | 64 ++ src/overlaybd/cache/frontend/CMakeLists.txt | 6 - src/overlaybd/cache/frontend/cached_file.cpp | 362 ------------ src/overlaybd/cache/frontend/cached_file.h | 127 ---- src/overlaybd/cache/frontend/cached_fs.cpp | 146 ----- .../cache/full_file_cache/CMakeLists.txt | 3 - .../cache/full_file_cache/cache_pool.cpp | 84 +-- .../cache/full_file_cache/cache_pool.h | 18 +- .../cache/full_file_cache/cache_store.cpp | 33 +- .../cache/full_file_cache/cache_store.h | 23 +- .../cache/full_file_cache/test/cache_test.cpp | 388 ------------ src/overlaybd/cache/gzip_cache/cached_fs.cpp | 7 +- .../cache/ocf_cache/test/ocf_perf_test.cpp | 2 +- src/overlaybd/cache/policy/lru.h | 8 +- src/overlaybd/cache/pool_store.h | 289 ++++++--- src/overlaybd/cache/store.cpp | 427 ++++++++++++++ .../{full_file_cache => }/test/CMakeLists.txt | 3 +- src/overlaybd/cache/test/cache_test.cpp | 558 ++++++++++++++++++ .../test/random_generator.h | 57 +- src/overlaybd/gzindex/test/test.cpp | 27 +- src/overlaybd/registryfs/registryfs.cpp | 5 +- src/overlaybd/registryfs/registryfs_v2.cpp | 6 +- src/overlaybd/tar/tar_file.cpp | 2 +- src/overlaybd/zfile/test/test.cpp | 1 - 31 files changed, 2003 insertions(+), 1385 deletions(-) create mode 100644 src/overlaybd/cache/cached_fs.cpp create mode 100644 src/overlaybd/cache/forwardcfs.h delete mode 100644 src/overlaybd/cache/frontend/CMakeLists.txt delete mode 100644 src/overlaybd/cache/frontend/cached_file.cpp delete mode 100644 src/overlaybd/cache/frontend/cached_file.h delete mode 100644 src/overlaybd/cache/frontend/cached_fs.cpp delete mode 100644 src/overlaybd/cache/full_file_cache/test/cache_test.cpp create mode 100644 src/overlaybd/cache/store.cpp rename src/overlaybd/cache/{full_file_cache => }/test/CMakeLists.txt (99%) create mode 100644 src/overlaybd/cache/test/cache_test.cpp rename src/overlaybd/cache/{full_file_cache => }/test/random_generator.h (52%) diff --git a/src/image_file.cpp b/src/image_file.cpp index 7fa34e29..e22da6d2 100644 --- a/src/image_file.cpp +++ b/src/image_file.cpp @@ -128,57 +128,59 @@ IFile *ImageFile::__open_ro_target_remote(const std::string &dir, const std::str return remote_file; } +void get_error_msg(int eno, std::string &err_msg) { + if (eno == EPERM || eno == EACCES) { + err_msg = "Authentication failed"; + } else if (eno == ENOTCONN) { + err_msg = "Connection failed"; + } else if (eno == ETIMEDOUT) { + err_msg = "Get meta timedout"; + } else if (eno == ENOENT) { + err_msg = "No such file or directory"; + } else if (eno == EBUSY) { + err_msg = "Too many requests"; + } else if (eno == EIO) { + err_msg = "Unexpected response"; + } else { + err_msg = std::string(strerror(eno)); + } +} + IFile *ImageFile::__open_ro_remote(const std::string &dir, const std::string &digest, const uint64_t size, int layer_index) { - std::string url; - if (conf.repoBlobUrl() == "") { set_failed("empty repoBlobUrl"); LOG_ERROR_RETURN(0, nullptr, "empty repoBlobUrl for remote layer"); } - url = conf.repoBlobUrl(); - - if (url[url.length() - 1] != '/') - url += "/"; - url += digest; + estring url = estring().appends("/", conf.repoBlobUrl(), + (conf.repoBlobUrl().back() != '/') ? "/" : "", + digest); LOG_INFO("open file from remotefs: `, size: `", url, size); IFile *remote_file = image_service.global_fs.remote_fs->open(url.c_str(), O_RDONLY); if (!remote_file) { - std::string err_msg = "failed to open remote file " + url + ": "; - if (errno == EPERM || errno == EACCES) { - err_msg += "Authentication failed"; - } else if (errno == ENOTCONN) { - err_msg += "Connection failed"; - } else if (errno == ETIMEDOUT) { - err_msg += "Get meta timedout"; - } else if (errno == ENOENT) { - err_msg += "No such file or directory"; - } else if (errno == EBUSY) { - err_msg += "Too many requests"; - } else if (errno == EIO) { - err_msg += "Unexpected response"; - } else { - err_msg += std::string(strerror(errno)); - } - set_failed(err_msg); - LOG_ERRNO_RETURN(0, nullptr, err_msg); + std::string err_msg; + get_error_msg(errno, err_msg); + set_failed("failed to open remote file ", url, ": ", err_msg); + LOG_ERRNO_RETURN(0, nullptr, "failed to open remote file `: `", url, err_msg); } remote_file->ioctl(SET_SIZE, size); remote_file->ioctl(SET_LOCAL_DIR, dir); IFile *tar_file = new_tar_file_adaptor(remote_file); if (!tar_file) { - set_failed("failed to open remote file as tar file " + url); + std::string err_msg; + get_error_msg(errno, err_msg); + set_failed("failed to open remote file as tar file ", url, ": ", err_msg); delete remote_file; - LOG_ERROR_RETURN(0, nullptr, "failed to open remote file as tar file `", url); + LOG_ERRNO_RETURN(0, nullptr, "failed to open remote file as tar file `: `", url, err_msg); } ISwitchFile *switch_file = new_switch_file(tar_file, false, url.c_str()); if (!switch_file) { - set_failed("failed to open switch file " + url); + set_failed("failed to open switch file ", url); delete tar_file; - LOG_ERROR_RETURN(0, nullptr, "failed to open switch file `", url); + LOG_ERRNO_RETURN(0, nullptr, "failed to open switch file `", url); } if (conf.HasMember("download") && conf.download().enable() == 1) { @@ -517,10 +519,11 @@ void ImageFile::set_auth_failed() { } } -void ImageFile::set_failed(std::string reason) { +template +void ImageFile::set_failed(const Ts&...xs) { if (m_status == 0) // only set exit in image boot phase { m_status = -1; - m_exception = reason; + m_exception = estring().appends(xs...); } } diff --git a/src/image_file.h b/src/image_file.h index df747fba..344487e5 100644 --- a/src/image_file.h +++ b/src/image_file.h @@ -116,7 +116,7 @@ class ImageFile : public photon::fs::ForwardFile { ImageService &image_service; int init_image_file(); - void set_failed(std::string reason); + template void set_failed(const Ts&...xs); LSMT::IFileRO *open_lowers(std::vector &, bool &); LSMT::IFileRW *open_upper(ImageConfigNS::UpperConfig &); IFile *__open_ro_file(const std::string &); diff --git a/src/image_service.cpp b/src/image_service.cpp index d1c6f4a8..09544ca9 100644 --- a/src/image_service.cpp +++ b/src/image_service.cpp @@ -274,8 +274,16 @@ void ImageService::set_result_file(std::string &filename, std::string &data) { data.c_str()); } -static std::string cache_fn_trans_sha256(std::string_view path) { - return std::string(photon::fs::Path(path).basename()); +size_t cache_fn_trans_sha256(void *, std::string_view origin, char *name, size_t namesize) { + auto target = photon::fs::Path(origin).basename(); + if (target.size()+2 > namesize) { + // return 0, no name trans, use origin name for cache + LOG_ERROR_RETURN(ERANGE, 0, "name out of range"); + } + name[0] = '/'; + strncpy(name + 1, target.data(), target.size()); + name[target.size()+1] = 0; + return target.size()+1; } bool check_accelerate_url(std::string_view a_url) { @@ -369,7 +377,7 @@ int ImageService::init() { // file cache will delete its src_fs automatically when destructed global_fs.cached_fs = FileSystem::new_full_file_cached_fs( global_fs.srcfs, registry_cache_fs, refill_size, cache_size_GB, 10000000, - (uint64_t)1048576 * 4096, global_fs.io_alloc, cache_fn_trans_sha256); + (uint64_t)1048576 * 1024, global_fs.io_alloc, 0, {nullptr, &cache_fn_trans_sha256}); } else if (cache_type == "ocf") { auto namespace_dir = std::string(cache_dir + "/namespace"); diff --git a/src/overlaybd/cache/CMakeLists.txt b/src/overlaybd/cache/CMakeLists.txt index a193908d..760567be 100644 --- a/src/overlaybd/cache/CMakeLists.txt +++ b/src/overlaybd/cache/CMakeLists.txt @@ -1,4 +1,3 @@ -add_subdirectory(frontend) add_subdirectory(full_file_cache) add_subdirectory(ocf_cache) add_subdirectory(download_cache) @@ -9,7 +8,6 @@ file(GLOB SRC_CACHE "*.cpp") add_library(cache_lib STATIC ${SRC_CACHE}) target_link_libraries(cache_lib photon_static - cache_frontend_lib full_file_cache_lib ocf_cache_lib download_cache_lib @@ -18,3 +16,7 @@ target_link_libraries(cache_lib target_include_directories(cache_lib PUBLIC ${PHOTON_INCLUDE_DIR} ) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/src/overlaybd/cache/cache.cpp b/src/overlaybd/cache/cache.cpp index b2b59cb0..46c2ba09 100644 --- a/src/overlaybd/cache/cache.cpp +++ b/src/overlaybd/cache/cache.cpp @@ -15,95 +15,99 @@ */ #include "cache.h" #include +#include #include #include +#include +#include -#include "frontend/cached_file.h" -#include "pool_store.h" #include "full_file_cache/cache_pool.h" namespace FileSystem { using namespace photon::fs; - -ICacheStore::try_preadv_result ICacheStore::try_preadv(const struct iovec *iov, int iovcnt, - off_t offset) { - try_preadv_result rst; - iovector_view view((iovec *)iov, iovcnt); - rst.iov_sum = view.sum(); - auto q = queryRefillRange(offset, rst.iov_sum); - if (q.second == 0) { // no need to refill - rst.refill_size = 0; - rst.size = this->preadv(iov, iovcnt, offset); - } else { - rst.refill_size = q.second; - rst.refill_offset = q.first; - } - return rst; -} -ICacheStore::try_preadv_result ICacheStore::try_preadv_mutable(struct iovec *iov, int iovcnt, - off_t offset) { - return try_preadv(iov, iovcnt, offset); -} -ssize_t ICacheStore::preadv(const struct iovec *iov, int iovcnt, off_t offset) { - SmartCloneIOV<32> ciov(iov, iovcnt); - return preadv_mutable(ciov.iov, iovcnt, offset); -} -ssize_t ICacheStore::preadv_mutable(struct iovec *iov, int iovcnt, off_t offset) { - return preadv(iov, iovcnt, offset); -} -ssize_t ICacheStore::pwritev(const struct iovec *iov, int iovcnt, off_t offset) { - SmartCloneIOV<32> ciov(iov, iovcnt); - return pwritev_mutable(ciov.iov, iovcnt, offset); -} -ssize_t ICacheStore::pwritev_mutable(struct iovec *iov, int iovcnt, off_t offset) { - return pwritev(iov, iovcnt, offset); -} - ICachedFileSystem *new_full_file_cached_fs(IFileSystem *srcFs, IFileSystem *mediaFs, uint64_t refillUnit, uint64_t capacityInGB, uint64_t periodInUs, uint64_t diskAvailInBytes, - IOAlloc *allocator, Fn_trans_func name_trans) { - if (refillUnit % 4096 != 0) { - LOG_ERROR_RETURN(EINVAL, nullptr, "refill Unit need to be aligned to 4KB") + IOAlloc *allocator, int quotaDirLevel, + CacheFnTransFunc fn_trans_func) { + if (refillUnit % 4096 != 0 || !is_power_of_2(refillUnit)) { + LOG_ERROR_RETURN(EINVAL, nullptr, "refill Unit need to be aligned to 4KB and power of 2") } if (!allocator) { allocator = new IOAlloc; } Cache::FileCachePool *pool = nullptr; pool = - new ::Cache::FileCachePool(mediaFs, capacityInGB, periodInUs, diskAvailInBytes, refillUnit, name_trans); + new ::Cache::FileCachePool(mediaFs, capacityInGB, periodInUs, diskAvailInBytes, refillUnit); pool->Init(); - return new_cached_fs(srcFs, pool, 4096, refillUnit, allocator); + return new_cached_fs(srcFs, pool, 4096, allocator, fn_trans_func); +} + +using OC = ObjectCache; +ICachePool::ICachePool(uint32_t pool_size, uint32_t max_refilling, uint32_t refilling_threshold) + : m_stores(new OC(10UL * 1000 * 1000)), m_max_refilling(max_refilling), + m_refilling_threshold(refilling_threshold) { + if (pool_size != 0) { + m_thread_pool = photon::new_thread_pool(pool_size, 128 * 1024UL); + m_vcpu = photon::get_vcpu(); + }; +} + +#define cast(x) static_cast(x) +ICachePool::~ICachePool() { + stores_clear(); + delete cast(m_stores); +} + +void ICachePool::stores_clear() { + if (m_thread_pool) { + auto pool = static_cast(m_thread_pool); + m_thread_pool = nullptr; + photon::delete_thread_pool(pool); + } + cast(m_stores)->clear(); } ICacheStore *ICachePool::open(std::string_view filename, int flags, mode_t mode) { - ICacheStore *cache_store = nullptr; - auto it = m_stores.find(filename); - if (it != m_stores.end()) - cache_store = it->second; - if (cache_store == nullptr) { - cache_store = this->do_open(filename, flags, mode); + char store_name[4096]; + std::string x(filename); + auto len = this->fn_trans_func(filename, store_name, sizeof(store_name)); + std::string_view store_sv = len ? std::string_view(store_name, len) : filename; + auto ctor = [&]() -> ICacheStore * { + auto cache_store = this->do_open(store_sv, flags, mode); if (nullptr == cache_store) { LOG_ERRNO_RETURN(0, nullptr, "fileCachePool_ open file failed, name : `", filename.data()); } - m_stores.emplace(filename, cache_store); - auto it = m_stores.find(filename); - std::string_view map_key = it->first; - cache_store->set_pathname(map_key); + auto it = cast(m_stores)->find(store_sv); + std::string_view map_key = (*it)->key(); + cache_store->set_store_key(map_key); + cache_store->set_src_name(filename); cache_store->set_pool(this); + struct stat st; + SET_STRUCT_STAT(&st); + st.st_size = -1; + if (cache_store->fstat(&st) == 0) { + cache_store->set_cached_size(st.st_size); + cache_store->set_actual_size(st.st_size); + } + cache_store->set_open_flags(flags); + return cache_store; + }; + auto store = cast(m_stores)->acquire(store_sv, ctor); + if (store) { + auto cnt = store->ref_.fetch_add(1, std::memory_order_relaxed); + if (cnt) + cast(m_stores)->release(store_sv); } - cache_store->add_ref(); - return cache_store; + return store; +} + +void ICachePool::set_trans_func(CacheFnTransFunc fn_trans_func) { + this->fn_trans_func = fn_trans_func; } int ICachePool::store_release(ICacheStore *store) { - auto iter = m_stores.find(store->get_pathname()); - if (iter == m_stores.end()) { - LOG_ERROR_RETURN(0, -1, "try to erase an unexist store from map m_stores , name : `", - store->get_pathname().data()); - } - m_stores.erase(iter); - return 0; + return cast(m_stores)->release(store->get_store_key()); } } // namespace FileSystem diff --git a/src/overlaybd/cache/cache.h b/src/overlaybd/cache/cache.h index 243f8569..06b8b38b 100644 --- a/src/overlaybd/cache/cache.h +++ b/src/overlaybd/cache/cache.h @@ -14,32 +14,59 @@ limitations under the License. */ #pragma once -#include +#include #include #include +#include #include #include "pool_store.h" +#define O_WRITE_THROUGH 0x01000000 // write backing store and cache +#define O_WRITE_AROUND 0x02000000 // write backing store only, default +#define O_WRITE_BACK 0x04000000 // write cache and async flush to backing store, not support yet +#define O_CACHE_ONLY 0x08000000 // write cache only +#define O_DIRECT_LOCAL 0x20000000 // read local +#define O_MMAP_READ 0x00800000 // mmap like read + +#define RW_V2_HIGH_PRIORITY 0x00000001 // preadv2/pwritev2 high priority cache data +#define RW_V2_PROMOTE 0x00000002 // preadv2 promote flag +#define RW_V2_CACHE_ONLY 0x00000004 // preadv2 cache only flag +#define RW_V2_TO_BUFFER_WITHOUT_SYNC \ + 0x00000010 // pwritev2 to buffered accessor file's buffer without sync +#define RW_V2_MEMORY_ONLY 0x00000020 // pwritev2 memory cache only + +#define IS_STRUCT_STAT_SETTED(x) ((*(uint64_t *)x) == 0xF19A336DB7CA28E7ull) +#define SET_STRUCT_STAT(x) ((*(uint64_t *)x) = 0xF19A336DB7CA28E7ull) + +const int IOCTL_GET_PAGE_SIZE = 161; + +namespace Cache { +namespace Block { +struct Options; +} +} // namespace Cache struct IOAlloc; namespace FileSystem { class ICachedFileSystem : public photon::fs::IFileSystem { public: // get the source file system - UNIMPLEMENTED_POINTER(photon::fs::IFileSystem *get_source()); + UNIMPLEMENTED_POINTER(IFileSystem *get_source()); // set the source file system - UNIMPLEMENTED(int set_source(photon::fs::IFileSystem *src)); + UNIMPLEMENTED(int set_source(IFileSystem *src)); UNIMPLEMENTED_POINTER(ICachePool *get_pool()); + + UNIMPLEMENTED(int set_pool(ICachePool *pool)); }; class ICachedFile : public photon::fs::IFile { public: // get the source file system - UNIMPLEMENTED_POINTER(photon::fs::IFile *get_source()); + UNIMPLEMENTED_POINTER(IFile *get_source()); // set the source file system, and enable `auto_refill` - UNIMPLEMENTED(int set_source(photon::fs::IFile *src)); + UNIMPLEMENTED(int set_source(IFile *src)); UNIMPLEMENTED_POINTER(ICacheStore *get_store()); @@ -55,15 +82,9 @@ class ICachedFile : public photon::fs::IFile { return pwritev(iov, iovcnt, offset); } - // refilling a range without providing data, is treated as prefeching + // refilling a range without providing data, is treated as prefetching ssize_t refill(off_t offset, size_t count) { - return prefetch(offset, count); - } - - // prefeching a range is implemented as reading the range without a buffer - ssize_t prefetch(off_t offset, size_t count) { - iovec iov{nullptr, count}; - return preadv(&iov, 1, offset); + return fadvise(offset, count, POSIX_FADV_WILLNEED); } // query cached extents is implemented as fiemap() @@ -82,38 +103,22 @@ class ICachedFile : public photon::fs::IFile { } }; -class IMemCachedFile : public ICachedFile { -public: - // Get the internal buffer for the specified LBA range (usually aligned), - // which will remain valid for user until released by unpin_buffer(). - // Will allocate pages for missed ranges. - // Will refill / fetch / load data from source if `refill`. - // Concurrent R/W to a same range are guaranteed to work, but considered - // a race-condition and the result is undefiend. - // returns # of bytes actually got, or <0 for failures - virtual ssize_t pin_buffer(off_t offset, size_t count, bool refill, /*OUT*/ iovector *iov) = 0; - - // Release buffers got from pin_buffer(), - // and the buffer is no longer valid for user. - // return 0 for success, < 0 for failures - virtual int unpin_buffer(off_t offset, const iovector *iov) = 0; -}; - extern "C" { ICachedFileSystem *new_cached_fs(photon::fs::IFileSystem *src, ICachePool *pool, uint64_t pageSize, - uint64_t refillUnit, IOAlloc *allocator); + IOAlloc *allocator, CacheFnTransFunc fn_trans_func = nullptr); + +ICachedFile *new_cached_file(ICacheStore *store, uint64_t pageSize, photon::fs::IFileSystem *fs); -/** Full file cache will automatically delete its media_fs when destructed */ ICachedFileSystem *new_full_file_cached_fs(photon::fs::IFileSystem *srcFs, - photon::fs::IFileSystem *media_fs, - uint64_t refillUnit, uint64_t capacityInGB, - uint64_t periodInUs, uint64_t diskAvailInBytes, - IOAlloc *allocator, - Fn_trans_func name_trans = ICachePool::same_name_trans); + photon::fs::IFileSystem *media_fs, uint64_t refillUnit, + uint64_t capacityInGB, uint64_t periodInUs, + uint64_t diskAvailInBytes, IOAlloc *allocator, + int quotaDirLevel, + CacheFnTransFunc fn_trans_func = nullptr); /** - * @param blk_size The proper size for cache metadata and IO efficiency. - * Large writes to cache media will be split into blk_size. Reads are not affected. + * @param blk_size The proper size for cache metadata and IO efficiency. Large writes to cache media + * will be split into blk_size. Reads and small writes are not affected. * @param prefetch_unit Controls the expand prefetch size from src file. 0 means to disable this * feature. */ diff --git a/src/overlaybd/cache/cached_fs.cpp b/src/overlaybd/cache/cached_fs.cpp new file mode 100644 index 00000000..6d775f9b --- /dev/null +++ b/src/overlaybd/cache/cached_fs.cpp @@ -0,0 +1,446 @@ +/* + Copyright The Overlaybd Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "cache.h" +#include "photon/common/alog.h" +#include "photon/common/io-alloc.h" +#include "photon/common/iovector.h" +#include "photon/common/string_view.h" +#include "photon/fs/filesystem.h" +#include "photon/fs/range-split.h" +#include "pool_store.h" + +namespace Cache { + +using namespace FileSystem; +using namespace photon::fs; + +const uint64_t kMaxPrefetchSize = 32 * 1024 * 1024; + +class CachedFs : public ICachedFileSystem, public IFileSystemXAttr { +public: + CachedFs(IFileSystem *srcFs, ICachePool *fileCachePool, size_t pageSize, IOAlloc *allocator, + CacheFnTransFunc fn_trans_func) + : srcFs_(srcFs), fileCachePool_(fileCachePool), pageSize_(pageSize), allocator_(allocator), + xattrFs_(dynamic_cast(srcFs)) { + fileCachePool_->set_trans_func(fn_trans_func); + } + + ~CachedFs() { + delete fileCachePool_; + } + + IFile *open(const char *pathname, int flags, mode_t mode) override { + int cflags = (flags & (O_WRITE_THROUGH | O_CACHE_ONLY | O_WRITE_BACK)); + auto cache_store = fileCachePool_->open(pathname, O_RDWR | O_CREAT | cflags, 0644); + if (nullptr == cache_store) { + LOG_ERRNO_RETURN(0, nullptr, "fileCachePool_ open file failed, name : `", pathname) + } + + cache_store->set_src_fs(srcFs_); + cache_store->set_page_size(pageSize_); + cache_store->set_allocator(allocator_); + auto ret = new_cached_file(cache_store, pageSize_, this); + if (ret == nullptr) { // if create file is failed + // cache_store must be release, or will leak + cache_store->release(); + } + return ret; + } + + IFile *open(const char *pathname, int flags) override { + return open(pathname, flags, 0); // mode and flags are meaningless in RoCacheFS::open(2)(3) + } + + int mkdir(const char *pathname, mode_t mode) override { + return srcFs_ ? srcFs_->mkdir(pathname, mode) : -1; + } + + int rmdir(const char *pathname) override { + return srcFs_ ? srcFs_->rmdir(pathname) : -1; + } + + ssize_t readlink(const char *path, char *buf, size_t bufsiz) override { + return srcFs_ ? srcFs_->readlink(path, buf, bufsiz) : -1; + } + + int rename(const char *oldname, const char *newname) override { + return fileCachePool_->rename(oldname, newname); + } + + int unlink(const char *filename) override { + auto cache_store = fileCachePool_->open(filename, O_RDONLY, 0); + if (cache_store != nullptr) { + cache_store->set_cached_size(0); + cache_store->set_actual_size(0); + cache_store->release(); + } + auto ret = fileCachePool_->evict(filename); + return srcFs_ ? srcFs_->unlink(filename) : ret; + } + + int statfs(const char *path, struct statfs *buf) override { + return srcFs_ ? srcFs_->statfs(path, buf) : -1; + } + int statvfs(const char *path, struct statvfs *buf) override { + return srcFs_ ? srcFs_->statvfs(path, buf) : -1; + } + int stat(const char *path, struct stat *buf) override { + return srcFs_ ? srcFs_->stat(path, buf) : -1; + } + int lstat(const char *path, struct stat *buf) override { + return srcFs_ ? srcFs_->lstat(path, buf) : -1; + } + + int access(const char *pathname, int mode) override { + if (srcFs_) + return srcFs_->access(pathname, mode); + auto cache_store = fileCachePool_->open(pathname, O_RDONLY, 0); + if (cache_store == nullptr) + return -1; + cache_store->release(); + return 0; + } + + DIR *opendir(const char *name) override { + return srcFs_ ? srcFs_->opendir(name) : nullptr; + } + + IFileSystem *get_source() override { + return srcFs_; + } + + int set_source(IFileSystem *src) override { + srcFs_ = src; + return 0; + } + + ICachePool *get_pool() override { + return fileCachePool_; + } + + int set_pool(ICachePool *pool) override { + fileCachePool_ = pool; + return 0; + } + + ssize_t getxattr(const char *path, const char *name, void *value, size_t size) override { + return xattrFs_ ? xattrFs_->getxattr(path, name, value, size) : -1; + } + + virtual ssize_t lgetxattr(const char *path, const char *name, void *value, + size_t size) override { + return xattrFs_ ? xattrFs_->lgetxattr(path, name, value, size) : -1; + } + + ssize_t listxattr(const char *path, char *list, size_t size) override { + return xattrFs_ ? xattrFs_->listxattr(path, list, size) : -1; + } + + ssize_t llistxattr(const char *path, char *list, size_t size) override { + return xattrFs_ ? xattrFs_->llistxattr(path, list, size) : -1; + } + + int setxattr(const char *path, const char *name, const void *value, size_t size, + int flags) override { + return xattrFs_ ? xattrFs_->setxattr(path, name, value, size, flags) : -1; + } + + int lsetxattr(const char *path, const char *name, const void *value, size_t size, + int flags) override { + return xattrFs_ ? xattrFs_->lsetxattr(path, name, value, size, flags) : -1; + } + + int removexattr(const char *path, const char *name) override { + return xattrFs_ ? xattrFs_->removexattr(path, name) : -1; + } + + int lremovexattr(const char *path, const char *name) override { + return xattrFs_ ? xattrFs_->lremovexattr(path, name) : -1; + } + + UNIMPLEMENTED_POINTER(IFile *creat(const char *pathname, mode_t mode)); + UNIMPLEMENTED(int symlink(const char *oldname, const char *newname)); + UNIMPLEMENTED(int link(const char *oldname, const char *newname)); + UNIMPLEMENTED(int chmod(const char *pathname, mode_t mode)); + UNIMPLEMENTED(int chown(const char *pathname, uid_t owner, gid_t group)); + UNIMPLEMENTED(int lchown(const char *pathname, uid_t owner, gid_t group)); + UNIMPLEMENTED(int truncate(const char *path, off_t length)); + UNIMPLEMENTED(int utime(const char *path, const struct utimbuf *file_times)); + UNIMPLEMENTED(int utimes(const char *path, const struct timeval times[2])); + UNIMPLEMENTED(int lutimes(const char *path, const struct timeval times[2])); + UNIMPLEMENTED(int mknod(const char *path, mode_t mode, dev_t dev)); + UNIMPLEMENTED(int syncfs()); + +private: + IFileSystem *srcFs_; // owned by extern + ICachePool *fileCachePool_; // owned by current class + size_t pageSize_; + + IOAlloc *allocator_; + IFileSystemXAttr *xattrFs_; +}; + +/* + * the procedures of pread are as follows: + * 1. check that the cache is hit(contain unaligned block). + * 2. if hit, just read from cache. + * 3. if not, merge all holes into one read request(offset, size), + * then read missing data from source of file and write it into cache, + * after that read cache' data into user's buffer. + */ +class CachedFile : public ICachedFile, public IFileXAttr { +public: + CachedFile(ICacheStore *cache_store, size_t pageSize, IFileSystem *fs) + : cache_store_(cache_store), pageSize_(pageSize), fs_(fs) { + } + + ~CachedFile() { + cache_store_->release(); + } + + IFileSystem *filesystem() override { + return fs_; + } + + ssize_t pread(void *buf, size_t count, off_t offset) override { + struct iovec v { + buf, count + }; + return preadv(&v, 1, offset); + } + + ssize_t preadv(const struct iovec *iov, int iovcnt, off_t offset) override { + return preadv2(iov, iovcnt, offset, 0); + } + + ssize_t preadv2(const struct iovec *iov, int iovcnt, off_t offset, int flags) override { + return cache_store_->preadv2(iov, iovcnt, offset, flags); + } + + // pwrite* need to be aligned to 4KB for avoiding write padding. + ssize_t pwrite(const void *buf, size_t count, off_t offset) override { + struct iovec v { + const_cast(buf), count + }; + return pwritev(&v, 1, offset); + } + + ssize_t pwritev(const struct iovec *iov, int iovcnt, off_t offset) override { + return pwritev2(iov, iovcnt, offset, 0); + } + + ssize_t pwritev2(const struct iovec *iov, int iovcnt, off_t offset, int flags) override { + return cache_store_->pwritev2(iov, iovcnt, offset, flags); + } + + int fstat(struct stat *buf) override { + DEFER({ buf->st_ino = cache_store_->get_handle(); }); + auto size = cache_store_->get_actual_size(); + if (size % pageSize_ != 0) { + buf->st_size = size; + return 0; + } + IFile *src_file = nullptr; + if (cache_store_->open_src_file(&src_file) != 0) + return -1; + if (src_file) + return src_file->fstat(buf); + return cache_store_->fstat(buf); + } + + int close() override { + return 0; + } + + ssize_t read(void *buf, size_t count) override { + struct iovec v { + buf, count + }; + return readv(&v, 1); + } + + ssize_t readv(const struct iovec *iov, int iovcnt) override { + auto ret = preadv(iov, iovcnt, readOffset_); + if (ret > 0) { + readOffset_ += ret; + } + return ret; + } + + ssize_t write(const void *buf, size_t count) override { + struct iovec v { + const_cast(buf), count + }; + return writev(&v, 1); + } + + ssize_t writev(const struct iovec *iov, int iovcnt) override { + auto ret = pwritev(iov, iovcnt, writeOffset_); + if (ret > 0) { + writeOffset_ += ret; + } + return ret; + } + + int query(off_t offset, size_t count) override { + auto ret = cache_store_->queryRefillRange(offset, count); + if (ret.first < 0) + return -1; + return ret.second; + } + + // offset and len must be aligned 4k, otherwise it's useless. + // !!! need ensure no other read operation, otherwise read may read hole data(zero). + int fallocate(int mode, off_t offset, off_t len) override { + if (len == -1) { + return cache_store_->evict(offset, len); + } + range_split rs(offset, len, pageSize_); + auto aligned_offset = rs.aligned_begin_offset(); + auto aligned_len = rs.aligned_length(); + LOG_DEBUG(VALUE(offset), VALUE(len), VALUE(aligned_offset), VALUE(aligned_len)); + return cache_store_->evict(aligned_offset, aligned_len); + } + + int fadvise(off_t offset, off_t len, int advice) override { + if (advice == POSIX_FADV_WILLNEED) { + int ret = prefetch(len, offset, 0); + if (ret < 0) { + LOG_ERROR_RETURN(0, -1, "prefetch read failed"); + } + return 0; + } + LOG_ERRNO_RETURN(ENOSYS, -1, "advice ` is not implemented", advice); + } + + IFile *get_source() override { + IFile *src = nullptr; + if (cache_store_->open_src_file(&src) != 0) + return nullptr; + return src; + } + + inline void get_source_filexattr() { + if (!source_filexattr_) { + auto sfile = get_source(); + source_filexattr_ = dynamic_cast(sfile); + } + } + + // set the source file system, and enable `auto_refill` + int set_source(IFile *src) override { + cache_store_->set_src_file(src); + return 0; + } + + ICacheStore *get_store() override { + return cache_store_; + } + + int ftruncate(off_t length) override { + cache_store_->set_cached_size(length); + cache_store_->set_actual_size(length); + return 0; + } + + std::string_view get_pathname() { + return get_store()->get_src_name(); + } + + ssize_t fgetxattr(const char *name, void *value, size_t size) override { + get_source_filexattr(); + return source_filexattr_ ? source_filexattr_->fgetxattr(name, value, size) : -1; + } + + ssize_t flistxattr(char *list, size_t size) override { + get_source_filexattr(); + return source_filexattr_ ? source_filexattr_->flistxattr(list, size) : -1; + } + + int fsetxattr(const char *name, const void *value, size_t size, int flags) override { + get_source_filexattr(); + return source_filexattr_ ? source_filexattr_->fsetxattr(name, value, size, flags) : -1; + } + + int fremovexattr(const char *name) override { + get_source_filexattr(); + return source_filexattr_ ? source_filexattr_->fremovexattr(name) : -1; + } + + UNIMPLEMENTED(off_t lseek(off_t offset, int whence)); + UNIMPLEMENTED(int fsync()); + UNIMPLEMENTED(int fdatasync()); + UNIMPLEMENTED(int fchmod(mode_t mode)); + UNIMPLEMENTED(int fchown(uid_t owner, gid_t group)); + UNIMPLEMENTED(int fiemap(photon::fs::fiemap *map)); + +protected: + ssize_t prefetch(size_t count, off_t offset, int flags) { + uint64_t end = photon::sat_add(offset, count); + if (offset % pageSize_ != 0) { + offset = offset / pageSize_ * pageSize_; + } + if (end % pageSize_ != 0) { + end = photon::sat_add(end, pageSize_ - 1) / pageSize_ * pageSize_; + } + + uint64_t remain = end - offset; + ssize_t read = 0; + while (remain > 0) { + off_t min = std::min(kMaxPrefetchSize, remain); + remain -= min; + auto ret = cache_store_->try_refill_range(offset, static_cast(min)); + if (ret < 0) { + LOG_ERRNO_RETURN(0, -1, + "try_refill_range failed, ret : `, len : `, offset : `, flags : `", + ret, min, offset, flags); + } + read += ret; + // read end of file. + if (ret < min) { + return read; + } + offset += ret; + } + return read; + } + + ICacheStore *cache_store_; + size_t pageSize_; + IFileSystem *fs_; + + off_t readOffset_ = 0; + off_t writeOffset_ = 0; + IFileXAttr *source_filexattr_ = nullptr; +}; + +} // namespace Cache + +namespace FileSystem { +using namespace photon::fs; +ICachedFileSystem *new_cached_fs(IFileSystem *src, ICachePool *pool, uint64_t pageSize, + IOAlloc *allocator, CacheFnTransFunc fn_trans_func) { + if (!allocator) { + allocator = new IOAlloc; + } + return new ::Cache::CachedFs(src, pool, pageSize, allocator, fn_trans_func); +} + +ICachedFile *new_cached_file(ICacheStore *store, uint64_t pageSize, IFileSystem *fs) { + return new ::Cache::CachedFile(store, pageSize, fs); +} +} // namespace FileSystem diff --git a/src/overlaybd/cache/forwardcfs.h b/src/overlaybd/cache/forwardcfs.h new file mode 100644 index 00000000..e5cf520f --- /dev/null +++ b/src/overlaybd/cache/forwardcfs.h @@ -0,0 +1,64 @@ +/* + Copyright The Overlaybd Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#pragma once +#include "cache.h" +#include "photon/fs/forwardfs.h" + +namespace FileSystem { +template +class ForwardCachedFileBase : public IForwardCachedFile { +protected: + using Base = IForwardCachedFile; + using Base::Base; + virtual photon::fs::IFile *get_source() override { + return Base::m_file->get_source(); + } + virtual int set_source(photon::fs::IFile *src) override { + return Base::m_file->set_source(src); + } + virtual ICacheStore *get_store() override { + return Base::m_file->get_store(); + } + virtual int query(off_t offset, size_t count) override { + return Base::m_file->query(offset, count); + } +}; +using ForwardCachedFile = ForwardCachedFileBase>; +using ForwardCachedFile_Ownership = + ForwardCachedFileBase>; + +template +class ForwardCachedFSBase : public IForwardCachedFS { +protected: + using Base = IForwardCachedFS; + using Base::Base; + virtual photon::fs::IFileSystem *get_source() override { + return Base::m_fs->get_source(); + } + virtual int set_source(photon::fs::IFileSystem *src) override { + return Base::m_fs->set_source(src); + } + virtual ICachePool *get_pool() override { + return Base::m_fs->get_pool(); + } + virtual int set_pool(ICachePool *pool) override { + return Base::m_fs->set_pool(pool); + } +}; +using ForwardCachedFS = ForwardCachedFSBase>; +using ForwardCachedFS_Ownership = + ForwardCachedFSBase>; +} // namespace FileSystem diff --git a/src/overlaybd/cache/frontend/CMakeLists.txt b/src/overlaybd/cache/frontend/CMakeLists.txt deleted file mode 100644 index 2fb8e1fb..00000000 --- a/src/overlaybd/cache/frontend/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -file(GLOB SRC_FRONTEND "*.cpp") - -add_library(cache_frontend_lib STATIC ${SRC_FRONTEND}) -target_include_directories(cache_frontend_lib PUBLIC - ${PHOTON_INCLUDE_DIR} -) \ No newline at end of file diff --git a/src/overlaybd/cache/frontend/cached_file.cpp b/src/overlaybd/cache/frontend/cached_file.cpp deleted file mode 100644 index 4a67f8bb..00000000 --- a/src/overlaybd/cache/frontend/cached_file.cpp +++ /dev/null @@ -1,362 +0,0 @@ -/* - Copyright The Overlaybd Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -#include "cached_file.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "../pool_store.h" - -namespace Cache { - -using namespace photon::fs; - -const off_t kMaxPrefetchSize = 16 * 1024 * 1024; - -CachedFile::CachedFile(IFile *src_file, FileSystem::ICacheStore *cache_store, off_t size, - size_t pageSize, size_t refillUnit, IOAlloc *allocator, IFileSystem *fs) - : src_file_(src_file), cache_store_(cache_store), size_(size), pageSize_(pageSize), - refillUnit_(refillUnit), allocator_(allocator), fs_(fs), readOffset_(0), writeOffset_(0){}; - -CachedFile::~CachedFile() { - cache_store_->release(); - delete src_file_; -} - -IFileSystem *CachedFile::filesystem() { - return fs_; -} - -ssize_t CachedFile::pread(void *buf, size_t count, off_t offset) { - struct iovec v { - buf, count - }; - return preadv(&v, 1, offset); -} - -ssize_t CachedFile::preadv(const struct iovec *iov, int iovcnt, off_t offset) { - if (1 == iovcnt && !iov->iov_base) { - return prefetch(iov->iov_len, offset); - } - return preadvInternal(iov, iovcnt, offset); -} - -ssize_t CachedFile::prefetch(size_t count, off_t offset) { - static char buf[kMaxPrefetchSize * 2] = {0}; - void *alignBuf = align_ptr(buf, pageSize_); - struct iovec iov { - alignBuf, count - }; - - auto end = offset + count; - if (offset % pageSize_ != 0) { - offset = offset & ~(pageSize_ - 1); - } - if (end % pageSize_ != 0) { - end = (end + pageSize_ - 1) & ~(pageSize_ - 1); - } - - off_t remain = end - offset; - ssize_t read = 0; - while (remain > 0) { - off_t min = std::min(kMaxPrefetchSize, remain); - remain -= min; - iov.iov_len = min; - auto ret = preadvInternal(&iov, 1, offset); - if (ret < 0) { - LOG_ERRNO_RETURN(0, -1, "preadv failed, ret : `, len : `, offset : `, size_ : `", ret, - min, offset, size_); - } - read += ret; - // read end of file. - if (ret < min) { - return read; - } - offset += ret; - } - return read; -} - -ssize_t CachedFile::preadvInternal(const struct iovec *iov, int iovcnt, off_t offset) { - if (offset < 0) { - LOG_ERROR_RETURN(EINVAL, -1, "offset is invalid, offset : `", offset) - } - - iovector_view view(const_cast(iov), iovcnt); - size_t iovSize = view.sum(); - if (0u == iovSize) { - return 0; - } - - if (offset >= size_ || offset + static_cast(iovSize) > size_) { - struct stat st; - auto ok = fstat(&st); - if (ok == 0 && st.st_size > size_) { - off_t last = align_down(size_, pageSize_); - if (last != size_) - cache_store_->evict(last, pageSize_); - size_ = st.st_size; - } - } - - if (offset >= size_) { - return 0; - } - - IOVector input(iov, iovcnt); - if (offset + static_cast(iovSize) > size_) { - input.extract_back(offset + static_cast(iovSize) - size_); - iovSize = size_ - offset; - } - -again: - auto tr = cache_store_->try_preadv(input.iovec(), input.iovcnt(), offset); - if (tr.refill_offset < 0) { - if (src_file_) { - ssize_t ret; - SCOPE_AUDIT("download", AU_FILEOP(get_pathname(), offset, ret)); - ret = src_file_->preadv(input.iovec(), input.iovcnt(), offset); - return ret; - } - - return -1; - } else if (tr.refill_size == 0 && tr.size >= 0) { - return tr.size; - } - - if (!src_file_) { - return -1; - } - - uint64_t refillOff = tr.refill_offset; - uint64_t refillSize = tr.refill_size; - if (refillOff + refillSize > static_cast(size_)) { - refillSize = size_ - refillOff; - } - - int ret = rangeLock_.try_lock_wait(refillOff, refillSize); - if (ret < 0) { - goto again; - } - - IOVector buffer(*allocator_); - { - DEFER(rangeLock_.unlock(refillOff, refillSize)); - auto alloc = buffer.push_back(refillSize); - if (alloc < refillSize) { - LOG_ERROR("memory allocate failed, refillSize:`, alloc:`", refillSize, alloc); - ssize_t ret; - SCOPE_AUDIT("download", AU_FILEOP(get_pathname(), offset, ret)); - ret = src_file_->preadv(input.iovec(), input.iovcnt(), offset); - return ret; - } - - ssize_t read; - { - SCOPE_AUDIT("download", AU_FILEOP(get_pathname(), offset, read)); - read = src_file_->preadv(buffer.iovec(), buffer.iovcnt(), refillOff); - } - - if (read != static_cast(refillSize)) { - LOG_ERRNO_RETURN( - 0, -1, - "src file read failed, read : `, expectRead : `, size_ : `, offset : `, sum : `", - read, refillSize, size_, refillOff, buffer.sum()); - } - - auto write = cache_store_->pwritev(buffer.iovec(), buffer.iovcnt(), refillOff); - - if (write != static_cast(refillSize)) { - if (ENOSPC != errno) - LOG_ERROR("cache file write failed : `, error : `, size_ : `, offset : `, sum : `", - write, ERRNO(errno), size_, refillOff, buffer.sum()); - ssize_t ret; - { - SCOPE_AUDIT("download", AU_FILEOP(get_pathname(), offset, ret)); - ret = src_file_->preadv(input.iovec(), input.iovcnt(), offset); - } - return ret; - } - } - - IOVector refillBuf(buffer.iovec(), buffer.iovcnt()); - int remain = iovSize; - int result = 0; - if (tr.refill_offset <= offset) { - auto inView = input.view(); - refillBuf.extract_front(offset - tr.refill_offset); - auto copy = refillBuf.memcpy_to(&inView, iovSize); - remain -= copy; - offset += copy; - result += copy; - } else if (tr.refill_offset + tr.refill_size >= offset + iovSize) { - iovector_view tailIov; - tailIov.iovcnt = 0; - input.slice(iovSize - (tr.refill_offset - offset), tr.refill_offset - offset, &tailIov); - auto copy = refillBuf.memcpy_to(&tailIov); - input.extract_back(copy); - result += copy; - remain -= copy; - } - - if (remain > 0) { - auto readRet = cache_store_->preadv(input.iovec(), input.iovcnt(), offset); - if (readRet < 0) { - SCOPE_AUDIT("download", AU_FILEOP(get_pathname(), offset, readRet)); - readRet = src_file_->preadv(input.iovec(), input.iovcnt(), offset); - if (readRet < 0) - LOG_ERRNO_RETURN(0, readRet, "read failed, ret:`, offset:`,sum:`,size_:`", readRet, - offset, input.sum(), size_); - } - - return result + readRet; - } - - return result; -} - -ssize_t CachedFile::pwrite(const void *buf, size_t count, off_t offset) { - struct iovec v { - const_cast(buf), count - }; - return pwritev(&v, 1, offset); -} - -ssize_t CachedFile::pwritev(const struct iovec *iov, int iovcnt, off_t offset) { - if (offset >= size_) { - return 0; - } - - iovector_view view(const_cast(iov), iovcnt); - size_t size = view.sum(); - - if (offset % pageSize_ != 0 || - (size % pageSize_ != 0 && offset + static_cast(size) < size_)) { - LOG_ERROR_RETURN(EINVAL, -1, "size or offset is not aligned to 4K, size : `, offset : `", - size, offset); - } - - if (offset + static_cast(size) <= size_) { - return cache_store_->pwritev(iov, iovcnt, offset); - } - - IOVector ioVector(iov, iovcnt); - if (offset + static_cast(size) > size_) { - auto ret = ioVector.extract_back(size - (size_ - offset)); - if (ret != size - (size_ - offset)) - LOG_ERRNO_RETURN(EINVAL, -1, "extract failed, extractSize : `, expected : ", ret, - size - (size_ - offset)) - } - - auto write = cache_store_->pwritev(ioVector.iovec(), ioVector.iovcnt(), offset); - if (write != static_cast(ioVector.sum())) { - if (ENOSPC != errno) - LOG_ERROR("cache file write failed : `, error : `, size_ : `, offset : `, sum : `", - write, ERRNO(errno), size_, offset, ioVector.sum()); - } - - return write; -} - -int CachedFile::fiemap(struct fiemap *map) { - errno = ENOSYS; - return -1; -} - -int CachedFile::query(off_t offset, size_t count) { - auto ret = cache_store_->queryRefillRange(offset, count); - return ret.second; -} - -int CachedFile::fallocate(int mode, off_t offset, off_t len) { - if (len == -1) { - len = size_ - offset; - } - range_split_power2 rs(offset, len, pageSize_); - auto aligned_offset = rs.aligned_begin_offset(); - auto aligned_len = rs.aligned_length(); - LOG_DEBUG("fallocate offset: `, len: `, aligned offset: `, aligned len: `", offset, len, - aligned_offset, aligned_len); - return cache_store_->evict(aligned_offset, aligned_len); -} - -int CachedFile::fstat(struct stat *buf) { - return src_file_ ? src_file_->fstat(buf) : -1; -} - -int CachedFile::close() { - if (src_file_) { - return src_file_->close(); - } - return 0; -} - -ssize_t CachedFile::read(void *buf, size_t count) { - struct iovec v { - buf, count - }; - return readv(&v, 1); -} - -ssize_t CachedFile::readv(const struct iovec *iov, int iovcnt) { - auto ret = preadv(iov, iovcnt, readOffset_); - if (ret > 0) { - readOffset_ += ret; - } - return ret; -} - -ssize_t CachedFile::write(const void *buf, size_t count) { - struct iovec v { - const_cast(buf), count - }; - return writev(&v, 1); -} - -ssize_t CachedFile::writev(const struct iovec *iov, int iovcnt) { - auto ret = pwritev(iov, iovcnt, writeOffset_); - if (ret > 0) { - writeOffset_ += ret; - } - return ret; -} - -std::string_view CachedFile::get_pathname() { - return get_store()->get_pathname(); -} - -ICachedFile *new_cached_file(IFile *src, ICacheStore *store, uint64_t pageSize, uint64_t refillUnit, - IOAlloc *allocator, IFileSystem *fs) { - // new_cached_file requires src is able to fstat - // once stat is failed, it will return nullptr - struct stat st = {}; - if (src) { - auto ok = src->fstat(&st); - if (-1 == ok) { - LOG_ERRNO_RETURN(0, nullptr, "src_file fstat failed : `", ok); - } - } - if (st.st_size > 0) { - store->ftruncate(st.st_size); - } - return new CachedFile(src, store, st.st_size, pageSize, refillUnit, allocator, fs); -} - -} // namespace Cache diff --git a/src/overlaybd/cache/frontend/cached_file.h b/src/overlaybd/cache/frontend/cached_file.h deleted file mode 100644 index 661dec54..00000000 --- a/src/overlaybd/cache/frontend/cached_file.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - Copyright The Overlaybd Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -#pragma once - -#include -#include -#include -#include -#include -#include "../cache.h" -#include -#include - -struct IOAlloc; - -namespace FileSystem { -class ICacheStore; -} - -namespace Cache { -using namespace FileSystem; - -/* - * the procedures of pread are as follows: - * 1. check that the cache is hit(contain unaligned block). - * 2. if hit, just read from cache. - * 3. if not, merge all holes into one read request(offset, size), - * then read missing data from source of file and write it into cache, - * after that read cache' data into user's buffer. - */ - -class CachedFile : public ICachedFile { -public: - CachedFile(IFile *src_file, ICacheStore *cache_store, off_t size, uint64_t pageSize, - uint64_t refillUnit, IOAlloc *allocator, photon::fs::IFileSystem *fs); - ~CachedFile(); - - photon::fs::IFileSystem *filesystem(); - - ssize_t pread(void *buf, size_t count, off_t offset) override; - ssize_t preadv(const struct iovec *iov, int iovcnt, off_t offset) override; - - // pwrite* need to be aligned to 4KB for avoiding write padding. - ssize_t pwrite(const void *buf, size_t count, off_t offset) override; - ssize_t pwritev(const struct iovec *iov, int iovcnt, off_t offset) override; - - UNIMPLEMENTED(off_t lseek(off_t offset, int whence)); - UNIMPLEMENTED(int fsync()); - UNIMPLEMENTED(int fdatasync()); - UNIMPLEMENTED(int fchmod(mode_t mode)); - UNIMPLEMENTED(int fchown(uid_t owner, gid_t group)); - int fstat(struct stat *buf) override; - - int close(); - - ssize_t read(void *buf, size_t count) override; - ssize_t readv(const struct iovec *iov, int iovcnt) override; - ssize_t write(const void *buf, size_t count) override; - ssize_t writev(const struct iovec *iov, int iovcnt) override; - - int fiemap(struct photon::fs::fiemap *map) override; - - int query(off_t offset, size_t count) override; - - // offset and len must be aligned 4k, otherwise it's useless. - // !!! need ensure no other read operation, otherwise read may read hole data(zero). - int fallocate(int mode, off_t offset, off_t len) override; - - IFile *get_source() override { - return src_file_; - } - - // set the source file system, and enable `auto_refill` - int set_source(IFile *src) override { - src_file_ = src; - return 0; - } - - ICacheStore *get_store() override { - return cache_store_; - } - - int ftruncate(off_t length) override { - assert(!src_file_); - size_ = length; - return 0; - } - - std::string_view get_pathname(); - -private: - ssize_t prefetch(size_t count, off_t offset); - - ssize_t preadvInternal(const struct iovec *iov, int iovcnt, off_t offset); - - IFile *src_file_; // owned by current class - ICacheStore *cache_store_; // owned by current class - off_t size_; - size_t pageSize_; - size_t refillUnit_; - - RangeLock rangeLock_; - - IOAlloc *allocator_; - photon::fs::IFileSystem *fs_; - - off_t readOffset_; - off_t writeOffset_; -}; - -ICachedFile *new_cached_file(photon::fs::IFile *src, ICacheStore *store, uint64_t pageSize, - uint64_t refillUnit, IOAlloc *allocator, photon::fs::IFileSystem *fs); - -} // namespace Cache diff --git a/src/overlaybd/cache/frontend/cached_fs.cpp b/src/overlaybd/cache/frontend/cached_fs.cpp deleted file mode 100644 index 7b3af73a..00000000 --- a/src/overlaybd/cache/frontend/cached_fs.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - Copyright The Overlaybd Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include -#include -#include -#include -#include "../cache.h" -#include "cached_file.h" -#include -#include - -namespace Cache { - -using namespace FileSystem; -using namespace photon::fs; - -class CachedFs : public ICachedFileSystem { -public: - CachedFs(IFileSystem *srcFs, ICachePool *fileCachePool, size_t pageSize, size_t refillUnit, - IOAlloc *allocator) - : srcFs_(srcFs), fileCachePool_(fileCachePool), pageSize_(pageSize), - refillUnit_(refillUnit), allocator_(allocator) { - } - - ~CachedFs() { - delete fileCachePool_; - } - - IFile *open(const char *pathname, int flags, mode_t mode) { - IFile *srcFile = nullptr; - if (srcFs_) { - srcFile = srcFs_->open(pathname, O_RDONLY); - if (!srcFile) - LOG_ERRNO_RETURN(0, nullptr, "Open source file failed"); - } - - auto cache_store = fileCachePool_->open(pathname, O_RDWR | O_CREAT, 0644); - if (nullptr == cache_store) { - delete srcFile; - LOG_ERRNO_RETURN(0, nullptr, "fileCachePool_ open file failed, name : `", pathname) - } - - auto ret = new_cached_file(srcFile, cache_store, pageSize_, refillUnit_, allocator_, this); - if (ret == nullptr) { // if create file is failed - // srcFile and cache_store must be release, or will leak - delete srcFile; - cache_store->release(); - } - return ret; - } - - IFile *open(const char *pathname, int flags) { - return open(pathname, flags, 0); // mode and flags are meaningless in RoCacheFS::open(2)(3) - } - - UNIMPLEMENTED_POINTER(IFile *creat(const char *pathname, mode_t mode)); - UNIMPLEMENTED(int mkdir(const char *pathname, mode_t mode)); - UNIMPLEMENTED(int rmdir(const char *pathname)); - UNIMPLEMENTED(int symlink(const char *oldname, const char *newname)); - - ssize_t readlink(const char *path, char *buf, size_t bufsiz) { - return srcFs_ ? srcFs_->readlink(path, buf, bufsiz) : -1; - } - - UNIMPLEMENTED(int link(const char *oldname, const char *newname)); - UNIMPLEMENTED(int rename(const char *oldname, const char *newname)); - UNIMPLEMENTED(int unlink(const char *filename)); - UNIMPLEMENTED(int chmod(const char *pathname, mode_t mode)); - UNIMPLEMENTED(int chown(const char *pathname, uid_t owner, gid_t group)); - UNIMPLEMENTED(int lchown(const char *pathname, uid_t owner, gid_t group)); - UNIMPLEMENTED(int utime(const char *path, const struct utimbuf *file_times) override); - UNIMPLEMENTED(int utimes(const char *path, const struct timeval times[2]) override); - UNIMPLEMENTED(int lutimes(const char *path, const struct timeval times[2]) override); - UNIMPLEMENTED(int mknod(const char *path, mode_t mode, dev_t dev) override); - - int statfs(const char *path, struct statfs *buf) { - return srcFs_ ? srcFs_->statfs(path, buf) : -1; - } - int statvfs(const char *path, struct statvfs *buf) { - return srcFs_ ? srcFs_->statvfs(path, buf) : -1; - } - int stat(const char *path, struct stat *buf) { - return srcFs_ ? srcFs_->stat(path, buf) : -1; - } - int lstat(const char *path, struct stat *buf) { - return srcFs_ ? srcFs_->stat(path, buf) : -1; - } - int access(const char *pathname, int mode) { - return srcFs_ ? srcFs_->access(pathname, mode) : -1; - } - - UNIMPLEMENTED(int truncate(const char *path, off_t length)); - UNIMPLEMENTED(int syncfs()); - - DIR *opendir(const char *name) override { - return srcFs_ ? srcFs_->opendir(name) : nullptr; - } - - IFileSystem *get_source() override { - return srcFs_; - } - - int set_source(IFileSystem *src) override { - srcFs_ = src; - return 0; - } - - ICachePool *get_pool() override { - return fileCachePool_; - } - -private: - IFileSystem *srcFs_; // owned by extern - ICachePool *fileCachePool_; // owned by current class - size_t pageSize_; - size_t refillUnit_; - - IOAlloc *allocator_; -}; - -} // namespace Cache - -namespace FileSystem { -using namespace photon::fs; -ICachedFileSystem *new_cached_fs(IFileSystem *src, ICachePool *pool, uint64_t pageSize, - uint64_t refillUnit, IOAlloc *allocator) { - if (!allocator) { - allocator = new IOAlloc; - } - return new ::Cache::CachedFs(src, pool, pageSize, refillUnit, allocator); -} -} // namespace FileSystem diff --git a/src/overlaybd/cache/full_file_cache/CMakeLists.txt b/src/overlaybd/cache/full_file_cache/CMakeLists.txt index e24124ef..8eacb764 100644 --- a/src/overlaybd/cache/full_file_cache/CMakeLists.txt +++ b/src/overlaybd/cache/full_file_cache/CMakeLists.txt @@ -4,6 +4,3 @@ add_library(full_file_cache_lib STATIC ${SRC_FULLFILECACHE}) target_include_directories(full_file_cache_lib PUBLIC ${PHOTON_INCLUDE_DIR} ) -if(BUILD_TESTING) - add_subdirectory(test) -endif() diff --git a/src/overlaybd/cache/full_file_cache/cache_pool.cpp b/src/overlaybd/cache/full_file_cache/cache_pool.cpp index a848b1cb..982bef04 100644 --- a/src/overlaybd/cache/full_file_cache/cache_pool.cpp +++ b/src/overlaybd/cache/full_file_cache/cache_pool.cpp @@ -20,23 +20,25 @@ #include #include #include -#include +#include "cache_store.h" #include +#include #include +#include #include -#include "cache_store.h" namespace Cache { using namespace FileSystem; +using namespace photon::fs; const uint64_t kGB = 1024 * 1024 * 1024; const uint64_t kMaxFreeSpace = 50 * kGB; const int64_t kEvictionMark = 5ll * kGB; -FileCachePool::FileCachePool(photon::fs::IFileSystem *mediaFs, uint64_t capacityInGB, uint64_t periodInUs, - uint64_t diskAvailInBytes, uint64_t refillUnit, Fn_trans_func name_trans) - : mediaFs_(mediaFs), capacityInGB_(capacityInGB), periodInUs_(periodInUs), +FileCachePool::FileCachePool(IFileSystem *mediaFs, uint64_t capacityInGB, uint64_t periodInUs, + uint64_t diskAvailInBytes, uint64_t refillUnit) + : ICachePool(0), mediaFs_(mediaFs), capacityInGB_(capacityInGB), periodInUs_(periodInUs), diskAvailInBytes_(diskAvailInBytes), refillUnit_(refillUnit), totalUsed_(0), timer_(nullptr), running_(false), exit_(false), isFull_(false) { int64_t capacityInBytes = capacityInGB_ * kGB; @@ -44,9 +46,6 @@ FileCachePool::FileCachePool(photon::fs::IFileSystem *mediaFs, uint64_t capacity // keep this relation : waterMark < riskMark < capacity riskMark_ = std::max(capacityInBytes - kEvictionMark, (static_cast(waterMark_) + capacityInBytes) >> 1); - if (name_trans != nullptr) { - file_name_trans = name_trans; - } } FileCachePool::~FileCachePool() { @@ -57,26 +56,27 @@ FileCachePool::~FileCachePool() { } delete timer_; } + this->stores_clear(); delete mediaFs_; } void FileCachePool::Init() { traverseDir("/"); - timer_ = new photon::Timer(periodInUs_, {this, FileCachePool::timerHandler}); + timer_ = new photon::Timer(periodInUs_, {this, FileCachePool::timerHandler}, true, + 8UL * 1024 * 1024); } ICacheStore *FileCachePool::do_open(std::string_view pathname, int flags, mode_t mode) { - auto filename = file_name_trans(pathname); - auto localFile = openMedia(filename, flags, mode); + auto localFile = openMedia(pathname, flags, mode); if (!localFile) { return nullptr; } - auto find = fileIndex_.find(filename); + auto find = fileIndex_.find(pathname); if (find == fileIndex_.end()) { auto lruIter = lru_.push_front(fileIndex_.end()); std::unique_ptr entry(new LruEntry{lruIter, 1, 0}); - find = fileIndex_.emplace(filename, std::move(entry)).first; + find = fileIndex_.emplace(pathname, std::move(entry)).first; lru_.front() = find; } else { lru_.access(find->second->lruIter); @@ -86,12 +86,12 @@ ICacheStore *FileCachePool::do_open(std::string_view pathname, int flags, mode_t return new FileCacheStore(this, localFile, refillUnit_, find); } -photon::fs::IFile *FileCachePool::openMedia(std::string_view name, int flags, int mode) { - if (name.empty()) { +IFile *FileCachePool::openMedia(std::string_view name, int flags, int mode) { + if (name.empty() || name[0] != '/') { LOG_ERROR_RETURN(EINVAL, nullptr, "pathname is invalid, path : `", name); } - auto base_directory = photon::fs::Path(name.data()).dirname(); + auto base_directory = Path(name.data()).dirname(); auto ret = mkdir_recursive(base_directory, mediaFs_); if (ret) { LOG_ERRNO_RETURN(0, nullptr, "mkdir failed, path : `", name); @@ -105,6 +105,11 @@ photon::fs::IFile *FileCachePool::openMedia(std::string_view name, int flags, in return localFile; } +int FileCachePool::set_quota(std::string_view pathname, size_t quota) { + errno = ENOSYS; + return -1; +} + int FileCachePool::stat(CacheStat *stat, std::string_view pathname) { errno = ENOSYS; return -1; @@ -120,6 +125,11 @@ int FileCachePool::evict(size_t size) { return -1; } +int FileCachePool::rename(std::string_view oldname, std::string_view newname) { + errno = ENOSYS; + return -1; +} + bool FileCachePool::isFull() { return isFull_; } @@ -215,7 +225,7 @@ void FileCachePool::eviction() { if (0 == fileIter->second->openCount) { afterFtrucate(fileIter); } - photon::thread_usleep(kDeleteDelayInUs); + photon::thread_yield(); continue; } @@ -224,16 +234,21 @@ void FileCachePool::eviction() { err = mediaFs_->truncate(fileName.data(), 0); } - if (err && errno != ENOENT) { - LOG_ERROR("truncate(0) failed, name : `, ret : `, error code : `", fileName, err, - ERRNO()); - continue; - } else { - fileSize = lruEntry->size; - afterFtrucate(fileIter); - actualEvict -= fileSize; + if (err) { + ERRNO e; + LOG_ERROR("truncate(0) failed, name : `, ret : `, error code : `", fileName, err, e); + // truncate to 0 failed means unable to free the file, it should not consider as a part + // of cache. Deal as it already release. + // The only exception is errno EINTR, means truncate interrupted by signal, should try + // again + if (e.no == EINTR) { + photon::thread_yield(); + continue; + } } - photon::thread_usleep(kDeleteDelayInUs); + afterFtrucate(fileIter); + actualEvict -= fileSize; + photon::thread_yield(); } } @@ -251,19 +266,22 @@ bool FileCachePool::afterFtrucate(FileNameMap::iterator iter) { } if (0 == iter->second->openCount) { auto err = mediaFs_->unlink(iter->first.data()); - if (0 != err) { - LOG_ERROR("unlink failed, name : `, ret : `, error code : `", iter->first, err, - ERRNO()); - } else { - lru_.remove(iter->second->lruIter); - fileIndex_.erase(iter); + ERRNO e; + LOG_ERROR("unlink failed, name : `, ret : `, error code : `", iter->first, err, e); + // unlik failed may caused by multiple reasons + // only EBUSY should may be able to trying to unlink again + // other reason should never try to clean it. + if (err && (e.no == EBUSY)) { + return false; } + lru_.remove(iter->second->lruIter); + fileIndex_.erase(iter); } return true; } int FileCachePool::traverseDir(const std::string &root) { - for (auto file : enumerable(photon::fs::Walker(mediaFs_, root))) { + for (auto file : enumerable(Walker(mediaFs_, root))) { insertFile(file); } return 0; diff --git a/src/overlaybd/cache/full_file_cache/cache_pool.h b/src/overlaybd/cache/full_file_cache/cache_pool.h index 81d1de0c..1f17be7a 100644 --- a/src/overlaybd/cache/full_file_cache/cache_pool.h +++ b/src/overlaybd/cache/full_file_cache/cache_pool.h @@ -27,20 +27,14 @@ #include "../policy/lru.h" #include "../pool_store.h" -namespace photon { - namespace fs { - class IFileSystem; - class IFile; - }; -}; // photon +#include namespace Cache { - class FileCachePool : public FileSystem::ICachePool { public: FileCachePool(photon::fs::IFileSystem *mediaFs, uint64_t capacityInGB, uint64_t periodInUs, - uint64_t diskAvailInBytes, uint64_t refillUnit, Fn_trans_func name_trans = nullptr); + uint64_t diskAvailInBytes, uint64_t refillUnit); ~FileCachePool(); static const uint64_t kDiskBlockSize = 512; // stat(2) @@ -52,10 +46,13 @@ class FileCachePool : public FileSystem::ICachePool { // pathname must begin with '/' FileSystem::ICacheStore *do_open(std::string_view pathname, int flags, mode_t mode) override; - int stat(FileSystem::CacheStat *stat, std::string_view pathname = std::string_view(nullptr, 0)) override; + int set_quota(std::string_view pathname, size_t quota) override; + int stat(FileSystem::CacheStat *stat, + std::string_view pathname = std::string_view(nullptr, 0)) override; int evict(std::string_view filename) override; int evict(size_t size = 0) override; + int rename(std::string_view oldname, std::string_view newname) override; struct LruEntry { LruEntry(uint32_t lruIt, int openCnt, uint64_t fileSize) @@ -111,7 +108,6 @@ class FileCachePool : public FileSystem::ICachePool { LRUContainer lru_; // filename -> lruEntry FileNameMap fileIndex_; - Fn_trans_func file_name_trans = &same_name_trans; }; -} // namespace Cache \ No newline at end of file +} // namespace Cache diff --git a/src/overlaybd/cache/full_file_cache/cache_store.cpp b/src/overlaybd/cache/full_file_cache/cache_store.cpp index 9fb8d46a..1b46de05 100644 --- a/src/overlaybd/cache/full_file_cache/cache_store.cpp +++ b/src/overlaybd/cache/full_file_cache/cache_store.cpp @@ -18,20 +18,23 @@ #include "sys/statvfs.h" #include #include -#include #include -#include +#include #include #include +#include #include "cache_pool.h" +using namespace FileSystem; +using namespace photon::fs; + namespace Cache { const uint64_t kDiskBlockSize = 512; // stat(2) constexpr int kFieExtentSize = 1000; const int kBlockSize = 4 * 1024; -FileCacheStore::FileCacheStore(FileSystem::ICachePool *cachePool, photon::fs::IFile *localFile, +FileCacheStore::FileCacheStore(FileSystem::ICachePool *cachePool, IFile *localFile, size_t refillUnit, FileIterator iterator) : cachePool_(static_cast(cachePool)), localFile_(localFile), refillUnit_(refillUnit), iterator_(iterator) { @@ -42,12 +45,14 @@ FileCacheStore::~FileCacheStore() { cachePool_->removeOpenFile(iterator_); } -ssize_t FileCacheStore::preadv(const struct iovec *iov, int iovcnt, off_t offset) { +ssize_t FileCacheStore::do_preadv2(const struct iovec *iov, int iovcnt, off_t offset, int flags) { + // TODO(suoshi.yf): maybe a new interface for updating lru is better for avoiding + // multiple cacheStore preadvs but cacheFile preadv only once ssize_t ret; cachePool_->updateLru(iterator_); auto lruEntry = static_cast(iterator_->second.get()); photon::scoped_rwlock rl(lruEntry->rw_lock_, photon::RLOCK); - SCOPE_AUDIT_THRESHOLD(10UL * 1000, "file:read", AU_FILEOP("", offset, ret)); + SCOPE_AUDIT_THRESHOLD(1UL * 1000, "file:read", AU_FILEOP("", offset, ret)); ret = localFile_->preadv(iov, iovcnt, offset); return ret; } @@ -61,7 +66,7 @@ ssize_t FileCacheStore::do_pwritev(const struct iovec *iov, int iovcnt, off_t of return ret; } -ssize_t FileCacheStore::pwritev(const struct iovec *iov, int iovcnt, off_t offset) { +ssize_t FileCacheStore::do_pwritev2(const struct iovec *iov, int iovcnt, off_t offset, int flags) { if (cacheIsFull()) { errno = ENOSPC; return -1; @@ -89,7 +94,7 @@ std::pair FileCacheStore::queryRefillRange(off_t offset, size_t s off_t alignLeft = align_down(offset, kBlockSize); off_t alignRight = align_up(offset + size, kBlockSize); ReadRequest request{alignLeft, static_cast(alignRight - alignLeft)}; - struct photon::fs::fiemap_t fie(request.offset, request.size); + struct fiemap_t fie(request.offset, request.size); fie.fm_mapped_extents = 0; if (request.size > 0) { // fiemap cannot handle size zero. @@ -112,7 +117,7 @@ std::pair FileCacheStore::queryRefillRange(off_t offset, size_t s uint64_t holeStart = request.offset; uint64_t holeEnd = request.offset + request.size; - for (auto i = fie.fm_mapped_extents - 1; i < fie.fm_mapped_extents; i--) { + for (ssize_t i = (ssize_t)(fie.fm_mapped_extents) - 1; i >= 0; i--) { auto &extent = fie.fm_extents[i]; if ((extent.fe_flags == FIEMAP_EXTENT_UNKNOWN) || (extent.fe_flags == FIEMAP_EXTENT_UNWRITTEN)) @@ -146,7 +151,12 @@ std::pair FileCacheStore::queryRefillRange(off_t offset, size_t s return std::make_pair(left, right - left); } -int FileCacheStore::stat(FileSystem::CacheStat *stat) { +int FileCacheStore::set_quota(size_t quota) { + errno = ENOSYS; + return -1; +} + +int FileCacheStore::stat(CacheStat *stat) { errno = ENOSYS; return -1; } @@ -161,7 +171,6 @@ int FileCacheStore::evict(off_t offset, size_t count) { #ifndef FALLOC_FL_PUNCH_HOLE #define FALLOC_FL_PUNCH_HOLE 0x02 /* de-allocates range */ #endif - ScopedRangeLock lock(rangeLock_, offset, count); int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE; return localFile_->fallocate(mode, offset, count); } @@ -175,8 +184,4 @@ bool FileCacheStore::cacheIsFull() { return cachePool_->isFull(); } -int FileCacheStore::ftruncate(off_t length) { - return localFile_->ftruncate(length); -} - } // namespace Cache diff --git a/src/overlaybd/cache/full_file_cache/cache_store.h b/src/overlaybd/cache/full_file_cache/cache_store.h index 86faf917..ae1ed5a4 100644 --- a/src/overlaybd/cache/full_file_cache/cache_store.h +++ b/src/overlaybd/cache/full_file_cache/cache_store.h @@ -20,13 +20,6 @@ #include #include "cache_pool.h" -namespace photon { - namespace fs { - class IFileSystem; - struct fiemap; - } -} // namespace FileSystem - namespace Cache { class FileCachePool; @@ -34,17 +27,17 @@ class FileCachePool; class FileCacheStore : public FileSystem::ICacheStore { public: typedef FileCachePool::FileNameMap::iterator FileIterator; - FileCacheStore(FileSystem::ICachePool *cachePool, photon::fs::IFile *localFile, size_t refillUnit, - FileIterator iterator); + FileCacheStore(FileSystem::ICachePool *cachePool, photon::fs::IFile *localFile, + size_t refillUnit, FileIterator iterator); ~FileCacheStore(); - ssize_t preadv(const struct iovec *iov, int iovcnt, off_t offset) override; + ssize_t do_preadv2(const struct iovec *iov, int iovcnt, off_t offset, int flags) override; - ssize_t pwritev(const struct iovec *iov, int iovcnt, off_t offset) override; + ssize_t do_pwritev2(const struct iovec *iov, int iovcnt, off_t offset, int flags) override; + int set_quota(size_t quota) override; int stat(FileSystem::CacheStat *stat) override; int evict(off_t offset, size_t count = -1) override; - int ftruncate(off_t length) override; std::pair queryRefillRange(off_t offset, size_t size) override; @@ -65,10 +58,10 @@ class FileCacheStore : public FileSystem::ICacheStore { std::pair getLastMergedExtents(struct fiemap *fie); std::pair getHoleFromCacheHitResult(off_t offset, size_t alignSize, - struct photon::fs::fiemap *fie); + struct fiemap *fie); - FileCachePool *cachePool_; // owned by extern class - photon::fs::IFile *localFile_; // owned by current class + FileCachePool *cachePool_; // owned by extern class + photon::fs::IFile *localFile_; // owned by current class size_t refillUnit_; FileIterator iterator_; RangeLock rangeLock_; diff --git a/src/overlaybd/cache/full_file_cache/test/cache_test.cpp b/src/overlaybd/cache/full_file_cache/test/cache_test.cpp deleted file mode 100644 index bf9840f6..00000000 --- a/src/overlaybd/cache/full_file_cache/test/cache_test.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/* - Copyright The Overlaybd Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "../../cache.h" -#include "random_generator.h" - -namespace Cache { - -using namespace FileSystem; -using namespace photon::fs; - -// Cleanup and recreate the test dir -inline void SetupTestDir(const std::string &dir) { - std::string cmd = std::string("rm -r ") + dir; - system(cmd.c_str()); - cmd = std::string("mkdir -p ") + dir; - system(cmd.c_str()); -} - -void commonTest(bool cacheIsFull, bool enableDirControl, bool dirFull) { - std::string prefix = ""; - const size_t dirQuota = 32ul * 1024 * 1024; - const uint64_t refillSize = 1024 * 1024; - - std::string root("/tmp/obdcache/cache_test/"); - SetupTestDir(root); - - std::string subDir = prefix + "dir/dir/"; - SetupTestDir(root + subDir); - std::system(std::string("touch " + root + subDir + "testFile").c_str()); - - struct stat st; - auto ok = ::stat(std::string(root + subDir + "testFile").c_str(), &st); - EXPECT_EQ(0, ok); - - std::string srcRoot("/tmp/obdcache/src_test/"); - SetupTestDir(srcRoot); - auto srcFs = new_localfs_adaptor(srcRoot.c_str(), ioengine_psync); - - auto mediaFs = new_localfs_adaptor(root.c_str(), ioengine_libaio); - auto alignFs = new_aligned_fs_adaptor(mediaFs, 4 * 1024, true, true); - auto cacheAllocator = new AlignedAlloc(4 * 1024); - auto roCachedFs = new_full_file_cached_fs(srcFs, alignFs, refillSize, cacheIsFull ? 0 : 512, - 1000 * 1000 * 1, 128ul * 1024 * 1024, cacheAllocator); - auto cachePool = roCachedFs->get_pool(); - - SetupTestDir(srcRoot + prefix + "testDir"); - auto srcFile = srcFs->open(std::string(prefix + "/testDir/file_1").c_str(), - O_RDWR | O_CREAT | O_TRUNC, 0644); - - UniformCharRandomGen gen(0, 255); - off_t offset = 0; - uint32_t kPageSize = 4 * 1024; - uint32_t kFileSize = kPageSize * 16384; // 64MB - uint32_t kPageCount = kFileSize / kPageSize; - for (uint32_t i = 0; i < kPageCount; ++i) { - std::vector data; - for (uint32_t j = 0; j < kPageSize; ++j) { - data.push_back(gen.next()); - } - srcFile->pwrite(data.data(), data.size(), offset); - offset += kPageSize; - } - - // write some unaligned - off_t lastOffset = offset; - off_t unAlignedLen = 750; - { - std::vector data; - for (uint32_t j = 0; j < kPageSize; ++j) { - data.push_back(gen.next()); - } - srcFile->pwrite(data.data(), unAlignedLen, offset); - } - - auto cachedFile = static_cast( - roCachedFs->open(std::string(prefix + "/testDir/file_1").c_str(), 0, 0644)); - - // test unaligned block - { - void *buf = malloc(kPageSize); - auto ret = cachedFile->pread(buf, kPageSize, lastOffset); - - std::vector src; - src.reserve(kPageSize); - auto retSrc = srcFile->pread(src.data(), kPageSize, lastOffset); - - EXPECT_EQ(0, std::memcmp(buf, src.data(), unAlignedLen)); - EXPECT_EQ(unAlignedLen, retSrc); - EXPECT_EQ(unAlignedLen, ret); - - LOG_INFO("read again"); - - // read again - ret = cachedFile->pread(buf, kPageSize, lastOffset); - EXPECT_EQ(unAlignedLen, ret); - - free(buf); - } - - // test aligned and unaligned block - { - void *buf = malloc(kPageSize * 4); - auto ret = cachedFile->pread(buf, kPageSize * 4, lastOffset - 2 * kPageSize); - - std::vector src; - src.reserve(kPageSize * 4); - auto retSrc = srcFile->pread(src.data(), kPageSize * 4, lastOffset - 2 * kPageSize); - - EXPECT_EQ(0, std::memcmp(buf, src.data(), 2 * kPageSize + unAlignedLen)); - EXPECT_EQ(2 * kPageSize + unAlignedLen, retSrc); - EXPECT_EQ(2 * kPageSize + unAlignedLen, ret); - - LOG_INFO("read again"); - - // read again - ret = cachedFile->pread(buf, kPageSize * 4, lastOffset - 2 * kPageSize); - EXPECT_EQ(2 * kPageSize + unAlignedLen, ret); - - free(buf); - } - - std::vector readBuf; - readBuf.reserve(kPageSize); - std::vector readSrcBuf; - readSrcBuf.reserve(kPageSize); - for (int i = 0; i != 5; ++i) { - EXPECT_EQ(kPageSize, cachedFile->read(readBuf.data(), kPageSize)); - srcFile->read(readSrcBuf.data(), kPageSize); - EXPECT_EQ(0, std::memcmp(readBuf.data(), readSrcBuf.data(), kPageSize)); - } - - // test refill(3) - if (!cacheIsFull) { - auto inSrcFile = cachedFile->get_source(); - cachedFile->set_source(nullptr); - struct stat stat; - inSrcFile->fstat(&stat); - cachedFile->ftruncate(stat.st_size); - void *buf = malloc(kPageSize * 3); - DEFER(free(buf)); - std::vector src; - src.reserve(kPageSize * 3); - EXPECT_EQ(kPageSize, srcFile->pread(src.data(), kPageSize, 0)); - memcpy(buf, src.data(), kPageSize); - - EXPECT_EQ(kPageSize, cachedFile->refill(buf, kPageSize, 0)); - - memset(buf, 0, kPageSize); - EXPECT_EQ(kPageSize, cachedFile->pread(buf, kPageSize, 0)); - EXPECT_EQ(0, memcmp(buf, src.data(), kPageSize)); - - struct stat st1; - ::stat(std::string(root + prefix + "/testDir/file_1").c_str(), &st1); - EXPECT_EQ(0, cachedFile->evict(0, kPageSize)); - struct stat st2; - ::stat(std::string(root + prefix + "/testDir/file_1").c_str(), &st2); - EXPECT_EQ(kPageSize, st1.st_blocks * 512 - st2.st_blocks * 512); - - // test refill last block - src.clear(); - EXPECT_EQ(kPageSize + unAlignedLen, - srcFile->pread(src.data(), kPageSize * 3, lastOffset - kPageSize)); - memcpy(buf, src.data(), kPageSize * 3); - EXPECT_EQ(kPageSize + unAlignedLen, - cachedFile->refill(buf, kPageSize * 3, lastOffset - kPageSize)); - memset(buf, 0, kPageSize * 3); - EXPECT_EQ(kPageSize + unAlignedLen, - cachedFile->pread(buf, kPageSize * 3, lastOffset - kPageSize)); - EXPECT_EQ(0, memcmp(buf, src.data(), kPageSize + unAlignedLen)); - - cachedFile->set_source(inSrcFile); - } - - // test refill(2) - if (!cacheIsFull) { - auto inSrcFile = cachedFile->get_source(); - - void *buf = malloc(kPageSize * 2); - DEFER(free(buf)); - EXPECT_EQ(2 * kPageSize, cachedFile->refill(kPageSize, 2 * kPageSize)); - - cachedFile->set_source(nullptr); - EXPECT_EQ(2 * kPageSize, cachedFile->pread(buf, 2 * kPageSize, kPageSize)); - std::vector src; - src.reserve(kPageSize * 2); - EXPECT_EQ(kPageSize * 2, srcFile->pread(src.data(), 2 * kPageSize, kPageSize)); - EXPECT_EQ(0, memcmp(buf, src.data(), 2 * kPageSize)); - cachedFile->set_source(inSrcFile); - - // prefetch more than 16MB - EXPECT_EQ(5000 * kPageSize + kPageSize, cachedFile->prefetch(234, 5000 * kPageSize)); - // prefetch tail - EXPECT_EQ(kPageSize + unAlignedLen, - cachedFile->prefetch(lastOffset - kPageSize, 5000 * kPageSize)); - } - - if (dirFull) { - CacheStat cstat = {}; - EXPECT_EQ(0, cachePool->stat(&cstat, prefix)); - EXPECT_EQ(dirQuota / refillSize, cstat.total_size); - } - - // test aligned section - UniformInt32RandomGen genOffset(0, (kPageCount + 1) * kPageSize); - UniformInt32RandomGen genSize(0, 8 * kPageSize); - struct stat srcSt = {}; - srcFile->fstat(&srcSt); - for (int i = 0; i != 10000; ++i) { - auto tmpOffset = genOffset.next(); - auto size = genSize.next(); - - if (tmpOffset >= srcSt.st_size) { - size = 0; - } else { - size = tmpOffset + size > srcSt.st_size ? srcSt.st_size - tmpOffset : size; - } - void *buf = malloc(size); - auto ret = cachedFile->pread(buf, size, tmpOffset); - - std::vector src; - src.reserve(size); - auto retSrc = srcFile->pread(src.data(), size, tmpOffset); - - EXPECT_EQ(0, std::memcmp(buf, src.data(), size)); - EXPECT_EQ(size, retSrc); - EXPECT_EQ(size, ret); - free(buf); - } - srcFile->close(); - - photon::thread_usleep(1000 * 1000ull); - ok = ::stat(std::string(root + subDir + "testFile").c_str(), &st); - EXPECT_EQ(cacheIsFull || dirFull ? -1 : 0, ok); - - delete cachedFile; - - // test smaller file - { - auto smallFile = srcFs->open(std::string(prefix + "/testDir/small").c_str(), - O_RDWR | O_CREAT | O_TRUNC, 0644); - DEFER(delete smallFile); - int smallSize = 102; - std::vector smallData; - for (int i = 0; i != smallSize; ++i) { - smallData.push_back(gen.next()); - } - EXPECT_EQ(smallSize, smallFile->pwrite(smallData.data(), smallData.size(), 0)); - - auto smallCache = static_cast( - roCachedFs->open(std::string(prefix + "/testDir/small").c_str(), 0, 0644)); - DEFER(delete smallCache); - - void *sBuffer = malloc(kPageSize); - DEFER(free(sBuffer)); - EXPECT_EQ(smallSize, smallCache->pread(sBuffer, kPageSize, 0)); - EXPECT_EQ(0, std::memcmp(sBuffer, smallData.data(), smallSize)); - - memset(sBuffer, 0, kPageSize); - EXPECT_EQ(smallSize, smallCache->pread(sBuffer, kPageSize, 0)); - EXPECT_EQ(0, std::memcmp(sBuffer, smallData.data(), smallSize)); - - smallFile->close(); - } - - // test refill - { - auto refillFile = srcFs->open(std::string(prefix + "/testDir/refill").c_str(), - O_RDWR | O_CREAT | O_TRUNC, 0644); - DEFER(delete refillFile); - int refillSize = 4097; - std::vector refillData; - for (int i = 0; i != refillSize; ++i) { - refillData.push_back(gen.next()); - } - EXPECT_EQ(refillSize, refillFile->pwrite(refillData.data(), refillData.size(), 0)); - - auto refillCache = static_cast( - roCachedFs->open(std::string(prefix + "/testDir/refill").c_str(), 0, 0644)); - DEFER(delete refillCache); - - void *sBuffer = malloc(kPageSize * 2); - DEFER(free(sBuffer)); - memset(sBuffer, 0, kPageSize * 2); - EXPECT_EQ(kPageSize, refillCache->pread(sBuffer, kPageSize, 0)); - EXPECT_EQ(0, std::memcmp(sBuffer, refillData.data(), kPageSize)); - - memset(sBuffer, 0, kPageSize * 2); - EXPECT_EQ(refillSize, refillCache->pread(sBuffer, kPageSize * 2, 0)); - EXPECT_EQ(0, std::memcmp(sBuffer, refillData.data(), refillSize)); - - refillFile->close(); - } - - delete srcFs; - delete roCachedFs; -} - -TEST(RoCachedFs, Basic) { - commonTest(false, false, false); -} - -TEST(RoCachedFs, BasicCacheFull) { - commonTest(true, false, false); -} - -TEST(RoCachedFs, CacheWithOutSrcFile) { - std::string root("/tmp/obdcache/cache_test_no_src/"); - SetupTestDir(root); - - auto mediaFs = new_localfs_adaptor(root.c_str(), ioengine_libaio); - auto alignFs = new_aligned_fs_adaptor(mediaFs, 4 * 1024, true, true); - auto cacheAllocator = new AlignedAlloc(4 * 1024); - DEFER(delete cacheAllocator); - auto roCachedFs = new_full_file_cached_fs(nullptr, alignFs, 1024 * 1024, 512, 1000 * 1000 * 1, - 128ul * 1024 * 1024, cacheAllocator); - DEFER(delete roCachedFs); - auto cachedFile = static_cast( - roCachedFs->open(std::string("/testDir/file_1").c_str(), 0, 0644)); - DEFER(delete cachedFile); - - cachedFile->ftruncate(1024 * 1024); - std::vector buf; - int len = 8 * 1024; - buf.reserve(len); - EXPECT_EQ(len, cachedFile->pwrite(buf.data(), len, 4 * 1024)); - EXPECT_EQ(len / 2, cachedFile->pread(buf.data(), 4 * 1024, 4 * 1024)); - EXPECT_EQ(-1, cachedFile->pread(buf.data(), len, 0)); - - auto writeFile = static_cast( - roCachedFs->open(std::string("/testDir/file_2").c_str(), 0, 0644)); - DEFER(delete writeFile); - writeFile->ftruncate(1024 * 1024); - buf.assign(len, 'a'); - EXPECT_EQ(len, writeFile->write(buf.data(), len)); - EXPECT_EQ(len, writeFile->write(buf.data(), len)); - std::vector res; - res.reserve(len); - EXPECT_EQ(len, writeFile->pread(res.data(), len, 0)); - EXPECT_EQ(0, std::memcmp(buf.data(), res.data(), len)); - res.assign(len, '0'); - EXPECT_EQ(len, writeFile->pread(res.data(), len, len)); - EXPECT_EQ(0, std::memcmp(buf.data(), res.data(), len)); - EXPECT_EQ(-1, writeFile->pread(res.data(), len, len * 2)); -} - -} // namespace Cache - -int main(int argc, char **argv) { - log_output_level = 0; - ::testing::InitGoogleTest(&argc, argv); - - photon::init(photon::INIT_EVENT_DEFAULT, photon::INIT_IO_DEFAULT); - int ret = RUN_ALL_TESTS(); - return ret; -} diff --git a/src/overlaybd/cache/gzip_cache/cached_fs.cpp b/src/overlaybd/cache/gzip_cache/cached_fs.cpp index 4b09e10e..07181c3f 100644 --- a/src/overlaybd/cache/gzip_cache/cached_fs.cpp +++ b/src/overlaybd/cache/gzip_cache/cached_fs.cpp @@ -15,7 +15,7 @@ */ #include "cached_fs.h" #include "../full_file_cache/cache_pool.h" -#include "../frontend/cached_file.h" +#include "../cache.h" namespace Cache { @@ -39,7 +39,10 @@ class GzipCachedFsImpl : public GzipCachedFs { delete file; LOG_ERRNO_RETURN(0, nullptr, "file cache pool open file failed, name : `", file_name); } - auto ret = Cache::new_cached_file(file, cache_store, page_size_, refill_unit_, io_alloc_, nullptr); + cache_store->set_src_file(file); + cache_store->set_allocator(io_alloc_); + cache_store->set_page_size(page_size_); + auto ret = FileSystem::new_cached_file(cache_store, page_size_, nullptr); if (ret == nullptr) { // if create file is failed // file and cache_store must be release, or will leak delete file; diff --git a/src/overlaybd/cache/ocf_cache/test/ocf_perf_test.cpp b/src/overlaybd/cache/ocf_cache/test/ocf_perf_test.cpp index e3a6fb8f..18b4a082 100644 --- a/src/overlaybd/cache/ocf_cache/test/ocf_perf_test.cpp +++ b/src/overlaybd/cache/ocf_cache/test/ocf_perf_test.cpp @@ -224,7 +224,7 @@ static int single_file_file_cache(IOAlloc *io_alloc, photon::fs::IFileSystem *sr } auto cached_fs = FileSystem::new_full_file_cached_fs( src_fs, media_fs, FLAGS_page_size, FLAGS_media_file_size_gb, 1000 * 1000, - 2UL * FLAGS_media_file_size_gb * 1024 * 1024 * 1024, io_alloc); + 2UL * FLAGS_media_file_size_gb * 1024 * 1024 * 1024, io_alloc, 0); if (cached_fs == nullptr) { LOG_ERROR_RETURN(0, -1, "new_ocf_cached_fs error"); } diff --git a/src/overlaybd/cache/policy/lru.h b/src/overlaybd/cache/policy/lru.h index 5210bff3..48af6a40 100644 --- a/src/overlaybd/cache/policy/lru.h +++ b/src/overlaybd/cache/policy/lru.h @@ -1,3 +1,4 @@ + /* Copyright The Overlaybd Authors @@ -13,15 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ - #pragma once +#include #include #include -#include -#include #include -#include #include +#include +#include namespace FileSystem { // This is a generic LRU container, highly optimized for both speed and memory. diff --git a/src/overlaybd/cache/pool_store.h b/src/overlaybd/cache/pool_store.h index 8e6d028d..3aea97e8 100644 --- a/src/overlaybd/cache/pool_store.h +++ b/src/overlaybd/cache/pool_store.h @@ -14,33 +14,76 @@ limitations under the License. */ #pragma once +#include #include #include #include #include #include +#include #include -#include #include +#include +#include +#include + +enum ListType : int { + LIST_ALL = 0, + LIST_FILES = 1, + LIST_DIRS = 2, +}; -struct iovector; +// reset cache flags +enum ResetType : int { + RST_ALL = 0x0, // reset all cache's data, include file meta + RST_MEMORY = 0x1, // reset memory cache's data + RST_DISK = 0x2, // reset disk cache's data + RST_UPPER = 0x10, // reset upper layer cache's data + RST_LOWER = 0x20, // reset lower layer cache's data +}; -typedef std::string (*Fn_trans_func)(std::string_view name); +// resize cache flags +enum ResizeType : int { + RSZ_MEMORY = 0x1, // resize memory cache's capacity + RSZ_DISK = 0x2, // resize disk cache's capacity + RSZ_UPPER = 0x10, // resize upper layer cache's capacity + RSZ_LOWER = 0x20, // resize lower layer cache's capacity +}; namespace FileSystem { +// `CacheFnTransFunc` use to transform the filename in the cached store. +// `std::string_view` is the filename before transformation (as src_name). +// `char *` is the transformed filename (as store_key). +// `size_t` is the max buffer length of store_key. +// If transform occurs an error (such as result length more than buffer size) +// or there is not necessary to transform, this function returns 0, +// otherwise, it returns string length after transformation. +using CacheFnTransFunc = Delegate; class ICacheStore; struct CacheStat { uint32_t struct_size = sizeof(CacheStat); - uint32_t refill_unit; // in bytes - uint32_t total_size; // in refill_unit - uint32_t used_size; // in refill_unit + uint32_t refill_unit; // in bytes + uint32_t total_size; // in refill_unit + uint32_t used_size; // in refill_unit + uint64_t evict_other; // in bytes, initialized to -1UL means reset + uint64_t evict_global; // in bytes, initialized to -1UL means reset + uint64_t evict_user; // in bytes, initialized to -1UL means reset }; class ICachePool : public Object { public: - virtual ICacheStore *open(std::string_view filename, int flags, mode_t mode); + ICachePool(uint32_t pool_size = 128, uint32_t max_refilling = 128, + uint32_t refilling_threshold = -1U); + ~ICachePool(); + + ICacheStore *open(std::string_view filename, int flags, mode_t mode); + + // set quota to a dir or a file + virtual int set_quota(std::string_view pathname, size_t quota) = 0; // if pathname is {nullptr, 0} or "/", returns the overall stat + // if pathname is a dir, and it has quota set, returns its quota usage + // if pathname is a file, returns the file's stat virtual int stat(CacheStat *stat, std::string_view pathname = std::string_view(nullptr, 0)) = 0; // force to evict specified files(s) @@ -52,120 +95,196 @@ class ICachePool : public Object { int store_release(ICacheStore *store); + void stores_clear(); + + void set_trans_func(CacheFnTransFunc fn_trans_func); + virtual ICacheStore *do_open(std::string_view filename, int flags, mode_t mode) = 0; - ICacheStore *find_store_map(std::string_view pathname); + virtual int rename(std::string_view oldname, std::string_view newname) = 0; - static std::string same_name_trans(std::string_view filename) { return std::string(filename); } -protected: - unordered_map_string_key m_stores; -}; + virtual ssize_t list(const char *dirname, ListType type, const struct iovec *iov, int iovcnt, + const char *marker, uint32_t count) { + errno = ENOSYS; + return -1; + } -class ICacheStore : public Object { -public: - struct try_preadv_result { - size_t iov_sum; // sum of the iovec[] - size_t refill_size; // size in bytes to refill, 0 means cache hit - union { - off_t refill_offset; // the offset to fill, if not hit - ssize_t size; // the return value of preadv(), if hit - }; - }; + UNIMPLEMENTED_POINTER(void *get_underlay_object(int i = 0)); - // either override try_preadv() or try_preadv_mutable() - virtual try_preadv_result try_preadv(const struct iovec *iov, int iovcnt, off_t offset); - virtual try_preadv_result try_preadv_mutable(struct iovec *iov, int iovcnt, off_t offset); + // reset cache's data + virtual int reset(int flags = 0) { + errno = ENOSYS; + return -1; + } - // either override preadv() or preadv_mutable() - virtual ssize_t preadv(const struct iovec *iov, int iovcnt, off_t offset); - virtual ssize_t preadv_mutable(struct iovec *iov, int iovcnt, off_t offset); + // resize cache's capacity + virtual int resize(size_t n, int flags = 0) { + errno = ENOSYS; + return -1; + } - // either override pwritev() or pwritev_mutable() - virtual ssize_t pwritev(const struct iovec *iov, int iovcnt, off_t offset); - virtual ssize_t pwritev_mutable(struct iovec *iov, int iovcnt, off_t offset); +protected: + void *m_stores; + CacheFnTransFunc fn_trans_func; + void *m_thread_pool = nullptr; + void *m_vcpu = nullptr; // vcpu where m_therad_pool is created + std::atomic m_refilling{0}; + const uint32_t m_max_refilling = 128; + const uint32_t m_refilling_threshold = -1U; + friend class ICacheStore; +}; +class ICacheStore : public Object { +public: + virtual ~ICacheStore(); + // public interface for reading cache file store, dealing with cache-miss + // and deduplication of concurrent reading of source file. + ssize_t preadv2(const struct iovec *iov, int iovcnt, off_t offset, int flags); + ssize_t pwritev2(const struct iovec *iov, int iovcnt, off_t offset, int flags); + ssize_t try_refill_range(off_t offset, size_t count); + + virtual int set_quota(size_t quota) = 0; virtual int stat(CacheStat *stat) = 0; virtual int evict(off_t offset, size_t count = -1) = 0; - virtual int ftruncate(off_t length) = 0; - - void release() { - ref_count--; - if (ref_count == 0) { - pool_->store_release(this); - try_destruct(); - } + // offset + size must <= origin file size + virtual std::pair queryRefillRange(off_t offset, size_t size) = 0; + virtual int fstat(struct stat *buf) = 0; + virtual int set_crc(uint32_t crc) { + errno = ENOSYS; + return -1; } - void add_ref() { - ref_count++; + virtual int get_crc(uint32_t *crc) { + errno = ENOSYS; + return -1; } - - uint32_t get_ref_count() { - return ref_count; + virtual uint64_t get_handle() { + return -1UL; } - bool try_destruct() { - if (ref_count == 0) { - delete this; - return true; - } - return false; + void release() { + auto ref = ref_.fetch_sub(1, std::memory_order_relaxed); + if (ref == 1 && pool_) { + pool_->store_release(this); + } else if (ref == 0) + delete this; // call do_open directly } ssize_t pread(void *buf, size_t count, off_t offset) { struct iovec iov { buf, count }; - return preadv_mutable(&iov, 1, offset); + return do_preadv2(&iov, 1, offset, 0); } + ssize_t pwrite(const void *buf, size_t count, off_t offset) { struct iovec iov { (void *)buf, count }; - return pwritev_mutable(&iov, 1, offset); + return do_pwritev2(&iov, 1, offset, 0); } - // offset + size must <= origin file size - virtual std::pair queryRefillRange(off_t offset, size_t size) = 0; - - virtual int fstat(struct stat *buf) = 0; - - virtual std::string_view get_pathname() { - return f_name_; - }; - - virtual void set_pathname(std::string_view pathname) { - f_name_ = pathname; + std::string_view get_src_name() { + return src_name_; } - - virtual uint32_t get_refcount() { - return ref_count; + void set_src_name(std::string_view pathname) { + src_name_ = pathname.data(); } - - virtual void set_pool(ICachePool *pool) { + std::string_view get_store_key() { + return store_key_; + } + void set_store_key(std::string_view pathname) { + store_key_ = pathname; + } + void set_pool(ICachePool *pool) { pool_ = pool; } + void set_cached_size(off_t cached_size); + off_t get_actual_size() { + return actual_size_; + } + void set_actual_size(off_t actual_size) { + actual_size_ = actual_size; + } + void set_open_flags(int open_flags) { + open_flags_ = open_flags; + } + int open_src_file(photon::fs::IFile **src_file = nullptr); + void set_src_file(photon::fs::IFile *src_file) { + src_file_ = src_file; + } + photon::fs::IFileSystem *get_src_fs() { + return src_fs_; + } + void set_src_fs(photon::fs::IFileSystem *src_fs) { + src_fs_ = src_fs; + } + size_t get_page_size() { + return page_size_; + } + void set_page_size(size_t page_size) { + page_size_ = page_size; + } + IOAlloc *get_allocator() { + return allocator_; + } + void set_allocator(IOAlloc *allocator) { + allocator_ = allocator; + } + + struct try_preadv_result { + size_t iov_sum; // sum of the iovec[] + size_t refill_size; // size in bytes to refill, 0 means cache hit + union { + off_t refill_offset; // the offset to fill, if not hit + ssize_t size; // the return value of preadv(), if hit + }; + }; + virtual try_preadv_result try_preadv2(const struct iovec *iov, int iovcnt, off_t offset, + int flags); + virtual ssize_t do_preadv2(const struct iovec *iov, int iovcnt, off_t offset, int flags); + virtual ssize_t do_preadv2_mutable(struct iovec *iov, int iovcnt, off_t offset, int flags); + virtual ssize_t do_pwritev2(const struct iovec *iov, int iovcnt, off_t offset, int flags); + virtual ssize_t do_pwritev2_mutable(struct iovec *iov, int iovcnt, off_t offset, int flags); + +private: + ssize_t pwritev2_extend(const struct iovec *iov, int iovcnt, off_t offset, int flags); + ssize_t do_refill_range(uint64_t refill_off, uint64_t refill_size, size_t count, + IOVector *input = nullptr, off_t offset = 0, int flags = 0); + int tryget_size(); + static void *async_refill(void *args); protected: - uint32_t ref_count = 0; // store's referring count - std::string_view f_name_; - ICachePool *pool_; - ~ICacheStore(){}; + std::string src_name_; + std::string_view store_key_; + ICachePool *pool_ = nullptr; + off_t cached_size_ = 0; + off_t actual_size_ = 0; + int open_flags_ = 0; + std::atomic ref_{0}; + photon::fs::IFile *src_file_ = nullptr; + photon::fs::IFileSystem *src_fs_ = nullptr; + size_t page_size_ = 4096; + IOAlloc *allocator_ = nullptr; + RangeLock range_lock_; + photon::mutex open_lock_; + friend class ICachePool; }; class IMemCacheStore : public ICacheStore { public: - // Get the internal buffer for the specified LBA range (usually aligned), - // which will remain valid for user until released by unpin_buffer(). - // Will allocate pages for missed ranges. - // Will refill / fetch / load data from source if `refill`. - // Concurrent R/W to a same range are guaranteed to work, but considered - // a race-condition and the result is undefiend. - // returns # of bytes actually got, or <0 for failures - virtual ssize_t pin_buffer(off_t offset, size_t count, /*OUT*/ iovector *iov) = 0; - - // Release buffers got from pin_buffer(), - // and the buffer is no longer valid for user. - // return 0 for success, < 0 for failures - virtual int unpin_buffer(off_t offset, size_t count) = 0; + virtual ssize_t pin_buffer(off_t offset, size_t count, int flags, /*OUT*/ iovector *iov, + void **pin_result) = 0; + + virtual int unpin_buffer(void *pin_result) = 0; +}; + +class IMemCachePool : public ICachePool { +public: + using ICachePool::ICachePool; + + virtual ssize_t pin_buffer(uint64_t handle, off_t offset, size_t count, int flags, + /*OUT*/ iovector *iov, void **pin_result) = 0; + + virtual int unpin_buffer(void *pin_result) = 0; }; } // namespace FileSystem diff --git a/src/overlaybd/cache/store.cpp b/src/overlaybd/cache/store.cpp new file mode 100644 index 00000000..9ae2ba28 --- /dev/null +++ b/src/overlaybd/cache/store.cpp @@ -0,0 +1,427 @@ +/* + Copyright The Overlaybd Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#include "pool_store.h" +#include "cache.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace FileSystem; +using namespace photon::fs; + +namespace FileSystem { + +static const uint32_t MAX_REFILLING = 128; + +ICacheStore::~ICacheStore() { + delete src_file_; +} + +ssize_t ICacheStore::preadv2(const struct iovec *iov, int iovcnt, off_t offset, int flags) { + if (offset < 0) + LOG_ERROR_RETURN(EINVAL, -1, "offset is invalid, offset : `", offset); + iovector_view view(const_cast(iov), iovcnt); + size_t iov_size = view.sum(); + if (0u == iov_size) + return 0; + if (offset >= actual_size_ || offset + static_cast(iov_size) > actual_size_) { + if (tryget_size() != 0) { + LOG_ERROR_RETURN(0, -1, "try get size failed, actual_size_ : `, offset : `, count : `", + actual_size_, offset, iov_size); + } + } + + if (offset >= actual_size_) + return 0; + IOVector input(iov, iovcnt); + if (offset + static_cast(iov_size) > actual_size_) { + input.extract_back(offset + static_cast(iov_size) - actual_size_); + iov_size = actual_size_ - offset; + } + + if ((flags & RW_V2_CACHE_ONLY) || (open_flags_ & O_CACHE_ONLY)) { + auto tr = try_preadv2(input.iovec(), input.iovcnt(), offset, flags); + if (tr.refill_size == 0 && tr.size >= 0) { + return tr.size; + } else { + return -1; + } + } + +again: + auto tr = try_preadv2(input.iovec(), input.iovcnt(), offset, flags); + if (tr.refill_size == 0 && tr.size >= 0) + return tr.size; + // open src file only when cache miss + if (open_src_file() != 0 || !src_file_) { + LOG_ERROR_RETURN(0, -1, "cache preadv2 failed, offset : `, count : `, flags : `", offset, + iov_size, flags); + } + + if (tr.refill_offset < 0) { + SCOPE_AUDIT("download", AU_FILEOP(get_src_name(), offset, tr.size)); + tr.size = src_file_->preadv2(input.iovec(), input.iovcnt(), offset, flags); + return tr.size; + } + + ssize_t ret = + do_refill_range(tr.refill_offset, tr.refill_size, iov_size, &input, offset, flags); + if (ret == -EAGAIN) + goto again; + return ret; +} + +ssize_t ICacheStore::pwritev2(const struct iovec *iov, int iovcnt, off_t offset, int flags) { + if (open_flags_ & (O_WRITE_THROUGH | O_CACHE_ONLY | O_WRITE_BACK)) { + return pwritev2_extend(iov, iovcnt, offset, flags); + } + + iovector_view view(const_cast(iov), iovcnt); + size_t size = view.sum(); + if (offset >= actual_size_ || offset + static_cast(size) > actual_size_) { + if (tryget_size() < 0) { + LOG_ERROR_RETURN(0, -1, "try get size failed, actual_size_ : `, offset : `, count : `", + actual_size_, offset, size); + } + } + + if (offset >= actual_size_) + return 0; + if (offset % page_size_ != 0 || + (size % page_size_ != 0 && offset + static_cast(size) < actual_size_)) { + LOG_ERROR_RETURN(EINVAL, -1, "size or offset is not aligned to `, size : `, offset : `", + page_size_, size, offset); + } + + if (offset + static_cast(size) <= actual_size_) { + return do_pwritev2(iov, iovcnt, offset, flags); + } + + IOVector io_vector(iov, iovcnt); + if (offset + static_cast(size) > actual_size_) { + auto ret = io_vector.extract_back(size - (actual_size_ - offset)); + if (ret != size - (actual_size_ - offset)) + LOG_ERRNO_RETURN(EINVAL, -1, "extract failed, extractSize : `, expected : ", ret, + size - (actual_size_ - offset)) + } + + auto write = do_pwritev2(io_vector.iovec(), io_vector.iovcnt(), offset, flags); + if (write != static_cast(io_vector.sum())) { + if (ENOSPC != errno) + LOG_ERROR( + "cache file write failed : `, error : `, actual_size_ : `, offset : `, sum : `", + write, ERRNO(errno), actual_size_, offset, io_vector.sum()); + } + + return write; +} + +ssize_t ICacheStore::try_refill_range(off_t offset, size_t count) { + if (offset >= actual_size_ || offset + static_cast(count) > actual_size_) { + if (tryget_size() != 0) { + LOG_ERROR_RETURN(0, -1, "try get size failed, actual_size_ : `, offset : `, count : `", + actual_size_, offset, count); + } + } + + if (offset >= actual_size_) + return 0; + if (offset + static_cast(count) > actual_size_) { + count = actual_size_ - offset; + } + +again: + auto qres = queryRefillRange(offset, count); + if (qres.first < 0) + return -1; + if (qres.second == 0) + return static_cast(count); + // open src file only when cache miss + if (open_src_file() != 0 || !src_file_) { + LOG_ERROR_RETURN(0, -1, + "try refill_range failed due to null src file, offset : `, count : `", + offset, count); + } + + ssize_t ret = do_refill_range(qres.first, qres.second, count); + if (ret == -EAGAIN) + goto again; + return ret; +} + +struct RefillContext { + ICacheStore *store; + IOVector buffer; + uint64_t refill_off; + uint64_t refill_size; + int flags; +}; + +void *ICacheStore::async_refill(void *args) { + auto ctx = (RefillContext *)args; + auto write = ctx->store->do_pwritev2(ctx->buffer.iovec(), ctx->buffer.iovcnt(), ctx->refill_off, + ctx->flags); + if (write != static_cast(ctx->refill_size)) { + if (ENOSPC != errno) + LOG_ERROR( + "cache file write failed : `, error : `, actual_size_ : `, offset : `, sum : `", + write, ERRNO(errno), ctx->store->actual_size_, ctx->refill_off, ctx->buffer.sum()); + } + + ctx->store->pool_->m_refilling.fetch_sub(1, std::memory_order_relaxed); + ctx->store->range_lock_.unlock(ctx->refill_off, ctx->refill_size); + ctx->store->release(); + photon::thread_migrate(photon::CURRENT, + static_cast(ctx->store->pool_->m_vcpu)); + delete ctx; + return nullptr; +} + +ssize_t ICacheStore::do_refill_range(uint64_t refill_off, uint64_t refill_size, size_t count, + IOVector *input, off_t offset, int flags) { + ssize_t ret = 0; + if (input && pool_ && + pool_->m_refilling.load(std::memory_order_relaxed) > pool_->m_refilling_threshold) { + SCOPE_AUDIT("download", AU_FILEOP(get_src_name(), offset, ret)); + ret = src_file_->preadv2(input->iovec(), input->iovcnt(), offset, flags); + return ret; + } + + if (refill_off + refill_size > static_cast(actual_size_)) { + refill_size = actual_size_ - refill_off; + } + + ret = range_lock_.try_lock_wait(refill_off, refill_size); + if (ret < 0) + return -EAGAIN; + { + static uint32_t max_refilling = pool_ ? pool_->m_max_refilling : MAX_REFILLING; + uint32_t refilling = max_refilling; + DEFER({ + if (refilling >= max_refilling) + range_lock_.unlock(refill_off, refill_size); + }); + IOVector buffer(*allocator_); + auto alloc = buffer.push_back(refill_size); + if (alloc < refill_size) { + LOG_ERROR("memory allocate failed, refill_size:`, alloc:`", refill_size, alloc); + if (input) { + SCOPE_AUDIT("download", AU_FILEOP(get_src_name(), offset, ret)); + ret = src_file_->preadv2(input->iovec(), input->iovcnt(), offset, flags); + return ret; + } else + return -1; + } + + { + SCOPE_AUDIT("download", AU_FILEOP(get_src_name(), refill_off, ret)); + ret = src_file_->preadv2(buffer.iovec(), buffer.iovcnt(), refill_off, flags); + } + + if (ret != static_cast(refill_size)) { + LOG_ERRNO_RETURN( + 0, -1, + "src file read failed, read : `, expectRead : `, actual_size_ : `, offset : `, sum : `", + ret, refill_size, actual_size_, refill_off, buffer.sum()); + } + + // buffer need async refill + IOVector refill_buf(buffer.iovec(), buffer.iovcnt()); + if (input && (off_t)refill_off <= offset) { + auto view = input->view(); + refill_buf.extract_front(offset - refill_off); + ret = refill_buf.memcpy_to(&view, count); + offset += ret; + } else if (input && refill_off + refill_size >= offset + count) { + iovector_view tail_iov; + tail_iov.iovcnt = 0; + input->slice(count - (refill_off - offset), refill_off - offset, &tail_iov); + ret = refill_buf.memcpy_to(&tail_iov); + input->extract_back(ret); + } else + ret = 0; + + if (input && pool_ && pool_->m_thread_pool && + (refilling = pool_->m_refilling.load(std::memory_order_relaxed)) < + pool_->m_max_refilling) { + pool_->m_refilling.fetch_add(1, std::memory_order_relaxed); + ref_.fetch_add(1, std::memory_order_relaxed); + auto ctx = new RefillContext{this, std::move(buffer), refill_off, refill_size, flags}; + auto th = static_cast(pool_->m_thread_pool) + ->thread_create(&async_refill, ctx); + photon::thread_migrate(th, photon::get_vcpu()); + } else { + auto write = do_pwritev2(buffer.iovec(), buffer.iovcnt(), refill_off, flags); + if (write != static_cast(refill_size)) { + if (ENOSPC != errno) + LOG_ERROR( + "cache file write failed : `, error : `, actual_size_ : `, offset : `, sum : `", + write, ERRNO(errno), actual_size_, refill_off, buffer.sum()); + if (!input) + return -1; + } + } + } + + if (input && ret != (ssize_t)count) { + auto tr = try_preadv2(input->iovec(), input->iovcnt(), offset, flags); + if (tr.refill_size != 0 || tr.size < 0) { + SCOPE_AUDIT("download", AU_FILEOP(get_src_name(), offset, tr.size)); + tr.size = src_file_->preadv2(input->iovec(), input->iovcnt(), offset, flags); + if (tr.size + ret != static_cast(count)) + LOG_ERRNO_RETURN(0, -1, "read failed, ret:`, offset:`,sum:`,actual_size_:`", + tr.size, offset, input->sum(), actual_size_); + } + } + + return count; +} + +void ICacheStore::set_cached_size(off_t cached_size) { + if (cached_size_ == 0) { + cached_size_ = cached_size; + } else if (cached_size > cached_size_) { + off_t last = cached_size_ / page_size_ * page_size_; + if (last != cached_size_) + evict(last); + cached_size_ = last; + } else if (cached_size < cached_size_) { + off_t last = cached_size / page_size_ * page_size_; + evict(last); + cached_size_ = last; + } +} + +ICacheStore::try_preadv_result ICacheStore::try_preadv2(const struct iovec *iov, int iovcnt, + off_t offset, int flags) { + try_preadv_result rst; + iovector_view view((iovec *)iov, iovcnt); + rst.iov_sum = view.sum(); + auto q = queryRefillRange(offset, rst.iov_sum); + if (q.first >= 0 && q.second == 0) { // no need to refill + rst.refill_size = 0; + rst.size = do_preadv2(iov, iovcnt, offset, flags); + if (rst.size != (ssize_t)rst.iov_sum) { + rst.refill_size = (size_t)-1; + rst.refill_offset = -1; + } + } else { + rst.refill_size = q.second; + rst.refill_offset = q.first; + } + + return rst; +} + +ssize_t ICacheStore::do_preadv2(const struct iovec *iov, int iovcnt, off_t offset, int flags) { + SmartCloneIOV<32> ciov(iov, iovcnt); + return do_preadv2_mutable(ciov.iov, iovcnt, offset, flags); +} + +ssize_t ICacheStore::do_preadv2_mutable(struct iovec *iov, int iovcnt, off_t offset, int flags) { + return do_preadv2(iov, iovcnt, offset, flags); +} + +ssize_t ICacheStore::do_pwritev2(const struct iovec *iov, int iovcnt, off_t offset, int flags) { + SmartCloneIOV<32> ciov(iov, iovcnt); + return do_pwritev2_mutable(ciov.iov, iovcnt, offset, flags); +} + +ssize_t ICacheStore::do_pwritev2_mutable(struct iovec *iov, int iovcnt, off_t offset, int flags) { + return do_pwritev2(iov, iovcnt, offset, flags); +} + +int ICacheStore::open_src_file(IFile **src_file) { + if (!src_fs_ || (open_flags_ & O_CACHE_ONLY)) { + if (src_file) + *src_file = src_file_; + return 0; + } + photon::scoped_lock l(open_lock_); + if (src_file_) { + if (src_file) + *src_file = src_file_; + return 0; + } + int flags = O_RDONLY; + if (open_flags_ & (O_WRITE_THROUGH | O_WRITE_BACK)) + flags |= O_CREAT; + src_file_ = src_fs_->open(src_name_.c_str(), flags); + if (!src_file_) + LOG_ERRNO_RETURN(0, -1, "open source ` failed", src_name_.c_str()); + if (src_file) + *src_file = src_file_; + return 0; +} + +ssize_t ICacheStore::pwritev2_extend(const struct iovec *iov, int iovcnt, off_t offset, int flags) { + iovector_view view(const_cast(iov), iovcnt); + size_t size = view.sum(); + if (offset % page_size_ != 0) { + LOG_ERROR_RETURN(EINVAL, -1, "offset is not aligned to `, size : `, offset : `", page_size_, + size, offset); + } + + // append only + if (offset + (off_t)size > cached_size_) { + off_t last = cached_size_ / page_size_ * page_size_; + if (last != cached_size_) { + evict(last); + cached_size_ = last; + actual_size_ = cached_size_; + } + } + + auto write = do_pwritev2(iov, iovcnt, offset, flags); + if (write != static_cast(size)) { + if (ENOSPC != errno) + LOG_ERROR( + "cache file write failed : `, error : `, actual_size_ : `, offset : `, sum : `", + write, ERRNO(errno), actual_size_, offset, size); + } + + // append only + if (write > 0 && offset + write > cached_size_) { + cached_size_ = offset + write; + if (actual_size_ < cached_size_) { + actual_size_ = cached_size_; + } + } + + return write; +} + +int ICacheStore::tryget_size() { + if (actual_size_ % page_size_ != 0) + return 0; + if (open_src_file() != 0) + return -1; + struct stat buf; + buf.st_size = 0; + if ((src_file_ && src_file_->fstat(&buf) != 0) || (!src_file_ && fstat(&buf) != 0)) + return -1; + if (buf.st_size != actual_size_) { + set_cached_size(buf.st_size); + actual_size_ = buf.st_size; + } + return 0; +} + +} // namespace FileSystem diff --git a/src/overlaybd/cache/full_file_cache/test/CMakeLists.txt b/src/overlaybd/cache/test/CMakeLists.txt similarity index 99% rename from src/overlaybd/cache/full_file_cache/test/CMakeLists.txt rename to src/overlaybd/cache/test/CMakeLists.txt index 6d57d2be..768642e4 100644 --- a/src/overlaybd/cache/full_file_cache/test/CMakeLists.txt +++ b/src/overlaybd/cache/test/CMakeLists.txt @@ -11,5 +11,4 @@ target_link_libraries(cache_test gtest gtest_main gflags pthread photon_static o add_test( NAME cache_test COMMAND ${EXECUTABLE_OUTPUT_PATH}/cache_test -) - +) \ No newline at end of file diff --git a/src/overlaybd/cache/test/cache_test.cpp b/src/overlaybd/cache/test/cache_test.cpp new file mode 100644 index 00000000..d0c0553b --- /dev/null +++ b/src/overlaybd/cache/test/cache_test.cpp @@ -0,0 +1,558 @@ +/* + Copyright The Overlaybd Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "photon/common/alog.h" +#include "photon/common/callback.h" +#include "photon/fs/localfs.h" +#include "photon/fs/aligned-file.h" +#include "photon/thread/thread.h" +#include "photon/io/fd-events.h" +#include "photon/io/aio-wrapper.h" +#include "photon/common/io-alloc.h" +#include "../cache.h" +#include "random_generator.h" + +namespace Cache { + +using namespace FileSystem; +using namespace photon::fs; + +// Cleanup and recreate the test dir +inline void SetupTestDir(const std::string& dir) { + std::string cmd = std::string("rm -r ") + dir; + system(cmd.c_str()); + cmd = std::string("mkdir -p ") + dir; + system(cmd.c_str()); +} + +void commonTest(bool cacheIsFull, bool enableDirControl, bool dirFull) { + std::string prefix = ""; + const size_t dirQuota = 32ul * 1024 * 1024; + const uint64_t refillSize = 1024 * 1024; + if (enableDirControl) { + prefix = "/John/bucket/"; + } + + std::string root("/tmp/ease/cache/cache_test/"); + SetupTestDir(root); + + std::string subDir = prefix + "dir/dir/"; + SetupTestDir(root + subDir); + std::system(std::string("touch " + root + subDir + "testFile").c_str()); + + struct stat st; + auto ok = ::stat(std::string(root + subDir + "testFile").c_str(), &st); + EXPECT_EQ(0, ok); + + std::string srcRoot("/tmp/ease/cache/src_test/"); + SetupTestDir(srcRoot); + auto srcFs = new_localfs_adaptor(srcRoot.c_str(), ioengine_psync); + + auto mediaFs = new_localfs_adaptor(root.c_str(), ioengine_libaio); + auto alignFs = new_aligned_fs_adaptor(mediaFs, 4 * 1024, true, true); + auto cacheAllocator = new AlignedAlloc(4 * 1024); + auto roCachedFs = new_full_file_cached_fs(srcFs, alignFs, refillSize, + cacheIsFull ? 0 : 512, 1000 * 1000 * 1, 128ul * 1024 * 1024, cacheAllocator, enableDirControl ? 2 : 0); + auto cachePool = roCachedFs->get_pool(); + + if (dirFull) { + cachePool->set_quota(prefix, dirQuota); + } + SetupTestDir(srcRoot + prefix + "testDir"); + auto srcFile = srcFs->open(std::string(prefix + "/testDir/file_1").c_str(), + O_RDWR|O_CREAT|O_TRUNC, 0644); + + UniformCharRandomGen gen(0, 255); + off_t offset = 0; + uint32_t kPageSize = 4 * 1024; + uint32_t kFileSize = kPageSize * 16384; // 64MB + uint32_t kPageCount = kFileSize / kPageSize; + for (uint32_t i = 0; i < kPageCount; ++i) { + std::vector data; + for (uint32_t j = 0; j < kPageSize; ++j) { + data.push_back(gen.next()); + } + srcFile->pwrite(data.data(), data.size(), offset); + offset += kPageSize; + } + + // write some unaligned + off_t lastOffset = offset; + off_t unAlignedLen = 750; + { + std::vector data; + for (uint32_t j = 0; j < kPageSize; ++j) { + data.push_back(gen.next()); + } + srcFile->pwrite(data.data(), unAlignedLen, offset); + } + + auto cachedFile = static_cast(roCachedFs->open( + std::string(prefix + "/testDir/file_1").c_str(), 0, 0644)); + + // test unaligned block + { + void* buf = malloc(kPageSize); + auto ret = cachedFile->pread(buf, kPageSize, lastOffset); + + std::vector src; + src.reserve(kPageSize); + auto retSrc = srcFile->pread(src.data(), kPageSize, lastOffset); + + EXPECT_EQ(0, std::memcmp(buf, src.data(), unAlignedLen)); + EXPECT_EQ(unAlignedLen, retSrc); + EXPECT_EQ(unAlignedLen, ret); + + LOG_INFO("read again"); + + // read again + ret = cachedFile->pread(buf, kPageSize, lastOffset); + EXPECT_EQ(unAlignedLen, ret); + + free(buf); + } + + // test aligned and unaligned block + { + void* buf = malloc(kPageSize * 4); + auto ret = cachedFile->pread(buf, kPageSize * 4, lastOffset - 2 * kPageSize); + + std::vector src; + src.reserve(kPageSize * 4); + auto retSrc = srcFile->pread(src.data(), kPageSize * 4, lastOffset - 2 * kPageSize); + + EXPECT_EQ(0, std::memcmp(buf, src.data(), 2 * kPageSize + unAlignedLen)); + EXPECT_EQ(2 * kPageSize + unAlignedLen, retSrc); + EXPECT_EQ(2 * kPageSize + unAlignedLen, ret); + + LOG_INFO("read again"); + + // read again + ret = cachedFile->pread(buf, kPageSize * 4, lastOffset - 2 * kPageSize); + EXPECT_EQ(2 * kPageSize + unAlignedLen, ret); + + free(buf); + } + + std::vector readBuf; + readBuf.reserve(kPageSize); + std::vector readSrcBuf; + readSrcBuf.reserve(kPageSize); + for (int i = 0; i != 5; ++i) { + EXPECT_EQ(kPageSize, cachedFile->read(readBuf.data(), kPageSize)); + srcFile->read(readSrcBuf.data(), kPageSize); + EXPECT_EQ(0, std::memcmp(readBuf.data(), readSrcBuf.data(), kPageSize)); + } + + if (enableDirControl && !cacheIsFull) { + CacheStat cstat = {}; + EXPECT_EQ(0, cachePool->stat(&cstat, std::string(prefix + "/testDir/file_1").c_str())); + EXPECT_EQ(kFileSize / refillSize, cstat.total_size); + cstat = {}; + EXPECT_EQ(0, cachedFile->get_store()->stat(&cstat)); + EXPECT_EQ(kFileSize / refillSize, cstat.total_size); + } + + // test refill(3) + if (!cacheIsFull) { + auto inSrcFile = cachedFile->get_source(); + cachedFile->set_source(nullptr); + struct stat stat; + inSrcFile->fstat(&stat); + cachedFile->ftruncate(stat.st_size); + void* buf = malloc(kPageSize * 3); + DEFER(free(buf)); + std::vector src; + src.reserve(kPageSize * 3); + EXPECT_EQ(kPageSize, srcFile->pread(src.data(), kPageSize, 0)); + memcpy(buf, src.data(), kPageSize); + + EXPECT_EQ(kPageSize, cachedFile->refill(buf, kPageSize, 0)); + + memset(buf, 0, kPageSize); + EXPECT_EQ(kPageSize, cachedFile->pread(buf, kPageSize, 0)); + EXPECT_EQ(0, memcmp(buf, src.data(), kPageSize)); + + struct stat st1; + ::stat(std::string(root + prefix + "/testDir/file_1").c_str(), &st1); + EXPECT_EQ(0, cachedFile->evict(0, kPageSize)); + struct stat st2; + ::stat(std::string(root + prefix + "/testDir/file_1").c_str(), &st2); + EXPECT_EQ(kPageSize, st1.st_blocks * 512 - st2.st_blocks * 512); + + // test refill last block + src.clear(); + EXPECT_EQ(kPageSize + unAlignedLen, srcFile->pread(src.data(), kPageSize * 3, lastOffset - kPageSize)); + memcpy(buf, src.data(), kPageSize * 3); + EXPECT_EQ(kPageSize + unAlignedLen, cachedFile->refill(buf, kPageSize * 3, lastOffset - kPageSize)); + memset(buf, 0, kPageSize * 3); + EXPECT_EQ(kPageSize + unAlignedLen, cachedFile->pread(buf, kPageSize * 3, lastOffset - kPageSize)); + EXPECT_EQ(0, memcmp(buf, src.data(), kPageSize + unAlignedLen)); + + cachedFile->set_source(inSrcFile); + } + + // test refill(2) + if (!cacheIsFull) { + auto inSrcFile = cachedFile->get_source(); + + void* buf = malloc(kPageSize * 2); + DEFER(free(buf)); + EXPECT_EQ(0, cachedFile->refill(kPageSize, 2 * kPageSize)); + + cachedFile->set_source(nullptr); + EXPECT_EQ(2 * kPageSize, cachedFile->pread(buf, 2 * kPageSize, kPageSize)); + std::vector src; + src.reserve(kPageSize * 2); + EXPECT_EQ(kPageSize * 2, srcFile->pread(src.data(), 2 * kPageSize, kPageSize)); + EXPECT_EQ(0, memcmp(buf, src.data(), 2 * kPageSize)); + cachedFile->set_source(inSrcFile); + + // prefetch more than 16MB + EXPECT_EQ(0, cachedFile->fadvise(234, 5000 * kPageSize, POSIX_FADV_WILLNEED)); + // prefetch tail + EXPECT_EQ(0, cachedFile->fadvise(lastOffset - kPageSize, 5000 * kPageSize, POSIX_FADV_WILLNEED)); + } + + if (dirFull) { + CacheStat cstat = {}; + EXPECT_EQ(0, cachePool->stat(&cstat, prefix)); + EXPECT_EQ(dirQuota / refillSize, cstat.total_size); + } + + // test aligned section + UniformInt32RandomGen genOffset(0, (kPageCount + 1) * kPageSize); + UniformInt32RandomGen genSize(0, 8 * kPageSize); + struct stat srcSt = {}; + srcFile->fstat(&srcSt); + for (int i = 0; i != 10000; ++i) { + auto tmpOffset = genOffset.next(); + auto size = genSize.next(); + + if (tmpOffset >= srcSt.st_size) { + size = 0; + } else { + size = tmpOffset + size > srcSt.st_size ? srcSt.st_size - tmpOffset : size; + } + void* buf = malloc(size); + auto ret = cachedFile->pread(buf, size, tmpOffset); + + std::vector src; + src.reserve(size); + auto retSrc = srcFile->pread(src.data(), size, tmpOffset); + + EXPECT_EQ(0, std::memcmp(buf, src.data(), size)); + EXPECT_EQ(size, retSrc); + EXPECT_EQ(size, ret); + free(buf); + + if (9900 == i && dirFull) { + cachedFile->get_store()->set_quota(0); + } + } + srcFile->close(); + + photon::thread_usleep(1000 * 1000ull); + ok = ::stat(std::string(root + subDir + "testFile").c_str(), &st); + EXPECT_EQ(cacheIsFull || dirFull ? -1 : 0, ok); + + if (enableDirControl) { + auto ret = cachePool->evict(std::string(prefix + "/testDir").c_str()); + EXPECT_EQ(0, ret); + } + + delete cachedFile; + + // test smaller file + { + auto smallFile = srcFs->open(std::string(prefix + "/testDir/small").c_str(), + O_RDWR|O_CREAT|O_TRUNC, 0644); + DEFER(delete smallFile); + int smallSize = 102; + std::vector smallData; + for (int i = 0; i != smallSize; ++i) { + smallData.push_back(gen.next()); + } + EXPECT_EQ(smallSize, smallFile->pwrite(smallData.data(), smallData.size(), 0)); + + auto smallCache = static_cast(roCachedFs->open( + std::string(prefix + "/testDir/small").c_str(), 0, 0644)); + DEFER(delete smallCache); + + void* sBuffer = malloc(kPageSize); + DEFER(free(sBuffer)); + EXPECT_EQ(smallSize, smallCache->pread(sBuffer, kPageSize, 0)); + EXPECT_EQ(0, std::memcmp(sBuffer, smallData.data(), smallSize)); + + memset(sBuffer, 0, kPageSize); + EXPECT_EQ(smallSize, smallCache->pread(sBuffer, kPageSize, 0)); + EXPECT_EQ(0, std::memcmp(sBuffer, smallData.data(), smallSize)); + + smallFile->close(); + } + + // test refill + { + auto refillFile = srcFs->open(std::string(prefix + "/testDir/refill").c_str(), + O_RDWR|O_CREAT|O_TRUNC, 0644); + DEFER(delete refillFile); + int refillSize = 4097; + std::vector refillData; + for (int i = 0; i != refillSize; ++i) { + refillData.push_back(gen.next()); + } + EXPECT_EQ(refillSize, refillFile->pwrite(refillData.data(), refillData.size(), 0)); + + auto refillCache = static_cast(roCachedFs->open( + std::string(prefix + "/testDir/refill").c_str(), 0, 0644)); + DEFER(delete refillCache); + + void* sBuffer = malloc(kPageSize * 2); + DEFER(free(sBuffer)); + memset(sBuffer, 0, kPageSize * 2); + EXPECT_EQ(kPageSize, refillCache->pread(sBuffer, kPageSize, 0)); + EXPECT_EQ(0, std::memcmp(sBuffer, refillData.data(), kPageSize)); + + memset(sBuffer, 0, kPageSize * 2); + EXPECT_EQ(refillSize, refillCache->pread(sBuffer, kPageSize * 2, 0)); + EXPECT_EQ(0, std::memcmp(sBuffer, refillData.data(), refillSize)); + + refillFile->close(); + } + + delete srcFs; + delete roCachedFs; +} + +TEST(RoCachedFs, Basic) { + commonTest(false, false, false); +} + +TEST(RoCachedFs, BasicCacheFull) { + commonTest(true, false, false); +} + +// TEST(RoCachedFs, BasicWithDirControl) { +// commonTest(false, true, false); +// } + +// TEST(RoCachedFs, BasicCacheFullWithDirControl) { +// commonTest(true, true, false); +// } + +TEST(RoCachedFs, CacheWithOutSrcFile) { + std::string root("/tmp/ease/cache/cache_test_no_src/"); + SetupTestDir(root); + + auto mediaFs = new_localfs_adaptor(root.c_str(), ioengine_libaio); + auto alignFs = new_aligned_fs_adaptor(mediaFs, 4 * 1024, true, true); + auto cacheAllocator = new AlignedAlloc(4 * 1024); + DEFER(delete cacheAllocator); + auto roCachedFs = new_full_file_cached_fs(nullptr, alignFs, 1024 * 1024, + 512, 1000 * 1000 * 1, 128ul * 1024 * 1024, cacheAllocator, 0); + DEFER(delete roCachedFs); + auto cachedFile = static_cast(roCachedFs->open( + std::string("/testDir/file_1").c_str(), 0, 0644)); + DEFER(delete cachedFile); + + cachedFile->ftruncate(1024 * 1024); + std::vector buf; + int len = 8 * 1024; + buf.reserve(len); + EXPECT_EQ(len, cachedFile->pwrite(buf.data(), len, 4 * 1024)); + EXPECT_EQ(len / 2, cachedFile->pread(buf.data(), 4 * 1024, 4 * 1024)); + EXPECT_EQ(-1, cachedFile->pread(buf.data(), len, 0)); + + auto writeFile = static_cast(roCachedFs->open( + std::string("/testDir/file_2").c_str(), 0, 0644)); + DEFER(delete writeFile); + writeFile->ftruncate(1024 * 1024); + buf.assign(len, 'a'); + EXPECT_EQ(len, writeFile->write(buf.data(), len)); + EXPECT_EQ(len, writeFile->write(buf.data(), len)); + std::vector res; + res.reserve(len); + EXPECT_EQ(len, writeFile->pread(res.data(), len, 0)); + EXPECT_EQ(0, std::memcmp(buf.data(), res.data(), len)); + res.assign(len, '0'); + EXPECT_EQ(len, writeFile->pread(res.data(), len, len)); + EXPECT_EQ(0, std::memcmp(buf.data(), res.data(), len)); + EXPECT_EQ(-1, writeFile->pread(res.data(), len, len * 2)); +} + +TEST(RoCachedFS, xattr) { + std::string root("/tmp/ease/cache/cache_xattr/"); + SetupTestDir(root); + + auto srcFs = new_localfs_adaptor(); + auto mediaFs = new_localfs_adaptor(root.c_str()); + auto roCachedFs = new_full_file_cached_fs(srcFs, mediaFs, 1024 * 1024, 512, 1000 * 1000 * 1, + 128ul * 1024 * 1024, nullptr, 0); + DEFER(delete roCachedFs); + + std::string path = "/tmp/ease/cache/cache_xattr/filexattr"; + auto xttarFile = srcFs->open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644); + DEFER(delete xttarFile); + auto xattrFs = dynamic_cast(roCachedFs); + std::string name = "user.testxattr", value = "yes"; + char key[20], val[20]; + auto ret = xattrFs->setxattr(path.c_str(), name.c_str(), value.c_str(), value.size(), 0); + EXPECT_EQ(0, ret); + ret = xattrFs->listxattr(path.c_str(), key, 20); + EXPECT_EQ(0, std::memcmp(key, name.data(), ret)); + ret = xattrFs->getxattr(path.c_str(), key, val, 20); + EXPECT_EQ(value.size(), ret); + EXPECT_EQ(0, std::memcmp(val, value.data(), ret)); + ret = xattrFs->removexattr(path.c_str(), key); + EXPECT_EQ(0, ret); + + auto cachedFile = static_cast(roCachedFs->open(path.c_str(), 0, 0644)); + DEFER(delete cachedFile); + auto xattrfile = dynamic_cast(cachedFile); + ret = xattrfile->fsetxattr(name.c_str(), value.c_str(), value.size(), 0); + EXPECT_EQ(0, ret); + ret = xattrfile->flistxattr(key, 20); + EXPECT_EQ(0, std::memcmp(key, name.data(), ret)); + ret = xattrfile->fgetxattr(key, val, 20); + EXPECT_EQ(value.size(), ret); + EXPECT_EQ(0, std::memcmp(val, value.data(), ret)); + ret = xattrfile->fremovexattr(key); + EXPECT_EQ(0, ret); +} + +void* worker(void* arg) { + auto fs = (ICachedFileSystem*)arg; + char buffer[1024*1024]; + char buffersrc[1024*1024]; + std::vector offset; + for (auto i = 0; i < 2048; i++) { + offset.push_back(i * 1024 * 1024); + } + auto fd = ::open("/tmp/ease/cache/src_test/huge", O_RDONLY); + DEFER(::close(fd)); + auto f = fs->open("/huge", O_RDONLY); + DEFER(delete f); + for (int i=0;i<4;i++) { + std::random_shuffle(offset.begin(), offset.end()); + for (const auto &x : offset) { + ::pread(fd, buffersrc, 1024*1024, x); + f->pread(buffer, 1024*1024, x); + EXPECT_EQ(0, memcmp(buffer, buffersrc, 1024*1024)); + fs->get_pool()->evict(); + photon::thread_yield(); + } + } + return nullptr; +} + +TEST(CachedFS, write_while_full) { + std::string srcRoot("/tmp/ease/cache/src_test/"); + SetupTestDir(srcRoot); + system("dd if=/dev/urandom of=/tmp/ease/cache/src_test/huge bs=1M count=2048"); + + std::string root("/tmp/ease/cache/cache_test/"); + SetupTestDir(root); + auto srcFs = new_localfs_adaptor(srcRoot.c_str()); + auto mediaFs = new_localfs_adaptor(root.c_str()); + auto roCachedFs = new_full_file_cached_fs(srcFs, mediaFs, 1024 * 1024, 1, 100 * 1000 * 1, + 128ul * 1024 * 1024, nullptr, 0); + + std::vector jhs; + + for (int i=0;i<2;i++) { + jhs.emplace_back(photon::thread_enable_join(photon::thread_create(worker, roCachedFs))); + } + for (auto &x : jhs) { + photon::thread_join(x); + } +} + +TEST(CachedFS, fn_trans_func) { + std::string srcRoot("/tmp/ease/cache/src_test/"); + SetupTestDir(srcRoot); + system("mkdir /tmp/ease/cache/src_test/path_aaa/"); + system("mkdir /tmp/ease/cache/src_test/path_bbb/"); + system("dd if=/dev/urandom of=/tmp/ease/cache/src_test/path_aaa/sha256:test bs=1K count=1"); + system("cp /tmp/ease/cache/src_test/path_aaa/sha256:test /tmp/ease/cache/src_test/path_bbb/sha256:test"); + + std::string root("/tmp/ease/cache/cache_test/"); + SetupTestDir(root); + auto srcFs = new_localfs_adaptor(srcRoot.c_str()); + auto mediaFs = new_localfs_adaptor(root.c_str()); + + struct NameTransCB { + size_t fn_trans_sha256(std::string_view src, char *dest, size_t size) { + auto p = src.find("/sha256:"); + if (p == std::string_view::npos) { + return 0; + } + size_t len = src.size() - p; + if (len > size) { + LOG_WARN("filename length ` exceed `", len, size); + return 0; + } + strcpy(dest, src.data() + p); + return len; + } + }cb; + auto cachedFs = new_full_file_cached_fs(srcFs, mediaFs, 1024 * 1024, 1, 100 * 1000 * 1, + 128ul * 1024 * 1024, nullptr, 0, {&cb, &NameTransCB::fn_trans_sha256}); + char buf1[1024], buf2[1024]; + auto cachedFile1 = static_cast(cachedFs->open("/path_aaa/sha256:test", 0, 0644)); + auto cachedFile2 = static_cast(cachedFs->open("/path_bbb/sha256:test", 0, 0644)); + cachedFile1->read(buf1, 1024); + auto cFile = mediaFs->open("/sha256:test", 0, 0644); + cFile->read(buf2, 1024); + EXPECT_EQ(0, memcmp(buf1, buf2, 1024)); + auto cs1 = cachedFile1->get_store(); + auto cs2 = cachedFile2->get_store(); + EXPECT_EQ(cs1, cs2); +} + +} // namespace Cache + +int main(int argc, char** argv) { + log_output_level = ALOG_ERROR; + photon::vcpu_init(); + ::testing::InitGoogleTest(&argc, argv); + + int ret = photon::fd_events_init(photon::INIT_EVENT_EPOLL); + if (ret < 0) { + LOG_ERROR_RETURN(0, -1, "failed to init epoll subsystem"); + } + + ret = photon::libaio_wrapper_init(); + if (ret < 0) + LOG_ERROR_RETURN(0, -1, "failed to init libaio subsystem"); + + ret = RUN_ALL_TESTS(); + + photon::libaio_wrapper_fini(); + photon::fd_events_fini(); + return ret; +} diff --git a/src/overlaybd/cache/full_file_cache/test/random_generator.h b/src/overlaybd/cache/test/random_generator.h similarity index 52% rename from src/overlaybd/cache/full_file_cache/test/random_generator.h rename to src/overlaybd/cache/test/random_generator.h index 4dce7605..8662a1df 100644 --- a/src/overlaybd/cache/full_file_cache/test/random_generator.h +++ b/src/overlaybd/cache/test/random_generator.h @@ -24,44 +24,35 @@ namespace Cache { /* DataTypes for random generator */ template class RandomValueGen { -public: - RandomValueGen() { - } - virtual ~RandomValueGen() { - } - virtual T next() = 0; + public: + RandomValueGen() {} + virtual ~RandomValueGen() {} + virtual T next() = 0; }; // an uniform int random generator that produces inclusive-inclusive value range -class UniformInt32RandomGen : public RandomValueGen { -public: - UniformInt32RandomGen(uint32_t start, uint32_t end, int seed = 1213) - : gen_(seed), dis_(start, end) { - } - uint32_t next() override { - return dis_(gen_); - } - void seed(std::mt19937::result_type val) { - gen_.seed(val); - } - -private: - std::mt19937 gen_; - std::uniform_int_distribution dis_; +class UniformInt32RandomGen: public RandomValueGen { + public: + UniformInt32RandomGen() = default; + UniformInt32RandomGen(uint32_t start, uint32_t end, int seed = 1213) + : gen_(seed), dis_(start, end) {} + uint32_t next() override { return dis_(gen_); } + void seed(std::mt19937::result_type val) { gen_.seed(val); } + + private: + std::mt19937 gen_; + std::uniform_int_distribution dis_; }; class UniformCharRandomGen : public RandomValueGen { -public: - UniformCharRandomGen(uint32_t start, uint32_t end, int seed = 1213) - : gen_(seed), dis_(start, end) { - } - unsigned char next() { - return dis_(gen_); - } - -private: - std::mt19937 gen_; - std::uniform_int_distribution dis_; + public: + UniformCharRandomGen(uint32_t start, uint32_t end, int seed = 1213) + : gen_(seed), dis_(start, end) {} + unsigned char next() { return dis_(gen_); } + + private: + std::mt19937 gen_; + std::uniform_int_distribution dis_; }; -} // namespace Cache +} // namespace Cache diff --git a/src/overlaybd/gzindex/test/test.cpp b/src/overlaybd/gzindex/test/test.cpp index 940da52c..887e045e 100644 --- a/src/overlaybd/gzindex/test/test.cpp +++ b/src/overlaybd/gzindex/test/test.cpp @@ -18,7 +18,6 @@ #include "../../cache/pool_store.h" #include "../../cache/full_file_cache/cache_pool.h" #include "../../cache/cache.h" -#include "../../cache/frontend/cached_file.h" #include #include #include @@ -183,9 +182,9 @@ photon::fs::IFile *GzIndexTest::defile = nullptr; photon::fs::IFile *GzIndexTest::gzfile = nullptr; photon::fs::IFile *GzIndexTest::gzdata = nullptr; photon::fs::IFile *GzIndexTest::gzindex = nullptr; -const char *GzIndexTest::fn_defile = "fdata"; -const char *GzIndexTest::fn_gzdata = "fdata.gz"; -const char *GzIndexTest::fn_gzindex = "findex"; +const char *GzIndexTest::fn_defile = "/fdata"; +const char *GzIndexTest::fn_gzdata = "/fdata.gz"; +const char *GzIndexTest::fn_gzindex = "/findex"; TEST_F(GzIndexTest, pread) { std::vector t{ @@ -256,7 +255,8 @@ class GzCacheTest : public ::testing::Test { system(cmd.c_str()); lfs = photon::fs::new_localfs_adaptor("/tmp/gzip_src"); cfs = photon::fs::new_localfs_adaptor("/tmp/gzip_cache"); - pool = new Cache::FileCachePool(cfs, 4, 10000000, (uint64_t)1048576 * 4096, 1024 * 1024); + cfs1 = photon::fs::new_localfs_adaptor("/tmp/gzip_cache"); + pool = new Cache::FileCachePool(cfs1, 4, 10000000, (uint64_t)1048576 * 4096, 1024 * 1024); if (buildDataFile() != 0) { LOG_ERROR("failed to build ` and `", fn_defile, fn_gzdata); @@ -268,7 +268,7 @@ class GzCacheTest : public ::testing::Test { } lfs = FileSystem::new_full_file_cached_fs( lfs, cfs, 1024 * 1024, 1, 10000000, - (uint64_t)1048576 * 4096, nullptr, nullptr); + (uint64_t)1048576 * 4096, nullptr, 0, nullptr); gzdata = lfs->open(fn_gzdata, O_CREAT | O_TRUNC | O_RDWR, 0644); if (gzdata == nullptr) { LOG_ERROR("gzdata create failed"); @@ -288,7 +288,11 @@ class GzCacheTest : public ::testing::Test { exit(-1); } auto io_alloc = new IOAlloc; - gzfile = new Cache::CachedFile(gzfile, store, st.st_size, 4 * 1024, 4, io_alloc, nullptr); + store->set_src_file(gzfile); + store->set_page_size(4096); + store->set_allocator(io_alloc); + store->set_actual_size(st.st_size); + gzfile = new_cached_file(store, 4096, nullptr); if (gzfile == nullptr) { LOG_ERROR("failed create new cached gzip file"); @@ -311,6 +315,7 @@ class GzCacheTest : public ::testing::Test { if (lfs->access(fn_gzindex, 0) == 0) { lfs->unlink(fn_gzindex); } + delete pool; delete lfs; } @@ -339,6 +344,7 @@ class GzCacheTest : public ::testing::Test { private: static photon::fs::IFileSystem *lfs; static photon::fs::IFileSystem *cfs; + static photon::fs::IFileSystem *cfs1; static FileSystem::ICachePool *pool; static photon::fs::IFile *gzdata; static photon::fs::IFile *gzindex; @@ -414,14 +420,15 @@ class GzCacheTest : public ::testing::Test { photon::fs::IFileSystem *GzCacheTest::lfs = nullptr; photon::fs::IFileSystem *GzCacheTest::cfs = nullptr; +photon::fs::IFileSystem *GzCacheTest::cfs1 = nullptr; FileSystem::ICachePool *GzCacheTest::pool = nullptr; photon::fs::IFile *GzCacheTest::defile = nullptr; photon::fs::IFile *GzCacheTest::gzfile = nullptr; photon::fs::IFile *GzCacheTest::gzdata = nullptr; photon::fs::IFile *GzCacheTest::gzindex = nullptr; -const char *GzCacheTest::fn_defile = "fdata"; -const char *GzCacheTest::fn_gzdata = "fdata.gz"; -const char *GzCacheTest::fn_gzindex = "findex"; +const char *GzCacheTest::fn_defile = "/fdata"; +const char *GzCacheTest::fn_gzdata = "/fdata.gz"; +const char *GzCacheTest::fn_gzindex = "/findex"; bool check_in_interval(int val, int l, int r) { return l <= val && val < r; diff --git a/src/overlaybd/registryfs/registryfs.cpp b/src/overlaybd/registryfs/registryfs.cpp index d19238f2..70719dfb 100644 --- a/src/overlaybd/registryfs/registryfs.cpp +++ b/src/overlaybd/registryfs/registryfs.cpp @@ -250,7 +250,7 @@ class RegistryFSImpl : public RegistryFS { // unexpected situation if (!scope.empty()) m_scope_token.release(scope, true); - LOG_ERROR_RETURN(0, nullptr, "Failed to get actual url ", VALUE(url), VALUE(ret)); + LOG_ERROR_RETURN(0, nullptr, "Failed to get actual url ", VALUE(code), VALUE(url), VALUE(ret)); } virtual int setAccelerateAddress(const char* addr = "") override { @@ -380,7 +380,8 @@ class RegistryFileImpl : public photon::fs::VirtualReadOnlyFile { size_t m_filesize; RegistryFileImpl(const char *filename, const char *url, RegistryFSImpl *fs, uint64_t timeout) - : m_filename(filename), m_url(url), m_fs(fs), m_timeout(timeout) { + : m_filename(filename), m_fs(fs), m_timeout(timeout) { + m_url = url[0] == '/' ? url + 1 : url; m_filesize = 0; } diff --git a/src/overlaybd/registryfs/registryfs_v2.cpp b/src/overlaybd/registryfs/registryfs_v2.cpp index 70abac7d..82baaf22 100644 --- a/src/overlaybd/registryfs/registryfs_v2.cpp +++ b/src/overlaybd/registryfs/registryfs_v2.cpp @@ -378,7 +378,9 @@ class RegistryFileImpl_v2 : public photon::fs::VirtualReadOnlyFile { size_t m_filesize = 0; RegistryFileImpl_v2(const char *url, RegistryFSImpl_v2 *fs, uint64_t timeout) - : m_url(url), m_fs(fs), m_timeout(timeout) {} + : m_fs(fs), m_timeout(timeout) { + m_url = url[0] == '/' ? url + 1 : url; + } virtual IFileSystem *filesystem() override { return m_fs; @@ -467,7 +469,7 @@ inline IFile *RegistryFSImpl_v2::open(const char *pathname, int) { int ret = file->fstat(&buf); if (ret < 0) { delete file; - LOG_ERROR_RETURN(0, nullptr, "failed to open and stat registry file `, ret `", pathname, ret); + LOG_ERRNO_RETURN(0, nullptr, "failed to open and stat registry file `, ret `", pathname, ret); } return file; } diff --git a/src/overlaybd/tar/tar_file.cpp b/src/overlaybd/tar/tar_file.cpp index 57552ead..d0cb26ff 100644 --- a/src/overlaybd/tar/tar_file.cpp +++ b/src/overlaybd/tar/tar_file.cpp @@ -309,7 +309,7 @@ int is_tar_file(IFile *file) { TarHeader th_buf; auto ret = file->pread(&th_buf, T_BLOCKSIZE, 0); if (ret < 0) { - LOG_ERROR_RETURN(0, -1, "read tar file header failed"); + LOG_ERRNO_RETURN(0, -1, "read tar file header failed"); } else if (ret != T_BLOCKSIZE) { LOG_WARN("read tar file header error, expect `, ret `", T_BLOCKSIZE, ret); return 0; diff --git a/src/overlaybd/zfile/test/test.cpp b/src/overlaybd/zfile/test/test.cpp index 4ee05a22..a506814b 100644 --- a/src/overlaybd/zfile/test/test.cpp +++ b/src/overlaybd/zfile/test/test.cpp @@ -30,7 +30,6 @@ #include "../compressor.cpp" #include -#include #include #include #include