blob: 94a5986955b3a4591b82607b4b7e7ad4a314158e [file] [log] [blame]
/*
* pmem_namespaces: test functionality of PMEM namespaces
*
* Copyright (c) 2015, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*/
#include <errno.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <ndctl/libndctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <uuid/uuid.h>
#include <libkmod.h>
#include <linux/version.h>
#include <test.h>
#include <ccan/array_size/array_size.h>
#ifdef HAVE_NDCTL_H
#include <linux/ndctl.h>
#else
#include <ndctl.h>
#endif
#define err(msg)\
fprintf(stderr, "%s:%d: %s (%s)\n", __func__, __LINE__, msg, strerror(errno))
static struct ndctl_namespace *create_pmem_namespace(struct ndctl_region *region)
{
struct ndctl_namespace *seed_ns = NULL;
unsigned long long size;
uuid_t uuid;
seed_ns = ndctl_region_get_namespace_seed(region);
if (!seed_ns)
return NULL;
uuid_generate(uuid);
size = ndctl_region_get_size(region);
if (ndctl_namespace_set_uuid(seed_ns, uuid) < 0)
return NULL;
if (ndctl_namespace_set_size(seed_ns, size) < 0)
return NULL;
if (ndctl_namespace_enable(seed_ns) < 0)
return NULL;
return seed_ns;
}
static int disable_pmem_namespace(struct ndctl_namespace *ndns)
{
if (ndctl_namespace_disable_invalidate(ndns) < 0)
return -ENODEV;
if (ndctl_namespace_delete(ndns) < 0)
return -ENODEV;
return 0;
}
static int ns_do_io(const char *bdev)
{
unsigned long num_dev_pages, num_blocks;
const int page_size = 4096;
const int num_pages = 2;
off_t addr;
int rc = 0;
int fd, i;
void *random_page[num_pages];
void *pmem_page[num_pages];
rc = posix_memalign(random_page, page_size, page_size * num_pages);
if (rc) {
fprintf(stderr, "posix_memalign failure\n");
return rc;
}
rc = posix_memalign(pmem_page, page_size, page_size * num_pages);
if (rc) {
fprintf(stderr, "posix_memalign failure\n");
goto err_free_pmem;
}
for (i = 1; i < num_pages; i++) {
random_page[i] = (char*)random_page[0] + page_size * i;
pmem_page[i] = (char*)pmem_page[0] + page_size * i;
}
/* read random data into random_page */
if ((fd = open("/dev/urandom", O_RDONLY)) < 0) {
err("open");
rc = -ENODEV;
goto err_free_all;
}
rc = read(fd, random_page[0], page_size * num_pages);
if (rc < 0) {
err("read");
close(fd);
goto err_free_all;
}
close(fd);
/* figure out our dev size */
if ((fd = open(bdev, O_RDWR|O_DIRECT)) < 0) {
err("open");
rc = -ENODEV;
goto err_free_all;
}
ioctl(fd, BLKGETSIZE, &num_blocks);
num_dev_pages = num_blocks / 8;
/* write the random data out to each of the segments */
rc = pwrite(fd, random_page[0], page_size, 0);
if (rc < 0) {
err("write");
goto err_close;
}
addr = page_size * (num_dev_pages - 1);
rc = pwrite(fd, random_page[1], page_size, addr);
if (rc < 0) {
err("write");
goto err_close;
}
/* read back the random data into pmem_page */
rc = pread(fd, pmem_page[0], page_size, 0);
if (rc < 0) {
err("read");
goto err_close;
}
addr = page_size * (num_dev_pages - 1);
rc = pread(fd, pmem_page[1], page_size, addr);
if (rc < 0) {
err("read");
goto err_close;
}
/* verify the data */
if (memcmp(random_page[0], pmem_page[0], page_size * num_pages)) {
fprintf(stderr, "PMEM data miscompare\n");
rc = -EIO;
goto err_close;
}
rc = 0;
err_close:
close(fd);
err_free_all:
free(random_page[0]);
err_free_pmem:
free(pmem_page[0]);
return rc;
}
static const char *comm = "test-pmem-namespaces";
int test_pmem_namespaces(int log_level, struct ndctl_test *test,
struct ndctl_ctx *ctx)
{
struct ndctl_region *region, *pmem_region = NULL;
struct kmod_ctx *kmod_ctx = NULL;
struct kmod_module *mod = NULL;
struct ndctl_namespace *ndns;
struct ndctl_dimm *dimm;
struct ndctl_bus *bus;
int rc = -ENXIO;
char bdev[50];
if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 2, 0)))
return 77;
ndctl_set_log_priority(ctx, log_level);
bus = ndctl_bus_get_by_provider(ctx, "ACPI.NFIT");
if (bus) {
/* skip this bus if no label-enabled PMEM regions */
ndctl_region_foreach(bus, region)
if (ndctl_region_get_nstype(region)
== ND_DEVICE_NAMESPACE_PMEM)
break;
if (!region)
bus = NULL;
}
if (!bus) {
fprintf(stderr, "ACPI.NFIT unavailable falling back to nfit_test\n");
rc = nfit_test_init(&kmod_ctx, &mod, log_level, test);
ndctl_invalidate(ctx);
bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0");
if (rc < 0 || !bus) {
rc = 77;
ndctl_test_skip(test);
fprintf(stderr, "nfit_test unavailable skipping tests\n");
goto err_module;
}
}
fprintf(stderr, "%s: found provider: %s\n", comm,
ndctl_bus_get_provider(bus));
/* get the system to a clean state */
ndctl_region_foreach(bus, region)
ndctl_region_disable_invalidate(region);
ndctl_dimm_foreach(bus, dimm) {
rc = ndctl_dimm_zero_labels(dimm);
if (rc < 0) {
fprintf(stderr, "failed to zero %s\n",
ndctl_dimm_get_devname(dimm));
goto err;
}
}
/* create our config */
ndctl_region_foreach(bus, region)
if (strcmp(ndctl_region_get_type_name(region), "pmem") == 0) {
pmem_region = region;
break;
}
if (!pmem_region || ndctl_region_enable(pmem_region) < 0) {
fprintf(stderr, "%s: failed to find PMEM region\n", comm);
rc = -ENODEV;
goto err;
}
rc = -ENODEV;
ndns = create_pmem_namespace(pmem_region);
if (!ndns) {
fprintf(stderr, "%s: failed to create PMEM namespace\n", comm);
goto err;
}
sprintf(bdev, "/dev/%s", ndctl_namespace_get_block_device(ndns));
rc = ns_do_io(bdev);
disable_pmem_namespace(ndns);
err:
/* unload nfit_test */
bus = ndctl_bus_get_by_provider(ctx, "nfit_test.0");
if (bus)
ndctl_region_foreach(bus, region)
ndctl_region_disable_invalidate(region);
bus = ndctl_bus_get_by_provider(ctx, "nfit_test.1");
if (bus)
ndctl_region_foreach(bus, region)
ndctl_region_disable_invalidate(region);
kmod_module_remove_module(mod, 0);
err_module:
kmod_unref(kmod_ctx);
return rc;
}
int __attribute__((weak)) main(int argc, char *argv[])
{
struct ndctl_test *test = ndctl_test_new(0);
struct ndctl_ctx *ctx;
int rc;
comm = argv[0];
if (!test) {
fprintf(stderr, "failed to initialize test\n");
return EXIT_FAILURE;
}
rc = ndctl_new(&ctx);
if (rc)
return ndctl_test_result(test, rc);
rc = test_pmem_namespaces(LOG_DEBUG, test, ctx);
ndctl_unref(ctx);
return ndctl_test_result(test, rc);
}