/**
* Copyright (C) Mellanox Technologies Ltd. 2001-2015. ALL RIGHTS RESERVED.
*
* See file LICENSE for terms.
*/
#include "test_ucp_memheap.h"
extern "C" {
#include <ucp/core/ucp_context.h>
#include <ucp/core/ucp_mm.h>
#include <ucp/core/ucp_ep.inl>
}
class test_ucp_mmap : public test_ucp_memheap {
public:
static ucp_params_t get_ctx_params() {
ucp_params_t params = ucp_test::get_ctx_params();
params.features |= UCP_FEATURE_RMA;
return params;
}
static int rand_flags() {
if ((ucs::rand() % 2) == 0) {
return 0;
} else {
return UCP_MEM_MAP_NONBLOCK;
}
}
protected:
bool resolve_rma(entity *e, ucp_rkey_h rkey);
bool resolve_amo(entity *e, ucp_rkey_h rkey);
bool resolve_rma_bw(entity *e, ucp_rkey_h rkey);
void test_length0(unsigned flags);
void test_rkey_management(entity *e, ucp_mem_h memh, bool is_dummy);
};
bool test_ucp_mmap::resolve_rma(entity *e, ucp_rkey_h rkey)
{
ucs_status_t status;
{
scoped_log_handler slh(hide_errors_logger);
status = UCP_RKEY_RESOLVE(rkey, e->ep(), rma);
}
if (status == UCS_OK) {
EXPECT_NE(UCP_NULL_LANE, rkey->cache.rma_lane);
return true;
} else if (status == UCS_ERR_UNREACHABLE) {
EXPECT_EQ(UCP_NULL_LANE, rkey->cache.rma_lane);
return false;
} else {
UCS_TEST_ABORT("Invalid status from UCP_RKEY_RESOLVE");
}
}
bool test_ucp_mmap::resolve_amo(entity *e, ucp_rkey_h rkey)
{
ucs_status_t status;
{
scoped_log_handler slh(hide_errors_logger);
status = UCP_RKEY_RESOLVE(rkey, e->ep(), amo);
}
if (status == UCS_OK) {
EXPECT_NE(UCP_NULL_LANE, rkey->cache.amo_lane);
return true;
} else if (status == UCS_ERR_UNREACHABLE) {
EXPECT_EQ(UCP_NULL_LANE, rkey->cache.amo_lane);
return false;
} else {
UCS_TEST_ABORT("Invalid status from UCP_RKEY_RESOLVE");
}
}
bool test_ucp_mmap::resolve_rma_bw(entity *e, ucp_rkey_h rkey)
{
ucp_ep_config_t *ep_config = ucp_ep_config(e->ep());
ucp_lane_index_t lane;
uct_rkey_t uct_rkey;
lane = ucp_rkey_find_rma_lane(e->ucph(), ep_config, UCS_MEMORY_TYPE_HOST,
ep_config->tag.rndv.get_zcopy_lanes, rkey, 0,
&uct_rkey);
if (lane != UCP_NULL_LANE) {
return true;
} else {
return false;
}
}
void test_ucp_mmap::test_rkey_management(entity *e, ucp_mem_h memh, bool is_dummy)
{
size_t rkey_size;
void *rkey_buffer;
ucs_status_t status;
/* Some transports don't support memory registration, so the memory
* can be inaccessible remotely. But it should always be possible
* to pack/unpack a key, even if empty. */
status = ucp_rkey_pack(e->ucph(), memh, &rkey_buffer, &rkey_size);
if (status == UCS_ERR_UNSUPPORTED && !is_dummy) {
return;
}
ASSERT_UCS_OK(status);
EXPECT_EQ(ucp_rkey_packed_size(e->ucph(), memh->md_map), rkey_size);
/* Unpack remote key buffer */
ucp_rkey_h rkey;
status = ucp_ep_rkey_unpack(e->ep(), rkey_buffer, &rkey);
if (status == UCS_ERR_UNREACHABLE && !is_dummy) {
ucp_rkey_buffer_release(rkey_buffer);
return;
}
ASSERT_UCS_OK(status);
/* Test ucp_rkey_packed_md_map() */
EXPECT_EQ(rkey->md_map, ucp_rkey_packed_md_map(rkey_buffer));
bool have_rma = resolve_rma(e, rkey);
bool have_amo = resolve_amo(e, rkey);
bool have_rma_bw = resolve_rma_bw(e, rkey);
/* Test that lane resolution on the remote key returns consistent results */
for (int i = 0; i < 10; ++i) {
switch (ucs::rand() % 3) {
case 0:
EXPECT_EQ(have_rma, resolve_rma(e, rkey));
break;
case 1:
EXPECT_EQ(have_amo, resolve_amo(e, rkey));
break;
case 2:
EXPECT_EQ(have_rma_bw, resolve_rma_bw(e, rkey));
break;
}
}
/* Test obtaining direct-access pointer */
void *ptr;
status = ucp_rkey_ptr(rkey, (uint64_t)memh->address, &ptr);
if (status == UCS_OK) {
EXPECT_EQ(0, memcmp(memh->address, ptr, memh->length));
} else {
EXPECT_EQ(UCS_ERR_UNREACHABLE, status);
}
ucp_rkey_destroy(rkey);
ucp_rkey_buffer_release(rkey_buffer);
}
UCS_TEST_P(test_ucp_mmap, alloc) {
ucs_status_t status;
bool is_dummy;
sender().connect(&sender(), get_ep_params());
for (int i = 0; i < 1000 / ucs::test_time_multiplier(); ++i) {
size_t size = ucs::rand() % (UCS_MBYTE);
ucp_mem_h memh;
ucp_mem_map_params_t params;
params.field_mask = UCP_MEM_MAP_PARAM_FIELD_ADDRESS |
UCP_MEM_MAP_PARAM_FIELD_LENGTH |
UCP_MEM_MAP_PARAM_FIELD_FLAGS;
params.address = NULL;
params.length = size;
params.flags = rand_flags() | UCP_MEM_MAP_ALLOCATE;
status = ucp_mem_map(sender().ucph(), ¶ms, &memh);
ASSERT_UCS_OK(status);
is_dummy = (size == 0);
test_rkey_management(&sender(), memh, is_dummy);
status = ucp_mem_unmap(sender().ucph(), memh);
ASSERT_UCS_OK(status);
}
}
UCS_TEST_P(test_ucp_mmap, reg) {
ucs_status_t status;
bool is_dummy;
sender().connect(&sender(), get_ep_params());
for (int i = 0; i < 1000 / ucs::test_time_multiplier(); ++i) {
size_t size = ucs::rand() % (UCS_MBYTE);
void *ptr = malloc(size);
ucs::fill_random(ptr, size);
ucp_mem_h memh;
ucp_mem_map_params_t params;
params.field_mask = UCP_MEM_MAP_PARAM_FIELD_ADDRESS |
UCP_MEM_MAP_PARAM_FIELD_LENGTH |
UCP_MEM_MAP_PARAM_FIELD_FLAGS;
params.address = ptr;
params.length = size;
params.flags = rand_flags();
status = ucp_mem_map(sender().ucph(), ¶ms, &memh);
ASSERT_UCS_OK(status);
is_dummy = (size == 0);
test_rkey_management(&sender(), memh, is_dummy);
status = ucp_mem_unmap(sender().ucph(), memh);
ASSERT_UCS_OK(status);
free(ptr);
}
}
void test_ucp_mmap::test_length0(unsigned flags)
{
ucs_status_t status;
int buf_num = 2;
ucp_mem_h memh[buf_num];
int dummy[1];
ucp_mem_map_params_t params;
int i;
sender().connect(&sender(), get_ep_params());
/* Check that ucp_mem_map accepts any value for buffer if size is 0 and
* UCP_MEM_FLAG_ZERO_REG flag is passed to it. */
params.field_mask = UCP_MEM_MAP_PARAM_FIELD_ADDRESS |
UCP_MEM_MAP_PARAM_FIELD_LENGTH |
UCP_MEM_MAP_PARAM_FIELD_FLAGS;
params.address = NULL;
params.length = 0;
params.flags = rand_flags() | flags;
status = ucp_mem_map(sender().ucph(), ¶ms, &memh[0]);
ASSERT_UCS_OK(status);
params.address = dummy;
status = ucp_mem_map(sender().ucph(), ¶ms, &memh[1]);
ASSERT_UCS_OK(status);
for (i = 0; i < buf_num; i++) {
test_rkey_management(&sender(), memh[i], true);
status = ucp_mem_unmap(sender().ucph(), memh[i]);
ASSERT_UCS_OK(status);
}
}
UCS_TEST_P(test_ucp_mmap, reg0) {
test_length0(0);
}
UCS_TEST_P(test_ucp_mmap, alloc0) {
test_length0(UCP_MEM_MAP_ALLOCATE);
}
UCS_TEST_P(test_ucp_mmap, alloc_advise) {
ucs_status_t status;
bool is_dummy;
sender().connect(&sender(), get_ep_params());
size_t size = 128 * UCS_MBYTE;
ucp_mem_h memh;
ucp_mem_map_params_t params;
ucp_mem_attr_t attr;
ucp_mem_advise_params_t advise_params;
params.field_mask = UCP_MEM_MAP_PARAM_FIELD_ADDRESS |
UCP_MEM_MAP_PARAM_FIELD_LENGTH |
UCP_MEM_MAP_PARAM_FIELD_FLAGS;
params.address = NULL;
params.length = size;
params.flags = UCP_MEM_MAP_NONBLOCK | UCP_MEM_MAP_ALLOCATE;
status = ucp_mem_map(sender().ucph(), ¶ms, &memh);
ASSERT_UCS_OK(status);
attr.field_mask = UCP_MEM_ATTR_FIELD_ADDRESS | UCP_MEM_ATTR_FIELD_LENGTH;
status = ucp_mem_query(memh, &attr);
ASSERT_UCS_OK(status);
EXPECT_GE(attr.length, size);
advise_params.field_mask = UCP_MEM_ADVISE_PARAM_FIELD_ADDRESS |
UCP_MEM_ADVISE_PARAM_FIELD_LENGTH |
UCP_MEM_ADVISE_PARAM_FIELD_ADVICE;
advise_params.address = attr.address;
advise_params.length = size;
advise_params.advice = UCP_MADV_WILLNEED;
status = ucp_mem_advise(sender().ucph(), memh, &advise_params);
ASSERT_UCS_OK(status);
is_dummy = (size == 0);
test_rkey_management(&sender(), memh, is_dummy);
status = ucp_mem_unmap(sender().ucph(), memh);
ASSERT_UCS_OK(status);
}
UCS_TEST_P(test_ucp_mmap, reg_advise) {
ucs_status_t status;
bool is_dummy;
sender().connect(&sender(), get_ep_params());
size_t size = 128 * UCS_MBYTE;
void *ptr = malloc(size);
ucs::fill_random(ptr, size);
ucp_mem_h memh;
ucp_mem_map_params_t params;
ucp_mem_attr_t mem_attr;
ucp_mem_advise_params_t advise_params;
params.field_mask = UCP_MEM_MAP_PARAM_FIELD_ADDRESS |
UCP_MEM_MAP_PARAM_FIELD_LENGTH |
UCP_MEM_MAP_PARAM_FIELD_FLAGS;
params.address = ptr;
params.length = size;
params.flags = UCP_MEM_MAP_NONBLOCK;
status = ucp_mem_map(sender().ucph(), ¶ms, &memh);
ASSERT_UCS_OK(status);
mem_attr.field_mask = UCP_MEM_ATTR_FIELD_ADDRESS;
status = ucp_mem_query(memh, &mem_attr);
ASSERT_UCS_OK(status);
advise_params.field_mask = UCP_MEM_ADVISE_PARAM_FIELD_ADDRESS |
UCP_MEM_ADVISE_PARAM_FIELD_LENGTH |
UCP_MEM_ADVISE_PARAM_FIELD_ADVICE;
advise_params.address = mem_attr.address;
advise_params.length = size;
advise_params.advice = UCP_MADV_WILLNEED;
status = ucp_mem_advise(sender().ucph(), memh, &advise_params);
ASSERT_UCS_OK(status);
is_dummy = (size == 0);
test_rkey_management(&sender(), memh, is_dummy);
status = ucp_mem_unmap(sender().ucph(), memh);
ASSERT_UCS_OK(status);
free(ptr);
}
UCS_TEST_P(test_ucp_mmap, fixed) {
ucs_status_t status;
bool is_dummy;
sender().connect(&sender(), get_ep_params());
for (int i = 0; i < 1000 / ucs::test_time_multiplier(); ++i) {
size_t size = (i + 1) * ((i % 2) ? 1000 : 1);
void *ptr = ucs::mmap_fixed_address();
ucp_mem_h memh;
ucp_mem_map_params_t params;
params.field_mask = UCP_MEM_MAP_PARAM_FIELD_ADDRESS |
UCP_MEM_MAP_PARAM_FIELD_LENGTH |
UCP_MEM_MAP_PARAM_FIELD_FLAGS;
params.address = ptr;
params.length = size;
params.flags = UCP_MEM_MAP_FIXED | UCP_MEM_MAP_ALLOCATE;
status = ucp_mem_map(sender().ucph(), ¶ms, &memh);
ASSERT_UCS_OK(status);
EXPECT_EQ(memh->address, ptr);
EXPECT_GE(memh->length, size);
is_dummy = (size == 0);
test_rkey_management(&sender(), memh, is_dummy);
status = ucp_mem_unmap(sender().ucph(), memh);
ASSERT_UCS_OK(status);
}
}
UCP_INSTANTIATE_TEST_CASE(test_ucp_mmap)