CMAKE_DEPENDENT_OPTION(WITH_RBD_RWL "Enable librbd persistent write back cache" OFF
"WITH_RBD" OFF)
+CMAKE_DEPENDENT_OPTION(WITH_RBD_SSD_CACHE "Enable librbd persistent write back cache for SSDs" OFF
+ "WITH_RBD" OFF)
+
CMAKE_DEPENDENT_OPTION(WITH_SYSTEM_PMDK "Require and build with system PMDK" OFF
"WITH_RBD_RWL OR WITH_BLUESTORE_PMEM" OFF)
-if(WITH_BLUESTORE OR WITH_RBD_RWL)
+if(WITH_BLUESTORE OR WITH_RBD_SSD_CACHE)
list(APPEND libblk_srcs
BlockDevice.cc)
endif()
/* Define if RWL is enabled */
#cmakedefine WITH_RBD_RWL
+/* Define if PWL-SSD is enabled */
+#cmakedefine WITH_RBD_SSD_CACHE
+
/* Shared library extension, such as .so, .dll or .dylib */
#cmakedefine CMAKE_SHARED_LIBRARY_SUFFIX "@CMAKE_SHARED_LIBRARY_SUFFIX@"
-add_library(rbd_types STATIC
+set(librbd_types_srcs
journal/Types.cc
mirroring_watcher/Types.cc
trash_watcher/Types.cc
watcher/Types.cc
WatchNotifyTypes.cc)
+if(WITH_RBD_RWL OR WITH_RBD_SSD_CACHE)
+ list(APPEND librbd_types_srcs cache/pwl/Types.cc)
+endif()
+
+add_library(rbd_types STATIC
+ ${librbd_types_srcs})
+
set(librbd_internal_srcs
AsioEngine.cc
AsyncObjectThrottle.cc
list(APPEND librbd_internal_srcs ../common/EventTrace.cc)
endif()
-if(WITH_RBD_RWL)
+if(WITH_RBD_RWL OR WITH_RBD_SSD_CACHE)
set(librbd_internal_srcs
${librbd_internal_srcs}
cache/pwl/ImageCacheState.cc
cache/pwl/ReadRequest.cc
cache/pwl/Request.cc
cache/pwl/SyncPoint.cc
- cache/pwl/Types.cc
- cache/pwl/ReplicatedWriteLog.cc
cache/pwl/AbstractWriteLog.cc
cache/WriteLogImageDispatch.cc)
+ if(WITH_RBD_RWL)
+ set(librbd_internal_srcs
+ ${librbd_internal_srcs}
+ cache/pwl/ReplicatedWriteLog.cc)
+ endif()
+ if(WITH_RBD_SSD_CACHE)
+ set(librbd_internal_srcs
+ ${librbd_internal_srcs}
+ cache/pwl/SSDWriteLog.cc)
+ endif()
endif()
add_library(rbd_api STATIC librbd.cc)
osdc rbd_types)
target_include_directories(rbd_internal PRIVATE ${OPENSSL_INCLUDE_DIR})
-if(WITH_RBD_RWL)
+if(WITH_RBD_RWL OR WITH_RBD_SSD_CACHE)
target_link_libraries(rbd_internal
PUBLIC blk)
endif()
-
+if(WITH_RBD_RWL)
+ target_link_libraries(rbd_types
+ PUBLIC blk)
+endif()
add_custom_target(librbd_plugins)
set(librbd_plugins_dir ${CEPH_INSTALL_PKGLIBDIR}/librbd)
enum ImageCacheType {
IMAGE_CACHE_TYPE_RWL = 1,
+ IMAGE_CACHE_TYPE_SSD,
};
typedef std::list<Context *> Contexts;
#include "librbd/cache/pwl/ImageCacheState.h"
#include "librbd/cache/pwl/LogEntry.h"
#include "librbd/cache/pwl/ReadRequest.h"
-#include "librbd/cache/pwl/Types.h"
#include <map>
#include <vector>
m_thread_pool(
image_ctx.cct, "librbd::cache::pwl::AbstractWriteLog::thread_pool", "tp_pwl", 4, ""),
m_cache_state(cache_state),
- m_pwl_pool_layout_name(POBJ_LAYOUT_NAME(rbd_pwl)),
m_image_ctx(image_ctx),
m_log_pool_config_size(DEFAULT_POOL_SIZE),
m_image_writeback(image_ctx),
}
template <typename I>
-void AbstractWriteLog<I>::add_into_log_map(GenericWriteLogEntries &log_entries) {
+void AbstractWriteLog<I>::add_into_log_map(GenericWriteLogEntries &log_entries,
+ C_BlockIORequestT *req) {
+ copy_pmem(req);
m_blocks_to_log_entries.add_log_entries(log_entries);
}
uint32_t get_free_log_entries() {
return m_free_log_entries;
}
- void add_into_log_map(pwl::GenericWriteLogEntries &log_entries);
+ void add_into_log_map(pwl::GenericWriteLogEntries &log_entries,
+ C_BlockIORequestT *req);
private:
typedef std::list<pwl::C_WriteRequest<This> *> C_WriteRequests;
std::atomic<bool> m_shutting_down = {false};
std::atomic<bool> m_invalidating = {false};
- const char* m_pwl_pool_layout_name;
ImageCtxT &m_image_ctx;
virtual void alloc_op_log_entries(pwl::GenericLogOperations &ops) {}
virtual bool retire_entries(const unsigned long int frees_per_tx) {return false;}
virtual void schedule_flush_and_append(pwl::GenericLogOperationsVector &ops) {}
+ virtual void copy_pmem(C_BlockIORequestT *req) {}
virtual void persist_last_flushed_sync_gen() {}
virtual void reserve_pmem(C_BlockIORequestT *req, bool &alloc_succeeds, bool &no_space) {}
virtual Context *construct_flush_entry_ctx(
#include "common/errno.h"
#include "librbd/asio/ContextWQ.h"
-#if defined(WITH_RBD_RWL)
+#if defined(WITH_RBD_RWL) || defined(WITH_RBD_SSD_CACHE)
#include "librbd/cache/pwl/ImageCacheState.h"
-#include "librbd/cache/pwl/ReplicatedWriteLog.h"
#include "librbd/cache/WriteLogImageDispatch.h"
-#endif // WITH_RBD_RWL
+#endif // WITH_RBD_RWL || WITH_RBD_SSD_CACHE
+#ifdef WITH_RBD_RWL
+#include "librbd/cache/pwl/ReplicatedWriteLog.h"
+#endif
+#ifdef WITH_RBD_SSD_CACHE
+#include "librbd/cache/pwl/SSDWriteLog.h"
+#endif
#include "librbd/cache/Utils.h"
#include "librbd/ImageCtx.h"
template <typename I>
void InitRequest<I>::send() {
-#if defined(WITH_RBD_RWL)
+#if defined(WITH_RBD_RWL) || defined(WITH_RBD_SSD_CACHE)
get_image_cache_state();
#else
finish();
#endif // WITH_RBD_RWL
}
-#if defined(WITH_RBD_RWL)
+#if defined(WITH_RBD_RWL) || defined(WITH_RBD_SSD_CACHE)
template <typename I>
void InitRequest<I>::get_image_cache_state() {
CephContext *cct = m_image_ctx.cct;
auto cache_type = cache_state->get_image_cache_type();
switch(cache_type) {
+ #ifdef WITH_RBD_RWL
case cache::IMAGE_CACHE_TYPE_RWL:
m_image_cache =
new librbd::cache::pwl::ReplicatedWriteLog<I>(m_image_ctx,
cache_state);
break;
+ #endif
+ #ifdef WITH_RBD_SSD_CACHE
+ case cache::IMAGE_CACHE_TYPE_SSD:
+ m_image_cache =
+ new librbd::cache::pwl::SSDWriteLog<I>(m_image_ctx,
+ cache_state);
+ break;
+ #endif
default:
delete cache_state;
cache_state = nullptr;
return entry.format(os);
}
-void WriteLogEntry::init(bool has_data, std::vector<WriteBufferAllocation>::iterator allocation,
- uint64_t current_sync_gen, uint64_t last_op_sequence_num, bool persist_on_flush) {
- ram_entry.has_data = 1;
+#ifdef WITH_RBD_RWL
+void WriteLogEntry::init_pmem_buffer(std::vector<WriteBufferAllocation>::iterator allocation) {
ram_entry.write_data = allocation->buffer_oid;
ceph_assert(!TOID_IS_NULL(ram_entry.write_data));
pmem_buffer = D_RW(ram_entry.write_data);
+}
+#endif
+
+void WriteLogEntry::init(bool has_data,
+ uint64_t current_sync_gen, uint64_t last_op_sequence_num, bool persist_on_flush) {
+ ram_entry.has_data = 1;
ram_entry.sync_gen_number = current_sync_gen;
if (persist_on_flush) {
/* Persist on flush. Sequence #0 is never used. */
~WriteLogEntry() override {};
WriteLogEntry(const WriteLogEntry&) = delete;
WriteLogEntry &operator=(const WriteLogEntry&) = delete;
- void init(bool has_data, std::vector<WriteBufferAllocation>::iterator allocation,
+ void init(bool has_data,
uint64_t current_sync_gen, uint64_t last_op_sequence_num, bool persist_on_flush);
+ #ifdef WITH_RBD_RWL
+ void init_pmem_buffer(std::vector<WriteBufferAllocation>::iterator allocation);
+ #endif
BlockExtent block_extent();
unsigned int reader_count() const;
/* Returns a ref to a bl containing bufferptrs to the entry pmem buffer */
void WriteLogOperation::init(bool has_data, std::vector<WriteBufferAllocation>::iterator allocation, uint64_t current_sync_gen,
uint64_t last_op_sequence_num, bufferlist &write_req_bl, uint64_t buffer_offset,
bool persist_on_flush) {
- log_entry->init(has_data, allocation, current_sync_gen, last_op_sequence_num, persist_on_flush);
+ log_entry->init(has_data, current_sync_gen, last_op_sequence_num, persist_on_flush);
buffer_alloc = &(*allocation);
bl.substr_of(write_req_bl, buffer_offset,
log_entry->write_bytes());
m_perfcounter->tinc(l_librbd_pwl_log_op_buf_to_app_t, log_append_time - buf_persist_time);
}
-void WriteLogOperation::copy_bl_to_pmem_buffer() {
+#ifdef WITH_RBD_RWL
+void WriteLogOperation::copy_bl_to_pmem_buffer(std::vector<WriteBufferAllocation>::iterator allocation) {
/* operation is a shared_ptr, so write_op is only good as long as operation is in scope */
bufferlist::iterator i(&bl);
m_perfcounter->inc(l_librbd_pwl_log_op_bytes, log_entry->write_bytes());
ldout(m_cct, 20) << bl << dendl;
+ log_entry->init_pmem_buffer(allocation);
i.copy((unsigned)log_entry->write_bytes(), (char*)log_entry->pmem_buffer);
}
buf_persist_time = ceph_clock_now();
pmemobj_flush(log_pool, log_entry->pmem_buffer, log_entry->write_bytes());
}
+#endif
WriteLogOperationSet::WriteLogOperationSet(utime_t dispatched, PerfCounters *perfcounter, std::shared_ptr<SyncPoint> sync_point,
bool persist_on_flush, CephContext *cct, Context *on_finish)
virtual bool is_writing_op() const {
return false;
}
- virtual void copy_bl_to_pmem_buffer() {};
+ #ifdef WITH_RBD_RWL
+ virtual void copy_bl_to_pmem_buffer(
+ std::vector<WriteBufferAllocation>::iterator allocation) {};
virtual void flush_pmem_buf_to_cache(PMEMobjpool *log_pool) {};
+ #endif
};
class SyncPointLogOperation : public GenericLogOperation {
}
void complete(int r) override;
- void copy_bl_to_pmem_buffer() override;
+ #ifdef WITH_RBD_RWL
+ void copy_bl_to_pmem_buffer(
+ std::vector<WriteBufferAllocation>::iterator allocation) override;
void flush_pmem_buf_to_cache(PMEMobjpool *log_pool) override;
+ #endif
};
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
-#include <libpmemobj.h>
#include "ReplicatedWriteLog.h"
#include "include/buffer.h"
#include "include/Context.h"
#include "librbd/asio/ContextWQ.h"
#include "librbd/cache/pwl/ImageCacheState.h"
#include "librbd/cache/pwl/LogEntry.h"
-#include "librbd/cache/pwl/Types.h"
#include <map>
#include <vector>
template <typename I>
ReplicatedWriteLog<I>::ReplicatedWriteLog(
I &image_ctx, librbd::cache::pwl::ImageCacheState<I>* cache_state)
-: AbstractWriteLog<I>(image_ctx, cache_state)
+: AbstractWriteLog<I>(image_ctx, cache_state),
+ m_pwl_pool_layout_name(POBJ_LAYOUT_NAME(rbd_pwl))
{
}
}
}
+template <typename I>
+void ReplicatedWriteLog<I>::copy_pmem(C_BlockIORequestT *req) {
+ req->copy_pmem();
+}
+
template <typename I>
bool ReplicatedWriteLog<I>::alloc_resources(C_BlockIORequestT *req) {
bool alloc_succeeds = true;
#ifndef CEPH_LIBRBD_CACHE_REPLICATED_WRITE_LOG
#define CEPH_LIBRBD_CACHE_REPLICATED_WRITE_LOG
+#include <libpmemobj.h>
#include "common/RWLock.h"
#include "common/WorkQueue.h"
#include "common/AsyncOpTracker.h"
using C_CompAndWriteRequestT = pwl::C_CompAndWriteRequest<This>;
PMEMobjpool *m_log_pool = nullptr;
+ const char* m_pwl_pool_layout_name;
void remove_pool_file();
void load_existing_entries(pwl::DeferredContexts &later);
using AbstractWriteLog<ImageCtxT>::m_first_valid_entry;
void process_work() override;
+ void copy_pmem(C_BlockIORequestT *req) override;
void schedule_append_ops(pwl::GenericLogOperations &ops) override;
void append_scheduled_ops(void) override;
void reserve_pmem(C_BlockIORequestT *req, bool &alloc_succeeds, bool &no_space) override;
#include "Request.h"
#include "librbd/BlockGuard.h"
#include "librbd/cache/pwl/LogEntry.h"
-#include "librbd/cache/pwl/ReplicatedWriteLog.h"
+#include "librbd/cache/pwl/AbstractWriteLog.h"
#define dout_subsys ceph_subsys_rbd_pwl
#undef dout_prefix
op_set->extent_ops_appending->activate();
op_set->extent_ops_persist->activate();
- /* Write data */
+ pwl.add_into_log_map(log_entries, this);
+}
+
+#ifdef WITH_RBD_RWL
+template <typename T>
+void C_WriteRequest<T>::copy_pmem() {
+ auto allocation = m_resources.buffers.begin();
for (auto &operation : op_set->operations) {
- operation->copy_bl_to_pmem_buffer();
+ operation->copy_bl_to_pmem_buffer(allocation);
+ allocation++;
}
- pwl.add_into_log_map(log_entries);
}
+#endif
template <typename T>
bool C_WriteRequest<T>::append_write_request(std::shared_ptr<SyncPoint> sync_point) {
discard_req->release_cell();
});
op->init(current_sync_gen, persist_on_flush, pwl.get_last_op_sequence_num(), on_write_persist);
- pwl.add_into_log_map(log_entries);
+ pwl.add_into_log_map(log_entries, this);
}
template <typename T>
virtual void dispatch() = 0;
+ virtual void copy_pmem() {};
+
virtual const char *get_name() const {
return "C_BlockIORequest";
}
void dispatch() override;
+ #ifdef WITH_RBD_RWL
+ void copy_pmem() override;
+ #endif
+
virtual std::shared_ptr<WriteLogOperation> create_operation(uint64_t offset, uint64_t len);
virtual void setup_log_operations(DeferredContexts &on_exit);
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_CACHE_SSD_TYPES_H
+#define CEPH_LIBRBD_CACHE_SSD_TYPES_H
+
+#include "acconfig.h"
+
+#include "librbd/io/Types.h"
+#include "Types.h" //generic type = to be renamed
+
+namespace librbd {
+namespace cache {
+namespace pwl {
+
+struct SuperBlock{
+ WriteLogPoolRoot root;
+
+ DENC(SuperBlock, v, p) {
+ DENC_START(1, 1, p);
+ denc(v.root, p);
+ DENC_FINISH(p);
+ }
+
+ void dump(Formatter *f) const {
+ f->dump_object("super", root);
+ }
+
+ static void generate_test_instances(list<SuperBlock*>& ls) {
+ ls.push_back(new SuperBlock);
+ ls.push_back(new SuperBlock);
+ ls.back()->root.first_valid_entry = 2;
+ }
+};
+
+} // namespace pwl
+} // namespace cache
+} // namespace librbd
+
+WRITE_CLASS_DENC(librbd::cache::pwl::SuperBlock)
+
+#endif // CEPH_LIBRBD_CACHE_SSD_TYPES_H
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "SSDWriteLog.h"
+#include "include/buffer.h"
+#include "include/Context.h"
+#include "include/ceph_assert.h"
+#include "common/deleter.h"
+#include "common/dout.h"
+#include "common/environment.h"
+#include "common/errno.h"
+#include "common/WorkQueue.h"
+#include "common/Timer.h"
+#include "common/perf_counters.h"
+#include "librbd/ImageCtx.h"
+#include "librbd/asio/ContextWQ.h"
+#include "librbd/cache/pwl/ImageCacheState.h"
+#include "librbd/cache/pwl/LogEntry.h"
+#include <map>
+#include <vector>
+
+#undef dout_subsys
+#define dout_subsys ceph_subsys_rbd_pwl
+#undef dout_prefix
+#define dout_prefix *_dout << "librbd::cache::pwl::SSDWriteLog: " << this << " " \
+ << __func__ << ": "
+
+namespace librbd {
+namespace cache {
+namespace pwl {
+
+using namespace librbd::cache::pwl;
+
+// SSD: this number can be updated later
+const unsigned long int ops_appended_together = MAX_WRITES_PER_SYNC_POINT;
+
+template <typename I>
+SSDWriteLog<I>::SSDWriteLog(
+ I &image_ctx, librbd::cache::pwl::ImageCacheState<I>* cache_state)
+ : AbstractWriteLog<I>(image_ctx, cache_state)
+{
+}
+
+template <typename I>
+void SSDWriteLog<I>::initialize_pool(Context *on_finish, pwl::DeferredContexts &later) {
+ CephContext *cct = m_image_ctx.cct;
+ ceph_assert(ceph_mutex_is_locked_by_me(m_lock));
+ if (access(this->m_log_pool_name.c_str(), F_OK) != 0) {
+ int fd = ::open(this->m_log_pool_name.c_str(), O_RDWR|O_CREAT, 0644);
+ bool succeed = true;
+ if (fd >= 0) {
+ if (truncate(this->m_log_pool_name.c_str(), this->m_log_pool_config_size) != 0) {
+ succeed = false;
+ }
+ ::close(fd);
+ } else {
+ succeed = false;
+ }
+ if (!succeed) {
+ m_cache_state->present = false;
+ m_cache_state->clean = true;
+ m_cache_state->empty = true;
+ /* TODO: filter/replace errnos that are meaningless to the caller */
+ on_finish->complete(-errno);
+ return;
+ }
+
+ bdev = BlockDevice::create(cct, this->m_log_pool_name, aio_cache_cb,
+ nullptr, nullptr, nullptr);
+ int r = bdev->open(this->m_log_pool_name);
+ if (r < 0) {
+ delete bdev;
+ on_finish->complete(-1);
+ return;
+ }
+ m_cache_state->present = true;
+ m_cache_state->clean = true;
+ m_cache_state->empty = true;
+ /* new pool, calculate and store metadata */
+ size_t small_write_size = MIN_WRITE_ALLOC_SIZE + sizeof(struct WriteLogPmemEntry);
+
+ uint64_t num_small_writes = (uint64_t)(this->m_log_pool_config_size / small_write_size);
+ if (num_small_writes > MAX_LOG_ENTRIES) {
+ num_small_writes = MAX_LOG_ENTRIES;
+ }
+ assert(num_small_writes > 2);
+ m_log_pool_ring_buffer_size = this->m_log_pool_config_size - DATA_RING_BUFFER_OFFSET;
+ /* Log ring empty */
+ m_first_free_entry = DATA_RING_BUFFER_OFFSET;
+ m_first_valid_entry = DATA_RING_BUFFER_OFFSET;
+
+ pool_size = this->m_log_pool_config_size;
+ auto new_root = std::make_shared<WriteLogPoolRoot>(pool_root);
+ new_root->pool_size = this->m_log_pool_config_size;
+ new_root->flushed_sync_gen = this->m_flushed_sync_gen;
+ new_root->block_size = MIN_WRITE_ALLOC_SIZE;
+ new_root->first_free_entry = m_first_free_entry;
+ new_root->first_valid_entry = m_first_valid_entry;
+ new_root->num_log_entries = num_small_writes;
+ pool_root = *new_root;
+
+ r = update_pool_root_sync(new_root);
+ if (r != 0) {
+ this->m_total_log_entries = 0;
+ this->m_free_log_entries = 0;
+ lderr(m_image_ctx.cct) << "failed to initialize pool ("
+ << this->m_log_pool_name << ")" << dendl;
+ on_finish->complete(r);
+ }
+ this->m_total_log_entries = new_root->num_log_entries;
+ this->m_free_log_entries = new_root->num_log_entries - 1;
+ } else {
+ m_cache_state->present = true;
+ bdev = BlockDevice::create(
+ cct, this->m_log_pool_name, aio_cache_cb,
+ static_cast<void*>(this), nullptr, static_cast<void*>(this));
+ int r = bdev->open(this->m_log_pool_name);
+ if (r < 0) {
+ delete bdev;
+ on_finish->complete(r);
+ return;
+ }
+ //load_existing_entries(later); #TODO: Implement and uncomment in later PR
+ if (m_first_free_entry < m_first_valid_entry) {
+ /* Valid entries wrap around the end of the ring, so first_free is lower
+ * than first_valid. If first_valid was == first_free+1, the entry at
+ * first_free would be empty. The last entry is never used, so in
+ * that case there would be zero free log entries. */
+ this->m_free_log_entries = this->m_total_log_entries -
+ (m_first_valid_entry - m_first_free_entry) - 1;
+ } else {
+ /* first_valid is <= first_free. If they are == we have zero valid log
+ * entries, and n-1 free log entries */
+ this->m_free_log_entries = this->m_total_log_entries -
+ (m_first_free_entry - m_first_valid_entry) - 1;
+ }
+ m_cache_state->clean = this->m_dirty_log_entries.empty();
+ m_cache_state->empty = m_log_entries.empty();
+ }
+}
+
+template <typename I>
+int SSDWriteLog<I>::update_pool_root_sync(
+ std::shared_ptr<WriteLogPoolRoot> root) {
+ bufferlist bl;
+ SuperBlock superblock;
+ superblock.root = *root;
+ encode(superblock, bl);
+ bl.append_zero(MIN_WRITE_ALLOC_SIZE - bl.length());
+ ceph_assert(bl.length() % MIN_WRITE_ALLOC_SIZE == 0);
+ return bdev->write(0, bl, false);
+}
+
+} // namespace pwl
+} // namespace cache
+} // namespace librbd
+
+template class librbd::cache::pwl::SSDWriteLog<librbd::ImageCtx>;
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_LIBRBD_CACHE_PWL_SSD_WRITE_LOG
+#define CEPH_LIBRBD_CACHE_PWL_SSD_WRITE_LOG
+
+#include "AbstractWriteLog.h"
+#include "blk/BlockDevice.h"
+#include "common/AsyncOpTracker.h"
+#include "common/Checksummer.h"
+#include "common/environment.h"
+#include "common/RWLock.h"
+#include "common/WorkQueue.h"
+#include "librbd/BlockGuard.h"
+#include "librbd/Utils.h"
+#include "librbd/cache/ImageWriteback.h"
+#include "librbd/cache/Types.h"
+#include "librbd/cache/pwl/LogMap.h"
+#include "librbd/cache/pwl/LogOperation.h"
+#include "librbd/cache/pwl/Request.h"
+#include "librbd/cache/pwl/SSDTypes.h"
+#include <functional>
+#include <list>
+
+namespace librbd {
+
+struct ImageCtx;
+
+namespace cache {
+
+namespace pwl {
+
+template <typename ImageCtxT>
+class SSDWriteLog : public AbstractWriteLog<ImageCtxT> {
+public:
+ SSDWriteLog(ImageCtxT &image_ctx,
+ librbd::cache::pwl::ImageCacheState<ImageCtxT>* cache_state);
+ ~SSDWriteLog() {}
+ SSDWriteLog(const SSDWriteLog&) = delete;
+ SSDWriteLog &operator=(const SSDWriteLog&) = delete;
+
+ using This = AbstractWriteLog<ImageCtxT>;
+ using C_BlockIORequestT = pwl::C_BlockIORequest<This>;
+
+ //TODO: Implement below functions in later PR
+ bool alloc_resources(C_BlockIORequestT *req) override { return false; }
+ void setup_schedule_append(
+ pwl::GenericLogOperationsVector &ops, bool do_early_flush) override {}
+
+protected:
+ using AbstractWriteLog<ImageCtxT>::m_lock;
+ using AbstractWriteLog<ImageCtxT>::m_log_entries;
+ using AbstractWriteLog<ImageCtxT>::m_image_ctx;
+ using AbstractWriteLog<ImageCtxT>::m_cache_state;
+ using AbstractWriteLog<ImageCtxT>::m_first_free_entry;
+ using AbstractWriteLog<ImageCtxT>::m_first_valid_entry;
+
+ void initialize_pool(Context *on_finish, pwl::DeferredContexts &later) override;
+ //TODO: Implement below functions in later PR
+ void process_work() override {}
+ void append_scheduled_ops(void) override {}
+ void schedule_append_ops(pwl::GenericLogOperations &ops) override {}
+ void remove_pool_file() override {}
+
+private:
+ uint64_t m_log_pool_ring_buffer_size; /* Size of ring buffer */
+
+ //classes and functions to faciliate block device operations
+ class AioTransContext {
+ public:
+ Context *on_finish;
+ ::IOContext ioc;
+ explicit AioTransContext(CephContext* cct, Context *cb)
+ :on_finish(cb), ioc(cct, this) {
+ }
+ ~AioTransContext(){}
+
+ void aio_finish() {
+ on_finish->complete(ioc.get_return_value());
+ delete this;
+ }
+ }; //class AioTransContext
+
+ BlockDevice *bdev = nullptr;
+ uint64_t pool_size;
+ pwl::WriteLogPoolRoot pool_root;
+
+ int update_pool_root_sync(std::shared_ptr<pwl::WriteLogPoolRoot> root);
+
+ static void aio_cache_cb(void *priv, void *priv2) {
+ AioTransContext *c = static_cast<AioTransContext*>(priv2);
+ c->aio_finish();
+ }
+};//class SSDWriteLog
+
+} // namespace pwl
+} // namespace cache
+} // namespace librbd
+
+extern template class librbd::cache::pwl::SSDWriteLog<librbd::ImageCtx>;
+
+#endif // CEPH_LIBRBD_CACHE_PWL_SSD_WRITE_LOG
#undef dout_prefix
#define dout_prefix *_dout << "librbd::cache::pwl::Types: " << this << " " \
<< __func__ << ": "
+using ceph::Formatter;
namespace librbd {
-
namespace cache {
-
namespace pwl {
DeferredContexts::~DeferredContexts() {
return write_bytes;
}
+#ifdef WITH_RBD_SSD_CACHE
+void WriteLogPmemEntry::dump(Formatter *f) const {
+ f->dump_unsigned("sync_gen_number", sync_gen_number);
+ f->dump_unsigned("write_sequence_number", write_sequence_number);
+ f->dump_unsigned("image_offset_bytes", image_offset_bytes);
+ f->dump_unsigned("write_bytes", write_bytes);
+ f->dump_unsigned("write_data_pos", write_data_pos);
+ f->dump_unsigned("entry_valid", entry_valid);
+ f->dump_unsigned("sync_point", sync_point);
+ f->dump_unsigned("sequenced", sequenced);
+ f->dump_unsigned("has_data", has_data);
+ f->dump_unsigned("discard", discard);
+ f->dump_unsigned("writesame", writesame);
+ f->dump_unsigned("ws_datalen", ws_datalen);
+ f->dump_unsigned("entry_index", entry_index);
+}
+
+void WriteLogPmemEntry::generate_test_instances(list<WriteLogPmemEntry*>& ls) {
+ ls.push_back(new WriteLogPmemEntry);
+ ls.push_back(new WriteLogPmemEntry);
+ ls.back()->sync_gen_number = 1;
+ ls.back()->write_sequence_number = 1;
+ ls.back()->image_offset_bytes = 1;
+ ls.back()->write_bytes = 1;
+ ls.back()->write_data_pos = 1;
+ ls.back()->entry_valid = 1;
+ ls.back()->sync_point = 1;
+ ls.back()->sequenced = 1;
+ ls.back()->has_data = 1;
+ ls.back()->discard = 1;
+ ls.back()->writesame = 1;
+ ls.back()->ws_datalen = 1;
+ ls.back()->entry_index = 1;
+}
+
+void WriteLogPoolRoot::dump(Formatter *f) const {
+ f->dump_unsigned("layout_version", layout_version);
+ f->dump_unsigned("cur_sync_gen", cur_sync_gen);
+ f->dump_unsigned("pool_size", pool_size);
+ f->dump_unsigned("flushed_sync_gen", flushed_sync_gen);
+ f->dump_unsigned("block_size", block_size);
+ f->dump_unsigned("num_log_entries", num_log_entries);
+ f->dump_unsigned("first_free_entry", first_free_entry);
+ f->dump_unsigned("first_valid_entry", first_valid_entry); }
+
+void WriteLogPoolRoot::generate_test_instances(list<WriteLogPoolRoot*>& ls) {
+ ls.push_back(new WriteLogPoolRoot);
+ ls.push_back(new WriteLogPoolRoot);
+ ls.back()->layout_version = 2;
+ ls.back()->cur_sync_gen = 1;
+ ls.back()->pool_size = 1024;
+ ls.back()->flushed_sync_gen = 1;
+ ls.back()->block_size = 4096;
+ ls.back()->num_log_entries = 10000000;
+ ls.back()->first_free_entry = 1;
+ ls.back()->first_valid_entry = 0;
+}
+#endif
+
std::ostream& operator<<(std::ostream& os,
const WriteLogPmemEntry &entry) {
os << "entry_valid=" << (bool)entry.entry_valid << ", "
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
-#ifndef CEPH_LIBRBD_CACHE_RWL_TYPES_H
-#define CEPH_LIBRBD_CACHE_RWL_TYPES_H
+#ifndef CEPH_LIBRBD_CACHE_PWL_TYPES_H
+#define CEPH_LIBRBD_CACHE_PWL_TYPES_H
+
+#include "acconfig.h"
+
+#ifdef WITH_RBD_RWL
+#include "libpmemobj.h"
+#endif
#include <vector>
-#include <libpmemobj.h>
#include "librbd/BlockGuard.h"
#include "librbd/io/Types.h"
+namespace ceph {
+class Formatter;
+}
+
class Context;
enum {
const uint64_t MAX_BYTES_PER_SYNC_POINT = (1024 * 1024 * 8);
const uint32_t MIN_WRITE_ALLOC_SIZE = 512;
+const uint32_t MIN_WRITE_ALLOC_SSD_SIZE = 4096;
const uint32_t LOG_STATS_INTERVAL_SECONDS = 5;
/**** Write log entries ****/
const double RETIRE_HIGH_WATER = 0.50;
const double RETIRE_LOW_WATER = 0.40;
const int RETIRE_BATCH_TIME_LIMIT_MS = 250;
+const uint64_t CONTROL_BLOCK_MAX_LOG_ENTRIES = 32;
+const uint64_t SPAN_MAX_DATA_LEN = (16*1024*1024);
+
+/* offset of ring on SSD */
+const uint64_t DATA_RING_BUFFER_OFFSET = 8192;
/* Defer a set of Contexts until destruct/exit. Used for deferring
* work on a given thread until a required lock is dropped. */
};
/* Pmem structures */
+#ifdef WITH_RBD_RWL
POBJ_LAYOUT_BEGIN(rbd_pwl);
POBJ_LAYOUT_ROOT(rbd_pwl, struct WriteLogPoolRoot);
POBJ_LAYOUT_TOID(rbd_pwl, uint8_t);
POBJ_LAYOUT_TOID(rbd_pwl, struct WriteLogPmemEntry);
POBJ_LAYOUT_END(rbd_pwl);
+#endif
struct WriteLogPmemEntry {
uint64_t sync_gen_number = 0;
uint64_t write_sequence_number = 0;
uint64_t image_offset_bytes;
uint64_t write_bytes;
+ #ifdef WITH_RBD_RWL
TOID(uint8_t) write_data;
- struct {
- uint8_t entry_valid :1; /* if 0, this entry is free */
- uint8_t sync_point :1; /* No data. No write sequence number. Marks sync
- point for this sync gen number */
- uint8_t sequenced :1; /* write sequence number is valid */
- uint8_t has_data :1; /* write_data field is valid (else ignore) */
- uint8_t discard :1; /* has_data will be 0 if this is a discard */
- uint8_t writesame :1; /* ws_datalen indicates length of data at write_bytes */
+ #endif
+ #ifdef WITH_RBD_SSD_CACHE
+ uint64_t write_data_pos; /* SSD data offset */
+ #endif
+ union {
+ uint8_t flags;
+ struct {
+ uint8_t entry_valid :1; /* if 0, this entry is free */
+ uint8_t sync_point :1; /* No data. No write sequence number. Marks sync
+ point for this sync gen number */
+ uint8_t sequenced :1; /* write sequence number is valid */
+ uint8_t has_data :1; /* write_data field is valid (else ignore) */
+ uint8_t discard :1; /* has_data will be 0 if this is a discard */
+ uint8_t writesame :1; /* ws_datalen indicates length of data at write_bytes */
+ };
};
uint32_t ws_datalen = 0; /* Length of data buffer (writesame only) */
uint32_t entry_index = 0; /* For debug consistency check. Can be removed if
* we need the space */
- WriteLogPmemEntry(const uint64_t image_offset_bytes, const uint64_t write_bytes)
+ WriteLogPmemEntry(const uint64_t image_offset_bytes=0, const uint64_t write_bytes=0)
: image_offset_bytes(image_offset_bytes), write_bytes(write_bytes),
entry_valid(0), sync_point(0), sequenced(0), has_data(0), discard(0), writesame(0) {
}
}
friend std::ostream& operator<<(std::ostream& os,
const WriteLogPmemEntry &entry);
+ #ifdef WITH_RBD_SSD_CACHE
+ DENC(WriteLogPmemEntry, v, p) {
+ DENC_START(1, 1, p);
+ denc(v.sync_gen_number, p);
+ denc(v.write_sequence_number, p);
+ denc(v.image_offset_bytes, p);
+ denc(v.write_bytes, p);
+ denc(v.write_data_pos, p);
+ denc(v.flags, p);
+ denc(v.ws_datalen, p);
+ denc(v.entry_index, p);
+ DENC_FINISH(p);
+ }
+ #endif
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(list<WriteLogPmemEntry*>& ls);
};
-static_assert(sizeof(WriteLogPmemEntry) == 64);
-
struct WriteLogPoolRoot {
+ #ifdef WITH_RBD_RWL
union {
struct {
uint8_t layout_version; /* Version of this structure (RWL_POOL_VERSION) */
uint64_t _u64;
} header;
TOID(struct WriteLogPmemEntry) log_entries; /* contiguous array of log entries */
+ #endif
+ #ifdef WITH_RBD_SSD_CACHE
+ uint64_t layout_version = 0;
+ uint64_t cur_sync_gen = 0;
+ #endif
uint64_t pool_size;
uint64_t flushed_sync_gen; /* All writing entries with this or a lower
* sync gen number are flushed. */
uint32_t num_log_entries;
uint32_t first_free_entry; /* Entry following the newest valid entry */
uint32_t first_valid_entry; /* Index of the oldest valid entry in the log */
+
+ #ifdef WITH_RBD_SSD_CACHE
+ DENC(WriteLogPoolRoot, v, p) {
+ DENC_START(1, 1, p);
+ denc(v.layout_version, p);
+ denc(v.cur_sync_gen, p);
+ denc(v.pool_size, p);
+ denc(v.flushed_sync_gen, p);
+ denc(v.block_size, p);
+ denc(v.num_log_entries, p);
+ denc(v.first_free_entry, p);
+ denc(v.first_valid_entry, p);
+ DENC_FINISH(p);
+ }
+ #endif
+
+ void dump(ceph::Formatter *f) const;
+ static void generate_test_instances(list<WriteLogPoolRoot*>& ls);
};
struct WriteBufferAllocation {
unsigned int allocation_size = 0;
+ #ifdef WITH_RBD_RWL
pobj_action buffer_alloc_action;
TOID(uint8_t) buffer_oid = OID_NULL;
+ #endif
bool allocated = false;
utime_t allocation_lat;
};
} // namespace cache
} // namespace librbd
-#endif // CEPH_LIBRBD_CACHE_RWL_TYPES_H
+#ifdef WITH_RBD_SSD_CACHE
+WRITE_CLASS_DENC(librbd::cache::pwl::WriteLogPmemEntry)
+WRITE_CLASS_DENC(librbd::cache::pwl::WriteLogPoolRoot)
+#endif
+
+#endif // CEPH_LIBRBD_CACHE_PWL_TYPES_H
if(WITH_RBD_RWL)
set(unittest_librbd_srcs
${unittest_librbd_srcs}
- cache/test_mock_ReplicatedWriteLog.cc
+ cache/pwl/test_mock_ReplicatedWriteLog.cc
cache/pwl/test_WriteLogMap.cc)
endif(WITH_RBD_RWL)
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <iostream>
+#include "common/hostname.h"
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/test_support.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/cache/pwl/AbstractWriteLog.h"
+#include "librbd/cache/pwl/ImageCacheState.h"
+#include "librbd/cache/pwl/Types.h"
+#include "librbd/cache/ImageWriteback.h"
+
+namespace librbd {
+namespace {
+
+struct MockContextRWL : public C_SaferCond {
+ MOCK_METHOD1(complete, void(int));
+ MOCK_METHOD1(finish, void(int));
+
+ void do_complete(int r) {
+ C_SaferCond::complete(r);
+ }
+};
+
+} // anonymous namespace
+
+namespace util {
+
+inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
+ return image_ctx->image_ctx;
+}
+
+} // namespace util
+} // namespace librbd
+
+#include "librbd/cache/pwl/AbstractWriteLog.cc"
+#include "librbd/cache/pwl/ReplicatedWriteLog.cc"
+
+// template definitions
+#include "librbd/cache/ImageWriteback.cc"
+#include "librbd/cache/pwl/ImageCacheState.cc"
+#include "librbd/cache/pwl/Request.cc"
+
+namespace librbd {
+namespace cache {
+namespace pwl {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Invoke;
+
+struct TestMockCacheReplicatedWriteLog : public TestMockFixture {
+ typedef librbd::cache::pwl::ReplicatedWriteLog<librbd::MockImageCtx> MockReplicatedWriteLog;
+ typedef librbd::cache::pwl::ImageCacheState<librbd::MockImageCtx> MockImageCacheStateRWL;
+
+ MockImageCacheStateRWL *get_cache_state(MockImageCtx& mock_image_ctx) {
+ MockImageCacheStateRWL *rwl_state = new MockImageCacheStateRWL(&mock_image_ctx);
+ return rwl_state;
+ }
+
+ void validate_cache_state(librbd::ImageCtx *image_ctx,
+ MockImageCacheStateRWL &state,
+ bool present, bool empty, bool clean,
+ string host, string path,
+ uint64_t size) {
+ ConfigProxy &config = image_ctx->config;
+ ASSERT_EQ(present, state.present);
+ ASSERT_EQ(empty, state.empty);
+ ASSERT_EQ(clean, state.clean);
+
+ ASSERT_EQ(host, state.host);
+ ASSERT_EQ(path, state.path);
+ ASSERT_EQ(size, state.size);
+ ASSERT_EQ(config.get_val<bool>("rbd_rwl_log_periodic_stats"),
+ state.log_periodic_stats);
+ }
+
+ void expect_op_work_queue(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.op_work_queue, queue(_, _))
+ .WillRepeatedly(Invoke([](Context* ctx, int r) {
+ ctx->complete(r);
+ }));
+ }
+
+ void expect_context_complete(MockContextRWL& mock_context, int r) {
+ EXPECT_CALL(mock_context, complete(r))
+ .WillRepeatedly(Invoke([&mock_context](int r) {
+ mock_context.do_complete(r);
+ }));
+ }
+
+ void expect_metadata_set(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_set(_, _, _))
+ .WillRepeatedly(Invoke([](std::string key, std::string val, Context* ctx) {
+ ctx->complete(0);
+ }));
+ }
+
+ void expect_metadata_remove(MockImageCtx& mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_remove(_, _))
+ .WillRepeatedly(Invoke([](std::string key, Context* ctx) {
+ ctx->complete(0);
+ }));
+ }
+};
+
+TEST_F(TestMockCacheReplicatedWriteLog, init_state_write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockImageCacheStateRWL image_cache_state(&mock_image_ctx);
+
+ validate_cache_state(ictx, image_cache_state, false, true, true, "", "", 0);
+
+ image_cache_state.empty = false;
+ image_cache_state.clean = false;
+ MockContextRWL finish_ctx;
+ expect_metadata_set(mock_image_ctx);
+ expect_context_complete(finish_ctx, 0);
+ image_cache_state.write_image_cache_state(&finish_ctx);
+ ASSERT_EQ(0, finish_ctx.wait());
+}
+
+static void get_jf(const string& s, JSONFormattable *f)
+{
+ JSONParser p;
+ bool result = p.parse(s.c_str(), s.size());
+ if (!result) {
+ cout << "Failed to parse: '" << s << "'" << std::endl;
+ }
+ ASSERT_EQ(true, result);
+ try {
+ decode_json_obj(*f, &p);
+ } catch (JSONDecoder::err& e) {
+ ASSERT_TRUE(0 == "Failed to decode JSON object");
+ }
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, init_state_json_write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+
+ JSONFormattable f;
+ string strf = "{ \"present\": \"1\", \"empty\": \"0\", \"clean\": \"0\", \
+ \"pwl_host\": \"testhost\", \
+ \"pwl_path\": \"/tmp\", \
+ \"pwl_size\": \"1024\" }";
+ get_jf(strf, &f);
+ MockImageCacheStateRWL image_cache_state(&mock_image_ctx, f);
+
+ validate_cache_state(ictx, image_cache_state, true, false, false,
+ "testhost", "/tmp", 1024);
+
+ MockContextRWL finish_ctx;
+ expect_metadata_remove(mock_image_ctx);
+ expect_context_complete(finish_ctx, 0);
+ image_cache_state.clear_image_cache_state(&finish_ctx);
+ ASSERT_EQ(0, finish_ctx.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, init_shutdown) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ MockContextRWL finish_ctx1;
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ rwl.shut_down(&finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, write) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+
+ MockContextRWL finish_ctx1;
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(&finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush_source_shutdown) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(io::FLUSH_SOURCE_SHUTDOWN, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush_source_internal) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(io::FLUSH_SOURCE_INTERNAL, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, flush_source_user) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ usleep(10000);
+ MockContextRWL finish_ctx_flush;
+ expect_context_complete(finish_ctx_flush, 0);
+ rwl.flush(io::FLUSH_SOURCE_USER, &finish_ctx_flush);
+ ASSERT_EQ(0, finish_ctx_flush.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, read_hit_rwl_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ expect_context_complete(finish_ctx_read, 0);
+ Extents image_extents_read{{0, 4096}};
+ bufferlist read_bl;
+ rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl_copy.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, read_hit_part_rwl_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ Extents image_extents_read{{512, 4096}};
+ bufferlist hit_bl;
+ bl_copy.begin(511).copy(4096-512, hit_bl);
+ expect_context_complete(finish_ctx_read, 512);
+ bufferlist read_bl;
+ rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(512, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ bufferlist read_bl_hit;
+ read_bl.begin(0).copy(4096-512, read_bl_hit);
+ ASSERT_TRUE(hit_bl.contents_equal(read_bl_hit));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, read_miss_rwl_cache) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ Extents image_extents_read{{4096, 4096}};
+ expect_context_complete(finish_ctx_read, 4096);
+ bufferlist read_bl;
+ ASSERT_EQ(0, read_bl.length());
+ rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(4096, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, discard) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_discard;
+ expect_context_complete(finish_ctx_discard, 0);
+ rwl.discard(0, 4096, 1, &finish_ctx_discard);
+ ASSERT_EQ(0, finish_ctx_discard.wait());
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(read_bl.is_zero());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, writesame) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ bufferlist bl, test_bl;
+ bl.append(std::string(512, '1'));
+ test_bl.append(std::string(4096, '1'));
+ int fadvise_flags = 0;
+ rwl.writesame(0, 4096, std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(test_bl.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, invalidate) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl;
+ bl.append(std::string(4096, '1'));
+ bufferlist bl_copy = bl;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_invalidate;
+ expect_context_complete(finish_ctx_invalidate, 0);
+ rwl.invalidate(&finish_ctx_invalidate);
+ ASSERT_EQ(0, finish_ctx_invalidate.wait());
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, compare_and_write_compare_matched) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl1;
+ bl1.append(std::string(4096, '1'));
+ bufferlist com_bl = bl1;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_cw;
+ bufferlist bl2;
+ bl2.append(std::string(4096, '2'));
+ bufferlist bl2_copy = bl2;
+ uint64_t mismatch_offset = -1;
+ expect_context_complete(finish_ctx_cw, 0);
+ rwl.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
+ &mismatch_offset, fadvise_flags, &finish_ctx_cw);
+ ASSERT_EQ(0, finish_ctx_cw.wait());
+ ASSERT_EQ(0, mismatch_offset);
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl2_copy.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+TEST_F(TestMockCacheReplicatedWriteLog, compare_and_write_compare_failed) {
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
+ expect_op_work_queue(mock_image_ctx);
+ expect_metadata_set(mock_image_ctx);
+
+ MockContextRWL finish_ctx1;
+ expect_context_complete(finish_ctx1, 0);
+ rwl.init(&finish_ctx1);
+ ASSERT_EQ(0, finish_ctx1.wait());
+
+ MockContextRWL finish_ctx2;
+ expect_context_complete(finish_ctx2, 0);
+ Extents image_extents{{0, 4096}};
+ bufferlist bl1;
+ bl1.append(std::string(4096, '1'));
+ bufferlist bl1_copy = bl1;
+ int fadvise_flags = 0;
+ rwl.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
+ ASSERT_EQ(0, finish_ctx2.wait());
+
+ MockContextRWL finish_ctx_cw;
+ bufferlist bl2;
+ bl2.append(std::string(4096, '2'));
+ bufferlist com_bl = bl2;
+ uint64_t mismatch_offset = -1;
+ expect_context_complete(finish_ctx_cw, -EILSEQ);
+ rwl.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
+ &mismatch_offset, fadvise_flags, &finish_ctx_cw);
+ ASSERT_EQ(-EILSEQ, finish_ctx_cw.wait());
+ ASSERT_EQ(0, mismatch_offset);
+
+ MockContextRWL finish_ctx_read;
+ bufferlist read_bl;
+ expect_context_complete(finish_ctx_read, 0);
+ rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
+ ASSERT_EQ(0, finish_ctx_read.wait());
+ ASSERT_EQ(4096, read_bl.length());
+ ASSERT_TRUE(bl1_copy.contents_equal(read_bl));
+
+ MockContextRWL finish_ctx3;
+ expect_context_complete(finish_ctx3, 0);
+ rwl.shut_down(&finish_ctx3);
+ ASSERT_EQ(0, finish_ctx3.wait());
+}
+
+} // namespace pwl
+} // namespace cache
+} // namespace librbd
+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-
-#include <iostream>
-#include "common/hostname.h"
-#include "test/librbd/test_mock_fixture.h"
-#include "test/librbd/test_support.h"
-#include "test/librbd/mock/MockImageCtx.h"
-#include "include/rbd/librbd.hpp"
-#include "librbd/cache/pwl/AbstractWriteLog.h"
-#include "librbd/cache/pwl/ImageCacheState.h"
-#include "librbd/cache/pwl/Types.h"
-#include "librbd/cache/ImageWriteback.h"
-
-
-namespace librbd {
-namespace {
-
-struct MockContextRWL : public C_SaferCond {
- MOCK_METHOD1(complete, void(int));
- MOCK_METHOD1(finish, void(int));
-
- void do_complete(int r) {
- C_SaferCond::complete(r);
- }
-};
-
-} // anonymous namespace
-
-namespace util {
-
-inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) {
- return image_ctx->image_ctx;
-}
-
-} // namespace util
-} // namespace librbd
-
-#include "librbd/cache/pwl/AbstractWriteLog.cc"
-#include "librbd/cache/pwl/ReplicatedWriteLog.cc"
-
-// template definitions
-#include "librbd/cache/ImageWriteback.cc"
-#include "librbd/cache/pwl/ImageCacheState.cc"
-#include "librbd/cache/pwl/Request.cc"
-
-namespace librbd {
-namespace cache {
-namespace pwl {
-
-using ::testing::_;
-using ::testing::DoDefault;
-using ::testing::InSequence;
-using ::testing::Invoke;
-
-struct TestMockCacheReplicatedWriteLog : public TestMockFixture {
- typedef librbd::cache::pwl::ReplicatedWriteLog<librbd::MockImageCtx> MockReplicatedWriteLog;
- typedef librbd::cache::pwl::ImageCacheState<librbd::MockImageCtx> MockImageCacheStateRWL;
-
- MockImageCacheStateRWL *get_cache_state(MockImageCtx& mock_image_ctx) {
- MockImageCacheStateRWL *rwl_state = new MockImageCacheStateRWL(&mock_image_ctx);
- return rwl_state;
- }
-
- void validate_cache_state(librbd::ImageCtx *image_ctx,
- MockImageCacheStateRWL &state,
- bool present, bool empty, bool clean,
- string host, string path,
- uint64_t size) {
- ConfigProxy &config = image_ctx->config;
- ASSERT_EQ(present, state.present);
- ASSERT_EQ(empty, state.empty);
- ASSERT_EQ(clean, state.clean);
-
- ASSERT_EQ(host, state.host);
- ASSERT_EQ(path, state.path);
- ASSERT_EQ(size, state.size);
- ASSERT_EQ(config.get_val<bool>("rbd_rwl_log_periodic_stats"),
- state.log_periodic_stats);
- }
-
- void expect_op_work_queue(MockImageCtx& mock_image_ctx) {
- EXPECT_CALL(*mock_image_ctx.op_work_queue, queue(_, _))
- .WillRepeatedly(Invoke([](Context* ctx, int r) {
- ctx->complete(r);
- }));
- }
-
- void expect_context_complete(MockContextRWL& mock_context, int r) {
- EXPECT_CALL(mock_context, complete(r))
- .WillRepeatedly(Invoke([&mock_context](int r) {
- mock_context.do_complete(r);
- }));
- }
-
- void expect_metadata_set(MockImageCtx& mock_image_ctx) {
- EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_set(_, _, _))
- .WillRepeatedly(Invoke([](std::string key, std::string val, Context* ctx) {
- ctx->complete(0);
- }));
- }
-
- void expect_metadata_remove(MockImageCtx& mock_image_ctx) {
- EXPECT_CALL(*mock_image_ctx.operations, execute_metadata_remove(_, _))
- .WillRepeatedly(Invoke([](std::string key, Context* ctx) {
- ctx->complete(0);
- }));
- }
-};
-
-TEST_F(TestMockCacheReplicatedWriteLog, init_state_write) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockImageCacheStateRWL image_cache_state(&mock_image_ctx);
-
- validate_cache_state(ictx, image_cache_state, false, true, true, "", "", 0);
-
- image_cache_state.empty = false;
- image_cache_state.clean = false;
- MockContextRWL finish_ctx;
- expect_metadata_set(mock_image_ctx);
- expect_context_complete(finish_ctx, 0);
- image_cache_state.write_image_cache_state(&finish_ctx);
- ASSERT_EQ(0, finish_ctx.wait());
-}
-
-static void get_jf(const string& s, JSONFormattable *f)
-{
- JSONParser p;
- bool result = p.parse(s.c_str(), s.size());
- if (!result) {
- cout << "Failed to parse: '" << s << "'" << std::endl;
- }
- ASSERT_EQ(true, result);
- try {
- decode_json_obj(*f, &p);
- } catch (JSONDecoder::err& e) {
- ASSERT_TRUE(0 == "Failed to decode JSON object");
- }
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, init_state_json_write) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
-
- JSONFormattable f;
- string strf = "{ \"present\": \"1\", \"empty\": \"0\", \"clean\": \"0\", \
- \"pwl_host\": \"testhost\", \
- \"pwl_path\": \"/tmp\", \
- \"pwl_size\": \"1024\" }";
- get_jf(strf, &f);
- MockImageCacheStateRWL image_cache_state(&mock_image_ctx, f);
-
- validate_cache_state(ictx, image_cache_state, true, false, false,
- "testhost", "/tmp", 1024);
-
- MockContextRWL finish_ctx;
- expect_metadata_remove(mock_image_ctx);
- expect_context_complete(finish_ctx, 0);
- image_cache_state.clear_image_cache_state(&finish_ctx);
- ASSERT_EQ(0, finish_ctx.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, init_shutdown) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- MockContextRWL finish_ctx1;
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- rwl.shut_down(&finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, write) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
-
- MockContextRWL finish_ctx1;
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl;
- bl.append(std::string(4096, '1'));
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, flush) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl;
- bl.append(std::string(4096, '1'));
- bufferlist bl_copy = bl;
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx_flush;
- expect_context_complete(finish_ctx_flush, 0);
- rwl.flush(&finish_ctx_flush);
- ASSERT_EQ(0, finish_ctx_flush.wait());
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
-
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, flush_source_shutdown) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl;
- bl.append(std::string(4096, '1'));
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx_flush;
- expect_context_complete(finish_ctx_flush, 0);
- rwl.flush(io::FLUSH_SOURCE_SHUTDOWN, &finish_ctx_flush);
- ASSERT_EQ(0, finish_ctx_flush.wait());
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, flush_source_internal) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl;
- bl.append(std::string(4096, '1'));
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx_flush;
- expect_context_complete(finish_ctx_flush, 0);
- rwl.flush(io::FLUSH_SOURCE_INTERNAL, &finish_ctx_flush);
- ASSERT_EQ(0, finish_ctx_flush.wait());
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, flush_source_user) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl;
- bl.append(std::string(4096, '1'));
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- usleep(10000);
- MockContextRWL finish_ctx_flush;
- expect_context_complete(finish_ctx_flush, 0);
- rwl.flush(io::FLUSH_SOURCE_USER, &finish_ctx_flush);
- ASSERT_EQ(0, finish_ctx_flush.wait());
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, read_hit_rwl_cache) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl;
- bl.append(std::string(4096, '1'));
- bufferlist bl_copy = bl;
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx_read;
- expect_context_complete(finish_ctx_read, 0);
- Extents image_extents_read{{0, 4096}};
- bufferlist read_bl;
- rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
- ASSERT_EQ(0, finish_ctx_read.wait());
- ASSERT_EQ(4096, read_bl.length());
- ASSERT_TRUE(bl_copy.contents_equal(read_bl));
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, read_hit_part_rwl_cache) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl;
- bl.append(std::string(4096, '1'));
- bufferlist bl_copy = bl;
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx_read;
- Extents image_extents_read{{512, 4096}};
- bufferlist hit_bl;
- bl_copy.begin(511).copy(4096-512, hit_bl);
- expect_context_complete(finish_ctx_read, 512);
- bufferlist read_bl;
- rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
- ASSERT_EQ(512, finish_ctx_read.wait());
- ASSERT_EQ(4096, read_bl.length());
- bufferlist read_bl_hit;
- read_bl.begin(0).copy(4096-512, read_bl_hit);
- ASSERT_TRUE(hit_bl.contents_equal(read_bl_hit));
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, read_miss_rwl_cache) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl;
- bl.append(std::string(4096, '1'));
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx_read;
- Extents image_extents_read{{4096, 4096}};
- expect_context_complete(finish_ctx_read, 4096);
- bufferlist read_bl;
- ASSERT_EQ(0, read_bl.length());
- rwl.read(std::move(image_extents_read), &read_bl, fadvise_flags, &finish_ctx_read);
- ASSERT_EQ(4096, finish_ctx_read.wait());
- ASSERT_EQ(4096, read_bl.length());
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, discard) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl;
- bl.append(std::string(4096, '1'));
- bufferlist bl_copy = bl;
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx_discard;
- expect_context_complete(finish_ctx_discard, 0);
- rwl.discard(0, 4096, 1, &finish_ctx_discard);
- ASSERT_EQ(0, finish_ctx_discard.wait());
-
- MockContextRWL finish_ctx_read;
- bufferlist read_bl;
- expect_context_complete(finish_ctx_read, 0);
- rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
- ASSERT_EQ(0, finish_ctx_read.wait());
- ASSERT_EQ(4096, read_bl.length());
- ASSERT_TRUE(read_bl.is_zero());
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
-
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, writesame) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- bufferlist bl, test_bl;
- bl.append(std::string(512, '1'));
- test_bl.append(std::string(4096, '1'));
- int fadvise_flags = 0;
- rwl.writesame(0, 4096, std::move(bl), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx_read;
- bufferlist read_bl;
- expect_context_complete(finish_ctx_read, 0);
- rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
- ASSERT_EQ(0, finish_ctx_read.wait());
- ASSERT_EQ(4096, read_bl.length());
- ASSERT_TRUE(test_bl.contents_equal(read_bl));
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
-
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, invalidate) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl;
- bl.append(std::string(4096, '1'));
- bufferlist bl_copy = bl;
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx_invalidate;
- expect_context_complete(finish_ctx_invalidate, 0);
- rwl.invalidate(&finish_ctx_invalidate);
- ASSERT_EQ(0, finish_ctx_invalidate.wait());
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
-
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, compare_and_write_compare_matched) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl1;
- bl1.append(std::string(4096, '1'));
- bufferlist com_bl = bl1;
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx_cw;
- bufferlist bl2;
- bl2.append(std::string(4096, '2'));
- bufferlist bl2_copy = bl2;
- uint64_t mismatch_offset = -1;
- expect_context_complete(finish_ctx_cw, 0);
- rwl.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
- &mismatch_offset, fadvise_flags, &finish_ctx_cw);
- ASSERT_EQ(0, finish_ctx_cw.wait());
- ASSERT_EQ(0, mismatch_offset);
-
- MockContextRWL finish_ctx_read;
- bufferlist read_bl;
- expect_context_complete(finish_ctx_read, 0);
- rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
- ASSERT_EQ(0, finish_ctx_read.wait());
- ASSERT_EQ(4096, read_bl.length());
- ASSERT_TRUE(bl2_copy.contents_equal(read_bl));
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
-
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-TEST_F(TestMockCacheReplicatedWriteLog, compare_and_write_compare_failed) {
- librbd::ImageCtx *ictx;
- ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
- MockImageCtx mock_image_ctx(*ictx);
- MockReplicatedWriteLog rwl(mock_image_ctx, get_cache_state(mock_image_ctx));
- expect_op_work_queue(mock_image_ctx);
- expect_metadata_set(mock_image_ctx);
-
- MockContextRWL finish_ctx1;
- expect_context_complete(finish_ctx1, 0);
- rwl.init(&finish_ctx1);
- ASSERT_EQ(0, finish_ctx1.wait());
-
- MockContextRWL finish_ctx2;
- expect_context_complete(finish_ctx2, 0);
- Extents image_extents{{0, 4096}};
- bufferlist bl1;
- bl1.append(std::string(4096, '1'));
- bufferlist bl1_copy = bl1;
- int fadvise_flags = 0;
- rwl.write(std::move(image_extents), std::move(bl1), fadvise_flags, &finish_ctx2);
- ASSERT_EQ(0, finish_ctx2.wait());
-
- MockContextRWL finish_ctx_cw;
- bufferlist bl2;
- bl2.append(std::string(4096, '2'));
- bufferlist com_bl = bl2;
- uint64_t mismatch_offset = -1;
- expect_context_complete(finish_ctx_cw, -EILSEQ);
- rwl.compare_and_write({{0, 4096}}, std::move(com_bl), std::move(bl2),
- &mismatch_offset, fadvise_flags, &finish_ctx_cw);
- ASSERT_EQ(-EILSEQ, finish_ctx_cw.wait());
- ASSERT_EQ(0, mismatch_offset);
-
- MockContextRWL finish_ctx_read;
- bufferlist read_bl;
- expect_context_complete(finish_ctx_read, 0);
- rwl.read({{0, 4096}}, &read_bl, fadvise_flags, &finish_ctx_read);
- ASSERT_EQ(0, finish_ctx_read.wait());
- ASSERT_EQ(4096, read_bl.length());
- ASSERT_TRUE(bl1_copy.contents_equal(read_bl));
-
- MockContextRWL finish_ctx3;
- expect_context_complete(finish_ctx3, 0);
- rwl.shut_down(&finish_ctx3);
- ASSERT_EQ(0, finish_ctx3.wait());
-}
-
-} // namespace pwl
-} // namespace cache
-} // namespace librbd
TYPE(rbd::mirror::image_map::PolicyData)
#endif
+#if defined(WITH_RBD) && defined(WITH_RBD_SSD_CACHE)
+#include "librbd/cache/pwl/Types.h"
+#include "librbd/cache/pwl/SSDTypes.h"
+TYPE(librbd::cache::pwl::WriteLogPmemEntry)
+TYPE(librbd::cache::pwl::WriteLogPoolRoot)
+TYPE(librbd::cache::pwl::SuperBlock)
+#endif
+
#ifdef WITH_RBD
#include "cls/rbd/cls_rbd.h"
TYPE_FEATUREFUL(cls_rbd_parent)