]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rados : implementation of async stat/getxattr(s)/rmxattr/setxattr
authorSebastien Ponce <Sebastien.Ponce@cern.ch>
Sat, 25 Jun 2016 16:04:34 +0000 (18:04 +0200)
committerroot <root@lxbre43a05.cern.ch>
Tue, 1 Nov 2016 15:36:05 +0000 (16:36 +0100)
This completes the asynchronous API of librados for stats and external attributes

Signed-off-by: Sebastien Ponce <sebastien.ponce@cern.ch>
src/include/rados/librados.h
src/include/rados/librados.hpp
src/librados/IoCtxImpl.cc
src/librados/IoCtxImpl.h
src/librados/librados.cc
src/test/librados/aio.cc
src/tracing/librados.tp

index 5738290f6d1dc1c60ece31f43004415ce8b959db..53328f996158f8bfeaecbe1b126be604ab3fa792 100644 (file)
@@ -2059,6 +2059,76 @@ CEPH_RADOS_API int rados_aio_cancel(rados_ioctx_t io,
 
 /** @} Asynchronous I/O */
 
+/**
+ * @name Asynchronous Xattrs
+ * Extended attributes are stored as extended attributes on the files
+ * representing an object on the OSDs. Thus, they have the same
+ * limitations as the underlying filesystem. On ext4, this means that
+ * the total data stored in xattrs cannot exceed 4KB.
+ *
+ * @{
+ */
+
+/**
+ * Asynchronously get the value of an extended attribute on an object.
+ *
+ * @param io the context in which the attribute is read
+ * @param o name of the object
+ * @param completion what to do when the getxattr completes
+ * @param name which extended attribute to read
+ * @param buf where to store the result
+ * @param len size of buf in bytes
+ * @returns length of xattr value on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_getxattr(rados_ioctx_t io, const char *o,
+                                     rados_completion_t completion,
+                                     const char *name, char *buf, size_t len);
+
+/**
+ * Asynchronously set an extended attribute on an object.
+ *
+ * @param io the context in which xattr is set
+ * @param o name of the object
+ * @param completion what to do when the setxattr completes
+ * @param name which extended attribute to set
+ * @param buf what to store in the xattr
+ * @param len the number of bytes in buf
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_setxattr(rados_ioctx_t io, const char *o,
+                                     rados_completion_t completion,
+                                     const char *name, const char *buf,
+                                     size_t len);
+
+/**
+ * Asynchronously delete an extended attribute from an object.
+ *
+ * @param io the context in which to delete the xattr
+ * @param o the name of the object
+ * @param completion what to do when the rmxattr completes
+ * @param name which xattr to delete
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_rmxattr(rados_ioctx_t io, const char *o,
+                                    rados_completion_t completion,
+                                    const char *name);
+
+/**
+ * Asynchronously start iterating over xattrs on an object.
+ *
+ * @post iter is a valid iterator
+ *
+ * @param io the context in which to list xattrs
+ * @param oid name of the object
+ * @param iter where to store the iterator
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RADOS_API int rados_aio_getxattrs(rados_ioctx_t io, const char *oid,
+                                      rados_completion_t completion,
+                                      rados_xattrs_iter_t *iter);
+
+/** @} Asynchronous Xattrs */
+
 /**
  * @name Watch/Notify
  *
index 7ef5ae3367dba6e54acbd2b11fdc549c9da01812..6746dea4d28baf207045b001a4b98a3b442c7873 100644 (file)
@@ -968,7 +968,10 @@ namespace librados
      * @returns 0 on success, negative error code on failure
      */
     int aio_flush_async(AioCompletion *c);
-
+    int aio_getxattr(const std::string& oid, AioCompletion *c, const char *name, bufferlist& bl);
+    int aio_getxattrs(const std::string& oid, AioCompletion *c, std::map<std::string, bufferlist>& attrset);
+    int aio_setxattr(const std::string& oid, AioCompletion *c, const char *name, bufferlist& bl);
+    int aio_rmxattr(const std::string& oid, AioCompletion *c, const char *name);
     int aio_stat(const std::string& oid, AioCompletion *c, uint64_t *psize, time_t *pmtime);
     int aio_stat2(const std::string& oid, AioCompletion *c, uint64_t *psize, struct timespec *pts);
 
index cc115a3d2b7926f3d5e0e26d667dde783df3d9d5..c3fb5fa95fe1cfd1b24a551c63a8ab77d944c4a6 100644 (file)
@@ -1026,6 +1026,73 @@ int librados::IoCtxImpl::aio_stat2(const object_t& oid, AioCompletionImpl *c,
   return 0;
 }
 
+int librados::IoCtxImpl::aio_getxattr(const object_t& oid, AioCompletionImpl *c,
+                                     const char *name, bufferlist& bl)
+{
+  ::ObjectOperation rd;
+  prepare_assert_ops(&rd);
+  rd.getxattr(name, &bl, NULL);
+  int r = aio_operate_read(oid, &rd, c, 0, &bl);
+  return r;
+}
+
+int librados::IoCtxImpl::aio_rmxattr(const object_t& oid, AioCompletionImpl *c,
+                                    const char *name)
+{
+  ::ObjectOperation op;
+  prepare_assert_ops(&op);
+  op.rmxattr(name);
+  return aio_operate(oid, &op, c, snapc, 0);
+}
+
+int librados::IoCtxImpl::aio_setxattr(const object_t& oid, AioCompletionImpl *c,
+                                     const char *name, bufferlist& bl)
+{
+  ::ObjectOperation op;
+  prepare_assert_ops(&op);
+  op.setxattr(name, bl);
+  return aio_operate(oid, &op, c, snapc, 0);
+}
+
+struct AioGetxattrsData {
+  AioGetxattrsData(librados::AioCompletionImpl *c, map<string, bufferlist>* attrset,
+                  librados::RadosClient *_client) :
+    user_completion(c), user_attrset(attrset), client(_client) {}
+  struct librados::C_AioCompleteAndSafe user_completion;
+  map<string, bufferlist> result_attrset;
+  map<std::string, bufferlist>* user_attrset;
+  librados::RadosClient *client;
+};
+
+static void aio_getxattrs_complete(rados_completion_t c, void *arg) {
+  AioGetxattrsData *cdata = reinterpret_cast<AioGetxattrsData*>(arg);
+  int rc = rados_aio_get_return_value(c);
+  cdata->user_attrset->clear();
+  if (rc >= 0) {
+    for (map<string,bufferlist>::iterator p = cdata->result_attrset.begin();
+        p != cdata->result_attrset.end();
+        ++p) {
+      ldout(cdata->client->cct, 10) << "IoCtxImpl::getxattrs: xattr=" << p->first << dendl;
+      (*cdata->user_attrset)[p->first] = p->second;
+    }
+  }
+  cdata->user_completion.finish(rc);
+  ((librados::AioCompletionImpl*)c)->put();
+  delete cdata;
+}
+
+int librados::IoCtxImpl::aio_getxattrs(const object_t& oid, AioCompletionImpl *c,
+                                      map<std::string, bufferlist>& attrset)
+{
+  AioGetxattrsData *cdata = new AioGetxattrsData(c, &attrset, client);
+  ::ObjectOperation rd;
+  prepare_assert_ops(&rd);
+  rd.getxattrs(&cdata->result_attrset, NULL);
+  librados::AioCompletionImpl *comp = new librados::AioCompletionImpl;
+  comp->set_complete_callback(cdata, aio_getxattrs_complete);
+  return aio_operate_read(oid, &rd, comp, 0, NULL);
+}
+
 int librados::IoCtxImpl::aio_cancel(AioCompletionImpl *c)
 {
   return objecter->op_cancel(c->tid, -ECANCELED);
index 9e4d4cf0540a1532b173c462911782d60b1efe51..b3431f81b4d47d643088308e758943eb0d34c55d 100644 (file)
@@ -207,6 +207,14 @@ struct librados::IoCtxImpl {
               const char *method, bufferlist& inbl, bufferlist *outbl);
   int aio_stat(const object_t& oid, AioCompletionImpl *c, uint64_t *psize, time_t *pmtime);
   int aio_stat2(const object_t& oid, AioCompletionImpl *c, uint64_t *psize, struct timespec *pts);
+  int aio_getxattr(const object_t& oid, AioCompletionImpl *c,
+                  const char *name, bufferlist& bl);
+  int aio_setxattr(const object_t& oid, AioCompletionImpl *c,
+                  const char *name, bufferlist& bl);
+  int aio_getxattrs(const object_t& oid, AioCompletionImpl *c,
+                   map<string, bufferlist>& attrset);
+  int aio_rmxattr(const object_t& oid, AioCompletionImpl *c,
+                 const char *name);
   int aio_cancel(AioCompletionImpl *c);
 
   int pool_change_auid(unsigned long long auid);
index 01595ea327cf411d73b3937163bd11b8fdb79518..db31c58b32508fe22c9984e51e05ede96a6c8755 100644 (file)
@@ -1882,6 +1882,60 @@ int librados::IoCtx::aio_flush()
   return 0;
 }
 
+struct AioGetxattrDataPP {
+  AioGetxattrDataPP(librados::AioCompletionImpl *c, bufferlist *_bl) :
+    bl(_bl), completion(c) {}
+  bufferlist *bl;
+  struct librados::C_AioCompleteAndSafe completion;
+};
+
+static void rados_aio_getxattr_completepp(rados_completion_t c, void *arg) {
+  AioGetxattrDataPP *cdata = reinterpret_cast<AioGetxattrDataPP*>(arg);
+  int rc = rados_aio_get_return_value(c);
+  if (rc >= 0) {
+    rc = cdata->bl->length();
+  }
+  cdata->completion.finish(rc);
+  delete cdata;
+}
+
+int librados::IoCtx::aio_getxattr(const std::string& oid, librados::AioCompletion *c,
+                                 const char *name, bufferlist& bl)
+{
+  // create data object to be passed to async callback
+  AioGetxattrDataPP *cdata = new AioGetxattrDataPP(c->pc, &bl);
+  if (!cdata) {
+    return -ENOMEM;
+  }
+  // create completion callback
+  librados::AioCompletionImpl *comp = new librados::AioCompletionImpl;
+  comp->set_complete_callback(cdata, rados_aio_getxattr_completepp);
+  // call actual getxattr from IoCtxImpl
+  object_t obj(oid);
+  return io_ctx_impl->aio_getxattr(obj, comp, name, bl);
+}
+
+int librados::IoCtx::aio_getxattrs(const std::string& oid, AioCompletion *c,
+                                  map<std::string, bufferlist>& attrset)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_getxattrs(obj, c->pc, attrset);
+}
+
+int librados::IoCtx::aio_setxattr(const std::string& oid, AioCompletion *c,
+                                 const char *name, bufferlist& bl)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_setxattr(obj, c->pc, name, bl);
+}
+
+int librados::IoCtx::aio_rmxattr(const std::string& oid, AioCompletion *c,
+                                const char *name)
+{
+  object_t obj(oid);
+  return io_ctx_impl->aio_rmxattr(obj, c->pc, name);
+}
+
 int librados::IoCtx::aio_stat(const std::string& oid, librados::AioCompletion *c,
                              uint64_t *psize, time_t *pmtime)
 {
@@ -4524,6 +4578,129 @@ extern "C" int rados_aio_flush(rados_ioctx_t io)
   return 0;
 }
 
+struct AioGetxattrData {
+  AioGetxattrData(char* buf, rados_completion_t c, size_t l) :
+    user_buf(buf), len(l), user_completion((librados::AioCompletionImpl*)c) {}
+  bufferlist bl;
+  char* user_buf;
+  size_t len;
+  struct librados::C_AioCompleteAndSafe user_completion;
+};
+
+static void rados_aio_getxattr_complete(rados_completion_t c, void *arg) {
+  AioGetxattrData *cdata = reinterpret_cast<AioGetxattrData*>(arg);
+  int rc = rados_aio_get_return_value(c);
+  if (rc >= 0) {
+    if (cdata->bl.length() > cdata->len) {
+      rc = -ERANGE;
+    } else {
+      if (!cdata->bl.is_provided_buffer(cdata->user_buf))
+       cdata->bl.copy(0, cdata->bl.length(), cdata->user_buf);
+      rc = cdata->bl.length();
+    }
+  }
+  cdata->user_completion.finish(rc);
+  delete cdata;
+}
+
+extern "C" int rados_aio_getxattr(rados_ioctx_t io, const char *o,
+                                 rados_completion_t completion,
+                                 const char *name, char *buf, size_t len)
+{
+  tracepoint(librados, rados_aio_getxattr_enter, io, o, completion, name, len);
+  // create data object to be passed to async callback
+  AioGetxattrData *cdata = new AioGetxattrData(buf, completion, len);
+  if (!cdata) {
+    tracepoint(librados, rados_aio_getxattr_exit, -ENOMEM, NULL, 0);
+    return -ENOMEM;
+  }
+  cdata->bl.push_back(buffer::create_static(len, buf));
+  // create completion callback
+  librados::AioCompletionImpl *c = new librados::AioCompletionImpl;
+  c->set_complete_callback(cdata, rados_aio_getxattr_complete);
+  // call async getxattr of IoCtx
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int ret = ctx->aio_getxattr(oid, c, name, cdata->bl);
+  tracepoint(librados, rados_aio_getxattr_exit, ret, buf, ret);
+  return ret;
+}
+
+struct AioGetxattrsData {
+  AioGetxattrsData(rados_completion_t c, rados_xattrs_iter_t *_iter) :
+    iter(_iter), user_completion((librados::AioCompletionImpl*)c) {
+    it = new librados::RadosXattrsIter();
+  }
+  ~AioGetxattrsData() {
+    if (it) delete it;
+  }
+  librados::RadosXattrsIter *it;
+  rados_xattrs_iter_t *iter;
+  struct librados::C_AioCompleteAndSafe user_completion;
+};
+
+static void rados_aio_getxattrs_complete(rados_completion_t c, void *arg) {
+  AioGetxattrsData *cdata = reinterpret_cast<AioGetxattrsData*>(arg);
+  int rc = rados_aio_get_return_value(c);
+  if (rc) {
+    cdata->user_completion.finish(rc);
+  } else {
+    cdata->it->i = cdata->it->attrset.begin();
+    *cdata->iter = cdata->it;
+    cdata->it = 0;
+    cdata->user_completion.finish(0);
+  }
+  delete cdata;
+}
+
+extern "C" int rados_aio_getxattrs(rados_ioctx_t io, const char *oid,
+                                  rados_completion_t completion,
+                                  rados_xattrs_iter_t *iter)
+{
+  tracepoint(librados, rados_aio_getxattrs_enter, io, oid, completion);
+  // create data object to be passed to async callback
+  AioGetxattrsData *cdata = new AioGetxattrsData(completion, iter);
+  if (!cdata) {
+    tracepoint(librados, rados_getxattrs_exit, -ENOMEM, NULL);
+    return -ENOMEM;
+  }
+  // create completion callback
+  librados::AioCompletionImpl *c = new librados::AioCompletionImpl;
+  c->set_complete_callback(cdata, rados_aio_getxattrs_complete);
+  // call async getxattrs of IoCtx
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t obj(oid);
+  int ret = ctx->aio_getxattrs(obj, c, cdata->it->attrset);
+  tracepoint(librados, rados_aio_getxattrs_exit, ret, cdata->it);
+  return ret;
+}
+
+extern "C" int rados_aio_setxattr(rados_ioctx_t io, const char *o,
+                                 rados_completion_t completion,
+                                 const char *name, const char *buf, size_t len)
+{
+  tracepoint(librados, rados_aio_setxattr_enter, io, o, completion, name, buf, len);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  bufferlist bl;
+  bl.append(buf, len);
+  int retval = ctx->aio_setxattr(oid, (librados::AioCompletionImpl*)completion, name, bl);
+  tracepoint(librados, rados_aio_setxattr_exit, retval);
+  return retval;
+}
+
+extern "C" int rados_aio_rmxattr(rados_ioctx_t io, const char *o,
+                                rados_completion_t completion,
+                                const char *name)
+{
+  tracepoint(librados, rados_aio_rmxattr_enter, io, o, completion, name);
+  librados::IoCtxImpl *ctx = (librados::IoCtxImpl *)io;
+  object_t oid(o);
+  int retval = ctx->aio_rmxattr(oid, (librados::AioCompletionImpl*)completion, name);
+  tracepoint(librados, rados_aio_rmxattr_exit, retval);
+  return retval;
+}
+
 extern "C" int rados_aio_stat(rados_ioctx_t io, const char *o, 
                              rados_completion_t completion,
                              uint64_t *psize, time_t *pmtime)
index 550e350b2ef6c263930535dc0695a822a8bd5c0b..421050adcf506892d06d01a5efe38e2d23f79972 100644 (file)
@@ -669,6 +669,406 @@ TEST(LibRadosAio, RoundTripAppendPP) {
   delete my_completion3;
 }
 
+TEST(LibRadosAio, RemoveTest) {
+  char buf[128];
+  char buf2[sizeof(buf)];
+  rados_completion_t my_completion;
+  AioTestData test_data;
+  ASSERT_EQ("", test_data.init());
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+             set_completion_complete, set_completion_safe, &my_completion));
+  memset(buf, 0xaa, sizeof(buf));
+  ASSERT_EQ(0, rados_append(test_data.m_ioctx, "foo", buf, sizeof(buf)));
+  ASSERT_EQ(0, rados_aio_remove(test_data.m_ioctx, "foo", my_completion));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+  }
+  ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+  memset(buf2, 0, sizeof(buf2));
+  ASSERT_EQ(-ENOENT, rados_read(test_data.m_ioctx, "foo", buf2, sizeof(buf2), 0));
+  rados_aio_release(my_completion);
+}
+
+TEST(LibRadosAioPP, RemoveTestPP) {
+  char buf[128];
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
+  boost::scoped_ptr<AioCompletion> my_completion
+    (test_data.m_cluster.aio_create_completion
+     ((void*)&test_data, set_completion_completePP, set_completion_safePP));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_remove("foo", my_completion.get()));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  ASSERT_EQ(-ENOENT, test_data.m_ioctx.read("foo", bl2, sizeof(buf), 0));
+}
+
+TEST(LibRadosAio, XattrsRoundTrip) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  // append
+  AioTestData test_data;
+  ASSERT_EQ("", test_data.init());
+  memset(buf, 0xaa, sizeof(buf));
+  ASSERT_EQ(0, rados_append(test_data.m_ioctx, "foo", buf, sizeof(buf)));
+  // async getxattr
+  rados_completion_t my_completion;
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+             set_completion_complete, set_completion_safe, &my_completion));
+  ASSERT_EQ(0, rados_aio_getxattr(test_data.m_ioctx, "foo", my_completion, attr1, buf, sizeof(buf)));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+  }
+  ASSERT_EQ(-ENODATA, rados_aio_get_return_value(my_completion));
+  rados_aio_release(my_completion);
+  // async setxattr
+  rados_completion_t my_completion2;
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+             set_completion_complete, set_completion_safe, &my_completion2));
+  ASSERT_EQ(0, rados_aio_setxattr(test_data.m_ioctx, "foo", my_completion2, attr1, attr1_buf, sizeof(attr1_buf)));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+  }
+  ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+  rados_aio_release(my_completion2);
+  // async getxattr
+  rados_completion_t my_completion3;
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+             set_completion_complete, set_completion_safe, &my_completion3));
+  ASSERT_EQ(0, rados_aio_getxattr(test_data.m_ioctx, "foo", my_completion3, attr1, buf, sizeof(buf)));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+  }
+  ASSERT_EQ((int)sizeof(attr1_buf), rados_aio_get_return_value(my_completion3));
+  rados_aio_release(my_completion3);
+  // check content of attribute
+  ASSERT_EQ(0, memcmp(attr1_buf, buf, sizeof(attr1_buf)));
+}
+
+TEST(LibRadosAioPP, XattrsRoundTripPP) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
+  bufferlist bl2;
+  // async getxattr
+  boost::scoped_ptr<AioCompletion> my_completion
+    (test_data.m_cluster.aio_create_completion
+     ((void*)&test_data, set_completion_completePP, set_completion_safePP));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr("foo", my_completion.get(), attr1, bl2));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(-ENODATA, my_completion->get_return_value());
+  // append
+  bufferlist bl3;
+  bl3.append(attr1_buf, sizeof(attr1_buf));
+  // async setxattr
+  AioTestDataPP test_data2;
+  ASSERT_EQ("", test_data2.init());
+  boost::scoped_ptr<AioCompletion> my_completion2
+    (test_data.m_cluster.aio_create_completion
+     ((void*)&test_data2, set_completion_completePP, set_completion_safePP));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr("foo", my_completion2.get(), attr1, bl3));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  // async getxattr
+  bufferlist bl4;
+  AioTestDataPP test_data3;
+  ASSERT_EQ("", test_data3.init());
+  boost::scoped_ptr<AioCompletion> my_completion3
+    (test_data.m_cluster.aio_create_completion
+     ((void*)&test_data3, set_completion_completePP, set_completion_safePP));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr("foo", my_completion3.get(), attr1, bl4));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ((int)sizeof(attr1_buf), my_completion3->get_return_value());
+  // check content of attribute
+  ASSERT_EQ(0, memcmp(bl4.c_str(), attr1_buf, sizeof(attr1_buf)));
+}
+
+TEST(LibRadosAio, RmXattr) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  // append
+  memset(buf, 0xaa, sizeof(buf));
+  AioTestData test_data;
+  ASSERT_EQ("", test_data.init());
+  ASSERT_EQ(0, rados_append(test_data.m_ioctx, "foo", buf, sizeof(buf)));  
+  // async setxattr
+  rados_completion_t my_completion;
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+             set_completion_complete, set_completion_safe, &my_completion));
+  ASSERT_EQ(0, rados_aio_setxattr(test_data.m_ioctx, "foo", my_completion, attr1, attr1_buf, sizeof(attr1_buf)));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+  }
+  ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+  rados_aio_release(my_completion);
+  // async rmxattr
+  rados_completion_t my_completion2;
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+            set_completion_complete, set_completion_safe, &my_completion2));
+  ASSERT_EQ(0, rados_aio_rmxattr(test_data.m_ioctx, "foo", my_completion2, attr1));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion2));
+  }
+  ASSERT_EQ(0, rados_aio_get_return_value(my_completion2));
+  rados_aio_release(my_completion2);
+  // async getxattr after deletion
+  rados_completion_t my_completion3;
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+            set_completion_complete, set_completion_safe, &my_completion3));
+  ASSERT_EQ(0, rados_aio_getxattr(test_data.m_ioctx, "foo", my_completion3, attr1, buf, sizeof(buf)));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion3));
+  }
+  ASSERT_EQ(-ENODATA, rados_aio_get_return_value(my_completion3));
+  rados_aio_release(my_completion3);
+  // Test rmxattr on a removed object
+  char buf2[128];
+  char attr2[] = "attr2";
+  char attr2_buf[] = "foo bar baz";
+  memset(buf2, 0xbb, sizeof(buf2));
+  ASSERT_EQ(0, rados_write(test_data.m_ioctx, "foo_rmxattr", buf2, sizeof(buf2), 0));
+  // asynx setxattr
+  rados_completion_t my_completion4;
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+            set_completion_complete, set_completion_safe, &my_completion4));
+  ASSERT_EQ(0, rados_aio_setxattr(test_data.m_ioctx, "foo_rmxattr", my_completion4, attr2, attr2_buf, sizeof(attr2_buf)));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion4));
+  }
+  ASSERT_EQ(0, rados_aio_get_return_value(my_completion4));
+  rados_aio_release(my_completion4);
+  // remove object
+  ASSERT_EQ(0, rados_remove(test_data.m_ioctx, "foo_rmxattr"));
+  // async rmxattr on non existing object
+  rados_completion_t my_completion5;
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+            set_completion_complete, set_completion_safe, &my_completion5));
+  ASSERT_EQ(0, rados_aio_rmxattr(test_data.m_ioctx, "foo_rmxattr", my_completion5, attr2));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion5));
+  }
+  ASSERT_EQ(-ENOENT, rados_aio_get_return_value(my_completion5));
+  rados_aio_release(my_completion5);
+}
+
+TEST(LibRadosAioPP, RmXattrPP) {
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
+  // async setxattr
+  bufferlist bl2;
+  bl2.append(attr1_buf, sizeof(attr1_buf));
+  boost::scoped_ptr<AioCompletion> my_completion
+    (test_data.m_cluster.aio_create_completion
+     ((void*)&test_data, set_completion_completePP, set_completion_safePP));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr("foo", my_completion.get(), attr1, bl2));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  // async rmxattr
+  AioTestDataPP test_data2;
+  ASSERT_EQ("", test_data2.init());
+  boost::scoped_ptr<AioCompletion> my_completion2
+    (test_data.m_cluster.aio_create_completion
+     ((void*)&test_data2, set_completion_completePP, set_completion_safePP));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_rmxattr("foo", my_completion2.get(), attr1));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion2->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion2->get_return_value());
+  // async getxattr
+  AioTestDataPP test_data3;
+  ASSERT_EQ("", test_data3.init());
+  boost::scoped_ptr<AioCompletion> my_completion3
+    (test_data.m_cluster.aio_create_completion
+     ((void*)&test_data3, set_completion_completePP, set_completion_safePP));
+  bufferlist bl3;
+  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattr("foo", my_completion3.get(), attr1, bl3));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion3->wait_for_complete());
+  }
+  ASSERT_EQ(-ENODATA, my_completion3->get_return_value());
+  // Test rmxattr on a removed object
+  char buf2[128];
+  char attr2[] = "attr2";
+  char attr2_buf[] = "foo bar baz";
+  memset(buf2, 0xbb, sizeof(buf2));
+  bufferlist bl21;
+  bl21.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.write("foo_rmxattr", bl21, sizeof(buf2), 0));
+  bufferlist bl22;
+  bl22.append(attr2_buf, sizeof(attr2_buf));
+  // async setxattr
+  AioTestDataPP test_data4;
+  ASSERT_EQ("", test_data4.init());
+  boost::scoped_ptr<AioCompletion> my_completion4
+    (test_data.m_cluster.aio_create_completion
+     ((void*)&test_data4, set_completion_completePP, set_completion_safePP));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_setxattr("foo_rmxattr", my_completion4.get(), attr2, bl22));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion4->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion4->get_return_value());
+  // remove object
+  ASSERT_EQ(0, test_data.m_ioctx.remove("foo_rmxattr"));
+  // async rmxattr on non existing object
+  AioTestDataPP test_data5;
+  ASSERT_EQ("", test_data5.init());
+  boost::scoped_ptr<AioCompletion> my_completion5
+    (test_data.m_cluster.aio_create_completion
+     ((void*)&test_data5, set_completion_completePP, set_completion_safePP));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_rmxattr("foo_rmxattr", my_completion5.get(), attr2));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion5->wait_for_complete());
+  }
+  ASSERT_EQ(-ENOENT, my_completion5->get_return_value());
+}
+
+TEST(LibRadosAio, XattrIter) {
+  AioTestData test_data;
+  ASSERT_EQ("", test_data.init());
+  // Create an object with 2 attributes
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  char attr2[] = "attr2";
+  char attr2_buf[256];
+  for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+    attr2_buf[j] = j % 0xff;
+  }
+  memset(buf, 0xaa, sizeof(buf));
+  ASSERT_EQ(0, rados_append(test_data.m_ioctx, "foo", buf, sizeof(buf)));
+  ASSERT_EQ(0, rados_setxattr(test_data.m_ioctx, "foo", attr1, attr1_buf, sizeof(attr1_buf)));
+  ASSERT_EQ(0, rados_setxattr(test_data.m_ioctx, "foo", attr2, attr2_buf, sizeof(attr2_buf)));
+  // call async version of getxattrs and wait for completion
+  rados_completion_t my_completion;
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+            set_completion_complete, set_completion_safe, &my_completion));
+  rados_xattrs_iter_t iter;
+  ASSERT_EQ(0, rados_aio_getxattrs(test_data.m_ioctx, "foo", my_completion, &iter));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+  }
+  ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+  // loop over attributes
+  int num_seen = 0;
+  while (true) {
+    const char *name;
+    const char *val;
+    size_t len;
+    ASSERT_EQ(0, rados_getxattrs_next(iter, &name, &val, &len));
+    if (name == NULL) {
+      break;
+    }
+    ASSERT_LT(num_seen, 2);
+    if ((strcmp(name, attr1) == 0) && (val != NULL) && (memcmp(val, attr1_buf, len) == 0)) {
+      num_seen++;
+      continue;
+    }
+    else if ((strcmp(name, attr2) == 0) && (val != NULL) && (memcmp(val, attr2_buf, len) == 0)) {
+      num_seen++;
+      continue;
+    }
+    else {
+      ASSERT_EQ(0, 1);
+    }
+  }
+  rados_getxattrs_end(iter);
+}
+
+TEST(LibRadosIoPP, XattrListPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ("", test_data.init());
+  // create an object with 2 attributes
+  char buf[128];
+  char attr1[] = "attr1";
+  char attr1_buf[] = "foo bar baz";
+  char attr2[] = "attr2";
+  char attr2_buf[256];
+  for (size_t j = 0; j < sizeof(attr2_buf); ++j) {
+    attr2_buf[j] = j % 0xff;
+  }
+  memset(buf, 0xaa, sizeof(buf));
+  bufferlist bl1;
+  bl1.append(buf, sizeof(buf));
+  ASSERT_EQ(0, test_data.m_ioctx.append("foo", bl1, sizeof(buf)));
+  bufferlist bl2;
+  bl2.append(attr1_buf, sizeof(attr1_buf));
+  ASSERT_EQ(0, test_data.m_ioctx.setxattr("foo", attr1, bl2));
+  bufferlist bl3;
+  bl3.append(attr2_buf, sizeof(attr2_buf));
+  ASSERT_EQ(0, test_data.m_ioctx.setxattr("foo", attr2, bl3));
+  // call async version of getxattrs
+  boost::scoped_ptr<AioCompletion> my_completion
+    (test_data.m_cluster.aio_create_completion
+     ((void*)&test_data, set_completion_completePP, set_completion_safePP));
+  std::map<std::string, bufferlist> attrset;
+  ASSERT_EQ(0, test_data.m_ioctx.aio_getxattrs("foo", my_completion.get(), attrset));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(-ENODATA, my_completion->get_return_value());
+  for (std::map<std::string, bufferlist>::iterator i = attrset.begin();
+       i != attrset.end(); ++i) {
+    if (i->first == string(attr1)) {
+      ASSERT_EQ(0, memcmp(i->second.c_str(), attr1_buf, sizeof(attr1_buf)));
+    }
+    else if (i->first == string(attr2)) {
+      ASSERT_EQ(0, memcmp(i->second.c_str(), attr2_buf, sizeof(attr2_buf)));
+    }
+    else {
+      ASSERT_EQ(0, 1);
+    }
+  }
+}
+
 TEST(LibRadosAio, IsComplete) {
   AioTestData test_data;
   rados_completion_t my_completion;
@@ -1886,6 +2286,38 @@ TEST(LibRadosAio, MultiWritePP) {
   delete my_completion3;
 }
 
+TEST(LibRadosAio, AioUnlock) {
+  AioTestData test_data;
+  ASSERT_EQ("", test_data.init());
+  ASSERT_EQ(0, rados_lock_exclusive(test_data.m_ioctx, "foo", "TestLock", "Cookie", "", NULL, 0));
+  rados_completion_t my_completion;
+  ASSERT_EQ(0, rados_aio_create_completion((void*)&test_data,
+            set_completion_complete, set_completion_safe, &my_completion));
+  ASSERT_EQ(0, rados_aio_unlock(test_data.m_ioctx, "foo", "TestLock", "Cookie", my_completion));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, rados_aio_wait_for_complete(my_completion));
+  }
+  ASSERT_EQ(0, rados_aio_get_return_value(my_completion));
+  ASSERT_EQ(0, rados_lock_exclusive(test_data.m_ioctx, "foo", "TestLock", "Cookie", "", NULL,  0));
+}
+
+TEST(LibRadosAio, AioUnlockPP) {
+  AioTestDataPP test_data;
+  ASSERT_EQ(0, test_data.m_ioctx.lock_exclusive("foo", "TestLock", "Cookie", "", NULL, 0));
+  boost::scoped_ptr<AioCompletion> my_completion
+    (test_data.m_cluster.aio_create_completion
+     ((void*)&test_data, set_completion_completePP, set_completion_safePP));
+  ASSERT_EQ(0, test_data.m_ioctx.aio_unlock("foo", "TestLock", "Cookie", my_completion.get()));
+  {
+    TestAlarm alarm;
+    ASSERT_EQ(0, my_completion->wait_for_complete());
+  }
+  ASSERT_EQ(0, my_completion->get_return_value());
+  bufferlist bl2;
+  ASSERT_EQ(0, test_data.m_ioctx.lock_exclusive("foo", "TestLock", "Cookie", "", NULL, 0));
+}
+
 // EC test cases
 class AioTestDataEC
 {
index 837145c5dc24c1c92c8bb61f2f3540a8cffbf01a..e8d78fade430d4b69a0f968d4f52f5a1ec1475ec 100644 (file)
@@ -2291,6 +2291,102 @@ TRACEPOINT_EVENT(librados, rados_aio_flush_exit,
     )
 )
 
+TRACEPOINT_EVENT(librados, rados_aio_getxattr_enter,
+    TP_ARGS(
+        rados_ioctx_t, ioctx,
+        const char*, oid,
+        rados_completion_t, completion,
+        const char*, aname,
+        size_t, len),
+    TP_FIELDS(
+        ctf_integer_hex(rados_ioctx_t, ioctx, ioctx)
+        ctf_string(oid, oid)
+        ctf_integer_hex(rados_completion_t, completion, completion)
+        ctf_string(aname, aname)
+        ctf_integer(size_t, len, len)
+    )
+)
+
+TRACEPOINT_EVENT(librados, rados_aio_getxattr_exit,
+    TP_ARGS(
+        int, retval,
+        const char*, value,
+        int, len),
+    TP_FIELDS(
+        ctf_integer(int, retval, retval)
+        ceph_ctf_sequence(unsigned char, value, value, uint64_t, len)
+    )
+)
+
+TRACEPOINT_EVENT(librados, rados_aio_getxattrs_enter,
+    TP_ARGS(
+        rados_ioctx_t, ioctx,
+        const char*, oid,
+        rados_completion_t, completion),
+    TP_FIELDS(
+        ctf_integer_hex(rados_ioctx_t, ioctx, ioctx)
+        ctf_string(oid, oid)
+        ctf_integer_hex(rados_completion_t, completion, completion)
+    )
+)
+
+TRACEPOINT_EVENT(librados, rados_aio_getxattrs_exit,
+    TP_ARGS(
+        int, retval,
+        rados_xattrs_iter_t, iter),
+    TP_FIELDS(
+        ctf_integer(int, retval, retval)
+        ctf_integer_hex(rados_xattrs_iter_t, iter, iter)
+    )
+)
+
+TRACEPOINT_EVENT(librados, rados_aio_setxattr_enter,
+    TP_ARGS(
+        rados_ioctx_t, ioctx,
+        const char*, oid,
+        rados_completion_t, completion,
+        const char*, aname,
+        const char*, value,
+        size_t, len),
+    TP_FIELDS(
+        ctf_integer_hex(rados_ioctx_t, ioctx, ioctx)
+        ctf_string(oid, oid)
+        ctf_integer_hex(rados_completion_t, completion, completion)
+        ctf_string(aname, aname)
+        ceph_ctf_sequence(unsigned char, value, value, size_t, len)
+    )
+)
+
+TRACEPOINT_EVENT(librados, rados_aio_setxattr_exit,
+    TP_ARGS(
+        int, retval),
+    TP_FIELDS(
+        ctf_integer(int, retval, retval)
+    )
+)
+
+TRACEPOINT_EVENT(librados, rados_aio_rmxattr_enter,
+    TP_ARGS(
+        rados_ioctx_t, ioctx,
+        const char*, oid,
+        rados_completion_t, completion,
+        const char*, aname),
+    TP_FIELDS(
+        ctf_integer_hex(rados_ioctx_t, ioctx, ioctx)
+        ctf_string(oid, oid)
+        ctf_integer_hex(rados_completion_t, completion, completion)
+        ctf_string(aname, aname)
+    )
+)
+
+TRACEPOINT_EVENT(librados, rados_aio_rmxattr_exit,
+    TP_ARGS(
+        int, retval),
+    TP_FIELDS(
+        ctf_integer(int, retval, retval)
+    )
+)
+
 TRACEPOINT_EVENT(librados, rados_aio_stat_enter,
     TP_ARGS(
         rados_ioctx_t, ioctx,