aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2020-09-17 10:44:53 +0100
committerStefan Hajnoczi <stefanha@redhat.com>2020-09-23 13:41:58 +0100
commit9dd6f7c28eaa7d001a526d51c74bbf89d5402b8c (patch)
tree5ce0c2924bf4607bbdbc892eb806b37971c52dff
parentbd0bbb9aba2afbc2ea24b0475be04f795468b381 (diff)
util/iov: add iov_discard_undo()
The iov_discard_front/back() operations are useful for parsing iovecs but they modify the array elements. If the original array is needed after parsing finishes there is currently no way to restore it. Although g_memdup() can be used before performing destructive iov_discard_front/back() operations, this is inefficient. Introduce iov_discard_undo() to restore the array to the state prior to an iov_discard_front/back() operation. Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Li Qiang <liq3ea@gmail.com> Message-Id: <20200917094455.822379-2-stefanha@redhat.com>
-rw-r--r--include/qemu/iov.h23
-rw-r--r--tests/test-iov.c165
-rw-r--r--util/iov.c50
3 files changed, 234 insertions, 4 deletions
diff --git a/include/qemu/iov.h b/include/qemu/iov.h
index bffc151282..b6b283a5e5 100644
--- a/include/qemu/iov.h
+++ b/include/qemu/iov.h
@@ -130,6 +130,29 @@ size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt,
size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
size_t bytes);
+/* Information needed to undo an iov_discard_*() operation */
+typedef struct {
+ struct iovec *modified_iov;
+ struct iovec orig;
+} IOVDiscardUndo;
+
+/*
+ * Undo an iov_discard_front_undoable() or iov_discard_back_undoable()
+ * operation. If multiple operations are made then each one needs a separate
+ * IOVDiscardUndo and iov_discard_undo() must be called in the reverse order
+ * that the operations were made.
+ */
+void iov_discard_undo(IOVDiscardUndo *undo);
+
+/*
+ * Undoable versions of iov_discard_front() and iov_discard_back(). Use
+ * iov_discard_undo() to reset to the state before the discard operations.
+ */
+size_t iov_discard_front_undoable(struct iovec **iov, unsigned int *iov_cnt,
+ size_t bytes, IOVDiscardUndo *undo);
+size_t iov_discard_back_undoable(struct iovec *iov, unsigned int *iov_cnt,
+ size_t bytes, IOVDiscardUndo *undo);
+
typedef struct QEMUIOVector {
struct iovec *iov;
int niov;
diff --git a/tests/test-iov.c b/tests/test-iov.c
index 458ca25099..9c415e2f1f 100644
--- a/tests/test-iov.c
+++ b/tests/test-iov.c
@@ -26,6 +26,12 @@ static void iov_free(struct iovec *iov, unsigned niov)
g_free(iov);
}
+static bool iov_equals(const struct iovec *a, const struct iovec *b,
+ unsigned niov)
+{
+ return memcmp(a, b, sizeof(a[0]) * niov) == 0;
+}
+
static void test_iov_bytes(struct iovec *iov, unsigned niov,
size_t offset, size_t bytes)
{
@@ -335,6 +341,87 @@ static void test_discard_front(void)
iov_free(iov, iov_cnt);
}
+static void test_discard_front_undo(void)
+{
+ IOVDiscardUndo undo;
+ struct iovec *iov;
+ struct iovec *iov_tmp;
+ struct iovec *iov_orig;
+ unsigned int iov_cnt;
+ unsigned int iov_cnt_tmp;
+ size_t size;
+
+ /* Discard zero bytes */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_tmp = iov;
+ iov_cnt_tmp = iov_cnt;
+ iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, 0, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+
+ /* Discard more bytes than vector size */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_tmp = iov;
+ iov_cnt_tmp = iov_cnt;
+ size = iov_size(iov, iov_cnt);
+ iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size + 1, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+
+ /* Discard entire vector */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_tmp = iov;
+ iov_cnt_tmp = iov_cnt;
+ size = iov_size(iov, iov_cnt);
+ iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+
+ /* Discard within first element */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_tmp = iov;
+ iov_cnt_tmp = iov_cnt;
+ size = g_test_rand_int_range(1, iov->iov_len);
+ iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+
+ /* Discard entire first element */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_tmp = iov;
+ iov_cnt_tmp = iov_cnt;
+ iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, iov->iov_len, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+
+ /* Discard within second element */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_tmp = iov;
+ iov_cnt_tmp = iov_cnt;
+ size = iov->iov_len + g_test_rand_int_range(1, iov[1].iov_len);
+ iov_discard_front_undoable(&iov_tmp, &iov_cnt_tmp, size, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+}
+
static void test_discard_back(void)
{
struct iovec *iov;
@@ -404,6 +491,82 @@ static void test_discard_back(void)
iov_free(iov, iov_cnt);
}
+static void test_discard_back_undo(void)
+{
+ IOVDiscardUndo undo;
+ struct iovec *iov;
+ struct iovec *iov_orig;
+ unsigned int iov_cnt;
+ unsigned int iov_cnt_tmp;
+ size_t size;
+
+ /* Discard zero bytes */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_cnt_tmp = iov_cnt;
+ iov_discard_back_undoable(iov, &iov_cnt_tmp, 0, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+
+ /* Discard more bytes than vector size */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_cnt_tmp = iov_cnt;
+ size = iov_size(iov, iov_cnt);
+ iov_discard_back_undoable(iov, &iov_cnt_tmp, size + 1, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+
+ /* Discard entire vector */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_cnt_tmp = iov_cnt;
+ size = iov_size(iov, iov_cnt);
+ iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+
+ /* Discard within last element */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_cnt_tmp = iov_cnt;
+ size = g_test_rand_int_range(1, iov[iov_cnt - 1].iov_len);
+ iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+
+ /* Discard entire last element */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_cnt_tmp = iov_cnt;
+ size = iov[iov_cnt - 1].iov_len;
+ iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+
+ /* Discard within second-to-last element */
+ iov_random(&iov, &iov_cnt);
+ iov_orig = g_memdup(iov, sizeof(iov[0]) * iov_cnt);
+ iov_cnt_tmp = iov_cnt;
+ size = iov[iov_cnt - 1].iov_len +
+ g_test_rand_int_range(1, iov[iov_cnt - 2].iov_len);
+ iov_discard_back_undoable(iov, &iov_cnt_tmp, size, &undo);
+ iov_discard_undo(&undo);
+ assert(iov_equals(iov, iov_orig, iov_cnt));
+ g_free(iov_orig);
+ iov_free(iov, iov_cnt);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -412,5 +575,7 @@ int main(int argc, char **argv)
g_test_add_func("/basic/iov/io", test_io);
g_test_add_func("/basic/iov/discard-front", test_discard_front);
g_test_add_func("/basic/iov/discard-back", test_discard_back);
+ g_test_add_func("/basic/iov/discard-front-undo", test_discard_front_undo);
+ g_test_add_func("/basic/iov/discard-back-undo", test_discard_back_undo);
return g_test_run();
}
diff --git a/util/iov.c b/util/iov.c
index ae61d696aa..f3a9e92a37 100644
--- a/util/iov.c
+++ b/util/iov.c
@@ -636,14 +636,33 @@ void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf)
}
}
-size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt,
- size_t bytes)
+void iov_discard_undo(IOVDiscardUndo *undo)
+{
+ /* Restore original iovec if it was modified */
+ if (undo->modified_iov) {
+ *undo->modified_iov = undo->orig;
+ }
+}
+
+size_t iov_discard_front_undoable(struct iovec **iov,
+ unsigned int *iov_cnt,
+ size_t bytes,
+ IOVDiscardUndo *undo)
{
size_t total = 0;
struct iovec *cur;
+ if (undo) {
+ undo->modified_iov = NULL;
+ }
+
for (cur = *iov; *iov_cnt > 0; cur++) {
if (cur->iov_len > bytes) {
+ if (undo) {
+ undo->modified_iov = cur;
+ undo->orig = *cur;
+ }
+
cur->iov_base += bytes;
cur->iov_len -= bytes;
total += bytes;
@@ -659,12 +678,24 @@ size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt,
return total;
}
-size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
- size_t bytes)
+size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt,
+ size_t bytes)
+{
+ return iov_discard_front_undoable(iov, iov_cnt, bytes, NULL);
+}
+
+size_t iov_discard_back_undoable(struct iovec *iov,
+ unsigned int *iov_cnt,
+ size_t bytes,
+ IOVDiscardUndo *undo)
{
size_t total = 0;
struct iovec *cur;
+ if (undo) {
+ undo->modified_iov = NULL;
+ }
+
if (*iov_cnt == 0) {
return 0;
}
@@ -673,6 +704,11 @@ size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
while (*iov_cnt > 0) {
if (cur->iov_len > bytes) {
+ if (undo) {
+ undo->modified_iov = cur;
+ undo->orig = *cur;
+ }
+
cur->iov_len -= bytes;
total += bytes;
break;
@@ -687,6 +723,12 @@ size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
return total;
}
+size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt,
+ size_t bytes)
+{
+ return iov_discard_back_undoable(iov, iov_cnt, bytes, NULL);
+}
+
void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes)
{
size_t total;