Re: [PATCH v7 4/8] cxl: Add HDM-DB region creation
Dave Jiang <[email protected]> Tue, 4 Aug 2026 15:28:03 -0700
| Newsgroups | org.kernel.vger.linux-cxl |
|---|---|
| Message-ID | <[email protected]> |
On 7/28/26 7:41 AM, Davidlohr Bueso wrote: > A region inherits its coherency from the chosen root decoder: HDM-DB > if the root has CXL_DECODER_F_BI, otherwise HDM-H. > > Surface the topology through read-only sysfs: > > - decoderX.Y/cap_bi (root): CFMWS BI restriction. > - decoderX.Y/bi (endpoint): '1' when configured for HDM-DB. > > cxl_region_attach() rejects endpoints whose device or HDM cannot > serve the region's type; target_type is inherited from cxlr->type > in cxl_rr_assign_decoder() and restored to the endpoint default on > detach and on a failed attach. > > The HDM Decoder Control BI bit is set at commit time only when the > target_type is DEVMEM and the BI capability is advertised > (cxlds->bi for endpoints, root F_BI for switches). > > Signed-off-by: Davidlohr Bueso <[email protected]> Reviewed-by: Dave Jiang <[email protected]> > --- > Documentation/ABI/testing/sysfs-bus-cxl | 18 ++++++-- > drivers/cxl/acpi.c | 15 +++++++ > drivers/cxl/core/hdm.c | 23 ++++++++++ > drivers/cxl/core/port.c | 39 +++++++++++++++-- > drivers/cxl/core/region.c | 57 +++++++++++++++++++------ > drivers/cxl/cxl.h | 5 +++ > 6 files changed, 136 insertions(+), 21 deletions(-) > > diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl > index 16a9b3d2e2c0..370c6fd7d245 100644 > --- a/Documentation/ABI/testing/sysfs-bus-cxl > +++ b/Documentation/ABI/testing/sysfs-bus-cxl > @@ -297,7 +297,7 @@ Description: > Each entry in the list is a dport id. > > > -What: /sys/bus/cxl/devices/decoderX.Y/cap_{pmem,ram,type2,type3} > +What: /sys/bus/cxl/devices/decoderX.Y/cap_{pmem,ram,type2,type3,bi} > Date: June, 2021 > KernelVersion: v5.14 > Contact: [email protected] > @@ -306,8 +306,9 @@ Description: > represents a fixed memory window identified by platform > firmware. A fixed window may only support a subset of memory > types. The 'cap_*' attributes indicate whether persistent > - memory, volatile memory, accelerator memory, and / or expander > - memory may be mapped behind this decoder's memory window. > + memory, volatile memory, accelerator memory, expander memory, > + and / or back-invalidate (HDM-DB) memory may be mapped behind > + this decoder's memory window. > > > What: /sys/bus/cxl/devices/decoderX.Y/target_type > @@ -426,6 +427,17 @@ Description: > current cached value. > > > +What: /sys/bus/cxl/devices/decoderX.Y/bi > +Date: July, 2026 > +KernelVersion: v7.3 > +Contact: [email protected] > +Description: > + (RO) Shows '1' if this endpoint decoder is currently configured > + for HDM-DB (device-managed coherency with back-invalidate). > + The HDM-DB state is inherited from the region the decoder is > + attached to, which is in turn set from the chosen root > + decoder's CFMWS BI restriction (see cap_bi). > + > What: /sys/bus/cxl/devices/decoderX.Y/delete_region > Date: May, 2022 > KernelVersion: v6.0 > diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c > index 3b818adbd38b..eceb8dd97df2 100644 > --- a/drivers/cxl/acpi.c > +++ b/drivers/cxl/acpi.c > @@ -152,6 +152,8 @@ static unsigned long cfmws_to_decoder_flags(int restrictions) > flags |= CXL_DECODER_F_PMEM; > if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_FIXED) > flags |= CXL_DECODER_F_LOCK; > + if (restrictions & ACPI_CEDT_CFMWS_RESTRICT_BI) > + flags |= CXL_DECODER_F_BI; > > return flags; > } > @@ -198,6 +200,12 @@ static int cxl_acpi_cfmws_verify(struct device *dev, > dev_dbg(dev, "CFMWS length %d greater than expected %d\n", > cfmws->header.length, expected_len); > > + if ((cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM) && > + (cfmws->restrictions & ACPI_CEDT_CFMWS_RESTRICT_BI)) { > + dev_err(dev, "CFMWS cannot have both HDM-H and HDM-DB\n"); > + return -EINVAL; > + } > + > return 0; > } > > @@ -437,7 +445,14 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws, > > cxld = &cxlrd->cxlsd.cxld; > cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions); > + /* host-only wins if firmware sets both coherency restrictions */ > cxld->target_type = CXL_DECODER_HOSTONLYMEM; > + if (cxld->flags & CXL_DECODER_F_TYPE2) { > + if (cxld->flags & CXL_DECODER_F_TYPE3) > + dev_dbg(dev, "CFMWS has both HDM-H and HDM-D\n"); > + else > + cxld->target_type = CXL_DECODER_DEVMEM; > + } > cxld->hpa_range = (struct range) { > .start = cfmws->base_hpa, > .end = cfmws->base_hpa + cfmws->window_size - 1, > diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c > index 9da8aa211609..f437fe15c6df 100644 > --- a/drivers/cxl/core/hdm.c > +++ b/drivers/cxl/core/hdm.c > @@ -705,9 +705,25 @@ static void cxld_set_interleave(struct cxl_decoder *cxld, u32 *ctrl) > > static void cxld_set_type(struct cxl_decoder *cxld, u32 *ctrl) > { > + bool bi = cxld->target_type == CXL_DECODER_DEVMEM; > + > + if (bi) { > + if (is_endpoint_decoder(&cxld->dev)) { > + struct cxl_endpoint_decoder *cxled = > + to_cxl_endpoint_decoder(&cxld->dev); > + struct cxl_dev_state *cxlds = > + cxled_to_memdev(cxled)->cxlds; > + > + bi = cxlds->bi; > + } else if (cxld->region) { > + bi = cxl_root_decoder_is_bi(cxld->region->cxlrd); > + } > + } > + > u32p_replace_bits(ctrl, > !!(cxld->target_type == CXL_DECODER_HOSTONLYMEM), > CXL_HDM_DECODER0_CTRL_HOSTONLY); > + u32p_replace_bits(ctrl, bi, CXL_HDM_DECODER0_CTRL_BI); > } > > static void cxlsd_set_targets(struct cxl_switch_decoder *cxlsd, u64 *tgt) > @@ -970,6 +986,13 @@ static int cxl_setup_hdm_decoder_from_dvsec( > return 0; > } > > +/* > + * HDMs that advertise support for both coherency modes > + * (CXL_HDM_DECODER_COHERENCY_BOTH) default to host-only; the region > + * attach path switches target_type to device-coherent if the region's > + * root decoder has the CFMWS BI bit set. Only HDMs that strictly > + * support device-coherent mode default to HDM-DB. > + */ > enum cxl_decoder_type cxled_default_type(struct cxl_endpoint_decoder *cxled) > { > struct cxl_dev_state *cxlds = cxled_to_memdev(cxled)->cxlds; > diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c > index 7817109026b1..2dbd8c24f968 100644 > --- a/drivers/cxl/core/port.c > +++ b/drivers/cxl/core/port.c > @@ -131,6 +131,7 @@ CXL_DECODER_FLAG_ATTR(cap_ram, CXL_DECODER_F_RAM); > CXL_DECODER_FLAG_ATTR(cap_type2, CXL_DECODER_F_TYPE2); > CXL_DECODER_FLAG_ATTR(cap_type3, CXL_DECODER_F_TYPE3); > CXL_DECODER_FLAG_ATTR(locked, CXL_DECODER_F_LOCK); > +CXL_DECODER_FLAG_ATTR(cap_bi, CXL_DECODER_F_BI); > > static ssize_t target_type_show(struct device *dev, > struct device_attribute *attr, char *buf) > @@ -233,6 +234,26 @@ static ssize_t mode_store(struct device *dev, struct device_attribute *attr, > } > static DEVICE_ATTR_RW(mode); > > +static ssize_t bi_show(struct device *dev, struct device_attribute *attr, > + char *buf) > +{ > + struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(dev); > + struct cxl_dev_state *cxlds = cxled_to_memdev(cxled)->cxlds; > + struct cxl_region *cxlr; > + > + guard(rwsem_read)(&cxl_rwsem.region); > + /* > + * An endpoint decoder is HDM-DB when the device advertises BI > + * (cxlds->bi) and it is attached to a device-coherent (DEVMEM) > + * region whose root decoder advertises the CFMWS BI restriction. > + */ > + cxlr = cxled->cxld.region; > + return sysfs_emit(buf, "%d\n", cxlds->bi && cxlr && > + cxled->cxld.target_type == CXL_DECODER_DEVMEM && > + cxl_root_decoder_is_bi(cxlr->cxlrd)); > +} > +static DEVICE_ATTR_RO(bi); > + > static ssize_t dpa_resource_show(struct device *dev, struct device_attribute *attr, > char *buf) > { > @@ -329,6 +350,7 @@ static struct attribute *cxl_decoder_root_attrs[] = { > &dev_attr_cap_ram.attr, > &dev_attr_cap_type2.attr, > &dev_attr_cap_type3.attr, > + &dev_attr_cap_bi.attr, > &dev_attr_target_list.attr, > &dev_attr_qos_class.attr, > SET_CXL_REGION_ATTR(create_pmem_region) > @@ -339,16 +361,24 @@ static struct attribute *cxl_decoder_root_attrs[] = { > > static bool can_create_pmem(struct cxl_root_decoder *cxlrd) > { > - unsigned long flags = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_PMEM; > + unsigned long flags = cxlrd->cxlsd.cxld.flags; > + unsigned long hdm_h, hdm_db; > > - return (cxlrd->cxlsd.cxld.flags & flags) == flags; > + hdm_h = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_PMEM; > + hdm_db = CXL_DECODER_F_TYPE2 | CXL_DECODER_F_BI | CXL_DECODER_F_PMEM; > + > + return (flags & hdm_h) == hdm_h || (flags & hdm_db) == hdm_db; > } > > static bool can_create_ram(struct cxl_root_decoder *cxlrd) > { > - unsigned long flags = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_RAM; > + unsigned long flags = cxlrd->cxlsd.cxld.flags; > + unsigned long hdm_h, hdm_db; > + > + hdm_h = CXL_DECODER_F_TYPE3 | CXL_DECODER_F_RAM; > + hdm_db = CXL_DECODER_F_TYPE2 | CXL_DECODER_F_BI | CXL_DECODER_F_RAM; > > - return (cxlrd->cxlsd.cxld.flags & flags) == flags; > + return (flags & hdm_h) == hdm_h || (flags & hdm_db) == hdm_db; > } > > static umode_t cxl_root_decoder_visible(struct kobject *kobj, struct attribute *a, int n) > @@ -402,6 +432,7 @@ static const struct attribute_group *cxl_decoder_switch_attribute_groups[] = { > static struct attribute *cxl_decoder_endpoint_attrs[] = { > &dev_attr_target_type.attr, > &dev_attr_mode.attr, > + &dev_attr_bi.attr, > &dev_attr_dpa_size.attr, > &dev_attr_dpa_resource.attr, > SET_CXL_REGION_ATTR(region) > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c > index 1e211542b6b6..840acc330ede 100644 > --- a/drivers/cxl/core/region.c > +++ b/drivers/cxl/core/region.c > @@ -1130,16 +1130,11 @@ static int cxl_rr_assign_decoder(struct cxl_port *port, struct cxl_region *cxlr, > } > > /* > - * Endpoints should already match the region type, but backstop that > - * assumption with an assertion. Switch-decoders change mapping-type > - * based on what is mapped when they are assigned to a region. > + * Endpoint decoders inherit their type from cxlr->type; broken > + * pairings were already rejected by the coherency checks in > + * cxl_region_attach(). Switch-decoders change mapping-type based > + * on what is mapped when they are assigned to a region. > */ > - dev_WARN_ONCE(&cxlr->dev, > - port == cxled_to_port(cxled) && > - cxld->target_type != cxlr->type, > - "%s:%s mismatch decoder type %d -> %d\n", > - dev_name(&cxled_to_memdev(cxled)->dev), > - dev_name(&cxld->dev), cxld->target_type, cxlr->type); > cxld->target_type = cxlr->type; > cxl_rr->decoder = cxld; > return 0; > @@ -1827,6 +1822,8 @@ static int cxl_region_attach_position(struct cxl_region *cxlr, > for (iter = cxled_to_port(cxled); !is_cxl_root(iter); > iter = to_cxl_port(iter->dev.parent)) > cxl_port_detach_region(iter, cxlr, cxled); > + /* undo cxl_rr_assign_decoder() type inheritance */ > + cxled->cxld.target_type = cxled_default_type(cxled); > return rc; > } > > @@ -2059,6 +2056,7 @@ static int cxl_region_attach(struct cxl_region *cxlr, > struct cxl_region_params *p = &cxlr->params; > struct cxl_port *ep_port, *root_port; > struct cxl_dport *dport; > + struct cxl_hdm *cxlhdm; > int rc = -ENXIO; > > rc = check_interleave_cap(&cxled->cxld, p->interleave_ways, > @@ -2108,10 +2106,31 @@ static int cxl_region_attach(struct cxl_region *cxlr, > return -ENXIO; > } > > - if (cxled->cxld.target_type != cxlr->type) { > - dev_dbg(&cxlr->dev, "%s:%s type mismatch: %d vs %d\n", > - dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), > - cxled->cxld.target_type, cxlr->type); > + /* > + * Verify the device and HDM are capable of the region's flavor before > + * proceeding. The endpoint decoder's target_type is then inherited > + * from cxlr->type later in cxl_rr_assign_decoder(). > + */ > + if (cxlr->type == CXL_DECODER_DEVMEM && > + cxl_root_decoder_is_bi(cxlrd) && !cxlds->bi) { > + dev_err(&cxlr->dev, "%s:%s BI not enabled on device\n", > + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev)); > + return -ENXIO; > + } > + > + cxlhdm = dev_get_drvdata(&ep_port->dev); > + if (!cxlhdm) > + return -ENXIO; > + if (cxlr->type == CXL_DECODER_HOSTONLYMEM && > + cxlhdm->supported_coherency == CXL_HDM_DECODER_COHERENCY_DEV) { > + dev_warn(&cxlr->dev, "%s:%s HDM is device-coherent only\n", > + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev)); > + return -ENXIO; > + } > + if (cxlr->type == CXL_DECODER_DEVMEM && > + cxlhdm->supported_coherency == CXL_HDM_DECODER_COHERENCY_HOST) { > + dev_warn(&cxlr->dev, "%s:%s HDM is host-only coherent\n", > + dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev)); > return -ENXIO; > } > > @@ -2327,6 +2346,8 @@ __cxl_decoder_detach(struct cxl_region *cxlr, > .start = 0, > .end = -1, > }; > + /* undo cxl_rr_assign_decoder() type inheritance */ > + cxled->cxld.target_type = cxled_default_type(cxled); > > get_device(&cxlr->dev); > return cxlr; > @@ -2823,6 +2844,7 @@ static ssize_t create_region_store(struct device *dev, const char *buf, > size_t len, enum cxl_partition_mode mode) > { > struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev); > + enum cxl_decoder_type target_type; > struct cxl_region *cxlr; > int rc, id; > > @@ -2834,7 +2856,14 @@ static ssize_t create_region_store(struct device *dev, const char *buf, > if ((rc = ACQUIRE_ERR(mutex_intr, ®ions_lock))) > return rc; > > - cxlr = __create_region(cxlrd, mode, id, CXL_DECODER_HOSTONLYMEM); > + /* > + * The CFMWS dictates endpoint coherency: a BI-restricted Window > + * produces an HDM-DB region; otherwise HDM-H. > + */ > + target_type = cxl_root_decoder_is_bi(cxlrd) ? > + CXL_DECODER_DEVMEM : CXL_DECODER_HOSTONLYMEM; > + > + cxlr = __create_region(cxlrd, mode, id, target_type); > if (IS_ERR(cxlr)) > return PTR_ERR(cxlr); > > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h > index c323c38d0da2..ec2203561446 100644 > --- a/drivers/cxl/cxl.h > +++ b/drivers/cxl/cxl.h > @@ -300,6 +300,7 @@ int cxl_dport_map_rcd_linkcap(struct pci_dev *pdev, struct cxl_dport *dport); > #define CXL_DECODER_F_LOCK BIT(4) > #define CXL_DECODER_F_ENABLE BIT(5) > #define CXL_DECODER_F_NORMALIZED_ADDRESSING BIT(6) > +#define CXL_DECODER_F_BI BIT(7) > #define CXL_DECODER_F_RESET_MASK (CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK) > > enum cxl_decoder_type { > @@ -826,6 +827,10 @@ static inline int cxl_root_decoder_autoremove(struct device *host, > { > return cxl_decoder_autoremove(host, &cxlrd->cxlsd.cxld); > } > +static inline bool cxl_root_decoder_is_bi(struct cxl_root_decoder *cxlrd) > +{ > + return cxlrd->cxlsd.cxld.flags & CXL_DECODER_F_BI; > +} > int cxl_endpoint_autoremove(struct cxl_memdev *cxlmd, struct cxl_port *endpoint); > > /**