Skip to content

Commit b1966a1

Browse files
committed
Merge tag 'cxl-fixes-6.12-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl
Pull cxl fixes from Ira Weiny: "The bulk of these fixes center around an initialization order bug reported by Gregory Price and some additional fall out from the debugging effort. In summary, cxl_acpi and cxl_mem race and previously worked because of a bus_rescan_devices() while testing without modules built in. Unfortunately with modules built in the rescan would fail due to the cxl_port driver being registered late via the build order. Furthermore it was found bus_rescan_devices() did not guarantee a probe barrier which CXL was expecting. Additional fixes to cxl-test and decoder allocation came along as they were found in this debugging effort. The other fixes are pretty minor but one affects trace point data seen by user space. Summary: - Fix crashes when running with cxl-test code - Fix Trace DRAM Event Record field decodes - Fix module/built in initialization order errors - Fix use after free on decoder shutdowns - Fix out of order decoder allocations - Improve cxl-test to better reflect real world systems" * tag 'cxl-fixes-6.12-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: cxl/test: Improve init-order fidelity relative to real-world systems cxl/port: Prevent out-of-order decoder allocation cxl/port: Fix use-after-free, permit out-of-order decoder shutdown cxl/acpi: Ensure ports ready at cxl_acpi_probe() return cxl/port: Fix cxl_bus_rescan() vs bus_rescan_devices() cxl/port: Fix CXL port initialization order when the subsystem is built-in cxl/events: Fix Trace DRAM Event Record cxl/core: Return error when cxl_endpoint_gather_bandwidth() handles a non-PCI device
2 parents f4a1e8e + 3a2b97b commit b1966a1

File tree

14 files changed

+302
-159
lines changed

14 files changed

+302
-159
lines changed

drivers/base/core.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4037,6 +4037,41 @@ int device_for_each_child_reverse(struct device *parent, void *data,
40374037
}
40384038
EXPORT_SYMBOL_GPL(device_for_each_child_reverse);
40394039

4040+
/**
4041+
* device_for_each_child_reverse_from - device child iterator in reversed order.
4042+
* @parent: parent struct device.
4043+
* @from: optional starting point in child list
4044+
* @fn: function to be called for each device.
4045+
* @data: data for the callback.
4046+
*
4047+
* Iterate over @parent's child devices, starting at @from, and call @fn
4048+
* for each, passing it @data. This helper is identical to
4049+
* device_for_each_child_reverse() when @from is NULL.
4050+
*
4051+
* @fn is checked each iteration. If it returns anything other than 0,
4052+
* iteration stop and that value is returned to the caller of
4053+
* device_for_each_child_reverse_from();
4054+
*/
4055+
int device_for_each_child_reverse_from(struct device *parent,
4056+
struct device *from, const void *data,
4057+
int (*fn)(struct device *, const void *))
4058+
{
4059+
struct klist_iter i;
4060+
struct device *child;
4061+
int error = 0;
4062+
4063+
if (!parent->p)
4064+
return 0;
4065+
4066+
klist_iter_init_node(&parent->p->klist_children, &i,
4067+
(from ? &from->p->knode_parent : NULL));
4068+
while ((child = prev_device(&i)) && !error)
4069+
error = fn(child, data);
4070+
klist_iter_exit(&i);
4071+
return error;
4072+
}
4073+
EXPORT_SYMBOL_GPL(device_for_each_child_reverse_from);
4074+
40404075
/**
40414076
* device_find_child - device iterator for locating a particular device.
40424077
* @parent: parent struct device

drivers/cxl/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ config CXL_ACPI
6060
default CXL_BUS
6161
select ACPI_TABLE_LIB
6262
select ACPI_HMAT
63+
select CXL_PORT
6364
help
6465
Enable support for host managed device memory (HDM) resources
6566
published by a platform's ACPI CXL memory layout description. See

drivers/cxl/Makefile

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
# SPDX-License-Identifier: GPL-2.0
2+
3+
# Order is important here for the built-in case:
4+
# - 'core' first for fundamental init
5+
# - 'port' before platform root drivers like 'acpi' so that CXL-root ports
6+
# are immediately enabled
7+
# - 'mem' and 'pmem' before endpoint drivers so that memdevs are
8+
# immediately enabled
9+
# - 'pci' last, also mirrors the hardware enumeration hierarchy
210
obj-y += core/
3-
obj-$(CONFIG_CXL_PCI) += cxl_pci.o
4-
obj-$(CONFIG_CXL_MEM) += cxl_mem.o
11+
obj-$(CONFIG_CXL_PORT) += cxl_port.o
512
obj-$(CONFIG_CXL_ACPI) += cxl_acpi.o
613
obj-$(CONFIG_CXL_PMEM) += cxl_pmem.o
7-
obj-$(CONFIG_CXL_PORT) += cxl_port.o
14+
obj-$(CONFIG_CXL_MEM) += cxl_mem.o
15+
obj-$(CONFIG_CXL_PCI) += cxl_pci.o
816

9-
cxl_mem-y := mem.o
10-
cxl_pci-y := pci.o
17+
cxl_port-y := port.o
1118
cxl_acpi-y := acpi.o
1219
cxl_pmem-y := pmem.o security.o
13-
cxl_port-y := port.o
20+
cxl_mem-y := mem.o
21+
cxl_pci-y := pci.o

drivers/cxl/acpi.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,13 @@ static void __exit cxl_acpi_exit(void)
924924

925925
/* load before dax_hmem sees 'Soft Reserved' CXL ranges */
926926
subsys_initcall(cxl_acpi_init);
927+
928+
/*
929+
* Arrange for host-bridge ports to be active synchronous with
930+
* cxl_acpi_probe() exit.
931+
*/
932+
MODULE_SOFTDEP("pre: cxl_port");
933+
927934
module_exit(cxl_acpi_exit);
928935
MODULE_DESCRIPTION("CXL ACPI: Platform Support");
929936
MODULE_LICENSE("GPL v2");

drivers/cxl/core/cdat.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,9 @@ static int cxl_endpoint_gather_bandwidth(struct cxl_region *cxlr,
641641
void *ptr;
642642
int rc;
643643

644+
if (!dev_is_pci(cxlds->dev))
645+
return -ENODEV;
646+
644647
if (cxlds->rcd)
645648
return -ENODEV;
646649

drivers/cxl/core/hdm.c

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,44 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld)
712712
return 0;
713713
}
714714

715-
static int cxl_decoder_reset(struct cxl_decoder *cxld)
715+
static int commit_reap(struct device *dev, const void *data)
716+
{
717+
struct cxl_port *port = to_cxl_port(dev->parent);
718+
struct cxl_decoder *cxld;
719+
720+
if (!is_switch_decoder(dev) && !is_endpoint_decoder(dev))
721+
return 0;
722+
723+
cxld = to_cxl_decoder(dev);
724+
if (port->commit_end == cxld->id &&
725+
((cxld->flags & CXL_DECODER_F_ENABLE) == 0)) {
726+
port->commit_end--;
727+
dev_dbg(&port->dev, "reap: %s commit_end: %d\n",
728+
dev_name(&cxld->dev), port->commit_end);
729+
}
730+
731+
return 0;
732+
}
733+
734+
void cxl_port_commit_reap(struct cxl_decoder *cxld)
735+
{
736+
struct cxl_port *port = to_cxl_port(cxld->dev.parent);
737+
738+
lockdep_assert_held_write(&cxl_region_rwsem);
739+
740+
/*
741+
* Once the highest committed decoder is disabled, free any other
742+
* decoders that were pinned allocated by out-of-order release.
743+
*/
744+
port->commit_end--;
745+
dev_dbg(&port->dev, "reap: %s commit_end: %d\n", dev_name(&cxld->dev),
746+
port->commit_end);
747+
device_for_each_child_reverse_from(&port->dev, &cxld->dev, NULL,
748+
commit_reap);
749+
}
750+
EXPORT_SYMBOL_NS_GPL(cxl_port_commit_reap, CXL);
751+
752+
static void cxl_decoder_reset(struct cxl_decoder *cxld)
716753
{
717754
struct cxl_port *port = to_cxl_port(cxld->dev.parent);
718755
struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
@@ -721,14 +758,14 @@ static int cxl_decoder_reset(struct cxl_decoder *cxld)
721758
u32 ctrl;
722759

723760
if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)
724-
return 0;
761+
return;
725762

726-
if (port->commit_end != id) {
763+
if (port->commit_end == id)
764+
cxl_port_commit_reap(cxld);
765+
else
727766
dev_dbg(&port->dev,
728767
"%s: out of order reset, expected decoder%d.%d\n",
729768
dev_name(&cxld->dev), port->id, port->commit_end);
730-
return -EBUSY;
731-
}
732769

733770
down_read(&cxl_dpa_rwsem);
734771
ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
@@ -741,7 +778,6 @@ static int cxl_decoder_reset(struct cxl_decoder *cxld)
741778
writel(0, hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id));
742779
up_read(&cxl_dpa_rwsem);
743780

744-
port->commit_end--;
745781
cxld->flags &= ~CXL_DECODER_F_ENABLE;
746782

747783
/* Userspace is now responsible for reconfiguring this decoder */
@@ -751,8 +787,6 @@ static int cxl_decoder_reset(struct cxl_decoder *cxld)
751787
cxled = to_cxl_endpoint_decoder(&cxld->dev);
752788
cxled->state = CXL_DECODER_STATE_MANUAL;
753789
}
754-
755-
return 0;
756790
}
757791

758792
static int cxl_setup_hdm_decoder_from_dvsec(

drivers/cxl/core/port.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,11 +2084,18 @@ static void cxl_bus_remove(struct device *dev)
20842084

20852085
static struct workqueue_struct *cxl_bus_wq;
20862086

2087-
static void cxl_bus_rescan_queue(struct work_struct *w)
2087+
static int cxl_rescan_attach(struct device *dev, void *data)
20882088
{
2089-
int rc = bus_rescan_devices(&cxl_bus_type);
2089+
int rc = device_attach(dev);
2090+
2091+
dev_vdbg(dev, "rescan: %s\n", rc ? "attach" : "detached");
20902092

2091-
pr_debug("CXL bus rescan result: %d\n", rc);
2093+
return 0;
2094+
}
2095+
2096+
static void cxl_bus_rescan_queue(struct work_struct *w)
2097+
{
2098+
bus_for_each_dev(&cxl_bus_type, NULL, NULL, cxl_rescan_attach);
20922099
}
20932100

20942101
void cxl_bus_rescan(void)

drivers/cxl/core/region.c

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,8 @@ static int cxl_region_invalidate_memregion(struct cxl_region *cxlr)
232232
"Bypassing cpu_cache_invalidate_memregion() for testing!\n");
233233
return 0;
234234
} else {
235-
dev_err(&cxlr->dev,
236-
"Failed to synchronize CPU cache state\n");
235+
dev_WARN(&cxlr->dev,
236+
"Failed to synchronize CPU cache state\n");
237237
return -ENXIO;
238238
}
239239
}
@@ -242,19 +242,17 @@ static int cxl_region_invalidate_memregion(struct cxl_region *cxlr)
242242
return 0;
243243
}
244244

245-
static int cxl_region_decode_reset(struct cxl_region *cxlr, int count)
245+
static void cxl_region_decode_reset(struct cxl_region *cxlr, int count)
246246
{
247247
struct cxl_region_params *p = &cxlr->params;
248-
int i, rc = 0;
248+
int i;
249249

250250
/*
251-
* Before region teardown attempt to flush, and if the flush
252-
* fails cancel the region teardown for data consistency
253-
* concerns
251+
* Before region teardown attempt to flush, evict any data cached for
252+
* this region, or scream loudly about missing arch / platform support
253+
* for CXL teardown.
254254
*/
255-
rc = cxl_region_invalidate_memregion(cxlr);
256-
if (rc)
257-
return rc;
255+
cxl_region_invalidate_memregion(cxlr);
258256

259257
for (i = count - 1; i >= 0; i--) {
260258
struct cxl_endpoint_decoder *cxled = p->targets[i];
@@ -277,23 +275,17 @@ static int cxl_region_decode_reset(struct cxl_region *cxlr, int count)
277275
cxl_rr = cxl_rr_load(iter, cxlr);
278276
cxld = cxl_rr->decoder;
279277
if (cxld->reset)
280-
rc = cxld->reset(cxld);
281-
if (rc)
282-
return rc;
278+
cxld->reset(cxld);
283279
set_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags);
284280
}
285281

286282
endpoint_reset:
287-
rc = cxled->cxld.reset(&cxled->cxld);
288-
if (rc)
289-
return rc;
283+
cxled->cxld.reset(&cxled->cxld);
290284
set_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags);
291285
}
292286

293287
/* all decoders associated with this region have been torn down */
294288
clear_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags);
295-
296-
return 0;
297289
}
298290

299291
static int commit_decoder(struct cxl_decoder *cxld)
@@ -409,16 +401,8 @@ static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
409401
* still pending.
410402
*/
411403
if (p->state == CXL_CONFIG_RESET_PENDING) {
412-
rc = cxl_region_decode_reset(cxlr, p->interleave_ways);
413-
/*
414-
* Revert to committed since there may still be active
415-
* decoders associated with this region, or move forward
416-
* to active to mark the reset successful
417-
*/
418-
if (rc)
419-
p->state = CXL_CONFIG_COMMIT;
420-
else
421-
p->state = CXL_CONFIG_ACTIVE;
404+
cxl_region_decode_reset(cxlr, p->interleave_ways);
405+
p->state = CXL_CONFIG_ACTIVE;
422406
}
423407
}
424408

@@ -794,26 +778,50 @@ static size_t show_targetN(struct cxl_region *cxlr, char *buf, int pos)
794778
return rc;
795779
}
796780

781+
static int check_commit_order(struct device *dev, const void *data)
782+
{
783+
struct cxl_decoder *cxld = to_cxl_decoder(dev);
784+
785+
/*
786+
* if port->commit_end is not the only free decoder, then out of
787+
* order shutdown has occurred, block further allocations until
788+
* that is resolved
789+
*/
790+
if (((cxld->flags & CXL_DECODER_F_ENABLE) == 0))
791+
return -EBUSY;
792+
return 0;
793+
}
794+
797795
static int match_free_decoder(struct device *dev, void *data)
798796
{
797+
struct cxl_port *port = to_cxl_port(dev->parent);
799798
struct cxl_decoder *cxld;
800-
int *id = data;
799+
int rc;
801800

802801
if (!is_switch_decoder(dev))
803802
return 0;
804803

805804
cxld = to_cxl_decoder(dev);
806805

807-
/* enforce ordered allocation */
808-
if (cxld->id != *id)
806+
if (cxld->id != port->commit_end + 1)
809807
return 0;
810808

811-
if (!cxld->region)
812-
return 1;
813-
814-
(*id)++;
809+
if (cxld->region) {
810+
dev_dbg(dev->parent,
811+
"next decoder to commit (%s) is already reserved (%s)\n",
812+
dev_name(dev), dev_name(&cxld->region->dev));
813+
return 0;
814+
}
815815

816-
return 0;
816+
rc = device_for_each_child_reverse_from(dev->parent, dev, NULL,
817+
check_commit_order);
818+
if (rc) {
819+
dev_dbg(dev->parent,
820+
"unable to allocate %s due to out of order shutdown\n",
821+
dev_name(dev));
822+
return 0;
823+
}
824+
return 1;
817825
}
818826

819827
static int match_auto_decoder(struct device *dev, void *data)
@@ -840,7 +848,6 @@ cxl_region_find_decoder(struct cxl_port *port,
840848
struct cxl_region *cxlr)
841849
{
842850
struct device *dev;
843-
int id = 0;
844851

845852
if (port == cxled_to_port(cxled))
846853
return &cxled->cxld;
@@ -849,7 +856,7 @@ cxl_region_find_decoder(struct cxl_port *port,
849856
dev = device_find_child(&port->dev, &cxlr->params,
850857
match_auto_decoder);
851858
else
852-
dev = device_find_child(&port->dev, &id, match_free_decoder);
859+
dev = device_find_child(&port->dev, NULL, match_free_decoder);
853860
if (!dev)
854861
return NULL;
855862
/*
@@ -2054,13 +2061,7 @@ static int cxl_region_detach(struct cxl_endpoint_decoder *cxled)
20542061
get_device(&cxlr->dev);
20552062

20562063
if (p->state > CXL_CONFIG_ACTIVE) {
2057-
/*
2058-
* TODO: tear down all impacted regions if a device is
2059-
* removed out of order
2060-
*/
2061-
rc = cxl_region_decode_reset(cxlr, p->interleave_ways);
2062-
if (rc)
2063-
goto out;
2064+
cxl_region_decode_reset(cxlr, p->interleave_ways);
20642065
p->state = CXL_CONFIG_ACTIVE;
20652066
}
20662067

0 commit comments

Comments
 (0)