Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+72 -1
+11 -1
drivers/acpi/nfit.c
··· 87 87 88 88 static int xlat_status(void *buf, unsigned int cmd) 89 89 { 90 + struct nd_cmd_clear_error *clear_err; 90 91 struct nd_cmd_ars_status *ars_status; 91 92 struct nd_cmd_ars_start *ars_start; 92 93 struct nd_cmd_ars_cap *ars_cap; ··· 149 148 /* Unknown status */ 150 149 if (ars_status->status >> 16) 151 150 return -EIO; 151 + break; 152 + case ND_CMD_CLEAR_ERROR: 153 + clear_err = buf; 154 + if (clear_err->status & 0xffff) 155 + return -EIO; 156 + if (!clear_err->cleared) 157 + return -EIO; 158 + if (clear_err->length > clear_err->cleared) 159 + return clear_err->cleared; 152 160 break; 153 161 default: 154 162 break; ··· 1012 1002 if (!adev) 1013 1003 return; 1014 1004 1015 - for (i = ND_CMD_ARS_CAP; i <= ND_CMD_ARS_STATUS; i++) 1005 + for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++) 1016 1006 if (acpi_check_dsm(adev->handle, uuid, 1, 1ULL << i)) 1017 1007 set_bit(i, &nd_desc->dsm_mask); 1018 1008 }
+19
drivers/nvdimm/bus.c
··· 421 421 .out_num = 3, 422 422 .out_sizes = { 4, 4, UINT_MAX, }, 423 423 }, 424 + [ND_CMD_CLEAR_ERROR] = { 425 + .in_num = 2, 426 + .in_sizes = { 8, 8, }, 427 + .out_num = 3, 428 + .out_sizes = { 4, 4, 8, }, 429 + }, 424 430 }; 425 431 426 432 const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd) ··· 495 489 } while (true); 496 490 } 497 491 492 + static int pmem_active(struct device *dev, void *data) 493 + { 494 + if (is_nd_pmem(dev) && dev->driver) 495 + return -EBUSY; 496 + return 0; 497 + } 498 + 498 499 /* set_config requires an idle interleave set */ 499 500 static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus, 500 501 struct nvdimm *nvdimm, unsigned int cmd) ··· 515 502 if (rc) 516 503 return rc; 517 504 } 505 + 506 + /* require clear error to go through the pmem driver */ 507 + if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR) 508 + return device_for_each_child(&nvdimm_bus->dev, NULL, 509 + pmem_active); 518 510 519 511 if (!nvdimm || cmd != ND_CMD_SET_CONFIG_DATA) 520 512 return 0; ··· 569 551 case ND_CMD_VENDOR: 570 552 case ND_CMD_SET_CONFIG_DATA: 571 553 case ND_CMD_ARS_START: 554 + case ND_CMD_CLEAR_ERROR: 572 555 dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n", 573 556 nvdimm ? nvdimm_cmd_name(cmd) 574 557 : nvdimm_bus_cmd_name(cmd));
+13
include/uapi/linux/ndctl.h
··· 98 98 } __packed records[0]; 99 99 } __packed; 100 100 101 + struct nd_cmd_clear_error { 102 + __u64 address; 103 + __u64 length; 104 + __u32 status; 105 + __u8 reserved[4]; 106 + __u64 cleared; 107 + } __packed; 108 + 101 109 enum { 102 110 ND_CMD_IMPLEMENTED = 0, 103 111 ··· 113 105 ND_CMD_ARS_CAP = 1, 114 106 ND_CMD_ARS_START = 2, 115 107 ND_CMD_ARS_STATUS = 3, 108 + ND_CMD_CLEAR_ERROR = 4, 116 109 117 110 /* per-dimm commands */ 118 111 ND_CMD_SMART = 1, ··· 138 129 [ND_CMD_ARS_CAP] = "ars_cap", 139 130 [ND_CMD_ARS_START] = "ars_start", 140 131 [ND_CMD_ARS_STATUS] = "ars_status", 132 + [ND_CMD_CLEAR_ERROR] = "clear_error", 141 133 }; 142 134 143 135 if (cmd < ARRAY_SIZE(names) && names[cmd]) ··· 196 186 197 187 #define ND_IOCTL_ARS_STATUS _IOWR(ND_IOCTL, ND_CMD_ARS_STATUS,\ 198 188 struct nd_cmd_ars_status) 189 + 190 + #define ND_IOCTL_CLEAR_ERROR _IOWR(ND_IOCTL, ND_CMD_CLEAR_ERROR,\ 191 + struct nd_cmd_clear_error) 199 192 200 193 #define ND_DEVICE_DIMM 1 /* nd_dimm: container for "config data" */ 201 194 #define ND_DEVICE_REGION_PMEM 2 /* nd_region: (parent of PMEM namespaces) */
+29
tools/testing/nvdimm/test/nfit.c
··· 223 223 } 224 224 225 225 #define NFIT_TEST_ARS_RECORDS 4 226 + #define NFIT_TEST_CLEAR_ERR_UNIT 256 226 227 227 228 static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd, 228 229 unsigned int buf_len) ··· 234 233 nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status) 235 234 + NFIT_TEST_ARS_RECORDS * sizeof(struct nd_ars_record); 236 235 nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16; 236 + nd_cmd->clear_err_unit = NFIT_TEST_CLEAR_ERR_UNIT; 237 237 238 238 return 0; 239 239 } ··· 308 306 return 0; 309 307 } 310 308 309 + static int nfit_test_cmd_clear_error(struct nd_cmd_clear_error *clear_err, 310 + unsigned int buf_len, int *cmd_rc) 311 + { 312 + const u64 mask = NFIT_TEST_CLEAR_ERR_UNIT - 1; 313 + if (buf_len < sizeof(*clear_err)) 314 + return -EINVAL; 315 + 316 + if ((clear_err->address & mask) || (clear_err->length & mask)) 317 + return -EINVAL; 318 + 319 + /* 320 + * Report 'all clear' success for all commands even though a new 321 + * scrub will find errors again. This is enough to have the 322 + * error removed from the 'badblocks' tracking in the pmem 323 + * driver. 324 + */ 325 + clear_err->status = 0; 326 + clear_err->cleared = clear_err->length; 327 + *cmd_rc = 0; 328 + return 0; 329 + } 330 + 311 331 static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, 312 332 struct nvdimm *nvdimm, unsigned int cmd, void *buf, 313 333 unsigned int buf_len, int *cmd_rc) ··· 388 364 case ND_CMD_ARS_STATUS: 389 365 rc = nfit_test_cmd_ars_status(ars_state, buf, buf_len, 390 366 cmd_rc); 367 + break; 368 + case ND_CMD_CLEAR_ERROR: 369 + rc = nfit_test_cmd_clear_error(buf, buf_len, cmd_rc); 391 370 break; 392 371 default: 393 372 return -ENOTTY; ··· 1257 1230 set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en); 1258 1231 set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en); 1259 1232 set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en); 1233 + set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_dsm_force_en); 1260 1234 } 1261 1235 1262 1236 static void nfit_test1_setup(struct nfit_test *t) ··· 1318 1290 set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en); 1319 1291 set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en); 1320 1292 set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en); 1293 + set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_dsm_force_en); 1321 1294 } 1322 1295 1323 1296 static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa,