diff --git a/Makefile.am b/Makefile.am
index e5b4b4980100..e998c5766631 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -53,6 +53,10 @@ lib_libndctl_la_SOURCES =\
 	lib/libndctl.c
 lib_libndctl_la_LIBADD = $(UDEV_LIBS) $(UUID_LIBS) $(KMOD_LIBS)
 
+if ENABLE_ARS
+lib_libndctl_la_SOURCES += lib/libndctl-ars.c
+endif
+
 bin_PROGRAMS = ndctl
 
 ndctl_SOURCES = ndctl.c \
diff --git a/configure.ac b/configure.ac
index deaa94b0338b..c1c0f684e35c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -73,10 +73,38 @@ PKG_CHECK_MODULES([KMOD], [libkmod])
 PKG_CHECK_MODULES([UDEV], [libudev])
 PKG_CHECK_MODULES([UUID], [uuid])
 
-AC_CHECK_HEADER([linux/ndctl.h],
-		[AC_DEFINE([HAVE_NDCTL_H], [1],
-			[Define to 1 if you have <linux/ndctl.h>.])],
-		[])
+AC_ARG_ENABLE([local],
+        AS_HELP_STRING([--enable-local], [build with local ndctl.h @<:@default=system@:>@]),
+        [], [enable_local=no])
+
+AS_IF([test "x$enable_local" = "xyes"], [], [
+	AC_CHECK_HEADER([linux/ndctl.h], [
+		AC_DEFINE([HAVE_NDCTL_H], [1],
+			[Define to 1 if you have <linux/ndctl.h>.])
+		], [])
+	]
+)
+
+# when building against kernel headers check version specific features
+AC_MSG_CHECKING([for ARS support])
+AC_LANG(C)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+			#ifdef HAVE_NDCTL_H
+			#include <linux/ndctl.h>
+			#else
+			#include "ndctl.h"
+			#endif
+			]], [[
+			int x = ARS_STATUS_MASK;
+			]]
+		)], [AC_MSG_RESULT([yes])
+		     enable_ars=yes
+		     AC_DEFINE([HAVE_NDCTL_ARS], [1],
+				[Define to 1 if ndctl.h has ARS support.])
+		], [AC_MSG_RESULT([no])]
+)
+AM_CONDITIONAL([ENABLE_ARS], [test "x$enable_ars" = "xyes"])
+
 AC_CHECK_HEADERS_ONCE([linux/version.h])
 
 AC_CHECK_FUNCS([ \
diff --git a/lib/libndctl-ars.c b/lib/libndctl-ars.c
new file mode 100644
index 000000000000..fd988996501d
--- /dev/null
+++ b/lib/libndctl-ars.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2014-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 <stdlib.h>
+#include <ndctl/libndctl.h>
+#include "libndctl-private.h"
+
+NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus,
+		unsigned long long address, unsigned long long len)
+{
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
+	struct ndctl_cmd *cmd;
+	size_t size;
+
+	if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) {
+		dbg(ctx, "unsupported cmd\n");
+		return NULL;
+	}
+
+	size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_cap);
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->bus = bus;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_ARS_CAP;
+	cmd->size = size;
+	cmd->status = 1;
+	cmd->firmware_status = &cmd->ars_cap->status;
+	cmd->ars_cap->address = address;
+	cmd->ars_cap->length = len;
+
+	return cmd;
+}
+
+NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(struct ndctl_cmd *ars_cap,
+		int type)
+{
+	struct ndctl_bus *bus = ars_cap->bus;
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
+	struct ndctl_cmd *cmd;
+	size_t size;
+
+	if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_START)) {
+		dbg(ctx, "unsupported cmd\n");
+		return NULL;
+	}
+	if (ars_cap->status != 0) {
+		dbg(ctx, "expected sucessfully completed ars_cap command\n");
+		return NULL;
+	}
+	if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) {
+		dbg(ctx, "expected sucessfully completed ars_cap command\n");
+		return NULL;
+	}
+	if (!(*ars_cap->firmware_status >> ARS_EXT_STATUS_SHIFT & type)) {
+		dbg(ctx, "ars_cap does not show requested type as supported\n");
+		return NULL;
+	}
+
+	size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_start);
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->bus = bus;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_ARS_START;
+	cmd->size = size;
+	cmd->status = 1;
+	cmd->firmware_status = &cmd->ars_start->status;
+	cmd->ars_start->address = ars_cap->ars_cap->address;
+	cmd->ars_start->length = ars_cap->ars_cap->length;
+	cmd->ars_start->type = type;
+
+	return cmd;
+}
+
+NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_status(struct ndctl_cmd *ars_cap)
+{
+	struct nd_cmd_ars_cap *ars_cap_cmd = ars_cap->ars_cap;
+	struct ndctl_bus *bus = ars_cap->bus;
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
+	struct ndctl_cmd *cmd;
+	size_t size;
+
+	if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) {
+		dbg(ctx, "unsupported cmd\n");
+		return NULL;
+	}
+	if (ars_cap->status != 0) {
+		dbg(ctx, "expected sucessfully completed ars_cap command\n");
+		return NULL;
+	}
+	if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) {
+		dbg(ctx, "expected sucessfully completed ars_cap command\n");
+		return NULL;
+	}
+	if (ars_cap_cmd->max_ars_out == 0) {
+		dbg(ctx, "invalid ars_cap\n");
+		return NULL;
+	}
+
+	size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_status) +
+			ars_cap_cmd->max_ars_out;
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->bus = bus;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_ARS_STATUS;
+	cmd->size = size;
+	cmd->status = 1;
+	cmd->firmware_status = &cmd->ars_status->status;
+
+	return cmd;
+}
+
+NDCTL_EXPORT unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap)
+{
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_cap));
+
+	if (ars_cap->type == ND_CMD_ARS_CAP && ars_cap->status == 0) {
+		dbg(ctx, "max_ars_out: %d\n",
+			ars_cap->ars_cap->max_ars_out);
+		return ars_cap->ars_cap->max_ars_out;
+	}
+
+	dbg(ctx, "invalid ars_cap\n");
+	return 0;
+}
+
+NDCTL_EXPORT unsigned int ndctl_cmd_ars_in_progress(struct ndctl_cmd *ars_stat)
+{
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat));
+	int rc;
+
+	if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) {
+		rc = (((*ars_stat->firmware_status >> ARS_EXT_STATUS_SHIFT) == 1) ? 1 : 0);
+		/*
+		 * If in-progress, invalidate the ndctl_cmd, so that if we're
+		 * called again without a fresh ars_stat command, we fail.
+		 */
+		if (rc)
+			ars_stat->status = 1;
+		return rc;
+	}
+
+	dbg(ctx, "invalid ars_status\n");
+	return 0;
+}
+
+NDCTL_EXPORT unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat)
+{
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat));
+
+	if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0)
+		return ars_stat->ars_status->num_records;
+
+	dbg(ctx, "invalid ars_status\n");
+	return 0;
+}
+
+NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_addr(
+		struct ndctl_cmd *ars_stat, unsigned int rec_index)
+{
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat));
+
+	if (rec_index >= ars_stat->ars_status->num_records) {
+		dbg(ctx, "invalid record index\n");
+		return 0;
+	}
+
+	if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0)
+		return ars_stat->ars_status->records[rec_index].err_address;
+
+	dbg(ctx, "invalid ars_status\n");
+	return 0;
+}
+
+NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_len(
+		struct ndctl_cmd *ars_stat, unsigned int rec_index)
+{
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat));
+
+	if (rec_index >= ars_stat->ars_status->num_records) {
+		dbg(ctx, "invalid record index\n");
+		return 0;
+	}
+
+	if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0)
+		return ars_stat->ars_status->records[rec_index].length;
+
+	dbg(ctx, "invalid ars_status\n");
+	return 0;
+}
diff --git a/lib/libndctl-private.h b/lib/libndctl-private.h
index 78fbb2d46b42..d3cca9401b45 100644
--- a/lib/libndctl-private.h
+++ b/lib/libndctl-private.h
@@ -13,11 +13,14 @@
 #ifndef _LIBNDCTL_PRIVATE_H_
 #define _LIBNDCTL_PRIVATE_H_
 
+#include <errno.h>
 #include <stdbool.h>
 #include <syslog.h>
+#include <string.h>
 #include <libudev.h>
 #include <libkmod.h>
 #include <uuid/uuid.h>
+#include <ccan/array_size/array_size.h>
 #ifdef HAVE_NDCTL_H
 #include <linux/ndctl.h>
 #else
@@ -117,6 +120,63 @@ struct namespace_label {
 	le32 unused;
 };
 
+/**
+ * struct ndctl_cmd - device-specific-method (_DSM ioctl) container
+ * @dimm: set if the command is relative to a dimm, NULL otherwise
+ * @bus: set if the command is relative to a bus (like ARS), NULL otherwise
+ * @refcount: reference for passing command buffer around
+ * @type: cmd number
+ * @size: total size of the ndctl_cmd allocation
+ * @status: negative if failed, 0 if success, > 0 if never submitted
+ * @firmware_status: NFIT command output status code
+ * @iter: iterator for multi-xfer commands
+ * @source: source cmd of an inherited iter.total_buf
+ *
+ * For dynamically sized commands like 'get_config', 'set_config', or
+ * 'vendor', @size encompasses the entire buffer for the command input
+ * and response output data.
+ *
+ * A command may only specify one of @source, or @iter.total_buf, not both.
+ */
+enum {
+	READ, WRITE,
+};
+struct ndctl_cmd {
+	struct ndctl_dimm *dimm;
+	struct ndctl_bus *bus;
+	int refcount;
+	int type;
+	int size;
+	int status;
+	u32 *firmware_status;
+	struct ndctl_cmd_iter {
+		u32 *offset;
+		u8 *data; /* pointer to the data buffer location in cmd */
+		u32 max_xfer;
+		char *total_buf;
+		u32 total_xfer;
+		int dir;
+	} iter;
+	struct ndctl_cmd *source;
+	union {
+		struct nd_cmd_ars_cap ars_cap[0];
+		struct nd_cmd_ars_start ars_start[0];
+		struct nd_cmd_ars_status ars_status[0];
+		struct nd_cmd_get_config_size get_size[0];
+		struct nd_cmd_get_config_data_hdr get_data[0];
+		struct nd_cmd_set_config_hdr set_data[0];
+		struct nd_cmd_vendor_hdr vendor[0];
+		char cmd_buf[0];
+	};
+};
+
+static inline struct ndctl_bus *cmd_to_bus(struct ndctl_cmd *cmd)
+{
+	if (cmd->dimm)
+		return ndctl_dimm_get_bus(cmd->dimm);
+	return cmd->bus;
+}
+
 static inline void __attribute__((always_inline, format(printf, 2, 3)))
 ndctl_log_null(struct ndctl_ctx *ctx, const char *format, ...) {}
 
diff --git a/lib/libndctl.c b/lib/libndctl.c
index 7db5b6013ab1..6ebb4eb3d3cd 100644
--- a/lib/libndctl.c
+++ b/lib/libndctl.c
@@ -296,56 +296,6 @@ struct ndctl_ctx {
 	unsigned long timeout;
 };
 
-/**
- * struct ndctl_cmd - device-specific-method (_DSM ioctl) container
- * @dimm: set if the command is relative to a dimm, NULL otherwise
- * @bus: set if the command is relative to a bus (like ARS), NULL otherwise
- * @refcount: reference for passing command buffer around
- * @type: cmd number
- * @size: total size of the ndctl_cmd allocation
- * @status: negative if failed, 0 if success, > 0 if never submitted
- * @firmware_status: NFIT command output status code
- * @iter: iterator for multi-xfer commands
- * @source: source cmd of an inherited iter.total_buf
- *
- * For dynamically sized commands like 'get_config', 'set_config', or
- * 'vendor', @size encompasses the entire buffer for the command input
- * and response output data.
- *
- * A command may only specify one of @source, or @iter.total_buf, not both.
- */
-enum {
-	READ, WRITE,
-};
-struct ndctl_cmd {
-	struct ndctl_dimm *dimm;
-	struct ndctl_bus *bus;
-	int refcount;
-	int type;
-	int size;
-	int status;
-	u32 *firmware_status;
-	struct ndctl_cmd_iter {
-		u32 *offset;
-		u8 *data; /* pointer to the data buffer location in cmd */
-		u32 max_xfer;
-		char *total_buf;
-		u32 total_xfer;
-		int dir;
-	} iter;
-	struct ndctl_cmd *source;
-	union {
-		struct nd_cmd_ars_cap ars_cap[0];
-		struct nd_cmd_ars_start ars_start[0];
-		struct nd_cmd_ars_status ars_status[0];
-		struct nd_cmd_get_config_size get_size[0];
-		struct nd_cmd_get_config_data_hdr get_data[0];
-		struct nd_cmd_set_config_hdr set_data[0];
-		struct nd_cmd_vendor_hdr vendor[0];
-		char cmd_buf[0];
-	};
-};
-
 void ndctl_log(struct ndctl_ctx *ctx,
 		int priority, const char *file, int line, const char *fn,
 		const char *format, ...)
@@ -1668,13 +1618,6 @@ static const char *ndctl_device_type_name(int type)
 	}
 }
 
-static struct ndctl_bus *cmd_to_bus(struct ndctl_cmd *cmd)
-{
-	if (cmd->dimm)
-		return ndctl_dimm_get_bus(cmd->dimm);
-	return cmd->bus;
-}
-
 NDCTL_EXPORT const char *ndctl_region_get_type_name(struct ndctl_region *region)
 {
 	return ndctl_device_type_name(ndctl_region_get_type(region));
@@ -1720,198 +1663,6 @@ NDCTL_EXPORT struct ndctl_dimm *ndctl_region_get_next_dimm(struct ndctl_region *
 	return NULL;
 }
 
-NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus,
-		unsigned long long address, unsigned long long len)
-{
-	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
-	struct ndctl_cmd *cmd;
-	size_t size;
-
-	if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) {
-		dbg(ctx, "unsupported cmd\n");
-		return NULL;
-	}
-
-	size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_cap);
-	cmd = calloc(1, size);
-	if (!cmd)
-		return NULL;
-
-	cmd->bus = bus;
-	ndctl_cmd_ref(cmd);
-	cmd->type = ND_CMD_ARS_CAP;
-	cmd->size = size;
-	cmd->status = 1;
-	cmd->firmware_status = &cmd->ars_cap->status;
-	cmd->ars_cap->address = address;
-	cmd->ars_cap->length = len;
-
-	return cmd;
-}
-
-NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(struct ndctl_cmd *ars_cap,
-		int type)
-{
-	struct ndctl_bus *bus = ars_cap->bus;
-	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
-	struct ndctl_cmd *cmd;
-	size_t size;
-
-	if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_START)) {
-		dbg(ctx, "unsupported cmd\n");
-		return NULL;
-	}
-	if (ars_cap->status != 0) {
-		dbg(ctx, "expected sucessfully completed ars_cap command\n");
-		return NULL;
-	}
-	if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) {
-		dbg(ctx, "expected sucessfully completed ars_cap command\n");
-		return NULL;
-	}
-	if (!(*ars_cap->firmware_status >> ARS_EXT_STATUS_SHIFT & type)) {
-		dbg(ctx, "ars_cap does not show requested type as supported\n");
-		return NULL;
-	}
-
-	size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_start);
-	cmd = calloc(1, size);
-	if (!cmd)
-		return NULL;
-
-	cmd->bus = bus;
-	ndctl_cmd_ref(cmd);
-	cmd->type = ND_CMD_ARS_START;
-	cmd->size = size;
-	cmd->status = 1;
-	cmd->firmware_status = &cmd->ars_start->status;
-	cmd->ars_start->address = ars_cap->ars_cap->address;
-	cmd->ars_start->length = ars_cap->ars_cap->length;
-	cmd->ars_start->type = type;
-
-	return cmd;
-}
-
-NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_status(struct ndctl_cmd *ars_cap)
-{
-	struct nd_cmd_ars_cap *ars_cap_cmd = ars_cap->ars_cap;
-	struct ndctl_bus *bus = ars_cap->bus;
-	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
-	struct ndctl_cmd *cmd;
-	size_t size;
-
-	if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) {
-		dbg(ctx, "unsupported cmd\n");
-		return NULL;
-	}
-	if (ars_cap->status != 0) {
-		dbg(ctx, "expected sucessfully completed ars_cap command\n");
-		return NULL;
-	}
-	if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) {
-		dbg(ctx, "expected sucessfully completed ars_cap command\n");
-		return NULL;
-	}
-	if (ars_cap_cmd->max_ars_out == 0) {
-		dbg(ctx, "invalid ars_cap\n");
-		return NULL;
-	}
-
-	size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_status) +
-			ars_cap_cmd->max_ars_out;
-	cmd = calloc(1, size);
-	if (!cmd)
-		return NULL;
-
-	cmd->bus = bus;
-	ndctl_cmd_ref(cmd);
-	cmd->type = ND_CMD_ARS_STATUS;
-	cmd->size = size;
-	cmd->status = 1;
-	cmd->firmware_status = &cmd->ars_status->status;
-
-	return cmd;
-}
-
-NDCTL_EXPORT unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap)
-{
-	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_cap));
-
-	if (ars_cap->type == ND_CMD_ARS_CAP && ars_cap->status == 0) {
-		dbg(ctx, "max_ars_out: %d\n",
-			ars_cap->ars_cap->max_ars_out);
-		return ars_cap->ars_cap->max_ars_out;
-	}
-
-	dbg(ctx, "invalid ars_cap\n");
-	return 0;
-}
-
-NDCTL_EXPORT unsigned int ndctl_cmd_ars_in_progress(struct ndctl_cmd *ars_stat)
-{
-	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat));
-	int rc;
-
-	if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) {
-		rc = (((*ars_stat->firmware_status >> ARS_EXT_STATUS_SHIFT) == 1) ? 1 : 0);
-		/*
-		 * If in-progress, invalidate the ndctl_cmd, so that if we're
-		 * called again without a fresh ars_stat command, we fail.
-		 */
-		if (rc)
-			ars_stat->status = 1;
-		return rc;
-	}
-
-	dbg(ctx, "invalid ars_status\n");
-	return 0;
-}
-
-NDCTL_EXPORT unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat)
-{
-	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat));
-
-	if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0)
-		return ars_stat->ars_status->num_records;
-
-	dbg(ctx, "invalid ars_status\n");
-	return 0;
-}
-
-NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_addr(
-		struct ndctl_cmd *ars_stat, unsigned int rec_index)
-{
-	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat));
-
-	if (rec_index >= ars_stat->ars_status->num_records) {
-		dbg(ctx, "invalid record index\n");
-		return 0;
-	}
-
-	if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0)
-		return ars_stat->ars_status->records[rec_index].err_address;
-
-	dbg(ctx, "invalid ars_status\n");
-	return 0;
-}
-
-NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_len(
-		struct ndctl_cmd *ars_stat, unsigned int rec_index)
-{
-	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat));
-
-	if (rec_index >= ars_stat->ars_status->num_records) {
-		dbg(ctx, "invalid record index\n");
-		return 0;
-	}
-
-	if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0)
-		return ars_stat->ars_status->records[rec_index].length;
-
-	dbg(ctx, "invalid ars_status\n");
-	return 0;
-}
-
 static struct nd_cmd_vendor_tail *to_vendor_tail(struct ndctl_cmd *cmd)
 {
 	struct nd_cmd_vendor_tail *tail = (struct nd_cmd_vendor_tail *)
diff --git a/lib/ndctl/libndctl.h b/lib/ndctl/libndctl.h
index 31118c646520..cec036fc3386 100644
--- a/lib/ndctl/libndctl.h
+++ b/lib/ndctl/libndctl.h
@@ -149,6 +149,7 @@ int ndctl_dimm_disable(struct ndctl_dimm *dimm);
 int ndctl_dimm_enable(struct ndctl_dimm *dimm);
 
 struct ndctl_cmd;
+#ifdef HAVE_NDCTL_ARS
 struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus,
 		unsigned long long address, unsigned long long len);
 struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(struct ndctl_cmd *ars_cap, int type);
@@ -160,6 +161,54 @@ unsigned long long ndctl_cmd_ars_get_record_addr(struct ndctl_cmd *ars_stat,
 		unsigned int rec_index);
 unsigned long long ndctl_cmd_ars_get_record_len(struct ndctl_cmd *ars_stat,
 		unsigned int rec_index);
+
+#else
+static inline struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus,
+		unsigned long long address, unsigned long long len)
+{
+	return NULL;
+}
+
+static inline struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(
+		struct ndctl_cmd *ars_cap, int type)
+{
+	return NULL;
+}
+
+static inline struct ndctl_cmd *ndctl_bus_cmd_new_ars_status(
+		struct ndctl_cmd *ars_cap)
+{
+	return NULL;
+}
+
+static inline unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap)
+{
+	return 0;
+}
+
+static inline unsigned int ndctl_cmd_ars_in_progress(struct ndctl_cmd *ars_status)
+{
+	return 0;
+}
+
+static inline unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat)
+{
+	return 0;
+}
+
+static inline unsigned long long ndctl_cmd_ars_get_record_addr(
+		struct ndctl_cmd *ars_stat, unsigned int rec_index)
+{
+	return 0;
+}
+
+static inline unsigned long long ndctl_cmd_ars_get_record_len(
+		struct ndctl_cmd *ars_stat, unsigned int rec_index)
+{
+	return 0;
+}
+#endif
+
 struct ndctl_cmd *ndctl_dimm_cmd_new_vendor_specific(struct ndctl_dimm *dimm,
 		unsigned int opcode, size_t input_size, size_t output_size);
 ssize_t ndctl_cmd_vendor_set_input(struct ndctl_cmd *cmd, void *buf,
diff --git a/lib/test-libndctl.c b/lib/test-libndctl.c
index b0221335606e..5f03ba594815 100644
--- a/lib/test-libndctl.c
+++ b/lib/test-libndctl.c
@@ -1249,6 +1249,7 @@ static int check_set_config_data(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
 	return 0;
 }
 
+#ifdef HAVE_NDCTL_ARS
 static int check_ars_cap(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
 		struct check_cmd *check)
 {
@@ -1360,6 +1361,28 @@ static int check_ars_status(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
 	check->cmd = cmd;
 	return 0;
 }
+#else
+static int check_ars_cap(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
+		struct check_cmd *check)
+{
+	fprintf(stderr, "%s: HAVE_NDCTL_ARS disabled, skipping\n", __func__);
+	return 0;
+}
+
+static int check_ars_start(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
+		struct check_cmd *check)
+{
+	fprintf(stderr, "%s: HAVE_NDCTL_ARS disabled, skipping\n", __func__);
+	return 0;
+}
+
+static int check_ars_status(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
+		struct check_cmd *check)
+{
+	fprintf(stderr, "%s: HAVE_NDCTL_ARS disabled, skipping\n", __func__);
+	return 0;
+}
+#endif
 
 #define BITS_PER_LONG 32
 static int check_commands(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
