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: Vishal Verma <vishal.l.verma@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
Родитель
ec92777f2b
Коммит
39c686b862
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -147,75 +147,153 @@ static struct nfit_test *to_nfit_test(struct device *dev)
|
|||
return container_of(pdev, struct nfit_test, pdev);
|
||||
}
|
||||
|
||||
static int nfit_test_cmd_get_config_size(struct nd_cmd_get_config_size *nd_cmd,
|
||||
unsigned int buf_len)
|
||||
{
|
||||
if (buf_len < sizeof(*nd_cmd))
|
||||
return -EINVAL;
|
||||
|
||||
nd_cmd->status = 0;
|
||||
nd_cmd->config_size = LABEL_SIZE;
|
||||
nd_cmd->max_xfer = SZ_4K;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
if (offset >= LABEL_SIZE)
|
||||
return -EINVAL;
|
||||
if (nd_cmd->in_length + sizeof(*nd_cmd) > buf_len)
|
||||
return -EINVAL;
|
||||
|
||||
nd_cmd->status = 0;
|
||||
len = min(nd_cmd->in_length, LABEL_SIZE - offset);
|
||||
memcpy(nd_cmd->out_buf, label + offset, len);
|
||||
rc = buf_len - sizeof(*nd_cmd) - len;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
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;
|
||||
if (offset >= LABEL_SIZE)
|
||||
return -EINVAL;
|
||||
if (nd_cmd->in_length + sizeof(*nd_cmd) + 4 > buf_len)
|
||||
return -EINVAL;
|
||||
|
||||
status = (void *)nd_cmd + nd_cmd->in_length + sizeof(*nd_cmd);
|
||||
*status = 0;
|
||||
len = min(nd_cmd->in_length, LABEL_SIZE - offset);
|
||||
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);
|
||||
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
||||
int i, rc;
|
||||
int i, rc = 0;
|
||||
|
||||
if (!nfit_mem || !test_bit(cmd, &nfit_mem->dsm_mask))
|
||||
return -ENOTTY;
|
||||
if (nvdimm) {
|
||||
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
|
||||
|
||||
/* 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])
|
||||
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;
|
||||
if (i >= ARRAY_SIZE(handle))
|
||||
return -ENXIO;
|
||||
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_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;
|
||||
}
|
||||
case ND_CMD_GET_CONFIG_DATA: {
|
||||
struct nd_cmd_get_config_data_hdr *nd_cmd = buf;
|
||||
unsigned int len, offset = nd_cmd->in_offset;
|
||||
|
||||
if (buf_len < sizeof(*nd_cmd))
|
||||
return -EINVAL;
|
||||
if (offset >= LABEL_SIZE)
|
||||
return -EINVAL;
|
||||
if (nd_cmd->in_length + sizeof(*nd_cmd) > buf_len)
|
||||
return -EINVAL;
|
||||
|
||||
nd_cmd->status = 0;
|
||||
len = min(nd_cmd->in_length, LABEL_SIZE - offset);
|
||||
memcpy(nd_cmd->out_buf, t->label[i] + offset, len);
|
||||
rc = buf_len - sizeof(*nd_cmd) - len;
|
||||
break;
|
||||
}
|
||||
case ND_CMD_SET_CONFIG_DATA: {
|
||||
struct nd_cmd_set_config_hdr *nd_cmd = buf;
|
||||
unsigned int len, offset = nd_cmd->in_offset;
|
||||
u32 *status;
|
||||
|
||||
if (buf_len < sizeof(*nd_cmd))
|
||||
return -EINVAL;
|
||||
if (offset >= LABEL_SIZE)
|
||||
return -EINVAL;
|
||||
if (nd_cmd->in_length + sizeof(*nd_cmd) + 4 > buf_len)
|
||||
return -EINVAL;
|
||||
|
||||
status = buf + 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);
|
||||
rc = buf_len - sizeof(*nd_cmd) - (len + 4);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче