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

Commit 39c686b8 authored by Vishal Verma's avatar Vishal Verma Committed by Dan Williams
Browse files

libnvdimm: Add DSM support for Address Range Scrub commands

Add support for the three ARS DSM commands:
- Query ARS Capabilities - Queries the firmware to check if a given
  range supports scrub, and if so, which type (persistent vs. volatile)
- Start ARS - Starts a scrub for a given range/type
- Query ARS Status - Checks status of a previously started scrub, and
  provides the error logs if any.

  The commands are described by the example DSM spec at:
  http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf



Also add these commands to the nfit_test test framework, and return
canned data.

Signed-off-by: default avatarVishal Verma <vishal.l.verma@intel.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent ec92777f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -868,6 +868,7 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
	struct acpi_device *adev;
	int i;

	nd_desc->dsm_mask = acpi_desc->bus_dsm_force_en;
	adev = to_acpi_dev(acpi_desc);
	if (!adev)
		return;
+1 −0
Original line number Diff line number Diff line
@@ -107,6 +107,7 @@ struct acpi_nfit_desc {
	struct nvdimm_bus *nvdimm_bus;
	struct device *dev;
	unsigned long dimm_dsm_force_en;
	unsigned long bus_dsm_force_en;
	int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,
			void *iobuf, u64 len, int rw);
};
+10 −0
Original line number Diff line number Diff line
@@ -111,6 +111,11 @@ enum {
	ND_CMD_VENDOR = 9,
};

enum {
	ND_ARS_VOLATILE = 1,
	ND_ARS_PERSISTENT = 2,
};

static inline const char *nvdimm_bus_cmd_name(unsigned cmd)
{
	static const char * const names[] = {
@@ -194,4 +199,9 @@ enum nd_driver_flags {
enum {
	ND_MIN_NAMESPACE_SIZE = 0x00400000,
};

enum ars_masks {
	ARS_STATUS_MASK = 0x0000FFFF,
	ARS_EXT_STATUS_SHIFT = 16,
};
#endif /* __NDCTL_H__ */
+140 −59
Original line number Diff line number Diff line
@@ -147,40 +147,24 @@ static struct nfit_test *to_nfit_test(struct device *dev)
	return container_of(pdev, struct nfit_test, pdev);
}

static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
		struct nvdimm *nvdimm, unsigned int cmd, void *buf,
static int nfit_test_cmd_get_config_size(struct nd_cmd_get_config_size *nd_cmd,
		unsigned int buf_len)
{
	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
	struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc);
	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
	int i, rc;

	if (!nfit_mem || !test_bit(cmd, &nfit_mem->dsm_mask))
		return -ENOTTY;

	/* lookup label space for the given dimm */
	for (i = 0; i < ARRAY_SIZE(handle); i++)
		if (__to_nfit_memdev(nfit_mem)->device_handle == handle[i])
			break;
	if (i >= ARRAY_SIZE(handle))
		return -ENXIO;

	switch (cmd) {
	case ND_CMD_GET_CONFIG_SIZE: {
		struct nd_cmd_get_config_size *nd_cmd = buf;

	if (buf_len < sizeof(*nd_cmd))
		return -EINVAL;

	nd_cmd->status = 0;
	nd_cmd->config_size = LABEL_SIZE;
	nd_cmd->max_xfer = SZ_4K;
		rc = 0;
		break;

	return 0;
}
	case ND_CMD_GET_CONFIG_DATA: {
		struct nd_cmd_get_config_data_hdr *nd_cmd = buf;

static int nfit_test_cmd_get_config_data(struct nd_cmd_get_config_data_hdr
		*nd_cmd, unsigned int buf_len, void *label)
{
	unsigned int len, offset = nd_cmd->in_offset;
	int rc;

	if (buf_len < sizeof(*nd_cmd))
		return -EINVAL;
@@ -191,14 +175,18 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,

	nd_cmd->status = 0;
	len = min(nd_cmd->in_length, LABEL_SIZE - offset);
		memcpy(nd_cmd->out_buf, t->label[i] + offset, len);
	memcpy(nd_cmd->out_buf, label + offset, len);
	rc = buf_len - sizeof(*nd_cmd) - len;
		break;

	return rc;
}
	case ND_CMD_SET_CONFIG_DATA: {
		struct nd_cmd_set_config_hdr *nd_cmd = buf;

static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd,
		unsigned int buf_len, void *label)
{
	unsigned int len, offset = nd_cmd->in_offset;
	u32 *status;
	int rc;

	if (buf_len < sizeof(*nd_cmd))
		return -EINVAL;
@@ -207,16 +195,106 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
	if (nd_cmd->in_length + sizeof(*nd_cmd) + 4 > buf_len)
		return -EINVAL;

		status = buf + nd_cmd->in_length + sizeof(*nd_cmd);
	status = (void *)nd_cmd + nd_cmd->in_length + sizeof(*nd_cmd);
	*status = 0;
	len = min(nd_cmd->in_length, LABEL_SIZE - offset);
		memcpy(t->label[i] + offset, nd_cmd->in_buf, len);
	memcpy(label + offset, nd_cmd->in_buf, len);
	rc = buf_len - sizeof(*nd_cmd) - (len + 4);

	return rc;
}

static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd,
		unsigned int buf_len)
{
	if (buf_len < sizeof(*nd_cmd))
		return -EINVAL;

	nd_cmd->max_ars_out = 256;
	nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16;

	return 0;
}

static int nfit_test_cmd_ars_start(struct nd_cmd_ars_start *nd_cmd,
		unsigned int buf_len)
{
	if (buf_len < sizeof(*nd_cmd))
		return -EINVAL;

	nd_cmd->status = 0;

	return 0;
}

static int nfit_test_cmd_ars_status(struct nd_cmd_ars_status *nd_cmd,
		unsigned int buf_len)
{
	if (buf_len < sizeof(*nd_cmd))
		return -EINVAL;

	nd_cmd->out_length = 256;
	nd_cmd->num_records = 0;
	nd_cmd->status = 0;

	return 0;
}

static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
		struct nvdimm *nvdimm, unsigned int cmd, void *buf,
		unsigned int buf_len)
{
	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
	struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc);
	int i, rc = 0;

	if (nvdimm) {
		struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);

		if (!nfit_mem || !test_bit(cmd, &nfit_mem->dsm_mask))
			return -ENOTTY;

		/* lookup label space for the given dimm */
		for (i = 0; i < ARRAY_SIZE(handle); i++)
			if (__to_nfit_memdev(nfit_mem)->device_handle ==
					handle[i])
				break;
		if (i >= ARRAY_SIZE(handle))
			return -ENXIO;

		switch (cmd) {
		case ND_CMD_GET_CONFIG_SIZE:
			rc = nfit_test_cmd_get_config_size(buf, buf_len);
			break;
		case ND_CMD_GET_CONFIG_DATA:
			rc = nfit_test_cmd_get_config_data(buf, buf_len,
				t->label[i]);
			break;
		case ND_CMD_SET_CONFIG_DATA:
			rc = nfit_test_cmd_set_config_data(buf, buf_len,
				t->label[i]);
			break;
		default:
			return -ENOTTY;
		}
	} else {
		if (!nd_desc || !test_bit(cmd, &nd_desc->dsm_mask))
			return -ENOTTY;

		switch (cmd) {
		case ND_CMD_ARS_CAP:
			rc = nfit_test_cmd_ars_cap(buf, buf_len);
			break;
		case ND_CMD_ARS_START:
			rc = nfit_test_cmd_ars_start(buf, buf_len);
			break;
		case ND_CMD_ARS_STATUS:
			rc = nfit_test_cmd_ars_status(buf, buf_len);
			break;
		default:
			return -ENOTTY;
		}
	}

	return rc;
}
@@ -876,6 +954,9 @@ static void nfit_test0_setup(struct nfit_test *t)
	set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_dsm_force_en);
	set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en);
	set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en);
	set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en);
	set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en);
	set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en);
	nd_desc = &acpi_desc->nd_desc;
	nd_desc->ndctl = nfit_test_ctl;
}