Exemple #1
0
int
main(int argc, char *argv[])
{
	char *pool_desc, *log_file;
	char root_dir[PATH_MAX];

	START(argc, argv, "rpmemd_db");

	if (argc != 4)
		UT_FATAL("usage: %s <root_dir> <pool_desc> <log-file>",
				argv[0]);

	if (realpath(argv[1], root_dir) == NULL)
		UT_FATAL("!realpath(%s)", argv[1]);

	pool_desc = argv[2];
	log_file = argv[3];

	if (rpmemd_log_init("rpmemd error: ", log_file, 0))
		FAILED_FUNC("rpmemd_log_init");

	test_init(root_dir);
	test_check_dir(root_dir);
	test_create(root_dir, pool_desc);
	test_open(root_dir, pool_desc);

	rpmemd_log_close();

	DONE(NULL);
}
Exemple #2
0
int
main(int argc, char *argv[])
{
	base64_init();

	START(argc, argv, "rpmemd_obc");

	out_init("rpmemd_obc",
		"RPMEM_LOG_LEVEL",
		"RPMEM_LOG_FILE", 0, 0);
	rpmemd_log_init("rpmemd", getenv("RPMEMD_LOG_FILE"), 0);
	rpmemd_log_level = rpmemd_log_level_from_str(
			getenv("RPMEMD_LOG_LEVEL"));

	TEST_CASE_PROCESS(argc, argv, test_cases, NTESTS);

	rpmemd_log_close();

	out_fini();
	DONE(NULL);
}
Exemple #3
0
int
main(int argc, char *argv[])
{
    util_init();

    int send_status = 1;
    int ret = 1;

    struct rpmemd *rpmemd = calloc(1, sizeof(*rpmemd));
    if (!rpmemd) {
        RPMEMD_LOG(ERR, "!calloc");
        goto err_rpmemd;
    }

    rpmemd->obc = rpmemd_obc_init(STDIN_FILENO, STDOUT_FILENO);
    if (!rpmemd->obc) {
        RPMEMD_LOG(ERR, "out-of-band connection intitialization");
        goto err_obc;
    }

    if (rpmemd_log_init(DAEMON_NAME, NULL, 0)) {
        RPMEMD_LOG(ERR, "logging subsystem initialization failed");
        goto err_log_init;
    }

    if (rpmemd_config_read(&rpmemd->config, argc, argv) != 0) {
        RPMEMD_LOG(ERR, "reading configuration failed");
        goto err_config;
    }

    rpmemd_log_close();
    rpmemd_log_level = rpmemd->config.log_level;
    if (rpmemd_log_init(DAEMON_NAME, rpmemd->config.log_file,
                        rpmemd->config.use_syslog)) {
        RPMEMD_LOG(ERR, "logging subsystem initialization"
                   " failed (%s, %d)", rpmemd->config.log_file,
                   rpmemd->config.use_syslog);
        goto err_log_init_config;
    }

    RPMEMD_LOG(INFO, "%s version %s", DAEMON_NAME, SRCVERSION);
    rpmemd->persist = pmem_persist;
    rpmemd->persist_method = rpmemd_get_pm(&rpmemd->config);
    rpmemd->nthreads = rpmemd_get_nthreads();
    if (!rpmemd->nthreads) {
        RPMEMD_LOG(ERR, "invalid number of threads -- '%lu'",
                   rpmemd->nthreads);
        goto err_nthreads;
    }

    rpmemd->db = rpmemd_db_init(rpmemd->config.poolset_dir, 0666);
    if (!rpmemd->db) {
        RPMEMD_LOG(ERR, "!pool set db initialization");
        goto err_db_init;
    }

    if (rpmemd->config.rm_poolset) {
        RPMEMD_LOG(INFO, "removing '%s'",
                   rpmemd->config.rm_poolset);
        if (rpmemd_db_pool_remove(rpmemd->db,
                                  rpmemd->config.rm_poolset,
                                  rpmemd->config.force)) {
            RPMEMD_LOG(ERR, "removing '%s' failed",
                       rpmemd->config.rm_poolset);
        } else {
            RPMEMD_LOG(NOTICE, "removed '%s'",
                       rpmemd->config.rm_poolset);
            ret = 0;
        }
        send_status = 0;
        goto out_rm;
    }

    ret = rpmemd_obc_status(rpmemd->obc, 0);
    if (ret) {
        RPMEMD_LOG(ERR, "writing status failed");
        goto err_status;
    }

    rpmemd_print_info(rpmemd);

    while (!ret) {
        ret = rpmemd_obc_process(rpmemd->obc, &rpmemd_req, rpmemd);
        if (ret) {
            RPMEMD_LOG(ERR, "out-of-band connection"
                       " process failed");
            goto err;
        }

        if (rpmemd->closing)
            break;
    }

    rpmemd_db_fini(rpmemd->db);
    rpmemd_config_free(&rpmemd->config);
    rpmemd_log_close();
    rpmemd_obc_fini(rpmemd->obc);
    free(rpmemd);

    return 0;
err:
    rpmemd_req_cleanup(rpmemd);
err_status:
out_rm:
    rpmemd_db_fini(rpmemd->db);
err_db_init:
err_nthreads:
err_log_init_config:
    rpmemd_config_free(&rpmemd->config);
err_config:
    rpmemd_log_close();
err_log_init:
    if (send_status) {
        if (rpmemd_obc_status(rpmemd->obc, (uint32_t)errno))
            RPMEMD_LOG(ERR, "writing status failed");
    }
    rpmemd_obc_fini(rpmemd->obc);
err_obc:
    free(rpmemd);
err_rpmemd:
    return ret;
}
Exemple #4
0
int
main(int argc, char *argv[])
{
	START(argc, argv, "rpmemd_log");

	if (argc < 4) {
		USAGE();
		return 1;
	}

	const char *log_op = argv[1];
	const char *log_type = argv[2];
	const char *file = argv[3];

	int do_fatal = 0;
	int do_assert = 0;
	if (strcmp(log_op, "fatal") == 0) {
		do_fatal = 1;
	} else if (strcmp(log_op, "assert") == 0) {
		do_assert = 1;
	} else if (strcmp(log_op, "log") == 0) {
	} else {
		USAGE();
		return 1;
	}

	enum test_log_type type;
	if (strcmp(log_type, "stdout") == 0) {
		type = TEST_STDOUT;
	} else if (strcmp(log_type, "file") == 0) {
		type = TEST_FILE;
	} else if (strcmp(log_type, "syslog") == 0) {
		type = TEST_SYSLOG;
	} else {
		USAGE();
		return 1;
	}

	int fd_stdout = -1;
	FILE *stdout_fh = NULL;
	switch (type) {
	case TEST_STDOUT:
		/*
		 * Duplicate stdout file descriptor in order to preserve
		 * the file list after redirecting the stdout to a file.
		 */
		fd_stdout = dup(1);
		UT_ASSERTne(fd_stdout, -1);
		close(1);
		stdout_fh = fopen(file, "a");
		UT_ASSERTne(stdout_fh, NULL);
		break;
	case TEST_SYSLOG:
		syslog_fh = fopen(file, "a");
		UT_ASSERTne(syslog_fh, NULL);
		break;
	default:
		break;
	}

	/*
	 * Check an invalid configuration
	 */
	int ret;
	ret = rpmemd_log_init("rpmemd_log", file, 1);
	UT_ASSERTne(ret, 0);

	switch (type) {
	case TEST_STDOUT:
		ret = rpmemd_log_init("rpmemd_log", NULL, 0);
		UT_ASSERTeq(ret, 0);
		break;
	case TEST_SYSLOG:
		ret = rpmemd_log_init("rpmemd_log", NULL, 1);
		UT_ASSERTeq(ret, 0);
		break;
	case TEST_FILE:
		ret = rpmemd_log_init("rpmemd_log", file, 0);
		UT_ASSERTeq(ret, 0);
		break;
	default:
		break;
	}

	if (do_fatal) {
		RPMEMD_FATAL("fatal");
	} else if (do_assert) {
		RPMEMD_ASSERT(1);
		RPMEMD_ASSERT(0);
	} else {
		test_all_log_messages();
	}

	rpmemd_log_close();

	switch (type) {
	case TEST_STDOUT:
		/* restore the original stdout file descriptor */
		fclose(stdout_fh);
		UT_ASSERTeq(dup2(fd_stdout, 1), 1);
		close(fd_stdout);
		break;
	case TEST_SYSLOG:
		fclose(syslog_fh);
		break;
	default:
		break;
	}

	DONE(NULL);
}
Exemple #5
0
/*
 * server -- run server and process clients requests
 */
int
server(const struct test_case *tc, int argc, char *argv[])
{
	int ret;

	struct req_arg arg = {
		.resp = RESP_ATTR_INIT,
		.pool_attr = POOL_ATTR_INIT,
		.closing = 0,
	};

	struct rpmemd_obc *obc;

	obc = rpmemd_obc_init(0, 1);
	UT_ASSERTne(obc, NULL);

	ret = rpmemd_obc_status(obc, 0);
	UT_ASSERTeq(ret, 0);


	while (1) {
		ret = rpmemd_obc_process(obc, &REQ, &arg);
		if (arg.closing) {
			break;
		} else {
			UT_ASSERTeq(ret, 0);
		}
	}

	ret = rpmemd_obc_process(obc, &REQ, &arg);
	UT_ASSERTeq(ret, 1);

	rpmemd_obc_fini(obc);

	return 0;
}

/*
 * test_cases -- available test cases
 */
static struct test_case test_cases[] = {
	TEST_CASE(server),
	TEST_CASE(client_create),
	TEST_CASE(client_open),
};

#define NTESTS	(sizeof(test_cases) / sizeof(test_cases[0]))

int
main(int argc, char *argv[])
{
	START(argc, argv, "rpmem_obc");
	common_init("rpmem_fip",
		"RPMEM_LOG_LEVEL",
		"RPMEM_LOG_FILE", 0, 0);
	rpmemd_log_init("rpmemd", getenv("RPMEMD_LOG_FILE"), 0);
	rpmemd_log_level = rpmemd_log_level_from_str(
			getenv("RPMEMD_LOG_LEVEL"));

	TEST_CASE_PROCESS(argc, argv, test_cases, NTESTS);

	common_fini();
	rpmemd_log_close();
	DONE(NULL);
}
Exemple #6
0
int
main(int argc, char *argv[])
{
	int ret;
	util_init();

	struct rpmemd_config config;
	rpmemd_log_init(DAEMON_NAME, NULL, 0);
	if (rpmemd_config_read(&config, argc, argv) != 0) {
		ret = 1;
		goto err_config;
	}

	rpmemd_log_level = config.log_level;
	rpmemd_log_init(DAEMON_NAME, config.log_file, config.use_syslog);

	RPMEMD_LOG(INFO, "%s version %s", DAEMON_NAME, SRCVERSION);

	struct rpmemd_obc *obc = rpmemd_obc_init(STDIN_FILENO, STDOUT_FILENO);
	if (!obc) {
		RPMEMD_LOG(ERR, "out-of-band connection intitialization");
		ret = 1;
		goto err_obc;
	}

	struct rpmemd *rpmemd = calloc(1, sizeof(*rpmemd));
	if (!rpmemd) {
		RPMEMD_LOG(ERR, "!calloc");
		ret = rpmemd_obc_status(obc, (uint32_t)errno);
		if (ret)
			RPMEMD_LOG(ERR, "writing status failed");
		goto err_rpmemd;
	}

	rpmemd->persist = pmem_persist;
	rpmemd->persist_method = rpmemd_get_pm(&config);
	rpmemd->nthreads = rpmemd_get_nthreads();
	if (!rpmemd->nthreads) {
		RPMEMD_LOG(ERR, "invalid number of threads -- '%lu'",
				rpmemd->nthreads);
		ret = rpmemd_obc_status(obc, (uint32_t)errno);
		if (ret)
			RPMEMD_LOG(ERR, "writing status failed");
		goto err_nthreads;
	}

	rpmemd->db = rpmemd_db_init(config.poolset_dir, 0666);
	if (!rpmemd->db) {
		RPMEMD_LOG(ERR, "!pool set db initialization");
		ret = rpmemd_obc_status(obc, (uint32_t)errno);
		if (ret)
			RPMEMD_LOG(ERR, "writing status failed");
		goto err_db_init;
	}

	rpmemd->obc = obc;

	ret = rpmemd_obc_status(obc, 0);
	if (ret) {
		RPMEMD_LOG(ERR, "writing status failed");
		goto err_status;
	}

	while (!ret) {
		ret = rpmemd_obc_process(obc, &rpmemd_req, rpmemd);
		if (ret) {
			RPMEMD_LOG(ERR, "out-of-band connection"
					" process failed");
			goto err;
		}

		if (rpmemd->closing)
			break;
	}

	rpmemd_obc_fini(rpmemd->obc);
	rpmemd_db_fini(rpmemd->db);
	free(rpmemd);
	rpmemd_log_close();
	rpmemd_config_free(&config);

	return 0;
err:
err_status:
err_db_init:
err_nthreads:
	free(rpmemd);
err_rpmemd:
	rpmemd_obc_fini(obc);
err_obc:
	rpmemd_log_close();
	rpmemd_config_free(&config);
err_config:
	return ret;
}
Exemple #7
0
/*
 * client_init -- test case for client initialization
 */
int
client_init(const struct test_case *tc, int argc, char *argv[])
{
	if (argc < 3)
		UT_FATAL("usage: %s <target> <provider> <persist method>",
				tc->name);

	char *target = argv[0];
	char *prov_name = argv[1];
	char *persist_method = argv[2];

	set_rpmem_cmd("server_init %s", persist_method);

	char fip_service[NI_MAXSERV];

	struct rpmem_target_info *info;
	info = rpmem_target_parse(target);
	UT_ASSERTne(info, NULL);

	unsigned nlanes;
	enum rpmem_provider provider = get_provider(info->node,
			prov_name, &nlanes);

	client_t *client;
	struct rpmem_resp_attr resp;
	client = client_exchange(info, NLANES, provider, &resp);

	struct rpmem_fip_attr attr = {
		.provider = provider,
		.persist_method = resp.persist_method,
		.laddr = lpool,
		.size = POOL_SIZE,
		.nlanes = resp.nlanes,
		.raddr = (void *)resp.raddr,
		.rkey = resp.rkey,
	};

	ssize_t sret = snprintf(fip_service, NI_MAXSERV, "%u", resp.port);
	UT_ASSERT(sret > 0);

	struct rpmem_fip *fip;
	fip = rpmem_fip_init(info->node, fip_service, &attr, &nlanes);
	UT_ASSERTne(fip, NULL);

	client_close_begin(client);
	client_close_end(client);

	rpmem_fip_fini(fip);
	rpmem_target_free(info);

	return 3;
}

/*
 * server_init -- test case for server initialization
 */
int
server_init(const struct test_case *tc, int argc, char *argv[])
{
	if (argc < 1)
		UT_FATAL("usage: %s <persist method>", tc->name);

	enum rpmem_persist_method persist_method = get_persist_method(argv[0]);

	unsigned nlanes;
	enum rpmem_provider provider;
	char *addr = NULL;
	server_exchange_begin(&nlanes, &provider, &addr);
	UT_ASSERTne(addr, NULL);

	struct rpmemd_fip_attr attr = {
		.addr = rpool,
		.size = POOL_SIZE,
		.nlanes = nlanes,
		.provider = provider,
		.persist_method = persist_method,
		.persist = pmem_persist,
		.nthreads = NTHREADS,
	};

	struct rpmem_resp_attr resp;
	struct rpmemd_fip *fip;
	enum rpmem_err err;

	fip = rpmemd_fip_init(addr, NULL, &attr, &resp, &err);
	UT_ASSERTne(fip, NULL);

	server_exchange_end(resp);
	server_close_begin();
	server_close_end();

	rpmemd_fip_fini(fip);

	FREE(addr);

	return 1;
}

/*
 * client_connect -- test case for establishing connection - client side
 */
int
client_connect(const struct test_case *tc, int argc, char *argv[])
{
	if (argc < 3)
		UT_FATAL("usage: %s <target> <provider> <persist method>",
				tc->name);

	char *target = argv[0];
	char *prov_name = argv[1];
	char *persist_method = argv[2];

	set_rpmem_cmd("server_connect %s", persist_method);

	char fip_service[NI_MAXSERV];
	struct rpmem_target_info *info;
	int ret;

	info = rpmem_target_parse(target);
	UT_ASSERTne(info, NULL);

	unsigned nlanes;
	enum rpmem_provider provider = get_provider(info->node,
			prov_name, &nlanes);

	client_t *client;
	struct rpmem_resp_attr resp;
	client = client_exchange(info, NLANES, provider, &resp);

	struct rpmem_fip_attr attr = {
		.provider = provider,
		.persist_method = resp.persist_method,
		.laddr = lpool,
		.size = POOL_SIZE,
		.nlanes = resp.nlanes,
		.raddr = (void *)resp.raddr,
		.rkey = resp.rkey,
	};

	ssize_t sret = snprintf(fip_service, NI_MAXSERV, "%u", resp.port);
	UT_ASSERT(sret > 0);

	struct rpmem_fip *fip;
	fip = rpmem_fip_init(info->node, fip_service, &attr, &nlanes);
	UT_ASSERTne(fip, NULL);

	ret = rpmem_fip_connect(fip);
	UT_ASSERTeq(ret, 0);

	client_close_begin(client);

	ret = rpmem_fip_close(fip);
	UT_ASSERTeq(ret, 0);

	client_close_end(client);

	rpmem_fip_fini(fip);
	rpmem_target_free(info);

	return 3;
}

/*
 * server_connect -- test case for establishing connection - server side
 */
int
server_connect(const struct test_case *tc, int argc, char *argv[])
{
	if (argc < 1)
		UT_FATAL("usage: %s <persist method>", tc->name);

	enum rpmem_persist_method persist_method = get_persist_method(argv[0]);

	unsigned nlanes;
	enum rpmem_provider provider;
	char *addr = NULL;
	server_exchange_begin(&nlanes, &provider, &addr);
	UT_ASSERTne(addr, NULL);

	struct rpmemd_fip_attr attr = {
		.addr = rpool,
		.size = POOL_SIZE,
		.nlanes = nlanes,
		.provider = provider,
		.persist_method = persist_method,
		.persist = pmem_persist,
		.nthreads = NTHREADS,
	};

	int ret;
	struct rpmem_resp_attr resp;
	struct rpmemd_fip *fip;
	enum rpmem_err err;

	fip = rpmemd_fip_init(addr, NULL, &attr, &resp, &err);
	UT_ASSERTne(fip, NULL);

	server_exchange_end(resp);

	ret = rpmemd_fip_accept(fip, -1);
	UT_ASSERTeq(ret, 0);

	server_close_begin();
	server_close_end();

	ret = rpmemd_fip_wait_close(fip, -1);
	UT_ASSERTeq(ret, 0);

	ret = rpmemd_fip_close(fip);
	UT_ASSERTeq(ret, 0);

	rpmemd_fip_fini(fip);

	FREE(addr);

	return 1;
}

/*
 * server_process -- test case for processing data on server side
 */
int
server_process(const struct test_case *tc, int argc, char *argv[])
{
	if (argc < 1)
		UT_FATAL("usage: %s <persist method>", tc->name);

	enum rpmem_persist_method persist_method = get_persist_method(argv[0]);

	set_pool_data(rpool, 1);

	unsigned nlanes;
	enum rpmem_provider provider;
	char *addr = NULL;
	server_exchange_begin(&nlanes, &provider, &addr);
	UT_ASSERTne(addr, NULL);

	struct rpmemd_fip_attr attr = {
		.addr = rpool,
		.size = POOL_SIZE,
		.nlanes = nlanes,
		.provider = provider,
		.persist_method = persist_method,
		.persist = pmem_persist,
		.nthreads = NTHREADS,
	};

	int ret;
	struct rpmem_resp_attr resp;
	struct rpmemd_fip *fip;
	enum rpmem_err err;

	fip = rpmemd_fip_init(addr, NULL, &attr, &resp, &err);
	UT_ASSERTne(fip, NULL);

	server_exchange_end(resp);

	ret = rpmemd_fip_accept(fip, -1);
	UT_ASSERTeq(ret, 0);

	ret = rpmemd_fip_process_start(fip);

	server_close_begin();

	ret = rpmemd_fip_process_stop(fip);
	UT_ASSERTeq(ret, 0);

	server_close_end();

	ret = rpmemd_fip_wait_close(fip, -1);
	UT_ASSERTeq(ret, 0);

	ret = rpmemd_fip_close(fip);
	UT_ASSERTeq(ret, 0);

	rpmemd_fip_fini(fip);

	FREE(addr);

	return 1;
}

/*
 * client_persist -- test case for single-threaded persist operation
 */
int
client_persist(const struct test_case *tc, int argc, char *argv[])
{
	if (argc < 3)
		UT_FATAL("usage: %s <target> <provider> <persist method>",
				tc->name);

	char *target = argv[0];
	char *prov_name = argv[1];
	char *persist_method = argv[2];

	set_rpmem_cmd("server_process %s", persist_method);

	char fip_service[NI_MAXSERV];
	struct rpmem_target_info *info;

	info = rpmem_target_parse(target);
	UT_ASSERTne(info, NULL);

	int ret;

	set_pool_data(lpool, 1);
	set_pool_data(rpool, 1);

	unsigned nlanes;
	enum rpmem_provider provider = get_provider(info->node,
			prov_name, &nlanes);

	client_t *client;
	struct rpmem_resp_attr resp;
	client = client_exchange(info, NLANES, provider, &resp);

	struct rpmem_fip_attr attr = {
		.provider = provider,
		.persist_method = resp.persist_method,
		.laddr = lpool,
		.size = POOL_SIZE,
		.nlanes = resp.nlanes,
		.raddr = (void *)resp.raddr,
		.rkey = resp.rkey,
	};

	ssize_t sret = snprintf(fip_service, NI_MAXSERV, "%u", resp.port);
	UT_ASSERT(sret > 0);

	struct rpmem_fip *fip;
	fip = rpmem_fip_init(info->node, fip_service, &attr, &nlanes);
	UT_ASSERTne(fip, NULL);

	ret = rpmem_fip_connect(fip);
	UT_ASSERTeq(ret, 0);

	ret = rpmem_fip_process_start(fip);
	UT_ASSERTeq(ret, 0);

	struct persist_arg arg = {
		.fip = fip,
		.lane = 0,
	};

	client_persist_thread(&arg);

	ret = rpmem_fip_read(fip, rpool, POOL_SIZE, 0);
	UT_ASSERTeq(ret, 0);

	ret = rpmem_fip_process_stop(fip);
	UT_ASSERTeq(ret, 0);

	client_close_begin(client);

	ret = rpmem_fip_close(fip);
	UT_ASSERTeq(ret, 0);

	client_close_end(client);

	rpmem_fip_fini(fip);

	ret = memcmp(rpool, lpool, POOL_SIZE);
	UT_ASSERTeq(ret, 0);

	rpmem_target_free(info);

	return 3;
}

/*
 * client_persist_mt -- test case for multi-threaded persist operation
 */
int
client_persist_mt(const struct test_case *tc, int argc, char *argv[])
{
	if (argc < 3)
		UT_FATAL("usage: %s <target> <provider> <persist method>",
				tc->name);

	char *target = argv[0];
	char *prov_name = argv[1];
	char *persist_method = argv[2];

	set_rpmem_cmd("server_process %s", persist_method);

	char fip_service[NI_MAXSERV];
	struct rpmem_target_info *info;
	int ret;

	info = rpmem_target_parse(target);
	UT_ASSERTne(info, NULL);

	set_pool_data(lpool, 1);
	set_pool_data(rpool, 1);

	unsigned nlanes;
	enum rpmem_provider provider = get_provider(info->node,
			prov_name, &nlanes);

	client_t *client;
	struct rpmem_resp_attr resp;
	client = client_exchange(info, NLANES, provider, &resp);

	struct rpmem_fip_attr attr = {
		.provider = provider,
		.persist_method = resp.persist_method,
		.laddr = lpool,
		.size = POOL_SIZE,
		.nlanes = resp.nlanes,
		.raddr = (void *)resp.raddr,
		.rkey = resp.rkey,
	};

	ssize_t sret = snprintf(fip_service, NI_MAXSERV, "%u", resp.port);
	UT_ASSERT(sret > 0);

	struct rpmem_fip *fip;
	fip = rpmem_fip_init(info->node, fip_service, &attr, &nlanes);
	UT_ASSERTne(fip, NULL);

	ret = rpmem_fip_connect(fip);
	UT_ASSERTeq(ret, 0);

	ret = rpmem_fip_process_start(fip);
	UT_ASSERTeq(ret, 0);

	pthread_t *persist_thread = MALLOC(resp.nlanes * sizeof(pthread_t));
	struct persist_arg *args = MALLOC(resp.nlanes *
			sizeof(struct persist_arg));

	for (unsigned i = 0; i < nlanes; i++) {
		args[i].fip = fip;
		args[i].lane = i;
		PTHREAD_CREATE(&persist_thread[i], NULL,
				client_persist_thread, &args[i]);
	}

	for (unsigned i = 0; i < nlanes; i++)
		PTHREAD_JOIN(persist_thread[i], NULL);

	ret = rpmem_fip_read(fip, rpool, POOL_SIZE, 0);
	UT_ASSERTeq(ret, 0);

	ret = rpmem_fip_process_stop(fip);
	UT_ASSERTeq(ret, 0);

	client_close_begin(client);

	ret = rpmem_fip_close(fip);
	UT_ASSERTeq(ret, 0);

	client_close_end(client);

	rpmem_fip_fini(fip);

	FREE(persist_thread);
	FREE(args);

	ret = memcmp(rpool, lpool, POOL_SIZE);
	UT_ASSERTeq(ret, 0);

	rpmem_target_free(info);

	return 3;
}

/*
 * client_read -- test case for read operation
 */
int
client_read(const struct test_case *tc, int argc, char *argv[])
{
	if (argc < 3)
		UT_FATAL("usage: %s <target> <provider> <persist method>",
				tc->name);

	char *target = argv[0];
	char *prov_name = argv[1];
	char *persist_method = argv[2];

	set_rpmem_cmd("server_process %s", persist_method);

	char fip_service[NI_MAXSERV];
	struct rpmem_target_info *info;
	int ret;

	info = rpmem_target_parse(target);
	UT_ASSERTne(info, NULL);

	set_pool_data(lpool, 0);
	set_pool_data(rpool, 1);

	unsigned nlanes;
	enum rpmem_provider provider = get_provider(info->node,
			prov_name, &nlanes);

	client_t *client;
	struct rpmem_resp_attr resp;
	client = client_exchange(info, NLANES, provider, &resp);

	struct rpmem_fip_attr attr = {
		.provider = provider,
		.persist_method = resp.persist_method,
		.laddr = lpool,
		.size = POOL_SIZE,
		.nlanes = resp.nlanes,
		.raddr = (void *)resp.raddr,
		.rkey = resp.rkey,
	};

	ssize_t sret = snprintf(fip_service, NI_MAXSERV, "%u", resp.port);
	UT_ASSERT(sret > 0);

	struct rpmem_fip *fip;
	fip = rpmem_fip_init(info->node, fip_service, &attr, &nlanes);
	UT_ASSERTne(fip, NULL);

	ret = rpmem_fip_connect(fip);
	UT_ASSERTeq(ret, 0);

	ret = rpmem_fip_process_start(fip);
	UT_ASSERTeq(ret, 0);

	ret = rpmem_fip_read(fip, lpool, POOL_SIZE, 0);
	UT_ASSERTeq(ret, 0);

	ret = rpmem_fip_process_stop(fip);
	UT_ASSERTeq(ret, 0);

	client_close_begin(client);

	ret = rpmem_fip_close(fip);
	UT_ASSERTeq(ret, 0);

	client_close_end(client);

	rpmem_fip_fini(fip);

	ret = memcmp(rpool, lpool, POOL_SIZE);
	UT_ASSERTeq(ret, 0);

	rpmem_target_free(info);

	return 3;
}

/*
 * test_cases -- available test cases
 */
static struct test_case test_cases[] = {
	TEST_CASE(client_init),
	TEST_CASE(server_init),
	TEST_CASE(client_connect),
	TEST_CASE(server_connect),
	TEST_CASE(client_persist),
	TEST_CASE(client_persist_mt),
	TEST_CASE(server_process),
	TEST_CASE(client_read),
};

#define NTESTS	(sizeof(test_cases) / sizeof(test_cases[0]))

int
main(int argc, char *argv[])
{
	/* workaround for left-opened files by libfabric */
	rpmem_fip_probe_get("localhost", NULL);
	START(argc, argv, "rpmem_obc");
	common_init("rpmem_fip",
		"RPMEM_LOG_LEVEL",
		"RPMEM_LOG_FILE", 0, 0);
	rpmem_util_cmds_init();
	rpmemd_log_init("rpmemd", getenv("RPMEMD_LOG_FILE"), 0);
	rpmemd_log_level = rpmemd_log_level_from_str(
			getenv("RPMEMD_LOG_LEVEL"));
	TEST_CASE_PROCESS(argc, argv, test_cases, NTESTS);

	common_fini();
	rpmemd_log_close();
	rpmem_util_cmds_fini();
	DONE(NULL);
}