rebuild
This commit is contained in:
13
tests/Makefile
Normal file
13
tests/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
SUBDIRS := ioengine_test hook
|
||||
|
||||
.PHONY: all clean $(SUBDIRS)
|
||||
|
||||
all: $(SUBDIRS)
|
||||
|
||||
$(SUBDIRS):
|
||||
$(MAKE) -C $@
|
||||
|
||||
clean:
|
||||
for dir in $(SUBDIRS); do \
|
||||
$(MAKE) -C $$dir clean; \
|
||||
done
|
||||
8
tests/hook/Makefile
Normal file
8
tests/hook/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
BIN_DIR := $(abspath $(CURDIR)/../bin)
|
||||
|
||||
all:
|
||||
gcc -g -o $(BIN_DIR)/hook_api_test hook_api_test.c
|
||||
|
||||
clean:
|
||||
rm -rf $(BIN_DIR)/hook_api_test
|
||||
322
tests/hook/hook_api_test.c
Normal file
322
tests/hook/hook_api_test.c
Normal file
@@ -0,0 +1,322 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define ASSERT_TRUE(cond, fmt, ...) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
fprintf(stderr, "[FAIL] %s:%d " fmt "\n", __func__, __LINE__, \
|
||||
##__VA_ARGS__); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_SYS_OK(expr) \
|
||||
do { \
|
||||
if ((expr) < 0) { \
|
||||
fprintf(stderr, "[FAIL] %s:%d %s: %s\n", __func__, __LINE__, \
|
||||
#expr, strerror(errno)); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int
|
||||
join_path(char *out, size_t out_sz, const char *dir, const char *name)
|
||||
{
|
||||
int n = snprintf(out, out_sz, "%s/%s", dir, name);
|
||||
if (n < 0 || (size_t)n >= out_sz) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_basic_rw_seek_stat(const char *workdir)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
ASSERT_SYS_OK(join_path(path, sizeof(path), workdir, "basic_rw.txt"));
|
||||
|
||||
int fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0644);
|
||||
ASSERT_SYS_OK(fd);
|
||||
|
||||
const char *init = "abcdef";
|
||||
ssize_t nr = write(fd, init, 6);
|
||||
ASSERT_TRUE(nr == 6, "write expected 6, got %zd", nr);
|
||||
|
||||
off_t off = lseek(fd, 0, SEEK_SET);
|
||||
ASSERT_TRUE(off == 0, "lseek expected 0, got %lld", (long long)off);
|
||||
|
||||
char buf[16] = {0};
|
||||
nr = read(fd, buf, 6);
|
||||
ASSERT_TRUE(nr == 6, "read expected 6, got %zd", nr);
|
||||
ASSERT_TRUE(memcmp(buf, "abcdef", 6) == 0, "read content mismatch");
|
||||
|
||||
nr = pwrite(fd, "XYZ", 3, 3);
|
||||
ASSERT_TRUE(nr == 3, "pwrite expected 3, got %zd", nr);
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
nr = pread(fd, buf, 6, 0);
|
||||
ASSERT_TRUE(nr == 6, "pread expected 6, got %zd", nr);
|
||||
ASSERT_TRUE(memcmp(buf, "abcXYZ", 6) == 0, "pread content mismatch");
|
||||
|
||||
struct stat st;
|
||||
ASSERT_SYS_OK(fstat(fd, &st));
|
||||
ASSERT_TRUE(st.st_size == 6, "fstat size expected 6, got %lld",
|
||||
(long long)st.st_size);
|
||||
|
||||
ASSERT_SYS_OK(ftruncate(fd, 4));
|
||||
ASSERT_SYS_OK(fstat(fd, &st));
|
||||
ASSERT_TRUE(st.st_size == 4, "ftruncate size expected 4, got %lld",
|
||||
(long long)st.st_size);
|
||||
|
||||
ASSERT_SYS_OK(fdatasync(fd));
|
||||
ASSERT_SYS_OK(fsync(fd));
|
||||
ASSERT_SYS_OK(close(fd));
|
||||
ASSERT_SYS_OK(unlink(path));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_openat_rename_unlink(const char *workdir)
|
||||
{
|
||||
char subdir[PATH_MAX];
|
||||
ASSERT_SYS_OK(join_path(subdir, sizeof(subdir), workdir, "openat_dir"));
|
||||
|
||||
ASSERT_SYS_OK(mkdir(subdir, 0755));
|
||||
|
||||
int dfd = open(subdir, O_RDONLY | O_DIRECTORY);
|
||||
ASSERT_SYS_OK(dfd);
|
||||
|
||||
int fd = openat(dfd, "a.txt", O_CREAT | O_TRUNC | O_RDWR, 0644);
|
||||
ASSERT_SYS_OK(fd);
|
||||
|
||||
ssize_t nr = write(fd, "hello", 5);
|
||||
ASSERT_TRUE(nr == 5, "write expected 5, got %zd", nr);
|
||||
ASSERT_SYS_OK(close(fd));
|
||||
|
||||
struct stat st;
|
||||
ASSERT_SYS_OK(fstatat(dfd, "a.txt", &st, 0));
|
||||
ASSERT_TRUE(st.st_size == 5, "fstatat size expected 5, got %lld",
|
||||
(long long)st.st_size);
|
||||
|
||||
ASSERT_SYS_OK(renameat(dfd, "a.txt", dfd, "b.txt"));
|
||||
ASSERT_SYS_OK(fstatat(dfd, "b.txt", &st, 0));
|
||||
ASSERT_TRUE(st.st_size == 5, "renamed file size expected 5, got %lld",
|
||||
(long long)st.st_size);
|
||||
|
||||
ASSERT_SYS_OK(unlinkat(dfd, "b.txt", 0));
|
||||
|
||||
errno = 0;
|
||||
ASSERT_TRUE(fstatat(dfd, "b.txt", &st, 0) == -1 && errno == ENOENT,
|
||||
"fstatat after unlink should be ENOENT");
|
||||
|
||||
ASSERT_SYS_OK(close(dfd));
|
||||
ASSERT_SYS_OK(rmdir(subdir));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_dup_fcntl_ioctl(const char *workdir)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
ASSERT_SYS_OK(join_path(path, sizeof(path), workdir, "dup_fcntl.txt"));
|
||||
|
||||
int fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0644);
|
||||
ASSERT_SYS_OK(fd);
|
||||
|
||||
ASSERT_TRUE(write(fd, "0123456789", 10) == 10, "write expected 10 bytes");
|
||||
ASSERT_SYS_OK(lseek(fd, 0, SEEK_SET));
|
||||
|
||||
int fd2 = dup(fd);
|
||||
bool dup_supported = true;
|
||||
if (fd2 < 0 && (errno == ENOTSUP || errno == EOPNOTSUPP || errno == ENOSYS)) {
|
||||
dup_supported = false;
|
||||
fprintf(stderr, "[INFO] dup on this backend is unsupported, skip shared-offset check\n");
|
||||
} else {
|
||||
ASSERT_SYS_OK(fd2);
|
||||
}
|
||||
|
||||
char buf[4] = {0};
|
||||
if (dup_supported) {
|
||||
ASSERT_TRUE(read(fd, buf, 2) == 2, "read(fd) expected 2 bytes");
|
||||
ASSERT_TRUE(memcmp(buf, "01", 2) == 0, "first read mismatch");
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
ASSERT_TRUE(read(fd2, buf, 2) == 2, "read(fd2) expected 2 bytes");
|
||||
ASSERT_TRUE(memcmp(buf, "23", 2) == 0,
|
||||
"dup offset should be shared, expected \"23\"");
|
||||
}
|
||||
|
||||
int fd_flags = fcntl(fd, F_GETFD);
|
||||
ASSERT_SYS_OK(fd_flags);
|
||||
ASSERT_SYS_OK(fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC));
|
||||
|
||||
int fd_flags_after = fcntl(fd, F_GETFD);
|
||||
ASSERT_SYS_OK(fd_flags_after);
|
||||
ASSERT_TRUE((fd_flags_after & FD_CLOEXEC) != 0,
|
||||
"FD_CLOEXEC should be set");
|
||||
|
||||
int file_flags = fcntl(fd, F_GETFL);
|
||||
ASSERT_SYS_OK(file_flags);
|
||||
ASSERT_SYS_OK(fcntl(fd, F_SETFL, file_flags | O_APPEND));
|
||||
|
||||
int file_flags_after = fcntl(fd, F_GETFL);
|
||||
ASSERT_SYS_OK(file_flags_after);
|
||||
ASSERT_TRUE((file_flags_after & O_APPEND) != 0, "O_APPEND should be set");
|
||||
|
||||
int avail = -1;
|
||||
ASSERT_SYS_OK(ioctl(dup_supported ? fd2 : fd, FIONREAD, &avail));
|
||||
if (dup_supported) {
|
||||
ASSERT_TRUE(avail == 6, "FIONREAD expected 6, got %d", avail);
|
||||
ASSERT_SYS_OK(close(fd2));
|
||||
} else {
|
||||
ASSERT_TRUE(avail == 10, "FIONREAD expected 10, got %d", avail);
|
||||
}
|
||||
|
||||
ASSERT_SYS_OK(close(fd));
|
||||
ASSERT_SYS_OK(unlink(path));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
test_readv_writev_pwritev(const char *workdir)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
ASSERT_SYS_OK(join_path(path, sizeof(path), workdir, "iov.txt"));
|
||||
|
||||
int fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0644);
|
||||
ASSERT_SYS_OK(fd);
|
||||
|
||||
struct iovec wiov[3];
|
||||
wiov[0].iov_base = "ab";
|
||||
wiov[0].iov_len = 2;
|
||||
wiov[1].iov_base = "cd";
|
||||
wiov[1].iov_len = 2;
|
||||
wiov[2].iov_base = "ef";
|
||||
wiov[2].iov_len = 2;
|
||||
|
||||
ssize_t nr = writev(fd, wiov, 3);
|
||||
ASSERT_TRUE(nr == 6, "writev expected 6, got %zd", nr);
|
||||
|
||||
ASSERT_SYS_OK(lseek(fd, 0, SEEK_SET));
|
||||
|
||||
char a[2] = {0}, b[2] = {0}, c[2] = {0};
|
||||
struct iovec riov[3];
|
||||
riov[0].iov_base = a;
|
||||
riov[0].iov_len = 2;
|
||||
riov[1].iov_base = b;
|
||||
riov[1].iov_len = 2;
|
||||
riov[2].iov_base = c;
|
||||
riov[2].iov_len = 2;
|
||||
|
||||
nr = readv(fd, riov, 3);
|
||||
ASSERT_TRUE(nr == 6, "readv expected 6, got %zd", nr);
|
||||
ASSERT_TRUE(memcmp(a, "ab", 2) == 0 &&
|
||||
memcmp(b, "cd", 2) == 0 &&
|
||||
memcmp(c, "ef", 2) == 0, "readv content mismatch");
|
||||
|
||||
struct iovec pwiov[2];
|
||||
pwiov[0].iov_base = "12";
|
||||
pwiov[0].iov_len = 2;
|
||||
pwiov[1].iov_base = "34";
|
||||
pwiov[1].iov_len = 2;
|
||||
nr = pwritev(fd, pwiov, 2, 1);
|
||||
ASSERT_TRUE(nr == 4, "pwritev expected 4, got %zd", nr);
|
||||
|
||||
char out[8] = {0};
|
||||
nr = pread(fd, out, 6, 0);
|
||||
ASSERT_TRUE(nr == 6, "pread expected 6, got %zd", nr);
|
||||
ASSERT_TRUE(memcmp(out, "a1234f", 6) == 0, "pwritev content mismatch");
|
||||
|
||||
ASSERT_SYS_OK(close(fd));
|
||||
ASSERT_SYS_OK(unlink(path));
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*test_fn)(const char *workdir);
|
||||
|
||||
struct test_case {
|
||||
const char *name;
|
||||
test_fn fn;
|
||||
};
|
||||
|
||||
static int
|
||||
run_test(const struct test_case *tc, const char *workdir)
|
||||
{
|
||||
int rc = tc->fn(workdir);
|
||||
if (rc == 0) {
|
||||
printf("[PASS] %s\n", tc->name);
|
||||
return 0;
|
||||
}
|
||||
printf("[FAIL] %s\n", tc->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *base = getenv("ZVFS_TEST_ROOT");
|
||||
if (!base || base[0] == '\0')
|
||||
base = "/tmp";
|
||||
|
||||
char workdir[PATH_MAX];
|
||||
int n = snprintf(workdir, sizeof(workdir), "%s/zvfs-hook-api-XXXXXX", base);
|
||||
if (n < 0 || (size_t)n >= sizeof(workdir)) {
|
||||
fprintf(stderr, "workdir template too long\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (!mkdtemp(workdir)) {
|
||||
fprintf(stderr, "mkdtemp(%s) failed: %s\n", workdir, strerror(errno));
|
||||
return 2;
|
||||
}
|
||||
|
||||
printf("workdir=%s\n", workdir);
|
||||
printf("hint: set ZVFS_TEST_ROOT=/zvfs when validating LD_PRELOAD hook path.\n");
|
||||
|
||||
struct test_case tests[] = {
|
||||
{"basic_rw_seek_stat", test_basic_rw_seek_stat},
|
||||
{"openat_rename_unlink", test_openat_rename_unlink},
|
||||
{"dup_fcntl_ioctl", test_dup_fcntl_ioctl},
|
||||
{"readv_writev_pwritev", test_readv_writev_pwritev},
|
||||
};
|
||||
|
||||
int failed = 0;
|
||||
for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) {
|
||||
if (run_test(&tests[i], workdir) != 0)
|
||||
failed++;
|
||||
}
|
||||
|
||||
const char *keep = getenv("ZVFS_TEST_KEEP");
|
||||
if (!keep || strcmp(keep, "1") != 0) {
|
||||
if (rmdir(workdir) < 0) {
|
||||
fprintf(stderr,
|
||||
"warning: failed to remove workdir %s: %s\n",
|
||||
workdir, strerror(errno));
|
||||
}
|
||||
} else {
|
||||
printf("kept workdir=%s\n", workdir);
|
||||
}
|
||||
|
||||
if (failed == 0) {
|
||||
printf("ALL TESTS PASSED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("FAILED=%d\n", failed);
|
||||
return 1;
|
||||
}
|
||||
43
tests/ioengine_test/Makefile
Normal file
43
tests/ioengine_test/Makefile
Normal file
@@ -0,0 +1,43 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../spdk)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.modules.mk
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.app_vars.mk
|
||||
|
||||
# 输出目录
|
||||
BIN_DIR := $(abspath $(CURDIR)/../bin)
|
||||
|
||||
TEST_BINS := \
|
||||
ioengine_single_blob_test \
|
||||
ioengine_multi_blob_test \
|
||||
ioengine_same_blob_mt_test
|
||||
|
||||
COMMON_SRCS := \
|
||||
test_common.c \
|
||||
../../src/spdk_engine/io_engine.c \
|
||||
../../src/common/utils.c
|
||||
|
||||
SPDK_LIB_LIST = $(ALL_MODULES_LIST) event event_bdev
|
||||
LIBS += $(SPDK_LIB_LINKER_ARGS)
|
||||
|
||||
CFLAGS += -I$(abspath $(CURDIR)/../../src) -I$(CURDIR)
|
||||
|
||||
.PHONY: all clean
|
||||
all: $(BIN_DIR) $(addprefix $(BIN_DIR)/,$(TEST_BINS))
|
||||
|
||||
# 创建 bin 目录
|
||||
$(BIN_DIR):
|
||||
mkdir -p $(BIN_DIR)
|
||||
|
||||
$(BIN_DIR)/ioengine_single_blob_test: ioengine_single_blob_test.c $(COMMON_SRCS) $(SPDK_LIB_FILES) $(ENV_LIBS)
|
||||
$(CC) $(CFLAGS) -o $@ $< $(COMMON_SRCS) $(LDFLAGS) $(LIBS) $(ENV_LDFLAGS) $(SYS_LIBS)
|
||||
|
||||
$(BIN_DIR)/ioengine_multi_blob_test: ioengine_multi_blob_test.c $(COMMON_SRCS) $(SPDK_LIB_FILES) $(ENV_LIBS)
|
||||
$(CC) $(CFLAGS) -o $@ $< $(COMMON_SRCS) $(LDFLAGS) $(LIBS) $(ENV_LDFLAGS) $(SYS_LIBS)
|
||||
|
||||
$(BIN_DIR)/ioengine_same_blob_mt_test: ioengine_same_blob_mt_test.c $(COMMON_SRCS) $(SPDK_LIB_FILES) $(ENV_LIBS)
|
||||
$(CC) $(CFLAGS) -o $@ $< $(COMMON_SRCS) $(LDFLAGS) $(LIBS) $(ENV_LDFLAGS) $(SYS_LIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(addprefix $(BIN_DIR)/,$(TEST_BINS))
|
||||
106
tests/ioengine_test/ioengine_multi_blob_test.c
Normal file
106
tests/ioengine_test/ioengine_multi_blob_test.c
Normal file
@@ -0,0 +1,106 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "spdk_engine/io_engine.h"
|
||||
#include "test_common.h"
|
||||
|
||||
#define MULTI_BLOB_COUNT 3
|
||||
|
||||
int main(void) {
|
||||
int rc = 0;
|
||||
const char *bdev_name = getenv("SPDK_BDEV_NAME");
|
||||
struct zvfs_blob_handle *handles[MULTI_BLOB_COUNT] = {0};
|
||||
uint64_t ids[MULTI_BLOB_COUNT] = {0};
|
||||
uint64_t cluster = 0;
|
||||
void *wbuf = NULL;
|
||||
void *rbuf = NULL;
|
||||
int i = 0;
|
||||
|
||||
if (!bdev_name) {
|
||||
bdev_name = "Malloc0";
|
||||
}
|
||||
if (io_engine_init(bdev_name) != 0) {
|
||||
fprintf(stderr, "TEST2: io_engine_init failed (bdev=%s)\n", bdev_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("[TEST2] single thread / multi blob\n");
|
||||
|
||||
handles[0] = blob_create(0);
|
||||
if (!handles[0]) {
|
||||
fprintf(stderr, "TEST2: create first blob failed\n");
|
||||
return 1;
|
||||
}
|
||||
ids[0] = handles[0]->id;
|
||||
cluster = handles[0]->size;
|
||||
if (cluster == 0) {
|
||||
fprintf(stderr, "TEST2: invalid cluster size\n");
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
if (blob_resize(handles[0], cluster * 2) != 0) {
|
||||
fprintf(stderr, "TEST2: resize first blob failed\n");
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 1; i < MULTI_BLOB_COUNT; i++) {
|
||||
handles[i] = blob_create(cluster * 2);
|
||||
if (!handles[i]) {
|
||||
fprintf(stderr, "TEST2: create blob %d failed\n", i);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
ids[i] = handles[i]->id;
|
||||
}
|
||||
|
||||
if (alloc_aligned_buf(&wbuf, cluster) != 0 || alloc_aligned_buf(&rbuf, cluster) != 0) {
|
||||
fprintf(stderr, "TEST2: alloc aligned buffer failed\n");
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < MULTI_BLOB_COUNT; i++) {
|
||||
fill_pattern((uint8_t *)wbuf, cluster, (uint8_t)(0x20 + i));
|
||||
memset(rbuf, 0, cluster);
|
||||
|
||||
if (blob_write(handles[i], 0, wbuf, cluster) != 0) {
|
||||
fprintf(stderr, "TEST2: blob_write[%d] failed\n", i);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
if (blob_read(handles[i], 0, rbuf, cluster) != 0) {
|
||||
fprintf(stderr, "TEST2: blob_read[%d] failed\n", i);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
if (memcmp(wbuf, rbuf, cluster) != 0) {
|
||||
fprintf(stderr, "TEST2: blob[%d] readback mismatch\n", i);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
for (i = 0; i < MULTI_BLOB_COUNT; i++) {
|
||||
if (handles[i]) {
|
||||
(void)blob_close(handles[i]);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < MULTI_BLOB_COUNT; i++) {
|
||||
if (ids[i] != 0) {
|
||||
(void)blob_delete(ids[i]);
|
||||
}
|
||||
}
|
||||
free(wbuf);
|
||||
free(rbuf);
|
||||
|
||||
if (rc == 0) {
|
||||
printf("[TEST2] PASS\n");
|
||||
return 0;
|
||||
}
|
||||
printf("[TEST2] FAIL\n");
|
||||
return 1;
|
||||
}
|
||||
147
tests/ioengine_test/ioengine_same_blob_mt_test.c
Normal file
147
tests/ioengine_test/ioengine_same_blob_mt_test.c
Normal file
@@ -0,0 +1,147 @@
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "spdk_engine/io_engine.h"
|
||||
#include "test_common.h"
|
||||
|
||||
#define THREAD_COUNT 4
|
||||
|
||||
struct mt_case_arg {
|
||||
struct zvfs_blob_handle *handle;
|
||||
uint64_t cluster_size;
|
||||
uint64_t offset;
|
||||
uint8_t seed;
|
||||
pthread_barrier_t *barrier;
|
||||
int rc;
|
||||
};
|
||||
|
||||
static void *mt_case_worker(void *arg) {
|
||||
struct mt_case_arg *ctx = (struct mt_case_arg *)arg;
|
||||
void *wbuf = NULL;
|
||||
void *rbuf = NULL;
|
||||
|
||||
if (alloc_aligned_buf(&wbuf, ctx->cluster_size) != 0 ||
|
||||
alloc_aligned_buf(&rbuf, ctx->cluster_size) != 0) {
|
||||
free(wbuf);
|
||||
free(rbuf);
|
||||
ctx->rc = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fill_pattern((uint8_t *)wbuf, ctx->cluster_size, ctx->seed);
|
||||
(void)pthread_barrier_wait(ctx->barrier);
|
||||
|
||||
if (blob_write(ctx->handle, ctx->offset, wbuf, ctx->cluster_size) != 0) {
|
||||
ctx->rc = 1;
|
||||
goto out;
|
||||
}
|
||||
if (blob_read(ctx->handle, ctx->offset, rbuf, ctx->cluster_size) != 0) {
|
||||
ctx->rc = 1;
|
||||
goto out;
|
||||
}
|
||||
if (memcmp(wbuf, rbuf, ctx->cluster_size) != 0) {
|
||||
ctx->rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->rc = 0;
|
||||
|
||||
out:
|
||||
free(wbuf);
|
||||
free(rbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int rc = 0;
|
||||
const char *bdev_name = getenv("SPDK_BDEV_NAME");
|
||||
int i = 0;
|
||||
struct zvfs_blob_handle *h = NULL;
|
||||
uint64_t blob_id = 0;
|
||||
uint64_t cluster = 0;
|
||||
pthread_t tids[THREAD_COUNT];
|
||||
struct mt_case_arg args[THREAD_COUNT];
|
||||
pthread_barrier_t barrier;
|
||||
int barrier_inited = 0;
|
||||
|
||||
if (!bdev_name) {
|
||||
bdev_name = "Malloc0";
|
||||
}
|
||||
if (io_engine_init(bdev_name) != 0) {
|
||||
fprintf(stderr, "TEST3: io_engine_init failed (bdev=%s)\n", bdev_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("[TEST3] multi thread / same blob\n");
|
||||
|
||||
h = blob_create(0);
|
||||
if (!h) {
|
||||
fprintf(stderr, "TEST3: blob_create failed\n");
|
||||
return 1;
|
||||
}
|
||||
blob_id = h->id;
|
||||
cluster = h->size;
|
||||
if (cluster == 0) {
|
||||
fprintf(stderr, "TEST3: invalid cluster size\n");
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
if (blob_resize(h, cluster * THREAD_COUNT) != 0) {
|
||||
fprintf(stderr, "TEST3: blob_resize failed\n");
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pthread_barrier_init(&barrier, NULL, THREAD_COUNT) != 0) {
|
||||
fprintf(stderr, "TEST3: barrier init failed\n");
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
barrier_inited = 1;
|
||||
|
||||
for (i = 0; i < THREAD_COUNT; i++) {
|
||||
args[i].handle = h;
|
||||
args[i].cluster_size = cluster;
|
||||
args[i].offset = cluster * (uint64_t)i;
|
||||
args[i].seed = (uint8_t)(0x40 + i);
|
||||
args[i].barrier = &barrier;
|
||||
args[i].rc = 1;
|
||||
if (pthread_create(&tids[i], NULL, mt_case_worker, &args[i]) != 0) {
|
||||
fprintf(stderr, "TEST3: pthread_create[%d] failed\n", i);
|
||||
rc = 1;
|
||||
while (--i >= 0) {
|
||||
pthread_join(tids[i], NULL);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < THREAD_COUNT; i++) {
|
||||
pthread_join(tids[i], NULL);
|
||||
if (args[i].rc != 0) {
|
||||
fprintf(stderr, "TEST3: worker[%d] failed\n", i);
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (barrier_inited) {
|
||||
(void)pthread_barrier_destroy(&barrier);
|
||||
}
|
||||
if (h) {
|
||||
(void)blob_close(h);
|
||||
}
|
||||
if (blob_id != 0) {
|
||||
(void)blob_delete(blob_id);
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
printf("[TEST3] PASS\n");
|
||||
return 0;
|
||||
}
|
||||
printf("[TEST3] FAIL\n");
|
||||
return 1;
|
||||
}
|
||||
136
tests/ioengine_test/ioengine_single_blob_test.c
Normal file
136
tests/ioengine_test/ioengine_single_blob_test.c
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "spdk_engine/io_engine.h"
|
||||
#include "test_common.h"
|
||||
|
||||
int main(void) {
|
||||
int rc = 0;
|
||||
const char *bdev_name = getenv("SPDK_BDEV_NAME");
|
||||
struct zvfs_blob_handle *h = NULL;
|
||||
struct zvfs_blob_handle *reopen = NULL;
|
||||
uint64_t blob_id = 0;
|
||||
uint64_t cluster = 0;
|
||||
void *wbuf = NULL;
|
||||
void *rbuf = NULL;
|
||||
|
||||
if (!bdev_name) {
|
||||
bdev_name = "Malloc0";
|
||||
}
|
||||
if (io_engine_init(bdev_name) != 0) {
|
||||
fprintf(stderr, "TEST1: io_engine_init failed (bdev=%s)\n", bdev_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("[TEST1] single thread / single blob\n");
|
||||
|
||||
h = blob_create(0);
|
||||
if (!h) {
|
||||
fprintf(stderr, "TEST1: blob_create failed\n");
|
||||
return 1;
|
||||
}
|
||||
blob_id = h->id;
|
||||
cluster = h->size;
|
||||
if (cluster == 0) {
|
||||
fprintf(stderr, "TEST1: invalid cluster size\n");
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = blob_resize(h, cluster * 2);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "TEST1: blob_resize failed: %d\n", rc);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = alloc_aligned_buf(&wbuf, cluster);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "TEST1: alloc write buf failed: %d\n", rc);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
rc = alloc_aligned_buf(&rbuf, cluster);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "TEST1: alloc read buf failed: %d\n", rc);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
fill_pattern((uint8_t *)wbuf, cluster, 0x11);
|
||||
|
||||
rc = blob_write(h, 0, wbuf, cluster);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "TEST1: blob_write failed: %d\n", rc);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = blob_read(h, 0, rbuf, cluster);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "TEST1: blob_read failed: %d\n", rc);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
if (memcmp(wbuf, rbuf, cluster) != 0) {
|
||||
fprintf(stderr, "TEST1: readback mismatch\n");
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = blob_sync_md(h);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "TEST1: blob_sync_md failed: %d\n", rc);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = blob_close(h);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "TEST1: blob_close failed: %d\n", rc);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
h = NULL;
|
||||
|
||||
reopen = blob_open(blob_id);
|
||||
if (!reopen) {
|
||||
fprintf(stderr, "TEST1: blob_open(reopen) failed\n");
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(rbuf, 0, cluster);
|
||||
rc = blob_read(reopen, 0, rbuf, cluster);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "TEST1: reopen blob_read failed: %d\n", rc);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
if (memcmp(wbuf, rbuf, cluster) != 0) {
|
||||
fprintf(stderr, "TEST1: reopen readback mismatch\n");
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (reopen) {
|
||||
(void)blob_close(reopen);
|
||||
}
|
||||
if (h) {
|
||||
(void)blob_close(h);
|
||||
}
|
||||
if (blob_id != 0) {
|
||||
(void)blob_delete(blob_id);
|
||||
}
|
||||
free(wbuf);
|
||||
free(rbuf);
|
||||
|
||||
if (rc == 0) {
|
||||
printf("[TEST1] PASS\n");
|
||||
return 0;
|
||||
}
|
||||
printf("[TEST1] FAIL\n");
|
||||
return 1;
|
||||
}
|
||||
20
tests/ioengine_test/test_common.c
Normal file
20
tests/ioengine_test/test_common.c
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "test_common.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int alloc_aligned_buf(void **buf, size_t len) {
|
||||
int rc = posix_memalign(buf, 4096, len);
|
||||
if (rc != 0) {
|
||||
return -rc;
|
||||
}
|
||||
memset(*buf, 0, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fill_pattern(uint8_t *buf, size_t len, uint8_t seed) {
|
||||
size_t i = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[i] = (uint8_t)(seed + (uint8_t)i);
|
||||
}
|
||||
}
|
||||
10
tests/ioengine_test/test_common.h
Normal file
10
tests/ioengine_test/test_common.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __IOENGINE_TEST_COMMON_H__
|
||||
#define __IOENGINE_TEST_COMMON_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int alloc_aligned_buf(void **buf, size_t len);
|
||||
void fill_pattern(uint8_t *buf, size_t len, uint8_t seed);
|
||||
|
||||
#endif // __IOENGINE_TEST_COMMON_H__
|
||||
Reference in New Issue
Block a user