libnvdimm, dimm: handle EACCES failures from label reads
authorDan Williams <dan.j.williams@intel.com>
Mon, 9 Apr 2018 19:34:24 +0000 (12:34 -0700)
committerDan Williams <dan.j.williams@intel.com>
Mon, 16 Apr 2018 15:18:51 +0000 (08:18 -0700)
The new support for the standard _LSR and _LSW methods neglected to also
update the nvdimm_init_config_data() and nvdimm_set_config_data() to
return the translated error code from failed commands. This precision is
necessary because the locked status that was previously returned on
ND_CMD_GET_CONFIG_SIZE commands is now returned on
ND_CMD_{GET,SET}_CONFIG_DATA commands.

If the kernel misses this indication it can inadvertently fall back to
label-less mode when it should otherwise avoid all access to locked
regions.

Cc: <stable@vger.kernel.org>
Fixes: 4b27db7e26cd ("acpi, nfit: add support for the _LSI, _LSR, and...")
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/nvdimm/dimm_devs.c

index e00d455..8d348b2 100644 (file)
@@ -88,9 +88,9 @@ int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd)
 int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
 {
        struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
+       int rc = validate_dimm(ndd), cmd_rc = 0;
        struct nd_cmd_get_config_data_hdr *cmd;
        struct nvdimm_bus_descriptor *nd_desc;
-       int rc = validate_dimm(ndd);
        u32 max_cmd_size, config_size;
        size_t offset;
 
@@ -124,9 +124,11 @@ int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
                cmd->in_offset = offset;
                rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
                                ND_CMD_GET_CONFIG_DATA, cmd,
-                               cmd->in_length + sizeof(*cmd), NULL);
-               if (rc || cmd->status) {
-                       rc = -ENXIO;
+                               cmd->in_length + sizeof(*cmd), &cmd_rc);
+               if (rc < 0)
+                       break;
+               if (cmd_rc < 0) {
+                       rc = cmd_rc;
                        break;
                }
                memcpy(ndd->data + offset, cmd->out_buf, cmd->in_length);
@@ -140,9 +142,9 @@ int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
 int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
                void *buf, size_t len)
 {
-       int rc = validate_dimm(ndd);
        size_t max_cmd_size, buf_offset;
        struct nd_cmd_set_config_hdr *cmd;
+       int rc = validate_dimm(ndd), cmd_rc = 0;
        struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
        struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
 
@@ -164,7 +166,6 @@ int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
        for (buf_offset = 0; len; len -= cmd->in_length,
                        buf_offset += cmd->in_length) {
                size_t cmd_size;
-               u32 *status;
 
                cmd->in_offset = offset + buf_offset;
                cmd->in_length = min(max_cmd_size, len);
@@ -172,12 +173,13 @@ int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
 
                /* status is output in the last 4-bytes of the command buffer */
                cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32);
-               status = ((void *) cmd) + cmd_size - sizeof(u32);
 
                rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
-                               ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, NULL);
-               if (rc || *status) {
-                       rc = rc ? rc : -ENXIO;
+                               ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, &cmd_rc);
+               if (rc < 0)
+                       break;
+               if (cmd_rc < 0) {
+                       rc = cmd_rc;
                        break;
                }
        }