diff --git a/doc/manuals_3.txt b/doc/manuals_3.txt index 726d9247d9..8ddd792284 100644 --- a/doc/manuals_3.txt +++ b/doc/manuals_3.txt @@ -56,3 +56,4 @@ rpma_utils_get_ibv_context.3 rpma_utils_ibv_context_is_odp_capable.3 rpma_write.3 rpma_write_atomic.3 +rpma_write_with_imm.3 diff --git a/examples/11-write-with-imm/CMakeLists.txt b/examples/11-write-with-imm/CMakeLists.txt new file mode 100644 index 0000000000..614e4565d9 --- /dev/null +++ b/examples/11-write-with-imm/CMakeLists.txt @@ -0,0 +1,44 @@ +# +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2021 Fujitsu +# + +cmake_minimum_required(VERSION 3.3) +project(write-with-imm C) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${CMAKE_SOURCE_DIR}/../cmake + ${CMAKE_SOURCE_DIR}/../../cmake) + +include(${CMAKE_SOURCE_DIR}/../../cmake/functions.cmake) +# set LIBRT_LIBRARIES if linking with librt is required +check_if_librt_is_required() + +find_package(PkgConfig QUIET) + +if(PKG_CONFIG_FOUND) + pkg_check_modules(LIBRPMA librpma) + pkg_check_modules(LIBIBVERBS libibverbs) +endif() +if(NOT LIBRPMA_FOUND) + find_package(LIBRPMA REQUIRED librpma) +endif() +if(NOT LIBIBVERBS_FOUND) + find_package(LIBIBVERBS REQUIRED libibverbs) +endif() + +link_directories(${LIBRPMA_LIBRARY_DIRS} ${LIBIBVERBS_LIBRARY_DIRS}) + +function(add_example name) + set(srcs ${ARGN}) + add_executable(${name} ${srcs}) + target_include_directories(${name} + PRIVATE + ${LIBRPMA_INCLUDE_DIRS} + ${LIBIBVERBS_INCLUDE_DIRS} + ../common) + target_link_libraries(${name} rpma ${LIBIBVERBS_LIBRARIES} ${LIBRT_LIBRARIES}) +endfunction() + +add_example(server server.c ../common/common-conn.c) +add_example(client client.c ../common/common-conn.c) diff --git a/examples/11-write-with-imm/README.md b/examples/11-write-with-imm/README.md new file mode 100644 index 0000000000..318e16ee48 --- /dev/null +++ b/examples/11-write-with-imm/README.md @@ -0,0 +1,19 @@ +Example of rpma write with immediate data +=== + +The rpma write with immediate data implements two parts of the process: +- The client connects to the server and writes a message with immediate +data to the server. +- The server receives an immediate data from the client. +The immediate data has the same value as the message written by the +client so the immediate data is compared with the expected message. + +## Usage + +```bash +[user@server]$ ./server $server_address $port +``` + +```bash +[user@client]$ ./client $server_address $port $imm +``` diff --git a/examples/11-write-with-imm/client.c b/examples/11-write-with-imm/client.c new file mode 100644 index 0000000000..14d97a470b --- /dev/null +++ b/examples/11-write-with-imm/client.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright (c) 2021 Fujitsu */ + +/* + * client.c -- a client of the write-with-imm example + * + * Please see README.md for a detailed description of this example. + */ + +#include +#include +#include +#include +#include + +#include "common-conn.h" + +#define USAGE_STR "usage: %s \n" + +int +main(int argc, char *argv[]) +{ + /* validate parameters */ + if (argc < 4) { + fprintf(stderr, USAGE_STR, argv[0]); + return -1; + } + + /* configure logging thresholds to see more details */ + rpma_log_set_threshold(RPMA_LOG_THRESHOLD, RPMA_LOG_LEVEL_INFO); + rpma_log_set_threshold(RPMA_LOG_THRESHOLD_AUX, RPMA_LOG_LEVEL_INFO); + + /* read common parameters */ + char *addr = argv[1]; + char *port = argv[2]; + + uint64_t imm = strtoul(argv[3], NULL, 10); + if (imm == ULONG_MAX && errno == ERANGE) { + fprintf(stderr, "strtoul() overflowed\n"); + return -1; + } + + if (imm > UINT32_MAX) { + fprintf(stderr, + "the provided immediate data is too big(%lu > %u)\n", + imm, UINT32_MAX); + return -1; + } + + /* RPMA resources - general */ + struct rpma_peer *peer = NULL; + struct rpma_mr_local *src_mr = NULL; + struct rpma_mr_remote *dst_mr = NULL; + struct rpma_conn *conn = NULL; + struct rpma_completion cmpl; + int ret; + + /* prepare memory */ + uint32_t *src = malloc_aligned(KILOBYTE); + if (!src) + return -1; + + /* + * lookup an ibv_context via the address and create a new peer using it + */ + ret = client_peer_via_address(addr, &peer); + if (ret) + goto err_mr_free; + + /* register the memory */ + ret = rpma_mr_reg(peer, src, KILOBYTE, RPMA_MR_USAGE_WRITE_SRC, + &src_mr); + if (ret) + goto err_peer_delete; + + /* establish a new connection to a server */ + ret = client_connect(peer, addr, port, NULL, &conn); + if (ret) + goto err_mr_dereg; + + /* obtain the remote memory description */ + struct rpma_conn_private_data pdata; + ret = rpma_conn_get_private_data(conn, &pdata); + if (ret) + goto err_conn_disconnect; + if (pdata.len < sizeof(struct common_data)) { + fprintf(stderr, + "received connection's private data is too small (%u < %zu)\n", + pdata.len, sizeof(struct common_data)); + ret = -1; + goto err_conn_disconnect; + } + + struct common_data *dst_data = pdata.ptr; + ret = rpma_mr_remote_from_descriptor(&dst_data->descriptors[0], + dst_data->mr_desc_size, &dst_mr); + if (ret) + goto err_conn_disconnect; + + /* write a message with immediate data to the server */ + memcpy(src, (uint32_t *)&imm, sizeof(uint32_t)); + fprintf(stdout, "write a value %u with immediate data %u\n", + *src, (uint32_t)imm); + ret = rpma_write_with_imm(conn, dst_mr, dst_data->data_offset, src_mr, + 0, KILOBYTE, RPMA_F_COMPLETION_ALWAYS, (uint32_t)imm, + NULL); + if (ret) + goto err_mr_remote_delete; + + /* prepare completions, get one and validate it */ + ret = rpma_conn_completion_wait(conn); + if (ret) + goto err_mr_remote_delete; + + ret = rpma_conn_completion_get(conn, &cmpl); + if (ret) + goto err_mr_remote_delete; + + if (cmpl.op_status != IBV_WC_SUCCESS) { + fprintf(stderr, + "an unexpected completion: %s\n", + ibv_wc_status_str(cmpl.op_status)); + ret = -1; + goto err_mr_remote_delete; + } + + if (cmpl.op != RPMA_OP_WRITE) { + fprintf(stderr, + "an unexpected type of operation (%d != %d)\n", + cmpl.op, RPMA_OP_WRITE); + ret = -1; + } + +err_mr_remote_delete: + /* delete the remote memory region's structure */ + (void) rpma_mr_remote_delete(&dst_mr); + +err_conn_disconnect: + (void) common_disconnect_and_wait_for_conn_close(&conn); + +err_mr_dereg: + /* deregister the memory regions */ + (void) rpma_mr_dereg(&src_mr); + +err_peer_delete: + /* delete the peer object */ + (void) rpma_peer_delete(&peer); + +err_mr_free: + /* free the memory */ + free(src); + + return ret; +} diff --git a/examples/11-write-with-imm/server.c b/examples/11-write-with-imm/server.c new file mode 100644 index 0000000000..8c8f613e85 --- /dev/null +++ b/examples/11-write-with-imm/server.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright (c) 2021 Fujitsu */ + +/* + * server.c -- a server of the write-with-imm example + * + * Please see README.md for a detailed description of this example. + */ + +#include +#include +#include +#include + +#include "common-conn.h" + +#define USAGE_STR "usage: %s \n" + +int +main(int argc, char *argv[]) +{ + /* validate parameters */ + if (argc < 3) { + fprintf(stderr, USAGE_STR, argv[0]); + return -1; + } + + /* configure logging thresholds to see more details */ + rpma_log_set_threshold(RPMA_LOG_THRESHOLD, RPMA_LOG_LEVEL_INFO); + rpma_log_set_threshold(RPMA_LOG_THRESHOLD_AUX, RPMA_LOG_LEVEL_INFO); + + /* read common parameters */ + char *addr = argv[1]; + char *port = argv[2]; + + /* prepare memory */ + uint32_t *dst = malloc_aligned(KILOBYTE); + if (!dst) + return -1; + + uint32_t *recv = malloc_aligned(1); + if (!recv) { + free(dst); + return -1; + } + + /* RPMA resources */ + struct rpma_peer *peer = NULL; + struct rpma_mr_local *dst_mr = NULL; + struct rpma_mr_local *recv_mr = NULL; + struct rpma_ep *ep = NULL; + struct rpma_conn_req *req = NULL; + struct rpma_conn *conn = NULL; + enum rpma_conn_event conn_event = RPMA_CONN_UNDEFINED; + struct rpma_completion cmpl; + int ret; + + /* + * lookup an ibv_context via the address and create a new peer using it + */ + ret = server_peer_via_address(addr, &peer); + if (ret) + goto err_mr_free; + + /* register the memory */ + ret = rpma_mr_reg(peer, dst, KILOBYTE, RPMA_MR_USAGE_WRITE_DST, + &dst_mr); + if (ret) + goto err_peer_delete; + + ret = rpma_mr_reg(peer, recv, 1, RPMA_MR_USAGE_RECV, &recv_mr); + if (ret) { + rpma_mr_dereg(&dst_mr); + goto err_peer_delete; + } + + /* get size of the memory region's descriptor */ + size_t mr_desc_size; + ret = rpma_mr_get_descriptor_size(dst_mr, &mr_desc_size); + if (ret) + goto err_mr_dereg; + + /* calculate data for the client write */ + struct common_data dst_data; + dst_data.data_offset = 0; + dst_data.mr_desc_size = mr_desc_size; + + /* get the memory region's descriptor */ + ret = rpma_mr_get_descriptor(dst_mr, &dst_data.descriptors[0]); + if (ret) + goto err_mr_dereg; + + /* start a listening endpoint at addr:port */ + ret = rpma_ep_listen(peer, addr, port, &ep); + if (ret) + goto err_mr_dereg; + + /* receive an incoming connection request */ + ret = rpma_ep_next_conn_req(ep, NULL, &req); + if (ret) + goto err_ep_shutdown; + + /* prepare to receive an immediate data from the client */ + ret = rpma_conn_req_recv(req, recv_mr, 0, 1, NULL); + if (ret) { + rpma_conn_req_delete(&req); + goto err_ep_shutdown; + } + + /* accept the connection request and obtain the connection object */ + struct rpma_conn_private_data pdata; + pdata.ptr = &dst_data; + pdata.len = sizeof(struct common_data); + ret = rpma_conn_req_connect(&req, &pdata, &conn); + if (ret) { + rpma_conn_req_delete(&req); + goto err_ep_shutdown; + } + + /* wait for the connection to be established */ + ret = rpma_conn_next_event(conn, &conn_event); + if (ret) + goto err_conn_disconnect; + if (conn_event != RPMA_CONN_ESTABLISHED) { + fprintf(stderr, + "rpma_conn_next_event returned an unexptected event\n"); + ret = -1; + goto err_conn_disconnect; + } + + /* prepare completions, get one and validate it */ + ret = rpma_conn_completion_wait(conn); + if (ret) + goto err_conn_disconnect; + + ret = rpma_conn_completion_get(conn, &cmpl); + if (ret) + goto err_conn_disconnect; + + if (cmpl.op_status != IBV_WC_SUCCESS) { + fprintf(stderr, + "an unexpected completion %s\n", + ibv_wc_status_str(cmpl.op_status)); + ret = -1; + goto err_conn_disconnect; + } + + if (cmpl.op != RPMA_OP_RECV_RDMA_WITH_IMM) { + fprintf(stderr, + "an unexpected type of operation (%d != %d)\n", + cmpl.op, RPMA_OP_RECV_RDMA_WITH_IMM); + ret = -1; + goto err_conn_disconnect; + } + + if (!(cmpl.flags & IBV_WC_WITH_IMM)) { + fprintf(stderr, + "an unexpected completion flag (no IBV_WC_WITH_IMM)\n"); + ret = -1; + goto err_conn_disconnect; + } + + uint32_t *exp_imm = dst; + if (cmpl.imm != *exp_imm) { + fprintf(stderr, + "an unexpected immediate data (%u != %u)\n", + cmpl.imm, *exp_imm); + ret = -1; + } else { + printf("a value %u written with immediate data %u\n", + *exp_imm, cmpl.imm); + } + +err_conn_disconnect: + (void) common_wait_for_conn_close_and_disconnect(&conn); + +err_ep_shutdown: + (void) rpma_ep_shutdown(&ep); + +err_mr_dereg: + /* deregister the memory regions */ + (void) rpma_mr_dereg(&recv_mr); + (void) rpma_mr_dereg(&dst_mr); + +err_peer_delete: + /* delete the peer object */ + (void) rpma_peer_delete(&peer); + +err_mr_free: + /* free the memory */ + free(dst); + free(recv); + + return ret; +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 49fa40b712..98d7ce670a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -133,6 +133,10 @@ add_example(NAME 10-send-with-imm BIN server USE_LIBIBVERBS SRCS 10-send-with-imm/server.c common/common-conn.c) add_example(NAME 10-send-with-imm BIN client USE_LIBIBVERBS SRCS 10-send-with-imm/client.c common/common-conn.c) +add_example(NAME 11-write-with-imm BIN server USE_LIBIBVERBS + SRCS 11-write-with-imm/server.c common/common-conn.c) +add_example(NAME 11-write-with-imm BIN client USE_LIBIBVERBS + SRCS 11-write-with-imm/client.c common/common-conn.c) add_example(NAME log BIN log SRCS log/log-example.c diff --git a/examples/run-all-on-SoftRoCE.sh b/examples/run-all-on-SoftRoCE.sh index 8d25a81cc2..b0cc4af948 100755 --- a/examples/run-all-on-SoftRoCE.sh +++ b/examples/run-all-on-SoftRoCE.sh @@ -102,6 +102,11 @@ function run_example() { $VLD_CCMD $DIR/client $IP_ADDRESS $PORT "1st_word" "1234" RV=$? ;; + 11-write-with-imm) + echo "Starting the client ..." + $VLD_CCMD $DIR/client $IP_ADDRESS $PORT "1234" + RV=$? + ;; *) echo "Starting the client ..." $VLD_CCMD $DIR/client $IP_ADDRESS $PORT diff --git a/src/conn.c b/src/conn.c index c42ff976f0..bd0dbe5bea 100644 --- a/src/conn.c +++ b/src/conn.c @@ -325,7 +325,29 @@ rpma_write(struct rpma_conn *conn, return rpma_mr_write(conn->id->qp, dst, dst_offset, src, src_offset, - len, flags, op_context, false); + len, flags, + IBV_WR_RDMA_WRITE, 0, + op_context, false); +} + +/* + * rpma_write_with_imm -- initiate the write operation with immediate data + */ +int +rpma_write_with_imm(struct rpma_conn *conn, + struct rpma_mr_remote *dst, size_t dst_offset, + const struct rpma_mr_local *src, size_t src_offset, + size_t len, int flags, uint32_t imm, const void *op_context) +{ + if (conn == NULL || dst == NULL || src == NULL || flags == 0) + return RPMA_E_INVAL; + + return rpma_mr_write(conn->id->qp, + dst, dst_offset, + src, src_offset, + len, flags, + IBV_WR_RDMA_WRITE_WITH_IMM, imm, + op_context, false); } /* @@ -346,7 +368,9 @@ rpma_write_atomic(struct rpma_conn *conn, return rpma_mr_write(conn->id->qp, dst, dst_offset, src, src_offset, - RPMA_ATOMIC_WRITE_ALIGNMENT, flags, op_context, true); + RPMA_ATOMIC_WRITE_ALIGNMENT, flags, + IBV_WR_RDMA_WRITE, 0, + op_context, true); } /* @@ -531,6 +555,9 @@ rpma_conn_completion_get(struct rpma_conn *conn, case IBV_WC_RECV: cmpl->op = RPMA_OP_RECV; break; + case IBV_WC_RECV_RDMA_WITH_IMM: + cmpl->op = RPMA_OP_RECV_RDMA_WITH_IMM; + break; default: RPMA_LOG_ERROR("unsupported wc.opcode == %d", wc.opcode); return RPMA_E_NOSUPP; @@ -546,8 +573,11 @@ rpma_conn_completion_get(struct rpma_conn *conn, * The value of imm_data can only be placed in the receive Completion * Queue Element. */ - if ((cmpl->op == RPMA_OP_RECV) && (cmpl->flags & IBV_WC_WITH_IMM)) - cmpl->imm = ntohl(wc.imm_data); + if ((cmpl->op == RPMA_OP_RECV) || + (cmpl->op == RPMA_OP_RECV_RDMA_WITH_IMM)) { + if (cmpl->flags & IBV_WC_WITH_IMM) + cmpl->imm = ntohl(wc.imm_data); + } if (unlikely(wc.status != IBV_WC_SUCCESS)) { RPMA_LOG_WARNING("failed rpma_completion(op_context=0x%" PRIx64 diff --git a/src/include/librpma.h b/src/include/librpma.h index 747b34f812..50c52b9414 100644 --- a/src/include/librpma.h +++ b/src/include/librpma.h @@ -2044,6 +2044,50 @@ int rpma_write(struct rpma_conn *conn, const struct rpma_mr_local *src, size_t src_offset, size_t len, int flags, const void *op_context); +/** 3 + * rpma_write_with_imm - initiate the write operation with immediate data + * + * SYNOPSIS + * + * #include + * + * struct rpma_conn; + * struct rpma_mr_local; + * struct rpma_mr_remote; + * int rpma_write_with_imm(struct rpma_conn *conn, + * struct rpma_mr_remote *dst, size_t dst_offset, + * const struct rpma_mr_local *src, size_t src_offset, + * size_t len, int flags, uint32_t imm, + * const void *op_context); + * + * DESCRIPTION + * rpma_write_with_imm() initiates the write operation with immediate data + * (transferring data from the local memory to the remote memory. + * The attribute flags set the completion notification indicator: + * - RPMA_F_COMPLETION_ON_ERROR - generate the completion on error + * - RPMA_F_COMPLETION_ALWAYS - generate the completion regardless of result of + * the operation + * + * RETURN VALUE + * The rpma_write_with_imm() function returns 0 on success or a negative + * error code on failure. + * + * ERRORS + * rpma_write_with_imm() can fail with the following errors: + * + * - RPMA_E_INVAL - conn, dst or src is NULL + * - RPMA_E_INVAL - flags are not set + * - RPMA_E_PROVIDER - ibv_post_send(3) failed + * + * SEE ALSO + * rpma_conn_req_connect(3), rpma_mr_reg(3), + * rpma_mr_remote_from_descriptor(3), librpma(7) and https://pmem.io/rpma/ + */ +int rpma_write_with_imm(struct rpma_conn *conn, + struct rpma_mr_remote *dst, size_t dst_offset, + const struct rpma_mr_local *src, size_t src_offset, + size_t len, int flags, uint32_t imm, const void *op_context); + #define RPMA_ATOMIC_WRITE_ALIGNMENT 8 /** 3 @@ -2323,6 +2367,7 @@ enum rpma_op { RPMA_OP_FLUSH, RPMA_OP_SEND, RPMA_OP_RECV, + RPMA_OP_RECV_RDMA_WITH_IMM, }; struct rpma_completion { diff --git a/src/librpma.map b/src/librpma.map index 2d1b3946f0..cd8f2712c7 100644 --- a/src/librpma.map +++ b/src/librpma.map @@ -66,6 +66,7 @@ LIBRPMA_1.0 { rpma_utils_ibv_context_is_odp_capable; rpma_write; rpma_write_atomic; + rpma_write_with_imm; local: *; }; diff --git a/src/mr.c b/src/mr.c index 50ce389eb5..be1c2c99ac 100644 --- a/src/mr.c +++ b/src/mr.c @@ -146,8 +146,9 @@ rpma_mr_read(struct ibv_qp *qp, int rpma_mr_write(struct ibv_qp *qp, struct rpma_mr_remote *dst, size_t dst_offset, - const struct rpma_mr_local *src, size_t src_offset, - size_t len, int flags, const void *op_context, bool fence) + const struct rpma_mr_local *src, size_t src_offset, + size_t len, int flags, enum ibv_wr_opcode operation, + uint32_t imm, const void *op_context, bool fence) { struct ibv_send_wr wr; struct ibv_sge sge; @@ -165,7 +166,19 @@ rpma_mr_write(struct ibv_qp *qp, wr.wr_id = (uint64_t)op_context; wr.next = NULL; - wr.opcode = IBV_WR_RDMA_WRITE; + + wr.opcode = operation; + switch (wr.opcode) { + case IBV_WR_RDMA_WRITE: + break; + case IBV_WR_RDMA_WRITE_WITH_IMM: + wr.imm_data = htonl(imm); + break; + default: + RPMA_LOG_ERROR("unsupported wr.opcode == %d", wr.opcode); + return RPMA_E_NOSUPP; + } + wr.send_flags = (flags & RPMA_F_COMPLETION_ON_SUCCESS) ? IBV_SEND_SIGNALED : 0; wr.send_flags |= fence ? IBV_SEND_FENCE : 0; diff --git a/src/mr.h b/src/mr.h index b84b523830..0d8838c71c 100644 --- a/src/mr.h +++ b/src/mr.h @@ -33,12 +33,14 @@ int rpma_mr_read(struct ibv_qp *qp, * ERRORS * rpma_mr_write() can fail with the following error: * + * - RPMA_E_NOSUPP - unsupported 'operation' argument * - RPMA_E_PROVIDER - ibv_post_send(3) failed */ int rpma_mr_write(struct ibv_qp *qp, struct rpma_mr_remote *dst, size_t dst_offset, const struct rpma_mr_local *src, size_t src_offset, - size_t len, int flags, const void *op_context, bool fence); + size_t len, int flags, enum ibv_wr_opcode operation, + uint32_t imm, const void *op_context, bool fence); /* * ASSUMPTIONS diff --git a/tests/unit/common/mocks-rpma-mr.c b/tests/unit/common/mocks-rpma-mr.c index edf83aa8b6..fcea4da061 100644 --- a/tests/unit/common/mocks-rpma-mr.c +++ b/tests/unit/common/mocks-rpma-mr.c @@ -45,7 +45,8 @@ int rpma_mr_write(struct ibv_qp *qp, struct rpma_mr_remote *dst, size_t dst_offset, const struct rpma_mr_local *src, size_t src_offset, - size_t len, int flags, const void *op_context, bool fence) + size_t len, int flags, enum ibv_wr_opcode operation, + uint32_t imm, const void *op_context, bool fence) { assert_non_null(qp); assert_non_null(dst); @@ -59,6 +60,8 @@ rpma_mr_write(struct ibv_qp *qp, check_expected(src_offset); check_expected(len); check_expected(flags); + check_expected(operation); + check_expected(imm); check_expected_ptr(op_context); check_expected(fence); diff --git a/tests/unit/conn/CMakeLists.txt b/tests/unit/conn/CMakeLists.txt index f0905b046c..a6ada936a7 100644 --- a/tests/unit/conn/CMakeLists.txt +++ b/tests/unit/conn/CMakeLists.txt @@ -46,3 +46,4 @@ add_test_conn(send) add_test_conn(send_with_imm) add_test_conn(write) add_test_conn(write_atomic) +add_test_conn(write_with_imm) diff --git a/tests/unit/conn/conn-completion_get.c b/tests/unit/conn/conn-completion_get.c index fcd4e6f419..c54b35fee7 100644 --- a/tests/unit/conn/conn-completion_get.c +++ b/tests/unit/conn/conn-completion_get.c @@ -163,20 +163,23 @@ completion_get__success(void **cstate_ptr) IBV_WC_RDMA_WRITE, IBV_WC_SEND, IBV_WC_RECV, - IBV_WC_RECV + IBV_WC_RECV, + IBV_WC_RECV_RDMA_WITH_IMM }; enum rpma_op ops[] = { RPMA_OP_READ, RPMA_OP_WRITE, RPMA_OP_SEND, RPMA_OP_RECV, - RPMA_OP_RECV + RPMA_OP_RECV, + RPMA_OP_RECV_RDMA_WITH_IMM }; unsigned flags[] = { 0, 0, 0, 0, + IBV_WC_WITH_IMM, IBV_WC_WITH_IMM }; diff --git a/tests/unit/conn/conn-write.c b/tests/unit/conn/conn-write.c index fba0249379..236b2ba747 100644 --- a/tests/unit/conn/conn-write.c +++ b/tests/unit/conn/conn-write.c @@ -104,6 +104,8 @@ write__success(void **cstate_ptr) expect_value(rpma_mr_write, src_offset, MOCK_LOCAL_OFFSET); expect_value(rpma_mr_write, len, MOCK_LEN); expect_value(rpma_mr_write, flags, MOCK_FLAGS); + expect_value(rpma_mr_write, operation, IBV_WR_RDMA_WRITE); + expect_value(rpma_mr_write, imm, 0); expect_value(rpma_mr_write, op_context, MOCK_OP_CONTEXT); expect_value(rpma_mr_write, fence, MOCK_NOFENCE); will_return(rpma_mr_write, MOCK_OK); diff --git a/tests/unit/conn/conn-write_atomic.c b/tests/unit/conn/conn-write_atomic.c index dd8964c373..6159244ff2 100644 --- a/tests/unit/conn/conn-write_atomic.c +++ b/tests/unit/conn/conn-write_atomic.c @@ -119,6 +119,8 @@ write_atomic__success(void **cstate_ptr) expect_value(rpma_mr_write, src_offset, MOCK_LOCAL_OFFSET); expect_value(rpma_mr_write, len, RPMA_ATOMIC_WRITE_ALIGNMENT); expect_value(rpma_mr_write, flags, MOCK_FLAGS); + expect_value(rpma_mr_write, operation, IBV_WR_RDMA_WRITE); + expect_value(rpma_mr_write, imm, 0); expect_value(rpma_mr_write, op_context, MOCK_OP_CONTEXT); expect_value(rpma_mr_write, fence, MOCK_FENCE); will_return(rpma_mr_write, MOCK_OK); diff --git a/tests/unit/conn/conn-write_with_imm.c b/tests/unit/conn/conn-write_with_imm.c new file mode 100644 index 0000000000..a803f178ec --- /dev/null +++ b/tests/unit/conn/conn-write_with_imm.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright (c) 2021 Fujitsu */ + +/* + * conn-write_with_imm.c -- the rpma_write_with_imm() unit tests + * + * APIs covered: + * - rpma_write_with_imm() + */ + +#include "conn-common.h" +#include "mocks-ibverbs.h" +#include "mocks-rdma_cm.h" + +/* + * write__conn_NULL -- NULL conn is invalid + */ +static void +write_with_imm__conn_NULL(void **unused) +{ + /* run test */ + int ret = rpma_write_with_imm(NULL, MOCK_RPMA_MR_REMOTE, + MOCK_REMOTE_OFFSET, MOCK_RPMA_MR_LOCAL, + MOCK_LOCAL_OFFSET, MOCK_LEN, MOCK_FLAGS, + MOCK_IMM_DATA, MOCK_OP_CONTEXT); + + /* verify the results */ + assert_int_equal(ret, RPMA_E_INVAL); +} + +/* + * write_with_imm__dst_NULL -- NULL dst is invalid + */ +static void +write_with_imm__dst_NULL(void **unused) +{ + /* run test */ + int ret = rpma_write_with_imm(MOCK_CONN, NULL, + MOCK_REMOTE_OFFSET, MOCK_RPMA_MR_LOCAL, + MOCK_LOCAL_OFFSET, MOCK_LEN, MOCK_FLAGS, + MOCK_IMM_DATA, MOCK_OP_CONTEXT); + + /* verify the results */ + assert_int_equal(ret, RPMA_E_INVAL); +} + +/* + * write_with_imm__src_NULL -- NULL src is invalid + */ +static void +write_with_imm__src_NULL(void **unused) +{ + /* run test */ + int ret = rpma_write_with_imm(MOCK_CONN, MOCK_RPMA_MR_REMOTE, + MOCK_REMOTE_OFFSET, NULL, MOCK_LOCAL_OFFSET, + MOCK_LEN, MOCK_FLAGS, MOCK_IMM_DATA, + MOCK_OP_CONTEXT); + + /* verify the results */ + assert_int_equal(ret, RPMA_E_INVAL); +} + +/* + * write_with_imm__flags_0 -- flags == 0 is invalid + */ +static void +write_with_imm__flags_0(void **unused) +{ + /* run test */ + int ret = rpma_write_with_imm(MOCK_CONN, MOCK_RPMA_MR_REMOTE, + MOCK_REMOTE_OFFSET, MOCK_RPMA_MR_LOCAL, + MOCK_LOCAL_OFFSET, MOCK_LEN, 0, MOCK_IMM_DATA, + MOCK_OP_CONTEXT); + + /* verify the results */ + assert_int_equal(ret, RPMA_E_INVAL); +} + +/* + * write_with_imm__conn_dst_src_NULL_flags_0 -- NULL conn, dst, src + * and flags == 0 are invalid + */ +static void +write_with_imm__conn_dst_src_NULL_flags_0(void **unused) +{ + /* run test */ + int ret = rpma_write_with_imm(NULL, NULL, MOCK_REMOTE_OFFSET, + NULL, MOCK_LOCAL_OFFSET, MOCK_LEN, 0, + MOCK_IMM_DATA, MOCK_OP_CONTEXT); + + /* verify the results */ + assert_int_equal(ret, RPMA_E_INVAL); +} + +/* + * write_with_imm__success -- happy day scenario + */ +static void +write_with_imm__success(void **cstate_ptr) +{ + struct conn_test_state *cstate = *cstate_ptr; + + /* configure mocks */ + expect_value(rpma_mr_write, qp, MOCK_QP); + expect_value(rpma_mr_write, dst, MOCK_RPMA_MR_REMOTE); + expect_value(rpma_mr_write, dst_offset, MOCK_REMOTE_OFFSET); + expect_value(rpma_mr_write, src, MOCK_RPMA_MR_LOCAL); + expect_value(rpma_mr_write, src_offset, MOCK_LOCAL_OFFSET); + expect_value(rpma_mr_write, len, MOCK_LEN); + expect_value(rpma_mr_write, flags, MOCK_FLAGS); + expect_value(rpma_mr_write, operation, IBV_WR_RDMA_WRITE_WITH_IMM); + expect_value(rpma_mr_write, imm, MOCK_IMM_DATA); + expect_value(rpma_mr_write, op_context, MOCK_OP_CONTEXT); + expect_value(rpma_mr_write, fence, MOCK_NOFENCE); + will_return(rpma_mr_write, MOCK_OK); + + /* run test */ + int ret = rpma_write_with_imm(cstate->conn, + MOCK_RPMA_MR_REMOTE, MOCK_REMOTE_OFFSET, + MOCK_RPMA_MR_LOCAL, MOCK_LOCAL_OFFSET, + MOCK_LEN, MOCK_FLAGS, MOCK_IMM_DATA, + MOCK_OP_CONTEXT); + + /* verify the results */ + assert_int_equal(ret, MOCK_OK); +} + +/* + * group_setup_write -- prepare resources for all tests in the group + */ +static int +group_setup_write(void **unused) +{ + /* set value of QP in mock of CM ID */ + Cm_id.qp = MOCK_QP; + + return 0; +} + +static const struct CMUnitTest tests_write[] = { + /* rpma_read() unit tests */ + cmocka_unit_test(write_with_imm__conn_NULL), + cmocka_unit_test(write_with_imm__dst_NULL), + cmocka_unit_test(write_with_imm__src_NULL), + cmocka_unit_test(write_with_imm__flags_0), + cmocka_unit_test(write_with_imm__conn_dst_src_NULL_flags_0), + cmocka_unit_test_setup_teardown(write_with_imm__success, + setup__conn_new, teardown__conn_delete), + cmocka_unit_test(NULL) +}; + +int +main(int argc, char *argv[]) +{ + return cmocka_run_group_tests(tests_write, group_setup_write, NULL); +} diff --git a/tests/unit/mr/mr-common.h b/tests/unit/mr/mr-common.h index 5a4fe5b75e..d20ce3aaae 100644 --- a/tests/unit/mr/mr-common.h +++ b/tests/unit/mr/mr-common.h @@ -40,6 +40,7 @@ #define MOCK_DST_OFFSET (size_t)0xC413 #define MOCK_SRC_OFFSET (size_t)0xC414 #define MOCK_LEN (size_t)0xC415 +#define MOCK_UNKNOWN_OP ((enum ibv_wr_opcode)(-1)) #define MOCK_OP_CONTEXT (void *)0xC417 #define MOCK_DESC (void *)0xC418 diff --git a/tests/unit/mr/mr-send.c b/tests/unit/mr/mr-send.c index d7a32f0752..d628f527c2 100644 --- a/tests/unit/mr/mr-send.c +++ b/tests/unit/mr/mr-send.c @@ -17,8 +17,6 @@ #include "mr-common.h" #include "test-common.h" -#define MOCK_UNKNOWN_OP ((enum ibv_wr_opcode)(-1)) - /* * send__failed_E_NOSUPP - rpma_mr_send failed with RPMA_E_NOSUPP */ diff --git a/tests/unit/mr/mr-write.c b/tests/unit/mr/mr-write.c index 49fdf3ef74..df1cf3b17e 100644 --- a/tests/unit/mr/mr-write.c +++ b/tests/unit/mr/mr-write.c @@ -7,6 +7,7 @@ #include #include +#include #include "cmocka_headers.h" #include "mr.h" @@ -16,6 +17,24 @@ #include "mr-common.h" #include "test-common.h" +/* + * write__failed_E_NOSUPP - rpma_mr_write failed with RPMA_E_NOSUPP + */ +static void +write__failed_E_NOSUPP(void **mrs_ptr) +{ + struct mrs *mrs = (struct mrs *)*mrs_ptr; + + /* run test */ + int ret = rpma_mr_write(MOCK_QP, mrs->remote, MOCK_DST_OFFSET, + mrs->local, MOCK_SRC_OFFSET, MOCK_LEN, + RPMA_F_COMPLETION_ALWAYS, MOCK_UNKNOWN_OP, + 0, MOCK_OP_CONTEXT, MOCK_NOFENCE); + + /* verify the results */ + assert_int_equal(ret, RPMA_E_NOSUPP); +} + /* * write__COMPL_ALWAYS_failed_E_PROVIDER - rpma_mr_write failed * with RPMA_E_PROVIDER when send_flags == RPMA_F_COMPLETION_ON_SUCCESS @@ -36,9 +55,9 @@ write__COMPL_ALWAYS_failed_E_PROVIDER(void **mrs_ptr) /* run test */ int ret = rpma_mr_write(MOCK_QP, mrs->remote, MOCK_DST_OFFSET, - mrs->local, MOCK_SRC_OFFSET, - MOCK_LEN, RPMA_F_COMPLETION_ALWAYS, - MOCK_OP_CONTEXT, MOCK_NOFENCE); + mrs->local, MOCK_SRC_OFFSET, MOCK_LEN, + RPMA_F_COMPLETION_ALWAYS, IBV_WR_RDMA_WRITE, + 0, MOCK_OP_CONTEXT, MOCK_NOFENCE); /* verify the results */ assert_int_equal(ret, RPMA_E_PROVIDER); @@ -66,9 +85,9 @@ write__COMPL_ALWAYS_FENCE_failed_E_PROVIDER(void **mrs_ptr) /* run test */ int ret = rpma_mr_write(MOCK_QP, mrs->remote, MOCK_DST_OFFSET, - mrs->local, MOCK_SRC_OFFSET, - MOCK_LEN, RPMA_F_COMPLETION_ALWAYS, - MOCK_OP_CONTEXT, MOCK_FENCE); + mrs->local, MOCK_SRC_OFFSET, MOCK_LEN, + RPMA_F_COMPLETION_ALWAYS, IBV_WR_RDMA_WRITE, + 0, MOCK_OP_CONTEXT, MOCK_FENCE); /* verify the results */ assert_int_equal(ret, RPMA_E_PROVIDER); @@ -94,9 +113,9 @@ write__COMPL_ON_ERROR_failed_E_PROVIDER(void **mrs_ptr) /* run test */ int ret = rpma_mr_write(MOCK_QP, mrs->remote, MOCK_DST_OFFSET, - mrs->local, MOCK_SRC_OFFSET, - MOCK_LEN, RPMA_F_COMPLETION_ON_ERROR, - MOCK_OP_CONTEXT, MOCK_NOFENCE); + mrs->local, MOCK_SRC_OFFSET, MOCK_LEN, + RPMA_F_COMPLETION_ON_ERROR, IBV_WR_RDMA_WRITE, + 0, MOCK_OP_CONTEXT, MOCK_NOFENCE); /* verify the results */ assert_int_equal(ret, RPMA_E_PROVIDER); @@ -123,9 +142,9 @@ write__COMPL_ON_ERROR_FENCE_failed_E_PROVIDER(void **mrs_ptr) /* run test */ int ret = rpma_mr_write(MOCK_QP, mrs->remote, MOCK_DST_OFFSET, - mrs->local, MOCK_SRC_OFFSET, - MOCK_LEN, RPMA_F_COMPLETION_ON_ERROR, - MOCK_OP_CONTEXT, MOCK_FENCE); + mrs->local, MOCK_SRC_OFFSET, MOCK_LEN, + RPMA_F_COMPLETION_ON_ERROR, IBV_WR_RDMA_WRITE, + 0, MOCK_OP_CONTEXT, MOCK_FENCE); /* verify the results */ assert_int_equal(ret, RPMA_E_PROVIDER); @@ -139,23 +158,39 @@ write__success(void **mrs_ptr) { struct mrs *mrs = (struct mrs *)*mrs_ptr; - /* configure mocks */ - struct ibv_post_send_mock_args args; - args.qp = MOCK_QP; - args.opcode = IBV_WR_RDMA_WRITE; - args.send_flags = IBV_SEND_SIGNALED; /* for RPMA_F_COMPLETION_ALWAYS */ - args.wr_id = (uint64_t)MOCK_OP_CONTEXT; - args.ret = MOCK_OK; - will_return(ibv_post_send_mock, &args); - - /* run test */ - int ret = rpma_mr_write(MOCK_QP, mrs->remote, MOCK_DST_OFFSET, - mrs->local, MOCK_SRC_OFFSET, - MOCK_LEN, RPMA_F_COMPLETION_ALWAYS, - MOCK_OP_CONTEXT, MOCK_NOFENCE); - - /* verify the results */ - assert_int_equal(ret, MOCK_OK); + enum ibv_wr_opcode opcodes[] = { + IBV_WR_RDMA_WRITE, + IBV_WR_RDMA_WRITE_WITH_IMM + }; + uint32_t imms[] = { + 0, + MOCK_IMM_DATA + }; + + int n_values = sizeof(opcodes) / sizeof(opcodes[0]); + + for (int i = 0; i < n_values; i++) { + /* configure mocks */ + struct ibv_post_send_mock_args args; + args.qp = MOCK_QP; + args.opcode = opcodes[i]; + /* for RPMA_F_COMPLETION_ALWAYS */ + args.send_flags = IBV_SEND_SIGNALED; + args.wr_id = (uint64_t)MOCK_OP_CONTEXT; + if (opcodes[i] == IBV_WR_RDMA_WRITE_WITH_IMM) + args.imm_data = htonl(MOCK_IMM_DATA); + args.ret = MOCK_OK; + will_return(ibv_post_send_mock, &args); + + /* run test */ + int ret = rpma_mr_write(MOCK_QP, mrs->remote, MOCK_DST_OFFSET, + mrs->local, MOCK_SRC_OFFSET, MOCK_LEN, + RPMA_F_COMPLETION_ALWAYS, opcodes[i], + imms[i], MOCK_OP_CONTEXT, MOCK_NOFENCE); + + /* verify the results */ + assert_int_equal(ret, MOCK_OK); + } } /* @@ -178,9 +213,9 @@ write__FENCE_success(void **mrs_ptr) /* run test */ int ret = rpma_mr_write(MOCK_QP, mrs->remote, MOCK_DST_OFFSET, - mrs->local, MOCK_SRC_OFFSET, - MOCK_LEN, RPMA_F_COMPLETION_ALWAYS, - MOCK_OP_CONTEXT, MOCK_FENCE); + mrs->local, MOCK_SRC_OFFSET, MOCK_LEN, + RPMA_F_COMPLETION_ALWAYS, IBV_WR_RDMA_WRITE, + 0, MOCK_OP_CONTEXT, MOCK_FENCE); /* verify the results */ assert_int_equal(ret, MOCK_OK); @@ -212,6 +247,9 @@ group_setup_mr_write(void **unused) static const struct CMUnitTest tests_mr_write[] = { /* rpma_mr_write() unit tests */ + cmocka_unit_test_setup_teardown(write__failed_E_NOSUPP, + setup__mr_local_and_remote, + teardown__mr_local_and_remote), cmocka_unit_test_setup_teardown( write__COMPL_ALWAYS_failed_E_PROVIDER, setup__mr_local_and_remote,