diff options
| -rw-r--r-- | drivers/acpi/nfit/core.c | 48 | ||||
| -rw-r--r-- | drivers/nvdimm/core.c | 8 | ||||
| -rw-r--r-- | drivers/nvdimm/nd.h | 22 | ||||
| -rw-r--r-- | drivers/nvdimm/region_devs.c | 22 | ||||
| -rw-r--r-- | tools/testing/nvdimm/test/nfit.c | 3 |
5 files changed, 70 insertions, 33 deletions
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 80cc7c089a15..e1d5ea6d5e40 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c | |||
| @@ -94,54 +94,50 @@ static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc) | |||
| 94 | return to_acpi_device(acpi_desc->dev); | 94 | return to_acpi_device(acpi_desc->dev); |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | static int xlat_status(void *buf, unsigned int cmd) | 97 | static int xlat_status(void *buf, unsigned int cmd, u32 status) |
| 98 | { | 98 | { |
| 99 | struct nd_cmd_clear_error *clear_err; | 99 | struct nd_cmd_clear_error *clear_err; |
| 100 | struct nd_cmd_ars_status *ars_status; | 100 | struct nd_cmd_ars_status *ars_status; |
| 101 | struct nd_cmd_ars_start *ars_start; | ||
| 102 | struct nd_cmd_ars_cap *ars_cap; | ||
| 103 | u16 flags; | 101 | u16 flags; |
| 104 | 102 | ||
| 105 | switch (cmd) { | 103 | switch (cmd) { |
| 106 | case ND_CMD_ARS_CAP: | 104 | case ND_CMD_ARS_CAP: |
| 107 | ars_cap = buf; | 105 | if ((status & 0xffff) == NFIT_ARS_CAP_NONE) |
| 108 | if ((ars_cap->status & 0xffff) == NFIT_ARS_CAP_NONE) | ||
| 109 | return -ENOTTY; | 106 | return -ENOTTY; |
| 110 | 107 | ||
| 111 | /* Command failed */ | 108 | /* Command failed */ |
| 112 | if (ars_cap->status & 0xffff) | 109 | if (status & 0xffff) |
| 113 | return -EIO; | 110 | return -EIO; |
| 114 | 111 | ||
| 115 | /* No supported scan types for this range */ | 112 | /* No supported scan types for this range */ |
| 116 | flags = ND_ARS_PERSISTENT | ND_ARS_VOLATILE; | 113 | flags = ND_ARS_PERSISTENT | ND_ARS_VOLATILE; |
| 117 | if ((ars_cap->status >> 16 & flags) == 0) | 114 | if ((status >> 16 & flags) == 0) |
| 118 | return -ENOTTY; | 115 | return -ENOTTY; |
| 119 | break; | 116 | break; |
| 120 | case ND_CMD_ARS_START: | 117 | case ND_CMD_ARS_START: |
| 121 | ars_start = buf; | ||
| 122 | /* ARS is in progress */ | 118 | /* ARS is in progress */ |
| 123 | if ((ars_start->status & 0xffff) == NFIT_ARS_START_BUSY) | 119 | if ((status & 0xffff) == NFIT_ARS_START_BUSY) |
| 124 | return -EBUSY; | 120 | return -EBUSY; |
| 125 | 121 | ||
| 126 | /* Command failed */ | 122 | /* Command failed */ |
| 127 | if (ars_start->status & 0xffff) | 123 | if (status & 0xffff) |
| 128 | return -EIO; | 124 | return -EIO; |
| 129 | break; | 125 | break; |
| 130 | case ND_CMD_ARS_STATUS: | 126 | case ND_CMD_ARS_STATUS: |
| 131 | ars_status = buf; | 127 | ars_status = buf; |
| 132 | /* Command failed */ | 128 | /* Command failed */ |
| 133 | if (ars_status->status & 0xffff) | 129 | if (status & 0xffff) |
| 134 | return -EIO; | 130 | return -EIO; |
| 135 | /* Check extended status (Upper two bytes) */ | 131 | /* Check extended status (Upper two bytes) */ |
| 136 | if (ars_status->status == NFIT_ARS_STATUS_DONE) | 132 | if (status == NFIT_ARS_STATUS_DONE) |
| 137 | return 0; | 133 | return 0; |
| 138 | 134 | ||
| 139 | /* ARS is in progress */ | 135 | /* ARS is in progress */ |
| 140 | if (ars_status->status == NFIT_ARS_STATUS_BUSY) | 136 | if (status == NFIT_ARS_STATUS_BUSY) |
| 141 | return -EBUSY; | 137 | return -EBUSY; |
| 142 | 138 | ||
| 143 | /* No ARS performed for the current boot */ | 139 | /* No ARS performed for the current boot */ |
| 144 | if (ars_status->status == NFIT_ARS_STATUS_NONE) | 140 | if (status == NFIT_ARS_STATUS_NONE) |
| 145 | return -EAGAIN; | 141 | return -EAGAIN; |
| 146 | 142 | ||
| 147 | /* | 143 | /* |
| @@ -149,19 +145,19 @@ static int xlat_status(void *buf, unsigned int cmd) | |||
| 149 | * agent wants the scan to stop. If we didn't overflow | 145 | * agent wants the scan to stop. If we didn't overflow |
| 150 | * then just continue with the returned results. | 146 | * then just continue with the returned results. |
| 151 | */ | 147 | */ |
| 152 | if (ars_status->status == NFIT_ARS_STATUS_INTR) { | 148 | if (status == NFIT_ARS_STATUS_INTR) { |
| 153 | if (ars_status->flags & NFIT_ARS_F_OVERFLOW) | 149 | if (ars_status->flags & NFIT_ARS_F_OVERFLOW) |
| 154 | return -ENOSPC; | 150 | return -ENOSPC; |
| 155 | return 0; | 151 | return 0; |
| 156 | } | 152 | } |
| 157 | 153 | ||
| 158 | /* Unknown status */ | 154 | /* Unknown status */ |
| 159 | if (ars_status->status >> 16) | 155 | if (status >> 16) |
| 160 | return -EIO; | 156 | return -EIO; |
| 161 | break; | 157 | break; |
| 162 | case ND_CMD_CLEAR_ERROR: | 158 | case ND_CMD_CLEAR_ERROR: |
| 163 | clear_err = buf; | 159 | clear_err = buf; |
| 164 | if (clear_err->status & 0xffff) | 160 | if (status & 0xffff) |
| 165 | return -EIO; | 161 | return -EIO; |
| 166 | if (!clear_err->cleared) | 162 | if (!clear_err->cleared) |
| 167 | return -EIO; | 163 | return -EIO; |
| @@ -172,6 +168,9 @@ static int xlat_status(void *buf, unsigned int cmd) | |||
| 172 | break; | 168 | break; |
| 173 | } | 169 | } |
| 174 | 170 | ||
| 171 | /* all other non-zero status results in an error */ | ||
| 172 | if (status) | ||
| 173 | return -EIO; | ||
| 175 | return 0; | 174 | return 0; |
| 176 | } | 175 | } |
| 177 | 176 | ||
| @@ -186,10 +185,10 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, | |||
| 186 | struct nd_cmd_pkg *call_pkg = NULL; | 185 | struct nd_cmd_pkg *call_pkg = NULL; |
| 187 | const char *cmd_name, *dimm_name; | 186 | const char *cmd_name, *dimm_name; |
| 188 | unsigned long cmd_mask, dsm_mask; | 187 | unsigned long cmd_mask, dsm_mask; |
| 188 | u32 offset, fw_status = 0; | ||
| 189 | acpi_handle handle; | 189 | acpi_handle handle; |
| 190 | unsigned int func; | 190 | unsigned int func; |
| 191 | const u8 *uuid; | 191 | const u8 *uuid; |
| 192 | u32 offset; | ||
| 193 | int rc, i; | 192 | int rc, i; |
| 194 | 193 | ||
| 195 | func = cmd; | 194 | func = cmd; |
| @@ -317,6 +316,15 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, | |||
| 317 | out_obj->buffer.pointer + offset, out_size); | 316 | out_obj->buffer.pointer + offset, out_size); |
| 318 | offset += out_size; | 317 | offset += out_size; |
| 319 | } | 318 | } |
| 319 | |||
| 320 | /* | ||
| 321 | * Set fw_status for all the commands with a known format to be | ||
| 322 | * later interpreted by xlat_status(). | ||
| 323 | */ | ||
| 324 | if (i >= 1 && ((cmd >= ND_CMD_ARS_CAP && cmd <= ND_CMD_CLEAR_ERROR) | ||
| 325 | || (cmd >= ND_CMD_SMART && cmd <= ND_CMD_VENDOR))) | ||
| 326 | fw_status = *(u32 *) out_obj->buffer.pointer; | ||
| 327 | |||
| 320 | if (offset + in_buf.buffer.length < buf_len) { | 328 | if (offset + in_buf.buffer.length < buf_len) { |
| 321 | if (i >= 1) { | 329 | if (i >= 1) { |
| 322 | /* | 330 | /* |
| @@ -325,7 +333,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, | |||
| 325 | */ | 333 | */ |
| 326 | rc = buf_len - offset - in_buf.buffer.length; | 334 | rc = buf_len - offset - in_buf.buffer.length; |
| 327 | if (cmd_rc) | 335 | if (cmd_rc) |
| 328 | *cmd_rc = xlat_status(buf, cmd); | 336 | *cmd_rc = xlat_status(buf, cmd, fw_status); |
| 329 | } else { | 337 | } else { |
| 330 | dev_err(dev, "%s:%s underrun cmd: %s buf_len: %d out_len: %d\n", | 338 | dev_err(dev, "%s:%s underrun cmd: %s buf_len: %d out_len: %d\n", |
| 331 | __func__, dimm_name, cmd_name, buf_len, | 339 | __func__, dimm_name, cmd_name, buf_len, |
| @@ -335,7 +343,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, | |||
| 335 | } else { | 343 | } else { |
| 336 | rc = 0; | 344 | rc = 0; |
| 337 | if (cmd_rc) | 345 | if (cmd_rc) |
| 338 | *cmd_rc = xlat_status(buf, cmd); | 346 | *cmd_rc = xlat_status(buf, cmd, fw_status); |
| 339 | } | 347 | } |
| 340 | 348 | ||
| 341 | out: | 349 | out: |
diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index 715583f69d28..4d7bbd2df5c0 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c | |||
| @@ -99,8 +99,11 @@ static struct nvdimm_map *alloc_nvdimm_map(struct device *dev, | |||
| 99 | nvdimm_map->size = size; | 99 | nvdimm_map->size = size; |
| 100 | kref_init(&nvdimm_map->kref); | 100 | kref_init(&nvdimm_map->kref); |
| 101 | 101 | ||
| 102 | if (!request_mem_region(offset, size, dev_name(&nvdimm_bus->dev))) | 102 | if (!request_mem_region(offset, size, dev_name(&nvdimm_bus->dev))) { |
| 103 | dev_err(&nvdimm_bus->dev, "failed to request %pa + %zd for %s\n", | ||
| 104 | &offset, size, dev_name(dev)); | ||
| 103 | goto err_request_region; | 105 | goto err_request_region; |
| 106 | } | ||
| 104 | 107 | ||
| 105 | if (flags) | 108 | if (flags) |
| 106 | nvdimm_map->mem = memremap(offset, size, flags); | 109 | nvdimm_map->mem = memremap(offset, size, flags); |
| @@ -171,6 +174,9 @@ void *devm_nvdimm_memremap(struct device *dev, resource_size_t offset, | |||
| 171 | kref_get(&nvdimm_map->kref); | 174 | kref_get(&nvdimm_map->kref); |
| 172 | nvdimm_bus_unlock(dev); | 175 | nvdimm_bus_unlock(dev); |
| 173 | 176 | ||
| 177 | if (!nvdimm_map) | ||
| 178 | return NULL; | ||
| 179 | |||
| 174 | if (devm_add_action_or_reset(dev, nvdimm_map_put, nvdimm_map)) | 180 | if (devm_add_action_or_reset(dev, nvdimm_map_put, nvdimm_map)) |
| 175 | return NULL; | 181 | return NULL; |
| 176 | 182 | ||
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 8024a0ef86d3..0b78a8211f4a 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h | |||
| @@ -52,10 +52,28 @@ struct nvdimm_drvdata { | |||
| 52 | struct nd_region_data { | 52 | struct nd_region_data { |
| 53 | int ns_count; | 53 | int ns_count; |
| 54 | int ns_active; | 54 | int ns_active; |
| 55 | unsigned int flush_mask; | 55 | unsigned int hints_shift; |
| 56 | void __iomem *flush_wpq[0][0]; | 56 | void __iomem *flush_wpq[0]; |
| 57 | }; | 57 | }; |
| 58 | 58 | ||
| 59 | static inline void __iomem *ndrd_get_flush_wpq(struct nd_region_data *ndrd, | ||
| 60 | int dimm, int hint) | ||
| 61 | { | ||
| 62 | unsigned int num = 1 << ndrd->hints_shift; | ||
| 63 | unsigned int mask = num - 1; | ||
| 64 | |||
| 65 | return ndrd->flush_wpq[dimm * num + (hint & mask)]; | ||
| 66 | } | ||
| 67 | |||
| 68 | static inline void ndrd_set_flush_wpq(struct nd_region_data *ndrd, int dimm, | ||
| 69 | int hint, void __iomem *flush) | ||
| 70 | { | ||
| 71 | unsigned int num = 1 << ndrd->hints_shift; | ||
| 72 | unsigned int mask = num - 1; | ||
| 73 | |||
| 74 | ndrd->flush_wpq[dimm * num + (hint & mask)] = flush; | ||
| 75 | } | ||
| 76 | |||
| 59 | static inline struct nd_namespace_index *to_namespace_index( | 77 | static inline struct nd_namespace_index *to_namespace_index( |
| 60 | struct nvdimm_drvdata *ndd, int i) | 78 | struct nvdimm_drvdata *ndd, int i) |
| 61 | { | 79 | { |
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index e8d5ba7b29af..4c0ac4abb629 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c | |||
| @@ -38,7 +38,7 @@ static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm, | |||
| 38 | 38 | ||
| 39 | dev_dbg(dev, "%s: map %d flush address%s\n", nvdimm_name(nvdimm), | 39 | dev_dbg(dev, "%s: map %d flush address%s\n", nvdimm_name(nvdimm), |
| 40 | nvdimm->num_flush, nvdimm->num_flush == 1 ? "" : "es"); | 40 | nvdimm->num_flush, nvdimm->num_flush == 1 ? "" : "es"); |
| 41 | for (i = 0; i < nvdimm->num_flush; i++) { | 41 | for (i = 0; i < (1 << ndrd->hints_shift); i++) { |
| 42 | struct resource *res = &nvdimm->flush_wpq[i]; | 42 | struct resource *res = &nvdimm->flush_wpq[i]; |
| 43 | unsigned long pfn = PHYS_PFN(res->start); | 43 | unsigned long pfn = PHYS_PFN(res->start); |
| 44 | void __iomem *flush_page; | 44 | void __iomem *flush_page; |
| @@ -54,14 +54,15 @@ static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm, | |||
| 54 | 54 | ||
| 55 | if (j < i) | 55 | if (j < i) |
| 56 | flush_page = (void __iomem *) ((unsigned long) | 56 | flush_page = (void __iomem *) ((unsigned long) |
| 57 | ndrd->flush_wpq[dimm][j] & PAGE_MASK); | 57 | ndrd_get_flush_wpq(ndrd, dimm, j) |
| 58 | & PAGE_MASK); | ||
| 58 | else | 59 | else |
| 59 | flush_page = devm_nvdimm_ioremap(dev, | 60 | flush_page = devm_nvdimm_ioremap(dev, |
| 60 | PHYS_PFN(pfn), PAGE_SIZE); | 61 | PFN_PHYS(pfn), PAGE_SIZE); |
| 61 | if (!flush_page) | 62 | if (!flush_page) |
| 62 | return -ENXIO; | 63 | return -ENXIO; |
| 63 | ndrd->flush_wpq[dimm][i] = flush_page | 64 | ndrd_set_flush_wpq(ndrd, dimm, i, flush_page |
| 64 | + (res->start & ~PAGE_MASK); | 65 | + (res->start & ~PAGE_MASK)); |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | return 0; | 68 | return 0; |
| @@ -93,7 +94,10 @@ int nd_region_activate(struct nd_region *nd_region) | |||
| 93 | return -ENOMEM; | 94 | return -ENOMEM; |
| 94 | dev_set_drvdata(dev, ndrd); | 95 | dev_set_drvdata(dev, ndrd); |
| 95 | 96 | ||
| 96 | ndrd->flush_mask = (1 << ilog2(num_flush)) - 1; | 97 | if (!num_flush) |
| 98 | return 0; | ||
| 99 | |||
| 100 | ndrd->hints_shift = ilog2(num_flush); | ||
| 97 | for (i = 0; i < nd_region->ndr_mappings; i++) { | 101 | for (i = 0; i < nd_region->ndr_mappings; i++) { |
| 98 | struct nd_mapping *nd_mapping = &nd_region->mapping[i]; | 102 | struct nd_mapping *nd_mapping = &nd_region->mapping[i]; |
| 99 | struct nvdimm *nvdimm = nd_mapping->nvdimm; | 103 | struct nvdimm *nvdimm = nd_mapping->nvdimm; |
| @@ -900,8 +904,8 @@ void nvdimm_flush(struct nd_region *nd_region) | |||
| 900 | */ | 904 | */ |
| 901 | wmb(); | 905 | wmb(); |
| 902 | for (i = 0; i < nd_region->ndr_mappings; i++) | 906 | for (i = 0; i < nd_region->ndr_mappings; i++) |
| 903 | if (ndrd->flush_wpq[i][0]) | 907 | if (ndrd_get_flush_wpq(ndrd, i, 0)) |
| 904 | writeq(1, ndrd->flush_wpq[i][idx & ndrd->flush_mask]); | 908 | writeq(1, ndrd_get_flush_wpq(ndrd, i, idx)); |
| 905 | wmb(); | 909 | wmb(); |
| 906 | } | 910 | } |
| 907 | EXPORT_SYMBOL_GPL(nvdimm_flush); | 911 | EXPORT_SYMBOL_GPL(nvdimm_flush); |
| @@ -925,7 +929,7 @@ int nvdimm_has_flush(struct nd_region *nd_region) | |||
| 925 | 929 | ||
| 926 | for (i = 0; i < nd_region->ndr_mappings; i++) | 930 | for (i = 0; i < nd_region->ndr_mappings; i++) |
| 927 | /* flush hints present, flushing required */ | 931 | /* flush hints present, flushing required */ |
| 928 | if (ndrd->flush_wpq[i][0]) | 932 | if (ndrd_get_flush_wpq(ndrd, i, 0)) |
| 929 | return 1; | 933 | return 1; |
| 930 | 934 | ||
| 931 | /* | 935 | /* |
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index dd48f421844c..f64c57bf1d4b 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c | |||
| @@ -603,7 +603,8 @@ static int nfit_test0_alloc(struct nfit_test *t) | |||
| 603 | return -ENOMEM; | 603 | return -ENOMEM; |
| 604 | sprintf(t->label[i], "label%d", i); | 604 | sprintf(t->label[i], "label%d", i); |
| 605 | 605 | ||
| 606 | t->flush[i] = test_alloc(t, sizeof(u64) * NUM_HINTS, | 606 | t->flush[i] = test_alloc(t, max(PAGE_SIZE, |
| 607 | sizeof(u64) * NUM_HINTS), | ||
| 607 | &t->flush_dma[i]); | 608 | &t->flush_dma[i]); |
| 608 | if (!t->flush[i]) | 609 | if (!t->flush[i]) |
| 609 | return -ENOMEM; | 610 | return -ENOMEM; |
