]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/test/librados_test_stub/TestMemIoCtxImpl.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / test / librados_test_stub / TestMemIoCtxImpl.cc
index cc99b1ee710cc7b002cbf4717395031aee224737..77ea14366cd097166f7c76661d9d30750c5a5249 100644 (file)
@@ -4,13 +4,17 @@
 #include "test/librados_test_stub/TestMemIoCtxImpl.h"
 #include "test/librados_test_stub/TestMemRadosClient.h"
 #include "common/Clock.h"
-#include "common/RWLock.h"
 #include "include/err.h"
+#include <functional>
 #include <boost/algorithm/string/predicate.hpp>
-#include <boost/bind.hpp>
 #include <errno.h>
 #include <include/compat.h>
 
+#define dout_subsys ceph_subsys_rados
+#undef dout_prefix
+#define dout_prefix *_dout << "TestMemIoCtxImpl: " << this << " " << __func__ \
+                           << ": " << oid << " "
+
 static void to_vector(const interval_set<uint64_t> &set,
                       std::vector<std::pair<uint64_t, uint64_t> > *vec) {
   vec->clear();
@@ -20,6 +24,17 @@ static void to_vector(const interval_set<uint64_t> &set,
   }
 }
 
+// see PrimaryLogPG::finish_extent_cmp()
+static int cmpext_compare(const bufferlist &bl, const bufferlist &read_bl) {
+  for (uint64_t idx = 0; idx < bl.length(); ++idx) {
+    char read_byte = (idx < read_bl.length() ? read_bl[idx] : 0);
+    if (bl[idx] != read_byte) {
+      return -MAX_ERRNO - idx;
+    }
+  }
+  return 0;
+}
+
 namespace librados {
 
 TestMemIoCtxImpl::TestMemIoCtxImpl() {
@@ -46,10 +61,10 @@ TestIoCtxImpl *TestMemIoCtxImpl::clone() {
   return new TestMemIoCtxImpl(*this);
 }
 
-int TestMemIoCtxImpl::aio_remove(const std::string& oid, AioCompletionImpl *c) {
+int TestMemIoCtxImpl::aio_remove(const std::string& oid, AioCompletionImpl *c, int flags) {
   m_client->add_aio_operation(oid, true,
-                              boost::bind(&TestMemIoCtxImpl::remove, this, oid,
-                                          get_snap_context()),
+                              std::bind(&TestMemIoCtxImpl::remove, this, oid,
+                                       get_snap_context()),
                               c);
   return 0;
 }
@@ -58,56 +73,96 @@ int TestMemIoCtxImpl::append(const std::string& oid, const bufferlist &bl,
                              const SnapContext &snapc) {
   if (get_snap_read() != CEPH_NOSNAP) {
     return -EROFS;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  } else if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
+  auto cct = m_client->cct();
+  ldout(cct, 20) << "length=" << bl.length() << ", snapc=" << snapc << dendl;
+
   TestMemCluster::SharedFile file;
   {
-    RWLock::WLocker l(m_pool->file_lock);
-    file = get_file(oid, true, snapc);
+    std::unique_lock l{m_pool->file_lock};
+    file = get_file(oid, true, CEPH_NOSNAP, snapc);
   }
 
-  RWLock::WLocker l(file->lock);
-  file->data.append(bl);
+  std::unique_lock l{file->lock};
+  auto off = file->data.length();
+  ensure_minimum_length(off + bl.length(), &file->data);
+  file->data.begin(off).copy_in(bl.length(), bl);
   return 0;
 }
 
-int TestMemIoCtxImpl::assert_exists(const std::string &oid) {
-  if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+int TestMemIoCtxImpl::assert_exists(const std::string &oid, uint64_t snap_id) {
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
-  RWLock::RLocker l(m_pool->file_lock);
-  TestMemCluster::SharedFile file = get_file(oid, false, get_snap_context());
+  std::shared_lock l{m_pool->file_lock};
+  TestMemCluster::SharedFile file = get_file(oid, false, snap_id, {});
   if (file == NULL) {
     return -ENOENT;
   }
   return 0;
 }
 
-int TestMemIoCtxImpl::create(const std::string& oid, bool exclusive) {
+int TestMemIoCtxImpl::assert_version(const std::string &oid, uint64_t ver) {
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
+  }
+
+  std::shared_lock l{m_pool->file_lock};
+  TestMemCluster::SharedFile file = get_file(oid, false, CEPH_NOSNAP, {});
+  if (file == NULL || !file->exists) {
+    return -ENOENT;
+  }
+  if (ver < file->objver) {
+    return -ERANGE;
+  }
+  if (ver > file->objver) {
+    return -EOVERFLOW;
+  }
+
+  return 0;
+}
+
+int TestMemIoCtxImpl::create(const std::string& oid, bool exclusive,
+                             const SnapContext &snapc) {
   if (get_snap_read() != CEPH_NOSNAP) {
     return -EROFS;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  } else if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
-  RWLock::WLocker l(m_pool->file_lock);
-  get_file(oid, true, get_snap_context());
+  auto cct = m_client->cct();
+  ldout(cct, 20) << "snapc=" << snapc << dendl;
+
+  std::unique_lock l{m_pool->file_lock};
+  if (exclusive) {
+    TestMemCluster::SharedFile file = get_file(oid, false, CEPH_NOSNAP, {});
+    if (file != NULL && file->exists) {
+      return -EEXIST;
+    }
+  }
+
+  get_file(oid, true, CEPH_NOSNAP, snapc);
   return 0;
 }
 
 int TestMemIoCtxImpl::list_snaps(const std::string& oid, snap_set_t *out_snaps) {
-  if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  auto cct = m_client->cct();
+  ldout(cct, 20) << dendl;
+
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
   out_snaps->seq = 0;
   out_snaps->clones.clear();
 
-  RWLock::RLocker l(m_pool->file_lock);
-  TestMemCluster::Files::iterator it = m_pool->files.find(oid);
+  std::shared_lock l{m_pool->file_lock};
+  TestMemCluster::Files::iterator it = m_pool->files.find(
+    {get_namespace(), oid});
   if (it == m_pool->files.end()) {
     return -ENOENT;
   }
@@ -154,7 +209,7 @@ int TestMemIoCtxImpl::list_snaps(const std::string& oid, snap_set_t *out_snaps)
     // Include the SNAP_HEAD
     TestMemCluster::File &file = *file_snaps.back();
     if (file.exists) {
-      RWLock::RLocker l2(file.lock);
+      std::shared_lock l2{file.lock};
       if (out_snaps->seq == 0 && !include_head) {
         out_snaps->seq = file.snap_id;
       }
@@ -164,6 +219,23 @@ int TestMemIoCtxImpl::list_snaps(const std::string& oid, snap_set_t *out_snaps)
       out_snaps->clones.push_back(head_clone);
     }
   }
+
+  ldout(cct, 20) << "seq=" << out_snaps->seq << ", "
+                 << "clones=[";
+  bool first_clone = true;
+  for (auto& clone : out_snaps->clones) {
+    *_dout << "{"
+           << "cloneid=" << clone.cloneid << ", "
+           << "snaps=" << clone.snaps << ", "
+           << "overlap=" << clone.overlap << ", "
+           << "size=" << clone.size << "}";
+    if (!first_clone) {
+      *_dout << ", ";
+    } else {
+      first_clone = false;
+    }
+  }
+  *_dout << "]" << dendl;
   return 0;
 
 }
@@ -176,14 +248,14 @@ int TestMemIoCtxImpl::omap_get_vals2(const std::string& oid,
                                     bool *pmore) {
   if (out_vals == NULL) {
     return -EINVAL;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  } else if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
   TestMemCluster::SharedFile file;
   {
-    RWLock::RLocker l(m_pool->file_lock);
-    file = get_file(oid, false, get_snap_context());
+    std::shared_lock l{m_pool->file_lock};
+    file = get_file(oid, false, CEPH_NOSNAP, {});
     if (file == NULL) {
       return -ENOENT;
     }
@@ -191,8 +263,9 @@ int TestMemIoCtxImpl::omap_get_vals2(const std::string& oid,
 
   out_vals->clear();
 
-  RWLock::RLocker l(file->lock);
-  TestMemCluster::FileOMaps::iterator o_it = m_pool->file_omaps.find(oid);
+  std::shared_lock l{file->lock};
+  TestMemCluster::FileOMaps::iterator o_it = m_pool->file_omaps.find(
+    {get_namespace(), oid});
   if (o_it == m_pool->file_omaps.end()) {
     if (pmore) {
       *pmore = false;
@@ -232,23 +305,23 @@ int TestMemIoCtxImpl::omap_rm_keys(const std::string& oid,
                                    const std::set<std::string>& keys) {
   if (get_snap_read() != CEPH_NOSNAP) {
     return -EROFS;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  } else if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
   TestMemCluster::SharedFile file;
   {
-    RWLock::WLocker l(m_pool->file_lock);
-    file = get_file(oid, true, get_snap_context());
+    std::unique_lock l{m_pool->file_lock};
+    file = get_file(oid, true, CEPH_NOSNAP, get_snap_context());
     if (file == NULL) {
       return -ENOENT;
     }
   }
 
-  RWLock::WLocker l(file->lock);
+  std::unique_lock l{file->lock};
   for (std::set<std::string>::iterator it = keys.begin();
        it != keys.end(); ++it) {
-    m_pool->file_omaps[oid].erase(*it);
+    m_pool->file_omaps[{get_namespace(), oid}].erase(*it);
   }
   return 0;
 }
@@ -257,46 +330,47 @@ int TestMemIoCtxImpl::omap_set(const std::string& oid,
                                const std::map<std::string, bufferlist> &map) {
   if (get_snap_read() != CEPH_NOSNAP) {
     return -EROFS;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  } else if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
   TestMemCluster::SharedFile file;
   {
-    RWLock::WLocker l(m_pool->file_lock);
-    file = get_file(oid, true, get_snap_context());
+    std::unique_lock l{m_pool->file_lock};
+    file = get_file(oid, true, CEPH_NOSNAP, get_snap_context());
     if (file == NULL) {
       return -ENOENT;
     }
   }
 
-  RWLock::WLocker l(file->lock);
+  std::unique_lock l{file->lock};
   for (std::map<std::string, bufferlist>::const_iterator it = map.begin();
       it != map.end(); ++it) {
     bufferlist bl;
     bl.append(it->second);
-    m_pool->file_omaps[oid][it->first] = bl;
+    m_pool->file_omaps[{get_namespace(), oid}][it->first] = bl;
   }
 
   return 0;
 }
 
 int TestMemIoCtxImpl::read(const std::string& oid, size_t len, uint64_t off,
-                           bufferlist *bl) {
-  if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+                           bufferlist *bl, uint64_t snap_id,
+                           uint64_t* objver) {
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
   TestMemCluster::SharedFile file;
   {
-    RWLock::RLocker l(m_pool->file_lock);
-    file = get_file(oid, false, get_snap_context());
+    std::shared_lock l{m_pool->file_lock};
+    file = get_file(oid, false, snap_id, {});
     if (file == NULL) {
       return -ENOENT;
     }
   }
 
-  RWLock::RLocker l(file->lock);
+  std::shared_lock l{file->lock};
   if (len == 0) {
     len = file->data.length();
   }
@@ -306,52 +380,72 @@ int TestMemIoCtxImpl::read(const std::string& oid, size_t len, uint64_t off,
     bit.substr_of(file->data, off, len);
     append_clone(bit, bl);
   }
+  if (objver != nullptr) {
+    *objver = file->objver;
+  }
   return len;
 }
 
 int TestMemIoCtxImpl::remove(const std::string& oid, const SnapContext &snapc) {
   if (get_snap_read() != CEPH_NOSNAP) {
     return -EROFS;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  } else if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
-  RWLock::WLocker l(m_pool->file_lock);
-  TestMemCluster::SharedFile file = get_file(oid, false, snapc);
+  auto cct = m_client->cct();
+  ldout(cct, 20) << "snapc=" << snapc << dendl;
+
+  std::unique_lock l{m_pool->file_lock};
+  TestMemCluster::SharedFile file = get_file(oid, false, CEPH_NOSNAP, snapc);
   if (file == NULL) {
     return -ENOENT;
   }
-  file = get_file(oid, true, snapc);
+  file = get_file(oid, true, CEPH_NOSNAP, snapc);
 
-  RWLock::WLocker l2(file->lock);
-  file->exists = false;
+  {
+    std::unique_lock l2{file->lock};
+    file->exists = false;
+  }
+
+  TestCluster::ObjectLocator locator(get_namespace(), oid);
+  TestMemCluster::Files::iterator it = m_pool->files.find(locator);
+  ceph_assert(it != m_pool->files.end());
+
+  if (*it->second.rbegin() == file) {
+    TestMemCluster::ObjectHandlers object_handlers;
+    std::swap(object_handlers, m_pool->file_handlers[locator]);
+    m_pool->file_handlers.erase(locator);
+
+    for (auto object_handler : object_handlers) {
+      object_handler->handle_removed(m_client);
+    }
+  }
 
-  TestMemCluster::Files::iterator it = m_pool->files.find(oid);
-  assert(it != m_pool->files.end());
   if (it->second.size() == 1) {
     m_pool->files.erase(it);
-    m_pool->file_omaps.erase(oid);
+    m_pool->file_omaps.erase(locator);
   }
   return 0;
 }
 
 int TestMemIoCtxImpl::selfmanaged_snap_create(uint64_t *snapid) {
-  if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
-  RWLock::WLocker l(m_pool->file_lock);
+  std::unique_lock l{m_pool->file_lock};
   *snapid = ++m_pool->snap_id;
   m_pool->snap_seqs.insert(*snapid);
   return 0;
 }
 
 int TestMemIoCtxImpl::selfmanaged_snap_remove(uint64_t snapid) {
-  if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
-  RWLock::WLocker l(m_pool->file_lock);
+  std::unique_lock l{m_pool->file_lock};
   TestMemCluster::SnapSeqs::iterator it =
     m_pool->snap_seqs.find(snapid);
   if (it == m_pool->snap_seqs.end()) {
@@ -365,14 +459,15 @@ int TestMemIoCtxImpl::selfmanaged_snap_remove(uint64_t snapid) {
 
 int TestMemIoCtxImpl::selfmanaged_snap_rollback(const std::string& oid,
                                                 uint64_t snapid) {
-  if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
-  RWLock::WLocker l(m_pool->file_lock);
+  std::unique_lock l{m_pool->file_lock};
 
   TestMemCluster::SharedFile file;
-  TestMemCluster::Files::iterator f_it = m_pool->files.find(oid);
+  TestMemCluster::Files::iterator f_it = m_pool->files.find(
+    {get_namespace(), oid});
   if (f_it == m_pool->files.end()) {
     return 0;
   }
@@ -414,16 +509,17 @@ int TestMemIoCtxImpl::selfmanaged_snap_rollback(const std::string& oid,
 int TestMemIoCtxImpl::set_alloc_hint(const std::string& oid,
                                      uint64_t expected_object_size,
                                      uint64_t expected_write_size,
+                                     uint32_t flags,
                                      const SnapContext &snapc) {
   if (get_snap_read() != CEPH_NOSNAP) {
     return -EROFS;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  } else if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
   {
-    RWLock::WLocker l(m_pool->file_lock);
-    get_file(oid, true, snapc);
+    std::unique_lock l{m_pool->file_lock};
+    get_file(oid, true, CEPH_NOSNAP, snapc);
   }
 
   return 0;
@@ -432,23 +528,24 @@ int TestMemIoCtxImpl::set_alloc_hint(const std::string& oid,
 int TestMemIoCtxImpl::sparse_read(const std::string& oid, uint64_t off,
                                   uint64_t len,
                                   std::map<uint64_t,uint64_t> *m,
-                                  bufferlist *data_bl) {
-  if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+                                  bufferlist *data_bl, uint64_t snap_id) {
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
   // TODO verify correctness
   TestMemCluster::SharedFile file;
   {
-    RWLock::RLocker l(m_pool->file_lock);
-    file = get_file(oid, false, get_snap_context());
+    std::shared_lock l{m_pool->file_lock};
+    file = get_file(oid, false, snap_id, {});
     if (file == NULL) {
       return -ENOENT;
     }
   }
 
-  RWLock::RLocker l(file->lock);
+  std::shared_lock l{file->lock};
   len = clip_io(off, len, file->data.length());
+  // TODO support sparse read
   if (m != NULL) {
     m->clear();
     if (len > 0) {
@@ -460,25 +557,25 @@ int TestMemIoCtxImpl::sparse_read(const std::string& oid, uint64_t off,
     bit.substr_of(file->data, off, len);
     append_clone(bit, data_bl);
   }
-  return 0;
+  return len > 0 ? 1 : 0;
 }
 
 int TestMemIoCtxImpl::stat(const std::string& oid, uint64_t *psize,
                            time_t *pmtime) {
-  if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
   TestMemCluster::SharedFile file;
   {
-    RWLock::RLocker l(m_pool->file_lock);
-    file = get_file(oid, false, get_snap_context());
+    std::shared_lock l{m_pool->file_lock};
+    file = get_file(oid, false, CEPH_NOSNAP, {});
     if (file == NULL) {
       return -ENOENT;
     }
   }
 
-  RWLock::RLocker l(file->lock);
+  std::shared_lock l{file->lock};
   if (psize != NULL) {
     *psize = file->data.length();
   }
@@ -492,17 +589,20 @@ int TestMemIoCtxImpl::truncate(const std::string& oid, uint64_t size,
                                const SnapContext &snapc) {
   if (get_snap_read() != CEPH_NOSNAP) {
     return -EROFS;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  } else if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
+  auto cct = m_client->cct();
+  ldout(cct, 20) << "size=" << size << ", snapc=" << snapc << dendl;
+
   TestMemCluster::SharedFile file;
   {
-    RWLock::WLocker l(m_pool->file_lock);
-    file = get_file(oid, true, snapc);
+    std::unique_lock l{m_pool->file_lock};
+    file = get_file(oid, true, CEPH_NOSNAP, snapc);
   }
 
-  RWLock::WLocker l(file->lock);
+  std::unique_lock l{file->lock};
   bufferlist bl(size);
 
   interval_set<uint64_t> is;
@@ -530,17 +630,21 @@ int TestMemIoCtxImpl::write(const std::string& oid, bufferlist& bl, size_t len,
                             uint64_t off, const SnapContext &snapc) {
   if (get_snap_read() != CEPH_NOSNAP) {
     return -EROFS;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  } else if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
+  auto cct = m_client->cct();
+  ldout(cct, 20) << "extent=" << off << "~" << len << ", snapc=" << snapc
+                 << dendl;
+
   TestMemCluster::SharedFile file;
   {
-    RWLock::WLocker l(m_pool->file_lock);
-    file = get_file(oid, true, snapc);
+    std::unique_lock l{m_pool->file_lock};
+    file = get_file(oid, true, CEPH_NOSNAP, snapc);
   }
 
-  RWLock::WLocker l(file->lock);
+  std::unique_lock l{file->lock};
   if (len > 0) {
     interval_set<uint64_t> is;
     is.insert(off, len);
@@ -549,7 +653,7 @@ int TestMemIoCtxImpl::write(const std::string& oid, bufferlist& bl, size_t len,
   }
 
   ensure_minimum_length(off + len, &file->data);
-  file->data.copy_in(off, len, bl);
+  file->data.begin(off).copy_in(len, bl);
   return 0;
 }
 
@@ -557,20 +661,23 @@ int TestMemIoCtxImpl::write_full(const std::string& oid, bufferlist& bl,
                                  const SnapContext &snapc) {
   if (get_snap_read() != CEPH_NOSNAP) {
     return -EROFS;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  } else if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
+  auto cct = m_client->cct();
+  ldout(cct, 20) << "length=" << bl.length() << ", snapc=" << snapc << dendl;
+
   TestMemCluster::SharedFile file;
   {
-    RWLock::WLocker l(m_pool->file_lock);
-    file = get_file(oid, true, snapc);
+    std::unique_lock l{m_pool->file_lock};
+    file = get_file(oid, true, CEPH_NOSNAP, snapc);
     if (file == NULL) {
       return -ENOENT;
     }
   }
 
-  RWLock::WLocker l(file->lock);
+  std::unique_lock l{file->lock};
   if (bl.length() > 0) {
     interval_set<uint64_t> is;
     is.insert(0, bl.length());
@@ -579,16 +686,18 @@ int TestMemIoCtxImpl::write_full(const std::string& oid, bufferlist& bl,
   }
 
   file->data.clear();
-  file->data.append(bl);
+  ensure_minimum_length(bl.length(), &file->data);
+  file->data.begin().copy_in(bl.length(), bl);
   return 0;
 }
 
-int TestMemIoCtxImpl::writesame(const std::string& oid, bufferlist& bl, size_t len,
-                                uint64_t off, const SnapContext &snapc) {
+int TestMemIoCtxImpl::writesame(const std::string& oid, bufferlist& bl,
+                                size_t len, uint64_t off,
+                                const SnapContext &snapc) {
   if (get_snap_read() != CEPH_NOSNAP) {
     return -EROFS;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  } else if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
   if (len == 0 || (len % bl.length())) {
@@ -597,11 +706,11 @@ int TestMemIoCtxImpl::writesame(const std::string& oid, bufferlist& bl, size_t l
 
   TestMemCluster::SharedFile file;
   {
-    RWLock::WLocker l(m_pool->file_lock);
-    file = get_file(oid, true, snapc);
+    std::unique_lock l{m_pool->file_lock};
+    file = get_file(oid, true, CEPH_NOSNAP, snapc);
   }
 
-  RWLock::WLocker l(file->lock);
+  std::unique_lock l{file->lock};
   if (len > 0) {
     interval_set<uint64_t> is;
     is.insert(off, len);
@@ -611,7 +720,7 @@ int TestMemIoCtxImpl::writesame(const std::string& oid, bufferlist& bl, size_t l
 
   ensure_minimum_length(off + len, &file->data);
   while (len > 0) {
-    file->data.copy_in(off, bl.length(), bl);
+    file->data.begin(off).copy_in(bl.length(), bl);
     off += bl.length();
     len -= bl.length();
   }
@@ -619,44 +728,43 @@ int TestMemIoCtxImpl::writesame(const std::string& oid, bufferlist& bl, size_t l
 }
 
 int TestMemIoCtxImpl::cmpext(const std::string& oid, uint64_t off,
-                             bufferlist& cmp_bl) {
-  if (get_snap_read() != CEPH_NOSNAP) {
-    return -EROFS;
-  } else if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+                             bufferlist& cmp_bl, uint64_t snap_id) {
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
-  if (cmp_bl.length() == 0) {
-    return -EINVAL;
-  }
+  bufferlist read_bl;
+  uint64_t len = cmp_bl.length();
 
   TestMemCluster::SharedFile file;
   {
-    RWLock::WLocker l(m_pool->file_lock);
-    file = get_file(oid, true, get_snap_context());
+    std::shared_lock l{m_pool->file_lock};
+    file = get_file(oid, false, snap_id, {});
+    if (file == NULL) {
+      return cmpext_compare(cmp_bl, read_bl);
+    }
   }
 
-  RWLock::RLocker l(file->lock);
-  size_t len = cmp_bl.length();
-  ensure_minimum_length(off + len, &file->data);
-  if (len > 0 && off <= len) {
-    for (uint64_t p = off; p < len; p++)  {
-      if (file->data[p] != cmp_bl[p])
-        return -MAX_ERRNO - p;
-    }
+  std::shared_lock l{file->lock};
+  if (off >= file->data.length()) {
+    len = 0;
+  } else if (off + len > file->data.length()) {
+    len = file->data.length() - off;
   }
-  return 0;
+  read_bl.substr_of(file->data, off, len);
+  return cmpext_compare(cmp_bl, read_bl);
 }
 
 int TestMemIoCtxImpl::xattr_get(const std::string& oid,
                                 std::map<std::string, bufferlist>* attrset) {
-  if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
   TestMemCluster::SharedFile file;
-  RWLock::RLocker l(m_pool->file_lock);
-  TestMemCluster::FileXAttrs::iterator it = m_pool->file_xattrs.find(oid);
+  std::shared_lock l{m_pool->file_lock};
+  TestMemCluster::FileXAttrs::iterator it = m_pool->file_xattrs.find(
+    {get_namespace(), oid});
   if (it == m_pool->file_xattrs.end()) {
     return -ENODATA;
   }
@@ -666,32 +774,36 @@ int TestMemIoCtxImpl::xattr_get(const std::string& oid,
 
 int TestMemIoCtxImpl::xattr_set(const std::string& oid, const std::string &name,
                                 bufferlist& bl) {
-  if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
-  RWLock::WLocker l(m_pool->file_lock);
-  m_pool->file_xattrs[oid][name] = bl;
+  std::unique_lock l{m_pool->file_lock};
+  m_pool->file_xattrs[{get_namespace(), oid}][name] = bl;
   return 0;
 }
 
 int TestMemIoCtxImpl::zero(const std::string& oid, uint64_t off, uint64_t len,
                            const SnapContext &snapc) {
-  if (m_client->is_blacklisted()) {
-    return -EBLACKLISTED;
+  if (m_client->is_blocklisted()) {
+    return -EBLOCKLISTED;
   }
 
+  auto cct = m_client->cct();
+  ldout(cct, 20) << "extent=" << off << "~" << len << ", snapc=" << snapc
+                 << dendl;
+
   bool truncate_redirect = false;
   TestMemCluster::SharedFile file;
   {
-    RWLock::WLocker l(m_pool->file_lock);
-    file = get_file(oid, false, snapc);
+    std::unique_lock l{m_pool->file_lock};
+    file = get_file(oid, false, CEPH_NOSNAP, snapc);
     if (!file) {
       return 0;
     }
-    file = get_file(oid, true, snapc);
+    file = get_file(oid, true, CEPH_NOSNAP, snapc);
 
-    RWLock::RLocker l2(file->lock);
+    std::shared_lock l2{file->lock};
     if (len > 0 && off + len >= file->data.length()) {
       // Zero -> Truncate logic embedded in OSD
       truncate_redirect = true;
@@ -735,12 +847,15 @@ void TestMemIoCtxImpl::ensure_minimum_length(size_t len, bufferlist *bl) {
 }
 
 TestMemCluster::SharedFile TestMemIoCtxImpl::get_file(
-    const std::string &oid, bool write, const SnapContext &snapc) {
-  assert(m_pool->file_lock.is_locked() || m_pool->file_lock.is_wlocked());
-  assert(!write || m_pool->file_lock.is_wlocked());
+    const std::string &oid, bool write, uint64_t snap_id,
+    const SnapContext &snapc) {
+  ceph_assert(ceph_mutex_is_locked(m_pool->file_lock) ||
+             ceph_mutex_is_wlocked(m_pool->file_lock));
+  ceph_assert(!write || ceph_mutex_is_wlocked(m_pool->file_lock));
 
   TestMemCluster::SharedFile file;
-  TestMemCluster::Files::iterator it = m_pool->files.find(oid);
+  TestMemCluster::Files::iterator it = m_pool->files.find(
+    {get_namespace(), oid});
   if (it != m_pool->files.end()) {
     file = it->second.back();
   } else if (!write) {
@@ -777,14 +892,16 @@ TestMemCluster::SharedFile TestMemIoCtxImpl::get_file(
     if (new_version) {
       file->snap_id = snapc.seq;
       file->mtime = ceph_clock_now().sec();
-      m_pool->files[oid].push_back(file);
+      m_pool->files[{get_namespace(), oid}].push_back(file);
     }
+
+    file->objver++;
     return file;
   }
 
-  if (get_snap_read() == CEPH_NOSNAP) {
+  if (snap_id == CEPH_NOSNAP) {
     if (!file->exists) {
-      assert(it->second.size() > 1);
+      ceph_assert(it->second.size() > 1);
       return TestMemCluster::SharedFile();
     }
     return file;
@@ -794,7 +911,7 @@ TestMemCluster::SharedFile TestMemIoCtxImpl::get_file(
   for (TestMemCluster::FileSnapshots::reverse_iterator it = snaps.rbegin();
       it != snaps.rend(); ++it) {
     TestMemCluster::SharedFile file = *it;
-    if (file->snap_id < get_snap_read()) {
+    if (file->snap_id < snap_id) {
       if (!file->exists) {
         return TestMemCluster::SharedFile();
       }