diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 98020848997baf23b5339a0755f013ca9ce2084c..e2f7011e5d716be7904d6e7fb0a66ba9e4a4ff02 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,6 +59,7 @@ set (common_sources object.cc exception.cc ipc.cc + iovec.c errinj.cc errcode.c assoc.c diff --git a/src/box/box.cc b/src/box/box.cc index 5f079c7e811580fecec6fc870c593077dc56f2ba..3b059b3d5a3406ab2a4a79453d77756a2428eac6 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -299,8 +299,9 @@ boxk(enum iproto_type type, uint32_t space_id, const char *format, ...) } va_end(ap); assert(data <= buf + sizeof(buf)); - req.tuple = buf; - req.tuple_end = data; + req.tuple_cnt = 1; + req.tuple[0].iov_base = (void *) buf; + req.tuple[0].iov_len = data - buf; process_rw(&null_port, &req); } diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc index 538bbe29453e27ce2abf0988bef8c6128921a7a5..282dc2633ae22022a9678a431087f94cd0fb6bc5 100644 --- a/src/box/lua/call.cc +++ b/src/box/lua/call.cc @@ -255,10 +255,12 @@ lbox_request_create(struct request *request, if (tuple > 0) { struct tbuf *tuple_buf = tbuf_new(&fiber()->gc); luamp_encode(L, luaL_msgpack_default, tuple_buf, tuple); - request->tuple = tuple_buf->data; - request->tuple_end = tuple_buf->data + tuple_buf->size; - if (mp_typeof(*request->tuple) != MP_ARRAY) + assert(tuple_buf->size > 0); + if (mp_typeof(*tuple_buf->data) != MP_ARRAY) tnt_raise(ClientError, ER_TUPLE_NOT_ARRAY); + request->tuple_cnt = 1; + request->tuple[0].iov_base = tuple_buf->data; + request->tuple[0].iov_len = tuple_buf->size; } } @@ -563,7 +565,9 @@ box_lua_call(struct request *request, struct port *port) */ SetuidGuard setuid(name, name_len, user, PRIV_X); /* Push the rest of args (a tuple). */ - const char *args = request->tuple; + size_t args_len; + const char *args = (char *) iovec_join(&fiber()->gc, request->tuple, + request->tuple_cnt, &args_len); uint32_t arg_count = mp_decode_array(&args); luaL_checkstack(L, arg_count, "call: out of stack"); diff --git a/src/box/request.cc b/src/box/request.cc index f8a19d2aa510610c5c809815a4026cfe5aef969f..5d577734907f2622da83fa57172aa1250778bbfc 100644 --- a/src/box/request.cc +++ b/src/box/request.cc @@ -54,8 +54,8 @@ execute_replace(struct request *request, struct port *port) struct space *space = space_cache_find(request->space_id); access_check_space(space, PRIV_W); - struct tuple *new_tuple = tuple_new(space->format, request->tuple, - request->tuple_end); + struct tuple *new_tuple = tuple_newv(space->format, request->tuple, + request->tuple_cnt); TupleGuard guard(new_tuple); space_validate_tuple(space, new_tuple); enum dup_replace_mode mode = dup_replace_mode(request->type); @@ -86,13 +86,23 @@ execute_update(struct request *request, struct port *port) return; } + /* + * tuple_update() currently doesn't support scattered tuples. + * Update expression usually fits to first iovec and it's not + * a problem. For huge expressions prepare contiguous chunk of memory + * from vector. + */ + size_t expr_len; + const char *expr = (const char *) iovec_join(&fiber()->gc, + request->tuple, request->tuple_cnt, &expr_len); + /* Update the tuple. */ struct tuple *new_tuple = tuple_update(space->format, region_alloc_cb, &fiber()->gc, - old_tuple, request->tuple, - request->tuple_end, + old_tuple, expr, expr + expr_len, request->field_base); + TupleGuard guard(new_tuple); space_validate_tuple(space, new_tuple); txn_replace(txn, space, old_tuple, new_tuple, DUP_INSERT); @@ -166,7 +176,17 @@ execute_auth(struct request *request, struct port * /* port */) { const char *user = request->key; uint32_t len = mp_decode_strl(&user); - authenticate(user, len, request->tuple, request->tuple_end); + + /* + * authenticate() doesn't support scattered tuple and requires + * contiguous chunk of memory. Since size of first iovec is usually + * enough to fit credentials, a slow path of iovec_join() is not a + * problem. + */ + size_t credentials_len; + const char *credentials = (const char *) iovec_join(&fiber()->gc, + request->tuple, request->tuple_cnt, &credentials_len); + authenticate(user, len, credentials, credentials + credentials_len); } /** }}} */ @@ -228,8 +248,9 @@ request_decode(struct request *request, const char *data, uint32_t len) request->iterator = mp_decode_uint(&value); break; case IPROTO_TUPLE: - request->tuple = value; - request->tuple_end = data; + request->tuple_cnt = 1; + request->tuple[0].iov_base = (void *) value; + request->tuple[0].iov_len = data - value; break; case IPROTO_KEY: case IPROTO_FUNCTION_NAME: @@ -276,11 +297,10 @@ request_encode(struct request *request, struct iovec *iov) pos += key_len; map_size++; } - if (request->tuple) { + if (request->tuple_cnt > 0) { pos = mp_encode_uint(pos, IPROTO_TUPLE); - iov[1].iov_base = (void *) request->tuple; - iov[1].iov_len = (request->tuple_end - request->tuple); - iovcnt++; + iovec_copy(iov + iovcnt, request->tuple, request->tuple_cnt); + iovcnt += request->tuple_cnt; map_size++; } @@ -289,5 +309,6 @@ request_encode(struct request *request, struct iovec *iov) iov[0].iov_base = begin; iov[0].iov_len = pos - begin; + assert(iovcnt <= REQUEST_IOVMAX); return iovcnt; } diff --git a/src/box/request.h b/src/box/request.h index e257109f65fc3b07820e2d040831cc57b7c77b8c..fb4964005efb5bf9c99423f95975b9f454b2baa7 100644 --- a/src/box/request.h +++ b/src/box/request.h @@ -29,7 +29,9 @@ * SUCH DAMAGE. */ #include <stdbool.h> +#include "iovec.h" #include "xrow.h" +#include "small/region.h" struct txn; struct port; @@ -57,8 +59,8 @@ struct request const char *key; const char *key_end; /** Insert/replace tuple or proc argument or update operations. */ - const char *tuple; - const char *tuple_end; + struct iovec tuple[TUPLE_IOVMAX]; + int tuple_cnt; /** Base field offset for error messages, e.g. 0 for C and 1 for Lua. */ int field_base; diff --git a/src/box/tuple.cc b/src/box/tuple.cc index 4c656f84c8b1a98910f805380f6c73949eb54a63..8110a53da1652f3db44a3967ac19a8c908a0a91d 100644 --- a/src/box/tuple.cc +++ b/src/box/tuple.cc @@ -396,12 +396,17 @@ tuple_update(struct tuple_format *format, } struct tuple * -tuple_new(struct tuple_format *format, const char *data, const char *end) +tuple_newv(struct tuple_format *format, struct iovec *tbody, size_t tbody_cnt) { - size_t tuple_len = end - data; - assert(mp_typeof(*data) == MP_ARRAY); - struct tuple *new_tuple = tuple_alloc(format, tuple_len); - memcpy(new_tuple->data, data, tuple_len); + size_t len = iovec_len(tbody, tbody_cnt); + struct tuple *new_tuple = tuple_alloc(format, len); + char *data = new_tuple->data; + for (int i = 0; i < tbody_cnt; i++) { + memcpy(data, tbody->iov_base, tbody->iov_len); + data += tbody->iov_len; + tbody++; + } + try { tuple_init_field_map(format, new_tuple, (uint32_t *)new_tuple); } catch (...) { @@ -532,7 +537,7 @@ tuple_init(float arena_prealloc, uint32_t objsize_min, say_warn("disable shared arena since running under OpenVZ " "(https://bugzilla.openvz.org/show_bug.cgi?id=2805)"); flags = MAP_PRIVATE; - } else { + } else { say_info("mapping %zu bytes for a shared arena...", prealloc); flags = MAP_SHARED; diff --git a/src/box/tuple.h b/src/box/tuple.h index 959a75da265aa6dca1af5e4e3d2d34673e74b8a7..cbe87345ead2a7393d4bdd935f8ab17cbe1f6e75 100644 --- a/src/box/tuple.h +++ b/src/box/tuple.h @@ -30,6 +30,7 @@ */ #include "trivia/util.h" #include "key_def.h" /* for enum field_type */ +#include "iovec.h" enum { FORMAT_ID_MAX = UINT16_MAX - 1, FORMAT_ID_NIL = UINT16_MAX }; enum { FORMAT_REF_MAX = INT32_MAX, TUPLE_REF_MAX = UINT16_MAX }; @@ -166,12 +167,17 @@ tuple_alloc(struct tuple_format *format, size_t size); * Create a new tuple from a sequence of BER-len encoded fields. * tuple->refs is 0. * - * @post *data is advanced to the length of tuple data - * * Throws an exception if tuple format is incorrect. */ struct tuple * -tuple_new(struct tuple_format *format, const char *data, const char *end); +tuple_newv(struct tuple_format *format, struct iovec *tbody, size_t tbody_cnt); + +static inline struct tuple * +tuple_new(struct tuple_format *format, const char *data, const char *end) +{ + struct iovec tuple = { (void *) data, (size_t) (end - data) }; + return tuple_newv(format, &tuple, 1); +} /** * Free the tuple. diff --git a/src/box/xrow.h b/src/box/xrow.h index 9cf1327779c3f39ba2964b68510cb2fe98d3ab0d..30f6fd1d24405440dc984a7a8c0d8e92a8e80141 100644 --- a/src/box/xrow.h +++ b/src/box/xrow.h @@ -37,8 +37,11 @@ extern "C" { #endif enum { + /* tuplmax <= obuf_alloc_factor << TUPLE_IOVMAX */ + TUPLE_IOVMAX = 14, + REQUEST_IOVMAX = TUPLE_IOVMAX + 2, XROW_HEADER_IOVMAX = 1, - XROW_BODY_IOVMAX = 2, + XROW_BODY_IOVMAX = REQUEST_IOVMAX, XROW_IOVMAX = XROW_HEADER_IOVMAX + XROW_BODY_IOVMAX + 1 }; diff --git a/src/iovec.c b/src/iovec.c new file mode 100644 index 0000000000000000000000000000000000000000..97c5abf18b6fe0b5a7ef3cf9c5ad0f68c8911ed1 --- /dev/null +++ b/src/iovec.c @@ -0,0 +1,31 @@ + +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "iovec.h" diff --git a/src/iovec.h b/src/iovec.h new file mode 100644 index 0000000000000000000000000000000000000000..38ac5bbf8b9620ed0c708c64aa3ca8077dc44590 --- /dev/null +++ b/src/iovec.h @@ -0,0 +1,111 @@ +#ifndef TARANTOOL_IOVEC_H_INCLUDED +#define TARANTOOL_IOVEC_H_INCLUDED +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stddef.h> +#include <sys/uio.h> /* struct iovec */ + +#include "trivia/util.h" +#include "small/region.h" + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +/** + * \brief Calculate total length of \a iov + * \param iov iovec + * \param iovcnt size of \a iov + * \return total length of \a iov + */ +static inline size_t +iovec_len(struct iovec *iov, int iovcnt) +{ + size_t len = 0; + for (int i = 0; i < iovcnt; i++) + len += iov[i].iov_len; + return len; +} + +/** + * \brief Copy \a src into \a dst up to \a size bytes + * \param dst iovec + * \param src iovec + * \param size size of \a iov + */ +static inline size_t +iovec_copy(struct iovec *dst, struct iovec *src, int iovcnt) +{ + size_t len = 0; + for (int i = 0; i < iovcnt; i++) { + len += src->iov_len; + *(dst++) = *(src++); + } + return len; +} + +#if defined(__cplusplus) +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + +#if defined(__cplusplus) + +/** + * \brief Conventional function to join iovec into a solid memory chunk. + * For iovec of size 1 returns iov->iov_base without allocation extra memory. + * \param region region alloc + * \param iov vector + * \param iovcnt size of iovec + * \param[out] size calculated length of \a iov + * \return solid memory chunk + */ +static inline void * +iovec_join(struct region *region, struct iovec *iov, int iovcnt, size_t *plen) +{ + assert(iovcnt > 0 && plen != NULL); + if (likely(iovcnt == 1)) { + /* Fast path for single iovec or zero size */ + *plen = iov->iov_len; + return iov->iov_base; + } + + size_t len = iovec_len(iov, iovcnt); + char *data = (char *) region_alloc(region, len); + char *pos = data; + for (int i = 0; i < iovcnt; i++) + memcpy(pos, iov[i].iov_base, iov[i].iov_len); + + *plen = len; + return data; +} + +#endif /* defined(__cplusplus) */ + +#endif /* TARANTOOL_IOVEC_H_INCLUDED */