nfit, libnvdimm: clear poison command support
Add the boiler-plate for a 'clear error' command based on section 9.20.7.6 "Function Index 4 - Clear Uncorrectable Error" from the ACPI 6.1 specification, and add a reference implementation in nfit_test. Reviewed-by: Vishal Verma <vishal.l.verma@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
@ -87,6 +87,7 @@ static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc)
|
|||||||
|
|
||||||
static int xlat_status(void *buf, unsigned int cmd)
|
static int xlat_status(void *buf, unsigned int cmd)
|
||||||
{
|
{
|
||||||
|
struct nd_cmd_clear_error *clear_err;
|
||||||
struct nd_cmd_ars_status *ars_status;
|
struct nd_cmd_ars_status *ars_status;
|
||||||
struct nd_cmd_ars_start *ars_start;
|
struct nd_cmd_ars_start *ars_start;
|
||||||
struct nd_cmd_ars_cap *ars_cap;
|
struct nd_cmd_ars_cap *ars_cap;
|
||||||
@ -149,6 +150,15 @@ static int xlat_status(void *buf, unsigned int cmd)
|
|||||||
if (ars_status->status >> 16)
|
if (ars_status->status >> 16)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
break;
|
break;
|
||||||
|
case ND_CMD_CLEAR_ERROR:
|
||||||
|
clear_err = buf;
|
||||||
|
if (clear_err->status & 0xffff)
|
||||||
|
return -EIO;
|
||||||
|
if (!clear_err->cleared)
|
||||||
|
return -EIO;
|
||||||
|
if (clear_err->length > clear_err->cleared)
|
||||||
|
return clear_err->cleared;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1002,7 +1012,7 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
|
|||||||
if (!adev)
|
if (!adev)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = ND_CMD_ARS_CAP; i <= ND_CMD_ARS_STATUS; i++)
|
for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++)
|
||||||
if (acpi_check_dsm(adev->handle, uuid, 1, 1ULL << i))
|
if (acpi_check_dsm(adev->handle, uuid, 1, 1ULL << i))
|
||||||
set_bit(i, &nd_desc->dsm_mask);
|
set_bit(i, &nd_desc->dsm_mask);
|
||||||
}
|
}
|
||||||
|
@ -421,6 +421,12 @@ static const struct nd_cmd_desc __nd_cmd_bus_descs[] = {
|
|||||||
.out_num = 3,
|
.out_num = 3,
|
||||||
.out_sizes = { 4, 4, UINT_MAX, },
|
.out_sizes = { 4, 4, UINT_MAX, },
|
||||||
},
|
},
|
||||||
|
[ND_CMD_CLEAR_ERROR] = {
|
||||||
|
.in_num = 2,
|
||||||
|
.in_sizes = { 8, 8, },
|
||||||
|
.out_num = 3,
|
||||||
|
.out_sizes = { 4, 4, 8, },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd)
|
const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd)
|
||||||
@ -489,6 +495,13 @@ void wait_nvdimm_bus_probe_idle(struct device *dev)
|
|||||||
} while (true);
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pmem_active(struct device *dev, void *data)
|
||||||
|
{
|
||||||
|
if (is_nd_pmem(dev) && dev->driver)
|
||||||
|
return -EBUSY;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* set_config requires an idle interleave set */
|
/* set_config requires an idle interleave set */
|
||||||
static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus,
|
static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus,
|
||||||
struct nvdimm *nvdimm, unsigned int cmd)
|
struct nvdimm *nvdimm, unsigned int cmd)
|
||||||
@ -503,6 +516,11 @@ static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* require clear error to go through the pmem driver */
|
||||||
|
if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR)
|
||||||
|
return device_for_each_child(&nvdimm_bus->dev, NULL,
|
||||||
|
pmem_active);
|
||||||
|
|
||||||
if (!nvdimm || cmd != ND_CMD_SET_CONFIG_DATA)
|
if (!nvdimm || cmd != ND_CMD_SET_CONFIG_DATA)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -551,6 +569,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
|
|||||||
case ND_CMD_VENDOR:
|
case ND_CMD_VENDOR:
|
||||||
case ND_CMD_SET_CONFIG_DATA:
|
case ND_CMD_SET_CONFIG_DATA:
|
||||||
case ND_CMD_ARS_START:
|
case ND_CMD_ARS_START:
|
||||||
|
case ND_CMD_CLEAR_ERROR:
|
||||||
dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n",
|
dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n",
|
||||||
nvdimm ? nvdimm_cmd_name(cmd)
|
nvdimm ? nvdimm_cmd_name(cmd)
|
||||||
: nvdimm_bus_cmd_name(cmd));
|
: nvdimm_bus_cmd_name(cmd));
|
||||||
|
@ -98,6 +98,14 @@ struct nd_cmd_ars_status {
|
|||||||
} __packed records[0];
|
} __packed records[0];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct nd_cmd_clear_error {
|
||||||
|
__u64 address;
|
||||||
|
__u64 length;
|
||||||
|
__u32 status;
|
||||||
|
__u8 reserved[4];
|
||||||
|
__u64 cleared;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ND_CMD_IMPLEMENTED = 0,
|
ND_CMD_IMPLEMENTED = 0,
|
||||||
|
|
||||||
@ -105,6 +113,7 @@ enum {
|
|||||||
ND_CMD_ARS_CAP = 1,
|
ND_CMD_ARS_CAP = 1,
|
||||||
ND_CMD_ARS_START = 2,
|
ND_CMD_ARS_START = 2,
|
||||||
ND_CMD_ARS_STATUS = 3,
|
ND_CMD_ARS_STATUS = 3,
|
||||||
|
ND_CMD_CLEAR_ERROR = 4,
|
||||||
|
|
||||||
/* per-dimm commands */
|
/* per-dimm commands */
|
||||||
ND_CMD_SMART = 1,
|
ND_CMD_SMART = 1,
|
||||||
@ -129,6 +138,7 @@ static inline const char *nvdimm_bus_cmd_name(unsigned cmd)
|
|||||||
[ND_CMD_ARS_CAP] = "ars_cap",
|
[ND_CMD_ARS_CAP] = "ars_cap",
|
||||||
[ND_CMD_ARS_START] = "ars_start",
|
[ND_CMD_ARS_START] = "ars_start",
|
||||||
[ND_CMD_ARS_STATUS] = "ars_status",
|
[ND_CMD_ARS_STATUS] = "ars_status",
|
||||||
|
[ND_CMD_CLEAR_ERROR] = "clear_error",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (cmd < ARRAY_SIZE(names) && names[cmd])
|
if (cmd < ARRAY_SIZE(names) && names[cmd])
|
||||||
@ -187,6 +197,9 @@ static inline const char *nvdimm_cmd_name(unsigned cmd)
|
|||||||
#define ND_IOCTL_ARS_STATUS _IOWR(ND_IOCTL, ND_CMD_ARS_STATUS,\
|
#define ND_IOCTL_ARS_STATUS _IOWR(ND_IOCTL, ND_CMD_ARS_STATUS,\
|
||||||
struct nd_cmd_ars_status)
|
struct nd_cmd_ars_status)
|
||||||
|
|
||||||
|
#define ND_IOCTL_CLEAR_ERROR _IOWR(ND_IOCTL, ND_CMD_CLEAR_ERROR,\
|
||||||
|
struct nd_cmd_clear_error)
|
||||||
|
|
||||||
#define ND_DEVICE_DIMM 1 /* nd_dimm: container for "config data" */
|
#define ND_DEVICE_DIMM 1 /* nd_dimm: container for "config data" */
|
||||||
#define ND_DEVICE_REGION_PMEM 2 /* nd_region: (parent of PMEM namespaces) */
|
#define ND_DEVICE_REGION_PMEM 2 /* nd_region: (parent of PMEM namespaces) */
|
||||||
#define ND_DEVICE_REGION_BLK 3 /* nd_region: (parent of BLK namespaces) */
|
#define ND_DEVICE_REGION_BLK 3 /* nd_region: (parent of BLK namespaces) */
|
||||||
|
@ -223,6 +223,7 @@ static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define NFIT_TEST_ARS_RECORDS 4
|
#define NFIT_TEST_ARS_RECORDS 4
|
||||||
|
#define NFIT_TEST_CLEAR_ERR_UNIT 256
|
||||||
|
|
||||||
static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd,
|
static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd,
|
||||||
unsigned int buf_len)
|
unsigned int buf_len)
|
||||||
@ -233,6 +234,7 @@ static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd,
|
|||||||
nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status)
|
nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status)
|
||||||
+ NFIT_TEST_ARS_RECORDS * sizeof(struct nd_ars_record);
|
+ NFIT_TEST_ARS_RECORDS * sizeof(struct nd_ars_record);
|
||||||
nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16;
|
nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16;
|
||||||
|
nd_cmd->clear_err_unit = NFIT_TEST_CLEAR_ERR_UNIT;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -306,6 +308,28 @@ static int nfit_test_cmd_ars_status(struct ars_state *ars_state,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nfit_test_cmd_clear_error(struct nd_cmd_clear_error *clear_err,
|
||||||
|
unsigned int buf_len, int *cmd_rc)
|
||||||
|
{
|
||||||
|
const u64 mask = NFIT_TEST_CLEAR_ERR_UNIT - 1;
|
||||||
|
if (buf_len < sizeof(*clear_err))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if ((clear_err->address & mask) || (clear_err->length & mask))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Report 'all clear' success for all commands even though a new
|
||||||
|
* scrub will find errors again. This is enough to have the
|
||||||
|
* error removed from the 'badblocks' tracking in the pmem
|
||||||
|
* driver.
|
||||||
|
*/
|
||||||
|
clear_err->status = 0;
|
||||||
|
clear_err->cleared = clear_err->length;
|
||||||
|
*cmd_rc = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
||||||
struct nvdimm *nvdimm, unsigned int cmd, void *buf,
|
struct nvdimm *nvdimm, unsigned int cmd, void *buf,
|
||||||
unsigned int buf_len, int *cmd_rc)
|
unsigned int buf_len, int *cmd_rc)
|
||||||
@ -365,6 +389,9 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|||||||
rc = nfit_test_cmd_ars_status(ars_state, buf, buf_len,
|
rc = nfit_test_cmd_ars_status(ars_state, buf, buf_len,
|
||||||
cmd_rc);
|
cmd_rc);
|
||||||
break;
|
break;
|
||||||
|
case ND_CMD_CLEAR_ERROR:
|
||||||
|
rc = nfit_test_cmd_clear_error(buf, buf_len, cmd_rc);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return -ENOTTY;
|
return -ENOTTY;
|
||||||
}
|
}
|
||||||
@ -1230,6 +1257,7 @@ static void nfit_test0_setup(struct nfit_test *t)
|
|||||||
set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_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_START, &acpi_desc->bus_dsm_force_en);
|
||||||
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en);
|
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en);
|
||||||
|
set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_dsm_force_en);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nfit_test1_setup(struct nfit_test *t)
|
static void nfit_test1_setup(struct nfit_test *t)
|
||||||
@ -1290,6 +1318,7 @@ static void nfit_test1_setup(struct nfit_test *t)
|
|||||||
set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_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_START, &acpi_desc->bus_dsm_force_en);
|
||||||
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en);
|
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en);
|
||||||
|
set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_dsm_force_en);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa,
|
static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa,
|
||||||
|
Reference in New Issue
Block a user