]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
cls/rbd: add sparse_copyup method
authorMykola Golub <mgolub@suse.com>
Wed, 1 May 2019 10:25:20 +0000 (11:25 +0100)
committerMykola Golub <mgolub@suse.com>
Thu, 9 May 2019 17:11:46 +0000 (18:11 +0100)
Signed-off-by: Mykola Golub <mgolub@suse.com>
src/cls/rbd/cls_rbd.cc
src/cls/rbd/cls_rbd_client.cc
src/cls/rbd/cls_rbd_client.h
src/test/cls_rbd/test_cls_rbd.cc

index 317cec69f68500ce8d9218725484ddbd1cbd06b7..8a3201c91f29ff636582856c7ea5671e8d6381e8 100644 (file)
@@ -2694,6 +2694,67 @@ int copyup(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
   return cls_cxx_write(hctx, 0, in->length(), in);
 }
 
+/**
+ * Input:
+ * @param extent_map map of extents to write
+ * @param data bufferlist of data to write
+ *
+ * Output:
+ * @returns 0 on success, or if block already exists in child
+ *  negative error code on other error
+ */
+
+int sparse_copyup(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+  std::map<uint64_t, uint64_t> extent_map;
+  bufferlist data;
+
+  try {
+    auto iter = in->cbegin();
+    decode(extent_map, iter);
+    decode(data, iter);
+  } catch (const buffer::error &err) {
+    CLS_LOG(20, "sparse_copyup: invalid decode");
+    return -EINVAL;
+  }
+
+  int r = check_exists(hctx);
+  if (r == 0) {
+    return 0;
+  }
+
+  if (extent_map.empty()) {
+    CLS_LOG(20, "sparse_copyup: create empty object");
+    r = cls_cxx_create(hctx, true);
+    return r;
+  }
+
+  uint64_t data_offset = 0;
+  for (auto &it: extent_map) {
+    auto off = it.first;
+    auto len = it.second;
+
+    bufferlist tmpbl;
+    try {
+      tmpbl.substr_of(data, data_offset, len);
+    } catch (const buffer::error &err) {
+      CLS_LOG(20, "sparse_copyup: invalid data");
+      return -EINVAL;
+    }
+    data_offset += len;
+
+    CLS_LOG(20, "sparse_copyup: writing extent %" PRIu64 "~%" PRIu64 "\n", off,
+            len);
+    int r = cls_cxx_write(hctx, off, len, &tmpbl);
+    if (r < 0) {
+      CLS_ERR("sparse_copyup: error writing extent %" PRIu64 "~%" PRIu64 ": %s",
+              off, len, cpp_strerror(r).c_str());
+      return r;
+    }
+  }
+
+  return 0;
+}
 
 /************************ rbd_id object methods **************************/
 
@@ -7549,6 +7610,7 @@ CLS_INIT(rbd)
   cls_method_handle_t h_namespace_remove;
   cls_method_handle_t h_namespace_list;
   cls_method_handle_t h_copyup;
+  cls_method_handle_t h_sparse_copyup;
   cls_method_handle_t h_assert_snapc_seq;
   cls_method_handle_t h_sparsify;
 
@@ -7939,6 +8001,9 @@ CLS_INIT(rbd)
   cls_register_cxx_method(h_class, "copyup",
                          CLS_METHOD_RD | CLS_METHOD_WR,
                          copyup, &h_copyup);
+  cls_register_cxx_method(h_class, "sparse_copyup",
+                         CLS_METHOD_RD | CLS_METHOD_WR,
+                         sparse_copyup, &h_sparse_copyup);
   cls_register_cxx_method(h_class, "assert_snapc_seq",
                           CLS_METHOD_RD | CLS_METHOD_WR,
                           assert_snapc_seq,
index ebf2104ad96a6791b3672923a4a55dbdcf772135..1e03fbce01bc9736d198cba3e8502dd860afde2a 100644 (file)
@@ -839,6 +839,24 @@ int copyup(librados::IoCtx *ioctx, const std::string &oid,
   return ioctx->exec(oid, "rbd", "copyup", data, out);
 }
 
+void sparse_copyup(librados::ObjectWriteOperation *op,
+                   const std::map<uint64_t, uint64_t> &extent_map,
+                   bufferlist data) {
+  bufferlist bl;
+  encode(extent_map, bl);
+  encode(data, bl);
+  op->exec("rbd", "sparse_copyup", bl);
+}
+
+int sparse_copyup(librados::IoCtx *ioctx, const std::string &oid,
+                  const std::map<uint64_t, uint64_t> &extent_map,
+                  bufferlist data) {
+  librados::ObjectWriteOperation op;
+  sparse_copyup(&op, extent_map, data);
+
+  return ioctx->operate(oid, &op);
+}
+
 void get_protection_status_start(librados::ObjectReadOperation *op,
                                  snapid_t snap_id)
 {
index e03f0f410c1dd9c363ba77b4e8fb91014d3dbefb..d9a01acd110559c93b8f5cfbec76184bf9cb5157 100644 (file)
@@ -600,6 +600,13 @@ void assert_snapc_seq(librados::ObjectWriteOperation *op,
 int copyup(librados::IoCtx *ioctx, const std::string &oid,
            bufferlist data);
 
+void sparse_copyup(librados::ObjectWriteOperation *op,
+                   const std::map<uint64_t, uint64_t> &extent_map,
+                   bufferlist data);
+int sparse_copyup(librados::IoCtx *ioctx, const std::string &oid,
+                  const std::map<uint64_t, uint64_t> &extent_map,
+                  bufferlist data);
+
 void sparsify(librados::ObjectWriteOperation *op, size_t sparse_size,
               bool remove_empty);
 int sparsify(librados::IoCtx *ioctx, const std::string &oid, size_t sparse_size,
index b3ee9e44a12b9b869fbe13cfb384fe0c99e72337..724bfa21ebf9b8d2c5b6b1286c09a7491ed945d1 100644 (file)
@@ -179,6 +179,74 @@ TEST_F(TestClsRbd, copyup)
   ioctx.close();
 }
 
+TEST_F(TestClsRbd, sparse_copyup)
+{
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string oid = get_temp_image_name();
+  ioctx.remove(oid);
+
+  bool sparse_read_supported = is_sparse_read_supported(ioctx, oid);
+
+  // copyup of 0-len nonexistent object should create new 0-len object
+  uint64_t size;
+  ASSERT_EQ(-ENOENT, ioctx.stat(oid, &size, nullptr));
+  std::map<uint64_t, uint64_t> m;
+  bufferlist inbl;
+  ASSERT_EQ(0, sparse_copyup(&ioctx, oid, m, inbl));
+  ASSERT_EQ(0, ioctx.stat(oid, &size, nullptr));
+  ASSERT_EQ(0U, size);
+
+  // create some data to write
+  inbl.append(std::string(4096, '1'));
+  inbl.append(std::string(4096, '2'));
+  m = {{1024, 4096}, {8192, 4096}};
+
+  // copyup to nonexistent object should create new object
+  ioctx.remove(oid);
+  ASSERT_EQ(-ENOENT, ioctx.remove(oid));
+  ASSERT_EQ(0, sparse_copyup(&ioctx, oid, m, inbl));
+  // and its contents should match
+  bufferlist outbl;
+  bufferlist expected_outbl;
+  expected_outbl.append(std::string(1024, '\0'));
+  expected_outbl.append(std::string(4096, '1'));
+  expected_outbl.append(std::string(8192 - 4096 - 1024, '\0'));
+  expected_outbl.append(std::string(4096, '2'));
+  ASSERT_EQ((int)expected_outbl.length(),
+            ioctx.read(oid, outbl, expected_outbl.length() + 1, 0));
+  ASSERT_TRUE(outbl.contents_equal(expected_outbl));
+  std::map<uint64_t, uint64_t> expected_m;
+  if (sparse_read_supported) {
+    expected_m = m;
+    expected_outbl = inbl;
+  } else {
+    expected_m = {{0, expected_outbl.length()}};
+  }
+  m.clear();
+  outbl.clear();
+  ASSERT_EQ((int)expected_m.size(), ioctx.sparse_read(oid, m, outbl, 65536, 0));
+  ASSERT_EQ(m, expected_m);
+  ASSERT_TRUE(outbl.contents_equal(expected_outbl));
+
+  // now send different data, but with a preexisting object
+  bufferlist inbl2;
+  inbl2.append(std::string(1024, 'X'));
+
+  // should still succeed
+  ASSERT_EQ(0, sparse_copyup(&ioctx, oid, {{0, 1024}}, inbl2));
+  // but contents should not have changed
+  m.clear();
+  outbl.clear();
+  ASSERT_EQ((int)expected_m.size(), ioctx.sparse_read(oid, m, outbl, 65536, 0));
+  ASSERT_EQ(m, expected_m);
+  ASSERT_TRUE(outbl.contents_equal(expected_outbl));
+
+  ASSERT_EQ(0, ioctx.remove(oid));
+  ioctx.close();
+}
+
 TEST_F(TestClsRbd, get_and_set_id)
 {
   librados::IoCtx ioctx;