From e475586b2a578d18d40510455c8e3a29d6841c45 Mon Sep 17 00:00:00 2001 From: Ben Walker Date: Fri, 27 Sep 2019 13:30:09 -0700 Subject: [PATCH] util: Add spdk_iovcpy, an iovec to iovec copy function. Like memcpy, but works on two iovecs. Change-Id: Ia1cf462a95690286f0c19325fc10937b9ba6baf3 Signed-off-by: Ben Walker Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/473976 Tested-by: SPDK CI Jenkins Reviewed-by: Jim Harris Reviewed-by: Shuhei Matsumoto --- include/spdk/util.h | 7 + lib/util/Makefile | 2 +- lib/util/iov.c | 111 ++++++++++++ test/unit/lib/util/Makefile | 2 +- test/unit/lib/util/iov.c/.gitignore | 1 + test/unit/lib/util/iov.c/Makefile | 38 ++++ test/unit/lib/util/iov.c/iov_ut.c | 258 ++++++++++++++++++++++++++++ test/unit/unittest.sh | 1 + 8 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 lib/util/iov.c create mode 100644 test/unit/lib/util/iov.c/.gitignore create mode 100644 test/unit/lib/util/iov.c/Makefile create mode 100644 test/unit/lib/util/iov.c/iov_ut.c diff --git a/include/spdk/util.h b/include/spdk/util.h index 4e8e553b5..8b52dc358 100644 --- a/include/spdk/util.h +++ b/include/spdk/util.h @@ -92,6 +92,13 @@ spdk_divide_round_up(uint64_t num, uint64_t divisor) return (num + divisor - 1) / divisor; } +/** + * Copy the data described by the source iovec to the destination iovec. + * + * \return The number of bytes copied. + */ +size_t spdk_iovcpy(struct iovec *siov, size_t siovcnt, struct iovec *diov, size_t diovcnt); + /** * Scan build is really pessimistic and assumes that mempool functions can diff --git a/lib/util/Makefile b/lib/util/Makefile index c4e58eb37..d6e979240 100644 --- a/lib/util/Makefile +++ b/lib/util/Makefile @@ -35,7 +35,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk C_SRCS = base64.c bit_array.c cpuset.c crc16.c crc32.c crc32c.c crc32_ieee.c \ - dif.c fd.c file.c math.c pipe.c strerror_tls.c string.c uuid.c + dif.c fd.c file.c iov.c math.c pipe.c strerror_tls.c string.c uuid.c LIBNAME = util LOCAL_SYS_LIBS = -luuid diff --git a/lib/util/iov.c b/lib/util/iov.c new file mode 100644 index 000000000..e89ef9d21 --- /dev/null +++ b/lib/util/iov.c @@ -0,0 +1,111 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "spdk/util.h" + +size_t +spdk_iovcpy(struct iovec *siov, size_t siovcnt, struct iovec *diov, size_t diovcnt) +{ + size_t total_sz; + size_t sidx; + size_t didx; + int siov_len; + uint8_t *siov_base; + int diov_len; + uint8_t *diov_base; + + /* d prefix = destination. s prefix = source. */ + + assert(diovcnt > 0); + assert(siovcnt > 0); + + total_sz = 0; + sidx = 0; + didx = 0; + siov_len = siov[0].iov_len; + siov_base = siov[0].iov_base; + diov_len = diov[0].iov_len; + diov_base = diov[0].iov_base; + while (siov_len > 0 && diov_len > 0) { + if (siov_len == diov_len) { + memcpy(diov_base, siov_base, siov_len); + total_sz += siov_len; + + /* Advance both iovs to the next element */ + sidx++; + if (sidx == siovcnt) { + break; + } + + didx++; + if (didx == diovcnt) { + break; + } + + siov_len = siov[sidx].iov_len; + siov_base = siov[sidx].iov_base; + diov_len = diov[didx].iov_len; + diov_base = diov[didx].iov_base; + } else if (siov_len < diov_len) { + memcpy(diov_base, siov_base, siov_len); + total_sz += siov_len; + + /* Advance only the source to the next element */ + sidx++; + if (sidx == siovcnt) { + break; + } + + diov_base += siov_len; + diov_len -= siov_len; + siov_len = siov[sidx].iov_len; + siov_base = siov[sidx].iov_base; + } else { + memcpy(diov_base, siov_base, diov_len); + total_sz += diov_len; + + /* Advance only the destination to the next element */ + didx++; + if (didx == diovcnt) { + break; + } + + siov_base += diov_len; + siov_len -= diov_len; + diov_len = diov[didx].iov_len; + diov_base = diov[didx].iov_base; + } + } + + return total_sz; +} diff --git a/test/unit/lib/util/Makefile b/test/unit/lib/util/Makefile index 752031897..20cf5b124 100644 --- a/test/unit/lib/util/Makefile +++ b/test/unit/lib/util/Makefile @@ -35,7 +35,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) include $(SPDK_ROOT_DIR)/mk/spdk.common.mk DIRS-y = base64.c bit_array.c cpuset.c crc16.c crc32_ieee.c crc32c.c dif.c \ - pipe.c string.c + iov.c pipe.c string.c .PHONY: all clean $(DIRS-y) diff --git a/test/unit/lib/util/iov.c/.gitignore b/test/unit/lib/util/iov.c/.gitignore new file mode 100644 index 000000000..94d8d9621 --- /dev/null +++ b/test/unit/lib/util/iov.c/.gitignore @@ -0,0 +1 @@ +iov_ut diff --git a/test/unit/lib/util/iov.c/Makefile b/test/unit/lib/util/iov.c/Makefile new file mode 100644 index 000000000..c7b4ccd5a --- /dev/null +++ b/test/unit/lib/util/iov.c/Makefile @@ -0,0 +1,38 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 THE COPYRIGHT +# OWNER 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. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..) + +TEST_FILE = iov_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/test/unit/lib/util/iov.c/iov_ut.c b/test/unit/lib/util/iov.c/iov_ut.c new file mode 100644 index 000000000..b6871ebbe --- /dev/null +++ b/test/unit/lib/util/iov.c/iov_ut.c @@ -0,0 +1,258 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER 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 "spdk/stdinc.h" + +#include "spdk_cunit.h" + +#include "util/iov.c" + +static int +_check_val(void *buf, size_t len, uint8_t val) +{ + size_t i; + uint8_t *data = buf; + + for (i = 0; i < len; i++) { + if (data[i] != val) { + return -1; + } + } + + return 0; +} + +static void +test_single_iov(void) +{ + struct iovec siov[1]; + struct iovec diov[1]; + uint8_t sdata[64]; + uint8_t ddata[64]; + ssize_t rc; + + /* Simplest cases- 1 element in each iovec. */ + + /* Same size. */ + memset(sdata, 1, sizeof(sdata)); + memset(ddata, 0, sizeof(ddata)); + siov[0].iov_base = sdata; + siov[0].iov_len = sizeof(sdata); + diov[0].iov_base = ddata; + diov[0].iov_len = sizeof(ddata); + + rc = spdk_iovcpy(siov, 1, diov, 1); + CU_ASSERT(rc == sizeof(sdata)); + CU_ASSERT(_check_val(ddata, 64, 1) == 0); + + /* Source smaller than dest */ + memset(sdata, 1, sizeof(sdata)); + memset(ddata, 0, sizeof(ddata)); + siov[0].iov_base = sdata; + siov[0].iov_len = 48; + diov[0].iov_base = ddata; + diov[0].iov_len = sizeof(ddata); + + rc = spdk_iovcpy(siov, 1, diov, 1); + CU_ASSERT(rc == 48); + CU_ASSERT(_check_val(ddata, 48, 1) == 0); + CU_ASSERT(_check_val(&ddata[48], 16, 0) == 0); + + /* Dest smaller than source */ + memset(sdata, 1, sizeof(sdata)); + memset(ddata, 0, sizeof(ddata)); + siov[0].iov_base = sdata; + siov[0].iov_len = sizeof(sdata); + diov[0].iov_base = ddata; + diov[0].iov_len = 48; + + rc = spdk_iovcpy(siov, 1, diov, 1); + CU_ASSERT(rc == 48); + CU_ASSERT(_check_val(ddata, 48, 1) == 0); + CU_ASSERT(_check_val(&ddata[48], 16, 0) == 0); +} + +static void +test_simple_iov(void) +{ + struct iovec siov[4]; + struct iovec diov[4]; + uint8_t sdata[64]; + uint8_t ddata[64]; + ssize_t rc; + int i; + + /* Simple cases with 4 iov elements */ + + /* Same size. */ + memset(sdata, 1, sizeof(sdata)); + memset(ddata, 0, sizeof(ddata)); + for (i = 0; i < 4; i++) { + siov[i].iov_base = sdata + (16 * i); + siov[i].iov_len = 16; + diov[i].iov_base = ddata + (16 * i); + diov[i].iov_len = 16; + } + + rc = spdk_iovcpy(siov, 4, diov, 4); + CU_ASSERT(rc == sizeof(sdata)); + CU_ASSERT(_check_val(ddata, 64, 1) == 0); + + /* Source smaller than dest */ + memset(sdata, 1, sizeof(sdata)); + memset(ddata, 0, sizeof(ddata)); + for (i = 0; i < 4; i++) { + siov[i].iov_base = sdata + (8 * i); + siov[i].iov_len = 8; + diov[i].iov_base = ddata + (16 * i); + diov[i].iov_len = 16; + } + + rc = spdk_iovcpy(siov, 4, diov, 4); + CU_ASSERT(rc == 32); + CU_ASSERT(_check_val(ddata, 32, 1) == 0); + CU_ASSERT(_check_val(&ddata[32], 32, 0) == 0); + + /* Dest smaller than source */ + memset(sdata, 1, sizeof(sdata)); + memset(ddata, 0, sizeof(ddata)); + for (i = 0; i < 4; i++) { + siov[i].iov_base = sdata + (16 * i); + siov[i].iov_len = 16; + diov[i].iov_base = ddata + (8 * i); + diov[i].iov_len = 8; + } + + rc = spdk_iovcpy(siov, 4, diov, 4); + CU_ASSERT(rc == 32); + CU_ASSERT(_check_val(ddata, 32, 1) == 0); + CU_ASSERT(_check_val(&ddata[32], 32, 0) == 0); +} + +static void +test_complex_iov(void) +{ + struct iovec siov[4]; + struct iovec diov[4]; + uint8_t sdata[64]; + uint8_t ddata[64]; + ssize_t rc; + int i; + + /* More source elements */ + memset(sdata, 1, sizeof(sdata)); + memset(ddata, 0, sizeof(ddata)); + for (i = 0; i < 4; i++) { + siov[i].iov_base = sdata + (16 * i); + siov[i].iov_len = 16; + } + diov[0].iov_base = ddata; + diov[0].iov_len = sizeof(ddata); + + rc = spdk_iovcpy(siov, 4, diov, 1); + CU_ASSERT(rc == sizeof(sdata)); + CU_ASSERT(_check_val(ddata, 64, 1) == 0); + + /* More dest elements */ + memset(sdata, 1, sizeof(sdata)); + memset(ddata, 0, sizeof(ddata)); + for (i = 0; i < 4; i++) { + diov[i].iov_base = ddata + (16 * i); + diov[i].iov_len = 16; + } + siov[0].iov_base = sdata; + siov[0].iov_len = sizeof(sdata); + + rc = spdk_iovcpy(siov, 1, diov, 4); + CU_ASSERT(rc == sizeof(sdata)); + CU_ASSERT(_check_val(ddata, 64, 1) == 0); + + /* Build one by hand that's really terrible */ + memset(sdata, 1, sizeof(sdata)); + memset(ddata, 0, sizeof(ddata)); + siov[0].iov_base = sdata; + siov[0].iov_len = 1; + siov[1].iov_base = siov[0].iov_base + siov[0].iov_len; + siov[1].iov_len = 13; + siov[2].iov_base = siov[1].iov_base + siov[1].iov_len; + siov[2].iov_len = 6; + siov[3].iov_base = siov[2].iov_base + siov[2].iov_len; + siov[3].iov_len = 44; + + diov[0].iov_base = ddata; + diov[0].iov_len = 31; + diov[1].iov_base = diov[0].iov_base + diov[0].iov_len; + diov[1].iov_len = 9; + diov[2].iov_base = diov[1].iov_base + diov[1].iov_len; + diov[2].iov_len = 1; + diov[3].iov_base = diov[2].iov_base + diov[2].iov_len; + diov[3].iov_len = 23; + + rc = spdk_iovcpy(siov, 4, diov, 4); + CU_ASSERT(rc == 64); + CU_ASSERT(_check_val(ddata, 64, 1) == 0); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + suite = CU_add_suite("iov", NULL, NULL); + if (suite == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "test_single_iov", test_single_iov) == NULL || + CU_add_test(suite, "test_simple_iov", test_simple_iov) == NULL || + CU_add_test(suite, "test_complex_iov", test_complex_iov) == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + + CU_basic_run_tests(); + + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + + return num_failures; +} diff --git a/test/unit/unittest.sh b/test/unit/unittest.sh index 4325d785a..cc7b70256 100755 --- a/test/unit/unittest.sh +++ b/test/unit/unittest.sh @@ -155,6 +155,7 @@ $valgrind $testdir/lib/util/crc32_ieee.c/crc32_ieee_ut $valgrind $testdir/lib/util/crc32c.c/crc32c_ut $valgrind $testdir/lib/util/string.c/string_ut $valgrind $testdir/lib/util/dif.c/dif_ut +$valgrind $testdir/lib/util/iov.c/iov_ut $valgrind $testdir/lib/util/pipe.c/pipe_ut if [ $(uname -s) = Linux ]; then