};
 /** @} */
 
+typedef enum {
+       LIBRADOS_CHECKSUM_TYPE_XXHASH32 = 0,
+       LIBRADOS_CHECKSUM_TYPE_XXHASH64 = 1,
+       LIBRADOS_CHECKSUM_TYPE_CRC32C   = 2
+} rados_checksum_type_t;
+
 /*
  * snap id contants
  */
  *   rados_read_op_omap_cmp()
  * - Object properties: rados_read_op_stat(), rados_read_op_assert_exists(),
  *   rados_read_op_assert_version()
- * - IO on objects: rados_read_op_read()
+ * - IO on objects: rados_read_op_read(), rados_read_op_checksum()
  * - Custom operations: rados_read_op_exec(), rados_read_op_exec_user_buf()
  * - Request properties: rados_read_op_set_flags()
  * - Performing the operation: rados_read_op_operate(),
 CEPH_RADOS_API int rados_read(rados_ioctx_t io, const char *oid, char *buf,
                               size_t len, uint64_t off);
 
+/**
+ * Compute checksum from object data
+ *
+ * The io context determines the snapshot to checksum, if any was set
+ * by rados_ioctx_snap_set_read(). The length of the init_value and
+ * resulting checksum are dependent upon the checksum type:
+ *
+ *    XXHASH64: le64
+ *    XXHASH32: le32
+ *    CRC32C:  le32
+ *
+ * The checksum result is encoded the following manner:
+ *
+ *    le32 num_checksum_chunks
+ *    {
+ *      leXX checksum for chunk (where XX = appropriate size for the checksum type)
+ *    } * num_checksum_chunks
+ *
+ * @param io the context in which to perform the checksum
+ * @param oid the name of the object to checksum
+ * @param type the checksum algorithm to utilize
+ * @param init_value the init value for the algorithm
+ * @param init_value_len the length of the init value
+ * @param len the number of bytes to checksum
+ * @param off the offset to start checksuming in the object
+ * @param chunk_size optional length-aligned chunk size for checksums
+ * @param pchecksum where to store the checksum result
+ * @param checksum_len the number of bytes available for the result
+ * @return negative error code on failure
+ */
+CEPH_RADOS_API int rados_checksum(rados_ioctx_t io, const char *oid,
+                                 rados_checksum_type_t type,
+                                 const char *init_value, size_t init_value_len,
+                                 size_t len, uint64_t off, size_t chunk_size,
+                                 char *pchecksum, size_t checksum_len);
+
 /**
  * Delete an object
  *
                                       size_t *bytes_read,
                                       int *prval);
 
+/**
+ * Compute checksum from object data
+ *
+ * @param read_op operation to add this action to
+ * @param oid the name of the object to checksum
+ * @param type the checksum algorithm to utilize
+ * @param init_value the init value for the algorithm
+ * @param init_value_len the length of the init value
+ * @param len the number of bytes to checksum
+ * @param off the offset to start checksuming in the object
+ * @param chunk_size optional length-aligned chunk size for checksums
+ * @param pchecksum where to store the checksum result for this action
+ * @param checksum_len the number of bytes available for the result
+ * @param prval where to store the return value for this action
+ */
+CEPH_RADOS_API void rados_read_op_checksum(rados_read_op_t read_op,
+                                          rados_checksum_type_t type,
+                                          const char *init_value,
+                                          size_t init_value_len,
+                                          uint64_t offset, size_t len,
+                                          size_t chunk_size, char *pchecksum,
+                                          size_t checksum_len, int *prval);
+
 /**
  * Execute an OSD class method on an object
  * See rados_exec() for general description.
 
     void getxattr(const char *name, bufferlist *pbl, int *prval);
     void getxattrs(std::map<std::string, bufferlist> *pattrs, int *prval);
     void read(size_t off, uint64_t len, bufferlist *pbl, int *prval);
+    void checksum(rados_checksum_type_t type, const bufferlist &init_value_bl,
+                 uint64_t off, size_t len, size_t chunk_size, bufferlist *pbl,
+                 int *prval);
+
     /**
      * see aio_sparse_read()
      */
     int writesame(const std::string& oid, bufferlist& bl,
                  size_t write_len, uint64_t off);
     int read(const std::string& oid, bufferlist& bl, size_t len, uint64_t off);
+    int checksum(const std::string& o, rados_checksum_type_t type,
+                const bufferlist &init_value_bl, size_t len, uint64_t off,
+                size_t chunk_size, bufferlist *pbl);
     int remove(const std::string& oid);
     int remove(const std::string& oid, int flags);
     int trunc(const std::string& oid, uint64_t size);
 
   return m.size();
 }
 
+int librados::IoCtxImpl::checksum(const object_t& oid, uint8_t type,
+                                 const bufferlist &init_value, size_t len,
+                                 uint64_t off, size_t chunk_size,
+                                 bufferlist *pbl)
+{
+  if (len > (size_t) INT_MAX) {
+    return -EDOM;
+  }
+
+  ::ObjectOperation rd;
+  prepare_assert_ops(&rd);
+  rd.checksum(type, init_value, off, len, chunk_size, pbl, nullptr, nullptr);
+
+  int r = operate_read(oid, &rd, nullptr);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
 int librados::IoCtxImpl::stat(const object_t& oid, uint64_t *psize, time_t *pmtime)
 {
   uint64_t size;
 
             std::map<uint64_t,uint64_t>& m);
   int sparse_read(const object_t& oid, std::map<uint64_t,uint64_t>& m,
                  bufferlist& bl, size_t len, uint64_t off);
+  int checksum(const object_t& oid, uint8_t type, const bufferlist &init_value,
+              size_t len, uint64_t off, size_t chunk_size, bufferlist *pbl);
   int remove(const object_t& oid);
   int remove(const object_t& oid, int flags);
   int stat(const object_t& oid, uint64_t *psize, time_t *pmtime);
 
 
 TracepointProvider::Traits tracepoint_traits("librados_tp.so", "rados_tracing");
 
+uint8_t get_checksum_op_type(rados_checksum_type_t type) {
+  switch (type) {
+  case LIBRADOS_CHECKSUM_TYPE_XXHASH32:
+    return CEPH_OSD_CHECKSUM_OP_TYPE_XXHASH32;
+  case LIBRADOS_CHECKSUM_TYPE_XXHASH64:
+    return CEPH_OSD_CHECKSUM_OP_TYPE_XXHASH64;
+  case LIBRADOS_CHECKSUM_TYPE_CRC32C:
+    return CEPH_OSD_CHECKSUM_OP_TYPE_CRC32C;
+  default:
+    return -1;
+  }
+}
+
 } // anonymous namespace
 
 /*
   o->sparse_read(off, len, m, data_bl, prval);
 }
 
+void librados::ObjectReadOperation::checksum(rados_checksum_type_t type,
+                                            const bufferlist &init_value_bl,
+                                            uint64_t off, size_t len,
+                                            size_t chunk_size, bufferlist *pbl,
+                                            int *prval)
+{
+  ::ObjectOperation *o = &impl->o;
+  o->checksum(get_checksum_op_type(type), init_value_bl, off, len, chunk_size,
+             pbl, prval, nullptr);
+}
+
 void librados::ObjectReadOperation::tmap_get(bufferlist *pbl, int *prval)
 {
   ::ObjectOperation *o = &impl->o;
   return io_ctx_impl->read(obj, bl, len, off);
 }
 
+int librados::IoCtx::checksum(const std::string& oid,
+                             rados_checksum_type_t type,
+                             const bufferlist &init_value_bl, size_t len,
+                             uint64_t off, size_t chunk_size, bufferlist *pbl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->checksum(obj, get_checksum_op_type(type), init_value_bl,
+                              len, off, chunk_size, pbl);
+}
+
 int librados::IoCtx::remove(const std::string& oid)
 {
   object_t obj(oid);
   return ret;
 }
 
+extern "C" int rados_checksum(rados_ioctx_t io, const char *o,
+                              rados_checksum_type_t type,
+                              const char *init_value, size_t init_value_len,
+                              size_t len, uint64_t off, size_t chunk_size,
+                             char *pchecksum, size_t checksum_len)
+{
+  tracepoint(librados, rados_checksum_enter, io, o, type, init_value,
+            init_value_len, len, off, chunk_size);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+
+  bufferlist init_value_bl;
+  init_value_bl.append(init_value, init_value_len);
+
+  bufferlist checksum_bl;
+
+  int retval = ctx->checksum(oid, get_checksum_op_type(type), init_value_bl,
+                            len, off, chunk_size, &checksum_bl);
+  if (retval >= 0) {
+    if (checksum_bl.length() > checksum_len) {
+      tracepoint(librados, rados_checksum_exit, -ERANGE, NULL, 0);
+      return -ERANGE;
+    }
+
+    checksum_bl.copy(0, checksum_bl.length(), pchecksum);
+  }
+  tracepoint(librados, rados_checksum_exit, retval, pchecksum, checksum_len);
+  return retval;
+}
+
 extern "C" uint64_t rados_get_last_version(rados_ioctx_t io)
 {
   tracepoint(librados, rados_get_last_version_enter, io);
   tracepoint(librados, rados_read_op_read_exit);
 }
 
+extern "C" void rados_read_op_checksum(rados_read_op_t read_op,
+                                       rados_checksum_type_t type,
+                                       const char *init_value,
+                                       size_t init_value_len,
+                                       uint64_t offset, size_t len,
+                                       size_t chunk_size, char *pchecksum,
+                                      size_t checksum_len, int *prval)
+{
+  tracepoint(librados, rados_read_op_checksum_enter, read_op, type, init_value,
+            init_value_len, offset, len, chunk_size);
+  bufferlist init_value_bl;
+  init_value_bl.append(init_value, init_value_len);
+
+  C_bl_to_buf *ctx = nullptr;
+  if (pchecksum != nullptr) {
+    ctx = new C_bl_to_buf(pchecksum, checksum_len, nullptr, prval);
+  }
+  ((::ObjectOperation *)read_op)->checksum(get_checksum_op_type(type),
+                                          init_value_bl, offset, len,
+                                          chunk_size,
+                                          (ctx ? &ctx->out_bl : nullptr),
+                                          prval, ctx);
+  tracepoint(librados, rados_read_op_checksum_exit);
+}
+
 class C_out_buffer : public Context {
   char **out_buf;
   size_t *out_len;
 
     add_data(CEPH_OSD_OP_SPARSE_READ, off, len, bl);
   }
 
+  void checksum(uint8_t type, const bufferlist &init_value_bl,
+               uint64_t off, uint64_t len, size_t chunk_size,
+               bufferlist *pbl, int *prval, Context *ctx) {
+    OSDOp& osd_op = add_op(CEPH_OSD_OP_CHECKSUM);
+    osd_op.op.checksum.offset = off;
+    osd_op.op.checksum.length = len;
+    osd_op.op.checksum.type = type;
+    osd_op.op.checksum.chunk_size = chunk_size;
+    osd_op.indata.append(init_value_bl);
+
+    unsigned p = ops.size() - 1;
+    out_bl[p] = pbl;
+    out_rval[p] = prval;
+    out_handler[p] = ctx;
+  }
+
   // object attrs
   void getxattr(const char *name, bufferlist *pbl, int *prval) {
     bufferlist bl;
 
 std::string RadosTestNS::pool_name;
 rados_t RadosTestNS::s_cluster = NULL;
 
+namespace {
+
+void init_rand() {
+  static bool seeded = false;
+  if (!seeded) {
+    seeded = true;
+    int seed = getpid();
+    std::cout << "seed " << seed << std::endl;
+    srand(seed);
+  }
+}
+
+} // anonymous namespace
+
 void RadosTestNS::SetUpTestCase()
 {
   pool_name = get_temp_pool_name();
 
 void RadosTestPP::SetUpTestCase()
 {
+  init_rand();
+
   pool_name = get_temp_pool_name();
   ASSERT_EQ("", create_one_pool_pp(pool_name, s_cluster));
 }
 
   rados_aio_release(my_completion2);
 }
 
+TEST(LibRadosAio, RoundTrip3) {
+  AioTestData test_data;
+  rados_completion_t my_completion;
+  ASSERT_EQ("", test_data.init());
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+             set_completion_complete, set_completion_safe, &my_completion));
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+
+  rados_write_op_t op1 = rados_create_write_op();
+  rados_write_op_write(op1, buf, sizeof(buf), 0);
+  rados_write_op_set_alloc_hint2(op1, 0, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED); 
+  ASSERT_EQ(0, rados_aio_write_op_operate(op1, test_data.m_ioctx, my_completion,
+                                         "foo", NULL, 0));
+  rados_release_write_op(op1);
+
+  {
+    TestAlarm alarm;
+    sem_wait(test_data.m_sem);
+    sem_wait(test_data.m_sem);
+  }
+
+  ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+  rados_aio_release(my_completion);
+
+  char buf2[128];
+  memset(buf2, 0, sizeof(buf2));
+  rados_completion_t my_completion2;
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+             set_completion_complete, set_completion_safe, &my_completion2));
+
+  rados_read_op_t op2 = rados_create_read_op();
+  rados_read_op_read(op2, 0, sizeof(buf2), buf2, NULL, NULL);
+  rados_read_op_set_flags(op2, LIBRADOS_OP_FLAG_FADVISE_NOCACHE |
+                              LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+  __le32 init_value = -1;
+  __le32 checksum[2];
+  rados_read_op_checksum(op2, LIBRADOS_CHECKSUM_TYPE_CRC32C,
+                        reinterpret_cast<char *>(&init_value),
+                        sizeof(init_value), 0, 0, 0,
+                         reinterpret_cast<char *>(&checksum),
+                         sizeof(checksum), NULL);
+  ASSERT_EQ(0, rados_aio_read_op_operate(op2, test_data.m_ioctx, my_completion2,
+                                        "foo", 0));
+  rados_release_read_op(op2);
+  
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+  }
+  ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+  ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
+  rados_aio_release(my_completion2);
+
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(1U, checksum[0]);
+  ASSERT_EQ(bl.crc32c(-1), checksum[1]);
+}
+
 TEST(LibRadosAio, RoundTripPP) {
   AioTestDataPP test_data;
   ASSERT_EQ("", test_data.init());
   char buf[128];
   memset(buf, 0xcc, sizeof(buf));
   bufferlist bl;
-  bl.append(buf);
+  bl.append(buf, sizeof(buf));
 
   op.write(0, bl);
   op.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED);
   ObjectReadOperation op1;
   op1.read(0, sizeof(buf), &bl, NULL);
   op1.set_op_flags2(LIBRADOS_OP_FLAG_FADVISE_DONTNEED|LIBRADOS_OP_FLAG_FADVISE_RANDOM);
+  bufferlist init_value_bl;
+  ::encode(static_cast<int32_t>(-1), init_value_bl);
+  bufferlist csum_bl;
+  op1.checksum(LIBRADOS_CHECKSUM_TYPE_CRC32C, init_value_bl,
+              0, 0, 0, &csum_bl, nullptr);
   ioctx.aio_operate("test_obj", my_completion2.get(), &op1, 0);
   {
     TestAlarm alarm;
   }
   EXPECT_EQ(0, my_completion2->get_return_value());
   ASSERT_EQ(0, memcmp(buf, bl.c_str(), sizeof(buf)));
+
+  ASSERT_EQ(8U, csum_bl.length());
+  auto csum_bl_it = csum_bl.begin();
+  uint32_t csum_count;
+  uint32_t csum;
+  ::decode(csum_count, csum_bl_it);
+  ASSERT_EQ(1U, csum_count);
+  ::decode(csum, csum_bl_it);
+  ASSERT_EQ(bl.crc32c(-1), csum);
   ioctx.remove("test_obj");
   destroy_one_pool_pp(pool_name, cluster);
 }
 
   remove_object();
 }
 
+TEST_F(CReadOpsTest, Checksum) {
+  write_object();
+
+  {
+    rados_read_op_t op = rados_create_read_op();
+    uint64_t init_value = -1;
+    rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_XXHASH64,
+                          reinterpret_cast<char *>(&init_value),
+                          sizeof(init_value), 0, len, 0, NULL, 0, NULL);
+    ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+    rados_release_read_op(op);
+  }
+
+  {
+    uint32_t init_value = -1;
+    uint32_t crc[2];
+    rados_read_op_t op = rados_create_read_op();
+    rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_CRC32C,
+                          reinterpret_cast<char *>(&init_value),
+                          sizeof(init_value), 0, len, 0,
+                          reinterpret_cast<char *>(&crc), sizeof(crc),
+                          nullptr);
+    ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+    ASSERT_EQ(1U, crc[0]);
+    uint32_t expected_crc = ceph_crc32c(
+      -1, reinterpret_cast<const uint8_t*>(data), static_cast<uint32_t>(len));
+    ASSERT_EQ(expected_crc, crc[1]);
+    rados_release_read_op(op);
+  }
+
+  {
+    uint32_t init_value = -1;
+    int rval;
+    rados_read_op_t op = rados_create_read_op();
+    rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_XXHASH32,
+                           reinterpret_cast<char *>(&init_value),
+                          sizeof(init_value), 0, len, 0, nullptr, 0, &rval);
+    ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+    ASSERT_EQ(0, rval);
+    rados_release_read_op(op);
+  }
+
+  {
+    uint32_t init_value = -1;
+    uint32_t crc[3];
+    int rval;
+    rados_read_op_t op = rados_create_read_op();
+    rados_read_op_checksum(op, LIBRADOS_CHECKSUM_TYPE_CRC32C,
+                           reinterpret_cast<char *>(&init_value),
+                          sizeof(init_value), 0, len, 4,
+                          reinterpret_cast<char *>(&crc), sizeof(crc), &rval);
+    ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0));
+    ASSERT_EQ(2U, crc[0]);
+    uint32_t expected_crc[2];
+    expected_crc[0] = ceph_crc32c(
+      -1, reinterpret_cast<const uint8_t*>(data), 4U);
+    expected_crc[1] = ceph_crc32c(
+      -1, reinterpret_cast<const uint8_t*>(data + 4), 4U);
+    ASSERT_EQ(expected_crc[0], crc[1]);
+    ASSERT_EQ(expected_crc[1], crc[2]);
+    ASSERT_EQ(0, rval);
+    rados_release_read_op(op);
+  }
+
+  remove_object();
+}
 
 TEST_F(CReadOpsTest, RWOrderedRead) {
   write_object();
 
 
 #include "include/rados/librados.h"
 #include "include/rados/librados.hpp"
+#include "include/encoding.h"
 #include "include/scope_guard.h"
 #include "test/librados/test.h"
 #include "test/librados/TestCase.h"
   ASSERT_EQ(0, memcmp(bl.c_str(), "ceph", 4));
 }
 
+TEST_F(LibRadosIo, Checksum) {
+  char buf[128];
+  memset(buf, 0xcc, sizeof(buf));
+  ASSERT_EQ(0, rados_write(ioctx, "foo", buf, sizeof(buf), 0));
+
+  uint32_t expected_crc = ceph_crc32c(-1, reinterpret_cast<const uint8_t*>(buf),
+                                      sizeof(buf));
+  uint32_t init_value = -1;
+  uint32_t crc[2];
+  ASSERT_EQ(0, rados_checksum(ioctx, "foo", LIBRADOS_CHECKSUM_TYPE_CRC32C,
+                             reinterpret_cast<char*>(&init_value),
+                             sizeof(init_value), sizeof(buf), 0, 0,
+                             reinterpret_cast<char*>(&crc), sizeof(crc)));
+  ASSERT_EQ(1U, crc[0]);
+  ASSERT_EQ(expected_crc, crc[1]);
+}
+
+TEST_F(LibRadosIoPP, Checksum) {
+  char buf[128];
+  Rados cluster;
+  memset(buf, 0xcc, sizeof(buf));
+  bufferlist bl;
+  bl.append(buf, sizeof(buf));
+  ASSERT_EQ(0, ioctx.write("foo", bl, sizeof(buf), 0));
+  bufferlist init_value_bl;
+  ::encode(static_cast<uint32_t>(-1), init_value_bl);
+  bufferlist csum_bl;
+  ASSERT_EQ(0, ioctx.checksum("foo", LIBRADOS_CHECKSUM_TYPE_CRC32C,
+                             init_value_bl, sizeof(buf), 0, 0, &csum_bl));
+  auto csum_bl_it = csum_bl.begin();
+  uint32_t csum_count;
+  ::decode(csum_count, csum_bl_it);
+  ASSERT_EQ(1U, csum_count);
+  uint32_t csum;
+  ::decode(csum, csum_bl_it);
+  ASSERT_EQ(bl.crc32c(-1), csum);
+}
+
 TEST_F(LibRadosIo, OverlappingWriteRoundTrip) {
   char buf[128];
   char buf2[64];
 
 #include "include/rados/librados.h"
 #include "include/rados/librados.hpp"
 #include "include/stringify.h"
+#include "common/Checksummer.h"
 #include "global/global_context.h"
 #include "test/librados/test.h"
 #include "test/librados/TestCase.h"
   /* write_len = data_len, i.e. same as rados_write() */
   ASSERT_EQ(0, rados_writesame(ioctx, "ws", buf, sizeof(buf), sizeof(buf), 0));
 }
+
+template <typename T>
+class LibRadosChecksum : public LibRadosMiscPP {
+public:
+  typedef typename T::alg_t alg_t;
+  typedef typename T::value_t value_t;
+  typedef typename alg_t::init_value_t init_value_t;
+
+  static const rados_checksum_type_t type = T::type;
+
+  bufferlist content_bl;
+
+  using LibRadosMiscPP::SetUpTestCase;
+  using LibRadosMiscPP::TearDownTestCase;
+
+  void SetUp() override {
+    LibRadosMiscPP::SetUp();
+
+    std::string content(4096, '\0');
+    for (size_t i = 0; i < content.length(); ++i) {
+      content[i] = static_cast<char>(rand() % (126 - 33) + 33);
+    }
+    content_bl.append(content);
+    ASSERT_EQ(0, ioctx.write("foo", content_bl, content_bl.length(), 0));
+  }
+};
+
+template <rados_checksum_type_t _type, typename AlgT, typename ValueT>
+class LibRadosChecksumParams {
+public:
+  typedef AlgT alg_t;
+  typedef ValueT value_t;
+  static const rados_checksum_type_t type = _type;
+};
+
+typedef ::testing::Types<
+    LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_XXHASH32,
+                          Checksummer::xxhash32, uint32_t>,
+    LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_XXHASH64,
+                          Checksummer::xxhash64, uint64_t>,
+    LibRadosChecksumParams<LIBRADOS_CHECKSUM_TYPE_CRC32C,
+                          Checksummer::crc32c, uint32_t>
+  > LibRadosChecksumTypes;
+
+TYPED_TEST_CASE(LibRadosChecksum, LibRadosChecksumTypes);
+
+TYPED_TEST(LibRadosChecksum, Subset) {
+  uint32_t chunk_size = 1024;
+  uint32_t csum_count = this->content_bl.length() / chunk_size;
+
+  typename TestFixture::init_value_t init_value = -1;
+  bufferlist init_value_bl;
+  ::encode(init_value, init_value_bl);
+
+  std::vector<bufferlist> checksum_bls(csum_count);
+  std::vector<int> checksum_rvals(csum_count);
+
+  // individual checksum ops for each chunk
+  ObjectReadOperation op;
+  for (uint32_t i = 0; i < csum_count; ++i) {
+    op.checksum(TestFixture::type, init_value_bl, i * chunk_size, chunk_size,
+               0, &checksum_bls[i], &checksum_rvals[i]);
+  }
+  ASSERT_EQ(0, this->ioctx.operate("foo", &op, NULL));
+
+  for (uint32_t i = 0; i < csum_count; ++i) {
+    ASSERT_EQ(0, checksum_rvals[i]);
+
+    auto bl_it = checksum_bls[i].begin();
+    uint32_t count;
+    ::decode(count, bl_it);
+    ASSERT_EQ(1U, count);
+
+    typename TestFixture::value_t value;
+    ::decode(value, bl_it);
+
+    bufferlist content_sub_bl;
+    content_sub_bl.substr_of(this->content_bl, i * chunk_size, chunk_size);
+
+    typename TestFixture::value_t expected_value;
+    bufferptr expected_value_bp = buffer::create_static(
+      sizeof(expected_value), reinterpret_cast<char*>(&expected_value));
+    Checksummer::template calculate<typename TestFixture::alg_t>(
+      init_value, chunk_size, 0, chunk_size, content_sub_bl,
+      &expected_value_bp);
+    ASSERT_EQ(expected_value, value);
+  }
+}
+
+TYPED_TEST(LibRadosChecksum, Chunked) {
+  uint32_t chunk_size = 1024;
+  uint32_t csum_count = this->content_bl.length() / chunk_size;
+
+  typename TestFixture::init_value_t init_value = -1;
+  bufferlist init_value_bl;
+  ::encode(init_value, init_value_bl);
+
+  bufferlist checksum_bl;
+  int checksum_rval;
+
+  // single op with chunked checksum results
+  ObjectReadOperation op;
+  op.checksum(TestFixture::type, init_value_bl, 0, this->content_bl.length(),
+             chunk_size, &checksum_bl, &checksum_rval);
+  ASSERT_EQ(0, this->ioctx.operate("foo", &op, NULL));
+  ASSERT_EQ(0, checksum_rval);
+
+  auto bl_it = checksum_bl.begin();
+  uint32_t count;
+  ::decode(count, bl_it);
+  ASSERT_EQ(csum_count, count);
+
+  std::vector<typename TestFixture::value_t> expected_values(csum_count);
+  bufferptr expected_values_bp = buffer::create_static(
+    csum_count * sizeof(typename TestFixture::value_t),
+    reinterpret_cast<char*>(&expected_values[0]));
+
+  Checksummer::template calculate<typename TestFixture::alg_t>(
+    init_value, chunk_size, 0, this->content_bl.length(), this->content_bl,
+    &expected_values_bp);
+
+  for (uint32_t i = 0; i < csum_count; ++i) {
+    typename TestFixture::value_t value;
+    ::decode(value, bl_it);
+    ASSERT_EQ(expected_values[i], value);
+  }
+}
 
   vector<bool> is_sparse_read;
   uint64_t waiting_on;
 
+  vector<bufferlist> checksums;
+  vector<int> checksum_retvals;
+
   map<string, bufferlist> attrs;
   int attrretval;
 
       extent_results(3),
       is_sparse_read(3, false),
       waiting_on(0),
+      checksums(3),
+      checksum_retvals(3),
       attrretval(0)
   {}
 
                   len,
                   &results[index],
                   &retvals[index]);
+      bufferlist init_value_bl;
+      ::encode(static_cast<uint32_t>(-1), init_value_bl);
+      read_op.checksum(LIBRADOS_CHECKSUM_TYPE_CRC32C, init_value_bl, 0, len,
+                      0, &checksums[index], &checksum_retvals[index]);
     } else {
       is_sparse_read[index] = true;
       read_op.sparse_read(0,
              cerr << num << ": oid " << oid << " contents " << to_check << " corrupt" << std::endl;
              context->errors++;
            }
+
+           uint32_t checksum = 0;
+           if (checksum_retvals[i] == 0) {
+             try {
+               auto bl_it = checksums[i].begin();
+               uint32_t csum_count;
+               ::decode(csum_count, bl_it);
+               ::decode(checksum, bl_it);
+             } catch (const buffer::error &err) {
+               checksum_retvals[i] = -EBADMSG;
+             }
+           }
+           if (checksum_retvals[i] != 0 || checksum != results[i].crc32c(-1)) {
+             cerr << num << ": oid " << oid << " checksum " << checksums[i]
+                  << " incorrect, expecting " << results[i].crc32c(-1)
+                   << std::endl;
+             context->errors++;
+           }
          }
        }
        if (context->errors) ceph_abort();
 
     )
 )
 
+TRACEPOINT_EVENT(librados, rados_checksum_enter,
+    TP_ARGS(
+        rados_ioctx_t, ioctx,
+        const char*, oid,
+       int, type,
+       const char*, init_value,
+       size_t, init_value_len,
+        size_t, len,
+        uint64_t, off,
+       size_t, chunk_size),
+    TP_FIELDS(
+        ctf_integer_hex(rados_ioctx_t, ioctx, ioctx)
+        ctf_string(oid, oid)
+        ctf_integer(int, type, type)
+        ceph_ctf_sequence(unsigned char, init_value, init_value, size_t, init_value_len)
+        ctf_integer(size_t, len, len)
+        ctf_integer(uint64_t, off, off)
+        ctf_integer(size_t, chunk_size, chunk_size)
+    )
+)
+
+TRACEPOINT_EVENT(librados, rados_checksum_exit,
+    TP_ARGS(
+        int, retval,
+       const char*, checksum,
+       size_t, checksum_len
+        ),
+    TP_FIELDS(
+        ctf_integer(int, retval, retval)
+       ceph_ctf_sequence(unsigned char, checksum, checksum, size_t, checksum_len)
+    )
+)
+
 TRACEPOINT_EVENT(librados, rados_get_last_version_enter,
     TP_ARGS(
         rados_ioctx_t, ioctx),
     TP_FIELDS()
 )
 
+TRACEPOINT_EVENT(librados, rados_read_op_checksum_enter,
+    TP_ARGS(
+        rados_read_op_t, read_op,
+       int, type,
+       const char*, init_value,
+       size_t, init_value_len,
+        uint64_t, offset,
+        size_t, len,
+       size_t, chunk_size),
+    TP_FIELDS(
+        ctf_integer_hex(rados_read_op_t, read_op, read_op)
+        ctf_integer(int, type, type)
+        ceph_ctf_sequence(unsigned char, init_value, init_value, size_t, init_value_len)
+        ctf_integer(uint64_t, offset, offset)
+        ctf_integer(size_t, len, len)
+        ctf_integer(size_t, chunk_size, chunk_size)
+    )
+)
+
+TRACEPOINT_EVENT(librados, rados_read_op_checksum_exit,
+    TP_ARGS(),
+    TP_FIELDS()
+)
+
 TRACEPOINT_EVENT(librados, rados_read_op_exec_enter,
     TP_ARGS(
         rados_read_op_t, read_op,