@@ -11,6 +11,7 @@ AM_CPPFLAGS = \
-DNDCTL_MAN_PATH=\""$(mandir)"\" \
-I${top_srcdir}/ndctl/lib \
-I${top_srcdir}/ndctl \
+ -I${top_srcdir}/daxctl \
-I${top_srcdir}/ \
$(KMOD_CFLAGS) \
$(UDEV_CFLAGS) \
@@ -123,6 +123,14 @@ AS_IF([test "x$enable_local" = "xyes"], [], [
]
)
+AS_IF([test "x$enable_local" = "xyes"], [], [
+ AC_CHECK_HEADER([linux/dax.h], [
+ AC_DEFINE([HAVE_DAX_H], [1],
+ [Define to 1 if you have <linux/dax.h>.])
+ ], [])
+ ]
+)
+
# when building against kernel headers check version specific features
AC_MSG_CHECKING([for ARS support])
AC_LANG(C)
@@ -2,9 +2,23 @@ include $(top_srcdir)/Makefile.am.in
bin_PROGRAMS = daxctl
+bin_SCRIPTS = daxon daxoff
+CLEANFILES += $(bin_SCRIPTS)
+EXTRA_DIST += daxon.in daxoff.in
+
+do_subst = sed -e 's,BINDIR,$(bindir),g'
+
+daxon: daxon.in
+ $(AM_V_GEN) $(do_subst) < $< > $@ && chmod +x $@
+
+daxoff: daxoff.in
+ $(AM_V_GEN) $(do_subst) < $< > $@ && chmod +x $@
+
daxctl_SOURCES =\
daxctl.c \
+ daxfile.c \
list.c \
+ ../util/log.c \
../util/json.c
daxctl_LDADD =\
new file mode 100644
@@ -0,0 +1,8 @@
+#ifndef _LINUX_DAX_H
+#define _LINUX_DAX_H
+
+#define DAXCTL_F_GET (1 << 0)
+#define DAXCTL_F_DAX (1 << 1)
+#define DAXCTL_F_STATIC (1 << 2)
+
+#endif /* _LINUX_DAX_H */
@@ -67,10 +67,14 @@ static int cmd_help(int argc, const char **argv, void *ctx)
}
int cmd_list(int argc, const char **argv, void *ctx);
+int cmd_enable(int argc, const char **argv, void *ctx);
+int cmd_disable(int argc, const char **argv, void *ctx);
static struct cmd_struct commands[] = {
{ "version", cmd_version },
{ "list", cmd_list },
+ { "enable", cmd_enable },
+ { "disable", cmd_disable },
{ "help", cmd_help },
};
new file mode 100644
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2015-2017 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <util/log.h>
+#include <util/size.h>
+#include <sys/syscall.h>
+#include <util/parse-options.h>
+#include <ccan/endian/endian.h>
+#include <ccan/short_types/short_types.h>
+
+#ifdef HAVE_DAX_H
+#include <linux/dax.h>
+#else
+#include <dax.h>
+#endif
+
+static struct parameters {
+ bool verbose;
+ bool check;
+ bool static_mode;
+ const char *align;
+} param;
+
+#define BASE_OPTIONS() \
+OPT_BOOLEAN('v', "verbose", ¶m.verbose, "enable extra logging"), \
+OPT_BOOLEAN('s', "static", ¶m.static_mode, \
+ "toggle / check <daxfile> static-dax capability")
+
+#define ENABLE_OPTIONS() \
+OPT_BOOLEAN('c', "check", ¶m.check, \
+ "check if <daxfile> is enabled for dax"), \
+OPT_STRING('a', "align", ¶m.align, "align", \
+ "specify expected minimum alignment of allocated extents")
+
+static const struct option enable_options[] = {
+ BASE_OPTIONS(),
+ ENABLE_OPTIONS(),
+ OPT_END(),
+};
+
+static const struct option disable_options[] = {
+ BASE_OPTIONS(),
+ OPT_END(),
+};
+
+struct dax_ctl {
+ struct log_ctx ctx;
+ const char *path;
+ struct stat stat;
+ int fd;
+};
+
+#ifdef __NR_daxctl
+#define daxctl(path, flags, align) \
+ syscall(__NR_daxctl, (path), (flags), (align))
+#else
+static int daxctl(const char *path, int flags, int align)
+{
+ errno = ENOTTY;
+ return -1;
+}
+#endif
+
+static bool enable_checks(struct dax_ctl *ctl)
+{
+ int fd;
+ struct stat st;
+
+ fd = open(ctl->path, O_RDONLY);
+ if (fd < 0) {
+ err(ctl, "failed to open %s (%s\n)", ctl->path, strerror(errno));
+ goto err;
+ }
+ ctl->fd = fd;
+
+ if (fstat(fd, &st) < 0) {
+ err(ctl, "failed to stat %s (%s\n)", ctl->path, strerror(errno));
+ goto err;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ err(ctl, "error: %s not a regular file\n", ctl->path);
+ goto err;
+ }
+
+ /* test for holes by LBT */
+ if (st.st_blocks * 512 < st.st_size) {
+ err(ctl, "error: %s appears to be a sparse file\n", ctl->path);
+ goto err;
+ }
+
+ close(fd);
+ ctl->fd = -1;
+ return true;
+err:
+ if (fd >= 0)
+ close(fd);
+ ctl->fd = -1;
+ return false;
+}
+
+int cmd_enable(int argc, const char **argv, void *ctx)
+{
+ const char * const u[] = {
+ "daxctl enable <daxfile> [<options>]",
+ NULL
+ };
+ struct dax_ctl _ctl, *ctl = &_ctl;
+ int i, rc, flags = DAXCTL_F_STATIC;
+ unsigned long long align = 0; /* default to PAGE_SIZE */
+
+ argc = parse_options(argc, argv, enable_options, u, 0);
+ for (i = 1; i < argc; i++)
+ error("unknown parameter \"%s\"\n", argv[i]);
+
+ if (argc != 1) {
+ error("missing 'daxfile' to register\n");
+ usage_with_options(u, enable_options);
+ }
+
+ log_init(&ctl->ctx, "enable", "DAXCTL_ENABLE_LOGLEVEL");
+ if (param.verbose)
+ ctl->ctx.log_priority = LOG_DEBUG;
+ else
+ ctl->ctx.log_priority = LOG_INFO;
+
+ if (param.align) {
+ align = parse_size64(param.align);
+ if (align == ULLONG_MAX) {
+ error("could not parse --align parameter '%s'\n",
+ param.align);
+ return EXIT_FAILURE;
+ }
+ if (align > SZ_1G || !is_power_of_2(align)) {
+ error("invalid --align parameter '%s'\n",
+ param.align);
+ return EXIT_FAILURE;
+ }
+ }
+
+ ctl->path = argv[0];
+
+ if (param.check)
+ flags |= DAXCTL_F_GET;
+ else if (!enable_checks(ctl))
+ return EXIT_FAILURE;
+
+ rc = daxctl(ctl->path, flags, align);
+ if (rc < 0) {
+ err(ctl, "failed to %s daxfile: %s (%s)\n",
+ ctl->path, param.check ? "check" : "register",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ if (param.check && rc != DAXCTL_F_STATIC) {
+ dbg(ctl, "static-dax disabled for: %s\n", ctl->path);
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
+
+int cmd_disable(int argc, const char **argv, void *ctx)
+{
+ const char * const u[] = {
+ "daxctl disable <daxfile> [<options>]",
+ NULL
+ };
+ struct dax_ctl _ctl, *ctl = &_ctl;
+ int i, rc;
+
+ argc = parse_options(argc, argv, disable_options, u, 0);
+ for (i = 1; i < argc; i++)
+ error("unknown parameter \"%s\"\n", argv[i]);
+
+ if (argc != 1) {
+ error("missing 'daxfile' to unregister\n");
+ usage_with_options(u, disable_options);
+ }
+
+ log_init(&ctl->ctx, "disable", "DAXCTL_DISABLE_LOGLEVEL");
+ ctl->path = argv[0];
+
+ rc = daxctl(ctl->path, 0, 0);
+ if (rc < 0) {
+ err(ctl, "failed to unregister daxfile: %s (%s)\n",
+ ctl->path, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
new file mode 100644
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+BINDIR/daxctl disable $@
new file mode 100644
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+BINDIR/daxctl enable $@
@@ -11,6 +11,7 @@
* more details.
*/
#include <stdlib.h>
+#include <util/size.h>
#include <ndctl/libndctl.h>
#include "libndctl-private.h"
@@ -44,11 +45,6 @@ NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus,
}
#ifdef HAVE_NDCTL_CLEAR_ERROR
-static bool is_power_of_2(unsigned int v)
-{
- return v && ((v & (v - 1)) == 0);
-}
-
static bool validate_clear_error(struct ndctl_cmd *ars_cap)
{
if (!is_power_of_2(ars_cap->ars_cap->clear_err_unit))
@@ -75,7 +75,9 @@ parent_uuid_LDADD = $(LIBNDCTL_LIB) $(UUID_LIBS) $(KMOD_LIBS)
dax_dev_SOURCES = dax-dev.c $(testcore)
dax_dev_LDADD = $(LIBNDCTL_LIB) $(KMOD_LIBS)
-dax_pmd_SOURCES = dax-pmd.c
+dax_pmd_SOURCES = dax-pmd.c \
+ $(testcore)
+
mmap_SOURCES = mmap.c
dax_errors_SOURCES = dax-errors.c
daxdev_errors_SOURCES = daxdev-errors.c \
@@ -24,7 +24,25 @@
#include <linux/fs.h>
#include <test.h>
#include <util/size.h>
+#include <sys/syscall.h>
#include <linux/fiemap.h>
+#include <linux/version.h>
+#ifdef HAVE_DAX_H
+#include <linux/dax.h>
+#else
+#include <dax.h>
+#endif
+
+#ifdef __NR_daxctl
+#define daxctl(path, flags, align) \
+ syscall(__NR_daxctl, (path), (flags), (align))
+#else
+static int daxctl(const char *path, int flags, int align)
+{
+ errno = ENOTTY;
+ return -1;
+}
+#endif
#define NUM_EXTENTS 5
#define fail() fprintf(stderr, "%s: failed at: %d\n", __func__, __LINE__)
@@ -185,8 +203,59 @@ static int test_pmd(int fd)
return rc;
}
+static int test_daxfile(char *daxfile)
+{
+ int fd, rc;
+
+ rc = daxctl(daxfile, DAXCTL_F_GET, 0);
+ if (rc < 0) {
+ fprintf(stderr, "%s: failed to retrieve dax flags: %s\n",
+ __func__, strerror(errno));
+ return -errno;
+ }
+ if (rc != 0) {
+ fprintf(stderr, "%s: expected dax flags %d got %d\n",
+ __func__, 0, rc);
+ return -ENXIO;
+ }
+
+ rc = daxctl(daxfile, DAXCTL_F_STATIC, 0);
+ if (rc < 0) {
+ fprintf(stderr, "%s: failed to set static dax: %s\n",
+ __func__, strerror(errno));
+ return -errno;
+ }
+
+ rc = daxctl(daxfile, DAXCTL_F_GET, 0);
+ if (rc < 0) {
+ fprintf(stderr, "%s: failed to retrieve dax flags: %s\n",
+ __func__, strerror(errno));
+ return -errno;
+ }
+ if (rc != DAXCTL_F_STATIC) {
+ fprintf(stderr, "%s: expected dax flags %d got %d\n",
+ __func__, DAXCTL_F_STATIC, rc);
+ return -ENXIO;
+ }
+
+ fd = open(daxfile, O_RDWR);
+ rc = test_pmd(fd);
+ if (rc)
+ return rc;
+
+ rc = daxctl(daxfile, 0, 0);
+ if (rc < 0) {
+ fprintf(stderr, "%s: failed to clear static dax: %s\n",
+ __func__, strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
int __attribute__((weak)) main(int argc, char *argv[])
{
+ struct ndctl_test *test = ndctl_test_new(0);
int fd, rc;
if (argc < 1)
@@ -196,5 +265,11 @@ int __attribute__((weak)) main(int argc, char *argv[])
rc = test_pmd(fd);
if (fd >= 0)
close(fd);
- return rc;
+ if (rc)
+ return rc;
+
+ /* try the same test with a daxfile */
+ if (!ndctl_test_attempt(test, KERNEL_VERSION(4, 14, 0)))
+ return 0;
+ return test_daxfile(argv[1]);
}
@@ -29,6 +29,22 @@ err() {
exit $rc
}
+alloc_file() {
+ # Note, we use dd here to get guarantees that the filesystem
+ # allocates the extents. ext4 is fine to use fallocate like so:
+ #
+ # fallocate -l 1GiB $MNT/$FILE
+ #
+ # ...but that does not work for xfs. xfs does support the -z
+ # option to allocate unwritten extents, like so:
+ #
+ # fallocate -z -l 1GiB $MNT/$FILE
+ #
+ # ...but that does not appear to work for ext4.
+
+ dd if=/dev/zero bs=1G count=1 of=$MNT/$FILE
+}
+
set -e
mkdir -p $MNT
trap 'err $LINENO' ERR
@@ -39,7 +55,7 @@ eval $(echo $json | sed -e "$json2var")
mkfs.ext4 /dev/$blockdev
mount /dev/$blockdev $MNT -o dax
-fallocate -l 1GiB $MNT/$FILE
+alloc_file
./dax-pmd $MNT/$FILE
umount $MNT
@@ -51,7 +67,7 @@ eval $(echo $json | sed -e "$json2var")
#note the blockdev returned from ndctl create-namespace lacks the /dev prefix
mkfs.ext4 /dev/$blockdev
mount /dev/$blockdev $MNT -o dax
-fallocate -l 1GiB $MNT/$FILE
+alloc_file
./dax-pmd $MNT/$FILE
umount $MNT
@@ -61,7 +77,7 @@ eval $(echo $json | sed -e "$json2var")
mkfs.xfs -f /dev/$blockdev
mount /dev/$blockdev $MNT -o dax
-fallocate -l 1GiB $MNT/$FILE
+alloc_file
./dax-pmd $MNT/$FILE
umount $MNT
@@ -72,7 +88,7 @@ eval $(echo $json | sed -e "$json2var")
mkfs.xfs -f /dev/$blockdev
mount /dev/$blockdev $MNT -o dax
-fallocate -l 1GiB $MNT/$FILE
+alloc_file
./dax-pmd $MNT/$FILE
umount $MNT
@@ -13,9 +13,12 @@
#ifndef _NDCTL_SIZE_H_
#define _NDCTL_SIZE_H_
+#include <stdbool.h>
#define SZ_1K 0x00000400
#define SZ_4K 0x00001000
+#define SZ_32K 0x00008000
+#define SZ_64K 0x00010000
#define SZ_1M 0x00100000
#define SZ_2M 0x00200000
#define SZ_4M 0x00400000
@@ -27,6 +30,11 @@
unsigned long long parse_size64(const char *str);
unsigned long long __parse_size64(const char *str, unsigned long long *units);
+static inline bool is_power_of_2(unsigned int v)
+{
+ return v && ((v & (v - 1)) == 0);
+}
+
#define ALIGN(x, a) ((((unsigned long long) x) + (a - 1)) & ~(a - 1))
#define BITS_PER_LONG (sizeof(unsigned long) * 8)
#define HPAGE_SIZE (2 << 20)
'daxctl enable' and 'daxctl disable' are wrapper utilities around the new daxctl() syscall that pins / guarantees a given block-map for a file. The "dax.sh" unit test is extended to run its tests against the target file in daxfile mode. Signed-off-by: Dan Williams <dan.j.williams@intel.com> --- Makefile.am.in | 1 configure.ac | 8 ++ daxctl/Makefile.am | 14 +++ daxctl/dax.h | 8 ++ daxctl/daxctl.c | 4 + daxctl/daxfile.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++ daxctl/daxoff.in | 3 + daxctl/daxon.in | 3 + ndctl/lib/libndctl-ars.c | 6 - test/Makefile.am | 4 + test/dax-pmd.c | 77 +++++++++++++++++ test/dax.sh | 24 ++++- util/size.h | 8 ++ 13 files changed, 360 insertions(+), 11 deletions(-) create mode 100644 daxctl/dax.h create mode 100644 daxctl/daxfile.c create mode 100644 daxctl/daxoff.in create mode 100644 daxctl/daxon.in