From: Sebastien Ponce Date: Sat, 25 Jun 2016 16:04:34 +0000 (+0200) Subject: rados : implementation of async stat/getxattr(s)/rmxattr/setxattr X-Git-Tag: v11.1.0~355^2~7 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=65e5500b56407e0318247889c25a7812b9ce9489;p=ceph.git rados : implementation of async stat/getxattr(s)/rmxattr/setxattr This completes the asynchronous API of librados for stats and external attributes Signed-off-by: Sebastien Ponce --- diff --git a/src/include/rados/librados.h b/src/include/rados/librados.h index 5738290f6d1d..53328f996158 100644 --- a/src/include/rados/librados.h +++ b/src/include/rados/librados.h @@ -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 * diff --git a/src/include/rados/librados.hpp b/src/include/rados/librados.hpp index 7ef5ae3367db..6746dea4d28b 100644 --- a/src/include/rados/librados.hpp +++ b/src/include/rados/librados.hpp @@ -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& 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); diff --git a/src/librados/IoCtxImpl.cc b/src/librados/IoCtxImpl.cc index cc115a3d2b79..c3fb5fa95fe1 100644 --- a/src/librados/IoCtxImpl.cc +++ b/src/librados/IoCtxImpl.cc @@ -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* attrset, + librados::RadosClient *_client) : + user_completion(c), user_attrset(attrset), client(_client) {} + struct librados::C_AioCompleteAndSafe user_completion; + map result_attrset; + map* user_attrset; + librados::RadosClient *client; +}; + +static void aio_getxattrs_complete(rados_completion_t c, void *arg) { + AioGetxattrsData *cdata = reinterpret_cast(arg); + int rc = rados_aio_get_return_value(c); + cdata->user_attrset->clear(); + if (rc >= 0) { + for (map::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& 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); diff --git a/src/librados/IoCtxImpl.h b/src/librados/IoCtxImpl.h index 9e4d4cf0540a..b3431f81b4d4 100644 --- a/src/librados/IoCtxImpl.h +++ b/src/librados/IoCtxImpl.h @@ -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& 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); diff --git a/src/librados/librados.cc b/src/librados/librados.cc index 01595ea327cf..db31c58b3250 100644 --- a/src/librados/librados.cc +++ b/src/librados/librados.cc @@ -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(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& 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(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(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) diff --git a/src/test/librados/aio.cc b/src/test/librados/aio.cc index 550e350b2ef6..421050adcf50 100644 --- a/src/test/librados/aio.cc +++ b/src/test/librados/aio.cc @@ -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 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 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 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 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 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 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 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 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 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 my_completion + (test_data.m_cluster.aio_create_completion + ((void*)&test_data, set_completion_completePP, set_completion_safePP)); + std::map 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::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 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 { diff --git a/src/tracing/librados.tp b/src/tracing/librados.tp index 837145c5dc24..e8d78fade430 100644 --- a/src/tracing/librados.tp +++ b/src/tracing/librados.tp @@ -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,