From 2543cde79326088da232c1ce1a45287dbdb4397a Mon Sep 17 00:00:00 2001 From: Josh Durgin Date: Thu, 6 Feb 2014 19:51:59 -0800 Subject: [PATCH] librados: add exec to the c object operations api Add two versions: one that allocates a buffer of the appropriate length for the user, but relies on the user to free it, and one that uses a user-supplied buffer but may fail if it is too short. Reuse the bufferlist -> buffer conversion context added for reads into the user supplied buffer. The librados-allocated buffer can be handled just like librados allocated buffers used by the various command functions, so just reuse do_out_buffer() for them. Signed-off-by: Josh Durgin --- src/include/rados/librados.h | 54 +++++++++++++++++++++++ src/librados/librados.cc | 47 ++++++++++++++++++++ src/test/librados/c_read_operations.cc | 59 ++++++++++++++++++++++++++ 3 files changed, 160 insertions(+) diff --git a/src/include/rados/librados.h b/src/include/rados/librados.h index bd97bc71be01d..29d20858cb37f 100644 --- a/src/include/rados/librados.h +++ b/src/include/rados/librados.h @@ -241,6 +241,7 @@ typedef void *rados_write_op_t; * - Creation and deletion: rados_create_read_op() rados_release_read_op() * - Object properties: rados_read_op_stat(), rados_read_op_assert_exists() * - IO on objects: rados_read_op_read() + * - 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(), * rados_aio_read_op_operate() @@ -1987,6 +1988,59 @@ void rados_read_op_read(rados_read_op_t read_op, int *prval); /** + * Execute an OSD class method on an object + * See rados_exec() for general description. + * + * The output buffer is allocated on the heap; the caller is + * expected to release that memory with rados_buffer_free(). The + * buffer and length pointers can all be NULL, in which case they are + * not filled in. + * + * @param read_op operation to add this action to + * @param cls the name of the class + * @param method the name of the method + * @param in_buf where to find input + * @param in_len length of in_buf in bytes + * @param out_buf where to put librados-allocated output buffer + * @param out_len length of out_buf in bytes + * @param prval where to store the return value from the method + */ +void rados_read_op_exec(rados_read_op_t read_op, + const char *cls, + const char *method, + const char *in_buf, + size_t in_len, + char **out_buf, + size_t *out_len, + int *prval); + +/** + * Execute an OSD class method on an object + * See rados_exec() for general description. + * + * If the output buffer is too small, prval will + * be set to -ERANGE and used_len will be 0. + * + * @param read_op operation to add this action to + * @param cls the name of the class + * @param method the name of the method + * @param in_buf where to find input + * @param in_len length of in_buf in bytes + * @param out_buf user-provided buffer to read into + * @param out_len length of out_buf in bytes + * @param used_len where to store the number of bytes read into out_buf + * @param prval where to store the return value from the method + */ +void rados_read_op_exec_user_buf(rados_read_op_t read_op, + const char *cls, + const char *method, + const char *in_buf, + size_t in_len, + char *out_buf, + size_t out_len, + size_t *used_len, + int *prval); + /** * Perform a write operation synchronously diff --git a/src/librados/librados.cc b/src/librados/librados.cc index 8dfc91315afaf..652994130a844 100644 --- a/src/librados/librados.cc +++ b/src/librados/librados.cc @@ -3247,6 +3247,53 @@ extern "C" void rados_read_op_read(rados_read_op_t read_op, ((::ObjectOperation *)read_op)->read(offset, len, &ctx->out_bl, prval, ctx); } +class C_out_buffer : public Context { + char **out_buf; + size_t *out_len; +public: + bufferlist out_bl; + C_out_buffer(char **out_buf, size_t *out_len) : out_buf(out_buf), + out_len(out_len) {} + void finish(int r) { + // ignore r since we don't know the meaning of return values + // from custom class methods + do_out_buffer(out_bl, out_buf, out_len); + } +}; + +extern "C" void rados_read_op_exec(rados_read_op_t read_op, + const char *cls, + const char *method, + const char *in_buf, + size_t in_len, + char **out_buf, + size_t *out_len, + int *prval) +{ + bufferlist inbl; + inbl.append(in_buf, in_len); + C_out_buffer *ctx = new C_out_buffer(out_buf, out_len); + ((::ObjectOperation *)read_op)->call(cls, method, inbl, &ctx->out_bl, ctx, + prval); +} + +extern "C" void rados_read_op_exec_user_buf(rados_read_op_t read_op, + const char *cls, + const char *method, + const char *in_buf, + size_t in_len, + char *out_buf, + size_t out_len, + size_t *used_len, + int *prval) +{ + C_bl_to_buf *ctx = new C_bl_to_buf(out_buf, out_len, used_len, prval); + bufferlist inbl; + inbl.append(in_buf, in_len); + ((::ObjectOperation *)read_op)->call(cls, method, inbl, &ctx->out_bl, ctx, + prval); +} + extern "C" int rados_read_op_operate(rados_read_op_t read_op, rados_ioctx_t io, const char *oid, diff --git a/src/test/librados/c_read_operations.cc b/src/test/librados/c_read_operations.cc index bec72f5a7ead7..9d303acfa0f46 100644 --- a/src/test/librados/c_read_operations.cc +++ b/src/test/librados/c_read_operations.cc @@ -173,6 +173,65 @@ TEST_F(CReadOpsTest, ShortRead) { remove_object(); } +TEST_F(CReadOpsTest, Exec) { + // create object so we don't get -ENOENT + write_object(); + + rados_read_op_t op = rados_create_read_op(); + ASSERT_TRUE(op); + size_t bytes_read = 0; + char *out = NULL; + int rval = 0; + rados_read_op_exec(op, "rbd", "get_all_features", NULL, 0, &out, + &bytes_read, &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + EXPECT_EQ(0, rval); + EXPECT_TRUE(out); + uint64_t features; + EXPECT_EQ(sizeof(features), bytes_read); + // make sure buffer is at least as long as it claims + ASSERT_TRUE(out[bytes_read-1] == out[bytes_read-1]); + rados_buffer_free(out); + + remove_object(); +} + +TEST_F(CReadOpsTest, ExecUserBuf) { + // create object so we don't get -ENOENT + write_object(); + + rados_read_op_t op = rados_create_read_op(); + size_t bytes_read = 0; + uint64_t features; + char out[sizeof(features)]; + int rval = 0; + rados_read_op_exec_user_buf(op, "rbd", "get_all_features", NULL, 0, out, + sizeof(out), &bytes_read, &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + EXPECT_EQ(0, rval); + EXPECT_EQ(sizeof(features), bytes_read); + + // buffer too short + bytes_read = 1024; + op = rados_create_read_op(); + rados_read_op_exec_user_buf(op, "rbd", "get_all_features", NULL, 0, out, + sizeof(features) - 1, &bytes_read, &rval); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + EXPECT_EQ(0u, bytes_read); + EXPECT_EQ(-ERANGE, rval); + + // input buffer and no rval or bytes_read + op = rados_create_read_op(); + rados_read_op_exec_user_buf(op, "rbd", "get_all_features", out, sizeof(out), + out, sizeof(out), NULL, NULL); + ASSERT_EQ(0, rados_read_op_operate(op, ioctx, obj, 0)); + rados_release_read_op(op); + + remove_object(); +} + TEST_F(CReadOpsTest, Stat) { rados_read_op_t op = rados_create_read_op(); uint64_t size = 1; -- 2.39.5