Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 58138820 authored by Dan Williams's avatar Dan Williams
Browse files

libnvdimm, nfit: handle unarmed dimms, mark namespaces read-only



Upon detection of an unarmed dimm in a region, arrange for descendant
BTT, PMEM, or BLK instances to be read-only.  A dimm is primarily marked
"unarmed" via flags passed by platform firmware (NFIT).

The flags in the NFIT memory device sub-structure indicate the state of
the data on the nvdimm relative to its energy source or last "flush to
persistence".  For the most part there is nothing the driver can do but
advertise the state of these flags in sysfs and emit a message if
firmware indicates that the contents of the device may be corrupted.
However, for the case of ACPI_NFIT_MEM_ARMED, the driver can arrange for
the block devices incorporating that nvdimm to be marked read-only.
This is a safe default as the data is still available and new writes are
held off until the administrator either forces read-write mode, or the
energy source becomes armed.

A 'read_only' attribute is added to REGION devices to allow for
overriding the default read-only policy of all descendant block devices.

Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent 0f51c4fa
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -668,6 +668,20 @@ static ssize_t serial_show(struct device *dev,
}
static DEVICE_ATTR_RO(serial);

static ssize_t flags_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	u16 flags = to_nfit_memdev(dev)->flags;

	return sprintf(buf, "%s%s%s%s%s\n",
			flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save " : "",
			flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore " : "",
			flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush " : "",
			flags & ACPI_NFIT_MEM_ARMED ? "arm " : "",
			flags & ACPI_NFIT_MEM_HEALTH_OBSERVED ? "smart " : "");
}
static DEVICE_ATTR_RO(flags);

static struct attribute *acpi_nfit_dimm_attributes[] = {
	&dev_attr_handle.attr,
	&dev_attr_phys_id.attr,
@@ -676,6 +690,7 @@ static struct attribute *acpi_nfit_dimm_attributes[] = {
	&dev_attr_format.attr,
	&dev_attr_serial.attr,
	&dev_attr_rev_id.attr,
	&dev_attr_flags.attr,
	NULL,
};

@@ -768,6 +783,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
		struct nvdimm *nvdimm;
		unsigned long flags = 0;
		u32 device_handle;
		u16 mem_flags;
		int rc;

		device_handle = __to_nfit_memdev(nfit_mem)->device_handle;
@@ -785,6 +801,10 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
		if (nfit_mem->bdw && nfit_mem->memdev_pmem)
			flags |= NDD_ALIASING;

		mem_flags = __to_nfit_memdev(nfit_mem)->flags;
		if (mem_flags & ACPI_NFIT_MEM_ARMED)
			flags |= NDD_UNARMED;

		rc = acpi_nfit_add_dimm(acpi_desc, nfit_mem, device_handle);
		if (rc)
			continue;
@@ -797,6 +817,17 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)

		nfit_mem->nvdimm = nvdimm;
		dimm_count++;

		if ((mem_flags & ACPI_NFIT_MEM_FAILED_MASK) == 0)
			continue;

		dev_info(acpi_desc->dev, "%s: failed: %s%s%s%s\n",
				nvdimm_name(nvdimm),
			mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save " : "",
			mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore " : "",
			mem_flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush " : "",
			mem_flags & ACPI_NFIT_MEM_ARMED ? "arm " : "");

	}

	return nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count);
+3 −0
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@

#define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba"
#define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66"
#define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \
		| ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \
		| ACPI_NFIT_MEM_ARMED)

enum nfit_uuids {
	NFIT_SPA_VOLATILE,
+2 −0
Original line number Diff line number Diff line
@@ -232,6 +232,7 @@ static int nd_blk_rw_bytes(struct nd_namespace_common *ndns,

static const struct block_device_operations nd_blk_fops = {
	.owner = THIS_MODULE,
	.revalidate_disk = nvdimm_revalidate_disk,
};

static int nd_blk_attach_disk(struct nd_namespace_common *ndns,
@@ -283,6 +284,7 @@ static int nd_blk_attach_disk(struct nd_namespace_common *ndns,
	}

	set_capacity(disk, available_disk_size >> SECTOR_SHIFT);
	revalidate_disk(disk);
	return 0;
}

+8 −2
Original line number Diff line number Diff line
@@ -1245,6 +1245,7 @@ static const struct block_device_operations btt_fops = {
	.owner =		THIS_MODULE,
	.rw_page =		btt_rw_page,
	.getgeo =		btt_getgeo,
	.revalidate_disk =	nvdimm_revalidate_disk,
};

static int btt_blk_init(struct btt *btt)
@@ -1292,6 +1293,7 @@ static int btt_blk_init(struct btt *btt)
		}
	}
	set_capacity(btt->btt_disk, btt->nlba * btt->sector_size >> 9);
	revalidate_disk(btt->btt_disk);

	return 0;
}
@@ -1346,7 +1348,11 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
		goto out_free;
	}

	if (btt->init_state != INIT_READY) {
	if (btt->init_state != INIT_READY && nd_region->ro) {
		dev_info(dev, "%s is read-only, unable to init btt metadata\n",
				dev_name(&nd_region->dev));
		goto out_free;
	} else if (btt->init_state != INIT_READY) {
		btt->num_arenas = (rawsize / ARENA_MAX_SIZE) +
			((rawsize % ARENA_MAX_SIZE) ? 1 : 0);
		dev_dbg(dev, "init: %d arenas for %llu rawsize\n",
@@ -1361,7 +1367,7 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
		ret = btt_meta_init(btt);
		if (ret) {
			dev_err(dev, "init: error in meta_init: %d\n", ret);
			return NULL;
			goto out_free;
		}
	}

+18 −0
Original line number Diff line number Diff line
@@ -227,6 +227,24 @@ int __nd_driver_register(struct nd_device_driver *nd_drv, struct module *owner,
}
EXPORT_SYMBOL(__nd_driver_register);

int nvdimm_revalidate_disk(struct gendisk *disk)
{
	struct device *dev = disk->driverfs_dev;
	struct nd_region *nd_region = to_nd_region(dev->parent);
	const char *pol = nd_region->ro ? "only" : "write";

	if (nd_region->ro == get_disk_ro(disk))
		return 0;

	dev_info(dev, "%s read-%s, marking %s read-%s\n",
			dev_name(&nd_region->dev), pol, disk->disk_name, pol);
	set_disk_ro(disk, nd_region->ro);

	return 0;

}
EXPORT_SYMBOL(nvdimm_revalidate_disk);

static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
		char *buf)
{
Loading