nvdimm.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* [ndctl PATCH 0/8] add an inject-error command to ndctl
@ 2017-10-06  1:53 Vishal Verma
  2017-10-06  1:53 ` [ndctl PATCH 1/8] libndctl: fix a memory leak in add_bus Vishal Verma
                   ` (7 more replies)
  0 siblings, 8 replies; 15+ messages in thread
From: Vishal Verma @ 2017-10-06  1:53 UTC (permalink / raw)
  To: linux-nvdimm

These patches add a new command to ndctl for error injection. They are
implemented such that the interface provided to a user is consistent
with the kernel - i.e. all media errors are expected/displayed in terms
of 512 byte sectors. The underlying ACPI DSMs need and provide byte
relative offsets/lengths, but these are converted to 512B sectors for
consistency.

These also update unit tests to use the new error injection commands,
and add two new unit tests - first, to test the error injection commands
themselves, and second, to test BTT error clearing.

Vishal Verma (8):
  libndctl: fix a memory leak in add_bus
  ndctl, list: move the --human description to an include
  libndctl: add APIs to get scrub count and to wait for a scrub
  ccan/list: add a list_add_after helper
  ndctl: add an inject-error command
  ndctl/test: add a new unit test for inject-error
  ndctl/test: update existing unit tests to use error-inject
  ndctl/test: add a new unit test for BTT error clearing

 Documentation/ndctl/Makefile.am            |   1 +
 Documentation/ndctl/human-option.txt       |   5 +
 Documentation/ndctl/ndctl-inject-error.txt | 108 +++++
 Documentation/ndctl/ndctl-list.txt         |   8 +-
 Documentation/ndctl/ndctl.txt              |   1 +
 builtin.h                                  |   1 +
 ccan/list/list.h                           |  32 ++
 contrib/ndctl                              |   5 +-
 ndctl/Makefile.am                          |   3 +-
 ndctl/inject-error.c                       | 745 +++++++++++++++++++++++++++++
 ndctl/lib/libndctl.c                       |  86 ++++
 ndctl/lib/libndctl.sym                     |   2 +
 ndctl/lib/private.h                        |   1 +
 ndctl/libndctl-nfit.h                      |   8 +
 ndctl/libndctl.h.in                        |   2 +
 ndctl/ndctl.c                              |   1 +
 test/Makefile.am                           |   4 +-
 test/btt-errors.sh                         | 152 ++++++
 test/clear.sh                              |   5 +-
 test/dax-errors.sh                         |   5 +-
 test/daxdev-errors.sh                      |  17 +-
 test/inject-error.sh                       |  89 ++++
 util/json.c                                |  26 +
 util/json.h                                |   3 +
 util/size.h                                |   1 +
 25 files changed, 1297 insertions(+), 14 deletions(-)
 create mode 100644 Documentation/ndctl/human-option.txt
 create mode 100644 Documentation/ndctl/ndctl-inject-error.txt
 create mode 100644 ndctl/inject-error.c
 create mode 100755 test/btt-errors.sh
 create mode 100755 test/inject-error.sh

-- 
2.9.5

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [ndctl PATCH 1/8] libndctl: fix a memory leak in add_bus
  2017-10-06  1:53 [ndctl PATCH 0/8] add an inject-error command to ndctl Vishal Verma
@ 2017-10-06  1:53 ` Vishal Verma
  2017-10-07 15:44   ` Dan Williams
  2017-10-06  1:53 ` [ndctl PATCH 2/8] ndctl, list: move the --human description to an include Vishal Verma
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Vishal Verma @ 2017-10-06  1:53 UTC (permalink / raw)
  To: linux-nvdimm

We allocated a buffer for wait_probe_path via strdup, but failed to free
it in the error path.

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
 ndctl/lib/libndctl.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
index 027842b..60143d9 100644
--- a/ndctl/lib/libndctl.c
+++ b/ndctl/lib/libndctl.c
@@ -809,6 +809,7 @@ static void *add_bus(void *parent, int id, const char *ctl_base)
 
  err_dev_path:
  err_read:
+	free(bus->wait_probe_path);
 	free(bus->provider);
 	free(bus->bus_buf);
 	free(bus);
-- 
2.9.5

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [ndctl PATCH 2/8] ndctl, list: move the --human description to an include
  2017-10-06  1:53 [ndctl PATCH 0/8] add an inject-error command to ndctl Vishal Verma
  2017-10-06  1:53 ` [ndctl PATCH 1/8] libndctl: fix a memory leak in add_bus Vishal Verma
@ 2017-10-06  1:53 ` Vishal Verma
  2017-10-07 15:55   ` Dan Williams
  2017-10-06  1:54 ` [ndctl PATCH 3/8] libndctl: add APIs to get scrub count and to wait for a scrub Vishal Verma
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Vishal Verma @ 2017-10-06  1:53 UTC (permalink / raw)
  To: linux-nvdimm

In preparation for inject-error, move the --human option description
to a file that can be included. Reword the description improving
the grammar, and making it more generic.

The example in the list man page for --human was also missing a [verse]
keyword, making the --human part of the example appear in a single line
rather than formatted json - fix that.

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
 Documentation/ndctl/human-option.txt | 5 +++++
 Documentation/ndctl/ndctl-list.txt   | 8 ++------
 2 files changed, 7 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/ndctl/human-option.txt

diff --git a/Documentation/ndctl/human-option.txt b/Documentation/ndctl/human-option.txt
new file mode 100644
index 0000000..ce2fefa
--- /dev/null
+++ b/Documentation/ndctl/human-option.txt
@@ -0,0 +1,5 @@
+-u::
+--human::
+	Format numbers representing storage sizes, or offsets as human
+	readable strings with units instead of the default machine-friendly
+	raw-integer data. Convert other numeric fields into hexadecimal strings.
diff --git a/Documentation/ndctl/ndctl-list.txt b/Documentation/ndctl/ndctl-list.txt
index cdcf238..e19ded8 100644
--- a/Documentation/ndctl/ndctl-list.txt
+++ b/Documentation/ndctl/ndctl-list.txt
@@ -180,12 +180,7 @@ include::xable-region-options.txt[]
   ]
 }
 
--u::
---human::
-	By default 'ndctl list' will output machine-friendly raw-integer
-	data. Instead, with this flag, numbers representing storage size
-	will be formatted as human readable strings with units, other
-	fields are converted to hexadecimal strings.  Example:
+include::human-option.txt[]
 
 [verse]
 # ndctl list --region=7
@@ -198,6 +193,7 @@ include::xable-region-options.txt[]
   "badblock_count":8
 }
 
+[verse]
 # ndctl list --human --region=7
 {
   "dev":"region7",
-- 
2.9.5

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [ndctl PATCH 3/8] libndctl: add APIs to get scrub count and to wait for a scrub
  2017-10-06  1:53 [ndctl PATCH 0/8] add an inject-error command to ndctl Vishal Verma
  2017-10-06  1:53 ` [ndctl PATCH 1/8] libndctl: fix a memory leak in add_bus Vishal Verma
  2017-10-06  1:53 ` [ndctl PATCH 2/8] ndctl, list: move the --human description to an include Vishal Verma
@ 2017-10-06  1:54 ` Vishal Verma
  2017-10-07 16:11   ` Dan Williams
  2017-10-06  1:54 ` [ndctl PATCH 4/8] ccan/list: add a list_add_after helper Vishal Verma
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Vishal Verma @ 2017-10-06  1:54 UTC (permalink / raw)
  To: linux-nvdimm

The kernel uses sysfs to notify userspace of the number of completed
Address Range Scrubs, as well as any ongoing scrubs. Add libndctl
helpers to get the scrub count, and to wait for an in-progress scrub to
complete.

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
 ndctl/lib/libndctl.c   | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++
 ndctl/lib/libndctl.sym |  2 ++
 ndctl/lib/private.h    |  1 +
 ndctl/libndctl.h.in    |  2 ++
 4 files changed, 90 insertions(+)

diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
index 60143d9..3943978 100644
--- a/ndctl/lib/libndctl.c
+++ b/ndctl/lib/libndctl.c
@@ -555,6 +555,7 @@ static void free_bus(struct ndctl_bus *bus, struct list_head *head)
 	free(bus->bus_path);
 	free(bus->bus_buf);
 	free(bus->wait_probe_path);
+	free(bus->scrub_path);
 	free(bus);
 }
 
@@ -785,6 +786,11 @@ static void *add_bus(void *parent, int id, const char *ctl_base)
 	if (!bus->wait_probe_path)
 		goto err_read;
 
+	sprintf(path, "%s/device/nfit/scrub", ctl_base);
+	bus->scrub_path = strdup(path);
+	if (!bus->scrub_path)
+		goto err_read;
+
 	bus->bus_path = parent_dev_path("char", bus->major, bus->minor);
 	if (!bus->bus_path)
 		goto err_dev_path;
@@ -810,6 +816,7 @@ static void *add_bus(void *parent, int id, const char *ctl_base)
  err_dev_path:
  err_read:
 	free(bus->wait_probe_path);
+	free(bus->scrub_path);
 	free(bus->provider);
 	free(bus->bus_buf);
 	free(bus);
@@ -1082,6 +1089,84 @@ NDCTL_EXPORT int ndctl_bus_wait_probe(struct ndctl_bus *bus)
 	return rc < 0 ? -ENXIO : 0;
 }
 
+NDCTL_EXPORT unsigned int ndctl_bus_get_scrub_count(struct ndctl_bus *bus)
+{
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
+	char buf[SYSFS_ATTR_SIZE];
+	unsigned int scrub_count;
+	char in_progress = '\0';
+	int rc;
+
+	rc = sysfs_read_attr(ctx, bus->scrub_path, buf);
+	if (rc < 0)
+		return UINT_MAX;
+
+	rc = sscanf(buf, "%u%c", &scrub_count, &in_progress);
+	if (rc < 0)
+		return UINT_MAX;
+	if (rc == 0) {
+		/* unable to read scrub count */
+		return UINT_MAX;
+	}
+	if (rc >= 1)
+		return scrub_count;
+
+	return UINT_MAX;
+}
+
+/**
+ * ndctl_bus_wait_for_scrub - wait for a scrub to complete
+ * @bus: bus for which to check whether a scrub is in progress
+ *
+ * Upon return this bus has completed any in-progress scrubs. This is
+ * different from ndctl_cmd_ars_in_progress in that the latter checks
+ * the output of an ars_status command to see if the in-progress flag
+ * is set, i.e. provides the firmware's view of whether a scrub is in
+ * progress. ndctl_bus_wait_for_scrub instead checks the kernel's view
+ * of whether a scrub is in progress by looking at the 'scrub' file in
+ * sysfs.
+ */
+NDCTL_EXPORT int ndctl_bus_wait_for_scrub_completion(struct ndctl_bus *bus)
+{
+	struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
+	unsigned int tmo = 120, scrub_count;
+	char buf[SYSFS_ATTR_SIZE];
+	char in_progress = '\0';
+	int rc, elapsed = 0;
+
+	do {
+		rc = sysfs_read_attr(ctx, bus->scrub_path, buf);
+		if (rc < 0)
+			break;
+
+		rc = sscanf(buf, "%u%c", &scrub_count, &in_progress);
+		if (rc < 0)
+			break;
+		else if (rc <= 1) {
+			/* scrub complete, break successfully */
+			rc = 0;
+			break;
+		} else if (rc == 2 && in_progress == '+') {
+			/* scrub in progress, continue to wait */
+			;
+		} else {
+			/* unknown condition */
+			rc = -ENXIO;
+			break;
+		}
+
+		elapsed++;
+		sleep(1);
+	} while (tmo-- != 0);
+
+	if (elapsed)
+		dbg(ctx, "waited %d second%s for bus%d scrub to complete\n",
+			elapsed, elapsed == 1 ? "" : "s",
+			ndctl_bus_get_id(bus));
+
+	return rc < 0 ? -ENXIO : 0;
+}
+
 static int ndctl_bind(struct ndctl_ctx *ctx, struct kmod_module *module,
 		const char *devname);
 static int ndctl_unbind(struct ndctl_ctx *ctx, const char *devpath);
diff --git a/ndctl/lib/libndctl.sym b/ndctl/lib/libndctl.sym
index 58db669..4c22c0f 100644
--- a/ndctl/lib/libndctl.sym
+++ b/ndctl/lib/libndctl.sym
@@ -286,6 +286,8 @@ global:
 	ndctl_bus_get_region_by_physical_address;
 	ndctl_bus_get_dimm_by_physical_address;
 	ndctl_bus_is_nfit_cmd_supported;
+	ndctl_bus_wait_for_scrub_completion;
+	ndctl_bus_get_scrub_count;
 	ndctl_dimm_read_labels;
 	ndctl_dimm_validate_labels;
 	ndctl_dimm_init_labels;
diff --git a/ndctl/lib/private.h b/ndctl/lib/private.h
index 2de314c..6b909aa 100644
--- a/ndctl/lib/private.h
+++ b/ndctl/lib/private.h
@@ -152,6 +152,7 @@ struct ndctl_bus {
 	char *bus_buf;
 	size_t buf_len;
 	char *wait_probe_path;
+	char *scrub_path;
 	unsigned long dsm_mask;
 	unsigned long nfit_dsm_mask;
 };
diff --git a/ndctl/libndctl.h.in b/ndctl/libndctl.h.in
index 42e53c6..a4fbe33 100644
--- a/ndctl/libndctl.h.in
+++ b/ndctl/libndctl.h.in
@@ -113,6 +113,8 @@ unsigned int ndctl_bus_get_revision(struct ndctl_bus *bus);
 unsigned int ndctl_bus_get_id(struct ndctl_bus *bus);
 const char *ndctl_bus_get_provider(struct ndctl_bus *bus);
 int ndctl_bus_wait_probe(struct ndctl_bus *bus);
+int ndctl_bus_wait_for_scrub_completion(struct ndctl_bus *bus);
+unsigned int ndctl_bus_get_scrub_count(struct ndctl_bus *bus);
 
 struct ndctl_dimm;
 struct ndctl_dimm *ndctl_dimm_get_first(struct ndctl_bus *bus);
-- 
2.9.5

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [ndctl PATCH 4/8] ccan/list: add a list_add_after helper
  2017-10-06  1:53 [ndctl PATCH 0/8] add an inject-error command to ndctl Vishal Verma
                   ` (2 preceding siblings ...)
  2017-10-06  1:54 ` [ndctl PATCH 3/8] libndctl: add APIs to get scrub count and to wait for a scrub Vishal Verma
@ 2017-10-06  1:54 ` Vishal Verma
  2017-10-06 17:39   ` Dan Williams
  2017-10-06  1:54 ` [ndctl PATCH 5/8] ndctl: add an inject-error command Vishal Verma
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Vishal Verma @ 2017-10-06  1:54 UTC (permalink / raw)
  To: linux-nvdimm

In preparation for the error-inject command, add anew helper to
ccan/list for adding a list element in the middle of a list.

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
 ccan/list/list.h | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/ccan/list/list.h b/ccan/list/list.h
index 4d1d34e..e6ecfa3 100644
--- a/ccan/list/list.h
+++ b/ccan/list/list.h
@@ -193,6 +193,38 @@ static inline void list_add_tail_(struct list_head *h,
 }
 
 /**
+ * list_add_after - add an entry after the given node in the linked list.
+ * @h: the list_head to add the node to
+ * @l: the list_node after which to add to
+ * @n: the list_node to add to the list.
+ *
+ * The list_node does not need to be initialized; it will be overwritten.
+ * Example:
+ *	struct child *child = malloc(sizeof(*child));
+ *
+ *	child->name = "geoffrey";
+ *	list_add_after(&parent->children, &child1->list, &child->list);
+ *	parent->num_children++;
+ */
+#define list_add_after(h, l, n) list_add_after_(h, l, n, LIST_LOC)
+static inline void list_add_after_(struct list_head *h,
+				   struct list_node *l,
+				   struct list_node *n,
+				   const char *abortstr)
+{
+	if (l->next == &h->n) {
+		/* l is the last element, this becomes a list_add_tail */
+		list_add_tail(h, n);
+		return;
+	}
+	n->next = l->next;
+	n->prev = l;
+	l->next->prev = n;
+	l->next = n;
+	(void)list_debug(h, abortstr);
+}
+
+/**
  * list_empty - is a list empty?
  * @h: the list_head
  *
-- 
2.9.5

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [ndctl PATCH 5/8] ndctl: add an inject-error command
  2017-10-06  1:53 [ndctl PATCH 0/8] add an inject-error command to ndctl Vishal Verma
                   ` (3 preceding siblings ...)
  2017-10-06  1:54 ` [ndctl PATCH 4/8] ccan/list: add a list_add_after helper Vishal Verma
@ 2017-10-06  1:54 ` Vishal Verma
  2017-10-09 16:27   ` Dan Williams
  2017-10-06  1:54 ` [ndctl PATCH 6/8] ndctl/test: add a new unit test for inject-error Vishal Verma
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Vishal Verma @ 2017-10-06  1:54 UTC (permalink / raw)
  To: linux-nvdimm

Add an inject-error command to ndctl. This uses the error injection DSMs
in ACPI6.2 to provide a generic error injection and management
interface. Once can inject errors, and view as well as clear injected
errors using these commands.

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
 Documentation/ndctl/Makefile.am            |   1 +
 Documentation/ndctl/ndctl-inject-error.txt | 108 +++++
 Documentation/ndctl/ndctl.txt              |   1 +
 builtin.h                                  |   1 +
 contrib/ndctl                              |   5 +-
 ndctl/Makefile.am                          |   3 +-
 ndctl/inject-error.c                       | 745 +++++++++++++++++++++++++++++
 ndctl/libndctl-nfit.h                      |   8 +
 ndctl/ndctl.c                              |   1 +
 util/json.c                                |  26 +
 util/json.h                                |   3 +
 util/size.h                                |   1 +
 12 files changed, 901 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ndctl/ndctl-inject-error.txt
 create mode 100644 ndctl/inject-error.c

diff --git a/Documentation/ndctl/Makefile.am b/Documentation/ndctl/Makefile.am
index 229d908..615baf0 100644
--- a/Documentation/ndctl/Makefile.am
+++ b/Documentation/ndctl/Makefile.am
@@ -30,6 +30,7 @@ man1_MANS = \
 	ndctl-create-namespace.1 \
 	ndctl-destroy-namespace.1 \
 	ndctl-check-namespace.1 \
+	ndctl-inject-error.1 \
 	ndctl-list.1
 
 CLEANFILES = $(man1_MANS)
diff --git a/Documentation/ndctl/ndctl-inject-error.txt b/Documentation/ndctl/ndctl-inject-error.txt
new file mode 100644
index 0000000..bd9e197
--- /dev/null
+++ b/Documentation/ndctl/ndctl-inject-error.txt
@@ -0,0 +1,108 @@
+ndctl-inject-error(1)
+=====================
+
+NAME
+----
+ndctl-inject-error - inject media errors at a namespace offset
+
+SYNOPSIS
+--------
+[verse]
+'ndctl inject-error' <namespace> [<options>]
+
+include::namespace-description.txt[]
+
+ndctl-inject-error can be used to ask the platform to simulate media errors
+in the nvdimm address space to aid debugging and development of features
+related to error handling.
+
+WARNING: These commands are DANGEROUS and can cause data loss. They are
+only provided for testing and debugging purposes.
+
+EXAMPLES
+--------
+
+Inject errors in namespace0.0 at sector 12 for a 2 sectors (i.e. 12, 13)
+[verse]
+ndctl inject-error --sector=12 --count=2 namespace0.0
+
+Check status of injected errors on namespace0.0
+[verse]
+ndctl inject-error --status namespacce0.0
+
+Clear the injected errors at sector 12 for 2 sectors on namespace0.0
+[verse]
+ndctl inject-error --clear --sector=12 --count=2 namespacce0.0
+
+OPTIONS
+-------
+-S::
+--sector=::
+	Namespace sector offset in 512 byte sized sectors where the error is
+	to be injected.
+
+	NOTE: The offset is interpreted in different ways based on the "mode"
+	of the namespace. For "raw" mode, the offset is the base namespace
+	offset. For "memory" mode (i.e. a "pfn" namespace), the offset is
+	relative to the user-visible part of the namespace, and the offset
+	introduced by the kernel's metadata will be accounted for. For a
+	"sector" mode namespace (i.e. a "BTT" namespace), the offset is
+	relative to the base namespace, as the BTT translation details are
+	internal to the kernel, and can't be accounted for while injecting
+	errors.
+
+-c::
+--count=::
+	Number of sectors to inject as errors. This is also in terms of fixed,
+	512 byte sectors.
+
+-d::
+--clear::
+	This option will ask the platform to clear any injected errors for the
+	specified sector offset, and count.
+
+	WARNING: This will not clear the kernel's internal "badrange" and
+	"badblock" tracking - those can only be cleared by doing a write to
+	the affected locations. Hence use the --clear option only if you know
+	exactly what you are doing. For normal usage, injected errors should
+	only be cleared by doing writes. Do not expect have the original data
+	intact after injecting an error, and clearing it using --clear - it
+	will be lost, as the only "real" way to clear the error location is
+	to write to it or zero it (truncate/hole-punch).
+
+-t::
+--status::
+	This option will retrieve the status of injected errors. Note that
+	this will not retrieve all known/latent errors (i.e. non injected
+	ones), and is NOT equivalent to performing an Address Range Scrub.
+
+-N::
+--no-notify::
+	This option is only valid when injecting errors. By default, the error
+	inject command and will ask platform firmware to trigger a notification
+	in the kernel, asking it to update its state of known errors.
+	With this option, the error will still be injected, the kernel will not
+	get a notification, and the error will appear as a latent media error
+	when the location is accessed. If the platform firmware does not
+	support this feature, this will have no effect.
+
+-v::
+--verbose::
+	Emit debug messages for the error injection process
+
+include::human-option.txt[]
+
+-r::
+--region=::
+include::xable-region-options.txt[]
+
+COPYRIGHT
+---------
+Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
+version 2 <http://gnu.org/licenses/gpl.html>.  This is free software:
+you are free to change and redistribute it.  There is NO WARRANTY, to
+the extent permitted by law.
+
+SEE ALSO
+--------
+linkndctl:ndctl-list[1],
diff --git a/Documentation/ndctl/ndctl.txt b/Documentation/ndctl/ndctl.txt
index b02f613..b2e2ab9 100644
--- a/Documentation/ndctl/ndctl.txt
+++ b/Documentation/ndctl/ndctl.txt
@@ -50,6 +50,7 @@ linkndctl:ndctl-enable-namespace[1],
 linkndctl:ndctl-disable-namespace[1],
 linkndctl:ndctl-zero-labels[1],
 linkndctl:ndctl-read-labels[1],
+linkndctl:ndctl-inject-error[1],
 linkndctl:ndctl-list[1],
 https://www.kernel.org/doc/Documentation/nvdimm/nvdimm.txt[LIBNVDIMM
 Overview],
diff --git a/builtin.h b/builtin.h
index 5c8b611..5e1b7ef 100644
--- a/builtin.h
+++ b/builtin.h
@@ -35,6 +35,7 @@ int cmd_read_labels(int argc, const char **argv, void *ctx);
 int cmd_write_labels(int argc, const char **argv, void *ctx);
 int cmd_init_labels(int argc, const char **argv, void *ctx);
 int cmd_check_labels(int argc, const char **argv, void *ctx);
+int cmd_inject_error(int argc, const char **argv, void *ctx);
 int cmd_list(int argc, const char **argv, void *ctx);
 #ifdef ENABLE_TEST
 int cmd_test(int argc, const char **argv, void *ctx);
diff --git a/contrib/ndctl b/contrib/ndctl
index c7d1b67..8745fb5 100755
--- a/contrib/ndctl
+++ b/contrib/ndctl
@@ -91,7 +91,7 @@ __ndctlcomp()
 
 	COMPREPLY=( $( compgen -W "$1" -- "$2" ) )
 	for cword in "${COMPREPLY[@]}"; do
-		if [[ "$cword" == @(--bus|--region|--type|--mode|--size|--dimm|--reconfig|--uuid|--name|--sector-size|--map|--namespace|--input|--output|--label-version|--align) ]]; then
+		if [[ "$cword" == @(--bus|--region|--type|--mode|--size|--dimm|--reconfig|--uuid|--name|--sector-size|--map|--namespace|--input|--output|--label-version|--align|--sector|--count) ]]; then
 			COMPREPLY[$i]="${cword}="
 		else
 			COMPREPLY[$i]="${cword} "
@@ -257,6 +257,9 @@ __ndctl_comp_non_option_args()
 	zero-labels)
 		opts="$(__ndctl_get_dimms -i) all"
 		;;
+	inject-error)
+		opts="$(__ndctl_get_ns -i)"
+		;;
 	*)
 		return
 		;;
diff --git a/ndctl/Makefile.am b/ndctl/Makefile.am
index d346c04..a0cf500 100644
--- a/ndctl/Makefile.am
+++ b/ndctl/Makefile.am
@@ -11,7 +11,8 @@ ndctl_SOURCES = ndctl.c \
 		 ../util/log.c \
 		list.c \
 		test.c \
-		../util/json.c
+		../util/json.c \
+		inject-error.c
 
 if ENABLE_SMART
 ndctl_SOURCES += util/json-smart.c
diff --git a/ndctl/inject-error.c b/ndctl/inject-error.c
new file mode 100644
index 0000000..a6bcc1b
--- /dev/null
+++ b/ndctl/inject-error.c
@@ -0,0 +1,745 @@
+/*
+ * Copyright(c) 2015-2017 Intel Corporation. All rights reserved.
+ *
+ * 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 <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <libkmod.h>
+#include <stdbool.h>
+#include <linux/fs.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <linux/fiemap.h>
+
+#include <util/log.h>
+#include <util/size.h>
+#include <util/json.h>
+#include <util/sysfs.h>
+#include <json-c/json.h>
+#include <util/filter.h>
+#include <ndctl/libndctl.h>
+#include <ccan/list/list.h>
+#include <util/parse-options.h>
+#include <ndctl/libndctl-nfit.h>
+#include <ccan/array_size/array_size.h>
+#include <ccan/short_types/short_types.h>
+#ifdef HAVE_NDCTL_H
+#include <linux/ndctl.h>
+#else
+#include <ndctl.h>
+#endif
+
+#include "private.h"
+#include <builtin.h>
+#include <test.h>
+
+static bool verbose;
+static struct parameters {
+	const char *bus;
+	const char *region;
+	const char *namespace;
+	const char *sector;
+	const char *count;
+	bool clear;
+	bool status;
+	bool notify;
+	bool human;
+} param;
+
+static struct inject_ctx {
+	u64 sector;
+	u64 count;
+	u64 off_bytes;
+	u64 len_bytes;
+	u64 options;
+	unsigned int op_mask;
+	unsigned long flags;
+	struct list_head bb_list;
+} ictx;
+
+#define BASE_OPTIONS() \
+OPT_STRING('b', "bus", &param.bus, "bus-id", \
+	"limit namespace to a bus with an id or provider of <bus-id>"), \
+OPT_STRING('r', "region", &param.region, "region-id", \
+	"limit namespace to a region with an id or name of <region-id>"), \
+OPT_BOOLEAN('v', "verbose", &verbose, "emit extra debug messages to stderr")
+
+#define INJECT_OPTIONS() \
+OPT_STRING('S', "sector", &param.sector, "namespace sector offset", \
+	"specify the sector at which to inject the error"), \
+OPT_STRING('c', "count", &param.count, "count", \
+	"specify the number of sectors of errors to inject"), \
+OPT_BOOLEAN('d', "clear", &param.clear, \
+	"send the ARS error inject clear DSM"), \
+OPT_BOOLEAN('t', "status", &param.status, "get error injection status"), \
+OPT_BOOLEAN('N', "no-notify", &param.notify, "firmware should not notify OS"), \
+OPT_BOOLEAN('u', "human", &param.human, "use human friendly number formats ")
+
+static const struct option inject_options[] = {
+	BASE_OPTIONS(),
+	INJECT_OPTIONS(),
+	OPT_END(),
+};
+
+enum {
+	OP_INJECT = 0,
+	OP_CLEAR,
+	OP_STATUS,
+};
+
+struct bb {
+	u64 sector;
+	u64 count;
+	struct list_node list;
+};
+
+static int inject_init(void)
+{
+	if (!param.clear && !param.status) {
+		ictx.op_mask |= 1 << OP_INJECT;
+		ictx.options |= 1 << ND_ARS_ERR_INJ_OPT_NOTIFY;
+		if (param.notify)
+			ictx.options &= ~(1 << ND_ARS_ERR_INJ_OPT_NOTIFY);
+	}
+	if (param.clear) {
+		if (param.status) {
+			error("status is invalid with clear or inject\n");
+			return -EINVAL;
+		}
+		ictx.op_mask |= 1 << OP_CLEAR;
+	}
+	if (param.status) {
+		if (param.sector || param.count) {
+			error("status is invalid with clear or inject\n");
+			return -EINVAL;
+		}
+		ictx.op_mask |= 1 << OP_STATUS;
+	}
+
+	if (ictx.op_mask == 0) {
+		error("Unable to determine operation\n");
+		return -EINVAL;
+	}
+	ictx.op_mask &= (
+		(1 << OP_INJECT) |
+		(1 << OP_CLEAR) |
+		(1 << OP_STATUS));
+
+	if (param.sector) {
+		ictx.sector = parse_size64(param.sector);
+		if (ictx.sector == ULLONG_MAX) {
+			error("Invalid sector: %s\n", param.sector);
+			return -EINVAL;
+		}
+		ictx.off_bytes = ictx.sector * 512;
+	}
+	if (param.count) {
+		ictx.count = parse_size64(param.count);
+		if (ictx.count == ULLONG_MAX) {
+			error("Invalid count: %s\n", param.count);
+			return -EINVAL;
+		}
+		ictx.len_bytes = ictx.count * 512;
+	}
+
+	/* For inject or clear, an sector and count are required */
+	if (ictx.op_mask & ((1 << OP_INJECT) | (1 << OP_CLEAR))) {
+		if (!param.sector || !param.count) {
+			error("sector and count required for inject/clear\n");
+			return -EINVAL;
+		}
+	}
+
+	if (param.human)
+		ictx.flags |= UTIL_JSON_HUMAN;
+
+	list_head_init(&ictx.bb_list);
+
+	return 0;
+}
+
+static int bus_has_ars_inject(struct ndctl_bus *bus)
+{
+	if (!ndctl_bus_has_nfit(bus))
+		return 0;
+
+	if (ndctl_bus_is_nfit_cmd_supported(bus, NFIT_CMD_ARS_INJECT_SET) &&
+		ndctl_bus_is_nfit_cmd_supported(bus, NFIT_CMD_ARS_INJECT_GET) &&
+		ndctl_bus_is_nfit_cmd_supported(bus, NFIT_CMD_ARS_INJECT_CLEAR))
+		return 1;
+	else
+		return 0;
+}
+
+static struct ndctl_cmd *ndctl_bus_cmd_new_err_inj(struct ndctl_bus *bus)
+{
+	struct nd_cmd_ars_err_inj *err_inj;
+	size_t size, cmd_length;
+	struct nd_cmd_pkg *pkg;
+	struct ndctl_cmd *cmd;
+
+	cmd_length = sizeof(struct nd_cmd_ars_err_inj);
+	size = sizeof(*cmd) + sizeof(*pkg) + cmd_length;
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->bus = bus;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_CALL;
+	cmd->size = size;
+	cmd->status = 1;
+	pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
+	pkg->nd_command = NFIT_CMD_ARS_INJECT_SET;
+	pkg->nd_size_in = (2 * sizeof(u64)) + sizeof(u32);
+	pkg->nd_size_out = cmd_length;
+	pkg->nd_fw_size = cmd_length;
+	err_inj = (struct nd_cmd_ars_err_inj *)&pkg->nd_payload[0];
+	cmd->firmware_status = &err_inj->status;
+
+	return cmd;
+}
+
+static struct ndctl_cmd *ndctl_bus_cmd_new_err_inj_clr(struct ndctl_bus *bus)
+{
+	struct nd_cmd_ars_err_inj_clr *err_inj_clr;
+	size_t size, cmd_length;
+	struct nd_cmd_pkg *pkg;
+	struct ndctl_cmd *cmd;
+
+	cmd_length = sizeof(struct nd_cmd_ars_err_inj_clr);
+	size = sizeof(*cmd) + sizeof(*pkg) + cmd_length;
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->bus = bus;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_CALL;
+	cmd->size = size;
+	cmd->status = 1;
+	pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
+	pkg->nd_command = NFIT_CMD_ARS_INJECT_CLEAR;
+	pkg->nd_size_in = 2 * sizeof(u64);
+	pkg->nd_size_out = cmd_length;
+	pkg->nd_fw_size = cmd_length;
+	err_inj_clr = (struct nd_cmd_ars_err_inj_clr *)&pkg->nd_payload[0];
+	cmd->firmware_status = &err_inj_clr->status;
+
+	return cmd;
+}
+
+static struct ndctl_cmd *ndctl_bus_cmd_new_err_inj_stat(struct ndctl_bus *bus,
+	u32 buf_size)
+{
+	struct nd_cmd_ars_err_inj_stat *err_inj_stat;
+	size_t size, cmd_length;
+	struct nd_cmd_pkg *pkg;
+	struct ndctl_cmd *cmd;
+
+
+	cmd_length = sizeof(struct nd_cmd_ars_err_inj_stat);
+	size = sizeof(*cmd) + sizeof(*pkg) + cmd_length + buf_size;
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->bus = bus;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_CALL;
+	cmd->size = size;
+	cmd->status = 1;
+	pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
+	pkg->nd_command = NFIT_CMD_ARS_INJECT_GET;
+	pkg->nd_size_in = cmd_length;
+	pkg->nd_size_out = cmd_length + buf_size;
+	pkg->nd_fw_size = cmd_length + buf_size;
+	err_inj_stat = (struct nd_cmd_ars_err_inj_stat *)&pkg->nd_payload[0];
+	cmd->firmware_status = &err_inj_stat->status;
+
+	return cmd;
+}
+
+static void translate_status(u32 status)
+{
+	if (status == ND_ARS_ERR_INJ_STATUS_NOT_SUPP)
+		fprintf(stderr,
+			"error: error injection is not supported\n");
+	if (status == ND_ARS_ERR_INJ_STATUS_INVALID_PARAM)
+		fprintf(stderr, "error: invalid parameters\n");
+}
+
+static int ndctl_bus_nfit_err_inj(struct ndctl_bus *bus, u64 offset,
+	u64 length, u32 options)
+{
+	struct nd_cmd_ars_err_inj *err_inj;
+	struct nd_cmd_pkg *pkg;
+	struct ndctl_cmd *cmd;
+	int rc;
+
+	if (!bus)
+		return -EINVAL;
+
+	cmd = ndctl_bus_cmd_new_err_inj(bus);
+	if (!cmd)
+		return -ENOMEM;
+
+	pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
+	err_inj = (struct nd_cmd_ars_err_inj *)&pkg->nd_payload[0];
+	err_inj->err_inj_spa_range_base = offset;
+	err_inj->err_inj_spa_range_length = length;
+	err_inj->err_inj_options = options;
+
+	rc = ndctl_cmd_submit(cmd);
+	if (rc) {
+		fprintf(stderr, "Error submitting command: %d\n", rc);
+		goto out;
+	}
+	translate_status(err_inj->status);
+
+ out:
+	ndctl_cmd_unref(cmd);
+	return rc;
+}
+
+static int ndctl_bus_nfit_err_inj_clr(struct ndctl_bus *bus, u64 offset,
+	u64 length)
+{
+	struct nd_cmd_ars_err_inj_clr *err_inj_clr;
+	struct nd_cmd_pkg *pkg;
+	struct ndctl_cmd *cmd;
+	int rc;
+
+	if (!bus)
+		return -EINVAL;
+
+	cmd = ndctl_bus_cmd_new_err_inj_clr(bus);
+	if (!cmd)
+		return -ENOMEM;
+
+	pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
+	err_inj_clr = (struct nd_cmd_ars_err_inj_clr *)&pkg->nd_payload[0];
+	err_inj_clr->err_inj_clr_spa_range_base = offset;
+	err_inj_clr->err_inj_clr_spa_range_length = length;
+
+	rc = ndctl_cmd_submit(cmd);
+	if (rc) {
+		fprintf(stderr, "Error submitting command: %d\n", rc);
+		goto out;
+	}
+	translate_status(err_inj_clr->status);
+	printf("Warning: Clearing injected errors here clears them in the\n");
+	printf("badrange list in nfit_test, but the kernel won't 'forget'\n");
+	printf("any entries it has found in a scrub until they are cleared\n");
+	printf("through the normal process of writing the affected blocks\n\n");
+ out:
+	ndctl_cmd_unref(cmd);
+	return rc;
+}
+
+static int bb_add_record(u64 sector, u64 count)
+{
+	struct bb *bb, *bb_iter, *bb_next, *bb_prev;
+	struct list_head *h = &ictx.bb_list;
+	int merged = 0;
+
+	bb = calloc(1, sizeof(*bb));
+	if (bb == NULL)
+		return -ENOMEM;
+	bb->sector = sector;
+	bb->count = count;
+
+	if (list_empty(h)) {
+		list_add(h, &bb->list);
+		return 0;
+	}
+
+	/* add 'bb' to the list such that it remains sorted */
+	list_for_each(h, bb_iter, list) {
+		/* Find insertion point */
+		bb_prev = list_prev(h, bb_iter, list);
+		bb_next = list_next(h, bb_iter, list);
+
+		if (bb_prev == NULL) {
+			/* bb_iter is the first entry */
+			if (bb->sector < bb_iter->sector) {
+				list_add(h, &bb->list);
+				break;
+			}
+		}
+		if (bb_next == NULL) {
+			/*
+			 * bb_iter is the last entry. If we've reached here,
+			 * the only option is to add to the tail as the case
+			 * for "tail - 1" should have been covered by the
+			 * following checks for the previous iteration.
+			 */
+			list_add_tail(h, &bb->list);
+			break;
+		}
+		/* Add to the left of bb_iter */
+		if (bb->sector <= bb_iter->sector) {
+			if (bb_prev && (bb_prev->sector <= bb->sector)) {
+				list_add_after(h, &bb_prev->list, &bb->list);
+				break;
+			}
+		}
+		/* Add to the right of bb_iter */
+		if (bb_iter->sector <= bb->sector) {
+			if (bb_next && (bb->sector <= bb_next->sector)) {
+				list_add_after(h, &bb_iter->list, &bb->list);
+				break;
+			}
+		}
+	}
+
+	/* second pass over the list looking for mergeable entries */
+	list_for_each(h, bb_iter, list) {
+		u64 cur_end, next_end, cur_start, next_start;
+
+		/*
+		 * test for merges in a loop here because one addition can
+		 * potentially have a cascading merge effect on multiple
+		 * remaining entries
+		 */
+		do {
+			/* reset the merged flag */
+			merged = 0;
+
+			bb_next = list_next(h, bb_iter, list);
+			if (bb_next == NULL)
+				break;
+
+			cur_start = bb_iter->sector;
+			next_start = bb_next->sector;
+			cur_end = bb_iter->sector + bb_iter->count - 1;
+			next_end = bb_next->sector + bb_next->count - 1;
+
+			if (cur_end >= next_start) {
+				/* overlapping records that can be merged */
+				if (next_end > cur_end) {
+					/* next extends cur */
+					bb_iter->count =
+						next_end - cur_start + 1;
+				} else {
+					/* next is contained in cur */
+					;
+				}
+				/* next is now redundant */
+				list_del_from(h, &bb_next->list);
+				free(bb_next);
+				merged = 1;
+				continue;
+			}
+			if (next_start == cur_end + 1) {
+				/* adjoining records that can be merged */
+				bb_iter->count = next_end - cur_start + 1;
+				list_del_from(h, &bb_next->list);
+				free(bb_next);
+				merged = 1;
+				continue;
+			}
+		} while (merged);
+	}
+
+	return 0;
+}
+
+static int print_err_inj_status(struct nd_cmd_ars_err_inj_stat *stat,
+	u64 ns_spa, u64 ns_size)
+{
+	struct list_head *h = &ictx.bb_list;
+	struct json_object *jbbs, *jbb, *jobj;
+	struct bb *bb_iter, *next;
+	int rc = 0;
+	u32 i;
+
+	for (i = 0; i < stat->inj_err_rec_count; i++) {
+		u64 ns_off, rec_off, rec_len;
+		u64 sector, count, start_pad;
+
+		rec_off = stat->record[i].err_inj_stat_spa_range_base;
+		rec_len = stat->record[i].err_inj_stat_spa_range_length;
+		/* discard ranges outside the provided namespace */
+		if (rec_off < ns_spa)
+			continue;
+		if (rec_off >= ns_spa + ns_size)
+			continue;
+
+		/* translate spa offset to namespace offset */
+		ns_off = rec_off - ns_spa;
+
+		sector = ALIGN_DOWN(ns_off, 512)/512;
+		start_pad = ns_off - (sector * 512);
+		count = ALIGN(start_pad + rec_len, 512)/512;
+		rc = bb_add_record(sector, count);
+		if (rc)
+			goto err_list;
+	}
+
+	jobj = json_object_new_object();
+	if (!jobj) {
+		rc = -ENOMEM;
+		goto err_list;
+	}
+	jbbs = json_object_new_array();
+	if (!jbbs) {
+		rc = -ENOMEM;
+		goto err_obj;
+	}
+
+	list_for_each(h, bb_iter, list) {
+		jbb = util_badblock_rec_to_json(bb_iter->sector,
+				bb_iter->count, ictx.flags);
+		if (!jbb)
+			break;
+		json_object_array_add(jbbs, jbb);
+	}
+	if (!list_empty(h)) {
+		json_object_object_add(jobj, "badblocks", jbbs);
+		printf("%s\n", json_object_to_json_string_ext(jobj,
+			JSON_C_TO_STRING_PRETTY));
+	}
+
+	json_object_put(jbbs);
+ err_obj:
+	json_object_put(jobj);
+ err_list:
+	/* done with the bb list, we can free it now */
+	list_for_each_safe(h, bb_iter, next, list) {
+		list_del_from(h, &bb_iter->list);
+		free(bb_iter);
+	}
+
+	return rc;
+}
+
+static int ndctl_bus_nfit_err_inj_stat(struct ndctl_bus *bus, u64 ns_offset,
+	u64 ns_size)
+{
+	struct ndctl_cmd *cmd;
+	struct nd_cmd_pkg *pkg;
+	struct nd_cmd_ars_err_inj_stat *err_inj_stat;
+	int rc, buf_size;
+
+	if (!bus)
+		return -EINVAL;
+
+	cmd = ndctl_bus_cmd_new_ars_cap(bus, ns_offset, ns_size);
+	rc = ndctl_cmd_submit(cmd);
+	if (rc) {
+		fprintf(stderr, "Error submitting ars_cap: %d\n", rc);
+		goto out;
+	}
+	buf_size = ndctl_cmd_ars_cap_get_size(cmd);
+	if (buf_size == 0) {
+		fprintf(stderr, "Got an invalid max_ars_out from ars_cap\n");
+		rc = -EINVAL;
+		goto out;
+	}
+	ndctl_cmd_unref(cmd);
+
+	cmd = ndctl_bus_cmd_new_err_inj_stat(bus, buf_size);
+	if (!cmd)
+		return -ENOMEM;
+
+	pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
+	err_inj_stat = (struct nd_cmd_ars_err_inj_stat *)&pkg->nd_payload[0];
+
+	rc = ndctl_cmd_submit(cmd);
+	if (rc) {
+		fprintf(stderr, "Error submitting command: %d\n", rc);
+		goto out;
+	}
+	translate_status(err_inj_stat->status);
+	rc = print_err_inj_status(err_inj_stat, ns_offset, ns_size);
+
+ out:
+	ndctl_cmd_unref(cmd);
+	return rc;
+}
+
+static int ns_errors_to_json(struct ndctl_namespace *ndns,
+		unsigned int start_count)
+{
+	unsigned long flags = ictx.flags | UTIL_JSON_MEDIA_ERRORS;
+	struct ndctl_bus *bus = ndctl_namespace_get_bus(ndns);
+	struct json_object *jndns;
+	unsigned int count;
+	int rc;
+
+	/* only wait for scrubs for the inject and notify case */
+	if (ictx.options & (1 << ND_ARS_ERR_INJ_OPT_NOTIFY)) {
+		do {
+			count = ndctl_bus_get_scrub_count(bus);
+			if (count == UINT_MAX) {
+				fprintf(stderr, "Unable to get scrub count\n");
+				return -ENXIO;
+			}
+			sleep(1);
+		} while (count <= start_count);
+
+		rc = ndctl_bus_wait_for_scrub_completion(bus);
+		if (rc) {
+			fprintf(stderr, "Error waiting for scrub\n");
+			return rc;
+		}
+	}
+
+	jndns = util_namespace_to_json(ndns, flags);
+	if (jndns)
+		printf("%s\n", json_object_to_json_string_ext(jndns,
+				JSON_C_TO_STRING_PRETTY));
+	return 0;
+}
+
+static int err_inject_ns(struct ndctl_bus *bus, struct ndctl_namespace *ndns)
+{
+	unsigned long long ns_offset, err_spa_offset;
+	unsigned long long ns_size;
+	unsigned int scrub_count;
+	struct ndctl_pfn *pfn;
+	struct ndctl_dax *dax;
+	unsigned int op_mask;
+	int rc;
+
+	dax = ndctl_namespace_get_dax(ndns);
+	pfn = ndctl_namespace_get_pfn(ndns);
+	if (pfn) {
+		ns_offset = ndctl_pfn_get_resource(pfn);
+		ns_size = ndctl_pfn_get_size(pfn);
+	} else if (dax) {
+		ns_offset = ndctl_dax_get_resource(dax);
+		ns_size = ndctl_dax_get_size(dax);
+	} else {
+		/* raw or btt */
+		ns_offset = ndctl_namespace_get_resource(ndns);
+		ns_size = ndctl_namespace_get_size(ndns);
+	}
+
+	if (ictx.off_bytes + ictx.len_bytes > ns_size) {
+		fprintf(stderr,
+			"Error: sector %#lx, count %#lx are out of bounds\n",
+			ictx.sector, ictx.count);
+		fprintf(stderr, "  namespace size is %#llx\n", ns_size);
+		return -EINVAL;
+	}
+	err_spa_offset = ns_offset + ictx.off_bytes;
+
+	op_mask = ictx.op_mask;
+	while (op_mask) {
+		if (op_mask & (1 << OP_INJECT)) {
+			scrub_count = ndctl_bus_get_scrub_count(bus);
+			if (scrub_count == UINT_MAX) {
+				fprintf(stderr, "Unable to get scrub count\n");
+				return -ENXIO;
+			}
+			rc = ndctl_bus_nfit_err_inj(bus, err_spa_offset,
+				ictx.len_bytes, ictx.options);
+			op_mask &= ~(1 << OP_INJECT);
+			rc = ns_errors_to_json(ndns, scrub_count);
+			if (rc)
+				return rc;
+		}
+		if (op_mask & (1 << OP_CLEAR)) {
+			rc = ndctl_bus_nfit_err_inj_clr(bus, err_spa_offset,
+				ictx.len_bytes);
+			op_mask &= ~(1 << OP_CLEAR);
+			rc = ns_errors_to_json(ndns, 0);
+			if (rc)
+				return rc;
+		}
+		if (op_mask & (1 << OP_STATUS)) {
+			rc = ndctl_bus_nfit_err_inj_stat(bus, ns_offset,
+				ns_size);
+			op_mask &= ~(1 << OP_STATUS);
+		}
+	}
+
+	return rc;
+}
+
+static int do_inject(const char *namespace, struct ndctl_ctx *ctx)
+{
+	struct ndctl_namespace *ndns;
+	struct ndctl_region *region;
+	const char *ndns_name;
+	struct ndctl_bus *bus;
+	int rc = -ENXIO;
+
+	if (namespace == NULL)
+		return rc;
+
+	if (verbose)
+		ndctl_set_log_priority(ctx, LOG_DEBUG);
+
+        ndctl_bus_foreach(ctx, bus) {
+		if (!util_bus_filter(bus, param.bus))
+			continue;
+
+		ndctl_region_foreach(bus, region) {
+			if (!util_region_filter(region, param.region))
+				continue;
+
+			ndctl_namespace_foreach(region, ndns) {
+				ndns_name = ndctl_namespace_get_devname(ndns);
+
+				if (strcmp(namespace, ndns_name) != 0)
+					continue;
+
+				if (!bus_has_ars_inject(bus)) {
+					fprintf(stderr,
+						"%s: error injection not supported\n",
+						ndns_name);
+					return -EOPNOTSUPP;
+				}
+				return err_inject_ns(bus, ndns);
+			}
+		}
+	}
+
+	return 0;
+}
+
+int cmd_inject_error(int argc, const char **argv, void *ctx)
+{
+	const char * const u[] = {
+		"ndctl inject-error <namespace> [<options>]",
+		NULL
+	};
+	int i, rc;
+
+        argc = parse_options(argc, argv, inject_options, u, 0);
+	rc = inject_init();
+	if (rc)
+		return rc;
+
+	if (argc == 0)
+		error("specify a namespace to inject error to\n");
+	for (i = 1; i < argc; i++)
+		error("unknown extra parameter \"%s\"\n", argv[i]);
+	if (argc == 0 || argc > 1) {
+		usage_with_options(u, inject_options);
+		return -ENODEV; /* we won't return from usage_with_options() */
+	}
+
+	return do_inject(argv[0], ctx);
+}
diff --git a/ndctl/libndctl-nfit.h b/ndctl/libndctl-nfit.h
index ae9e5ce..7dafd64 100644
--- a/ndctl/libndctl-nfit.h
+++ b/ndctl/libndctl-nfit.h
@@ -31,6 +31,14 @@ enum {
 /* error number of Translate SPA by firmware  */
 #define ND_TRANSLATE_SPA_STATUS_INVALID_SPA  2
 
+/* status definitions for error injection */
+#define ND_ARS_ERR_INJ_STATUS_NOT_SUPP 1
+#define ND_ARS_ERR_INJ_STATUS_INVALID_PARAM 2
+
+enum err_inj_options {
+	ND_ARS_ERR_INJ_OPT_NOTIFY = 0,
+};
+
 /*
  * The following structures are command packages which are
  * defined by ACPI 6.2 (or later).
diff --git a/ndctl/ndctl.c b/ndctl/ndctl.c
index d10718e..0f748e1 100644
--- a/ndctl/ndctl.c
+++ b/ndctl/ndctl.c
@@ -83,6 +83,7 @@ static struct cmd_struct commands[] = {
 	{ "write-labels", cmd_write_labels },
 	{ "init-labels", cmd_init_labels },
 	{ "check-labels", cmd_check_labels },
+	{ "inject-error", cmd_inject_error },
 	{ "list", cmd_list },
 	{ "help", cmd_help },
 	#ifdef ENABLE_TEST
diff --git a/util/json.c b/util/json.c
index 9b7773e..052e6c5 100644
--- a/util/json.c
+++ b/util/json.c
@@ -20,6 +20,7 @@
 #include <ndctl/libndctl.h>
 #include <daxctl/libdaxctl.h>
 #include <ccan/array_size/array_size.h>
+#include <ccan/short_types/short_types.h>
 
 #ifdef HAVE_NDCTL_H
 #include <linux/ndctl.h>
@@ -845,3 +846,28 @@ struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
 	json_object_put(jmapping);
 	return NULL;
 }
+
+struct json_object *util_badblock_rec_to_json(u64 sector, u64 count,
+		unsigned long flags)
+{
+	struct json_object *jerr = json_object_new_object();
+	struct json_object *jobj;
+
+	if (!jerr)
+		return NULL;
+
+	jobj = util_json_object_hex(sector, flags);
+	if (!jobj)
+		goto err;
+	json_object_object_add(jerr, "sector", jobj);
+
+	jobj = util_json_object_hex(count, flags);
+	if (!jobj)
+		goto err;
+	json_object_object_add(jerr, "count", jobj);
+
+	return jerr;
+ err:
+	json_object_put(jerr);
+	return NULL;
+}
diff --git a/util/json.h b/util/json.h
index d934b2e..4d0dcc9 100644
--- a/util/json.h
+++ b/util/json.h
@@ -15,6 +15,7 @@
 #include <stdio.h>
 #include <stdbool.h>
 #include <ndctl/libndctl.h>
+#include <ccan/short_types/short_types.h>
 
 enum util_json_flags {
 	UTIL_JSON_IDLE = (1 << 0),
@@ -33,6 +34,8 @@ struct json_object *util_mapping_to_json(struct ndctl_mapping *mapping,
 		unsigned long flags);
 struct json_object *util_namespace_to_json(struct ndctl_namespace *ndns,
 		unsigned long flags);
+struct json_object *util_badblock_rec_to_json(u64 sector, u64 count,
+		unsigned long flags);
 struct daxctl_region;
 struct daxctl_dev;
 struct json_object *util_region_badblocks_to_json(struct ndctl_region *region,
diff --git a/util/size.h b/util/size.h
index 3c27079..34fac58 100644
--- a/util/size.h
+++ b/util/size.h
@@ -28,6 +28,7 @@ unsigned long long parse_size64(const char *str);
 unsigned long long __parse_size64(const char *str, unsigned long long *units);
 
 #define ALIGN(x, a) ((((unsigned long long) x) + (a - 1)) & ~(a - 1))
+#define ALIGN_DOWN(x, a) (((((unsigned long long) x) + a) & ~(a - 1)) - a)
 #define BITS_PER_LONG (sizeof(unsigned long) * 8)
 #define HPAGE_SIZE (2 << 20)
 
-- 
2.9.5

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [ndctl PATCH 6/8] ndctl/test: add a new unit test for inject-error
  2017-10-06  1:53 [ndctl PATCH 0/8] add an inject-error command to ndctl Vishal Verma
                   ` (4 preceding siblings ...)
  2017-10-06  1:54 ` [ndctl PATCH 5/8] ndctl: add an inject-error command Vishal Verma
@ 2017-10-06  1:54 ` Vishal Verma
  2017-10-09 23:49   ` Verma, Vishal L
  2017-10-06  1:54 ` [ndctl PATCH 7/8] ndctl/test: update existing unit tests to use error-inject Vishal Verma
  2017-10-06  1:54 ` [ndctl PATCH 8/8] ndctl/test: add a new unit test for BTT error clearing Vishal Verma
  7 siblings, 1 reply; 15+ messages in thread
From: Vishal Verma @ 2017-10-06  1:54 UTC (permalink / raw)
  To: linux-nvdimm

Add a new unit test to test all the features of the inject-error
command.

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
 test/Makefile.am     |  3 +-
 test/inject-error.sh | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+), 1 deletion(-)
 create mode 100755 test/inject-error.sh

diff --git a/test/Makefile.am b/test/Makefile.am
index ac3547e..8cec451 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -13,7 +13,8 @@ TESTS =\
 	multi-dax.sh \
 	btt-check.sh \
 	label-compat.sh \
-	blk-exhaust.sh
+	blk-exhaust.sh \
+	inject-error.sh
 
 check_PROGRAMS =\
 	libndctl \
diff --git a/test/inject-error.sh b/test/inject-error.sh
new file mode 100755
index 0000000..a8348df
--- /dev/null
+++ b/test/inject-error.sh
@@ -0,0 +1,89 @@
+#!/bin/bash -Ex
+
+# Copyright(c) 2015-2017 Intel Corporation. All rights reserved.
+#
+# 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.
+
+[ -f "../ndctl/ndctl" ] && [ -x "../ndctl/ndctl" ] && ndctl="../ndctl/ndctl"
+[ -f "./ndctl/ndctl" ] && [ -x "./ndctl/ndctl" ] && ndctl="./ndctl/ndctl"
+[ -z "$ndctl" ] && echo "Couldn't find an ndctl binary" && exit 1
+bus="nfit_test.0"
+json2var="s/[{}\",]//g; s/:/=/g"
+dev=""
+mode=""
+size=""
+sector_size=""
+blockdev=""
+bs=4096
+rc=77
+
+trap 'err $LINENO' ERR
+
+# sample json:
+# {
+#   "dev":"namespace5.0",
+#   "mode":"sector",
+#   "size":32440320,
+#   "uuid":"51805176-e124-4635-ae17-0e6a4a16671a",
+#   "sector_size":4096,
+#   "blockdev":"pmem5s"
+# }
+
+# $1: Line number
+# $2: exit code
+err()
+{
+	[ -n "$2" ] && rc="$2"
+	echo "test/btt-check: failed at line $1"
+	exit "$rc"
+}
+
+check_min_kver()
+{
+	local ver="$1"
+	: "${KVER:=$(uname -r)}"
+
+	[ -n "$ver" ] || return 1
+	[[ "$ver" == "$(echo -e "$ver\n$KVER" | sort -V | head -1)" ]]
+}
+
+check_min_kver "4.15" || { echo "kernel $KVER may not support error injection"; exit $rc; }
+
+create()
+{
+	json=$($ndctl create-namespace -b "$bus" -t pmem -m sector)
+	eval "$(echo "$json" | sed -e "$json2var")"
+	[ -n "$dev" ] || err "$LINENO" 2
+	[ "$mode" = "sector" ] || err "$LINENO" 2
+	[ -n "$size" ] || err "$LINENO" 2
+	[ -n "$sector_size" ] || err "$LINENO" 2
+	[ -n "$blockdev" ] || err "$LINENO" 2
+	[ $size -gt 0 ] || err "$LINENO" 2
+}
+
+reset()
+{
+	$ndctl disable-region -b "$bus" all
+	$ndctl zero-labels -b "$bus" all
+	$ndctl enable-region -b "$bus" all
+}
+
+do_tests()
+{
+	# TODO
+	return
+}
+
+modprobe nfit_test
+rc=1
+create && reset
+do_tests
+reset
+exit 0
-- 
2.9.5

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [ndctl PATCH 7/8] ndctl/test: update existing unit tests to use error-inject
  2017-10-06  1:53 [ndctl PATCH 0/8] add an inject-error command to ndctl Vishal Verma
                   ` (5 preceding siblings ...)
  2017-10-06  1:54 ` [ndctl PATCH 6/8] ndctl/test: add a new unit test for inject-error Vishal Verma
@ 2017-10-06  1:54 ` Vishal Verma
  2017-10-06  1:54 ` [ndctl PATCH 8/8] ndctl/test: add a new unit test for BTT error clearing Vishal Verma
  7 siblings, 0 replies; 15+ messages in thread
From: Vishal Verma @ 2017-10-06  1:54 UTC (permalink / raw)
  To: linux-nvdimm

Unitl now, various unit tests related to error handling used to expect
'canned' errors to be present in the middle of every nfit_test
namespace. With the ACPI error injection patches for nfit_test, this is
no longer the case. Update the existing unit tests it inject any errors
they need for testing themselves using inject-error, rather than
expecting canned errors.

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
 test/clear.sh         |  5 ++++-
 test/dax-errors.sh    |  5 ++++-
 test/daxdev-errors.sh | 17 ++++++++++++++---
 3 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/test/clear.sh b/test/clear.sh
index a60c3d9..13dcce0 100755
--- a/test/clear.sh
+++ b/test/clear.sh
@@ -54,7 +54,10 @@ eval $(echo $json | sed -e "$json2var")
 [ $dev = "x" ] && echo "fail: $LINENO" && exit 1
 [ $mode != "raw" ] && echo "fail: $LINENO" && exit 1
 
-# check for expected errors in the middle of the namespace
+# inject errors in the middle of the namespace, verify that reading fails
+err_sector="$(((size/512) / 2))"
+err_count=8
+$NDCTL inject-error --sector="$err_sector" --count=$err_count $dev
 read sector len < /sys/block/$blockdev/badblocks
 [ $((sector * 2)) -ne $((size /512)) ] && echo "fail: $LINENO" && exit 1
 if dd if=/dev/$blockdev of=/dev/null iflag=direct bs=512 skip=$sector count=$len; then
diff --git a/test/dax-errors.sh b/test/dax-errors.sh
index 5af3859..ac56fb1 100755
--- a/test/dax-errors.sh
+++ b/test/dax-errors.sh
@@ -64,7 +64,10 @@ eval $(echo $json | sed -e "$json2var")
 [ $dev = "x" ] && echo "fail: $LINENO" && exit 1
 [ $mode != "raw" ] && echo "fail: $LINENO" && exit 1
 
-# check for expected errors in the middle of the namespace
+# inject errors in the middle of the namespace, verify that reading fails
+err_sector="$(((size/512) / 2))"
+err_count=8
+$NDCTL inject-error --sector="$err_sector" --count=$err_count $dev
 read sector len < /sys/block/$blockdev/badblocks
 [ $((sector * 2)) -ne $((size /512)) ] && echo "fail: $LINENO" && exit 1
 if dd if=/dev/$blockdev of=/dev/null iflag=direct bs=512 skip=$sector count=$len; then
diff --git a/test/daxdev-errors.sh b/test/daxdev-errors.sh
index 8115087..1ee9ad6 100755
--- a/test/daxdev-errors.sh
+++ b/test/daxdev-errors.sh
@@ -37,6 +37,7 @@ check_min_kver "4.12" || { echo "kernel $KVER lacks dax dev error handling"; exi
 
 set -e
 trap 'err $LINENO' ERR
+rc=1
 
 # setup (reset nfit_test dimms)
 modprobe nfit_test
@@ -68,18 +69,28 @@ chardev=$(echo $json | jq ". | select(.mode == \"dax\") | .daxregion.devices[0].
 #  }
 #}
 
+json1=$($NDCTL list $BUS --mode=dax --namespaces)
+eval $(echo $json1 | sed -e "$json2var")
+nsdev=$dev
+
 json1=$($NDCTL list $BUS)
 eval $(echo $json1 | sed -e "$json2var")
+busdev=$dev
+
+# inject errors in the middle of the namespace
+err_sector="$(((size/512) / 2))"
+err_count=8
+$NDCTL inject-error --sector="$err_sector" --count=$err_count $nsdev
 
-read sector len < /sys/bus/platform/devices/nfit_test.0/$dev/$region/badblocks
+read sector len < /sys/bus/nd/devices/$region/badblocks
 echo "sector: $sector len: $len"
 
 # run the daxdev-errors test
 test -x ./daxdev-errors
-./daxdev-errors $dev $region
+./daxdev-errors $busdev $region
 
 # check badblocks, should be empty
-if read sector len < /sys/bus/platform/devices/nfit_test.0/$dev/$region/badblocks; then
+if read sector len < /sys/bus/platform/devices/nfit_test.0/$busdev/$region/badblocks; then
 	echo "badblocks empty, expected"
 fi
 [ -n "$sector" ] && echo "fail: $LINENO" && exit 1
-- 
2.9.5

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [ndctl PATCH 8/8] ndctl/test: add a new unit test for BTT error clearing
  2017-10-06  1:53 [ndctl PATCH 0/8] add an inject-error command to ndctl Vishal Verma
                   ` (6 preceding siblings ...)
  2017-10-06  1:54 ` [ndctl PATCH 7/8] ndctl/test: update existing unit tests to use error-inject Vishal Verma
@ 2017-10-06  1:54 ` Vishal Verma
  7 siblings, 0 replies; 15+ messages in thread
From: Vishal Verma @ 2017-10-06  1:54 UTC (permalink / raw)
  To: linux-nvdimm

The new error injection command allows us to inject errors that persist
through changing the mode of a BTT namespace to 'raw' and back. This
allows us to test error clearing with a BTT by adding a selective error
block to the raw namespace, enabling the BTT, and then clearing it via a
write.

Cc: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
---
 test/Makefile.am   |   3 +-
 test/btt-errors.sh | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 154 insertions(+), 1 deletion(-)
 create mode 100755 test/btt-errors.sh

diff --git a/test/Makefile.am b/test/Makefile.am
index 8cec451..881fcea 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -14,7 +14,8 @@ TESTS =\
 	btt-check.sh \
 	label-compat.sh \
 	blk-exhaust.sh \
-	inject-error.sh
+	inject-error.sh \
+	btt-errors.sh
 
 check_PROGRAMS =\
 	libndctl \
diff --git a/test/btt-errors.sh b/test/btt-errors.sh
new file mode 100755
index 0000000..9d96ac9
--- /dev/null
+++ b/test/btt-errors.sh
@@ -0,0 +1,152 @@
+#!/bin/bash -x
+
+# Copyright(c) 2015-2017 Intel Corporation. All rights reserved.
+#
+# 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.
+
+NDCTL="../ndctl/ndctl"
+BUS="nfit_test.0"
+MNT=test_btt_mnt
+FILE=image
+json2var="s/[{}\",]//g; s/:/=/g"
+blockdev=""
+rc=77
+
+err() {
+	rc=1
+	echo "test/btt-errors: failed at line $1"
+
+	#TODO remove
+	exit
+
+	rm -f $FILE
+	rm -f $MNT/$FILE
+	if [ -n "$blockdev" ]; then
+		umount "/dev/$blockdev"
+	else
+		rc=77
+	fi
+	rmdir $MNT
+	exit $rc
+}
+
+check_min_kver()
+{
+	local ver="$1"
+	: "${KVER:=$(uname -r)}"
+
+	[ -n "$ver" ] || return 1
+	[[ "$ver" == "$(echo -e "$ver\n$KVER" | sort -V | head -1)" ]]
+}
+
+force_raw()
+{
+	raw="$1"
+	if grep -q "$MNT" /proc/mounts; then umount $MNT; fi
+	ndctl disable-namespace "$dev"
+	echo "$raw" > "/sys/bus/nd/devices/$dev/force_raw"
+	ndctl enable-namespace "$dev"
+	echo "Set $dev to raw mode: $raw"
+	if [[ "$raw" == "1" ]]; then
+		raw_bdev=${blockdev%s}
+		test -b "/dev/$raw_bdev"
+	else
+		raw_bdev=""
+	fi
+}
+
+check_min_kver "4.14" || { echo "kernel $KVER may lack BTT error handling"; exit $rc; }
+
+set -e
+mkdir -p $MNT
+trap 'err $LINENO' ERR
+
+# setup (reset nfit_test dimms)
+modprobe nfit_test
+$NDCTL disable-region -b "$BUS" all
+$NDCTL zero-labels -b "$BUS" all
+$NDCTL enable-region -b "$BUS" all
+
+rc=1
+
+# create a btt namespace and clear errors (if any)
+dev="x"
+json=$($NDCTL create-namespace -b "$BUS" -t pmem -m sector)
+eval "$(echo "$json" | sed -e "$json2var")"
+[ $dev = "x" ] && echo "fail: $LINENO" && exit 1
+
+force_raw 1
+if read -r sector len < "/sys/block/$raw_bdev/badblocks"; then
+	dd of=/dev/$raw_bdev if=/dev/zero oflag=direct bs=512 seek="$sector" count="$len"
+fi
+force_raw 0
+
+mkfs.ext4 "/dev/$blockdev" -b 4096
+mount -o nodelalloc "/dev/$blockdev" $MNT
+
+# prepare an image file with random data
+dd if=/dev/urandom of=$FILE bs=4096 count=1
+test -s $FILE
+
+# copy it to the file system
+cp $FILE $MNT/$FILE
+
+# Get the start sector for the file
+start_sect=$(filefrag -v -b512 $MNT/$FILE | grep -E "^[ ]+[0-9]+.*" | head -1 | awk '{ print $4 }' | cut -d. -f1)
+start_4k=$((start_sect/8))
+test -n "$start_sect"
+echo "start sector of the file is: $start_sect (512B) or $start_4k (4096B)"
+
+# figure out the btt offset
+
+force_raw 1
+
+# calculate start of the map
+map=$(hexdump -s 96 -n 4 "/dev/$raw_bdev" | head -1 | cut -d' ' -f2-)
+map=$(tr -d ' ' <<< "0x${map#* }${map%% *}")
+printf "btt map starts at: %x\n" "$map"
+
+# calculate map entry byte offset for the file's block
+map_idx=$((map + (4 * start_4k)))
+printf "btt map entry location for sector %x: %x\n" "$start_4k" "$map_idx"
+
+# read the map entry
+map_ent=$(hexdump -s $map_idx -n 4 "/dev/$raw_bdev" | head -1 | cut -d' ' -f2-)
+map_ent=$(tr -d ' ' <<< "0x${map_ent#* }${map_ent%% *}")
+map_ent=$((map_ent & 0x3fffffff))
+printf "btt map entry: 0x%x\n" "$map_ent"
+
+# calculate the data offset
+dataoff=$(((map_ent * 4096) + 4096))
+printf "dataoff: 0x%x\n" "$dataoff"
+
+bb_inj=$((dataoff/512))
+
+# inject badblocks for one page at the start of the file
+$NDCTL inject-error --sector="$bb_inj" --count=8 $dev
+
+force_raw 0
+mount -o nodelalloc "/dev/$blockdev" $MNT
+
+# make sure reading the first block of the file fails as expected
+: The following 'dd' is expected to hit an I/O Error
+dd if=$MNT/$FILE of=/dev/null iflag=direct bs=4096 count=1 && err $LINENO || true
+
+# write via btt to clear the error
+dd if=/dev/zero of=$MNT/$FILE oflag=direct bs=4096 count=1
+
+# read again and that should succeed
+dd if=$MNT/$FILE of=/dev/null iflag=direct bs=4096 count=1
+
+# done, exit
+$NDCTL disable-region -b "$BUS" all
+$NDCTL zero-labels -b "$BUS" all
+$NDCTL enable-region -b "$BUS" all
+exit 0
-- 
2.9.5

_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [ndctl PATCH 4/8] ccan/list: add a list_add_after helper
  2017-10-06  1:54 ` [ndctl PATCH 4/8] ccan/list: add a list_add_after helper Vishal Verma
@ 2017-10-06 17:39   ` Dan Williams
  0 siblings, 0 replies; 15+ messages in thread
From: Dan Williams @ 2017-10-06 17:39 UTC (permalink / raw)
  To: Vishal Verma; +Cc: linux-nvdimm

On Thu, Oct 5, 2017 at 6:54 PM, Vishal Verma <vishal.l.verma@intel.com> wrote:
> In preparation for the error-inject command, add anew helper to
> ccan/list for adding a list element in the middle of a list.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
> ---
>  ccan/list/list.h | 32 ++++++++++++++++++++++++++++++++
>  1 file changed, 32 insertions(+)

Let's not touch the ccan files since these should always match what is
in upstream ccan. Create a local util/list.h to add new routines. It
would also be nice to rename this to list_splice() to bring us closer
in line with kernel list primitives.
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [ndctl PATCH 1/8] libndctl: fix a memory leak in add_bus
  2017-10-06  1:53 ` [ndctl PATCH 1/8] libndctl: fix a memory leak in add_bus Vishal Verma
@ 2017-10-07 15:44   ` Dan Williams
  0 siblings, 0 replies; 15+ messages in thread
From: Dan Williams @ 2017-10-07 15:44 UTC (permalink / raw)
  To: Vishal Verma; +Cc: linux-nvdimm

On Thu, Oct 5, 2017 at 6:53 PM, Vishal Verma <vishal.l.verma@intel.com> wrote:
> We allocated a buffer for wait_probe_path via strdup, but failed to free
> it in the error path.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
> ---

Applied, thanks.
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [ndctl PATCH 2/8] ndctl, list: move the --human description to an include
  2017-10-06  1:53 ` [ndctl PATCH 2/8] ndctl, list: move the --human description to an include Vishal Verma
@ 2017-10-07 15:55   ` Dan Williams
  0 siblings, 0 replies; 15+ messages in thread
From: Dan Williams @ 2017-10-07 15:55 UTC (permalink / raw)
  To: Vishal Verma; +Cc: linux-nvdimm

On Thu, Oct 5, 2017 at 6:53 PM, Vishal Verma <vishal.l.verma@intel.com> wrote:
> In preparation for inject-error, move the --human option description
> to a file that can be included. Reword the description improving
> the grammar, and making it more generic.
>
> The example in the list man page for --human was also missing a [verse]
> keyword, making the --human part of the example appear in a single line
> rather than formatted json - fix that.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>

Applied, thanks.
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [ndctl PATCH 3/8] libndctl: add APIs to get scrub count and to wait for a scrub
  2017-10-06  1:54 ` [ndctl PATCH 3/8] libndctl: add APIs to get scrub count and to wait for a scrub Vishal Verma
@ 2017-10-07 16:11   ` Dan Williams
  0 siblings, 0 replies; 15+ messages in thread
From: Dan Williams @ 2017-10-07 16:11 UTC (permalink / raw)
  To: Vishal Verma; +Cc: linux-nvdimm

On Thu, Oct 5, 2017 at 6:54 PM, Vishal Verma <vishal.l.verma@intel.com> wrote:
> The kernel uses sysfs to notify userspace of the number of completed
> Address Range Scrubs, as well as any ongoing scrubs. Add libndctl
> helpers to get the scrub count, and to wait for an in-progress scrub to
> complete.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
> ---
>  ndctl/lib/libndctl.c   | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  ndctl/lib/libndctl.sym |  2 ++
>  ndctl/lib/private.h    |  1 +
>  ndctl/libndctl.h.in    |  2 ++
>  4 files changed, 90 insertions(+)
>
> diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
> index 60143d9..3943978 100644
> --- a/ndctl/lib/libndctl.c
> +++ b/ndctl/lib/libndctl.c
> @@ -555,6 +555,7 @@ static void free_bus(struct ndctl_bus *bus, struct list_head *head)
>         free(bus->bus_path);
>         free(bus->bus_buf);
>         free(bus->wait_probe_path);
> +       free(bus->scrub_path);
>         free(bus);
>  }
>
> @@ -785,6 +786,11 @@ static void *add_bus(void *parent, int id, const char *ctl_base)
>         if (!bus->wait_probe_path)
>                 goto err_read;
>
> +       sprintf(path, "%s/device/nfit/scrub", ctl_base);
> +       bus->scrub_path = strdup(path);
> +       if (!bus->scrub_path)
> +               goto err_read;
> +
>         bus->bus_path = parent_dev_path("char", bus->major, bus->minor);
>         if (!bus->bus_path)
>                 goto err_dev_path;
> @@ -810,6 +816,7 @@ static void *add_bus(void *parent, int id, const char *ctl_base)
>   err_dev_path:
>   err_read:
>         free(bus->wait_probe_path);
> +       free(bus->scrub_path);
>         free(bus->provider);
>         free(bus->bus_buf);
>         free(bus);
> @@ -1082,6 +1089,84 @@ NDCTL_EXPORT int ndctl_bus_wait_probe(struct ndctl_bus *bus)
>         return rc < 0 ? -ENXIO : 0;
>  }
>
> +NDCTL_EXPORT unsigned int ndctl_bus_get_scrub_count(struct ndctl_bus *bus)
> +{
> +       struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
> +       char buf[SYSFS_ATTR_SIZE];
> +       unsigned int scrub_count;
> +       char in_progress = '\0';
> +       int rc;
> +
> +       rc = sysfs_read_attr(ctx, bus->scrub_path, buf);
> +       if (rc < 0)
> +               return UINT_MAX;
> +
> +       rc = sscanf(buf, "%u%c", &scrub_count, &in_progress);
> +       if (rc < 0)
> +               return UINT_MAX;
> +       if (rc == 0) {
> +               /* unable to read scrub count */
> +               return UINT_MAX;
> +       }
> +       if (rc >= 1)
> +               return scrub_count;
> +
> +       return UINT_MAX;
> +}
> +
> +/**
> + * ndctl_bus_wait_for_scrub - wait for a scrub to complete
> + * @bus: bus for which to check whether a scrub is in progress
> + *
> + * Upon return this bus has completed any in-progress scrubs. This is
> + * different from ndctl_cmd_ars_in_progress in that the latter checks
> + * the output of an ars_status command to see if the in-progress flag
> + * is set, i.e. provides the firmware's view of whether a scrub is in
> + * progress. ndctl_bus_wait_for_scrub instead checks the kernel's view
> + * of whether a scrub is in progress by looking at the 'scrub' file in
> + * sysfs.
> + */
> +NDCTL_EXPORT int ndctl_bus_wait_for_scrub_completion(struct ndctl_bus *bus)
> +{
> +       struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
> +       unsigned int tmo = 120, scrub_count;
> +       char buf[SYSFS_ATTR_SIZE];
> +       char in_progress = '\0';
> +       int rc, elapsed = 0;
> +
> +       do {
> +               rc = sysfs_read_attr(ctx, bus->scrub_path, buf);
> +               if (rc < 0)
> +                       break;
> +
> +               rc = sscanf(buf, "%u%c", &scrub_count, &in_progress);
> +               if (rc < 0)
> +                       break;
> +               else if (rc <= 1) {
> +                       /* scrub complete, break successfully */
> +                       rc = 0;
> +                       break;
> +               } else if (rc == 2 && in_progress == '+') {
> +                       /* scrub in progress, continue to wait */
> +                       ;
> +               } else {
> +                       /* unknown condition */
> +                       rc = -ENXIO;
> +                       break;
> +               }
> +
> +               elapsed++;
> +               sleep(1);
> +       } while (tmo-- != 0);

Hmm, we don't need to do a sleep loop here we can use select(2) or
poll(2) to wait until the scrub notifies completion.

Something like:

seq = read_count()
fd = ndctl_bus_get_scrub_eventfd()
while (in_progress(seq)) {
    select(..., fd, timeout);
    seq = read_count();
}

We already have the kernel doing sleep polling, no need to compound
wake up the problem in userspace.
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [ndctl PATCH 5/8] ndctl: add an inject-error command
  2017-10-06  1:54 ` [ndctl PATCH 5/8] ndctl: add an inject-error command Vishal Verma
@ 2017-10-09 16:27   ` Dan Williams
  0 siblings, 0 replies; 15+ messages in thread
From: Dan Williams @ 2017-10-09 16:27 UTC (permalink / raw)
  To: Vishal Verma; +Cc: linux-nvdimm

On Thu, Oct 5, 2017 at 6:54 PM, Vishal Verma <vishal.l.verma@intel.com> wrote:
> Add an inject-error command to ndctl. This uses the error injection DSMs
> in ACPI6.2 to provide a generic error injection and management
> interface. Once can inject errors, and view as well as clear injected
> errors using these commands.
>
> Cc: Dan Williams <dan.j.williams@intel.com>
> Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
> ---
>  Documentation/ndctl/Makefile.am            |   1 +
>  Documentation/ndctl/ndctl-inject-error.txt | 108 +++++
>  Documentation/ndctl/ndctl.txt              |   1 +
>  builtin.h                                  |   1 +
>  contrib/ndctl                              |   5 +-
>  ndctl/Makefile.am                          |   3 +-
>  ndctl/inject-error.c                       | 745 +++++++++++++++++++++++++++++
>  ndctl/libndctl-nfit.h                      |   8 +
>  ndctl/ndctl.c                              |   1 +
>  util/json.c                                |  26 +
>  util/json.h                                |   3 +
>  util/size.h                                |   1 +
>  12 files changed, 901 insertions(+), 2 deletions(-)
>  create mode 100644 Documentation/ndctl/ndctl-inject-error.txt
>  create mode 100644 ndctl/inject-error.c
>
> diff --git a/Documentation/ndctl/Makefile.am b/Documentation/ndctl/Makefile.am
> index 229d908..615baf0 100644
> --- a/Documentation/ndctl/Makefile.am
> +++ b/Documentation/ndctl/Makefile.am
> @@ -30,6 +30,7 @@ man1_MANS = \
>         ndctl-create-namespace.1 \
>         ndctl-destroy-namespace.1 \
>         ndctl-check-namespace.1 \
> +       ndctl-inject-error.1 \
>         ndctl-list.1
>
>  CLEANFILES = $(man1_MANS)
> diff --git a/Documentation/ndctl/ndctl-inject-error.txt b/Documentation/ndctl/ndctl-inject-error.txt
> new file mode 100644
> index 0000000..bd9e197
> --- /dev/null
> +++ b/Documentation/ndctl/ndctl-inject-error.txt
> @@ -0,0 +1,108 @@
> +ndctl-inject-error(1)
> +=====================
> +
> +NAME
> +----
> +ndctl-inject-error - inject media errors at a namespace offset
> +
> +SYNOPSIS
> +--------
> +[verse]
> +'ndctl inject-error' <namespace> [<options>]
> +
> +include::namespace-description.txt[]
> +
> +ndctl-inject-error can be used to ask the platform to simulate media errors
> +in the nvdimm address space to aid debugging and development of features
> +related to error handling.
> +
> +WARNING: These commands are DANGEROUS and can cause data loss. They are
> +only provided for testing and debugging purposes.
> +
> +EXAMPLES
> +--------
> +
> +Inject errors in namespace0.0 at sector 12 for a 2 sectors (i.e. 12, 13)
> +[verse]
> +ndctl inject-error --sector=12 --count=2 namespace0.0
> +
> +Check status of injected errors on namespace0.0
> +[verse]
> +ndctl inject-error --status namespacce0.0
> +
> +Clear the injected errors at sector 12 for 2 sectors on namespace0.0
> +[verse]
> +ndctl inject-error --clear --sector=12 --count=2 namespacce0.0
> +
> +OPTIONS
> +-------
> +-S::
> +--sector=::
> +       Namespace sector offset in 512 byte sized sectors where the error is
> +       to be injected.

Let's use the term "block" instead of "sector" since the --media-error
json in ndctl list reports bad 'blocks' and the kernel interfaces use
'block'.

> +
> +       NOTE: The offset is interpreted in different ways based on the "mode"
> +       of the namespace. For "raw" mode, the offset is the base namespace
> +       offset. For "memory" mode (i.e. a "pfn" namespace), the offset is
> +       relative to the user-visible part of the namespace, and the offset
> +       introduced by the kernel's metadata will be accounted for. For a
> +       "sector" mode namespace (i.e. a "BTT" namespace), the offset is
> +       relative to the base namespace, as the BTT translation details are
> +       internal to the kernel, and can't be accounted for while injecting
> +       errors.
> +
> +-c::
> +--count=::
> +       Number of sectors to inject as errors. This is also in terms of fixed,
> +       512 byte sectors.
> +
> +-d::
> +--clear::

How about "--uninject"?

> +       This option will ask the platform to clear any injected errors for the
> +       specified sector offset, and count.
> +
> +       WARNING: This will not clear the kernel's internal "badrange" and
> +       "badblock" tracking - those can only be cleared by doing a write to

badrange is a kernel internal implementation detail. So we can just
say "This will not clear the kernel's internal bad block tracking"

> +       the affected locations. Hence use the --clear option only if you know
> +       exactly what you are doing. For normal usage, injected errors should
> +       only be cleared by doing writes. Do not expect have the original data
> +       intact after injecting an error, and clearing it using --clear - it
> +       will be lost, as the only "real" way to clear the error location is
> +       to write to it or zero it (truncate/hole-punch).
> +
> +-t::
> +--status::

"--query"?

> +       This option will retrieve the status of injected errors. Note that
> +       this will not retrieve all known/latent errors (i.e. non injected
> +       ones), and is NOT equivalent to performing an Address Range Scrub.
> +
> +-N::
> +--no-notify::
> +       This option is only valid when injecting errors. By default, the error
> +       inject command and will ask platform firmware to trigger a notification
> +       in the kernel, asking it to update its state of known errors.
> +       With this option, the error will still be injected, the kernel will not
> +       get a notification, and the error will appear as a latent media error
> +       when the location is accessed. If the platform firmware does not
> +       support this feature, this will have no effect.
> +
> +-v::
> +--verbose::
> +       Emit debug messages for the error injection process
> +
> +include::human-option.txt[]
> +
> +-r::
> +--region=::
> +include::xable-region-options.txt[]
> +
> +COPYRIGHT
> +---------
> +Copyright (c) 2016 - 2017, Intel Corporation. License GPLv2: GNU GPL
> +version 2 <http://gnu.org/licenses/gpl.html>.  This is free software:
> +you are free to change and redistribute it.  There is NO WARRANTY, to
> +the extent permitted by law.
> +
> +SEE ALSO
> +--------
> +linkndctl:ndctl-list[1],
> diff --git a/Documentation/ndctl/ndctl.txt b/Documentation/ndctl/ndctl.txt
> index b02f613..b2e2ab9 100644
> --- a/Documentation/ndctl/ndctl.txt
> +++ b/Documentation/ndctl/ndctl.txt
> @@ -50,6 +50,7 @@ linkndctl:ndctl-enable-namespace[1],
>  linkndctl:ndctl-disable-namespace[1],
>  linkndctl:ndctl-zero-labels[1],
>  linkndctl:ndctl-read-labels[1],
> +linkndctl:ndctl-inject-error[1],
>  linkndctl:ndctl-list[1],
>  https://www.kernel.org/doc/Documentation/nvdimm/nvdimm.txt[LIBNVDIMM
>  Overview],
> diff --git a/builtin.h b/builtin.h
> index 5c8b611..5e1b7ef 100644
> --- a/builtin.h
> +++ b/builtin.h
> @@ -35,6 +35,7 @@ int cmd_read_labels(int argc, const char **argv, void *ctx);
>  int cmd_write_labels(int argc, const char **argv, void *ctx);
>  int cmd_init_labels(int argc, const char **argv, void *ctx);
>  int cmd_check_labels(int argc, const char **argv, void *ctx);
> +int cmd_inject_error(int argc, const char **argv, void *ctx);
>  int cmd_list(int argc, const char **argv, void *ctx);
>  #ifdef ENABLE_TEST
>  int cmd_test(int argc, const char **argv, void *ctx);
> diff --git a/contrib/ndctl b/contrib/ndctl
> index c7d1b67..8745fb5 100755
> --- a/contrib/ndctl
> +++ b/contrib/ndctl
> @@ -91,7 +91,7 @@ __ndctlcomp()
>
>         COMPREPLY=( $( compgen -W "$1" -- "$2" ) )
>         for cword in "${COMPREPLY[@]}"; do
> -               if [[ "$cword" == @(--bus|--region|--type|--mode|--size|--dimm|--reconfig|--uuid|--name|--sector-size|--map|--namespace|--input|--output|--label-version|--align) ]]; then
> +               if [[ "$cword" == @(--bus|--region|--type|--mode|--size|--dimm|--reconfig|--uuid|--name|--sector-size|--map|--namespace|--input|--output|--label-version|--align|--sector|--count) ]]; then
>                         COMPREPLY[$i]="${cword}="
>                 else
>                         COMPREPLY[$i]="${cword} "
> @@ -257,6 +257,9 @@ __ndctl_comp_non_option_args()
>         zero-labels)
>                 opts="$(__ndctl_get_dimms -i) all"
>                 ;;
> +       inject-error)
> +               opts="$(__ndctl_get_ns -i)"
> +               ;;
>         *)
>                 return
>                 ;;
> diff --git a/ndctl/Makefile.am b/ndctl/Makefile.am
> index d346c04..a0cf500 100644
> --- a/ndctl/Makefile.am
> +++ b/ndctl/Makefile.am
> @@ -11,7 +11,8 @@ ndctl_SOURCES = ndctl.c \
>                  ../util/log.c \
>                 list.c \
>                 test.c \
> -               ../util/json.c
> +               ../util/json.c \
> +               inject-error.c
>
>  if ENABLE_SMART
>  ndctl_SOURCES += util/json-smart.c
> diff --git a/ndctl/inject-error.c b/ndctl/inject-error.c
> new file mode 100644
> index 0000000..a6bcc1b
> --- /dev/null
> +++ b/ndctl/inject-error.c
> @@ -0,0 +1,745 @@
> +/*
> + * Copyright(c) 2015-2017 Intel Corporation. All rights reserved.
> + *
> + * 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 <fcntl.h>
> +#include <errno.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <setjmp.h>
> +#include <limits.h>
> +#include <unistd.h>
> +#include <stdint.h>
> +#include <libkmod.h>
> +#include <stdbool.h>
> +#include <linux/fs.h>
> +#include <sys/wait.h>
> +#include <sys/stat.h>
> +#include <sys/mman.h>
> +#include <sys/types.h>
> +#include <sys/ioctl.h>
> +#include <linux/fiemap.h>
> +
> +#include <util/log.h>
> +#include <util/size.h>
> +#include <util/json.h>
> +#include <util/sysfs.h>
> +#include <json-c/json.h>
> +#include <util/filter.h>
> +#include <ndctl/libndctl.h>
> +#include <ccan/list/list.h>
> +#include <util/parse-options.h>
> +#include <ndctl/libndctl-nfit.h>
> +#include <ccan/array_size/array_size.h>
> +#include <ccan/short_types/short_types.h>
> +#ifdef HAVE_NDCTL_H
> +#include <linux/ndctl.h>
> +#else
> +#include <ndctl.h>
> +#endif
> +
> +#include "private.h"
> +#include <builtin.h>
> +#include <test.h>
> +
> +static bool verbose;
> +static struct parameters {
> +       const char *bus;
> +       const char *region;
> +       const char *namespace;
> +       const char *sector;
> +       const char *count;
> +       bool clear;
> +       bool status;
> +       bool notify;
> +       bool human;
> +} param;
> +
> +static struct inject_ctx {
> +       u64 sector;
> +       u64 count;
> +       u64 off_bytes;
> +       u64 len_bytes;
> +       u64 options;
> +       unsigned int op_mask;
> +       unsigned long flags;
> +       struct list_head bb_list;
> +} ictx;
> +
> +#define BASE_OPTIONS() \
> +OPT_STRING('b', "bus", &param.bus, "bus-id", \
> +       "limit namespace to a bus with an id or provider of <bus-id>"), \
> +OPT_STRING('r', "region", &param.region, "region-id", \
> +       "limit namespace to a region with an id or name of <region-id>"), \
> +OPT_BOOLEAN('v', "verbose", &verbose, "emit extra debug messages to stderr")
> +
> +#define INJECT_OPTIONS() \
> +OPT_STRING('S', "sector", &param.sector, "namespace sector offset", \
> +       "specify the sector at which to inject the error"), \
> +OPT_STRING('c', "count", &param.count, "count", \
> +       "specify the number of sectors of errors to inject"), \
> +OPT_BOOLEAN('d', "clear", &param.clear, \
> +       "send the ARS error inject clear DSM"), \
> +OPT_BOOLEAN('t', "status", &param.status, "get error injection status"), \
> +OPT_BOOLEAN('N', "no-notify", &param.notify, "firmware should not notify OS"), \
> +OPT_BOOLEAN('u', "human", &param.human, "use human friendly number formats ")
> +
> +static const struct option inject_options[] = {
> +       BASE_OPTIONS(),
> +       INJECT_OPTIONS(),
> +       OPT_END(),
> +};
> +
> +enum {
> +       OP_INJECT = 0,
> +       OP_CLEAR,
> +       OP_STATUS,
> +};
> +
> +struct bb {
> +       u64 sector;
> +       u64 count;
> +       struct list_node list;
> +};
> +
> +static int inject_init(void)
> +{
> +       if (!param.clear && !param.status) {
> +               ictx.op_mask |= 1 << OP_INJECT;
> +               ictx.options |= 1 << ND_ARS_ERR_INJ_OPT_NOTIFY;
> +               if (param.notify)
> +                       ictx.options &= ~(1 << ND_ARS_ERR_INJ_OPT_NOTIFY);
> +       }
> +       if (param.clear) {
> +               if (param.status) {
> +                       error("status is invalid with clear or inject\n");
> +                       return -EINVAL;
> +               }
> +               ictx.op_mask |= 1 << OP_CLEAR;
> +       }
> +       if (param.status) {
> +               if (param.sector || param.count) {
> +                       error("status is invalid with clear or inject\n");
> +                       return -EINVAL;
> +               }
> +               ictx.op_mask |= 1 << OP_STATUS;
> +       }
> +
> +       if (ictx.op_mask == 0) {
> +               error("Unable to determine operation\n");
> +               return -EINVAL;
> +       }
> +       ictx.op_mask &= (
> +               (1 << OP_INJECT) |
> +               (1 << OP_CLEAR) |
> +               (1 << OP_STATUS));
> +
> +       if (param.sector) {
> +               ictx.sector = parse_size64(param.sector);
> +               if (ictx.sector == ULLONG_MAX) {
> +                       error("Invalid sector: %s\n", param.sector);
> +                       return -EINVAL;
> +               }
> +               ictx.off_bytes = ictx.sector * 512;
> +       }
> +       if (param.count) {
> +               ictx.count = parse_size64(param.count);
> +               if (ictx.count == ULLONG_MAX) {
> +                       error("Invalid count: %s\n", param.count);
> +                       return -EINVAL;
> +               }
> +               ictx.len_bytes = ictx.count * 512;
> +       }
> +
> +       /* For inject or clear, an sector and count are required */
> +       if (ictx.op_mask & ((1 << OP_INJECT) | (1 << OP_CLEAR))) {
> +               if (!param.sector || !param.count) {
> +                       error("sector and count required for inject/clear\n");
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       if (param.human)
> +               ictx.flags |= UTIL_JSON_HUMAN;
> +
> +       list_head_init(&ictx.bb_list);
> +
> +       return 0;
> +}
> +
> +static int bus_has_ars_inject(struct ndctl_bus *bus)
> +{
> +       if (!ndctl_bus_has_nfit(bus))
> +               return 0;
> +
> +       if (ndctl_bus_is_nfit_cmd_supported(bus, NFIT_CMD_ARS_INJECT_SET) &&
> +               ndctl_bus_is_nfit_cmd_supported(bus, NFIT_CMD_ARS_INJECT_GET) &&
> +               ndctl_bus_is_nfit_cmd_supported(bus, NFIT_CMD_ARS_INJECT_CLEAR))
> +               return 1;
> +       else
> +               return 0;

This bus specific detail knowledge should be hidden in libndctl. I.e.
I want it to be the case that if another bus type with error injection
capabilities appeared tomorrow it could be enabled for error injection
just by updated the library. If a new bus requires changes to
"ndctl/inject-error.c" then the abstraction is broken.


> +}
> +
> +static struct ndctl_cmd *ndctl_bus_cmd_new_err_inj(struct ndctl_bus *bus)
> +{
> +       struct nd_cmd_ars_err_inj *err_inj;
> +       size_t size, cmd_length;
> +       struct nd_cmd_pkg *pkg;
> +       struct ndctl_cmd *cmd;
> +
> +       cmd_length = sizeof(struct nd_cmd_ars_err_inj);
> +       size = sizeof(*cmd) + sizeof(*pkg) + cmd_length;
> +       cmd = calloc(1, size);
> +       if (!cmd)
> +               return NULL;
> +
> +       cmd->bus = bus;
> +       ndctl_cmd_ref(cmd);
> +       cmd->type = ND_CMD_CALL;
> +       cmd->size = size;
> +       cmd->status = 1;
> +       pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
> +       pkg->nd_command = NFIT_CMD_ARS_INJECT_SET;
> +       pkg->nd_size_in = (2 * sizeof(u64)) + sizeof(u32);
> +       pkg->nd_size_out = cmd_length;
> +       pkg->nd_fw_size = cmd_length;
> +       err_inj = (struct nd_cmd_ars_err_inj *)&pkg->nd_payload[0];
> +       cmd->firmware_status = &err_inj->status;
> +
> +       return cmd;
> +}
> +
> +static struct ndctl_cmd *ndctl_bus_cmd_new_err_inj_clr(struct ndctl_bus *bus)
> +{
> +       struct nd_cmd_ars_err_inj_clr *err_inj_clr;
> +       size_t size, cmd_length;
> +       struct nd_cmd_pkg *pkg;
> +       struct ndctl_cmd *cmd;
> +
> +       cmd_length = sizeof(struct nd_cmd_ars_err_inj_clr);
> +       size = sizeof(*cmd) + sizeof(*pkg) + cmd_length;
> +       cmd = calloc(1, size);
> +       if (!cmd)
> +               return NULL;
> +
> +       cmd->bus = bus;
> +       ndctl_cmd_ref(cmd);
> +       cmd->type = ND_CMD_CALL;
> +       cmd->size = size;
> +       cmd->status = 1;
> +       pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
> +       pkg->nd_command = NFIT_CMD_ARS_INJECT_CLEAR;
> +       pkg->nd_size_in = 2 * sizeof(u64);
> +       pkg->nd_size_out = cmd_length;
> +       pkg->nd_fw_size = cmd_length;
> +       err_inj_clr = (struct nd_cmd_ars_err_inj_clr *)&pkg->nd_payload[0];
> +       cmd->firmware_status = &err_inj_clr->status;
> +
> +       return cmd;
> +}
> +
> +static struct ndctl_cmd *ndctl_bus_cmd_new_err_inj_stat(struct ndctl_bus *bus,
> +       u32 buf_size)
> +{
> +       struct nd_cmd_ars_err_inj_stat *err_inj_stat;
> +       size_t size, cmd_length;
> +       struct nd_cmd_pkg *pkg;
> +       struct ndctl_cmd *cmd;
> +
> +
> +       cmd_length = sizeof(struct nd_cmd_ars_err_inj_stat);
> +       size = sizeof(*cmd) + sizeof(*pkg) + cmd_length + buf_size;
> +       cmd = calloc(1, size);
> +       if (!cmd)
> +               return NULL;
> +
> +       cmd->bus = bus;
> +       ndctl_cmd_ref(cmd);
> +       cmd->type = ND_CMD_CALL;
> +       cmd->size = size;
> +       cmd->status = 1;
> +       pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
> +       pkg->nd_command = NFIT_CMD_ARS_INJECT_GET;
> +       pkg->nd_size_in = cmd_length;
> +       pkg->nd_size_out = cmd_length + buf_size;
> +       pkg->nd_fw_size = cmd_length + buf_size;
> +       err_inj_stat = (struct nd_cmd_ars_err_inj_stat *)&pkg->nd_payload[0];
> +       cmd->firmware_status = &err_inj_stat->status;
> +
> +       return cmd;
> +}
> +
> +static void translate_status(u32 status)
> +{
> +       if (status == ND_ARS_ERR_INJ_STATUS_NOT_SUPP)
> +               fprintf(stderr,
> +                       "error: error injection is not supported\n");
> +       if (status == ND_ARS_ERR_INJ_STATUS_INVALID_PARAM)
> +               fprintf(stderr, "error: invalid parameters\n");
> +}
> +
> +static int ndctl_bus_nfit_err_inj(struct ndctl_bus *bus, u64 offset,
> +       u64 length, u32 options)
> +{
> +       struct nd_cmd_ars_err_inj *err_inj;
> +       struct nd_cmd_pkg *pkg;
> +       struct ndctl_cmd *cmd;
> +       int rc;
> +
> +       if (!bus)
> +               return -EINVAL;
> +
> +       cmd = ndctl_bus_cmd_new_err_inj(bus);
> +       if (!cmd)
> +               return -ENOMEM;
> +
> +       pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
> +       err_inj = (struct nd_cmd_ars_err_inj *)&pkg->nd_payload[0];
> +       err_inj->err_inj_spa_range_base = offset;
> +       err_inj->err_inj_spa_range_length = length;
> +       err_inj->err_inj_options = options;
> +
> +       rc = ndctl_cmd_submit(cmd);
> +       if (rc) {
> +               fprintf(stderr, "Error submitting command: %d\n", rc);
> +               goto out;
> +       }
> +       translate_status(err_inj->status);
> +
> + out:
> +       ndctl_cmd_unref(cmd);
> +       return rc;
> +}
> +
> +static int ndctl_bus_nfit_err_inj_clr(struct ndctl_bus *bus, u64 offset,
> +       u64 length)
> +{
> +       struct nd_cmd_ars_err_inj_clr *err_inj_clr;
> +       struct nd_cmd_pkg *pkg;
> +       struct ndctl_cmd *cmd;
> +       int rc;
> +
> +       if (!bus)
> +               return -EINVAL;
> +
> +       cmd = ndctl_bus_cmd_new_err_inj_clr(bus);
> +       if (!cmd)
> +               return -ENOMEM;
> +
> +       pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
> +       err_inj_clr = (struct nd_cmd_ars_err_inj_clr *)&pkg->nd_payload[0];
> +       err_inj_clr->err_inj_clr_spa_range_base = offset;
> +       err_inj_clr->err_inj_clr_spa_range_length = length;
> +
> +       rc = ndctl_cmd_submit(cmd);
> +       if (rc) {
> +               fprintf(stderr, "Error submitting command: %d\n", rc);
> +               goto out;
> +       }
> +       translate_status(err_inj_clr->status);
> +       printf("Warning: Clearing injected errors here clears them in the\n");
> +       printf("badrange list in nfit_test, but the kernel won't 'forget'\n");
> +       printf("any entries it has found in a scrub until they are cleared\n");
> +       printf("through the normal process of writing the affected blocks\n\n");
> + out:
> +       ndctl_cmd_unref(cmd);
> +       return rc;
> +}
> +

All these command helpers belong in the library with fronting wrapper
calls that don't reference "nfit". For example we have
ndctl_bus_get_dimm_by_physical_address() fronting
ndctl_bus_nfit_translate_spa().
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [ndctl PATCH 6/8] ndctl/test: add a new unit test for inject-error
  2017-10-06  1:54 ` [ndctl PATCH 6/8] ndctl/test: add a new unit test for inject-error Vishal Verma
@ 2017-10-09 23:49   ` Verma, Vishal L
  0 siblings, 0 replies; 15+ messages in thread
From: Verma, Vishal L @ 2017-10-09 23:49 UTC (permalink / raw)
  To: linux-nvdimm

On Thu, 2017-10-05 at 19:54 -0600, Vishal Verma wrote:
> +do_tests()
> +{
> +       # TODO
> +       return
> +}

Not sure how this happened, but I managed to include a stale version of
this file or something, that only has the boilerplate. I'll fix up for
the next rev.
_______________________________________________
Linux-nvdimm mailing list
Linux-nvdimm@lists.01.org
https://lists.01.org/mailman/listinfo/linux-nvdimm

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2017-10-09 23:46 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-06  1:53 [ndctl PATCH 0/8] add an inject-error command to ndctl Vishal Verma
2017-10-06  1:53 ` [ndctl PATCH 1/8] libndctl: fix a memory leak in add_bus Vishal Verma
2017-10-07 15:44   ` Dan Williams
2017-10-06  1:53 ` [ndctl PATCH 2/8] ndctl, list: move the --human description to an include Vishal Verma
2017-10-07 15:55   ` Dan Williams
2017-10-06  1:54 ` [ndctl PATCH 3/8] libndctl: add APIs to get scrub count and to wait for a scrub Vishal Verma
2017-10-07 16:11   ` Dan Williams
2017-10-06  1:54 ` [ndctl PATCH 4/8] ccan/list: add a list_add_after helper Vishal Verma
2017-10-06 17:39   ` Dan Williams
2017-10-06  1:54 ` [ndctl PATCH 5/8] ndctl: add an inject-error command Vishal Verma
2017-10-09 16:27   ` Dan Williams
2017-10-06  1:54 ` [ndctl PATCH 6/8] ndctl/test: add a new unit test for inject-error Vishal Verma
2017-10-09 23:49   ` Verma, Vishal L
2017-10-06  1:54 ` [ndctl PATCH 7/8] ndctl/test: update existing unit tests to use error-inject Vishal Verma
2017-10-06  1:54 ` [ndctl PATCH 8/8] ndctl/test: add a new unit test for BTT error clearing Vishal Verma

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).