diff --git a/MAINTAINERS b/MAINTAINERS index 926a17df059e..0ef0a11a5f25 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6456,6 +6456,7 @@ M: Alison Schofield M: Vishal Verma M: Dan Williams R: Ira Weiny +R: Li Ming L: linux-cxl@vger.kernel.org S: Maintained F: Documentation/driver-api/cxl diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c index 127537628817..3b818adbd38b 100644 --- a/drivers/cxl/acpi.c +++ b/drivers/cxl/acpi.c @@ -101,8 +101,8 @@ static int cxl_parse_cxims(union acpi_subtable_headers *header, void *arg, struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; struct device *dev = ctx->dev; struct cxl_cxims_data *cximsd; - unsigned int hbig, nr_maps; - int rc; + unsigned int nr_maps; + int hbig, rc; rc = eig_to_granularity(cxims->hbig, &hbig); if (rc) @@ -160,7 +160,7 @@ static int cxl_acpi_cfmws_verify(struct device *dev, struct acpi_cedt_cfmws *cfmws) { int rc, expected_len; - unsigned int ways; + int ways; if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO && cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_XOR) { @@ -405,7 +405,7 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws, struct cxl_cxims_context cxims_ctx; struct device *dev = ctx->dev; struct cxl_decoder *cxld; - unsigned int ways, i, ig; + int ways, i, ig; int rc; rc = cxl_acpi_cfmws_verify(dev, cfmws); @@ -464,7 +464,7 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws, if (rc < 0) return rc; if (!cxlrd->platform_data) { - dev_err(dev, "No CXIMS for HBIG %u\n", ig); + dev_err(dev, "No CXIMS for HBIG %d\n", ig); return -EINVAL; } } diff --git a/drivers/cxl/core/core.h b/drivers/cxl/core/core.h index 82ca3a476708..07555ae63859 100644 --- a/drivers/cxl/core/core.h +++ b/drivers/cxl/core/core.h @@ -52,6 +52,7 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, u64 dpa); int devm_cxl_add_dax_region(struct cxl_region *cxlr); int devm_cxl_add_pmem_region(struct cxl_region *cxlr); +void kill_regions(struct cxl_root_decoder *cxlrd); #else static inline u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, @@ -81,6 +82,7 @@ static inline int cxl_region_init(void) static inline void cxl_region_exit(void) { } +static inline void kill_regions(struct cxl_root_decoder *cxlrd) { }; #define CXL_REGION_ATTR(x) NULL #define CXL_REGION_TYPE(x) NULL #define SET_CXL_REGION_ATTR(x) diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index 3435db9ea6b1..85185af46b72 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -423,6 +423,7 @@ static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, rpc_out->size = struct_size(feat_out, ents, requested); feat_out = &rpc_out->get_sup_feats_out; + feat_out->num_entries = cpu_to_le16(requested); for (i = start, pos = &feat_out->ents[0]; i < cxlfs->entries->num_features; i++, pos++) { @@ -444,7 +445,6 @@ static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, } } - feat_out->num_entries = cpu_to_le16(requested); feat_out->supported_feats = cpu_to_le16(cxlfs->entries->num_features); rpc_out->retval = CXL_MBOX_CMD_RC_SUCCESS; *out_len = out_size; diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index 80e65690eb77..33a3d2e7b13a 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -25,9 +25,11 @@ static DEFINE_IDA(cxl_memdev_ida); static void cxl_memdev_release(struct device *dev) { struct cxl_memdev *cxlmd = to_cxl_memdev(dev); + struct device *parent = dev->parent; ida_free(&cxl_memdev_ida, cxlmd->id); kfree(cxlmd); + put_device(parent); } static char *cxl_memdev_devnode(const struct device *dev, umode_t *mode, kuid_t *uid, @@ -572,16 +574,23 @@ void cxl_memdev_update_perf(struct cxl_memdev *cxlmd) } EXPORT_SYMBOL_NS_GPL(cxl_memdev_update_perf, "CXL"); -static const struct device_type cxl_memdev_type = { +static const struct device_type cxl_class_memdev_type = { .name = "cxl_memdev", .release = cxl_memdev_release, .devnode = cxl_memdev_devnode, .groups = cxl_memdev_attribute_groups, }; +static const struct device_type cxl_memdev_type = { + .name = "cxl_memdev", + .release = cxl_memdev_release, + .devnode = cxl_memdev_devnode, +}; + bool is_cxl_memdev(const struct device *dev) { - return dev->type == &cxl_memdev_type; + return (dev->type == &cxl_class_memdev_type || + dev->type == &cxl_memdev_type); } EXPORT_SYMBOL_NS_GPL(is_cxl_memdev, "CXL"); @@ -707,10 +716,13 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds, dev = &cxlmd->dev; device_initialize(dev); lockdep_set_class(&dev->mutex, &cxl_memdev_key); - dev->parent = cxlds->dev; + dev->parent = get_device(cxlds->dev); dev->bus = &cxl_bus_type; dev->devt = MKDEV(cxl_mem_major, cxlmd->id); - dev->type = &cxl_memdev_type; + if (cxlds->type == CXL_DEVTYPE_DEVMEM) + dev->type = &cxl_memdev_type; + else + dev->type = &cxl_class_memdev_type; device_set_pm_not_required(dev); INIT_WORK(&cxlmd->detach_work, detach_memdev); diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index d1f487b3d809..e4338fd7e01b 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -89,7 +89,7 @@ static int cxl_dvsec_mem_range_valid(struct cxl_dev_state *cxlds, int id) d + PCI_DVSEC_CXL_RANGE_SIZE_LOW(id), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); valid = FIELD_GET(PCI_DVSEC_CXL_MEM_INFO_VALID, temp); if (valid) @@ -123,7 +123,7 @@ static int cxl_dvsec_mem_range_active(struct cxl_dev_state *cxlds, int id) rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_SIZE_LOW(id), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); active = FIELD_GET(PCI_DVSEC_CXL_MEM_ACTIVE, temp); if (active) @@ -156,7 +156,7 @@ int cxl_await_media_ready(struct cxl_dev_state *cxlds) rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CAP, &cap); if (rc) - return rc; + return pcibios_err_to_errno(rc); hdm_count = FIELD_GET(PCI_DVSEC_CXL_HDM_COUNT, cap); for (i = 0; i < hdm_count; i++) { @@ -187,8 +187,8 @@ static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val) int rc; rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, &ctrl); - if (rc < 0) - return rc; + if (rc) + return pcibios_err_to_errno(rc); if ((ctrl & PCI_DVSEC_CXL_MEM_ENABLE) == val) return 1; @@ -196,8 +196,8 @@ static int cxl_set_mem_enable(struct cxl_dev_state *cxlds, u16 val) ctrl |= val; rc = pci_write_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, ctrl); - if (rc < 0) - return rc; + if (rc) + return pcibios_err_to_errno(rc); return 0; } @@ -275,7 +275,7 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CAP, &cap); if (rc) - return rc; + return pcibios_err_to_errno(rc); if (!(cap & PCI_DVSEC_CXL_MEM_CAPABLE)) { dev_dbg(dev, "Not MEM Capable\n"); @@ -299,7 +299,7 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, */ rc = pci_read_config_word(pdev, d + PCI_DVSEC_CXL_CTRL, &ctrl); if (rc) - return rc; + return pcibios_err_to_errno(rc); info->mem_enabled = FIELD_GET(PCI_DVSEC_CXL_MEM_ENABLE, ctrl); if (!info->mem_enabled) @@ -316,14 +316,14 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_SIZE_HIGH(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); size = (u64)temp << 32; rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_SIZE_LOW(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); size |= temp & PCI_DVSEC_CXL_MEM_SIZE_LOW; if (!size) { @@ -333,14 +333,14 @@ int cxl_dvsec_rr_decode(struct cxl_dev_state *cxlds, rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_BASE_HIGH(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); base = (u64)temp << 32; rc = pci_read_config_dword( pdev, d + PCI_DVSEC_CXL_RANGE_BASE_LOW(i), &temp); if (rc) - return rc; + return pcibios_err_to_errno(rc); base |= temp & PCI_DVSEC_CXL_MEM_BASE_LOW; diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index c5aacd7054f1..1215ee4f4035 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -458,6 +458,8 @@ static void cxl_root_decoder_release(struct device *dev) if (atomic_read(&cxlrd->region_id) >= 0) memregion_free(atomic_read(&cxlrd->region_id)); + mutex_destroy(&cxlrd->regions_lock); + xa_destroy(&cxlrd->regions); __cxl_decoder_release(&cxlrd->cxlsd.cxld); kfree(cxlrd); } @@ -2016,7 +2018,8 @@ struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port, return ERR_PTR(rc); } - mutex_init(&cxlrd->range_lock); + mutex_init(&cxlrd->regions_lock); + xa_init(&cxlrd->regions); cxld = &cxlsd->cxld; cxld->dev.type = &cxl_decoder_root_type; @@ -2192,6 +2195,8 @@ static void cxld_unregister(void *dev) if (is_endpoint_decoder(dev)) cxl_decoder_detach(NULL, to_cxl_endpoint_decoder(dev), -1, DETACH_INVALIDATE); + if (is_root_decoder(dev)) + kill_regions(to_cxl_root_decoder(dev)); device_unregister(dev); } diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c index 006c6ffc2f56..99fb00949c2f 100644 --- a/drivers/cxl/core/ras.c +++ b/drivers/cxl/core/ras.c @@ -8,6 +8,10 @@ #include #include "trace.h" +/* Check that UCE header definition is maintained to keep ABI intact */ +static_assert(CXL_HEADERLOG_TRACE_SIZE_U32 == 128, + "rasdaemon ABI requires exactly 128 u32s"); + static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, struct cxl_ras_capability_regs ras_cap) { @@ -19,6 +23,7 @@ static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, struct cxl_ras_capability_regs ras_cap) { + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; u32 fe; @@ -28,8 +33,8 @@ static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, else fe = status; - trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe, - ras_cap.header_log); + memcpy(hl, ras_cap.header_log, CXL_HEADERLOG_SIZE); + trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe, hl); } static void cxl_cper_trace_corr_prot_err(struct cxl_memdev *cxlmd, @@ -44,6 +49,7 @@ static void cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd, struct cxl_ras_capability_regs ras_cap) { + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; u32 fe; @@ -53,8 +59,15 @@ cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd, else fe = status; - trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, - ras_cap.header_log); + /* + * ras_cap.header_log[] holds CXL_HEADERLOG_SIZE_U32 (16) hardware + * dwords. Copy them into the front of a zero-filled + * CXL_HEADERLOG_TRACE_SIZE_U32 (128) u32 staging buffer so the trace + * event memcpy sees a full 512-byte source and the userspace ABI + * (rasdaemon) is preserved. + */ + memcpy(hl, ras_cap.header_log, CXL_HEADERLOG_SIZE); + trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, hl); } static int match_memdev_by_parent(struct device *dev, const void *uport) @@ -204,12 +217,12 @@ static void header_log_copy(void __iomem *ras_base, u32 *log) { void __iomem *addr; u32 *log_addr; - int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32); + int i; addr = ras_base + CXL_RAS_HEADER_LOG_OFFSET; log_addr = log; - for (i = 0; i < log_u32_size; i++) { + for (i = 0; i < CXL_HEADERLOG_SIZE_U32; i++) { *log_addr = readl(addr); log_addr++; addr += sizeof(u32); @@ -222,7 +235,7 @@ static void header_log_copy(void __iomem *ras_base, u32 *log) */ bool cxl_handle_ras(struct device *dev, void __iomem *ras_base) { - u32 hl[CXL_HEADERLOG_SIZE_U32]; + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; void __iomem *addr; u32 status; u32 fe; diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index e50dc716d4e8..1e211542b6b6 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -1148,6 +1148,19 @@ static int cxl_rr_assign_decoder(struct cxl_port *port, struct cxl_region *cxlr, static void cxl_region_setup_flags(struct cxl_region *cxlr, struct cxl_decoder *cxld) { + if (is_endpoint_decoder(&cxld->dev)) { + struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(&cxld->dev); + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + + /* + * When a region's memdevs specify an @attach method the attach + * provider is responsible for dispositioning the region for + * both probe and userspace management + */ + if (cxlmd->attach) + set_bit(CXL_REGION_F_LOCK, &cxlr->flags); + } + if (cxld->flags & CXL_DECODER_F_LOCK) { set_bit(CXL_REGION_F_LOCK, &cxlr->flags); clear_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags); @@ -1224,8 +1237,6 @@ static int cxl_port_attach_region(struct cxl_port *port, nr_targets_inc = true; } } else { - struct cxl_decoder *cxld; - cxld = cxl_port_pick_region_decoder(port, cxled, cxlr); if (!cxld) { dev_dbg(&cxlr->dev, "%s: no decoder available\n", @@ -1848,8 +1859,21 @@ static int cxl_region_attach_auto(struct cxl_region *cxlr, * this means that userspace can view devices in the wrong position * before the region activates, and must be careful to understand when * it might be racing region autodiscovery. + * + * The endpoint decoder will be recorded into the first free slot of + * the target array. */ - pos = p->nr_targets; + for (pos = 0; pos < p->interleave_ways; pos++) { + if (!p->targets[pos]) + break; + } + + if (pos == p->interleave_ways) { + dev_err(&cxlr->dev, "%s: unable to find a free target slot\n", + dev_name(&cxled->cxld.dev)); + return -ENXIO; + } + p->targets[pos] = cxled; cxled->pos = pos; cxled->state = CXL_DECODER_STATE_AUTO_STAGED; @@ -2011,8 +2035,9 @@ static int cxl_region_sort_targets(struct cxl_region *cxlr) cxled->pos = cxl_calc_interleave_pos(cxled, &cxlr->hpa_range); /* * Record that sorting failed, but still continue to calc - * cxled->pos so that follow-on code paths can reliably - * do p->targets[cxled->pos] to self-reference their entry. + * cxled->pos so that cxl_calc_interleave_pos() emits its + * dev_dbg() for every member. which is useful for auto + * discovery debug. */ if (cxled->pos < 0) rc = -ENXIO; @@ -2189,31 +2214,43 @@ static int cxl_region_attach(struct cxl_region *cxlr, * will fail when presented as CXL_REGION_F_AUTO. */ for (int i = 0; i < p->nr_targets; i++) { - struct cxl_endpoint_decoder *cxled = p->targets[i]; + struct cxl_endpoint_decoder *target = p->targets[i]; int test_pos; - test_pos = cxl_calc_interleave_pos(cxled, &cxlr->hpa_range); - dev_dbg(&cxled->cxld.dev, - "Test cxl_calc_interleave_pos(): %s test_pos:%d cxled->pos:%d\n", - (test_pos == cxled->pos) ? "success" : "fail", - test_pos, cxled->pos); + test_pos = cxl_calc_interleave_pos(target, &cxlr->hpa_range); + dev_dbg(&target->cxld.dev, + "Test cxl_calc_interleave_pos(): %s test_pos:%d target->pos:%d\n", + (test_pos == target->pos) ? "success" : "fail", + test_pos, target->pos); } return 0; } -static int cxl_region_by_target(struct device *dev, const void *data) +static int cxl_region_remove_target(struct device *dev, void *data) { - const struct cxl_endpoint_decoder *cxled = data; + struct cxl_endpoint_decoder *cxled = data; struct cxl_region_params *p; struct cxl_region *cxlr; + int i; if (!is_cxl_region(dev)) return 0; cxlr = to_cxl_region(dev); p = &cxlr->params; - return p->targets[cxled->pos] == cxled; + for (i = 0; i < p->interleave_ways; i++) { + if (p->targets[i] == cxled) { + p->nr_targets--; + cxled->state = CXL_DECODER_STATE_AUTO; + cxled->pos = -1; + p->targets[i] = NULL; + + return 1; + } + } + + return 0; } /* @@ -2222,25 +2259,10 @@ static int cxl_region_by_target(struct device *dev, const void *data) */ static void cxl_cancel_auto_attach(struct cxl_endpoint_decoder *cxled) { - struct cxl_region_params *p; - struct cxl_region *cxlr; - int pos = cxled->pos; - if (cxled->state != CXL_DECODER_STATE_AUTO_STAGED) return; - struct device *dev __free(put_device) = - bus_find_device(&cxl_bus_type, NULL, cxled, cxl_region_by_target); - if (!dev) - return; - - cxlr = to_cxl_region(dev); - p = &cxlr->params; - - p->nr_targets--; - cxled->state = CXL_DECODER_STATE_AUTO; - cxled->pos = -1; - p->targets[pos] = NULL; + bus_for_each_dev(&cxl_bus_type, NULL, cxled, cxl_region_remove_target); } static struct cxl_region * @@ -2537,12 +2559,13 @@ static struct cxl_region *to_cxl_region(struct device *dev) return container_of(dev, struct cxl_region, dev); } -static void unregister_region(void *_cxlr) +static void unregister_region(struct cxl_region *cxlr) { - struct cxl_region *cxlr = _cxlr; + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); struct cxl_region_params *p = &cxlr->params; int i; + xa_erase(&cxlrd->regions, cxlr->id); device_del(&cxlr->dev); /* @@ -2559,6 +2582,17 @@ static void unregister_region(void *_cxlr) put_device(&cxlr->dev); } +static void endpoint_unregister_region(void *_cxlr) +{ + struct cxl_region *cxlr = _cxlr; + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); + + guard(mutex)(&cxlrd->regions_lock); + if (xa_load(&cxlrd->regions, cxlr->id)) + unregister_region(cxlr); + put_device(&cxlr->dev); +} + static struct lock_class_key cxl_region_key; static struct cxl_region *cxl_region_alloc(struct cxl_root_decoder *cxlrd, int id) @@ -2673,6 +2707,19 @@ static int cxl_region_calculate_adistance(struct notifier_block *nb, return NOTIFY_STOP; } +/* unwind all remaining regions */ +void kill_regions(struct cxl_root_decoder *cxlrd) +{ + unsigned long index; + struct cxl_region *cxlr; + + guard(mutex)(&cxlrd->regions_lock); + /* no more region creation */ + cxlrd->dead = true; + xa_for_each(&cxlrd->regions, index, cxlr) + unregister_region(cxlr); +} + /** * devm_cxl_add_region - Adds a region to a decoder * @cxlrd: root decoder @@ -2711,14 +2758,15 @@ static struct cxl_region *devm_cxl_add_region(struct cxl_root_decoder *cxlrd, if (rc) goto err; - rc = devm_add_action_or_reset(port->uport_dev, unregister_region, cxlr); - if (rc) + rc = xa_insert(&cxlrd->regions, cxlr->id, cxlr, GFP_KERNEL); + if (rc) { + unregister_region(cxlr); return ERR_PTR(rc); + } dev_dbg(port->uport_dev, "%s: created %s\n", dev_name(&cxlrd->cxlsd.cxld.dev), dev_name(dev)); return cxlr; - err: put_device(dev); return ERR_PTR(rc); @@ -2747,6 +2795,9 @@ static struct cxl_region *__create_region(struct cxl_root_decoder *cxlrd, { int rc; + if (cxlrd->dead) + return ERR_PTR(-ENXIO); + switch (mode) { case CXL_PARTMODE_RAM: case CXL_PARTMODE_PMEM: @@ -2779,6 +2830,10 @@ static ssize_t create_region_store(struct device *dev, const char *buf, if (rc != 1) return -EINVAL; + ACQUIRE(mutex_intr, regions_lock)(&cxlrd->regions_lock); + if ((rc = ACQUIRE_ERR(mutex_intr, ®ions_lock))) + return rc; + cxlr = __create_region(cxlrd, mode, id, CXL_DECODER_HOSTONLYMEM); if (IS_ERR(cxlr)) return PTR_ERR(cxlr); @@ -2818,33 +2873,27 @@ static ssize_t region_show(struct device *dev, struct device_attribute *attr, } DEVICE_ATTR_RO(region); -static struct cxl_region * -cxl_find_region_by_name(struct cxl_root_decoder *cxlrd, const char *name) -{ - struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; - struct device *region_dev; - - region_dev = device_find_child_by_name(&cxld->dev, name); - if (!region_dev) - return ERR_PTR(-ENODEV); - - return to_cxl_region(region_dev); -} - static ssize_t delete_region_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); - struct cxl_port *port = to_cxl_port(dev->parent); struct cxl_region *cxlr; + int rc, id; - cxlr = cxl_find_region_by_name(cxlrd, buf); - if (IS_ERR(cxlr)) - return PTR_ERR(cxlr); + ACQUIRE(mutex_intr, regions_lock)(&cxlrd->regions_lock); + if ((rc = ACQUIRE_ERR(mutex_intr, ®ions_lock))) + return rc; - devm_release_action(port->uport_dev, unregister_region, cxlr); - put_device(&cxlr->dev); + rc = sscanf(buf, "region%d\n", &id); + if (rc != 1) + return -EINVAL; + + cxlr = xa_load(&cxlrd->regions, id); + if (!cxlr || !sysfs_streq(buf, dev_name(&cxlr->dev))) + return -ENODEV; + + unregister_region(cxlr); return len; } @@ -3060,7 +3109,7 @@ int cxl_validate_translation_params(u8 eiw, u16 eig, int pos) return -EINVAL; } if (pos < 0 || pos >= ways) { - pr_debug("%s: invalid pos=%d for ways=%u\n", __func__, pos, + pr_debug("%s: invalid pos=%d for ways=%d\n", __func__, pos, ways); return -EINVAL; } @@ -3106,7 +3155,7 @@ EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_dpa_offset, "cxl_translate"); int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig) { - unsigned int ways = 0; + int ways = 0; u64 shifted, rem; int pos, ret; @@ -3709,11 +3758,13 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd, { struct cxl_endpoint_decoder *cxled = ctx->cxled; struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); - struct cxl_port *port = cxlrd_to_port(cxlrd); struct cxl_dev_state *cxlds = cxlmd->cxlds; int rc, part = READ_ONCE(cxled->part); struct cxl_region *cxlr; + if (part < 0) + return ERR_PTR(-EBUSY); + do { cxlr = __create_region(cxlrd, cxlds->part[part].mode, atomic_read(&cxlrd->region_id), @@ -3730,7 +3781,7 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd, rc = __construct_region(cxlr, ctx); if (rc) { - devm_release_action(port->uport_dev, unregister_region, cxlr); + unregister_region(cxlr); return ERR_PTR(rc); } @@ -3776,12 +3827,11 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled) * for the HPA range, one does the construction and the others * add to that. */ - mutex_lock(&cxlrd->range_lock); + guard(mutex)(&cxlrd->regions_lock); struct cxl_region *cxlr __free(put_cxl_region) = cxl_find_region_by_range(cxlrd, &ctx.hpa_range); if (!cxlr) cxlr = construct_region(cxlrd, &ctx); - mutex_unlock(&cxlrd->range_lock); rc = PTR_ERR_OR_ZERO(cxlr); if (rc) @@ -4043,6 +4093,103 @@ static int cxl_region_can_probe(struct cxl_region *cxlr) return 0; } +static int first_mapped_decoder(struct device *dev, const void *data) +{ + struct cxl_endpoint_decoder *cxled; + + if (!is_endpoint_decoder(dev)) + return 0; + + cxled = to_cxl_endpoint_decoder(dev); + if (cxled->cxld.region) + return 1; + + return 0; +} + +/* + * Runs in cxl_mem_probe context after successful endpoint probe, assumes the + * simple case of single mapped decoder per memdev. + */ +int cxl_memdev_attach_region(struct cxl_memdev *cxlmd) +{ + struct cxl_attach_region *attach = + container_of(cxlmd->attach, typeof(*attach), attach); + struct cxl_port *endpoint = cxlmd->endpoint; + struct cxl_endpoint_decoder *cxled; + struct cxl_region *cxlr; + int rc; + + /* hold endpoint lock to setup autoremove of the region */ + guard(device)(&endpoint->dev); + if (!endpoint->dev.driver) + return -ENXIO; + guard(rwsem_read)(&cxl_rwsem.region); + guard(rwsem_read)(&cxl_rwsem.dpa); + + /* + * TODO auto-instantiate a region, for now assume this will find an + * auto-region + */ + struct device *dev __free(put_device) = + device_find_child(&endpoint->dev, NULL, first_mapped_decoder); + + if (!dev) { + dev_dbg(cxlmd->cxlds->dev, "no region found for memdev %s\n", + dev_name(&cxlmd->dev)); + return -ENXIO; + } + + cxled = to_cxl_endpoint_decoder(dev); + cxlr = cxled->cxld.region; + + if (cxlr->params.state < CXL_CONFIG_COMMIT) { + dev_dbg(cxlmd->cxlds->dev, + "region %s not committed for memdev %s\n", + dev_name(&cxlr->dev), dev_name(&cxlmd->dev)); + return -ENXIO; + } + + if (cxlr->params.nr_targets > 1) { + dev_dbg(cxlmd->cxlds->dev, + "Only attach to local non-interleaved region\n"); + return -ENXIO; + } + + /* Only teardown regions that pass validation, ignore the rest */ + get_device(&cxlr->dev); + rc = devm_add_action_or_reset(&endpoint->dev, + endpoint_unregister_region, cxlr); + if (rc) + return rc; + + attach->hpa_range = (struct range) { + .start = cxlr->params.res->start, + .end = cxlr->params.res->end, + }; + return 0; +} +EXPORT_SYMBOL_FOR_MODULES(cxl_memdev_attach_region, "cxl_mem"); + +/* + * The presence of an attach method indicates that the region is designated for + * a purpose outside of CXL core memory expansion defaults. + */ +static bool cxl_region_has_memdev_attach(struct cxl_region *cxlr) +{ + struct cxl_region_params *p = &cxlr->params; + + for (int i = 0; i < p->nr_targets; i++) { + struct cxl_endpoint_decoder *cxled = p->targets[i]; + struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); + + if (cxlmd->attach) + return true; + } + + return false; +} + static int cxl_region_probe(struct device *dev) { struct cxl_region *cxlr = to_cxl_region(dev); @@ -4074,6 +4221,9 @@ static int cxl_region_probe(struct device *dev) if (rc) return rc; + if (cxl_region_has_memdev_attach(cxlr)) + return 0; + switch (cxlr->mode) { case CXL_PARTMODE_PMEM: rc = devm_cxl_region_edac_register(cxlr); diff --git a/drivers/cxl/core/trace.h b/drivers/cxl/core/trace.h index a972e4ef1936..d37876096dd7 100644 --- a/drivers/cxl/core/trace.h +++ b/drivers/cxl/core/trace.h @@ -56,7 +56,7 @@ TRACE_EVENT(cxl_port_aer_uncorrectable_error, __string(host, dev_name(dev->parent)) __field(u32, status) __field(u32, first_error) - __array(u32, header_log, CXL_HEADERLOG_SIZE_U32) + __array(u32, header_log, CXL_HEADERLOG_TRACE_SIZE_U32) ), TP_fast_assign( __assign_str(device); @@ -64,10 +64,14 @@ TRACE_EVENT(cxl_port_aer_uncorrectable_error, __entry->status = status; __entry->first_error = fe; /* - * Embed the 512B headerlog data for user app retrieval and - * parsing, but no need to print this in the trace buffer. + * Embed headerlog data for user app retrieval and parsing, + * but no need to print in the trace buffer. Only + * CXL_HEADERLOG_SIZE_U32 (16) dwords are hardware data; + * the remaining entries preserve the 512-byte ABI layout + * rasdaemon depends on and are zero-filled by the caller. */ - memcpy(__entry->header_log, hl, CXL_HEADERLOG_SIZE); + memcpy(__entry->header_log, hl, + CXL_HEADERLOG_TRACE_SIZE_U32 * sizeof(u32)); ), TP_printk("device=%s host=%s status: '%s' first_error: '%s'", __get_str(device), __get_str(host), @@ -85,7 +89,7 @@ TRACE_EVENT(cxl_aer_uncorrectable_error, __field(u64, serial) __field(u32, status) __field(u32, first_error) - __array(u32, header_log, CXL_HEADERLOG_SIZE_U32) + __array(u32, header_log, CXL_HEADERLOG_TRACE_SIZE_U32) ), TP_fast_assign( __assign_str(memdev); @@ -94,10 +98,14 @@ TRACE_EVENT(cxl_aer_uncorrectable_error, __entry->status = status; __entry->first_error = fe; /* - * Embed the 512B headerlog data for user app retrieval and - * parsing, but no need to print this in the trace buffer. + * Embed headerlog data for user app retrieval and parsing, + * but no need to print in the trace buffer. Only + * CXL_HEADERLOG_SIZE_U32 (16) dwords are hardware data; + * the remaining entries preserve the 512-byte ABI layout + * rasdaemon depends on and are zero-filled by the caller. */ - memcpy(__entry->header_log, hl, CXL_HEADERLOG_SIZE); + memcpy(__entry->header_log, hl, + CXL_HEADERLOG_TRACE_SIZE_U32 * sizeof(u32)); ), TP_printk("memdev=%s host=%s serial=%lld: status: '%s' first_error: '%s'", __get_str(memdev), __get_str(host), __entry->serial, diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 1297594beaec..c0e5308e4d1b 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -91,7 +91,7 @@ static inline int cxl_hdm_decoder_count(u32 cap_hdr) } /* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */ -static inline int eig_to_granularity(u16 eig, unsigned int *granularity) +static inline int eig_to_granularity(u16 eig, int *granularity) { if (eig > CXL_DECODER_MAX_ENCODED_IG) return -EINVAL; @@ -100,7 +100,7 @@ static inline int eig_to_granularity(u16 eig, unsigned int *granularity) } /* Encode defined in CXL ECN "3, 6, 12 and 16-way memory Interleaving" */ -static inline int eiw_to_ways(u8 eiw, unsigned int *ways) +static inline int eiw_to_ways(u8 eiw, int *ways) { switch (eiw) { case 0 ... 4: @@ -118,6 +118,7 @@ static inline int eiw_to_ways(u8 eiw, unsigned int *ways) static inline int granularity_to_eig(int granularity, u16 *eig) { + *eig = 0; if (granularity > SZ_16K || granularity < CXL_DECODER_MIN_GRANULARITY || !is_power_of_2(granularity)) return -EINVAL; @@ -127,6 +128,7 @@ static inline int granularity_to_eig(int granularity, u16 *eig) static inline int ways_to_eiw(unsigned int ways, u8 *eiw) { + *eiw = 0; if (ways > 16) return -EINVAL; if (is_power_of_2(ways)) { @@ -158,8 +160,18 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw) #define CXL_RAS_CAP_CONTROL_FE_MASK GENMASK(5, 0) #define CXL_RAS_HEADER_LOG_OFFSET 0x18 #define CXL_RAS_CAPABILITY_LENGTH 0x58 -#define CXL_HEADERLOG_SIZE SZ_512 -#define CXL_HEADERLOG_SIZE_U32 SZ_512 / sizeof(u32) +#define CXL_HEADERLOG_SIZE SZ_64 +#define CXL_HEADERLOG_SIZE_U32 (CXL_HEADERLOG_SIZE / sizeof(u32)) + +/* + * The RAS UCE trace event header array was originally sized at SZ_512/sizeof(u32) + * = 128 u32s due to a bug. Userspace tools (rasdaemon) have grown a dependency + * on that 512-byte layout. Keep the trace array at 128 u32s to preserve the + * ABI; only CXL_HEADERLOG_SIZE_U32 (16) dwords are valid hardware data, the + * remainder are zero-filled. + */ +#define CXL_HEADERLOG_TRACE_SIZE SZ_512 +#define CXL_HEADERLOG_TRACE_SIZE_U32 (CXL_HEADERLOG_TRACE_SIZE / sizeof(u32)) /* CXL 2.0 8.2.8.1 Device Capabilities Array Register */ #define CXLDEV_CAP_ARRAY_OFFSET 0x0 @@ -359,7 +371,9 @@ struct cxl_rd_ops { * @cache_size: extended linear cache size if exists, otherwise zero. * @region_id: region id for next region provisioning event * @platform_data: platform specific configuration data - * @range_lock: sync region autodiscovery by address range + * @regions_lock: sync region discovery, construction, and deletion + * @regions: regions to remove at root decoder destruct time + * @dead: root decoder dead to region creation * @qos_class: QoS performance class cookie * @ops: CXL root decoder operations * @cxlsd: base cxl switch decoder @@ -369,7 +383,9 @@ struct cxl_root_decoder { resource_size_t cache_size; atomic_t region_id; void *platform_data; - struct mutex range_lock; + struct mutex regions_lock; + struct xarray regions; + bool dead; int qos_class; struct cxl_rd_ops ops; struct cxl_switch_decoder cxlsd; diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index 776c50d1db51..ed419d0c59f2 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -34,10 +34,6 @@ (FIELD_GET(CXLMDEV_RESET_NEEDED_MASK, status) != \ CXLMDEV_RESET_NEEDED_NOT) -struct cxl_memdev_attach { - int (*probe)(struct cxl_memdev *cxlmd); -}; - /** * struct cxl_memdev - CXL bus object representing a Type-3 Memory Device * @dev: driver core device object @@ -101,10 +97,36 @@ static inline bool is_cxl_endpoint(struct cxl_port *port) return is_cxl_memdev(port->uport_dev); } +struct cxl_memdev_attach { + int (*probe)(struct cxl_memdev *cxlmd); +}; + +/** + * struct cxl_attach_region - coordinate mapping a region at memdev registration + * @attach: common core attachment descriptor + * @hpa_range: physical address range of the region + * + * For the common simple case of a CXL device with private (non-general purpose + * / "accelerator") memory, enumerate firmware instantiated region, or + * instantiate a region for the device's capacity. Destroy the region on detach. + */ +struct cxl_attach_region { + struct cxl_memdev_attach attach; + struct range hpa_range; +}; + +#ifdef CONFIG_CXL_REGION +int cxl_memdev_attach_region(struct cxl_memdev *cxlmd); +#else +static inline int cxl_memdev_attach_region(struct cxl_memdev *cxlmd) +{ + return -EOPNOTSUPP; +} +#endif + +struct cxl_memdev *devm_cxl_add_classdev(struct cxl_dev_state *cxlds); struct cxl_memdev *__devm_cxl_add_memdev(struct cxl_dev_state *cxlds, const struct cxl_memdev_attach *attach); -struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds, - const struct cxl_memdev_attach *attach); int devm_cxl_sanitize_setup_notifier(struct device *host, struct cxl_memdev *cxlmd); struct cxl_memdev_state; diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c index fcffe24dcb42..798e5c369cfc 100644 --- a/drivers/cxl/mem.c +++ b/drivers/cxl/mem.c @@ -48,6 +48,11 @@ static int cxl_mem_dpa_show(struct seq_file *file, void *data) static int cxl_debugfs_poison_inject(void *data, u64 dpa) { struct cxl_memdev *cxlmd = data; + int rc; + + ACQUIRE(device_intr, devlock)(&cxlmd->dev); + if ((rc = ACQUIRE_ERR(device_intr, &devlock))) + return rc; return cxl_inject_poison(cxlmd, dpa); } @@ -58,6 +63,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_inject_fops, NULL, static int cxl_debugfs_poison_clear(void *data, u64 dpa) { struct cxl_memdev *cxlmd = data; + int rc; + + ACQUIRE(device_intr, devlock)(&cxlmd->dev); + if ((rc = ACQUIRE_ERR(device_intr, &devlock))) + return rc; return cxl_clear_poison(cxlmd, dpa); } @@ -65,6 +75,26 @@ static int cxl_debugfs_poison_clear(void *data, u64 dpa) DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_clear_fops, NULL, cxl_debugfs_poison_clear, "%llx\n"); +static void cxl_memdev_poison_enable(struct cxl_memdev_state *mds, + struct cxl_memdev *cxlmd, + struct dentry *dentry) +{ + /* + * Avoid poison debugfs for DEVMEM aka accelerators as they rely on + * cxl_memdev_state. + */ + if (!mds) + return; + + if (test_bit(CXL_POISON_ENABLED_INJECT, mds->poison.enabled_cmds)) + debugfs_create_file("inject_poison", 0200, dentry, cxlmd, + &cxl_poison_inject_fops); + + if (test_bit(CXL_POISON_ENABLED_CLEAR, mds->poison.enabled_cmds)) + debugfs_create_file("clear_poison", 0200, dentry, cxlmd, + &cxl_poison_clear_fops); +} + static int cxl_mem_probe(struct device *dev) { struct cxl_memdev *cxlmd = to_cxl_memdev(dev); @@ -92,12 +122,7 @@ static int cxl_mem_probe(struct device *dev) dentry = cxl_debugfs_create_dir(dev_name(dev)); debugfs_create_devm_seqfile(dev, "dpamem", dentry, cxl_mem_dpa_show); - if (test_bit(CXL_POISON_ENABLED_INJECT, mds->poison.enabled_cmds)) - debugfs_create_file("inject_poison", 0200, dentry, cxlmd, - &cxl_poison_inject_fops); - if (test_bit(CXL_POISON_ENABLED_CLEAR, mds->poison.enabled_cmds)) - debugfs_create_file("clear_poison", 0200, dentry, cxlmd, - &cxl_poison_clear_fops); + cxl_memdev_poison_enable(mds, cxlmd, dentry); rc = devm_add_action_or_reset(dev, remove_debugfs, dentry); if (rc) @@ -168,27 +193,59 @@ static int cxl_mem_probe(struct device *dev) } /** - * devm_cxl_add_memdev - Add a CXL memory device + * devm_cxl_add_classdev - Add a CXL memory class-code device * @cxlds: CXL device state to associate with the memdev - * @attach: Caller depends on CXL topology attachment * * Upon return the device will have had a chance to attach to the * cxl_mem driver, but may fail to attach if the CXL topology is not ready * (hardware CXL link down, or software platform CXL root not attached). * - * When @attach is NULL it indicates the caller wants the memdev to remain - * registered even if it does not immediately attach to the CXL hierarchy. When - * @attach is provided a cxl_mem_probe() failure leads to failure of this routine. + * The parent of the resulting device and the devm context for allocations is + * @cxlds->dev. + */ +struct cxl_memdev *devm_cxl_add_classdev(struct cxl_dev_state *cxlds) +{ + return __devm_cxl_add_memdev(cxlds, NULL); +} +EXPORT_SYMBOL_NS_GPL(devm_cxl_add_classdev, "CXL"); + +/** + * devm_cxl_probe_mem - Add a CXL memory device and probe its region + * @cxlds: CXL device state to associate with the memdev + * @hpa_range: CXL.mem physical address range result + * + * Upon return the device will have had a chance to attach to the + * cxl_mem driver, but may fail to attach if the CXL topology is not ready + * (hardware CXL link down, or software platform CXL root not attached). + * + * Failure to probe the memdev and/or setup a region for the memdev + * results in this function failing. * * The parent of the resulting device and the devm context for allocations is * @cxlds->dev. */ -struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds, - const struct cxl_memdev_attach *attach) +struct cxl_memdev *devm_cxl_probe_mem(struct cxl_dev_state *cxlds, + struct range *hpa_range) { - return __devm_cxl_add_memdev(cxlds, attach); + struct cxl_attach_region *attach = + devm_kmalloc(cxlds->dev, sizeof(*attach), GFP_KERNEL); + struct cxl_memdev *cxlmd; + + if (!attach) + return ERR_PTR(-ENOMEM); + + *attach = (struct cxl_attach_region) { + .attach = { + .probe = cxl_memdev_attach_region, + }, + .hpa_range = { 0, -1 }, + }; + + cxlmd = __devm_cxl_add_memdev(cxlds, &attach->attach); + *hpa_range = attach->hpa_range; + return cxlmd; } -EXPORT_SYMBOL_NS_GPL(devm_cxl_add_memdev, "CXL"); +EXPORT_SYMBOL_NS_GPL(devm_cxl_probe_mem, "CXL"); static ssize_t trigger_poison_list_store(struct device *dev, struct device_attribute *attr, @@ -206,16 +263,24 @@ static ssize_t trigger_poison_list_store(struct device *dev, } static DEVICE_ATTR_WO(trigger_poison_list); -static umode_t cxl_mem_visible(struct kobject *kobj, struct attribute *a, int n) +static bool cxl_poison_attr_visible(struct kobject *kobj, struct attribute *a) { struct device *dev = kobj_to_dev(kobj); struct cxl_memdev *cxlmd = to_cxl_memdev(dev); struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds); - if (a == &dev_attr_trigger_poison_list.attr) - if (!test_bit(CXL_POISON_ENABLED_LIST, - mds->poison.enabled_cmds)) - return 0; + if (!mds || + !test_bit(CXL_POISON_ENABLED_LIST, mds->poison.enabled_cmds)) + return false; + + return true; +} + +static umode_t cxl_mem_visible(struct kobject *kobj, struct attribute *a, int n) +{ + if (a == &dev_attr_trigger_poison_list.attr && + !cxl_poison_attr_visible(kobj, a)) + return 0; return a->mode; } diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index bace662dc988..267c679b0b3c 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -878,7 +878,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (rc) dev_dbg(&pdev->dev, "No CXL Features discovered\n"); - cxlmd = devm_cxl_add_memdev(cxlds, NULL); + cxlmd = devm_cxl_add_classdev(cxlds); if (IS_ERR(cxlmd)) return PTR_ERR(cxlmd); diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c index ada51948d52f..99cf77b6b699 100644 --- a/drivers/cxl/port.c +++ b/drivers/cxl/port.c @@ -18,7 +18,7 @@ * firmware) are managed in this drivers context. Each driver instance * is responsible for tearing down the driver context of immediate * descendant ports. The locking for this is validated by - * CONFIG_PROVE_CXL_LOCKING. + * CONFIG_PROVE_LOCKING. * * The primary service this driver provides is presenting APIs to other * drivers to utilize the decoders, and indicating to userspace (via bind diff --git a/include/cxl/cxl.h b/include/cxl/cxl.h index fa7269154620..016c74fb747c 100644 --- a/include/cxl/cxl.h +++ b/include/cxl/cxl.h @@ -223,4 +223,7 @@ struct cxl_dev_state *_devm_cxl_dev_state_create(struct device *dev, (drv_struct *)_devm_cxl_dev_state_create(parent, type, serial, dvsec, \ sizeof(drv_struct), mbox); \ }) + +struct cxl_memdev *devm_cxl_probe_mem(struct cxl_dev_state *cxlds, + struct range *range); #endif /* __CXL_CXL_H__ */ diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 296516eecfd6..ef92dd35e030 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -318,7 +318,7 @@ static struct { .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, .qtg_id = FAKE_QTG_ID, - .window_size = SZ_256M, + .window_size = SZ_256M > PMD_SIZE ? SZ_256M : PMD_SIZE, }, .target = { 3 }, }, @@ -433,12 +433,16 @@ static void depopulate_all_mock_resources(void) static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) { - struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL); struct genpool_data_align data = { .align = align, }; unsigned long phys; + struct cxl_mock_res *res __free(kfree) = kzalloc(sizeof(*res), + GFP_KERNEL); + if (!res) + return NULL; + INIT_LIST_HEAD(&res->list); phys = gen_pool_alloc_algo(cxl_mock_pool, size, gen_pool_first_fit_align, &data); @@ -453,7 +457,7 @@ static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) list_add(&res->list, &mock_res); mutex_unlock(&mock_res_lock); - return res; + return no_free_ptr(res); } /* Only update CFMWS0 as this is used by the auto region. */ @@ -495,9 +499,12 @@ static int populate_cedt(void) for (i = cfmws_start; i <= cfmws_end; i++) { struct acpi_cedt_cfmws *window = mock_cfmws[i]; + int align = SZ_256M; cfmws_elc_update(window, i); - res = alloc_mock_res(window->window_size, SZ_256M); + if (window->restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE) + align = max_t(int, SZ_256M, PMD_SIZE); + res = alloc_mock_res(window->window_size, align); if (!res) return -ENOMEM; window->base_hpa = res->range.start; @@ -1181,15 +1188,11 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) cxlsd = to_cxl_switch_decoder(dev); if (i == 0) { /* put cxl_mem.4 second in the decode order */ - if (pdev->id == 4) { - cxlsd->target[1] = dport; + if (pdev->id == 4) cxlsd->cxld.target_map[1] = dport->port_id; - } else { - cxlsd->target[0] = dport; + else cxlsd->cxld.target_map[0] = dport->port_id; - } } else { - cxlsd->target[0] = dport; cxlsd->cxld.target_map[0] = dport->port_id; } cxld = &cxlsd->cxld; @@ -1212,6 +1215,16 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) cxld->commit = mock_decoder_commit; cxld->reset = mock_decoder_reset; + /* + * Only target_map[] is programmed above, mimicking + * firmware. On real hardware target[] is populated as + * dports enumerate, via update_decoder_targets(). The + * mock's dports are already bound by now, so fire that + * resolution explicitly here rather than stamping + * target[] directly. + */ + cxl_port_update_decoder_targets(iter, dport); + cxld_registry_update(cxld); put_device(dev); } @@ -1819,6 +1832,12 @@ static __init int cxl_test_init(void) int rc, i; struct range mappable; + if (!IS_ALIGNED(mock_auto_region_size, PMD_SIZE)) { + pr_err_once("mock_auto_region_size %d must be PMD-aligned\n", + mock_auto_region_size); + return -EINVAL; + } + cxl_acpi_test(); cxl_core_test(); cxl_mem_test(); @@ -1951,7 +1970,7 @@ static __init int cxl_test_init(void) err_mem: cxl_mem_exit(); err_root: - platform_device_put(cxl_acpi); + platform_device_unregister(cxl_acpi); err_rch: cxl_rch_topo_exit(); err_single: diff --git a/tools/testing/cxl/test/cxl_translate.c b/tools/testing/cxl/test/cxl_translate.c index 16328b2112b2..25a27e01ac21 100644 --- a/tools/testing/cxl/test/cxl_translate.c +++ b/tools/testing/cxl/test/cxl_translate.c @@ -236,8 +236,8 @@ static int setup_xor_mapping(void) if (!cximsd) return -ENOMEM; - memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps)); cximsd->nr_maps = nr_maps; + memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps)); return 0; } diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 271c7ad8cc32..a1d170f88fee 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -312,12 +312,17 @@ static int mock_get_event(struct device *dev, struct cxl_mbox_cmd *cmd) static int mock_clear_event(struct device *dev, struct cxl_mbox_cmd *cmd) { - struct cxl_mbox_clear_event_payload *pl = cmd->payload_in; + struct cxl_mbox_clear_event_payload *pl; struct mock_event_log *log; - u8 log_type = pl->event_log; + u8 log_type; u16 handle; int nr; + if (cmd->size_in < sizeof(*pl)) + return -EINVAL; + + pl = cmd->payload_in; + log_type = pl->event_log; if (log_type >= CXL_EVENT_TYPE_MAX) return -EINVAL; @@ -574,14 +579,19 @@ static int mock_gsl(struct cxl_mbox_cmd *cmd) static int mock_get_log(struct cxl_memdev_state *mds, struct cxl_mbox_cmd *cmd) { struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; - struct cxl_mbox_get_log *gl = cmd->payload_in; - u32 offset = le32_to_cpu(gl->offset); - u32 length = le32_to_cpu(gl->length); uuid_t uuid = DEFINE_CXL_CEL_UUID; + struct cxl_mbox_get_log *gl; void *data = &mock_cel; + u32 offset; + u32 length; if (cmd->size_in < sizeof(*gl)) return -EINVAL; + + gl = cmd->payload_in; + offset = le32_to_cpu(gl->offset); + length = le32_to_cpu(gl->length); + if (length > cxl_mbox->payload_size) return -EINVAL; if (offset + length > sizeof(mock_cel)) @@ -1053,7 +1063,7 @@ static int mock_get_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(get_lsa->offset); length = le32_to_cpu(get_lsa->length); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; if (length > cmd->size_out) return -EINVAL; @@ -1073,7 +1083,7 @@ static int mock_set_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(set_lsa->offset); length = cmd->size_in - sizeof(*set_lsa); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; memcpy(lsa + offset, &set_lsa->data[0], length); @@ -1336,10 +1346,14 @@ static int mock_fw_info(struct cxl_mockmem_data *mdata, static int mock_transfer_fw(struct cxl_mockmem_data *mdata, struct cxl_mbox_cmd *cmd) { - struct cxl_mbox_transfer_fw *transfer = cmd->payload_in; + struct cxl_mbox_transfer_fw *transfer; void *fw = mdata->fw; size_t offset, length; + if (cmd->size_in < sizeof(*transfer)) + return -EINVAL; + + transfer = cmd->payload_in; offset = le32_to_cpu(transfer->offset) * CXL_FW_TRANSFER_ALIGNMENT; length = cmd->size_in - sizeof(*transfer); if (offset + length > FW_SIZE) @@ -1415,11 +1429,18 @@ static int mock_get_test_feature(struct cxl_mockmem_data *mdata, struct cxl_mbox_cmd *cmd) { struct vendor_test_feat *output = cmd->payload_out; - struct cxl_mbox_get_feat_in *input = cmd->payload_in; - u16 offset = le16_to_cpu(input->offset); - u16 count = le16_to_cpu(input->count); + struct cxl_mbox_get_feat_in *input; + u16 offset; + u16 count; u8 *ptr; + if (cmd->size_in < sizeof(*input)) + return -EINVAL; + + input = cmd->payload_in; + offset = le16_to_cpu(input->offset); + count = le16_to_cpu(input->count); + if (offset > sizeof(*output)) { cmd->return_code = CXL_MBOX_CMD_RC_INPUT; return -EINVAL; @@ -1703,7 +1724,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) return -ENOMEM; dev_set_drvdata(dev, mdata); - mdata->lsa = vmalloc(LSA_SIZE); + mdata->lsa = vzalloc(LSA_SIZE); if (!mdata->lsa) return -ENOMEM; mdata->fw = vmalloc(FW_SIZE); @@ -1769,7 +1790,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) cxl_mock_add_event_logs(&mdata->mes); - cxlmd = devm_cxl_add_memdev(cxlds, NULL); + cxlmd = devm_cxl_add_classdev(cxlds); if (IS_ERR(cxlmd)) return PTR_ERR(cxlmd);