diff mbox

[ndctl,2/6] ndctl: trigger the kernel to re-read labels after zeroing

Message ID 20150618000923.13255.27307.stgit@dwillia2-desk3.amr.corp.intel.com (mailing list archive)
State Accepted
Commit e424ebceb1a8
Headers show

Commit Message

Dan Williams June 18, 2015, 12:09 a.m. UTC
When a dimm is enabled the kernel caches a copy of the label space in
memory.  ndctl_dimm_zero_labels() writes directly to the label area, so,
after the writes, we need to trigger the kernel to re-read labels.  This
two step process to write the labels and force a re-read is similar to
drive partitioning where after writing a partition table fdisk triggers
the kernel to re-read the partition table.

For libnvdimm the mechanism to trigger a label re-read is re-binding the
nvdimm driver to a given nmem device.  Note that disabling an nvdimm
while a region is active will cause namespace manipulations for that
region to fail.  The kernel will enforce that no label writes occur
while a dimm is a member of an active region, but if userspace races
itself to enable regions while an nmem device is disabled, it gets to
keep the pieces.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 lib/libndctl.c       |   80 +++++++++++++++++++++++++++++++++++++++++++-------
 lib/libndctl.sym     |    2 +
 lib/ndctl/libndctl.h |    2 +
 3 files changed, 73 insertions(+), 11 deletions(-)
diff mbox

Patch

diff --git a/lib/libndctl.c b/lib/libndctl.c
index 468c0853e024..b2452a737f68 100644
--- a/lib/libndctl.c
+++ b/lib/libndctl.c
@@ -108,6 +108,7 @@  struct ndctl_bus {
 
 /**
  * struct ndctl_dimm - memory device as identified by NFIT
+ * @module: kernel module (libnvdimm)
  * @handle: NFIT-handle value
  * @major: /dev/nmemX major character device number
  * @minor: /dev/nmemX minor character device number
@@ -123,6 +124,7 @@  struct ndctl_bus {
  * @dimm: dimm-id in the channel
  */
 struct ndctl_dimm {
+	struct kmod_module *module;
 	struct ndctl_bus *bus;
 	unsigned int handle, major, minor, serial;
 	unsigned short phys_id;
@@ -1063,6 +1065,8 @@  static int add_dimm(void *parent, int id, const char *dimm_base)
 	if (!dimm->dimm_path)
 		goto err_read;
 
+	dimm->module = to_module(ctx, buf);
+
 	dimm->handle = -1;
 	dimm->phys_id = -1;
 	dimm->vendor_id = -1;
@@ -1241,6 +1245,45 @@  NDCTL_EXPORT struct ndctl_ctx *ndctl_dimm_get_ctx(struct ndctl_dimm *dimm)
 	return dimm->bus->ctx;
 }
 
+NDCTL_EXPORT int ndctl_dimm_disable(struct ndctl_dimm *dimm)
+{
+	struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm);
+	const char *devname = ndctl_dimm_get_devname(dimm);
+
+	if (!ndctl_dimm_is_enabled(dimm))
+		return 0;
+
+	ndctl_unbind(ctx, dimm->dimm_path);
+
+	if (ndctl_dimm_is_enabled(dimm)) {
+		err(ctx, "%s: failed to disable\n", devname);
+		return -EBUSY;
+	}
+
+	dbg(ctx, "%s: disabled\n", devname);
+	return 0;
+}
+
+NDCTL_EXPORT int ndctl_dimm_enable(struct ndctl_dimm *dimm)
+{
+	const char *devname = ndctl_dimm_get_devname(dimm);
+	struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm);
+
+	if (ndctl_dimm_is_enabled(dimm))
+		return 0;
+
+	ndctl_bind(ctx, dimm->module, devname);
+
+	if (!ndctl_dimm_is_enabled(dimm)) {
+		err(ctx, "%s: failed to enable\n", devname);
+		return -ENXIO;
+	}
+
+	dbg(ctx, "%s: enabled\n", devname);
+
+	return 0;
+}
+
 NDCTL_EXPORT struct ndctl_dimm *ndctl_dimm_get_by_handle(struct ndctl_bus *bus,
 		unsigned int handle)
 {
@@ -1784,8 +1827,13 @@  NDCTL_EXPORT int ndctl_dimm_zero_labels(struct ndctl_dimm *dimm)
 {
 	struct ndctl_cmd *cmd_size, *cmd_read, *cmd_write;
 	struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm);
+	struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
 	int rc;
 
+	rc = ndctl_bus_wait_probe(bus);
+	if (rc < 0)
+		return rc;
+
 	if (ndctl_dimm_is_active(dimm)) {
 		dbg(ctx, "%s: regions active, abort label write\n",
 			ndctl_dimm_get_devname(dimm));
@@ -1797,40 +1845,50 @@  NDCTL_EXPORT int ndctl_dimm_zero_labels(struct ndctl_dimm *dimm)
 		return -ENOTTY;
 	rc = ndctl_cmd_submit(cmd_size);
 	if (rc || ndctl_cmd_get_firmware_status(cmd_size))
-		goto err_size;
+		goto out_size;
 
 	cmd_read = ndctl_dimm_cmd_new_cfg_read(cmd_size);
 	if (!cmd_read) {
 		rc = -ENOTTY;
-		goto err_size;
+		goto out_size;
 	}
 	rc = ndctl_cmd_submit(cmd_read);
 	if (rc || ndctl_cmd_get_firmware_status(cmd_read))
-		goto err_read;
+		goto out_read;
 
 	cmd_write = ndctl_dimm_cmd_new_cfg_write(cmd_read);
 	if (!cmd_write) {
 		rc = -ENOTTY;
-		goto err_read;
+		goto out_read;
 	}
 	if (ndctl_cmd_cfg_write_zero_data(cmd_write) < 0) {
 		rc = -ENXIO;
-		goto err_write;
+		goto out_write;
 	}
 	rc = ndctl_cmd_submit(cmd_write);
 	if (rc || ndctl_cmd_get_firmware_status(cmd_write))
-		goto err_write;
+		goto out_write;
 
-	return 0;
+	/*
+	 * If the dimm is already disabled the kernel is not holding a cached
+	 * copy of the label space.
+	 */
+	if (!ndctl_dimm_is_enabled(dimm))
+		goto out_write;
+
+	rc = ndctl_dimm_disable(dimm);
+	if (rc)
+		goto out_write;
+	rc = ndctl_dimm_enable(dimm);
 
- err_write:
+ out_write:
 	ndctl_cmd_unref(cmd_write);
- err_read:
+ out_read:
 	ndctl_cmd_unref(cmd_read);
- err_size:
+ out_size:
 	ndctl_cmd_unref(cmd_size);
 
-	return rc < 0 ? rc : -ENXIO;
+	return rc;
 }
 
 NDCTL_EXPORT void ndctl_cmd_unref(struct ndctl_cmd *cmd)
diff --git a/lib/libndctl.sym b/lib/libndctl.sym
index 18be6f286c4a..2435dcc0ee5e 100644
--- a/lib/libndctl.sym
+++ b/lib/libndctl.sym
@@ -57,6 +57,8 @@  global:
 	ndctl_dimm_get_by_handle;
 	ndctl_dimm_is_active;
 	ndctl_dimm_is_enabled;
+	ndctl_dimm_disable;
+	ndctl_dimm_enable;
 	ndctl_dimm_cmd_new_vendor_specific;
 	ndctl_cmd_vendor_set_input;
 	ndctl_cmd_vendor_get_output_size;
diff --git a/lib/ndctl/libndctl.h b/lib/ndctl/libndctl.h
index 2531072dd18f..2a38e913db3a 100644
--- a/lib/ndctl/libndctl.h
+++ b/lib/ndctl/libndctl.h
@@ -142,6 +142,8 @@  struct ndctl_dimm *ndctl_dimm_get_by_handle(struct ndctl_bus *bus,
 		unsigned int handle);
 int ndctl_dimm_is_active(struct ndctl_dimm *dimm);
 int ndctl_dimm_is_enabled(struct ndctl_dimm *dimm);
+int ndctl_dimm_disable(struct ndctl_dimm *dimm);
+int ndctl_dimm_enable(struct ndctl_dimm *dimm);
 
 struct ndctl_cmd;
 struct ndctl_cmd *ndctl_dimm_cmd_new_vendor_specific(struct ndctl_dimm *dimm,