CXL changes for v7.2

Introduce devm_cxl_probe_mem(), in prep for type2 basic enabling:
 cxl: Add dummy function for cxl_memdev_attach_region for !CONFIG_CXL_REGION
 cxl/region: Introduce devm_cxl_probe_mem()
 cxl/memdev: Introduce cxl_class_memdev_type
 cxl/memdev: Pin parents for entire memdev lifetime
 cxl/region: Resolve region deletion races
 cxl/region: Block region delete during region creation
 
 CXL Misc:
 cxl/region: Fill first free targets[] slot during auto-discovery
 cxl/region: Fix out-of-bounds access in cxl_cancel_auto_attach()
 tools/testing/cxl: Resolve auto-region decoder targets like real HW
 cxl: Align interleave decode/encode helpers with their callers
 cxl/test: Add check after kzalloc() memory in alloc_mock_res()
 cxl/test: Unregister cxl_acpi in cxl_test_init() error path
 cxl/test: Zero out LSA backing memory to avoid leaking to user
 cxl/test: Fix integer overflow in mock LSA bounds checks
 cxl/test: Verify cmd->size_in before accessing payload
 cxl/port: update reference to removed CONFIG_PROVE_CXL_LOCKING
 cxl/region: Avoid variable shadowing in region attach paths
 cxl: Fix CXL_HEADERLOG_SIZE to match RAS Capability size
 cxl/test: Fix __fortify_panic
 cxl/fwctl: Fix __fortify_panic
 MAINTAINERS: Add CXL reviewer
 cxl/test: Enforce PMD alignment for volatile mock regions
 cxl/region: Validate partition index before array access
 cxl/memdev: Hold memdev lock during memdev poison injection/clear
 
 CXL PCI fixes:
 cxl/pci: Convert PCIBIOS errors to errno on DVSEC config accesses
 cxl/pci: Fix the incorrect check of pci_read_config_word() return
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE5DAy15EJMCV1R6v9YGjFFmlTOEoFAmo0MqsACgkQYGjFFmlT
 OEp5qQ/+N//u4DqTq54P7ZV1jpVzMEchV411frsWF5wl/pvEz9cr/ruPNxpg6Gdz
 AtryU1hcXRrDu9hvmO3odDM7jdZsIcF8GbpAWy5blAZaRt37aU6A4+fsFb8apFst
 SoFQ9BV23clPAEM7TYKXgya+G4kFu+btf3uxIutc8R78e88aEHGDz/Si//cocLmo
 sZY5Qky5eGca7wtX5rEImJtwsKDcTrtTQKQYGcuV6swLtLuJsHgL86TEfrWAH36M
 Vugb83a9AhQnGaTtKhvViHZkbMTOjCfOZom9BS++YDl/hVSPU1kF184ZPAI9oi4y
 0kKDyoKbTN+oEIhrmhC4SGi0UgIW2O4Y6zR7lgr2iYm08Ki9ehbOI5EX6+PTVMSH
 lzLVunyLY/AAU9wrQ4YENYuZgfdUXpOgUzseCVIhN4kVo0oWhnUPHVRj2m2fZ7oH
 JzQV1MP2YJEbfqtAFVndBBZhBjrMZMcJCHhdISvz0Axv9ezj02LYJPVgmnGDthX7
 kKvPgK7u/3sShAk2qmh1csFlT3H+UH1lpPcGM6btOjpI9TNIsYnyH/VaoHt4CIVL
 LZpMwQEMbz7S+An1+Ckwehe7LZAXO4IMUVFSdx6pAIRILNBv+68QkFtg2Ok5v/4s
 7p5WPOvS97tTf6z5G/dQkDkGQVg1rdCfrQNN/MrZK4bEvMoQ5DM=
 =CimP
 -----END PGP SIGNATURE-----

Merge tag 'cxl-for-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl

Pull CXL updates from Dave Jiang:
 "A collection of CXL fixes and changes, including an update to
  MAINTAINERS to add Ming Li as a CXL subsystem reviewer.

  There's also a series to introduce devm_cxl_probe_mem() core API in
  order to pave the way for CXL type2 device drivers to setup and
  retrieve CXL region resource during probe"

* tag 'cxl-for-7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (26 commits)
  cxl: Add dummy function for cxl_memdev_attach_region for !CONFIG_CXL_REGION
  cxl/region: Introduce devm_cxl_probe_mem()
  cxl/memdev: Introduce cxl_class_memdev_type
  cxl/memdev: Pin parents for entire memdev lifetime
  cxl/region: Resolve region deletion races
  cxl/region: Block region delete during region creation
  cxl/region: Fill first free targets[] slot during auto-discovery
  cxl/region: Fix out-of-bounds access in cxl_cancel_auto_attach()
  tools/testing/cxl: Resolve auto-region decoder targets like real HW
  cxl: Align interleave decode/encode helpers with their callers
  cxl/test: Add check after kzalloc() memory in alloc_mock_res()
  cxl/test: Unregister cxl_acpi in cxl_test_init() error path
  cxl/test: Zero out LSA backing memory to avoid leaking to user
  cxl/test: Fix integer overflow in mock LSA bounds checks
  cxl/test: Verify cmd->size_in before accessing payload
  cxl/port: update reference to removed CONFIG_PROVE_CXL_LOCKING
  cxl/region: Avoid variable shadowing in region attach paths
  cxl: Fix CXL_HEADERLOG_SIZE to match RAS Capability size
  cxl/pci: Convert PCIBIOS errors to errno on DVSEC config accesses
  cxl/pci: Fix the incorrect check of pci_read_config_word() return
  ...
This commit is contained in:
Linus Torvalds 2026-06-18 16:38:36 -07:00
commit 5cd1731cc8
19 changed files with 495 additions and 158 deletions

View File

@ -6456,6 +6456,7 @@ M: Alison Schofield <alison.schofield@intel.com>
M: Vishal Verma <vishal.l.verma@intel.com>
M: Dan Williams <djbw@kernel.org>
R: Ira Weiny <iweiny@kernel.org>
R: Li Ming <ming.li@zohomail.com>
L: linux-cxl@vger.kernel.org
S: Maintained
F: Documentation/driver-api/cxl

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -8,6 +8,10 @@
#include <cxlpci.h>
#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;

View File

@ -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, &regions_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, &regions_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);

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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__ */

View File

@ -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:

View File

@ -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;
}

View File

@ -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);