diff --git a/cmd/zrepl/zrepl.c b/cmd/zrepl/zrepl.c index 4d0df385ce62..d850312a540a 100644 --- a/cmd/zrepl/zrepl.c +++ b/cmd/zrepl/zrepl.c @@ -205,7 +205,7 @@ uzfs_zvol_socket_read(int fd, char *buf, uint64_t nbytes) nbytes -= count; } ZREPL_LOG("Successful read count:%lu nbytes: %lu\n", count, nbytes); - return (1); + return (0); } @@ -294,6 +294,40 @@ uzfs_zvol_worker(void *arg) uzfs_zinfo_drop_refcnt(zinfo, false); } +/* + * Return value < 0 => error + * > 0 => invalid version + * = 0 => ok + */ +static int +read_handshake(int fd, zvol_io_hdr_t *hdr) +{ + int rc; + + rc = uzfs_zvol_socket_read(fd, (char *)hdr, sizeof (hdr->version)); + if (rc != 0) { + printf("error reading from socket: %d\n", errno); + return (-1); + } + if (hdr->version != REPLICA_VERSION) { + printf("invalid replica protocol version %d\n", hdr->version); + return (1); + } + rc = uzfs_zvol_socket_read(fd, + ((char *)hdr) + sizeof (hdr->version), + sizeof (*hdr) - sizeof (hdr->version)); + if (rc != 0) { + printf("error reading from socket: %d\n", errno); + return (-1); + } + if (hdr->opcode != ZVOL_OPCODE_HANDSHAKE) { + printf("Handshake yet to happen\n"); + return (-1); + } + + return (0); +} + /* * IO-Receiver would be per ZVOL, it would be * responsible for receiving IOs on given socket. @@ -306,42 +340,43 @@ uzfs_zvol_io_receiver(void *arg) zvol_io_hdr_t hdr; thread_args_t *thrd_arg; zvol_io_cmd_t *zio_cmd; - int count = 0; kthread_t *thrd_info; fd = *(int *)arg; free(arg); while (1) { - count = uzfs_zvol_socket_read(fd, (char *)&hdr, sizeof (hdr)); - if (count <= 0) { - printf("error has come on socket" - " with error %d\n", errno); - goto exit; + /* + * if we don't know the version yet, be more careful when + * reading header + */ + if (zinfo == NULL) { + if (read_handshake(fd, &hdr) != 0) + goto exit; + } else { + rc = uzfs_zvol_socket_read(fd, (char *)&hdr, + sizeof (hdr)); + if (rc != 0) { + printf("error reading from socket: %d\n", + errno); + goto exit; + } + if (hdr.opcode != ZVOL_OPCODE_WRITE && + hdr.opcode != ZVOL_OPCODE_READ && + hdr.opcode != ZVOL_OPCODE_SYNC) { + printf("Unexpected opcode %d\n", hdr.opcode); + goto exit; + } } - printf("op_code=%d io_seq=%ld offset=%ld len=%ld\n", hdr.opcode, - hdr.io_seq, hdr.offset, hdr.len); - - ASSERT((hdr.opcode == ZVOL_OPCODE_WRITE) || - (hdr.opcode == ZVOL_OPCODE_READ) || - (hdr.opcode == ZVOL_OPCODE_HANDSHAKE) || - (hdr.opcode == ZVOL_OPCODE_SYNC)); - if ((hdr.opcode != ZVOL_OPCODE_HANDSHAKE) && - (zinfo == NULL)) { - /* - * TODO: Stats need to be maintained for any - * such IO which came before handshake ? - */ - ZREPL_ERRLOG("Handshake yet to happen\n"); - continue; - } + printf("op_code=%d io_seq=%ld offset=%ld len=%ld\n", + hdr.opcode, hdr.io_seq, hdr.offset, hdr.len); zio_cmd = zio_cmd_alloc(&hdr, fd); if ((hdr.opcode == ZVOL_OPCODE_WRITE) || (hdr.opcode == ZVOL_OPCODE_HANDSHAKE)) { - count = uzfs_zvol_socket_read(fd, zio_cmd->buf, + rc = uzfs_zvol_socket_read(fd, zio_cmd->buf, (sizeof (char) * hdr.len)); - if (count <= 0) { + if (rc != 0) { zio_cmd_free(&zio_cmd); ZREPL_ERRLOG("Socket read failed with " "error: %d\n", errno); @@ -349,7 +384,6 @@ uzfs_zvol_io_receiver(void *arg) } } - ZREPL_LOG("Count:%d Size: %ld\n", count, hdr.len); if (hdr.opcode == ZVOL_OPCODE_HANDSHAKE) { zinfo = uzfs_zinfo_lookup(zio_cmd->buf); zio_cmd_free(&zio_cmd); @@ -429,50 +463,48 @@ uzfs_zvol_io_receiver(void *arg) static int uzfs_zvol_mgmt_do_handshake(zvol_io_hdr_t *hdr, int sfd, char *name) { - int count, rc = 0; + int rc; zvol_info_t *zinfo; mgmt_ack_t mgmt_ack; - char *packet = NULL; - char *p = NULL; printf("Volume: %s sent for enq\n", name); - zinfo = uzfs_zinfo_lookup(name); - /* - * XXX if anything in this function fails we should not send any - * payload at all - just a header with failed status. - */ - if (zinfo == NULL) { - hdr->status = ZVOL_OP_STATUS_FAILED; - } else { - hdr->status = ZVOL_OP_STATUS_OK; - } - hdr->len = sizeof (mgmt_ack_t); + + hdr->len = 0; + hdr->version = REPLICA_VERSION; + hdr->opcode = ZVOL_OPCODE_HANDSHAKE; bzero(&mgmt_ack, sizeof (mgmt_ack)); - strncpy(mgmt_ack.volname, name, strlen(name)); + strncpy(mgmt_ack.volname, name, sizeof (mgmt_ack.volname)); mgmt_ack.port = atoi(accpt_port); rc = uzfs_zvol_get_ip(mgmt_ack.ip); + if (rc == -1) { + ZREPL_ERRLOG("Unable to get IP with err: %d\n", errno); hdr->status = ZVOL_OP_STATUS_FAILED; - ZREPL_ERRLOG("Unable to get IP" - " with err:%d\n", errno); + } else if ((zinfo = uzfs_zinfo_lookup(name)) == NULL) { + ZREPL_ERRLOG("Unknown zvol: %s\n", name); + hdr->status = ZVOL_OP_STATUS_FAILED; + } else { + hdr->status = ZVOL_OP_STATUS_OK; + hdr->len = sizeof (mgmt_ack_t); } + if (zinfo != NULL) + uzfs_zinfo_drop_refcnt(zinfo, false); - packet = kmem_alloc((sizeof (mgmt_ack_t) + sizeof (*hdr)) * - sizeof (char), KM_SLEEP); - bcopy(hdr, packet, sizeof (*hdr)); - p = packet + sizeof (*hdr); - bcopy(&mgmt_ack, p, sizeof (mgmt_ack)); - count = write(sfd, packet, (sizeof (*hdr) + sizeof (mgmt_ack_t))); - if (count == -1) { - ZREPL_ERRLOG("Write to socket failed" - " with err:%d\n", errno); + rc = uzfs_zvol_socket_write(sfd, (char *)hdr, sizeof (*hdr)); + if (rc != 0) { + ZREPL_ERRLOG("Write to socket failed with err: %d\n", errno); + return (-1); + } + if (hdr->status != ZVOL_OP_STATUS_OK) { + return (-1); + } + + rc = uzfs_zvol_socket_write(sfd, (char *)&mgmt_ack, sizeof (mgmt_ack)); + if (rc != 0) { + ZREPL_ERRLOG("Write to socket failed with err: %d\n", errno); rc = -1; } - if (packet != NULL) - free(packet); - if (zinfo != NULL) - uzfs_zinfo_drop_refcnt(zinfo, false); return (rc); } @@ -511,10 +543,11 @@ uzfs_zvol_mgmt_thread(void *arg) { const char *target_addr = arg; char buf[256]; - int sfd, rc, count; + int rc; struct sockaddr_in istgt_addr; zvol_io_hdr_t hdr = {0, }; char *name = NULL; + int sfd = -1; sfd = create_and_bind(mgmt_port, false); @@ -550,11 +583,10 @@ uzfs_zvol_mgmt_thread(void *arg) } while (1) { - bzero(&hdr, sizeof (hdr)); - count = read(sfd, (char *)&hdr, sizeof (hdr)); - if (count <= 0) { - ZREPL_ERRLOG("Replica-iSCSI Tgt connection got " - "disconnected with err:%d\n", errno); + rc = read_handshake(sfd, &hdr); + if (rc < 0) { +close_conn: + ZREPL_ERRLOG("Management connection disconnected\n"); /* * Error has occurred on this socket * close it and open a new socket after @@ -564,9 +596,8 @@ uzfs_zvol_mgmt_thread(void *arg) printf("Retrying ....\n"); sleep(5); sfd = create_and_bind(mgmt_port, false); - if (sfd == -1) { + if (sfd == -1) goto exit; - } retry1: rc = connect(sfd, (struct sockaddr *)&istgt_addr, @@ -585,190 +616,36 @@ uzfs_zvol_mgmt_thread(void *arg) "is successful\n"); } continue; + } else if (rc > 0) { + /* Send to target the correct version */ + hdr.version = REPLICA_VERSION; + hdr.status = ZVOL_OP_STATUS_VERSION_MISMATCH; + hdr.opcode = ZVOL_OPCODE_HANDSHAKE; + hdr.len = 0; + (void) uzfs_zvol_socket_write(sfd, (char *)&hdr, + sizeof (hdr)); + goto close_conn; } - if (hdr.opcode == ZVOL_OPCODE_HANDSHAKE) { - name = kmem_alloc( - hdr.len * sizeof (char), KM_SLEEP); - count = read(sfd, name, sizeof (char) * hdr.len); - if (count == -1) { - ZREPL_ERRLOG("Read from socket failed" - " with err:%d\n", errno); - goto exit; - } + name = kmem_alloc(hdr.len * sizeof (char), KM_SLEEP); + rc = uzfs_zvol_socket_read(sfd, name, hdr.len); + if (rc != 0) + goto close_conn; - rc = uzfs_zvol_mgmt_do_handshake(&hdr, sfd, name); - free(name); - if (rc == -1) { - ZREPL_ERRLOG("handshake failed with" - " errno:%d\n", errno); - goto exit; - } + rc = uzfs_zvol_mgmt_do_handshake(&hdr, sfd, name); + free(name); + if (rc != 0) { + ZREPL_ERRLOG("Handshake failed: %d\n", errno); + goto close_conn; } } exit: + if (sfd < 0) + close(sfd); printf("uzfs_zvol_mgmt_thread thread exiting\n"); zk_thread_exit(); } -#if 0 -/* - * One thread per replica, which will be - * responsible for initial handshake and - * exchanging info like IP add, port etc. - */ -static void -uzfs_zvol_mgmt_thread(void *arg) -{ - - int sfd, efd, rc, count; - struct epoll_event event; - struct sockaddr_in istgt_addr; - zvol_io_hdr_t hdr = {0, }; - struct epoll_event *events = NULL; - char *name = NULL; - char *buf = NULL; - - sfd = create_and_bind(mgmt_port, false); - if (sfd == -1) { - goto exit; - } - - rc = make_socket_non_blocking(sfd); - if (rc == -1) { - goto exit; - } - - buf = get_controller_ip_address(); - if (buf == NULL) { - printf("parsing IP address did not work\n"); - goto exit; - } - printf("Controller IP address is: %s", buf); - bzero((char *)&istgt_addr, sizeof (istgt_addr)); - istgt_addr.sin_family = AF_INET; - istgt_addr.sin_addr.s_addr = inet_addr(buf); - istgt_addr.sin_port = htons(6060); - free(buf); -retry: - rc = connect(sfd, (struct sockaddr *)&istgt_addr, sizeof (istgt_addr)); - if ((rc == -1) && (errno == EINTR)) { - ZREPL_ERRLOG("Failed to connect to istgt_controller" - " with err:%d\n", errno); - sleep(10); - goto retry; - } else { - printf("Connection to TGT controller successful\n"); - ZREPL_LOG("Connection to TGT controller iss successful\n"); - } - - efd = epoll_create1(0); - if (efd == -1) { - ZREPL_ERRLOG("epoll_create() failed with errno:%d\n", errno); - goto exit; - } - - event.data.fd = sfd; - event.events = EPOLLIN | EPOLLET | EPOLLERR | - EPOLLHUP | EPOLLRDHUP; - rc = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event); - if (rc == -1) { - ZREPL_ERRLOG("epoll_ctl() failed with errno:%d\n", errno); - goto exit; - } - - /* Buffer where events are returned */ - events = calloc(MAXEVENTS, sizeof (event)); - - /* The event loop */ - while (1) { - int n, i; - n = epoll_wait(efd, events, MAXEVENTS, -1); - for (i = 0; i < n; i++) { - if ((events[i].events & EPOLLERR) || - (events[i].events & EPOLLHUP) || - (events[i].events & EPOLLRDHUP)) { - /* - * Error has occurred on this socket - * close it and open a new socket after - * 5 sec of sleep. - */ - ZREPL_ERRLOG("epoll err() :%d\n", errno); - close(events[i].data.fd); - printf("Retrying ....\n"); - sleep(5); - sfd = create_and_bind(mgmt_port, false); - if (sfd == -1) { - goto exit; - } - - rc = make_socket_non_blocking(sfd); - if (rc == -1) { - goto exit; - } -retry1: - rc = connect(sfd, - (struct sockaddr *)&istgt_addr, - sizeof (istgt_addr)); - if ((rc == -1) && (errno == EINTR)) { - ZREPL_ERRLOG("Failed to connect to" - " istgt_controller with err:%d\n", - errno); - sleep(2); - goto retry1; - } - - event.data.fd = sfd; - event.events = EPOLLIN | EPOLLET | - EPOLLERR | EPOLLHUP | EPOLLRDHUP; - rc = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event); - if (rc == -1) { - ZREPL_ERRLOG("epoll_ctl() failed with" - " errno:%d\n", errno); - goto exit; - } - continue; - } - - bzero(&hdr, sizeof (hdr)); - count = read(events[i].data.fd, (char *)&hdr, - sizeof (hdr)); - if (count == -1) { - ZREPL_ERRLOG("Read from socket failed" - " with err:%d\n", errno); - goto exit; - } - - if (hdr.opcode == ZVOL_OPCODE_HANDSHAKE) { - name = kmem_alloc( - hdr.len * sizeof (char), KM_SLEEP); - count = read(events[i].data.fd, name, - sizeof (char) * hdr.len); - if (count == -1) { - ZREPL_ERRLOG("Read from socket failed" - " with err:%d\n", errno); - goto exit; - } - - rc = uzfs_zvol_mgmt_do_handshake(&hdr, - events[i].data.fd, name); - free(name); - if (rc == -1) { - ZREPL_ERRLOG("handshake failed with" - " errno:%d\n", errno); - goto exit; - } - } - } - } -exit: - if (events != NULL) { - free(events); - } - printf("uzfs_zvol_mgmt_thread thread exiting\n"); - zk_thread_exit(); -} -#endif /* * One thread per replica. Responsible for accepting * IO connections. This thread will accept a connection diff --git a/include/zrepl_prot.h b/include/zrepl_prot.h index 36aa941cb7c6..25e5e4961292 100644 --- a/include/zrepl_prot.h +++ b/include/zrepl_prot.h @@ -32,45 +32,65 @@ extern "C" { /* * Over the wire spec for replica protocol. * - * TODO: All structures should be defined with "packed" attribute to avoid - * ambiguous padding added by compiler. + * We don't expect replica protocol to be used between nodes with different + * architecture nevetheless we try to be precise in defining size of members + * and all number values are supposed to be little endian. + * + * Version can be negotiated on mgmt conn. Target sends handshake message with + * version number. If replica does not support the version, then it replies + * with "version mismatch" error, puts supported version in version field + * and closes the connection. */ +#define REPLICA_VERSION 1 #define MAX_NAME_LEN 256 -#define MAX_IP_LEN 56 +#define MAX_IP_LEN 64 #define TARGET_PORT 6060 -typedef enum zvol_op_code_e { - ZVOL_OPCODE_HANDSHAKE = 1, +enum zvol_op_code { + ZVOL_OPCODE_HANDSHAKE = 0, ZVOL_OPCODE_READ, ZVOL_OPCODE_WRITE, ZVOL_OPCODE_UNMAP, ZVOL_OPCODE_SYNC, ZVOL_OPCODE_SNAP_CREATE, ZVOL_OPCODE_SNAP_ROLLBACK, -} zvol_op_code_t; +} __attribute__((packed)); + +typedef enum zvol_op_code zvol_op_code_t; -typedef enum zvol_op_status_e { - ZVOL_OP_STATUS_OK = 1, +enum zvol_op_status { + ZVOL_OP_STATUS_OK = 0, ZVOL_OP_STATUS_FAILED, -} zvol_op_status_t; + ZVOL_OP_STATUS_VERSION_MISMATCH, +} __attribute__((packed)); + +typedef enum zvol_op_status zvol_op_status_t; -typedef struct zvol_io_hdr_s { +/* + * Future protocol versions need to respect that the first field must be + * 2-byte version number. The rest of struct is version dependent. + * Version number is regarded only in handshake message. For rest of the ops + * the version number from handshake message is assumed. + */ +struct zvol_io_hdr { + uint16_t version; zvol_op_code_t opcode; + zvol_op_status_t status; uint64_t io_seq; uint64_t offset; uint64_t len; - // XXX (void *) must be removed from over-the-wire data - void *q_ptr; - zvol_op_status_t status; -} zvol_io_hdr_t; +} __attribute__((packed)); -typedef struct mgmt_ack_s { - char volname[MAX_NAME_LEN]; +typedef struct zvol_io_hdr zvol_io_hdr_t; + +struct mgmt_ack { + uint16_t port; char ip[MAX_IP_LEN]; - // XXX this should be uint16_t type - int port; -} mgmt_ack_t; + char volname[MAX_NAME_LEN]; +} __attribute__((packed)); + +typedef struct mgmt_ack mgmt_ack_t; #ifdef __cplusplus } diff --git a/lib/fio/replica.c b/lib/fio/replica.c index 103c5c2e0fef..60570f2e86c5 100644 --- a/lib/fio/replica.c +++ b/lib/fio/replica.c @@ -249,11 +249,12 @@ static int write_handshake(struct thread_data *td, int fd, const char *volname) zvol_io_hdr_t hdr; int volname_size = strlen(volname) + 1; + hdr.version = REPLICA_VERSION; hdr.opcode = ZVOL_OPCODE_HANDSHAKE; - hdr.len = volname_size; + hdr.status = 0; hdr.io_seq = 0; hdr.offset = 0; - hdr.q_ptr = NULL; + hdr.len = volname_size; rc = write_to_socket(fd, &hdr, sizeof (hdr), 1); if (rc != 0) { @@ -272,10 +273,17 @@ static int write_handshake(struct thread_data *td, int fd, const char *volname) static int read_handshake(struct thread_data *td, int fd, mgmt_ack_t *mgmt_ack, const char *volname) { + uint16_t vers; ssize_t rc; zvol_io_hdr_t hdr; - rc = read_from_socket(fd, &hdr, sizeof (hdr)); + rc = read_from_socket(fd, &hdr, sizeof (vers)); + if (hdr.version != REPLICA_VERSION) { + log_err("repl: Incompatible replica version %d\n", hdr.version); + return (-1); + } + rc = read_from_socket(fd, ((char *)&hdr) + sizeof (vers), + sizeof (hdr) - sizeof (vers)); if (rc != 0) { td_verror(td, rc, "read"); return (-1); @@ -573,7 +581,6 @@ static int fio_repl_queue(struct thread_data *td, struct io_u *io_u) hdr.io_seq = io_ent->io_num; hdr.offset = io_u->offset; hdr.len = io_u->xfer_buflen; - hdr.q_ptr = NULL; hdr.status = 0; if (io_u->ddir == DDIR_WRITE) { diff --git a/tests/cbtest/gtest/test_uzfs.cc b/tests/cbtest/gtest/test_uzfs.cc index e0ab60b16fff..2e2b4e0496be 100644 --- a/tests/cbtest/gtest/test_uzfs.cc +++ b/tests/cbtest/gtest/test_uzfs.cc @@ -1,5 +1,16 @@ #include +#include +#include +#include +#include +#include +#include + +#include + +/* Avoid including conflicting C++ declarations for LE-BE conversions */ +#define _SYS_BYTEORDER_H #include TEST(uZFSServer, Setup) { @@ -18,3 +29,219 @@ TEST(uZFSServer, InitServer) { TEST(uZFSServer, ClientConnectServer) { EXPECT_EQ(0, libuzfs_client_init(NULL)); } + +std::string getCmdPath(std::string zfsCmd) { + std::string cmdPath; + const char *srcPath = std::getenv("SRC_PATH"); + + if (srcPath == NULL) { + cmdPath += "."; + } else { + cmdPath = srcPath; + } + cmdPath += "/cmd/" + zfsCmd + "/" + zfsCmd; + + return cmdPath; +} + +void execCmd(std::string zfsCmd, std::string args) { + int rc; + std::string cmdLine; + + cmdLine = getCmdPath(zfsCmd) + " " + args; + rc = system(cmdLine.c_str()); + if (rc != 0) { + throw std::runtime_error( + std::string("Command failed: ") + cmdLine); + } +} + +class TestZvol { +public: + TestZvol(std::string poolname) { + pool = poolname; + path = std::string("/tmp/") + pool; + name = pool + "/vol"; + } + + ~TestZvol() { + try { + execCmd("zpool", std::string("destroy -f ") + pool); + } catch (std::runtime_error re) { + ; + } + unlink(path.c_str()); + } + + void create() { + int fd, rc; + + fd = open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666); + if (fd < 0) + throw std::system_error(errno, std::system_category(), + "Cannot create vdev file"); + + rc = ftruncate(fd, 100 * 1024 * 1024); + close(fd); + if (rc != 0) + throw std::system_error(errno, std::system_category(), + "Cannot truncate vdev file"); + execCmd("zpool", std::string("create ") + pool + " " + path); + execCmd("zfs", std::string("create -V 10m -s ") + name); + } + + std::string name; +private: + std::string pool; + std::string path; +}; + +class ZreplTest : public testing::Test { +protected: + ZreplTest() { + m_pid = 0; + m_fd = -1; + m_listenfd = -1; + } + + ~ZreplTest() { + if (m_listenfd >= 0) + close(m_listenfd); + if (m_fd >= 0) + close(m_fd); + } + + virtual void SetUp () override { + std::string zrepl_path = getCmdPath("zrepl"); + + m_pid = fork(); + if (m_pid == 0) + execl(zrepl_path.c_str(), zrepl_path.c_str(), + "127.0.0.1", NULL); + } + + virtual void TearDown() override { + if (m_pid != 0) + kill(m_pid, SIGTERM); + } + + void connect(void) { + struct sockaddr_in addr; + int opt = 1; + int rc; + + m_listenfd = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_TRUE(m_listenfd >= 0); + setsockopt(m_listenfd, SOL_SOCKET, SO_REUSEADDR, (void *) &opt, + sizeof (opt)); + memset(&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(TARGET_PORT); + rc = bind(m_listenfd, (struct sockaddr *) &addr, sizeof (addr)); + ASSERT_TRUE(rc >= 0); + rc = listen(m_listenfd, 1); + ASSERT_TRUE(rc >= 0); + m_fd = accept(m_listenfd, NULL, NULL); + ASSERT_TRUE(m_fd >= 0); + } + + pid_t m_pid; + int m_listenfd; + int m_fd; +}; + +TEST_F(ZreplTest, HandshakeOk) { + zvol_io_hdr_t hdr_out, hdr_in; + int rc; + mgmt_ack_t mgmt_ack; + TestZvol zvol("handshake"); + + connect(); + sleep(5); + zvol.create(); + + hdr_out.version = REPLICA_VERSION; + hdr_out.opcode = ZVOL_OPCODE_HANDSHAKE; + hdr_out.status = ZVOL_OP_STATUS_OK; + hdr_out.io_seq = 0; + hdr_out.offset = 0; + hdr_out.len = zvol.name.length() + 1; + + rc = write(m_fd, &hdr_out, sizeof (hdr_out)); + ASSERT_EQ(rc, sizeof (hdr_out)); + rc = write(m_fd, zvol.name.c_str(), hdr_out.len); + ASSERT_EQ(rc, hdr_out.len); + + rc = read(m_fd, &hdr_in, sizeof (hdr_in)); + ASSERT_EQ(rc, sizeof (hdr_in)); + EXPECT_EQ(hdr_in.version, REPLICA_VERSION); + EXPECT_EQ(hdr_in.opcode, ZVOL_OPCODE_HANDSHAKE); + EXPECT_EQ(hdr_in.status, ZVOL_OP_STATUS_OK); + EXPECT_EQ(hdr_in.io_seq, 0); + EXPECT_EQ(hdr_in.offset, 0); + ASSERT_EQ(hdr_in.len, sizeof (mgmt_ack)); + rc = read(m_fd, &mgmt_ack, sizeof (mgmt_ack)); + ASSERT_EQ(rc, sizeof (mgmt_ack)); + EXPECT_STREQ(mgmt_ack.volname, zvol.name.c_str()); +} + +TEST_F(ZreplTest, HandshakeWrongVersion) { + zvol_io_hdr_t hdr_out, hdr_in; + int rc; + mgmt_ack_t mgmt_ack; + TestZvol zvol("handshake"); + + connect(); + + hdr_out.version = REPLICA_VERSION + 1; + hdr_out.opcode = ZVOL_OPCODE_HANDSHAKE; + hdr_out.status = ZVOL_OP_STATUS_OK; + hdr_out.io_seq = 0; + hdr_out.offset = 0; + hdr_out.len = zvol.name.length() + 1; + + rc = write(m_fd, &hdr_out, sizeof (hdr_out)); + ASSERT_EQ(rc, sizeof (hdr_out)); + rc = write(m_fd, zvol.name.c_str(), hdr_out.len); + ASSERT_EQ(rc, hdr_out.len); + + rc = read(m_fd, &hdr_in, sizeof (hdr_in)); + ASSERT_EQ(rc, sizeof (hdr_in)); + EXPECT_EQ(hdr_in.version, REPLICA_VERSION); + EXPECT_EQ(hdr_in.opcode, ZVOL_OPCODE_HANDSHAKE); + EXPECT_EQ(hdr_in.status, ZVOL_OP_STATUS_VERSION_MISMATCH); + EXPECT_EQ(hdr_in.io_seq, 0); + EXPECT_EQ(hdr_in.offset, 0); + ASSERT_EQ(hdr_in.len, 0); +} + +TEST_F(ZreplTest, HandshakeUnknownZvol) { + zvol_io_hdr_t hdr_out, hdr_in; + int rc; + const char *volname = "handshake/vol"; + mgmt_ack_t mgmt_ack; + + connect(); + + hdr_out.version = REPLICA_VERSION; + hdr_out.opcode = ZVOL_OPCODE_HANDSHAKE; + hdr_out.status = ZVOL_OP_STATUS_OK; + hdr_out.io_seq = 0; + hdr_out.offset = 0; + hdr_out.len = strlen(volname) + 1; + + rc = write(m_fd, &hdr_out, sizeof (hdr_out)); + ASSERT_EQ(rc, sizeof (hdr_out)); + rc = write(m_fd, volname, hdr_out.len); + ASSERT_EQ(rc, hdr_out.len); + + rc = read(m_fd, &hdr_in, sizeof (hdr_in)); + ASSERT_EQ(rc, sizeof (hdr_in)); + EXPECT_EQ(hdr_in.version, REPLICA_VERSION); + EXPECT_EQ(hdr_in.opcode, ZVOL_OPCODE_HANDSHAKE); + EXPECT_EQ(hdr_in.status, ZVOL_OP_STATUS_FAILED); + EXPECT_EQ(hdr_in.io_seq, 0); + EXPECT_EQ(hdr_in.offset, 0); + ASSERT_EQ(hdr_in.len, 0); +} diff --git a/tests/cbtest/script/test_uzfs.sh b/tests/cbtest/script/test_uzfs.sh index 707f883dc5c8..fc732d57dd1b 100755 --- a/tests/cbtest/script/test_uzfs.sh +++ b/tests/cbtest/script/test_uzfs.sh @@ -592,6 +592,10 @@ filename=$SRCPOOL/vol2 EOF # run the fio + echo "Running $FIO_SRCDIR/fio with lib path $SRC_PATH/lib/fio/.libs" + echo " and following configuration:" + cat $TMPDIR/test.fio + echo LD_LIBRARY_PATH=$SRC_PATH/lib/fio/.libs $FIO_SRCDIR/fio $TMPDIR/test.fio [ $? -eq 0 ] || log_fail "Fio test run failed"